1 /*
2 * Copyright (c) 2024, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <gmock/gmock.h>
30 #include <gtest/gtest.h>
31
32 #include <sys/time.h>
33
34 #include <openthread/dataset.h>
35 #include <openthread/dataset_ftd.h>
36
37 #include "common/mainloop.hpp"
38 #include "common/mainloop_manager.hpp"
39 #include "host/rcp_host.hpp"
40
41 #include "fake_platform.hpp"
42
MainloopProcessUntil(otbr::MainloopContext & aMainloop,uint32_t aTimeoutSec,std::function<bool (void)> aCondition)43 static void MainloopProcessUntil(otbr::MainloopContext &aMainloop,
44 uint32_t aTimeoutSec,
45 std::function<bool(void)> aCondition)
46 {
47 timeval startTime;
48 timeval now;
49 gettimeofday(&startTime, nullptr);
50
51 while (!aCondition())
52 {
53 gettimeofday(&now, nullptr);
54 // Simply compare the second. We don't need high precision here.
55 if (now.tv_sec - startTime.tv_sec > aTimeoutSec)
56 {
57 break;
58 }
59
60 otbr::MainloopManager::GetInstance().Update(aMainloop);
61 otbr::MainloopManager::GetInstance().Process(aMainloop);
62 }
63 }
64
TEST(RcpHostApi,DeviceRoleChangesCorrectlyAfterSetThreadEnabled)65 TEST(RcpHostApi, DeviceRoleChangesCorrectlyAfterSetThreadEnabled)
66 {
67 otError error = OT_ERROR_FAILED;
68 bool resultReceived = false;
69 otbr::Host::ThreadEnabledState threadEnabledState = otbr::Host::ThreadEnabledState::kStateInvalid;
70 otbr::MainloopContext mainloop;
71 otbr::Host::ThreadHost::AsyncResultReceiver receiver = [&resultReceived, &error](otError aError,
72 const std::string &aErrorMsg) {
73 OT_UNUSED_VARIABLE(aErrorMsg);
74 resultReceived = true;
75 error = aError;
76 };
77 otbr::Host::ThreadHost::ThreadEnabledStateCallback enabledStateCallback =
78 [&threadEnabledState](otbr::Host::ThreadEnabledState aState) { threadEnabledState = aState; };
79 otbr::Host::RcpHost host("wpan0", std::vector<const char *>(), /* aBackboneInterfaceName */ "", /* aDryRun */ false,
80 /* aEnableAutoAttach */ false);
81
82 host.Init();
83 host.AddThreadEnabledStateChangedCallback(enabledStateCallback);
84
85 // 1. Active dataset hasn't been set, should succeed with device role still being disabled.
86 host.SetThreadEnabled(true, receiver);
87 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 1, [&resultReceived]() { return resultReceived; });
88 EXPECT_EQ(error, OT_ERROR_NONE);
89 EXPECT_EQ(host.GetDeviceRole(), OT_DEVICE_ROLE_DISABLED);
90 EXPECT_EQ(threadEnabledState, otbr::Host::ThreadEnabledState::kStateEnabled);
91
92 // 2. Set active dataset and start it
93 {
94 otOperationalDataset dataset;
95 otOperationalDatasetTlvs datasetTlvs;
96 OT_UNUSED_VARIABLE(otDatasetCreateNewNetwork(ot::FakePlatform::CurrentInstance(), &dataset));
97 otDatasetConvertToTlvs(&dataset, &datasetTlvs);
98 OT_UNUSED_VARIABLE(otDatasetSetActiveTlvs(ot::FakePlatform::CurrentInstance(), &datasetTlvs));
99 }
100 OT_UNUSED_VARIABLE(otIp6SetEnabled(ot::FakePlatform::CurrentInstance(), true));
101 OT_UNUSED_VARIABLE(otThreadSetEnabled(ot::FakePlatform::CurrentInstance(), true));
102
103 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 1,
104 [&host]() { return host.GetDeviceRole() != OT_DEVICE_ROLE_DETACHED; });
105 EXPECT_EQ(host.GetDeviceRole(), OT_DEVICE_ROLE_LEADER);
106
107 // 3. Enable again, the enabled state should not change.
108 error = OT_ERROR_FAILED;
109 resultReceived = false;
110 host.SetThreadEnabled(true, receiver);
111 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 1, [&resultReceived]() { return resultReceived; });
112 EXPECT_EQ(error, OT_ERROR_NONE);
113 EXPECT_EQ(threadEnabledState, otbr::Host::ThreadEnabledState::kStateEnabled);
114
115 // 4. Disable it
116 error = OT_ERROR_FAILED;
117 resultReceived = false;
118 host.SetThreadEnabled(false, receiver);
119 EXPECT_EQ(threadEnabledState, otbr::Host::ThreadEnabledState::kStateDisabling);
120 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 1, [&resultReceived]() { return resultReceived; });
121 EXPECT_EQ(error, OT_ERROR_NONE);
122 EXPECT_EQ(host.GetDeviceRole(), OT_DEVICE_ROLE_DISABLED);
123 EXPECT_EQ(threadEnabledState, otbr::Host::ThreadEnabledState::kStateDisabled);
124
125 // 5. Duplicate call, should get OT_ERROR_BUSY
126 error = OT_ERROR_FAILED;
127 resultReceived = false;
128 otError error2 = OT_ERROR_FAILED;
129 bool resultReceived2 = false;
130 host.SetThreadEnabled(false, receiver);
131 host.SetThreadEnabled(false, [&resultReceived2, &error2](otError aError, const std::string &aErrorMsg) {
132 OT_UNUSED_VARIABLE(aErrorMsg);
133 error2 = aError;
134 resultReceived2 = true;
135 });
136 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 1,
137 [&resultReceived, &resultReceived2]() { return resultReceived && resultReceived2; });
138 EXPECT_EQ(error, OT_ERROR_NONE);
139 EXPECT_EQ(error2, OT_ERROR_BUSY);
140 EXPECT_EQ(threadEnabledState, otbr::Host::ThreadEnabledState::kStateDisabled);
141
142 host.Deinit();
143 }
144
TEST(RcpHostApi,SetCountryCodeWorkCorrectly)145 TEST(RcpHostApi, SetCountryCodeWorkCorrectly)
146 {
147 otError error = OT_ERROR_FAILED;
148 bool resultReceived = false;
149 otbr::MainloopContext mainloop;
150 otbr::Host::ThreadHost::AsyncResultReceiver receiver = [&resultReceived, &error](otError aError,
151 const std::string &aErrorMsg) {
152 OT_UNUSED_VARIABLE(aErrorMsg);
153 resultReceived = true;
154 error = aError;
155 };
156 otbr::Host::RcpHost host("wpan0", std::vector<const char *>(), /* aBackboneInterfaceName */ "", /* aDryRun */ false,
157 /* aEnableAutoAttach */ false);
158
159 // 1. Call SetCountryCode when host hasn't been initialized.
160 otbr::MainloopManager::GetInstance().RemoveMainloopProcessor(
161 &host); // Temporarily remove RcpHost because it's not initialized yet.
162 host.SetCountryCode("AF", receiver);
163 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
164 EXPECT_EQ(error, OT_ERROR_INVALID_STATE);
165 otbr::MainloopManager::GetInstance().AddMainloopProcessor(&host);
166
167 host.Init();
168 // 2. Call SetCountryCode with invalid arguments
169 resultReceived = false;
170 error = OT_ERROR_NONE;
171 host.SetCountryCode("AFA", receiver);
172 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
173 EXPECT_EQ(error, OT_ERROR_INVALID_ARGS);
174
175 resultReceived = false;
176 error = OT_ERROR_NONE;
177 host.SetCountryCode("A", receiver);
178 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
179 EXPECT_EQ(error, OT_ERROR_INVALID_ARGS);
180
181 resultReceived = false;
182 error = OT_ERROR_NONE;
183 host.SetCountryCode("12", receiver);
184 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
185 EXPECT_EQ(error, OT_ERROR_INVALID_ARGS);
186
187 // 3. Call SetCountryCode with valid argument
188 resultReceived = false;
189 error = OT_ERROR_NONE;
190 host.SetCountryCode("AF", receiver);
191 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
192 EXPECT_EQ(error, OT_ERROR_NOT_IMPLEMENTED); // The current platform weak implmentation returns 'NOT_IMPLEMENTED'.
193
194 host.Deinit();
195 }
196
TEST(RcpHostApi,StateChangesCorrectlyAfterLeave)197 TEST(RcpHostApi, StateChangesCorrectlyAfterLeave)
198 {
199 otError error = OT_ERROR_NONE;
200 std::string errorMsg = "";
201 bool resultReceived = false;
202 otbr::MainloopContext mainloop;
203 otbr::Host::ThreadHost::AsyncResultReceiver receiver = [&resultReceived, &error,
204 &errorMsg](otError aError, const std::string &aErrorMsg) {
205 resultReceived = true;
206 error = aError;
207 errorMsg = aErrorMsg;
208 };
209
210 otbr::Host::RcpHost host("wpan0", std::vector<const char *>(), /* aBackboneInterfaceName */ "", /* aDryRun */ false,
211 /* aEnableAutoAttach */ false);
212
213 // 1. Call Leave when host hasn't been initialized.
214 otbr::MainloopManager::GetInstance().RemoveMainloopProcessor(
215 &host); // Temporarily remove RcpHost because it's not initialized yet.
216 host.Leave(/* aEraseDataset */ true, receiver);
217 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
218 EXPECT_EQ(error, OT_ERROR_INVALID_STATE);
219 EXPECT_STREQ(errorMsg.c_str(), "OT is not initialized");
220 otbr::MainloopManager::GetInstance().AddMainloopProcessor(&host);
221
222 host.Init();
223
224 // 2. Call Leave when disabling Thread.
225 error = OT_ERROR_NONE;
226 resultReceived = false;
227 host.SetThreadEnabled(false, nullptr);
228 host.Leave(/* aEraseDataset */ true, receiver);
229 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
230 EXPECT_EQ(error, OT_ERROR_BUSY);
231 EXPECT_STREQ(errorMsg.c_str(), "Thread is disabling");
232
233 // 3. Call Leave when Thread is disabled.
234 error = OT_ERROR_NONE;
235 resultReceived = false;
236 otOperationalDataset dataset;
237 otOperationalDatasetTlvs datasetTlvs;
238 OT_UNUSED_VARIABLE(otDatasetCreateNewNetwork(ot::FakePlatform::CurrentInstance(), &dataset));
239 otDatasetConvertToTlvs(&dataset, &datasetTlvs);
240 OT_UNUSED_VARIABLE(otDatasetSetActiveTlvs(ot::FakePlatform::CurrentInstance(), &datasetTlvs));
241 host.Leave(/* aEraseDataset */ true, receiver);
242 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
243 EXPECT_EQ(error, OT_ERROR_NONE);
244
245 error = otDatasetGetActive(ot::FakePlatform::CurrentInstance(), &dataset);
246 EXPECT_EQ(error, OT_ERROR_NOT_FOUND);
247
248 // 4. Call Leave when Thread is enabled.
249 error = OT_ERROR_NONE;
250 resultReceived = false;
251 OT_UNUSED_VARIABLE(otDatasetSetActiveTlvs(ot::FakePlatform::CurrentInstance(), &datasetTlvs));
252 host.SetThreadEnabled(true, nullptr);
253 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 1,
254 [&host]() { return host.GetDeviceRole() != OT_DEVICE_ROLE_DETACHED; });
255 EXPECT_EQ(host.GetDeviceRole(), OT_DEVICE_ROLE_LEADER);
256 host.Leave(/* aEraseDataset */ false, receiver);
257 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
258 EXPECT_EQ(error, OT_ERROR_NONE);
259
260 error = otDatasetGetActive(ot::FakePlatform::CurrentInstance(), &dataset); // Dataset should still be there.
261 EXPECT_EQ(error, OT_ERROR_NONE);
262
263 host.Deinit();
264 }
265
TEST(RcpHostApi,StateChangesCorrectlyAfterScheduleMigration)266 TEST(RcpHostApi, StateChangesCorrectlyAfterScheduleMigration)
267 {
268 otError error = OT_ERROR_NONE;
269 std::string errorMsg = "";
270 bool resultReceived = false;
271 otbr::MainloopContext mainloop;
272 otbr::Host::ThreadHost::AsyncResultReceiver receiver = [&resultReceived, &error,
273 &errorMsg](otError aError, const std::string &aErrorMsg) {
274 resultReceived = true;
275 error = aError;
276 errorMsg = aErrorMsg;
277 };
278 otbr::Host::RcpHost host("wpan0", std::vector<const char *>(), /* aBackboneInterfaceName */ "", /* aDryRun */ false,
279 /* aEnableAutoAttach */ false);
280
281 otOperationalDataset dataset;
282 otOperationalDatasetTlvs datasetTlvs;
283
284 // 1. Call ScheduleMigration when host hasn't been initialized.
285 otbr::MainloopManager::GetInstance().RemoveMainloopProcessor(
286 &host); // Temporarily remove RcpHost because it's not initialized yet.
287 host.ScheduleMigration(datasetTlvs, receiver);
288 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
289 EXPECT_EQ(error, OT_ERROR_INVALID_STATE);
290 EXPECT_STREQ(errorMsg.c_str(), "OT is not initialized");
291 otbr::MainloopManager::GetInstance().AddMainloopProcessor(&host);
292
293 host.Init();
294
295 // 2. Call ScheduleMigration when the Thread is not enabled.
296 error = OT_ERROR_NONE;
297 resultReceived = false;
298 host.ScheduleMigration(datasetTlvs, receiver);
299 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
300 EXPECT_EQ(error, OT_ERROR_INVALID_STATE);
301 EXPECT_STREQ(errorMsg.c_str(), "Thread is disabled");
302
303 // 3. Schedule migration to another network.
304 OT_UNUSED_VARIABLE(otDatasetCreateNewNetwork(ot::FakePlatform::CurrentInstance(), &dataset));
305 otDatasetConvertToTlvs(&dataset, &datasetTlvs);
306 OT_UNUSED_VARIABLE(otDatasetSetActiveTlvs(ot::FakePlatform::CurrentInstance(), &datasetTlvs));
307 error = OT_ERROR_NONE;
308 resultReceived = false;
309 host.SetThreadEnabled(true, receiver);
310 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 1,
311 [&host]() { return host.GetDeviceRole() != OT_DEVICE_ROLE_DETACHED; });
312 EXPECT_EQ(host.GetDeviceRole(), OT_DEVICE_ROLE_LEADER);
313
314 host.ScheduleMigration(datasetTlvs, receiver);
315 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
316 EXPECT_EQ(error, OT_ERROR_NONE);
317
318 host.Deinit();
319 }
320
TEST(RcpHostApi,StateChangesCorrectlyAfterJoin)321 TEST(RcpHostApi, StateChangesCorrectlyAfterJoin)
322 {
323 otError error = OT_ERROR_NONE;
324 otError error_ = OT_ERROR_NONE;
325 std::string errorMsg = "";
326 std::string errorMsg_ = "";
327 bool resultReceived = false;
328 bool resultReceived_ = false;
329 otbr::MainloopContext mainloop;
330 otbr::Host::ThreadHost::AsyncResultReceiver receiver = [&resultReceived, &error,
331 &errorMsg](otError aError, const std::string &aErrorMsg) {
332 resultReceived = true;
333 error = aError;
334 errorMsg = aErrorMsg;
335 };
336 otbr::Host::ThreadHost::AsyncResultReceiver receiver_ = [&resultReceived_, &error_,
337 &errorMsg_](otError aError, const std::string &aErrorMsg) {
338 resultReceived_ = true;
339 error_ = aError;
340 errorMsg_ = aErrorMsg;
341 };
342 otbr::Host::RcpHost host("wpan0", std::vector<const char *>(), /* aBackboneInterfaceName */ "", /* aDryRun */ false,
343 /* aEnableAutoAttach */ false);
344
345 otOperationalDataset dataset;
346 (void)dataset;
347 otOperationalDatasetTlvs datasetTlvs;
348
349 // 1. Call Join when host hasn't been initialized.
350 otbr::MainloopManager::GetInstance().RemoveMainloopProcessor(
351 &host); // Temporarily remove RcpHost because it's not initialized yet.
352 host.Join(datasetTlvs, receiver);
353 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
354 EXPECT_EQ(error, OT_ERROR_INVALID_STATE);
355 EXPECT_STREQ(errorMsg.c_str(), "OT is not initialized");
356 otbr::MainloopManager::GetInstance().AddMainloopProcessor(&host);
357
358 host.Init();
359 OT_UNUSED_VARIABLE(otDatasetCreateNewNetwork(ot::FakePlatform::CurrentInstance(), &dataset));
360 otDatasetConvertToTlvs(&dataset, &datasetTlvs);
361
362 // 2. Call Join when Thread is not enabled.
363 error = OT_ERROR_NONE;
364 resultReceived = false;
365 host.Join(datasetTlvs, receiver);
366 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
367 EXPECT_EQ(error, OT_ERROR_INVALID_STATE);
368 EXPECT_STREQ(errorMsg.c_str(), "Thread is not enabled");
369
370 // 3. Call two consecutive Join. The first one should be aborted. The second one should succeed.
371 error = OT_ERROR_NONE;
372 resultReceived = false;
373 host.SetThreadEnabled(true, receiver);
374 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
375 error = OT_ERROR_NONE;
376 resultReceived = false;
377 host.Join(datasetTlvs, receiver_);
378 host.Join(datasetTlvs, receiver);
379
380 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0,
381 [&resultReceived, &resultReceived_]() { return resultReceived && resultReceived_; });
382 EXPECT_EQ(error_, OT_ERROR_ABORT);
383 EXPECT_STREQ(errorMsg_.c_str(), "Aborted by leave/disable operation"); // The second Join will trigger Leave first.
384 EXPECT_EQ(error, OT_ERROR_NONE);
385 EXPECT_STREQ(errorMsg.c_str(), "Join succeeded");
386 EXPECT_EQ(host.GetDeviceRole(), OT_DEVICE_ROLE_LEADER);
387
388 // 4. Call Join with the same dataset.
389 error = OT_ERROR_NONE;
390 resultReceived = false;
391 host.Join(datasetTlvs, receiver);
392 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
393 EXPECT_EQ(error, OT_ERROR_NONE);
394 EXPECT_STREQ(errorMsg.c_str(), "Already Joined the target network");
395
396 // 5. Call Disable right after Join (Already Attached).
397 error = OT_ERROR_NONE;
398 resultReceived = false;
399 error_ = OT_ERROR_NONE;
400 resultReceived_ = false;
401
402 OT_UNUSED_VARIABLE(otDatasetCreateNewNetwork(ot::FakePlatform::CurrentInstance(), &dataset));
403 otDatasetConvertToTlvs(&dataset, &datasetTlvs); // Use a different dataset.
404
405 host.Join(datasetTlvs, receiver_);
406 host.SetThreadEnabled(false, receiver);
407
408 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0,
409 [&resultReceived, &resultReceived_]() { return resultReceived && resultReceived_; });
410 EXPECT_EQ(error_, OT_ERROR_BUSY);
411 EXPECT_STREQ(errorMsg_.c_str(), "Thread is disabling");
412 EXPECT_EQ(error, OT_ERROR_NONE);
413 EXPECT_EQ(host.GetDeviceRole(), OT_DEVICE_ROLE_DISABLED);
414
415 // 6. Call Disable right after Join (not attached).
416 resultReceived = false;
417 host.Leave(true, receiver); // Leave the network first.
418 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
419 resultReceived = false; // Enale Thread.
420 host.SetThreadEnabled(true, receiver);
421 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
422
423 error = OT_ERROR_NONE;
424 resultReceived = false;
425 error_ = OT_ERROR_NONE;
426 resultReceived_ = false;
427 host.Join(datasetTlvs, receiver_);
428 host.SetThreadEnabled(false, receiver);
429
430 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0,
431 [&resultReceived, &resultReceived_]() { return resultReceived && resultReceived_; });
432 EXPECT_EQ(error_, OT_ERROR_ABORT);
433 EXPECT_STREQ(errorMsg_.c_str(), "Aborted by leave/disable operation");
434 EXPECT_EQ(error, OT_ERROR_NONE);
435 EXPECT_EQ(host.GetDeviceRole(), OT_DEVICE_ROLE_DISABLED);
436
437 host.Deinit();
438 }
439