pw_unit_test#

GoogleTest for embedded

Stable

#include "mylib.h"

#include "pw_unit_test/framework.h"

namespace {

TEST(MyTestSuite, MyTestCase) {
  pw::InlineString<32> expected = "(╯°□°)╯︵ ┻━┻";
  pw::InlineString<32> actual = mylib::flip_table();
  EXPECT_STREQ(expected.c_str(), actual.c_str());
}

}
load("@pigweed//pw_unit_test:pw_cc_test.bzl", "pw_cc_test")

cc_library(
    name = "mylib",
    srcs = ["mylib.cc"],
    hdrs = ["mylib.h"],
    includes = ["."],
    deps = ["@pigweed//pw_string"],
)

pw_cc_test(
    name = "mylib_test",
    srcs = ["mylib_test.cc"],
    deps = [
        "@pigweed//pw_unit_test",
        ":mylib",
    ],
)
#include "mylib.h"

#include "pw_string/string.h"

namespace mylib {

pw::InlineString<32> flip_table() {
  pw::InlineString<32> textmoji = "(╯°□°)╯︵ ┻━┻";
  return textmoji;
}

}
#include "pw_string/string.h"

namespace mylib {

pw::InlineString<32> flip_table();

}

pw_unit_test provides a GoogleTest-compatible unit testing framework for Pigweed-based projects. The default backend is a lightweight subset of GoogleTest that uses embedded-friendly primitives.

Quickstart

Set up your project for testing and learn testing basics.

Guides

Learn how to do common tasks.

C++ API reference

Get detailed C++ API reference information.

Bazel API reference

Get detailed Bazel API reference information.

GN API reference

Get detailed GN API reference information.

CMake API reference

Get detailed CMake API reference information.

Python API reference

Get detailed Python API reference information.

Quickstart#

Set up your build system#

Load the pw_cc_test rule and create a target that depends on @pigweed//pw_unit_test as well as the code you want to test:

load("@pigweed//pw_unit_test:pw_cc_test.bzl", "pw_cc_test")

cc_library(
    name = "mylib",
    srcs = ["mylib.cc"],
    hdrs = ["mylib.h"],
    includes = ["."],
    deps = ["..."],
)

pw_cc_test(
    name = "mylib_test",
    srcs = ["mylib_test.cc"],
    deps = [
        "@pigweed//pw_unit_test",
        ":mylib",
    ],
)

This assumes that your Bazel WORKSPACE has a repository named @pigweed that points to the upstream Pigweed repository.

See also Bazel API reference.

Import $dir_pw_unit_test/test.gni and create a pw_test rule that depends on the code you want to test:

import("$dir_pw_unit_test/test.gni")

pw_source_set("mylib") {
  sources = [ "mylib.cc" ]
}

pw_test("mylib_test") {
  sources = [ "mylib_test.cc" ]
  deps = [ ":mylib" ]
}

See GN reference for more information.

pw_unit_test generates a simple main function for running tests on host. See Create a custom main function to learn how to create your own main function for running on-device tests.

Write tests#

Create test suites and test cases:

#include "mylib.h"

#include "pw_unit_test/framework.h"

namespace {

TEST(MyTestSuite, MyTestCase) {
  pw::InlineString<32> expected = "(╯°□°)╯︵ ┻━┻";
  pw::InlineString<32> actual = app::flip_table();
  EXPECT_STREQ(expected.c_str(), actual.c_str());
}

}  // namespace

pw_unit_test provides a standard set of TEST, EXPECT, ASSERT and FAIL macros. The default backend, pw_unit_test:light, offers an embedded-friendly implementation which prioritizes small code size.

Alternativley, users can opt into a larger set of assertion macros, matchers, and more detailed error messages by using the pw_unit_test:googletest backend. See Choose a backend.

See GoogleTest Primer for the basics of using GoogleTest.

Run tests#

$ bazel test //src:mylib_test

Run the generated test binary:

$ ./out/.../mylib_test

Guides#

Choose a backend#

The default backend, pw_unit_test:light, is a lightweight subset of GoogleTest that uses embedded-friendly primitives. It’s also highly portable because it offloads the responsibility of test reporting and output to the underlying system, communicating its results through a common interface. This lets you write unit tests once and run them under many different environments.

If the subset of GoogleTest that pw_unit_test:light supports doesn’t meet your needs, you can access the full upstream GoogleTest API through pw_unit_test:googletest. See Use upstream GoogleTest.

Create a custom main function#

For simple unit tests that run on host the workflow outlined in Quickstart is all you need. Pigweed’s build templates generate a simple main function to run the tests with.

To do more complex testing, such as on-device testing:

  1. Create your own main function:

    #include "pw_unit_test/framework.h"
    // pw_unit_test:light requires an event handler to be configured.
    #include "pw_unit_test/simple_printing_event_handler.h"
    
    void WriteString(std::string_view string, bool newline) {
      printf("%s", string.data());
      if (newline) {
        printf("\n");
      }
    }
    
    int main() {
      // Make the binary compatible with pw_unit_test:googletest. Has no effect
      // when using pw_unit_test:light.
      testing::InitGoogleTest();
      // Set up the event handler for pw_unit_test:light.
      pw::unit_test::SimplePrintingEventHandler handler(WriteString);
      pw::unit_test::RegisterEventHandler(&handler);
      return RUN_ALL_TESTS();
    }
    

    See Create event handlers for more information about handling events.

  2. Set the build argument that instructs your build system to use your custom main function:

Create event handlers#

The pw::unit_test::EventHandler class defines the interface through which pw_unit_test:light communicates the results of its test runs. If you’re using a custom main function you need to register an event handler to receive test output from the framework.

Predefined event handlers#

Pigweed provides some standard event handlers to simplify the process of getting started with pw_unit_test:light. All event handlers provide for GoogleTest-style output using the shared pw::unit_test::GoogleTestStyleEventHandler base. Example output:

[==========] Running all tests.
[ RUN      ] Status.Default
[       OK ] Status.Default
[ RUN      ] Status.ConstructWithStatusCode
[       OK ] Status.ConstructWithStatusCode
[ RUN      ] Status.AssignFromStatusCode
[       OK ] Status.AssignFromStatusCode
[ RUN      ] Status.CompareToStatusCode
[       OK ] Status.CompareToStatusCode
[ RUN      ] Status.Ok_OkIsTrue
[       OK ] Status.Ok_OkIsTrue
[ RUN      ] Status.NotOk_OkIsFalse
[       OK ] Status.NotOk_OkIsFalse
[ RUN      ] Status.KnownString
[       OK ] Status.KnownString
[ RUN      ] Status.UnknownString
[       OK ] Status.UnknownString
[==========] Done running all tests.
[  PASSED  ] 8 test(s).

Run a subset of test suites#

Note

This feature is only supported in C++17.

To run only a subset of registered test suites, use the pw::unit_test::SetTestSuitesToRun function. See //pw_unit_test/light_public_overrides/pw_unit_test/framework_backend.h.

This is useful when you’ve got a lot of test suites bundled up into a single test binary and you only need to run some of them.

Skip tests in Bazel#

Use target_compatible_with in Bazel to skip tests. The following test is skipped when using upstream GoogleTest:

load("//pw_unit_test:pw_cc_test.bzl", "pw_cc_test")

pw_cc_test(
    name = "no_upstream_test",
    srcs = ["no_upstream_test.cc"],
     target_compatible_with = select({
         "//pw_unit_test:light_setting": [],
         "//conditions:default": ["@platforms//:incompatible"],
     }),
}

Run tests in static libraries#

To run tests in a static library, use the PW_UNIT_TEST_LINK_FILE_CONTAINING_TEST macro.

Linkers usually ignore tests through static libraries (i.e. .a files) because test registration relies on the test instance’s static constructor adding itself to a global list of tests. When linking against a static library, static constructors in an object file are ignored unless at least one entity in that object file is linked.

Use upstream GoogleTest#

To use the upstream GoogleTest backend (pw_unit_test:googletest) instead of the default backend:

  1. Clone the GoogleTest repository into your project. See GoogleTest.

  2. Create a custom main function.

  3. Combine GoogleTestHandlerAdapter with a predefined event handler to enable your main function to work with upstream GoogleTest without modification.

    #include "pw_unit_test/framework.h"
    #include "pw_unit_test/logging_event_handler.h"
    
    int main() {
      testing::InitGoogleTest();
      pw::unit_test::LoggingEventHandler logger;
      pw::unit_test::RegisterEventHandler(&logger);
      return RUN_ALL_TESTS();
    }
    
  4. If your tests needs GoogleTest functionality not included in the default pw_unit_test:light backend, include the upstream GoogleTest headers (e.g. gtest/gtest.h) directly and guard your target definition to avoid compiling with pw_unit_test:light (the default backend).

Run tests over serial#

To accelerate automated unit test bringup for devices with plain-text logging, pw_unit_test provides a serial-based test runner in Python that triggers a device flash and evaluates whether the test passed or failed based on the produced output.

To set up a serial test runner in Python:

  1. Implement a SerialTestingDevice class for your device. See //pw_unit_test/py/pw_unit_test/serial_test_runner.py.

  2. Configure your device code to wait to run unit tests until DEFAULT_TEST_START_CHARACTER is sent over the serial connection.

Run tests over RPC#

pw_unit_test provides an RPC service which runs unit tests on demand and streams the results back to the client. The service is defined in //pw_unit_test/pw_unit_test_proto/unit_test.proto.

The RPC service is primarily intended for use with the default pw_unit_test:light backend. It has some support for the upstream GoogleTest backend (pw_unit_test:googletest), however some features (such as test suite filtering) are missing.

To set up RPC-based unit tests in your application:

  1. Depend on the relevant target for your build system:

    • Bazel: @pigweed//pw_unit_test:rpc_service

    • GN: $dir_pw_unit_test:rpc_service

  2. Create a pw::unit_test::UnitTestService instance.

  3. Register the instance with your RPC server.

    #include "pw_rpc/server.h"
    #include "pw_unit_test/unit_test_service.h"
    
    pw::rpc::Channel channels[] = {
      pw::rpc::Channel::Create<1>(&my_output),
    };
    pw::rpc::Server server(channels);
    
    pw::unit_test::UnitTestService unit_test_service;
    
    void RegisterServices() {
      server.RegisterService(unit_test_services);
    }
    

    See pw_rpc for more guidance around setting up RPC.

  4. Run tests that have been flashed to a device by calling pw_unit_test.rpc.run_tests() in Python. The argument should be an RPC client services object that has the unit testing RPC service enabled. By default, the results output via logging. The return value is a TestRecord dataclass instance containing the results of the test run.

    import serial
    
    from pw_hdlc import rpc
    from pw_unit_test.rpc import run_tests
    
    PROTO = Path(
        os.environ['PW_ROOT'],
        'pw_unit_test/pw_unit_test_proto/unit_test.proto'
    )
    serial_device = serial.Serial(device, baud)
    with rpc.SerialReader(serial_device) as reader:
        with rpc.HdlcRpcClient(
            reader, PROTO, rpc.default_channels(serial_device.write)
        ) as client:
            run_tests(client.rpcs())
    

C++ API reference#

pw_status Helpers#

Both the light and GoogleTest backends of pw_unit_test expose some matchers for dealing with Pigweed pw::Status and pw::Result values. See Expectations and Assertions for details.

pw_unit_test:light API compatibility#

pw_unit_test:light offers a number of primitives for test declaration, assertion, event handlers, and configuration.

Note

The googletest_test_matchers target which provides Pigweed-specific StatusIs, IsOkAndHolds isn’t part of the pw_unit_test:light backend. These matchers are only usable when including the full upstream GoogleTest backend.

Missing features include:

  • GoogleMock and matchers (e.g. EXPECT_THAT).

  • Death tests (e.g. EXPECT_DEATH). EXPECT_DEATH_IF_SUPPORTED does nothing but silently passes.

  • Value-parameterized tests.

  • Stream messages (e.g. EXPECT_TRUE(...) << "My message") will compile, but no message will be logged.

See Use upstream GoogleTest for guidance on using the upstream GoogleTest backend (pw_unit_test:googletest) instead of pw_unit_test:light.

Test declaration#

Note that TEST_F may allocate fixtures separately from the stack. Large variables should be stored in test fixture fields, rather than stack variables. This allows the test framework to statically ensure that enough space is available to store these variables.

TEST(test_suite_name, test_name)#

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.

Parameters:
  • test_suite_name[in] The name of the test suite or collection of tests.

  • test_name[in] The name of the test case.

GTEST_TEST(test_suite_name, test_name)#

Alias for TEST.

TEST_F(test_fixture, test_name)#

Defines a test case using a test fixture.

Parameters:
  • test_fixture[in] The name of the test fixture class to use.

  • test_name[in] The name of the test case.

FRIEND_TEST(test_suite_name, test_name)#

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.

Parameters:
  • test_suite_name[in] The name of the test suite to befriend.

  • test_name[in] The name of the test case to befriend.

Test control#

inline int 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.

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.

GTEST_FAIL()#

Alias of FAIL.

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.

GTEST_SUCCEED()#

Alias of SUCCEED.

GTEST_SKIP()#

Skips test at runtime. Skips are neither successful nor failed. They abort the current function.

ADD_FAILURE()#

Generates a non-fatal failure with a generic message.

GTEST_HAS_DEATH_TEST#

Death tests are not supported. The *_DEATH_IF_SUPPORTED macros do nothing.

EXPECT_DEATH_IF_SUPPORTED(statement, regex)#

See GTEST_HAS_DEATH_TEST.

ASSERT_DEATH_IF_SUPPORTED(statement, regex)#

See GTEST_HAS_DEATH_TEST.

Expectations#

When a test fails an expectation, the framework marks the test as a failure and then continues executing the test. They’re useful when you want to verify multiple dimensions of the same feature and see all the errors at the same time.

EXPECT_TRUE(expr)#

Verifies that expr evaluates to true.

Parameters:
  • expr[in] The expression to evaluate.

EXPECT_FALSE(expr)#

Verifies that expr evaluates to false.

Parameters:
  • expr[in] The expression to evaluate.

EXPECT_EQ(lhs, rhs)#

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).

Parameters:
  • lhs[in] The left side of the equality comparison.

  • rhs[in] The right side of the equality comparison.

EXPECT_NE(lhs, rhs)#

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).

Parameters:
  • lhs[in] The left side of the inequality comparison.

  • rhs[in] The right side of the inequality comparison.

EXPECT_GT(lhs, rhs)#

Verifies that lhs > rhs.

Parameters:
  • lhs[in] The left side of the comparison.

  • rhs[in] The right side of the comparison.

EXPECT_GE(lhs, rhs)#

Verifies that lhs >= rhs.

Parameters:
  • lhs[in] The left side of the comparison.

  • rhs[in] The right side of the comparison.

EXPECT_LT(lhs, rhs)#

Verifies that lhs < rhs.

Parameters:
  • lhs[in] The left side of the comparison.

  • rhs[in] The right side of the comparison.

EXPECT_LE(lhs, rhs)#

Verifies that lhs <= rhs.

Parameters:
  • lhs[in] The left side of the comparison.

  • rhs[in] The right side of the comparison.

EXPECT_NEAR(lhs, rhs, epsilon)#

Verifies that the difference between lhs and rhs does not exceed the absolute error bound epsilon.

Parameters:
  • lhs[in] The left side of the comparison.

  • rhs[in] The right side of the comparison.

  • epsilon[in] The maximum difference between lhs and rhs.

EXPECT_FLOAT_EQ(lhs, rhs)#

Verifies that the two float values rhs and lhs are approximately equal, to within 4 units in the last place (ULPs) from each other.

Parameters:
  • lhs[in] The left side of the equality comparison.

  • rhs[in] The right side of the equality comparison.

EXPECT_DOUBLE_EQ(lhs, rhs)#

Verifies that the two double values rhs and lhs are approximately equal, to within 4 units in the last place (ULPs) from each other.

Parameters:
  • lhs[in] The left side of the equality comparison.

  • rhs[in] The right side of the equality comparison.

EXPECT_STREQ(lhs, rhs)#

Verifies that the two C strings lhs and rhs have the same contents.

Parameters:
  • lhs[in] The left side of the equality comparison.

  • [] – rhs The right side of the equality comparison.

EXPECT_STRNE(lhs, rhs)#

Verifies that the two C strings lhs and rhs have different content.

Parameters:
  • lhs[in] The left side of the inequality comparison.

  • rhs[in] The right side of the inequality comparison.

PW_TEST_EXPECT_OK(expr)#

Verifies that expr is OkStatus()

Converts expr to a Status value and checks that it is OkStatus().

Parameters:
  • expr[in] The expression to check.

Assertions#

Assertions work the same as expectations except they stop the execution of the test as soon as a failed condition is met.

ASSERT_TRUE(expr)#

See EXPECT_TRUE.

ASSERT_FALSE(expr)#

See EXPECT_FALSE.

ASSERT_EQ(lhs, rhs)#

See EXPECT_EQ.

ASSERT_NE(lhs, rhs)#

See EXPECT_NE.

ASSERT_GT(lhs, rhs)#

See EXPECT_GT.

ASSERT_GE(lhs, rhs)#

See EXPECT_GE.

ASSERT_LT(lhs, rhs)#

See EXPECT_LT.

ASSERT_LE(lhs, rhs)#

See EXPECT_LE.

ASSERT_NEAR(lhs, rhs, epsilon)#

See EXPECT_NEAR.

ASSERT_FLOAT_EQ(lhs, rhs)#

See EXPECT_FLOAT_EQ.

ASSERT_DOUBLE_EQ(lhs, rhs)#

See EXPECT_DOUBLE_EQ.

ASSERT_STREQ(lhs, rhs)#

See EXPECT_STREQ.

ASSERT_STRNE(lhs, rhs)#

See EXPECT_STRNE.

PW_TEST_ASSERT_OK(expr)#

See PW_TEST_EXPECT_OK.

PW_TEST_ASSERT_OK_AND_ASSIGN(lhs, rexpr)#

Executes an expression that returns a pw::Result or pw::StatusWithSize and assigns or moves that value to lhs if the error code is OK. If the status is non-OK, generates a test failure and returns from the current function, which must have a void return type.

Example: Declaring and initializing a new value. E.g.: PW_TEST_ASSERT_OK_AND_ASSIGN(auto value, MaybeGetValue(arg)); PW_TEST_ASSERT_OK_AND_ASSIGN(const ValueType value, MaybeGetValue(arg));

Example: Assigning to an existing value ValueType value; PW_TEST_ASSERT_OK_AND_ASSIGN(value, MaybeGetValue(arg));

The value assignment example would expand into something like: auto status_or_value = MaybeGetValue(arg); PW_TEST_ASSERT_OK(status_or_value.status()); value = status_or_value.ValueOrDie();

WARNING: PW_TEST_ASSERT_OK_AND_ASSIGN expand into multiple statements; it cannot be used in a single statement (e.g. as the body of an if statement without {})!

Parameters:
  • lhs[in] Variable to assign unwrapped value to

  • rexpr[in] Expression to unwrap. Must be a Result or StatusWithSize.

Event handlers#

void pw::unit_test::RegisterEventHandler(EventHandler *event_handler)#

Sets the event handler for a test run. Must be called before RUN_ALL_TESTS() to receive test output. Set event_handler to null to disable event handling.

Warning

This method is not thread-safe.

class EventHandler#

Collects and processes the results of a unit test run. Its interface is called by the unit test framework as tests are executed and various test events occur.

A program wanting to process test events must define a class implementing the pw::unit_test::EventHandler interface and register it with the framework. When RUN_ALL_TESTS() is called, pw_unit_test notifies the handler of various events which occur in the test process. For example, consider a file containing the following test definitions:

TEST(MyTestSuite, MyFirstCase) {
  EXPECT_TRUE(true);
}

TEST(MyTestSuite, MySecondCase) {
  EXPECT_TRUE(false);
}

There’s one test suite consisting of two test cases. When pw_unit_test starts running the first test case (MyFirstCase), it dispatches a TestCaseStart event to the event handler. It then runs the body of the test, sequentially checking each expectation within. After each expectation, a TestCaseExpect event is sent to the event handler with the expectation’s result. In this case, there’s only one, which passes successfully. Finally, after the test is finished, a TestCaseEnd event is dispatched with the overall result of the test case. pw_unit_test then runs MySecondCase in the same way.

Subclassed by pw::unit_test::GoogleTestStyleEventHandler, pw::unit_test::LoggingEventHandler, pw::unit_test::MultiEventHandler< kNumHandlers >, pw::unit_test::TestRecordEventHandler

Public Functions

virtual void TestProgramStart(const ProgramSummary &program_summary) = 0#

Called before any test activity starts.

virtual void EnvironmentsSetUpEnd() = 0#

Called after environment setup for each iteration of tests ends.

virtual void TestSuiteStart(const TestSuite &test_suite) = 0#

Called before the test suite starts.

virtual void TestSuiteEnd(const TestSuite &test_suite) = 0#

Called after the test suite ends.

virtual void EnvironmentsTearDownEnd() = 0#

Called after environment teardown for each iteration of tests ends.

virtual void TestProgramEnd(const ProgramSummary &program_summary) = 0#

Called after all test activities have ended.

virtual void RunAllTestsStart() = 0#

Called before all tests are run.

virtual void RunAllTestsEnd(const RunTestsSummary &run_tests_summary) = 0#

Called after all tests are run.

virtual void TestCaseStart(const TestCase &test_case) = 0#

Called when a new test case is started.

virtual void TestCaseEnd(const TestCase &test_case, TestResult result) = 0#

Called when a test case completes. The overall result of the test case is provided.

inline virtual void TestCaseDisabled(const TestCase&)#

Called when a disabled test case is encountered.

virtual void TestCaseExpect(const TestCase &test_case, const TestExpectation &expectation) = 0#

Called after each expect or assert statement within a test case with the result.

class GoogleTestHandlerAdapter : public testing::EmptyTestEventListener#

Adapts a custom main() function to work with upstream GoogleTest without modification. Custom main() functions are used for complex testing scenarios, such as on-device testing. Must be paired with a predefined event handler, such as pw::unit_test::GoogleTestStyleEventHandler. See pw::unit_test::EventHandler for an explanation of each event.

class GoogleTestStyleEventHandler : public pw::unit_test::EventHandler#

Provides GoogleTest-style output for pw_unit_test:light events. Must be extended to define how to output the results. See pw::unit_test::EventHandler for an explanation of each event and pw::unit_test::SimplePrintingEventHandler for an example of a concrete implementation of this interface.

Subclassed by pw::unit_test::PrintfEventHandler, pw::unit_test::SimplePrintingEventHandler

class SimplePrintingEventHandler : public pw::unit_test::GoogleTestStyleEventHandler#

Predefined event handler implementation that produces human-readable GoogleTest-style test output and sends it to a sink that you define. See pw::unit_test::EventHandler for explanations of emitted events.

Example:

#include "pw_unit_test/framework.h"
// pw_unit_test:light requires an event handler to be configured.
#include "pw_unit_test/simple_printing_event_handler.h"

void WriteString(std::string_view string, bool newline) {
  printf("%s", string.data());
  if (newline) {
    printf("\n");
  }
}

int main() {
  // The following line has no effect with pw_unit_test_light, but makes
  // this test compatible with upstream GoogleTest.
  testing::InitGoogleTest();
  // Since we are using pw_unit_test:light, set up an event handler.
  pw::unit_test::SimplePrintingEventHandler handler(WriteString);
  pw::unit_test::RegisterEventHandler(&handler);
  return RUN_ALL_TESTS();
}

Example output:

>>> Running MyTestSuite.TestCase1
[SUCCESS] 128 <= 129
[FAILURE] 'a' == 'b'
  at ../path/to/my/file_test.cc:4831
<<< Test MyTestSuite.TestCase1 failed
class LoggingEventHandler : public pw::unit_test::EventHandler#

Predefined event handler implementation that produces human-readable GoogleTest-style test output and logs it via pw_log. See pw::unit_test::EventHandler for explanations of emitted events.

class PrintfEventHandler : public pw::unit_test::GoogleTestStyleEventHandler#

Predefined event handler implementation that produces human-readable GoogleTest-style test output and logs it via std::printf. See pw::unit_test::EventHandler for explanations of emitted events.

template<size_t kNumHandlers>
class MultiEventHandler : public pw::unit_test::EventHandler#

Event handler adapter that allows for multiple event handlers to be registered and used during test runs.

class TestRecordEventHandler : public pw::unit_test::EventHandler#

Predefined event handler implementation that outputs a test record (or summary) in Chromium JSON Test Results Format. To use it, register the event handler, call the RUN_ALL_TESTS macro, then extract the test record json as a string using the GetTestRecordJsonString method. If you only want to extract the failing tests, set the failing_results_only parameter to true. See pw::unit_test::EventHandler for explanations of emitted events.

Warning

This event handler uses dynamic allocation (new/delete/std::string) to generate the test record json.

Configuration#

PW_UNIT_TEST_CONFIG_EVENT_BUFFER_SIZE#

The size of the event buffer that the UnitTestService contains. This buffer is used to encode events. By default this is set to 128 bytes.

PW_UNIT_TEST_CONFIG_MEMORY_POOL_SIZE#

The size of the memory pool to use for test fixture instances. By default this is set to 16K.

Helpers#

Ensures tests in a static library are linked and executed. Provide the test suite name and test name for one test in the file linked into a static library. Any test in the file may be used, but it’s recommended to use the first for consistency. The test must be in a static library that’s a dependency of this target. Referring to a test that does not exist causes a linker error.

The linker usually ignores tests linked through a static library because test registration relies on the test instance’s static constructor adding itself to a global list of tests. When linking against a static library, static constructors in an object file will be ignored unless at least one entity in that object file is linked.

This macro works by passing the internal TestInfo instance to a constructor defined in a source file. This guarantees that the TestInfo instance is referenced, so the linker will link it and the other tests in that file.

Constexpr unit tests#

The PW_CONSTEXPR_TEST macro defines a test that is executed both at compile time in a static_assert and as a regular GoogleTest-style TEST(). This offers the advantages of compile-time testing in a structured, familiar API, without sacrificing anything from GoogleTest-style tests. The framework uses the standard GoogleTest macros at run time, and is compatible with GoogleTest or Pigweed’s pw_unit_test:light framework.

To create a constexpr test:

  • Include "pw_unit_test/constexpr.h" alongside the test framework ("pw_unit_test/framework.h" or "gtest/gtest.h").

  • Use the macro PW_CONSTEXPR_TEST instead of TEST. Note that the function body passed as the third argument to the macro.

  • Use the familiar GoogleTest macros, but with a PW_TEST_ prefix. For example:

    • EXPECT_TRUE PW_TEST_EXPECT_TRUE

    • EXPECT_EQ PW_TEST_EXPECT_EQ

    • ASSERT_STREQ PW_TEST_ASSERT_STREQ

    • etc.

The result is a familiar-looking unit test that executes both at compile time and run time.

constexpr int ComputeSum(int lhs, int rhs) { return lhs + rhs; }

PW_CONSTEXPR_TEST(PwConstexprTestExample, AddNumbersOverflow, {
  // Use PW_TEST_EXPECT/ASSERT macros like regular GoogleTest macros.
  PW_TEST_EXPECT_EQ(ComputeSum(1, -2), -1);
  PW_TEST_EXPECT_LT(ComputeSum(1, 1), ComputeSum(2, 2));

  PW_TEST_ASSERT_EQ(ComputeSum(0, 0), 0);
  PW_TEST_EXPECT_EQ(ComputeSum(-123, 0), -123) << "Additive identity";
});

Why should I run tests at compile time?

  • Cross compile and execute tests without having to flash them to a device.

  • Ensure constexpr functions can actually be evaluated at compile time. For example, function templates may be marked as constexpr, even if they do not support constant evaluation when instantiated.

  • Catch undefined behavior, out-of-bounds access, and other issues during compilation on any platform, without needing to run sanitizers.

If compile-time testing is so great, why execute the tests are run time at all?

  • Code may run differently at compile time and execution, particularly when std::is_constant_evaluated or if consteval are used.

  • Error messages are much better at runtime. PW_CONSTEXPR_TEST makes it simple to temporarily disable compile-time tests and see the rich GoogleTest-like output (see SKIP_CONSTEXPR_TESTS_DONT_SUBMIT ).

  • Tools like code coverage only work for code that is executed normally.

PW_CONSTEXPR_TEST uses stdcompat’s cpp20::is_constant_evaluated(). If the compiler does not support is_constant_evaluated, only the regular GoogleTest version will run. Note that compiler support is independent of the C++ standard in use.

API reference#

PW_CONSTEXPR_TEST(test_suite, test_name, ...)#

Defines a test that is executed both at compile time in a static_assert and as a regular GoogleTest-style TEST().

PW_CONSTEXPR_TEST works similarly to the GoogleTest TEST() macro, but has some differences.

  • All tested code must be constexpr.

  • Requires the PW_TEST_* prefixed versions of GoogleTest’s EXPECT_* and ASSERT_* macros.

  • The function body is a macro argument. This has two implications:

    • The function body cannot contain preprocessor directives, such as #define. If these are needed, move them to a separate function that is called from the test.

    • The PW_CONSTEXPR_TEST macro must be terminated with ); after the test body argument.

Parameters:
  • test_suite – GoogleTest test suite name

  • test_name – GoogleTest test name

  • ... – test function body surrounded by { }.

SKIP_CONSTEXPR_TESTS_DONT_SUBMIT#

Define the SKIP_CONSTEXPR_TESTS_DONT_SUBMIT macro to temporarily disable the constexpr portion of subsequent PW_CONSTEXPR_TESTs. Use this to view GoogleTest output, which is usually more informative than the compiler’s constexpr test failure output.

Defines of this macro should never be submitted. If a test shouldn’t run at compile time, use a plain TEST().

// Subsequent PW_CONSTEXPR_TESTs will skip the constexpr portion of the test.
// This allows you to use the richer GoogleTest-style output to debug failures.
#define SKIP_CONSTEXPR_TESTS_DONT_SUBMIT

void NotConstexpr() {}

PW_CONSTEXPR_TEST(PwConstexprTest, NotConstexprButDisabledByMacro, {
  // This test is not constexpr, but the constexpr test is skipped because the
  // SKIP_CONSTEXPR_TESTS macro is defined.
  NotConstexpr();
  PW_TEST_EXPECT_TRUE(true);
});

// Now, constexpr tests will no longer be skipped, and the same test will fail.
#undef SKIP_CONSTEXPR_TESTS_DONT_SUBMIT

Python API reference#

pw_unit_test.serial_test_runner#

Facilitates automating unit tests on devices with serial ports.

This library assumes that the on-device test runner emits the test results as plain-text over a serial port, and that tests are triggered by a pre-defined input (DEFAULT_TEST_START_CHARACTER) over the same serial port that results are emitted from.

class pw_unit_test.serial_test_runner.SerialTestingDevice#

A device that supports automated testing via parsing serial output.

abstract baud_rate() int#

Returns the baud rate to use when connecting to this device.

Raises:

DeviceNotFound – This device is no longer available.

abstract load_binary(binary: Path) bool#

Flashes the specified binary to this device.

Raises:
  • DeviceNotFound – This device is no longer available.

  • FlashingFailure – The binary could not be flashed.

Returns:

True if the binary was loaded successfully.

abstract serial_port() str#

Returns the name of the com port this device is enumerated on.

Raises:

DeviceNotFound – This device is no longer available.

pw_unit_test.serial_test_runner.run_device_test(
device: ~pw_unit_test.serial_test_runner.SerialTestingDevice,
binary: ~pathlib.Path,
test_timeout: float,
logger: ~logging.Logger = <Logger serial_test_runner (WARNING)>,
) bool#

Runs tests on a device.

When a unit test run fails, results are logged as an error.

Parameters:
  • device – The device to run tests on.

  • binary – The binary containing tests to flash on the device.

  • test_timeout – If the device stops producing output longer than this timeout, the test is considered stuck and is aborted.

Returns:

True if all tests passed.

pw_unit_test.rpc#

Utilities for running unit tests over pw_rpc.

class pw_unit_test.rpc.EventHandler#
abstract run_all_tests_end(passed_tests: int, failed_tests: int) None#

Called after the test run is complete.

abstract run_all_tests_start() None#

Called before all tests are run.

abstract test_case_disabled(test_case: TestCase) None#

Called when a disabled test case is encountered.

abstract test_case_end(test_case: TestCase, result: TestCaseResult) None#

Called when a test case completes with its overall result.

abstract test_case_expect(test_case: TestCase, expectation: TestExpectation) None#

Called after each expect or assert statement within a test case.

abstract test_case_start(test_case: TestCase) None#

Called when a new test case is started.

class pw_unit_test.rpc.TestRecord(
passing_tests: tuple[TestCase, ...],
failing_tests: tuple[TestCase, ...],
disabled_tests: tuple[TestCase, ...],
)#

Records test results.

__init__(
passing_tests: tuple[TestCase, ...],
failing_tests: tuple[TestCase, ...],
disabled_tests: tuple[TestCase, ...],
) None#
pw_unit_test.rpc.run_tests(
rpcs: ~pw_rpc.client.Services,
report_passed_expectations: bool = False,
test_suites: ~typing.Iterable[str] = (),
event_handlers: ~typing.Iterable[~pw_unit_test.rpc.EventHandler] = (<pw_unit_test.rpc.LoggingEventHandler object>,
),
timeout_s: ~pw_rpc.callback_client.call.UseDefault | float | None = UseDefault.VALUE,
) TestRecord#

Runs unit tests on a device over pw_rpc.

Calls each of the provided event handlers as test events occur, and returns True if all tests pass.

Build helper libraries#

The following helper libraries can simplify setup and are supported in all build systems.

simple_printing_event_handler

When running tests, output test results as plain text over pw_sys_io.

simple_printing_main

Implements a main() function that simply runs tests using the simple_printing_event_handler.

logging_event_handler

When running tests, log test results as plain text using pw_log. Make sure your target has set a pw_log backend.

logging_main

Implements a main() function that simply runs tests using the logging_event_handler.

Bazel API reference#

See also Build helper libraries.

pw_cc_test#

pw_cc_test is a wrapper for cc_test that provides some defaults, such as a dependency on @pigweed//pw_unit_test:main. It supports and passes through all the arguments recognized by cc_test.

Bazel build arguments#

pw_unit_test_backend <target>#

The GoogleTest implementation to use for Pigweed unit tests. This library provides gtest/gtest.h and related headers. Defaults to @pigweed//pw_unit_test:light, which implements a subset of GoogleTest.

Type: string (Bazel target label)

Usage: toolchain-controlled only

pw_unit_test_main <target>#

Implementation of a main function for pw_cc_test unit test binaries.

Type: string (Bazel target label)

Usage: toolchain-controlled only

GN reference#

See also Build helper libraries.

pw_test#

pw_test defines a single unit test suite.

Targets#

<target_name>

The test suite within a single binary. The test code is linked against the target set in the build arg pw_unit_test_MAIN.

<target_name>.run

If pw_unit_test_AUTOMATIC_RUNNER is set, this target runs the test as part of the build.

<target_name>.lib

The test sources without pw_unit_test_MAIN.

Arguments#

All GN executable arguments are accepted and forwarded to the underlying pw_executable.

enable_if#

Boolean indicating whether the test should be built. If false, replaces the test with an empty target. Default true.

source_gen_deps#

List of target labels that generate source files used by this test. The labels must meet the constraints of GN’s get_target_outputs, namely they must have been previously defined in the current file. This argument is required if a test uses generated source files and enable_if can evaluate to false.

test_main#

Target label to add to the tests’s dependencies to provide the main() function. Defaults to pw_unit_test_MAIN. Set to "" if main() is implemented in the test’s sources.

test_automatic_runner_args#

Array of args to pass to automatic test runner. Defaults to pw_unit_test_AUTOMATIC_RUNNER_ARGS.

envvars#

Array of key=value strings representing environment variables to set when invoking the automatic test runner.

Example#

import("$dir_pw_unit_test/test.gni")

pw_test("large_test") {
  sources = [ "large_test.cc" ]
  enable_if = device_has_1m_flash
}

pw_test_group#

pw_test_group defines a collection of tests or other test groups.

Targets#

<target_name>

The test group itself.

<target_name>.run

If pw_unit_test_AUTOMATIC_RUNNER is set, this target runs all of the tests in the group and all of its group dependencies individually. See Run tests over serial.

<target_name>.lib

The sources of all of the tests in this group and their dependencies.

<target_name>.bundle

All of the tests in the group and its dependencies bundled into a single binary.

<target_name>.bundle.run

Automatic runner for the test bundle.

Arguments#

tests#

List of the pw_test targets in the group.

group_deps#

List of other pw_test_group targets on which this one depends.

enable_if#

Boolean indicating whether the group target should be created. If false, an empty GN group is created instead. Default true.

Example#

import("$dir_pw_unit_test/test.gni")

pw_test_group("tests") {
  tests = [
    ":bar_test",
    ":foo_test",
  ]
}

pw_test("foo_test") {
  # ...
}

pw_test("bar_test") {
  # ...
}

pw_facade_test#

Pigweed facade test templates allow individual unit tests to build under the current device target configuration while overriding specific build arguments. This allows these tests to replace a facade’s backend for the purpose of testing the facade layer.

Facade tests are disabled by default. To build and run facade tests, set the GN arg pw_unit_test_FACADE_TESTS_ENABLED to true.

Warning

Facade tests are costly because each facade test will trigger a re-build of every dependency of the test. While this sounds excessive, it’s the only technically correct way to handle this type of test.

Warning

Some facade test configurations may not be compatible with your target. Be careful when running a facade test on a system that heavily depends on the facade being tested.

GN build arguments#

pw_unit_test_BACKEND <source_set>#

The GoogleTest implementation to use for Pigweed unit tests. This library provides gtest/gtest.h and related headers. Defaults to pw_unit_test:light, which implements a subset of GoogleTest.

Type: string (GN path to a source set)

Usage: toolchain-controlled only

pw_unit_test_MAIN <source_set>#

Implementation of a main function for pw_test unit test binaries. See Create a custom main function.

Type: string (GN path to a source set)

Usage: toolchain-controlled only

pw_unit_test_AUTOMATIC_RUNNER <executable>#

Path to a test runner to automatically run unit tests after they are built. See Run tests over serial.

If set, a pw_test target’s <target_name>.run action invokes the test runner specified by this argument, passing the path to the unit test to run. If this is unset, the pw_test target’s <target_name>.run step will do nothing.

Targets that don’t support parallelized execution of tests (e.g. an on-device test runner that must flash a device and run the test in serial) should set pw_unit_test_POOL_DEPTH to 1.

Type: string (name of an executable on PATH, or a path to an executable)

Usage: toolchain-controlled only

pw_unit_test_AUTOMATIC_RUNNER_ARGS <args>#

An optional list of strings to pass as args to the test runner specified by pw_unit_test_AUTOMATIC_RUNNER.

Type: list of strings (args to pass to pw_unit_test_AUTOMATIC_RUNNER)

Usage: toolchain-controlled only

pw_unit_test_AUTOMATIC_RUNNER_TIMEOUT <timeout_seconds>#

An optional timeout to apply when running the automatic runner. Timeout is in seconds. Defaults to empty which means no timeout.

Type: string (number of seconds to wait before killing test runner)

Usage: toolchain-controlled only

pw_unit_test_POOL_DEPTH <pool_depth>#

The maximum number of unit tests that may be run concurrently for the current toolchain. Setting this to 0 disables usage of a pool, allowing unlimited parallelization.

Note: A single target with two toolchain configurations (e.g. release and debug) uses two separate test runner pools by default. Set pw_unit_test_POOL_TOOLCHAIN to the same toolchain for both targets to merge the pools and force serialization.

Type: integer

Usage: toolchain-controlled only

pw_unit_test_POOL_TOOLCHAIN <toolchain>#

The toolchain to use when referring to the pw_unit_test runner pool. When this is disabled, the current toolchain is used. This means that every toolchain will use its own pool definition. If two toolchains should share the same pool, this argument should be by one of the toolchains to the GN path of the other toolchain.

Type: string (GN path to a toolchain)

Usage: toolchain-controlled only

pw_unit_test_EXECUTABLE_TARGET_TYPE <template name>#

The name of the GN target type used to build pw_unit_test executables.

Type: string (name of a GN template)

Usage: toolchain-controlled only

pw_unit_test_EXECUTABLE_TARGET_TYPE_FILE <gni file path>#

The path to the .gni file that defines pw_unit_test_EXECUTABLE_TARGET_TYPE.

If pw_unit_test_EXECUTABLE_TARGET_TYPE is not the default of pw_executable, this .gni file is imported to provide the template definition.

Type: string (path to a .gni file)

Usage: toolchain-controlled only

pw_unit_test_FACADE_TESTS_ENABLED <boolean>#

Controls whether to build and run facade tests. Facade tests add considerably to build time, so they are disabled by default.

pw_unit_test_TESTONLY <boolean>#

Controls the testonly variable in pw_test, pw_test_group, and miscellaneous testing targets. This is useful if your test libraries (e.g. GoogleTest) used by pw_unit_test have the testonly flag set. False by default for backwards compatibility.

CMake reference#

See also Build helper libraries.

pw_add_test#

pw_add_test declares a single unit test suite.

Tip

Upstream Pigweed tests can be disabled in downstream projects by setting pw_unit_test_ENABLE_PW_ADD_TEST to OFF before adding the pigweed directory to an existing cmake build.

set(pw_unit_test_ENABLE_PW_ADD_TEST OFF)
add_subdirectory(path/to/pigweed pigweed)
set(pw_unit_test_ENABLE_PW_ADD_TEST ON)

See also: Use Pigweed from an existing CMake project.

Targets#

{NAME}

Depends on ${NAME}.run if pw_unit_test_AUTOMATIC_RUNNER is set, else it depends on ${NAME}.bin.

{NAME}.lib

Contains the provided test sources as a library target, which can then be linked into a test executable.

{NAME}.bin

A standalone executable which contains only the test sources specified in the pw_unit_test template.

{NAME}.run

Runs the unit test executable after building it if pw_unit_test_AUTOMATIC_RUNNER is set, else it fails to build.

Required arguments#

NAME#

Name to use for the produced test targets specified above.

Optional arguments#

SOURCES#

Source files for this library.

HEADERS#

Header files for this library.

PRIVATE_DEPS#

Private pw_target_link_targets arguments.

PRIVATE_INCLUDES#

Public target_include_directories argument.

PRIVATE_DEFINES#

Private target_compile_definitions arguments.

PRIVATE_COMPILE_OPTIONS#

Private target_compile_options arguments.

Private target_link_options arguments.

Example#

include($ENV{PW_ROOT}/pw_unit_test/test.cmake)

pw_add_test(my_module.foo_test
  SOURCES
    foo_test.cc
  PRIVATE_DEPS
    my_module.foo
)

pw_add_test_group#

pw_add_test_group defines a collection of tests or other test groups.

Targets#

{NAME}

Depends on ${NAME}.run if pw_unit_test_AUTOMATIC_RUNNER is set, else it depends on ${NAME}.bin.

{NAME}.bundle

Depends on ${NAME}.bundle.run if pw_unit_test_AUTOMATIC_RUNNER is set, else it depends on ${NAME}.bundle.bin.

{NAME}.lib

Depends on ${NAME}.bundle.lib.

{NAME}.bin

Depends on the provided TESTS’s <test_dep>.bin targets.

{NAME}.run

Depends on the provided TESTS’s <test_dep>.run targets if pw_unit_test_AUTOMATIC_RUNNER is set, else it fails to build.

{NAME}.bundle.lib

Contains the provided tests bundled as a library target, which can then be linked into a test executable.

{NAME}.bundle.bin

Standalone executable which contains the bundled tests.

{NAME}.bundle.run

Runs the {NAME}.bundle.bin test bundle executable after building it if pw_unit_test_AUTOMATIC_RUNNER is set, else it fails to build.

Required arguments#

NAME#

The name of the executable target to be created.

TESTS#

pw_add_test targets and pw_add_test_group bundles to be included in this test bundle.

Example#

include($ENV{PW_ROOT}/pw_unit_test/test.cmake)

pw_add_test_group(tests
  TESTS
    bar_test
    foo_test
)

pw_add_test(foo_test
  # ...
)

pw_add_test(bar_test
  # ...
)

CMake build arguments#

pw_unit_test_BACKEND <target>#

The GoogleTest implementation to use for Pigweed unit tests. This library provides gtest/gtest.h and related headers. Defaults to pw_unit_test.light, which implements a subset of GoogleTest.

Type: string (CMake target name)

Usage: toolchain-controlled only

pw_unit_test_AUTOMATIC_RUNNER <executable>#

Path to a test runner to automatically run unit tests after they’re built.

If set, a pw_test target’s ${NAME} and ${NAME}.run targets will invoke the test runner specified by this argument, passing the path to the unit test to run. If this is unset, the pw_test target’s ${NAME} will only build the unit test(s) and ${NAME}.run will fail to build.

Type: string (name of an executable on the PATH, or path to an executable)

Usage: toolchain-controlled only

pw_unit_test_AUTOMATIC_RUNNER_ARGS <args>#

An optional list of strings to pass as args to the test runner specified by pw_unit_test_AUTOMATIC_RUNNER.

Type: list of strings (args to pass to pw_unit_test_AUTOMATIC_RUNNER)

Usage: toolchain-controlled only

pw_unit_test_AUTOMATIC_RUNNER_TIMEOUT_SECONDS <timeout_seconds>#

An optional timeout to apply when running the automatic runner. Timeout is in seconds. Defaults to empty which means no timeout.

Type: string (number of seconds to wait before killing test runner)

Usage: toolchain-controlled only

pw_unit_test_ADD_EXECUTABLE_FUNCTION <function name>#

The name of the CMake function used to build pw_unit_test executables. The provided function must take a NAME and a TEST_LIB argument which are the expected name of the executable target and the target which provides the unit test(s).

Type: string (name of a CMake function)

Usage: toolchain-controlled only

pw_unit_test_ADD_EXECUTABLE_FUNCTION_FILE <cmake file path>#

The path to the .cmake file that defines pw_unit_test_ADD_EXECUTABLE_FUNCTION.

Type: string (path to a .cmake file)

Usage: toolchain-controlled only