diff options
author | Carlos Chinchilla <cachinchilla@google.com> | 2023-11-06 20:11:31 +0000 |
---|---|---|
committer | CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2023-11-06 20:11:31 +0000 |
commit | f84b844862813d4989e0ca5f5968912b6bae2b3e (patch) | |
tree | 7e33f0ab844e8a0fa83aedbf1f1685ab5eeaa272 /pw_unit_test | |
parent | a565608495205e2a7b095e275a26586b38cadef6 (diff) | |
download | pigweed-f84b844862813d4989e0ca5f5968912b6bae2b3e.tar.gz |
pw_unit_test: Add googletest test matchers
Add pw::Status test matchers for googletest to make it easy to test
the status expectation when using googletest.
Change-Id: Idd55560f565a67a5616113e737b66f832da65439
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/177878
Commit-Queue: Carlos Chinchilla <cachinchilla@google.com>
Pigweed-Auto-Submit: Carlos Chinchilla <cachinchilla@google.com>
Reviewed-by: Taylor Cramer <cramertj@google.com>
Diffstat (limited to 'pw_unit_test')
-rw-r--r-- | pw_unit_test/BUILD.bazel | 22 | ||||
-rw-r--r-- | pw_unit_test/BUILD.gn | 19 | ||||
-rw-r--r-- | pw_unit_test/CMakeLists.txt | 21 | ||||
-rw-r--r-- | pw_unit_test/docs.rst | 4 | ||||
-rw-r--r-- | pw_unit_test/googletest_test_matchers_test.cc | 144 | ||||
-rw-r--r-- | pw_unit_test/public/pw_unit_test/googletest_test_matchers.h | 108 |
6 files changed, 317 insertions, 1 deletions
diff --git a/pw_unit_test/BUILD.bazel b/pw_unit_test/BUILD.bazel index c4e5e5f4c..c67672f42 100644 --- a/pw_unit_test/BUILD.bazel +++ b/pw_unit_test/BUILD.bazel @@ -105,6 +105,28 @@ pw_cc_library( ) pw_cc_library( + name = "googletest_test_matchers", + hdrs = ["public/pw_unit_test/googletest_test_matchers.h"], + includes = ["public"], + deps = [ + "//pw_result", + "//pw_status", + "@com_google_googletest//:gtest", + ], +) + +pw_cc_test( + name = "googletest_test_matchers_test", + srcs = ["googletest_test_matchers_test.cc"], + target_compatible_with = select({ + "//conditions:default": ["@platforms//:incompatible"], + }), + deps = [ + ":googletest_test_matchers", + ], +) + +pw_cc_library( name = "simple_printing_event_handler", srcs = ["simple_printing_event_handler.cc"], hdrs = [ diff --git a/pw_unit_test/BUILD.gn b/pw_unit_test/BUILD.gn index ba516e113..714acec38 100644 --- a/pw_unit_test/BUILD.gn +++ b/pw_unit_test/BUILD.gn @@ -116,6 +116,22 @@ pw_source_set("googletest_handler_adapter") { sources = [ "googletest_handler_adapter.cc" ] } +pw_source_set("googletest_test_matchers") { + public_configs = [ ":public_include_path" ] + public = [ "public/pw_unit_test/googletest_test_matchers.h" ] + public_deps = [ + "$dir_pw_third_party/googletest", + dir_pw_result, + dir_pw_status, + ] +} + +pw_test("googletest_test_matchers_test") { + enable_if = pw_unit_test_GOOGLETEST_BACKEND != "" + sources = [ "googletest_test_matchers_test.cc" ] + deps = [ ":googletest_test_matchers" ] +} + # Library providing an event handler which outputs human-readable text. pw_source_set("simple_printing_event_handler") { public_deps = [ @@ -311,4 +327,7 @@ pw_test_group("tests") { ":framework_test", ":static_library_support_test", ] + if (dir_pw_third_party_googletest != "") { + tests += [ ":googletest_test_matchers_test" ] + } } diff --git a/pw_unit_test/CMakeLists.txt b/pw_unit_test/CMakeLists.txt index f66f9dfdc..6892f46d0 100644 --- a/pw_unit_test/CMakeLists.txt +++ b/pw_unit_test/CMakeLists.txt @@ -74,6 +74,27 @@ pw_add_library(pw_unit_test.googletest_style_event_handler STATIC googletest_style_event_handler.cc ) +if(NOT ${pw_unit_test_GOOGLETEST_BACKEND} STREQUAL "") + pw_add_library(pw_unit_test.googletest_test_matchers INTERFACE + HEADERS + public/pw_unit_test/googletest_test_matchers.h + PUBLIC_INCLUDES + public + PUBLIC_DEPS + pw_result + pw_status + ${pw_unit_test_GOOGLETEST_BACKEND} + ) + pw_add_test(pw_unit_test.googletest_test_matchers_test + SOURCES + googletest_test_matchers_test.cc + PRIVATE_DEPS + pw_unit_test.googletest_test_matchers + GROUPS + pw_unit_test + ) +endif() + pw_add_library(pw_unit_test.simple_printing_main STATIC SOURCES simple_printing_main.cc diff --git a/pw_unit_test/docs.rst b/pw_unit_test/docs.rst index e1dd9cc8a..0f89e76b5 100644 --- a/pw_unit_test/docs.rst +++ b/pw_unit_test/docs.rst @@ -38,7 +38,9 @@ GoogleTest compatibility pw_unit_test implements a subset of GoogleTest. Supported features include: * Test and test suite declarations. -* Most ``EXPECT`` and ``ASSERT`` macros. +* Most ``EXPECT`` and ``ASSERT`` macros, including ``EXPECT_OK`` and + ``ASSERT_OK`` for functions returning a status. +* ``StatusIs`` matcher to expect a specific ``pw::Status`` other that `OK`. * Stream-style expectation messages, such as ``EXPECT_EQ(val, 5) << "Inputs: " << input``. Messages are currently ignored. diff --git a/pw_unit_test/googletest_test_matchers_test.cc b/pw_unit_test/googletest_test_matchers_test.cc new file mode 100644 index 000000000..c7f8fde3e --- /dev/null +++ b/pw_unit_test/googletest_test_matchers_test.cc @@ -0,0 +1,144 @@ +// Copyright 2023 The Pigweed Authors +// +// 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 +// +// https://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. +#include "pw_unit_test/googletest_test_matchers.h" + +#include <cstdlib> + +#include "pw_status/status.h" + +namespace pw::unit_test { +namespace { + +TEST(TestMatchers, AssertOk) { ASSERT_OK(OkStatus()); } +TEST(TestMatchers, AssertOkStatusWithSize) { ASSERT_OK(StatusWithSize(123)); } +TEST(TestMatchers, AssertOkResult) { ASSERT_OK(Result<int>(123)); } + +TEST(TestMatchers, ExpectOk) { EXPECT_OK(OkStatus()); } +TEST(TestMatchers, ExpectOkStatusWithSize) { EXPECT_OK(StatusWithSize(123)); } +TEST(TestMatchers, ExpectOkResult) { EXPECT_OK(Result<int>(123)); } + +TEST(TestMatchers, StatusIsSuccess) { + EXPECT_THAT(OkStatus(), StatusIs(OkStatus())); + EXPECT_THAT(Status::Cancelled(), StatusIs(Status::Cancelled())); + EXPECT_THAT(Status::Unknown(), StatusIs(Status::Unknown())); + EXPECT_THAT(Status::InvalidArgument(), StatusIs(Status::InvalidArgument())); + EXPECT_THAT(Status::DeadlineExceeded(), StatusIs(Status::DeadlineExceeded())); + EXPECT_THAT(Status::NotFound(), StatusIs(Status::NotFound())); + EXPECT_THAT(Status::AlreadyExists(), StatusIs(Status::AlreadyExists())); + EXPECT_THAT(Status::PermissionDenied(), StatusIs(Status::PermissionDenied())); + EXPECT_THAT(Status::ResourceExhausted(), + StatusIs(Status::ResourceExhausted())); + EXPECT_THAT(Status::FailedPrecondition(), + StatusIs(Status::FailedPrecondition())); + EXPECT_THAT(Status::Aborted(), StatusIs(Status::Aborted())); + EXPECT_THAT(Status::OutOfRange(), StatusIs(Status::OutOfRange())); + EXPECT_THAT(Status::Unimplemented(), StatusIs(Status::Unimplemented())); + EXPECT_THAT(Status::Internal(), StatusIs(Status::Internal())); + EXPECT_THAT(Status::Unavailable(), StatusIs(Status::Unavailable())); + EXPECT_THAT(Status::DataLoss(), StatusIs(Status::DataLoss())); + EXPECT_THAT(Status::Unauthenticated(), StatusIs(Status::Unauthenticated())); +} + +TEST(TestMatchers, StatusIsSuccessStatusWithSize) { + EXPECT_THAT(StatusWithSize(), StatusIs(OkStatus())); + EXPECT_THAT(StatusWithSize::Cancelled(), StatusIs(Status::Cancelled())); + EXPECT_THAT(StatusWithSize::Unknown(), StatusIs(Status::Unknown())); + EXPECT_THAT(StatusWithSize::InvalidArgument(), + StatusIs(Status::InvalidArgument())); + EXPECT_THAT(StatusWithSize::DeadlineExceeded(), + StatusIs(Status::DeadlineExceeded())); + EXPECT_THAT(StatusWithSize::NotFound(), StatusIs(Status::NotFound())); + EXPECT_THAT(StatusWithSize::AlreadyExists(), + StatusIs(Status::AlreadyExists())); + EXPECT_THAT(StatusWithSize::PermissionDenied(), + StatusIs(Status::PermissionDenied())); + EXPECT_THAT(StatusWithSize::ResourceExhausted(), + StatusIs(Status::ResourceExhausted())); + EXPECT_THAT(StatusWithSize::FailedPrecondition(), + StatusIs(Status::FailedPrecondition())); + EXPECT_THAT(StatusWithSize::Aborted(), StatusIs(Status::Aborted())); + EXPECT_THAT(StatusWithSize::OutOfRange(), StatusIs(Status::OutOfRange())); + EXPECT_THAT(StatusWithSize::Unimplemented(), + StatusIs(Status::Unimplemented())); + EXPECT_THAT(StatusWithSize::Internal(), StatusIs(Status::Internal())); + EXPECT_THAT(StatusWithSize::Unavailable(), StatusIs(Status::Unavailable())); + EXPECT_THAT(StatusWithSize::DataLoss(), StatusIs(Status::DataLoss())); + EXPECT_THAT(StatusWithSize::Unauthenticated(), + StatusIs(Status::Unauthenticated())); +} + +TEST(TestMatchers, StatusIsSuccessOkResult) { + const Result<int> result = 46; + EXPECT_THAT(result, StatusIs(OkStatus())); +} + +TEST(TestMatchers, StatusIsSuccessResult) { + EXPECT_THAT(Result<int>(Status::Cancelled()), StatusIs(Status::Cancelled())); + EXPECT_THAT(Result<int>(Status::Unknown()), StatusIs(Status::Unknown())); + EXPECT_THAT(Result<int>(Status::InvalidArgument()), + StatusIs(Status::InvalidArgument())); + EXPECT_THAT(Result<int>(Status::DeadlineExceeded()), + StatusIs(Status::DeadlineExceeded())); + EXPECT_THAT(Result<int>(Status::NotFound()), StatusIs(Status::NotFound())); + EXPECT_THAT(Result<int>(Status::AlreadyExists()), + StatusIs(Status::AlreadyExists())); + EXPECT_THAT(Result<int>(Status::PermissionDenied()), + StatusIs(Status::PermissionDenied())); + EXPECT_THAT(Result<int>(Status::ResourceExhausted()), + StatusIs(Status::ResourceExhausted())); + EXPECT_THAT(Result<int>(Status::FailedPrecondition()), + StatusIs(Status::FailedPrecondition())); + EXPECT_THAT(Result<int>(Status::Aborted()), StatusIs(Status::Aborted())); + EXPECT_THAT(Result<int>(Status::OutOfRange()), + StatusIs(Status::OutOfRange())); + EXPECT_THAT(Result<int>(Status::Unimplemented()), + StatusIs(Status::Unimplemented())); + EXPECT_THAT(Result<int>(Status::Internal()), StatusIs(Status::Internal())); + EXPECT_THAT(Result<int>(Status::Unavailable()), + StatusIs(Status::Unavailable())); + EXPECT_THAT(Result<int>(Status::DataLoss()), StatusIs(Status::DataLoss())); + EXPECT_THAT(Result<int>(Status::Unauthenticated()), + StatusIs(Status::Unauthenticated())); +} + +// The following test is commented out and is only for checking what +// failure cases would look like. For example, when uncommenting the test, +// the output is: +// +// ERR pw_unit_test/googletest_test_matchers_test.cc:50: Failure +// ERR Expected: +// ERR Actual: Value of: OkStatus() +// Expected: has status UNKNOWN +// Actual: 4-byte object <00-00 00-00>, which has status OK + +// ERR pw_unit_test/googletest_test_matchers_test.cc:51: Failure +// ERR Expected: +// ERR Actual: Value of: Status::Unknown() +// Expected: is OK +// Actual: 4-byte object <02-00 00-00>, which has status UNKNOWN + +// ERR pw_unit_test/googletest_test_matchers_test.cc:52: Failure +// ERR Expected: +// ERR Actual: Value of: Status::Unknown() +// Expected: is OK +// Actual: 4-byte object <02-00 00-00>, which has status UNKNOWN +// +// TEST(TestMatchers, SampleFailures) { +// EXPECT_THAT(OkStatus(), StatusIs(Status::Unknown())); +// EXPECT_OK(Status::Unknown()); +// ASSERT_OK(Status::Unknown()); +// } + +} // namespace +} // namespace pw::unit_test
\ No newline at end of file diff --git a/pw_unit_test/public/pw_unit_test/googletest_test_matchers.h b/pw_unit_test/public/pw_unit_test/googletest_test_matchers.h new file mode 100644 index 000000000..bb912c389 --- /dev/null +++ b/pw_unit_test/public/pw_unit_test/googletest_test_matchers.h @@ -0,0 +1,108 @@ +// Copyright 2023 The Pigweed Authors +// +// 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 +// +// https://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. +#pragma once + +#include <type_traits> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "pw_result/result.h" +#include "pw_status/status.h" +#include "pw_status/status_with_size.h" + +namespace pw::unit_test { +namespace internal { +// Gets the pw::Status of different types of objects with a pw::Status for +// Matchers that check the status. +inline constexpr Status GetStatus(Status status) { return status; } + +inline constexpr Status GetStatus(StatusWithSize status_with_size) { + return status_with_size.status(); +} + +template <typename T> +inline constexpr Status GetStatus(const Result<T>& result) { + return result.status(); +} + +// Implements IsOk(). +class IsOkMatcher { + public: + using is_gtest_matcher = void; + + void DescribeTo(std::ostream* os) const { *os << "is OK"; } + + void DescribeNegationTo(std::ostream* os) const { *os << "isn't OK"; } + + template <typename T> + bool MatchAndExplain(T&& actual_value, + ::testing::MatchResultListener* listener) const { + const auto status = GetStatus(actual_value); + if (!status.ok()) { + *listener << "which has status " << pw_StatusString(status); + return false; + } + return true; + } +}; + +// Implements StatusIs(). +class StatusIsMatcher { + public: + explicit StatusIsMatcher(Status expected_status) + : expected_status_(expected_status) {} + + void DescribeTo(std::ostream* os) const { + *os << "has status " << pw_StatusString(expected_status_); + } + + void DescribeNegationTo(std::ostream* os) const { + *os << "does not have status " << pw_StatusString(expected_status_); + } + + template <typename T> + bool MatchAndExplain(T&& actual_value, + ::testing::MatchResultListener* listener) const { + const auto status = GetStatus(actual_value); + if (status != expected_status_) { + *listener << "which has status " << pw_StatusString(status); + return false; + } + return true; + } + + private: + const Status expected_status_; +}; + +} // namespace internal + +/// Macros for testing the results of functions that return ``pw::Status``, +/// ``pw::StatusWithSize``, or ``pw::Result<T>`` (for any T). +#define EXPECT_OK(expression) EXPECT_THAT(expression, ::pw::unit_test::IsOk()) +#define ASSERT_OK(expression) ASSERT_THAT(expression, ::pw::unit_test::IsOk()) + +/// Returns a gMock matcher that matches a `pw::Status`, `pw::StatusWithSize`, +/// or `pw::Result<T>` (for any T) which is OK. +inline internal::IsOkMatcher IsOk() { return {}; } + +/// Returns a gMock matcher that matches a `pw::Status`, `pw::StatusWithSize`, +/// or `pw::Result<T>` (for any T) which has the given status. +inline auto StatusIs(Status expected_status) { + return ::testing::MakePolymorphicMatcher( + internal::StatusIsMatcher(expected_status)); +} + +} // namespace pw::unit_test
\ No newline at end of file |