Skip to content

Commit

Permalink
feat: add test runner (#2)
Browse files Browse the repository at this point in the history
* feat: add test runner

* feat: finish test runner & add test isolation checks

* ci: install solc

* chore: rename Greet to Greeter and break into 2 contracts

* feat: add multi-contract runner

* feat: add gas consumption per test

we subtract 21k gas and the associated calldata costs of calling the function

* chore: fix clippy lints

* feat: add dapp cli
  • Loading branch information
gakonst authored Sep 12, 2021
1 parent e3e7f7a commit 1df7f08
Show file tree
Hide file tree
Showing 6 changed files with 635 additions and 1 deletion.
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,17 @@ jobs:
toolchain: stable
override: true

- name: Install Solc
run: |
mkdir -p "$HOME/bin"
wget -q https://github.com/ethereum/solidity/releases/download/v0.8.6/solc-static-linux -O $HOME/bin/solc
chmod u+x "$HOME/bin/solc"
export PATH=$HOME/bin:$PATH
solc --version
- name: cargo test
run: |
export PATH=$HOME/bin:$PATH
cargo test
lint:
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ eyre = "0.6.5"
tokio = { version = "1.10.1", features = ["macros"] }
rustc-hex = "2.1.0"
serde_json = "1.0.67"
evm = "0.30.1"
rpassword = "5.0.1"
evm = "0.30.1"
ansi_term = "0.12.1"

[patch.'crates-io']
ethabi = { git = "https://github.com/gakonst/ethabi/", branch = "patch-1" }
55 changes: 55 additions & 0 deletions GreetTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.6;

contract Greeter {
string public greeting;

function greet(string memory _greeting) public {
greeting = _greeting;
}

function gm() public {
greeting = "gm";
}
}

contract GreeterTestSetup {
Greeter greeter;

function greeting() public view returns (string memory) {
return greeter.greeting();
}

function setUp() public {
greeter = new Greeter();
}
}

contract GreeterTest is GreeterTestSetup {
function greet(string memory greeting) public {
greeter.greet(greeting);
}

// check the positive case
function testGreeting() public {
greeter.greet("yo");
require(keccak256(abi.encodePacked(greeter.greeting())) == keccak256(abi.encodePacked("yo")), "not equal");
}

// check the unhappy case
function testFailGreeting() public {
greeter.greet("yo");
require(keccak256(abi.encodePacked(greeter.greeting())) == keccak256(abi.encodePacked("hi")), "not equal to `hi`");
}

function testIsolation() public {
require(bytes(greeter.greeting()).length == 0);
}
}

contract GmTest is GreeterTestSetup {
function testGm() public {
greeter.gm();
require(keccak256(abi.encodePacked(greeter.greeting())) == keccak256(abi.encodePacked("gm")), "not equal");
}
}
62 changes: 62 additions & 0 deletions src/bin/dapp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use structopt::StructOpt;

use dapptools::dapp::{Executor, MultiContractRunner};
use evm::Config;

use ansi_term::Colour;

#[derive(Debug, StructOpt)]
pub struct Opts {
#[structopt(subcommand)]
pub sub: Subcommands,
}

#[derive(Debug, StructOpt)]
#[structopt(about = "Perform Ethereum RPC calls from the comfort of your command line.")]
pub enum Subcommands {
Test {
#[structopt(
help = "glob path to your smart contracts",
long,
short,
default_value = "./src/**/*.sol"
)]
contracts: String,
// TODO: Add extra configuration options around blockchain context
},
}

fn main() -> eyre::Result<()> {
let opts = Opts::from_args();
match opts.sub {
Subcommands::Test { contracts } => {
let cfg = Config::istanbul();
let gas_limit = 12_500_000;
let env = Executor::new_vicinity();

let runner = MultiContractRunner::new(&contracts, &cfg, gas_limit, env).unwrap();
let results = runner.test().unwrap();

// TODO: Once we add traces in the VM, proceed to print them in a nice and structured
// way
for (contract_name, tests) in results {
if !tests.is_empty() {
println!("Running {} tests for {}", tests.len(), contract_name);
}

for (name, result) in tests {
let status = if result.success {
Colour::Green.paint("[PASS]")
} else {
Colour::Red.paint("[FAIL]")
};
println!("{} {} (gas: {})", status, name, result.gas_used);
}
// skip a line
println!();
}
}
}

Ok(())
}
Loading

0 comments on commit 1df7f08

Please sign in to comment.