DriverTestRealm

驅動程式庫整合測試架構

DriverTestRealm 是一種整合測試架構,可在 也更加個人化這對駕駛人來說相當實用 讓系統開發人員執行使用特定驅動程式庫堆疊的整合測試。 DriverTestRealm 為所有驅動程式架構提供密封版本 API,並提供與執行中系統幾乎完全相同的環境。

DriverTestRealm 用於整合測試。用於輕重單位測試 請使用 mock DDK

DriverTestRealm 總覽

DriverTestRealm 是測試可以存取的元件。這個 元件包含所有 DriverFramework 的元件,例如 DriverManager 以及 DriverIndex可抽出這些元件所需的所有功能。

圖: DriverTestRealm 的非密封設定

DriverTestRealm 瀏覽 fuchsia.driver.test/Realm 通訊協定 用於啟動 DriverTestRealmStart 函式採用引數 此類別可用於設定可載入哪些驅動程式、根層級等項目 以及驅動程式如何關閉每個開始時間只能呼叫一次 元件;如果每項測試都需要新的 DriverTestRealm,那麼 RealmBuilder 。

與駕駛互動

DriverTestRealm 元件會從 DriverManager 公開發布 /dev/ 目錄, 測試程式碼與驅動程式的互動方式。/dev/ 目錄可以運作 與執行中系統上的開發團隊相同舉例來說,如果這項測試新增了 模擬輸入裝置,將於 /dev/class/input-report/XXX 顯示。

包含駕駛人

根據預設, DriverTestRealm 元件會從自身的套件載入驅動程式。測試 作者必須確實將他們納入測試套件中的所有驅動程式

裝訂司機

根據預設, DriverTest 領域的根驅動程式庫為 測試父項驅動程式。也就是說 如要繫結驅動程式庫,請建立一個模擬驅動程式庫, 測試父項。可用的值包括 會以下列繫結規則完成:

fuchsia.BIND_PROTOCOL == fuchsia.test.BIND_PROTOCOL.PARENT;

接著,模擬驅動程式庫就能新增具備正確屬性的裝置, 駕駛未經認證的駕駛會繫結至模擬驅動程式庫。

密封與非維生化

DriverTestRealm 的使用方式有兩種。可用於 不一定是上妝品

Hermetic

在這個密封版的驅動程式測試領域中,每次測試都能獲得 原始版本的驅動程式測試領域元件也就是說,每次測試 以及獨立於其他測試中的隱密或獨立的測試測試將不會分享 狀態,因為該測試對於每個驅動程式測試運作範圍而言是唯一的。

圖: DriverTestRealm 的密封設定

使用密封的駕駛測試運作範圍可能會變慢,因為每次測試都必須 就能產生及設定新元件

非傳統

使用驅動程式測試運作範圍並非密封的方法,就是使用單一驅動程式 測試每個測試執行個體之間共用的運作範圍子項元件。

圖: DriverTestRealm 的非密封設定

測試作者需要特別謹慎,確保測試者的確 系統會清除每項測試之間的狀態,以免個別測試 互動。

使用非密封的驅動程式測試運作範圍可能會因為每次測試而加快處理速度 不必產生及設定新元件或測試程式碼也可以 變得比較簡單

DriverTestRealm 範例

以下提供幾個隱密 或非正規化的 DriverTestRealm 使用範例 C++ 和 Rust 中都有一些問題

您可以前往 //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/async-loop",
    "//sdk/lib/async-loop:async-loop-cpp",
    "//sdk/lib/async-loop:async-loop-default",
    "//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/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_deprecated(
        "/dev",
        fuchsia_fs::OpenFlags::empty(),
    )?;
    device_watcher::recursive_wait(&dev, "sys/test").await?;
    Ok(())
}

常見問題:

  • 駕駛人未綁定
    • 確認套件中已隨附驅動程式庫
    • 如果使用預設根驅動程式,請確保包含 src/devices/misc/drivers/test-parent
  • 呼叫 /dev/ 正在執行呼叫
    • 確認已呼叫 fuchsia.driver.test/Realm:Start
  • fuchsia.driver.test/Realm:Start 會傳回 ZX_ERR_ALREADY_BOUND
    • 每個元件只能呼叫一次 Start。如要針對每項測試使用新的 DriverTestRealm,請參閱 RealmBuilder 部分。