请按照以下快速入门,基于简单的单元测试代码示例编写驱动程序单元测试:
添加库依赖项
添加以下库依赖项:
#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::DeviceServer
的 fdf_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* 函数(RunInDriverContext
、RunInBackground
、RunInEnvironmentTypeContext
、RunInNodeContext
)时要小心。这些任务在特定调度程序上运行,因此执行以下操作可能不安全:
- 将原始指针从要在函数中使用的另一个上下文(主线程或其他 Run* 种类)传递给它们
- (通过捕获的引用或返回值类型)向它们返回一个原始指针,以便在主线程中使用或捕获/用于另一个 Run* 函数(同一类型的 Run* 函数除外)。