1 /************************************************************************
2  **
3  **  @file   abstracttest.cpp
4  **  @author Roman Telezhynskyi <dismine(at)gmail.com>
5  **  @date   7 5, 2015
6  **
7  **  @brief
8  **  @copyright
9  **  This source code is part of the Valentina project, a pattern making
10  **  program, whose allow create and modeling patterns of clothing.
11  **  Copyright (C) 2015 Valentina project
12  **  <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
13  **
14  **  Valentina is free software: you can redistribute it and/or modify
15  **  it under the terms of the GNU General Public License as published by
16  **  the Free Software Foundation, either version 3 of the License, or
17  **  (at your option) any later version.
18  **
19  **  Valentina is distributed in the hope that it will be useful,
20  **  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  **  GNU General Public License for more details.
23  **
24  **  You should have received a copy of the GNU General Public License
25  **  along with Valentina.  If not, see <http://www.gnu.org/licenses/>.
26  **
27  *************************************************************************/
28 
29 #include "abstracttest.h"
30 
31 #include <qtestcase.h>
32 #include <QApplication>
33 #include <QByteArray>
34 #include <QDir>
35 #include <QFile>
36 #include <QFileInfo>
37 #include <QFlags>
38 #include <QIODevice>
39 #include <QPointF>
40 #include <QProcess>
41 #include <QScopedPointer>
42 #include <QStaticStringData>
43 #include <QStringData>
44 #include <QStringDataPtr>
45 #include <QStringList>
46 #include <QVector>
47 #include <QtGlobal>
48 #include <QLineF>
49 #include <QJsonDocument>
50 #include <QJsonObject>
51 #include <QJsonArray>
52 
53 #include "vsysexits.h"
54 #include "../vgeometry/vgobject.h"
55 #include "../vgeometry/vpointf.h"
56 #include "../vgeometry/vspline.h"
57 #include "../vgeometry/vsplinepath.h"
58 #include "../vlayout/vabstractpiece.h"
59 #include "../vlayout/vrawsapoint.h"
60 #include "../vpatterndb/vcontainer.h"
61 #include "../vpatterndb/vpiece.h"
62 #include "../vpatterndb/vpiecenode.h"
63 #include "../vpatterndb/vpassmark.h"
64 
65 //---------------------------------------------------------------------------------------------------------------------
AbstractTest(QObject * parent)66 AbstractTest::AbstractTest(QObject *parent) :
67     QObject(parent)
68 {
69 }
70 
71 //---------------------------------------------------------------------------------------------------------------------
VectorFromJson(const QString & json,QVector<QPointF> & vector) const72 void AbstractTest::VectorFromJson(const QString &json, QVector<QPointF>& vector) const
73 {
74     QByteArray saveData;
75     PrepareDocument(json, saveData);
76     QJsonDocument loadDoc(QJsonDocument::fromJson(saveData));
77 
78     const QString vectorKey = QStringLiteral("vector");
79     const QString typeKey = QStringLiteral("type");
80 
81     QJsonObject vectorObject = loadDoc.object();
82     TestRoot(vectorObject, vectorKey, json);
83 
84     QJsonArray vectorArray = vectorObject[vectorKey].toArray();
85     for (int i = 0; i < vectorArray.size(); ++i)
86     {
87         QJsonObject pointObject = vectorArray[i].toObject();
88 
89         QString type;
90         AbstractTest::ReadStringValue(pointObject, typeKey, type);
91 
92         if (type != QLatin1String("QPointF"))
93         {
94             const QString error = QStringLiteral("Invalid json file '%1'. Unexpected class '%2'.")
95                     .arg(json, pointObject[typeKey].toString());
96             QFAIL(qUtf8Printable(error));
97         }
98 
99         QPointF point;
100         QPointFromJson(pointObject, point);
101         vector.append(point);
102     }
103 }
104 
105 //---------------------------------------------------------------------------------------------------------------------
VectorFromJson(const QString & json,QVector<VSAPoint> & vector) const106 void AbstractTest::VectorFromJson(const QString &json, QVector<VSAPoint> &vector) const
107 {
108     QByteArray saveData;
109     PrepareDocument(json, saveData);
110     QJsonDocument loadDoc(QJsonDocument::fromJson(saveData));
111 
112     const QString vectorKey = QStringLiteral("vector");
113 
114     QJsonObject vectorObject = loadDoc.object();
115     TestRoot(vectorObject, vectorKey, json);
116 
117     QJsonArray vectorArray = vectorObject[vectorKey].toArray();
118     for (int i = 0; i < vectorArray.size(); ++i)
119     {
120         QJsonObject pointObject = vectorArray[i].toObject();
121 
122         QString type;
123         AbstractTest::ReadStringValue(pointObject, QStringLiteral("type"), type);
124 
125         if (type != QLatin1String("VSAPoint"))
126         {
127             const QString error = QStringLiteral("Invalid json file '%1'. Unexpected class '%2'.").arg(json, type);
128             QFAIL(qUtf8Printable(error));
129         }
130 
131         VSAPoint point;
132         SAPointFromJson(pointObject, point);
133         vector.append(point);
134     }
135 }
136 
137 //---------------------------------------------------------------------------------------------------------------------
VectorFromJson(const QString & json,QVector<VRawSAPoint> & vector) const138 void AbstractTest::VectorFromJson(const QString &json, QVector<VRawSAPoint> &vector) const
139 {
140     QByteArray saveData;
141     PrepareDocument(json, saveData);
142     QJsonDocument loadDoc(QJsonDocument::fromJson(saveData));
143 
144     const QString vectorKey = QStringLiteral("vector");
145 
146     QJsonObject vectorObject = loadDoc.object();
147     TestRoot(vectorObject, vectorKey, json);
148 
149     QJsonArray vectorArray = vectorObject[vectorKey].toArray();
150     for (int i = 0; i < vectorArray.size(); ++i)
151     {
152         QJsonObject pointObject = vectorArray[i].toObject();
153 
154         QString type;
155         AbstractTest::ReadStringValue(pointObject, QStringLiteral("type"), type);
156 
157         if (type != QLatin1String("VRawSAPoint"))
158         {
159             const QString error = QStringLiteral("Invalid json file '%1'. Unexpected class '%2'.").arg(json, type);
160             QFAIL(qUtf8Printable(error));
161         }
162 
163         VRawSAPoint point;
164         RawSAPointFromJson(pointObject, point);
165         vector.append(point);
166     }
167 }
168 
169 //---------------------------------------------------------------------------------------------------------------------
PieceFromJson(const QString & json,VPiece & piece,QSharedPointer<VContainer> & data)170 void AbstractTest::PieceFromJson(const QString &json, VPiece &piece, QSharedPointer<VContainer> &data)
171 {
172     QByteArray saveData;
173     PrepareDocument(json, saveData);
174     QJsonDocument loadDoc(QJsonDocument::fromJson(saveData));
175 
176     const QString testCaseKey = QStringLiteral("testCase");
177     const QString bdKey = QStringLiteral("bd");
178     const QString pieceKey = QStringLiteral("piece");
179 
180     QJsonObject testCaseObject = loadDoc.object();
181     TestRoot(testCaseObject, testCaseKey, json);
182 
183     QJsonObject testCase = testCaseObject[testCaseKey].toObject();
184 
185     if (testCase.contains(bdKey))
186     {
187         DBFromJson(testCase[bdKey].toObject(), data);
188     }
189     else
190     {
191         const QString error = QStringLiteral("Test case json object does not contain db data.");
192         QFAIL(qUtf8Printable(error));
193     }
194 
195     if (testCase.contains(pieceKey))
196     {
197         MainPathFromJson(testCase[pieceKey].toObject(), piece);
198     }
199     else
200     {
201         const QString error = QStringLiteral("Test case json object does not contain piece data.");
202         QFAIL(qUtf8Printable(error));
203     }
204 }
205 
206 //---------------------------------------------------------------------------------------------------------------------
PassmarkDataFromJson(const QString & json,VPiecePassmarkData & data)207 void AbstractTest::PassmarkDataFromJson(const QString &json, VPiecePassmarkData &data)
208 {
209     QByteArray saveData;
210     PrepareDocument(json, saveData);
211     QJsonDocument loadDoc(QJsonDocument::fromJson(saveData));
212 
213     const QString dataKey = QStringLiteral("data");
214 
215     QJsonObject dataObject = loadDoc.object();
216     TestRoot(dataObject, dataKey, json);
217 
218     QJsonObject passmarkData = dataObject[dataKey].toObject();
219 
220     VSAPoint previousSAPoint;
221     SAPointFromJson(passmarkData[QStringLiteral("previousSAPoint")].toObject(), previousSAPoint);
222     data.previousSAPoint = previousSAPoint;
223 
224     VSAPoint passmarkSAPoint;
225     SAPointFromJson(passmarkData[QStringLiteral("passmarkSAPoint")].toObject(), passmarkSAPoint);
226     data.passmarkSAPoint = passmarkSAPoint;
227 
228     VSAPoint nextSAPoint;
229     SAPointFromJson(passmarkData[QStringLiteral("nextSAPoint")].toObject(), nextSAPoint);
230     data.nextSAPoint = nextSAPoint;
231 
232     qreal saWidth = 0;
233     AbstractTest::ReadDoubleValue(passmarkData, QStringLiteral("saWidth"), saWidth);
234     data.saWidth = saWidth;
235 
236     QString nodeName;
237     AbstractTest::ReadStringValue(passmarkData, QStringLiteral("nodeName"), nodeName);
238     data.nodeName = nodeName;
239 
240     QString pieceName;
241     AbstractTest::ReadStringValue(passmarkData, QStringLiteral("pieceName"), pieceName);
242     data.pieceName = pieceName;
243 
244     PassmarkLineType passmarkLineType = PassmarkLineType::OneLine;
245     AbstractTest::ReadDoubleValue(passmarkData, QStringLiteral("passmarkLineType"), passmarkLineType,
246                                   QString::number(static_cast<int>(PassmarkLineType::OneLine)));
247     data.passmarkLineType = passmarkLineType;
248 
249     PassmarkAngleType passmarkAngleType = PassmarkAngleType::Straightforward;
250     AbstractTest::ReadDoubleValue(passmarkData, QStringLiteral("passmarkAngleType"), passmarkAngleType,
251                                   QString::number(static_cast<int>(PassmarkAngleType::Straightforward)));
252     data.passmarkAngleType = passmarkAngleType;
253 
254     bool isMainPathNode = true;
255     AbstractTest::ReadBooleanValue(passmarkData, QStringLiteral("isMainPathNode"), isMainPathNode);
256     data.isMainPathNode = isMainPathNode;
257 
258     bool isShowSecondPassmark = true;
259     AbstractTest::ReadBooleanValue(passmarkData, QStringLiteral("isShowSecondPassmark"), isShowSecondPassmark);
260     data.isShowSecondPassmark = isShowSecondPassmark;
261 
262     int passmarkIndex = -1;
263     AbstractTest::ReadDoubleValue(passmarkData, QStringLiteral("passmarkIndex"), passmarkIndex, QStringLiteral("-1"));
264     data.passmarkIndex = passmarkIndex;
265 
266     vidtype id = NULL_ID;
267     AbstractTest::ReadDoubleValue(passmarkData, QStringLiteral("id"), id, QString::number(NULL_ID));
268     data.id = id;
269 
270     qreal globalPassmarkLength;
271     AbstractTest::ReadDoubleValue(passmarkData, QStringLiteral("globalPassmarkLength"), globalPassmarkLength,
272                                   QString::number(NULL_ID));
273     data.globalPassmarkLength = globalPassmarkLength;
274 }
275 
276 //---------------------------------------------------------------------------------------------------------------------
PassmarkShapeFromJson(const QString & json,QVector<QLineF> & shape)277 void AbstractTest::PassmarkShapeFromJson(const QString &json, QVector<QLineF> &shape)
278 {
279     QByteArray saveData;
280     PrepareDocument(json, saveData);
281     QJsonDocument loadDoc(QJsonDocument::fromJson(saveData));
282 
283     const QString shapeKey = QStringLiteral("shape");
284     const QString typeKey = QStringLiteral("type");
285 
286     QJsonObject shapeObject = loadDoc.object();
287     TestRoot(shapeObject, shapeKey, json);
288 
289     QJsonArray vectorArray = shapeObject[shapeKey].toArray();
290     for (int i = 0; i < vectorArray.size(); ++i)
291     {
292         QJsonObject lineObject = vectorArray[i].toObject();
293 
294         QString type;
295         AbstractTest::ReadStringValue(lineObject, typeKey, type);
296 
297         if (type != QLatin1String("QLineF"))
298         {
299             const QString error = QStringLiteral("Invalid json file '%1'. Unexpected class '%2'.")
300                     .arg(json, lineObject[typeKey].toString());
301             QFAIL(qUtf8Printable(error));
302         }
303 
304         QLineF line;
305         QLineFromJson(lineObject, line);
306         shape.append(line);
307     }
308 }
309 
310 //---------------------------------------------------------------------------------------------------------------------
Comparison(const QVector<QPointF> & ekv,const QVector<QPointF> & ekvOrig) const311 void AbstractTest::Comparison(const QVector<QPointF> &ekv, const QVector<QPointF> &ekvOrig) const
312 {
313     // Begin comparison
314     QCOMPARE(ekv.size(), ekvOrig.size());// First check if sizes equal
315     const qreal testAccuracy = (1.0/*mm*/ / 25.4) * PrintDPI;
316 
317     for (int i=0; i < ekv.size(); i++)
318     {
319         Comparison(ekv.at(i), ekvOrig.at(i), testAccuracy);
320     }
321 }
322 
323 //---------------------------------------------------------------------------------------------------------------------
Comparison(const QPointF & result,const QPointF & expected,qreal testAccuracy) const324 void AbstractTest::Comparison(const QPointF &result, const QPointF &expected, qreal testAccuracy) const
325 {
326     const QString msg = QStringLiteral("Actual '%2;%3', Expected '%4;%5'. Distance between points %6 mm.")
327             .arg(result.x()).arg(result.y()).arg(expected.x()).arg(expected.y())
328             .arg(UnitConvertor(QLineF(result, expected).length(), Unit::Px, Unit::Mm));
329     // Check each point. Don't use comparison float values
330     QVERIFY2(VFuzzyComparePoints(result, expected, testAccuracy), qUtf8Printable(msg));
331 }
332 
333 
334 //---------------------------------------------------------------------------------------------------------------------
Comparison(const QVector<QLineF> & result,const QVector<QLineF> & expected) const335 void AbstractTest::Comparison(const QVector<QLineF> &result, const QVector<QLineF> &expected) const
336 {
337     // Begin comparison
338     QCOMPARE(result.size(), expected.size());// First check if sizes equal
339 
340     for (int i=0; i < result.size(); i++)
341     {
342         const QLineF &line1 = result.at(i);
343         const QLineF &line2 = expected.at(i);
344         // Check each point. Don't use comparison float values
345         QVERIFY2(VFuzzyComparePoints(line1.p1(), line2.p1()) && VFuzzyComparePoints(line1.p2(), line2.p2()),
346                  qUtf8Printable(
347                      QStringLiteral("Index: %1. Got line '(%2;%3):(%4;%5)', Expected line '(%6;%7):(%8;%9)'.")
348                      .arg(i)
349                      .arg(line1.p1().x())
350                      .arg(line1.p1().y())
351                      .arg(line1.p2().x())
352                      .arg(line1.p2().y())
353                      .arg(line2.p1().x())
354                      .arg(line2.p1().y())
355                      .arg(line2.p2().x())
356                      .arg(line2.p2().y())
357                      )
358                  );
359     }
360 }
361 
362 //---------------------------------------------------------------------------------------------------------------------
ValentinaPath() const363 QString AbstractTest::ValentinaPath() const
364 {
365     const QString path = QStringLiteral("/../../../app/valentina/bin/valentina");
366 #ifdef Q_OS_WIN
367     return QCoreApplication::applicationDirPath() + path + QLatin1String(".exe");
368 #else
369     return QCoreApplication::applicationDirPath() + path;
370 #endif
371 }
372 
373 //---------------------------------------------------------------------------------------------------------------------
TapePath() const374 QString AbstractTest::TapePath() const
375 {
376     const QString path = QStringLiteral("/../../../app/tape/bin/tape");
377 #ifdef Q_OS_WIN
378     return QCoreApplication::applicationDirPath() + path + QLatin1String(".exe");
379 #else
380     return QCoreApplication::applicationDirPath() + path;
381 #endif
382 }
383 
384 //---------------------------------------------------------------------------------------------------------------------
TranslationsPath() const385 QString AbstractTest::TranslationsPath() const
386 {
387     return QCoreApplication::applicationDirPath() + QStringLiteral("/../../../app/valentina/bin/translations");
388 }
389 
390 //---------------------------------------------------------------------------------------------------------------------
RunTimeout(int defMsecs)391 int AbstractTest::RunTimeout(int defMsecs)
392 {
393 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
394     QString timeout = QString::fromLocal8Bit(qgetenv("VTEST_RUN_TIMEOUT"));
395     if (timeout.isEmpty())
396     {
397         return defMsecs;
398     }
399 #else
400     QString timeout = qEnvironmentVariable("VTEST_RUN_TIMEOUT", QString::number(defMsecs));
401 #endif
402 
403     bool ok = false;
404     int msecs = timeout.toInt(&ok);
405     return ok ? msecs : defMsecs;
406 }
407 
408 //---------------------------------------------------------------------------------------------------------------------
Run(int exit,const QString & program,const QStringList & arguments,QString & error,int msecs)409 int AbstractTest::Run(int exit, const QString &program, const QStringList &arguments, QString &error, int msecs)
410 {
411     msecs = AbstractTest::RunTimeout(msecs);
412 
413     const QString parameters = QStringLiteral("Program: %1 \nArguments: %2.")
414             .arg(program, arguments.join(QStringLiteral(", ")));
415 
416     QFileInfo info(program);
417     if (not info.exists())
418     {
419         error = QStringLiteral("Can't find binary.\n%1").arg(parameters);
420         return TST_EX_BIN;
421     }
422 
423     QScopedPointer<QProcess> process(new QProcess());
424     process->setWorkingDirectory(info.absoluteDir().absolutePath());
425     process->start(program, arguments);
426 
427     if (not process->waitForStarted(msecs))
428     {
429         error = QStringLiteral("The start operation timed out or an error occurred.\n%1\n%2")
430                 .arg(parameters, QString(process->readAllStandardError()));
431         process->kill();
432         return TST_EX_START_TIME_OUT;
433     }
434 
435     if (not process->waitForFinished(msecs))
436     {
437         error = QStringLiteral("The finish operation timed out or an error occurred.\n%1\n%2")
438                 .arg(parameters, QString(process->readAllStandardError()));
439         process->kill();
440         return TST_EX_FINISH_TIME_OUT;
441     }
442 
443     if (process->exitStatus() == QProcess::CrashExit)
444     {
445         error = QStringLiteral("Program crashed.\n%1\n%2").arg(parameters, QString(process->readAllStandardError()));
446         return TST_EX_CRASH;
447     }
448 
449     if (process->exitCode() != exit)
450     {
451         error = QStringLiteral("Unexpected finish. Exit code: %1\n%2").arg(process->exitCode())
452                 .arg(QString(process->readAllStandardError()));
453         return process->exitCode();
454     }
455 
456     return process->exitCode();
457 }
458 
459 //---------------------------------------------------------------------------------------------------------------------
CopyRecursively(const QString & srcFilePath,const QString & tgtFilePath) const460 bool AbstractTest::CopyRecursively(const QString &srcFilePath, const QString &tgtFilePath) const
461 {
462     QFileInfo srcFileInfo(srcFilePath);
463     if (srcFileInfo.isDir())
464     {
465         QDir targetDir(tgtFilePath);
466         targetDir.cdUp();
467         const QString dirName = QFileInfo(tgtFilePath).fileName();
468         if (not targetDir.mkdir(dirName))
469         {
470             const QString msg = QStringLiteral("Can't create dir '%1'.").arg(dirName);
471             QWARN(qUtf8Printable(msg));
472             return false;
473         }
474         QDir sourceDir(srcFilePath);
475         const QStringList fileNames = sourceDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot |
476                                                           QDir::Hidden | QDir::System);
477         for (auto &fileName : fileNames)
478         {
479             const QString newSrcFilePath = srcFilePath + QDir::separator() + fileName;
480             const QString newTgtFilePath = tgtFilePath + QDir::separator() + fileName;
481             if (not CopyRecursively(newSrcFilePath, newTgtFilePath))
482             {
483                 return false;
484             }
485         }
486     }
487     else
488     {
489         if (QFileInfo::exists(tgtFilePath))
490         {
491             const QString msg = QStringLiteral("File '%1' exists.").arg(srcFilePath);
492             QWARN(qUtf8Printable(msg));
493 
494             if (QFile::remove(tgtFilePath))
495             {
496                 QWARN("File successfully removed.");
497             }
498             else
499             {
500                 QWARN("Can't remove file.");
501                 return false;
502             }
503         }
504 
505         // Check error: Cannot open %file for input
506         QFile srcFile(srcFilePath);
507         if (not srcFile.open(QFile::ReadOnly))
508         {
509             const QString msg = QStringLiteral("Can't copy file '%1'. Error: %2")
510                     .arg(srcFilePath, srcFile.errorString());
511             QWARN(qUtf8Printable(msg));
512             return false;
513         }
514         srcFile.close();
515 
516         if (not srcFile.copy(tgtFilePath))
517         {
518             const QString msg = QStringLiteral("Can't copy file '%1' to '%2'. Error: %3")
519                     .arg(srcFilePath, tgtFilePath, srcFile.errorString());
520             QWARN(qUtf8Printable(msg));
521             return false;
522         }
523     }
524     return true;
525 }
526 
527 //---------------------------------------------------------------------------------------------------------------------
PrepareDocument(const QString & json,QByteArray & data) const528 void AbstractTest::PrepareDocument(const QString &json, QByteArray &data) const
529 {
530     QFile loadFile(json);
531     if (not loadFile.open(QIODevice::ReadOnly))
532     {
533         const QString error = QStringLiteral("Couldn't open json file. %1").arg(loadFile.errorString());
534         QFAIL(qUtf8Printable(error));
535     }
536 
537     data = loadFile.readAll();
538 }
539 
540 //---------------------------------------------------------------------------------------------------------------------
TestRoot(const QJsonObject & root,const QString & attribute,const QString & file) const541 void AbstractTest::TestRoot(const QJsonObject &root, const QString &attribute, const QString &file) const
542 {
543     if (not root.contains(attribute))
544     {
545         const QString error = QStringLiteral("Invalid json file '%1'. File doesn't contain root object.").arg(file);
546         QFAIL(qUtf8Printable(error));
547     }
548 }
549 
550 //---------------------------------------------------------------------------------------------------------------------
ReadStringValue(const QJsonObject & itemObject,const QString & attribute,QString & value,const QString & defaultValue) const551 void AbstractTest::ReadStringValue(const QJsonObject &itemObject, const QString &attribute, QString &value,
552                                    const QString &defaultValue) const
553 {
554     if (itemObject.contains(attribute))
555     {
556         QJsonValue attributeValue = itemObject[attribute];
557         if (attributeValue.isString())
558         {
559             value = attributeValue.toString();
560         }
561         else
562         {
563             const QString error = QStringLiteral("%1 is not string '%2'.").arg(attribute, attributeValue.toString());
564             QFAIL(qUtf8Printable(error));
565         }
566     }
567     else
568     {
569         if (not defaultValue.isEmpty())
570         {
571             value = defaultValue;
572         }
573         else
574         {
575             const QString error = QStringLiteral("Json object does not contain attribute '%1'.").arg(attribute);
576             QFAIL(qUtf8Printable(error));
577         }
578     }
579 }
580 
581 //---------------------------------------------------------------------------------------------------------------------
ReadBooleanValue(const QJsonObject & itemObject,const QString & attribute,bool & value,const QString & defaultValue) const582 void AbstractTest::ReadBooleanValue(const QJsonObject &itemObject, const QString &attribute, bool &value,
583                                     const QString &defaultValue) const
584 {
585     if (itemObject.contains(attribute))
586     {
587         QJsonValue attributeValue = itemObject[attribute];
588         if (attributeValue.isBool())
589         {
590             value = attributeValue.toBool();
591         }
592         else
593         {
594             const QString error = QStringLiteral("%1 is not boolean value '%2'.").arg(attribute,
595                                                                                       attributeValue.toString());
596             QFAIL(qUtf8Printable(error));
597         }
598     }
599     else
600     {
601         if (not defaultValue.isEmpty())
602         {
603             bool ok = false;
604             int defVal = defaultValue.toInt(&ok);
605 
606             if (not ok)
607             {
608                 const QString error = QStringLiteral("Cannot convert default value '%1' to int.").arg(defaultValue);
609                 QFAIL(qUtf8Printable(error));
610             }
611 
612             value = static_cast<bool>(defVal);
613         }
614         else
615         {
616             const QString error = QStringLiteral("Json object does not contain attribute '%1'.").arg(attribute);
617             QFAIL(qUtf8Printable(error));
618         }
619     }
620 }
621 
622 //---------------------------------------------------------------------------------------------------------------------
ReadPointValue(const QJsonObject & itemObject,const QString & attribute,VPointF & value)623 void AbstractTest::ReadPointValue(const QJsonObject &itemObject, const QString &attribute, VPointF &value)
624 {
625     if (itemObject.contains(attribute))
626     {
627         QJsonObject p1Object = itemObject[attribute].toObject();
628         VPointFromJson(p1Object, value);
629     }
630     else
631     {
632         const QString error = QStringLiteral("Json object does not contain attribute '%1'.").arg(attribute);
633         QFAIL(qUtf8Printable(error));
634     }
635 }
636 
637 //---------------------------------------------------------------------------------------------------------------------
ReadSplinePointValues(const QJsonObject & itemObject,const QString & attribute,QVector<VSplinePoint> & points)638 void AbstractTest::ReadSplinePointValues(const QJsonObject &itemObject, const QString &attribute,
639                                          QVector<VSplinePoint> &points)
640 {
641     points.clear();
642     if (itemObject.contains(attribute))
643     {
644         QJsonArray nodes = itemObject[attribute].toArray();
645         for (int i = 0; i < nodes.size(); ++i)
646         {
647             QJsonObject item = nodes[i].toObject();
648             VSplinePoint point;
649             ReadSplinePointValue(item, point);
650             points.append(point);
651         }
652     }
653     else
654     {
655         const QString error = QStringLiteral("Json object does not contain attribute '%1'.").arg(attribute);
656         QFAIL(qUtf8Printable(error));
657     }
658 }
659 
660 //---------------------------------------------------------------------------------------------------------------------
ReadSplinePointValue(const QJsonObject & itemObject,VSplinePoint & point)661 void AbstractTest::ReadSplinePointValue(const QJsonObject &itemObject, VSplinePoint &point)
662 {
663     qreal angle1 = 0;
664     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("angle1"), angle1);
665 
666     QString angle1Formula;
667     AbstractTest::ReadStringValue(itemObject, QStringLiteral("angle1Formula"), angle1Formula);
668 
669     qreal angle2 = 0;
670     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("angle2"), angle2);
671 
672     QString angle2Formula;
673     AbstractTest::ReadStringValue(itemObject, QStringLiteral("angle2Formula"), angle2Formula);
674 
675     qreal length1 = 0;
676     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("length1"), length1);
677 
678     QString length1Formula;
679     AbstractTest::ReadStringValue(itemObject, QStringLiteral("length1Formula"), length1Formula);
680 
681     qreal length2 = 0;
682     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("length2"), length2);
683 
684     QString length2Formula;
685     AbstractTest::ReadStringValue(itemObject, QStringLiteral("length2Formula"), length2Formula);
686 
687     VPointF pSpline;
688     ReadPointValue(itemObject, QStringLiteral("point"), pSpline);
689 
690     point = VSplinePoint(pSpline, angle1, angle1Formula, angle2, angle2Formula, length1, length1Formula, length2,
691                          length2Formula);
692 }
693 
694 //---------------------------------------------------------------------------------------------------------------------
ReadPieceNodeValue(const QJsonObject & itemObject,VPieceNode & node)695 void AbstractTest::ReadPieceNodeValue(const QJsonObject &itemObject, VPieceNode &node)
696 {
697     vidtype id = NULL_ID;
698     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("id"), id);
699 
700     Tool typeTool = Tool::LAST_ONE_DO_NOT_USE;
701     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("type"), typeTool);
702 
703     bool reverse = false;
704     AbstractTest::ReadBooleanValue(itemObject, QStringLiteral("reverse"), reverse, QChar('0'));
705 
706     node = VPieceNode(id, typeTool, reverse);
707 }
708 
709 //---------------------------------------------------------------------------------------------------------------------
QPointFromJson(const QJsonObject & itemObject,QPointF & point) const710 void AbstractTest::QPointFromJson(const QJsonObject &itemObject, QPointF &point) const
711 {
712     qreal x = 0;
713     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("x"), x);
714     point.setX(x);
715 
716     qreal y = 0;
717     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("y"), y);
718     point.setY(y);
719 }
720 
721 //---------------------------------------------------------------------------------------------------------------------
722 template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type*>
ReadDoubleValue(const QJsonObject & itemObject,const QString & attribute,T & value,const QString & defaultValue) const723 void AbstractTest::ReadDoubleValue(const QJsonObject &itemObject, const QString &attribute, T &value,
724                                    const QString &defaultValue) const
725 {
726     if (itemObject.contains(attribute))
727     {
728         QJsonValue attributeValue = itemObject[attribute];
729         if (attributeValue.isDouble())
730         {
731             value = static_cast<T>(attributeValue.toDouble());
732         }
733         else
734         {
735             const QString error = QStringLiteral("%1 is not double '%2'.").arg(attribute, attributeValue.toString());
736             QFAIL(qUtf8Printable(error));
737         }
738     }
739     else
740     {
741         if (not defaultValue.isEmpty())
742         {
743             bool ok = false;
744             value = static_cast<T>(defaultValue.toDouble(&ok));
745 
746             if (not ok)
747             {
748                 const QString error = QStringLiteral("Cannot convert default value '%1' to double.").arg(defaultValue);
749                 QFAIL(qUtf8Printable(error));
750             }
751         }
752         else
753         {
754             const QString error = QStringLiteral("Json object does not contain attribute '%1'.").arg(attribute);
755             QFAIL(qUtf8Printable(error));
756         }
757     }
758 }
759 
760 //---------------------------------------------------------------------------------------------------------------------
761 template<typename T, typename std::enable_if<std::is_enum<T>::value>::type*>
ReadDoubleValue(const QJsonObject & itemObject,const QString & attribute,T & value,const QString & defaultValue) const762 void AbstractTest::ReadDoubleValue(const QJsonObject &itemObject, const QString &attribute, T &value,
763                                    const QString &defaultValue) const
764 {
765     if (itemObject.contains(attribute))
766     {
767         QJsonValue attributeValue = itemObject[attribute];
768         if (attributeValue.isDouble())
769         {
770             value = static_cast<T>(static_cast<int>(attributeValue.toDouble()));
771         }
772         else
773         {
774             const QString error = QStringLiteral("%1 is not double '%2'.").arg(attribute, attributeValue.toString());
775             QFAIL(qUtf8Printable(error));
776         }
777     }
778     else
779     {
780         if (not defaultValue.isEmpty())
781         {
782             bool ok = false;
783             value = static_cast<T>(defaultValue.toInt(&ok));
784 
785             if (not ok)
786             {
787                 const QString error = QStringLiteral("Cannot convert default value '%1' to int.").arg(defaultValue);
788                 QFAIL(qUtf8Printable(error));
789             }
790         }
791         else
792         {
793             const QString error = QStringLiteral("Json object does not contain attribute '%1'.").arg(attribute);
794             QFAIL(qUtf8Printable(error));
795         }
796     }
797 }
798 
799 //---------------------------------------------------------------------------------------------------------------------
800 template<typename T, typename std::enable_if<std::is_integral<T>::value>::type*>
ReadDoubleValue(const QJsonObject & itemObject,const QString & attribute,T & value,const QString & defaultValue) const801 void AbstractTest::ReadDoubleValue(const QJsonObject &itemObject, const QString &attribute, T &value,
802                                    const QString &defaultValue) const
803 {
804     if (itemObject.contains(attribute))
805     {
806         QJsonValue attributeValue = itemObject[attribute];
807         if (attributeValue.isDouble())
808         {
809             value = static_cast<T>(attributeValue.toDouble());
810         }
811         else
812         {
813             const QString error = QStringLiteral("%1 is not double '%2'.").arg(attribute, attributeValue.toString());
814             QFAIL(qUtf8Printable(error));
815         }
816     }
817     else
818     {
819         if (not defaultValue.isEmpty())
820         {
821             bool ok = false;
822             value = static_cast<T>(defaultValue.toInt(&ok));
823 
824             if (not ok)
825             {
826                 const QString error = QStringLiteral("Cannot convert default value '%1' to int.").arg(defaultValue);
827                 QFAIL(qUtf8Printable(error));
828             }
829         }
830         else
831         {
832             const QString error = QStringLiteral("Json object does not contain attribute '%1'.").arg(attribute);
833             QFAIL(qUtf8Printable(error));
834         }
835     }
836 }
837 
838 //---------------------------------------------------------------------------------------------------------------------
VPointFromJson(const QJsonObject & itemObject,VPointF & point)839 void AbstractTest::VPointFromJson(const QJsonObject &itemObject, VPointF &point)
840 {
841     vidtype id = NULL_ID;
842     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("id"), id);
843 
844     qreal mx = 0;
845     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("mx"), mx);
846 
847     qreal my = 0;
848     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("my"), my);
849 
850     QString name;
851     AbstractTest::ReadStringValue(itemObject, QStringLiteral("name"), name);
852 
853     qreal x = 0;
854     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("x"), x);
855 
856     qreal y = 0;
857     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("y"), y);
858 
859     point = VPointF(x, y, name, mx, my);
860     point.setId(id);
861 }
862 
863 //---------------------------------------------------------------------------------------------------------------------
QLineFromJson(const QJsonObject & itemObject,QLineF & line)864 void AbstractTest::QLineFromJson(const QJsonObject &itemObject, QLineF &line)
865 {
866     QPointF p1;
867     QPointFromJson(itemObject[QStringLiteral("p1")].toObject(), p1);
868 
869     QPointF p2;
870     QPointFromJson(itemObject[QStringLiteral("p2")].toObject(), p2);
871 
872     line = QLineF(p1, p2);
873 }
874 
875 //---------------------------------------------------------------------------------------------------------------------
SAPointFromJson(const QJsonObject & itemObject,VSAPoint & point) const876 void AbstractTest::SAPointFromJson(const QJsonObject &itemObject, VSAPoint &point) const
877 {
878     qreal x = 0;
879     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("x"), x);
880     point.setX(x);
881 
882     qreal y = 0;
883     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("y"), y);
884     point.setY(y);
885 
886     qreal saBefore;
887     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("saBefore"), saBefore, QStringLiteral("-1"));
888     point.SetSABefore(saBefore);
889 
890     qreal saAfter;
891     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("saAfter"), saAfter, QStringLiteral("-1"));
892     point.SetSAAfter(saAfter);
893 
894     PieceNodeAngle angleType = PieceNodeAngle::ByLength;
895     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("angle"), angleType,
896                                   QString::number(static_cast<int>(PieceNodeAngle::ByLength)));
897     point.SetAngleType(angleType);
898 }
899 
900 //---------------------------------------------------------------------------------------------------------------------
RawSAPointFromJson(const QJsonObject & itemObject,VRawSAPoint & point) const901 void AbstractTest::RawSAPointFromJson(const QJsonObject &itemObject, VRawSAPoint &point) const
902 {
903     qreal x = 0;
904     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("x"), x);
905     point.setX(x);
906 
907     qreal y = 0;
908     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("y"), y);
909     point.setY(y);
910 
911     bool loopPoint;
912     AbstractTest::ReadBooleanValue(itemObject, QStringLiteral("loopPoint"), loopPoint, QStringLiteral("0"));
913     point.SetLoopPoint(loopPoint);
914 }
915 
916 //---------------------------------------------------------------------------------------------------------------------
SplineFromJson(const QJsonObject & itemObject,QSharedPointer<VContainer> & data)917 void AbstractTest::SplineFromJson(const QJsonObject &itemObject, QSharedPointer<VContainer> &data)
918 {
919     vidtype id = NULL_ID;
920     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("id"), id);
921 
922     qreal aScale = 0;
923     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("aScale"), aScale);
924 
925     qreal angle1 = 0;
926     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("angle1"), angle1);
927 
928     QString angle1Formula;
929     AbstractTest::ReadStringValue(itemObject, QStringLiteral("angle1Formula"), angle1Formula);
930 
931     qreal angle2 = 0;
932     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("angle2"), angle2);
933 
934     QString angle2Formula;
935     AbstractTest::ReadStringValue(itemObject, QStringLiteral("angle2Formula"), angle2Formula);
936 
937     qreal c1Length = 0;
938     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("c1Length"), c1Length);
939 
940     QString c1LengthFormula;
941     AbstractTest::ReadStringValue(itemObject, QStringLiteral("c1LengthFormula"), c1LengthFormula);
942 
943     qreal c2Length = 0;
944     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("c2Length"), c2Length);
945 
946     QString c2LengthFormula;
947     AbstractTest::ReadStringValue(itemObject, QStringLiteral("c2LengthFormula"), c2LengthFormula);
948 
949     VPointF p1;
950     ReadPointValue(itemObject, QStringLiteral("p1"), p1);
951     data->UpdateGObject(p1.id(), new VPointF(p1));
952 
953     VPointF p4;
954     ReadPointValue(itemObject, QStringLiteral("p4"), p4);
955     data->UpdateGObject(p4.id(), new VPointF(p4));
956 
957     VSpline *spl = new VSpline(p1, p4, angle1, angle1Formula, angle2, angle2Formula, c1Length, c1LengthFormula,
958                                c2Length, c2LengthFormula);
959     spl->SetApproximationScale(aScale);
960     data->UpdateGObject(id, spl);
961 }
962 
963 //---------------------------------------------------------------------------------------------------------------------
SplinePathFromJson(const QJsonObject & itemObject,QSharedPointer<VContainer> & data)964 void AbstractTest::SplinePathFromJson(const QJsonObject &itemObject, QSharedPointer<VContainer> &data)
965 {
966     vidtype id = NULL_ID;
967     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("id"), id);
968 
969     qreal aScale = 0;
970     AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("aScale"), aScale);
971 
972     QVector<VSplinePoint> points;
973     AbstractTest::ReadSplinePointValues(itemObject, QStringLiteral("nodes"), points);
974     for (auto &point : points)
975     {
976         data->UpdateGObject(point.P().id(), new VPointF(point.P()));
977     }
978 
979     VSplinePath *path = new VSplinePath(points);
980     path->SetApproximationScale(aScale);
981     data->UpdateGObject(id, path);
982 }
983 
984 //---------------------------------------------------------------------------------------------------------------------
DBFromJson(const QJsonObject & dbObject,QSharedPointer<VContainer> & data)985 void AbstractTest::DBFromJson(const QJsonObject &dbObject, QSharedPointer<VContainer> &data)
986 {
987     const QString itemsKey = QStringLiteral("items");
988 
989     if (dbObject.contains(itemsKey))
990     {
991         QJsonArray items = dbObject[itemsKey].toArray();
992         for (int i = 0; i < items.size(); ++i)
993         {
994             QJsonObject itemObject = items[i].toObject();
995             GOType objectType;
996             AbstractTest::ReadDoubleValue(itemObject, QStringLiteral("type"), objectType);
997 
998             switch(objectType)
999             {
1000                 case GOType::Point:
1001                 {
1002                     VPointF point;
1003                     VPointFromJson(itemObject, point);
1004                     data->UpdateGObject(point.id(), new VPointF(point));
1005                     break;
1006                 }
1007                 case GOType::Spline:
1008                     SplineFromJson(itemObject, data);
1009                     break;
1010                 case GOType::SplinePath:
1011                     SplinePathFromJson(itemObject, data);
1012                     break;
1013                 default:
1014                 {
1015                     const QString error = QStringLiteral("Not supported item type '%1'.")
1016                             .arg(static_cast<int>(objectType));
1017                     QFAIL(qUtf8Printable(error));
1018                 }
1019             }
1020         }
1021     }
1022     else
1023     {
1024         const QString error = QStringLiteral("DB json object does not contain items.");
1025         QFAIL(qUtf8Printable(error));
1026     }
1027 }
1028 
1029 //---------------------------------------------------------------------------------------------------------------------
MainPathFromJson(const QJsonObject & pieceObject,VPiece & piece)1030 void AbstractTest::MainPathFromJson(const QJsonObject &pieceObject, VPiece &piece)
1031 {
1032     qreal saWidth = 0;
1033     AbstractTest::ReadDoubleValue(pieceObject, QStringLiteral("saWidth"), saWidth);
1034 
1035     bool seamAllowance = false;
1036     AbstractTest::ReadBooleanValue(pieceObject, QStringLiteral("seamAllowance"), seamAllowance);
1037 
1038     piece.SetSeamAllowance(seamAllowance);
1039     piece.SetSAWidth(saWidth);
1040 
1041     piece.GetPath().Clear();
1042 
1043     const QString nodesKey = QStringLiteral("nodes");
1044 
1045     if (pieceObject.contains(nodesKey))
1046     {
1047         QJsonArray nodes = pieceObject[nodesKey].toArray();
1048         for (int i = 0; i < nodes.size(); ++i)
1049         {
1050             QJsonObject itemObject = nodes[i].toObject();
1051 
1052             VPieceNode node;
1053             ReadPieceNodeValue(itemObject, node);
1054             piece.GetPath().Append(node);
1055         }
1056     }
1057     else
1058     {
1059         const QString error = QStringLiteral("Piece json object does not contain nodes.");
1060         QFAIL(qUtf8Printable(error));
1061     }
1062 }
1063