units-test.c++ 13.4 KB
Newer Older
Kenton Varda's avatar
Kenton Varda committed
1 2
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
3
//
Kenton Varda's avatar
Kenton Varda committed
4 5 6 7 8 9
// 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:
10
//
Kenton Varda's avatar
Kenton Varda committed
11 12
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
13
//
Kenton Varda's avatar
Kenton Varda committed
14 15 16 17 18 19 20
// 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.
21

Kenton Varda's avatar
Kenton Varda committed
22
#include "units.h"
23
#include <kj/compat/gtest.h>
24 25
#include <iostream>

26
namespace kj {
27 28 29 30 31 32
namespace {

class Bytes;
class KiB;
class MiB;

33 34 35
typedef Quantity<int, Bytes> ByteCount;
typedef Quantity<int, KiB> KiBCount;
typedef Quantity<int, MiB> MiBCount;
36

37
constexpr ByteCount BYTE KJ_UNUSED = unit<ByteCount>();
38 39
constexpr KiBCount KIB = unit<KiBCount>();
constexpr MiBCount MIB = unit<MiBCount>();
40

41 42
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;
43
constexpr auto KIB_PER_MIB = 1024 * KIB / MIB;
44 45

template <typename T, typename U>
46 47
kj::String KJ_STRINGIFY(Quantity<T, U> value) {
  return kj::str(value / unit<Quantity<T, U>>());
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
}

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);
}

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
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>>();
}

99
TEST(UnitMeasure, BoundedConst) {
100 101 102
  // TODO(someday): Some script should attempt to compile this test once with each "COMPILE ERROR"
  //   line restored to verify that they actually error out.

103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 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<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>()));
124 125

  {
126
    uint16_t succ = unbound(bounded<12345>());
127 128
    KJ_EXPECT(succ == 12345);

129
    // COMPILE ERROR: uint8_t err KJ_UNUSED = unbound(bounded<12345>());
130 131
  }

132 133 134 135 136 137 138 139 140 141 142 143 144
  // 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>();
145 146
}

147
template <uint value, typename T = uint>
148 149
constexpr Bounded<value, T> boundedValue(NoInfer<T> runtimeValue = value) {
  return Bounded<value, T>(runtimeValue, unsafe);
150 151
}

152
TEST(UnitMeasure, Bounded) {
153 154 155
  // TODO(someday): Some script should attempt to compile this test once with each "COMPILE ERROR"
  //   line restored to verify that they actually error out.

156 157 158 159 160
  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);
161 162 163


  {
164 165 166
    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>();
167 168 169
  }

  {
170 171 172 173 174 175
    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>();
176 177
  }

178 179 180 181 182 183 184
  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>();
185 186 187 188 189 190 191 192 193

  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, []() {});

194
  Bounded<12, uint8_t> bar;
195 196 197
  // COMPILE ERROR: bar = foo;
  // COMPILE ERROR: bar = foo.assertMax<13>([]() {});
  bool caught = false;
198
  foo = boundedValue<13>();
199 200 201
  bar = foo.assertMax<12>([&]() { caught = true; });
  KJ_EXPECT(caught);

202 203
  foo = boundedValue<100>() + boundedValue<23>();
  // COMPILE ERROR: foo = boundedValue<100>() + boundedValue<24>();
204

205 206
  bar = boundedValue<3>() * boundedValue<4>();
  // COMPILE ERROR: bar = boundedValue<2>() * boundedValue<7>();
207

208 209
  foo.subtractChecked(boundedValue<122>(), []() { KJ_FAIL_EXPECT(""); });
  foo.subtractChecked(boundedValue<123>(), []() { KJ_FAIL_EXPECT(""); });
210
  caught = false;
211
  foo.subtractChecked(boundedValue<124>(), [&]() { caught = true; });
212 213 214
  KJ_EXPECT(caught);

  {
215 216
    Bounded<65535, uint16_t> succ1 KJ_UNUSED = bounded((uint16_t)123);
    // COMPILE ERROR: Bounded<65534, uint16_t> err KJ_UNUSED = bounded((uint16_t)123);
217 218 219 220 221 222 223
  }

  uint old = foo.unwrap();
  foo = foo * unit<decltype(foo)>();
  KJ_EXPECT(foo.unwrap() == old);

  {
224 225
    Bounded<1234, uint16_t> x = bounded<123>();
    uint16_t succ = unbound(x);
226 227
    KJ_EXPECT(succ == 123);

228
    // COMPILE ERROR: uint8_t err KJ_UNUSED = unbound(x);
229 230 231
  }
}

232
TEST(UnitMeasure, BoundedVsGuardedConst) {
233 234 235
  // TODO(someday): Some script should attempt to compile this test once with each "COMPILE ERROR"
  //   line restored to verify that they actually error out.

236 237 238 239 240
  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);
241 242

  {
243 244 245 246 247 248
    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>();
249 250
  }

251 252 253 254 255 256 257
  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>();
258

259 260 261
  Bounded<16, uint8_t> bar;
  // COMPILE ERROR: bar = foo >> bounded<2>();
  bar = foo >> bounded<3>();
262

263 264
  // COMPILE ERROR: foo = bar << bounded<3>();
  foo = bar << bounded<2>();
265 266
}

267
TEST(UnitMeasure, BoundedRange) {
268
  uint expected = 0;
269 270 271
  for (auto i: zeroTo(bounded<10>())) {
    Bounded<10, uint8_t> value = i;
    KJ_EXPECT(unbound(value) == expected++);
272 273 274 275
  }
  KJ_EXPECT(expected == 10);

  expected = 0;
276 277 278
  for (auto i: zeroTo(bounded((uint8_t)10))) {
    Bounded<255, uint8_t> value = i;
    KJ_EXPECT(unbound(value) == expected++);
279 280 281 282
  }
  KJ_EXPECT(expected == 10);

  expected = 3;
283 284 285
  for (auto i: range(bounded((uint8_t)3), bounded((uint8_t)10))) {
    Bounded<255, uint8_t> value = i;
    KJ_EXPECT(unbound(value) == expected++);
286 287 288 289
  }
  KJ_EXPECT(expected == 10);
}

290 291
TEST(UnitMeasure, BoundedQuantity) {
  auto BYTES = unit<Quantity<Bounded<12345, uint16_t>, byte>>();
292 293

  uint expected = 0;
294 295 296
  for (auto i: zeroTo(bounded<10>() * BYTES)) {
    Quantity<Bounded<10, uint8_t>, byte> value = i;
    KJ_EXPECT(unbound(value / BYTES) == expected++);
297 298 299 300
  }
  KJ_EXPECT(expected == 10);

  expected = 0;
301 302 303
  for (auto i: zeroTo(bounded((uint8_t)10) * BYTES)) {
    Quantity<Bounded<255, uint8_t>, byte> value = i;
    KJ_EXPECT(unbound(value / BYTES) == expected++);
304 305 306 307
  }
  KJ_EXPECT(expected == 10);

  expected = 3;
308 309 310
  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++);
311 312 313 314
  }
  KJ_EXPECT(expected == 10);
}

315 316 317
template <typename T>
void assertTypeAndValue(T a, T b) { KJ_EXPECT(a == b); }

318 319 320 321
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>()));
322

323 324 325
  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>()));
326 327 328 329

  typedef uint8_t t1;
  typedef uint16_t t2;

330 331 332
  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)));
333

334 335 336
  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)));
337

338 339
  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>()));
340

341 342
  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>()));
343 344

  // These two are degenerate cases. Currently they fail to compile but meybe they shouldn't?
345 346
//  assertTypeAndValue(bounded<5>(), kj::max(boundedValue<4,t2>(3), bounded<5>()));
//  assertTypeAndValue(bounded<5>(), kj::max(bounded<5>(), boundedValue<4,t2>(3)));
347

348 349
  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)));
350 351
}

352
}  // namespace
353
}  // namespace kj