shake-0.19.7: src/Test/Oracle.hs
{-# LANGUAGE TypeFamilies, ConstraintKinds, ScopedTypeVariables #-}
{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving #-}
module Test.Oracle(main) where
import Development.Shake
import Development.Shake.Classes
import Development.Shake.FilePath
import General.GetOpt
import Data.List.Extra
import Data.Tuple.Extra
import Test.Type hiding (RandomType)
import qualified Test.Type as T
import Control.Monad
-- These are instances we'll compute over
type instance RuleResult String = String
type instance RuleResult Int = String
type instance RuleResult () = String
type instance RuleResult Bool = Bool -- test results don't have to be a boolean
newtype FileLen = FileLen FilePath
deriving (Eq,Show,NFData,Typeable,Hashable,Binary)
type instance RuleResult FileLen = Int
newtype RandomType = RandomType (BinarySentinel String)
deriving (Eq,Show,NFData,Typeable,Hashable,Binary)
type instance RuleResult RandomType = Int
type instance RuleResult T.RandomType = Int
data Define = Define String String -- this type produces this result
opt = [Option "" ["def"] (ReqArg (Right . uncurry Define . second tail . breakOn "=") "type=value") ""]
main = testBuildArgs test opt $ \args -> do
addOracle $ \(T.RandomType _) -> pure 42
addOracle $ \(RandomType _) -> pure (-42)
"randomtype.txt" %> \out -> do
a <- askOracle $ T.RandomType $ BinarySentinel ()
b <- askOracle $ RandomType $ BinarySentinel ()
writeFile' out $ show (a,b)
addOracle $ \b -> pure $ not b
"true.txt" %> \out -> writeFile' out . show =<< askOracle False
let add :: forall a . (ShakeValue a, RuleResult a ~ String) => String -> a -> Rules ()
add name key = do
name <.> "txt" %> \out -> do
liftIO $ appendFile ".log" "."
writeFile' out =<< askOracle key
forM_ [val | Define nam val <- args, nam == name] $ \val ->
addOracle $ \(_ :: a) -> pure val
add "string" ""
add "unit" ()
add "int" (0 :: Int)
addOracleCache $ \(FileLen file) -> do
liftIO $ appendFile ".log" "#"
length <$> readFile' file
"cache_out.txt" %> \out -> do
o <- askOracle (FileLen "cache_in.txt")
liftIO $ appendFile ".log" "!"
writeFile' out $ show o
test build = do
build ["clean"]
build ["randomtype.txt"]
assertContents "randomtype.txt" "(42,-42)"
-- check it rebuilds when it should
writeFile ".log" ""
build ["--def=string=name","string.txt"]
assertContents "string.txt" "name"
build ["--def=string=name","string.txt"]
assertContents "string.txt" "name"
build ["--def=string=test","string.txt"]
assertContents "string.txt" "test"
assertContents ".log" ".."
-- check adding/removing redundant oracles does not trigger a rebuild
build ["--def=string=test","string.txt","--def=unit=bob"]
build ["--def=string=test","string.txt","--def=int=fred"]
build ["--def=string=test","string.txt"]
assertContents "string.txt" "test"
assertContents ".log" ".."
writeFile ".log" ""
writeFile "cache_in.txt" "aaa"
build ["cache_out.txt","--sleep"]
assertContents "cache_out.txt" "3"
assertContents ".log" "#!"
writeFile "cache_in.txt" "zzz"
build ["cache_out.txt","--sleep"]
build ["cache_out.txt","--sleep"]
assertContents "cache_out.txt" "3"
assertContents ".log" "#!#"
writeFile "cache_in.txt" "zzzz"
build ["cache_out.txt","--sleep"]
assertContents "cache_out.txt" "4"
assertContents ".log" "#!##!"
-- check error messages are good
let errors args err = assertExceptionAfter (replace "\\" "/") err $ build $ "--quiet" : args
build ["--def=unit=test","unit.txt"]
errors ["unit.txt"] -- Building with an an Oracle that has been removed
["missing a call to addOracle"]
errors ["int.txt"] -- Building with an Oracle that I know nothing about
["missing a call to addOracle"]
errors ["--def=string=1","--def=string=1"] -- Two Oracles defined in one go
["oracle defined twice", "Test/Oracle.hs"]