packages feed

directory-contents-0.2.0.1: test/Test.hs

{-# Language QuasiQuotes #-}
import Control.Monad.IO.Class
import Coquina
import Data.Maybe
import Data.String.Here
import Data.Text (Text)
import System.Directory
import System.Directory.Contents
import System.Directory.Contents.Zipper
import System.Exit
import System.FilePath
import System.Process

main :: IO ()
main = do
  _ <- setup test1
  return ()

readme_md = [here|
This test directory tree has the following structure:
```
.
├── a -> A/a
├── A
│   ├── a
│   ├── A -> ../A  [recursive, not followed]
│   └── B -> ../B
│       ├── A -> ../A  [recursive, not followed]
│       └── b
├── B
│   ├── A -> ../A  [recursive, not followed]
│   └── b
└── README.md

6 directories, 5 files
```
|]

-- | Annoying that we have to set it up this way, but there doesn't seem
-- to be any good way to include the symlinks we want as test fixtures
-- in cabal.
--
-- This function will manually recreate the contents of test/fixtures
setup :: (FilePath -> IO ()) -> IO (Text, Text, Either Int ())
setup testRunner = runShell $ do
  inTempDirectory "directory-contents-test" $ \fp -> do
    let fixtures = fp </> "test" </> "fixtures"
        test1 = fixtures </> "test-1"
        test2 = fixtures </> "test-2"
    run $ proc "mkdir" ["-p", fixtures]
    run $ proc "mkdir" ["-p", test1]
    run $ proc "mkdir" ["-p", test2]
    liftIO $ do
      writeFile (fixtures </> "test-1" </> "README.md") readme_md
      writeFile (fixtures </> "test-2" </> "rocket") "🚀"
    run $ proc "mkdir" ["-p", test1 </> "A"]
    run $ proc "touch" [test1 </> "A" </> "a"]
    run $ proc "mkdir" ["-p", test1 </> "B"]
    run $ proc "touch" [test1 </> "B" </> "b"]
    run $ proc "mkdir" ["-p", test1 </> "C"]
    run $ proc "ln" ["-s", "../A", test1 </> "A" </> "A"]
    run $ proc "ln" ["-s", "../B", test1 </> "A" </> "B"]
    run $ proc "ln" ["-s", "../A", test1 </> "B" </> "A"]
    run $ proc "ln" ["-s", "../README.md", test1 </> "C" </> "info.md"]
    run $ proc "ln" ["-s", "../test-2", test1 </> "D"]
    run $ proc "ln" ["-s", "D/rocket", test1 </> "d_rocket"]
    run $ proc "ln" ["-s", "../test-2/rocket", test1 </> "rocket"]
    liftIO $ testRunner test1

test1 :: FilePath -> IO ()
test1 fp = do
  p <- test
    "Build DirTree in the presence of recursive symlinks"
    (buildDirTree fp)
  test
    "Navigate to a directory that should exist"
    (pure $ walkContents "C" p)
  filtered <- test "Filter contents based on a predicate" $
    pure (filterDirTree ((/="README.md") . takeFileName) p)
  test "Check that filtering didn't prune directories" $
    pure $ walkContents "C" p
  test "Check that pruneDirTree does prune empty directories" $
    pure $ expectNothing $ walkContents "C" =<< pruneDirTree filtered
  rocket <- test "Check that symlink can be traversed" $
    pure (walkContents "D/rocket" filtered)
  test "Check that filtering removes internal symlinks to filtered files" $
    pure $ expectNothing $
      walkContents "C/info.md" filtered
  let filterRockets = filterDirTree ((/="rocket") . takeFileName)
  test "Check that filtering removes external symlinks to filtered files" $
    pure $ expectNothing $
      walkContents "d_rocket" =<< filterRockets p
  test "Check that dereferenced symlinks to filtered files are not removed" $ do
    deref <- dereferenceSymlinks p
    pure $ walkContents "d_rocket" =<< filterRockets deref
  test "Zipper down then up from root == id" $
    pure $ expectTrue $ fmap focused (up =<< down (zipped p)) == Just p
  test "Use zipper to remove a node" $ do
    let a = up =<< remove =<< downTo "info.md" =<< downTo "C" (zipped p)
    pure $ expectNothing $ walkContents "C/info.md" . focused =<< a
  readFile (filePath rocket) >>=
    putStr >> putStrLn " All systems go!"

test :: String -> IO (Maybe a) -> IO a
test describe run = do
  putStr describe
  putStr " ... "
  result <- run
  case result of
    Nothing -> putStrLn "FAILED" >> exitWith (ExitFailure 1)
    Just r -> putStrLn "passed" >> return r

expectNothing :: Maybe a -> Maybe ()
expectNothing x = case x of
  Nothing -> Just ()
  Just _ -> Nothing

expectTrue :: Bool -> Maybe ()
expectTrue x = if x then Just () else Nothing