Skip to content

Commit c0ffeec

Browse files
feat: integ test (hacky)
1 parent c0ffeec commit c0ffeec

File tree

6 files changed

+159
-9
lines changed

6 files changed

+159
-9
lines changed

cache/solidity-files-cache.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"_format":"","paths":{"artifacts":"out","build_infos":"out/build-info","sources":"src","tests":"test","scripts":"script","libraries":["lib"]},"files":{"/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_BitNot/src/Counter.sol":{"lastModificationDate":1743434619281,"contentHash":"409e7881b1f12b7eda886da0aadf38bd","sourceName":"/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_BitNot/src/Counter.sol","imports":[],"versionRequirement":"^0.8.13","artifacts":{"Counter":{"0.8.28":{"default":{"path":"mutation_211_203_UnaryOperator_BitNot/src/Counter.sol/Counter.json","build_id":"be8655b2c799b17ac6647f61ab46ccef"}}}},"seenByCompiler":true},"/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_BitNot/test/CounterTest.t.sol":{"lastModificationDate":1743434619272,"contentHash":"7ca0c40d195ed5b4cdc882be42cba837","sourceName":"/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_BitNot/test/CounterTest.t.sol","imports":["/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_BitNot/src/Counter.sol"],"versionRequirement":"^0.8.13","artifacts":{"CounterTest":{"0.8.28":{"default":{"path":"mutation_211_203_UnaryOperator_BitNot/test/CounterTest.t.sol/CounterTest.json","build_id":"be8655b2c799b17ac6647f61ab46ccef"}}}},"seenByCompiler":true},"/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_PostDec/src/Counter.sol":{"lastModificationDate":1743434619281,"contentHash":"23ec06146f4e636071d9d2f687bb39b8","sourceName":"/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_PostDec/src/Counter.sol","imports":[],"versionRequirement":"^0.8.13","artifacts":{"Counter":{"0.8.28":{"default":{"path":"mutation_211_203_UnaryOperator_PostDec/src/Counter.sol/Counter.json","build_id":"574e4c1c455d00a029d3589da294e5fa"}}}},"seenByCompiler":true},"/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_PostDec/test/CounterTest.t.sol":{"lastModificationDate":1743434619272,"contentHash":"7ca0c40d195ed5b4cdc882be42cba837","sourceName":"/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_PostDec/test/CounterTest.t.sol","imports":["/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_PostDec/src/Counter.sol"],"versionRequirement":"^0.8.13","artifacts":{"CounterTest":{"0.8.28":{"default":{"path":"mutation_211_203_UnaryOperator_PostDec/test/CounterTest.t.sol/CounterTest.json","build_id":"574e4c1c455d00a029d3589da294e5fa"}}}},"seenByCompiler":true},"/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_PreDec/src/Counter.sol":{"lastModificationDate":1743434619281,"contentHash":"fa61ead0fe3f3d3cf9a74043ca8a1cca","sourceName":"/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_PreDec/src/Counter.sol","imports":[],"versionRequirement":"^0.8.13","artifacts":{"Counter":{"0.8.28":{"default":{"path":"src/Counter.sol/Counter.json","build_id":"afce877b209389cf1cf46e71f0c4b713"}}}},"seenByCompiler":true},"/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_PreDec/test/CounterTest.t.sol":{"lastModificationDate":1743434619272,"contentHash":"7ca0c40d195ed5b4cdc882be42cba837","sourceName":"/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_PreDec/test/CounterTest.t.sol","imports":["/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_PreDec/src/Counter.sol"],"versionRequirement":"^0.8.13","artifacts":{"CounterTest":{"0.8.28":{"default":{"path":"test/CounterTest.t.sol/CounterTest.json","build_id":"afce877b209389cf1cf46e71f0c4b713"}}}},"seenByCompiler":true},"/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_PreInc/src/Counter.sol":{"lastModificationDate":1743434619280,"contentHash":"1129c6e517e1881612a094060e2866bd","sourceName":"/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_PreInc/src/Counter.sol","imports":[],"versionRequirement":"^0.8.13","artifacts":{"Counter":{"0.8.28":{"default":{"path":"Counter.sol/Counter.json","build_id":"503faaace5bbb52d3cdc012c25721426"}}}},"seenByCompiler":true},"/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_PreInc/test/CounterTest.t.sol":{"lastModificationDate":1743434619272,"contentHash":"7ca0c40d195ed5b4cdc882be42cba837","sourceName":"/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_PreInc/test/CounterTest.t.sol","imports":["/private/var/folders/zt/hvj1qf5j1fv7vrdb2c8hxr7m0000gn/T/.tmpT6r94g/Counter_sol/mutation_211_203_UnaryOperator_PreInc/src/Counter.sol"],"versionRequirement":"^0.8.13","artifacts":{"CounterTest":{"0.8.28":{"default":{"path":"CounterTest.t.sol/CounterTest.json","build_id":"503faaace5bbb52d3cdc012c25721426"}}}},"seenByCompiler":true}},"builds":["503faaace5bbb52d3cdc012c25721426","574e4c1c455d00a029d3589da294e5fa","afce877b209389cf1cf46e71f0c4b713","be8655b2c799b17ac6647f61ab46ccef"],"profiles":{"default":{"solc":{"optimizer":{"enabled":false,"runs":200},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode.object","evm.bytecode.sourceMap","evm.bytecode.linkReferences","evm.deployedBytecode.object","evm.deployedBytecode.sourceMap","evm.deployedBytecode.linkReferences","evm.deployedBytecode.immutableReferences","evm.methodIdentifiers","metadata"]}},"evmVersion":"cancun","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"cancun","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}}}}

crates/forge/src/mutation/mod.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,6 @@ impl MutationHandler {
190190
new_content.push_str(&replacement);
191191
new_content.push_str(after);
192192

193-
dbg!(mutation);
194-
dbg!(span);
195-
dbg!(replacement);
196-
dbg!(&new_content);
197-
198193
std::fs::write(&target_path, new_content)
199194
.unwrap_or_else(|_| panic!("Failed to write to target file {:?}", &target_path));
200195
}

crates/forge/src/mutation/mutant.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,33 @@ impl Mutant {
164164
)
165165
}
166166
}
167+
168+
#[cfg(test)]
169+
mod tests {
170+
use super::*;
171+
use solar_parse::ast::{BinOpKind, LitKind, Span, UnOpKind};
172+
173+
#[test]
174+
fn test_mutation_type_get_name() {
175+
assert_eq!(MutationType::DeleteExpression.get_name(), "DeleteExpression");
176+
assert_eq!(MutationType::ElimDelegate.get_name(), "ElimDelegate");
177+
assert_eq!(MutationType::FunctionCall.get_name(), "FunctionCall");
178+
assert_eq!(MutationType::Require.get_name(), "Require");
179+
assert_eq!(MutationType::SwapArgumentsFunction.get_name(), "SwapArgumentsFunction");
180+
assert_eq!(MutationType::SwapArgumentsOperator.get_name(), "SwapArgumentsOperator");
181+
182+
assert_eq!(MutationType::BinaryOp(BinOpKind::Add).get_name(), "BinaryOp_Add");
183+
184+
let lit_num = LitKind::Number(123.into());
185+
assert_eq!(
186+
MutationType::Assignment(AssignVarTypes::Literal(lit_num)).get_name(),
187+
"Assignment_number"
188+
);
189+
190+
let ident = AssignVarTypes::Identifier("myVar".to_string());
191+
assert_eq!(MutationType::Assignment(ident).get_name(), "Assignment_myVar");
192+
193+
let unary_mutated = UnaryOpMutated::new("a--".to_string(), UnOpKind::PreInc);
194+
assert_eq!(MutationType::UnaryOperator(unary_mutated).get_name(), "UnaryOperator_PreInc");
195+
}
196+
}

crates/forge/src/mutation/visitor.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ impl Visit<'_> for SolarVisitorWrapper {
2222
type BreakValue = ();
2323
}
2424

25-
2625
/// A visitor which collect all expression to mutate as well as the mutation types
2726
pub struct MutantVisitor {
2827
pub mutation_to_conduct: Vec<Mutant>,
@@ -33,22 +32,26 @@ pub struct MutantVisitor {
3332
impl MutantVisitor {
3433
/// Use all mutator from registry::default
3534
pub fn default() -> Self {
36-
Self { mutation_to_conduct: Vec::new(), mutator_registry: MutatorRegistry::default(), default_visitor: SolarVisitorWrapper {} }
35+
Self {
36+
mutation_to_conduct: Vec::new(),
37+
mutator_registry: MutatorRegistry::default(),
38+
default_visitor: SolarVisitorWrapper {},
39+
}
3740
}
3841

3942
/// Use only a set of mutators
4043
pub fn new_with_mutators(mutators: Vec<Box<dyn Mutator>>) -> Self {
4144
Self {
4245
mutation_to_conduct: Vec::new(),
4346
mutator_registry: MutatorRegistry::new_with_mutators(mutators),
44-
default_visitor: SolarVisitorWrapper {}
47+
default_visitor: SolarVisitorWrapper {},
4548
}
4649
}
4750
}
4851

4952
impl<'ast> Visit<'ast> for MutantVisitor {
5053
type BreakValue = ();
51-
54+
5255
fn visit_variable_definition(
5356
&mut self,
5457
var: &'ast VariableDefinition<'ast>,

crates/forge/tests/it/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod fs;
88
mod fuzz;
99
mod inline;
1010
mod invariant;
11+
mod mutation;
1112
mod repros;
1213
mod spec;
1314
mod vyper;

crates/forge/tests/it/mutation.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
use forge::mutation::MutationHandler;
2+
use forge_script::ScriptArgs;
3+
use foundry_common::shell::{ColorChoice, OutputFormat, OutputMode, Shell, Verbosity};
4+
use foundry_config::Config;
5+
use foundry_evm::opts::EvmOpts;
6+
use std::{path::PathBuf, sync::Arc};
7+
8+
#[tokio::test(flavor = "multi_thread")]
9+
async fn test_mutation_handler_lifecycle() {
10+
let contract = r#"
11+
// SPDX-License-Identifier: UNLICENSED
12+
pragma solidity ^0.8.13;
13+
14+
contract Counter {
15+
uint256 public number;
16+
17+
function increment() public {
18+
number++;
19+
// This should result in 5 mutants: ++number, --number, -number, ~number, number--
20+
// -number should be invalid
21+
// ++number should be alive
22+
// the rest should be dead
23+
}
24+
}"#;
25+
26+
let test = r#"
27+
// SPDX-License-Identifier: UNLICENSED
28+
pragma solidity ^0.8.13;
29+
30+
// Avoid having to manage a libs folder
31+
import {Counter} from "../src/Counter.sol";
32+
33+
contract CounterTest {
34+
Counter public counter;
35+
36+
function setUp() public {
37+
counter = new Counter();
38+
}
39+
40+
function test_Increment() public {
41+
uint256 _countBefore = counter.number();
42+
43+
counter.increment();
44+
45+
assert(counter.number() == _countBefore + 1);
46+
}
47+
}"#;
48+
49+
let temp_dir = tempfile::tempdir().unwrap();
50+
51+
let src_dir = temp_dir.path().join("src");
52+
std::fs::create_dir_all(&src_dir).expect("Failed to create src directory");
53+
54+
let test_dir = temp_dir.path().join("test");
55+
std::fs::create_dir_all(&test_dir).expect("Failed to create test directory");
56+
57+
let cache_dir = temp_dir.path().join("cache");
58+
std::fs::create_dir_all(&cache_dir).expect("Failed to create test directory");
59+
60+
let out_dir = temp_dir.path().join("out");
61+
std::fs::create_dir_all(&out_dir).expect("Failed to create test directory");
62+
63+
std::fs::write(&src_dir.join("Counter.sol"), contract)
64+
.unwrap_or_else(|_| panic!("Failed to write to target file {:?}", &src_dir));
65+
66+
std::fs::write(&test_dir.join("CounterTest.t.sol"), test)
67+
.unwrap_or_else(|_| panic!("Failed to write to target file {:?}", &src_dir));
68+
69+
let mut config = foundry_config::Config::default();
70+
config.cache_path = cache_dir;
71+
config.out = out_dir;
72+
config.src = src_dir.clone();
73+
config.test = test_dir.clone();
74+
75+
let mut mutation_handler = MutationHandler::new(src_dir.join("Counter.sol"), Arc::new(config));
76+
77+
mutation_handler.read_source_contract();
78+
mutation_handler.generate_ast().await;
79+
mutation_handler.create_mutation_folders();
80+
let mutants = mutation_handler.generate_and_compile().await;
81+
82+
// Test if we compile and collect the valid/invalid mutants
83+
assert_eq!(mutants.iter().filter(|(_, output)| output.is_none()).count(), 1);
84+
assert_eq!(mutants.iter().filter(|(_, output)| output.is_some()).count(), 4);
85+
86+
// @todo run the tests
87+
let mut invalids = 0;
88+
let mut alive = 0;
89+
let mut dead = 0;
90+
91+
// Create a new shell to suppress any script output
92+
let shell = Shell::new_with(OutputFormat::Json, OutputMode::Quiet, ColorChoice::Never, 0);
93+
shell.set();
94+
95+
// Run the tests as scripts, for convenience
96+
for mutant in mutants {
97+
if mutant.1.is_some() {
98+
let result = ScriptArgs {
99+
path: mutant.0.path.join("test/CounterTest.t.sol").to_string_lossy().to_string(),
100+
sig: "test_Increment".to_string(),
101+
args: vec![],
102+
..Default::default()
103+
}
104+
.run_script()
105+
.await;
106+
107+
if result.is_err() {
108+
dead += 1;
109+
} else {
110+
alive += 1;
111+
}
112+
} else {
113+
invalids += 1;
114+
}
115+
}
116+
117+
assert_eq!(invalids, 1);
118+
assert_eq!(alive, 1);
119+
assert_eq!(dead, 3);
120+
}

0 commit comments

Comments
 (0)