1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <pretty/sizes.h> 6#include <unittest/unittest.h> 7 8typedef struct { 9 const size_t input; 10 const char unit; 11 const char* expected_output; 12} format_size_test_case_t; 13 14#define KILO (1024ULL) 15#define MEGA (KILO * 1024) 16#define GIGA (MEGA * 1024) 17#define TERA (GIGA * 1024) 18#define PETA (TERA * 1024) 19#define EXA (PETA * 1024) 20 21static const format_size_test_case_t format_size_test_cases[] = { 22// Declare a test case that uses a unit of 0, 23// picking a natural unit for the size. 24#define TC0(i, o) \ 25 { .input = i, .unit = 0, .expected_output = o } 26 27 // Whole multiples don't print decimals, 28 // and always round up to their largest unit. 29 TC0(0, "0B"), 30 TC0(1, "1B"), 31 32 // Favor the largest unit when it loses no precision 33 // (e.g., "1k" not "1024B"). 34 // Larger values may still use a smaller unit 35 // (e.g., "1k" + 1 == "1025B") to preserve precision. 36 TC0(KILO - 1, "1023B"), 37 TC0(KILO, "1k"), 38 TC0(KILO + 1, "1025B"), 39 TC0(KILO * 9, "9k"), 40 TC0(KILO * 9 + 1, "9217B"), 41 TC0(KILO * 10, "10k"), 42 43 // Same demonstration for the next unit. 44 TC0(MEGA - KILO, "1023k"), 45 TC0(MEGA, "1M"), 46 TC0(MEGA + KILO, "1025k"), 47 TC0(MEGA * 9, "9M"), 48 TC0(MEGA * 9 + KILO, "9217k"), 49 TC0(MEGA * 10, "10M"), 50 51 // Sanity checks for remaining units. 52 TC0(MEGA, "1M"), 53 TC0(GIGA, "1G"), 54 TC0(TERA, "1T"), 55 TC0(PETA, "1P"), 56 TC0(EXA, "1E"), 57 58 // Non-whole multiples print decimals, and favor more whole digits 59 // (e.g., "1024.0k" not "1.0M") to retain precision. 60 TC0(MEGA - 1, "1024.0k"), 61 TC0(MEGA + MEGA / 3, "1365.3k"), // Only one decimal place is ever shown. 62 TC0(GIGA - 1, "1024.0M"), 63 TC0(TERA - 1, "1024.0G"), 64 TC0(PETA - 1, "1024.0T"), 65 TC0(EXA - 1, "1024.0P"), 66 TC0(UINT64_MAX, "16.0E"), 67 68 // Never show more than four whole digits, 69 // to make the values easier to eyeball. 70 TC0(9999, "9999B"), 71 TC0(10000, "9.8k"), 72 TC0(KILO * 9999, "9999k"), 73 TC0(KILO * 9999 + 1, "9999.0k"), 74 TC0(KILO * 10000, "9.8M"), 75 76// Declare a test case fixed to the specified unit. 77#define TCF(i, u, o) \ 78 { .input = i, .unit = u, .expected_output = o } 79 80 // When fixed, we can see a lot more digits. 81 TCF(UINT64_MAX, 'B', "18446744073709551615B"), 82 TCF(UINT64_MAX, 'k', "18014398509481984.0k"), 83 TCF(UINT64_MAX, 'M', "17592186044416.0M"), 84 TCF(UINT64_MAX, 'G', "17179869184.0G"), 85 TCF(UINT64_MAX, 'T', "16777216.0T"), 86 TCF(UINT64_MAX, 'P', "16384.0P"), 87 TCF(UINT64_MAX, 'E', "16.0E"), 88 89 // Smaller than natural fixed unit. 90 TCF(GIGA, 'k', "1048576k"), 91 92 // Larger than natural fixed unit. 93 TCF(MEGA / 10, 'M', "0.1M"), 94 95 // Unknown units fall back to natural, but add a '?' prefix. 96 TCF(GIGA, 'q', "?1G"), 97 TCF(KILO, 'q', "?1k"), 98 TCF(GIGA + 1, '#', "?1.0G"), 99 TCF(KILO + 1, '#', "?1025B"), 100}; 101 102bool format_size_fixed_test(void) { 103 BEGIN_TEST; 104 char str[MAX_FORMAT_SIZE_LEN]; 105 char msg[128]; 106 for (unsigned int i = 0; i < countof(format_size_test_cases); i++) { 107 const format_size_test_case_t* tc = format_size_test_cases + i; 108 memset(str, 0, sizeof(str)); 109 char* ret = format_size_fixed(str, sizeof(str), tc->input, tc->unit); 110 snprintf(msg, sizeof(msg), "format_size_fixed(bytes=%zd, unit=%c)", 111 tc->input, tc->unit == 0 ? '0' : tc->unit); 112 EXPECT_STR_EQ(tc->expected_output, str, msg); 113 // Should always return the input pointer. 114 EXPECT_EQ(&(str[0]), ret, msg); 115 } 116 END_TEST; 117} 118 119bool format_size_short_buf_truncates(void) { 120 BEGIN_TEST; 121 // Widest possible output: four whole digits + decimal. 122 static const size_t input = 1023 * 1024 + 1; 123 static const char expected_output[] = "1023.0k"; 124 125 char buf[sizeof(expected_output) * 2]; 126 char msg[128]; 127 for (size_t str_size = 0; str_size <= sizeof(expected_output); 128 str_size++) { 129 memset(buf, 0x55, sizeof(buf)); 130 char* ret = format_size(buf, str_size, input); 131 132 snprintf(msg, sizeof(msg), 133 "format_size(str_size=%zd, bytes=%zd)", str_size, input); 134 EXPECT_EQ(&(buf[0]), ret, msg); 135 if (str_size > 2) { 136 EXPECT_BYTES_EQ( 137 (uint8_t*)expected_output, (uint8_t*)buf, str_size - 1, msg); 138 } 139 if (str_size > 1) { 140 EXPECT_EQ(buf[str_size - 1], '\0', msg); 141 } 142 EXPECT_EQ(buf[str_size], 0x55, msg); 143 } 144 END_TEST; 145} 146 147// Tests the path where we add a prefix '?' to make sure we don't 148// overrun the buffer or return a non-null-terminated result. 149bool format_size_bad_unit_short_buf_truncates(void) { 150 BEGIN_TEST; 151 152 char buf[MAX_FORMAT_SIZE_LEN]; 153 154 // Size zero should not touch the buffer. 155 memset(buf, 0x55, sizeof(buf)); 156 format_size_fixed(buf, 0, GIGA, 'q'); 157 EXPECT_EQ(buf[0], 0x55, ""); 158 159 // Size 1 should only null out the first byte. 160 memset(buf, 0x55, sizeof(buf)); 161 format_size_fixed(buf, 1, GIGA, 'q'); 162 EXPECT_EQ(buf[0], '\0', ""); 163 EXPECT_EQ(buf[1], 0x55, ""); 164 165 // Size 2 should just be the warning '?'. 166 memset(buf, 0x55, sizeof(buf)); 167 format_size_fixed(buf, 2, GIGA, 'q'); 168 EXPECT_EQ(buf[0], '?', ""); 169 EXPECT_EQ(buf[1], '\0', ""); 170 EXPECT_EQ(buf[2], 0x55, ""); 171 172 // Then just the number without units. 173 memset(buf, 0x55, sizeof(buf)); 174 format_size_fixed(buf, 3, GIGA, 'q'); 175 EXPECT_EQ(buf[0], '?', ""); 176 EXPECT_EQ(buf[1], '1', ""); 177 EXPECT_EQ(buf[2], '\0', ""); 178 EXPECT_EQ(buf[3], 0x55, ""); 179 180 // Then the whole thing. 181 memset(buf, 0x55, sizeof(buf)); 182 format_size_fixed(buf, 4, GIGA, 'q'); 183 EXPECT_EQ(buf[0], '?', ""); 184 EXPECT_EQ(buf[1], '1', ""); 185 EXPECT_EQ(buf[2], 'G', ""); 186 EXPECT_EQ(buf[3], '\0', ""); 187 EXPECT_EQ(buf[4], 0x55, ""); 188 189 END_TEST; 190} 191 192bool format_size_empty_str_succeeds(void) { 193 BEGIN_TEST; 194 static const size_t input = 1023 * 1024 + 1; 195 196 char c = 0x55; 197 char* ret = format_size(&c, 0, input); 198 EXPECT_EQ(&c, ret, ""); 199 EXPECT_EQ(0x55, c, ""); 200 END_TEST; 201} 202 203bool format_size_empty_null_str_succeeds(void) { 204 BEGIN_TEST; 205 static const size_t input = 1023 * 1024 + 1; 206 207 char* ret = format_size(NULL, 0, input); 208 EXPECT_EQ(NULL, ret, ""); 209 END_TEST; 210} 211 212BEGIN_TEST_CASE(pretty_tests) 213RUN_TEST(format_size_fixed_test) 214RUN_TEST(format_size_short_buf_truncates) 215RUN_TEST(format_size_bad_unit_short_buf_truncates) 216RUN_TEST(format_size_empty_str_succeeds) 217RUN_TEST(format_size_empty_null_str_succeeds) 218END_TEST_CASE(pretty_tests) 219 220int main(int argc, char** argv) { 221 return unittest_run_all_tests(argc, argv) ? 0 : -1; 222} 223