text: #include <cstdint>
  #include <chrono>
  #include <map>
  #include <string>
  #include <type_traits>
  #include <utility>
  
  #include "broker/convert.hh"
  #include "broker/data.hh"
  #include "broker/optional.hh"
  
  #define SUITE data
  #include "test.hpp"
  
  using namespace broker;
  
  TEST(basic) {
    CHECK(std::is_same<boolean, bool>::value);
    CHECK(std::is_same<integer, int64_t>::value);
    CHECK(std::is_same<count, uint64_t>::value);
    CHECK(std::is_same<real, double>::value);
  }
  
  TEST(timespan) {
    auto s = timespan{42};
    CHECK(std::chrono::nanoseconds{42} == s);
  }
  
  TEST(timestamp) {
    auto ts = timestamp{timespan{42}};
    CHECK(ts.time_since_epoch() == timespan{42});
  }
  
  TEST(enum) {
    auto e = enum_value{"foo"};
    CHECK_EQUAL(e.name, "foo");
  }
  
  TEST(address) {
    address a;
    // Default-constructed addresses are considered IPv6.
    CHECK(!a.is_v4());
    CHECK(a.is_v6());
    MESSAGE("parsing");
    auto opt = to<address>("dead::beef");
    REQUIRE(opt);
    CHECK(!opt->is_v4());
    CHECK(opt->is_v6());
    opt = to<address>("1.2.3.4");
    REQUIRE(opt);
    CHECK(opt->is_v4());
    CHECK(!opt->is_v6());
    MESSAGE("printing");
    CHECK_EQUAL(to_string(*opt), "1.2.3.4");
    MESSAGE("masking");
    CHECK(opt->mask(96 + 16));
    CHECK_EQUAL(to_string(*opt), "1.2.0.0");
  }
  
  TEST(port) {
    port p;
    CHECK_EQUAL(p.number(), 0u);
    CHECK(p.type() == port::protocol::unknown);
    p = {80, port::protocol::tcp};
    MESSAGE("parsing");
    auto opt = to<port>("8/icmp");
    REQUIRE(opt);
    CHECK_EQUAL(*opt, port(8, port::protocol::icmp));
    opt = to<port>("42/nonsense");
    REQUIRE(opt);
    CHECK_EQUAL(*opt, port(42, port::protocol::unknown));
    MESSAGE("printing");
    CHECK_EQUAL(to_string(p), "80/tcp");
    p = {0, port::protocol::unknown};
    CHECK_EQUAL(to_string(p), "0/?");
  }
  
  TEST(subnet) {
    subnet sn;
    CHECK_EQUAL(sn.length(), 0u);
    CHECK_EQUAL(to_string(sn), "::/0");
    auto a = to<address>("1.2.3.4");
    auto b = to<address>("1.2.3.0");
    REQUIRE(a);
    REQUIRE(b);
    sn = {*a, 24};
    CHECK_EQUAL(sn.length(), 24u);
    CHECK_EQUAL(sn.network(), *b);
  }
  
  TEST(data - construction) {
    MESSAGE("default construction");
    data d;
    CHECK(caf::get_if<none>(&d));
  }
  
  TEST(data - assignment) {
    data d;
    d = 42;
    auto i = caf::get_if<integer>(&d);
    REQUIRE(i);
    CHECK_EQUAL(*i, 42);
    d = data{7};
    i = caf::get_if<integer>(&d);
    CHECK_EQUAL(*i, 7);
    d = "foo";
    auto s = caf::get_if<std::string>(&d);
    REQUIRE(s);
    CHECK_EQUAL(*s, "foo");
  }
  
  TEST(data - relational operators) {
    CHECK_NOT_EQUAL(data{true}, data{false});
    CHECK_NOT_EQUAL(data{1}, data{true});
    CHECK_NOT_EQUAL(data{-1}, data{1});
    CHECK_NOT_EQUAL(data{1}, data{1u});
    CHECK_NOT_EQUAL(data{1.111}, data{1.11});
    CHECK_EQUAL(data{1.111}, data{1.111});
  }
  
  TEST(data - vector) {
    vector v{42, 43, 44};
    REQUIRE_EQUAL(v.size(), 3u);
    CHECK_EQUAL(v[1], data{43});
    CHECK_EQUAL(to_string(v), "(42, 43, 44)");
  }
  
  TEST(data - set) {
    set s{"foo", "bar", "baz", "foo"};
    CHECK_EQUAL(s.size(), 3u); // one duplicate
    CHECK(s.find("bar") != s.end());
    CHECK(s.find("qux") == s.end());
    CHECK_EQUAL(to_string(s), "{bar, baz, foo}");
  }
  
  TEST(data - table) {
    table t{{"foo", 42}, {"bar", 43}, {"baz", 44}};
    auto i = t.find("foo");
    REQUIRE(i != t.end());
    CHECK_EQUAL(i->second, data{42});
    CHECK_EQUAL(to_string(t), "{bar -> 43, baz -> 44, foo -> 42}");
  }
  