Skip to content

Commit

Permalink
Adapt tests to the new shared state
Browse files Browse the repository at this point in the history
  • Loading branch information
arcz committed Apr 28, 2023
1 parent 1269fc7 commit bc9f2f3
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 59 deletions.
122 changes: 70 additions & 52 deletions src/test/Common.hs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ withSolcVersion (Just f) t = do
Right v' -> if f v' then t else assertBool "skip" True
Left e -> error $ show e

runContract :: FilePath -> Maybe ContractName -> EConfig -> IO [EchidnaTest]
runContract :: FilePath -> Maybe ContractName -> EConfig -> IO (Env, WorkerState)
runContract f selectedContract cfg = do
seed <- maybe (getRandomR (0, maxBound)) pure cfg.campaignConf.seed
(contracts, sourceCaches) <- compileContracts cfg.solConf (f :| [])
Expand All @@ -115,23 +115,27 @@ runContract f selectedContract cfg = do
, testsRef
, chainId = Nothing }
(vm, world, dict) <- prepareContract env contracts (f :| []) selectedContract seed

let corpus = []
-- start ui and run tests
_ <- runReaderT (runWorker (pure Nothing) vm world dict 0 corpus cfg.campaignConf.testLimit) env
readIORef testsRef
finalState <- flip runReaderT env $
runWorker (pure Nothing) vm world dict 0 corpus cfg.campaignConf.testLimit

-- TODO: consider snapshotting the state so checking function don't need to
-- be IO
pure (env, finalState)

testContract
:: FilePath
-> Maybe FilePath
-> [(String, [EchidnaTest] -> Bool)]
-> [(String, (Env, WorkerState) -> IO Bool)]
-> TestTree
testContract fp cfg = testContract' fp Nothing Nothing cfg True

testContractV
:: FilePath
-> Maybe SolcVersionComp
-> Maybe FilePath
-> [(String, [EchidnaTest] -> Bool)]
-> [(String, (Env, WorkerState) -> IO Bool)]
-> TestTree
testContractV fp v cfg = testContract' fp Nothing v cfg True

Expand All @@ -141,7 +145,7 @@ testContract'
-> Maybe SolcVersionComp
-> Maybe FilePath
-> Bool
-> [(String, [EchidnaTest] -> Bool)]
-> [(String, (Env, WorkerState) -> IO Bool)]
-> TestTree
testContract' fp n v configPath s expectations = testCase fp $ withSolcVersion v $ do
c <- case configPath of
Expand All @@ -151,9 +155,9 @@ testContract' fp n v configPath s expectations = testCase fp $ withSolcVersion v
Nothing -> pure testConfig
let c' = c & overrideQuiet
& (if s then overrideLimits else id)
tests <- runContract fp n c'
result <- runContract fp n c'
forM_ expectations $ \(message, assertion) -> do
assertBool message $ assertion tests
assertion result >>= assertBool message

checkConstructorConditions :: FilePath -> String -> TestTree
checkConstructorConditions fp as = testCase fp $ do
Expand Down Expand Up @@ -196,54 +200,68 @@ getResult n tests =
OptimizationTest t _ -> t == n
_ -> False

optnFor :: Text -> [EchidnaTest] -> Maybe TestValue
optnFor n tests = case getResult n tests of
Just t -> Just t.value
_ -> Nothing

optimized :: Text -> Int256 -> [EchidnaTest] -> Bool
optimized n v tests = case optnFor n tests of
Just (IntValue o1) -> o1 >= v
Nothing -> error "nothing"
_ -> error "incompatible values"

solnFor :: Text -> [EchidnaTest] -> Maybe [Tx]
solnFor n tests = case getResult n tests of
Just t -> if null t.reproducer then Nothing else Just t.reproducer
_ -> Nothing

solved :: Text -> [EchidnaTest] -> Bool
solved t = isJust . solnFor t

passed :: Text -> [EchidnaTest] -> Bool
passed n tests = case getResult n tests of
Just t | isPassed t -> True
Just t | isOpen t -> True
Nothing -> error ("no test was found with name: " ++ show n)
_ -> False

solvedLen :: Int -> Text -> [EchidnaTest] -> Bool
solvedLen i t = (== Just i) . fmap length . solnFor t

solvedUsing :: Text -> Text -> [EchidnaTest] -> Bool
solvedUsing f t = maybe False (any $ matchCall . (.call)) . solnFor t
where matchCall (SolCall (f',_)) = f' == f
matchCall _ = False
optnFor :: Text -> (Env, WorkerState) -> IO (Maybe TestValue)
optnFor n (env, _) = do
tests <- readIORef env.testsRef
pure $ case getResult n tests of
Just t -> Just t.value
_ -> Nothing

optimized :: Text -> Int256 -> (Env, WorkerState) -> IO Bool
optimized n v final = do
x <- optnFor n final
pure $ case x of
Just (IntValue o1) -> o1 >= v
Nothing -> error "nothing"
_ -> error "incompatible values"

solnFor :: Text -> (Env, WorkerState) -> IO (Maybe [Tx])
solnFor n (env, _) = do
tests <- readIORef env.testsRef
pure $ case getResult n tests of
Just t -> if null t.reproducer then Nothing else Just t.reproducer
_ -> Nothing

solved :: Text -> (Env, WorkerState) -> IO Bool
solved t f = isJust <$> solnFor t f

passed :: Text -> (Env, WorkerState) -> IO Bool
passed n (env, _) = do
tests <- readIORef env.testsRef
pure $ case getResult n tests of
Just t | isPassed t -> True
Just t | isOpen t -> True
Nothing -> error ("no test was found with name: " ++ show n)
_ -> False

solvedLen :: Int -> Text -> (Env, WorkerState) -> IO Bool
solvedLen i t final = (== Just i) . fmap length <$> solnFor t final

solvedUsing :: Text -> Text -> (Env, WorkerState) -> IO Bool
solvedUsing f t final =
maybe False (any $ matchCall . (.call)) <$> solnFor t final
where matchCall (SolCall (f',_)) = f' == f
matchCall _ = False

-- NOTE: this just verifies a call was found in the solution. Doesn't care about ordering/seq length
solvedWith :: TxCall -> Text -> [EchidnaTest] -> Bool
solvedWith tx t = maybe False (any $ (== tx) . (.call)) . solnFor t
solvedWith :: TxCall -> Text -> (Env, WorkerState) -> IO Bool
solvedWith tx t final =
maybe False (any $ (== tx) . (.call)) <$> solnFor t final

solvedWithout :: TxCall -> Text -> [EchidnaTest] -> Bool
solvedWithout tx t = maybe False (all $ (/= tx) . (.call)) . solnFor t
solvedWithout :: TxCall -> Text -> (Env, WorkerState) -> IO Bool
solvedWithout tx t final =
maybe False (all $ (/= tx) . (.call)) <$> solnFor t final

getGas :: Text -> WorkerState -> Maybe (Gas, [Tx])
getGas t camp = lookup t camp.gasInfo

gasInRange :: Text -> Gas -> Gas -> WorkerState -> Bool
gasInRange t l h c = case getGas t c of
Just (g, _) -> g >= l && g <= h
_ -> False
gasInRange :: Text -> Gas -> Gas -> (Env, WorkerState) -> IO Bool
gasInRange t l h (_, workerState) = do
pure $ case getGas t workerState of
Just (g, _) -> g >= l && g <= h
_ -> False

countCorpus :: Int -> WorkerState -> Bool
countCorpus _n _c = True --length c.corpus == n
countCorpus :: Int -> (Env, WorkerState) -> IO Bool
countCorpus n (env, _) = do
corpus <- readIORef env.corpusRef
pure $ length corpus == n
4 changes: 2 additions & 2 deletions src/test/Tests/Coverage.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Tests.Coverage (coverageTests) where

import Test.Tasty (TestTree, testGroup)

import Common (testContract, passed)
import Common (testContract, passed, countCorpus)

coverageTests :: TestTree
coverageTests = testGroup "Coverage tests"
Expand All @@ -14,6 +14,6 @@ coverageTests = testGroup "Coverage tests"
-- [ ("echidna_state3 failed", solved "echidna_state3") ]
testContract "coverage/boolean.sol" (Just "coverage/boolean.yaml")
[ ("echidna_true failed", passed "echidna_true")
, ("unexpected corpus count ", const True )] -- countCorpus 1)]
, ("unexpected corpus count ", countCorpus 1)]

]
4 changes: 1 addition & 3 deletions src/test/Tests/Integration.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Tests.Integration (integrationTests) where

import Test.Tasty (TestTree, testGroup)

import Common (testContract, testContractV, solcV, testContract', checkConstructorConditions, passed, solved, solvedLen, solvedWith, solvedWithout)
import Common (testContract, testContractV, solcV, testContract', checkConstructorConditions, passed, solved, solvedLen, solvedWith, solvedWithout, gasInRange)
import Data.Functor ((<&>))
import Data.Text (unpack)
import Echidna.Types.Tx (TxCall(..))
Expand Down Expand Up @@ -82,12 +82,10 @@ integrationTests = testGroup "Solidity Integration Testing"
[ ("echidna_mutated passed", solved "echidna_mutated") ]
, testContract "basic/gasuse.sol" (Just "basic/gasuse.yaml")
[ ("echidna_true failed", passed "echidna_true")
{- TODO
, ("g gas estimate wrong", gasInRange "g" 130000 40000000)
, ("f_close1 gas estimate wrong", gasInRange "f_close1" 400 2000)
, ("f_open1 gas estimate wrong", gasInRange "f_open1" 18000 23000)
, ("push_b gas estimate wrong", gasInRange "push_b" 39000 45000)
-}
]
, testContract "basic/gaslimit.sol" Nothing
[ ("echidna_gaslimit passed", passed "echidna_gaslimit") ]
Expand Down
6 changes: 4 additions & 2 deletions src/test/Tests/Seed.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import Test.Tasty.HUnit (testCase, assertBool)

import Common (runContract, overrideQuiet)
import Data.Function ((&))
import Data.IORef (readIORef)
import Echidna.Output.Source (CoverageFileType(..))
import Echidna.Types.Config (EConfig(..))
import Echidna.Types.Config (Env(..), EConfig(..))
import Echidna.Types.Campaign
import Echidna.Mutator.Corpus (defaultMutationConsts)
import Echidna.Config (defaultConfig)
Expand Down Expand Up @@ -36,5 +37,6 @@ seedTests =
}
& overrideQuiet
gen s = do
runContract "basic/flags.sol" Nothing (cfg s)
(env, _) <- runContract "basic/flags.sol" Nothing (cfg s)
readIORef env.testsRef
same s t = (==) <$> gen s <*> gen t

0 comments on commit bc9f2f3

Please sign in to comment.