From 87b43761f2aa9e23b80b431817b461c20ccf32d7 Mon Sep 17 00:00:00 2001 From: Manuel Fuchs Date: Tue, 22 Feb 2022 12:53:29 +0100 Subject: [PATCH] Add `assert_contains!` macro (#322) * Add assert_contains macro * Update CHANGELOG * Add unit tests for assert_contains * Use assert_contains in docs instead of assert * Replace assert! with assert_contains! --- .../ruby-sample/tests/integration_test.rs | 15 +-- libcnb-test/CHANGELOG.md | 1 + libcnb-test/README.md | 8 +- libcnb-test/src/lib.rs | 17 +-- libcnb-test/src/macros.rs | 116 ++++++++++++++++++ 5 files changed, 138 insertions(+), 19 deletions(-) create mode 100644 libcnb-test/src/macros.rs diff --git a/examples/ruby-sample/tests/integration_test.rs b/examples/ruby-sample/tests/integration_test.rs index 15fecc3f..39b4e18a 100644 --- a/examples/ruby-sample/tests/integration_test.rs +++ b/examples/ruby-sample/tests/integration_test.rs @@ -7,6 +7,7 @@ // https://rust-lang.github.io/rust-clippy/stable/index.html #![warn(clippy::pedantic)] +use libcnb_test::assert_contains; use libcnb_test::IntegrationTest; use std::io; use std::io::{Read, Write}; @@ -19,9 +20,9 @@ use std::time::Duration; fn basic() { IntegrationTest::new("heroku/buildpacks:20", "test-fixtures/simple-ruby-app").run_test( |context| { - assert!(context.pack_stdout.contains("---> Ruby Buildpack")); - assert!(context.pack_stdout.contains("---> Installing bundler")); - assert!(context.pack_stdout.contains("---> Installing gems")); + assert_contains!(context.pack_stdout, "---> Ruby Buildpack"); + assert_contains!(context.pack_stdout, "---> Installing bundler"); + assert_contains!(context.pack_stdout, "---> Installing gems"); context.start_container(&[12345], |container| { std::thread::sleep(Duration::from_secs(1)); @@ -35,10 +36,10 @@ fn basic() { "!dlroW olleH" ); - assert!(container - .shell_exec("ruby --version") - .stdout - .contains("ruby 2.7.0p0")); + assert_contains!( + container.shell_exec("ruby --version").stdout, + "ruby 2.7.0p0" + ); }); }, ); diff --git a/libcnb-test/CHANGELOG.md b/libcnb-test/CHANGELOG.md index 0c1f1655..0edb207b 100644 --- a/libcnb-test/CHANGELOG.md +++ b/libcnb-test/CHANGELOG.md @@ -4,6 +4,7 @@ - `libcnb-test` now cross-compiles and packages all binary targets of the buildpack for an integration test. The main buildpack binary is either the only binary target or the target with the same name as the crate. This feature allows the usage of additional binaries for i.e. execd. ([#314](https://github.com/Malax/libcnb.rs/pull/314)) - Increase minimum supported Rust version from 1.56 to 1.58 ([#318](https://github.com/Malax/libcnb.rs/pull/318)). +- Add `assert_contains!` macro for easier matching of `pack` output in integration tests. ([#322](https://github.com/Malax/libcnb.rs/pull/322)) ## [0.1.1] 2022-02-04 diff --git a/libcnb-test/README.md b/libcnb-test/README.md index a0a2d01c..767bb011 100644 --- a/libcnb-test/README.md +++ b/libcnb-test/README.md @@ -15,7 +15,7 @@ Please use the same tag for feature requests. ```rust,no_run // In $CRATE_ROOT/test/integration_test.rs -use libcnb_test::{IntegrationTest, BuildpackReference}; +use libcnb_test::{IntegrationTest, BuildpackReference, assert_contains}; #[test] fn test() { @@ -25,9 +25,9 @@ fn test() { BuildpackReference::Crate, ]) .run_test(|context| { - assert!(context.pack_stdout.contains("---> Maven Buildpack")); - assert!(context.pack_stdout.contains("---> Installing Maven")); - assert!(context.pack_stdout.contains("---> Running mvn package")); + assert_contains!(context.pack_stdout, "---> Maven Buildpack"); + assert_contains!(context.pack_stdout, "---> Installing Maven"); + assert_contains!(context.pack_stdout, "---> Running mvn package"); context.start_container(&[12345], |container| { assert_eq!( diff --git a/libcnb-test/src/lib.rs b/libcnb-test/src/lib.rs index 51cdc7fe..3dde3ceb 100644 --- a/libcnb-test/src/lib.rs +++ b/libcnb-test/src/lib.rs @@ -10,6 +10,7 @@ mod app; mod build; mod container_context; mod container_port_mapping; +mod macros; mod pack; mod util; @@ -32,7 +33,7 @@ use std::process::{Command, Stdio}; /// /// # Example /// ```no_run -/// use libcnb_test::{IntegrationTest, BuildpackReference}; +/// use libcnb_test::{IntegrationTest, BuildpackReference, assert_contains}; /// /// # fn call_test_fixture_service(addr: std::net::SocketAddr, payload: &str) -> Result { /// # unimplemented!() @@ -43,9 +44,9 @@ use std::process::{Command, Stdio}; /// BuildpackReference::Crate, /// ]) /// .run_test(|context| { -/// assert!(context.pack_stdout.contains("---> Maven Buildpack")); -/// assert!(context.pack_stdout.contains("---> Installing Maven")); -/// assert!(context.pack_stdout.contains("---> Running mvn package")); +/// assert_contains!(context.pack_stdout, "---> Maven Buildpack"); +/// assert_contains!(context.pack_stdout, "---> Installing Maven"); +/// assert_contains!(context.pack_stdout, "---> Running mvn package"); /// /// context.start_container(&[12345], |container| { /// assert_eq!( @@ -148,13 +149,13 @@ impl IntegrationTest { /// /// # Example /// ```no_run - /// use libcnb_test::IntegrationTest; + /// use libcnb_test::{IntegrationTest, assert_contains}; /// /// IntegrationTest::new("heroku/buildpacks:20", "test-fixtures/app") /// .run_test(|context| { - /// assert!(context.pack_stdout.contains("---> Ruby Buildpack")); - /// assert!(context.pack_stdout.contains("---> Installing bundler")); - /// assert!(context.pack_stdout.contains("---> Installing gems")); + /// assert_contains!(context.pack_stdout, "---> Ruby Buildpack"); + /// assert_contains!(context.pack_stdout, "---> Installing bundler"); + /// assert_contains!(context.pack_stdout, "---> Installing gems"); /// }) /// ``` pub fn run_test(&mut self, f: F) { diff --git a/libcnb-test/src/macros.rs b/libcnb-test/src/macros.rs new file mode 100644 index 00000000..4ca773c0 --- /dev/null +++ b/libcnb-test/src/macros.rs @@ -0,0 +1,116 @@ +/// Asserts that `left` contains `right`. +/// +/// Commonly used when asserting `pack` output in integration tests. Expands to a [`str::contains`] +/// call and logs `left` (in unescaped and escaped form) as well as `right` on failure. +/// +/// # Example +/// +/// ``` +/// use libcnb_test::assert_contains; +/// +/// let output = "Hello World!\nHello Integration Test!"; +/// assert_contains!(output, "Integration"); +/// ``` +#[macro_export] +macro_rules! assert_contains { + ($left:expr, $right:expr $(,)?) => {{ + if !$left.contains($right) { + ::std::panic!( + r#"assertion failed: `(left contains right)` +left (unescaped): +{} + +left (escaped): `{:?}` +right: `{:?}`"#, + $left, + $left, + $right, + ) + } + }}; + + ($left:expr, $right:expr, $($arg:tt)+) => {{ + if !$left.contains($right) { + ::std::panic!( + r#"assertion failed: `(left contains right)` +left (unescaped): +{} + +left (escaped): `{:?}` +right: `{:?}`: {}"#, + $left, + $left, + $right, + ::core::format_args!($($arg)+) + ) + } + }}; +} + +#[cfg(test)] +mod test { + #[test] + fn simple() { + assert_contains!("Hello World!", "World"); + } + + #[test] + fn simple_with_args() { + assert_contains!("Hello World!", "World", "World must be greeted!"); + } + + #[test] + #[should_panic(expected = "assertion failed: `(left contains right)` +left (unescaped): +foo + +left (escaped): `\"foo\"` +right: `\"bar\"`")] + fn simple_failure() { + assert_contains!("foo", "bar"); + } + + #[test] + #[should_panic(expected = "assertion failed: `(left contains right)` +left (unescaped): +Hello Germany! + +left (escaped): `\"Hello Germany!\"` +right: `\"World\"`: World must be greeted!")] + fn simple_failure_with_args() { + assert_contains!("Hello Germany!", "World", "World must be greeted!"); + } + + #[test] + fn multiline() { + assert_contains!("Hello World!\nFoo\nBar\nBaz", "Bar"); + } + + #[test] + #[should_panic(expected = "assertion failed: `(left contains right)` +left (unescaped): +Hello World! +Foo +Bar +Baz + +left (escaped): `\"Hello World!\\nFoo\\nBar\\nBaz\"` +right: `\"Eggs\"`")] + fn multiline_failure() { + assert_contains!("Hello World!\nFoo\nBar\nBaz", "Eggs"); + } + + #[test] + #[should_panic(expected = "assertion failed: `(left contains right)` +left (unescaped): +Hello World! +Foo +Bar +Baz + +left (escaped): `\"Hello World!\\nFoo\\nBar\\nBaz\"` +right: `\"Eggs\"`: We need eggs!")] + fn multiline_failure_with_args() { + assert_contains!("Hello World!\nFoo\nBar\nBaz", "Eggs", "We need eggs!"); + } +}