Giter Site home page Giter Site logo

googletest-rust's Introduction

GoogleTest Rust

crates.io docs.rs Apache licensed Build Status

This library brings the rich assertion types of Google's C++ testing library GoogleTest to Rust. It provides:

  • A framework for writing matchers which can be combined to make a wide range of assertions on data,
  • A rich set of matchers providing similar functionality to those included in GoogleTest, and
  • A new set of assertion macros offering similar functionality to those of GoogleTest.

The minimum supported Rust version is 1.66.

⚠️ The API is not fully stable and may still be changed until we publish version 1.0.

Moreover, any items or modules starting with __ (double underscores) must not be used directly. Those items or modules are only for internal uses and their API may change without a major version update.

Learning resources

If you're just getting started with googletest, consider going through the first chapter of "Advanced testing for Rust applications", a self-guided Rust course: it provides a guided introduction to the library, with exercises to help you get comfortable with googletest macros, its matchers and its overall philosophy.

Assertions and matchers

The core of GoogleTest is its matchers. Matchers indicate what aspect of an actual value one is asserting: (in-)equality, containment, regular expression matching, and so on.

To make an assertion using a matcher, GoogleTest offers three macros:

  • assert_that! panics if the assertion fails, aborting the test.
  • expect_that! logs an assertion failure, marking the test as having failed, but allows the test to continue running (called a non-fatal assertion). It requires the use of the gtest attribute macro on the test itself.
  • verify_that! has no side effects and evaluates to a Result<()> whose Err variant describes the assertion failure, if there is one. In combination with the ? operator, this can be used to abort the test on assertion failure without panicking. It is also the building block for the other two macros above.

For example:

use googletest::prelude::*;

#[test]
fn fails_and_panics() {
    let value = 2;
    assert_that!(value, eq(4));
}

#[gtest]
fn two_logged_failures() {
    let value = 2;
    expect_that!(value, eq(4)); // Test now failed, but continues executing.
    expect_that!(value, eq(5)); // Second failure is also logged.
}

#[test]
fn fails_immediately_without_panic() -> Result<()> {
    let value = 2;
    verify_that!(value, eq(4))?; // Test fails and aborts.
    verify_that!(value, eq(2))?; // Never executes.
    Ok(())
}

#[test]
fn simple_assertion() -> Result<()> {
    let value = 2;
    verify_that!(value, eq(4)) // One can also just return the last assertion.
}

This library includes a rich set of matchers, covering:

  • Equality, numeric inequality, and approximate equality;
  • Strings and regular expressions;
  • Containers and set-theoretic matching.

Matchers are composable:

use googletest::prelude::*;

#[gtest]
fn contains_at_least_one_item_at_least_3() {
    let value = vec![1, 2, 3];
    expect_that!(value, contains(ge(3)));
}

They can also be logically combined:

use googletest::prelude::*;

#[gtest]
fn strictly_between_9_and_11() {
    let value = 10;
    expect_that!(value, gt(9).and(not(ge(11))));
}

Pattern-matching

One can use the macro matches_pattern! to create a composite matcher for a struct or enum that matches fields with other matchers:

use googletest::prelude::*;

struct AStruct {
    a_field: i32,
    another_field: i32,
    a_third_field: &'static str,
}

#[test]
fn struct_has_expected_values() {
    let value = AStruct {
        a_field: 10,
        another_field: 100,
        a_third_field: "A correct value",
    };
    expect_that!(value, matches_pattern!(AStruct {
        a_field: eq(10),
        another_field: gt(50),
        a_third_field: contains_substring("correct"),
    }));
}

Writing matchers

One can extend the library by writing additional matchers. To do so, create a struct holding the matcher's data and have it implement the trait Matcher:

struct MyEqMatcher<T> {
    expected: T,
}

impl<T: PartialEq + Debug + Copy> Matcher<T> for MyEqMatcher<T> {
    fn matches(&self, actual: T) -> MatcherResult {
         (self.expected == actual).into()
    }

    fn describe(&self, matcher_result: MatcherResult) -> String {
        match matcher_result {
            MatcherResult::Match => {
                format!("is equal to {:?} the way I define it", self.expected)
            }
            MatcherResult::NoMatch => {
                format!("isn't equal to {:?} the way I define it", self.expected)
            }
        }
    }
}

It is recommended to expose a function which constructs the matcher:

pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<T> {
    MyEqMatcher { expected }
}

The new matcher can then be used in the assertion macros:

#[gtest]
fn should_be_equal_by_my_definition() {
    expect_that!(10, eq_my_way(10));
}

Non-fatal assertions

Using non-fatal assertions, a single test is able to log multiple assertion failures. Any single assertion failure causes the test to be considered having 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 gtest instead of the Rust-standard #[test].

use googletest::prelude::*;

#[gtest]
fn three_non_fatal_assertions() {
    let value = 2;
    expect_that!(value, eq(2));  // Passes; test still considered passing.
    expect_that!(value, eq(3));  // Fails; logs failure and marks the test failed.
    expect_that!(value, eq(4));  // A second failure, also logged.
}

This can be used in the same tests as verify_that!, in which case the test function must also return Result<()>:

use googletest::prelude::*;

#[gtest]
fn failing_non_fatal_assertion() -> Result<()> {
    let value = 2;
    expect_that!(value, eq(3));  // Just marks the test as having failed.
    verify_that!(value, eq(2))?;  // Passes, so does not abort the test.
    Ok(())        // Because of the failing expect_that! call above, the
                  // test fails despite returning Ok(())
}
use googletest::prelude::*;

#[gtest]
fn failing_fatal_assertion_after_non_fatal_assertion() -> Result<()> {
    let value = 2;
    verify_that!(value, eq(3))?; // Fails and aborts the test.
    expect_that!(value, eq(3));  // Never executes, since the test already aborted.
    Ok(())
}

Interoperability

You can use the #[gtest] macro together with many other libraries such as rstest. Just apply both attribute macros to the test:

#[gtest]
#[rstest]
#[case(1)]
#[case(2)]
#[case(3)]
fn rstest_works_with_google_test(#[case] value: u32) -> Result<()> {
   verify_that!(value, gt(0))
}

Make sure to put #[gtest] 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 in the same way:

#[gtest]
#[tokio::test]
async fn should_work_with_tokio() -> Result<()> {
    verify_that!(3, gt(0))
}

There is one caveat when running async tests: test failure reporting through and_log_failure will not work properly if the assertion occurs on a different thread than runs the test.

Predicate assertions

The macro verify_pred! provides predicate assertions analogous to GoogleTest's EXPECT_PRED family of macros. Wrap an invocation of a predicate in a verify_pred! invocation to turn that into a test assertion which passes precisely when the predicate returns true:

fn stuff_is_correct(x: i32, y: i32) -> bool {
    x == y
}

let x = 3;
let y = 4;
verify_pred!(stuff_is_correct(x, y))?;

The assertion failure message shows the arguments and the values to which they evaluate:

stuff_is_correct(x, y) was false with
  x = 3,
  y = 4

The verify_pred! invocation evaluates to a Result<()> just like verify_that!. There is also a macro expect_pred! to make a non-fatal predicaticate assertion.

Unconditionally generating a test failure

The macro fail! unconditionally evaluates to a Result indicating a test failure. It can be used analogously to verify_that! and verify_pred! to cause a test to fail, with an optional formatted message:

#[test]
fn always_fails() -> Result<()> {
    fail!("This test must fail with {}", "today")
}

Configuration

This library is configurable through environment variables. Since the configuration does not impact whether a test fails or not but how a failure is displayed, we recommend setting those variables in the personal ~/.cargo/config.toml instead of in the project-scoped Cargo.toml.

Configuration variable list

Variable name Description
NO_COLOR Disables colored output. See https://no-color.org/.
FORCE_COLOR Forces colors even when the output is piped to a file.

Contributing Changes

Please read CONTRIBUTING.md for details on how to contribute to this project.

googletest-rust's People

Contributors

bjacotg avatar dependabot[bot] avatar gribozavr avatar honglooker avatar hovinen avatar hovinenb avatar lukemathwalker avatar mgeisler avatar satylogin avatar tkiela1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

googletest-rust's Issues

Test method runs twice when used together with rstest

To reproduce:

Cargo.toml:

[package]
name = "double"
version = "0.1.0"
edition = "2021"

[dev-dependencies]
googletest = "0.3.0"
rstest = "0.16.0"

src/main.rs:

#[cfg(test)]
mod tests {
    use rstest::*;
    use googletest::{google_test, Result};

    #[rstest]
    #[google_test]
    fn this_runs_twice() -> Result<()> {
        println!("Hello");
        Ok(())
    }

    #[google_test]
    #[rstest]
    fn this_runs_once_with_warning() -> Result<()> {
        println!("Hello");
        Ok(())
    }
}

Output:

$ cargo test -- --show-output
warning: cannot test inner items
  --> src/main.rs:14:5
   |
14 |     #[rstest]
   |     ^^^^^^^^^
   |
   = note: `#[warn(unnameable_test_items)]` on by default
   = note: this warning originates in the attribute macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)

warning: `double` (bin "double" test) generated 1 warning
    Finished test [unoptimized + debuginfo] target(s) in 0.02s
     Running unittests src/main.rs (target/debug/deps/double-740489cfb1a1ebe7)

running 3 tests
test tests::this_runs_once_with_warning ... ok
test tests::this_runs_twice ... ok
test tests::this_runs_twice ... ok

successes:

---- tests::this_runs_once_with_warning stdout ----
Hello

---- tests::this_runs_twice stdout ----
Hello

---- tests::this_runs_twice stdout ----
Hello


successes:
    tests::this_runs_once_with_warning
    tests::this_runs_twice
    tests::this_runs_twice

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Are these the expected result? From https://github.com/google/googletest-rust#interoperability I'd expect the test method to run once without any warnings regardless of the attribute ordering.

`elements_are` does not support containers that don't iterate over borrows

Example:

struct IntContainer(Vec<i32>);
impl<'a> IntoIterator for &'a IntContainer {
    type IntoIter = std::iter::Copied<std::slice::Iter<'a, i32>>;
    type Item = i32;
    fn into_iter(self) -> Self::IntoIter {
        self.0.iter().copied()
    }
}

// I expect to be able to write:
let c = IntContainer(vec![1,2,3]);
assert_that!(c, elements_are![eq(1), eq(2), eq(3)]);

StructMatcher procedural macro

This is something I've dreamed of having in the C++ googletest library. The difference is in Rust this seems possible!

#[googletest::StructMatcher]
struct MyType {
  field_1: i32,
  field_2: String,
  ...
}

This attribute macro would generate something akin to:

#[cfg(test)]
struct MyTypeStructMatcher {
  field_1: Option<Box<dyn Matcher<ActualT = i32>>>,
  field_2: Option<Box<dyn Matcher<ActualT = String>>>,
  ...
}

#[cfg(test)]
impl googletest::matcher::Matcher for MyTypeStructMatcher {
  type ActualT = MyType;

  fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
    if let Some(field_1) = &self.field_1 {
      if field_1.matches(&actual.field_1).is_no_match() {
        return MatcherResult::NoMatch;
      }
    }
    ...
  }
}

(A supporting enum in googletest code to make it easier to construct the field matcher)

pub enum StructFieldMatcher<ActualT> {
  None,
  Matcher(Box<dyn Matcher<ActualT = ActualT>>)
}

impl<M: Matcher + 'static> From<M> for StructFieldMatcher<<M as Matcher>::ActualT> {
  fn from(value: M) -> Self {
    Self::Matcher(Box::new(value))
  }
}

This enables usage similar to the following:

#[googletest::test]
fn my_test() {
  let value = MyType {
      field_1: 3,
      field_2: "helloworld".into(),
      ...
    };
  expect_that!(value, MyTypeStructMatcher{
      field_1: lt(3).into(),
      field_2: contains_substring("llow").into(),
    });
}

Disadvantages

We need to make googletest (or some related crate) a public dependency of crates. Thankfully the #[cfg(test)] attribute should prune the code gen for the entire implementation, but we do still pay the cost of the procedural macro.

create_diff is unreasonably slow

Diffing files of ~500 lines with minor differences takes more than a couple minutes, which makes it practically unusable for interactive use.

Diff output should exclude common lines

The current diff output is verbose because it doesn't remove unchanged lines from the output. I noticed this when testing GoogleTest for PDL in google/pdl#8. There, we often diff files with 100-200 lines of text, and so it's distracting when you see all the unchanged lines.

I would suggest formatting the output line a normal patch with only 2-3 lines of context per hunk.

The documentation for `elements_are` has unclear requirements

I expected to be able to use elements_are! to be able to test that an iterator implementation is correct, but this isn't supported. The documentation says "The actual value must be a container implementing IntoIterator". This is incorrect - it's types where a shared borrow of the type is IntoIterator. The documentation should be more clear that it expects to work with a borrow. I expected to be able to test directly against a proxy type that represents a wrapped shared reference, but that itself is IntoIterator of shared references, not a reference to the proxy type (as that's essentially a double-reference).

If it didn't break consistency with the other matchers I would suggest this matcher consume its input for IntoIterator.

Multi-line diffs still show original strings

Hello!

I find the multi-line diffs super useful and a main reason to depend on the crate. However, I think they could be made better if they would

  • hide the original strings, at least if the strings are very long
  • not diff the escaped debug output

Today, I see output like this from AOSP:

---- cargo::metadata::tests::parse_metadata stdout ----
thread 'cargo::metadata::tests::parse_metadata' panicked at development/tools/cargo_embargo/src/cargo/metadata.rs:495:13:

Value of: format!("{crates:#?}")
Expected: is equal to "[\n    [\n        Crate {\n            name: \"either\",\n            package_name: \"either\",\n            version: Some(\n                \"1.9.0\",\n            ),\n            types: [\n                Lib,\n            ],\n            target: Some(\n                \"x86_64-unknown-linux-gnu\",\n            ),\n            features: [\n                \"default\",\n                \"use_std\",\n            ],\n            cfgs: [],\n            externs: [],\n            codegens: [],\n            cap_lints: \"\",\n            static_libs: [],\n            shared_libs: [],\n            edition: \"2018\",\n            package_dir: \".../external/rust/crates/either\",\n            main_src: \"src/lib.rs\",\n            empty_test: false,\n        },\n        Crate {\n            name: \"either\",\n            package_name: \"either\",\n            version: Some(\n                \"1.9.0\",\n            ),\n            types: [\n                Test,\n            ],\n            target: Some(\n                \"x86_64-unknown-linux-gnu\",\n            ),\n            features: [\n                \"default\",\n                \"use_std\",\n            ],\n            cfgs: [],\n            externs: [\n                Extern {\n                    name: \"serde_json\",\n                    lib_name: \"serde_json\",\n                    extern_type: Rust,\n                },\n            ],\n            codegens: [],\n            cap_lints: \"\",\n            static_libs: [],\n            shared_libs: [],\n            edition: \"2018\",\n            package_dir: \".../external/rust/crates/either\",\n            main_src: \"src/lib.rs\",\n            empty_test: false,\n        },\n    ],\n]"
Actual: "[\n    [\n        Crate {\n            name: \"either\",\n            package_name: \"either!\",\n            version: Some(\n                \"1.9.0\",\n            ),\n            types: [\n                Lib,\n            ],\n            target: Some(\n                \"x86_64-unknown-linux-gnu\",\n            ),\n            features: [\n                \"default\",\n                \"use_std\",\n            ],\n            cfgs: [],\n            externs: [],\n            codegens: [],\n            cap_lints: \"\",\n            static_libs: [],\n            shared_libs: [],\n            edition: \"2018\",\n            package_dir: \".../external/rust/crates/either\",\n            main_src: \"src/lib.rs\",\n            empty_test: false,\n        },\n        Crate {\n            name: \"either\",\n            package_name: \"either!\",\n            version: Some(\n                \"1.9.0\",\n            ),\n            types: [\n                Test,\n            ],\n            target: Some(\n                \"x86_64-unknown-linux-gnu\",\n            ),\n            features: [\n                \"default\",\n                \"use_std\",\n            ],\n            cfgs: [],\n            externs: [\n                Extern {\n                    name: \"serde_json\",\n                    lib_name: \"serde_json\",\n                    extern_type: Rust,\n                },\n            ],\n            codegens: [],\n            cap_lints: \"\",\n            static_libs: [],\n            shared_libs: [],\n            edition: \"2018\",\n            package_dir: \".../external/rust/crates/either\",\n            main_src: \"src/lib.rs\",\n            empty_test: false,\n        },\n    ],\n]",
  which isn't equal to "[\n    [\n        Crate {\n            name: \"either\",\n            package_name: \"either\",\n            version: Some(\n                \"1.9.0\",\n            ),\n            types: [\n                Lib,\n            ],\n            target: Some(\n                \"x86_64-unknown-linux-gnu\",\n            ),\n            features: [\n                \"default\",\n                \"use_std\",\n            ],\n            cfgs: [],\n            externs: [],\n            codegens: [],\n            cap_lints: \"\",\n            static_libs: [],\n            shared_libs: [],\n            edition: \"2018\",\n            package_dir: \".../external/rust/crates/either\",\n            main_src: \"src/lib.rs\",\n            empty_test: false,\n        },\n        Crate {\n            name: \"either\",\n            package_name: \"either\",\n            version: Some(\n                \"1.9.0\",\n            ),\n            types: [\n                Test,\n            ],\n            target: Some(\n                \"x86_64-unknown-linux-gnu\",\n            ),\n            features: [\n                \"default\",\n                \"use_std\",\n            ],\n            cfgs: [],\n            externs: [\n                Extern {\n                    name: \"serde_json\",\n                    lib_name: \"serde_json\",\n                    extern_type: Rust,\n                },\n            ],\n            codegens: [],\n            cap_lints: \"\",\n            static_libs: [],\n            shared_libs: [],\n            edition: \"2018\",\n            package_dir: \".../external/rust/crates/either\",\n            main_src: \"src/lib.rs\",\n            empty_test: false,\n        },\n    ],\n]"
  Difference(-actual / +expected):
   [
       [
           Crate {
               name: \"either\",
  -            package_name: \"either!\",
  +            package_name: \"either\",
               version: Some(
                   \"1.9.0\",
   <---- 22 common lines omitted ---->
           Crate {
               name: \"either\",
  -            package_name: \"either!\",
  +            package_name: \"either\",
               version: Some(
                   \"1.9.0\",
   <---- 28 common lines omitted ---->
       ],
   ]
  at development/tools/cargo_embargo/src/cargo/metadata.rs:495:13

The \" in the diff output makes me think that there is some double-escaping going on.

`assert_that!` and kin should allow a trailing comma

assert_that!(
     1,
     eq(1),
);

fails with requires at least a format string argument. It should not try to format an error if there is no format string.

A trailing comma should also be allowed when formatting arguments are specified, as format!("{}", 1,) works.

Latest HEAD breaks matching on certain patterns

Patterns with certain combinations of properties don't compile any more after the merge of #367. All changes implied by the documentation and examples in that PR have been applied.

Minimal failing example:

#[test]
fn matches_struct_with_method_returning_option_of_non_copy_value() -> Result<()> {
    #[derive(Debug)]
    struct AnInnerStruct;
    #[derive(Debug)]
    struct AStruct;
    impl AStruct {
        fn get_value(&self) -> Option<AnInnerStruct> {
            Some(AnInnerStruct)
        }
    }
    verify_that!(
        AStruct,
        matches_pattern!(&AStruct {
            get_value(): ref some(matches_pattern!(&AnInnerStruct))
        })
    )
}

Resulting compiler error:

error: implementation of `Matcher` is not general enough
    --> googletest/tests/matches_pattern_test.rs:1793:9
     |
1793 | /         matches_pattern!(&AStruct {
1794 | |             get_value(): ref some(matches_pattern!(&AnInnerStruct))
1795 | |         })
     | |__________^ implementation of `Matcher` is not general enough
     |
     = note: `matchers::some_matcher::SomeMatcher<PredicateMatcher<{closure@/home/hovinen/dev/googletest-rust/googletest/src/matchers/matches_pattern.rs:456:37: 456:40}, &str, &str>>` must implement `Matcher<&'0 Option<AnInnerStruct>>`, for any lifetime `'0`...
     = note: ...but it actually implements `Matcher<&'1 Option<AnInnerStruct>>`, for some specific lifetime `'1`
     = note: this error originates in the macro `$crate::matches_pattern_internal` which comes from the expansion of the macro `matches_pattern` (in Nightly builds, run with -Z macro-backtrace for more info)

error: could not compile `googletest` (test "matches_pattern_test") due to 2 previous errors
warning: build failed, waiting for other jobs to finish...
warning: `googletest` (test "field_matcher_test") generated 2 warnings
warning: `googletest` (lib test) generated 1 warning (run `cargo fix --lib -p googletest --tests` to apply 1 suggestion)

This example is coming from a real world example, so this is actually a hard blocker for me if not fixed before the next GoogleTest release.

Thanks!

Api suggestions?

Is it every the case when we have a verify_that!() we don't want a test to eventually fail if that one fails? Why is there a need for and_log_failure?

I was expecting the api to be something more like:

#[google-test]
pub fn some_test() -> Result<...>{
verify_that!(12, eq(12)); // Okay
verify_that!(12, eq(13)); // Marked test as failure, but test continues
// This one passes and provides the return value which is determined by if any of the previous checks failed. 
verify_that!(10, eq(10))
}

Maybe it can be an alternate syntax instated of verify_that, or maybe the case of verify_that!(...).and_log_error could be shortened to verify_and_continue!(...)

Limitations of the Matcher Trait with Proxy reference type

See this playground for a minimal example.

While implementing matchers for the Rust protobuf library, I stumbled on an issue with the lifetime requirement of the Matcher trait. The lifetime issue still did not click right in my head, so forgive me if what I say is not exactly what is happening. But, as far as I currently understand it, the Matcher trait accepts &ActualT with any lifetime even though ActualT may be bounded, which makes the impl Matcher not able to access most functions of ActualT.

I see a few options to solve this issue:

  • Add a lifetime requirement on &ActualT to have a lifetime smaller than ActualT.
    • Not sure if this will work. In my current set up with the protobuf, I managed to get it work, but of course all the other matchers do not work and will need to be updated.
  • Accept ActualT and not &ActualT in matcher, and constraint ActualT with Copy
    • Not sure if this will work either. Same as above, it fixes the current issue, but it may break other use cases.
  • Do not support this pattern. Or the library is buggy
    • Potentially, but I couldn't pinpoint where, so I still currently believe that the Matcher trait is just not right.

This reminds me of the issue we currently have with property!() and string slices as it was also a reference lifetime issue.

I will add more details while debugging further.

Current leads:

  • Adding the lifetime bound on the Matcher trait and see where it takes us
  • Constraining on Copy and see where it takes us
  • Changing the return lifetime of get_a_field() in the example from '_ to 'a seems to work, so maybe there is an issue in the library

Garbled formatting of assertion failure messages in some cases

When the all! or any! matcher is nested inside another matcher like some, ok, or err, the match explanation has broken formatting. For example:

verify_that!(Some(4), some(all![eq(1), eq(2), eq(3)]))

has the following match explanation:

Actual: Some(4),
  which has a value * which isn't equal to 1
  * which isn't equal to 2
  * which isn't equal to 3
  at googletest/tests/all_matcher_test.rs:96:18

The first bullet should be on the following line, like so:

Actual: Some(4),
  which has a value
  * which isn't equal to 1
  * which isn't equal to 2
  * which isn't equal to 3
  at googletest/tests/all_matcher_test.rs:96:18

Support for `#[should_panic]`?

Hi there! I wanted to write about GoogleTest for Comprehensive Rust, so I tried adding a failing test:

#[googletest::test]
fn two_logged_failures() {
    let value = 2;
    expect_that!(value, eq(4)); // Test now failed, but continues executing.
    expect_that!(value, eq(5)); // Second failure is also logged.
}

So far so good! However, now my cargo test fails — on purpose.

I went looking for something similar to Testing panics, but when I add the #[should_panic], I'm told that this isn't supported:

% cargo test
   Compiling testing v0.1.0 (/home/mgeisler/src/comprehensive-rust/src/testing)
error: functions using `#[should_panic]` must return `()`
 --> src/testing/googletest.rs:4:1
  |
4 | #[googletest::test]
  | ^^^^^^^^^^^^^^^^^^^
  |
  = note: this error originates in the attribute macro `googletest::test` (in Nightly builds, run with -Z macro-backtrace for more info)

error: could not compile `testing` (example "googletest-example" test) due to previous error

It does not help to return () — I suppose it's because the test body returns something else due to googletest::test?

Is it possible for me to silence the expected error?

Documentation on docs.rs lists no types

Hi there,

The auto-generated documentation on docs.rs (such as the documentation for elements_are!) does not list the types of the arguments. It just says

image

Questions that come to mind are:

  • Does it also work on a slice? Probably... what about an array?
  • What about my own type?
  • Would it even work on an interator?

The lack of type information is of course a consequence of the use of macros. To compensate for this, I would suggest mentioning any relevant traits in the doc comments.

matches_pattern/pat generates error: cannot find macro `matches_pattern_internal` in this scope

They were working fine in the 0.3 release, but are broken in 0.4

To reproduce:

#[cfg(test)]
mod tests {
    use googletest::{expect_that, google_test, Result, pat, matches_pattern};

    #[google_test]
    fn this_runs_twice() -> Result<()> {
        expect_that!(42, pat!(42));
        Ok(())
    }
}

With a minimal Cargo.toml

[package]
name = "repro"
version = "0.1.0"
edition = "2021"

[dev-dependencies]
googletest = "0.4.0"

Large error on nested use of `anything()` in a tuple in a vector

This code fails to compile:

use googletest::prelude::*;

#[googletest::test]
fn test_elements_are() {
    let value = vec![(1, "foo"), (2, "bar"), (3, "baz")];
    expect_that!(
        value,
        elements_are!(eq((1, "foo")), lt((anything(), "xxx")), anything())
    );
}

I'm getting a non-trivial error to decipher:

% cargo test
   Compiling testing v0.1.0 (/home/mgeisler/src/comprehensive-rust/src/testing)
error[E0277]: `impl Matcher<ActualT = _>` doesn't implement `Debug`
  --> src/testing/googletest.rs:8:43
   |
8  |         elements_are!(eq((1, "foo")), lt((anything(), "xxx")), anything())
   |                                       --  ^^^^^^^^^^ `impl Matcher<ActualT = _>` cannot be formatted using `{:?}` because it doesn't implement `Debug`
   |                                       |
   |                                       required by a bound introduced by this call
   |
   = help: the trait `Debug` is not implemented for `impl Matcher<ActualT = _>`
   = help: the following other types implement trait `Debug`:
             ()
             (C, B, A, Z, Y, X, W, V, U, T)
             (T,)
             (E, D, C, B, A, Z, Y, X, W, V, U, T)
             (A, Z, Y, X, W, V, U, T)
             (U, T)
             (V, U, T)
             (W, V, U, T)
           and 5 others
   = note: required for `(impl Matcher<ActualT = _>, &str)` to implement `Debug`
note: required by a bound in `googletest::matchers::lt`
  --> /home/mgeisler/.cargo/registry/src/index.crates.io-6f17d22bba15001f/googletest-0.10.0/src/matchers/lt_matcher.rs:74:62
   |
74 | pub fn lt<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug>(
   |                                                              ^^^^^ required by this bound in `lt`

error[E0277]: can't compare `({integer}, &str)` with `(impl Matcher<ActualT = _>, &str)`
  --> src/testing/googletest.rs:8:39
   |
8  |         elements_are!(eq((1, "foo")), lt((anything(), "xxx")), anything())
   |                                       ^^ no implementation for `({integer}, &str) < (impl Matcher<ActualT = _>, &str)` and `({integer}, &str) > (impl Matcher<ActualT = _>, &str)`
   |
   = help: the trait `PartialOrd<(impl Matcher<ActualT = _>, &str)>` is not implemented for `({integer}, &str)`
   = help: the following other types implement trait `PartialOrd<Rhs>`:
             ()
             (C, B, A, Z, Y, X, W, V, U, T)
             (T,)
             (E, D, C, B, A, Z, Y, X, W, V, U, T)
             (A, Z, Y, X, W, V, U, T)
             (U, T)
             (V, U, T)
             (W, V, U, T)
           and 5 others
note: required by a bound in `googletest::matchers::lt`
  --> /home/mgeisler/.cargo/registry/src/index.crates.io-6f17d22bba15001f/googletest-0.10.0/src/matchers/lt_matcher.rs:74:28
   |
74 | pub fn lt<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug>(
   |                            ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `lt`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `testing` (example "googletest-example" test) due to 2 previous errors

It works fine if I replace the first anything() with value such as 10.

Word diff via colored diffs

I would love to have colors in the diff output. Colors can be super helpful since they can make it possible to implement word-diffs in the terminal.

An example could look like this (this is from Magit in a text-mode Emacs):

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.