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 "ncp/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::MainloopContext mainloop;
70 otbr::Ncp::ThreadHost::AsyncResultReceiver receiver = [&resultReceived, &error](otError aError,
71 const std::string &aErrorMsg) {
72 OT_UNUSED_VARIABLE(aErrorMsg);
73 resultReceived = true;
74 error = aError;
75 };
76 otbr::Ncp::RcpHost host("wpan0", std::vector<const char *>(), /* aBackboneInterfaceName */ "", /* aDryRun */ false,
77 /* aEnableAutoAttach */ false);
78
79 host.Init();
80
81 // 1. Active dataset hasn't been set, should succeed with device role still being disabled.
82 host.SetThreadEnabled(true, receiver);
83 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 1, [&resultReceived]() { return resultReceived; });
84 EXPECT_EQ(error, OT_ERROR_NONE);
85 EXPECT_EQ(host.GetDeviceRole(), OT_DEVICE_ROLE_DISABLED);
86
87 // 2. Set active dataset and enable it
88 {
89 otOperationalDataset dataset;
90 otOperationalDatasetTlvs datasetTlvs;
91 OT_UNUSED_VARIABLE(otDatasetCreateNewNetwork(ot::FakePlatform::CurrentInstance(), &dataset));
92 otDatasetConvertToTlvs(&dataset, &datasetTlvs);
93 OT_UNUSED_VARIABLE(otDatasetSetActiveTlvs(ot::FakePlatform::CurrentInstance(), &datasetTlvs));
94 }
95 error = OT_ERROR_FAILED;
96 resultReceived = false;
97 host.SetThreadEnabled(true, receiver);
98 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 1, [&resultReceived]() { return resultReceived; });
99 EXPECT_EQ(error, OT_ERROR_NONE);
100 EXPECT_EQ(host.GetDeviceRole(), OT_DEVICE_ROLE_DETACHED);
101
102 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 1,
103 [&host]() { return host.GetDeviceRole() != OT_DEVICE_ROLE_DETACHED; });
104 EXPECT_EQ(host.GetDeviceRole(), OT_DEVICE_ROLE_LEADER);
105
106 // 3. Disable it
107 error = OT_ERROR_FAILED;
108 resultReceived = false;
109 host.SetThreadEnabled(false, receiver);
110 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 1, [&resultReceived]() { return resultReceived; });
111 EXPECT_EQ(error, OT_ERROR_NONE);
112 EXPECT_EQ(host.GetDeviceRole(), OT_DEVICE_ROLE_DISABLED);
113
114 // 4. Duplicate call, should get OT_ERROR_BUSY
115 error = OT_ERROR_FAILED;
116 resultReceived = false;
117 otError error2 = OT_ERROR_FAILED;
118 bool resultReceived2 = false;
119 host.SetThreadEnabled(false, receiver);
120 host.SetThreadEnabled(false, [&resultReceived2, &error2](otError aError, const std::string &aErrorMsg) {
121 OT_UNUSED_VARIABLE(aErrorMsg);
122 error2 = aError;
123 resultReceived2 = true;
124 });
125 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 1,
126 [&resultReceived, &resultReceived2]() { return resultReceived && resultReceived2; });
127 EXPECT_EQ(error, OT_ERROR_NONE);
128 EXPECT_EQ(error2, OT_ERROR_BUSY);
129
130 host.Deinit();
131 }
132
TEST(RcpHostApi,SetCountryCodeWorkCorrectly)133 TEST(RcpHostApi, SetCountryCodeWorkCorrectly)
134 {
135 otError error = OT_ERROR_FAILED;
136 bool resultReceived = false;
137 otbr::MainloopContext mainloop;
138 otbr::Ncp::ThreadHost::AsyncResultReceiver receiver = [&resultReceived, &error](otError aError,
139 const std::string &aErrorMsg) {
140 OT_UNUSED_VARIABLE(aErrorMsg);
141 resultReceived = true;
142 error = aError;
143 };
144 otbr::Ncp::RcpHost host("wpan0", std::vector<const char *>(), /* aBackboneInterfaceName */ "", /* aDryRun */ false,
145 /* aEnableAutoAttach */ false);
146
147 // 1. Call SetCountryCode when host hasn't been initialized.
148 otbr::MainloopManager::GetInstance().RemoveMainloopProcessor(
149 &host); // Temporarily remove RcpHost because it's not initialized yet.
150 host.SetCountryCode("AF", receiver);
151 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
152 EXPECT_EQ(error, OT_ERROR_INVALID_STATE);
153 otbr::MainloopManager::GetInstance().AddMainloopProcessor(&host);
154
155 host.Init();
156 // 2. Call SetCountryCode with invalid arguments
157 resultReceived = false;
158 error = OT_ERROR_NONE;
159 host.SetCountryCode("AFA", receiver);
160 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
161 EXPECT_EQ(error, OT_ERROR_INVALID_ARGS);
162
163 resultReceived = false;
164 error = OT_ERROR_NONE;
165 host.SetCountryCode("A", receiver);
166 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
167 EXPECT_EQ(error, OT_ERROR_INVALID_ARGS);
168
169 resultReceived = false;
170 error = OT_ERROR_NONE;
171 host.SetCountryCode("12", receiver);
172 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
173 EXPECT_EQ(error, OT_ERROR_INVALID_ARGS);
174
175 // 3. Call SetCountryCode with valid argument
176 resultReceived = false;
177 error = OT_ERROR_NONE;
178 host.SetCountryCode("AF", receiver);
179 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
180 EXPECT_EQ(error, OT_ERROR_NOT_IMPLEMENTED); // The current platform weak implmentation returns 'NOT_IMPLEMENTED'.
181
182 host.Deinit();
183 }
184
TEST(RcpHostApi,StateChangesCorrectlyAfterScheduleMigration)185 TEST(RcpHostApi, StateChangesCorrectlyAfterScheduleMigration)
186 {
187 otError error = OT_ERROR_NONE;
188 bool resultReceived = false;
189 otbr::MainloopContext mainloop;
190 otbr::Ncp::ThreadHost::AsyncResultReceiver receiver = [&resultReceived, &error](otError aError,
191 const std::string &aErrorMsg) {
192 OT_UNUSED_VARIABLE(aErrorMsg);
193 resultReceived = true;
194 error = aError;
195 };
196 otbr::Ncp::RcpHost host("wpan0", std::vector<const char *>(), /* aBackboneInterfaceName */ "", /* aDryRun */ false,
197 /* aEnableAutoAttach */ false);
198
199 otOperationalDataset dataset;
200 otOperationalDatasetTlvs datasetTlvs;
201
202 // 1. Call ScheduleMigration when host hasn't been initialized.
203 otbr::MainloopManager::GetInstance().RemoveMainloopProcessor(
204 &host); // Temporarily remove RcpHost because it's not initialized yet.
205 host.ScheduleMigration(datasetTlvs, receiver);
206 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
207 EXPECT_EQ(error, OT_ERROR_INVALID_STATE);
208 otbr::MainloopManager::GetInstance().AddMainloopProcessor(&host);
209
210 host.Init();
211
212 // 2. Call ScheduleMigration when the device is not attached.
213 error = OT_ERROR_NONE;
214 resultReceived = false;
215 host.ScheduleMigration(datasetTlvs, receiver);
216 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
217 EXPECT_EQ(error, OT_ERROR_FAILED);
218
219 // 3. Schedule migration to another network.
220 OT_UNUSED_VARIABLE(otDatasetCreateNewNetwork(ot::FakePlatform::CurrentInstance(), &dataset));
221 otDatasetConvertToTlvs(&dataset, &datasetTlvs);
222 OT_UNUSED_VARIABLE(otDatasetSetActiveTlvs(ot::FakePlatform::CurrentInstance(), &datasetTlvs));
223 error = OT_ERROR_NONE;
224 resultReceived = false;
225 host.SetThreadEnabled(true, receiver);
226 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 1,
227 [&host]() { return host.GetDeviceRole() != OT_DEVICE_ROLE_DETACHED; });
228 EXPECT_EQ(host.GetDeviceRole(), OT_DEVICE_ROLE_LEADER);
229
230 host.ScheduleMigration(datasetTlvs, receiver);
231 MainloopProcessUntil(mainloop, /* aTimeoutSec */ 0, [&resultReceived]() { return resultReceived; });
232 EXPECT_EQ(error, OT_ERROR_NONE);
233
234 host.Deinit();
235 }
236