diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e80713b..db1bdb7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,84 +1,73 @@ name: Build with CMake on: - - pull_request - - workflow_dispatch + pull_request: + workflow_dispatch: env: - BUILD_TYPE: Release - CMAKE_VERSION: 3.21 - NINJA_VERSION: 1.11.1 + CMAKE_VERSION: 3.30.0 + NINJA_VERSION: 1.13.2 jobs: build: - name: "${{ matrix.config.name }} Qt ${{ matrix.qt-version }}" - runs-on: ${{ matrix.config.os }} + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - qt-version: [6.3] - config: - - { - name: "Ubuntu Latest GCC", - os: ubuntu-latest, - arch: gcc_64, - cc: "gcc", cxx: "g++" - } - - { - name: "Windows Latest MSVC", - os: windows-latest, - arch: win64_msvc2019_64, - is_msvc: true, - cc: "cl", cxx: "cl" - } - - { - name: "Windows Latest MinGW", - os: windows-latest, - arch: win64_mingw, - is_msvc: false, - cc: "gcc", cxx: "g++", - } - - { - name: "macOS Latest Clang", - os: macos-latest, - arch: clang_64, - cc: "clang", cxx: "clang++" - } + include: + # Qt 5.15.2 + - name: "Ubuntu GCC Qt 5.15.2" + os: ubuntu-latest + qt-version: "5.15.2" + qt-arch: gcc_64 - steps: - - uses: actions/checkout@v3 + - name: "Windows MSVC Qt 5.15.2" + os: windows-latest + qt-version: "5.15.2" + qt-arch: win64_msvc2019_64 + + # Qt 6.9.3 + - name: "Ubuntu GCC Qt 6.9.3" + os: ubuntu-latest + qt-version: "6.9.3" + qt-arch: linux_gcc_64 + + - name: "macOS Clang Qt 6.9.3" + os: macos-latest + qt-version: "6.9.3" + qt-arch: clang_64 - - name: Set up Qt environment - uses: jurplel/install-qt-action@v3 - with: - cache: true - version: ${{ matrix.qt-version }} - arch: ${{ matrix.config.arch }} + - name: "Windows MSVC Qt 6.9.3" + os: windows-latest + qt-version: "6.9.3" + qt-arch: win64_msvc2022_64 + + steps: + - uses: actions/checkout@v4 - - name: Setup MSBuild - uses: ilammy/msvc-dev-cmd@v1 - if: runner.os == 'Windows' && matrix.config.is_msvc == true + - uses: lukka/get-cmake@latest + with: + cmakeVersion: ${{ env.CMAKE_VERSION }} + ninjaVersion: ${{ env.NINJA_VERSION }} - - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v1.9 - with: - cmake-version: ${{env.CMAKE_VERSION}} + - name: Install Qt + uses: jurplel/install-qt-action@v4 + with: + cache: true + version: ${{ matrix.qt-version }} + arch: ${{ matrix.qt-arch }} - - name: Setup Ninja - uses: ashutoshvarma/setup-ninja@v1.1 - with: - version: ${{env.NINJA_VERSION}} + - name: Setup MSVC + uses: ilammy/msvc-dev-cmd@v1 + if: contains(matrix.name, 'MSVC') - - name: Build with CMake - uses: ashutoshvarma/action-cmake-build@master - with: - build-dir: ${{ runner.workspace }}/build - cc: ${{ matrix.config.cc }} - cxx: ${{ matrix.config.cxx }} - configure-options: -G Ninja - build-type: Release + - uses: ashutoshvarma/action-cmake-build@master + with: + build-dir: ${{ github.workspace }}/build + build-type: Release + configure-options: >- + -G Ninja + -DQUNITCONV_QT_TESTS=ON - - name: Run tests - run: | - cd ../build/tests - ./QUnitConversionTests + - run: ctest --test-dir ${{ github.workspace }}/build --output-on-failure --verbose diff --git a/.gitignore b/.gitignore index aed5716..53bfb28 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ *.user* cmake-build* +build/ /doc/ -.idea \ No newline at end of file +.idea/ +.vscode/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 6758034..e1a773e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,64 @@ cmake_minimum_required(VERSION 3.19) -project(QUnitConversion) +project(QUnitConversion LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED On) -add_subdirectory(QUnitConversion) +set(SOURCES + src/qlinearfunction.cpp +) -option(QUNITCONV_BUILD_TESTS "Build QUnitConversion unttests" On) +set(HEADERS + src/qaliasdictionary.h + src/qlinearfunction.h + src/qunitconversionfamily.h + src/qunitconversionrule.h + src/qunitconvertor.h +) -if (QUNITCONV_BUILD_TESTS) - add_subdirectory(tests) +add_library(QUnitConversion STATIC + ${SOURCES} + ${HEADERS} +) + +target_include_directories(QUnitConversion PUBLIC + src +) + +option(QUNITCONV_BUILD_TESTS "Build QUnitConversion unittests" ON) + +if(QUNITCONV_BUILD_TESTS) + include(FetchContent) + + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.17.0 + ) + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + + option(QUNITCONV_QT_TESTS "Include Qt string types in typed tests" OFF) + + add_executable(QUnitConversionTests + src/qaliasdictionary_tests.cpp + src/qlinearfunction_tests.cpp + src/qunitconvertor_tests.cpp + ) + + target_link_libraries(QUnitConversionTests + GTest::gtest_main + QUnitConversion + ) + + if(QUNITCONV_QT_TESTS) + find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) + find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED) + target_link_libraries(QUnitConversionTests Qt::Core) + target_compile_definitions(QUnitConversionTests PRIVATE QUNITCONV_QT_TESTS) + endif() + + enable_testing() + include(GoogleTest) + gtest_discover_tests(QUnitConversionTests) endif() diff --git a/QUnitConversion/CMakeLists.txt b/QUnitConversion/CMakeLists.txt deleted file mode 100644 index 33ad3e9..0000000 --- a/QUnitConversion/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -cmake_minimum_required(VERSION 3.19) - -include_guard(GLOBAL) - -project(QUnitConversion LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED On) - -set(SOURCES qlinearfunction.cpp) - -set(HEADERS - qaliasdictionary.h - qlinearfunction.h - qunitconversionfamily.h - qunitconversionrule.h - qunitconvertor.h -) - -find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED) - -add_library(QUnitConversion STATIC - ${SOURCES} - ${HEADERS} -) - -target_include_directories(QUnitConversion PUBLIC - . -) - -target_link_libraries(QUnitConversion PUBLIC - Qt::Core -) diff --git a/README.md b/README.md index 079e2b2..3906156 100644 --- a/README.md +++ b/README.md @@ -53,11 +53,4 @@ will be converted to m/s properly. `QUnitConversion` is distributed under MIT license -## Repo contents - -| Directory | Contents | -|---------------------|--------------------------------| -| `./QUnitConversion` | Library source code. | -| `./tests` | Unittests code. | - -Copyright Dmitriy Linev 2020-2024 +Copyright Dmitriy Linev 2020-2026 diff --git a/QUnitConversion/qaliasdictionary.h b/src/qaliasdictionary.h similarity index 71% rename from QUnitConversion/qaliasdictionary.h rename to src/qaliasdictionary.h index 40a7876..0bc8ba5 100644 --- a/QUnitConversion/qaliasdictionary.h +++ b/src/qaliasdictionary.h @@ -1,8 +1,10 @@ #ifndef QALIASDICTIONARY_H #define QALIASDICTIONARY_H -#include -#include +#include +#include +#include +#include /** * @brief The QAliasDictionary class provides @@ -23,25 +25,34 @@ class QAliasDictionary { if (m_names.contains(alias)) return alias; - return m_aliases.value(alias); + auto it = m_aliases.find(alias); + if (it != m_aliases.end()) + return it->second; + return {}; } /** * @brief Gets a list of aliases for a given name * @param name name to get aliases - * @return QList containing aliases for a given name + * @return Vector of strings containing aliases for a given name */ - QList aliases(const String &name) const + std::vector aliases(const String &name) const { - return m_aliases.keys(name); + std::vector result; + for (const auto &pair : m_aliases) + if (pair.second == name) + result.push_back(pair.first); + return result; } + /** * @brief Checks if this dictionary is empty * @return true if empty, false otherwise */ bool isEmpty() const { - return m_aliases.isEmpty(); + return m_aliases.empty(); } + /** * @brief Adds an alias to the dictionary * @param name name which will be returned if an alias requested @@ -49,9 +60,10 @@ class QAliasDictionary */ void addAlias(String name, String alias) { - m_aliases.insert(std::move(alias), name); + m_aliases.insert_or_assign(std::move(alias), name); m_names.insert(std::move(name)); } + /** * @brief Checks if a dictionary contains name for the given alias * @param alias alias to check existence @@ -72,9 +84,10 @@ class QAliasDictionary m_aliases.clear(); m_names.clear(); } + protected: - QMap m_aliases; - QSet m_names; + std::map m_aliases; + std::set m_names; }; #endif // QALIASDICTIONARY_H diff --git a/src/qaliasdictionary_tests.cpp b/src/qaliasdictionary_tests.cpp new file mode 100644 index 0000000..b6fb999 --- /dev/null +++ b/src/qaliasdictionary_tests.cpp @@ -0,0 +1,87 @@ +#include + +#include + +#ifdef QUNITCONV_QT_TESTS +#include +#endif + +#include "qaliasdictionary.h" + +namespace { + +template +bool contains(const Container &c, const T &value) +{ + return std::find(c.begin(), c.end(), value) != c.end(); +} + +} + +template +class QAliasDictionaryTest : public ::testing::Test {}; + +using StringTypes = ::testing::Types< + std::string +#ifdef QUNITCONV_QT_TESTS + , QString +#endif +>; +TYPED_TEST_SUITE(QAliasDictionaryTest, StringTypes); + +TYPED_TEST(QAliasDictionaryTest, Basic) +{ + QAliasDictionary dictionary; + ASSERT_TRUE(dictionary.isEmpty()); + ASSERT_FALSE(dictionary.contains("name")); + ASSERT_TRUE(dictionary.aliases("name").empty()); + + dictionary.addAlias("name", "alias"); + dictionary.addAlias("name", "alias2"); + dictionary.addAlias("name", "alias3"); + dictionary.addAlias("name2", "alias_for_name2"); + dictionary.addAlias("name2", "alias2_for_name2"); + + ASSERT_FALSE(dictionary.isEmpty()); + + // name() resolves both names and aliases; returns default for unknown + ASSERT_EQ(dictionary.name("name"), "name"); + ASSERT_EQ(dictionary.name("alias"), "name"); + ASSERT_EQ(dictionary.name("alias2"), "name"); + ASSERT_EQ(dictionary.name("alias3"), "name"); + ASSERT_EQ(dictionary.name("alias_for_name2"), "name2"); + ASSERT_EQ(dictionary.name("alias2_for_name2"), "name2"); + ASSERT_EQ(dictionary.name("unknown"), TypeParam{}); + + // contains() matches names and aliases, but not unknown strings + ASSERT_TRUE(dictionary.contains("name")); + ASSERT_TRUE(dictionary.contains("alias")); + ASSERT_FALSE(dictionary.contains("unknown")); + + // aliases() lists only direct aliases; returns empty for unknown or alias inputs + auto aliases = dictionary.aliases("name"); + ASSERT_TRUE(contains(aliases, TypeParam{"alias"})); + ASSERT_TRUE(contains(aliases, TypeParam{"alias2"})); + ASSERT_TRUE(contains(aliases, TypeParam{"alias3"})); + ASSERT_TRUE(dictionary.aliases("unknown").empty()); + ASSERT_TRUE(dictionary.aliases("alias").empty()); + + // name() also returns the name itself when passed a name directly + ASSERT_EQ(dictionary.name("name2"), "name2"); + + // reassigning an alias to a different name overwrites the mapping + dictionary.addAlias("name2", "alias"); + ASSERT_EQ(dictionary.name("alias"), "name2"); + + // aliases() reflects the reassignment: removed from old name, added to new name + ASSERT_FALSE(contains(dictionary.aliases("name"), TypeParam{"alias"})); + ASSERT_TRUE(contains(dictionary.aliases("name2"), TypeParam{"alias"})); + + // clear() resets the dictionary entirely + dictionary.clear(); + ASSERT_TRUE(dictionary.isEmpty()); + ASSERT_EQ(dictionary.name("name"), TypeParam{}); + ASSERT_EQ(dictionary.name("alias2"), TypeParam{}); + ASSERT_FALSE(dictionary.contains("name")); + ASSERT_FALSE(dictionary.contains("alias2")); +} diff --git a/QUnitConversion/qlinearfunction.cpp b/src/qlinearfunction.cpp similarity index 85% rename from QUnitConversion/qlinearfunction.cpp rename to src/qlinearfunction.cpp index 60e8ec3..aa3424d 100644 --- a/QUnitConversion/qlinearfunction.cpp +++ b/src/qlinearfunction.cpp @@ -1,15 +1,15 @@ #include "qlinearfunction.h" QLinearFunction::QLinearFunction() - : m_k(0), m_b(0) + : m_k(0) + , m_b(0) { - } QLinearFunction::QLinearFunction(double k, double b) - : m_k(k), m_b(b) + : m_k(k) + , m_b(b) { - } bool QLinearFunction::isValid() const @@ -17,9 +17,9 @@ bool QLinearFunction::isValid() const return m_k != 0; } -QLinearFunction QLinearFunction::inversed() const +QLinearFunction QLinearFunction::inverted() const { - if (!isValid()) + if (!isValid()) return {}; return {1. / m_k, -m_b / m_k}; } diff --git a/QUnitConversion/qlinearfunction.h b/src/qlinearfunction.h similarity index 96% rename from QUnitConversion/qlinearfunction.h rename to src/qlinearfunction.h index c484ead..861f466 100644 --- a/QUnitConversion/qlinearfunction.h +++ b/src/qlinearfunction.h @@ -29,11 +29,11 @@ class QLinearFunction /** * @brief Reverses current linear function so X = k*Y + b - * @return an object of type QLinearFunction containing inversed function + * @return an object of type QLinearFunction containing inverted function * @details This function doesn't perform validity check so applying it to * invalid function will cause division by zero */ - QLinearFunction inversed() const; + QLinearFunction inverted() const; /** * @brief Function value diff --git a/src/qlinearfunction_tests.cpp b/src/qlinearfunction_tests.cpp new file mode 100644 index 0000000..62729c5 --- /dev/null +++ b/src/qlinearfunction_tests.cpp @@ -0,0 +1,85 @@ +#include + +#include "qlinearfunction.h" + +TEST(QLinearFunction, Basis) +{ + QLinearFunction function; + ASSERT_FALSE(function.isValid()); + ASSERT_DOUBLE_EQ(function.k(), 0); + ASSERT_DOUBLE_EQ(function.b(), 0); + + function.setK(15); + function.setB(5); + ASSERT_TRUE(function.isValid()); + ASSERT_DOUBLE_EQ(function.k(), 15); + ASSERT_DOUBLE_EQ(function.b(), 5); + + QLinearFunction secondFunction(1, 2); + ASSERT_TRUE(secondFunction.isValid()); + ASSERT_DOUBLE_EQ(secondFunction.k(), 1); + ASSERT_DOUBLE_EQ(secondFunction.b(), 2); +} + +TEST(QLinearFunction, Value) +{ + QLinearFunction function(5.5, 3); + ASSERT_TRUE(function.isValid()); + ASSERT_DOUBLE_EQ(function.y(0), 3); + ASSERT_DOUBLE_EQ(function.y(-5), -24.5); + ASSERT_DOUBLE_EQ(function.y(1), 8.5); + ASSERT_DOUBLE_EQ(function.y(0.7659), 7.21245); + ASSERT_DOUBLE_EQ(function.y(35.6), 198.8); +} + +TEST(QLinearFunction, Inverted) +{ + // inverted() of an invalid function (k=0) returns a default (invalid) function + QLinearFunction invalid; + ASSERT_FALSE(invalid.inverted().isValid()); + + // f(x) = 3x - 2 => f⁻¹(x) = x/3 + 2/3 + QLinearFunction function(3, -2); + function = function.inverted(); + ASSERT_DOUBLE_EQ(function.k(), double(1) / 3); + ASSERT_DOUBLE_EQ(function.b(), double(2) / 3); + + // f(x) = -2x - 16 => f⁻¹(x) = -0.5x - 8 + function.setK(-2); + function.setB(-16); + function = function.inverted(); + ASSERT_DOUBLE_EQ(function.k(), -0.5); + ASSERT_DOUBLE_EQ(function.b(), -8); + + // applying a function then its inverse round-trips to the original value: f⁻¹(f(x)) == x + QLinearFunction f(3, -2); + ASSERT_DOUBLE_EQ(f.inverted().y(f.y(0)), 0); + ASSERT_DOUBLE_EQ(f.inverted().y(f.y(7)), 7); + ASSERT_DOUBLE_EQ(f.inverted().y(f.y(-4.5)), -4.5); +} + +TEST(QLinearFunction, Combined) +{ + QLinearFunction cToK(1, 273.15); + QLinearFunction KToF(1.8, -459.67); + QLinearFunction combined = QLinearFunction::combined(cToK, KToF); + + double eps = 0.00001; + ASSERT_NEAR(combined.y(-273.15), -459.67, eps); + ASSERT_NEAR(combined.y(-45.56), -50.008, eps); + ASSERT_NEAR(combined.y(-40), -40, eps); + ASSERT_NEAR(combined.y(-34.44), -29.992, eps); + ASSERT_NEAR(combined.y(-28.89), -20.002, eps); + ASSERT_NEAR(combined.y(-23.33), -9.994, eps); + ASSERT_NEAR(combined.y(-12.22), 10.004, eps); + ASSERT_NEAR(combined.y(-6.67), 19.994, eps); + ASSERT_NEAR(combined.y(-1.11), 30.002, eps); + ASSERT_NEAR(combined.y(0), 32, eps); + ASSERT_NEAR(combined.y(4.44), 39.992, eps); + ASSERT_NEAR(combined.y(10), 50, eps); + ASSERT_NEAR(combined.y(60), 140, eps); + ASSERT_NEAR(combined.y(65.56), 150.008, eps); + ASSERT_NEAR(combined.y(100), 212, eps); + ASSERT_NEAR(combined.y(260), 500, eps); + ASSERT_NEAR(combined.y(537.78), 1000.004, eps); +} diff --git a/QUnitConversion/qunitconversionfamily.h b/src/qunitconversionfamily.h similarity index 61% rename from QUnitConversion/qunitconversionfamily.h rename to src/qunitconversionfamily.h index f458503..603f6da 100644 --- a/QUnitConversion/qunitconversionfamily.h +++ b/src/qunitconversionfamily.h @@ -1,9 +1,8 @@ #ifndef QUNITCONVERSIONFAMILY_H #define QUNITCONVERSIONFAMILY_H -#include - #include +#include #include #include "qunitconversionrule.h" @@ -12,25 +11,25 @@ * @brief The QUnitConversionFamily class is an internal class that provides * a conversion by holding all of the conversion rules for a single family */ -template +template class QUnitConversionFamily { public: QUnitConversionFamily() = default; - QUnitConversionFamily(String familyName, String baseUnit) - : m_family(std::move(familyName)), - m_baseUnit(std::move(baseUnit)) + + QUnitConversionFamily(const String & familyName, const String & baseUnit) { + m_family = familyName; + m_baseUnit = baseUnit; } /** * @brief Adds a conversion rule to convertor * @param rule rule to add - * @details */ - void addConversionRule(QUnitConversionRule rule) + void addConversionRule(const QUnitConversionRule & rule) { - if (m_rules.isEmpty()) + if (m_rules.empty()) { m_family = rule.family(); m_baseUnit = rule.baseUnit(); @@ -40,8 +39,7 @@ class QUnitConversionFamily if (m_family != rule.family() || m_baseUnit != rule.baseUnit()) throw std::invalid_argument("Incorrect rule added to family"); // Don Corleone will be unhappy } - auto unit = rule.unit(); - m_rules.insert(std::move(unit), std::move(rule)); + m_rules.insert_or_assign(rule.unit(), rule); } /** @@ -52,17 +50,22 @@ class QUnitConversionFamily */ QLinearFunction convert(const String & in, const String & out) const { - if (m_rules.isEmpty()) + if (m_rules.empty()) return {}; - if (in == m_baseUnit && m_rules.contains(out)) // conversion from base unit to unit - return m_rules[out].convertFunction(); - if (m_rules.contains(in) && out == m_baseUnit) // converson from unit to base unit - return m_rules[in].convertFunction().inversed(); + auto itIn = m_rules.find(in); + auto itOut = m_rules.find(out); + + if (in == m_baseUnit && itOut != m_rules.end()) // conversion from base unit to unit + return itOut->second.convertFunction(); + if (itIn != m_rules.end() && out == m_baseUnit) // conversion from unit to base unit + return itIn->second.convertFunction().inverted(); // conversion from one unit to another through the base unit if possible - QLinearFunction inToBase = m_rules[in].convertFunction().inversed(); - QLinearFunction baseToOut = m_rules[out].convertFunction(); + if (itIn == m_rules.end() || itOut == m_rules.end()) + return {}; + QLinearFunction inToBase = itIn->second.convertFunction().inverted(); + QLinearFunction baseToOut = itOut->second.convertFunction(); if (!inToBase.isValid() || !baseToOut.isValid()) // one of the conversions is not present return {}; return QLinearFunction::combined(inToBase, baseToOut); @@ -84,9 +87,9 @@ class QUnitConversionFamily } protected: - QMap> m_rules; ///< Key is a unit, it's assumed that all rules have the same base unit - String m_baseUnit; ///< Base unit for this family - String m_family; ///< Family name + std::map> m_rules; ///< Key is a unit, it's assumed that all rules have the same base unit + String m_baseUnit; ///< Base unit for this family + String m_family; ///< Family name }; #endif // QUNITCONVERSIONFAMILY_H diff --git a/QUnitConversion/qunitconversionrule.h b/src/qunitconversionrule.h similarity index 57% rename from QUnitConversion/qunitconversionrule.h rename to src/qunitconversionrule.h index 0450bf7..9a15409 100644 --- a/QUnitConversion/qunitconversionrule.h +++ b/src/qunitconversionrule.h @@ -11,20 +11,19 @@ * class each family can be represented with the single base unit. In other words you * can't have two unit conversion rules with the same family but different base units */ -template +template class QUnitConversionRule { public: - QUnitConversionRule() = default; QUnitConversionRule(String family, String baseUnit, String unit, QLinearFunction convertFunction) : m_family(std::move(family)), - m_baseUnit(std::move(baseUnit)), - m_unit(std::move(unit)), - m_convertFunction(std::move(convertFunction)) + m_baseUnit(std::move(baseUnit)), + m_unit(std::move(unit)), + m_convertFunction(std::move(convertFunction)) { } @@ -33,57 +32,56 @@ class QUnitConversionRule String unit, double k, double b) : m_family(std::move(family)), - m_baseUnit(std::move(baseUnit)), - m_unit(std::move(unit)), - m_convertFunction(k, b) + m_baseUnit(std::move(baseUnit)), + m_unit(std::move(unit)), + m_convertFunction(k, b) { } - - String family() const - { - return m_family; + String family() const + { + return m_family; } void setFamily(const String &family) - { - m_family = family; + { + m_family = family; } - String baseUnit() const - { - return m_baseUnit; + String baseUnit() const + { + return m_baseUnit; } - + void setBaseUnit(const String &baseUnit) - { - m_baseUnit = baseUnit; + { + m_baseUnit = baseUnit; } - String unit() const - { - return m_unit; + String unit() const + { + return m_unit; } void setUnit(const String &unit) - { - m_unit = unit; + { + m_unit = unit; } - QLinearFunction convertFunction() const + QLinearFunction convertFunction() const { - return m_convertFunction; + return m_convertFunction; } - void setConvertFunction(const QLinearFunction &function) + void setConvertFunction(const QLinearFunction &function) { - m_convertFunction = function; + m_convertFunction = function; } protected: - String m_family; ///< Family name for this pair of units, like length, speed - String m_baseUnit; ///< Base unit for this family - String m_unit; ///< Unit to convert to + String m_family; ///< Family name for this pair of units, like length, speed + String m_baseUnit; ///< Base unit for this family + String m_unit; ///< Unit to convert to QLinearFunction m_convertFunction; ///< Linear function to perform conversion from base unit to unit }; diff --git a/QUnitConversion/qunitconvertor.h b/src/qunitconvertor.h similarity index 58% rename from QUnitConversion/qunitconvertor.h rename to src/qunitconvertor.h index 9a09102..f3d1767 100644 --- a/QUnitConversion/qunitconvertor.h +++ b/src/qunitconvertor.h @@ -1,9 +1,10 @@ #ifndef QUNITCONVERTOR_H #define QUNITCONVERTOR_H -#include - +#include #include +#include +#include #include "qlinearfunction.h" #include "qunitconversionfamily.h" @@ -14,11 +15,15 @@ * in a string form. It uses "base" unit for each "family" (length, speed etc) * and perform conversions inside a family through conversion to and from base unit. */ -template +template class QUnitConvertor { - friend class QUnitConvertorTests; public: + /** + * @brief Default constructor + */ + QUnitConvertor() = default; + /** * @brief Checks if unit conversion from in unit to out unit is possible * @param in unit to convert from @@ -40,22 +45,15 @@ class QUnitConvertor { if (in == out) return {1, 0}; - String actualIn, actualOut; - if (m_aliases.contains(in)) - actualIn = m_aliases.name(in); - else - actualIn = in; - if (m_aliases.contains(out)) - actualOut = m_aliases.name(out); - else - actualOut = out; - auto inFamilyIt = m_familiesByUnit.find(actualIn); - - String outFamily = m_familiesByUnit.value(actualOut); - if (inFamilyIt == m_familiesByUnit.end() || inFamilyIt.value() != outFamily) + String actualIn = m_aliases.contains(in) ? m_aliases.name(in) : in; + String actualOut = m_aliases.contains(out) ? m_aliases.name(out) : out; + auto itIn = m_familiesByUnit.find(actualIn); + auto itOut = m_familiesByUnit.find(actualOut); + if (itIn == m_familiesByUnit.end() || itOut == m_familiesByUnit.end()) return {}; - - return m_families[inFamilyIt.value()].convert(actualIn, actualOut); + if (itIn->second != itOut->second) + return {}; + return m_families.at(itIn->second).convert(actualIn, actualOut); } /** @@ -78,32 +76,35 @@ class QUnitConvertor /** * @brief Adds a conversion rule to convertor * @param rule rule to add - * @details This function doesn't convert an alas for a unit to an actual unit name, so make sure to + * @details This function doesn't convert an alias for a unit to an actual unit name, so make sure to * pass here an actual unit name * @throw std::invalid_argument if a passed rule has existing family with different base unit, * existing unit with different family or existing unit with a different family or base unit */ void addConversionRule(const QUnitConversionRule & rule) { - if (m_baseUnitsByFamilies.contains(rule.family()) && m_baseUnitsByFamilies[rule.family()] != rule.baseUnit()) + auto itFamily = m_baseUnitsByFamilies.find(rule.family()); + if (itFamily != m_baseUnitsByFamilies.end() && itFamily->second != rule.baseUnit()) throw std::invalid_argument("Incorrect rule added: incorrect family base unit"); - if (m_familiesByUnit.contains(rule.baseUnit()) && m_familiesByUnit[rule.baseUnit()] != rule.family()) + auto itBaseUnit = m_familiesByUnit.find(rule.baseUnit()); + if (itBaseUnit != m_familiesByUnit.end() && itBaseUnit->second != rule.family()) throw std::invalid_argument("Incorrect rule added: incorrect base unit family"); - if (m_familiesByUnit.contains(rule.unit()) && m_familiesByUnit[rule.unit()] != rule.family()) + auto itUnit = m_familiesByUnit.find(rule.unit()); + if (itUnit != m_familiesByUnit.end() && itUnit->second != rule.family()) throw std::invalid_argument("Incorrect rule added: incorrect unit family"); - if (!m_families.contains(rule.family())) + if (m_families.find(rule.family()) == m_families.end()) { QUnitConversionFamily family; family.addConversionRule(rule); - m_families.insert(rule.family(), family); - m_baseUnitsByFamilies.insert(rule.family(), rule.baseUnit()); - m_familiesByUnit.insert(rule.baseUnit(), rule.family()); + m_families.emplace(rule.family(), std::move(family)); + m_baseUnitsByFamilies[rule.family()] = rule.baseUnit(); + m_familiesByUnit[rule.baseUnit()] = rule.family(); } else { m_families[rule.family()].addConversionRule(rule); } - m_familiesByUnit.insert(rule.unit(), rule.family()); + m_familiesByUnit[rule.unit()] = rule.family(); } /** @@ -118,15 +119,14 @@ class QUnitConvertor /** * @brief Method provides access to a list of families of units in this convertor - * @return StringList containing a list of unit families + * @return vector containing a list of unit families */ std::vector families() const { std::vector result; result.reserve(m_families.size()); - for (auto it = m_families.begin(); it != m_families.end(); ++it) - result.push_back(it.key()); - + for (const auto & [name, _] : m_families) + result.push_back(name); return result; } @@ -137,19 +137,18 @@ class QUnitConvertor */ String family(const String & unit) const { - String actualUnit; - if (m_aliases.contains(unit)) - actualUnit = m_aliases.name(unit); - else - actualUnit = unit; - return m_familiesByUnit.value(actualUnit); + String actualUnit = m_aliases.contains(unit) ? m_aliases.name(unit) : unit; + auto it = m_familiesByUnit.find(actualUnit); + if (it == m_familiesByUnit.end()) + return {}; + return it->second; } /** * @brief Gets a list of units with a possible connection to/from a given unit * @param unit unit to get a list of conversions - * @return StringList with units with possible conversion to a given unit, including a given unit. If - * conversion to/from a given unit is unknown returns an empty list + * @return vector with units with possible conversion to a given unit, including a given unit. + * If conversion to/from a given unit is unknown returns an empty vector */ std::vector conversions(const String & unit) const { @@ -158,24 +157,22 @@ class QUnitConvertor /** * @brief Method provides access to a list of units in this convertor within a given - * family, effectively providing a list of unit with a possible conversion from + * family, effectively providing a list of units with a possible conversion from * any unit of this list to any other * @param family family to return unit list - * @return StringList containing a list of units known by this unit convertor + * @return vector containing a list of units known by this unit convertor within the family */ - std::vector units(const String &family) const + std::vector units(const String & family) const { std::vector result; - result.reserve(m_familiesByUnit.size()); - for (auto it = m_familiesByUnit.begin(); it != m_familiesByUnit.end(); ++it) - if (it.value() == family) - result.push_back(it.key()); - + for (const auto & [unit, unitFamily] : m_familiesByUnit) + if (unitFamily == family) + result.push_back(unit); return result; } /** - * @brief adds aliases from the object QAliasDictionary + * @brief Sets aliases from a QAliasDictionary object */ void setAliases(QAliasDictionary aliases) { @@ -183,14 +180,13 @@ class QUnitConvertor } /** - * @brief adds one alias + * @brief Adds one alias */ void addAlias(String name, String alias) { - m_aliases.addAlias(name, alias); + m_aliases.addAlias(std::move(name), std::move(alias)); } - /** * @brief Removes all alias rules */ @@ -209,10 +205,23 @@ class QUnitConvertor return m_aliases.name(alias); } + /** + * @brief Gets the base unit for a given family + * @param family family name + * @return base unit name or an empty string if the family is unknown + */ + String baseUnit(const String & family) const + { + auto it = m_baseUnitsByFamilies.find(family); + if (it == m_baseUnitsByFamilies.end()) + return {}; + return it->second; + } + protected: - QMap m_familiesByUnit; ///< Key is a unit, Value is a corresponding family. Base units are also put here - QMap m_baseUnitsByFamilies; ///< Key is a family name, Value is a corresponding base unit - QMap> m_families; ///< Key is a family name, Value is a family + std::map m_familiesByUnit; ///< Key is a unit, Value is a corresponding family. Base units are also put here + std::map m_baseUnitsByFamilies; ///< Key is a family name, Value is a corresponding base unit + std::map> m_families; ///< Key is a family name, Value is a family QAliasDictionary m_aliases; }; diff --git a/src/qunitconvertor_tests.cpp b/src/qunitconvertor_tests.cpp new file mode 100644 index 0000000..004ad75 --- /dev/null +++ b/src/qunitconvertor_tests.cpp @@ -0,0 +1,167 @@ +#include + +#ifdef QUNITCONV_QT_TESTS +#include +#endif +#include + +#include "qunitconvertor.h" + +namespace { + +template +bool contains(const Container &c, const T &value) +{ + return std::find(c.begin(), c.end(), value) != c.end(); +} + +} + +template +class QUnitConvertorTest : public ::testing::Test {}; + +using StringTypes = ::testing::Types< + std::string +#ifdef QUNITCONV_QT_TESTS + , QString +#endif +>; +TYPED_TEST_SUITE(QUnitConvertorTest, StringTypes); + +TYPED_TEST(QUnitConvertorTest, AddRule) +{ + QUnitConvertor convertor; + convertor.addConversionRule(QUnitConversionRule("length", "m", "km", 0.001, 0)); + ASSERT_TRUE(contains(convertor.families(), TypeParam{"length"})); + ASSERT_NE(convertor.family("m"), TypeParam{}); + ASSERT_NE(convertor.family("km"), TypeParam{}); + ASSERT_EQ(convertor.families().size(), 1); + ASSERT_EQ(convertor.baseUnit("length"), "m"); + + convertor.addConversionRule(QUnitConversionRule("length", "m", "cm", 100, 0)); + ASSERT_NE(convertor.family("m"), TypeParam{}); + ASSERT_NE(convertor.family("km"), TypeParam{}); + ASSERT_NE(convertor.family("cm"), TypeParam{}); + ASSERT_EQ(convertor.families().size(), 1); + ASSERT_EQ(convertor.units("length").size(), 3); + + convertor.addConversionRule(QUnitConversionRule("length", "m", "mm", 1000, 0)); + ASSERT_NE(convertor.family("m"), TypeParam{}); + ASSERT_NE(convertor.family("km"), TypeParam{}); + ASSERT_NE(convertor.family("cm"), TypeParam{}); + ASSERT_NE(convertor.family("mm"), TypeParam{}); + ASSERT_EQ(convertor.families().size(), 1); + ASSERT_EQ(convertor.units("length").size(), 4); + + // passing existing family with a different base unit + ASSERT_THROW(convertor.addConversionRule( + QUnitConversionRule("length", "km", "m", 1000, 0)), + std::invalid_argument + ); + + // passing a different family with an existing base unit + ASSERT_THROW(convertor.addConversionRule( + QUnitConversionRule("notlength", "m", "km", 0.001, 0)), + std::invalid_argument + ); + + // passing the same conversion once again should work + convertor.addConversionRule(QUnitConversionRule("length", "m", "mm", 1000, 0)); + + // let's make sure that none of the state changed + ASSERT_EQ(convertor.families().size(), 1); + ASSERT_EQ(convertor.units("length").size(), 4); + + // note "min" here, since we have to make sure that all unit names are different + // we need to differ minutes from meters + convertor.addConversionRule(QUnitConversionRule("time", "s", "min", double(1) / 60, 0)); + ASSERT_TRUE(contains(convertor.families(), TypeParam{"length"})); + ASSERT_TRUE(contains(convertor.families(), TypeParam{"time"})); + ASSERT_EQ(convertor.families().size(), 2); + ASSERT_NE(convertor.family("m"), TypeParam{}); + ASSERT_NE(convertor.family("km"), TypeParam{}); + ASSERT_NE(convertor.family("cm"), TypeParam{}); + ASSERT_NE(convertor.family("mm"), TypeParam{}); + ASSERT_NE(convertor.family("s"), TypeParam{}); + ASSERT_NE(convertor.family("min"), TypeParam{}); + ASSERT_EQ(convertor.baseUnit("length"), "m"); + ASSERT_EQ(convertor.baseUnit("time"), "s"); + + auto families = convertor.families(); + ASSERT_TRUE(contains(families, TypeParam{"length"})); + ASSERT_TRUE(contains(families, TypeParam{"time"})); + + auto units = convertor.units("length"); + ASSERT_EQ(units.size(), 4); + ASSERT_TRUE(contains(units, TypeParam{"m"})); + ASSERT_TRUE(contains(units, TypeParam{"km"})); + ASSERT_TRUE(contains(units, TypeParam{"cm"})); + ASSERT_TRUE(contains(units, TypeParam{"mm"})); + + ASSERT_NEAR(convertor.convert(0, "m", "km"), 0, 1e-10); + ASSERT_NEAR(convertor.convert(50, "m", "km"), 0.05, 1e-10); + ASSERT_NEAR(convertor.convert(50, "km", "m"), 50000, 1e-10); + ASSERT_NEAR(convertor.convert(500, "cm", "m"), 5, 1e-10); + ASSERT_NEAR(convertor.convert(500, "cm", "km"), 0.005, 1e-10); + ASSERT_NEAR(convertor.convert(500, "m", "m"), 500, 1e-10); + + auto conversions = convertor.conversions("m"); + ASSERT_TRUE(contains(conversions, TypeParam{"m"})); + ASSERT_TRUE(contains(conversions, TypeParam{"km"})); + ASSERT_TRUE(contains(conversions, TypeParam{"cm"})); + ASSERT_TRUE(contains(conversions, TypeParam{"mm"})); + ASSERT_EQ(conversions.size(), 4); + + conversions = convertor.conversions("mm"); + ASSERT_TRUE(contains(conversions, TypeParam{"m"})); + ASSERT_TRUE(contains(conversions, TypeParam{"km"})); + ASSERT_TRUE(contains(conversions, TypeParam{"cm"})); + ASSERT_TRUE(contains(conversions, TypeParam{"mm"})); + ASSERT_EQ(conversions.size(), 4); + + conversions = convertor.conversions("mmmm"); + ASSERT_TRUE(conversions.empty()); + + ASSERT_EQ(convertor.family("m"), "length"); + ASSERT_EQ(convertor.family("km"), "length"); + ASSERT_EQ(convertor.family("mmmmm"), TypeParam{}); +} + +TYPED_TEST(QUnitConvertorTest, SetAliases) +{ + QUnitConvertor convertor; + convertor.addConversionRule(QUnitConversionRule("length", "m", "km", 0.001, 0)); + ASSERT_TRUE(convertor.canConvert("m", "km")); + ASSERT_TRUE(convertor.canConvert("km", "m")); + ASSERT_FALSE(convertor.canConvert("km", "meter")); + + QAliasDictionary aliases; + aliases.addAlias("m", "m"); + aliases.addAlias("m", "meter"); + aliases.addAlias("m", "meters"); + + convertor.setAliases(aliases); + + ASSERT_TRUE(convertor.canConvert("km", "meter")); + ASSERT_TRUE(convertor.canConvert("km", "meters")); +} + +TYPED_TEST(QUnitConvertorTest, AddAlias) +{ + QUnitConvertor convertor; + convertor.addConversionRule(QUnitConversionRule("length", "m", "km", 0.001, 0)); + + QAliasDictionary aliases; + aliases.addAlias("m", "m"); + aliases.addAlias("m", "meter"); + + convertor.setAliases(aliases); + + ASSERT_TRUE(convertor.canConvert("km", "meter")); + ASSERT_FALSE(convertor.canConvert("km", "meters")); + + convertor.addAlias("m", "meters"); + + ASSERT_TRUE(convertor.canConvert("km", "meters")); +} + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt deleted file mode 100644 index 107401c..0000000 --- a/tests/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -cmake_minimum_required(VERSION 3.19) - -include_guard(GLOBAL) - -project(QUnitConversionTests LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED On) - -set(CMAKE_AUTOMOC On) -set(CMAKE_AUTORCC On) - -find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Test REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Test REQUIRED) - -set(SOURCES - main.cpp - qaliasdictionarytests.cpp - qlinearfunctiontests.cpp - qunitconvertortests.cpp - ) - -set(HEADERS - qaliasdictionarytests.h - qlinearfunctiontests.h - qunitconvertortests.h - ) - -add_executable(QUnitConversionTests - ${SOURCES} - ${HEADERS} - ) - -target_link_libraries(QUnitConversionTests - Qt::Core - Qt::Test - QUnitConversion - ) diff --git a/tests/C_to_F.xlsx b/tests/C_to_F.xlsx deleted file mode 100644 index e13bcb0..0000000 Binary files a/tests/C_to_F.xlsx and /dev/null differ diff --git a/tests/main.cpp b/tests/main.cpp deleted file mode 100644 index 09e66c5..0000000 --- a/tests/main.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include - -#include "iostream" - -#include "qlinearfunctiontests.h" -#include "qaliasdictionarytests.h" -#include "qunitconvertortests.h" - -#define RUN_TESTS(x) {x member; result += QTest::qExec(&member, argc, argv); std::cout << std::endl; QThread::msleep(100); } - -int main(int argc, char *argv[]) -{ - QCoreApplication a(argc, argv); - Q_UNUSED(a) - int result = 0; - - RUN_TESTS(QLinearFunctionTests); - RUN_TESTS(QAliasDictionaryTests); - RUN_TESTS(QUnitConvertorTests); - - if (result == 0) - qDebug()<<"all tests passed succesfully"; - else - qDebug()<<"tests failed"; -} diff --git a/tests/qaliasdictionarytests.cpp b/tests/qaliasdictionarytests.cpp deleted file mode 100644 index d4c0737..0000000 --- a/tests/qaliasdictionarytests.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "qaliasdictionarytests.h" - -QAliasDictionaryTests::QAliasDictionaryTests(QObject *parent) : QObject(parent) -{ - -} - -void QAliasDictionaryTests::basicTests() -{ - QAliasDictionary dictionary; - QVERIFY(dictionary.isEmpty()); - dictionary.addAlias("name", "alias"); - dictionary.addAlias("name", "alias2"); - dictionary.addAlias("name", "alias3"); - - QVERIFY(!dictionary.isEmpty()); - QVERIFY(dictionary.name("name") == "name"); - QVERIFY(dictionary.name("alias") == "name"); - QVERIFY(dictionary.name("alias2") == "name"); - QVERIFY(dictionary.name("alias3") == "name"); - QVERIFY(dictionary.name("alias4").isEmpty()); - - dictionary.addAlias("name2", "alias_for_name2"); - dictionary.addAlias("name2", "alias2_for_name2"); - QVERIFY(dictionary.name("alias_for_name2") == "name2"); - QVERIFY(dictionary.name("alias2_for_name2") == "name2"); - QVERIFY(dictionary.name("alias2_for_name3").isEmpty()); - - auto aliases = dictionary.aliases("name"); - QVERIFY(aliases.contains("alias")); - QVERIFY(aliases.contains("alias2")); - QVERIFY(aliases.contains("alias3")); -} - -void QAliasDictionaryTests::basicStdStringTests() -{ - QAliasDictionary dictionary; - QVERIFY(dictionary.isEmpty()); - dictionary.addAlias("name", "alias"); - dictionary.addAlias("name", "alias2"); - dictionary.addAlias("name", "alias3"); - - QVERIFY(!dictionary.isEmpty()); - QVERIFY(dictionary.name("name") == "name"); - QVERIFY(dictionary.name("alias") == "name"); - QVERIFY(dictionary.name("alias2") == "name"); - QVERIFY(dictionary.name("alias3") == "name"); - QVERIFY(dictionary.name("alias4").size() == 0); - - dictionary.addAlias("name2", "alias_for_name2"); - dictionary.addAlias("name2", "alias2_for_name2"); - QVERIFY(dictionary.name("alias_for_name2") == "name2"); - QVERIFY(dictionary.name("alias2_for_name2") == "name2"); - QVERIFY(dictionary.name("alias2_for_name3").size() == 0); - - auto aliases = dictionary.aliases("name"); - QVERIFY(aliases.contains("alias")); - QVERIFY(aliases.contains("alias2")); - QVERIFY(aliases.contains("alias3")); -} diff --git a/tests/qaliasdictionarytests.h b/tests/qaliasdictionarytests.h deleted file mode 100644 index 616a29e..0000000 --- a/tests/qaliasdictionarytests.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef QALIASDICTIONARYTESTS_H -#define QALIASDICTIONARYTESTS_H - -#include -//#include - -#include "qaliasdictionary.h" - -class QAliasDictionaryTests : public QObject -{ - Q_OBJECT -public: - explicit QAliasDictionaryTests(QObject *parent = nullptr); - -private slots: - void basicTests(); - void basicStdStringTests(); -}; - -#endif // QALIASDICTIONARYTESTS_H diff --git a/tests/qlinearfunctiontests.cpp b/tests/qlinearfunctiontests.cpp deleted file mode 100644 index 530e08b..0000000 --- a/tests/qlinearfunctiontests.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "qlinearfunctiontests.h" - -QLinearFunctionTests::QLinearFunctionTests(QObject *parent) : QObject(parent) -{ - -} - -void QLinearFunctionTests::basisTests() -{ - QLinearFunction function; - QVERIFY(!function.isValid()); - QVERIFY(qFuzzyIsNull(function.k())); - QVERIFY(qFuzzyIsNull(function.b())); - - function.setK(15); - function.setB(5); - QVERIFY(function.isValid()); - QVERIFY(qFuzzyCompare(function.k(), 15)); - QVERIFY(qFuzzyCompare(function.b(), 5)); - - QLinearFunction secondFunction(1, 2); - QVERIFY(secondFunction.isValid()); - QVERIFY(qFuzzyCompare(secondFunction.k(), 1)); - QVERIFY(qFuzzyCompare(secondFunction.b(), 2)); -} - -void QLinearFunctionTests::valueTest() -{ - QLinearFunction function(5.5, 3); - QVERIFY(function.isValid()); - QVERIFY(qFuzzyCompare(function.y(0), 3)); - QVERIFY(qFuzzyCompare(function.y(-5), -24.5)); - QVERIFY(qFuzzyCompare(function.y(1), 8.5)); - QVERIFY(qFuzzyCompare(function.y(0.7659), 7.21245)); - QVERIFY(qFuzzyCompare(function.y(35.6), 198.8)); -} - -void QLinearFunctionTests::inversedTest() -{ - QLinearFunction function(3, -2); - function = function.inversed(); - QVERIFY(qFuzzyCompare(function.k(), double(1) / 3)); - QVERIFY(qFuzzyCompare(function.b(), double(2) / 3)); - - function.setK(-2); - function.setB(-16); - function = function.inversed(); - QVERIFY(qFuzzyCompare(function.k(), -0.5)); - QVERIFY(qFuzzyCompare(function.b(), -8)); -} - -void QLinearFunctionTests::combinedTest() -{ - QLinearFunction cToK(1, 273.15); - QLinearFunction KToF(1.8, -459.67); - QLinearFunction combined = QLinearFunction::combined(cToK, KToF); - // an underlying conversion is in /tests/C_to_F.xlsx file - QVERIFY(qFuzzyCompare(combined.y(-273.15), -459.67)); - QVERIFY(qFuzzyCompare(combined.y(-45.56), -50.008)); - QVERIFY(qFuzzyCompare(combined.y(-40), -40)); - QVERIFY(qFuzzyCompare(combined.y(-34.44), -29.992)); - QVERIFY(qFuzzyCompare(combined.y(-28.89), -20.002)); - QVERIFY(qFuzzyCompare(combined.y(-23.33), -9.994)); - QVERIFY(qFuzzyCompare(combined.y(-12.22), 10.004)); - QVERIFY(qFuzzyCompare(combined.y(-6.67), 19.994)); - QVERIFY(qFuzzyCompare(combined.y(-1.11), 30.002)); - QVERIFY(qFuzzyCompare(combined.y(0), 32)); - QVERIFY(qFuzzyCompare(combined.y(4.44), 39.992)); - QVERIFY(qFuzzyCompare(combined.y(10), 50)); - QVERIFY(qFuzzyCompare(combined.y(60), 140)); - QVERIFY(qFuzzyCompare(combined.y(65.56), 150.008)); - QVERIFY(qFuzzyCompare(combined.y(100), 212)); - QVERIFY(qFuzzyCompare(combined.y(260), 500)); - QVERIFY(qFuzzyCompare(combined.y(537.78), 1000.004)); - - -} diff --git a/tests/qlinearfunctiontests.h b/tests/qlinearfunctiontests.h deleted file mode 100644 index 311aa9f..0000000 --- a/tests/qlinearfunctiontests.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef QLINEARFUNCTIONTESTS_H -#define QLINEARFUNCTIONTESTS_H - -#include - -#include "qlinearfunction.h" - -class QLinearFunctionTests : public QObject -{ - Q_OBJECT -public: - explicit QLinearFunctionTests(QObject *parent = nullptr); - -private slots: - void basisTests(); - void valueTest(); - void inversedTest(); - void combinedTest(); -}; - -#endif // QLINEARFUNCTIONTESTS_H diff --git a/tests/qunitconvertortests.cpp b/tests/qunitconvertortests.cpp deleted file mode 100644 index b9caf4c..0000000 --- a/tests/qunitconvertortests.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include "qunitconvertortests.h" -#include "qaliasdictionary.h" - -#include - -using String = QString; - -namespace { - -template