pub mod execute;
pub mod setup;
use crate::execute::TestExecutor;
use crate::setup::{
ContractDeploymentSetup, ContractTestSetup, DeploymentSetup, ScriptTestSetup, TestSetup,
};
use forc_pkg::{self as pkg, BuildOpts};
use fuel_abi_types::error_codes::ErrorSignal;
use fuel_tx as tx;
use fuel_vm::checked_transaction::builder::TransactionBuilderExt;
use fuel_vm::{self as vm};
use fuels_core::codec::ABIDecoder;
use fuels_core::types::param_types::ParamType;
use pkg::TestPassCondition;
use pkg::{Built, BuiltPackage};
use rand::{Rng, SeedableRng};
use rayon::prelude::*;
use std::{collections::HashMap, fs, path::PathBuf, sync::Arc};
use sway_core::asm_generation::ProgramABI;
use sway_core::BuildTarget;
use sway_types::Span;
use tx::consensus_parameters::ConsensusParametersV1;
use tx::{ConsensusParameters, ContractParameters, ScriptParameters, TxParameters};
use vm::interpreter::{InterpreterParams, MemoryInstance};
use vm::prelude::SecretKey;
#[derive(Debug)]
pub enum Tested {
Package(Box<TestedPackage>),
Workspace(Vec<TestedPackage>),
}
#[derive(Debug)]
pub struct TestedPackage {
pub built: Box<pkg::BuiltPackage>,
pub tests: Vec<TestResult>,
}
#[derive(Debug)]
pub struct TestDetails {
pub file_path: Arc<PathBuf>,
pub line_number: usize,
}
#[derive(Debug, Clone)]
pub struct TestFilter<'a> {
pub filter_phrase: &'a str,
pub exact_match: bool,
}
#[derive(Debug, Clone)]
pub struct TestResult {
pub name: String,
pub duration: std::time::Duration,
pub span: Span,
pub file_path: Arc<PathBuf>,
pub state: vm::state::ProgramState,
pub condition: pkg::TestPassCondition,
pub logs: Vec<fuel_tx::Receipt>,
pub gas_used: u64,
}
const TEST_METADATA_SEED: u64 = 0x7E57u64;
type ContractDependencyMap = HashMap<pkg::Pinned, Vec<Arc<pkg::BuiltPackage>>>;
pub enum BuiltTests {
Package(PackageTests),
Workspace(Vec<PackageTests>),
}
#[derive(Debug)]
pub enum PackageTests {
Contract(PackageWithDeploymentToTest),
Script(PackageWithDeploymentToTest),
Predicate(Arc<pkg::BuiltPackage>),
Library(Arc<pkg::BuiltPackage>),
}
#[derive(Debug)]
pub struct ContractToTest {
pkg: Arc<pkg::BuiltPackage>,
without_tests_bytecode: pkg::BuiltPackageBytecode,
contract_dependencies: Vec<Arc<pkg::BuiltPackage>>,
}
#[derive(Debug)]
pub struct ScriptToTest {
pkg: Arc<pkg::BuiltPackage>,
contract_dependencies: Vec<Arc<pkg::BuiltPackage>>,
}
#[derive(Debug)]
pub enum PackageWithDeploymentToTest {
Script(ScriptToTest),
Contract(ContractToTest),
}
#[derive(Default, Clone)]
pub struct TestOpts {
pub pkg: pkg::PkgOpts,
pub print: pkg::PrintOpts,
pub minify: pkg::MinifyOpts,
pub binary_outfile: Option<String>,
pub debug_outfile: Option<String>,
pub build_target: BuildTarget,
pub build_profile: String,
pub release: bool,
pub error_on_warnings: bool,
pub time_phases: bool,
pub profile: bool,
pub metrics_outfile: Option<String>,
pub experimental: Vec<sway_features::Feature>,
pub no_experimental: Vec<sway_features::Feature>,
}
#[derive(Default, Clone)]
pub struct TestPrintOpts {
pub pretty_print: bool,
pub print_logs: bool,
}
pub struct DecodedLog {
pub value: String,
}
impl TestedPackage {
pub fn tests_passed(&self) -> bool {
self.tests.iter().all(|test| test.passed())
}
}
impl PackageWithDeploymentToTest {
fn pkg(&self) -> &BuiltPackage {
match self {
PackageWithDeploymentToTest::Script(script) => &script.pkg,
PackageWithDeploymentToTest::Contract(contract) => &contract.pkg,
}
}
fn contract_dependencies(&self) -> impl Iterator<Item = &Arc<BuiltPackage>> + '_ {
match self {
PackageWithDeploymentToTest::Script(script_to_test) => {
script_to_test.contract_dependencies.iter()
}
PackageWithDeploymentToTest::Contract(contract_to_test) => {
contract_to_test.contract_dependencies.iter()
}
}
}
fn deploy(&self) -> anyhow::Result<TestSetup> {
let gas_price = 0;
let params = maxed_consensus_params();
let storage = vm::storage::MemoryStorage::default();
let interpreter_params = InterpreterParams::new(gas_price, params.clone());
let mut interpreter: vm::prelude::Interpreter<_, _, _, vm::interpreter::NotSupportedEcal> =
vm::interpreter::Interpreter::with_storage(
MemoryInstance::new(),
storage,
interpreter_params,
);
let contract_dependency_setups = self
.contract_dependencies()
.map(|built_pkg| deployment_transaction(built_pkg, &built_pkg.bytecode, ¶ms));
let contract_dependency_ids = contract_dependency_setups
.map(|(contract_id, tx)| {
let tx = tx
.into_ready(gas_price, params.gas_costs(), params.fee_params())
.unwrap();
interpreter.transact(tx).map_err(anyhow::Error::msg)?;
Ok(contract_id)
})
.collect::<anyhow::Result<Vec<_>>>()?;
let deployment_setup = if let PackageWithDeploymentToTest::Contract(contract_to_test) = self
{
let (root_contract_id, root_contract_tx) = deployment_transaction(
&contract_to_test.pkg,
&contract_to_test.without_tests_bytecode,
¶ms,
);
let root_contract_tx = root_contract_tx
.into_ready(gas_price, params.gas_costs(), params.fee_params())
.unwrap();
interpreter
.transact(root_contract_tx)
.map_err(anyhow::Error::msg)?;
let storage = interpreter.as_ref().clone();
DeploymentSetup::Contract(ContractTestSetup {
storage,
contract_dependency_ids,
root_contract_id,
})
} else {
let storage = interpreter.as_ref().clone();
DeploymentSetup::Script(ScriptTestSetup {
storage,
contract_dependency_ids,
})
};
Ok(TestSetup::WithDeployment(deployment_setup))
}
}
fn get_contract_dependency_map(
built: &Built,
build_plan: &pkg::BuildPlan,
) -> ContractDependencyMap {
let built_members: HashMap<&pkg::Pinned, Arc<pkg::BuiltPackage>> =
built.into_members().collect();
build_plan
.member_nodes()
.map(|member_node| {
let graph = build_plan.graph();
let pinned_member = graph[member_node].clone();
let contract_dependencies = build_plan
.contract_dependencies(member_node)
.map(|contract_dependency_node_ix| graph[contract_dependency_node_ix].clone())
.filter_map(|pinned| built_members.get(&pinned))
.cloned()
.collect::<Vec<_>>();
(pinned_member, contract_dependencies)
})
.collect()
}
impl BuiltTests {
pub fn from_built(built: Built, build_plan: &pkg::BuildPlan) -> anyhow::Result<BuiltTests> {
let contract_dependencies = get_contract_dependency_map(&built, build_plan);
let built = match built {
Built::Package(built_pkg) => BuiltTests::Package(PackageTests::from_built_pkg(
built_pkg,
&contract_dependencies,
)),
Built::Workspace(built_workspace) => {
let pkg_tests = built_workspace
.into_iter()
.map(|built_pkg| {
PackageTests::from_built_pkg(built_pkg, &contract_dependencies)
})
.collect();
BuiltTests::Workspace(pkg_tests)
}
};
Ok(built)
}
}
impl<'a> PackageTests {
pub(crate) fn built_pkg_with_tests(&'a self) -> &'a BuiltPackage {
match self {
PackageTests::Contract(contract) => contract.pkg(),
PackageTests::Script(script) => script.pkg(),
PackageTests::Predicate(predicate) => predicate,
PackageTests::Library(library) => library,
}
}
fn from_built_pkg(
built_pkg: Arc<BuiltPackage>,
contract_dependencies: &ContractDependencyMap,
) -> PackageTests {
let built_without_tests_bytecode = built_pkg.bytecode_without_tests.clone();
let contract_dependencies: Vec<Arc<pkg::BuiltPackage>> = contract_dependencies
.get(&built_pkg.descriptor.pinned)
.cloned()
.unwrap_or_default();
match built_without_tests_bytecode {
Some(contract_without_tests) => {
let contract_to_test = ContractToTest {
pkg: built_pkg,
without_tests_bytecode: contract_without_tests,
contract_dependencies,
};
PackageTests::Contract(PackageWithDeploymentToTest::Contract(contract_to_test))
}
None => match built_pkg.tree_type {
sway_core::language::parsed::TreeType::Predicate => {
PackageTests::Predicate(built_pkg)
}
sway_core::language::parsed::TreeType::Library => PackageTests::Library(built_pkg),
sway_core::language::parsed::TreeType::Script => {
let script_to_test = ScriptToTest {
pkg: built_pkg,
contract_dependencies,
};
PackageTests::Script(PackageWithDeploymentToTest::Script(script_to_test))
}
_ => unreachable!("contracts are already handled"),
},
}
}
pub(crate) fn run_tests(
&self,
test_runners: &rayon::ThreadPool,
test_filter: Option<&TestFilter>,
) -> anyhow::Result<TestedPackage> {
let pkg_with_tests = self.built_pkg_with_tests();
let tests = test_runners.install(|| {
pkg_with_tests
.bytecode
.entries
.par_iter()
.filter_map(|entry| {
if let Some(test_entry) = entry.kind.test() {
let name = entry.finalized.fn_name.clone();
if let Some(filter) = test_filter {
if !filter.filter(&name) {
return None;
}
}
return Some((entry, test_entry));
}
None
})
.map(|(entry, test_entry)| {
let offset = u32::try_from(entry.finalized.imm)
.expect("test instruction offset out of range");
let name = entry.finalized.fn_name.clone();
let test_setup = self.setup()?;
TestExecutor::build(
&pkg_with_tests.bytecode.bytes,
offset,
test_setup,
test_entry,
name,
)?
.execute()
})
.collect::<anyhow::Result<_>>()
})?;
Ok(TestedPackage {
built: Box::new(pkg_with_tests.clone()),
tests,
})
}
pub fn setup(&self) -> anyhow::Result<TestSetup> {
match self {
PackageTests::Contract(contract_to_test) => {
let test_setup = contract_to_test.deploy()?;
Ok(test_setup)
}
PackageTests::Script(script_to_test) => {
let test_setup = script_to_test.deploy()?;
Ok(test_setup)
}
PackageTests::Predicate(_) | PackageTests::Library(_) => Ok(
TestSetup::WithoutDeployment(vm::storage::MemoryStorage::default()),
),
}
}
}
impl From<TestOpts> for pkg::BuildOpts {
fn from(val: TestOpts) -> Self {
pkg::BuildOpts {
pkg: val.pkg,
print: val.print,
minify: val.minify,
binary_outfile: val.binary_outfile,
debug_outfile: val.debug_outfile,
build_target: val.build_target,
build_profile: val.build_profile,
release: val.release,
error_on_warnings: val.error_on_warnings,
time_phases: val.time_phases,
profile: val.profile,
metrics_outfile: val.metrics_outfile,
tests: true,
member_filter: Default::default(),
experimental: val.experimental,
no_experimental: val.no_experimental,
}
}
}
impl TestOpts {
pub fn into_build_opts(self) -> pkg::BuildOpts {
pkg::BuildOpts {
pkg: self.pkg,
print: self.print,
minify: self.minify,
binary_outfile: self.binary_outfile,
debug_outfile: self.debug_outfile,
build_target: self.build_target,
build_profile: self.build_profile,
release: self.release,
error_on_warnings: self.error_on_warnings,
time_phases: self.time_phases,
profile: self.profile,
metrics_outfile: self.metrics_outfile,
tests: true,
member_filter: Default::default(),
experimental: self.experimental,
no_experimental: self.no_experimental,
}
}
}
impl TestResult {
pub fn passed(&self) -> bool {
match &self.condition {
TestPassCondition::ShouldRevert(revert_code) => match revert_code {
Some(revert_code) => self.state == vm::state::ProgramState::Revert(*revert_code),
None => matches!(self.state, vm::state::ProgramState::Revert(_)),
},
TestPassCondition::ShouldNotRevert => {
!matches!(self.state, vm::state::ProgramState::Revert(_))
}
}
}
pub fn revert_code(&self) -> Option<u64> {
match self.state {
vm::state::ProgramState::Revert(revert_code) => Some(revert_code),
_ => None,
}
}
pub fn error_signal(&self) -> anyhow::Result<ErrorSignal> {
let revert_code = self.revert_code().ok_or_else(|| {
anyhow::anyhow!("there is no revert code to convert to `ErrorSignal`")
})?;
ErrorSignal::try_from_revert_code(revert_code).map_err(|e| anyhow::anyhow!(e))
}
pub fn details(&self) -> anyhow::Result<TestDetails> {
let span_start = self.span.start();
let file_str = fs::read_to_string(&*self.file_path)?;
let line_number = file_str[..span_start]
.chars()
.filter(|&c| c == '\n')
.count();
Ok(TestDetails {
file_path: self.file_path.clone(),
line_number,
})
}
}
pub enum TestRunnerCount {
Manual(usize),
Auto,
}
#[derive(Clone, Debug, Default)]
pub struct TestCount {
pub total: usize,
pub ignored: usize,
}
impl<'a> TestFilter<'a> {
fn filter(&self, fn_name: &str) -> bool {
if self.exact_match {
fn_name == self.filter_phrase
} else {
fn_name.contains(self.filter_phrase)
}
}
}
impl BuiltTests {
pub fn test_count(&self, test_filter: Option<&TestFilter>) -> TestCount {
let pkgs: Vec<&PackageTests> = match self {
BuiltTests::Package(pkg) => vec![pkg],
BuiltTests::Workspace(workspace) => workspace.iter().collect(),
};
pkgs.iter()
.flat_map(|pkg| {
pkg.built_pkg_with_tests()
.bytecode
.entries
.iter()
.filter_map(|entry| entry.kind.test().map(|test| (entry, test)))
})
.fold(TestCount::default(), |acc, (pkg_entry, _)| {
let num_ignored = match &test_filter {
Some(filter) => {
if filter.filter(&pkg_entry.finalized.fn_name) {
acc.ignored
} else {
acc.ignored + 1
}
}
None => acc.ignored,
};
TestCount {
total: acc.total + 1,
ignored: num_ignored,
}
})
}
pub fn run(
self,
test_runner_count: TestRunnerCount,
test_filter: Option<TestFilter>,
) -> anyhow::Result<Tested> {
let test_runners = match test_runner_count {
TestRunnerCount::Manual(runner_count) => rayon::ThreadPoolBuilder::new()
.num_threads(runner_count)
.build(),
TestRunnerCount::Auto => rayon::ThreadPoolBuilder::new().build(),
}?;
run_tests(self, &test_runners, test_filter)
}
}
pub fn build(opts: TestOpts) -> anyhow::Result<BuiltTests> {
let build_opts: BuildOpts = opts.into();
let build_plan = pkg::BuildPlan::from_pkg_opts(&build_opts.pkg)?;
let built = pkg::build_with_options(&build_opts)?;
BuiltTests::from_built(built, &build_plan)
}
pub(crate) fn maxed_consensus_params() -> ConsensusParameters {
let script_params = ScriptParameters::DEFAULT
.with_max_script_length(u64::MAX)
.with_max_script_data_length(u64::MAX);
let tx_params = TxParameters::DEFAULT.with_max_size(u64::MAX);
let contract_params = ContractParameters::DEFAULT
.with_contract_max_size(u64::MAX)
.with_max_storage_slots(u64::MAX);
ConsensusParameters::V1(ConsensusParametersV1 {
script_params,
tx_params,
contract_params,
..Default::default()
})
}
fn deployment_transaction(
built_pkg: &pkg::BuiltPackage,
without_tests_bytecode: &pkg::BuiltPackageBytecode,
params: &tx::ConsensusParameters,
) -> ContractDeploymentSetup {
let mut storage_slots = built_pkg.storage_slots.clone();
storage_slots.sort();
let bytecode = &without_tests_bytecode.bytes;
let contract = tx::Contract::from(bytecode.clone());
let root = contract.root();
let state_root = tx::Contract::initial_state_root(storage_slots.iter());
let salt = tx::Salt::zeroed();
let contract_id = contract.id(&salt, &root, &state_root);
let rng = &mut rand::rngs::StdRng::seed_from_u64(TEST_METADATA_SEED);
let secret_key = SecretKey::random(rng);
let utxo_id = rng.gen();
let amount = 1;
let maturity = 1u32.into();
let asset_id = tx::AssetId::BASE;
let tx_pointer = rng.gen();
let block_height = (u32::MAX >> 1).into();
let tx = tx::TransactionBuilder::create(bytecode.as_slice().into(), salt, storage_slots)
.with_params(params.clone())
.add_unsigned_coin_input(secret_key, utxo_id, amount, asset_id, tx_pointer)
.add_output(tx::Output::contract_created(contract_id, state_root))
.maturity(maturity)
.finalize_checked(block_height);
(contract_id, tx)
}
pub fn decode_log_data(
log_id: &str,
log_data: &[u8],
program_abi: &ProgramABI,
) -> anyhow::Result<DecodedLog> {
let program_abi = match program_abi {
ProgramABI::Fuel(fuel_abi) => Some(
fuel_abi_types::abi::unified_program::UnifiedProgramABI::from_counterpart(fuel_abi)?,
),
_ => None,
}
.ok_or_else(|| anyhow::anyhow!("only fuelvm is supported for log decoding"))?;
let type_lookup = program_abi
.types
.iter()
.map(|decl| (decl.type_id, decl.clone()))
.collect::<HashMap<_, _>>();
let logged_type_lookup: HashMap<_, _> = program_abi
.logged_types
.iter()
.flatten()
.map(|logged_type| (logged_type.log_id.as_str(), logged_type.application.clone()))
.collect();
let type_application = logged_type_lookup
.get(&log_id)
.ok_or_else(|| anyhow::anyhow!("log id is missing"))?;
let abi_decoder = ABIDecoder::default();
let param_type = ParamType::try_from_type_application(type_application, &type_lookup)?;
let decoded_str = abi_decoder.decode_as_debug_str(¶m_type, log_data)?;
let decoded_log = DecodedLog { value: decoded_str };
Ok(decoded_log)
}
fn run_tests(
built: BuiltTests,
test_runners: &rayon::ThreadPool,
test_filter: Option<TestFilter>,
) -> anyhow::Result<Tested> {
match built {
BuiltTests::Package(pkg) => {
let tested_pkg = pkg.run_tests(test_runners, test_filter.as_ref())?;
Ok(Tested::Package(Box::new(tested_pkg)))
}
BuiltTests::Workspace(workspace) => {
let tested_pkgs = workspace
.into_iter()
.map(|pkg| pkg.run_tests(test_runners, test_filter.as_ref()))
.collect::<anyhow::Result<Vec<TestedPackage>>>()?;
Ok(Tested::Workspace(tested_pkgs))
}
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use crate::{build, BuiltTests, TestFilter, TestOpts, TestResult};
const TEST_DATA_FOLDER_NAME: &str = "test_data";
const TEST_LIBRARY_PACKAGE_NAME: &str = "test_library";
const TEST_CONTRACT_PACKAGE_NAME: &str = "test_contract";
const TEST_PREDICATE_PACKAGE_NAME: &str = "test_predicate";
const TEST_SCRIPT_PACKAGE_NAME: &str = "test_script";
fn test_package_built_tests(package_name: &str) -> anyhow::Result<BuiltTests> {
let cargo_manifest_dir = env!("CARGO_MANIFEST_DIR");
let library_package_dir = PathBuf::from(cargo_manifest_dir)
.join(TEST_DATA_FOLDER_NAME)
.join(package_name);
let library_package_dir_string = library_package_dir.to_string_lossy().to_string();
let build_options = TestOpts {
pkg: forc_pkg::PkgOpts {
path: Some(library_package_dir_string),
..Default::default()
},
..Default::default()
};
build(build_options)
}
fn test_package_test_results(
package_name: &str,
test_filter: Option<TestFilter>,
) -> anyhow::Result<Vec<TestResult>> {
let built_tests = test_package_built_tests(package_name)?;
let test_runner_count = crate::TestRunnerCount::Auto;
let tested = built_tests.run(test_runner_count, test_filter)?;
match tested {
crate::Tested::Package(tested_pkg) => Ok(tested_pkg.tests),
crate::Tested::Workspace(_) => {
unreachable!("test_library is a package, not a workspace.")
}
}
}
#[test]
fn test_filter_exact_match() {
let filter_phrase = "test_bam";
let test_filter = TestFilter {
filter_phrase,
exact_match: true,
};
let test_library_results =
test_package_test_results(TEST_LIBRARY_PACKAGE_NAME, Some(test_filter.clone()))
.unwrap();
let tested_library_test_count = test_library_results.len();
let test_contract_results =
test_package_test_results(TEST_CONTRACT_PACKAGE_NAME, Some(test_filter.clone()))
.unwrap();
let tested_contract_test_count = test_contract_results.len();
let test_predicate_results =
test_package_test_results(TEST_PREDICATE_PACKAGE_NAME, Some(test_filter.clone()))
.unwrap();
let tested_predicate_test_count = test_predicate_results.len();
let test_script_results =
test_package_test_results(TEST_SCRIPT_PACKAGE_NAME, Some(test_filter)).unwrap();
let tested_script_test_count = test_script_results.len();
assert_eq!(tested_library_test_count, 1);
assert_eq!(tested_contract_test_count, 1);
assert_eq!(tested_predicate_test_count, 1);
assert_eq!(tested_script_test_count, 1);
}
#[test]
fn test_filter_exact_match_all_ignored() {
let filter_phrase = "test_ba";
let test_filter = TestFilter {
filter_phrase,
exact_match: true,
};
let test_library_results =
test_package_test_results(TEST_LIBRARY_PACKAGE_NAME, Some(test_filter.clone()))
.unwrap();
let tested_library_test_count = test_library_results.len();
let test_contract_results =
test_package_test_results(TEST_CONTRACT_PACKAGE_NAME, Some(test_filter.clone()))
.unwrap();
let tested_contract_test_count = test_contract_results.len();
let test_predicate_results =
test_package_test_results(TEST_PREDICATE_PACKAGE_NAME, Some(test_filter.clone()))
.unwrap();
let tested_predicate_test_count = test_predicate_results.len();
let test_script_results =
test_package_test_results(TEST_SCRIPT_PACKAGE_NAME, Some(test_filter)).unwrap();
let tested_script_test_count = test_script_results.len();
assert_eq!(tested_library_test_count, 0);
assert_eq!(tested_contract_test_count, 0);
assert_eq!(tested_predicate_test_count, 0);
assert_eq!(tested_script_test_count, 0);
}
#[test]
fn test_filter_match_all_ignored() {
let filter_phrase = "this_test_does_not_exists";
let test_filter = TestFilter {
filter_phrase,
exact_match: false,
};
let test_library_results =
test_package_test_results(TEST_LIBRARY_PACKAGE_NAME, Some(test_filter.clone()))
.unwrap();
let tested_library_test_count = test_library_results.len();
let test_contract_results =
test_package_test_results(TEST_CONTRACT_PACKAGE_NAME, Some(test_filter.clone()))
.unwrap();
let tested_contract_test_count = test_contract_results.len();
let test_predicate_results =
test_package_test_results(TEST_PREDICATE_PACKAGE_NAME, Some(test_filter.clone()))
.unwrap();
let tested_predicate_test_count = test_predicate_results.len();
let test_script_results =
test_package_test_results(TEST_SCRIPT_PACKAGE_NAME, Some(test_filter)).unwrap();
let tested_script_test_count = test_script_results.len();
assert_eq!(tested_library_test_count, 0);
assert_eq!(tested_contract_test_count, 0);
assert_eq!(tested_predicate_test_count, 0);
assert_eq!(tested_script_test_count, 0);
}
#[test]
fn test_filter_one_match() {
let filter_phrase = "test_ba";
let test_filter = TestFilter {
filter_phrase,
exact_match: false,
};
let test_library_results =
test_package_test_results(TEST_LIBRARY_PACKAGE_NAME, Some(test_filter.clone()))
.unwrap();
let tested_library_test_count = test_library_results.len();
let test_contract_results =
test_package_test_results(TEST_CONTRACT_PACKAGE_NAME, Some(test_filter.clone()))
.unwrap();
let tested_contract_test_count = test_contract_results.len();
let test_predicate_results =
test_package_test_results(TEST_PREDICATE_PACKAGE_NAME, Some(test_filter.clone()))
.unwrap();
let tested_predicate_test_count = test_predicate_results.len();
let test_script_results =
test_package_test_results(TEST_SCRIPT_PACKAGE_NAME, Some(test_filter)).unwrap();
let tested_script_test_count = test_script_results.len();
assert_eq!(tested_library_test_count, 1);
assert_eq!(tested_contract_test_count, 1);
assert_eq!(tested_predicate_test_count, 1);
assert_eq!(tested_script_test_count, 1);
}
#[test]
fn test_filter_all_match() {
let filter_phrase = "est_b";
let test_filter = TestFilter {
filter_phrase,
exact_match: false,
};
let test_library_results =
test_package_test_results(TEST_LIBRARY_PACKAGE_NAME, Some(test_filter.clone()))
.unwrap();
let tested_library_test_count = test_library_results.len();
let test_contract_results =
test_package_test_results(TEST_CONTRACT_PACKAGE_NAME, Some(test_filter.clone()))
.unwrap();
let tested_contract_test_count = test_contract_results.len();
let test_predicate_results =
test_package_test_results(TEST_PREDICATE_PACKAGE_NAME, Some(test_filter.clone()))
.unwrap();
let tested_predicate_test_count = test_predicate_results.len();
let test_script_results =
test_package_test_results(TEST_SCRIPT_PACKAGE_NAME, Some(test_filter)).unwrap();
let tested_script_test_count = test_script_results.len();
assert_eq!(tested_library_test_count, 2);
assert_eq!(tested_contract_test_count, 2);
assert_eq!(tested_predicate_test_count, 2);
assert_eq!(tested_script_test_count, 2);
}
#[test]
fn test_no_filter() {
let test_filter = None;
let test_library_results =
test_package_test_results(TEST_LIBRARY_PACKAGE_NAME, test_filter.clone()).unwrap();
let tested_library_test_count = test_library_results.len();
let test_contract_results =
test_package_test_results(TEST_CONTRACT_PACKAGE_NAME, test_filter.clone()).unwrap();
let tested_contract_test_count = test_contract_results.len();
let test_predicate_results =
test_package_test_results(TEST_PREDICATE_PACKAGE_NAME, test_filter.clone()).unwrap();
let tested_predicate_test_count = test_predicate_results.len();
let test_script_results =
test_package_test_results(TEST_SCRIPT_PACKAGE_NAME, test_filter).unwrap();
let tested_script_test_count = test_script_results.len();
assert_eq!(tested_library_test_count, 2);
assert_eq!(tested_contract_test_count, 2);
assert_eq!(tested_predicate_test_count, 2);
assert_eq!(tested_script_test_count, 2);
}
}