diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8fc05ecc..3daf4278 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 }} @@ -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 }} diff --git a/googletest/Cargo.toml b/googletest/Cargo.toml index 003de021..ff6565bc 100644 --- a/googletest/Cargo.toml +++ b/googletest/Cargo.toml @@ -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" diff --git a/googletest/crate_docs.md b/googletest/crate_docs.md index b180e62f..0bd98c98 100644 --- a/googletest/crate_docs.md +++ b/googletest/crate_docs.md @@ -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 diff --git a/googletest/src/internal/test_outcome.rs b/googletest/src/internal/test_outcome.rs index 8a585911..f21f2f8f 100644 --- a/googletest/src/internal/test_outcome.rs +++ b/googletest/src/internal/test_outcome.rs @@ -163,3 +163,10 @@ impl From for TestAssertionFailure { TestAssertionFailure::create(format!("{value}")) } } + +#[cfg(feature = "proptest")] +impl From for proptest::test_runner::TestCaseError { + fn from(value: TestAssertionFailure) -> Self { + proptest::test_runner::TestCaseError::Fail(format!("{value}").into()) + } +} diff --git a/googletest/src/lib.rs b/googletest/src/lib.rs index 972e08a3..341b0348 100644 --- a/googletest/src/lib.rs +++ b/googletest/src/lib.rs @@ -232,3 +232,12 @@ impl IntoTestResult for std::result::Result { self.map_err(|e| TestAssertionFailure::create(format!("{e}"))) } } + +#[cfg(feature = "proptest")] +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/tests/lib.rs b/googletest/tests/lib.rs index caa6c30b..e96519f6 100644 --- a/googletest/tests/lib.rs +++ b/googletest/tests/lib.rs @@ -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; diff --git a/googletest/tests/proptest_integration_test.rs b/googletest/tests/proptest_integration_test.rs new file mode 100644 index 00000000..b33c9dda --- /dev/null +++ b/googletest/tests/proptest_integration_test.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. + +#![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() +}