1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_kvs/checksum.h"
16 
17 #include "gtest/gtest.h"
18 #include "pw_kvs/crc16_checksum.h"
19 
20 namespace pw::kvs {
21 namespace {
22 
23 using std::byte;
24 
25 constexpr std::string_view kString =
26     "In the beginning the Universe was created. This has made a lot of "
27     "people very angry and been widely regarded as a bad move.";
28 constexpr uint16_t kStringCrc = 0xC184;
29 
TEST(Checksum,UpdateAndVerify)30 TEST(Checksum, UpdateAndVerify) {
31   ChecksumCrc16 crc16_algo;
32   ChecksumAlgorithm& algo = crc16_algo;
33 
34   algo.Update(kString.data(), kString.size());
35   EXPECT_EQ(OkStatus(), algo.Verify(std::as_bytes(std::span(&kStringCrc, 1))));
36 }
37 
TEST(Checksum,Verify_Failure)38 TEST(Checksum, Verify_Failure) {
39   ChecksumCrc16 algo;
40   EXPECT_EQ(Status::DataLoss(),
41             algo.Verify(std::as_bytes(std::span(kString.data(), 2))));
42 }
43 
TEST(Checksum,Verify_InvalidSize)44 TEST(Checksum, Verify_InvalidSize) {
45   ChecksumCrc16 algo;
46   EXPECT_EQ(Status::InvalidArgument(), algo.Verify({}));
47   EXPECT_EQ(Status::InvalidArgument(),
48             algo.Verify(std::as_bytes(std::span(kString.substr(0, 1)))));
49 }
50 
TEST(Checksum,Verify_LargerState_ComparesToTruncatedData)51 TEST(Checksum, Verify_LargerState_ComparesToTruncatedData) {
52   byte crc[3] = {byte{0x84}, byte{0xC1}, byte{0x33}};
53   ChecksumCrc16 algo;
54   ASSERT_GT(sizeof(crc), algo.size_bytes());
55 
56   algo.Update(std::as_bytes(std::span(kString)));
57 
58   EXPECT_EQ(OkStatus(), algo.Verify(crc));
59 }
60 
TEST(Checksum,Reset)61 TEST(Checksum, Reset) {
62   ChecksumCrc16 crc_algo;
63   crc_algo.Update(std::as_bytes(std::span(kString)));
64   crc_algo.Reset();
65 
66   std::span state = crc_algo.Finish();
67   EXPECT_EQ(state[0], byte{0xFF});
68   EXPECT_EQ(state[1], byte{0xFF});
69 }
70 
TEST(IgnoreChecksum,NeverUpdate_VerifyWithoutData)71 TEST(IgnoreChecksum, NeverUpdate_VerifyWithoutData) {
72   IgnoreChecksum checksum;
73 
74   EXPECT_EQ(OkStatus(), checksum.Verify({}));
75 }
76 
TEST(IgnoreChecksum,NeverUpdate_VerifyWithData)77 TEST(IgnoreChecksum, NeverUpdate_VerifyWithData) {
78   IgnoreChecksum checksum;
79 
80   EXPECT_EQ(OkStatus(), checksum.Verify(std::as_bytes(std::span(kString))));
81 }
82 
TEST(IgnoreChecksum,AfterUpdate_Verify)83 TEST(IgnoreChecksum, AfterUpdate_Verify) {
84   IgnoreChecksum checksum;
85 
86   checksum.Update(std::as_bytes(std::span(kString)));
87   EXPECT_EQ(OkStatus(), checksum.Verify({}));
88 }
89 
90 constexpr size_t kAlignment = 10;
91 
92 constexpr std::string_view kData =
93     "123456789_123456789_123456789_123456789_123456789_"   //  50
94     "123456789_123456789_123456789_123456789_123456789_";  // 100
95 const std::span<const byte> kBytes = std::as_bytes(std::span(kData));
96 
97 class PickyChecksum final : public AlignedChecksum<kAlignment, 32> {
98  public:
PickyChecksum()99   PickyChecksum() : AlignedChecksum(data_), data_{}, size_(0) {}
100 
Reset()101   void Reset() override {}
102 
FinalizeAligned()103   void FinalizeAligned() override { EXPECT_EQ(kData.size(), size_); }
104 
UpdateAligned(std::span<const std::byte> data)105   void UpdateAligned(std::span<const std::byte> data) override {
106     ASSERT_EQ(data.size() % kAlignment, 0u);
107     EXPECT_EQ(kData.substr(0, data.size()),
108               std::string_view(reinterpret_cast<const char*>(data.data()),
109                                data.size()));
110 
111     std::memcpy(&data_[size_], data.data(), data.size());
112     size_ += data.size();
113   }
114 
115  private:
116   std::byte data_[kData.size()];
117   size_t size_;
118 };
119 
TEST(AlignedChecksum,MaintainsAlignment)120 TEST(AlignedChecksum, MaintainsAlignment) {
121   PickyChecksum checksum;
122 
123   // Write values smaller than the alignment.
124   checksum.Update(kBytes.subspan(0, 1));
125   checksum.Update(kBytes.subspan(1, 9));
126 
127   // Write values larger than the alignment but smaller than the buffer.
128   checksum.Update(kBytes.subspan(10, 11));
129 
130   // Exactly fill the remainder of the buffer.
131   checksum.Update(kBytes.subspan(21, 11));
132 
133   // Fill the buffer more than once.
134   checksum.Update(kBytes.subspan(32, 66));
135 
136   // Write nothing.
137   checksum.Update(kBytes.subspan(98, 0));
138 
139   // Write the remaining data.
140   checksum.Update(kBytes.subspan(98, 2));
141 
142   auto state = checksum.Finish();
143   EXPECT_EQ(std::string_view(reinterpret_cast<const char*>(state.data()),
144                              state.size()),
145             kData);
146   EXPECT_EQ(OkStatus(), checksum.Verify(kBytes));
147 }
148 
149 }  // namespace
150 }  // namespace pw::kvs
151