packages feed

libarchive-3.0.6.0: test/Spec.cpphs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE LambdaCase #-}
module Main ( main ) where

import           Codec.Archive
import           Codec.Archive.Roundtrip (itPacksUnpacks, itPacksUnpacksViaFS, roundtrip, roundtripFreaky, roundtripStrict, roundtripStrict')
import           Codec.Archive.Test
import qualified Data.ByteString         as BS
import qualified Data.ByteString.Lazy    as BSL
import           Data.Either             (isRight)
import           Data.List (uncons)
import           Data.Foldable           (traverse_)
import           System.Directory        (doesDirectoryExist, listDirectory)
import           System.FilePath         ((</>))
import           System.IO.Temp          (withSystemTempDirectory)
import           Control.Exception (evaluate)
import           Control.DeepSeq
#ifdef mingw32_HOST_OS
import           System.Environment
import           Test.Hspec.Core.Runner
#endif
import           Test.Hspec

import qualified Data.ByteString.Base16        as B16
import qualified Crypto.Hash.SHA256            as SHA256
import qualified Data.Text.Encoding            as E
import qualified Data.Text                     as T

testFp :: FilePath -> Spec
testFp fp = it ("sucessfully unpacks/packs (" ++ fp ++ ")") $
    roundtrip fp >>= (`shouldSatisfy` isRight)

testFpStrict :: FilePath -> Spec
testFpStrict fp = it ("works on strict bytestring (" ++ fp ++ ")") $
    roundtripStrict fp >>= (`shouldSatisfy` isRight)

testFpStrict' :: FilePath -> Spec
testFpStrict' fp = it ("works on strict bytestring (" ++ fp ++ ")") $
    roundtripStrict' fp >>= (`shouldSatisfy` isRight)

testFpFreaky :: FilePath -> Spec
testFpFreaky fp = it ("works on nonstandard bytestring (" ++ fp ++ ")") $
    roundtripFreaky fp >>= (`shouldSatisfy` isRight)

unpack :: FilePath -> IO (Either ArchiveResult ())
unpack fp = withSystemTempDirectory "libarchive" $
    \tmp -> fmap force (evaluate =<< (runArchiveM $ unpackArchive fp tmp))

readArchiveFile' :: FilePath -> IO (Either ArchiveResult [Entry FilePath BS.ByteString])
readArchiveFile' fp = evaluate =<< (runArchiveM . readArchiveFile $ fp)

testUnpackLibarchive :: FilePath -> Spec
testUnpackLibarchive fp = it ("unpacks " ++ fp) $
    unpack fp >>= (`shouldSatisfy` isRight)

testReadArchiveFile :: FilePath -> Spec
testReadArchiveFile fp = it ("reads " ++ fp) $
    readArchiveFile' fp >>= (`shouldSatisfy` isRight)

repack :: ([Entry FilePath BS.ByteString] -> BSL.ByteString) -> String -> FilePath -> Spec
repack packer str fp = it ("should repack (" ++ str ++ ")") $ do
    res <- fmap force (evaluate =<< (runArchiveM $ packer <$> readArchiveFile fp))
    res `shouldSatisfy` bsValid

bsValid :: Either a BSL.ByteString -> Bool
bsValid = \x -> case x of { Left{} -> False; Right b -> not $ BSL.null b }

testArchiveContentChecksum :: FilePath  -- ^ archive
                           -> FilePath  -- ^ file inside archive
						   -> String    -- ^ base64 encoded sha256sum
						   -> Spec
testArchiveContentChecksum archive file sha = it ("Verify checksum of " <> file <> " in archive " <> archive) $ do
  entries' <-  runArchiveM (readArchiveFile archive)
  entries <- either (ioError . userError . show) pure $ entries'
  let cabal_exe = uncons . fmap content . filter (\Entry{..} -> filepath == file) $ entries
  c <- case cabal_exe of
         (Just (NormalFile c, _)) -> pure c
         _ -> ioError $ userError $ "Could not find file " <> file <> " in archive " <> archive
  digest <- either (ioError . userError . show) pure . E.decodeUtf8' . B16.encode . SHA256.hash $ c
  digest `shouldBe` T.pack sha

main :: IO ()
main = do

    dir <- doesDirectoryExist "test/data"
    tarballs <- if dir then listDirectory "test/data" else pure []
    let tarPaths = ("test/data" </>) <$> tarballs

    hspec' $ do
        describe "roundtrip" $ do

            traverse_ testFp tarPaths
#ifndef LOW_MEMORY
            traverse_ (\fp -> repack entriesToBSLzip ("zip/" ++ fp) fp) tarPaths
            traverse_ (repack entriesToBSLCpio "cpio") tarPaths
            -- traverse_ (repack entriesToBSLXar "xar") tarPaths
            traverse_ (repack entriesToBSLShar "shar") tarPaths
            traverse_ (repack entriesToBSL7zip "7zip") tarPaths
            traverse_ testFpFreaky tarPaths
            traverse_ testFpStrict tarPaths
            traverse_ testFpStrict' tarPaths
#endif
            traverse_ testUnpackLibarchive tarPaths
            traverse_ testReadArchiveFile tarPaths

            context "with symlinks" $ do
                let entries =
                        [ simpleDir "x/"
                        , simpleFile "x/a.txt" (NormalFile "referenced")
                        , simpleFile "x/b.txt" (Symlink "a.txt" SymlinkUndefined)
                        ]
                itPacksUnpacks entries
                itPacksUnpacksViaFS entries

            context "with hardlinks" $ do
                let entries =
                        [ simpleDir "x/"
                        , simpleFile "x/a.txt" (NormalFile "shared")
                        , simpleFile "x/b.txt" (Hardlink "x/a.txt")
                        ]
                itPacksUnpacks entries
                context "issue#4" $ itPacksUnpacksViaFS entries

            context "with forward referenced hardlinks" $ do
                let entries =
                        [ simpleDir "x/"
                        , simpleFile "x/b.txt" (Hardlink "x/a.txt")
                        , simpleFile "x/a.txt" (NormalFile "shared")
                        ]
                itPacksUnpacks entries
                xcontext "re-ordering on unpack" $ itPacksUnpacksViaFS entries

            xcontext "having entry without ownership" . itPacksUnpacks $
                [ stripOwnership (simpleFile "a.txt" (NormalFile "text")) ]
            xcontext "having entry without timestamp" . itPacksUnpacks $
                [ stripTime (simpleFile "a.txt" (NormalFile "text")) ]

        describe "checksums" $ do
		  testArchiveContentChecksum
		    "test/data/plan_json.zip"
			"plan.json"
			"5eac50e94666fff2491e43f81a670ef243c272aa3afe1fc59c720004fe186feb"

  where
#ifdef mingw32_HOST_OS
    hspec' spec = getArgs >>= readConfig defaultConfig >>= (\c -> withArgs [] . runSpec spec $ c{ configConcurrentJobs = Nothing }) >>= evaluateSummary
#else
    hspec' = hspec
#endif