Added tests

This commit is contained in:
Dr. Patrick Urbanke
2025-03-29 06:09:34 +01:00
parent 527ac5cf17
commit 54e0d5e332
13 changed files with 150 additions and 36 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "vcpkg"]
path = vcpkg
url = git@github.com:microsoft/vcpkg.git

View File

@@ -5,9 +5,13 @@ option(SQLGEN_BUILD_SHARED "Build shared library" ${BUILD_SHARED_LIBS})
option(SQLGEN_SQLITE3 "Enable sqlite3 support" ON) # enabled by default
#option(SQLGEN_BUILD_BENCHMARKS "Build benchmarks" OFF)
#option(SQLGEN_BUILD_TESTS "Build tests" OFF)
option(SQLGEN_BUILD_TESTS "Build tests" OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 20)
endif()
set(SQLGEN_USE_VCPKG_DEFAULT ON)

View File

@@ -23,7 +23,7 @@ struct Connection {
/// Executes a statement. Note that in order for the statement to take effect,
/// you must call .commit() afterwards.
/// TODO: Abstract away the different statements using rfl::TaggedUnion.
virtual Result<Nothing> execute(const CreateTable& _stmt) = 0;
virtual Result<Nothing> execute(const dynamic::CreateTable& _stmt) = 0;
/// Reads the results of a SelectFrom statement.
virtual Result<Ref<Iterator>> read(const dynamic::SelectFrom& _query) = 0;

View File

@@ -26,38 +26,42 @@ struct PrimaryKey {
template <class U>
PrimaryKey(PrimaryKey<U>&& _other) : value_(_other.get()) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
template <class U,
typename std::enable_if<std::is_convertible_v<U, ReflectionType>,
bool>::type = true>
PrimaryKey(const U& _value) : value_(_value) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
template <class U,
typename std::enable_if<std::is_convertible_v<U, ReflectionType>,
bool>::type = true>
PrimaryKey(U&& _value) noexcept : value_(std::forward<U>(_value)) {}
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
template <class U,
typename std::enable_if<std::is_convertible_v<U, ReflectionType>,
bool>::type = true>
PrimaryKey(const PrimaryKey<U>& _other) : value_(_other.value()) {}
~PrimaryKey() = default;
/// Returns the underlying object.
const Type& get() const { return value_; }
const ReflectionType& get() const { return value_; }
/// Returns the underlying object.
Type& operator()() { return value_; }
ReflectionType& operator()() { return value_; }
/// Returns the underlying object.
const Type& operator()() const { return value_; }
const ReflectionType& operator()() const { return value_; }
/// Assigns the underlying object.
auto& operator=(const Type& _value) {
auto& operator=(const ReflectionType& _value) {
value_ = _value;
return *this;
}
/// Assigns the underlying object.
template <class U, typename std::enable_if<std::is_convertible_v<U, Type>,
bool>::type = true>
template <class U,
typename std::enable_if<std::is_convertible_v<U, ReflectionType>,
bool>::type = true>
auto& operator=(const U& _value) {
value_ = _value;
return *this;

View File

@@ -44,6 +44,22 @@ struct Int64 {
Properties properties;
};
struct UInt8 {
Properties properties;
};
struct UInt16 {
Properties properties;
};
struct UInt32 {
Properties properties;
};
struct UInt64 {
Properties properties;
};
struct Text {
Properties properties;
};

View File

@@ -6,11 +6,11 @@
#include "has_tablename.hpp"
namespace rfl::parsing {
namespace sqlgen::parsing {
namespace internal {
std::string remove_namespaces(const std::string& _str) {
inline std::string remove_namespaces(const std::string& _str) {
const auto pos = _str.find_last_of(':');
if (pos == std::string::npos) {
return _str;
@@ -24,13 +24,13 @@ template <class T>
std::string get_tablename() noexcept {
using Type = std::remove_cvref_t<T>;
if constexpr (has_tablename<Type>) {
using LiteralType = typename Type::Tablename;
return remove_namespaces(LiteralType().str());
using LiteralType = typename Type::tablename;
return internal::remove_namespaces(LiteralType().str());
} else {
return remove_namespaces(rfl::type_name_t<Type>().str());
return internal::remove_namespaces(rfl::type_name_t<Type>().str());
}
}
} // namespace rfl::parsing
} // namespace sqlgen::parsing
#endif

View File

@@ -7,7 +7,7 @@ namespace sqlgen::parsing {
template <typename T>
concept has_tablename = requires() {
{ typename T::Tablename() } -> std::same_as<typename T::Tablename>;
{ typename T::tablename() } -> std::same_as<typename T::tablename>;
};
} // namespace sqlgen::parsing

View File

@@ -11,6 +11,7 @@
#include "../dynamic/CreateTable.hpp"
#include "../dynamic/Table.hpp"
#include "../dynamic/Type.hpp"
#include "../dynamic/types.hpp"
#include "get_tablename.hpp"
#include "has_reflection_method.hpp"
#include "is_nullable.hpp"
@@ -46,57 +47,57 @@ dynamic::Type to_type() {
return to_type<typename T::value_type>().visit(set_nullable);
}
} else if constexpr (has_reflection_method_v<T>) {
} else if constexpr (has_reflection_method<T>) {
return to_type<typename Type::ReflectionType>();
} else if constexpr (std::is_same_v<T, bool>) {
return types::Boolean{};
return dynamic::types::Boolean{};
} else if constexpr (std::is_integral_v<T> && std::is_signed_v<T>) {
if constexpr (sizeof(T) == 1) {
return types::Int8{};
return dynamic::types::Int8{};
} else if constexpr (sizeof(T) == 2) {
return types::Int16{};
return dynamic::types::Int16{};
} else if constexpr (sizeof(T) == 4) {
return types::Int32{};
return dynamic::types::Int32{};
} else if constexpr (sizeof(T) == 8) {
return types::Int64{};
return dynamic::types::Int64{};
} else {
static_assert(rfl::always_false_v<T>, "Unsupported signed integer.");
}
} else if constexpr (std::is_integral_v<T> && !std::is_signed_v<T>) {
if constexpr (sizeof(T) == 1) {
return types::UInt8{};
return dynamic::types::UInt8{};
} else if constexpr (sizeof(T) == 2) {
return types::UInt16{};
return dynamic::types::UInt16{};
} else if constexpr (sizeof(T) == 4) {
return types::UInt32{};
return dynamic::types::UInt32{};
} else if constexpr (sizeof(T) == 8) {
return types::UInt64{};
return dynamic::types::UInt64{};
} else {
static_assert(rfl::always_false_v<T>, "Unsupported unsigned integer.");
}
} else if constexpr (std::is_floating_point_v<T>) {
if constexpr (sizeof(T) == 4) {
return types::Float32{};
return dynamic::types::Float32{};
} else if constexpr (sizeof(T) == 8) {
return types::Float64{};
return dynamic::types::Float64{};
} else {
static_assert(rfl::always_false_v<T>,
"Unsupported floating point value.");
}
} else if constexpr (std::is_same_v<T, std::string>) {
return types::Text{};
return dynamic::types::Text{};
}
}
@@ -118,12 +119,12 @@ template <class T>
requires std::is_class_v<std::remove_cvref_t<T>> &&
std::is_aggregate_v<std::remove_cvref_t<T>>
dynamic::CreateTable to_create_table() {
using NamedTupleType = rfl::name_tuple_t<std::remove_cvref_t<T>>;
using NamedTupleType = rfl::named_tuple_t<std::remove_cvref_t<T>>;
using Fields = typename NamedTupleType::Fields;
return dynamic::CreateTable{
.table = dynamic::Table{.name = get_tablename<T>()},
.columns = internal::make_columns<Fields>(
std::make_integer_sequence<int, rfl::tuple_size_v<Fields>>),
std::make_integer_sequence<int, rfl::tuple_size_v<Fields>>()),
.if_not_exists = true};
}

24
tests/CMakeLists.txt Normal file
View File

@@ -0,0 +1,24 @@
project(sqlgen-tests)
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS "*.cpp")
add_executable(
sqlgen-tests
${SOURCES}
)
target_precompile_headers(sqlgen-tests PRIVATE [["sqlgen.hpp"]] <iostream> <string> <functional> <gtest/gtest.h>)
if (MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std:c++20")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Wall -Werror -ggdb")
endif()
target_link_libraries(
sqlgen-tests
PRIVATE
"${SQLGEN_GTEST_LIB}"
)
find_package(GTest)
gtest_discover_tests(sqlgen-tests)

22
tests/test_tablename.cpp Normal file
View File

@@ -0,0 +1,22 @@
#include <gtest/gtest.h>
#include <sqlgen.hpp>
#include <sqlgen/parsing/to_create_table.hpp>
namespace test_tablename {
struct TestTable {
using tablename = sqlgen::Literal<"TEST_TABLE">;
std::string field1;
int32_t field2;
sqlgen::PrimaryKey<uint32_t> id;
std::optional<std::string> nullable;
};
TEST(general, test_tablename) {
const auto create_table_stmt = sqlgen::parsing::to_create_table<TestTable>();
EXPECT_EQ(create_table_stmt.table.name, "TEST_TABLE");
}
} // namespace test_tablename

View File

@@ -0,0 +1,35 @@
#include <gtest/gtest.h>
#include <sqlgen.hpp>
#include <sqlgen/parsing/to_create_table.hpp>
TEST(general, test_to_create_table) {
struct TestTable {
std::string field1;
int32_t field2;
sqlgen::PrimaryKey<uint32_t> id;
std::optional<std::string> nullable;
};
const auto create_table_stmt = sqlgen::parsing::to_create_table<TestTable>();
const auto get_properties = [](const auto& _t) { return _t.properties; };
EXPECT_EQ(create_table_stmt.table.name, "TestTable");
EXPECT_EQ(create_table_stmt.columns.size(), 4);
EXPECT_EQ(create_table_stmt.columns.at(0).name, "field1");
EXPECT_EQ(create_table_stmt.columns.at(1).name, "field2");
EXPECT_EQ(create_table_stmt.columns.at(2).name, "id");
EXPECT_EQ(create_table_stmt.columns.at(3).name, "nullable");
EXPECT_EQ(create_table_stmt.columns.at(0).type.visit(get_properties).primary,
false);
EXPECT_EQ(create_table_stmt.columns.at(0).type.visit(get_properties).nullable,
false);
EXPECT_EQ(create_table_stmt.columns.at(2).type.visit(get_properties).primary,
true);
EXPECT_EQ(create_table_stmt.columns.at(3).type.visit(get_properties).nullable,
true);
}

1
vcpkg Submodule

Submodule vcpkg added at 7354f1c8a0

View File

@@ -1,5 +1,9 @@
{
"dependencies": [
{
"name": "gtest",
"version>=": "1.14.0"
},
{
"name": "reflectcpp",
"version>=": "0.18.0"