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
16using std::cout;
17using std::string;
18using std::unique_ptr;
19
20using namespace nugget::app::weaver;
21
22namespace {
23
24class 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
61std::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.
65uint32_t WeaverTest::slot = WeaverTest::random_number_generator() & SLOT_MASK;
66
67unique_ptr<nos::NuggetClientInterface> WeaverTest::client;
68unique_ptr<test_harness::TestHarness> WeaverTest::uart_printer;
69
70void 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
78void WeaverTest::TearDownTestCase() {
79  client->Close();
80  client = unique_ptr<nos::NuggetClientInterface>();
81
82  uart_printer = nullptr;
83}
84
85void 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
97void 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
115void 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
124void 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
142void 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
161void 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
175TEST_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
186TEST_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
197TEST_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
205TEST_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
213TEST_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
221TEST_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
229TEST_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
235TEST_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
241TEST_F(WeaverTest, ReadThrottle) {
242  activateThrottle(WeaverTest::slot, TEST_KEY, WRONG_KEY, 30);
243  testReadThrottle(__STAMP__, WeaverTest::slot, WRONG_KEY, 30);
244}
245
246TEST_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
252TEST_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
258TEST_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
265TEST_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
278TEST_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
291TEST_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
300TEST_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
310TEST_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
318TEST_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
324TEST_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
332TEST_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