Skip to content

Commit

Permalink
Add support for #[should_panic] to the #[googletest::test] macro.
Browse files Browse the repository at this point in the history
Previously, using `#[should_panic]` together with `#[googletest::test]` would result in compilation failure, since the latter always output a test function returning a `Result`, while `#[should_panic]` requires that the test function return `()`.

This modifies `#[googletest::test]` by detecting `#[should_panic]` and, when it is present, modifying the emitted function to not return anything.

When `#[should_panic]` is present, the test also panics if any non-fatal failure was recorded. The test is still allowed to run to completion. Otherwise, non-fatal assertion failures would be silently ignored, which is certainly not intended.

Fixes #328.
  • Loading branch information
hovinen committed Jan 5, 2024
1 parent 147862d commit 0017a85
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 2 deletions.
41 changes: 39 additions & 2 deletions googletest_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,32 @@ use syn::{parse_macro_input, Attribute, ItemFn, ReturnType};
/// }
/// ```
///
/// This macro can be used with `#[should_panic]` to indicate that the test is
/// expected to panic. For example:
///
/// ```ignore
/// #[googletest::test]
/// #[should_panic]
/// fn passes_due_to_should_panic() {
/// let value = 2;
/// expect_that!(value, gt(0));
/// panic!("This panics");
/// }
/// ```
///
/// Using `#[should_panic]` modifies the behaviour of `#[googletest::test]` so
/// that the test panics if any non-fatal assertion occurs. For example, the
/// following test passes:
///
/// ```ignore
/// #[googletest::test]
/// #[should_panic]
/// fn passes_due_to_should_panic_and_failing_assertion() {
/// let value = 2;
/// expect_that!(value, eq(0));
/// }
/// ```
///
/// [`googletest::Result`]: type.Result.html
#[proc_macro_attribute]
pub fn test(
Expand All @@ -49,6 +75,15 @@ pub fn test(
let mut parsed_fn = parse_macro_input!(input as ItemFn);
let attrs = parsed_fn.attrs.drain(..).collect::<Vec<_>>();
let (mut sig, block) = (parsed_fn.sig, parsed_fn.block);
let (outer_return_type, trailer) =
if attrs.iter().any(|attr| attr.path().is_ident("should_panic")) {
(quote! { () }, quote! { .unwrap(); })
} else {
(
quote! { std::result::Result<(), googletest::internal::test_outcome::TestFailure> },
quote! {},
)
};
let output_type = match sig.output.clone() {
ReturnType::Type(_, output_type) => Some(output_type),
ReturnType::Default => None,
Expand Down Expand Up @@ -81,23 +116,25 @@ pub fn test(
let function = if let Some(output_type) = output_type {
quote! {
#(#attrs)*
#sig -> std::result::Result<(), googletest::internal::test_outcome::TestFailure> {
#sig -> #outer_return_type {
#maybe_closure
use googletest::internal::test_outcome::TestOutcome;
TestOutcome::init_current_test_outcome();
let result: #output_type = #invocation;
TestOutcome::close_current_test_outcome(result)
#trailer
}
}
} else {
quote! {
#(#attrs)*
#sig -> std::result::Result<(), googletest::internal::test_outcome::TestFailure> {
#sig -> #outer_return_type {
#maybe_closure
use googletest::internal::test_outcome::TestOutcome;
TestOutcome::init_current_test_outcome();
#invocation;
TestOutcome::close_current_test_outcome(googletest::Result::Ok(()))
#trailer
}
}
};
Expand Down
5 changes: 5 additions & 0 deletions integration_tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ name = "non_fatal_failure_in_subroutine"
path = "src/non_fatal_failure_in_subroutine.rs"
test = false

[[bin]]
name = "passing_test_with_should_panic"
path = "src/passing_test_with_should_panic.rs"
test = false

[[bin]]
name = "simple_assertion_failure"
path = "src/simple_assertion_failure.rs"
Expand Down
25 changes: 25 additions & 0 deletions integration_tests/src/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,31 @@ mod tests {
Ok(())
}

#[googletest::test]
#[should_panic]
fn should_pass_with_should_panic() {
expect_that!(2, eq(4));
}

#[googletest::test]
#[should_panic(expected = "See failure output above")]
fn should_pass_with_should_panic_with_expectation() {
expect_that!(2, eq(4));
}

#[should_panic]
#[googletest::test]
fn should_pass_with_should_panic_in_first_position() {
expect_that!(2, eq(4));
}

#[test]
fn should_fail_when_should_panic_is_present_and_no_panic_occurs() -> Result<()> {
let output = run_external_process_in_tests_directory("passing_test_with_should_panic")?;

verify_that!(output, contains_substring("should panic"))
}

#[::core::prelude::v1::test]
#[should_panic]
fn should_panic_when_expect_that_runs_without_attribute_macro_after_another_test() {
Expand Down
26 changes: 26 additions & 0 deletions integration_tests/src/passing_test_with_should_panic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

fn main() {}

#[cfg(test)]
mod tests {
use googletest::prelude::*;

#[googletest::test]
#[should_panic]
fn passes_but_fails_due_to_should_panic() {
expect_that!(2, eq(2));
}
}
1 change: 1 addition & 0 deletions run_integration_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ INTEGRATION_TEST_BINARIES=(
"first_failure_aborts"
"google_test_with_rstest"
"non_fatal_failure_in_subroutine"
"passing_test_with_should_panic"
"simple_assertion_failure"
"simple_assertion_failure_with_assert_that"
"test_returning_anyhow_error"
Expand Down

0 comments on commit 0017a85

Please sign in to comment.