請按照本快速入門導覽課程,根據簡易單元測試程式碼範例編寫驅動程式庫單元測試:
加入程式庫依附元件
在 C++ 檔案中加入這個程式庫依附元件,以及 gtest 依附元件:
#include <lib/driver/testing/cpp/driver_test.h>
#include <gtest/gtest.h>
您也需要在 BUILD.gn 檔案的 deps 中新增對應目標:
deps = [
"//sdk/lib/driver/testing/cpp",
"//src/lib/fxl/test:gtest_main",
]
程式庫提供可在測試中使用的兩個類別。本範例使用的是 ForegroundDriverTest,但您也可以使用 BackgroundDriverTest。詳情請參閱下方的「前景與背景」一節。
建立設定類別
測試會定義設定類別,透過範本參數傳遞至程式庫。這個類別會管理單元測試的個別部分,確保這些部分在正確的調度工具內容中執行。
這個設定類別必須定義兩種型別,一種用於驅動程式庫,另一種用於驅動程式庫的環境依附元件,我們會在下一節中說明。
以下是範例中的設定類別:
class TestConfig final {
public:
using DriverType = simple::SimpleDriver;
using EnvironmentType = SimpleDriverTestEnvironment;
};
定義環境類型類別
EnvironmentType 必須是提供驅動程式自訂依附元件的獨立類別。不需要提供架構依附元件 (compat::DeviceServer 除外),因為程式庫已提供這些元件。
如果不需要額外的依附元件,請使用 fdf_testing::MinimalCompatEnvironment,其提供預設的 compat::DeviceServer (請注意,這僅適用於樹狀結構內,因為相容通訊協定不在 SDK 中)。
以下是範例中的環境:
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_.Initialize(component::kDefaultInstance);
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()。
driver_test()
如上述測試範例所示,測試建立的 driver_test() 擷取器會傳回程式庫類別的參照。這個物件提供測試可用的所有控制項,以便執行各種測試作業,例如啟動驅動程式庫、連線至驅動程式,以及執行工作。有些方法適用於前景和背景測試,有些則專用於執行緒模式。請參閱下方可用方法。
前景測試可用的方法
驅動程式庫
這可用於直接從測試存取驅動程式庫。由於驅動程式庫位於前景,因此可以在主要測試執行緒上安全地存取。
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* 函式 (RunInDriverContext、RunOnBackgroundDispatcherSync、RunInEnvironmentTypeContext、RunInNodeContext) 時請務必小心。這些工作會在特定調度工具上執行,因此下列做法可能不安全:
- 從其他環境 (主執行緒或其他 Run* 類型) 將原始指標傳遞至這些指標,以便在函式中使用
- 從中傳回原始指標 (透過擷取的參照或傳回型別),以便在主執行緒上使用,或在另一個 Run* 函式中擷取/使用 (相同類型的 Run* 函式除外)。