File indexing completed on 2024-05-19 15:26:47

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #pragma once
0008 #include <QtTest/QtTest>
0009 
0010 #include"math/bezier/bezier.hpp"
0011 
0012 #define COMPARE_MULTIBEZIER(actual, expected) \
0013     compare_multibez(actual, expected, #actual, #expected, __FILE__, __LINE__)
0014 
0015 using namespace glaxnimate;
0016 
0017 class BezierTestBase
0018 {
0019 public:
0020 
0021     bool compare_multibez(const math::bezier::MultiBezier& actual, const math::bezier::MultiBezier& expected,
0022                           const QString& actual_name, const QString& expected_name, const char *file, int line)
0023     {
0024         std::string actual_s = build_name(actual_name, "size()");
0025         std::string expected_s = build_name(expected_name, "size()");
0026 
0027         if ( !QTest::qCompare(actual.size(), expected.size(), actual_s.c_str(), expected_s.c_str(), file, line) )
0028         {
0029             QString warning = "\n";
0030             build_warning_multi(warning, actual_name, actual);
0031             build_warning_multi(warning, expected_name, expected);
0032             qWarning() << warning;
0033             return false;
0034         }
0035 
0036         for ( int i = 0; i < actual.size(); i++ )
0037         {
0038             QString name_suf = QString("[%1]").arg(i);
0039             if ( !compare_bez(actual.beziers()[i], expected.beziers()[i], actual_name + name_suf, expected_name + name_suf, file, line) )
0040                 return false;
0041         }
0042 
0043         return true;
0044     }
0045 
0046     using WarningCollectData = std::pair<int, std::vector<QString>>;
0047 
0048     void collect_warning(const QPointF& p, WarningCollectData& out)
0049     {
0050         QString text = QTest::toString(p);
0051         if ( text.size() > out.first )
0052             out.first = text.size();
0053         out.second.push_back(text);
0054     }
0055 
0056     void build_warning(QString& warning, const QString& name, const math::bezier::Bezier& bez)
0057     {
0058         warning += name + "\n";
0059         WarningCollectData pos = {0, {}};
0060         WarningCollectData tan_in = {0, {}};
0061         WarningCollectData tan_out = {0, {}};
0062 
0063         for ( const auto& p : bez )
0064         {
0065             collect_warning(p.pos, pos);
0066             collect_warning(p.tan_in, tan_in);
0067             collect_warning(p.tan_out, tan_out);
0068         }
0069 
0070         for ( std::size_t i = 0; i < pos.second.size(); i++ )
0071         {
0072             warning += "   ";
0073             warning += pos.second[i].leftJustified(pos.first);
0074             warning += " in: ";
0075             warning += tan_in.second[i].leftJustified(tan_in.first);
0076             warning += " out: ";
0077             warning += tan_out.second[i].leftJustified(tan_out.first);
0078             warning += "\n";
0079         }
0080     }
0081 
0082     void build_warning_multi(QString& warning, const QString& name, const math::bezier::MultiBezier& bez)
0083     {
0084         warning += name + ":\n";
0085 
0086         for ( int i = 0; i < bez.size(); i++ )
0087             build_warning(warning, name + QString("[%1]").arg(i), bez.beziers()[i]);
0088     }
0089 
0090     bool compare_bez(const math::bezier::Bezier& actual, const math::bezier::Bezier& expected,
0091                      const QString& actual_name, const QString& expected_name,
0092                      const char *file, int line)
0093     {
0094         std::string actual_s = build_name(actual_name, "size()");
0095         std::string expected_s = build_name(expected_name, "size()");
0096 
0097         bool ret = true;
0098 
0099         if ( !QTest::qCompare(actual.size(), expected.size(), actual_s.c_str(), expected_s.c_str(), file, line) )
0100         {
0101             ret = false;
0102         }
0103         else
0104         {
0105             actual_s = build_name(actual_name, "closed()");
0106             expected_s = build_name(expected_name, "closed()");
0107             if ( !QTest::qCompare(actual.closed(), expected.closed(), actual_s.c_str(), expected_s.c_str(), file, line) )
0108             {
0109                 ret = false;
0110             }
0111 
0112             for ( int i = 0; i < actual.size(); i++ )
0113             {
0114                 QString name_suf = QString("[%1]").arg(i);
0115                 if ( !compare_point(actual[i], expected[i], actual_name + name_suf, expected_name + name_suf, file, line) )
0116                 {
0117                     ret = false;
0118                     break;
0119                 }
0120             }
0121         }
0122 
0123         if ( !ret )
0124         {
0125             QString warning = "\n";
0126             build_warning(warning, actual_name, actual);
0127             build_warning(warning, expected_name, expected);
0128             qWarning() << warning;
0129         }
0130 
0131         return ret;
0132     }
0133 
0134     std::string build_name(const QString& base, const char* member)
0135     {
0136         return QString("%1.%2").arg(base, member).toStdString();
0137     }
0138 
0139     bool compare_point(const math::bezier::Point& actual, const math::bezier::Point& expected,
0140                        const QString& actual_name, const QString& expected_name,
0141                        const char *file, int line)
0142     {
0143         bool ret = true;
0144         ret = compare_qpointf(actual.pos, expected.pos, actual_name, expected_name, "pos", file, line) && ret;
0145         ret = compare_qpointf(actual.tan_in, expected.tan_in, actual_name, expected_name, "tan_in", file, line) && ret;
0146         ret = compare_qpointf(actual.tan_out, expected.tan_out, actual_name, expected_name, "tan_out", file, line) && ret;
0147         return ret;
0148     }
0149 
0150 
0151     bool compare_qpointf(const QPointF& actual, const QPointF& expected,
0152                        const QString& actual_name, const QString& expected_name,  const char* point_name,
0153                        const char *file, int line)
0154     {
0155         // reduce accuracy requirements by casting to float
0156         if ( !qFuzzyCompare(float(actual.x()), float(expected.x())) || !qFuzzyCompare(float(actual.y()), float(expected.y())) )
0157         {
0158             // actual test comparisons for output
0159 
0160             std::string actual_s = build_name(actual_name, point_name);
0161             std::string expected_s = build_name(expected_name, point_name);
0162             QTest::qCompare(actual, expected, actual_s.c_str(), expected_s.c_str(), file, line);
0163 
0164             QString suffix = QString(".") + point_name;
0165             compare_qpointf_component(actual.x(), expected.x(), actual_name+suffix, expected_name+suffix, "x()", file, line);
0166             compare_qpointf_component(actual.y(), expected.y(), actual_name+suffix, expected_name+suffix, "y()", file, line);
0167             return false;
0168         }
0169 
0170         return true;
0171     }
0172 
0173     bool compare_qpointf_component(qreal actual, qreal expected,
0174                        const QString& actual_name, const QString& expected_name,  const char* component,
0175                        const char *file, int line)
0176     {
0177         std::string actual_s = build_name(actual_name, component);
0178         std::string expected_s = build_name(expected_name, component);
0179         return QTest::qCompare(actual, expected, actual_s.c_str(), expected_s.c_str(), file, line);
0180     }
0181 };