#include #include #include "gtest/gtest.h" #include "avb_tools.h" #include "nugget_tools.h" #include "nugget/app/weaver/weaver.pb.h" #include "util.h" #include "Weaver.client.h" #define __STAMP_STR1__(a) #a #define __STAMP_STR__(a) __STAMP_STR1__(a) #define __STAMP__ __STAMP_STR__(__FILE__) ":" __STAMP_STR__(__LINE__) using std::cout; using std::string; using std::unique_ptr; using namespace nugget::app::weaver; namespace { class WeaverTest: public testing::Test { protected: static const uint32_t SLOT_MASK = 0x3f; static std::random_device random_number_generator; static uint32_t slot; static unique_ptr client; static unique_ptr uart_printer; static void SetUpTestCase(); static void TearDownTestCase(); void testWrite(const string& msg, uint32_t slot, const uint8_t *key, const uint8_t *value); void testRead(const string& msg, const uint32_t slot, const uint8_t *key, const uint8_t *value); void testEraseValue(const string& msg, uint32_t slot); void testReadWrongKey(const string& msg, uint32_t slot, const uint8_t *key, uint32_t throttle_sec); void testReadThrottle(const string& msg, uint32_t slot, const uint8_t *key, uint32_t throttle_sec); void activateThrottle(uint32_t slot, const uint8_t *key, const uint8_t *wrong_key, uint32_t throttle_sec); public: static constexpr size_t KEY_SIZE = 16; static constexpr size_t VALUE_SIZE = 16; const uint8_t TEST_KEY[KEY_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; const uint8_t WRONG_KEY[KEY_SIZE] = {100, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; const uint8_t TEST_VALUE[VALUE_SIZE] = {1, 0, 3, 0, 5, 0, 7, 0, 0, 10, 0, 12, 0, 14, 0, 16}; const uint8_t ZERO_VALUE[VALUE_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; }; std::random_device WeaverTest::random_number_generator; // Randomly select a slot for the test rather than testing all slots to reduce // the wear on the flash. All slots behave the same, independently of each // other. uint32_t WeaverTest::slot = WeaverTest::random_number_generator() & SLOT_MASK; unique_ptr WeaverTest::client; unique_ptr WeaverTest::uart_printer; void WeaverTest::SetUpTestCase() { uart_printer = test_harness::TestHarness::MakeUnique(); client = nugget_tools::MakeNuggetClient(); client->Open(); EXPECT_TRUE(client->IsOpen()) << "Unable to connect"; } void WeaverTest::TearDownTestCase() { client->Close(); client = unique_ptr(); uart_printer = nullptr; } void WeaverTest::testWrite(const string& msg, uint32_t slot, const uint8_t *key, const uint8_t *value) { WriteRequest request; WriteResponse response; request.set_slot(slot); request.set_key(key, KEY_SIZE); request.set_value(value, VALUE_SIZE); Weaver service(*client); ASSERT_NO_ERROR(service.Write(request, &response), msg); } void WeaverTest::testRead(const string& msg, uint32_t slot, const uint8_t *key, const uint8_t *value) { ReadRequest request; ReadResponse response; request.set_slot(slot); request.set_key(key, KEY_SIZE); Weaver service(*client); ASSERT_NO_ERROR(service.Read(request, &response), msg); ASSERT_EQ(response.error(), ReadResponse::NONE) << msg; ASSERT_EQ(response.throttle_msec(), 0u) << msg; auto response_value = response.value(); for (size_t x = 0; x < KEY_SIZE; ++x) { ASSERT_EQ(response_value[x], value[x]) << "Inconsistency at index " << x <<" " << msg; } } void WeaverTest::testEraseValue(const string& msg, uint32_t slot) { EraseValueRequest request; EraseValueResponse response; request.set_slot(slot); Weaver service(*client); ASSERT_NO_ERROR(service.EraseValue(request, &response), msg); } void WeaverTest::testReadWrongKey(const string& msg, uint32_t slot, const uint8_t *key, uint32_t throttle_sec) { ReadRequest request; ReadResponse response; request.set_slot(slot); request.set_key(key, KEY_SIZE); Weaver service(*client); ASSERT_NO_ERROR(service.Read(request, &response), msg); ASSERT_EQ(response.error(), ReadResponse::WRONG_KEY) << msg; ASSERT_EQ(response.throttle_msec(), throttle_sec * 1000) << msg; auto response_value = response.value(); for (size_t x = 0; x < response_value.size(); ++x) { ASSERT_EQ(response_value[x], 0) << "Inconsistency at index " << x <<" " << msg; } } void WeaverTest::testReadThrottle(const string& msg, uint32_t slot, const uint8_t *key, uint32_t throttle_sec) { ReadRequest request; ReadResponse response; request.set_slot(slot); request.set_key(key, KEY_SIZE); Weaver service(*client); ASSERT_NO_ERROR(service.Read(request, &response), msg); ASSERT_EQ(response.error(), ReadResponse::THROTTLE) << msg; ASSERT_NE(response.throttle_msec(), 0u) << msg; ASSERT_LE(response.throttle_msec(), throttle_sec * 1000) << msg; auto response_value = response.value(); for (size_t x = 0; x < response_value.size(); ++x) { ASSERT_EQ(response_value[x], 0) << "Inconsistency at index " << x <<" " << msg; } } void WeaverTest::activateThrottle(uint32_t slot, const uint8_t *key, const uint8_t *wrong_key, uint32_t throttle_sec) { // Reset the slot testWrite(__STAMP__, slot, key, TEST_VALUE); // First throttle comes after 5 attempts testReadWrongKey(__STAMP__, slot, wrong_key, 0); testReadWrongKey(__STAMP__, slot, wrong_key, 0); testReadWrongKey(__STAMP__, slot, wrong_key, 0); testReadWrongKey(__STAMP__, slot, wrong_key, 0); testReadWrongKey(__STAMP__, slot, wrong_key, throttle_sec); } TEST_F(WeaverTest, GetConfig) { GetConfigRequest request; GetConfigResponse response; Weaver service(*client); ASSERT_NO_ERROR(service.GetConfig(request, &response), ""); EXPECT_EQ(response.number_of_slots(), 64u); EXPECT_EQ(response.key_size(), 16u); EXPECT_EQ(response.value_size(), 16u); } TEST_F(WeaverTest, WriteReadErase) { const uint8_t ZERO_VALUE[16] = {0}; testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE); testRead(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE); testEraseValue(__STAMP__, WeaverTest::slot); testRead(__STAMP__, WeaverTest::slot, TEST_KEY, ZERO_VALUE); } // 5 slots per record TEST_F(WeaverTest, WriteToMultipleSlotsInSameRecordIncreasingOrder) { testWrite(__STAMP__, 0, TEST_KEY, TEST_VALUE); testWrite(__STAMP__, 1, TEST_KEY, TEST_VALUE); testRead(__STAMP__, 0, TEST_KEY, TEST_VALUE); testRead(__STAMP__, 1, TEST_KEY, TEST_VALUE); } TEST_F(WeaverTest, WriteToMultipleSlotsInSameRecordDecreasingOrder) { testWrite(__STAMP__, 8, TEST_KEY, TEST_VALUE); testWrite(__STAMP__, 7, TEST_KEY, TEST_VALUE); testRead(__STAMP__, 8, TEST_KEY, TEST_VALUE); testRead(__STAMP__, 7, TEST_KEY, TEST_VALUE); } TEST_F(WeaverTest, WriteToMultipleSlotsInDifferentRecordsIncreasingOrder) { testWrite(__STAMP__, 9, TEST_KEY, TEST_VALUE); testWrite(__STAMP__, 10, TEST_KEY, TEST_VALUE); testRead(__STAMP__, 9, TEST_KEY, TEST_VALUE); testRead(__STAMP__, 10, TEST_KEY, TEST_VALUE); } TEST_F(WeaverTest, WriteToMultipleSlotsInDifferentRecordsDecreasingOrder) { testWrite(__STAMP__, 5, TEST_KEY, TEST_VALUE); testWrite(__STAMP__, 4, TEST_KEY, TEST_VALUE); testRead(__STAMP__, 4, TEST_KEY, TEST_VALUE); testRead(__STAMP__, 5, TEST_KEY, TEST_VALUE); } TEST_F(WeaverTest, WriteDeepSleepRead) { testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE); ASSERT_TRUE(nugget_tools::WaitForSleep(client.get(), 0)); testRead(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE); } TEST_F(WeaverTest, WriteHardRebootRead) { testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE); ASSERT_TRUE(nugget_tools::RebootNugget(client.get())); testRead(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE); } TEST_F(WeaverTest, ReadThrottle) { activateThrottle(WeaverTest::slot, TEST_KEY, WRONG_KEY, 30); testReadThrottle(__STAMP__, WeaverTest::slot, WRONG_KEY, 30); } TEST_F(WeaverTest, ReadThrottleAfterDeepSleep) { activateThrottle(WeaverTest::slot, TEST_KEY, WRONG_KEY, 30); ASSERT_TRUE(nugget_tools::WaitForSleep(client.get(), 0)); testReadThrottle(__STAMP__, WeaverTest::slot, WRONG_KEY, 30); } TEST_F(WeaverTest, ReadThrottleAfterHardReboot) { activateThrottle(WeaverTest::slot, TEST_KEY, WRONG_KEY, 30); ASSERT_TRUE(nugget_tools::RebootNugget(client.get())); testReadThrottle(__STAMP__, WeaverTest::slot, WRONG_KEY, 30); } TEST_F(WeaverTest, ReadThrottleAfterSleep) { uint32_t waited = 0; activateThrottle(WeaverTest::slot, TEST_KEY, WRONG_KEY, 30); ASSERT_TRUE(nugget_tools::WaitForSleep(client.get(), &waited)); testReadThrottle(__STAMP__, WeaverTest::slot, WRONG_KEY, 30 - waited); } TEST_F(WeaverTest, ReadAttemptCounterPersistsDeepSleep) { testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0); ASSERT_TRUE(nugget_tools::WaitForSleep(client.get(), 0)); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 30); } TEST_F(WeaverTest, ReadAttemptCounterPersistsHardReboot) { testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0); ASSERT_TRUE(nugget_tools::RebootNugget(client.get())); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 0); testReadWrongKey(__STAMP__, WeaverTest::slot, WRONG_KEY, 30); } TEST_F(WeaverTest, ReadInvalidSlot) { ReadRequest request; request.set_slot(std::numeric_limits::max() - 3); request.set_key(TEST_KEY, sizeof(TEST_KEY)); Weaver service(*client); ASSERT_EQ(service.Read(request, nullptr), APP_ERROR_BOGUS_ARGS); } TEST_F(WeaverTest, WriteInvalidSlot) { WriteRequest request; request.set_slot(std::numeric_limits::max() - 5); request.set_key(TEST_KEY, sizeof(TEST_KEY)); request.set_value(TEST_VALUE, sizeof(TEST_VALUE)); Weaver service(*client); ASSERT_EQ(service.Write(request, nullptr), APP_ERROR_BOGUS_ARGS); } TEST_F(WeaverTest, EraseValueInvalidSlot) { EraseValueRequest request; request.set_slot(std::numeric_limits::max() - 8); Weaver service(*client); ASSERT_EQ(service.EraseValue(request, nullptr), APP_ERROR_BOGUS_ARGS); } TEST_F(WeaverTest, WipeUserDataOnlyClearsValues) { testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE); ASSERT_TRUE(nugget_tools::WipeUserData(client.get())); testRead(__STAMP__, WeaverTest::slot, TEST_KEY, ZERO_VALUE); } TEST_F(WeaverTest, ProductionResetWipesUserData) { avb_tools::SetProduction(client.get(), true, NULL, 0); testWrite(__STAMP__, WeaverTest::slot, TEST_KEY, TEST_VALUE); avb_tools::ResetProduction(client.get()); testRead(__STAMP__, WeaverTest::slot, TEST_KEY, ZERO_VALUE); } // Regression tests TEST_F(WeaverTest, WipeUserDataWriteSlot0ReadSlot1) { testWrite(__STAMP__, 0, TEST_KEY, TEST_VALUE); testWrite(__STAMP__, 1, TEST_KEY, TEST_VALUE); ASSERT_TRUE(nugget_tools::WipeUserData(client.get())); testWrite(__STAMP__, 0, TEST_KEY, TEST_VALUE); testRead(__STAMP__, 1, TEST_KEY, ZERO_VALUE); } } // namespace