#include #include #include namespace { /// Path for resources const auto TESTS_RESOURCES_PATH = QFileInfo{QString{CORE_TESTS_RESOURCES_DIR}, "TestDataSeriesUtils"}.absoluteFilePath(); QString inputFilePath(const QString &inputFileName) { return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath(); } } // namespace class TestDataSeriesUtils : public QObject { Q_OBJECT private slots: /// Tests @sa DataSeriesUtils::thresholds() method void testThresholds_data(); void testThresholds(); /// Tests @sa DataSeriesUtils::fillDataHoles() method void testFillDataHoles_data(); void testFillDataHoles(); }; void TestDataSeriesUtils::testFillDataHoles_data() { QTest::addColumn >("xAxisData"); QTest::addColumn >("valuesData"); QTest::addColumn("resolution"); QTest::addColumn("fillValue"); QTest::addColumn("minBound"); QTest::addColumn("maxBound"); QTest::addColumn >( "expectedXAxisData"); // expected x-axis data after filling holes QTest::addColumn >( "expectedValuesData"); // expected values data after filling holes auto nan = std::numeric_limits::quiet_NaN(); QTest::newRow("fillDataHoles (basic case)") << std::vector{0., 1., 5., 7., 14.} << std::vector{0., 1., 2., 3., 4.} << 2. << nan << nan << nan << std::vector{0., 1., 3., 5., 7., 9., 11., 13., 14.} << std::vector{0., 1., nan, 2., 3., nan, nan, nan, 4.}; QTest::newRow("fillDataHoles (change nb components)") << std::vector{0., 1., 5., 7., 14.} << std::vector{0., 1., 2., 3., 4., 5., 6., 7., 8., 9.} << 2. << nan << nan << nan << std::vector{0., 1., 3., 5., 7., 9., 11., 13., 14.} << std::vector{0., 1., 2., 3., nan, nan, 4., 5., 6., 7., nan, nan, nan, nan, nan, nan, 8., 9.}; QTest::newRow("fillDataHoles (change resolution)") << std::vector{0., 1., 5., 7., 14.} << std::vector{0., 1., 2., 3., 4.} << 1.5 << nan << nan << nan << std::vector{0., 1., 2.5, 4., 5., 6.5, 7., 8.5, 10., 11.5, 13., 14.} << std::vector{0., 1., nan, nan, 2., nan, 3., nan, nan, nan, nan, 4.}; QTest::newRow("fillDataHoles (with no data (no changes made))") << std::vector{} << std::vector{} << 2. << nan << nan << nan << std::vector{} << std::vector{}; QTest::newRow("fillDataHoles (with no resolution (no changes made))") << std::vector{0., 1., 5., 7., 14.} << std::vector{0., 1., 2., 3., 4.} << 0. << nan << nan << nan << std::vector{0., 1., 5., 7., 14.} << std::vector{0., 1., 2., 3., 4.}; QTest::newRow("fillDataHoles (change fill value)") << std::vector{0., 1., 5., 7., 14.} << std::vector{0., 1., 2., 3., 4.} << 2. << -1. << nan << nan << std::vector{0., 1., 3., 5., 7., 9., 11., 13., 14.} << std::vector{0., 1., -1., 2., 3., -1., -1., -1., 4.}; QTest::newRow("fillDataHoles (add data holes to the beginning)") << std::vector{5., 7., 9., 11., 13.} << std::vector{0., 1., 2., 3., 4.} << 2. << nan << 0. << nan << std::vector{1., 3., 5., 7., 9., 11., 13.} << std::vector{nan, nan, 0., 1., 2., 3., 4.}; QTest::newRow("fillDataHoles (add data holes to the end)") << std::vector{5., 7., 9., 11., 13.} << std::vector{0., 1., 2., 3., 4.} << 2. << nan << nan << 21. << std::vector{5., 7., 9., 11., 13., 15., 17., 19., 21.} << std::vector{0., 1., 2., 3., 4., nan, nan, nan, nan}; QTest::newRow("fillDataHoles (invalid min/max bounds (no changes made))") << std::vector{5., 7., 9., 11., 13.} << std::vector{0., 1., 2., 3., 4.} << 2. << nan << 8. << 13. << std::vector{5., 7., 9., 11., 13.} << std::vector{0., 1., 2., 3., 4.}; } void TestDataSeriesUtils::testFillDataHoles() { QFETCH(std::vector, xAxisData); QFETCH(std::vector, valuesData); QFETCH(double, resolution); QFETCH(double, fillValue); QFETCH(double, minBound); QFETCH(double, maxBound); QFETCH(std::vector, expectedXAxisData); QFETCH(std::vector, expectedValuesData); // Executes method (xAxisData and valuesData are modified) DataSeriesUtils::fillDataHoles(xAxisData, valuesData, resolution, fillValue, minBound, maxBound); // Checks results auto equal = [](const auto &data, const auto &expectedData) { // Compares with NaN values return std::equal(data.begin(), data.end(), expectedData.begin(), expectedData.end(), [](const auto &val, const auto &expectedVal) { return (std::isnan(val) && std::isnan(expectedVal)) || val == expectedVal; }); }; QVERIFY(equal(xAxisData, expectedXAxisData)); QVERIFY(equal(valuesData, expectedValuesData)); } namespace { const auto LINE_SEP = QRegularExpression{QStringLiteral("\\s+")}; std::vector fromFile(const QString &filePath) { QFile file{filePath}; if (!file.open(QFile::ReadOnly | QIODevice::Text)) { return {}; } std::vector result{}; QTextStream stream{&file}; QString line{}; while (stream.readLineInto(&line)) { auto lineData = line.split(LINE_SEP, QString::SkipEmptyParts); for (auto data : lineData) { bool valueOk; auto value = data.toDouble(&valueOk); result.push_back(valueOk ? value : std::numeric_limits::quiet_NaN()); } } return result; } } // namespace void TestDataSeriesUtils::testThresholds_data() { QTest::addColumn >("input"); QTest::addColumn("logarithmic"); QTest::addColumn("expectedMinThreshold"); QTest::addColumn("expectedMaxThreshold"); auto nan = std::numeric_limits::quiet_NaN(); QTest::newRow("thresholds (basic case)") << std::vector{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.} << false << 1. << 10.; QTest::newRow("thresholds (with nan values)") << std::vector{nan, 2., 3., 4., 5., 6., 7., 8., 9., nan} << false << 2. << 9.; QTest::newRow("thresholds (case with low values and aberrant value)") << std::vector{1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 3., 3., 3., 3., 3., 3., 3., 3., 3., 3., 3., 3., 3., 3., 3., 100.} << false << 1. << 47.073; QTest::newRow("thresholds (empty data)") << std::vector{} << false << nan << nan; QTest::newRow("thresholds (only nan values)") << std::vector{nan, nan, nan, nan, nan} << false << nan << nan; QTest::newRow("thresholds (from file with logarithmic scale)") << fromFile(inputFilePath("TestThresholds.txt")) << true << 832.005 << 17655064.730; } void TestDataSeriesUtils::testThresholds() { QFETCH(std::vector, input); QFETCH(bool, logarithmic); QFETCH(double, expectedMinThreshold); QFETCH(double, expectedMaxThreshold); double minThreshold, maxThreshold; std::tie(minThreshold, maxThreshold) = DataSeriesUtils::thresholds(input.begin(), input.end(), logarithmic); auto compareWithNaN = [](const auto &v1, const auto &v2) { return (std::isnan(v1) && std::isnan(v2)) || std::abs(v1 - v2) < 1e-3; }; QVERIFY(compareWithNaN(minThreshold, expectedMinThreshold)); QVERIFY(compareWithNaN(maxThreshold, expectedMaxThreshold)); } QTEST_MAIN(TestDataSeriesUtils) #include "TestDataSeriesUtils.moc"