Driver unit testing quick start

Once you are familiar with the Driver Framework v2 testing framework, follow this quick start to write a test for drivers that need to make synchronous FIDL (see driver FIDL test code). If your driver doesn't need to make synchronous FIDL calls, see the driver base test.

Include library dependencies

To test drivers, unit tests need to have access to the various resources and environments needed by the drivers themselves. For example, these are the library dependencies for the Driver FIDL test:

#include <fidl/fuchsia.driver.component.test/cpp/driver/wire.h>
#include <fidl/fuchsia.driver.component.test/cpp/wire.h>
#include <lib/async_patterns/testing/cpp/dispatcher_bound.h>
#include <lib/component/incoming/cpp/service.h>
#include <lib/driver/component/cpp/tests/test_driver.h>
#include <lib/driver/incoming/cpp/namespace.h>
#include <lib/driver/testing/cpp/driver_lifecycle.h>
#include <lib/driver/testing/cpp/driver_runtime.h>
#include <lib/driver/testing/cpp/test_environment.h>
#include <lib/driver/testing/cpp/test_node.h>
#include <lib/fdio/directory.h>
#include <gtest/gtest.h>

DispatcherBound is required to test how the driver uses FIDL clients when calling into its environment. It ensures a server-end object runs in a single dispatcher, enabling the test to construct, call methods on, and destroy the object used (see Threading tips in tests).

GTest Runner is a test runner that launches a gtest binary as a component, parses its output, and translates it to fuchsia.test.Suite protocol on behalf of the test.

Provide handler to easily add server bindings

It's good practice for servers to provide a GetInstanceHandler to easily add server bindings and run them off a binding group. The bindings should be added on the current driver dispatcher. The expectation is that this class is run inside of a dispatcher bound to the environment. For example, Driver FIDL test, line 35-52, provides a handler to the driver service:

class DriverProtocolServer : public fdf::WireServer<fuchsia_driver_component_test::DriverProtocol> {
 public:
  fuchsia_driver_component_test::DriverService::InstanceHandler GetInstanceHandler() {
    return fuchsia_driver_component_test::DriverService::InstanceHandler({
        .device = bindings_.CreateHandler(this, fdf::Dispatcher::GetCurrent()->get(),
                                          fidl::kIgnoreBindingClosure),
    });
  }

 private:
  // fdf::WireServer<fuchsia_driver_component_test::DriverProtocol>
  void DriverMethod(fdf::Arena& arena, DriverMethodCompleter::Sync& completer) override {
    fdf::Arena reply_arena('TEST');
    completer.buffer(reply_arena).ReplySuccess();
  }

  fdf::ServerBindingGroup<fuchsia_driver_component_test::DriverProtocol> bindings_;
};

Set up testing framework

Create driver runtime

Creating the driver runtime automatically attaches a foreground dispatcher, Driver FIDL test, line 115:

fdf_testing::DriverRuntime runtime_;

Start background dispatcher

The driver dispatcher is set as a background dispatcher, Driver FIDL test, line 118:

fdf::UnownedSynchronizedDispatcher env_dispatcher_ = runtime_.StartBackgroundDispatcher();

Create TestNode object

The test node serves the fdf::Node protocol to the driver, Driver FIDL test, lines 127-128:

async_patterns::TestDispatcherBound<fdf_testing::TestNode> node_server_{
    env_dispatcher(), std::in_place, std::string("root")};

Create TestEnvironment object

The environment can serve both the Zircon and Driver transport based protocols to the driver, Driver FIDL test, lines 131-132:

async_patterns::TestDispatcherBound<fdf_testing::TestEnvironment> test_environment_{
    env_dispatcher(), std::in_place};

Create custom FIDL server

The custom FIDL server lives on the background environment dispatcher and has to be wrapped in a dispatcher bound, Driver FIDL test, lines 121-124:

async_patterns::TestDispatcherBound<ZirconProtocolServer> zircon_proto_server_{env_dispatcher(),
                                                                               std::in_place};
async_patterns::TestDispatcherBound<DriverProtocolServer> driver_proto_server_{env_dispatcher(),
                                                                               std::in_place};

Get custom FIDL server handler

Get the instance handler for the driver protocol, Driver FIDL test, lines 71-74:

fuchsia_driver_component_test::ZirconService::InstanceHandler zircon_proto_handler =
    zircon_proto_server_.SyncCall(&ZirconProtocolServer::GetInstanceHandler);
fuchsia_driver_component_test::DriverService::InstanceHandler driver_proto_handler =
    driver_proto_server_.SyncCall(&DriverProtocolServer::GetInstanceHandler);

Move custom FIDL server handler

Move the instance handler into our driver's incoming namespace, Driver FIDL test, lines 76-87:

test_environment_.SyncCall([zircon_proto_handler = std::move(zircon_proto_handler),
                            driver_proto_handler = std::move(driver_proto_handler)](
                               fdf_testing::TestEnvironment* env) mutable {
  zx::result result =
      env->incoming_directory().AddService<fuchsia_driver_component_test::ZirconService>(
          std::move(zircon_proto_handler));
  ASSERT_EQ(ZX_OK, result.status_value());

  result = env->incoming_directory().AddService<fuchsia_driver_component_test::DriverService>(
      std::move(driver_proto_handler));
  ASSERT_EQ(ZX_OK, result.status_value());
});

Call CreateStartArgsAndServe

Create and serve the start_args table, Driver FIDL test, lines 59-60:

zx::result start_args = node_server_.SyncCall(&fdf_testing::TestNode::CreateStartArgsAndServe);
ASSERT_EQ(ZX_OK, start_args.status_value());

Initialize test environment

Initialize the test environment, Driver FIDL test, lines 65-68:

zx::result init_result =
    test_environment_.SyncCall(&fdf_testing::TestEnvironment::Initialize,
                               std::move(start_args->incoming_directory_server));
ASSERT_EQ(ZX_OK, init_result.status_value());

Run tests

Add the driver under test

Add the driver under test which will use the foreground dispatcher, Driver FIDL test, lines 167:

fdf_testing::DriverUnderTest<TestDriver> driver_;

Start driver

Start the driver, Driver FIDL test, lines 237-238:

zx::result result = runtime().RunToCompletion(driver_.SyncCall(
  &fdf_testing::DriverUnderTest<TestDriver>::Start, std::move(start_args())));

Add tests

Use the arrow operator on the DriverUnderTest to add tests for the driver. The arrow operator gives access to the driver type (specified in the DriverUnderTest template), for example, Driver FIDL test, lines 384-287:

driver().SyncCall([](fdf_testing::DriverUnderTest<TestDriver>* driver) {
  zx::result result = (*driver)->ServeDriverService();
  ASSERT_EQ(ZX_OK, result.status_value());
});

Call PrepareStop

PrepareStop has to be called manually by tests, Driver FIDL test, line 159:

zx::result result = runtime().RunToCompletion(driver_.PrepareStop());

Run unit tests

Execute the following command to run the driver tests (for the iwlwifi driver):

tools/bazel test third_party/iwlwifi/test:iwlwifi_test_pkg