| // Copyright 2025 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/byte_count.h" |
| |
| #include "base/numerics/checked_math.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/test/gtest_util.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace base { |
| |
| TEST(ByteCount, ConstructionDefault) { |
| constexpr ByteCount bytes; |
| EXPECT_EQ(0, bytes.InBytes()); |
| EXPECT_EQ(0, bytes.InKiB()); |
| EXPECT_EQ(0, bytes.InMiB()); |
| EXPECT_EQ(0, bytes.InGiB()); |
| EXPECT_EQ(0, bytes.InBytesUnsigned()); |
| } |
| |
| TEST(ByteCount, ConstructionByteCount) { |
| constexpr ByteCount bytes(1024 * 1024 * 1024); |
| EXPECT_EQ(1024 * 1024 * 1024, bytes.InBytes()); |
| EXPECT_EQ(1024 * 1024, bytes.InKiB()); |
| EXPECT_EQ(1024, bytes.InMiB()); |
| EXPECT_EQ(1, bytes.InGiB()); |
| EXPECT_EQ(1024u * 1024 * 1024, bytes.InBytesUnsigned()); |
| } |
| |
| TEST(ByteCount, ConstructionUnsigned) { |
| auto bytes = ByteCount::FromUnsigned(5u); |
| EXPECT_EQ(5, bytes.InBytes()); |
| } |
| |
| TEST(ByteCountDeathTest, ConstructionUnsignedInvalid) { |
| BASE_EXPECT_DEATH( |
| ByteCount::FromUnsigned(std::numeric_limits<uint64_t>::max()), ""); |
| } |
| |
| TEST(ByteCount, ConstructionChecked) { |
| auto bytes = ByteCount::FromChecked(CheckedNumeric<uint64_t>(5)); |
| EXPECT_EQ(5, bytes.InBytes()); |
| } |
| |
| TEST(ByteCountDeathTest, ConstructionCheckedInvalid) { |
| BASE_EXPECT_DEATH( |
| ByteCount::FromChecked( |
| CheckedNumeric<int64_t>(std::numeric_limits<int64_t>::max()) + 1), |
| ""); |
| } |
| |
| TEST(ByteCount, ConstructionOtherUnitIntegral) { |
| // 32-bit numbers that will overflow if multiplied as |
| // CheckedNumeric<uint32_t>, but not if multiplied as CheckedNumeric<int64_t>. |
| // This verifies that the implementations of the KiB, MiB and GiB templates |
| // avoid a subtle conversion order bug. |
| constexpr int64_t kLargeKiB32 = |
| std::numeric_limits<uint32_t>::max() / 1024 + 1; |
| constexpr int64_t kLargeMiB32 = |
| std::numeric_limits<uint32_t>::max() / 1024 / 1024 + 1; |
| constexpr int64_t kLargeGiB32 = |
| std::numeric_limits<uint32_t>::max() / 1024 / 1024 / 1024 + 1; |
| |
| static_assert(kLargeKiB32 * 1024 > std::numeric_limits<uint32_t>::max()); |
| static_assert(kLargeMiB32 * 1024 * 1024 > |
| std::numeric_limits<uint32_t>::max()); |
| static_assert(kLargeGiB32 * 1024 * 1024 * 1024 > |
| std::numeric_limits<uint32_t>::max()); |
| |
| auto kib5 = KiB(5); |
| EXPECT_EQ(5 * 1024, kib5.InBytes()); |
| EXPECT_EQ(kLargeKiB32 * 1024, |
| KiB(checked_cast<uint32_t>(kLargeKiB32)).InBytes()); |
| |
| auto mib5 = MiB(5); |
| EXPECT_EQ(5 * 1024 * 1024, mib5.InBytes()); |
| EXPECT_EQ(kLargeMiB32 * 1024 * 1024, |
| MiB(checked_cast<uint32_t>(kLargeMiB32)).InBytes()); |
| |
| auto gib5 = GiB(5); |
| EXPECT_EQ(5ll * 1024 * 1024 * 1024, gib5.InBytes()); |
| EXPECT_EQ(kLargeGiB32 * 1024 * 1024 * 1024, |
| GiB(checked_cast<uint32_t>(kLargeGiB32)).InBytes()); |
| } |
| |
| TEST(ByteCount, ConstructionOtherUnitFloat) { |
| auto kib = KiB(5.5); |
| EXPECT_EQ(5632, kib.InBytes()); |
| EXPECT_EQ(5632.0, kib.InBytesF()); |
| |
| auto mib = MiB(2.3); |
| // Round down from 2411724.8 |
| EXPECT_EQ(2411724, mib.InBytes()); |
| EXPECT_EQ(2411724.0, mib.InBytesF()); |
| |
| auto gib = GiB(12.81); |
| // Round down from 13754632765.4 |
| EXPECT_EQ(13754632765, gib.InBytes()); |
| EXPECT_EQ(13754632765.0, gib.InBytesF()); |
| |
| auto negative_kib = KiB(-4.2); |
| // Round up from -4300.8 |
| EXPECT_EQ(-4300, negative_kib.InBytes()); |
| EXPECT_EQ(-4300.0, negative_kib.InBytesF()); |
| |
| auto negative_mib = MiB(-9.89); |
| // Round up from -10370416.64 |
| EXPECT_EQ(-10370416, negative_mib.InBytes()); |
| EXPECT_EQ(-10370416.0, negative_mib.InBytesF()); |
| |
| auto negative_gib = GiB(-5.17); |
| // Round up from -5551245230.08 |
| EXPECT_EQ(-5551245230, negative_gib.InBytes()); |
| EXPECT_EQ(-5551245230.0, negative_gib.InBytesF()); |
| } |
| |
| TEST(ByteCountDeathTest, ConstructionOtherUnitInvalid) { |
| BASE_EXPECT_DEATH(KiB(std::numeric_limits<int64_t>::max()), ""); |
| |
| BASE_EXPECT_DEATH(MiB(std::numeric_limits<int64_t>::max()), ""); |
| |
| BASE_EXPECT_DEATH(GiB(std::numeric_limits<int64_t>::max()), ""); |
| } |
| |
| TEST(ByteCount, IsPositive) { |
| EXPECT_FALSE(ByteCount(-2).is_positive()); |
| EXPECT_FALSE(ByteCount(0).is_positive()); |
| EXPECT_TRUE(ByteCount(2).is_positive()); |
| } |
| |
| TEST(ByteCount, IsZero) { |
| EXPECT_FALSE(ByteCount(-2).is_zero()); |
| EXPECT_TRUE(ByteCount(0).is_zero()); |
| EXPECT_FALSE(ByteCount(2).is_zero()); |
| } |
| |
| TEST(ByteCount, IsNegative) { |
| EXPECT_TRUE(ByteCount(-2).is_negative()); |
| EXPECT_FALSE(ByteCount(0).is_negative()); |
| EXPECT_FALSE(ByteCount(2).is_negative()); |
| } |
| |
| TEST(ByteCount, InFloating) { |
| constexpr ByteCount bytes(3435973836); |
| EXPECT_THAT(bytes.InBytesF(), testing::DoubleEq(3435973836.0)); |
| EXPECT_THAT(bytes.InKiBF(), testing::DoubleEq(3355443.19921875)); |
| EXPECT_THAT(bytes.InMiBF(), testing::DoubleEq(3276.7999992370605)); |
| EXPECT_THAT(bytes.InGiBF(), testing::DoubleEq(3.1999999992549419)); |
| constexpr ByteCount morebytes(3435973836343597383); |
| EXPECT_THAT(morebytes.InTiBF(), testing::DoubleEq(3124999.9995849044)); |
| EXPECT_THAT(morebytes.InPiBF(), testing::DoubleEq(3051.7578120946332)); |
| EXPECT_THAT(morebytes.InEiBF(), testing::DoubleEq(2.9802322383736652)); |
| } |
| |
| TEST(ByteCountDeathTest, InUnsignedInvalid) { |
| ByteCount bytes(-2); |
| BASE_EXPECT_DEATH(bytes.InBytesUnsigned(), ""); |
| } |
| |
| TEST(ByteCount, UnarySigns) { |
| ByteCount bytes(42); |
| EXPECT_EQ(bytes, +bytes); |
| |
| ByteCount negative_bytes(-42); |
| EXPECT_EQ(-bytes, negative_bytes); |
| } |
| |
| TEST(ByteCount, Arithmetic) { |
| ByteCount bytes(42); |
| |
| ByteCount add = bytes + ByteCount(10); |
| EXPECT_EQ(52, add.InBytes()); |
| |
| ByteCount sub = bytes - ByteCount(10); |
| EXPECT_EQ(32, sub.InBytes()); |
| |
| ByteCount mul = bytes * 10; |
| EXPECT_EQ(420, mul.InBytes()); |
| |
| ByteCount mul2 = 10 * bytes; |
| EXPECT_EQ(420, mul2.InBytes()); |
| |
| ByteCount div = bytes / 2; |
| EXPECT_EQ(21, div.InBytes()); |
| } |
| |
| TEST(ByteCount, ArithmeticCompound) { |
| ByteCount bytes(42); |
| |
| bytes += ByteCount(10); |
| EXPECT_EQ(52, bytes.InBytes()); |
| |
| bytes -= ByteCount(10); |
| EXPECT_EQ(42, bytes.InBytes()); |
| |
| bytes *= 10; |
| EXPECT_EQ(420, bytes.InBytes()); |
| |
| bytes /= 2; |
| EXPECT_EQ(210, bytes.InBytes()); |
| } |
| |
| TEST(ByteCountDeathTest, ArithmeticInvalid) { |
| ByteCount max_bytes(std::numeric_limits<int64_t>::max()); |
| |
| BASE_EXPECT_DEATH({ max_bytes + max_bytes; }, ""); |
| |
| BASE_EXPECT_DEATH({ ByteCount() - max_bytes - max_bytes; }, ""); |
| |
| BASE_EXPECT_DEATH({ max_bytes * 2; }, ""); |
| |
| BASE_EXPECT_DEATH({ max_bytes / 0; }, ""); |
| } |
| |
| TEST(ByteCount, Comparison) { |
| ByteCount a(1); |
| ByteCount b(2); |
| ByteCount c(2); |
| |
| EXPECT_TRUE(a < b); |
| EXPECT_FALSE(b < a); |
| EXPECT_FALSE(b < c); |
| |
| EXPECT_TRUE(a <= b); |
| EXPECT_FALSE(b <= a); |
| EXPECT_TRUE(b <= c); |
| |
| EXPECT_FALSE(a > b); |
| EXPECT_TRUE(b > a); |
| EXPECT_FALSE(b > c); |
| |
| EXPECT_FALSE(a >= b); |
| EXPECT_TRUE(b >= a); |
| EXPECT_TRUE(b >= c); |
| |
| EXPECT_FALSE(a == b); |
| EXPECT_TRUE(b == c); |
| |
| EXPECT_TRUE(a != b); |
| EXPECT_FALSE(b != c); |
| } |
| |
| TEST(ByteCount, StreamOperator) { |
| struct TestValue { |
| int64_t bytes; |
| const char* expected; |
| } kTestValues[] = { |
| {-1, "-1B"}, |
| {0, "0B"}, |
| {1, "1B"}, |
| |
| {1024 - 1, "1023B"}, |
| {1024, "1KiB"}, |
| {1024 + 1, "1025B (1.001KiB)"}, |
| {-(1024 - 1), "-1023B"}, |
| {-(1024), "-1KiB"}, |
| {-(1024 + 1), "-1025B (-1.001KiB)"}, |
| |
| {1024 * 1024 - 1, "1048575B (1023.999KiB)"}, |
| {1024 * 1024, "1MiB"}, |
| {1024 * 1024 + 1'000, "1049576B (1.001MiB)"}, |
| {-(1024 * 1024 - 1), "-1048575B (-1023.999KiB)"}, |
| {-(1024 * 1024), "-1MiB"}, |
| {-(1024 * 1024 + 1'000), "-1049576B (-1.001MiB)"}, |
| |
| {1024LL * 1024 * 1024 - 1'000, "1073740824B (1023.999MiB)"}, |
| {1024LL * 1024 * 1024, "1GiB"}, |
| {1024LL * 1024 * 1024 + 1'000'000, "1074741824B (1.001GiB)"}, |
| |
| {1024LL * 1024 * 1024 * 1024 - 1'000'000, "1099510627776B (1023.999GiB)"}, |
| {1024LL * 1024 * 1024 * 1024, "1TiB"}, |
| {1024LL * 1024 * 1024 * 1024 + 1'000'000'000, |
| "1100511627776B (1.001TiB)"}, |
| |
| {1024LL * 1024 * 1024 * 1024 * 1024 - 1'000'000'000, |
| "1125898906842624B (1023.999TiB)"}, |
| {1024LL * 1024 * 1024 * 1024 * 1024, "1PiB"}, |
| {1024LL * 1024 * 1024 * 1024 * 1024 + 1'000'000'000'000, |
| "1126899906842624B (1.001PiB)"}, |
| |
| {1024LL * 1024 * 1024 * 1024 * 1024 * 1024 - 1'000'000'000'000, |
| "1152920504606846976B (1023.999PiB)"}, |
| {1024LL * 1024 * 1024 * 1024 * 1024 * 1024, "1EiB"}, |
| {1024LL * 1024 * 1024 * 1024 * 1024 * 1024 + 1'000'000'000'000'000, |
| "1153921504606846976B (1.001EiB)"}, |
| {-(1024LL * 1024 * 1024 * 1024 * 1024 * 1024 - 1'000'000'000'000), |
| "-1152920504606846976B (-1023.999PiB)"}, |
| {-(1024LL * 1024 * 1024 * 1024 * 1024 * 1024), "-1EiB"}, |
| {-(1024LL * 1024 * 1024 * 1024 * 1024 * 1024 + 1'000'000'000'000'000), |
| "-1153921504606846976B (-1.001EiB)"}, |
| |
| {ByteCount::Max().InBytes(), "9223372036854775807B (8.000EiB)"}, |
| {std::numeric_limits<int64_t>::min(), "-8EiB"}, |
| {std::numeric_limits<int64_t>::min() + 1, |
| "-9223372036854775807B (-8.000EiB)"}, |
| }; |
| for (const auto& test_value : kTestValues) { |
| std::stringstream ss; |
| ss << ByteCount(test_value.bytes); |
| EXPECT_EQ(test_value.expected, ss.str()); |
| } |
| } |
| |
| } // namespace base |