hevm-0.51.0: test/EVM/Test/Utils.hs
{-# Language QuasiQuotes #-}
module EVM.Test.Utils where
import Data.String.Here
import Data.Text
import GHC.IO.Handle (hClose)
import qualified Paths_hevm as Paths
import System.Directory
import System.IO.Temp
import System.Process
import System.Exit
import qualified Data.Text as T
import qualified Data.Text.IO as T
import EVM.Solidity
import EVM.Solvers
import EVM.Dapp
import EVM.UnitTest
import EVM.Fetch (RpcInfo)
import qualified EVM.TTY as TTY
runSolidityTestCustom :: FilePath -> Text -> Maybe Integer -> Bool -> RpcInfo -> ProjectType -> IO Bool
runSolidityTestCustom testFile match maxIter ffiAllowed rpcinfo projectType = do
withSystemTempDirectory "dapp-test" $ \root -> do
compile projectType root testFile >>= \case
Left e -> error e
Right bo@(BuildOutput contracts _) -> do
withSolvers Z3 1 Nothing $ \solvers -> do
opts <- testOpts solvers root (Just bo) match maxIter ffiAllowed rpcinfo
unitTest opts contracts Nothing
runSolidityTest :: FilePath -> Text -> IO Bool
runSolidityTest testFile match = runSolidityTestCustom testFile match Nothing True Nothing Foundry
debugSolidityTest :: FilePath -> RpcInfo -> IO ()
debugSolidityTest testFile rpcinfo = do
withSystemTempDirectory "dapp-test" $ \root -> do
compile DappTools root testFile >>= \case
Left e -> error e
Right bo -> do
withSolvers Z3 1 Nothing $ \solvers -> do
opts <- testOpts solvers root (Just bo) ".*" Nothing True rpcinfo
TTY.main opts root (Just bo)
testOpts :: SolverGroup -> FilePath -> Maybe BuildOutput -> Text -> Maybe Integer -> Bool -> RpcInfo -> IO UnitTestOptions
testOpts solvers root buildOutput match maxIter allowFFI rpcinfo = do
let srcInfo = maybe emptyDapp (dappInfo root) buildOutput
params <- getParametersFromEnvironmentVariables Nothing
pure UnitTestOptions
{ solvers = solvers
, rpcInfo = rpcinfo
, maxIter = maxIter
, askSmtIters = 1
, smtDebug = False
, smtTimeout = Nothing
, solver = Nothing
, covMatch = Nothing
, verbose = Just 1
, match = match
, maxDepth = Nothing
, fuzzRuns = 100
, replay = Nothing
, vmModifier = id
, testParams = params
, dapp = srcInfo
, ffiAllowed = allowFFI
}
compile :: ProjectType -> FilePath -> FilePath -> IO (Either String BuildOutput)
compile DappTools root src = do
json <- compileWithDSTest src
createDirectory (root <> "/out")
T.writeFile (root <> "/out/dapp.sol.json") json
readBuildOutput root DappTools
compile Foundry root src = do
createDirectory (root <> "/src")
writeFile (root <> "/src/unit-tests.t.sol") =<< readFile =<< Paths.getDataFileName src
initLib (root <> "/lib/ds-test") "test/contracts/lib/test.sol" "test.sol"
initLib (root <> "/lib/tokens") "test/contracts/lib/erc20.sol" "erc20.sol"
r@(res,_,_) <- readProcessWithExitCode "forge" ["build", "--root", root] ""
case res of
ExitFailure _ -> pure . Left $ "compilation failed: " <> show r
ExitSuccess -> readBuildOutput root Foundry
where
initLib :: FilePath -> FilePath -> FilePath -> IO ()
initLib tld srcFile dstFile = do
createDirectoryIfMissing True (tld <> "/src")
writeFile (tld <> "/src/" <> dstFile) =<< readFile =<< Paths.getDataFileName srcFile
_ <- readProcessWithExitCode "git" ["init", tld] ""
callProcess "git" ["config", "--file", tld <> "/.git/config", "user.name", "'hevm'"]
callProcess "git" ["config", "--file", tld <> "/.git/config", "user.email", "'hevm@hevm.dev'"]
callProcess "git" ["--git-dir", tld <> "/.git", "--work-tree", tld, "add", tld]
_ <- readProcessWithExitCode "git" ["--git-dir", tld <> "/.git", "--work-tree", tld, "--no-gpg-sign", "commit", "-m"] ""
pure ()
-- We don't want to depend on dapptools here, so we cheat and just call solc with the same options that dapp itself uses
compileWithDSTest :: FilePath -> IO Text
compileWithDSTest src =
withSystemTempFile "input.json" $ \file handle -> do
hClose handle
dsTest <- readFile =<< Paths.getDataFileName "test/contracts/lib/test.sol"
erc20 <- readFile =<< Paths.getDataFileName "test/contracts/lib/erc20.sol"
testFilePath <- Paths.getDataFileName src
testFile <- readFile testFilePath
T.writeFile file
[i|
{
"language": "Solidity",
"sources": {
"ds-test/test.sol": {
"content": ${dsTest}
},
"tokens/erc20.sol": {
"content": ${erc20}
},
"unit-tests.sol": {
"content": ${testFile}
}
},
"settings": {
"metadata": {
"useLiteralContent": true
},
"outputSelection": {
"*": {
"*": [
"metadata",
"evm.bytecode",
"evm.deployedBytecode",
"abi",
"storageLayout",
"evm.bytecode.sourceMap",
"evm.bytecode.linkReferences",
"evm.bytecode.generatedSources",
"evm.deployedBytecode.sourceMap",
"evm.deployedBytecode.linkReferences",
"evm.deployedBytecode.generatedSources"
],
"": [
"ast"
]
}
}
}
}
|]
x <- T.pack <$>
readProcess
"solc"
["--allow-paths", file, "--standard-json", file]
""
return x