diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4023e2cb..26c26271 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,6 +57,24 @@ jobs: - name: cargo test --locked run: cargo test --locked --all-features + test-no-default-features: + runs-on: ubuntu-latest + name: test (no default features) / ubuntu / ${{ matrix.toolchain }} + strategy: + matrix: + toolchain: [stable] + steps: + - uses: actions/checkout@v3 + - name: Install ${{ matrix.toolchain }} + uses: dtolnay/rust-toolchain@b44cb146d03e8d870c57ab64b80f04586349ca5d + with: + toolchain: ${{ matrix.toolchain }} + - name: cargo generate-lockfile + if: hashFiles('Cargo.lock') == '' + run: cargo generate-lockfile + - name: cargo test --locked + run: cargo test --locked --no-default-features + integration-test: runs-on: ubuntu-latest name: integration-test / ubuntu / ${{ matrix.toolchain }} diff --git a/README.md b/README.md index d9026442..cc942a0f 100644 --- a/README.md +++ b/README.md @@ -182,15 +182,13 @@ failed, but execution continues until the test completes or otherwise aborts. This is analogous to the `EXPECT_*` family of macros in GoogleTest. To make a non-fatal assertion, use the macro [`expect_that!`]. The test must -also be marked with [`google_test`] instead of the Rust-standard `#[test]`. It -must return [`Result<()>`]. +also be marked with [`googletest::test`] instead of the Rust-standard `#[test]`. +It must return [`Result<()>`]. ```rust -use googletest::{ - expect_that, google_test, matchers::eq, Result -}; +use googletest::{expect_that, matchers::eq, Result}; -#[google_test] +#[googletest::test] fn more_than_one_failure() -> Result<()> { let value = 2; expect_that!(value, eq(3)); // Just marks the test as having failed. @@ -201,12 +199,12 @@ fn more_than_one_failure() -> Result<()> { ### Interoperability -You can use the `#[google_test]` macro together with many other libraries such -as [rstest](https://crates.io/crates/rstest). Just apply both attribute macros -to the test: +You can use the `#[googletest::test]` macro together with many other libraries +such as [rstest](https://crates.io/crates/rstest). Just apply both attribute +macros to the test: ```rust -#[google_test] +#[googletest::test] #[rstest] #[case(1)] #[case(2)] @@ -216,16 +214,16 @@ fn rstest_works_with_google_test(#[case] value: u32) -> Result<()> { } ``` -Make sure to put `#[google_test]` *before* `#[rstest]`. Otherwise the annotated -test will run twice, since both macros will attempt to register a test with the -Rust test harness. +Make sure to put `#[googletest::test]` *before* `#[rstest]`. Otherwise the +annotated test will run twice, since both macros will attempt to register a test +with the Rust test harness. The macro also works together with [async tests with Tokio](https://docs.rs/tokio/latest/tokio/attr.test.html) in the same way: ```rust -#[google_test] +#[googletest::test] #[tokio::test] async fn should_work_with_tokio() -> Result<()> { verify_that!(3, gt(0)) @@ -289,7 +287,7 @@ to this project. [`expect_pred!`]: https://docs.rs/googletest/*/googletest/macro.expect_pred.html [`expect_that!`]: https://docs.rs/googletest/*/googletest/macro.expect_that.html [`fail!`]: https://docs.rs/googletest/*/googletest/macro.fail.html -[`google_test`]: https://docs.rs/googletest/*/googletest/attr.google_test.html +[`googletest::test`]: https://docs.rs/googletest/*/googletest/attr.test.html [`matches_pattern!`]: https://docs.rs/googletest/*/googletest/macro.matches_pattern.html [`verify_pred!`]: https://docs.rs/googletest/*/googletest/macro.verify_pred.html [`verify_that!`]: https://docs.rs/googletest/*/googletest/macro.verify_that.html diff --git a/googletest/Cargo.toml b/googletest/Cargo.toml index d699dc89..d810bcb9 100644 --- a/googletest/Cargo.toml +++ b/googletest/Cargo.toml @@ -37,8 +37,10 @@ doctest = false googletest_macro = { path = "../googletest_macro", version = "0.4.2" } num-traits = "0.2.15" regex = "1.6.0" +anyhow = { version = "1.0.70", optional = true } indoc = { version = "2", optional = true } rstest = { version = "0.17.0", optional = true } +tokio = { version = "1", optional = true, features = ["time", "macros", "rt"] } [dev-dependencies] indoc = "2" @@ -59,6 +61,12 @@ name = "assertion_failure_in_subroutine" path = "integration_tests/assertion_failure_in_subroutine.rs" test = false +[[bin]] +name = "async_test_with_expect_that" +path = "integration_tests/async_test_with_expect_that.rs" +test = false +required_features = ["tokio"] + [[bin]] name = "custom_error_message" path = "integration_tests/custom_error_message.rs" @@ -120,6 +128,12 @@ name = "simple_assertion_failure_with_assert_that" path = "integration_tests/simple_assertion_failure_with_assert_that.rs" test = false +[[bin]] +name = "test_returning_anyhow_error" +path = "integration_tests/test_returning_anyhow_error.rs" +test = false +required-features = ["anyhow"] + [[bin]] name = "two_expect_pred_failures" path = "integration_tests/two_expect_pred_failures.rs" diff --git a/googletest/integration_tests/async_test_with_expect_that.rs b/googletest/integration_tests/async_test_with_expect_that.rs new file mode 100644 index 00000000..d5308174 --- /dev/null +++ b/googletest/integration_tests/async_test_with_expect_that.rs @@ -0,0 +1,42 @@ +// Copyright 2023 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() {} + +#[deny(warnings)] +#[cfg(test)] +mod tests { + #[cfg(not(google3))] + use googletest::matchers; + use googletest::{expect_that, verify_that, Result}; + use matchers::eq; + use std::time::Duration; + use tokio::time::sleep; + + #[googletest::test] + #[tokio::test] + async fn async_test_failure_with_non_fatal_assertion() -> Result<()> { + sleep(Duration::from_millis(1)).await; + expect_that!(2, eq(3)); + Ok(()) + } + + #[googletest::test] + #[tokio::test] + async fn async_test_failure_with_fatal_assertion() -> Result<()> { + sleep(Duration::from_millis(1)).await; + verify_that!(3, eq(4))?; + Ok(()) + } +} diff --git a/googletest/integration_tests/google_test_with_rstest.rs b/googletest/integration_tests/google_test_with_rstest.rs index ab77db3c..03c5498d 100644 --- a/googletest/integration_tests/google_test_with_rstest.rs +++ b/googletest/integration_tests/google_test_with_rstest.rs @@ -14,36 +14,36 @@ fn main() {} -// Mixing rstest and google_test should not result in any compiler warnings. The +// Mixing rstest and googletest::test should not result in any compiler warnings. The // fact that this successfully compiles is part of the test. #[deny(warnings)] #[cfg(test)] mod tests { #[cfg(not(google3))] use googletest::matchers; - use googletest::{google_test, verify_that, Result}; + use googletest::{verify_that, Result}; use matchers::eq; use rstest::rstest; #[rstest] - #[google_test] + #[googletest::test] fn test_should_work_with_rstest_first() -> Result<()> { verify_that!(1, eq(1)) } #[rstest::rstest] - #[google_test] + #[googletest::test] fn test_should_work_with_qualified_rstest_first() -> Result<()> { verify_that!(1, eq(1)) } - #[google_test] + #[googletest::test] #[rstest] fn test_should_work_with_rstest_second() -> Result<()> { verify_that!(1, eq(1)) } - #[google_test] + #[googletest::test] #[rstest::rstest] fn test_should_work_with_qualified_rstest_second() -> Result<()> { verify_that!(1, eq(1)) @@ -51,12 +51,12 @@ mod tests { #[rstest] #[case(1)] - #[google_test] + #[googletest::test] fn paramterised_test_should_work_with_rstest_first(#[case] value: u32) -> Result<()> { verify_that!(value, eq(value)) } - #[google_test] + #[googletest::test] #[rstest] #[case(1)] fn paramterised_test_should_work_with_rstest_second(#[case] value: u32) -> Result<()> { @@ -67,13 +67,13 @@ mod tests { pub use rstest::rstest as test; } - #[google_test] + #[googletest::test] #[submodule::test] fn test_should_work_with_qualified_test_annotation() -> Result<()> { verify_that!(1, eq(1)) } - #[google_test] + #[googletest::test] #[test] fn test_should_work_with_second_test_annotation() -> Result<()> { verify_that!(1, eq(1)) diff --git a/googletest/integration_tests/integration_tests.rs b/googletest/integration_tests/integration_tests.rs index 98dbc0be..7f9179d8 100644 --- a/googletest/integration_tests/integration_tests.rs +++ b/googletest/integration_tests/integration_tests.rs @@ -19,8 +19,8 @@ mod tests { #[cfg(not(google3))] use googletest::{all, matchers, matchers::str_matcher}; use googletest::{ - assert_that, expect_pred, expect_that, google_test, verify_pred, verify_that, - GoogleTestSupport, Result, + assert_that, expect_pred, expect_that, test, verify_pred, verify_that, GoogleTestSupport, + Result, }; use indoc::indoc; #[cfg(google3)] @@ -29,48 +29,54 @@ mod tests { use std::process::Command; use str_matcher::StrMatcherConfigurator; - #[google_test] + #[test] fn should_pass() -> Result<()> { let value = 2; verify_that!(value, eq(2)) } - #[google_test] + #[googletest::google_test] + fn should_pass_with_google_test() -> Result<()> { + let value = 2; + verify_that!(value, eq(2)) + } + + #[test] fn should_pass_with_assert_that() -> Result<()> { let value = 2; assert_that!(value, eq(2)); Ok(()) } - #[google_test] + #[test] fn should_pass_with_expect_that() -> Result<()> { let value = 2; expect_that!(value, eq(2)); Ok(()) } - #[google_test] + #[test] fn should_fail_on_assertion_failure() -> Result<()> { let status = run_external_process("simple_assertion_failure").status()?; verify_that!(status.success(), eq(false)) } - #[google_test] + #[test] fn should_fail_on_assertion_failure_with_assert_that() -> Result<()> { let status = run_external_process("simple_assertion_failure_with_assert_that").status()?; verify_that!(status.success(), eq(false)) } - #[google_test] + #[test] fn should_fail_on_assertion_failure_with_expect_that() -> Result<()> { let status = run_external_process("expect_that_failure").status()?; verify_that!(status.success(), eq(false)) } - #[google_test] + #[test] fn should_output_failure_message_on_assertion_failure() -> Result<()> { let output = run_external_process_in_tests_directory("simple_assertion_failure")?; @@ -84,7 +90,7 @@ mod tests { ) } - #[google_test] + #[test] fn should_output_failure_message_on_assertion_failure_with_assert_that() -> Result<()> { let output = run_external_process_in_tests_directory("simple_assertion_failure_with_assert_that")?; @@ -100,7 +106,7 @@ mod tests { ) } - #[google_test] + #[test] fn should_output_failure_message_on_assertion_failure_with_expect_that() -> Result<()> { let output = run_external_process_in_tests_directory("expect_that_failure")?; @@ -115,7 +121,7 @@ mod tests { ) } - #[google_test] + #[test] fn should_output_second_failure_message_on_second_assertion_failure_with_expect_that() -> Result<()> { let output = run_external_process_in_tests_directory("two_expect_that_failures")?; @@ -131,28 +137,28 @@ mod tests { ) } - #[google_test] + #[test] fn should_fail_due_to_assertion_failure_in_subroutine() -> Result<()> { let status = run_external_process("simple_assertion_failure").status()?; verify_that!(status.success(), eq(false)) } - #[google_test] + #[test] fn should_fail_due_to_returned_error_in_subroutine() -> Result<()> { let status = run_external_process("failure_due_to_returned_error").status()?; verify_that!(status.success(), eq(false)) } - #[google_test] + #[test] fn should_fail_test_on_and_log_failure() -> Result<()> { let status = run_external_process("non_fatal_failure_in_subroutine").status()?; verify_that!(status.success(), eq(false)) } - #[google_test] + #[test] fn should_log_test_failures_to_stdout() -> Result<()> { let output = run_external_process_in_tests_directory("two_non_fatal_failures")?; @@ -171,7 +177,7 @@ mod tests { ) } - #[google_test] + #[test] fn should_abort_after_first_failure() -> Result<()> { let output = run_external_process_in_tests_directory("first_failure_aborts")?; @@ -184,7 +190,7 @@ mod tests { ) } - #[google_test] + #[test] fn should_fail_with_assertion_in_a_subroutine() -> Result<()> { let output = run_external_process_in_tests_directory("non_fatal_failure_in_subroutine")?; @@ -197,7 +203,7 @@ mod tests { ) } - #[google_test] + #[test] fn should_include_custom_error_message_in_failure() -> Result<()> { let output = run_external_process_in_tests_directory("custom_error_message")?; @@ -206,19 +212,19 @@ mod tests { verify_that!(output, contains_substring("A custom error message from a closure")) } - #[google_test] + #[test] fn should_not_run_closure_with_custom_error_message_if_test_passes() -> Result<()> { let value = 2; verify_that!(value, eq(2)) .with_failure_message(|| panic!("This should not execute, since the assertion passes.")) } - #[google_test] + #[test] fn should_verify_predicate_with_success() -> Result<()> { verify_pred!(eq_predicate(1, 1)) } - #[google_test] + #[test] fn should_verify_predicate_with_trailing_comma() -> Result<()> { verify_pred!(eq_predicate(1, 1,)) } @@ -227,20 +233,20 @@ mod tests { a == b } - #[google_test] + #[test] fn should_verify_predicate_with_success_using_expect_pred() -> Result<()> { expect_pred!(eq_predicate(1, 1)); Ok(()) } - #[google_test] + #[test] fn verify_pred_should_fail_test_on_failure() -> Result<()> { let status = run_external_process("verify_predicate_with_failure").status()?; verify_that!(status.success(), eq(false)) } - #[google_test] + #[test] fn verify_pred_should_output_correct_failure_message() -> Result<()> { let output = run_external_process_in_tests_directory("verify_predicate_with_failure")?; @@ -254,21 +260,21 @@ mod tests { ) } - #[google_test] + #[test] fn assert_pred_should_fail_test_on_failure() -> Result<()> { let status = run_external_process("assert_predicate_with_failure").status()?; verify_that!(status.success(), eq(false)) } - #[google_test] + #[test] fn expect_pred_should_fail_test_on_failure() -> Result<()> { let status = run_external_process("expect_pred_failure").status()?; verify_that!(status.success(), eq(false)) } - #[google_test] + #[test] fn assert_pred_should_output_correct_failure_message() -> Result<()> { let output = run_external_process_in_tests_directory("assert_predicate_with_failure")?; @@ -282,7 +288,7 @@ mod tests { ) } - #[google_test] + #[test] fn expect_pred_should_output_correct_failure_message() -> Result<()> { let output = run_external_process_in_tests_directory("expect_pred_failure")?; @@ -296,7 +302,7 @@ mod tests { ) } - #[google_test] + #[test] fn expect_pred_should_output_failure_message_for_second_failure() -> Result<()> { let output = run_external_process_in_tests_directory("two_expect_pred_failures")?; @@ -310,7 +316,7 @@ mod tests { ) } - #[google_test] + #[test] fn should_verify_predicate_in_a_submodule() -> Result<()> { verify_pred!(submodule::eq_predicate_in_submodule(1, 1)) } @@ -321,14 +327,14 @@ mod tests { } } - #[google_test] + #[test] fn should_verify_predicate_as_a_method() -> Result<()> { let a_struct = AStruct {}; verify_pred!(a_struct.eq_predicate_as_method(1, 1)) } - #[google_test] + #[test] fn should_verify_predicate_as_a_method_on_an_expresion_result() -> Result<()> { verify_pred!((AStruct {}).eq_predicate_as_method(1, 1)) } @@ -341,7 +347,7 @@ mod tests { } } - #[google_test] + #[test] fn should_verify_predicate_as_a_method_in_submodule() -> Result<()> { verify_pred!(another_submodule::A_STRUCT_IN_SUBMODULE.eq_predicate_as_method(1, 1)) } @@ -350,7 +356,7 @@ mod tests { pub(super) static A_STRUCT_IN_SUBMODULE: super::AStruct = super::AStruct {}; } - #[google_test] + #[test] fn should_verify_predicate_as_a_method_on_a_field() -> Result<()> { let another_struct = AnotherStruct { a_struct: AStruct {} }; @@ -361,7 +367,7 @@ mod tests { a_struct: AStruct, } - #[google_test] + #[test] fn verify_pred_should_show_correct_qualified_function_name_in_test_failure_output() -> Result<()> { let output = run_external_process_in_tests_directory( @@ -378,14 +384,14 @@ mod tests { ) } - #[google_test] + #[test] fn fail_macro_causes_test_failure() -> Result<()> { let status = run_external_process("failure_due_to_fail_macro").status()?; verify_that!(status.success(), eq(false)) } - #[google_test] + #[test] fn fail_macro_outputs_message() -> Result<()> { let output = run_external_process_in_tests_directory("failure_due_to_fail_macro")?; @@ -398,7 +404,7 @@ mod tests { ) } - #[google_test] + #[test] fn fail_macro_allows_empty_message() -> Result<()> { let output = run_external_process_in_tests_directory( "failure_due_to_fail_macro_with_empty_message", @@ -407,7 +413,7 @@ mod tests { verify_that!(output, contains_substring("Test failed")) } - #[google_test] + #[test] fn fail_macro_allows_message_with_format_arguments() -> Result<()> { let output = run_external_process_in_tests_directory( "failure_due_to_fail_macro_with_format_arguments", @@ -416,7 +422,7 @@ mod tests { verify_that!(output, contains_substring("Failure message with argument: An argument")) } - #[google_test] + #[test] fn test_using_normal_test_attribute_macro_formats_failure_message_correctly() -> Result<()> { let result = should_display_error_correctly_without_google_test_macro(); @@ -437,7 +443,7 @@ mod tests { verify_that!(1, eq(2)) } - #[google_test] + #[test] fn failure_message_uses_pretty_print_for_actual_value() -> Result<()> { #[derive(Debug)] #[allow(unused)] @@ -458,14 +464,22 @@ mod tests { ) } - #[google_test] + #[test] fn test_with_google_test_and_rstest_runs_only_once() -> Result<()> { let output = run_external_process_in_tests_directory("google_test_with_rstest")?; + expect_that!( + output, + contains_substring("tests::test_should_work_with_rstest_first").times(eq(1)) + ); expect_that!( output, contains_substring("tests::test_should_work_with_rstest_second").times(eq(1)) ); + expect_that!( + output, + contains_substring("tests::test_should_work_with_qualified_rstest_first").times(eq(1)) + ); expect_that!( output, contains_substring("tests::test_should_work_with_qualified_rstest_second").times(eq(1)) @@ -481,6 +495,27 @@ mod tests { ) } + #[cfg(feature = "tokio")] + #[test] + fn async_test_with_google_test_runs_correctly() -> Result<()> { + let output = run_external_process_in_tests_directory("async_test_with_expect_that")?; + + expect_that!( + output, + contains_substring("tests::async_test_failure_with_non_fatal_assertion ... FAILED") + .times(eq(1)) + ); + verify_that!(output, contains_substring("Expected: is equal to 3")) + } + + #[cfg(feature = "anyhow")] + #[test] + fn test_can_return_anyhow_generated_error() -> Result<()> { + let output = run_external_process_in_tests_directory("test_returning_anyhow_error")?; + + verify_that!(output, contains_substring("Error from Anyhow")) + } + fn run_external_process_in_tests_directory(name: &'static str) -> Result { let mut command = run_external_process(name); let std::process::Output { stdout, .. } = command.output()?; diff --git a/googletest/integration_tests/test_returning_anyhow_error.rs b/googletest/integration_tests/test_returning_anyhow_error.rs new file mode 100644 index 00000000..79742d8b --- /dev/null +++ b/googletest/integration_tests/test_returning_anyhow_error.rs @@ -0,0 +1,30 @@ +// Copyright 2023 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::{IntoTestResult, Result}; + + #[test] + fn should_fail_due_to_error_in_subroutine() -> Result<()> { + returns_anyhow_error().into_test_result()?; + Ok(()) + } + + fn returns_anyhow_error() -> std::result::Result<(), anyhow::Error> { + Err(anyhow::anyhow!("Error from Anyhow")) + } +} diff --git a/googletest/src/lib.rs b/googletest/src/lib.rs index 8f63de5e..271f30a2 100644 --- a/googletest/src/lib.rs +++ b/googletest/src/lib.rs @@ -231,15 +231,13 @@ //! aborts. //! //! To make a non-fatal assertion, use the macro [`expect_that!`]. The test must -//! also be marked with [`google_test`] instead of the Rust-standard `#[test]`. -//! It must return [`Result<()>`]. +//! also be marked with [`googletest::test`][test] instead of the Rust-standard +//! `#[test]`. It must return [`Result<()>`]. //! //! ``` -//! use googletest::{ -//! expect_that, google_test, matchers::eq, Result -//! }; +//! use googletest::{expect_that, matchers::eq, Result}; //! -//! #[google_test] +//! #[googletest::test] //! fn more_than_one_failure() -> Result<()> { //! let value = 2; //! expect_that!(value, eq(3)); // Just marks the test as having failed. @@ -304,7 +302,11 @@ pub mod matcher; #[cfg(not(google3))] pub mod matchers; -pub use googletest_macro::google_test; +pub use googletest_macro::test; + +// For backwards compatibility. +#[deprecated(since = "0.5.0", note = "Use googletest::test instead")] +pub use googletest_macro::test as google_test; use internal::test_outcome::TestAssertionFailure; @@ -416,3 +418,41 @@ impl GoogleTestSupport for std::result::Result { self } } + +/// Provides an extension method for converting an arbitrary type into a +/// [`Result`]. +/// +/// A type can implement this trait to provide an easy way to return immediately +/// from a test in conjunction with the `?` operator. This is useful for +/// [`Result`][std::result::Result] types whose `Result::Err` variant does not +/// implement [`std::error::Error`]. +/// +/// There is an implementation of this trait for [`anyhow::Error`] (which does +/// not implement `std::error::Error`) when the `anyhow` feature is enabled. +/// Importing this trait allows one to easily map [`anyhow::Error`] to a test +/// failure: +/// +/// ``` +/// #[test] +/// fn should_work() -> Result<()> { +/// let value = something_which_can_fail().into_test_result()?; +/// ... +/// } +/// +/// fn something_which_can_fail() -> anyhow::Result<...> { ... } +/// ``` +pub trait IntoTestResult { + /// Converts this instance into a [`Result`]. + /// + /// Typically, the `Self` type is itself a [`std::result::Result`]. This + /// method should then map the `Err` variant to a [`TestAssertionFailure`] + /// and leave the `Ok` variant unchanged. + fn into_test_result(self) -> Result; +} + +#[cfg(feature = "anyhow")] +impl IntoTestResult for std::result::Result { + fn into_test_result(self) -> std::result::Result { + self.map_err(|e| TestAssertionFailure::create(format!("{e}"))) + } +} diff --git a/googletest_macro/src/lib.rs b/googletest_macro/src/lib.rs index 6bab420d..5b170ebb 100644 --- a/googletest_macro/src/lib.rs +++ b/googletest_macro/src/lib.rs @@ -20,7 +20,7 @@ use syn::{parse_macro_input, Attribute, ItemFn, ReturnType}; /// Annotate tests the same way ordinary Rust tests are annotated: /// /// ``` -/// #[google_test] +/// #[googletest::test] /// fn should_work() -> GoogleTestResult { /// ... /// Ok(()) @@ -36,7 +36,7 @@ use syn::{parse_macro_input, Attribute, ItemFn, ReturnType}; /// the rest of the test execution: /// /// ``` -/// #[google_test] +/// #[googletest::test] /// fn should_work() -> GoogleTestResult { /// ... /// assert_that_everything_is_okay()?; @@ -49,7 +49,7 @@ use syn::{parse_macro_input, Attribute, ItemFn, ReturnType}; /// } /// ``` #[proc_macro_attribute] -pub fn google_test( +pub fn test( _args: proc_macro::TokenStream, input: proc_macro::TokenStream, ) -> proc_macro::TokenStream { @@ -59,18 +59,42 @@ pub fn google_test( let ReturnType::Type(_, output_type) = sig.output.clone() else { return quote! { compile_error!( - "Test function with the #[google_test] attribute must return googletest::Result<()>" + "Test function with the #[googletest::test] attribute must return googletest::Result<()>" ); }.into(); }; sig.output = ReturnType::Default; + let (maybe_closure, invocation) = if sig.asyncness.is_some() { + ( + // In the async case, the ? operator returns from the *block* rather than the + // surrounding function. So we just put the test content in an async block. Async + // closures are still unstable (see https://github.com/rust-lang/rust/issues/62290), + // so we can't use the same solution as the sync case below. + quote! {}, + quote! { + async { #block }.await + }, + ) + } else { + ( + // In the sync case, the ? operator returns from the surrounding function. So we must + // create a separate closure from which the ? operator can return in order to capture + // the output. + quote! { + let test = move || #block; + }, + quote! { + test() + }, + ) + }; let function = quote! { #(#attrs)* #sig -> std::result::Result<(), ()> { - let test = move || #block; + #maybe_closure use googletest::internal::test_outcome::TestOutcome; TestOutcome::init_current_test_outcome(); - let result: #output_type = test(); + let result: #output_type = #invocation; TestOutcome::close_current_test_outcome(result) } }; @@ -78,7 +102,7 @@ pub fn google_test( function } else { quote! { - #[test] + #[::core::prelude::v1::test] #function } }; diff --git a/run_integration_tests.sh b/run_integration_tests.sh index 1ff69142..621ab33b 100644 --- a/run_integration_tests.sh +++ b/run_integration_tests.sh @@ -26,6 +26,7 @@ INTEGRATION_TEST_BINARIES=( "integration_tests" "assert_predicate_with_failure" "assertion_failure_in_subroutine" + "async_test_with_expect_that" "custom_error_message" "expect_pred_failure" "expect_that_failure" @@ -38,6 +39,7 @@ INTEGRATION_TEST_BINARIES=( "non_fatal_failure_in_subroutine" "simple_assertion_failure" "simple_assertion_failure_with_assert_that" + "test_returning_anyhow_error" "two_expect_pred_failures" "two_expect_that_failures" "two_non_fatal_failures" @@ -47,6 +49,6 @@ INTEGRATION_TEST_BINARIES=( cargo build for binary in ${INTEGRATION_TEST_BINARIES[@]}; do - cargo rustc -p googletest --bin $binary --features indoc,rstest -- --test + cargo rustc -p googletest --bin $binary --features anyhow,indoc,rstest,tokio -- --test done ./target/debug/integration_tests