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