Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cli/src/cmd/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use ethers::{
Artifact, ProjectCompileOutput,
},
};
use foundry_common::TestFunctionExt;
use foundry_config::Chain as ConfigChain;
use foundry_utils::Retry;
use std::{collections::BTreeMap, path::PathBuf};
Expand Down Expand Up @@ -148,8 +149,7 @@ impl From<RetryArgs> for Retry {
}

pub fn needs_setup(abi: &Abi) -> bool {
let setup_fns: Vec<_> =
abi.functions().filter(|func| func.name.to_lowercase() == "setup").collect();
let setup_fns: Vec<_> = abi.functions().filter(|func| func.name.is_setup()).collect();

for setup_fn in setup_fns.iter() {
if setup_fn.name != "setUp" {
Expand Down
3 changes: 2 additions & 1 deletion cli/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use ethers::{
prelude::Graph,
solc::{report::NoReporter, Artifact, FileFilter, Project, ProjectCompileOutput},
};
use foundry_common::TestFunctionExt;
use std::{
collections::BTreeMap,
fmt::Display,
Expand Down Expand Up @@ -188,7 +189,7 @@ impl ProjectCompiler {
let dev_functions =
contract.abi.as_ref().unwrap().abi.functions().into_iter().filter(
|func| {
func.name.starts_with("test") ||
func.name.is_test() ||
func.name.eq("IS_TEST") ||
func.name.eq("IS_SCRIPT")
},
Expand Down
2 changes: 2 additions & 0 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ pub mod errors;
pub mod evm;
pub mod fmt;
pub mod fs;
pub mod traits;
pub use constants::*;
pub use traits::*;
80 changes: 80 additions & 0 deletions common/src/traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//! Commonly used traits
Comment thread
mattsse marked this conversation as resolved.

use ethers_core::abi::Function;

/// Extension trait for matching tests
pub trait TestFilter: Send + Sync {
/// Returns whether the test should be included
fn matches_test(&self, test_name: impl AsRef<str>) -> bool;
/// Returns whether the contract should be included
fn matches_contract(&self, contract_name: impl AsRef<str>) -> bool;
/// Returns a contract with the given path should be included
fn matches_path(&self, path: impl AsRef<str>) -> bool;
}

/// Extension trait for `Function`
pub trait TestFunctionExt {
/// Whether this function should be executed as fuzz test
fn is_fuzz_test(&self) -> bool;
/// Whether this function is a test
fn is_test(&self) -> bool;
/// Whether this function is a test that should fail
fn is_test_fail(&self) -> bool;
/// Whether this function is a `setUp` function
fn is_setup(&self) -> bool;
}

impl TestFunctionExt for Function {
fn is_fuzz_test(&self) -> bool {
// test functions that have inputs are considered fuzz tests as those inputs will be fuzzed
!self.inputs.is_empty()
}

fn is_test(&self) -> bool {
self.name.is_test()
}

fn is_test_fail(&self) -> bool {
self.name.is_test_fail()
}

fn is_setup(&self) -> bool {
self.name.is_setup()
}
}

impl<'a> TestFunctionExt for &'a str {
fn is_fuzz_test(&self) -> bool {
self.contains("fuzz")
Comment thread
onbjerg marked this conversation as resolved.
Outdated
}

fn is_test(&self) -> bool {
self.starts_with("test")
}

fn is_test_fail(&self) -> bool {
self.starts_with("testFail")
}

fn is_setup(&self) -> bool {
self.to_lowercase() == "setup"
}
}

impl TestFunctionExt for String {
fn is_fuzz_test(&self) -> bool {
self.as_str().is_fuzz_test()
}

fn is_test(&self) -> bool {
self.as_str().is_test()
}

fn is_test_fail(&self) -> bool {
self.as_str().is_test_fail()
}

fn is_setup(&self) -> bool {
self.as_str().is_setup()
}
}
3 changes: 2 additions & 1 deletion evm/src/coverage/analysis.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::{ContractId, CoverageItem, CoverageItemKind, SourceLocation};
use ethers::solc::artifacts::ast::{self, Ast, Node, NodeType};
use foundry_common::TestFunctionExt;
use semver::Version;
use std::collections::HashMap;
use tracing::warn;
Expand Down Expand Up @@ -482,7 +483,7 @@ impl SourceAnalyzer {
)?;
let is_test = items.iter().any(|item| {
if let CoverageItemKind::Function { name } = &item.kind {
name.starts_with("test")
name.is_test()
} else {
false
}
Expand Down
3 changes: 2 additions & 1 deletion forge/src/gas_report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
};
use comfy_table::{modifiers::UTF8_ROUND_CORNERS, presets::UTF8_FULL, *};
use ethers::types::U256;
use foundry_common::TestFunctionExt;
use serde::{Deserialize, Serialize};
use std::{collections::BTreeMap, fmt::Display};

Expand Down Expand Up @@ -70,7 +71,7 @@ impl GasReport {
}
// TODO: More robust test contract filtering
RawOrDecodedCall::Decoded(func, sig, _)
if !func.starts_with("test") && func != "setUp" =>
if !func.is_test() && !func.is_setup() =>
{
let function_report = contract_report
.functions
Expand Down
4 changes: 2 additions & 2 deletions forge/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ pub use runner::ContractRunner;
mod multi_runner;
pub use multi_runner::{MultiContractRunner, MultiContractRunnerBuilder};

mod traits;
pub use traits::*;
/// reexport
pub use foundry_common::traits::TestFilter;

pub mod result;

Expand Down
7 changes: 4 additions & 3 deletions forge/src/multi_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use ethers::{
types::{Address, Bytes, U256},
};
use eyre::Result;
use foundry_common::TestFunctionExt;
use foundry_evm::{
executor::{
backend::Backend, fork::CreateFork, inspector::CheatsConfig, opts::EvmOpts, Executor,
Expand Down Expand Up @@ -74,7 +75,7 @@ impl MultiContractRunner {
filter.matches_contract(&id.name)
})
.flat_map(|(_, (abi, _, _))| abi.functions().map(|func| func.name.clone()))
.filter(|sig| sig.starts_with("test"))
.filter(|sig| sig.is_test())
.collect()
}

Expand All @@ -95,7 +96,7 @@ impl MultiContractRunner {
let name = id.name.clone();
let tests = abi
.functions()
.filter(|func| func.name.starts_with("test"))
.filter(|func| func.name.is_test())
.filter(|func| filter.matches_test(func.signature()))
.map(|func| func.name.clone())
.collect::<Vec<_>>();
Expand Down Expand Up @@ -282,7 +283,7 @@ impl MultiContractRunnerBuilder {
let abi = contract.abi.expect("We should have an abi by now");
// if it's a test, add it to deployable contracts
if abi.constructor.as_ref().map(|c| c.inputs.is_empty()).unwrap_or(true) &&
abi.functions().any(|func| func.name.starts_with("test"))
abi.functions().any(|func| func.name.is_test())
{
deployable_contracts.insert(
id.clone(),
Expand Down
7 changes: 4 additions & 3 deletions forge/src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use ethers::{
types::{Address, Bytes, U256},
};
use eyre::Result;
use foundry_common::TestFunctionExt;
use foundry_evm::{
executor::{CallResult, DeployResult, EvmError, Executor},
fuzz::FuzzedExecutor,
Expand Down Expand Up @@ -182,7 +183,7 @@ impl<'a> ContractRunner<'a> {
let mut warnings = Vec::new();

let setup_fns: Vec<_> =
self.contract.functions().filter(|func| func.name.to_lowercase() == "setup").collect();
self.contract.functions().filter(|func| func.name.is_setup()).collect();

let needs_setup = setup_fns.len() == 1 && setup_fns[0].name == "setUp";

Expand Down Expand Up @@ -247,11 +248,11 @@ impl<'a> ContractRunner<'a> {
.functions()
.into_iter()
.filter(|func| {
func.name.starts_with("test") &&
func.name.is_test() &&
filter.matches_test(func.signature()) &&
(include_fuzz_tests || func.inputs.is_empty())
})
.map(|func| (func, func.name.starts_with("testFail")))
.map(|func| (func, func.name.is_test_fail()))
.collect();

let test_results = tests
Expand Down
39 changes: 0 additions & 39 deletions forge/src/traits.rs

This file was deleted.