diff --git a/.github/workflows/build-internal-sqlite.yml b/.github/workflows/build-internal-sqlite.yml index f21ab71..d959a2d 100644 --- a/.github/workflows/build-internal-sqlite.yml +++ b/.github/workflows/build-internal-sqlite.yml @@ -29,8 +29,8 @@ jobs: steps: - uses: lukka/get-cmake@latest with: - cmakeVersion: "~3.30.0" # <--= optional, use most recent 3.25.x version - ninjaVersion: "^1.11.1" # <--= optional, use most recent 1.x version + cmakeVersion: "~3.31.0" + ninjaVersion: "^1.13.0" - name: Checkout repository uses: actions/checkout@v6 - name: Setup MSVC diff --git a/.github/workflows/build-project.yml b/.github/workflows/build-project.yml index a5ac410..237edf1 100644 --- a/.github/workflows/build-project.yml +++ b/.github/workflows/build-project.yml @@ -50,8 +50,8 @@ jobs: - uses: lukka/get-cmake@latest if: matrix.build.os != 'windows-2025-vs2026' with: - cmakeVersion: "~3.30.0" # <--= optional, use most recent 3.25.x version - ninjaVersion: "^1.11.1" # <--= optional, use most recent 1.x version + cmakeVersion: "~3.31.0" # <--= optional, use most recent 3.25.x version + ninjaVersion: "^1.13.1" # <--= optional, use most recent 1.x version - uses: lukka/get-cmake@latest if: matrix.build.os == 'windows-2025-vs2026' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 441c3b7..a10c76e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -28,8 +28,8 @@ jobs: version: "1.16.1" - uses: lukka/get-cmake@latest with: - cmakeVersion: "~3.30.0" # <--= optional, use most recent 3.25.x version - ninjaVersion: "^1.11.1" # <--= optional, use most recent 1.x version + cmakeVersion: "~3.31.0" + ninjaVersion: "^1.13.1" - name: Checkout uses: actions/checkout@v6 - name: Install lcov diff --git a/CMakeLists.txt b/CMakeLists.txt index f0011fd..5a87f05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.29) +cmake_minimum_required(VERSION 3.31) set(sl3_MAJOR_VERSION 1) diff --git a/CMakePresets.json b/CMakePresets.json index 359c67f..848be85 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,5 +1,5 @@ { - "version": 8, + "version": 10, "include": [ "cmake/preset/ninja.json", "cmake/preset/xcode.json", diff --git a/CMakeUserPresetsExample.json b/CMakeUserPresetsExample.json index dee649a..fcd2e19 100644 --- a/CMakeUserPresetsExample.json +++ b/CMakeUserPresetsExample.json @@ -1,9 +1,8 @@ { - "version": 8, + "version": 10, "include": [ "cmake/preset/ninja.json" - - ] , + ], "configurePresets": [ { "name": "memsan", @@ -43,5 +42,4 @@ "configurePreset": "memsan" } ] -} - +} \ No newline at end of file diff --git a/cmake/add-on/coverage-gcov.cmake b/cmake/add-on/coverage-gcov.cmake index 0d95b64..5047e20 100644 --- a/cmake/add-on/coverage-gcov.cmake +++ b/cmake/add-on/coverage-gcov.cmake @@ -21,20 +21,23 @@ if(NOT GCOV) message(FATAL_ERROR "gcov tool not found for GCC version ${GCC_MAJOR_VERSION}") endif() -set(COVERAGE_BRANCHES "--rc branch_coverage=1") +set(COVERAGE_BRANCHES "--rc branch_coverage=1 --rc no_exception_branch=1") # these warnings are ridiculous, they depend on the lcov genhtml version set(COVERAGE_WARNINGS "--ignore-errors gcov --ignore-errors mismatch --ignore-errors unused") +set(COVERAGE_FILTERS "--filter region,branch_region") + set(GENHTML_WARNINGS "") set(COVERAGE_TOOL "lcov") +separate_arguments(COVERAGE_FILTERS) separate_arguments(COVERAGE_BRANCHES) separate_arguments(COVERAGE_WARNINGS) separate_arguments(GENHTML_WARNINGS) add_custom_target(coverage COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/coverage - COMMAND lcov --directory . --capture --output-file ${CMAKE_BINARY_DIR}/coverage/coverage.info ${COVERAGE_WARNINGS} ${COVERAGE_BRANCHES} - COMMAND lcov --remove ${CMAKE_BINARY_DIR}/coverage/coverage.info '/usr/*' '*/tests/*' '*/vcpkg_installed/*' '${CMAKE_BINARY_DIR}/_deps/*' '${CMAKE_SOURCE_DIR}/external/*' --output-file ${CMAKE_BINARY_DIR}/coverage/coverage.info.cleaned ${COVERAGE_WARNINGS} ${COVERAGE_BRANCHES} + COMMAND lcov --directory . --capture --output-file ${CMAKE_BINARY_DIR}/coverage/coverage.info ${COVERAGE_WARNINGS} ${COVERAGE_BRANCHES} ${COVERAGE_FILTERS} + COMMAND lcov --remove ${CMAKE_BINARY_DIR}/coverage/coverage.info '/usr/*' '*/tests/*' '*/vcpkg_installed/*' '${CMAKE_BINARY_DIR}/_deps/*' '${CMAKE_SOURCE_DIR}/external/*' --output-file ${CMAKE_BINARY_DIR}/coverage/coverage.info.cleaned ${COVERAGE_WARNINGS} ${COVERAGE_BRANCHES} ${COVERAGE_FILTERS} COMMAND genhtml --branch-coverage ${CMAKE_BINARY_DIR}/coverage/coverage.info.cleaned --output-directory ${CMAKE_BINARY_DIR}/coverage ${GENHTML_WARNINGS} ${COVERAGE_BRANCHES} WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) diff --git a/cmake/lib/sl3Config.cmake.in b/cmake/lib/sl3Config.cmake.in index 07c0dc4..f4f2b73 100644 --- a/cmake/lib/sl3Config.cmake.in +++ b/cmake/lib/sl3Config.cmake.in @@ -1,3 +1,4 @@ + @PACKAGE_INIT@ include(CMakeFindDependencyMacro) diff --git a/cmake/preset/base.json b/cmake/preset/base.json index b8e030e..4175e2f 100644 --- a/cmake/preset/base.json +++ b/cmake/preset/base.json @@ -1,5 +1,5 @@ { - "version": 8, + "version": 10, "configurePresets": [ { "name": "base", @@ -13,8 +13,7 @@ "CMAKE_MODULE_PATH": "${sourceDir}/cmake", "CMAKE_PROJECT_INCLUDE": "project-setup" }, - "environment": { - } + "environment": {} } ], "testPresets": [ @@ -31,4 +30,4 @@ } } ] -} +} \ No newline at end of file diff --git a/cmake/preset/docgen.json b/cmake/preset/docgen.json index b57eb45..e81491c 100644 --- a/cmake/preset/docgen.json +++ b/cmake/preset/docgen.json @@ -1,5 +1,5 @@ { - "version": 8, + "version": 10, "include": [ "base.json" ], @@ -10,7 +10,7 @@ "generator": "Ninja", "cacheVariables": { "CMAKE_COMPILE_WARNING_AS_ERROR": "OFF", - "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_BUILD_TYPE": "None", "sl3_USE_COMMON_COMPILER_WARNINGS": "OFF", "sl3_USE_INTERNAL_SQLITE3": "OFF", "PROJECT_ADDONS": "add-on/fetch-dependencies;add-on/coverage-gcov" @@ -21,6 +21,16 @@ { "name": "docgen", "configurePreset": "docgen" + }, + { + "name": "docgen-doc", + "configurePreset": "docgen", + "targets": "doc" + }, + { + "name": "docgen-coverage", + "configurePreset": "docgen", + "targets": "coverage" } ], "testPresets": [ @@ -45,6 +55,14 @@ { "type": "test", "name": "docgen" + }, + { + "type": "build", + "name": "docgen-doc" + }, + { + "type": "build", + "name": "docgen-coverage" } ] } diff --git a/cmake/preset/msvc22.json b/cmake/preset/msvc22.json index 9e819df..0bc41b1 100644 --- a/cmake/preset/msvc22.json +++ b/cmake/preset/msvc22.json @@ -1,6 +1,8 @@ { - "version": 8, - "include": ["base.json"], + "version": 10, + "include": [ + "base.json" + ], "configurePresets": [ { "name": "msvc22", @@ -56,4 +58,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/cmake/preset/msvc26.json b/cmake/preset/msvc26.json index ab3260c..44bb688 100644 --- a/cmake/preset/msvc26.json +++ b/cmake/preset/msvc26.json @@ -1,5 +1,5 @@ { - "version": 8, + "version": 10, "include": [ "base.json" ], diff --git a/cmake/preset/ninja.json b/cmake/preset/ninja.json index 481319e..60e9b05 100644 --- a/cmake/preset/ninja.json +++ b/cmake/preset/ninja.json @@ -1,6 +1,8 @@ { - "version": 8, - "include": ["base.json"], + "version": 10, + "include": [ + "base.json" + ], "configurePresets": [ { "name": "ninja", @@ -50,4 +52,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/cmake/preset/xcode.json b/cmake/preset/xcode.json index cb112a1..fc06a8c 100644 --- a/cmake/preset/xcode.json +++ b/cmake/preset/xcode.json @@ -1,6 +1,8 @@ { - "version": 8, - "include": ["base.json"], + "version": 10, + "include": [ + "base.json" + ], "configurePresets": [ { "name": "xcode", @@ -51,4 +53,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/include/sl3/columns.hpp b/include/sl3/columns.hpp index 08fbd3f..2f0ea77 100644 --- a/include/sl3/columns.hpp +++ b/include/sl3/columns.hpp @@ -21,7 +21,7 @@ namespace sl3 // index access always checked, since docs say // 'if the column index is out of range, the result is undefined' // but if you feel like pre-optimization is required, - // feel free to access the underlying sqlite3_stmt via + // feel free to access the underlying sqlite3_stmt // and adopt the index access! /** @@ -40,10 +40,10 @@ namespace sl3 Columns (sqlite3_stmt* stmt); - // not be needed, even if they would not harm .. + // should not be needed, even if they would not harm Columns& operator= (const Columns&) = delete; - // not be needed, even if they would not harm .. + // should not be needed, even if they would not harm Columns& operator= (Columns&&) = delete; Columns (const Columns&) = default; @@ -140,7 +140,7 @@ namespace sl3 * If used, should be called before accessing the value of the column * at the given index, otherwise the typed access might set the type. * - * This method can be used to check if a column isNull. + * This method can be used to check if a column is null. * * \param idx wanted index * \throw sl3::ErrOutOfRange if idx is invalid @@ -221,7 +221,7 @@ namespace sl3 /** * \brief Get the underlying sqlite3_stmt * - * User defined QueryCallbacks might have their own way to do things, + * User-defined query callbacks might have their own way to do things, * so this getter provides access to the underlying sqlite3_stmt. * * \return underlying sqlite3_stmt diff --git a/include/sl3/command.hpp b/include/sl3/command.hpp index d9fbe15..4211c9e 100644 --- a/include/sl3/command.hpp +++ b/include/sl3/command.hpp @@ -239,6 +239,9 @@ namespace sl3 DbValues _parameters; }; + // Branch coverage for that is a nightmare, + // cant come over 60% with all the boilerplate in commandsexttest.cpp + // LCOV_EXCL_BR_START /** * \brief Syntax sugar to create command parameters * @@ -259,6 +262,7 @@ namespace sl3 { return {DbValue{vals}...}; } + // LCOV_EXCL_BR_STOP } #endif diff --git a/include/sl3/database.hpp b/include/sl3/database.hpp index 9d48cd1..a356065 100644 --- a/include/sl3/database.hpp +++ b/include/sl3/database.hpp @@ -27,7 +27,7 @@ namespace sl3 } /** - * \brief represents a SQLite3 Database + * \brief Represents a SQLite3 database * * * Encapsulates some of the most useful methods for a SQLite3 database @@ -83,7 +83,7 @@ namespace sl3 Database (Database&&) noexcept; /** - * \brief create a SqlCommand instance. + * \brief Create an SQL command instance. * * Parameters that the statement might contain will be automatically * deduced and created as DbVariant values @@ -94,7 +94,7 @@ namespace sl3 Command prepare (const std::string& sql); /** - * \brief create a Command. + * \brief Create a Command. * * Given parameters will be used to set up the command parameters, * @@ -236,11 +236,12 @@ namespace sl3 std::string getMostRecentErrMsg (); /** - * \brief Returns number of row that have changed since database opening. + * \brief Returns the number of rows that have changed since the database + * was opened. * * Returns the number of rows that have been changed - * Through successful SQL INSERT/UPDATE or DELETE statements since database - * was opened. + * through successful SQL INSERT/UPDATE or DELETE statements since the + * database was opened. * * \return Count of changed rows */ @@ -257,7 +258,7 @@ namespace sl3 std::size_t getRecentlyChanged (); /** - * \brief returns rowid of the most recent successful INSERT statement + * \brief Returns the rowid of the most recent successful INSERT statement * * Returns the rowid (ROWID, OID, or _ROWID_ or the column of * type INTEGER PRIMARY KEY ) diff --git a/include/sl3/dataset.hpp b/include/sl3/dataset.hpp index 5271251..b3cf5fa 100644 --- a/include/sl3/dataset.hpp +++ b/include/sl3/dataset.hpp @@ -20,9 +20,9 @@ namespace sl3 { /** - * \brief A utility for processing the result queries. + * \brief A utility for processing query results. * - * This class is a RowCallback that loads the result of a Query into a list + * This class is a RowCallback that loads the result of a query into a list * of DbValues. * The loaded list can be browsed and the loaded values can be accessed. * @@ -38,7 +38,7 @@ namespace sl3 * be thrown. * * - Without any specification \n - * In this case all fields will be DsVariantField , + * In this case all fields will be DsVariantField, * using the storage type sqlite reports for the actual value. * * @@ -52,7 +52,7 @@ namespace sl3 /** * \brief Constructor * - * All fields will be DsVariantField , using the storage type sqlite + * All fields will be DsVariantField, using the storage type sqlite * reports for the actual value. * Field count will be detected and applied. */ @@ -91,7 +91,7 @@ namespace sl3 Dataset& operator= (const Dataset&) = default; /** - * \brief Rvalues assignment + * \brief Rvalue assignment * \return reference to this */ Dataset& operator= (Dataset&&) = default; @@ -107,10 +107,9 @@ namespace sl3 * \brief Clear all states. * * Removes loaded data and sets a new specification for the field - * description - * so that the actual instance can be reused to populate with a different - - * * select statement / sql command. Passing an empty DbValuesTypeList + * description so that the actual instance can be reused to populate with a + * different select statement / SQL command. Passing an empty + * DbValuesTypeList * means that all fields will be * DsVariantField and field count will * be detected. @@ -124,8 +123,7 @@ namespace sl3 * * Appends the data of the given Dataset to the end of the actual data. * The field names and types of the given Dataset must match the current - - * * one. + * one. * * \throw sl3::ErrTypeMisMatch if field names types are not equal or * size differs. @@ -147,7 +145,7 @@ namespace sl3 void merge (const DbValues& row); /** - * \brief Get the index of a field by namespace + * \brief Get the index of a field by name * * \throw sl3::OutOfRange if name is not found * \param name field @@ -176,7 +174,7 @@ namespace sl3 * Sort according to the given field indexes. * The Dataset will be sorted according to sqlite rules. * - * \throw sl2::OutOfRange if a given index is invalid + * \throw sl3::OutOfRange if a given index is invalid * \param idxs list of field indexes * \param cmp pointer to a less than compare function, default dbval_lt */ diff --git a/include/sl3/dbvalue.hpp b/include/sl3/dbvalue.hpp index 7335d23..5e08c14 100644 --- a/include/sl3/dbvalue.hpp +++ b/include/sl3/dbvalue.hpp @@ -45,7 +45,7 @@ namespace sl3 /** * \brief Constructor * - * Constructs a type and the value is null. + * Constructs a type and the value is null. * * \param type wanted storage type * If Type::Null is given, the type will be a variant. @@ -55,7 +55,7 @@ namespace sl3 // TODO constructor Value, Value - Type, // would this maybe make the other value c'tors obsolete? // this would be fine - // don't like the ctors below anyway, they should take a flag, + // don't like the constructors below anyway; they should take a flag, // variant or not, but not the type /** \brief Constructor @@ -109,8 +109,8 @@ namespace sl3 /** \brief Assignment * \throw sl3::ErrTypeMisMatch if getType is incompatible * \note , only value assignment happens here, - * the type does not change, the storage type might change in case of type - * is a variant. + * the type does not change; the storage type might change if the type is + * a variant. * * \param val new value * \return reference to this @@ -164,8 +164,8 @@ namespace sl3 /** \brief Assignment * \throw sl3::ErrTypeMisMatch if getType is incompatible * \note , only value assignment happens here, - * the type does not change, the storage type might change in case of type - * is a variant. + * the type does not change; the storage type might change if the type is + * a variant. * \param val new value */ void set (int val); diff --git a/include/sl3/error.hpp b/include/sl3/error.hpp index 0e04014..cd339d2 100644 --- a/include/sl3/error.hpp +++ b/include/sl3/error.hpp @@ -21,7 +21,7 @@ namespace sl3 /** * \brief Error codes * - * These enum values used to create ErrType objects. + * These enum values are used to create ErrType objects. * * Each ErrCode becomes an ErrType object. * @@ -37,7 +37,7 @@ namespace sl3 }; /** - * \brief get textual representation (the name) of an ErrCode + * \brief Get the textual representation (the name) of an ErrCode * \param ec * wanted ErrCode name * \return the ErrCode name @@ -76,7 +76,7 @@ namespace sl3 }; /** - * \brief overloaded stream operator + * \brief Overloaded stream operator * \param os ostream * \param e the Error * \return the ostream @@ -86,7 +86,7 @@ namespace sl3 /** * \brief Object class representing an ErrCode * - * Allows typedef objects using an ErrCode. + * Allows typedefs using an ErrCode. * Each sl3::ErrCode becomes * an ErrType object. */ @@ -126,7 +126,7 @@ namespace sl3 using ErrUnexpected = ErrType; /** - * \brief packaging an error from sqlite3. + * \brief Package an error from sqlite3. * This exception will be thrown if sqlite3 reports an error. * * Holds the sqlite3 error code and the error message if it is available. diff --git a/include/sl3/rowcallback.hpp b/include/sl3/rowcallback.hpp index 4994385..ca2a598 100644 --- a/include/sl3/rowcallback.hpp +++ b/include/sl3/rowcallback.hpp @@ -30,16 +30,17 @@ namespace sl3 { friend class Command; - protected: + public: /** - * \brief Constructor + * \brief Destructor */ - RowCallback () noexcept = default; + virtual ~RowCallback () noexcept; + protected: /** * \brief Constructor */ - virtual ~RowCallback () noexcept = default; + RowCallback () noexcept; /** * \brief Process one row of the result from a SELECT statement diff --git a/include/sl3/value.hpp b/include/sl3/value.hpp index 86682f5..fa42395 100644 --- a/include/sl3/value.hpp +++ b/include/sl3/value.hpp @@ -40,7 +40,7 @@ namespace sl3 /** \brief Constructor * - * This constructor with an initialization value + * This constructor takes an initialization value. * \param val * initial value */ @@ -89,8 +89,8 @@ namespace sl3 /** \brief Assignment * \throw sl3::ErrTypeMisMatch if getType is incompatible * \note , only value assignment happens here, - * the type does not change, the storage type might change in case of type - * is a variant. + * the type does not change; the storage type might change if the type is + * a variant. * * \param val new value * \return reference to this @@ -147,8 +147,7 @@ namespace sl3 * \throw sl3::ErrNullValueAccess if value is null. * \throw sl3::ErrTypeMisMatch if getType is incompatible * \throw sl3::ErrOutOfRange if the stored value is an int64_t and out of - - * * the min or max double range. + * the min or max double range. * \return the value */ explicit operator double () const; diff --git a/src/sl3/columns.cpp b/src/sl3/columns.cpp index b717fba..4fc1c27 100644 --- a/src/sl3/columns.cpp +++ b/src/sl3/columns.cpp @@ -152,7 +152,7 @@ namespace sl3 type = Type::Null; break; - default: + default: // LCOV_EXCL_LINE throw ErrUnexpected ("never reach"); // LCOV_EXCL_LINE break; // LCOV_EXCL_LINE } diff --git a/src/sl3/connection.hpp b/src/sl3/connection.hpp index b96f804..2fc3666 100644 --- a/src/sl3/connection.hpp +++ b/src/sl3/connection.hpp @@ -102,7 +102,7 @@ namespace sl3 } // if busy, use v2 for garbed collecting, - if (sqlite3_close (sl3db) != SQLITE_OK) + if (sqlite3_close (sl3db) != SQLITE_OK) // LCOV_EXCL_BR_LINE { // but that should 'never' happen :-) sqlite3_close_v2 (sl3db); // LCOV_EXCL_LINE } diff --git a/src/sl3/dbvalue.cpp b/src/sl3/dbvalue.cpp index 07d4733..8c93179 100644 --- a/src/sl3/dbvalue.cpp +++ b/src/sl3/dbvalue.cpp @@ -108,6 +108,18 @@ namespace sl3 Type type; }; + std::string + assignmentTypeName (const DbValue& value) + { + if (value.dbtype () == Type::Variant) + { + return typeName (value.dbtype ()) + " with storage type" + + typeName (value.type ()); + } + + return typeName (value.dbtype ()); + } + } //-------------------------------------------------------------------------- bool @@ -137,7 +149,7 @@ namespace sl3 return false; } - // how, and does this makesense ? + // how, and does this make sense? bool dbval_eq (const DbValue& a, const DbValue& b) noexcept @@ -197,11 +209,7 @@ namespace sl3 if (!canAssign (other)) { throw ErrTypeMisMatch (typeName (_type) + "=" - + (other._type == Type::Variant - ? typeName (other._type) - + " with storage type" - + typeName (other.type ()) - : typeName (other._type))); + + assignmentTypeName (other)); } assign (other); @@ -214,11 +222,7 @@ namespace sl3 if (!canAssign (other)) { throw ErrTypeMisMatch (typeName (_type) + "=" - + (other._type == Type::Variant - ? typeName (other._type) - + " with storage type" - + typeName (other.type ()) - : typeName (other._type))); + + assignmentTypeName (other)); } _value = std::move (other._value); diff --git a/src/sl3/rowcallback.cpp b/src/sl3/rowcallback.cpp index c69924b..18baa0c 100644 --- a/src/sl3/rowcallback.cpp +++ b/src/sl3/rowcallback.cpp @@ -10,6 +10,10 @@ namespace sl3 { + RowCallback::RowCallback () noexcept = default; + + RowCallback::~RowCallback () noexcept = default; + void RowCallback::onStart () { diff --git a/src/sl3/utils.hpp b/src/sl3/utils.hpp index 7fe6c7e..583b2b5 100644 --- a/src/sl3/utils.hpp +++ b/src/sl3/utils.hpp @@ -10,38 +10,24 @@ namespace sl3 inline size_t as_size_t (int val) { - assert (val >= 0); + assert (val >= 0); // LCOV_EXCL_BR_LINE return static_cast (val); } inline size_t as_size_t (ptrdiff_t val) { - assert (val >= 0); + assert (val >= 0); // LCOV_EXCL_BR_LINE return static_cast (val); } inline int as_int (size_t val) { - assert (val < std::numeric_limits::max ()); + assert (val < std::numeric_limits::max ()); // LCOV_EXCL_BR_LINE return static_cast (val); } - // gcc -Werror=conversion helpers - // I keep that as a comment, since it's so funny, but not use it - // template struct common_typeype - // { - // typedef typename std::conditional < std::numeric_limits::is_iec559 - // &&std::numeric_limits::is_iec559, - // typename std::conditional< - // std::numeric_limits< - // T1>::digits::digits, T2, T1>::type, - // typename std::conditional::is_iec559, - // T1, - // T2>::type>::type type; - // }; - template bool is_less (const T1& a, const T2& b) diff --git a/src/sl3/value.cpp b/src/sl3/value.cpp index 0fa92e1..30e6987 100644 --- a/src/sl3/value.cpp +++ b/src/sl3/value.cpp @@ -105,13 +105,13 @@ namespace sl3 Value::Value (std::string val) noexcept : _type{Type::Text} { - new (&_store.textval) std::string (std::move (val)); + new (&_store.textval) std::string (std::move (val)); // LCOV_EXCL_BR_LINE } Value::Value (const char* val) : _type{Type::Text} { - new (&_store.textval) std::string (val); + new (&_store.textval) std::string (val); // LCOV_EXCL_BR_LINE } Value::Value (double val) noexcept @@ -123,7 +123,7 @@ namespace sl3 Value::Value (Blob val) noexcept : _type{Type::Blob} { - new (&_store.blobval) Blob (std::move (val)); + new (&_store.blobval) Blob (std::move (val)); // LCOV_EXCL_BR_LINE } Value::~Value () noexcept @@ -155,11 +155,11 @@ namespace sl3 break; case Type::Text: - new (&_store.textval) std::string (other._store.textval); + new (&_store.textval) std::string (other._store.textval); // LCOV_EXCL_BR_LINE break; case Type::Blob: - new (&_store.blobval) Blob (other._store.blobval); + new (&_store.blobval) Blob (other._store.blobval); // LCOV_EXCL_BR_LINE break; default: @@ -187,12 +187,12 @@ namespace sl3 break; case Type::Text: - new (&_store.textval) std::string (std::move (other._store.textval)); + new (&_store.textval) std::string (std::move (other._store.textval)); // LCOV_EXCL_BR_LINE other._store.textval.~basic_string (); break; case Type::Blob: - new (&_store.blobval) Blob (std::move (other._store.blobval)); + new (&_store.blobval) Blob (std::move (other._store.blobval)); // LCOV_EXCL_BR_LINE other._store.blobval.~vector (); break; @@ -244,14 +244,14 @@ namespace sl3 break; case Type::Text: - new (&_store.textval) std::string (other._store.textval); + new (&_store.textval) std::string (other._store.textval); // LCOV_EXCL_BR_LINE break; case Type::Blob: - new (&_store.blobval) Blob (other._store.blobval); + new (&_store.blobval) Blob (other._store.blobval); // LCOV_EXCL_BR_LINE break; - case Type::Variant: + case Type::Variant: // LCOV_EXCL_LINE raiseErrUnexpected ("never reach"); // LCOV_EXCL_LINE break; // LCOV_EXCL_LINE } @@ -301,12 +301,12 @@ namespace sl3 break; case Type::Text: - new (&_store.textval) std::string (std::move (other._store.textval)); + new (&_store.textval) std::string (std::move (other._store.textval)); // LCOV_EXCL_BR_LINE other._store.textval.~basic_string (); break; case Type::Blob: - new (&_store.blobval) Blob (std::move (other._store.blobval)); + new (&_store.blobval) Blob (std::move (other._store.blobval)); // LCOV_EXCL_BR_LINE other._store.blobval.~vector (); break; @@ -382,7 +382,7 @@ namespace sl3 _store.blobval.~vector (); } - new (&_store.textval) std::string (val); + new (&_store.textval) std::string (val); // LCOV_EXCL_BR_LINE _type = Type::Text; return *this; } @@ -399,12 +399,13 @@ namespace sl3 _store.blobval = val; return *this; } - new (&_store.blobval) Blob (val); + new (&_store.blobval) Blob (val); // LCOV_EXCL_BR_LINE _type = Type::Blob; return *this; } - Value::operator int () const + Value:: + operator int () const { if (isNull ()) throw ErrNullValueAccess (); @@ -422,7 +423,8 @@ namespace sl3 return static_cast (_store.intval); } - Value::operator int64_t () const + Value:: + operator int64_t () const { if (isNull ()) throw ErrNullValueAccess (); @@ -435,7 +437,8 @@ namespace sl3 return _store.intval; } - Value::operator double () const + Value:: + operator double () const { if (isNull ()) { @@ -459,7 +462,8 @@ namespace sl3 return _store.realval; } - Value::operator const std::string& () const + Value:: + operator const std::string&() const { if (isNull ()) throw ErrNullValueAccess (); @@ -470,7 +474,8 @@ namespace sl3 return _store.textval; } - Value::operator const Blob& () const + Value:: + operator const Blob&() const { if (isNull ()) throw ErrNullValueAccess (); @@ -721,7 +726,7 @@ namespace sl3 if (b.getType () != Type::Blob) return false; - // we are both bolb + // we are both blobs return a._store.blobval < b._store.blobval; } @@ -777,7 +782,7 @@ namespace sl3 retval = a._store.blobval == b._store.blobval; break; - default: + default: // LCOV_EXCL_LINE break; // LCOV_EXCL_LINE } @@ -832,7 +837,7 @@ namespace sl3 if (b.getType () != Type::Blob) return false; - // we are both bolb + // we are both blobs return a._store.blobval < b._store.blobval; } diff --git a/tests/commands/CMakeLists.txt b/tests/commands/CMakeLists.txt index a853e0a..248018f 100644 --- a/tests/commands/CMakeLists.txt +++ b/tests/commands/CMakeLists.txt @@ -2,4 +2,5 @@ add_doctest(commands SOURCES commandstest.cpp + commandsextest.cpp ) diff --git a/tests/commands/commandsextest.cpp b/tests/commands/commandsextest.cpp new file mode 100644 index 0000000..06b6521 --- /dev/null +++ b/tests/commands/commandsextest.cpp @@ -0,0 +1,170 @@ +#include "../testing.hpp" +#include + +#include +#include +#include + +SCENARIO ("raising branch coverage on sl3::parameters") +{ + using namespace sl3; + + auto byte = [] (int v) { return std::byte{static_cast (v)}; }; + + GIVEN ("parameter packs with different argument categories") + { + Blob blob{byte (1), byte (2), byte (3)}; + const char* ctext = "hello"; + int64_t big = 1LL << 40; + + WHEN ("creating empty and single-value packs") + { + auto empty = parameters (); + auto onlyInt = parameters (1); + auto onlyInt64 = parameters (big); + auto onlyFloat = parameters (1.25f); + auto onlyDouble = parameters (2.5); + auto onlyText = parameters (std::string{"text"}); + auto onlyCText = parameters (ctext); + auto onlyBlob = parameters (blob); + auto onlyNull = parameters (Type::Null); + auto onlyVar = parameters (Type::Variant); + + THEN ("the resulting values reflect the chosen constructor paths") + { + REQUIRE_EQ (empty.size (), std::size_t{0}); + + REQUIRE_EQ (onlyInt.size (), std::size_t{1}); + CHECK_EQ (onlyInt[0].dbtype (), Type::Int); + CHECK_EQ (onlyInt[0].getInt (), 1); + + REQUIRE_EQ (onlyInt64.size (), std::size_t{1}); + CHECK_EQ (onlyInt64[0].dbtype (), Type::Int); + CHECK_EQ (onlyInt64[0].getInt (), big); + + REQUIRE_EQ (onlyFloat.size (), std::size_t{1}); + CHECK_EQ (onlyFloat[0].dbtype (), Type::Real); + CHECK_EQ (onlyFloat[0].getReal (), doctest::Approx{1.25}); + + REQUIRE_EQ (onlyDouble.size (), std::size_t{1}); + CHECK_EQ (onlyDouble[0].dbtype (), Type::Real); + CHECK_EQ (onlyDouble[0].getReal (), doctest::Approx{2.5}); + + REQUIRE_EQ (onlyText.size (), std::size_t{1}); + CHECK_EQ (onlyText[0].dbtype (), Type::Text); + CHECK_EQ (onlyText[0].getText (), "text"); + + REQUIRE_EQ (onlyCText.size (), std::size_t{1}); + CHECK_EQ (onlyCText[0].dbtype (), Type::Text); + CHECK_EQ (onlyCText[0].getText (), "hello"); + + REQUIRE_EQ (onlyBlob.size (), std::size_t{1}); + CHECK_EQ (onlyBlob[0].dbtype (), Type::Blob); + CHECK_EQ (onlyBlob[0].getBlob (), blob); + + REQUIRE_EQ (onlyNull.size (), std::size_t{1}); + CHECK_EQ (onlyNull[0].dbtype (), Type::Variant); + CHECK (onlyNull[0].isNull ()); + + REQUIRE_EQ (onlyVar.size (), std::size_t{1}); + CHECK_EQ (onlyVar[0].dbtype (), Type::Variant); + CHECK (onlyVar[0].isNull ()); + } + } + + WHEN ("mixing lvalues, rvalues and repeated categories") + { + auto mixed = parameters (std::string{"alpha"}, + ctext, + blob, + Blob{byte (4), byte (5)}, + 7, + big, + 3.5f, + 9.25, + Type::Null, + Type::Variant); + + THEN ("every slot keeps the expected type and value") + { + REQUIRE_EQ (mixed.size (), std::size_t{10}); + + CHECK_EQ (mixed[0].dbtype (), Type::Text); + CHECK_EQ (mixed[0].getText (), "alpha"); + + CHECK_EQ (mixed[1].dbtype (), Type::Text); + CHECK_EQ (mixed[1].getText (), "hello"); + + CHECK_EQ (mixed[2].dbtype (), Type::Blob); + CHECK_EQ (mixed[2].getBlob (), blob); + + CHECK_EQ (mixed[3].dbtype (), Type::Blob); + CHECK_EQ (mixed[3].getBlob ().size (), std::size_t{2}); + + CHECK_EQ (mixed[4].dbtype (), Type::Int); + CHECK_EQ (mixed[4].getInt (), 7); + + CHECK_EQ (mixed[5].dbtype (), Type::Int); + CHECK_EQ (mixed[5].getInt (), big); + + CHECK_EQ (mixed[6].dbtype (), Type::Real); + CHECK_EQ (mixed[6].getReal (), doctest::Approx{3.5}); + + CHECK_EQ (mixed[7].dbtype (), Type::Real); + CHECK_EQ (mixed[7].getReal (), doctest::Approx{9.25}); + + CHECK_EQ (mixed[8].dbtype (), Type::Variant); + CHECK (mixed[8].isNull ()); + + CHECK_EQ (mixed[9].dbtype (), Type::Variant); + CHECK (mixed[9].isNull ()); + } + } + } +} + +SCENARIO ("exploring throwing conversions passed to sl3::parameters") +{ + using namespace sl3; + + struct ThrowToInt + { + operator int () const { throw std::runtime_error{"int conversion failed"}; } + }; + + struct ThrowToText + { + operator std::string () const + { + throw std::runtime_error{"text conversion failed"}; + } + }; + + struct ThrowToBlob + { + operator Blob () const + { + throw std::runtime_error{"blob conversion failed"}; + } + }; + + GIVEN ("types that can convert to accepted DbValue inputs") + { + WHEN ("the conversion throws during parameter pack expansion") + { + THEN ("the exception escapes to the caller") + { + CHECK_THROWS_AS ((void)parameters (ThrowToInt{}), std::runtime_error); + CHECK_THROWS_AS ((void)parameters (ThrowToText{}), std::runtime_error); + CHECK_THROWS_AS ((void)parameters (ThrowToBlob{}), std::runtime_error); + + CHECK_THROWS_AS ( + (void)parameters (1, ThrowToText{}, std::string{"tail"}), + std::runtime_error); + CHECK_THROWS_AS ( + (void)parameters (std::string{"head"}, ThrowToBlob{}, 2.0), + std::runtime_error); + } + } + } +} diff --git a/tests/commands/commandstest.cpp b/tests/commands/commandstest.cpp index 9b30eee..c220bc8 100644 --- a/tests/commands/commandstest.cpp +++ b/tests/commands/commandstest.cpp @@ -6,7 +6,7 @@ SCENARIO ("using precompiled commands") { using namespace sl3; - GIVEN ("cmake genertated config") + GIVEN ("a database with a table") { Database db{":memory:"}; db.execute ("CREATE TABLE tbl (fld1 , fld2 , fld3 );"); @@ -222,7 +222,7 @@ SCENARIO ("binding values of all types and using select to check the result") REQUIRE_EQ (ds[0][3].getBlob (), params[3].getBlob ()); REQUIRE (ds[0][4].isNull ()); } - THEN ("selecting all data with concrete types will retun typed info") + THEN ("selecting all data with concrete types returns typed info") { auto types = Types{ Type::Int, Type::Real, Type::Text, Type::Blob, Type::Variant}; @@ -239,7 +239,7 @@ SCENARIO ("binding values of all types and using select to check the result") REQUIRE_THROWS_AS ((void)db.select ("SELECT * FROM t;", types), ErrTypeMisMatch); } - THEN ("selecting all data with wrong numver of types will throw") + THEN ("selecting all data with the wrong number of types will throw") { auto types = Types{Type::Int, Type::Real, Type::Text, Type::Blob}; REQUIRE_THROWS_AS ((void)db.select ("SELECT * FROM t;", types), diff --git a/tests/database/CMakeLists.txt b/tests/database/CMakeLists.txt index 2c3f1b4..138c6d7 100644 --- a/tests/database/CMakeLists.txt +++ b/tests/database/CMakeLists.txt @@ -2,6 +2,7 @@ add_doctest(database SOURCES dbtest.cpp + dbextest.cpp ) diff --git a/tests/database/dbextest.cpp b/tests/database/dbextest.cpp new file mode 100644 index 0000000..c6b00a8 --- /dev/null +++ b/tests/database/dbextest.cpp @@ -0,0 +1,30 @@ +#include "../testing.hpp" + +#include +#include + +SCENARIO ("creating an in memory database with custom name") +{ + GIVEN ("an in-memory database with a custom name") + { + sl3::Database db{ + "", SQLITE_OPEN_MEMORY | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE}; + + WHEN ("creating a table and some test data") + { + db.execute ( + "CREATE TABLE tbltest (intFld INTEGER,txtFld TEXT, dblFld real );" + "INSERT INTO tbltest VALUES (1, 'eins', 1.111) ;" + "INSERT INTO tbltest VALUES (2, 'zwei', 2.22) ;" + "INSERT INTO tbltest VALUES (3, NULL, NULL) ;"); + + THEN ("I can verify that the table and some test data exists") + { + CHECK (db.getTotalChanges () == 3); + CHECK_NOTHROW (db.execute (std::string{"DELETE FROM tbltest;"})); + CHECK (db.getTotalChanges () == 6); + CHECK (db.getRecentlyChanged () == 3); + } + } + } +} diff --git a/tests/database/dbtest.cpp b/tests/database/dbtest.cpp index a660f94..0e43dcc 100644 --- a/tests/database/dbtest.cpp +++ b/tests/database/dbtest.cpp @@ -33,7 +33,7 @@ SCENARIO ("creating a database") SCENARIO ("creating some test data") { - GIVEN ("an im memory database") + GIVEN ("an in-memory database") { sl3::Database db{":memory:"}; @@ -54,7 +54,7 @@ SCENARIO ("creating some test data") "INSERT INTO tbltest VALUES (2, 'zwei', 2.22) ;" "INSERT INTO tbltest VALUES (3, NULL, NULL) ;"); - THEN ("I can veryfy that the table and some test data exists") + THEN ("I can verify that the table and some test data exists") { CHECK (db.getTotalChanges () == 3); CHECK_NOTHROW (db.execute (std::string{"DELETE FROM tbltest;"})); @@ -76,7 +76,7 @@ SCENARIO ("move construct a database") CHECK_NOTHROW (db.execute ("SELECT COUNT(*) FROM tbltest;")); - WHEN ("Moveconstruct a new db from an existing one") + WHEN ("move-constructing a new db from an existing one") { sl3::Database db1{std::move (db)}; THEN ("the new constructed db has data") @@ -88,19 +88,14 @@ SCENARIO ("move construct a database") { CHECK_THROWS_AS (db.execute ("SELECT COUNT(*) FROM tbltest;"), sl3::ErrNoConnection); - - // those are protected, does this make sense? - // adding a derivating from a database - // CHECK(db.db() == nullptr) ; - // CHECK(db1.db() != nullptr) ; } } } } -SCENARIO ("check the connections and if derivate works") +SCENARIO ("check the connections and if derivation works") { - GIVEN ("a derivated db that let me access db property") + GIVEN ("a derived db that lets me access the db property") { struct MyDb : public sl3::Database { @@ -115,7 +110,7 @@ SCENARIO ("check the connections and if derivate works") CHECK_NOTHROW (db.execute ("SELECT COUNT(*) FROM tbltest;")); - WHEN ("Moveconstruct a new db from an existing one") + WHEN ("move-constructing a new db from an existing one") { MyDb db1{std::move (db)}; THEN ("the new constructed db has data") @@ -415,7 +410,7 @@ SCENARIO ("selecting values") SCENARIO ("accessing database change properties") { - GIVEN ("an database with an empty table") + GIVEN ("a database with an empty table") { sl3::Database db{":memory:"}; @@ -451,7 +446,7 @@ SCENARIO ("accessing database change properties") sl3::Database db1{std::move (db)}; - THEN ("no undefined behavior but exceptions are thorwn") + THEN ("no undefined behavior but exceptions are thrown") { CHECK_THROWS_AS ((void)db.getTotalChanges (), sl3::ErrNoConnection); CHECK_THROWS_AS ((void)db.getRecentlyChanged (), sl3::ErrNoConnection); @@ -467,7 +462,7 @@ SCENARIO ("accessing database change properties") SCENARIO ("accessing database error properties") { - GIVEN ("an database an empty datbase") + GIVEN ("an empty database") { sl3::Database db{":memory:"}; @@ -490,7 +485,7 @@ SCENARIO ("accessing database error properties") } } - WHEN ("something has been done wrong, and than something that worked") + WHEN ("something has been done wrong, and then something worked") { REQUIRE_THROWS (db.execute ("CREATE SCHNALBE tbltest (f INTEGER);")); REQUIRE_NOTHROW (db.execute ("CREATE TABLE tbltest (f INTEGER);")); @@ -507,7 +502,7 @@ SCENARIO ("accessing database error properties") sl3::Database db1{std::move (db)}; - THEN ("exceptions are thrown, but not fot ") + THEN ("exceptions are thrown, but not for the moved-to db") { CHECK_THROWS_AS ((void)db.getMostRecentErrCode (), sl3::ErrNoConnection); @@ -567,7 +562,7 @@ SCENARIO ("using transactions") trans.commit (); } - THEN ("commit on the moved from object does not effect the database") + THEN ("commit on the moved-from object does not affect the database") { REQUIRE_THROWS (db.execute ("SELECT * FROM tbltest;")); } @@ -584,7 +579,7 @@ SCENARIO ("using transactions") trans1.commit (); } - THEN ("commit did effect the database") + THEN ("commit did affect the database") { REQUIRE_NOTHROW (db.execute ("SELECT * FROM tbltest;")); } diff --git a/tests/dataset/datasettest.cpp b/tests/dataset/datasettest.cpp index 83fcdba..1610ab5 100644 --- a/tests/dataset/datasettest.cpp +++ b/tests/dataset/datasettest.cpp @@ -3,10 +3,10 @@ #include -SCENARIO ("dataset creation and defautl operatores") +SCENARIO ("dataset creation and default operators") { using namespace sl3; - GIVEN ("a database, a table and knowen data") + GIVEN ("a database, a table, and known data") { Database db{":memory:"}; db.execute ("CREATE TABLE t (int INTEGER,txt TEXT, dbl real );" @@ -19,7 +19,7 @@ SCENARIO ("dataset creation and defautl operatores") WHEN ("having an empty dataset") { Dataset ds; - THEN ("assinging new dataset is possible without problems") + THEN ("assigning a new dataset is possible without problems") { CHECK_NOTHROW (ds = db.select ("SELECT * FROM t;")); CHECK_EQ (ds.getIndex ("int"), 0); @@ -36,7 +36,7 @@ SCENARIO ("dataset creation and defautl operatores") { auto ds = db.select ("SELECT * FROM t;", types); - THEN ("the fieds are not variants") + THEN ("the fields are not variants") { for (const auto& row : ds) { @@ -114,7 +114,7 @@ SCENARIO ("merging datasets") AND_WHEN ("reset the fields but not the types") { - THEN ("merging this ds still thorws") + THEN ("merging this ds still throws") { ds.reset (); CHECK_THROWS_AS (ds.merge (dsdifferent), ErrTypeMisMatch); @@ -198,7 +198,7 @@ SCENARIO ("sorting a dataset") SCENARIO ("doing some things via dbvalues on rows of datasets") { using namespace sl3; - GIVEN ("a database and a typed dataset with knowen content") + GIVEN ("a database and a typed dataset with known content") { Database db{":memory:"}; Types types = {Type::Int, Type::Text, Type::Real}; @@ -224,7 +224,7 @@ SCENARIO ("doing some things via dbvalues on rows of datasets") } } - WHEN ("having a dataset with same field count but incampatible types") + WHEN ("having a dataset with the same field count but incompatible types") { auto ds1 = db.select ("SELECT 1,'hello', 'hello';", {Type::Int, Type::Text, Type::Text}); @@ -236,7 +236,7 @@ SCENARIO ("doing some things via dbvalues on rows of datasets") } } - WHEN ("having an ohter , compatible dataset") + WHEN ("having another compatible dataset") { auto ds1 = db.select ("SELECT 2,'world', 3.3;", {Type::Int, Type::Text, Type::Real}); @@ -252,3 +252,49 @@ SCENARIO ("doing some things via dbvalues on rows of datasets") } } } + +SCENARIO ("covering remaining dataset edge cases") +{ + using namespace sl3; + + GIVEN ("datasets with matching sizes but special edge conditions") + { + Database db{":memory:"}; + + WHEN ("merging datasets with different field names") + { + auto namedA = db.select ("SELECT 1 as int, 'eins' as txt, 2.2 as dbl;"); + auto namedB = db.select ("SELECT 2 as foo, 'zwei' as bar, 3.3 as baz;"); + + THEN ("merge throws because the names differ") + { + CHECK_THROWS_AS (namedA.merge (namedB), ErrTypeMisMatch); + } + } + + WHEN ("merging a row into a variant dataset") + { + Dataset ds{Types{Type::Variant, Type::Variant, Type::Variant}}; + auto row = db.select ("SELECT 'he', 'll', 'o';").at (0); + + THEN ("the variant path accepts the row without type checks") + { + CHECK_NOTHROW (ds.merge (row)); + REQUIRE_EQ (ds.size (), std::size_t{1}); + CHECK_EQ (ds[0][0].getText (), "he"); + CHECK_EQ (ds[0][1].getText (), "ll"); + CHECK_EQ (ds[0][2].getText (), "o"); + } + } + + WHEN ("sorting with a null comparator") + { + auto ds = db.select ("SELECT 2 as v UNION ALL SELECT 1 as v;"); + + THEN ("the comparator guard throws") + { + CHECK_THROWS_AS (ds.sort ({0}, nullptr), ErrNullValueAccess); + } + } + } +} diff --git a/tests/dbvalue/dbvaluetest.cpp b/tests/dbvalue/dbvaluetest.cpp index 1313137..f77c3cd 100644 --- a/tests/dbvalue/dbvaluetest.cpp +++ b/tests/dbvalue/dbvaluetest.cpp @@ -268,7 +268,7 @@ SCENARIO ("using value, basics") WHEN ("copy a typed value to a variant one") { DbValue v{Type::Variant}; - THEN ("this always succeed") + THEN ("this always succeeds") { v = dbval; CHECK (value_type_eq (v.value (), dbval.value ())); @@ -281,7 +281,7 @@ SCENARIO ("using value, basics") SCENARIO ("variant") { using namespace sl3; - GIVEN ("an variant value") + GIVEN ("a variant value") { DbValue dbval{Type::Variant}; @@ -334,7 +334,7 @@ SCENARIO ("assign values") { DbValue dbval{Type::Variant}; - WHEN ("assingin any other typed value") + WHEN ("assigning any other typed value") { THEN ("this works") { @@ -352,13 +352,13 @@ SCENARIO ("assign values") { DbValue strval{"foo"}; - THEN ("this thorws") + THEN ("this throws") { CHECK_THROWS_AS (dbval = strval, ErrTypeMisMatch); CHECK_THROWS_AS (dbval = DbValue{Type::Real}, ErrTypeMisMatch); } } - WHEN ("assign a comaptible varaint") + WHEN ("assign a compatible variant") { DbValue strval{12, Type::Variant}; @@ -444,7 +444,7 @@ SCENARIO ("sorting") WHEN ("comparing them") { - THEN ("they are value equal but not value type eaql") + THEN ("they are value-equal but not value-type-equal") { CHECK (dbval_eq (ival, ival)); CHECK (dbval_eq (ival, rval)); diff --git a/tests/errors/errortest.cpp b/tests/errors/errortest.cpp index d95f9e5..2fc88e7 100644 --- a/tests/errors/errortest.cpp +++ b/tests/errors/errortest.cpp @@ -48,6 +48,27 @@ SCENARIO ("checking error codes") } } + GIVEN ("the concrete sl3 error aliases") + { + const ErrNoConnection noConnection; + const ErrOutOfRange outOfRange; + const ErrTypeMisMatch typeMisMatch; + const ErrNullValueAccess nullValueAccess; + const ErrUnexpected unexpected; + + WHEN ("asking each error for its identifier") + { + THEN ("each alias reports the matching ErrCode") + { + CHECK(noConnection.getId() == sl3::ErrCode::NoConnection); + CHECK(outOfRange.getId() == sl3::ErrCode::OutOfRange); + CHECK(typeMisMatch.getId() == sl3::ErrCode::TypeMisMatch); + CHECK(nullValueAccess.getId() == sl3::ErrCode::NullValueAccess); + CHECK(unexpected.getId() == sl3::ErrCode::UNEXPECTED); + } + } + } + } @@ -71,4 +92,4 @@ SCENARIO ("Container index errors throw sl3 errors") } } -} \ No newline at end of file +} diff --git a/tests/rowcallback/rowcallbacktest.cpp b/tests/rowcallback/rowcallbacktest.cpp index 92f6097..df62a7e 100644 --- a/tests/rowcallback/rowcallbacktest.cpp +++ b/tests/rowcallback/rowcallbacktest.cpp @@ -3,6 +3,7 @@ #include #include +#include #include SCENARIO ("Check RowCallback and Callback return values") @@ -102,7 +103,7 @@ SCENARIO ("Check RowCallback start and end calls") "INSERT INTO tbltest VALUES (2) ;" "INSERT INTO tbltest VALUES (3) ;")); - GIVEN ("a RowCallback that counts all calls and fantasi on start/end counts") + GIVEN ("a RowCallback that counts all calls and tracks start/end counts") { struct CB : sl3::RowCallback { @@ -145,6 +146,33 @@ SCENARIO ("Check RowCallback start and end calls") } } +SCENARIO ("destroying a RowCallback through a base pointer") +{ + GIVEN ("a concrete RowCallback owned as a base unique_ptr") + { + struct DummyCallback : sl3::RowCallback + { + bool + onRow (sl3::Columns /*unusedForNow*/) override + { + return true; + } + }; + + WHEN ("the unique_ptr goes out of scope") + { + THEN ("destroying via RowCallback base pointer does not throw") + { + CHECK_NOTHROW ( + [] { + std::unique_ptr cb = + std::make_unique (); + } ()); + } + } + } +} + SCENARIO ("testing column names and index properties") { sl3::Database db{":memory:"}; @@ -207,7 +235,7 @@ SCENARIO ("testing column names and index properties") } } -SCENARIO ("all index accesses throw out of range for an invalud index") +SCENARIO ("all index accesses throw out of range for an invalid index") { GIVEN ("a record with some fields") { @@ -448,7 +476,7 @@ SCENARIO ("getting rows from columns") WHEN ("request a row with wrong count of types") { - THEN ("exceptions are the consequenze") + THEN ("exceptions are the consequence") { REQUIRE_NOTHROW (db.execute (sql)); db.execute (sql, [] (sl3::Columns cols) { @@ -468,9 +496,9 @@ SCENARIO ("getting rows from columns") } } - WHEN ("request a row with spezific correct types") + WHEN ("request a row with specific correct types") { - THEN ("a row with those types is retunred") + THEN ("a row with those types is returned") { REQUIRE_NOTHROW (db.execute (sql)); db.execute (sql, [] (sl3::Columns cols) { @@ -495,3 +523,70 @@ SCENARIO ("getting rows from columns") } } } + +SCENARIO ("covering remaining edge cases of columns access") +{ + GIVEN ("a record containing empty text, empty blob and null data") + { + sl3::Database db{":memory:"}; + auto sql = "SELECT '' as empty_text, " + " zeroblob(0) as empty_blob, " + " NULL as empty_null; "; + + WHEN ("accessing values and metadata inside a callback") + { + THEN ("empty values and invalid indices are handled correctly") + { + REQUIRE_NOTHROW (db.execute (sql)); + db.execute (sql, [] (sl3::Columns cols) { + REQUIRE_EQ (cols.count (), 3); + + CHECK_EQ (cols.getName (0), "empty_text"); + CHECK_EQ (cols.getName (1), "empty_blob"); + CHECK_EQ (cols.getName (2), "empty_null"); + + auto names = cols.getNames (); + REQUIRE_EQ (names.size (), std::size_t{3}); + CHECK_EQ (names[0], "empty_text"); + CHECK_EQ (names[1], "empty_blob"); + CHECK_EQ (names[2], "empty_null"); + + CHECK_EQ (cols.getType (0), sl3::Type::Text); + CHECK_EQ (cols.getType (1), sl3::Type::Blob); + CHECK_EQ (cols.getType (2), sl3::Type::Null); + + CHECK_EQ (cols.getSize (0), std::size_t{0}); + CHECK_EQ (cols.getSize (1), std::size_t{0}); + CHECK_EQ (cols.getSize (2), std::size_t{0}); + + CHECK_EQ (cols.getText (0), ""); + CHECK (cols.getBlob (1).empty ()); + CHECK_EQ (cols.getText (2), ""); + CHECK (cols.getBlob (2).empty ()); + CHECK_EQ (cols.getInt (2), 0); + CHECK_EQ (cols.getInt64 (2), 0); + CHECK_EQ (cols.getReal (2), doctest::Approx{0.0}); + + auto nullAsNull = cols.getValue (2, sl3::Type::Null); + CHECK_EQ (nullAsNull.dbtype (), sl3::Type::Variant); + CHECK (nullAsNull.isNull ()); + + CHECK_THROWS_AS ((void)cols.getName (-1), sl3::ErrOutOfRange); + CHECK_THROWS_AS ((void)cols.getValue (-1), sl3::ErrOutOfRange); + CHECK_THROWS_AS ( + (void)cols.getValue (-1, sl3::Type::Variant), + sl3::ErrOutOfRange); + CHECK_THROWS_AS ((void)cols.getType (-1), sl3::ErrOutOfRange); + CHECK_THROWS_AS ((void)cols.getSize (-1), sl3::ErrOutOfRange); + CHECK_THROWS_AS ((void)cols.getText (-1), sl3::ErrOutOfRange); + CHECK_THROWS_AS ((void)cols.getInt (-1), sl3::ErrOutOfRange); + CHECK_THROWS_AS ((void)cols.getInt64 (-1), sl3::ErrOutOfRange); + CHECK_THROWS_AS ((void)cols.getReal (-1), sl3::ErrOutOfRange); + CHECK_THROWS_AS ((void)cols.getBlob (-1), sl3::ErrOutOfRange); + + return false; + }); + } + } + } +} diff --git a/tests/rowcallback/rowcallbacktest_coverage.cpp b/tests/rowcallback/rowcallbacktest_coverage.cpp index e76dbda..6032719 100644 --- a/tests/rowcallback/rowcallbacktest_coverage.cpp +++ b/tests/rowcallback/rowcallbacktest_coverage.cpp @@ -12,7 +12,7 @@ SCENARIO ("for coverage") sl3::Database db{":memory:"}; auto sql = "SELECT 'b' as byte, NULL as noval; "; REQUIRE_NOTHROW (db.execute (sql)); - WHEN ("asking for the used storegae space") + WHEN ("asking for the used storage space") { THEN ("some int will use some space, and null non space") { @@ -23,7 +23,7 @@ SCENARIO ("for coverage") }); } } - WHEN ("geting an int from a null value") + WHEN ("getting an int from a null value") { THEN ("sqlite will convert it to 0") { @@ -34,7 +34,7 @@ SCENARIO ("for coverage") } } - WHEN ("want accecc to shoot myslef into the foot") + WHEN ("want access to shoot myself in the foot") { THEN ("this is possible") { diff --git a/tests/typenames/typenamestest.cpp b/tests/typenames/typenamestest.cpp index e0d9497..2a2d19f 100644 --- a/tests/typenames/typenamestest.cpp +++ b/tests/typenames/typenamestest.cpp @@ -8,7 +8,7 @@ SCENARIO ("check typenames") { using namespace sl3; - GIVEN ("the sqlite version libsl3 was comiled with") + GIVEN ("the sqlite version libsl3 was compiled with") { std::string libUsedVersion = sl3::sqliteCompiledVersion (); @@ -27,11 +27,11 @@ SCENARIO ("check typenames") } } - WHEN ("getting typnmes in a stream") + WHEN ("getting type names in a stream") { std::string appUsedVersion = sl3::sqliteRuntimeVersion (); - THEN ("the full enum name in in the stream") + THEN ("the full enum name is in the stream") { { std::stringstream ss; diff --git a/tests/value/valuetest.cpp b/tests/value/valuetest.cpp index c584963..f18d007 100644 --- a/tests/value/valuetest.cpp +++ b/tests/value/valuetest.cpp @@ -33,9 +33,9 @@ namespace sl3 } // * interesting, does not happen on 6.3 // on 5.3 there is a Value::Value (const Value& other) noexcept -// call where oter has some type +// call where other has some type -SCENARIO ("create, assign, copy and moveing values") +SCENARIO ("create, assign, copy and move values") { using namespace sl3; GIVEN ("some values of each supported type") @@ -80,7 +80,7 @@ SCENARIO ("create, assign, copy and moveing values") ValueList vals{typed_values (ival, dval, txt, blob)}; vals.emplace_back (Value{}); - THEN ("every value can be copied to an other value") + THEN ("every value can be copied to another value") { ValueList vals_m{typed_values (ival, dval, txt, blob)}; vals_m.emplace_back (Value{}); @@ -109,7 +109,7 @@ SCENARIO ("create, assign, copy and moveing values") ValueList vals{typed_values (ival, dval, txt, blob)}; vals.emplace_back (Value{}); - THEN ("every value can be move assinged to an other value") + THEN ("every value can be move-assigned to another value") { for (auto val : vals) { @@ -139,7 +139,7 @@ SCENARIO ("create, assign, copy and moveing values") ValueList vals{typed_values (int64_t{ival}, dval, txt, blob)}; vals.emplace_back (Value{}); - THEN ("every value can be assinged to any other value") + THEN ("every value can be assigned to any other value") { for (auto val : vals) { @@ -183,7 +183,7 @@ SCENARIO ("null value values access") WHEN ("trying to access any value") { CHECK (val.isNull ()); - THEN ("err null values access will be throws") + THEN ("null value access throws") { CHECK_THROWS_AS ((void)val.int64 (), sl3::ErrNullValueAccess); CHECK_THROWS_AS ((void)static_cast (val), @@ -275,13 +275,13 @@ SCENARIO ("invalid type access") { ValueList vals{typed_values (ival, dval, txt, blob)}; - THEN ("running check_throw on each fo this value will work") + THEN ("running check_throw on each of these values will work") { for (auto v : vals) check_invariants (v); } - THEN ("assinging the values to something otherworks") + THEN ("assigning the values to something else works") { vals.emplace_back (Value{}); for (auto v : vals) @@ -343,7 +343,7 @@ SCENARIO ("number special") { v = std::numeric_limits::max (); - THEN ("converting to an integer will might throw") + THEN ("converting to an integer throws") { CHECK_THROWS_AS ((void)static_cast (v), ErrOutOfRange); } @@ -376,7 +376,7 @@ SCENARIO ("number special") } } - WHEN ("assign a real value bigger than is to big") + WHEN ("assign a real value that is too big") { v = std::numeric_limits::max (); THEN ("converting to an integer will throw") @@ -400,7 +400,7 @@ SCENARIO ("number special") SCENARIO ("eject values") { using namespace sl3; - GIVEN ("a blob an a string value with knowen value") + GIVEN ("a blob and a string value with known values") { const std::string txt = "hello"; const Blob blob{std::byte{'A'}, std::byte{'B'}}; @@ -417,16 +417,16 @@ SCENARIO ("eject values") CHECK_EQ (txt, etxt); CHECK_EQ (blob, eblob); - AND_THEN ("the values are resteded to Null") + AND_THEN ("the values are reset to Null") { CHECK (sval.isNull ()); CHECK (bval.isNull ()); } } } - WHEN ("ejecting the blob / string value from the wrongtype") + WHEN ("ejecting the blob / string value from the wrong type") { - THEN ("exceptions are throws") + THEN ("exceptions are thrown") { CHECK_THROWS_AS (sval.ejectBlob (), ErrTypeMisMatch); CHECK_THROWS_AS (bval.ejectText (), ErrTypeMisMatch); @@ -434,7 +434,7 @@ SCENARIO ("eject values") } WHEN ("ejecting from Null values") { - THEN ("exceptions are throws") + THEN ("exceptions are thrown") { Value val; CHECK_THROWS_AS (val.ejectBlob (), ErrNullValueAccess); @@ -466,7 +466,7 @@ SCENARIO ("compare values") { Value v{0}; ValueList smaller = {Value{-1}, Value{}, Value{-1.0}}; - THEN ("all check succeed") + THEN ("all checks succeed") { check_smaller (Value{0}, smaller); check_smaller (Value{1.0}, smaller); @@ -490,7 +490,7 @@ SCENARIO ("compare values") CHECK_FALSE (value_type_eq (val, v)); } }; - WHEN ("callin check_eq with knowen values") + WHEN ("calling check_eq with known values") { THEN ("all tests succeed") { @@ -517,7 +517,7 @@ SCENARIO ("compare values") Value ival{1}; Value rval{1.0}; - WHEN ("comparing with them self") + WHEN ("comparing with themselves") { THEN ("value_eq is true and value_type_eq is true") { @@ -571,7 +571,7 @@ SCENARIO ("stringing values") WHEN ("stringing a null value") { ss << Value{}; - THEN ("the stingstream contains ") + THEN ("the stringstream contains ") { CHECK (ss.str () == ""); } @@ -579,13 +579,13 @@ SCENARIO ("stringing values") WHEN ("stringing an int value") { ss << Value{1}; - THEN ("the stingstream contains the int") { CHECK (ss.str () == "1"); } + THEN ("the stringstream contains the int") { CHECK (ss.str () == "1"); } } WHEN ("stringing a real value") { ss << Value{1.1}; - THEN ("the stingstream contains the real") + THEN ("the stringstream contains the real") { CHECK (ss.str () == "1.1"); } @@ -594,7 +594,7 @@ SCENARIO ("stringing values") WHEN ("stringing a text value") { ss << Value{"hello"}; - THEN ("the stingstream contains the text") + THEN ("the stringstream contains the text") { CHECK (ss.str () == "hello"); } @@ -603,10 +603,55 @@ SCENARIO ("stringing values") WHEN ("stringing a blob value") { ss << Value{Blob{}}; - THEN ("the stingstream contains ") + THEN ("the stringstream contains ") { CHECK (ss.str () == ""); } } } } + +SCENARIO ("value edge cases that improve branch coverage") +{ + using namespace sl3; + + GIVEN ("values near tricky conversion and comparison boundaries") + { + WHEN ("converting a too-small int64_t to int") + { + Value v{int64_t{std::numeric_limits::min () - 1LL}}; + + THEN ("an out of range error is thrown") + { + CHECK_THROWS_AS ((void)static_cast (v), ErrOutOfRange); + } + } + + WHEN ("comparing subnormal real values") + { + Value zero{0.0}; + Value tiny{std::numeric_limits::denorm_min ()}; + + THEN ("value_type_eq uses the subnormal almost-equal path") + { + CHECK (value_type_eq (zero, tiny)); + CHECK (value_type_eq (tiny, zero)); + } + } + + WHEN ("resetting text and blob values to null explicitly") + { + Value text{"hello"}; + Value blob{Blob{std::byte{'A'}, std::byte{'B'}}}; + + text.setNull (); + blob.setNull (); + + THEN ("both values become null") + { + CHECK (text.isNull ()); + CHECK (blob.isNull ()); + } + } + } +} diff --git a/tests/version/versiontest.cpp b/tests/version/versiontest.cpp index 58f7219..c60b55c 100644 --- a/tests/version/versiontest.cpp +++ b/tests/version/versiontest.cpp @@ -3,8 +3,8 @@ #include -// believe it or not, this is actually usefull, -// there are often many different version of sqlite on a system +// believe it or not, this is actually useful, +// there are often many different versions of sqlite on a system // and which one cmake picks up depends ... so you want to know about that /* @@ -13,7 +13,7 @@ this happened on Mac CI, /Users/runner/work/libsl3/libsl3/tests/version/versiontest.cpp:6: Scenario: check sqlite versions for library and app - Given: cmake genertated config + Given: cmake generated config When: comparing the compile/runtime version numbers Then: they are the same @@ -28,11 +28,11 @@ SCENARIO ("check sqlite versions for library and app") // this can be used as an example to add into your project // assure the libsl3 was compiled using the same header as you app - GIVEN ("cmake genertated config") + GIVEN ("cmake generated config") { - WHEN ("comparing compile/runtime verion") + WHEN ("comparing the compile/runtime version") { - THEN ("these version are the same") + THEN ("these versions are the same") { std::string compiledVersion = sl3::sqliteCompiledVersion (); CHECK (compiledVersion == sl3::sqliteRuntimeVersion ());