Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
name: test / ubuntu / ${{ matrix.toolchain }}
strategy:
matrix:
toolchain: [stable, 1.59.0, nightly, beta]
toolchain: [stable, nightly, beta]
steps:
- uses: actions/checkout@v3
- name: Install ${{ matrix.toolchain }}
Expand All @@ -62,7 +62,7 @@ jobs:
name: test (no default features) / ubuntu / ${{ matrix.toolchain }}
strategy:
matrix:
toolchain: [stable]
toolchain: [stable, 1.59]
steps:
- uses: actions/checkout@v3
- name: Install ${{ matrix.toolchain }}
Expand Down
3 changes: 2 additions & 1 deletion googletest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ authors = [

[dependencies]
googletest_macro = { path = "../googletest_macro", version = "0.8.1" }
ansi_term = "0.12.0"
anyhow = { version = "1", optional = true }
num-traits = "0.2.15"
regex = "1.6.0"
ansi_term = "0.12.0"
proptest = { version = "1.2.0", optional = true }

[dev-dependencies]
indoc = "2"
Expand Down
57 changes: 57 additions & 0 deletions googletest/crate_docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -360,5 +360,62 @@ fn always_fails() -> Result<()> {
# always_fails().unwrap_err();
```

## Integrations with other crates

GoogleTest Rust includes integrations with the
[Anyhow](https://crates.io/crates/anyhow) and
[Proptest](https://crates.io/crates/proptest) crates to simplify turning
errors from those crates into test failures.

To use this, activate the `anyhow`, respectively `proptest` feature in
GoogleTest Rust and invoke the extension method [`into_test_result()`] on a
`Result` value in your test. For example:

```
# use googletest::prelude::*;
# #[cfg(feature = "anyhow")]
# use anyhow::anyhow;
# #[cfg(feature = "anyhow")]
# /* The attribute macro would prevent the function from being compiled in a doctest.
#[test]
# */
fn has_anyhow_failure() -> Result<()> {
Ok(just_return_error().into_test_result()?)
}

# #[cfg(feature = "anyhow")]
fn just_return_error() -> anyhow::Result<()> {
anyhow::Result::Err(anyhow!("This is an error"))
}
# #[cfg(feature = "anyhow")]
# has_anyhow_failure().unwrap_err();
```

One can convert Proptest test failures into GoogleTest test failures when the
test is invoked with
[`TestRunner::run`](https://docs.rs/proptest/latest/proptest/test_runner/struct.TestRunner.html#method.run):

```
# use googletest::prelude::*;
# #[cfg(feature = "proptest")]
# use proptest::test_runner::{Config, TestRunner};
# #[cfg(feature = "proptest")]
# /* The attribute macro would prevent the function from being compiled in a doctest.
#[test]
# */
fn numbers_are_greater_than_zero() -> Result<()> {
let mut runner = TestRunner::new(Config::default());
runner.run(&(1..100i32), |v| Ok(verify_that!(v, gt(0))?)).into_test_result()
}
# #[cfg(feature = "proptest")]
# numbers_are_greater_than_zero().unwrap();
```

Similarly, when the `proptest` feature is enabled, GoogleTest assertion failures
can automatically be converted into Proptest
[`TestCaseError`](https://docs.rs/proptest/latest/proptest/test_runner/enum.TestCaseError.html)
through the `?` operator as the example above shows.

[`and_log_failure()`]: GoogleTestSupport::and_log_failure
[`into_test_result()`]: IntoTestResult::into_test_result
[`Matcher`]: matcher::Matcher
7 changes: 7 additions & 0 deletions googletest/src/internal/test_outcome.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,10 @@ impl<T: std::error::Error> From<T> for TestAssertionFailure {
TestAssertionFailure::create(format!("{value}"))
}
}

#[cfg(feature = "proptest")]
impl From<TestAssertionFailure> for proptest::test_runner::TestCaseError {
fn from(value: TestAssertionFailure) -> Self {
proptest::test_runner::TestCaseError::Fail(format!("{value}").into())
}
}
9 changes: 9 additions & 0 deletions googletest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,12 @@ impl<T> IntoTestResult<T> for std::result::Result<T, anyhow::Error> {
self.map_err(|e| TestAssertionFailure::create(format!("{e}")))
}
}

#[cfg(feature = "proptest")]
impl<OkT, CaseT: std::fmt::Debug> IntoTestResult<OkT>
for std::result::Result<OkT, proptest::test_runner::TestError<CaseT>>
{
fn into_test_result(self) -> std::result::Result<OkT, TestAssertionFailure> {
self.map_err(|e| TestAssertionFailure::create(format!("{e}")))
}
}
2 changes: 2 additions & 0 deletions googletest/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ mod field_matcher_test;
mod matches_pattern_test;
mod pointwise_matcher_test;
mod property_matcher_test;
#[cfg(feature = "proptest")]
mod proptest_integration_test;
mod tuple_matcher_test;
mod unordered_elements_are_matcher_test;
30 changes: 30 additions & 0 deletions googletest/tests/proptest_integration_test.rs
Original file line number Diff line number Diff line change
@@ -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.

#![cfg(feature = "proptest")]

use googletest::prelude::*;
use proptest::test_runner::{Config, TestRunner};

#[test]
fn numbers_are_greater_than_zero() -> Result<()> {
let mut runner = TestRunner::new(Config::default());
runner.run(&(1..100i32), |v| Ok(verify_that!(v, gt(0))?)).into_test_result()
}

#[test]
fn strings_are_nonempty() -> Result<()> {
let mut runner = TestRunner::new(Config::default());
runner.run(&"[a-zA-Z0-9]+", |v| Ok(verify_that!(v, not(eq("")))?)).into_test_result()
}