xref: /aosp_15_r20/external/ot-br-posix/tests/gtest/test_rcp_host_api.cpp (revision 4a64e381480ef79f0532b2421e44e6ee336b8e0d)
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