blob: c2b4126bccc02c11002e2cc9a31c4d550b166284 [file] [log] [blame]
Armando Montanez593d0d52020-07-08 19:55:01 -07001// 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 <span>
22#include <string_view>
23
24#include "gtest/gtest.h"
25#include "pw_log/log.h"
26
27namespace pw::dump {
28namespace {
29
30std::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
41std::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
59class HexDump : public ::testing::Test {
60 protected:
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
75class SmallBuffer : public ::testing::Test {
76 protected:
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
100TEST_F(HexDump, DumpAddr_ZeroSizeT) {
101 constexpr const char* expected = EXPECTED_SIGNIFICANT_BYTES("00000000");
102 size_t zero = 0;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800103 EXPECT_EQ(DumpAddr(dest_, zero), OkStatus());
Armando Montanez593d0d52020-07-08 19:55:01 -0700104 EXPECT_STREQ(expected, dest_.data());
105}
106
107TEST_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
114TEST_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
121TEST_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
128TEST_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
138TEST_F(HexDump, FormattedHexDump_DefaultHeader) {
139 constexpr const char* expected =
Armando Montanez1cbc49a2021-11-19 18:30:27 -0800140 "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f";
Armando Montanez593d0d52020-07-08 19:55:01 -0700141
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
149TEST_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 }
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700159 EXPECT_EQ(dumper_.DumpLine(), Status::ResourceExhausted());
Armando Montanez593d0d52020-07-08 19:55:01 -0700160}
161
162// This test is provided for convenience of debugging, as it actually logs the
163// dump.
164TEST_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 }
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700175 EXPECT_EQ(dumper_.DumpLine(), Status::ResourceExhausted());
Armando Montanez593d0d52020-07-08 19:55:01 -0700176}
177
178TEST_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
189TEST_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
199TEST_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
210TEST_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
Armando Montanez019197b2021-06-16 14:39:47 -0700226TEST_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
Armando Montanez593d0d52020-07-08 19:55:01 -0700247TEST_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
264TEST_F(HexDump, FormattedHexDump_AsciiHeader) {
Armando Montanez1cbc49a2021-11-19 18:30:27 -0800265 constexpr const char* expected0 = "00 04 Text";
Armando Montanez593d0d52020-07-08 19:55:01 -0700266 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
287TEST_F(HexDump, FormattedHexDump_AsciiHeaderGroupEvery) {
Armando Montanez1cbc49a2021-11-19 18:30:27 -0800288 constexpr const char* expected0 = "00 01 02 03 04 05 06 07 Text";
Armando Montanez593d0d52020-07-08 19:55:01 -0700289 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
310TEST_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
332TEST_F(HexDump, FormattedHexDump_AbsolutePrefix) {
333 constexpr size_t kTestBytesPerLine = 16;
334 std::array<char, kHexAddrStringSize + 1> expected1;
335 std::array<char, kHexAddrStringSize + 1> expected2;
Adrien Larbanetd1ca56c2021-06-10 14:20:45 +0000336 DumpAddr(expected1, source_data.data())
337 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
338 DumpAddr(expected2, source_data.data() + kTestBytesPerLine)
339 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
Armando Montanez593d0d52020-07-08 19:55:01 -0700340
341 default_flags_.bytes_per_line = kTestBytesPerLine;
342 default_flags_.prefix_mode = FormattedHexDumper::AddressMode::kAbsolute;
343 dumper_ = FormattedHexDumper(dest_, default_flags_);
344
345 EXPECT_TRUE(dumper_.BeginDump(source_data).ok());
346 // Dump first line.
347 EXPECT_TRUE(dumper_.DumpLine().ok());
348 // Truncate string to only contain the offset.
349 dest_[kHexAddrStringSize] = '\0';
350 EXPECT_STREQ(expected1.data(), dest_.data());
351
352 // Dump second line.
353 EXPECT_TRUE(dumper_.DumpLine().ok());
354 // Truncate string to only contain the offset.
355 dest_[kHexAddrStringSize] = '\0';
356 EXPECT_STREQ(expected2.data(), dest_.data());
357}
358
359TEST_F(SmallBuffer, TinyHexDump) {
360 constexpr const char* expected = "a4cc32";
361
362 default_flags_.bytes_per_line = 3;
363 default_flags_.group_every = 4;
364 dumper_ = FormattedHexDumper(dest_, default_flags_);
365
366 EXPECT_TRUE(dumper_.BeginDump(source_data).ok());
367 EXPECT_TRUE(dumper_.DumpLine().ok());
368 EXPECT_STREQ(expected, dest_.data());
369}
370
371TEST_F(SmallBuffer, TooManyBytesPerLine) {
372 constexpr const char* expected = "";
373
374 default_flags_.bytes_per_line = 13;
375 dumper_ = FormattedHexDumper(dest_, default_flags_);
376
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700377 EXPECT_EQ(dumper_.BeginDump(source_data), Status::FailedPrecondition());
Armando Montanez593d0d52020-07-08 19:55:01 -0700378 EXPECT_FALSE(dumper_.DumpLine().ok());
379 EXPECT_STREQ(expected, dest_.data());
380}
381
382TEST_F(SmallBuffer, SpacesIncreaseBufferRequirement) {
383 constexpr const char* expected = "";
384
385 default_flags_.bytes_per_line = 3;
386 default_flags_.group_every = 1;
387 dumper_ = FormattedHexDumper(dest_, default_flags_);
388
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700389 EXPECT_EQ(dumper_.BeginDump(source_data), Status::FailedPrecondition());
Armando Montanez593d0d52020-07-08 19:55:01 -0700390 EXPECT_FALSE(dumper_.DumpLine().ok());
391 EXPECT_STREQ(expected, dest_.data());
392}
393
394TEST_F(SmallBuffer, PrefixIncreasesBufferRequirement) {
395 constexpr const char* expected = "";
396
397 default_flags_.bytes_per_line = 3;
398 default_flags_.prefix_mode = FormattedHexDumper::AddressMode::kOffset;
399 dumper_ = FormattedHexDumper(dest_, default_flags_);
400
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700401 EXPECT_EQ(dumper_.BeginDump(source_data), Status::FailedPrecondition());
Armando Montanez593d0d52020-07-08 19:55:01 -0700402 EXPECT_FALSE(dumper_.DumpLine().ok());
403 EXPECT_STREQ(expected, dest_.data());
404}
405
406TEST(BadBuffer, ZeroSize) {
407 char buffer[1] = {static_cast<char>(0xaf)};
408 FormattedHexDumper dumper(std::span<char>(buffer, 0));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700409 EXPECT_EQ(dumper.BeginDump(source_data), Status::FailedPrecondition());
410 EXPECT_EQ(dumper.DumpLine(), Status::FailedPrecondition());
Armando Montanez593d0d52020-07-08 19:55:01 -0700411 EXPECT_EQ(buffer[0], static_cast<char>(0xaf));
412}
413
414TEST(BadBuffer, NullPtrDest) {
415 FormattedHexDumper dumper;
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700416 EXPECT_EQ(dumper.SetLineBuffer(std::span<char>()), Status::InvalidArgument());
417 EXPECT_EQ(dumper.BeginDump(source_data), Status::FailedPrecondition());
418 EXPECT_EQ(dumper.DumpLine(), Status::FailedPrecondition());
Armando Montanez593d0d52020-07-08 19:55:01 -0700419}
420
421TEST(BadBuffer, NullPtrSrc) {
422 char buffer[24] = {static_cast<char>(0)};
423 FormattedHexDumper dumper(buffer);
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700424 EXPECT_EQ(dumper.BeginDump(ByteSpan(nullptr, 64)), Status::InvalidArgument());
Armando Montanez593d0d52020-07-08 19:55:01 -0700425 // Don't actually dump nullptr in this test as it could cause a crash.
426}
427
428} // namespace
429} // namespace pw::dump