blob: 31a29737937e1f848039b75c04fcdc4bbbd9d1b3 [file] [log] [blame]
// 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