1
2 #include <memory>
3 #include <random>
4
5 #include "gtest/gtest.h"
6 #include "avb_tools.h"
7 #include "nugget_tools.h"
8 #include "nugget/app/weaver/weaver.pb.h"
9 #include "util.h"
10 #include "Weaver.client.h"
11
12 #define __STAMP_STR1__(a) #a
13 #define __STAMP_STR__(a) __STAMP_STR1__(a)
14 #define __STAMP__ __STAMP_STR__(__FILE__) ":" __STAMP_STR__(__LINE__)
15
16 using std::cout;
17 using std::string;
18 using std::unique_ptr;
19
20 using namespace nugget::app::weaver;
21
22 namespace {
23
24 class WeaverTest: public testing::Test {
25 protected:
26 static const uint32_t SLOT_MASK = 0x3f;
27 static std::random_device random_number_generator;
28 static uint32_t slot;
29
30 static unique_ptr<nos::NuggetClientInterface> client;
31 static unique_ptr<test_harness::TestHarness> uart_printer;
32
33 static void SetUpTestCase();
34 static void TearDownTestCase();
35
36 void testWrite(const string& msg, uint32_t slot, const uint8_t *key,
37 const uint8_t *value);
38 void testRead(const string& msg, const uint32_t slot, const uint8_t *key,
39 const uint8_t *value);
40 void testEraseValue(const string& msg, uint32_t slot);
41 void testReadWrongKey(const string& msg, uint32_t slot, const uint8_t *key,
42 uint32_t throttle_sec);
43 void testReadThrottle(const string& msg, uint32_t slot, const uint8_t *key,
44 uint32_t throttle_sec);
45
46 void activateThrottle(uint32_t slot, const uint8_t *key,
47 const uint8_t *wrong_key, uint32_t throttle_sec);
48 public:
49 static constexpr size_t KEY_SIZE = 16;
50 static constexpr size_t VALUE_SIZE = 16;
51 const uint8_t TEST_KEY[KEY_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8,
52 9, 10, 11, 12, 13, 14, 15, 16};
53 const uint8_t WRONG_KEY[KEY_SIZE] = {100, 2, 3, 4, 5, 6, 7, 8,
54 9, 10, 11, 12, 13, 14, 15, 16};
55 const uint8_t TEST_VALUE[VALUE_SIZE] = {1, 0, 3, 0, 5, 0, 7, 0,
56 0, 10, 0, 12, 0, 14, 0, 16};
57 const uint8_t ZERO_VALUE[VALUE_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0,
58 0, 0, 0, 0, 0, 0, 0, 0};
59 };
60
61 std::random_device WeaverTest::random_number_generator;
62 // Randomly select a slot for the test rather than testing all slots to reduce
63 // the wear on the flash. All slots behave the same, independently of each
64 // other.
65 uint32_t WeaverTest::slot = WeaverTest::random_number_generator() & SLOT_MASK;
66
67 unique_ptr<nos::NuggetClientInterface> WeaverTest::client;
68 unique_ptr<test_harness::TestHarness> WeaverTest::uart_printer;
69
SetUpTestCase()70 void WeaverTest::SetUpTestCase() {
71 uart_printer = test_harness::TestHarness::MakeUnique();
72
73 client = nugget_tools::MakeNuggetClient();
74 client->Open();
75 EXPECT_TRUE(client->IsOpen()) << "Unable to connect";
76 }
77
TearDownTestCase()78 void WeaverTest::TearDownTestCase() {
79 client->Close();
80 client = unique_ptr<nos::NuggetClientInterface>();
81
82 uart_printer = nullptr;
83 }
84
testWrite(const string & msg,uint32_t slot,const uint8_t * key,const uint8_t * value)85 void WeaverTest::testWrite(const string& msg, uint32_t slot, const uint8_t *key,
86 const uint8_t *value) {
87 WriteRequest request;
88 WriteResponse response;
89 request.set_slot(slot);
90 request.set_key(key, KEY_SIZE);
91 request.set_value(value, VALUE_SIZE);
92
93 Weaver service(*client);
94 ASSERT_NO_ERROR(service.Write(request, &response), msg);
95 }
96
testRead(const string & msg,uint32_t slot,const uint8_t * key,const uint8_t * value)97 void WeaverTest::testRead(const string& msg, uint32_t slot, const uint8_t *key,
98 const uint8_t *value) {
99 ReadRequest request;
100 ReadResponse response;
101 request.set_slot(slot);
102 request.set_key(key, KEY_SIZE);
103
104 Weaver service(*client);
105 ASSERT_NO_ERROR(service.Read(request, &response), msg);
106 ASSERT_EQ(response.error(), ReadResponse::NONE) << msg;
107 ASSERT_EQ(response.throttle_msec(), 0u) << msg;
108 auto response_value = response.value();
109 for (size_t x = 0; x < KEY_SIZE; ++x) {
110 ASSERT_EQ(response_value[x], value[x]) << "Inconsistency at index " << x
111 <<" " << msg;
112 }
113 }
114
testEraseValue(const string & msg,uint32_t slot)115 void WeaverTest::testEraseValue(const string& msg, uint32_t slot) {
116 EraseValueRequest request;
117 EraseValueResponse response;
118 request.set_slot(slot);
119
120 Weaver service(*client);
121 ASSERT_NO_ERROR(service.EraseValue(request, &response), msg);
122 }
123
testReadWrongKey(const string & msg,uint32_t slot,const uint8_t * key,uint32_t throttle_sec)124 void WeaverTest::testReadWrongKey(const string& msg, uint32_t slot,
125 const uint8_t *key, uint32_t throttle_sec) {
126 ReadRequest request;
127 ReadResponse response;
128 request.set_slot(slot);
129 request.set_key(key, KEY_SIZE);
130
131 Weaver service(*client);
132 ASSERT_NO_ERROR(service.Read(request, &response), msg);
133 ASSERT_EQ(response.error(), ReadResponse::WRONG_KEY) << msg;
134 ASSERT_EQ(response.throttle_msec(), throttle_sec * 1000) << msg;
135 auto response_value = response.value();
136 for (size_t x = 0; x < response_value.size(); ++x) {
137 ASSERT_EQ(response_value[x], 0) << "Inconsistency at index " << x
138 <<" " << msg;
139 }
140 }
141
testReadThrottle(const string & msg,uint32_t slot,const uint8_t * key,uint32_t throttle_sec)142 void WeaverTest::testReadThrottle(const string& msg, uint32_t slot,
143 const uint8_t *key, uint32_t throttle_sec) {
144 ReadRequest request;
145 ReadResponse response;
146 request.set_slot(slot);
147 request.set_key(key, KEY_SIZE);
148
149 Weaver service(*client);
150 ASSERT_NO_ERROR(service.Read(request, &response), msg);
151 ASSERT_EQ(response.error(), ReadResponse::THROTTLE) << msg;
152 ASSERT_NE(response.throttle_msec(), 0u) << msg;
153 ASSERT_LE(response.throttle_msec(), throttle_sec * 1000) << msg;
154 auto response_value = response.value();
155 for (size_t x = 0; x < response_value.size(); ++x) {
156 ASSERT_EQ(response_value[x], 0) << "Inconsistency at index " << x
157 <<" " << msg;
158 }
159 }
160
activateThrottle(uint32_t slot,const uint8_t * key,const uint8_t * wrong_key,uint32_t throttle_sec)161 void WeaverTest::activateThrottle(uint32_t slot, const uint8_t *key,
162 const uint8_t *wrong_key,
163 uint32_t throttle_sec) {
164 // Reset the slot
165 testWrite(__STAMP__, slot, key, TEST_VALUE);
166
167 // First throttle comes after 5 attempts
168 testReadWrongKey(__STAMP__, slot, wrong_key, 0);
169 testReadWrongKey(__STAMP__, slot, wrong_key, 0);
170 testReadWrongKey(__STAMP__, slot, wrong_key, 0);
171 testReadWrongKey(__STAMP__, slot, wrong_key, 0);
172 testReadWrongKey(__STAMP__, slot, wrong_key, throttle_sec);
173 }
174
TEST_F(WeaverTest,GetConfig)175 TEST_F(WeaverTest, GetConfig) {
176 GetConfigRequest request;
177 GetConfigResponse response;
178
179 Weaver service(*client);
180 ASSERT_NO_ERROR(service.GetConfig(request, &response), "");
181 EXPECT_EQ(response.number_of_slots(), 64u);
182 EXPECT_EQ(response.key_size(), 16u);
183 EXPECT_EQ(response.value_size(), 16u);
184 }
185
TEST_F(WeaverTest,WriteReadErase)186 TEST_F(WeaverTest, WriteReadErase) {
187 const uint8_t ZERO_VALUE[16] = {0};
188
189 testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE);
190 testRead(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE);
191 testEraseValue(__STAMP__, WeaverTest::slot);
192
193 testRead(__STAMP__, WeaverTest::slot, TEST_KEY, ZERO_VALUE);
194 }
195
196 // 5 slots per record
TEST_F(WeaverTest,WriteToMultipleSlotsInSameRecordIncreasingOrder)197 TEST_F(WeaverTest, WriteToMultipleSlotsInSameRecordIncreasingOrder) {
198 testWrite(__STAMP__, 0, TEST_KEY, TEST_VALUE);
199 testWrite(__STAMP__, 1, TEST_KEY, TEST_VALUE);
200
201 testRead(__STAMP__, 0, TEST_KEY, TEST_VALUE);
202 testRead(__STAMP__, 1, TEST_KEY, TEST_VALUE);
203 }
204
TEST_F(WeaverTest,WriteToMultipleSlotsInSameRecordDecreasingOrder)205 TEST_F(WeaverTest, WriteToMultipleSlotsInSameRecordDecreasingOrder) {
206 testWrite(__STAMP__, 8, TEST_KEY, TEST_VALUE);
207 testWrite(__STAMP__, 7, TEST_KEY, TEST_VALUE);
208
209 testRead(__STAMP__, 8, TEST_KEY, TEST_VALUE);
210 testRead(__STAMP__, 7, TEST_KEY, TEST_VALUE);
211 }
212
TEST_F(WeaverTest,WriteToMultipleSlotsInDifferentRecordsIncreasingOrder)213 TEST_F(WeaverTest, WriteToMultipleSlotsInDifferentRecordsIncreasingOrder) {
214 testWrite(__STAMP__, 9, TEST_KEY, TEST_VALUE);
215 testWrite(__STAMP__, 10, TEST_KEY, TEST_VALUE);
216
217 testRead(__STAMP__, 9, TEST_KEY, TEST_VALUE);
218 testRead(__STAMP__, 10, TEST_KEY, TEST_VALUE);
219 }
220
TEST_F(WeaverTest,WriteToMultipleSlotsInDifferentRecordsDecreasingOrder)221 TEST_F(WeaverTest, WriteToMultipleSlotsInDifferentRecordsDecreasingOrder) {
222 testWrite(__STAMP__, 5, TEST_KEY, TEST_VALUE);
223 testWrite(__STAMP__, 4, TEST_KEY, TEST_VALUE);
224
225 testRead(__STAMP__, 4, TEST_KEY, TEST_VALUE);
226 testRead(__STAMP__, 5, TEST_KEY, TEST_VALUE);
227 }
228
TEST_F(WeaverTest,WriteDeepSleepRead)229 TEST_F(WeaverTest, WriteDeepSleepRead) {
230 testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE);
231 ASSERT_TRUE(nugget_tools::WaitForSleep(client.get(), 0));
232 testRead(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE);
233 }
234
TEST_F(WeaverTest,WriteHardRebootRead)235 TEST_F(WeaverTest, WriteHardRebootRead) {
236 testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE);
237 ASSERT_TRUE(nugget_tools::RebootNugget(client.get()));
238 testRead(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE);
239 }
240
TEST_F(WeaverTest,ReadThrottle)241 TEST_F(WeaverTest, ReadThrottle) {
242 activateThrottle(WeaverTest::slot, TEST_KEY, WRONG_KEY, 30);
243 testReadThrottle(__STAMP__, WeaverTest::slot, WRONG_KEY, 30);
244 }
245
TEST_F(WeaverTest,ReadThrottleAfterDeepSleep)246 TEST_F(WeaverTest, ReadThrottleAfterDeepSleep) {
247 activateThrottle(WeaverTest::slot, TEST_KEY, WRONG_KEY, 30);
248 ASSERT_TRUE(nugget_tools::WaitForSleep(client.get(), 0));
249 testReadThrottle(__STAMP__, WeaverTest::slot, WRONG_KEY, 30);
250 }
251
TEST_F(WeaverTest,ReadThrottleAfterHardReboot)252 TEST_F(WeaverTest, ReadThrottleAfterHardReboot) {
253 activateThrottle(WeaverTest::slot, TEST_KEY, WRONG_KEY, 30);
254 ASSERT_TRUE(nugget_tools::RebootNugget(client.get()));
255 testReadThrottle(__STAMP__, WeaverTest::slot, WRONG_KEY, 30);
256 }
257
TEST_F(WeaverTest,ReadThrottleAfterSleep)258 TEST_F(WeaverTest, ReadThrottleAfterSleep) {
259 uint32_t waited = 0;
260 activateThrottle(WeaverTest::slot, TEST_KEY, WRONG_KEY, 30);
261 ASSERT_TRUE(nugget_tools::WaitForSleep(client.get(), &waited));
262 testReadThrottle(__STAMP__, WeaverTest::slot, WRONG_KEY, 30 - waited);
263 }
264
TEST_F(WeaverTest,ReadAttemptCounterPersistsDeepSleep)265 TEST_F(WeaverTest, ReadAttemptCounterPersistsDeepSleep) {
266 testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE);
267
268 testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0);
269 testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0);
270 testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0);
271
272 ASSERT_TRUE(nugget_tools::WaitForSleep(client.get(), 0));
273
274 testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0);
275 testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 30);
276 }
277
TEST_F(WeaverTest,ReadAttemptCounterPersistsHardReboot)278 TEST_F(WeaverTest, ReadAttemptCounterPersistsHardReboot) {
279 testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE);
280
281 testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0);
282 testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0);
283
284 ASSERT_TRUE(nugget_tools::RebootNugget(client.get()));
285
286 testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0);
287 testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0);
288 testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 30);
289 }
290
TEST_F(WeaverTest,ReadInvalidSlot)291 TEST_F(WeaverTest, ReadInvalidSlot) {
292 ReadRequest request;
293 request.set_slot(std::numeric_limits<uint32_t>::max() - 3);
294 request.set_key(TEST_KEY, sizeof(TEST_KEY));
295
296 Weaver service(*client);
297 ASSERT_EQ(service.Read(request, nullptr), APP_ERROR_BOGUS_ARGS);
298 }
299
TEST_F(WeaverTest,WriteInvalidSlot)300 TEST_F(WeaverTest, WriteInvalidSlot) {
301 WriteRequest request;
302 request.set_slot(std::numeric_limits<uint32_t>::max() - 5);
303 request.set_key(TEST_KEY, sizeof(TEST_KEY));
304 request.set_value(TEST_VALUE, sizeof(TEST_VALUE));
305
306 Weaver service(*client);
307 ASSERT_EQ(service.Write(request, nullptr), APP_ERROR_BOGUS_ARGS);
308 }
309
TEST_F(WeaverTest,EraseValueInvalidSlot)310 TEST_F(WeaverTest, EraseValueInvalidSlot) {
311 EraseValueRequest request;
312 request.set_slot(std::numeric_limits<uint32_t>::max() - 8);
313
314 Weaver service(*client);
315 ASSERT_EQ(service.EraseValue(request, nullptr), APP_ERROR_BOGUS_ARGS);
316 }
317
TEST_F(WeaverTest,WipeUserDataOnlyClearsValues)318 TEST_F(WeaverTest, WipeUserDataOnlyClearsValues) {
319 testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE);
320 ASSERT_TRUE(nugget_tools::WipeUserData(client.get()));
321 testRead(__STAMP__, WeaverTest::slot, TEST_KEY, ZERO_VALUE);
322 }
323
TEST_F(WeaverTest,ProductionResetWipesUserData)324 TEST_F(WeaverTest, ProductionResetWipesUserData) {
325 avb_tools::SetProduction(client.get(), true, NULL, 0);
326 testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE);
327 avb_tools::ResetProduction(client.get());
328 testRead(__STAMP__, WeaverTest::slot, TEST_KEY, ZERO_VALUE);
329 }
330
331 // Regression tests
TEST_F(WeaverTest,WipeUserDataWriteSlot0ReadSlot1)332 TEST_F(WeaverTest, WipeUserDataWriteSlot0ReadSlot1) {
333 testWrite(__STAMP__, 0, TEST_KEY, TEST_VALUE);
334 testWrite(__STAMP__, 1, TEST_KEY, TEST_VALUE);
335 ASSERT_TRUE(nugget_tools::WipeUserData(client.get()));
336 testWrite(__STAMP__, 0, TEST_KEY, TEST_VALUE);
337 testRead(__STAMP__, 1, TEST_KEY, ZERO_VALUE);
338 }
339
340 } // namespace
341