本指南說明如何為 Fuchsia 的
bt-host 驅動程式,具體說明
SMP_Phase1Test。
測試韌體
FeatureExchangeBothSupportSCFeaturesHaveSC
測試案例
bt-host 驅動程式庫實作了多數 Bluetooth Core 規格 v5.2、
磁碟區 3 (主機子系統)。
Fuchsia 專案提出了 高度重視自動化測試 藍牙團隊也致力於 測試文化的領導人。
如要合併 Fuchsia 來源樹狀結構中的 bt-host 驅動程式庫變更,您必須
為這些變更編寫自動化測試
簡介
bt-host 是相對較大的程式碼集,具有許多抽象層。
以下是 bt-host 驅動程式庫的主要邏輯元件的圖表:
{#architecture-diagram}
圖表中的每個抽象層大致對應整個通訊協定。
處理 bt-host 測試時,請著重瞭解彼此之間的關係
但可在現行層 (例如 SM) 與對應層之間進行
放在正下方 (例如 L2CAP)。
雖然每個資料層可能都有額外的內部抽象層 通訊協定間關係最常模擬和/或練習 測試。
資源
bt-host 是以 C++ 編寫,而 Fuchsia 使用
gUnit
適用於 C++ 測試的 Googletest 程式庫。
如要進行 bt-host 單元測試,必須充分瞭解
下列資源:
設計 bt-host 測試時,偶爾會出現下列主題,
需要參考:
bt-host 單元測試總覽
大多數的 bt-host 測試都是以下列模式編寫:
建立測試固件來儲存資料 「test Doubles」和「Layer Under Test」(LUT)。
在該測試固件中建構 LUT,並使用測試替身取代 LUT 的依附元件
在 LUT 內執行功能。舉例來說,本指南會檢查
FeatureExchangeBothSupportSCFeaturesHaveSC測試案例。驗證較高層級的指令是否會產生預期行為。
測試固件
GTest 測試固件是可重複使用的環境,通常用於儲存測試
並提供撰寫單元測試的便利方法。測試
本例使用的固件是 SMP_Phase1Test 類別。
測試替身
測試替身可在測試程式碼中取代實際物件,有
是許多不同的測試替身這兩個範例測試用雙精度浮點數
SMP_Phase1Test為 FakeChannel,
FakeListener。
SMP_Phase1Test 測試韌體
SMP_Phase1Test敬上
是用於測試
sm::Phase1。
類別
Phase1 類別負責藍牙低功耗 (BLE) 階段 1
配對,讓裝置彼此協商配對連線的安全性功能。
本節將 SMP_Phase1Test 的測試固件設定程式碼註解為
代表性範例
「建立測試固件」和「建構 LUT」。
建議您建立
phase_1_unittest.cc
開啟這個對話方塊
SetUp()、TearDown() 和 NewPhase1() 方法
SMP_Phase1Test 建構函式不會執行任何動作。相反地,bt-host 測試
固件通常會使用 GTest SetUp() 方法初始化測試
工具。
void SetUp() override { NewPhase1(); }
NewPhase1 是包含可預設參數的 protected 瀏覽權限方法。
bt-host 測試韌體通常會委派給 New<test-fixture-name> 方法
使用 SetUp() 的可預設參數。New* 方法會負責
設定資源/測試替身,以及建立 LUT。這麼做可啟用測試
案件1,使用不同的值呼叫 New*,重新初始化測試固件
參數。
void NewPhase1(Role role = Role::kInitiator,
Phase1Args phase_args = Phase1Args(),
hci::Connection::LinkType ll_type =
hci::Connection::LinkType::kLE) {
如果是 NewPhase1,可設定的切面如下:
- 裝置角色
- 傳輸類型
- 包含 Phase1 引數的結構體,讓它成為 較簡單的做法是只變更「預設」的其中一個引數:
Phase1Args 結構體
struct Phase1Args {
PairingRequestParams preq = PairingRequestParams();
IOCapability io_capability = IOCapability::kNoInputNoOutput;
BondableMode bondable_mode = BondableMode::Bondable;
SecurityLevel level = SecurityLevel::kEncrypted;
bool sc_supported = false;
};
L2CAP 模擬依附元件
L2CAP 管道提供連至對等互連通訊協定/服務的邏輯連線,且
取決於 ATT、GATT、SMP、SDP 等較高層級的通訊協定。
FakeChannel 可做為模擬依附元件,用來測試實際物件傳送和
透過以下方式接收訊息:
L2CAP 頻道。
在 NewPhase1 中建立的第一個測試雙重測試是 FakeChannel 模擬物件:
uint16_t mtu =
phase_args.sc_supported ? l2cap::kMaxMTU : kNoSecureConnectionsMtu;
ChannelOptions options(cid, mtu);
options.link_type = ll_type;
...
fake_chan_ = CreateFakeChannel(options);
sm_chan_ = std::make_unique<PairingChannel>(fake_chan_);
您可以使用 CreateFakeChannel 方法,因為 SMP_Phase1Test 繼承
來自
FakeChannelTest。2。
FakeListener
在實際程式碼中,PairingPhase 會使用 PairingPhase::Listener 用來與
較高層級的 SecurityManager 類別FakeListener 提供以下的模擬畫面
進行測試的依附元件。
listener_ = std::make_unique<FakeListener>();
儘管並非通訊協定層級依附元件,但 FakeListener 更執行了另一個常見的
bt-host 測試模式。類別通常會採用介面指標來進行通訊
上面有層實作這些介面的測試替身會傳遞至
LUT 來確認 LUT 是否能與上方的層正確通訊。
完成回呼
Phase1 會儲存回呼參數。Phase1 完成後,會傳回
透過此回呼傳回 Phase1 的結果。此為「complete_cb」
回呼 Phase1。
complete_cb 會儲存 Phase1 的結果 (在本例中為 features,
preq 和 pres 引數) 放入測試固件變數 (features_、
last_pairing_req_ 和 last_pairing_res_),讓測試案例可以檢查
這些變數的產生方式正確
auto complete_cb = [this](PairingFeatures features,
PairingRequestParams preq,
PairingResponseParams pres) {
feature_exchange_count_++;
features_ = features;
last_pairing_req_ = util::NewPdu(sizeof(PairingRequestParams));
last_pairing_res_ = util::NewPdu(sizeof(PairingResponseParams));
PacketWriter preq_writer(kPairingRequest, last_pairing_req_.get());
PacketWriter pres_writer(kPairingResponse, last_pairing_res_.get());
*preq_writer.mutable_payload<PairingRequestParams>() = preq;
*pres_writer.mutable_payload<PairingResponseParams>() = pres;
};
LUT 例項化
下一步是根據 NewPhase1 建立 Phase1 LUT
參數。LUT 儲存在測試固件的 phase_1_ 變數中。
if (role == Role::kInitiator) {
phase_1_ = Phase1::CreatePhase1Initiator(
sm_chan_->GetWeakPtr(), listener_->as_weak_ptr(),
phase_args.io_capability, phase_args.bondable_mode,
phase_args.level, std::move(complete_cb));
} else {
phase_1_ = Phase1::CreatePhase1Responder(
sm_chan_->GetWeakPtr(), listener_->as_weak_ptr(),
phase_args.preq, phase_args.io_capability,
phase_args.bondable_mode, phase_args.level,
std::move(complete_cb));
}
其餘方法
其餘 Phase1 方法是可進行以下作業的簡易 get 方法:
FeatureExchangeBothSupportSCFeaturesHaveSC 測試案例
這個測試案例會確認配對的兩部裝置都支援
功能,在本例中為安全連線 (SC) 功能,PairingFeatures
Phase1 的完整回呼所傳回的正確回報方式。
預設的 NewPhase1 參數
不支援安全連線,因此程式碼集僅
Phase1Args 的 SC 欄位,其餘則預設為 NewPhase1:
Phase1Args args;
args.sc_supported = true;
NewPhase1(Role::kInitiator, args);
此測試案例中使用的 L2CAP 訊息已輸出,具備功能位元
測試用 (kSC) 組合:
const auto kRequest = StaticByteBuffer(
// [...omitted]
AuthReq::kSC | AuthReq::kBondingFlag,
// [...omitted]
);
const auto kResponse = StaticByteBuffer(
// [...omitted]
AuthReq::kSC | AuthReq::kBondingFlag,
// [...omitted]
);
bt-host 的某些部分會在非同步工作調度器上執行。在本例中
FakeChannelTest 會在調度器上執行 FakeChannel。Phase1::Start,需要
執行 Phase1 的工作,也需要在這個調度器上執行。
PostTask 會將 Start 方法放入調度工具。
FakeChannelTest::Expect 接著會執行調度工具,檢查下一個
Phase1 傳送給 L2CAP 的訊息為 kRequest:
// Initiate the request in a loop task for Expect to detect it.
async::PostTask(dispatcher(), [this] { phase_1()->Start(); });
ASSERT_TRUE(Expect(kRequest));
fake_chan 可用來模擬接收對等點的回覆,
完成第 1 階段功能交換在此情況下,程式碼會明確執行
呼叫 RunLoopUntilIdle() 來使工作調度工具迴圈,
「FakeChannelTest::Expect」在內部做了上述操作:
fake_chan()->Receive(kResponse);
RunLoopUntilIdle();
最後,程式碼會驗證:
- 「
Phase1」不會傳送錯誤通知「FakeListener」 - 所有預期的參數都會在
Phase1的完整回呼中傳遞。
EXPECT_EQ(0, listener()->pairing_error_count());
EXPECT_EQ(1, feature_exchange_count());
EXPECT_TRUE(features().initiator);
EXPECT_TRUE(features().secure_connections);
ASSERT_TRUE(last_preq());
ASSERT_TRUE(last_pres());
EXPECT_TRUE(ContainersEqual(kRequest, *last_preq()));
EXPECT_TRUE(ContainersEqual(kResponse, *last_pres()));