From f7ba80cc457d65dfb3612793b18f15aa8eebdd6c Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Mon, 23 Mar 2026 18:43:30 +0200 Subject: [PATCH 1/7] Remove dependency on Qt, make classes that have string members to be templates, allowing use of QString and std::string --- .gitignore | 4 +- CMakeLists.txt | 5 +- QUnitConversion/qunitconversionfamily.cpp | 48 ---- QUnitConversion/qunitconversionfamily.h | 51 ----- QUnitConversion/qunitconversionrule.cpp | 58 ----- QUnitConversion/qunitconversionrule.h | 48 ---- QUnitConversion/qunitconvertor.cpp | 109 --------- QUnitConversion/qunitconvertor.h | 128 ----------- {QUnitConversion => src}/CMakeLists.txt | 13 +- {QUnitConversion => src}/qaliasdictionary.h | 33 ++- {QUnitConversion => src}/qlinearfunction.cpp | 15 +- {QUnitConversion => src}/qlinearfunction.h | 6 +- src/qunitconversionfamily.h | 95 ++++++++ src/qunitconversionrule.h | 86 +++++++ src/qunitconvertor.h | 228 +++++++++++++++++++ tests/CMakeLists.txt | 53 +++-- tests/C_to_F.xlsx | Bin 10651 -> 0 bytes tests/main.cpp | 25 -- tests/qaliasdictionary_tests.cpp | 87 +++++++ tests/qaliasdictionarytests.cpp | 60 ----- tests/qaliasdictionarytests.h | 20 -- tests/qlinearfunction_tests.cpp | 85 +++++++ tests/qlinearfunctiontests.cpp | 77 ------- tests/qlinearfunctiontests.h | 21 -- tests/qunitconvertor_tests.cpp | 167 ++++++++++++++ tests/qunitconvertortests.cpp | 149 ------------ tests/qunitconvertortests.h | 21 -- 27 files changed, 818 insertions(+), 874 deletions(-) delete mode 100644 QUnitConversion/qunitconversionfamily.cpp delete mode 100644 QUnitConversion/qunitconversionfamily.h delete mode 100644 QUnitConversion/qunitconversionrule.cpp delete mode 100644 QUnitConversion/qunitconversionrule.h delete mode 100644 QUnitConversion/qunitconvertor.cpp delete mode 100644 QUnitConversion/qunitconvertor.h rename {QUnitConversion => src}/CMakeLists.txt (58%) rename {QUnitConversion => src}/qaliasdictionary.h (71%) rename {QUnitConversion => src}/qlinearfunction.cpp (81%) rename {QUnitConversion => src}/qlinearfunction.h (94%) create mode 100644 src/qunitconversionfamily.h create mode 100644 src/qunitconversionrule.h create mode 100644 src/qunitconvertor.h delete mode 100644 tests/C_to_F.xlsx delete mode 100644 tests/main.cpp create mode 100644 tests/qaliasdictionary_tests.cpp delete mode 100644 tests/qaliasdictionarytests.cpp delete mode 100644 tests/qaliasdictionarytests.h create mode 100644 tests/qlinearfunction_tests.cpp delete mode 100644 tests/qlinearfunctiontests.cpp delete mode 100644 tests/qlinearfunctiontests.h create mode 100644 tests/qunitconvertor_tests.cpp delete mode 100644 tests/qunitconvertortests.cpp delete mode 100644 tests/qunitconvertortests.h 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..00a661e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,13 @@ cmake_minimum_required(VERSION 3.19) project(QUnitConversion) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) -add_subdirectory(QUnitConversion) +add_subdirectory(src) option(QUNITCONV_BUILD_TESTS "Build QUnitConversion unttests" On) if (QUNITCONV_BUILD_TESTS) + enable_testing() add_subdirectory(tests) endif() diff --git a/QUnitConversion/qunitconversionfamily.cpp b/QUnitConversion/qunitconversionfamily.cpp deleted file mode 100644 index 8bf24ee..0000000 --- a/QUnitConversion/qunitconversionfamily.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "qunitconversionfamily.h" - -QUnitConversionFamily::QUnitConversionFamily(const QString &familyName, const QString &baseUnit) -{ - m_family = familyName; - m_baseUnit = baseUnit; -} - -void QUnitConversionFamily::addConversionRule(const QUnitConversionRule &rule) -{ - if (m_rules.isEmpty()) - { - m_family = rule.family(); - m_baseUnit = rule.baseUnit(); - } - else - { - if (m_family != rule.family() || m_baseUnit != rule.baseUnit()) - throw std::invalid_argument("Incorrect rule added to family"); // Don Corleone will be unhappy - } - m_rules.insert(rule.unit(), rule); -} - -QLinearFunction QUnitConversionFamily::convert(const QString &in, const QString &out) const -{ - if (m_rules.isEmpty()) - 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(); - - // 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 (!inToBase.isValid() || !baseToOut.isValid()) // one of the conversions is not present - return {}; - return QLinearFunction::combined(inToBase, baseToOut); -} - -double QUnitConversionFamily::convert(double value, const QString &in, const QString &out) const -{ - QLinearFunction function = convert(in, out); - if (function.isValid()) - return function.y(value); - return NAN; -} diff --git a/QUnitConversion/qunitconversionfamily.h b/QUnitConversion/qunitconversionfamily.h deleted file mode 100644 index 68bd392..0000000 --- a/QUnitConversion/qunitconversionfamily.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef QUNITCONVERSIONFAMILY_H -#define QUNITCONVERSIONFAMILY_H - -#include - -#include -#include - -#include "qunitconversionrule.h" - -/** - * @brief The QUnitConversionFamily class is an internal class that provides - * a conversion by holding all of the conversion rules for a single family - */ -class QUnitConversionFamily -{ -public: - QUnitConversionFamily() = default; - QUnitConversionFamily(const QString & familyName, const QString & baseUnit); - - /** - * @brief Adds a conversion rule to convertor - * @param rule rule to add - * @details - */ - void addConversionRule(const QUnitConversionRule & rule); - - /** - * @brief Converts from in unit to out unit - * @param in unit to convert from - * @param out unit to convert to - * @return QLinearFunction object containing conversion from in to out unit - */ - QLinearFunction convert(const QString & in, const QString & out) const; - - /** - * @brief Converts a given value from in unit to out unit - * @param value to convert - * @param in unit to convert from - * @param out unit to convert to - * @return value converted to - */ - double convert(double value, const QString & in, const QString & out) const; - -protected: - QMap m_rules; ///< Key is a unit, it's assumed that all rules have the same base unit - QString m_baseUnit; ///< Base unit for this family - QString m_family; ///< Family name -}; - -#endif // QUNITCONVERSIONFAMILY_H diff --git a/QUnitConversion/qunitconversionrule.cpp b/QUnitConversion/qunitconversionrule.cpp deleted file mode 100644 index 449f81e..0000000 --- a/QUnitConversion/qunitconversionrule.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "qunitconversionrule.h" - -QUnitConversionRule::QUnitConversionRule(const QString &family, const QString &baseUnit, const QString &unit, const QLinearFunction &convertFunction) -{ - m_family = family; - m_baseUnit = baseUnit; - m_unit = unit; - m_convertFunction = convertFunction; -} - -QUnitConversionRule::QUnitConversionRule(const QString &family, const QString &baseUnit, const QString &unit, double k, double b) -{ - m_family = family; - m_baseUnit = baseUnit; - m_unit = unit; - m_convertFunction.setK(k); - m_convertFunction.setB(b); -} - -QString QUnitConversionRule::family() const -{ - return m_family; -} - -void QUnitConversionRule::setFamily(const QString &family) -{ - m_family = family; -} - -QString QUnitConversionRule::baseUnit() const -{ - return m_baseUnit; -} - -void QUnitConversionRule::setBaseUnit(const QString &baseUnit) -{ - m_baseUnit = baseUnit; -} - -QString QUnitConversionRule::unit() const -{ - return m_unit; -} - -void QUnitConversionRule::setUnit(const QString &unit) -{ - m_unit = unit; -} - -QLinearFunction QUnitConversionRule::convertFunction() const -{ - return m_convertFunction; -} - -void QUnitConversionRule::setConvertFunction(const QLinearFunction &function) -{ - m_convertFunction = function; -} diff --git a/QUnitConversion/qunitconversionrule.h b/QUnitConversion/qunitconversionrule.h deleted file mode 100644 index 546594f..0000000 --- a/QUnitConversion/qunitconversionrule.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef QUNITCONVERSIONRULE_H -#define QUNITCONVERSIONRULE_H - -#include - -#include "qlinearfunction.h" - -/** - * @brief The QUnitConversionRule class represents a unit conversion rule from - * base unit to second unit within the specified family. Note that in QUnitConvertor - * 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 - */ -class QUnitConversionRule -{ -public: - QUnitConversionRule() = default; - - QUnitConversionRule(const QString & family, - const QString & baseUnit, - const QString & unit, - const QLinearFunction & convertFunction); - - QUnitConversionRule(const QString & family, - const QString & baseUnit, - const QString & unit, - double k, double b); - - QString family() const; - void setFamily(const QString &family); - - QString baseUnit() const; - void setBaseUnit(const QString &baseUnit); - - QString unit() const; - void setUnit(const QString &unit); - - QLinearFunction convertFunction() const; - void setConvertFunction(const QLinearFunction &function); - -protected: - QString m_family; ///< Family name for this pair of units, like length, speed - QString m_baseUnit; ///< Base unit for this family - QString m_unit; ///< Unit to convert to - QLinearFunction m_convertFunction; ///< Linear function to perform conversion from base unit to unit -}; - -#endif // QUNITCONVERSIONRULE_H diff --git a/QUnitConversion/qunitconvertor.cpp b/QUnitConversion/qunitconvertor.cpp deleted file mode 100644 index 72ed975..0000000 --- a/QUnitConversion/qunitconvertor.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include "qunitconvertor.h" - -bool QUnitConvertor::canConvert(const QString &in, const QString &out) const -{ - return convert(in, out).isValid(); -} - -QLinearFunction QUnitConvertor::convert(const QString &in, const QString &out) const -{ - if (in == out) - return {1, 0}; - QString 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; - QString inFamily = m_familiesByUnit.value(actualIn); - QString outFamily = m_familiesByUnit.value(actualOut); - if (inFamily.isEmpty() || inFamily != outFamily) - return {}; - return m_families[inFamily].convert(actualIn, actualOut); -} - -double QUnitConvertor::convert(double value, const QString &in, const QString &out, double defaultValue) const -{ - QLinearFunction function = convert(in, out); - if (!function.isValid()) - return defaultValue; - return function.y(value); -} - -void QUnitConvertor::addConversionRule(const QUnitConversionRule &rule) -{ - if (m_baseUnitsByFamilies.contains(rule.family()) && m_baseUnitsByFamilies[rule.family()] != 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()) - throw std::invalid_argument("Incorrect rule added: incorrect base unit family"); - if (m_familiesByUnit.contains(rule.unit()) && m_familiesByUnit[rule.unit()] != rule.family()) - throw std::invalid_argument("Incorrect rule added: incorrect unit family"); - if (!m_families.contains(rule.family())) - { - 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()); - } - else - { - m_families[rule.family()].addConversionRule(rule); - } - m_familiesByUnit.insert(rule.unit(), rule.family()); -} - -void QUnitConvertor::clear() -{ - m_families.clear(); - m_familiesByUnit.clear(); - m_baseUnitsByFamilies.clear(); -} - -QStringList QUnitConvertor::families() const -{ - return m_families.keys(); -} - -QString QUnitConvertor::family(const QString &unit) const -{ - QString actualUnit; - if (m_aliases.contains(unit)) - actualUnit = m_aliases.name(unit); - else - actualUnit = unit; - return m_familiesByUnit.value(actualUnit); -} - -QStringList QUnitConvertor::conversions(const QString &unit) const -{ - return units(family(unit)); -} - -QStringList QUnitConvertor::units(const QString &family) const -{ - return m_familiesByUnit.keys(family); -} - -void QUnitConvertor::setAliases(QAliasDictionary aliases) -{ - m_aliases = std::move(aliases); -} - -void QUnitConvertor::addAlias(QString name, QString alias) -{ - m_aliases.addAlias(name, alias); -} - -void QUnitConvertor::clearAliases() -{ - m_aliases.clear(); -} - -QString QUnitConvertor::unitName(const QString &alias) const -{ - return m_aliases.name(alias); -} diff --git a/QUnitConversion/qunitconvertor.h b/QUnitConversion/qunitconvertor.h deleted file mode 100644 index 547d462..0000000 --- a/QUnitConversion/qunitconvertor.h +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef QUNITCONVERTOR_H -#define QUNITCONVERTOR_H - -#include - -#include - -#include "qlinearfunction.h" -#include "qunitconversionfamily.h" -#include "qaliasdictionary.h" - -/** - * @brief The QUnitConvertor class provides tool for converting units stored - * 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. - */ -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 - * @param out unit to convert to - * @return true if conversion is possible, false otherwise - */ - bool canConvert(const QString & in, const QString & out) const; - - /** - * @brief Converts from in unit to out unit - * @param in unit to convert from - * @param out unit to convert to - * @return QLinearFunction object containing conversion from in to out unit - */ - QLinearFunction convert(const QString & in, const QString & out) const; - - /** - * @brief Converts a given value from in unit to out unit - * @param value to convert - * @param in unit to convert from - * @param out unit to convert to - * @param defaultValue value to return if conversion fails - * @return value converted to out unit - * @details Supports aliases for unit names, see QAliasDictionary - */ - double convert(double value, const QString & in, const QString & out, double defaultValue = NAN) const; - - /** - * @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 - * 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); - - /** - * @brief Clears unit convertor removing all unit conversion rules - */ - void clear(); - - /** - * @brief Method provides access to a list of families of units in this convertor - * @return QStringList containing a list of unit families - */ - QStringList families() const; - - /** - * @brief Gets a family for a given unit - * @param unit unit to return a family - * @return a family name or an empty string if unit is unknown to convertor - */ - QString family(const QString & unit) const; - - /** - * @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 QStringList 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 - */ - QStringList conversions(const QString & unit) const; - - /** - * @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 - * any unit of this list to any other - * @param family family to return unit list - * @return QStringList containing a list of units known by this unit convertor - */ - QStringList units(const QString &family) const; - - /** - * @brief adds aliases from the object QAliasDictionary - */ - void setAliases(QAliasDictionary aliases); - - /** - * @brief adds one alias - */ - void addAlias(QString name, QString alias); - - - /** - * @brief Removes all alias rules - */ - void clearAliases(); - - /** - * @brief Gets unit name by alias using internal alias dictionary - * @param alias unit alias to get unit name - * @return unit name or an empty string if a specified alias is not found - */ - QString unitName(const QString & alias) const; - -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 - QAliasDictionary m_aliases; -}; - -#endif // QUNITCONVERTOR_H diff --git a/QUnitConversion/CMakeLists.txt b/src/CMakeLists.txt similarity index 58% rename from QUnitConversion/CMakeLists.txt rename to src/CMakeLists.txt index 26086e3..d59c879 100644 --- a/QUnitConversion/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,16 +4,11 @@ include_guard(GLOBAL) project(QUnitConversion LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED On) -set(CMAKE_AUTOMOC On) - set(SOURCES qlinearfunction.cpp - qunitconversionfamily.cpp - qunitconversionrule.cpp - qunitconvertor.cpp ) set(HEADERS @@ -24,9 +19,6 @@ set(HEADERS 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} @@ -36,6 +28,3 @@ target_include_directories(QUnitConversion PUBLIC . ) -target_link_libraries(QUnitConversion PUBLIC - Qt::Core -) 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/QUnitConversion/qlinearfunction.cpp b/src/qlinearfunction.cpp similarity index 81% rename from QUnitConversion/qlinearfunction.cpp rename to src/qlinearfunction.cpp index 4034adc..aa3424d 100644 --- a/QUnitConversion/qlinearfunction.cpp +++ b/src/qlinearfunction.cpp @@ -1,25 +1,26 @@ #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 { - return !qFuzzyIsNull(m_k); + return m_k != 0; } -QLinearFunction QLinearFunction::inversed() const +QLinearFunction QLinearFunction::inverted() const { - if (!isValid()) return {}; + if (!isValid()) + return {}; return {1. / m_k, -m_b / m_k}; } diff --git a/QUnitConversion/qlinearfunction.h b/src/qlinearfunction.h similarity index 94% rename from QUnitConversion/qlinearfunction.h rename to src/qlinearfunction.h index dc588e9..861f466 100644 --- a/QUnitConversion/qlinearfunction.h +++ b/src/qlinearfunction.h @@ -1,8 +1,6 @@ #ifndef QLINEARFUNCTION_H #define QLINEARFUNCTION_H -#include - /** * @brief The QLinearFunction class describes a linear function * with a format of Y = k*X + b @@ -31,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/qunitconversionfamily.h b/src/qunitconversionfamily.h new file mode 100644 index 0000000..603f6da --- /dev/null +++ b/src/qunitconversionfamily.h @@ -0,0 +1,95 @@ +#ifndef QUNITCONVERSIONFAMILY_H +#define QUNITCONVERSIONFAMILY_H + +#include +#include +#include + +#include "qunitconversionrule.h" + +/** + * @brief The QUnitConversionFamily class is an internal class that provides + * a conversion by holding all of the conversion rules for a single family + */ +template +class QUnitConversionFamily +{ +public: + QUnitConversionFamily() = default; + + 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 + */ + void addConversionRule(const QUnitConversionRule & rule) + { + if (m_rules.empty()) + { + m_family = rule.family(); + m_baseUnit = rule.baseUnit(); + } + else + { + if (m_family != rule.family() || m_baseUnit != rule.baseUnit()) + throw std::invalid_argument("Incorrect rule added to family"); // Don Corleone will be unhappy + } + m_rules.insert_or_assign(rule.unit(), rule); + } + + /** + * @brief Converts from in unit to out unit + * @param in unit to convert from + * @param out unit to convert to + * @return QLinearFunction object containing conversion from in to out unit + */ + QLinearFunction convert(const String & in, const String & out) const + { + if (m_rules.empty()) + return {}; + + 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 + 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); + } + + /** + * @brief Converts a given value from in unit to out unit + * @param value to convert + * @param in unit to convert from + * @param out unit to convert to + * @return value converted to + */ + double convert(double value, const String & in, const String & out) const + { + QLinearFunction function = convert(in, out); + if (function.isValid()) + return function.y(value); + return NAN; + } + +protected: + 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/src/qunitconversionrule.h b/src/qunitconversionrule.h new file mode 100644 index 0000000..44f3532 --- /dev/null +++ b/src/qunitconversionrule.h @@ -0,0 +1,86 @@ +#ifndef QUNITCONVERSIONRULE_H +#define QUNITCONVERSIONRULE_H + +#include "qlinearfunction.h" + +/** + * @brief The QUnitConversionRule class represents a unit conversion rule from + * base unit to second unit within the specified family. Note that in QUnitConvertor + * 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 +class QUnitConversionRule +{ +public: + QUnitConversionRule(const String& family, + const String& baseUnit, + const String& unit, + const QLinearFunction & convertFunction) + { + m_family = family; + m_baseUnit = baseUnit; + m_unit = unit; + m_convertFunction = convertFunction; + } + + QUnitConversionRule(const String & family, + const String & baseUnit, + const String & unit, + double k, double b) + { + m_family = family; + m_baseUnit = baseUnit; + m_unit = unit; + m_convertFunction.setK(k); + m_convertFunction.setB(b); + } + + String family() const + { + return m_family; + } + + void setFamily(const String &family) + { + m_family = family; + } + + String baseUnit() const + { + return m_baseUnit; + } + + void setBaseUnit(const String &baseUnit) + { + m_baseUnit = baseUnit; + } + + String unit() const + { + return m_unit; + } + + void setUnit(const String &unit) + { + m_unit = unit; + } + + QLinearFunction convertFunction() const + { + return m_convertFunction; + } + + void setConvertFunction(const QLinearFunction &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 + QLinearFunction m_convertFunction; ///< Linear function to perform conversion from base unit to unit +}; + +#endif // QUNITCONVERSIONRULE_H diff --git a/src/qunitconvertor.h b/src/qunitconvertor.h new file mode 100644 index 0000000..f3d1767 --- /dev/null +++ b/src/qunitconvertor.h @@ -0,0 +1,228 @@ +#ifndef QUNITCONVERTOR_H +#define QUNITCONVERTOR_H + +#include +#include +#include +#include + +#include "qlinearfunction.h" +#include "qunitconversionfamily.h" +#include "qaliasdictionary.h" + +/** + * @brief The QUnitConvertor class provides tool for converting units stored + * 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 +class QUnitConvertor +{ +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 + * @param out unit to convert to + * @return true if conversion is possible, false otherwise + */ + bool canConvert(const String & in, const String & out) const + { + return convert(in, out).isValid(); + } + + /** + * @brief Converts from in unit to out unit + * @param in unit to convert from + * @param out unit to convert to + * @return QLinearFunction object containing conversion from in to out unit + */ + QLinearFunction convert(const String & in, const String & out) const + { + if (in == out) + return {1, 0}; + 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 {}; + if (itIn->second != itOut->second) + return {}; + return m_families.at(itIn->second).convert(actualIn, actualOut); + } + + /** + * @brief Converts a given value from in unit to out unit + * @param value to convert + * @param in unit to convert from + * @param out unit to convert to + * @param defaultValue value to return if conversion fails + * @return value converted to out unit + * @details Supports aliases for unit names, see QAliasDictionary + */ + double convert(double value, const String & in, const String & out, double defaultValue = NAN) const + { + QLinearFunction function = convert(in, out); + if (!function.isValid()) + return defaultValue; + return function.y(value); + } + + /** + * @brief Adds a conversion rule to convertor + * @param rule rule to add + * @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) + { + 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"); + 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"); + 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.find(rule.family()) == m_families.end()) + { + QUnitConversionFamily family; + family.addConversionRule(rule); + 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[rule.unit()] = rule.family(); + } + + /** + * @brief Clears unit convertor removing all unit conversion rules + */ + void clear() + { + m_families.clear(); + m_familiesByUnit.clear(); + m_baseUnitsByFamilies.clear(); + } + + /** + * @brief Method provides access to a list of families of units in this convertor + * @return vector containing a list of unit families + */ + std::vector families() const + { + std::vector result; + result.reserve(m_families.size()); + for (const auto & [name, _] : m_families) + result.push_back(name); + return result; + } + + /** + * @brief Gets a family for a given unit + * @param unit unit to return a family + * @return a family name or an empty string if unit is unknown to convertor + */ + String family(const String & unit) const + { + 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 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 + { + return units(family(unit)); + } + + /** + * @brief Method provides access to a list of units in this convertor within a given + * 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 vector containing a list of units known by this unit convertor within the family + */ + std::vector units(const String & family) const + { + std::vector result; + for (const auto & [unit, unitFamily] : m_familiesByUnit) + if (unitFamily == family) + result.push_back(unit); + return result; + } + + /** + * @brief Sets aliases from a QAliasDictionary object + */ + void setAliases(QAliasDictionary aliases) + { + m_aliases = std::move(aliases); + } + + /** + * @brief Adds one alias + */ + void addAlias(String name, String alias) + { + m_aliases.addAlias(std::move(name), std::move(alias)); + } + + /** + * @brief Removes all alias rules + */ + void clearAliases() + { + m_aliases.clear(); + } + + /** + * @brief Gets unit name by alias using internal alias dictionary + * @param alias unit alias to get unit name + * @return unit name or an empty string if a specified alias is not found + */ + String unitName(const String & alias) const + { + 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: + 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; +}; + +#endif // QUNITCONVERTOR_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 107401c..f97a34c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,35 +4,42 @@ include_guard(GLOBAL) project(QUnitConversionTests LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED On) -set(CMAKE_AUTOMOC On) -set(CMAKE_AUTORCC On) +include(FetchContent) -find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Test REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Test REQUIRED) +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) set(SOURCES - main.cpp - qaliasdictionarytests.cpp - qlinearfunctiontests.cpp - qunitconvertortests.cpp - ) - -set(HEADERS - qaliasdictionarytests.h - qlinearfunctiontests.h - qunitconvertortests.h - ) + qaliasdictionary_tests.cpp + qlinearfunction_tests.cpp + qunitconvertor_tests.cpp +) add_executable(QUnitConversionTests - ${SOURCES} - ${HEADERS} - ) + ${SOURCES} +) target_link_libraries(QUnitConversionTests - Qt::Core - Qt::Test - QUnitConversion - ) + 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() + +include(GoogleTest) +gtest_discover_tests(QUnitConversionTests) diff --git a/tests/C_to_F.xlsx b/tests/C_to_F.xlsx deleted file mode 100644 index e13bcb05a4630ef16c44c9f2b988b70cb00aeda1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10651 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 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/qaliasdictionary_tests.cpp b/tests/qaliasdictionary_tests.cpp new file mode 100644 index 0000000..b6fb999 --- /dev/null +++ b/tests/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/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/qlinearfunction_tests.cpp b/tests/qlinearfunction_tests.cpp new file mode 100644 index 0000000..62729c5 --- /dev/null +++ b/tests/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/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/qunitconvertor_tests.cpp b/tests/qunitconvertor_tests.cpp new file mode 100644 index 0000000..004ad75 --- /dev/null +++ b/tests/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/qunitconvertortests.cpp b/tests/qunitconvertortests.cpp deleted file mode 100644 index cd5dea0..0000000 --- a/tests/qunitconvertortests.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "qunitconvertortests.h" -#include "qaliasdictionary.h" - -QUnitConvertorTests::QUnitConvertorTests(QObject *parent) : QObject(parent) -{ - -} - -void QUnitConvertorTests::addRuleTest() -{ - QUnitConvertor convertor; - convertor.addConversionRule(QUnitConversionRule("length", "m", "km", 0.001, 0)); - QVERIFY(convertor.m_families.contains("length")); - QVERIFY(convertor.m_baseUnitsByFamilies.contains("length")); - QVERIFY(convertor.m_familiesByUnit.contains("m")); - QVERIFY(convertor.m_familiesByUnit.contains("km")); - QVERIFY(convertor.m_familiesByUnit.size() == 2); - QVERIFY(convertor.m_families.size() == 1); - QVERIFY(convertor.m_baseUnitsByFamilies.size() == 1); - - convertor.addConversionRule(QUnitConversionRule("length", "m", "cm", 100, 0)); - QVERIFY(convertor.m_familiesByUnit.contains("m")); - QVERIFY(convertor.m_familiesByUnit.contains("km")); - QVERIFY(convertor.m_familiesByUnit.contains("cm")); - QVERIFY(convertor.m_familiesByUnit.size() == 3); - QVERIFY(convertor.m_families.size() == 1); - QVERIFY(convertor.m_baseUnitsByFamilies.size() == 1); - - convertor.addConversionRule(QUnitConversionRule("length", "m", "mm", 1000, 0)); - QVERIFY(convertor.m_familiesByUnit.contains("m")); - QVERIFY(convertor.m_familiesByUnit.contains("km")); - QVERIFY(convertor.m_familiesByUnit.contains("cm")); - QVERIFY(convertor.m_familiesByUnit.contains("mm")); - QVERIFY(convertor.m_familiesByUnit.size() == 4); - QVERIFY(convertor.m_families.size() == 1); - QVERIFY(convertor.m_baseUnitsByFamilies.size() == 1); - - // passing existing family with a differnt base unit - QVERIFY_THROWS_EXCEPTION(std::invalid_argument, convertor.addConversionRule(QUnitConversionRule("length", "km", "m", 1000, 0))); - - // passing a different family with an existing base unit - QVERIFY_THROWS_EXCEPTION(std::invalid_argument, convertor.addConversionRule(QUnitConversionRule("notlength", "m", "km", 0.001, 0))); - - // passing the same conversion once again should work - convertor.addConversionRule(QUnitConversionRule("length", "m", "mm", 1000, 0)); - - // let's make sure that none of containers did change - QVERIFY(convertor.m_familiesByUnit.size() == 4); - QVERIFY(convertor.m_families.size() == 1); - QVERIFY(convertor.m_baseUnitsByFamilies.size() == 1); - - // 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)); - QVERIFY(convertor.m_families.contains("length")); - QVERIFY(convertor.m_families.contains("time")); - QVERIFY(convertor.m_families.size() == 2); - QVERIFY(convertor.m_familiesByUnit.contains("m")); - QVERIFY(convertor.m_familiesByUnit.contains("km")); - QVERIFY(convertor.m_familiesByUnit.contains("cm")); - QVERIFY(convertor.m_familiesByUnit.contains("mm")); - QVERIFY(convertor.m_familiesByUnit.contains("s")); - QVERIFY(convertor.m_familiesByUnit.contains("min")); - QVERIFY(convertor.m_baseUnitsByFamilies.contains("length")); - QVERIFY(convertor.m_baseUnitsByFamilies.contains("time")); - QVERIFY(convertor.m_baseUnitsByFamilies["length"] == "m"); - QVERIFY(convertor.m_baseUnitsByFamilies["time"] == "s"); - - QStringList families = convertor.families(); - QVERIFY(families.contains("length")); - QVERIFY(families.contains("time")); - - QStringList units = convertor.units("length"); - QVERIFY(units.size() == 4); - QVERIFY(units.contains("m")); - QVERIFY(units.contains("km")); - QVERIFY(units.contains("cm")); - QVERIFY(units.contains("mm")); - - QVERIFY(qFuzzyCompare(convertor.convert(0, "m", "km"), 0)); - QVERIFY(qFuzzyCompare(convertor.convert(50, "m", "km"), 0.05)); - QVERIFY(qFuzzyCompare(convertor.convert(50, "km", "m"), 50000)); - QVERIFY(qFuzzyCompare(convertor.convert(500, "cm", "m"), 5)); - QVERIFY(qFuzzyCompare(convertor.convert(500, "cm", "km"), 0.005)); - QVERIFY(qFuzzyCompare(convertor.convert(500, "m", "m"), 500)); - - QStringList conversions = convertor.conversions("m"); - QVERIFY(conversions.contains("m")); - QVERIFY(conversions.contains("km")); - QVERIFY(conversions.contains("cm")); - QVERIFY(conversions.contains("mm")); - QVERIFY(conversions.size() == 4); - - conversions = convertor.conversions("mm"); - QVERIFY(conversions.contains("m")); - QVERIFY(conversions.contains("km")); - QVERIFY(conversions.contains("cm")); - QVERIFY(conversions.contains("mm")); - QVERIFY(conversions.size() == 4); - - conversions = convertor.conversions("mmmm"); - QVERIFY(conversions.isEmpty()); - - QVERIFY(convertor.family("m") == "length"); - QVERIFY(convertor.family("km") == "length"); - QVERIFY(convertor.family("mmmmm").isEmpty()); - -} - -void QUnitConvertorTests::setAliasesTest() -{ - QUnitConvertor convertor; - convertor.addConversionRule(QUnitConversionRule("length", "m", "km", 0.001, 0)); - QVERIFY(convertor.canConvert("m", "km")); - QVERIFY(convertor.canConvert("km", "m")); - QVERIFY(!convertor.canConvert("km", "meter")); - - QAliasDictionary aliases; - aliases.addAlias("m", "m"); - aliases.addAlias("m", "meter"); - aliases.addAlias("m", "meters"); - - convertor.setAliases(aliases); - - QVERIFY(convertor.canConvert("km", "meter")); - QVERIFY(convertor.canConvert("km", "meters")); - -} - -void QUnitConvertorTests::addAliasTest() -{ - QUnitConvertor convertor; - convertor.addConversionRule(QUnitConversionRule("length", "m", "km", 0.001, 0)); - - QAliasDictionary aliases; - aliases.addAlias("m", "m"); - aliases.addAlias("m", "meter"); - - convertor.setAliases(aliases); - - QVERIFY(convertor.canConvert("km", "meter")); - QVERIFY(!convertor.canConvert("km", "meters")); - - convertor.addAlias("m", "meters"); - - QVERIFY(convertor.canConvert("km", "meters")); - -} - diff --git a/tests/qunitconvertortests.h b/tests/qunitconvertortests.h deleted file mode 100644 index 083f628..0000000 --- a/tests/qunitconvertortests.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef QUNITCONVERTORTESTS_H -#define QUNITCONVERTORTESTS_H - -#include -#include - -#include "qunitconvertor.h" - -class QUnitConvertorTests : public QObject -{ - Q_OBJECT -public: - explicit QUnitConvertorTests(QObject *parent = nullptr); - -private slots: - void addRuleTest(); - void setAliasesTest(); - void addAliasTest(); -}; - -#endif // QUNITCONVERTORTESTS_H From 836747161eabb2c19cddea35c4bd95f42b742399 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Mon, 23 Mar 2026 20:18:31 +0200 Subject: [PATCH 2/7] I like to move it move it --- CMakeLists.txt | 61 +++++++++++++++++++++-- src/CMakeLists.txt | 30 ----------- {tests => src}/qaliasdictionary_tests.cpp | 0 {tests => src}/qlinearfunction_tests.cpp | 0 {tests => src}/qunitconvertor_tests.cpp | 0 tests/CMakeLists.txt | 45 ----------------- 6 files changed, 56 insertions(+), 80 deletions(-) delete mode 100644 src/CMakeLists.txt rename {tests => src}/qaliasdictionary_tests.cpp (100%) rename {tests => src}/qlinearfunction_tests.cpp (100%) rename {tests => src}/qunitconvertor_tests.cpp (100%) delete mode 100644 tests/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 00a661e..e1a773e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,64 @@ cmake_minimum_required(VERSION 3.19) -project(QUnitConversion) +project(QUnitConversion LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED On) -add_subdirectory(src) +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 +) + +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() -if (QUNITCONV_BUILD_TESTS) enable_testing() - add_subdirectory(tests) + include(GoogleTest) + gtest_discover_tests(QUnitConversionTests) endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index d59c879..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -cmake_minimum_required(VERSION 3.19) - -include_guard(GLOBAL) - -project(QUnitConversion LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED On) - -set(SOURCES - qlinearfunction.cpp -) - -set(HEADERS - qaliasdictionary.h - qlinearfunction.h - qunitconversionfamily.h - qunitconversionrule.h - qunitconvertor.h -) - -add_library(QUnitConversion STATIC - ${SOURCES} - ${HEADERS} -) - -target_include_directories(QUnitConversion PUBLIC - . -) - diff --git a/tests/qaliasdictionary_tests.cpp b/src/qaliasdictionary_tests.cpp similarity index 100% rename from tests/qaliasdictionary_tests.cpp rename to src/qaliasdictionary_tests.cpp diff --git a/tests/qlinearfunction_tests.cpp b/src/qlinearfunction_tests.cpp similarity index 100% rename from tests/qlinearfunction_tests.cpp rename to src/qlinearfunction_tests.cpp diff --git a/tests/qunitconvertor_tests.cpp b/src/qunitconvertor_tests.cpp similarity index 100% rename from tests/qunitconvertor_tests.cpp rename to src/qunitconvertor_tests.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt deleted file mode 100644 index f97a34c..0000000 --- a/tests/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -cmake_minimum_required(VERSION 3.19) - -include_guard(GLOBAL) - -project(QUnitConversionTests LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED On) - -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) - -set(SOURCES - qaliasdictionary_tests.cpp - qlinearfunction_tests.cpp - qunitconvertor_tests.cpp -) - -add_executable(QUnitConversionTests - ${SOURCES} -) - -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() - -include(GoogleTest) -gtest_discover_tests(QUnitConversionTests) From 5fa81a58eabcd53d3ba1fa689ae0cf55c9741c8d Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Mon, 23 Mar 2026 20:39:02 +0200 Subject: [PATCH 3/7] Rework CI --- .github/workflows/build.yml | 154 +++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 71 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e80713b..e518617 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,84 +1,96 @@ 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 + is_msvc: false + + - name: "macOS Clang Qt 5.15.2" + os: macos-13 + qt-version: "5.15.2" + qt-arch: clang_64 + is_msvc: false + + - name: "Windows MSVC Qt 5.15.2" + os: windows-latest + qt-version: "5.15.2" + qt-arch: win64_msvc2019_64 + is_msvc: true + + - name: "Windows MinGW Qt 5.15.2" + os: windows-latest + qt-version: "5.15.2" + qt-arch: win64_mingw81 + is_msvc: false + + # Qt 6.9.3 + - name: "Ubuntu GCC Qt 6.9.3" + os: ubuntu-latest + qt-version: "6.9.3" + qt-arch: linux_gcc_64 + is_msvc: false + + - name: "macOS Clang Qt 6.9.3" + os: macos-latest + qt-version: "6.9.3" + qt-arch: clang_64 + is_msvc: false + + - name: "Windows MSVC Qt 6.9.3" + os: windows-latest + qt-version: "6.9.3" + qt-arch: win64_msvc2022_64 + is_msvc: true + + - name: "Windows MinGW Qt 6.9.3" + os: windows-latest + qt-version: "6.9.3" + qt-arch: win64_mingw + is_msvc: false steps: - - uses: actions/checkout@v3 - - - name: Set up Qt environment - uses: jurplel/install-qt-action@v3 - with: - cache: true - version: ${{ matrix.qt-version }} - arch: ${{ matrix.config.arch }} - - - name: Setup MSBuild - uses: ilammy/msvc-dev-cmd@v1 - if: runner.os == 'Windows' && matrix.config.is_msvc == true - - - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v1.9 - with: - cmake-version: ${{env.CMAKE_VERSION}} - - - name: Setup Ninja - uses: ashutoshvarma/setup-ninja@v1.1 - with: - version: ${{env.NINJA_VERSION}} - - - 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 - - - name: Run tests - run: | - cd ../build/tests - ./QUnitConversionTests + - uses: actions/checkout@v4 + + - uses: lukka/get-cmake@latest + with: + cmakeVersion: ${{ env.CMAKE_VERSION }} + ninjaVersion: ${{ env.NINJA_VERSION }} + + - name: Install Qt + uses: jurplel/install-qt-action@v4 + with: + cache: true + version: ${{ matrix.qt-version }} + arch: ${{ matrix.qt-arch }} + + - name: Setup MSVC + uses: ilammy/msvc-dev-cmd@v1 + if: matrix.is_msvc == true + + - uses: ashutoshvarma/action-cmake-build@master + with: + build-dir: ${{ github.workspace }}/build + build-type: Release + configure-options: >- + -G Ninja + -DQUNITCONV_QT_TESTS=ON + + - run: ctest --test-dir ${{ github.workspace }}/build --output-on-failure --verbose From d3c00508612c441fbed6e7fbe763b3763010c52d Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Mon, 23 Mar 2026 20:56:56 +0200 Subject: [PATCH 4/7] Update README.md --- README.md | 41 ++++++++--------------------------------- 1 file changed, 8 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index a18d91c..3906156 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,11 @@ ## Overview -`QUnitConversion` is a simple lightweight library providing tools for runtime unit conversion built on top of Qt5 Framework. +`QUnitConversion` is a simple lightweight library providing tools for runtime unit conversion built on top of Qt Framework. `QUnitConversion` stores units as strings grouped by "family" (for example length or temperature). Each family has its own base unit, conversion inside a family is performed by converting through base unit -providing conversion from any unit to any other unit in a family. Conversion rules can be added dynamically -and/or loaded from JSON-formatted string so you can add your own conversions if needed. An example of -an input JSON file is provided in `test/testdata/conversion_rules.json`. +providing conversion from any unit to any other unit in a family. Note that each unit should have a unique name, as long as conversion is unit name-based. @@ -23,7 +21,7 @@ Documentation is available [here](https://beardedbeaver.github.io/QUnitConversio ### Basic usage: ```cpp -QUnitConvertor convertor; +QUnitConvertor convertor; // fill the convertor instance with rules convertor.addConversionRule(QUnitConversionRule("length", "m", "km", 0.001, 0)); @@ -33,7 +31,7 @@ convertor.addConversionRule(QUnitConversionRule("length", "m", "cm", 100, 0)); double km = convertor.convert(50, "km", "m"); // returns value of a 50 km converted to meters // or get a linear function that holds conversion from one unit to another -// to apply this convertion to many numbers without finding a conversion each time +// to apply this conversion to many numbers without finding a conversion each time QLinearFunction convertFunction = convertor.convert("m", "km"); std::vector meters; // meters is filled here... @@ -45,37 +43,14 @@ for (double m: meters) ### Aliases: `QUnitConversion` supports aliases for units with possible conversion on the fly, so you km/h, kmph and kmh -will be converted to m/s properly. Also this example illustrates loading conversions and aliases from -JSON-formatted file. +will be converted to m/s properly. -```cpp -QUnitConvertor convertor; - -// load conversion rules from JSON -QFile conversions("conversion_rules.json"); -conversions.open(QIODevice::ReadOnly); -convertor.loadFromJson(QJsonDocument::fromJson(conversions.readAll()).object()); - -// load aliases for unit names from JSON -QFile aliases("aliases.json"); -aliases.open(QIODevice::ReadOnly); -convertor.loadAliasesFromJson(QJsonDocument::fromJson(aliases.readAll()).object()); - -double km; -km = convertor.convert(50, "km", "m"); // returns value of a 50 km converted to meters -km = convertor.convert(50, "km", "meter"); // "meter" is an alias for "m" written in loaded json -km = convertor.convert(50, "km", "meters"); // and "meters" a as well +``` +// TODO: add updated alias example ``` ## License `QUnitConversion` is distributed under MIT license -## Repo contents - -| Directory | Contents | -|---------------------|--------------------------------| -| `./QUnitConversion` | Library source code. | -| `./tests` | Unittests code. | - -Copyright Dmitriy Linev 2020-2022 +Copyright Dmitriy Linev 2020-2026 From 25eab383bb6f4115c8ec99a166d6b2488112efe4 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Mon, 23 Mar 2026 21:45:36 +0200 Subject: [PATCH 5/7] Move it! --- src/qunitconversionrule.h | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/qunitconversionrule.h b/src/qunitconversionrule.h index 44f3532..9a15409 100644 --- a/src/qunitconversionrule.h +++ b/src/qunitconversionrule.h @@ -1,6 +1,8 @@ #ifndef QUNITCONVERSIONRULE_H #define QUNITCONVERSIONRULE_H +#include + #include "qlinearfunction.h" /** @@ -13,27 +15,27 @@ template class QUnitConversionRule { public: - QUnitConversionRule(const String& family, - const String& baseUnit, - const String& unit, - const QLinearFunction & convertFunction) + + 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_family = family; - m_baseUnit = baseUnit; - m_unit = unit; - m_convertFunction = convertFunction; } - QUnitConversionRule(const String & family, - const String & baseUnit, - const String & unit, + QUnitConversionRule(String family, + String baseUnit, + 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_family = family; - m_baseUnit = baseUnit; - m_unit = unit; - m_convertFunction.setK(k); - m_convertFunction.setB(b); } String family() const From dcce6b25666d75c092cd56a9265cecab3bc96665 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Mon, 23 Mar 2026 21:59:11 +0200 Subject: [PATCH 6/7] Update macos runner --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e518617..3fbc549 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: is_msvc: false - name: "macOS Clang Qt 5.15.2" - os: macos-13 + os: macos-14 qt-version: "5.15.2" qt-arch: clang_64 is_msvc: false From 48df4b033a47f2a1b85fdafbb50e69a30f259d60 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Tue, 24 Mar 2026 09:06:27 +0200 Subject: [PATCH 7/7] No mingw, no macos on 5.15 --- .github/workflows/build.yml | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3fbc549..db1bdb7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,50 +21,27 @@ jobs: os: ubuntu-latest qt-version: "5.15.2" qt-arch: gcc_64 - is_msvc: false - - - name: "macOS Clang Qt 5.15.2" - os: macos-14 - qt-version: "5.15.2" - qt-arch: clang_64 - is_msvc: false - name: "Windows MSVC Qt 5.15.2" os: windows-latest qt-version: "5.15.2" qt-arch: win64_msvc2019_64 - is_msvc: true - - - name: "Windows MinGW Qt 5.15.2" - os: windows-latest - qt-version: "5.15.2" - qt-arch: win64_mingw81 - is_msvc: false # Qt 6.9.3 - name: "Ubuntu GCC Qt 6.9.3" os: ubuntu-latest qt-version: "6.9.3" qt-arch: linux_gcc_64 - is_msvc: false - name: "macOS Clang Qt 6.9.3" os: macos-latest qt-version: "6.9.3" qt-arch: clang_64 - is_msvc: false - name: "Windows MSVC Qt 6.9.3" os: windows-latest qt-version: "6.9.3" qt-arch: win64_msvc2022_64 - is_msvc: true - - - name: "Windows MinGW Qt 6.9.3" - os: windows-latest - qt-version: "6.9.3" - qt-arch: win64_mingw - is_msvc: false steps: - uses: actions/checkout@v4 @@ -83,7 +60,7 @@ jobs: - name: Setup MSVC uses: ilammy/msvc-dev-cmd@v1 - if: matrix.is_msvc == true + if: contains(matrix.name, 'MSVC') - uses: ashutoshvarma/action-cmake-build@master with: