packages feed

pfile-0.1.0.0: test/PFile/Mount/Tests.hs

{-# OPTIONS_GHC -Wno-name-shadowing #-}

{-# LANGUAGE BlockArguments    #-}
{-# LANGUAGE LambdaCase        #-}
{-# LANGUAGE OverloadedStrings #-}

module PFile.Mount.Tests
  ( tests
  ) where

import qualified Data.HashSet               as Set
import           PFile.Mount
  ( Mount (..)
  , Root (..)
  , mount
  , mountPath
  , originPath
  , unmount
  )
import           PFile.Mount.Tests.Env      ()
import           PFile.Path
  ( doesPathExist
  , dropDrive
  , findFiles
  , (<//>)
  )
import qualified PFile.Path                 as Path
import           PFile.Path.Tests           (ensureWriteFile)
import qualified PFile.Profile.LinkHandling as LinkHandling
import           PFile.Tests.Env            (withTempDirectory)
import           Protolude
import           System.FilePath            ((</>))
import           Test.Hspec
  ( around
  , describe
  , it
  , shouldBe
  , shouldReturn
  )
import           Test.HUnit                 (assertFailure)
import           Test.Tasty                 (TestTree, testGroup)
import           Test.Tasty.Hspec           (testSpec)
import           Test.Tasty.QuickCheck      (testProperty)

tests :: IO TestTree
tests = do
  unitTests_ <- unitTests
  pure $ testGroup "PFile.Mount" [properties, unitTests_]

properties :: TestTree
properties =
  testGroup
    "Properties"
    [ testProperty
        "`mountPath` removes drive from `path` and attaches result under `root`"
        \(Root root) path ->
          mountPath (Root root) path == Mount (root <//> dropDrive path)
    , testProperty
        "`originPath` is an inverse of `mountPath`"
        \(Root root) path ->
          originPath (Root root) (mountPath (Root root) path)
          & either (const False) (== path)
    ]

unitTests :: IO TestTree
unitTests = testSpec "Unit Tests" $
  around withTempDirectory do
    describe "mount" do
      it "mounts a file properly" \temporaryRoot ->
        (,) <$> filesNames <*> filesContents & traverse_ \(name, contents) ->
          mountFileTest temporaryRoot (temporaryRoot <//> "origin" </> name) contents
      it "mounts a directory properly" \temporaryRoot ->
        mountDirectoryTest temporaryRoot
          $ zip filesNames filesContents
    describe "unmount" do
      it "unmounts a file properly" \temporaryRoot ->
        (,) <$> filesNames <*> filesContents & traverse_ \(name, contents) ->
          unmountFileTest temporaryRoot (temporaryRoot <//> "origin" </> name) contents
      it "unmounts a directory properly" \temporaryRoot ->
        unmountDirectoryTest temporaryRoot
          $ zip filesNames filesContents
  where
    filesNames :: [FilePath]
    filesNames =
      [ "9h(ILN:/b<t{mg2"
      , "'*_"
      , "*CDy/|\rHh?/!@"
      , "C~(?3|(y/8&"
      , "1t=_rL'/%JB0\"8"
      , "ej/s$\\K.5ZT/Z*6rn-j\t3/x':@bj"
      , "YJ/%X4C$KMr=/z\r"
      , "f~NO*a j@7"
      , "swSgYJUM9r/.EYR\"wADf/*DJd:("
      , " Y(7*/|/<-[6nv3"
      , "yfF~"
      , "zETbVl w7./]^/[m>+"
      , ";)A/:H'^og@GB"
      , "\\g2\n( M#/scIU/o^A/Nj>/nC,ASzz7q"
      , "SB$N/;F"
      , "?,@Zo\tz/*|1%%\tWm}/ k>oAV_7M/yGn$+c 7AS"
      , "\"\\_"
      , "4g/M;FD901"
      , "\t$sL;G9/3zZ|p[1/w\\6f]/=926UD"
      ]

    filesContents :: [Text]
    filesContents =
      [ "\54951\170056NU+\"\92881a-\142680\177134\SOXKE\1097408\63631hq\1021187\ETBd\1019100#\1028440\1041796\1000925Gat"
      , "RD\177342\1036654\1084661\189502\1096769 \1011758\96759\67362\1058870\96334\74438o\1094509>\1095213[,\37703SFb\147814\25819F\153345#b"
      , "\SUB\170178\178560yv\1011719Q/\\$>L\GS7\1048669\&1z\1093012\1086214r\199200P,\nOrG{\1002826L"
      , "et\39933\152786\71222\51388WFY\DC1'il9kH\132409E\\C\1024119\68094]\991083B\11343\DC1Ez\18164"
      , "!dB\999918\SUBo`-<QrS<9+MT\1044433\DELt\96208\17044\1090040@1PP\147330]K"
      , "/7!SH\98989\183950J\"\138051\161420\35056WE<2F8&~)c\181084&!\78704N_$\15030"
      , "#[&)xPg$nH]9mt60\94378jU=*B^F[\29380x\199030D\92604"
      , "CD\149540\&1TqCL\132951H:,P)^4\2636I&{r?CmS\179807Nf*'"
      , "\SIhFN:CH=OS(2?{A+\GS\SOA\STXG{|nL?8!L_"
      , "CpH`8a?\NAK,\GS\ETXqG\ETXN9\EMa)\\1AYEco3Fd_"
      ]

mountFileTest :: Path.Absolute -> Path.Absolute -> Text -> IO ()
mountFileTest temporaryRoot originPath contents = do
  let root = temporaryRoot <//> "root"
  ensureWriteFile originPath contents
  mount LinkHandling.CopyLink (Root root) originPath & runExceptT >>= \case
    Left error -> assertFailure
      $ "Expected mount to succeed, instead got an error: " <> show error
    Right (Mount mountPath) -> do
      mountPath `shouldBe` root <//> dropDrive originPath
      readFile (Path.unAbsolute mountPath) `shouldReturn` contents
      whenM (doesPathExist originPath) . assertFailure
        $ "Expected file \"" <> Path.unAbsolute originPath <> "\" to not exist"

mountDirectoryTest :: Path.Absolute -> [(FilePath, Text)] -> IO ()
mountDirectoryTest temporaryRoot files = do
  let root = temporaryRoot <//> "root"
      originPath = temporaryRoot <//> "origin"
  files & traverse_ \(fileName, contents) ->
    ensureWriteFile (originPath <//> fileName) contents
  mount LinkHandling.CopyLink (Root root) originPath & runExceptT >>= \case
    Left error -> assertFailure
      $ "Expected mount to succeed, instead got an error: " <> show error
    Right (Mount mountPath) -> do
      mountPath `shouldBe` root <//> dropDrive originPath
      actualPaths <- findFiles mountPath
      let expectedPaths = files <&> \(fileName, _) -> mountPath <//> fileName
      Set.fromList actualPaths `shouldBe` Set.fromList expectedPaths
      files & traverse_ \(fileName, contents) ->
        readFile (Path.unAbsolute $ mountPath <//> fileName) `shouldReturn` contents
      whenM (doesPathExist originPath) . assertFailure
        $ "Expected directory \"" <> Path.unAbsolute originPath <> "\" to not exist"

unmountFileTest :: Path.Absolute -> Path.Absolute -> Text -> IO ()
unmountFileTest temporaryRoot originPath contents = do
  let root = temporaryRoot <//> "root"
      mountPath = root <//> dropDrive originPath
  ensureWriteFile mountPath contents
  unmount (Root root) (Mount mountPath) & runExceptT >>= \case
    Left error -> assertFailure
      $ "Expected unmount to succeed, instead got an error: " <> show error
    Right newOriginPath -> do
      newOriginPath `shouldBe` originPath
      readFile (Path.unAbsolute newOriginPath) `shouldReturn` contents
      whenM (doesPathExist mountPath) . assertFailure
        $ "Expected file \"" <> Path.unAbsolute mountPath <> "\" to not exist"

unmountDirectoryTest :: Path.Absolute -> [(FilePath, Text)] -> IO ()
unmountDirectoryTest temporaryRoot files = do
  let root = temporaryRoot <//> "root"
      originPath = temporaryRoot <//> "origin"
      mountPath = root <//> dropDrive originPath
  files & traverse_ \(fileName, contents) ->
    ensureWriteFile (mountPath <//> fileName) contents
  unmount (Root root) (Mount mountPath) & runExceptT >>= \case
    Left error -> assertFailure
      $ "Expected unmount to succeed, instead got an error: " <> show error
    Right newOriginPath -> do
      newOriginPath `shouldBe` originPath
      actualPaths <- findFiles newOriginPath
      let expectedPaths = files <&> \(fileName, _) -> newOriginPath <//> fileName
      Set.fromList actualPaths `shouldBe` Set.fromList expectedPaths
      files & traverse_ \(fileName, contents) ->
        readFile (Path.unAbsolute $ newOriginPath <//> fileName) `shouldReturn` contents
      whenM (doesPathExist mountPath) . assertFailure
        $ "Expected directory \"" <> Path.unAbsolute mountPath <> "\" to not exist"