diff --git a/docs/book/src/testing/unit-testing.md b/docs/book/src/testing/unit-testing.md index ecbe7ed02ba..c94930e084c 100644 --- a/docs/book/src/testing/unit-testing.md +++ b/docs/book/src/testing/unit-testing.md @@ -61,6 +61,15 @@ fn test_meaning_of_life() { } ``` +It is also possible to specify an expected revert code, like the following example. + +```sway +#[test(should_revert = "18446744073709486084")] +fn test_meaning_of_life() { + assert(6 * 6 == 42); +} +``` + Tests with `#[test(should_revert)]` considered to be passing if they are reverting. ## Calling Contracts diff --git a/forc-pkg/src/pkg.rs b/forc-pkg/src/pkg.rs index 94e7b1e903e..67a767328e0 100644 --- a/forc-pkg/src/pkg.rs +++ b/forc-pkg/src/pkg.rs @@ -148,7 +148,7 @@ pub enum PkgEntryKind { /// The possible conditions for a test result to be considered "passing". #[derive(Debug, Clone)] pub enum TestPassCondition { - ShouldRevert, + ShouldRevert(Option), ShouldNotRevert, } @@ -1949,18 +1949,35 @@ impl PkgTestEntry { let span = decl_ref.span(); let test_function_decl = decl_engine.get_function(&decl_ref); - let test_args: HashSet = test_function_decl + const FAILING_TEST_KEYWORD: &str = "should_revert"; + + let test_args: HashMap> = test_function_decl .attributes .get(&AttributeKind::Test) .expect("test declaration is missing test attribute") .iter() - .flat_map(|attr| attr.args.iter().map(|arg| arg.name.to_string())) + .flat_map(|attr| attr.args.iter()) + .map(|arg| { + ( + arg.name.to_string(), + arg.value + .as_ref() + .map(|val| val.span().as_str().to_string()), + ) + }) .collect(); let pass_condition = if test_args.is_empty() { anyhow::Ok(TestPassCondition::ShouldNotRevert) - } else if test_args.get("should_revert").is_some() { - anyhow::Ok(TestPassCondition::ShouldRevert) + } else if let Some(args) = test_args.get(FAILING_TEST_KEYWORD) { + let expected_revert_code = args + .as_ref() + .map(|arg| { + let arg_str = arg.replace('"', ""); + arg_str.parse::() + }) + .transpose()?; + anyhow::Ok(TestPassCondition::ShouldRevert(expected_revert_code)) } else { let test_name = &test_function_decl.name; bail!("Invalid test argument(s) for test: {test_name}.") diff --git a/forc-test/src/lib.rs b/forc-test/src/lib.rs index 9b3eebda93c..21d46f8df73 100644 --- a/forc-test/src/lib.rs +++ b/forc-test/src/lib.rs @@ -491,9 +491,10 @@ impl TestResult { /// Whether or not the test passed. pub fn passed(&self) -> bool { match &self.condition { - TestPassCondition::ShouldRevert => { - matches!(self.state, vm::state::ProgramState::Revert(_)) - } + 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(_)) } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/unit_tests/should_revert/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_pass/unit_tests/should_revert/src/lib.sw index 2c0b59959a0..280e62a02fb 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/unit_tests/should_revert/src/lib.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/unit_tests/should_revert/src/lib.sw @@ -4,3 +4,8 @@ library; fn should_revert_test() { assert(0 == 1) } + +#[test(should_revert = "18446744073709486084")] +fn should_revert_test_with_exact_code() { + assert(0 == 1) +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/unit_tests/should_revert/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/unit_tests/should_revert/test.toml new file mode 100644 index 00000000000..0f3f6d7e866 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/unit_tests/should_revert/test.toml @@ -0,0 +1 @@ +category = "unit_tests_pass"