本指南說明如何為 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()));