blob: 4c8801131b64cac4077f108766df0dfad7c03eeb [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <pretty/hexdump.h>
#include <pretty/sizes.h>
#include <unittest/unittest.h>
typedef struct {
const size_t input;
const char unit;
const char* expected_output;
} format_size_test_case_t;
#define KILO (1024ULL)
#define MEGA (KILO * 1024)
#define GIGA (MEGA * 1024)
#define TERA (GIGA * 1024)
#define PETA (TERA * 1024)
#define EXA (PETA * 1024)
static const format_size_test_case_t format_size_test_cases[] = {
// Declare a test case that uses a unit of 0,
// picking a natural unit for the size.
#define TC0(i, o) \
{ .input = i, .unit = 0, .expected_output = o }
// Whole multiples don't print decimals,
// and always round up to their largest unit.
TC0(0, "0B"),
TC0(1, "1B"),
// Favor the largest unit when it loses no precision
// (e.g., "1k" not "1024B").
// Larger values may still use a smaller unit
// (e.g., "1k" + 1 == "1025B") to preserve precision.
TC0(KILO - 1, "1023B"),
TC0(KILO, "1k"),
TC0(KILO + 1, "1025B"),
TC0(KILO * 9, "9k"),
TC0(KILO * 9 + 1, "9217B"),
TC0(KILO * 10, "10k"),
// Same demonstration for the next unit.
TC0(MEGA - KILO, "1023k"),
TC0(MEGA, "1M"),
TC0(MEGA + KILO, "1025k"),
TC0(MEGA * 9, "9M"),
TC0(MEGA * 9 + KILO, "9217k"),
TC0(MEGA * 10, "10M"),
// Sanity checks for remaining units.
TC0(MEGA, "1M"),
TC0(GIGA, "1G"),
TC0(TERA, "1T"),
TC0(PETA, "1P"),
TC0(EXA, "1E"),
// Non-whole multiples print decimals, and favor more whole digits
// (e.g., "1024.0k" not "1.0M") to retain precision.
TC0(MEGA - 1, "1024.0k"),
TC0(MEGA + MEGA / 3, "1365.3k"), // Only one decimal place is ever shown.
TC0(GIGA - 1, "1024.0M"),
TC0(TERA - 1, "1024.0G"),
TC0(PETA - 1, "1024.0T"),
TC0(EXA - 1, "1024.0P"),
TC0(UINT64_MAX, "16.0E"),
// Never show more than four whole digits,
// to make the values easier to eyeball.
TC0(9999, "9999B"),
TC0(10000, "9.8k"),
TC0(KILO * 9999, "9999k"),
TC0(KILO * 9999 + 1, "9999.0k"),
TC0(KILO * 10000, "9.8M"),
// Declare a test case fixed to the specified unit.
#define TCF(i, u, o) \
{ .input = i, .unit = u, .expected_output = o }
// When fixed, we can see a lot more digits.
TCF(UINT64_MAX, 'B', "18446744073709551615B"),
TCF(UINT64_MAX, 'k', "18014398509481984.0k"),
TCF(UINT64_MAX, 'M', "17592186044416.0M"),
TCF(UINT64_MAX, 'G', "17179869184.0G"),
TCF(UINT64_MAX, 'T', "16777216.0T"),
TCF(UINT64_MAX, 'P', "16384.0P"),
TCF(UINT64_MAX, 'E', "16.0E"),
// Smaller than natural fixed unit.
TCF(GIGA, 'k', "1048576k"),
// Larger than natural fixed unit.
TCF(MEGA / 10, 'M', "0.1M"),
// Unknown units fall back to natural, but add a '?' prefix.
TCF(GIGA, 'q', "?1G"),
TCF(KILO, 'q', "?1k"),
TCF(GIGA + 1, '#', "?1.0G"),
TCF(KILO + 1, '#', "?1025B"),
};
bool format_size_fixed_test(void) {
BEGIN_TEST;
char str[MAX_FORMAT_SIZE_LEN];
char msg[128];
for (unsigned int i = 0; i < countof(format_size_test_cases); i++) {
const format_size_test_case_t* tc = format_size_test_cases + i;
memset(str, 0, sizeof(str));
char* ret = format_size_fixed(str, sizeof(str), tc->input, tc->unit);
snprintf(msg, sizeof(msg), "format_size_fixed(bytes=%zd, unit=%c)",
tc->input, tc->unit == 0 ? '0' : tc->unit);
EXPECT_STR_EQ(tc->expected_output, str, msg);
// Should always return the input pointer.
EXPECT_EQ(&(str[0]), ret, msg);
}
END_TEST;
}
bool format_size_short_buf_truncates(void) {
BEGIN_TEST;
// Widest possible output: four whole digits + decimal.
static const size_t input = 1023 * 1024 + 1;
static const char expected_output[] = "1023.0k";
char buf[sizeof(expected_output) * 2];
char msg[128];
for (size_t str_size = 0; str_size <= sizeof(expected_output);
str_size++) {
memset(buf, 0x55, sizeof(buf));
char* ret = format_size(buf, str_size, input);
snprintf(msg, sizeof(msg),
"format_size(str_size=%zd, bytes=%zd)", str_size, input);
EXPECT_EQ(&(buf[0]), ret, msg);
if (str_size > 2) {
EXPECT_BYTES_EQ(
(uint8_t*)expected_output, (uint8_t*)buf, str_size - 1, msg);
}
if (str_size > 1) {
EXPECT_EQ(buf[str_size - 1], '\0', msg);
}
EXPECT_EQ(buf[str_size], 0x55, msg);
}
END_TEST;
}
// Tests the path where we add a prefix '?' to make sure we don't
// overrun the buffer or return a non-null-terminated result.
bool format_size_bad_unit_short_buf_truncates(void) {
BEGIN_TEST;
char buf[MAX_FORMAT_SIZE_LEN];
// Size zero should not touch the buffer.
memset(buf, 0x55, sizeof(buf));
format_size_fixed(buf, 0, GIGA, 'q');
EXPECT_EQ(buf[0], 0x55, "");
// Size 1 should only null out the first byte.
memset(buf, 0x55, sizeof(buf));
format_size_fixed(buf, 1, GIGA, 'q');
EXPECT_EQ(buf[0], '\0', "");
EXPECT_EQ(buf[1], 0x55, "");
// Size 2 should just be the warning '?'.
memset(buf, 0x55, sizeof(buf));
format_size_fixed(buf, 2, GIGA, 'q');
EXPECT_EQ(buf[0], '?', "");
EXPECT_EQ(buf[1], '\0', "");
EXPECT_EQ(buf[2], 0x55, "");
// Then just the number without units.
memset(buf, 0x55, sizeof(buf));
format_size_fixed(buf, 3, GIGA, 'q');
EXPECT_EQ(buf[0], '?', "");
EXPECT_EQ(buf[1], '1', "");
EXPECT_EQ(buf[2], '\0', "");
EXPECT_EQ(buf[3], 0x55, "");
// Then the whole thing.
memset(buf, 0x55, sizeof(buf));
format_size_fixed(buf, 4, GIGA, 'q');
EXPECT_EQ(buf[0], '?', "");
EXPECT_EQ(buf[1], '1', "");
EXPECT_EQ(buf[2], 'G', "");
EXPECT_EQ(buf[3], '\0', "");
EXPECT_EQ(buf[4], 0x55, "");
END_TEST;
}
bool format_size_empty_str_succeeds(void) {
BEGIN_TEST;
static const size_t input = 1023 * 1024 + 1;
char c = 0x55;
char* ret = format_size(&c, 0, input);
EXPECT_EQ(&c, ret, "");
EXPECT_EQ(0x55, c, "");
END_TEST;
}
bool format_size_empty_null_str_succeeds(void) {
BEGIN_TEST;
static const size_t input = 1023 * 1024 + 1;
char* ret = format_size(NULL, 0, input);
EXPECT_EQ(NULL, ret, "");
END_TEST;
}
bool hexdump_very_ex_test(void) {
BEGIN_TEST;
static const uint8_t input[] = { 0, 1, 2, 3, 'a', 'b', 'c', 'd' };
const uint64_t kTestDisplayAddr = 0x1000;
static const char expected[] =
"0x00001000: 03020100 64636261 |....abcd........|\n";
char output_buffer[sizeof(expected)];
FILE* f = fmemopen(output_buffer, sizeof(output_buffer), "w");
ASSERT_NONNULL(f, "");
hexdump_very_ex(input, sizeof(input), kTestDisplayAddr,
hexdump_stdio_printf, f);
fclose(f);
EXPECT_STR_EQ(output_buffer, expected, "");
END_TEST;
}
bool hexdump8_very_ex_test(void) {
BEGIN_TEST;
static const uint8_t input[] = { 0, 1, 2, 3, 'a', 'b', 'c', 'd' };
const uint64_t kTestDisplayAddr = 0x1000;
static const char expected[] =
"0x00001000: 00 01 02 03 61 62 63 64 |....abcd\n";
char output_buffer[sizeof(expected)];
FILE* f = fmemopen(output_buffer, sizeof(output_buffer), "w");
ASSERT_NONNULL(f, "");
hexdump8_very_ex(input, sizeof(input), kTestDisplayAddr,
hexdump_stdio_printf, f);
fclose(f);
EXPECT_STR_EQ(output_buffer, expected, "");
END_TEST;
}
BEGIN_TEST_CASE(pretty_tests)
RUN_TEST(format_size_fixed_test)
RUN_TEST(format_size_short_buf_truncates)
RUN_TEST(format_size_bad_unit_short_buf_truncates)
RUN_TEST(format_size_empty_str_succeeds)
RUN_TEST(format_size_empty_null_str_succeeds)
RUN_TEST(hexdump_very_ex_test)
RUN_TEST(hexdump8_very_ex_test)
END_TEST_CASE(pretty_tests)