驱动程序单元测试快速入门

请按照以下快速入门,基于简单的单元测试代码示例编写驱动程序单元测试:

添加库依赖项

添加以下库依赖项:

#include <lib/driver/testing/cpp/fixtures/gtest_fixture.h>

DriverTestFixture 是驱动程序单元测试固件类可以从其继承的基类。测试会定义要通过模板参数传递到固件的配置类。夹具负责在正确的调度程序上设置测试环境和驱动程序,并根据请求启动和停止驱动程序。

创建夹具配置类

DriverTestFixture 基类通过模板参数接受配置类。必须为此配置类提供特定值,用于指示应如何运行测试。

下面是一个配置类的示例:

class FixtureConfig final {
 public:
  static constexpr bool kDriverOnForeground = true;
  static constexpr bool kAutoStartDriver = true;
  static constexpr bool kAutoStopDriver = true;

  using DriverType = simple::SimpleDriver;
  using EnvironmentType = SimpleDriverTestEnvironment;
};

定义环境类型类

EnvironmentType 必须是一个隔离类,用于提供驱动程序的自定义依赖项。它无需提供框架依赖项(compat::DeviceServer 除外),因为固件已提供该依赖项。如果不需要额外的依赖项,请使用提供默认 compat::DeviceServerfdf_testing::MinimalEnvironment

下面是一个使用极简环境的基本测试示例:

#include <lib/driver/testing/cpp/fixtures/gtest_fixture.h>

class FixtureConfig final {
 public:
  static constexpr bool kDriverOnForeground = true;
  static constexpr bool kAutoStartDriver = true;
  static constexpr bool kAutoStopDriver = true;

  using DriverType = MyDriverType;
  using EnvironmentType = fdf_testing::MinimalEnvironment;
};

class MyFixture : public fdf_testing::DriverTestFixture<FixtureConfiguration> {};

TEST_F(MyFixture, MyTest) {
  driver().DoSomething();
}

运行单元测试

驱动程序单元测试在驱动程序本身的测试文件夹中执行。例如,执行以下命令以运行 iwlwifi 驱动程序的驱动程序测试:

tools/bazel test third_party/iwlwifi/test:iwlwifi_test_pkg

DriverTestFixture 配置参数

DriverType

受测驱动程序的类型。此属性必须是 fdf::DriverBase 的继承者。

使用 DriverType 可仅为固件的函数(例如 driver()RunInDriverContext())定义引用类型。请使用驱动程序注册符号(由 FUCHSIA_DRIVER_EXPORT 宏创建)来管理驱动程序生命周期。使用自定义驱动程序类型时,请确保自定义 DriverType 包含一个公共静态函数,函数具有如下所示的签名:

static DriverRegistration GetDriverRegistration()

该测试改为使用此注册来管理驱动程序生命周期。

EnvironmentType

一个类,包含被测驱动程序的自定义依赖项。环境将始终位于后台调度程序中。

它必须是默认可构造的,派生自 fdf_testing::Environment class,并替换以下函数:zx::result<> Serve(fdf::OutgoingDirectory& to_driver_vfs) override;

初始化期间,系统会在后台环境调度程序上自动调用该函数。它必须将其组件添加到提供的 fdf::OutgoingDirectory object 中,通常通过 AddService 方法完成。OutgoingDirectory 会支持驱动程序的传入命名空间,因此其名称为 to_driver_vfs

自定义环境示例:

class MyFidlServer : public fidl::WireServer<fuchsia_examples_gizmo::Proto> {...};

class CustomEnvironment : public fdf_testing::Environment {
 public:
  zx::result<> Serve(fdf::OutgoingDirectory& to_driver_vfs) {
    device_server_.Init(component::kDefaultInstance, "root");
    EXPECT_EQ(ZX_OK, device_server_.Serve(
    fdf::Dispatcher::GetCurrent()->async_dispatcher(), &to_driver_vfs));

    EXPECT_EQ(ZX_OK, to_driver_vfs.AddService<fuchsia_examples_gizmo::Service::Proto>(
      custom_server_.CreateInstanceHandler()).status_value());

    return zx::ok();
  }

 private:
  compat::DeviceServer device_server_;
 MyFidlServer custom_server_;
};

kDriverOnForeground(首选 = true)

是让被测驱动程序在前台调度程序上运行,还是在专用的后台调度程序上运行。

为 true 时,测试可以使用 driver() 方法访问被测驱动程序,并直接调用该驱动程序,但同步客户端任务必须通过 RunInBackground() 执行。

如果该值为 false,测试可以使用 RunInDriverContext() 方法在驱动程序上下文上运行任务,但可以直接运行同步客户端任务。

kAutoStartDriver(首选 = true)

如果为 true,测试将在构建 DriverTestFixture 时自动启动驱动程序,并预计成功启动。

kAutoStopDriver(首选 = true)

如果为 true,测试将在 DriverTestFixture 销毁时自动停止驱动程序,并预计成功停止。

在所有配置下可用于测试的方法

以下方法在所有配置下可用于测试。

运行时

访问驱动程序运行时对象。 这可用于创建新的后台调度程序或运行前台调度程序。用户不需要为环境或驱动程序明确创建调度程序,因为夹具已经做到了这一点。

连接

连接到受测驱动程序提供的 Service 成员的实例。 这可以是驱动程序传输服务,也可以是基于锆石通道传输的服务。

ConnectThroughDevfs

连接到驱动程序通过 devfs 导出的协议。 该参数可以为 devfs 节点的 node_name,或是在到达 devfs 节点之前要遍历的节点名称列表。

RunInEnvironmentTypeContext

在测试正在使用的 EnvironmentType 实例上运行任务。

RunInNodeContext

在测试正在使用的 fdf_testing::TestNode 实例上运行任务。 这可用于验证驱动程序与驱动程序框架节点的交互(例如,检查添加了多少个子项)。

在基于配置的限制下可用于测试的方法

以下方法可用于测试,但存在某些基于配置的限制(在括号中)。

StartDriver 和 StartDriverCustomized (kAutoStartDriver = false)

这可用于手动启动被测驱动程序。 仅当 kAutoStartDriver 为 false 时才应使用。 自定义变体可用于修改为驱动程序提供的起始参数。

StopDriver (kAutoStopDriver = false)

这可用于手动停止被测驱动程序。 仅当 kAutoStopDriver 为 false 时才应使用。

RunInDriverContext (kDriverOnForeground = false)

这可用于在受测驱动程序上运行回调。回调输入将引用驱动程序。对驱动程序的所有访问都必须通过这种方法,因为在此配置下,在主线程测试线程上轻触驱动程序是不安全的。

驱动程序 (kDriverOnForeground = true)

这可用于直接从测试访问驱动程序。由于驱动程序位于前台,因此可以在主测试线程上安全地访问该文件。

RunInBackground (kDriverOnForeground = true)

在独立于驱动程序的后台调度程序上运行任务。 这样做是为了避免在使用 kDriverOnForeground 配置进行同步客户端调用时与驱动程序发生死锁。

Run* functions 警告

使用 Run* 函数(RunInDriverContextRunInBackgroundRunInEnvironmentTypeContextRunInNodeContext)时要小心。这些任务在特定调度程序上运行,因此执行以下操作可能不安全:

  • 将原始指针从要在函数中使用的另一个上下文(主线程或其他 Run* 种类)传递给它们
  • (通过捕获的引用或返回值类型)向它们返回一个原始指针,以便在主线程中使用或捕获/用于另一个 Run* 函数(同一类型的 Run* 函数除外)。

示例