// Copyright 2020 The Pigweed Authors // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. #include "pw_kvs/checksum.h" #include "gtest/gtest.h" #include "pw_kvs/crc16_checksum.h" namespace pw::kvs { namespace { using std::byte; constexpr std::string_view kString = "In the beginning the Universe was created. This has made a lot of " "people very angry and been widely regarded as a bad move."; constexpr uint16_t kStringCrc = 0xC184; TEST(Checksum, UpdateAndVerify) { ChecksumCrc16 crc16_algo; ChecksumAlgorithm& algo = crc16_algo; algo.Update(kString.data(), kString.size()); EXPECT_EQ(OkStatus(), algo.Verify(std::as_bytes(std::span(&kStringCrc, 1)))); } TEST(Checksum, Verify_Failure) { ChecksumCrc16 algo; EXPECT_EQ(Status::DataLoss(), algo.Verify(std::as_bytes(std::span(kString.data(), 2)))); } TEST(Checksum, Verify_InvalidSize) { ChecksumCrc16 algo; EXPECT_EQ(Status::InvalidArgument(), algo.Verify({})); EXPECT_EQ(Status::InvalidArgument(), algo.Verify(std::as_bytes(std::span(kString.substr(0, 1))))); } TEST(Checksum, Verify_LargerState_ComparesToTruncatedData) { byte crc[3] = {byte{0x84}, byte{0xC1}, byte{0x33}}; ChecksumCrc16 algo; ASSERT_GT(sizeof(crc), algo.size_bytes()); algo.Update(std::as_bytes(std::span(kString))); EXPECT_EQ(OkStatus(), algo.Verify(crc)); } TEST(Checksum, Reset) { ChecksumCrc16 crc_algo; crc_algo.Update(std::as_bytes(std::span(kString))); crc_algo.Reset(); std::span state = crc_algo.Finish(); EXPECT_EQ(state[0], byte{0xFF}); EXPECT_EQ(state[1], byte{0xFF}); } TEST(IgnoreChecksum, NeverUpdate_VerifyWithoutData) { IgnoreChecksum checksum; EXPECT_EQ(OkStatus(), checksum.Verify({})); } TEST(IgnoreChecksum, NeverUpdate_VerifyWithData) { IgnoreChecksum checksum; EXPECT_EQ(OkStatus(), checksum.Verify(std::as_bytes(std::span(kString)))); } TEST(IgnoreChecksum, AfterUpdate_Verify) { IgnoreChecksum checksum; checksum.Update(std::as_bytes(std::span(kString))); EXPECT_EQ(OkStatus(), checksum.Verify({})); } constexpr size_t kAlignment = 10; constexpr std::string_view kData = "123456789_123456789_123456789_123456789_123456789_" // 50 "123456789_123456789_123456789_123456789_123456789_"; // 100 const std::span kBytes = std::as_bytes(std::span(kData)); class PickyChecksum final : public AlignedChecksum { public: PickyChecksum() : AlignedChecksum(data_), data_{}, size_(0) {} void Reset() override {} void FinalizeAligned() override { EXPECT_EQ(kData.size(), size_); } void UpdateAligned(std::span data) override { ASSERT_EQ(data.size() % kAlignment, 0u); EXPECT_EQ(kData.substr(0, data.size()), std::string_view(reinterpret_cast(data.data()), data.size())); std::memcpy(&data_[size_], data.data(), data.size()); size_ += data.size(); } private: std::byte data_[kData.size()]; size_t size_; }; TEST(AlignedChecksum, MaintainsAlignment) { PickyChecksum checksum; // Write values smaller than the alignment. checksum.Update(kBytes.subspan(0, 1)); checksum.Update(kBytes.subspan(1, 9)); // Write values larger than the alignment but smaller than the buffer. checksum.Update(kBytes.subspan(10, 11)); // Exactly fill the remainder of the buffer. checksum.Update(kBytes.subspan(21, 11)); // Fill the buffer more than once. checksum.Update(kBytes.subspan(32, 66)); // Write nothing. checksum.Update(kBytes.subspan(98, 0)); // Write the remaining data. checksum.Update(kBytes.subspan(98, 2)); auto state = checksum.Finish(); EXPECT_EQ(std::string_view(reinterpret_cast(state.data()), state.size()), kData); EXPECT_EQ(OkStatus(), checksum.Verify(kBytes)); } } // namespace } // namespace pw::kvs