module UnitTests.Distribution.Client.Store (tests) where --import Control.Monad --import Control.Concurrent (forkIO, threadDelay) --import Control.Concurrent.MVar import qualified Data.Set as Set import System.FilePath import System.Directory --import System.Random import Distribution.Package (UnitId, mkUnitId) import Distribution.Compiler (CompilerId(..), CompilerFlavor(..)) import Distribution.Version (mkVersion) import Distribution.Verbosity (Verbosity, silent) import Distribution.Simple.Utils (withTempDirectory) import Distribution.Client.Store import Distribution.Client.RebuildMonad import Test.Tasty import Test.Tasty.HUnit tests :: [TestTree] tests = [ testCase "list content empty" testListEmpty , testCase "install serial" testInstallSerial --, testCase "install parallel" testInstallParallel --TODO: figure out some way to do a parallel test, see issue below ] testListEmpty :: Assertion testListEmpty = withTempDirectory verbosity "." "store-" $ \tmp -> do let storeDirLayout = defaultStoreDirLayout (tmp "store") assertStoreEntryExists storeDirLayout compid unitid False assertStoreContent tmp storeDirLayout compid Set.empty where compid = CompilerId GHC (mkVersion [1,0]) unitid = mkUnitId "foo-1.0-xyz" testInstallSerial :: Assertion testInstallSerial = withTempDirectory verbosity "." "store-" $ \tmp -> do let storeDirLayout = defaultStoreDirLayout (tmp "store") copyFiles file content dir = do -- we copy into a prefix inside the tmp dir and return the prefix let destprefix = dir "prefix" createDirectory destprefix writeFile (destprefix file) content return (destprefix,[]) assertNewStoreEntry tmp storeDirLayout compid unitid1 (copyFiles "file1" "content-foo") (return ()) UseNewStoreEntry assertNewStoreEntry tmp storeDirLayout compid unitid1 (copyFiles "file1" "content-foo") (return ()) UseExistingStoreEntry assertNewStoreEntry tmp storeDirLayout compid unitid2 (copyFiles "file2" "content-bar") (return ()) UseNewStoreEntry let pkgDir :: UnitId -> FilePath pkgDir = storePackageDirectory storeDirLayout compid assertFileEqual (pkgDir unitid1 "file1") "content-foo" assertFileEqual (pkgDir unitid2 "file2") "content-bar" where compid = CompilerId GHC (mkVersion [1,0]) unitid1 = mkUnitId "foo-1.0-xyz" unitid2 = mkUnitId "bar-2.0-xyz" {- -- unfortunately a parallel test like the one below is thwarted by the normal -- process-internal file locking. If that locking were not in place then we -- ought to get the blocking behaviour, but due to the normal Handle locking -- it just fails instead. testInstallParallel :: Assertion testInstallParallel = withTempDirectory verbosity "." "store-" $ \tmp -> do let storeDirLayout = defaultStoreDirLayout (tmp "store") sync1 <- newEmptyMVar sync2 <- newEmptyMVar outv <- newEmptyMVar regv <- newMVar (0 :: Int) sequence_ [ do forkIO $ do let copyFiles dir = do delay <- randomRIO (1,100000) writeFile (dir "file") (show n) putMVar sync1 () readMVar sync2 threadDelay delay register = do modifyMVar_ regv (return . (+1)) threadDelay 200000 o <- newStoreEntry verbosity storeDirLayout compid unitid copyFiles register putMVar outv (n, o) | n <- [0..9 :: Int] ] replicateM_ 10 (takeMVar sync1) -- all threads are in the copyFiles action concurrently, release them: putMVar sync2 () outcomes <- replicateM 10 (takeMVar outv) regcount <- readMVar regv let regcount' = length [ () | (_, UseNewStoreEntry) <- outcomes ] assertEqual "num registrations" 1 regcount assertEqual "num registrations" 1 regcount' assertStoreContent tmp storeDirLayout compid (Set.singleton unitid) let pkgDir :: UnitId -> FilePath pkgDir = storePackageDirectory storeDirLayout compid case [ n | (n, UseNewStoreEntry) <- outcomes ] of [n] -> assertFileEqual (pkgDir unitid "file") (show n) _ -> assertFailure "impossible" where compid = CompilerId GHC (mkVersion [1,0]) unitid = mkUnitId "foo-1.0-xyz" -} ------------- -- Utils assertNewStoreEntry :: FilePath -> StoreDirLayout -> CompilerId -> UnitId -> (FilePath -> IO (FilePath,[FilePath])) -> IO () -> NewStoreEntryOutcome -> Assertion assertNewStoreEntry tmp storeDirLayout compid unitid copyFiles register expectedOutcome = do entries <- runRebuild tmp $ getStoreEntries storeDirLayout compid outcome <- newStoreEntry verbosity storeDirLayout compid unitid copyFiles register assertEqual "newStoreEntry outcome" expectedOutcome outcome assertStoreEntryExists storeDirLayout compid unitid True let expected = Set.insert unitid entries assertStoreContent tmp storeDirLayout compid expected assertStoreEntryExists :: StoreDirLayout -> CompilerId -> UnitId -> Bool -> Assertion assertStoreEntryExists storeDirLayout compid unitid expected = do actual <- doesStoreEntryExist storeDirLayout compid unitid assertEqual "store entry exists" expected actual assertStoreContent :: FilePath -> StoreDirLayout -> CompilerId -> Set.Set UnitId -> Assertion assertStoreContent tmp storeDirLayout compid expected = do actual <- runRebuild tmp $ getStoreEntries storeDirLayout compid assertEqual "store content" actual expected assertFileEqual :: FilePath -> String -> Assertion assertFileEqual path expected = do exists <- doesFileExist path assertBool ("file does not exist:\n" ++ path) exists actual <- readFile path assertEqual ("file content for:\n" ++ path) expected actual verbosity :: Verbosity verbosity = silent