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

请按照此快速入门,根据 简单单元测试代码示例

添加库依赖项

添加此库依赖项以及 gtest 依赖项:

#include <lib/driver/testing/cpp/driver_test.h>

#include <gtest/gtest.h>

该库提供了两个可在测试中使用的类。 在此示例中,我们使用了 ForegroundDriverTest,但还有一个 可用存储空间:BackgroundDriverTest。 有关详情,请参阅下面的前台与后台

创建配置类

测试定义要传入库的配置类 模板参数。 该类负责管理单元测试的各个部分, 确保它们在正确的调度程序环境中运行。

此配置类必须定义两种类型:一种为驱动程序,另一种为驱动程序 驱动程序的环境依赖项,我们将在下一个视频中 部分。

以下是该示例中的配置类示例:

class TestConfig final {
 public:
  using DriverType = simple::SimpleDriver;
  using EnvironmentType = SimpleDriverTestEnvironment;
};

定义环境类型类

EnvironmentType 必须是一个隔离的类 提供驱动程序的自定义依赖项。 它不需要提供框架依赖项(compat::DeviceServer 除外), 因为库已经这样做了

如果不需要额外的依赖项,请使用 fdf_testing::MinimalCompatEnvironment 该参数会提供一个默认的 compat::DeviceServer(请注意, 因为 compat 协议不在树内)。

以下是示例中的环境:

class SimpleDriverTestEnvironment : public fdf_testing::Environment {
 public:
  zx::result<> Serve(fdf::OutgoingDirectory& to_driver_vfs) override {
    // Perform any additional initialization here, such as setting up compat device servers
    // and FIDL servers.
    return zx::ok();
  }
};

定义测试

现在我们可以把所有元素放在一起进行测试。其外观如下 在我们的示例中:

class SimpleDriverTest : public ::testing::Test {
 public:
  void SetUp() override {
    zx::result<> result = driver_test().StartDriver();
    ASSERT_EQ(ZX_OK, result.status_value());
  }
  void TearDown() override {
    zx::result<> result = driver_test().StopDriver();
    ASSERT_EQ(ZX_OK, result.status_value());
  }

  fdf_testing::ForegroundDriverTest<TestConfig>& driver_test() {
    return driver_test_;
  }

 private:
  fdf_testing::ForegroundDriverTest<TestConfig> driver_test_;
};

TEST_F(SimpleDriverTest, VerifyChildNode) {
  driver_test().RunInNodeContext([](fdf_testing::TestNode& node) {
    EXPECT_EQ(1u, node.children().size());
    EXPECT_TRUE(node.children().count("simple_child"));
  });
}

运行单元测试

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

tools/bazel test third_party/iwlwifi/test:iwlwifi_test_pkg

配置参数

DriverType

将通过 driver()RunInDriverContext() 函数。

默认情况下,这用于驱动程序生命周期管理 (即启动/停止驱动程序)。 这是通过驾驶员注册标志实现的 由驱动程序中的 FUCHSIA_DRIVER_EXPORT 宏调用创建。

DriverType 中使用自定义测试专用驱动程序时(例如, 提供测试专用函数),请添加静态 GetDriverRegistration 函数,如下所示。这将替换全局注册符号。

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

以下是提供兼容型协议和 自定义测试定义的 FIDL 服务器,如下所示:

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_;
};

前台与后台

前台驱动程序测试和后台驱动程序测试的选择取决于测试计划 与被测驱动程序进行通信。如果测试将对驱动程序调用公共方法 则应选择前台驱动程序测试。如果测试将通过 驱动程序公开 FIDL 的频率提高,则应选择后台驱动程序测试。

使用前台版本时,测试可以访问被测驱动程序 使用 driver() 方法并直接对其进行调用, 但发送到驱动程序提供的 FIDL 的同步客户端任务必须通过 RunOnBackgroundDispatcherSync()

使用后台版本时,测试可以将 FIDL 调用同步至 驱动程序提供的 FIDL,但必须在出现以下情况时通过 RunInDriverContext(): 访问驱动程序实例。

drive_test()

如上面的示例测试所示,有一个 driver_test() getter, 已创建测试,以返回对库类的引用。该对象提供 测试可用于执行各种测试操作的所有控件 例如启动驱动程序、连接到驱动程序和运行任务。 在前台和后台测试中都提供了一些方法, 还有一些专用于线程模式请参阅下文,了解 方法。

前台测试可用的方法

驱动单元

这可用于直接从测试中访问驱动程序。 由于驾驶员在前台运行,因此可以安全访问此 在主测试线程上运行

RunOnBackgroundDispatcherSync

在后台调度程序(独立于驱动程序)上运行任务。 这样做是为了避免在进行同步客户端调用时与驱动程序发生死锁 转换为前台驱动程序。

适用于后台测试的方法

RunInDriverContext

这可用于在被测驱动程序上运行回调。 回调输入将引用驱动程序。 对驾驶员的所有访问都必须通过,因为触摸驾驶员是不安全的 。

方法

运行时

访问驱动程序运行时对象。 它可用于创建新的后台调度程序, 运行前台调度程序。 用户无需明确为环境创建调度程序, 而是由库负责完成这项工作

StartDriver

此令牌可用于启动受测驱动程序。等待启动 完成后才会返回结果。

StartDriverWithCustomStartArgs

与 StartDriver 相同,但可以在发送前修改驱动程序启动参数 提供给驾驶员

StopDriver

停止被测驾驶员。这会在 DriverBase 上调用 PrepareStop 并等待完成。如果是 StartDriver,必须调用此方法 成功。如果 StartDriver 失败,也可以调用该方法,但为了匹配 则为空操作。

ShutdownAndDestroyDriver

关闭属于被测驱动程序的驱动程序调度程序,然后 调用驱动程序的销毁钩子。销毁时会自动进行此操作 如果需要,也可以由测试手动调用 以进行某些验证 再次启动驾驶员

连接

连接到受测驱动程序提供的服务成员实例。 这可以是驱动程序传输或基于 zircon 信道传输的服务。

ConnectThroughDevfs

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

RunInEnvironmentTypeContext

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

RunInNodeContext

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

在单次测试中多次启动驱动程序

如需在测试中多次启动/停止驾驶员,而不更改 请确保完成全部 3 个步骤: - StartDriver/StartDriverWithCustomStartArgs - StopDriver - ShutdownAndDestroyDriver

“Run* 函数”警告

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

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

示例