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