/* * Copyright 2015 - 2025 gary@drinkingtea.net * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #if __has_include() #include #endif #include #undef NDEBUG #include #include #include [[nodiscard]] static uint64_t steadyNowMs() { #if __has_include() using namespace std::chrono; return static_cast( duration_cast( steady_clock::now().time_since_epoch()).count()); #else return 0; #endif } template> uint64_t timeMapStrToUuid(int elemCnt, int lookups, uint64_t seed = 4321) noexcept { ox::UUID::seedGenerator({1234, seed}); Map map; // setup test map for (int i = 0; i < elemCnt; ++i) { auto const uuid = ox::UUID::generate().unwrap(); map[uuid.toString()] = uuid; } auto const keys = map.keys(); ox::Random rand; // start auto const startTime = steadyNowMs(); for (int i = 0; i < lookups; ++i) { auto const&k = keys[rand.gen() % keys.size()]; map[k]; oxExpect(map[k], ox::UUID::fromString(k).unwrap()); } return steadyNowMs() - startTime; } template> uint64_t timeMapUuidToStr(int elemCnt, int lookups, uint64_t seed = 4321) noexcept { ox::UUID::seedGenerator({1234, seed}); Map map; // setup test map for (int i = 0; i < elemCnt; ++i) { auto const uuid = ox::UUID::generate().unwrap(); map[uuid] = uuid.toString(); } auto const keys = map.keys(); ox::Random rand; // start auto const startTime = steadyNowMs(); for (int i = 0; i < lookups; ++i) { auto const&k = keys[rand.gen() % keys.size()]; oxExpect(map[k], k.toString()); } return steadyNowMs() - startTime; } static ox::Error compareMaps(int lookupCnt = 1'000'000) { auto const seed = steadyNowMs(); uint64_t hashTime{}; uint64_t smallTime{}; int elemCnt = 1; oxOut("UUIDStr to UUID:\n\n"); while (hashTime >= smallTime) { smallTime = timeMapStrToUuid>(elemCnt, lookupCnt, seed); hashTime = timeMapStrToUuid>(elemCnt, lookupCnt, seed); oxOutf( "UUIDStr to UUID: elemCnt: {}, lookupCnt: {} - hash map time: {}ms, small map time: {}ms\n", elemCnt, lookupCnt, hashTime, smallTime); ++elemCnt; } oxOut("\n\nString to UUID:\n\n"); hashTime = 0; smallTime = 0; elemCnt = 1; while (hashTime >= smallTime) { smallTime = timeMapStrToUuid>(elemCnt, lookupCnt, seed); hashTime = timeMapStrToUuid>(elemCnt, lookupCnt, seed); oxOutf( "String to UUID: elemCnt: {}, lookupCnt: {} - hash map time: {}ms, small map time: {}ms\n", elemCnt, lookupCnt, hashTime, smallTime); ++elemCnt; } oxOut("\n\nUUID to UUIDStr:\n\n"); hashTime = 0; smallTime = 0; elemCnt = 1; while (hashTime >= smallTime) { smallTime = timeMapUuidToStr>(elemCnt, lookupCnt, seed); hashTime = timeMapUuidToStr>(elemCnt, lookupCnt, seed); oxOutf( "UUID to UUIDStr: elemCnt: {}, lookupCnt: {} - hash map time: {}ms, small map time: {}ms\n", elemCnt, lookupCnt, hashTime, smallTime); ++elemCnt; } oxOut("\n"); return {}; } static std::map tests = { { "malloc", [] { ox::Buffer buff(ox::units::MB); ox::heapmgr::initHeap(&buff[0], &buff[buff.size()-1]); auto a1 = static_cast(ox::heapmgr::malloc(5)); auto a2 = static_cast(ox::heapmgr::malloc(5)); oxAssert(a1 >= buff.front().unwrap() && a1 < buff.back().unwrap(), "malloc is broken"); OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage) oxAssert(a2 >= buff.front().unwrap() && a2 < buff.back().unwrap() && a2 > a1 + 5, "malloc is broken"); OX_CLANG_NOWARN_END ox::heapmgr::free(a1); ox::heapmgr::free(a2); return ox::Error(0); } }, { "itoa", []() { ox::Array buff; ox::CharBuffWriter bw(buff); oxAssert(ox::writeItoa(5, bw), "ox::writeItoa returned Error"); oxExpect(ox::StringView(buff.data()), ox::StringView("5")); OX_RETURN_ERROR(bw.seekp(0)); oxAssert(ox::writeItoa(50, bw), "ox::writeItoa returned Error"); oxExpect(ox::StringView(buff.data()), ox::StringView("50")); OX_RETURN_ERROR(bw.seekp(0)); oxAssert(ox::writeItoa(500, bw), "ox::writeItoa returned Error"); oxExpect(ox::StringView(buff.data()), ox::StringView("500")); return ox::Error{}; } }, { "ABCDEFG != HIJKLMN", []() { return ox::Error(ox::memcmp("ABCDEFG", "HIJKLMN", 7) >= 0); } }, { "HIJKLMN != ABCDEFG", []() { return ox::Error(ox::memcmp("HIJKLMN", "ABCDEFG", 7) <= 0); } }, { "ABCDEFG == ABCDEFG", []() { return ox::Error(ox::memcmp("ABCDEFG", "ABCDEFG", 7) != 0); } }, { "ABCDEFGHI == ABCDEFG", []() { return ox::Error(ox::memcmp("ABCDEFGHI", "ABCDEFG", 7) != 0); } }, { "IString", []() { ox::IString<5> s; OX_RETURN_ERROR(s.append("A")); OX_RETURN_ERROR(s.append("B")); OX_RETURN_ERROR(s.append("9")); OX_RETURN_ERROR(s.append("C")); oxAssert(s == "AB9C", "IString append broken"); s = "asdf"; oxAssert(s == "asdf", "String assign broken"); oxAssert(s != "aoeu", "String assign broken"); oxAssert(s.len() == 4, "String assign broken"); return ox::Error(0); } }, { "String", []() { ox::String s; s += "A"; s += "B"; s += 9; s += "C"; oxAssert(s == "AB9C", "String append broken"); s = "asdf"; oxAssert(s == "asdf", "String assign broken"); s += "aoeu"; oxAssert(s == "asdfaoeu", "String append broken"); const ox::StringView str = "asdf"; oxAssert(beginsWith(str, "as"), "String beginsWith is broken"); oxAssert(beginsWith(str, "asd"), "String beginsWith is broken"); oxAssert(beginsWith(str, "asdf"), "String beginsWith is broken"); oxAssert(!beginsWith(str, "aa"), "String beginsWith is broken"); oxAssert(!beginsWith(str, "aaaaaaa"), "String beginsWith is broken"); oxAssert(!beginsWith(str, "li"), "String beginsWith is broken"); oxAssert(!beginsWith(str, "afoif"), "String beginsWith is broken"); oxAssert(endsWith(str, "df"), "String endsWith is broken"); oxAssert(!endsWith(str, "awefawe"), "String endsWith is broken"); oxAssert(!endsWith(str, "eu"), "String endsWith is broken"); oxAssert(ox::StringView("Write") != ox::String(""), "String / StringView comparison broken"); oxAssert(ox::String("Write") != ox::StringView(""), "String / StringView comparison broken"); oxAssert(ox::String("Write") == ox::StringView("Write"), "String / StringView comparison broken"); oxExpect(ox::String("asdf").substr(1, 3), "sd"); oxAssert( ox::String(ox::StringView("Write")) == ox::StringView("Write"), "String / StringView comparison broken"); return ox::Error(0); } }, { "Vector", [] { ox::Vector v; oxAssert(v.size() == 0, "Initial Vector size not 0"); oxAssert(v.empty(), "Vector::empty() is broken"); auto insertTest = [&v](int val, std::size_t size) { v.push_back(val); OX_RETURN_ERROR(ox::Error(v.size() != size, "Vector size incorrect")); OX_RETURN_ERROR(ox::Error(v[v.size() - 1] != val, "Vector value wrong")); return ox::Error(0); }; oxAssert(insertTest(42, 1), "Vector insertion failed"); oxAssert(insertTest(100, 2), "Vector insertion failed"); oxAssert(insertTest(102, 3), "Vector insertion failed"); return ox::Error(0); } }, { "Vector::shrink_to_fit", [] { { ox::Vector> v; v.reserve(50); v.emplace_back("asdf"); v.emplace_back("aoeu"); auto const origData = v.data(); v.shrink_to_fit(); oxExpect(v[0], "asdf"); oxExpect(v[1], "aoeu"); oxExpect(v.capacity(), 2u); oxAssert(origData != v.data(), "shrink_to_fit did not create a new allocation"); } { ox::Vector> v; v.reserve(2); v.emplace_back("asdf"); v.emplace_back("aoeu"); auto const origData = v.data(); v.shrink_to_fit(); oxExpect(v[0], "asdf"); oxExpect(v[1], "aoeu"); oxExpect(v.capacity(), 2u); oxAssert(origData == v.data(), "shrink_to_fit inappropriately created a new allocation"); } return ox::Error{}; } }, { "findIdx", [] { ox::Vector> const v {"zero", "one", "two", "three", "four"}; oxExpect(ox::findIdx(v.begin(), v.end(), "zero").or_value(5), 0u); oxExpect(ox::findIdx(v.begin(), v.end(), "one").or_value(5), 1u); oxExpect(ox::findIdx(v.begin(), v.end(), "two").or_value(5), 2u); oxExpect(ox::findIdx(v.begin(), v.end(), "three").or_value(5), 3u); oxExpect(ox::findIdx(v.begin(), v.end(), "four").or_value(5), 4u); oxExpect(ox::findIdx(v.begin(), v.end(), "five").or_value(5), 5u); oxExpect(ox::findIdx(v.begin(), v.end(), "six").or_value(6), 6u); return ox::Error{}; } }, { "SmallMap", [] { ox::SmallMap map; map["asdf"] = "aoeu"; oxExpect(map["asdf"], "aoeu"); oxExpect(map.size(), 1u); oxExpect(map["aoeu"], ""); oxExpect(map.size(), 2u); ox::SmallMap cmap; cmap["asdf"] = "aoeu"; auto constexpr constTest = [](ox::SmallMap const&map) { OX_REQUIRE(asdf, map.at("asdf")); oxExpect(*asdf, "aoeu"); oxExpect(map.size(), 1u); auto const aoeu = map.at("aoeu"); oxExpect(aoeu.ok(), false); oxExpect(map.size(), 1u); return ox::Error{}; }; return constTest(cmap); } }, { "SmallMap2", [] { ox::SmallMap si; si["asdf"] = 42; si["aoeu"] = 100; oxAssert(si["asdf"] == 42, "asdf != 42"); oxAssert(si["aoeu"] == 100, "aoeu != 100"); ox::SmallMap ii; ii[4] = 42; ii[5] = 100; oxAssert(ii[4] == 42, "4 != 42"); oxAssert(ii[5] == 100, "5 != 100"); return ox::Error(0); } }, { "HashMap", [] { ox::HashMap si; si["asdf"] = 42; si["aoeu"] = 100; oxAssert(si["asdf"] == 42, "asdf != 42"); oxAssert(si["aoeu"] == 100, "aoeu != 100"); ox::HashMap ii; ii[4] = 42; ii[5] = 100; oxAssert(ii[4] == 42, "4 != 42"); oxAssert(ii[5] == 100, "5 != 100"); return ox::Error(0); } }, { "TimeSmallMapMillion", [] { timeMapStrToUuid>(1'000, 1'000'000); return ox::Error{}; } }, { "TimeHashMapMillion", [] { timeMapStrToUuid>(1'000, 1'000'000); return ox::Error{}; } }, { "CompareMaps", [] { return compareMaps(); } }, { "Serialize-Int", [] { using BA = ox::Array; const auto actual = ox::serialize(256).unwrap(); oxOutf("[{}, {}, {}, {}]", static_cast(actual[0]), static_cast(actual[1]), static_cast(actual[2]), static_cast(actual[3])); oxExpect(ox::serialize(4).unwrap(), BA({4, 0, 0, 0})); oxExpect(ox::serialize(256).unwrap(), BA({0, 1, 0, 0})); oxExpect(ox::serialize(257).unwrap(), BA({1, 1, 0, 0})); oxExpect(ox::serialize(4).unwrap(), BA({4, 0, 0, 0})); oxExpect(ox::serialize(256).unwrap(), BA({0, 1, 0, 0})); oxExpect(ox::serialize(257).unwrap(), BA({1, 1, 0, 0})); constexpr auto neg1 = static_cast(-1); // ARM64 Linux assumes -1 literals are ints... oxExpect(ox::serialize(0xffff'ffff).unwrap(), BA({neg1, neg1, neg1, neg1})); return ox::Error(0); } }, { "BufferWriter", [] { ox::Buffer b; ox::BufferWriter w(&b); oxAssert(w.write("asdf", 4), "write failed"); oxExpect(b.size(), 4u); oxAssert(w.write("aoeu", 4), "write failed"); oxExpect(b.size(), 8u); oxExpect(ox::StringView(b.data(), b.size()), "asdfaoeu"); ox::StringView constexpr qwerty = "qwerty"; oxAssert(w.write(qwerty.data(), qwerty.bytes()), "write failed"); oxExpect(b.size(), 14u); oxExpect(ox::StringView(b.data(), b.size()), "asdfaoeuqwerty"); return ox::Error(0); } }, { "FromHex", [] { oxExpect(ox::detail::fromHex("01").unwrap(), 0x01); oxExpect(ox::detail::fromHex("02").unwrap(), 0x02); oxExpect(ox::detail::fromHex("03").unwrap(), 0x03); oxExpect(ox::detail::fromHex("04").unwrap(), 0x04); oxExpect(ox::detail::fromHex("05").unwrap(), 0x05); oxExpect(ox::detail::fromHex("06").unwrap(), 0x06); oxExpect(ox::detail::fromHex("07").unwrap(), 0x07); oxExpect(ox::detail::fromHex("08").unwrap(), 0x08); oxExpect(ox::detail::fromHex("0d").unwrap(), 0x0d); oxExpect(ox::detail::fromHex("0e").unwrap(), 0x0e); oxExpect(ox::detail::fromHex("0f").unwrap(), 0x0f); oxExpect(ox::detail::fromHex("0F").unwrap(), 0x0f); oxExpect(ox::detail::fromHex("fF").unwrap(), 0xff); oxExpect(ox::detail::fromHex("ff").unwrap(), 0xff); oxExpect(ox::detail::fromHex("a0").unwrap(), 0xa0); oxExpect(ox::detail::fromHex("93").unwrap(), 0x93); oxExpect(ox::detail::fromHex("40").unwrap(), 0x40); return ox::Error(0); } }, { "ToHex", [] { oxExpect(ox::detail::toHex(0x01), "01"); oxExpect(ox::detail::toHex(0x02), "02"); oxExpect(ox::detail::toHex(0x03), "03"); oxExpect(ox::detail::toHex(0x04), "04"); oxExpect(ox::detail::toHex(0x05), "05"); oxExpect(ox::detail::toHex(0x06), "06"); oxExpect(ox::detail::toHex(0x07), "07"); oxExpect(ox::detail::toHex(0x08), "08"); oxExpect(ox::detail::toHex(0x0d), "0d"); oxExpect(ox::detail::toHex(0x0e), "0e"); oxExpect(ox::detail::toHex(0x0f), "0f"); oxExpect(ox::detail::toHex(0x93), "93"); oxExpect(ox::detail::toHex(0x40), "40"); oxExpect(ox::detail::toHex(0xf0), "f0"); return ox::Error(0); } }, { "UUID", [] { constexpr ox::StringView uuidStr = "8d814442-f46e-4cc3-8edc-ca3c01cc86db"; OX_REQUIRE(uuid, ox::UUID::fromString(uuidStr)); oxExpect(uuid.toString(), uuidStr); oxExpect(ox::UUID{}.isNull(), true); oxExpect(ox::UUID::fromString(uuidStr).value.isNull(), false); return ox::Error(0); } }, { "UUID::generate", [] { ox::UUID::seedGenerator({1234, 4321}); oxExpect(ox::UUID::generate().unwrap().toString(), "5c3f4b5e-ccbf-4727-7f03-3053dedc8827"); oxExpect(ox::UUID::generate().unwrap().toString(), "90d0274a-2774-4afa-88e5-0c1d60ba3abf"); oxExpect(ox::UUID::generate().unwrap().toString(), "7df77910-841c-48ba-ea2e-44521ac47c2e"); return ox::Error(0); } }, { "StringSplit", [] { ox::StringView sv = "ab.cd"; auto list = ox::split(sv, "."); oxExpect(list.size(), 2u); oxExpect(list[0], "ab"); oxExpect(list[1], "cd"); sv = "ab.cd.fg"; list = ox::split(sv, "."); oxExpect(list.size(), 3u); oxExpect(list[0], "ab"); oxExpect(list[1], "cd"); oxExpect(list[2], "fg"); sv = "ab.cd."; list = ox::split(sv, "."); oxExpect(list.size(), 2u); oxExpect(list[0], "ab"); oxExpect(list[1], "cd"); sv = ".ab.cd."; list = ox::split(sv, "."); oxExpect(list.size(), 2u); oxExpect(list[0], "ab"); oxExpect(list[1], "cd"); sv = "."; list = ox::split(sv, "."); oxExpect(list.size(), 0u); sv = "."; list = ox::split(sv, "."); oxExpect(list.size(), 0u); sv = ""; list = ox::split(sv, "."); oxExpect(list.size(), 0u); // split by single char sv = "ab.cd"; list = ox::split(sv, '.'); oxExpect(list.size(), 2u); oxExpect(list[0], "ab"); oxExpect(list[1], "cd"); sv = "ab.cd.fg"; list = ox::split(sv, '.'); oxExpect(list.size(), 3u); oxExpect(list[0], "ab"); oxExpect(list[1], "cd"); oxExpect(list[2], "fg"); sv = "ab.cd."; list = ox::split(sv, '.'); oxExpect(list.size(), 2u); oxExpect(list[0], "ab"); oxExpect(list[1], "cd"); sv = ".ab.cd."; list = ox::split(sv, '.'); oxExpect(list.size(), 2u); oxExpect(list[0], "ab"); oxExpect(list[1], "cd"); sv = "."; list = ox::split(sv, '.'); oxExpect(list.size(), 0u); sv = "."; list = ox::split(sv, '.'); oxExpect(list.size(), 0u); sv = ""; list = ox::split(sv, '.'); oxExpect(list.size(), 0u); return ox::Error(0); } }, }; int main(int argc, const char **argv) { if (argc < 2) { oxError("Must specify test to run"); return -1; } auto const args = ox::Span{argv, static_cast(argc)}; auto const testName = args[1]; auto const func = tests.find(testName); if (func != tests.end()) { oxAssert(func->second(), "Test returned Error"); return 0; } return -1; }