| // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors |
| // Licensed under the MIT License: |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a copy |
| // of this software and associated documentation files (the "Software"), to deal |
| // in the Software without restriction, including without limitation the rights |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| // copies of the Software, and to permit persons to whom the Software is |
| // furnished to do so, subject to the following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included in |
| // all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| // THE SOFTWARE. |
| |
| #include "units.h" |
| #include <kj/compat/gtest.h> |
| #include <iostream> |
| |
| namespace kj { |
| namespace { |
| |
| class Bytes; |
| class KiB; |
| class MiB; |
| |
| typedef Quantity<int, Bytes> ByteCount; |
| typedef Quantity<int, KiB> KiBCount; |
| typedef Quantity<int, MiB> MiBCount; |
| |
| constexpr ByteCount BYTE KJ_UNUSED = unit<ByteCount>(); |
| constexpr KiBCount KIB = unit<KiBCount>(); |
| constexpr MiBCount MIB = unit<MiBCount>(); |
| |
| constexpr UnitRatio<int, Bytes, KiB> BYTES_PER_KIB KJ_UNUSED = 1024 * BYTE / KIB; |
| constexpr UnitRatio<int, Bytes, MiB> BYTES_PER_MIB KJ_UNUSED = 1024 * 1024 * BYTE / MIB; |
| constexpr auto KIB_PER_MIB = 1024 * KIB / MIB; |
| |
| template <typename T, typename U> |
| kj::String KJ_STRINGIFY(Quantity<T, U> value) { |
| return kj::str(value / unit<Quantity<T, U>>()); |
| } |
| |
| TEST(UnitMeasure, Basics) { |
| KiBCount k = 15 * KIB; |
| EXPECT_EQ(15, k / KIB); |
| EXPECT_EQ(16 * KIB, k + KIB); |
| |
| k += KIB; |
| k *= 2048; |
| |
| EXPECT_EQ(32 * MIB, k / KIB_PER_MIB); |
| |
| EXPECT_TRUE(2 * KIB < 4 * KIB); |
| EXPECT_FALSE(8 * KIB < 4 * KIB); |
| } |
| |
| template <typename T, typename U> |
| static void assertSameType() { |
| U u; |
| T* t = &u; |
| *t = 0; |
| } |
| |
| TEST(UnitMeasure, AtLeastUInt) { |
| assertSameType<uint8_t , AtLeastUInt< 2>>(); |
| assertSameType<uint8_t , AtLeastUInt< 3>>(); |
| assertSameType<uint8_t , AtLeastUInt< 4>>(); |
| assertSameType<uint8_t , AtLeastUInt< 5>>(); |
| assertSameType<uint8_t , AtLeastUInt< 6>>(); |
| assertSameType<uint8_t , AtLeastUInt< 7>>(); |
| assertSameType<uint8_t , AtLeastUInt< 8>>(); |
| assertSameType<uint16_t, AtLeastUInt< 9>>(); |
| assertSameType<uint16_t, AtLeastUInt<10>>(); |
| assertSameType<uint16_t, AtLeastUInt<13>>(); |
| assertSameType<uint16_t, AtLeastUInt<16>>(); |
| assertSameType<uint32_t, AtLeastUInt<17>>(); |
| assertSameType<uint32_t, AtLeastUInt<23>>(); |
| assertSameType<uint32_t, AtLeastUInt<24>>(); |
| assertSameType<uint32_t, AtLeastUInt<25>>(); |
| assertSameType<uint32_t, AtLeastUInt<32>>(); |
| assertSameType<uint64_t, AtLeastUInt<33>>(); |
| assertSameType<uint64_t, AtLeastUInt<40>>(); |
| assertSameType<uint64_t, AtLeastUInt<41>>(); |
| assertSameType<uint64_t, AtLeastUInt<47>>(); |
| assertSameType<uint64_t, AtLeastUInt<48>>(); |
| assertSameType<uint64_t, AtLeastUInt<52>>(); |
| assertSameType<uint64_t, AtLeastUInt<64>>(); |
| |
| // COMPILE ERROR: assertSameType<uint64_t, AtLeastUInt<65>>(); |
| } |
| |
| TEST(UnitMeasure, BoundedConst) { |
| // TODO(someday): Some script should attempt to compile this test once with each "COMPILE ERROR" |
| // line restored to verify that they actually error out. |
| |
| KJ_EXPECT((bounded<456>() + bounded<123>()).unwrap() == 456 + 123); |
| KJ_EXPECT((bounded<456>() - bounded<123>()).unwrap() == 456 - 123); |
| KJ_EXPECT((bounded<456>() * bounded<123>()).unwrap() == 456 * 123); |
| KJ_EXPECT((bounded<456>() / bounded<123>()).unwrap() == 456 / 123); |
| KJ_EXPECT((bounded<456>() % bounded<123>()).unwrap() == 456 % 123); |
| KJ_EXPECT((bounded<456>() << bounded<5>()).unwrap() == 456 << 5); |
| KJ_EXPECT((bounded<456>() >> bounded<2>()).unwrap() == 456 >> 2); |
| |
| KJ_EXPECT(bounded<123>() == bounded<123>()); |
| KJ_EXPECT(bounded<123>() != bounded<456>()); |
| KJ_EXPECT(bounded<123>() < bounded<456>()); |
| KJ_EXPECT(bounded<456>() > bounded<123>()); |
| KJ_EXPECT(bounded<123>() <= bounded<456>()); |
| KJ_EXPECT(bounded<456>() >= bounded<123>()); |
| |
| KJ_EXPECT(!(bounded<123>() == bounded<456>())); |
| KJ_EXPECT(!(bounded<123>() != bounded<123>())); |
| KJ_EXPECT(!(bounded<456>() < bounded<123>())); |
| KJ_EXPECT(!(bounded<123>() > bounded<456>())); |
| KJ_EXPECT(!(bounded<456>() <= bounded<123>())); |
| KJ_EXPECT(!(bounded<123>() >= bounded<456>())); |
| |
| { |
| uint16_t succ = unbound(bounded<12345>()); |
| KJ_EXPECT(succ == 12345); |
| |
| // COMPILE ERROR: uint8_t err KJ_UNUSED = unbound(bounded<12345>()); |
| } |
| |
| // COMPILE ERROR: auto err1 KJ_UNUSED = bounded<(0xffffffffffffffffull)>() + bounded<1>(); |
| // COMPILE ERROR: auto err2 KJ_UNUSED = bounded<1>() - bounded<2>(); |
| // COMPILE ERROR: auto err3 KJ_UNUSED = bounded<(1ull << 60)>() * bounded<(1ull << 60)>(); |
| // COMPILE ERROR: auto err4 KJ_UNUSED = bounded<1>() / bounded<0>(); |
| // COMPILE ERROR: auto err5 KJ_UNUSED = bounded<1>() % bounded<0>(); |
| // COMPILE ERROR: auto err6 KJ_UNUSED = bounded<1>() << bounded<64>(); |
| // COMPILE ERROR: auto err7 KJ_UNUSED = bounded<(1ull << 60)>() << bounded<4>(); |
| // COMPILE ERROR: auto err8 KJ_UNUSED = bounded<1>() >> bounded<64>(); |
| |
| // COMPILE ERROR: boundedAdd<0xffffffffffffffffull, 1>(); |
| // COMPILE ERROR: boundedSub<1, 2>(); |
| // COMPILE ERROR: boundedMul<0x100000000, 0x100000000>(); |
| // COMPILE ERROR: boundedLShift<0x10, 60>(); |
| } |
| |
| template <uint value, typename T = uint> |
| constexpr Bounded<value, T> boundedValue(NoInfer<T> runtimeValue = value) { |
| return Bounded<value, T>(runtimeValue, unsafe); |
| } |
| |
| TEST(UnitMeasure, Bounded) { |
| // TODO(someday): Some script should attempt to compile this test once with each "COMPILE ERROR" |
| // line restored to verify that they actually error out. |
| |
| KJ_EXPECT((boundedValue<456>() + boundedValue<123>()).unwrap() == 456 + 123); |
| KJ_EXPECT(boundedValue<456>().subtractChecked(boundedValue<123>(), [](){}).unwrap() == 456 - 123); |
| KJ_EXPECT((boundedValue<456>() * boundedValue<123>()).unwrap() == 456 * 123); |
| KJ_EXPECT((boundedValue<456>() / boundedValue<123>()).unwrap() == 456 / 123); |
| KJ_EXPECT((boundedValue<456>() % boundedValue<123>()).unwrap() == 456 % 123); |
| |
| |
| { |
| Bounded<123, uint8_t> succ KJ_UNUSED; |
| // COMPILE ERROR: Bounded<1234, uint8_t> err KJ_UNUSED; |
| // COMPILE ERROR: auto err KJ_UNUSED = boundedValue<0xffffffffull>() + boundedValue<1>(); |
| } |
| |
| { |
| Bounded<123, uint8_t> succ1 KJ_UNUSED = boundedValue<123>(); |
| Bounded<123, uint8_t> succ2 KJ_UNUSED = boundedValue<122>(); |
| Bounded<123, uint8_t> succ3 KJ_UNUSED = boundedValue<0>(); |
| // COMPILE ERROR: Bounded<123, uint8_t> err KJ_UNUSED = boundedValue<124>(); |
| // COMPILE ERROR: Bounded<123, uint8_t> err KJ_UNUSED = boundedValue<125>(); |
| // COMPILE ERROR: Bounded<123, uint8_t> err KJ_UNUSED = boundedValue<123456>(); |
| } |
| |
| Bounded<123, uint8_t> foo; |
| foo = boundedValue<123>(); |
| foo = boundedValue<122>(); |
| foo = boundedValue<0>(); |
| // COMPILE ERROR: foo = boundedValue<124>(); |
| // COMPILE ERROR: foo = boundedValue<125>(); |
| // COMPILE ERROR: foo = boundedValue<123456>(); |
| |
| assertMax<122>(foo, []() {}); |
| // COMPILE ERROR: assertMax<123>(foo, []() {}); |
| // COMPILE ERROR: assertMax<124>(foo, []() {}); |
| |
| assertMaxBits<6>(foo, []() {}); |
| // COMPILE ERROR: assertMaxBits<7>(foo, []() {}); |
| // COMPILE ERROR: assertMaxBits<8>(foo, []() {}); |
| |
| Bounded<12, uint8_t> bar; |
| // COMPILE ERROR: bar = foo; |
| // COMPILE ERROR: bar = foo.assertMax<13>([]() {}); |
| bool caught = false; |
| foo = boundedValue<13>(); |
| bar = foo.assertMax<12>([&]() { caught = true; }); |
| KJ_EXPECT(caught); |
| |
| foo = boundedValue<100>() + boundedValue<23>(); |
| // COMPILE ERROR: foo = boundedValue<100>() + boundedValue<24>(); |
| |
| bar = boundedValue<3>() * boundedValue<4>(); |
| // COMPILE ERROR: bar = boundedValue<2>() * boundedValue<7>(); |
| |
| foo.subtractChecked(boundedValue<122>(), []() { KJ_FAIL_EXPECT(""); }); |
| foo.subtractChecked(boundedValue<123>(), []() { KJ_FAIL_EXPECT(""); }); |
| caught = false; |
| foo.subtractChecked(boundedValue<124>(), [&]() { caught = true; }); |
| KJ_EXPECT(caught); |
| |
| { |
| Bounded<65535, uint16_t> succ1 KJ_UNUSED = bounded((uint16_t)123); |
| // COMPILE ERROR: Bounded<65534, uint16_t> err KJ_UNUSED = bounded((uint16_t)123); |
| } |
| |
| uint old = foo.unwrap(); |
| foo = foo * unit<decltype(foo)>(); |
| KJ_EXPECT(foo.unwrap() == old); |
| |
| { |
| Bounded<1234, uint16_t> x = bounded<123>(); |
| uint16_t succ = unbound(x); |
| KJ_EXPECT(succ == 123); |
| |
| // COMPILE ERROR: uint8_t err KJ_UNUSED = unbound(x); |
| } |
| } |
| |
| TEST(UnitMeasure, BoundedVsGuardedConst) { |
| // TODO(someday): Some script should attempt to compile this test once with each "COMPILE ERROR" |
| // line restored to verify that they actually error out. |
| |
| KJ_EXPECT((boundedValue<456>() + bounded<123>()).unwrap() == 456 + 123); |
| KJ_EXPECT(boundedValue<456>().subtractChecked(bounded<123>(), [](){}).unwrap() == 456 - 123); |
| KJ_EXPECT((boundedValue<456>() * bounded<123>()).unwrap() == 456 * 123); |
| KJ_EXPECT((boundedValue<456>() / bounded<123>()).unwrap() == 456 / 123); |
| KJ_EXPECT((boundedValue<456>() % bounded<123>()).unwrap() == 456 % 123); |
| |
| { |
| Bounded<123, uint8_t> succ1 KJ_UNUSED = bounded<123>(); |
| Bounded<123, uint8_t> succ2 KJ_UNUSED = bounded<122>(); |
| Bounded<123, uint8_t> succ3 KJ_UNUSED = bounded<0>(); |
| // COMPILE ERROR: Bounded<123, uint8_t> err KJ_UNUSED = bounded<124>(); |
| // COMPILE ERROR: Bounded<123, uint8_t> err KJ_UNUSED = bounded<125>(); |
| // COMPILE ERROR: Bounded<123, uint8_t> err KJ_UNUSED = bounded<123456>(); |
| } |
| |
| Bounded<123, uint8_t> foo; |
| foo = bounded<123>(); |
| foo = bounded<122>(); |
| foo = bounded<0>(); |
| // COMPILE ERROR: foo = bounded<124>(); |
| // COMPILE ERROR: foo = bounded<125>(); |
| // COMPILE ERROR: foo = bounded<123456>(); |
| |
| Bounded<16, uint8_t> bar; |
| // COMPILE ERROR: bar = foo >> bounded<2>(); |
| bar = foo >> bounded<3>(); |
| |
| // COMPILE ERROR: foo = bar << bounded<3>(); |
| foo = bar << bounded<2>(); |
| } |
| |
| TEST(UnitMeasure, BoundedRange) { |
| uint expected = 0; |
| for (auto i: zeroTo(bounded<10>())) { |
| Bounded<10, uint8_t> value = i; |
| KJ_EXPECT(unbound(value) == expected++); |
| } |
| KJ_EXPECT(expected == 10); |
| |
| expected = 0; |
| for (auto i: zeroTo(bounded((uint8_t)10))) { |
| Bounded<255, uint8_t> value = i; |
| KJ_EXPECT(unbound(value) == expected++); |
| } |
| KJ_EXPECT(expected == 10); |
| |
| expected = 3; |
| for (auto i: range(bounded((uint8_t)3), bounded((uint8_t)10))) { |
| Bounded<255, uint8_t> value = i; |
| KJ_EXPECT(unbound(value) == expected++); |
| } |
| KJ_EXPECT(expected == 10); |
| } |
| |
| TEST(UnitMeasure, BoundedQuantity) { |
| auto BYTES = unit<Quantity<Bounded<12345, uint16_t>, byte>>(); |
| |
| uint expected = 0; |
| for (auto i: zeroTo(bounded<10>() * BYTES)) { |
| Quantity<Bounded<10, uint8_t>, byte> value = i; |
| KJ_EXPECT(unbound(value / BYTES) == expected++); |
| } |
| KJ_EXPECT(expected == 10); |
| |
| expected = 0; |
| for (auto i: zeroTo(bounded((uint8_t)10) * BYTES)) { |
| Quantity<Bounded<255, uint8_t>, byte> value = i; |
| KJ_EXPECT(unbound(value / BYTES) == expected++); |
| } |
| KJ_EXPECT(expected == 10); |
| |
| expected = 3; |
| for (auto i: range(bounded((uint8_t)3) * BYTES, bounded((uint8_t)10) * BYTES)) { |
| Quantity<Bounded<255, uint8_t>, byte> value = i; |
| KJ_EXPECT(unbound(value / BYTES) == expected++); |
| } |
| KJ_EXPECT(expected == 10); |
| } |
| |
| template <typename T> |
| void assertTypeAndValue(T a, T b) { KJ_EXPECT(a == b); } |
| |
| TEST(UnitMeasure, BoundedMinMax) { |
| assertTypeAndValue(bounded<5>(), kj::max(bounded<4>(), bounded<5>())); |
| assertTypeAndValue(bounded<5>(), kj::max(bounded<5>(), bounded<4>())); |
| assertTypeAndValue(bounded<4>(), kj::max(bounded<4>(), bounded<4>())); |
| |
| assertTypeAndValue(bounded<4>(), kj::min(bounded<4>(), bounded<5>())); |
| assertTypeAndValue(bounded<4>(), kj::min(bounded<5>(), bounded<4>())); |
| assertTypeAndValue(bounded<4>(), kj::min(bounded<4>(), bounded<4>())); |
| |
| typedef uint8_t t1; |
| typedef uint16_t t2; |
| |
| assertTypeAndValue(boundedValue<5,t2>(3), kj::max(boundedValue<4,t2>(3), boundedValue<5,t1>(2))); |
| assertTypeAndValue(boundedValue<5,t2>(3), kj::max(boundedValue<5,t1>(2), boundedValue<4,t2>(3))); |
| assertTypeAndValue(boundedValue<4,t2>(3), kj::max(boundedValue<4,t2>(3), boundedValue<4,t2>(3))); |
| |
| assertTypeAndValue(boundedValue<4,t2>(2), kj::min(boundedValue<4,t2>(3), boundedValue<5,t1>(2))); |
| assertTypeAndValue(boundedValue<4,t2>(2), kj::min(boundedValue<5,t1>(2), boundedValue<4,t2>(3))); |
| assertTypeAndValue(boundedValue<4,t2>(3), kj::min(boundedValue<4,t2>(3), boundedValue<4,t2>(3))); |
| |
| assertTypeAndValue(boundedValue<5,t1>(4), kj::max(bounded<4>(), boundedValue<5,t1>(2))); |
| assertTypeAndValue(boundedValue<5,t1>(4), kj::max(boundedValue<5,t1>(2), bounded<4>())); |
| |
| assertTypeAndValue(boundedValue<4,t1>(2), kj::min(bounded<4>(), boundedValue<5,t1>(2))); |
| assertTypeAndValue(boundedValue<4,t1>(2), kj::min(boundedValue<5,t1>(2), bounded<4>())); |
| |
| // These two are degenerate cases. Currently they fail to compile but maybe they shouldn't? |
| // assertTypeAndValue(bounded<5>(), kj::max(boundedValue<4,t2>(3), bounded<5>())); |
| // assertTypeAndValue(bounded<5>(), kj::max(bounded<5>(), boundedValue<4,t2>(3))); |
| |
| assertTypeAndValue(boundedValue<4,t2>(3), kj::min(boundedValue<4,t2>(3), bounded<5>())); |
| assertTypeAndValue(boundedValue<4,t2>(3), kj::min(bounded<5>(), boundedValue<4,t2>(3))); |
| } |
| |
| #if !_MSC_VER // MSVC barfs on this test and I just don't have time to care. |
| KJ_TEST("compare bounded quantities of different bounds") { |
| auto foo = boundedValue<12345, uint>(123) * unit<Quantity<BoundedConst<1>, byte>>(); |
| auto bar = boundedValue<54321, uint>(123) * unit<Quantity<BoundedConst<1>, byte>>(); |
| auto baz = bounded<123>() * unit<Quantity<BoundedConst<1>, byte>>(); |
| |
| KJ_EXPECT(foo == foo); |
| KJ_EXPECT(foo == bar); |
| KJ_EXPECT(foo == baz); |
| KJ_EXPECT(bar == foo); |
| KJ_EXPECT(bar == bar); |
| KJ_EXPECT(bar == baz); |
| KJ_EXPECT(baz == foo); |
| KJ_EXPECT(baz == bar); |
| KJ_EXPECT(baz == baz); |
| |
| auto denom = unit<Quantity<BoundedConst<1>, int>>(); |
| |
| KJ_EXPECT(foo / denom == foo / denom); |
| KJ_EXPECT(foo / denom == bar / denom); |
| KJ_EXPECT(foo / denom == baz / denom); |
| KJ_EXPECT(bar / denom == foo / denom); |
| KJ_EXPECT(bar / denom == bar / denom); |
| KJ_EXPECT(bar / denom == baz / denom); |
| KJ_EXPECT(baz / denom == foo / denom); |
| KJ_EXPECT(baz / denom == bar / denom); |
| KJ_EXPECT(baz / denom == baz / denom); |
| } |
| #endif |
| |
| } // namespace |
| } // namespace kj |