initial matador ng commit

This commit is contained in:
2025-02-02 20:37:12 +01:00
parent 19de5714f4
commit ded3daceb3
378 changed files with 14913 additions and 13431 deletions
@@ -0,0 +1,66 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/utils/types.hpp"
#include "matador/utils/basic_type_converter.hpp"
using namespace matador::utils;
TEST_CASE("Test convert any type to string", "[any type visitor]") {
basic_type_converter<std::string> to_string_visitor;
database_type value = 6;
auto res = basic_type_converter<std::string>::convert_value(value);
REQUIRE(res.is_ok());
REQUIRE(*res == "6");
value = 2.5;
res = basic_type_converter<std::string>::convert_value(value);
REQUIRE(res.is_ok());
REQUIRE(*res == "2.5");
value = true;
res = basic_type_converter<std::string>::convert_value(value);
REQUIRE(res.is_ok());
REQUIRE(*res == "true");
value = "hello";
res = basic_type_converter<std::string>::convert_value(value);
REQUIRE(res.is_ok());
REQUIRE(*res == "hello");
value = std::string{"world"};
res = basic_type_converter<std::string>::convert_value(value);
REQUIRE(res.is_ok());
REQUIRE(*res == "world");}
TEST_CASE("Test convert any type to integral", "[any type visitor]") {
std::vector<std::pair<database_type, int>> values = {
{6, 6},
{2.5, 2},
{true, 1},
{"hello", 0},
{std::string{"world"}, 0}
};
for (const auto &value : values) {
const auto res = basic_type_converter<long>::convert_value(value.first);
REQUIRE(res.is_ok());
REQUIRE(*res == value.second);
}
}
TEST_CASE("Test convert any type to floating point", "[any type visitor]") {
std::vector<std::pair<database_type, double>> values = {
{6, 6},
{2.5, 2.5},
{true, 1},
{"hello", 0},
{std::string{"world"}, 0}
};
for (const auto &value : values) {
const auto res = basic_type_converter<double>::convert_value(value.first);
REQUIRE(res.is_ok());
REQUIRE(*res == value.second);
}
}
+160
View File
@@ -0,0 +1,160 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/utils/convert.hpp"
//#include "matador/utils/date.hpp"
//#include "matador/utils/time.hpp"
#include "matador/utils/types.hpp"
using namespace matador::utils;
template<typename From, typename To>
void validate_conversion(From from)
{
auto res = to<To>(from);
REQUIRE(res.is_ok());
REQUIRE(static_cast<To>(from) == *res);
}
template<typename From, typename To>
void validate_conversion(const From &from, To expected_to)
{
auto res = to<To>(from);
REQUIRE(res.is_ok());
REQUIRE(*res == expected_to);
}
template<typename From>
void validate_conversion(const From &from, const std::string &expected_to)
{
auto res = to<std::string>(from);
REQUIRE(res.is_ok());
REQUIRE(*res == expected_to);
}
template<typename From>
void validate_conversion(const From &from, const char *expected_to)
{
validate_conversion<From>(from, std::string{expected_to});
}
template<typename From>
void validate_integral_conversion(From from) {
validate_conversion<From, int8_t>(from);
validate_conversion<From, int16_t>(from);
validate_conversion<From, int32_t>(from);
validate_conversion<From, int64_t>(from);
}
TEST_CASE("Test integral conversion", "[convert][integral]") {
validate_integral_conversion<int8_t>(-56);
validate_integral_conversion<int16_t>(-127);
validate_integral_conversion<int32_t>(-9876543);
validate_integral_conversion<int64_t>(-123456790);
validate_integral_conversion<uint8_t>(56);
validate_integral_conversion<uint16_t>(127);
validate_integral_conversion<uint32_t>(9876543);
validate_integral_conversion<uint64_t>(123456790);
validate_conversion<int16_t, int8_t>(513, 1);
validate_conversion<int32_t, int8_t>(515, 3);
validate_conversion<int64_t, int8_t>(516, 4);
}
TEST_CASE("Test floating point conversion", "[convert][floating_point]") {
validate_conversion<float, float>(-0.1f);
validate_conversion<float, double>(-0.1f);
validate_conversion<double, double>(-0.44444);
validate_conversion<double, float>(-0.44444);
}
TEST_CASE("Test integral to string conversion", "[convert][integral][string]") {
validate_conversion<int8_t>(-56, "-56");
validate_conversion<int16_t>(-127, "-127");
validate_conversion<int32_t>(-9876543, "-9876543");
validate_conversion<int64_t>(-123456790, "-123456790");
validate_conversion<uint8_t>(56, "56");
validate_conversion<uint16_t>(127, "127");
validate_conversion<uint32_t>(9876543, "9876543");
validate_conversion<uint64_t>(123456790, "123456790");
}
TEST_CASE("Test floating point to string conversion", "[convert][floating_point][string]") {
validate_conversion<float>(-56.1234f, "-56.1234");
validate_conversion<double>(-127.444449, "-127.444449");
}
TEST_CASE("Test string to integral conversion", "[convert][string][integral]") {
validate_conversion<std::string, int8_t>("-56", -56);
validate_conversion<std::string, int16_t>("-127", -127);
validate_conversion<std::string, int32_t>("-9876543", -9876543);
validate_conversion<std::string, int64_t>("-123456790", -123456790);
validate_conversion<std::string, uint8_t>("56", 56);
validate_conversion<std::string, uint16_t>("127", 127);
validate_conversion<std::string, uint32_t>("9876543", 9876543);
validate_conversion<std::string, uint64_t>("123456790", 123456790);
validate_conversion<const char*, int8_t>("-56", -56);
validate_conversion<const char*, int16_t>("-127", -127);
validate_conversion<const char*, int32_t>("-9876543", -9876543);
validate_conversion<const char*, int64_t>("-123456790", -123456790);
validate_conversion<const char*, uint8_t>("56", 56);
validate_conversion<const char*, uint16_t>("127", 127);
validate_conversion<const char*, uint32_t>("9876543", 9876543);
validate_conversion<const char*, uint64_t>("123456790", 123456790);
}
TEST_CASE("Test string to floating point conversion", "[convert][string][floating_point]") {
validate_conversion<std::string, float>("-56.1234", -56.1234f);
validate_conversion<std::string, double>("-127.444449", -127.444449);
validate_conversion<const char*, float>("-56.1234", -56.1234f);
validate_conversion<const char*, double>("-127.444449", -127.444449);
}
TEST_CASE("Test blob to blob conversion", "[convert][blob]") {
blob from{1, 2, 3, 4};
const auto res = to<blob>(from);
REQUIRE(res.is_ok());
REQUIRE(from == *res);
}
// TEST_CASE("Validate date to string conversion", "[convert][date][string]") {
// matador::date today;
// const auto expected_string = matador::utils::to_string(today);
// std::string to;
//
// convert(to, today);
//
// REQUIRE(expected_string == to);
// }
//
// TEST_CASE("Validate date conversion leads to an exception", "[convert][date][exception]") {
// matador::date today;
// int to{};
// convert(to, today);
// REQUIRE(to == today.julian_date());
// }
//
// TEST_CASE("Validate time to string conversion", "[convert][time][string]") {
// matador::time now;
// const auto expected_string = matador::utils::to_string(now);
// std::string to;
//
// convert(to, now);
//
// REQUIRE(expected_string == to);
// }
//
// TEST_CASE("Validate time conversion leads to an exception", "[convert][time][exception]") {
// matador::time now;
// int to;
// convert(to, now);
// REQUIRE(to == now.get_time_info().seconds_since_epoch);
// }
+28
View File
@@ -0,0 +1,28 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/utils/default_type_traits.hpp"
using namespace matador::utils;
TEST_CASE("Test default data types", "[data_types][type]") {
REQUIRE(data_type_traits<int8_t>::type() == basic_type::type_int8);
REQUIRE(data_type_traits<int16_t>::type() == basic_type::type_int16);
REQUIRE(data_type_traits<int32_t>::type() == basic_type::type_int32);
REQUIRE(data_type_traits<int64_t>::type() == basic_type::type_int64);
REQUIRE(data_type_traits<uint8_t>::type() == basic_type::type_uint8);
REQUIRE(data_type_traits<uint16_t>::type() == basic_type::type_uint16);
REQUIRE(data_type_traits<uint32_t>::type() == basic_type::type_uint32);
REQUIRE(data_type_traits<uint64_t>::type() == basic_type::type_uint64);
REQUIRE(data_type_traits<bool>::type() == basic_type::type_bool);
REQUIRE(data_type_traits<float>::type() == basic_type::type_float);
REQUIRE(data_type_traits<double>::type() == basic_type::type_double);
REQUIRE(data_type_traits<const char*>::type(32) == basic_type::type_varchar);
REQUIRE(data_type_traits<const char*>::type(0) == basic_type::type_text);
REQUIRE(data_type_traits<char*>::type(32) == basic_type::type_varchar);
REQUIRE(data_type_traits<char*>::type(0) == basic_type::type_text);
REQUIRE(data_type_traits<char[]>::type(32) == basic_type::type_varchar);
REQUIRE(data_type_traits<char[]>::type(0) == basic_type::type_text);
REQUIRE(data_type_traits<std::string>::type(32) == basic_type::type_varchar);
REQUIRE(data_type_traits<std::string>::type(0) == basic_type::type_text);
REQUIRE(data_type_traits<blob>::type(0) == basic_type::type_blob);
}
+113
View File
@@ -0,0 +1,113 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/utils/di.hpp"
#include <iostream>
namespace detail {
class greeter {
public:
virtual ~greeter() = default;
[[nodiscard]] virtual std::string greet() const = 0;
};
class smart_greeter final : public greeter {
public:
[[nodiscard]] std::string greet() const override { return "hey dude"; }
};
class hello_greeter final : public greeter {
public:
[[nodiscard]] std::string greet() const override { return "hello"; }
};
class vehicle
{
public:
virtual ~vehicle() = default;
[[nodiscard]] virtual long id() const = 0;
};
class truck final : public vehicle
{
public:
truck() : id_(++id_counter_) {}
[[nodiscard]] long id() const override { return id_; }
private:
static long id_counter_;
long id_{};
};
long truck::id_counter_ = 0;
class unknown {};
class per_thread {
public:
virtual ~per_thread() = default;
virtual void dump() = 0;
};
class per_thread_dumper final : public per_thread
{
public:
explicit per_thread_dumper(std::string name ) : name_(std::move(name)) {}
void dump() override {
std::cout << name_ << ": thread id " << std::this_thread::get_id() << "\n";
}
private:
std::string name_;
};
}
using namespace matador::utils;
TEST_CASE("Test dependency injection", "[dependency-injection]") {
di::install_module([](di::module &module) {
module.bind<detail::greeter>()->to_singleton<detail::smart_greeter>();
});
di::inject<detail::greeter> g1;
REQUIRE(g1);
auto g2 = g1;
REQUIRE(g2);
REQUIRE(g1 == g2);
g2 = std::move(g1);
REQUIRE(!g1);
REQUIRE(g2);
REQUIRE(g1 != g2);
auto g3(std::move(g2));
REQUIRE(!g2);
REQUIRE(g3);
REQUIRE(g3 != g2);
di::module m;
m.bind<detail::greeter>()->to_singleton<detail::hello_greeter>();
m.bind<detail::greeter>("smart")->to_singleton<detail::smart_greeter>();
di::inject<detail::greeter> g4(m, "smart");
REQUIRE(g4);
REQUIRE(g4->greet() == "hey dude");
di::inject<detail::greeter> g5(m);
REQUIRE(g5);
REQUIRE(g5->greet() == "hello");
REQUIRE(g4 != g5);
// UNIT_ASSERT_EXCEPTION(matador::di::inject<detail::unknown> u, std::logic_error, "unknown type");
}
+32
View File
@@ -0,0 +1,32 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/utils/field_attributes.hpp"
using namespace matador::utils;
TEST_CASE("Test field attribute", "[field-attribute]") {
field_attributes attr;
REQUIRE(attr.size() == 0);
REQUIRE(attr.options() == constraints::NONE);
attr = 255;
REQUIRE(attr.size() == 255);
REQUIRE(attr.options() == constraints::NONE);
attr = constraints::INDEX;
REQUIRE(attr.size() == 0);
REQUIRE(attr.options() == constraints::INDEX);
attr = { 255, constraints::DEFAULT };
REQUIRE(attr.size() == 255);
REQUIRE(attr.options() == constraints::DEFAULT);
field_attributes attr2{255};
REQUIRE(attr2.size() == 255);
REQUIRE(attr2.options() == constraints::NONE);
field_attributes attr3{constraints::UNIQUE};
REQUIRE(attr3.size() == 0);
REQUIRE(attr3.options() == constraints::UNIQUE);
}
+118
View File
@@ -0,0 +1,118 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/utils/identifier.hpp"
#include "matador/utils/default_type_traits.hpp"
using namespace matador::utils;
TEST_CASE("Test create identifier", "[identifier][create]") {
const identifier id;
REQUIRE(id.is_null());
REQUIRE(!id.is_integer());
REQUIRE(!id.is_floating_point());
REQUIRE(!id.is_bool());
REQUIRE(!id.is_varchar());
REQUIRE(!id.is_date());
REQUIRE(!id.is_time());
REQUIRE(!id.is_blob());
REQUIRE(!id.is_valid());
REQUIRE(id.str() == "null");
}
TEST_CASE("Test assign value to identifier", "[identifier][assign]") {
identifier id;
REQUIRE(id.is_null());
REQUIRE(!id.is_valid());
REQUIRE(id.str() == "null");
id = 7;
REQUIRE(!id.is_null());
REQUIRE(id.is_valid());
REQUIRE(id.is_integer());
REQUIRE(id.str() == "7");
REQUIRE(id.type() == basic_type::type_int32);
REQUIRE(id.type_index() == std::type_index(typeid(int)));
id = std::string{"UniqueId"};
REQUIRE(!id.is_null());
REQUIRE(id.is_valid());
REQUIRE(id.is_varchar());
REQUIRE(id.str() == "UniqueId");
id = "UniqueId";
REQUIRE(!id.is_null());
REQUIRE(id.is_valid());
REQUIRE(id.is_varchar());
// REQUIRE(id == identifier{"UniqueId"});
}
TEST_CASE("Test compare identifier", "[identifier][compare]") {
identifier id1{6}, id2{7};
REQUIRE(id1 != id2);
REQUIRE(id1 < id2);
REQUIRE(id1 <= id2);
REQUIRE(id2 > id1);
REQUIRE(id2 >= id1);
REQUIRE(id1.hash() != id2.hash());
id2 = 6;
REQUIRE(id1 == id2);
REQUIRE(id1.hash() == id2.hash());
}
identifier create(const int id) {
return identifier{id};
}
TEST_CASE("Test copy identifier" "[identifier][copy]") {
identifier id1{"Unique"};
REQUIRE(id1.is_valid());
REQUIRE(id1.str() == "Unique");
const identifier id2(id1);
REQUIRE(id1 == id2);
REQUIRE(id1.hash() == id2.hash());
id1.clear();
REQUIRE(id1.is_null());
identifier id3 = id1;
REQUIRE(id1 == id3);
REQUIRE(id1 < id3);
REQUIRE(id3.is_null());
REQUIRE(id1.hash() == id3.hash());
id3 = create(9);
id3 = id1;
}
TEST_CASE("Test move identifier", "[identifier][move]") {
identifier id1{6};
REQUIRE(id1.is_integer());
REQUIRE(!id1.is_null());
const auto id2 = std::move(id1);
REQUIRE(id1.is_null());
REQUIRE(id2.is_integer());
}
TEST_CASE("Test share identifier", "[identifier][share]") {
const identifier id1{6};
REQUIRE(id1.use_count() == 1);
auto id2 = id1.share();
REQUIRE(id1 == id2);
REQUIRE(id1.use_count() == 2);
}
+176
View File
@@ -0,0 +1,176 @@
#include <catch2/catch_test_macros.hpp>
#include <utility>
#include "matador/utils/result.hpp"
namespace matador::test {
enum class math_error : int32_t {
OK = 0,
DIVISION_BY_ZERO = 1,
FAILURE = 2
};
utils::result<float, math_error>divide(const float x, const float y) {
if (y == 0) {
return utils::failure(math_error::DIVISION_BY_ZERO);
}
return utils::ok(x / y);
}
utils::result<float, math_error>multiply(const float x, const float y) {
return utils::ok(x * y);
}
utils::result<float, math_error>plus(const float x, const float y) {
return utils::ok(x + y);
}
utils::result<void, math_error>action_on_greater_42(const float i) {
if (i > 42) {
return utils::ok<void>();
}
return utils::failure(math_error::FAILURE);
}
}
using namespace matador;
TEST_CASE("Test result", "[result]") {
auto res = test::divide(4, 2);
REQUIRE(res);
REQUIRE(res.is_ok());
REQUIRE(!res.is_error());
REQUIRE((res.value() == 2.0));
REQUIRE_THROWS(res.err());
res = test::divide(4, 0);
REQUIRE(!res);
REQUIRE(!res.is_ok());
REQUIRE(res.is_error());
REQUIRE((res.err() == test::math_error::DIVISION_BY_ZERO));
res = test::divide(4, 2)
.and_then([](const auto &val) { return test::multiply(val, 5); })
.and_then([](const auto &val) { return test::plus(val, 10); });
REQUIRE(res);
REQUIRE(res.is_ok());
REQUIRE(!res.is_error());
REQUIRE((res.value() == 20.0));
res = test::divide(4, 0)
.and_then([](const auto &val) {
return test::multiply(val, 5);
});
REQUIRE(!res);
REQUIRE(!res.is_ok());
REQUIRE(res.is_error());
REQUIRE((res.err() == test::math_error::DIVISION_BY_ZERO));
auto res2 = test::divide(4, 0)
.or_else([](const auto &err) {
switch (err) {
case test::math_error::DIVISION_BY_ZERO:
return utils::failure(std::string("division by zero error"));
default:
return utils::failure(std::string("unknown error"));
}
});
REQUIRE(!res2);
REQUIRE(!res2.is_ok());
REQUIRE(res2.is_error());
const auto e = res2.err();
// REQUIRE(res2.err() == "division by zero error");
res = test::divide(4, 2)
.and_then([](const auto &val) { return test::multiply(val, 5); })
.map([](const auto &val) { return val + 10; });
REQUIRE(res);
REQUIRE(res.is_ok());
REQUIRE(!res.is_error());
REQUIRE((res.value() == 20.0));
auto res_void = test::action_on_greater_42(43);
REQUIRE(res_void);
REQUIRE(res_void.is_ok());
REQUIRE(!res_void.is_error());
res_void = test::action_on_greater_42(41);
REQUIRE(!res_void);
REQUIRE(!res_void.is_ok());
REQUIRE(res_void.is_error());
auto res_float = test::divide(4, 2)
.and_then([](const auto &val) { return test::action_on_greater_42(val); });
REQUIRE(!res_float);
REQUIRE(!res_float.is_ok());
REQUIRE(res_float.is_error());
res_void = test::divide(120, 2)
.and_then([](const auto &val) { return test::action_on_greater_42(val); });
REQUIRE(res_void);
REQUIRE(res_void.is_ok());
REQUIRE(!res_void.is_error());
}
namespace matador::test {
class CustomError {
public:
CustomError( const math_error err, std::string msg)
: error_(err), message_(std::move(msg)) {}
[[nodiscard]] math_error error() const { return error_; }
[[nodiscard]] std::string message() const { return message_; }
private:
math_error error_{};
std::string message_;
};
utils::result<float, CustomError> custom_divide(const float x, const float y) {
utils::result<float, math_error> res = divide(x, y);
if (res.is_ok()) {
return utils::ok(res.value());
}
return utils::failure<CustomError>({res.err(), "ERROR"});
}
}
TEST_CASE("Test result with custom error", "[result][custom]") {
auto res = test::custom_divide(4, 2);
REQUIRE(res.is_ok());
REQUIRE(!res.is_error());
res = test::custom_divide(4, 0);
REQUIRE(!res.is_ok());
REQUIRE(res.is_error());
const auto err = res.err();
REQUIRE(err.error() == test::math_error::DIVISION_BY_ZERO);
REQUIRE(err.message() == "ERROR");
}
TEST_CASE("Test result with void type", "[result][void]") {
auto res = test::action_on_greater_42(43);
REQUIRE(res.is_ok());
res = test::action_on_greater_42(41);
REQUIRE(res.is_error());
REQUIRE(res.err() == test::math_error::FAILURE);
}
+28
View File
@@ -0,0 +1,28 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/utils/string.hpp"
using namespace matador::utils;
TEST_CASE("Test splitting of string", "[string][split]") {
const std::string str("1,2,3,5,6");
std::vector<std::string> string_vec;
const size_t count = split(str, ',', string_vec);
REQUIRE(count == 5);
REQUIRE(string_vec.size() == 5);
}
TEST_CASE("Test trimming of string", "[string][trim]") {
std::string str(" middle ");
std::string result = trim(str);
REQUIRE(result == "middle");
result = trim(str, "-");
REQUIRE(result == str);
}
+62
View File
@@ -0,0 +1,62 @@
#include <catch2/catch_test_macros.hpp>
#include "matador/utils/version.hpp"
using namespace matador::utils;
TEST_CASE("Test version interface", "[version][interface]") {
version v0;
REQUIRE(v0.major() == 0);
REQUIRE(v0.minor() == 0);
REQUIRE(v0.patch() == 0);
REQUIRE(v0.str() == "0.0.0");
v0.major(1);
v0.minor(2);
v0.patch(3);
REQUIRE(v0.major() == 1);
REQUIRE(v0.minor() == 2);
REQUIRE(v0.patch() == 3);
REQUIRE(v0.str() == "1.2.3");
version v02;
REQUIRE(v02 < v0);
REQUIRE(v02 <= v0);
REQUIRE(v0 > v02);
REQUIRE(v0 >= v02);
REQUIRE(v02 != v0);
version v1 = v0;
REQUIRE(v1 == v0);
version v2(v1);
REQUIRE(v2 == v1);
}
TEST_CASE("Test version parsing", "[version][parse]") {
const auto version_str{"13.67.34"};
const auto v1 = version::from_string(version_str);
REQUIRE(v1.is_ok());
REQUIRE(v1->major() == 13);
REQUIRE(v1->minor() == 67);
REQUIRE(v1->patch() == 34);
REQUIRE(v1->str() == version_str);
const auto v2 = version::from_string("01.02.03");
REQUIRE(v2.is_ok());
REQUIRE(v2->major() == 1);
REQUIRE(v2->minor() == 2);
REQUIRE(v2->patch() == 3);
REQUIRE(v2->str() == "1.2.3");
const auto v3 = version::from_string("a.b.c");
REQUIRE(v3.is_error());
REQUIRE(v3.err().message() == "Invalid version string");
}