Skip to content

Commit

Permalink
Fix support for async tests with googletest::test.
Browse files Browse the repository at this point in the history
Previously, an async test annotated with `googletest::test` would not compile because the test content was put in an ordinary closure. Any uses of `.await` inside that closure would generate a compiler error.

This changes the `googletest::test` macro implementation to detect that the test is async and adjust the generation accordingly. If the test is async, then no closure is generated, but the test content is placed in an async block.

Unfortunately, there does not appear to be a single way to cover both the sync and async cases currently, as explained in the code comments.

A further refinement of this approach could possibly address #4.

PiperOrigin-RevId: 524288885
  • Loading branch information
hovinenb authored and copybara-github committed Apr 14, 2023
1 parent 0733d77 commit 0d85933
Show file tree
Hide file tree
Showing 10 changed files with 286 additions and 83 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down
28 changes: 13 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)]
Expand All @@ -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))
Expand Down Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions googletest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
42 changes: 42 additions & 0 deletions googletest/integration_tests/async_test_with_expect_that.rs
Original file line number Diff line number Diff line change
@@ -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(())
}
}
20 changes: 10 additions & 10 deletions googletest/integration_tests/google_test_with_rstest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,49 +14,49 @@

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))
}

#[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<()> {
Expand All @@ -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))
Expand Down
Loading

0 comments on commit 0d85933

Please sign in to comment.