module Test.Database(main) where import Control.Concurrent.Extra import Control.Exception.Extra import Control.Monad import Data.List import Data.IORef import Development.Shake import Development.Shake.Database import Development.Shake.FilePath import System.Time.Extra import System.Directory as IO import Test.Type rules = do "*.out" %> \out -> do liftIO $ appendFile "log.txt" "x" copyFile' (out -<.> "in") out removeFilesAfter "." ["log.txt"] "*.err" %> \out -> fail out phony "sleep" $ liftIO $ sleep 20 main = testSimple $ do let opts = shakeOptions{shakeFiles="/dev/null"} writeFile "a.in" "a" writeFile "b.in" "b" sleepFileTime writeFile "log.txt" "" (open, close) <- shakeOpenDatabase opts rules db <- open ([12], after) <- shakeRunDatabase db [need ["a.out"] >> return 12] assertContents "log.txt" "x" writeFile "a.in" "A" shakeRunDatabase db [need ["a.out","b.out"]] assertContents "a.out" "A" assertContents "log.txt" "xxx" ([13,14], _) <- shakeRunDatabase db [need ["a.out"] >> return 13, return 14] assertContents "log.txt" "xxx" live <- shakeLiveFilesDatabase db sort live === ["a.in","a.out"] shakeProfileDatabase db "-" -- check that parallel runs blow up, and that we can throw async exceptions to kill it assertWithin 10 $ do threads <- newBarrier results <- replicateM 2 newBarrier ts <- forM results $ \result -> forkFinally (void $ shakeRunDatabase db [need ["sleep"]]) $ \r -> mask_ $ do print $ "Failed with " ++ show r signalBarrier result r threads <- waitBarrier threads me <- myThreadId forM_ threads $ \t -> when (t /= me) $ throwTo t $ ErrorCall "ab123c" signalBarrier threads ts results <- show <$> mapM waitBarrier results assertBool ("ab123c" `isInfixOf` results) "Contains ab123c" assertBool ("currently running" `isInfixOf` results) "Contains 'currently using'" close assertException ["already closed"] $ shakeRunDatabase db [] shakeRunAfter opts after assertBoolIO (not <$> IO.doesFileExist "log.txt") "Log must be deleted" errs <- shakeWithDatabase opts{shakeStaunch=True, shakeVerbosity=Silent} rules $ \db -> do assertException ["Error when running"] $ shakeRunDatabase db [need ["foo.err","bar.err"]] shakeErrorsDatabase db sort (map fst errs) === ["bar.err","foo.err"] -- check the progress thread gets killed properly on normal cleanup ref <- newIORef 0 opts <- return opts{shakeProgress = const $ bracket_ (modifyIORef ref succ) (modifyIORef ref succ) $ sleep 100} (open, close) <- shakeOpenDatabase opts rules db <- open ([12], after) <- shakeRunDatabase db [need ["a.out"] >> liftIO (modifyIORef ref succ) >> return 12] (=== 3) =<< readIORef ref -- success if it all shuts down cleanly -- and on an exception writeIORef ref 0 assertException ["terminate"] $ shakeRunDatabase db [liftIO (modifyIORef ref succ) >> fail "terminate"] (=== 3) =<< readIORef ref -- and on an external exception writeIORef ref 0 bar <- newBarrier; bar2 <- newBarrier t <- flip forkFinally (signalBarrier bar2) $ void $ shakeRunDatabase db $ return $ do liftIO $ modifyIORef ref succ liftIO $ signalBarrier bar () need ["sleep"] waitBarrier bar sleep 0.1 killThread t waitBarrier bar2 (=== 3) =<< readIORef ref