| // Copyright 2020 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. |
| |
| // The Pigweed unit test framework requires C++17 to use its full functionality. |
| #pragma once |
| |
| // IWYU pragma: private, include "pw_unit_test/framework.h" |
| |
| #if defined(GTEST_TEST) |
| #error \ |
| "GTEST_TEST is already defined. Make sure googletest headers are not " \ |
| "included when using the pw_unit_test light backend." |
| #endif // GTEST_TEST |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <cstring> |
| #include <new> |
| |
| #include "pw_bytes/alignment.h" |
| #include "pw_polyfill/standard.h" |
| #include "pw_preprocessor/compiler.h" |
| #include "pw_preprocessor/util.h" |
| #include "pw_span/span.h" |
| #include "pw_unit_test/config.h" |
| #include "pw_unit_test/event_handler.h" |
| |
| #if PW_CXX_STANDARD_IS_SUPPORTED(17) |
| #include <string_view> |
| |
| #include "pw_string/string_builder.h" |
| #endif // PW_CXX_STANDARD_IS_SUPPORTED(17) |
| |
| /// @def GTEST_TEST |
| /// Alias for `TEST`. |
| #define GTEST_TEST(test_suite_name, test_name) \ |
| _PW_TEST_SUITE_NAMES_MUST_BE_UNIQUE(void /* TEST */, test_suite_name); \ |
| _PW_TEST(test_suite_name, test_name, ::pw::unit_test::internal::Test) |
| |
| /// @def TEST |
| /// Defines a test given the suite name and test case name. |
| /// |
| /// If `TEST` is conflicting with other code, set `GTEST_DONT_DEFINE_TEST` to |
| /// 1 and use `GTEST_TEST` instead. |
| /// |
| /// @param[in] test_suite_name The name of the test suite or collection of |
| /// tests. |
| /// @param[in] test_name The name of the test case. |
| #if !(defined(GTEST_DONT_DEFINE_TEST) && GTEST_DONT_DEFINE_TEST) |
| #define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name) |
| #endif // !GTEST_DONT_DEFINE_TEST |
| |
| /// @def TEST_F |
| /// Defines a test case using a test fixture. |
| /// |
| /// @param[in] test_fixture The name of the test fixture class to use. |
| /// @param[in] test_name The name of the test case. |
| #define TEST_F(test_fixture, test_name) \ |
| _PW_TEST_SUITE_NAMES_MUST_BE_UNIQUE(int /* TEST_F */, test_fixture); \ |
| _PW_TEST(test_fixture, test_name, test_fixture) |
| |
| /// @def FRIEND_TEST |
| /// Defines a test case from a test suite as a friend class of an implementation |
| /// class. |
| /// |
| /// @warning Use of `FRIEND_TEST` is discouraged, because it induces coupling |
| /// between testing and implementation code. Consider this a last resort only. |
| /// |
| /// @param[in] test_suite_name The name of the test suite to befriend. |
| /// @param[in] test_name The name of the test case to befriend. |
| #define FRIEND_TEST(test_suite_name, test_name) \ |
| friend class test_suite_name##_##test_name##_Test |
| |
| /// @def EXPECT_TRUE |
| /// Verifies that @p expr evaluates to true. |
| /// |
| /// @param[in] expr The expression to evaluate. |
| #define EXPECT_TRUE(expr) _PW_TEST_EXPECT(_PW_TEST_BOOL(expr, true)) |
| |
| /// @def EXPECT_FALSE |
| /// Verifies that @p expr evaluates to false. |
| /// |
| /// @param[in] expr The expression to evaluate. |
| #define EXPECT_FALSE(expr) _PW_TEST_EXPECT(_PW_TEST_BOOL(expr, false)) |
| |
| /// @def EXPECT_EQ |
| /// Verifies that `lhs == rhs`. |
| /// |
| /// Does pointer equality on pointers. If used on two C strings, `EXPECT_EQ` |
| /// tests if they are in the same memory location, not if they have the same |
| /// value. Use `EXPECT_STREQ` to compare C strings (e.g. `const char*`) by |
| /// value. |
| /// |
| /// When comparing a pointer to `NULL` use `EXPECT_EQ(ptr, nullptr)` instead of |
| /// `EXPECT_EQ(ptr, NULL)`. |
| /// |
| /// @param[in] lhs The left side of the equality comparison. |
| /// @param[in] rhs The right side of the equality comparison. |
| #define EXPECT_EQ(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_OP(lhs, rhs, ==)) |
| |
| /// @def EXPECT_NE |
| /// Verifies that `lhs != rhs`. |
| /// |
| /// Does pointer equality on pointers. If used on two C strings, it tests if |
| /// they are in different memory locations, not if they have different values. |
| /// Use `EXPECT_STRNE` to compare C strings (e.g. `const char*`) by value. |
| /// |
| /// When comparing a pointer to `NULL`, use `EXPECT_NE(ptr, nullptr)` instead |
| /// of `EXPECT_NE(ptr, NULL)`. |
| |
| /// @param[in] lhs The left side of the inequality comparison. |
| /// @param[in] rhs The right side of the inequality comparison. |
| #define EXPECT_NE(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_OP(lhs, rhs, !=)) |
| |
| /// @def EXPECT_GT |
| /// Verifies that `lhs > rhs`. |
| /// |
| /// @param[in] lhs The left side of the comparison. |
| /// @param[in] rhs The right side of the comparison. |
| #define EXPECT_GT(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_OP(lhs, rhs, >)) |
| |
| /// @def EXPECT_GE |
| /// Verifies that `lhs >= rhs`. |
| /// |
| /// @param[in] lhs The left side of the comparison. |
| /// @param[in] rhs The right side of the comparison. |
| #define EXPECT_GE(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_OP(lhs, rhs, >=)) |
| |
| /// @def EXPECT_LT |
| /// Verifies that `lhs < rhs`. |
| /// |
| /// @param[in] lhs The left side of the comparison. |
| /// @param[in] rhs The right side of the comparison. |
| #define EXPECT_LT(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_OP(lhs, rhs, <)) |
| |
| /// @def EXPECT_LE |
| /// Verifies that `lhs <= rhs`. |
| /// |
| /// @param[in] lhs The left side of the comparison. |
| /// @param[in] rhs The right side of the comparison. |
| #define EXPECT_LE(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_OP(lhs, rhs, <=)) |
| |
| /// @def EXPECT_NEAR |
| /// Verifies that the difference between `lhs` and `rhs` does not exceed the |
| /// absolute error bound `epsilon`. |
| /// |
| /// @param[in] lhs The left side of the comparison. |
| /// @param[in] rhs The right side of the comparison. |
| /// @param[in] epsilon The maximum difference between `lhs` and `rhs`. |
| #define EXPECT_NEAR(lhs, rhs, epsilon) \ |
| _PW_TEST_EXPECT(_PW_TEST_NEAR(lhs, rhs, epsilon)) |
| |
| /// @def EXPECT_FLOAT_EQ |
| /// Verifies that the two float values `rhs` and `lhs` are approximately |
| /// equal, to within 4 units in the last place (ULPs) from each other. |
| /// |
| /// @param[in] lhs The left side of the equality comparison. |
| /// @param[in] rhs The right side of the equality comparison. |
| #define EXPECT_FLOAT_EQ(lhs, rhs) \ |
| _PW_TEST_EXPECT( \ |
| _PW_TEST_NEAR(lhs, rhs, 4 * std::numeric_limits<float>::epsilon())) |
| |
| /// @def EXPECT_DOUBLE_EQ |
| /// Verifies that the two double values `rhs` and `lhs` are approximately |
| /// equal, to within 4 units in the last place (ULPs) from each other. |
| /// |
| /// @param[in] lhs The left side of the equality comparison. |
| /// @param[in] rhs The right side of the equality comparison. |
| #define EXPECT_DOUBLE_EQ(lhs, rhs) \ |
| _PW_TEST_EXPECT( \ |
| _PW_TEST_NEAR(lhs, rhs, 4 * std::numeric_limits<double>::epsilon())) |
| |
| /// @def EXPECT_STREQ |
| /// Verifies that the two C strings `lhs` and `rhs` have the same contents. |
| /// |
| /// @param[in] lhs The left side of the equality comparison. |
| /// @param[] rhs The right side of the equality comparison. |
| #define EXPECT_STREQ(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_C_STR(lhs, rhs, ==)) |
| |
| /// @def EXPECT_STRNE |
| /// Verifies that the two C strings `lhs` and `rhs` have different content. |
| /// |
| /// @param[in] lhs The left side of the inequality comparison. |
| /// @param[in] rhs The right side of the inequality comparison. |
| #define EXPECT_STRNE(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_C_STR(lhs, rhs, !=)) |
| |
| /// @def ASSERT_TRUE |
| /// See `EXPECT_TRUE`. |
| #define ASSERT_TRUE(expr) _PW_TEST_ASSERT(_PW_TEST_BOOL(expr, true)) |
| |
| /// @def ASSERT_FALSE |
| /// See `EXPECT_FALSE`. |
| #define ASSERT_FALSE(expr) _PW_TEST_ASSERT(_PW_TEST_BOOL(expr, false)) |
| |
| /// @def ASSERT_EQ |
| /// See `EXPECT_EQ`. |
| #define ASSERT_EQ(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_OP(lhs, rhs, ==)) |
| |
| /// @def ASSERT_NE |
| /// See `EXPECT_NE`. |
| #define ASSERT_NE(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_OP(lhs, rhs, !=)) |
| |
| /// @def ASSERT_GT |
| /// See `EXPECT_GT`. |
| #define ASSERT_GT(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_OP(lhs, rhs, >)) |
| |
| /// @def ASSERT_GE |
| /// See `EXPECT_GE`. |
| #define ASSERT_GE(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_OP(lhs, rhs, >=)) |
| |
| /// @def ASSERT_LT |
| /// See `EXPECT_LT`. |
| #define ASSERT_LT(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_OP(lhs, rhs, <)) |
| |
| /// @def ASSERT_LE |
| /// See `EXPECT_LE`. |
| #define ASSERT_LE(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_OP(lhs, rhs, <=)) |
| |
| /// @def ASSERT_NEAR |
| /// See `EXPECT_NEAR`. |
| #define ASSERT_NEAR(lhs, rhs, epsilon) \ |
| _PW_TEST_ASSERT(_PW_TEST_NEAR(lhs, rhs, epsilon)) |
| |
| /// @def ASSERT_FLOAT_EQ |
| /// See `EXPECT_FLOAT_EQ`. |
| #define ASSERT_FLOAT_EQ(lhs, rhs) \ |
| _PW_TEST_ASSERT( \ |
| _PW_TEST_NEAR(lhs, rhs, 4 * std::numeric_limits<float>::epsilon())) |
| |
| /// @def ASSERT_DOUBLE_EQ |
| /// See `EXPECT_DOUBLE_EQ`. |
| #define ASSERT_DOUBLE_EQ(lhs, rhs) \ |
| _PW_TEST_ASSERT( \ |
| _PW_TEST_NEAR(lhs, rhs, 4 * std::numeric_limits<double>::epsilon())) |
| |
| /// @def ASSERT_STREQ |
| /// See `EXPECT_STREQ`. |
| #define ASSERT_STREQ(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_C_STR(lhs, rhs, ==)) |
| |
| /// @def ASSERT_STRNE |
| /// See `EXPECT_STRNE`. |
| #define ASSERT_STRNE(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_C_STR(lhs, rhs, !=)) |
| |
| /// @def ADD_FAILURE |
| /// Generates a non-fatal failure with a generic message. |
| #define ADD_FAILURE() \ |
| ::pw::unit_test::internal::Framework::Get().CurrentTestExpectSimple( \ |
| "(line is not executed)", "(line was executed)", __LINE__, false); \ |
| _PW_UNIT_TEST_LOG |
| |
| /// @def GTEST_FAIL |
| /// |
| /// Alias of `FAIL`. |
| #define GTEST_FAIL() return ADD_FAILURE() |
| |
| /// @def GTEST_SKIP |
| /// Skips test at runtime. Skips are neither successful nor failed. They |
| /// abort the current function. |
| #define GTEST_SKIP() \ |
| ::pw::unit_test::internal::Framework::Get().CurrentTestSkip(__LINE__); \ |
| return _PW_UNIT_TEST_LOG |
| |
| /// @def FAIL |
| /// Generates a fatal failure with a generic message. |
| /// |
| /// If this generic name is clashing with other code, set |
| /// `GTEST_DONT_DEFINE_FAIL` to 1 and use `GTEST_FAIL` instead. |
| #if !(defined(GTEST_DONT_DEFINE_FAIL) && GTEST_DONT_DEFINE_FAIL) |
| #define FAIL() GTEST_FAIL() |
| #endif // !GTEST_DONT_DEFINE_FAIL |
| |
| /// @def GTEST_SUCCEED |
| /// |
| /// Alias of `SUCCEED`. |
| #define GTEST_SUCCEED() \ |
| ::pw::unit_test::internal::Framework::Get().CurrentTestExpectSimple( \ |
| "(success)", "(success)", __LINE__, true); \ |
| _PW_UNIT_TEST_LOG |
| |
| /// @def SUCCEED |
| /// |
| /// Generates success with a generic message. |
| /// |
| /// If this generic name is conflicting with other code, set |
| /// `GTEST_DONT_DEFINE_SUCCEED` to 1 and use `GTEST_SUCCEED` instead. |
| #if !(defined(GTEST_DONT_DEFINE_SUCCEED) && GTEST_DONT_DEFINE_SUCCEED) |
| #define SUCCEED() GTEST_SUCCEED() |
| #endif // !GTEST_DONT_DEFINE_SUCCEED |
| |
| /// @def RUN_ALL_TESTS |
| /// The `pw_unit_test` framework entrypoint. Runs every registered test case |
| /// and dispatches the results through the event handler. |
| /// |
| /// @pre An event handler has been registered before calling `RUN_ALL_TESTS`. |
| /// |
| /// @returns A status of 0 if all tests passed, or non-zero if there were any |
| /// failures. This is compatible with GoogleTest. |
| #define RUN_ALL_TESTS() \ |
| ::pw::unit_test::internal::Framework::Get().RunAllTests() |
| |
| /// @def GTEST_HAS_DEATH_TEST |
| /// Death tests are not supported. The `*_DEATH_IF_SUPPORTED` macros do nothing. |
| #define GTEST_HAS_DEATH_TEST 0 |
| |
| /// @def EXPECT_DEATH_IF_SUPPORTED |
| /// See `GTEST_HAS_DEATH_TEST`. |
| #define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ |
| if (0) { \ |
| static_cast<void>(statement); \ |
| static_cast<void>(regex); \ |
| } \ |
| static_assert(true, "Macros must be terminated with a semicolon") |
| |
| /// @def ASSERT_DEATH_IF_SUPPORTED |
| /// See `GTEST_HAS_DEATH_TEST`. |
| #define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ |
| EXPECT_DEATH_IF_SUPPORTED(statement, regex) |
| |
| namespace pw { |
| |
| #if PW_CXX_STANDARD_IS_SUPPORTED(17) |
| |
| namespace string { |
| |
| // This function is used to print unknown types that are used in EXPECT or |
| // ASSERT statements in tests. |
| // |
| // You can add support for displaying custom types by defining a ToString |
| // template specialization. For example: |
| // |
| // namespace pw { |
| // |
| // template <> |
| // StatusWithSize ToString<MyType>(const MyType& value, |
| // span<char> buffer) { |
| // return string::Format("<MyType|%d>", value.id); |
| // } |
| // |
| // } // namespace pw |
| // |
| // See the documentation in pw_string/string_builder.h for more information. |
| template <typename T> |
| StatusWithSize UnknownTypeToString(const T& value, span<char> buffer) { |
| StringBuilder sb(buffer); |
| sb << '<' << sizeof(value) << "-byte object at 0x" << &value; |
| |
| // How many bytes of the object to print. |
| // |
| // WARNING: Printing the contents of an object may be undefined behavior! |
| // Accessing unintialized memory is undefined behavior, and objects sometimes |
| // contiain uninitialized regions, such as padding bytes or unalloacted |
| // storage (e.g. std::optional). kPrintMaybeUnintializedBytes MUST stay at 0, |
| // except when changed locally to help with debugging. |
| constexpr size_t kPrintMaybeUnintializedBytes = 0; |
| |
| constexpr size_t kBytesToPrint = |
| std::min(sizeof(value), kPrintMaybeUnintializedBytes); |
| |
| if (kBytesToPrint != 0u) { |
| sb << " |"; |
| |
| // reinterpret_cast to std::byte is permitted by C++'s type aliasing rules. |
| const std::byte* bytes = reinterpret_cast<const std::byte*>(&value); |
| |
| for (size_t i = 0; i < kBytesToPrint; ++i) { |
| sb << ' ' << bytes[i]; |
| } |
| |
| // If there's just one more byte, output it. Otherwise, output ellipsis. |
| if (sizeof(value) == kBytesToPrint + 1) { |
| sb << ' ' << bytes[sizeof(value) - 1]; |
| } else if (sizeof(value) > kBytesToPrint) { |
| sb << " …"; |
| } |
| } |
| |
| sb << '>'; |
| return sb.status_with_size(); |
| } |
| |
| } // namespace string |
| |
| #endif // PW_CXX_STANDARD_IS_SUPPORTED(17) |
| |
| namespace unit_test { |
| namespace internal { |
| |
| class Test; |
| class TestInfo; |
| |
| // Types of SetUpTestSuite() and TearDownTestSuite() functions. |
| using SetUpTestSuiteFunc = void (*)(); |
| using TearDownTestSuiteFunc = void (*)(); |
| |
| // Used to tag arguments to EXPECT_STREQ/EXPECT_STRNE so they are treated like C |
| // strings rather than pointers. |
| struct CStringArg { |
| const char* const c_str; |
| }; |
| |
| constexpr size_t MaxPaddingNeededToRaiseAlignment(size_t current_align, |
| size_t new_align) { |
| if (new_align < current_align) { |
| return 0; |
| } |
| return new_align - current_align; |
| } |
| |
| // Singleton test framework class responsible for managing and running test |
| // cases. This implementation is internal to Pigweed test; free functions |
| // wrapping its functionality are exposed as the public interface. |
| class Framework { |
| public: |
| constexpr Framework() |
| : current_test_(nullptr), |
| current_result_(TestResult::kSuccess), |
| run_tests_summary_{.passed_tests = 0, |
| .failed_tests = 0, |
| .skipped_tests = 0, |
| .disabled_tests = 0}, |
| exit_status_(0), |
| event_handler_(nullptr), |
| memory_pool_() {} |
| |
| static Framework& Get() { return framework_; } |
| |
| // Registers a single test case with the framework. The framework owns the |
| // registered unit test. Called during static initialization. |
| void RegisterTest(TestInfo* test) const; |
| |
| // Sets the handler to which the framework dispatches test events. During a |
| // test run, the framework owns the event handler. |
| inline void RegisterEventHandler(EventHandler* event_handler) { |
| event_handler_ = event_handler; |
| } |
| |
| // Runs all registered test cases, returning a status of 0 if all succeeded or |
| // nonzero if there were any failures. Test events that occur during the run |
| // are sent to the registered event handler, if any. |
| int RunAllTests(); |
| |
| #if PW_CXX_STANDARD_IS_SUPPORTED(17) |
| // Only run test suites whose names are included in the provided list during |
| // the next test run. This is C++17 only; older versions of C++ will run all |
| // non-disabled tests. |
| void SetTestSuitesToRun(span<std::string_view> test_suites) { |
| test_suites_to_run_ = test_suites; |
| } |
| #endif // PW_CXX_STANDARD_IS_SUPPORTED(17) |
| |
| bool ShouldRunTest(const TestInfo& test_info) const; |
| |
| // Whether the current test is skipped. |
| bool IsSkipped() const { return current_result_ == TestResult::kSkipped; } |
| |
| // Whether the current test has failed. |
| bool HasFailure() const { return current_result_ == TestResult::kFailure; } |
| |
| // Constructs an instance of a unit test class and runs the test. |
| // |
| // Tests are constructed within a static memory pool at run time instead of |
| // being statically allocated to avoid blowing up the size of the test binary |
| // in cases where users have large test fixtures (e.g. containing buffers) |
| // reused many times. Instead, only a small, fixed-size TestInfo struct is |
| // statically allocated per test case, with a run() function that references |
| // this method instantiated for its test class. |
| template <typename TestInstance> |
| static void CreateAndRunTest(const TestInfo& test_info) { |
| static_assert( |
| sizeof(TestInstance) + |
| MaxPaddingNeededToRaiseAlignment( |
| alignof(decltype(memory_pool_)), alignof(TestInstance)) <= |
| sizeof(memory_pool_), |
| "The test memory pool is too small for this test. Either increase " |
| "PW_UNIT_TEST_CONFIG_MEMORY_POOL_SIZE or decrease the size of your " |
| "test fixture."); |
| |
| Framework& framework = Get(); |
| framework.StartTest(test_info); |
| |
| // Reset the memory pool to a marker value to help detect use of |
| // uninitialized memory. |
| std::memset(&framework.memory_pool_, 0xa5, sizeof(framework.memory_pool_)); |
| |
| framework.SetUpTestSuiteIfNeeded(TestInstance::SetUpTestSuite); |
| |
| // Construct the test object within the static memory pool. The StartTest |
| // function has already been called by the TestInfo at this point. |
| void* aligned_pool = |
| AlignUp(&framework.memory_pool_, alignof(TestInstance)); |
| TestInstance* test_instance = new (aligned_pool) TestInstance(); |
| test_instance->PigweedTestRun(); |
| |
| // Manually call the destructor as it is not called automatically for |
| // objects constructed using placement new. |
| test_instance->~TestInstance(); |
| |
| framework.TearDownTestSuiteIfNeeded(TestInstance::TearDownTestSuite); |
| |
| framework.EndCurrentTest(); |
| } |
| |
| template <typename Expectation, typename Lhs, typename Rhs, typename Epsilon> |
| bool CurrentTestExpect(Expectation expectation, |
| const Lhs& lhs, |
| const Rhs& rhs, |
| const Epsilon& epsilon, |
| const char* expression, |
| int line) { |
| // Size of the buffer into which to write the string with the evaluated |
| // version of the arguments. This buffer is allocated on the unit test's |
| // stack, so it shouldn't be too large. |
| // TODO(hepler): Make this configurable. |
| [[maybe_unused]] constexpr size_t kExpectationBufferSizeBytes = 192; |
| |
| const bool success = expectation(lhs, rhs, epsilon); |
| CurrentTestExpectSimple( |
| expression, |
| #if PW_CXX_STANDARD_IS_SUPPORTED(17) |
| MakeString<kExpectationBufferSizeBytes>(ConvertForPrint(lhs), |
| " within ", |
| ConvertForPrint(epsilon), |
| " of ", |
| ConvertForPrint(rhs)) |
| .c_str(), |
| #else |
| "(evaluation requires C++17)", |
| #endif // PW_CXX_STANDARD_IS_SUPPORTED(17) |
| line, |
| success); |
| return success; |
| } |
| |
| // Runs an expectation function for the currently active test case. |
| template <typename Expectation, typename Lhs, typename Rhs> |
| bool CurrentTestExpect(Expectation expectation, |
| const Lhs& lhs, |
| const Rhs& rhs, |
| [[maybe_unused]] const char* expectation_string, |
| const char* expression, |
| int line) { |
| // Size of the buffer into which to write the string with the evaluated |
| // version of the arguments. This buffer is allocated on the unit test's |
| // stack, so it shouldn't be too large. |
| // TODO(hepler): Make this configurable. |
| [[maybe_unused]] constexpr size_t kExpectationBufferSizeBytes = 192; |
| |
| const bool success = expectation(lhs, rhs); |
| CurrentTestExpectSimple( |
| expression, |
| #if PW_CXX_STANDARD_IS_SUPPORTED(17) |
| MakeString<kExpectationBufferSizeBytes>(ConvertForPrint(lhs), |
| ' ', |
| expectation_string, |
| ' ', |
| ConvertForPrint(rhs)) |
| .c_str(), |
| #else |
| "(evaluation requires C++17)", |
| #endif // PW_CXX_STANDARD_IS_SUPPORTED(17) |
| line, |
| success); |
| return success; |
| } |
| |
| // Skips the current test and dispatches an event for it. |
| void CurrentTestSkip(int line); |
| |
| // Dispatches an event indicating the result of an expectation. |
| void CurrentTestExpectSimple(const char* expression, |
| const char* evaluated_expression, |
| int line, |
| bool success); |
| |
| private: |
| // Convert char* to void* so that they are printed as pointers instead of |
| // strings in EXPECT_EQ and other macros. EXPECT_STREQ wraps its pointers in a |
| // CStringArg so its pointers are treated like C strings. |
| static constexpr const void* ConvertForPrint(const char* str) { return str; } |
| |
| static constexpr const void* ConvertForPrint(char* str) { return str; } |
| |
| static constexpr const char* ConvertForPrint(CStringArg value) { |
| return value.c_str; |
| } |
| |
| template <typename T> |
| static constexpr T ConvertForPrint(T&& value) { |
| return std::forward<T>(value); |
| } |
| |
| // If current_test_ will be first of its suite, call set_up_ts |
| void SetUpTestSuiteIfNeeded(SetUpTestSuiteFunc set_up_ts) const; |
| |
| // If current_test_ was the last of its suite, call tear_down_ts |
| void TearDownTestSuiteIfNeeded(TearDownTestSuiteFunc tear_down_ts) const; |
| |
| // Sets current_test_ and dispatches an event indicating that a test started. |
| void StartTest(const TestInfo& test); |
| |
| // Dispatches event indicating that a test finished and clears current_test_. |
| void EndCurrentTest(); |
| |
| // Singleton instance of the framework class. |
| static Framework framework_; |
| |
| // Linked list of all registered test cases. This is static as it tests are |
| // registered using static initialization. |
| static TestInfo* tests_; |
| |
| // The current test case which is running. |
| const TestInfo* current_test_; |
| |
| // Overall result of the current test case (pass/fail/skip). |
| TestResult current_result_; |
| |
| // Overall result of the ongoing test run, which covers multiple tests. |
| RunTestsSummary run_tests_summary_; |
| |
| // Program exit status returned by RunAllTests for GoogleTest compatibility. |
| int exit_status_; |
| |
| // Handler to which to dispatch test events. |
| EventHandler* event_handler_; |
| |
| #if PW_CXX_STANDARD_IS_SUPPORTED(17) |
| span<std::string_view> test_suites_to_run_; |
| #else |
| span<const char*> test_suites_to_run_; // Always empty in C++14. |
| #endif // PW_CXX_STANDARD_IS_SUPPORTED(17) |
| |
| alignas(std::max_align_t) std::byte memory_pool_[config::kMemoryPoolSize]; |
| }; |
| |
| // Information about a single test case, including a pointer to a function which |
| // constructs and runs the test class. These are statically allocated instead of |
| // the test classes, as test classes can be very large. |
| class TestInfo { |
| public: |
| TestInfo(const char* const test_suite_name, |
| const char* const test_name, |
| const char* const file_name, |
| void (*run_func)(const TestInfo&)) |
| : test_case_{ |
| .suite_name = test_suite_name, |
| .test_name = test_name, |
| .file_name = file_name, |
| }, run_(run_func) { |
| Framework::Get().RegisterTest(this); |
| } |
| |
| // The name of the suite to which the test case belongs, the name of the test |
| // case itself, and the path to the file in which the test case is located. |
| const TestCase& test_case() const { return test_case_; } |
| |
| bool enabled() const; |
| |
| void run() const { run_(*this); } |
| |
| TestInfo* next() const { return next_; } |
| void set_next(TestInfo* next) { next_ = next; } |
| |
| private: |
| TestCase test_case_; |
| |
| // Function which runs the test case. Refers to Framework::CreateAndRunTest |
| // instantiated for the test case's class. |
| void (*run_)(const TestInfo&); |
| |
| // TestInfo structs are registered with the test framework and stored as a |
| // linked list. |
| TestInfo* next_ = nullptr; |
| }; |
| |
| // Base class for all test cases or custom test fixtures. |
| // Every unit test created using the TEST or TEST_F macro defines a class that |
| // inherits from this (or a subclass of this). |
| // |
| // For example, given the following test definition: |
| // |
| // TEST(MyTest, SaysHello) { |
| // ASSERT_STREQ(SayHello(), "Hello, world!"); |
| // } |
| // |
| // A new class is defined for the test, e.g. MyTest_SaysHello_Test. This class |
| // inherits from the Test class and implements its PigweedTestBody function with |
| // the block provided to the TEST macro. |
| class Test { |
| public: |
| Test(const Test&) = delete; |
| Test& operator=(const Test&) = delete; |
| |
| virtual ~Test() = default; |
| |
| static void SetUpTestSuite() {} |
| static void TearDownTestSuite() {} |
| |
| static bool HasFailure() { return Framework::Get().HasFailure(); } |
| |
| // Runs the unit test. |
| void PigweedTestRun() { |
| SetUp(); |
| // TODO(deymo): Skip the test body if there's a fatal error in SetUp(). |
| if (!Framework::Get().IsSkipped()) { |
| PigweedTestBody(); |
| } |
| TearDown(); |
| } |
| |
| protected: |
| Test() = default; |
| |
| // Called immediately before executing the test body. |
| // |
| // Setup and cleanup can typically be done in the test fixture's constructor |
| // and destructor, but there are cases where SetUp/TearDown must be used |
| // instead. See the Google Test documentation for more information. |
| virtual void SetUp() {} |
| |
| // Called immediately after executing the test body. |
| virtual void TearDown() {} |
| |
| private: |
| friend class internal::Framework; |
| |
| // The user-provided body of the test case. Populated by the TEST macro. |
| virtual void PigweedTestBody() = 0; |
| }; |
| |
| // Checks that a test suite name is valid. |
| constexpr bool HasNoUnderscores(const char* suite) { |
| const char* disabled_prefix = "DISABLED_"; |
| |
| for (; *suite != '\0'; ++suite) { |
| if (*suite == *disabled_prefix) { |
| disabled_prefix += 1; |
| } else { |
| disabled_prefix = ""; |
| if (*suite == '_') { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| // GoogleTest supports stream-style messages, but pw_unit_test does not. This |
| // class accepts and ignores C++ <<-style logs. This could be replaced with |
| // pw_log/glog_adapter.h. |
| class IgnoreLogs { |
| public: |
| constexpr IgnoreLogs() = default; |
| |
| template <typename T> |
| constexpr const IgnoreLogs& operator<<(const T&) const { |
| return *this; |
| } |
| }; |
| |
| // Used to ignore a stream-style message in an assert, which returns. This uses |
| // a similar approach as upstream GoogleTest, but drops any messages. |
| class ReturnHelper { |
| public: |
| constexpr ReturnHelper() = default; |
| |
| // Return void so that assigning to ReturnHelper converts the log expression |
| // to void without blocking the stream-style log with a closing parenthesis. |
| // NOLINTNEXTLINE(misc-unconventional-assign-operator) |
| constexpr void operator=(const IgnoreLogs&) const {} |
| }; |
| |
| #define _PW_UNIT_TEST_LOG \ |
| ::pw::unit_test::internal::ReturnHelper() = \ |
| ::pw::unit_test::internal::IgnoreLogs() |
| |
| } // namespace internal |
| |
| #if PW_CXX_STANDARD_IS_SUPPORTED(17) |
| inline void SetTestSuitesToRun(span<std::string_view> test_suites) { |
| internal::Framework::Get().SetTestSuitesToRun(test_suites); |
| } |
| #endif // PW_CXX_STANDARD_IS_SUPPORTED(17) |
| |
| } // namespace unit_test |
| } // namespace pw |
| |
| #define _PW_TEST(test_suite_name, test_name, parent_class) \ |
| static_assert(sizeof(#test_suite_name) > 1, \ |
| "The test suite name must not be empty"); \ |
| static_assert(::pw::unit_test::internal::HasNoUnderscores(#test_suite_name), \ |
| "The test suite name (" #test_suite_name \ |
| ") cannot contain underscores"); \ |
| static_assert(sizeof(#test_name) > 1, "The test name must not be empty"); \ |
| \ |
| _PW_TEST_CLASS(test_suite_name, \ |
| test_name, \ |
| test_suite_name##_##test_name##_Test, \ |
| parent_class) |
| |
| #define _PW_TEST_CLASS(suite, name, class_name, parent_class) \ |
| class class_name final : public parent_class { \ |
| private: \ |
| void PigweedTestBody() override; \ |
| }; \ |
| \ |
| extern "C" { \ |
| \ |
| /* Silence ASAN to avoid errors in the initialization order checker */ \ |
| /* caused by the intentional use of dynamic initializers which modify */ \ |
| /* other globals */ \ |
| PW_NO_SANITIZE("address") \ |
| /* Declare the TestInfo as non-const since const variables do not work */ \ |
| /* with the PW_UNIT_TEST_LINK_FILE_CONTAINING_TEST macro. */ \ |
| /* NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) */ \ |
| ::pw::unit_test::internal::TestInfo _pw_unit_test_Info_##suite##_##name( \ |
| #suite, \ |
| #name, \ |
| __FILE__, \ |
| ::pw::unit_test::internal::Framework::CreateAndRunTest<class_name>); \ |
| \ |
| } /* extern "C" */ \ |
| \ |
| void class_name::PigweedTestBody() |
| |
| #define _PW_TEST_ASSERT(expectation) \ |
| if (!(expectation)) \ |
| return _PW_UNIT_TEST_LOG |
| |
| #define _PW_TEST_EXPECT(expectation) \ |
| if (!(expectation)) \ |
| _PW_UNIT_TEST_LOG |
| |
| #define _PW_TEST_BOOL(expr, value) \ |
| ::pw::unit_test::internal::Framework::Get().CurrentTestExpect( \ |
| [](bool lhs, bool rhs) { return lhs == rhs; }, \ |
| static_cast<bool>(expr), \ |
| value, \ |
| "is", \ |
| #expr " is " #value, \ |
| __LINE__) |
| |
| #define _PW_TEST_OP(lhs, rhs, op) \ |
| ::pw::unit_test::internal::Framework::Get().CurrentTestExpect( \ |
| [](const auto& _pw_lhs, const auto& _pw_rhs) { \ |
| return _pw_lhs op _pw_rhs; \ |
| }, \ |
| (lhs), \ |
| (rhs), \ |
| #op, \ |
| #lhs " " #op " " #rhs, \ |
| __LINE__) |
| |
| #define _PW_TEST_NEAR(lhs, rhs, epsilon) \ |
| ::pw::unit_test::internal::Framework::Get().CurrentTestExpect( \ |
| [](const auto& _pw_lhs, const auto& _pw_rhs, const auto& _pw_epsilon) { \ |
| return std::abs(_pw_lhs - _pw_rhs) <= _pw_epsilon; \ |
| }, \ |
| (lhs), \ |
| (rhs), \ |
| (epsilon), \ |
| #lhs " within " #epsilon " of " #rhs, \ |
| __LINE__) |
| |
| #define _PW_TEST_C_STR(lhs, rhs, op) \ |
| ::pw::unit_test::internal::Framework::Get().CurrentTestExpect( \ |
| [](const auto& _pw_lhs, const auto& _pw_rhs) { \ |
| auto cmp = [](const char* l, const char* r) -> int { \ |
| if (!l || !r) { \ |
| return l != r; \ |
| } \ |
| return std::strcmp(l, r); \ |
| }; \ |
| return cmp(_pw_lhs.c_str, _pw_rhs.c_str) op 0; \ |
| }, \ |
| ::pw::unit_test::internal::CStringArg{lhs}, \ |
| ::pw::unit_test::internal::CStringArg{rhs}, \ |
| #op, \ |
| #lhs " " #op " " #rhs, \ |
| __LINE__) |
| |
| // Checks that test suite names between TEST and TEST_F declarations are unique. |
| // This works by declaring a function named for the test suite. The function |
| // takes no arguments but is declared with different return types in the TEST |
| // and TEST_F macros. If a TEST and TEST_F use the same test suite name, the |
| // function declarations conflict, resulting in a compilation error. |
| // |
| // This catches most conflicts, but a runtime check is ultimately needed since |
| // tests may be declared in different translation units. |
| #if !defined(__clang__) && !defined(__GNUC___) && __GNUC__ <= 8 |
| // For some reason GCC8 is unable to ignore -Wredundant-decls here. |
| #define _PW_TEST_SUITE_NAMES_MUST_BE_UNIQUE(return_type, test_suite) |
| #else // All other compilers. |
| #define _PW_TEST_SUITE_NAMES_MUST_BE_UNIQUE(return_type, test_suite) \ |
| PW_MODIFY_DIAGNOSTICS_PUSH(); \ |
| PW_MODIFY_DIAGNOSTIC(ignored, "-Wredundant-decls"); \ |
| extern "C" return_type /* use extern "C" to escape namespacing */ \ |
| PwUnitTestSuiteNamesMustBeUniqueBetweenTESTandTEST_F_##test_suite(void); \ |
| PW_MODIFY_DIAGNOSTICS_POP() |
| #endif // GCC8 or older. |
| |
| namespace testing { |
| |
| // Alias Test as ::testing::Test for GoogleTest compatibility. |
| using Test = ::pw::unit_test::internal::Test; |
| |
| // Provide a no-op init routine for GoogleTest compatibility. |
| inline void InitGoogleTest(int*, char**) {} |
| |
| } // namespace testing |