xref: /aosp_15_r20/external/pigweed/pw_hex_dump/hex_dump_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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_hex_dump/hex_dump.h"
16 
17 #include <array>
18 #include <cinttypes>
19 #include <cstdint>
20 #include <cstring>
21 #include <string_view>
22 
23 #include "pw_log/log.h"
24 #include "pw_span/span.h"
25 #include "pw_unit_test/framework.h"
26 
27 namespace pw::dump {
28 namespace {
29 
30 std::array<const std::byte, 33> source_data = {
31     std::byte(0xa4), std::byte(0xcc), std::byte(0x32), std::byte(0x62),
32     std::byte(0x9b), std::byte(0x46), std::byte(0x38), std::byte(0x1a),
33     std::byte(0x23), std::byte(0x1a), std::byte(0x2a), std::byte(0x7a),
34     std::byte(0xbc), std::byte(0xe2), std::byte(0x40), std::byte(0xa0),
35     std::byte(0xff), std::byte(0x33), std::byte(0xe5), std::byte(0x2b),
36     std::byte(0x9e), std::byte(0x9f), std::byte(0x6b), std::byte(0x3c),
37     std::byte(0xbe), std::byte(0x9b), std::byte(0x89), std::byte(0x3c),
38     std::byte(0x7e), std::byte(0x4a), std::byte(0x7a), std::byte(0x48),
39     std::byte(0x18)};
40 
41 std::array<const std::byte, 15> short_string = {
42     std::byte('m'),
43     std::byte('y'),
44     std::byte(' '),
45     std::byte('t'),
46     std::byte('e'),
47     std::byte('s'),
48     std::byte('t'),
49     std::byte(' '),
50     std::byte('s'),
51     std::byte('t'),
52     std::byte('r'),
53     std::byte('i'),
54     std::byte('n'),
55     std::byte('g'),
56     std::byte('\n'),
57 };
58 
59 class HexDump : public ::testing::Test {
60  protected:
HexDump()61   HexDump() { dumper_ = FormattedHexDumper(dest_, default_flags_); }
62 
63   // Sufficiently large destination buffer to hold line-by-line formatted hex
64   // dump.
65   std::array<char, 256> dest_ = {0};
66   FormattedHexDumper dumper_;
67   FormattedHexDumper::Flags default_flags_ = {
68       .bytes_per_line = 16,
69       .group_every = 1,
70       .show_ascii = false,
71       .show_header = false,
72       .prefix_mode = FormattedHexDumper::AddressMode::kDisabled};
73 };
74 
75 class SmallBuffer : public ::testing::Test {
76  protected:
SmallBuffer()77   SmallBuffer() {
78     // Disable address prefix for most of the tests as it's platform-specific.
79     dumper_ = FormattedHexDumper(dest_, default_flags_);
80   }
81 
82   // Small destination buffer that should be inadequate in some cases.
83   std::array<char, 7> dest_ = {0};
84   FormattedHexDumper dumper_;
85   FormattedHexDumper::Flags default_flags_ = {
86       .bytes_per_line = 16,
87       .group_every = 1,
88       .show_ascii = false,
89       .show_header = false,
90       .prefix_mode = FormattedHexDumper::AddressMode::kDisabled};
91 };
92 
93 // On platforms where uintptr_t is 32-bit this evaluates to a 10-byte string
94 // where hex_string is prefixed with "0x". On 64-bit targets, this expands to
95 // an 18-byte string with the significant bytes are zero padded.
96 #define EXPECTED_SIGNIFICANT_BYTES(hex_string)                    \
97   sizeof(uintptr_t) == sizeof(uint64_t) ? "0x00000000" hex_string \
98                                         : "0x" hex_string
99 
TEST_F(HexDump,DumpAddr_ZeroSizeT)100 TEST_F(HexDump, DumpAddr_ZeroSizeT) {
101   constexpr const char* expected = EXPECTED_SIGNIFICANT_BYTES("00000000");
102   size_t zero = 0;
103   EXPECT_EQ(DumpAddr(dest_, zero), OkStatus());
104   EXPECT_STREQ(expected, dest_.data());
105 }
106 
TEST_F(HexDump,DumpAddr_NonzeroSizeT)107 TEST_F(HexDump, DumpAddr_NonzeroSizeT) {
108   constexpr const char* expected = EXPECTED_SIGNIFICANT_BYTES("deadbeef");
109   size_t nonzero = 0xDEADBEEF;
110   EXPECT_TRUE(DumpAddr(dest_, nonzero).ok());
111   EXPECT_STREQ(expected, dest_.data());
112 }
113 
TEST_F(HexDump,DumpAddr_ZeroPtr)114 TEST_F(HexDump, DumpAddr_ZeroPtr) {
115   constexpr const char* expected = EXPECTED_SIGNIFICANT_BYTES("00000000");
116   uintptr_t zero = 0;
117   EXPECT_TRUE(DumpAddr(dest_, reinterpret_cast<const void*>(zero)).ok());
118   EXPECT_STREQ(expected, dest_.data());
119 }
120 
TEST_F(HexDump,DumpAddr_NonzeroPtr)121 TEST_F(HexDump, DumpAddr_NonzeroPtr) {
122   constexpr const char* expected = EXPECTED_SIGNIFICANT_BYTES("deadbeef");
123   uintptr_t nonzero = 0xDEADBEEF;
124   EXPECT_TRUE(DumpAddr(dest_, reinterpret_cast<const void*>(nonzero)).ok());
125   EXPECT_STREQ(expected, dest_.data());
126 }
127 
TEST_F(HexDump,FormattedHexDump_Defaults)128 TEST_F(HexDump, FormattedHexDump_Defaults) {
129   constexpr const char* expected =
130       "a4 cc 32 62 9b 46 38 1a 23 1a 2a 7a bc e2 40 a0  ..2b.F8.#.*z..@.";
131   default_flags_.show_ascii = true;
132   dumper_ = FormattedHexDumper(dest_, default_flags_);
133   EXPECT_TRUE(dumper_.BeginDump(source_data).ok());
134   EXPECT_TRUE(dumper_.DumpLine().ok());
135   EXPECT_STREQ(expected, dest_.data());
136 }
137 
TEST_F(HexDump,FormattedHexDump_DefaultHeader)138 TEST_F(HexDump, FormattedHexDump_DefaultHeader) {
139   constexpr const char* expected =
140       "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f";
141 
142   default_flags_.show_header = true;
143   dumper_ = FormattedHexDumper(dest_, default_flags_);
144   EXPECT_TRUE(dumper_.BeginDump(source_data).ok());
145   EXPECT_TRUE(dumper_.DumpLine().ok());
146   EXPECT_STREQ(expected, dest_.data());
147 }
148 
TEST_F(HexDump,FormattedHexDump_DumpEntireBuffer)149 TEST_F(HexDump, FormattedHexDump_DumpEntireBuffer) {
150   constexpr size_t kTestBytesPerLine = 8;
151 
152   default_flags_.bytes_per_line = kTestBytesPerLine;
153   dumper_ = FormattedHexDumper(dest_, default_flags_);
154 
155   EXPECT_TRUE(dumper_.BeginDump(source_data).ok());
156   for (size_t i = 0; i < source_data.size(); i += kTestBytesPerLine) {
157     EXPECT_TRUE(dumper_.DumpLine().ok());
158   }
159   EXPECT_EQ(dumper_.DumpLine(), Status::ResourceExhausted());
160 }
161 
162 // This test is provided for convenience of debugging, as it actually logs the
163 // dump.
TEST_F(HexDump,FormattedHexDump_LogDump)164 TEST_F(HexDump, FormattedHexDump_LogDump) {
165   default_flags_.show_ascii = true;
166   default_flags_.show_header = true;
167   default_flags_.prefix_mode = FormattedHexDumper::AddressMode::kOffset;
168   dumper_ = FormattedHexDumper(dest_, default_flags_);
169 
170   EXPECT_TRUE(dumper_.BeginDump(source_data).ok());
171   // Dump data.
172   while (dumper_.DumpLine().ok()) {
173     PW_LOG_INFO("%s", dest_.data());
174   }
175   EXPECT_EQ(dumper_.DumpLine(), Status::ResourceExhausted());
176 }
177 
TEST_F(HexDump,FormattedHexDump_NoSpaces)178 TEST_F(HexDump, FormattedHexDump_NoSpaces) {
179   constexpr const char* expected = "a4cc32629b46381a231a2a7abce240a0";
180 
181   default_flags_.group_every = 0;
182   dumper_ = FormattedHexDumper(dest_, default_flags_);
183 
184   EXPECT_TRUE(dumper_.BeginDump(source_data).ok());
185   EXPECT_TRUE(dumper_.DumpLine().ok());
186   EXPECT_STREQ(expected, dest_.data());
187 }
188 
TEST_F(HexDump,FormattedHexDump_SetGroupEveryByte)189 TEST_F(HexDump, FormattedHexDump_SetGroupEveryByte) {
190   constexpr const char* expected =
191       "a4 cc 32 62 9b 46 38 1a 23 1a 2a 7a bc e2 40 a0";
192   default_flags_.group_every = 1;
193   dumper_ = FormattedHexDumper(dest_, default_flags_);
194   EXPECT_TRUE(dumper_.BeginDump(source_data).ok());
195   EXPECT_TRUE(dumper_.DumpLine().ok());
196   EXPECT_STREQ(expected, dest_.data());
197 }
198 
TEST_F(HexDump,FormattedHexDump_SetGroupEveryThreeBytes)199 TEST_F(HexDump, FormattedHexDump_SetGroupEveryThreeBytes) {
200   constexpr const char* expected = "a4cc32 629b46 381a23 1a2a7a bce240 a0";
201 
202   default_flags_.group_every = 3;
203   dumper_ = FormattedHexDumper(dest_, default_flags_);
204 
205   EXPECT_TRUE(dumper_.BeginDump(source_data).ok());
206   EXPECT_TRUE(dumper_.DumpLine().ok());
207   EXPECT_STREQ(expected, dest_.data());
208 }
209 
TEST_F(HexDump,FormattedHexDump_TwoLines)210 TEST_F(HexDump, FormattedHexDump_TwoLines) {
211   constexpr const char* expected1 = "a4 cc 32 62 9b 46 38 1a";
212   constexpr const char* expected2 = "23 1a 2a 7a bc e2 40 a0";
213 
214   default_flags_.bytes_per_line = 8;
215   dumper_ = FormattedHexDumper(dest_, default_flags_);
216 
217   EXPECT_TRUE(dumper_.BeginDump(source_data).ok());
218   // Dump first line.
219   EXPECT_TRUE(dumper_.DumpLine().ok());
220   EXPECT_STREQ(expected1, dest_.data());
221   // Dump second line.
222   EXPECT_TRUE(dumper_.DumpLine().ok());
223   EXPECT_STREQ(expected2, dest_.data());
224 }
225 
TEST_F(HexDump,FormattedHexDump_LastLineCheck)226 TEST_F(HexDump, FormattedHexDump_LastLineCheck) {
227   constexpr const char* expected1 = "a4cc32629b46381a 231a2a7abce240a0";
228   constexpr const char* expected2 = "ff33e52b9e9f6b3c be9b893c7e4a7a48";
229   constexpr const char* expected3 = "18";
230 
231   default_flags_.bytes_per_line = 16;
232   default_flags_.group_every = 8;
233   dumper_ = FormattedHexDumper(dest_, default_flags_);
234 
235   EXPECT_TRUE(dumper_.BeginDump(source_data).ok());
236   // Dump first line.
237   EXPECT_TRUE(dumper_.DumpLine().ok());
238   EXPECT_STREQ(expected1, dest_.data());
239   // Dump second line.
240   EXPECT_TRUE(dumper_.DumpLine().ok());
241   EXPECT_STREQ(expected2, dest_.data());
242   // Dump third line.
243   EXPECT_TRUE(dumper_.DumpLine().ok());
244   EXPECT_STREQ(expected3, dest_.data());
245 }
246 
TEST_F(HexDump,FormattedHexDump_Ascii)247 TEST_F(HexDump, FormattedHexDump_Ascii) {
248   constexpr const char* expected1 = "6d 79 20 74 65 73 74 20  my test ";
249   constexpr const char* expected2 = "73 74 72 69 6e 67 0a     string.";
250 
251   default_flags_.bytes_per_line = 8;
252   default_flags_.show_ascii = true;
253   dumper_ = FormattedHexDumper(dest_, default_flags_);
254 
255   EXPECT_TRUE(dumper_.BeginDump(short_string).ok());
256   // Dump first line.
257   EXPECT_TRUE(dumper_.DumpLine().ok());
258   EXPECT_STREQ(expected1, dest_.data());
259   // Dump second line.
260   EXPECT_TRUE(dumper_.DumpLine().ok());
261   EXPECT_STREQ(expected2, dest_.data());
262 }
263 
TEST_F(HexDump,FormattedHexDump_AsciiHeader)264 TEST_F(HexDump, FormattedHexDump_AsciiHeader) {
265   constexpr const char* expected0 = "00       04        Text";
266   constexpr const char* expected1 = "6d792074 65737420  my test ";
267   constexpr const char* expected2 = "73747269 6e670a    string.";
268 
269   default_flags_.bytes_per_line = 8;
270   default_flags_.group_every = 4;
271   default_flags_.show_ascii = true;
272   default_flags_.show_header = true;
273   dumper_ = FormattedHexDumper(dest_, default_flags_);
274 
275   EXPECT_TRUE(dumper_.BeginDump(short_string).ok());
276   // Dump header.
277   EXPECT_TRUE(dumper_.DumpLine().ok());
278   EXPECT_STREQ(expected0, dest_.data());
279   // Dump first line.
280   EXPECT_TRUE(dumper_.DumpLine().ok());
281   EXPECT_STREQ(expected1, dest_.data());
282   // Dump second line.
283   EXPECT_TRUE(dumper_.DumpLine().ok());
284   EXPECT_STREQ(expected2, dest_.data());
285 }
286 
TEST_F(HexDump,FormattedHexDump_AsciiHeaderGroupEvery)287 TEST_F(HexDump, FormattedHexDump_AsciiHeaderGroupEvery) {
288   constexpr const char* expected0 = "00 01 02 03 04 05 06 07  Text";
289   constexpr const char* expected1 = "6d 79 20 74 65 73 74 20  my test ";
290   constexpr const char* expected2 = "73 74 72 69 6e 67 0a     string.";
291 
292   default_flags_.bytes_per_line = 8;
293   default_flags_.group_every = 1;
294   default_flags_.show_ascii = true;
295   default_flags_.show_header = true;
296   dumper_ = FormattedHexDumper(dest_, default_flags_);
297 
298   EXPECT_TRUE(dumper_.BeginDump(short_string).ok());
299   // Dump header.
300   EXPECT_TRUE(dumper_.DumpLine().ok());
301   EXPECT_STREQ(expected0, dest_.data());
302   // Dump first line.
303   EXPECT_TRUE(dumper_.DumpLine().ok());
304   EXPECT_STREQ(expected1, dest_.data());
305   // Dump second line.
306   EXPECT_TRUE(dumper_.DumpLine().ok());
307   EXPECT_STREQ(expected2, dest_.data());
308 }
309 
TEST_F(HexDump,FormattedHexDump_OffsetPrefix)310 TEST_F(HexDump, FormattedHexDump_OffsetPrefix) {
311   constexpr const char* expected1 = "0000:";
312   constexpr const char* expected2 = "0010:";
313 
314   default_flags_.bytes_per_line = 16;
315   default_flags_.prefix_mode = FormattedHexDumper::AddressMode::kOffset;
316   dumper_ = FormattedHexDumper(dest_, default_flags_);
317 
318   EXPECT_TRUE(dumper_.BeginDump(source_data).ok());
319   // Dump first line.
320   EXPECT_TRUE(dumper_.DumpLine().ok());
321   // Truncate string to only contain the offset.
322   dest_[strlen(expected1)] = '\0';
323   EXPECT_STREQ(expected1, dest_.data());
324 
325   // Dump second line.
326   EXPECT_TRUE(dumper_.DumpLine().ok());
327   // Truncate string to only contain the offset.
328   dest_[strlen(expected2)] = '\0';
329   EXPECT_STREQ(expected2, dest_.data());
330 }
331 
TEST_F(HexDump,FormattedHexDump_OffsetPrefix_ShortLine)332 TEST_F(HexDump, FormattedHexDump_OffsetPrefix_ShortLine) {
333   constexpr const char* expected = "0000:";
334 
335   default_flags_.bytes_per_line = 16;
336   default_flags_.prefix_mode = FormattedHexDumper::AddressMode::kOffset;
337   dumper_ = FormattedHexDumper(dest_, default_flags_);
338 
339   EXPECT_TRUE(dumper_.BeginDump(pw::span(source_data).first(8)).ok());
340   // Dump first and only line.
341   EXPECT_TRUE(dumper_.DumpLine().ok());
342   // Truncate string to only contain the offset.
343   dest_[strlen(expected)] = '\0';
344   EXPECT_STREQ(expected, dest_.data());
345 }
346 
TEST_F(HexDump,FormattedHexDump_OffsetPrefix_LongData)347 TEST_F(HexDump, FormattedHexDump_OffsetPrefix_LongData) {
348   constexpr std::array<std::byte, 300> long_data = {std::byte{0xff}};
349 
350   constexpr const char* expected1 = "0000:";
351   constexpr const char* expected2 = "0010:";
352 
353   default_flags_.bytes_per_line = 16;
354   default_flags_.prefix_mode = FormattedHexDumper::AddressMode::kOffset;
355   dumper_ = FormattedHexDumper(dest_, default_flags_);
356 
357   EXPECT_TRUE(dumper_.BeginDump(long_data).ok());
358   // Dump first line.
359   EXPECT_TRUE(dumper_.DumpLine().ok());
360   // Truncate string to only contain the offset.
361   dest_[strlen(expected1)] = '\0';
362   EXPECT_STREQ(expected1, dest_.data());
363 
364   // Dump second line.
365   EXPECT_TRUE(dumper_.DumpLine().ok());
366   // Truncate string to only contain the offset.
367   dest_[strlen(expected2)] = '\0';
368   EXPECT_STREQ(expected2, dest_.data());
369 }
370 
TEST_F(HexDump,FormattedHexDump_AbsolutePrefix)371 TEST_F(HexDump, FormattedHexDump_AbsolutePrefix) {
372   constexpr size_t kTestBytesPerLine = 16;
373   std::array<char, kHexAddrStringSize + 1> expected1;
374   std::array<char, kHexAddrStringSize + 1> expected2;
375   ASSERT_EQ(OkStatus(), DumpAddr(expected1, source_data.data()));
376   ASSERT_EQ(OkStatus(),
377             DumpAddr(expected2, source_data.data() + kTestBytesPerLine));
378 
379   default_flags_.bytes_per_line = kTestBytesPerLine;
380   default_flags_.prefix_mode = FormattedHexDumper::AddressMode::kAbsolute;
381   dumper_ = FormattedHexDumper(dest_, default_flags_);
382 
383   EXPECT_TRUE(dumper_.BeginDump(source_data).ok());
384   // Dump first line.
385   EXPECT_TRUE(dumper_.DumpLine().ok());
386   // Truncate string to only contain the offset.
387   EXPECT_EQ(dest_[kHexAddrStringSize], ':');
388   dest_[kHexAddrStringSize] = '\0';
389   EXPECT_STREQ(expected1.data(), dest_.data());
390 
391   // Dump second line.
392   EXPECT_TRUE(dumper_.DumpLine().ok());
393   // Truncate string to only contain the offset.
394   EXPECT_EQ(dest_[kHexAddrStringSize], ':');
395   dest_[kHexAddrStringSize] = '\0';
396   EXPECT_STREQ(expected2.data(), dest_.data());
397 }
398 
TEST_F(SmallBuffer,TinyHexDump)399 TEST_F(SmallBuffer, TinyHexDump) {
400   constexpr const char* expected = "a4cc32";
401 
402   default_flags_.bytes_per_line = 3;
403   default_flags_.group_every = 4;
404   dumper_ = FormattedHexDumper(dest_, default_flags_);
405 
406   EXPECT_TRUE(dumper_.BeginDump(source_data).ok());
407   EXPECT_TRUE(dumper_.DumpLine().ok());
408   EXPECT_STREQ(expected, dest_.data());
409 }
410 
TEST_F(SmallBuffer,TooManyBytesPerLine)411 TEST_F(SmallBuffer, TooManyBytesPerLine) {
412   constexpr const char* expected = "";
413 
414   default_flags_.bytes_per_line = 13;
415   dumper_ = FormattedHexDumper(dest_, default_flags_);
416 
417   EXPECT_EQ(dumper_.BeginDump(source_data), Status::FailedPrecondition());
418   EXPECT_FALSE(dumper_.DumpLine().ok());
419   EXPECT_STREQ(expected, dest_.data());
420 }
421 
TEST_F(SmallBuffer,SpacesIncreaseBufferRequirement)422 TEST_F(SmallBuffer, SpacesIncreaseBufferRequirement) {
423   constexpr const char* expected = "";
424 
425   default_flags_.bytes_per_line = 3;
426   default_flags_.group_every = 1;
427   dumper_ = FormattedHexDumper(dest_, default_flags_);
428 
429   EXPECT_EQ(dumper_.BeginDump(source_data), Status::FailedPrecondition());
430   EXPECT_FALSE(dumper_.DumpLine().ok());
431   EXPECT_STREQ(expected, dest_.data());
432 }
433 
TEST_F(SmallBuffer,PrefixIncreasesBufferRequirement)434 TEST_F(SmallBuffer, PrefixIncreasesBufferRequirement) {
435   constexpr const char* expected = "";
436 
437   default_flags_.bytes_per_line = 3;
438   default_flags_.prefix_mode = FormattedHexDumper::AddressMode::kOffset;
439   dumper_ = FormattedHexDumper(dest_, default_flags_);
440 
441   EXPECT_EQ(dumper_.BeginDump(source_data), Status::FailedPrecondition());
442   EXPECT_FALSE(dumper_.DumpLine().ok());
443   EXPECT_STREQ(expected, dest_.data());
444 }
445 
TEST(BadBuffer,ZeroSize)446 TEST(BadBuffer, ZeroSize) {
447   char buffer[1] = {static_cast<char>(0xaf)};
448   FormattedHexDumper dumper(span<char>(buffer, 0));
449   EXPECT_EQ(dumper.BeginDump(source_data), Status::FailedPrecondition());
450   EXPECT_EQ(dumper.DumpLine(), Status::FailedPrecondition());
451   EXPECT_EQ(buffer[0], static_cast<char>(0xaf));
452 }
453 
TEST(BadBuffer,NullPtrDest)454 TEST(BadBuffer, NullPtrDest) {
455   FormattedHexDumper dumper;
456   EXPECT_EQ(dumper.SetLineBuffer(span<char>()), Status::InvalidArgument());
457   EXPECT_EQ(dumper.BeginDump(source_data), Status::FailedPrecondition());
458   EXPECT_EQ(dumper.DumpLine(), Status::FailedPrecondition());
459 }
460 
TEST(BadBuffer,NullPtrSrc)461 TEST(BadBuffer, NullPtrSrc) {
462   char buffer[24] = {static_cast<char>(0)};
463   FormattedHexDumper dumper(buffer);
464   EXPECT_EQ(dumper.BeginDump(ByteSpan(static_cast<std::byte*>(nullptr), 64)),
465             Status::InvalidArgument());
466   // Don't actually dump nullptr in this test as it could cause a crash.
467 }
468 
469 }  // namespace
470 }  // namespace pw::dump
471