module LinksSpec(linksSpec) where

import Control.Concurrent ( threadDelay )
import Control.Exception ( finally )
import qualified System.Directory as D
import System.Info ( os )
import System.IO.Error ( tryIOError )
import System.IO.Temp
import System.PosixCompat
import Test.Hspec
import Test.HUnit

isWindows :: Bool
isWindows = os == "mingw32"

linksSpec :: Spec
linksSpec = do
  describe "createSymbolicLink" $ do
    it "should error on Windows and succeed on other OSes" $ do
      runInTempDir $ do
        writeFile "file" ""
        result <- tryIOError $ createSymbolicLink "file" "file_link"
        case result of
          Left _  | isWindows -> return ()
          Right _ | isWindows -> do
            assertFailure "Succeeded while expected to fail on Windows"
          Left e              -> assertFailure $ "Expected to succeed, but failed with " ++ show e
          Right _             -> return ()
  describe "getSymbolicLinkStatus" $ do
    it "should detect symbolic link to a file" $ do
      runFileLinkTest $ do
        stat <- getSymbolicLinkStatus "file_link"
        assert $ isSymbolicLink stat
    it "should detect symbolic link to a directory" $ do
      runDirLinkTest $ do
        stat <- getSymbolicLinkStatus "dir_link"
        assert $ isSymbolicLink stat
    it "should give later time stamp than getFileStatus for link to file" $ do
      runFileLinkTest $ do
        lstat_mtime <- modificationTimeHiRes <$> getSymbolicLinkStatus "file_link"
        stat_mtime <- modificationTimeHiRes <$> getFileStatus "file_link"
        assert $ lstat_mtime > stat_mtime
    it "should give later time stamp than getFileStatus for link to dir" $ do
      runDirLinkTest $ do
        lstat_mtime <- modificationTimeHiRes <$> getSymbolicLinkStatus "dir_link"
        stat_mtime <- modificationTimeHiRes <$> getFileStatus "dir_link"
        assert $ lstat_mtime > stat_mtime
    it "should give a different fileID than getFileStatus for link to file" $ do
      runFileLinkTest $ do
        lstat_id <- fileID <$> getSymbolicLinkStatus "file_link"
        fstat_id <- fileID <$> getFileStatus "file_link"
        assert $ lstat_id /= fstat_id
    it "should give a different fileID than getFileStatus for link to dir" $ do
      runDirLinkTest $ do
        lstat_id <- fileID <$> getSymbolicLinkStatus "dir_link"
        fstat_id <- fileID <$> getFileStatus "dir_link"
        assert $ lstat_id /= fstat_id
  describe "getFileStatus" $ do
    it "should detect that symbolic link target is a file" $ do
      runFileLinkTest $ do
        stat <- getFileStatus "file_link"
        assert $ isRegularFile stat
    it "should detect that symbolic link target is a directory" $ do
      runDirLinkTest $ do
        stat <- getFileStatus "dir_link"
        assert $ isDirectory stat
    it "should be equal for link and link target (except access time)" $ do
      runFileLinkTest $ do
        fstat <- getFileStatus "file"
        flstat <- getFileStatus "file_link"
        assert $ fstat `mostlyEq` flstat
      runDirLinkTest $ do
        fstat <- getFileStatus "dir"
        flstat <- getFileStatus "dir_link"
        assert $ fstat `mostlyEq` flstat

  where

    runFileLinkTest action =
      runInTempDir $ do
        writeFile "file" ""
        threadDelay delay
        D.createFileLink "file" "file_link"
        action

    runDirLinkTest action =
      runInTempDir $ do
        D.createDirectory "dir"
        threadDelay delay
        D.createDirectoryLink "dir" "dir_link"
        action

    runInTempDir action = do
      orig <- D.getCurrentDirectory
      withTempDirectory orig "xxxxxxx" $ \tmp -> do
        D.setCurrentDirectory tmp
        action `finally` D.setCurrentDirectory orig

    -- We need to set the delay this high because otherwise the timestamp test
  above fails on Linux and Windows , though not on MacOS . This seems to be
  an artefact of the GHC runtime system which gives two subsequently
    -- created files the same timestamp unless the delay is large enough.
    delay = 10000

    -- Test equality for all parts except accessTime
    mostlyEq :: FileStatus -> FileStatus -> Bool
    mostlyEq x y = tuple x == tuple y
      where
        tuple s =
          ( deviceID s
          , fileID s
          , fileMode s
          , linkCount s
          , fileOwner s
          , fileGroup s
          , specialDeviceID s
          , fileSize s
          , modificationTime s
          , statusChangeTime s
          , modificationTimeHiRes s
          , statusChangeTimeHiRes s
          )

 We need to set the delay this high because otherwise the timestamp test
 created files the same timestamp unless the delay is large enough.
 Test equality for all parts except accessTime
module LinksSpec(linksSpec) where

import Control.Concurrent ( threadDelay )
import Control.Exception ( finally )
import qualified System.Directory as D
import System.Info ( os )
import System.IO.Error ( tryIOError )
import System.IO.Temp
import System.PosixCompat
import Test.Hspec
import Test.HUnit

isWindows :: Bool
isWindows = os == "mingw32"

linksSpec :: Spec
linksSpec = do
  describe "createSymbolicLink" $ do
    it "should error on Windows and succeed on other OSes" $ do
      runInTempDir $ do
        writeFile "file" ""
        result <- tryIOError $ createSymbolicLink "file" "file_link"
        case result of
          Left _  | isWindows -> return ()
          Right _ | isWindows -> do
            assertFailure "Succeeded while expected to fail on Windows"
          Left e              -> assertFailure $ "Expected to succeed, but failed with " ++ show e
          Right _             -> return ()
  describe "getSymbolicLinkStatus" $ do
    it "should detect symbolic link to a file" $ do
      runFileLinkTest $ do
        stat <- getSymbolicLinkStatus "file_link"
        assert $ isSymbolicLink stat
    it "should detect symbolic link to a directory" $ do
      runDirLinkTest $ do
        stat <- getSymbolicLinkStatus "dir_link"
        assert $ isSymbolicLink stat
    it "should give later time stamp than getFileStatus for link to file" $ do
      runFileLinkTest $ do
        lstat_mtime <- modificationTimeHiRes <$> getSymbolicLinkStatus "file_link"
        stat_mtime <- modificationTimeHiRes <$> getFileStatus "file_link"
        assert $ lstat_mtime > stat_mtime
    it "should give later time stamp than getFileStatus for link to dir" $ do
      runDirLinkTest $ do
        lstat_mtime <- modificationTimeHiRes <$> getSymbolicLinkStatus "dir_link"
        stat_mtime <- modificationTimeHiRes <$> getFileStatus "dir_link"
        assert $ lstat_mtime > stat_mtime
    it "should give a different fileID than getFileStatus for link to file" $ do
      runFileLinkTest $ do
        lstat_id <- fileID <$> getSymbolicLinkStatus "file_link"
        fstat_id <- fileID <$> getFileStatus "file_link"
        assert $ lstat_id /= fstat_id
    it "should give a different fileID than getFileStatus for link to dir" $ do
      runDirLinkTest $ do
        lstat_id <- fileID <$> getSymbolicLinkStatus "dir_link"
        fstat_id <- fileID <$> getFileStatus "dir_link"
        assert $ lstat_id /= fstat_id
  describe "getFileStatus" $ do
    it "should detect that symbolic link target is a file" $ do
      runFileLinkTest $ do
        stat <- getFileStatus "file_link"
        assert $ isRegularFile stat
    it "should detect that symbolic link target is a directory" $ do
      runDirLinkTest $ do
        stat <- getFileStatus "dir_link"
        assert $ isDirectory stat
    it "should be equal for link and link target (except access time)" $ do
      runFileLinkTest $ do
        fstat <- getFileStatus "file"
        flstat <- getFileStatus "file_link"
        assert $ fstat `mostlyEq` flstat
      runDirLinkTest $ do
        fstat <- getFileStatus "dir"
        flstat <- getFileStatus "dir_link"
        assert $ fstat `mostlyEq` flstat

  where

    runFileLinkTest action =
      runInTempDir $ do
        writeFile "file" ""
        threadDelay delay
        D.createFileLink "file" "file_link"
        action

    runDirLinkTest action =
      runInTempDir $ do
        D.createDirectory "dir"
        threadDelay delay
        D.createDirectoryLink "dir" "dir_link"
        action

    runInTempDir action = do
      orig <- D.getCurrentDirectory
      withTempDirectory orig "xxxxxxx" $ \tmp -> do
        D.setCurrentDirectory tmp
        action `finally` D.setCurrentDirectory orig

  above fails on Linux and Windows , though not on MacOS . This seems to be
  an artefact of the GHC runtime system which gives two subsequently
    delay = 10000

    mostlyEq :: FileStatus -> FileStatus -> Bool
    mostlyEq x y = tuple x == tuple y
      where
        tuple s =
          ( deviceID s
          , fileID s
          , fileMode s
          , linkCount s
          , fileOwner s
          , fileGroup s
          , specialDeviceID s
          , fileSize s
          , modificationTime s
          , statusChangeTime s
          , modificationTimeHiRes s
          , statusChangeTimeHiRes s
          )
