驱动程序集成测试框架
DriverTestRealm 是一个集成测试框架, 封闭环境对驾驶员开发者来说,测试驱动程序非常有用, 让系统开发者能够运行使用特定驱动程序堆栈的集成测试。 DriverTestRealm 提供了所有驱动程序框架的封闭版本 API,并提供与运行系统几乎完全相同的环境。
DriverTestRealm 用于集成测试。用于轻量级单元测试 框架, 使用 模拟 DDK。
DriverTestRealm 概览
DriverTestRealm 是您的测试可以访问的组件。这个 组件包含 DriverFramework 的所有组件,例如 DriverManager 和 DriverIndex。它会模拟这些组件需要的所有功能。
DriverTestRealm 公开了
fuchsia.driver.test/Realm
协议
用于启动 DriverTestRealm。Start 函数采用参数
可用于配置要加载哪些驱动程序、
以及 DriverManager 如何关停。每次只能调用 1 次启动
组件;如果每次测试都需要新的 DriverTestRealm,则 RealmBuilder
。
与驾驶员互动
DriverTestRealm 组件公开了 DriverManager 中的 /dev/
目录,
这是测试代码与驱动程序交互的方式。/dev/
目录有效
与运行系统上的 devfs 完全相同。例如,如果测试添加一个
模拟输入设备,它将显示在 /dev/class/input-report/XXX
处。
包括司机
默认情况下,DriverTestRealm 组件会从自己的软件包中加载驱动程序。测试 开发者必须在测试软件包中包含他们自己负责的 预计会加载的内容
绑定驱动程序
默认情况下,DriverTest Realm 的根驱动程序是 测试父级驱动程序。也就是说, 要绑定驱动程序,您应该创建一个模拟驱动程序, 测试父级。可以是 完成以下绑定:
fuchsia.BIND_PROTOCOL == fuchsia.test.BIND_PROTOCOL.PARENT;
然后,您的模拟驱动程序便可以添加具有正确属性的设备, 被测驱动程序将绑定到模拟驱动程序。
封闭与非封闭
有两种使用 DriverTestRealm 的方法。它可用于 封闭式或非封闭式
Hermetic
在驾驶员测试区的封闭版本中,每项测试都会获得 自己的 Driver Test Realm 组件版本。这意味着每次测试 是与其他测试封闭或隔离的测试不会分享 状态,因为每个驱动程序测试领域对于该测试都是唯一的。
使用封闭的驾驶员测试领域可能会变慢,因为每次测试都必须 生成和设置新组件。
非封闭
使用 Driver Test Realm 的非封闭方式是让单个驾驶员 在每个测试实例之间共享的测试 Realm 子组件。
测试作者需要格外小心,确保司机 会在每次测试之间清除状态 彼此交互
使用非封闭的驾驶员测试领域可能会快得多,因为每次测试 不需要生成和设置新组件。测试代码 使其更加简单
DriverTestRealm 示例
以下是以封闭方式和非封闭方式使用 DriverTestRealm 的一些示例, 开发应用。
如需查看示例,请访问 //examples/drivers/driver_test_realm/。
Hermetic
测试作者可以使用 RealmBuilder 来为每个测试创建一个新的 DriverTestRealm。 DriverFramework 提供了一个非常实用的库, RealmBuilder 中的 DriverTestRealm。
下面是一个将 DriverTestRealm 与 RealmBuilder 配合使用的示例 BUILD.gn
文件。
请注意,必须依赖特定的 DriverTestRealm GN 目标,因此
测试生成的 CML 具有正确的 RealmBuilder 权限。
C++
test("driver_test_realm_example_hermetic_cpp") {
sources = [ "test.cc" ]
deps = [
"//examples/drivers/driver_test_realm/sample-driver:fuchsia.hardware.sample_cpp",
"//sdk/fidl/fuchsia.driver.test:fuchsia.driver.test_hlcpp",
"//sdk/fidl/fuchsia.io:fuchsia.io_hlcpp",
"//sdk/lib/component/outgoing/cpp",
"//sdk/lib/device-watcher/cpp",
"//sdk/lib/driver_test_realm/realm_builder/cpp",
"//src/lib/fxl/test:gtest_main",
"//src/lib/testing/loop_fixture",
"//zircon/system/ulib/async-loop",
"//zircon/system/ulib/async-loop:async-loop-cpp",
"//zircon/system/ulib/async-loop:async-loop-default",
"//zircon/system/ulib/fbl",
]
}
fuchsia_unittest_package("package") {
package_name = "driver_test_realm_example_hermetic_cpp"
deps = [
# Include your test component.
":driver_test_realm_example_hermetic_cpp",
# Include the driver(s) you will be testing.
"//examples/drivers/driver_test_realm/sample-driver",
# Include the test parent (if your driver binds to it).
"//src/devices/misc/drivers/test-parent",
]
}
Rust
rustc_test("driver_test_realm_example_realm_builder_rust") {
edition = "2021"
testonly = true
source_root = "test.rs"
sources = [ "test.rs" ]
deps = [
"//examples/drivers/driver_test_realm/sample-driver:fuchsia.hardware.sample_rust",
"//sdk/fidl/fuchsia.driver.test:fuchsia.driver.test_rust",
"//sdk/lib/device-watcher/rust",
"//sdk/lib/driver_test_realm/realm_builder/rust",
"//src/lib/fuchsia-async",
"//src/lib/fuchsia-component-test",
"//third_party/rust_crates:anyhow",
]
}
fuchsia_unittest_package("package") {
package_name = "driver_test_realm_example_realm_builder_rust"
deps = [
# Include your test component.
":driver_test_realm_example_realm_builder_rust",
# Include the driver(s) you will be testing.
"//examples/drivers/driver_test_realm/sample-driver",
# Include the platform bus (if your driver binds to it).
"//src/devices/bus/drivers/platform:platform-bus",
# Include the test parent (if your driver binds to it).
"//src/devices/misc/drivers/test-parent",
]
# There's expected error logs that happen due to races in driver enumeration.
test_specs = {
log_settings = {
max_severity = "ERROR"
}
}
}
以下是每次测试会生成新的 DriverTestRealm 的测试代码。
C++
class DriverTestRealmTest : public gtest::TestLoopFixture {};
TEST_F(DriverTestRealmTest, DriversExist) {
// Create and build the realm.
auto realm_builder = component_testing::RealmBuilder::Create();
driver_test_realm::Setup(realm_builder);
auto realm = realm_builder.Build(dispatcher());
// Start DriverTestRealm.
fidl::SynchronousInterfacePtr<fuchsia::driver::test::Realm> driver_test_realm;
ASSERT_EQ(ZX_OK, realm.component().Connect(driver_test_realm.NewRequest()));
fuchsia::driver::test::Realm_Start_Result realm_result;
ASSERT_EQ(ZX_OK, driver_test_realm->Start(fuchsia::driver::test::RealmArgs(), &realm_result));
ASSERT_FALSE(realm_result.is_err()) << zx_status_get_string(realm_result.err());
// Connect to dev.
fidl::InterfaceHandle<fuchsia::io::Node> dev;
ASSERT_EQ(ZX_OK, realm.component().Connect("dev-topological", dev.NewRequest().TakeChannel()));
fbl::unique_fd root_fd;
ASSERT_EQ(ZX_OK, fdio_fd_create(dev.TakeChannel().release(), root_fd.reset_and_get_address()));
// Wait for driver.
zx::result channel =
device_watcher::RecursiveWaitForFile(root_fd.get(), "sys/test/sample_driver");
ASSERT_EQ(channel.status_value(), ZX_OK);
// Turn the connection into FIDL.
fidl::WireSyncClient client(
fidl::ClientEnd<fuchsia_hardware_sample::Echo>(std::move(channel.value())));
// Send a FIDL request.
constexpr std::string_view sent_string = "hello";
fidl::WireResult result = client->EchoString(fidl::StringView::FromExternal(sent_string));
ASSERT_EQ(ZX_OK, result.status());
ASSERT_EQ(sent_string, result.value().response.get());
}
Rust
#[fasync::run_singlethreaded(test)]
async fn test_sample_driver() -> Result<()> {
// Create the RealmBuilder.
let builder = RealmBuilder::new().await?;
builder.driver_test_realm_setup().await?;
// Build the Realm.
let instance = builder.build().await?;
// Start DriverTestRealm
instance.driver_test_realm_start(fdt::RealmArgs::default()).await?;
// Connect to our driver.
let dev = instance.driver_test_realm_connect_to_dev()?;
let driver =
device_watcher::recursive_wait_and_open::<fidl_fuchsia_hardware_sample::EchoMarker>(
&dev,
"sys/test/sample_driver",
)
.await?;
// Call a FIDL method on the driver.
let response = driver.echo_string("Hello world!").await.unwrap();
// Verify the response.
assert_eq!(response, "Hello world!");
Ok(())
}
#[fasync::run_singlethreaded(test)]
async fn test_platform_bus() -> Result<()> {
// Create the RealmBuilder.
let builder = RealmBuilder::new().await?;
builder.driver_test_realm_setup().await?;
// Build the Realm.
let instance = builder.build().await?;
// Start DriverTestRealm.
let args = fdt::RealmArgs {
root_driver: Some("fuchsia-boot:///platform-bus#meta/platform-bus.cm".to_string()),
..Default::default()
};
instance.driver_test_realm_start(args).await?;
// Connect to our driver.
let dev = instance.driver_test_realm_connect_to_dev()?;
device_watcher::recursive_wait(&dev, "sys/platform").await?;
Ok(())
}
非封闭
下面是一个基本的测试示例,该测试启动 DriverTestRealm 组件,然后连接到
到 /dev
可查看已加载的驱动程序。
首先,正确设置构建规则非常重要。测试软件包 需要包含 DriverTestRealm 组件,以及 哪些对象要加载将驱动程序添加到软件包后,系统会自动 对 DriverTestRealm 可见的驱动程序。
C++
test("driver_test_realm_example_non_hermetic_cpp") {
sources = [ "test.cc" ]
deps = [
"//sdk/fidl/fuchsia.driver.test:fuchsia.driver.test_cpp",
"//sdk/lib/component/incoming/cpp",
"//sdk/lib/device-watcher/cpp",
"//sdk/lib/driver_test_realm:static",
"//sdk/lib/syslog/cpp",
"//third_party/googletest:gtest",
]
}
fuchsia_unittest_package("package") {
package_name = "driver_test_realm_example_non_hermetic_cpp"
deps = [
":driver_test_realm_example_non_hermetic_cpp",
# Add drivers to the package here.
# The test-parent driver is the default root driver for DriverTestRealm.
"//src/devices/misc/drivers/test-parent",
]
}
测试设置如下所示。请注意,您必须调用
fuchsia.driver.test/Realm:Start
,然后再运行测试。通过
可以设置 Start
的参数来配置 DriverManager 实现。
C++
TEST(DdkFirmwaretest, DriverWasLoaded) {
zx::result channel = device_watcher::RecursiveWaitForFile("/dev/sys/test");
ASSERT_EQ(channel.status_value(), ZX_OK);
}
int main(int argc, char **argv) {
fuchsia_logging::LogSettingsBuilder builder;
builder.WithTags({"driver_test_realm_test"}).BuildAndInitialize();
// Connect to DriverTestRealm.
auto client_end = component::Connect<fuchsia_driver_test::Realm>();
if (!client_end.is_ok()) {
FX_LOG_KV(ERROR, "Failed to connect to Realm FIDL", FX_KV("error", client_end.error_value()));
return 1;
}
fidl::WireSyncClient client{std::move(*client_end)};
// Start the DriverTestRealm with correct arguments.
auto wire_result = client->Start(fuchsia_driver_test::wire::RealmArgs());
if (wire_result.status() != ZX_OK) {
FX_LOG_KV(ERROR, "Failed to call to Realm:Start", FX_KV("status", wire_result.status()));
return 1;
}
if (wire_result.value().is_error()) {
FX_LOG_KV(ERROR, "Realm:Start failed", FX_KV("status", wire_result.value().error_value()));
return 1;
}
// Run the tests.
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
请注意,DriverTestRealm 启动后,测试环境
DriverFramework 在其组件命名空间中可用的所有 API。
可以观察和打开 /dev/
目录以连接到驱动程序。
简单示例
Simple 库可实现 测试使用带有默认参数的 DriverTestRealm。
大多数集成测试都适用于 DriverTestRealm 的默认配置; 也无需向 Start 传递参数。如果是这种情况 SimpleDriverTestRealm 会自动启动。
C++
test("driver_test_realm_example_simple_cpp") {
sources = [ "test.cc" ]
deps = [
"//sdk/lib/device-watcher/cpp",
"//sdk/lib/driver_test_realm/simple",
"//src/lib/fxl/test:gtest_main",
]
}
fuchsia_unittest_package("package") {
package_name = "driver_test_realm_example_simple_cpp"
deps = [ ":driver_test_realm_example_simple_cpp" ]
}
Rust
rustc_test("driver_test_realm_example_simple_rust") {
edition = "2021"
source_root = "test.rs"
sources = [ "test.rs" ]
deps = [
"//sdk/lib/device-watcher/rust",
"//sdk/lib/driver_test_realm/simple",
"//src/lib/fuchsia-async",
"//src/lib/fuchsia-fs",
"//third_party/rust_crates:anyhow",
]
}
fuchsia_unittest_package("package") {
package_name = "driver_test_realm_example_simple_rust"
deps = [ ":driver_test_realm_example_simple_rust" ]
}
此测试看起来完全相同,只是不需要设置 main
函数。
调用 fuchsia.driver.test/Realm:Start
。
C++
TEST(SimpleDriverTestRealmTest, DriversExist) {
zx::result channel = device_watcher::RecursiveWaitForFile("/dev/sys/test");
ASSERT_EQ(channel.status_value(), ZX_OK);
}
Rust
#[fasync::run_singlethreaded(test)]
async fn test_driver() -> Result<()> {
let dev = fuchsia_fs::directory::open_in_namespace("/dev", fuchsia_fs::OpenFlags::empty())?;
device_watcher::recursive_wait(&dev, "sys/test").await?;
Ok(())
}
常见问题:
- 驱动程序没有绑定
<ph type="x-smartling-placeholder">
- </ph>
- 确保驱动程序包含在软件包中
- 如果使用的是默认根驱动程序,请确保包含
src/devices/misc/drivers/test-parent
。
- 对 /dev/ 的调用挂起
<ph type="x-smartling-placeholder">
- </ph>
- 确保调用
fuchsia.driver.test/Realm:Start
。
- 确保调用
fuchsia.driver.test/Realm:Start
返回 ZX_ERR_ALREADY_BOUND <ph type="x-smartling-placeholder">- </ph>
- 每个组件只能调用一次 start。如果您希望每次测试都有新的 DriverTestRealm,请参阅 RealmBuilder 部分。