1 /**********************************************************************************************
2     Copyright (C) 2015-2016 Christian Eichler code@christian-eichler.de
3 
4     This program is free software: you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation, either version 3 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 
17 **********************************************************************************************/
18 
19 #include <QDebug>
20 #include <QTemporaryFile>
21 
22 #include "TestHelper.h"
23 #include "test_QMapShack.h"
24 
25 #include "gis/gpx/CGpxProject.h"
26 #include "gis/ovl/CGisItemOvlArea.h"
27 #include "gis/prj/IGisProject.h"
28 #include "gis/fit/CFitProject.h"
29 #include "gis/qms/CQmsProject.h"
30 #include "gis/rte/CGisItemRte.h"
31 #include "gis/slf/CSlfProject.h"
32 #include "gis/slf/CSlfReader.h"
33 #include "gis/trk/CGisItemTrk.h"
34 #include "gis/trk/CKnownExtension.h"
35 #include "gis/wpt/CGisItemWpt.h"
36 #include "helpers/CSettings.h"
37 #include "setup/IAppSetup.h"
38 
39 QString testInput;
40 
initTestCase()41 void test_QMapShack::initTestCase()
42 {
43     IAppSetup* env = IAppSetup::getPlatformInstance();
44     env->processArguments();
45     env->initLogHandler();
46     env->initQMapShack();
47 
48     SETTINGS;
49     IUnit::self().setUnitType((IUnit::type_e)cfg.value("MainWindow/units",IUnit::eTypeMetric).toInt(), nullptr);
50     CKnownExtension::init(IUnit::self());
51 
52     testInput = QCoreApplication::applicationDirPath() + "/input/";
53 
54     inputFiles =
55     {
56         "qtt_gpx_file0.gpx"
57         , "gpx_ext_GarminTPX1_gpxtpx.gpx"
58         , "gpx_ext_GarminTPX1_tp1.gpx"
59         , "V1.6.0_file1.qms"
60         , "V1.6.0_file2.qms"
61     };
62 }
63 
verify(expectedGisProject exp,const IGisProject & proj)64 void test_QMapShack::verify(expectedGisProject exp, const IGisProject &proj)
65 {
66     VERIFY_EQUAL(true,        proj.isValid());
67     VERIFY_EQUAL(exp.changed, proj.isChanged());
68 
69     VERIFY_EQUAL(exp.name, proj.getName());
70     VERIFY_EQUAL(exp.desc, proj.getDescription());
71 
72     VERIFY_EQUAL(exp.wpts.count(), proj.getItemCountByType(IGisItem::eTypeWpt));
73     VERIFY_EQUAL(exp.trks.count(), proj.getItemCountByType(IGisItem::eTypeTrk));
74     VERIFY_EQUAL(exp.rtes.count(), proj.getItemCountByType(IGisItem::eTypeRte));
75     VERIFY_EQUAL(exp.ovls.count(), proj.getItemCountByType(IGisItem::eTypeOvl));
76 
77     for(int i = 0; i < proj.childCount(); i++)
78     {
79         IGisItem *item = dynamic_cast<IGisItem*>(proj.child(i));
80 
81         CGisItemWpt *wpt = dynamic_cast<CGisItemWpt*>(item);
82         if(nullptr != wpt)
83         {
84             VERIFY_EQUAL(true, exp.wpts.contains(wpt->getName()));
85             exp.wpts.remove(wpt->getName());
86 
87             SUBVERIFY(wpt->getPosition() != QPointF(0., 0.), "Waypoint has position 0/0");
88         }
89 
90         CGisItemTrk *itemTrk = dynamic_cast<CGisItemTrk*>(item);
91         if(nullptr != itemTrk)
92         {
93             const CTrackData &trk = itemTrk->getTrackData();
94 
95             SUBVERIFY(exp.trks.contains(itemTrk->getName()), QString("Found track `%1`, there shouldn't be any track with that name").arg(itemTrk->getName()));
96 
97             const expectedTrack &expTrk = exp.trks.take(itemTrk->getName());
98 
99             int trkptCount = 0;
100             for(const trkseg_t &seg : trk.segs)
101             {
102                 trkptCount += seg.pts.count();
103 
104                 for(const trkpt_t &trkpt : seg.pts)
105                 {
106                     SUBVERIFY((0. != trkpt.lat) || (0. != trkpt.lon), "Trackpoint has position 0/0");
107 
108                     for(const QString &key : expTrk.extensions.keys())
109                     {
110                         VERIFY_EQUAL(expTrk.extensions[key].known, CKnownExtension::isKnown(key));
111                         if(expTrk.extensions[key].everyPoint)
112                         {
113                             SUBVERIFY(trkpt.extensions.contains(key), QString("Missing extension `%1`on trackpoint").arg(key));
114                         }
115                     }
116                 }
117             }
118 
119             VERIFY_EQUAL(expTrk.segCount, trk.segs.count());
120             VERIFY_EQUAL(expTrk.ptCount,  trkptCount);
121             VERIFY_EQUAL(expTrk.colorIdx, itemTrk->getColorIdx());
122 
123             QStringList existingSources = itemTrk->getExistingDataSources();
124             for(const QString &ext : expTrk.extensions.keys())
125             {
126                 SUBVERIFY(existingSources.contains(ext), QString("Missing extension `%1`").arg(ext));
127                 existingSources.removeOne(ext);
128             }
129 
130             auto accuFunc = [](const QString &accu, const QString &b) { return accu.isEmpty() ? b : QString("%1, %2").arg(accu).arg(b); };
131             QString remainingExts = std::accumulate(existingSources.cbegin(), existingSources.cend(), QString(), accuFunc);
132 
133             SUBVERIFY(existingSources.isEmpty(), QString("existingSources still contains: ") +  remainingExts);
134         }
135 
136         CGisItemRte *itemRte = dynamic_cast<CGisItemRte*>(item);
137         if(nullptr != itemRte)
138         {
139             SUBVERIFY(exp.rtes.contains(itemRte->getName()), QString("Found route `%1`, there shouldn't be any route with that name").arg(itemRte->getName()));
140             const CGisItemRte::rte_t &rte = itemRte->getRoute();
141 
142             const expectedRoute &expRte = exp.rtes.take(itemRte->getName());
143 
144             VERIFY_EQUAL(expRte.ptCount, rte.pts.size());
145         }
146 
147         CGisItemOvlArea *itemOvl = dynamic_cast<CGisItemOvlArea*>(item);
148         if(nullptr != itemOvl)
149         {
150             SUBVERIFY(exp.ovls.contains(itemOvl->getName()), QString("Found area `%1`, there shouldn't be any area with that name").arg(itemOvl->getName()));
151 
152             const expectedArea &expOvl = exp.ovls.take(itemOvl->getName());
153             VERIFY_EQUAL(expOvl.colorIdx, itemOvl->getColorIdx());
154 
155             const CGisItemOvlArea::area_t &area = itemOvl->getAreaData();
156             VERIFY_EQUAL(expOvl.ptCount, area.pts.size());
157         }
158     }
159 
160     // ensure all expected waypoints/tracks actually exist
161     SUBVERIFY(exp.wpts.isEmpty(), "Not all expected waypoints found");
162     SUBVERIFY(exp.trks.isEmpty(), "Not all expected tracks found");
163     SUBVERIFY(exp.rtes.isEmpty(), "Not all expected routes found");
164     SUBVERIFY(exp.ovls.isEmpty(), "Not all expected areas found");
165 }
166 
verify(const QString & projFile,const IGisProject & proj)167 void test_QMapShack::verify(const QString &projFile, const IGisProject &proj)
168 {
169     expectedGisProject exp = TestHelper::readExpProj(fileToPath(projFile) + ".xml");
170     verify(exp, proj);
171 }
172 
verify(const QString & projFile)173 void test_QMapShack::verify(const QString &projFile)
174 {
175     IGisProject        *proj = readProjFile(projFile);
176     expectedGisProject exp  = TestHelper::readExpProj(fileToPath(projFile) + ".xml");
177 
178     verify(exp, *proj);
179     delete proj;
180 }
181 
fileToPath(const QString & file)182 QString test_QMapShack::fileToPath(const QString &file)
183 {
184     if(!QFileInfo(file).exists())
185     {
186         return testInput + "/" + file.right(3) + "/" + file;
187     }
188     return file;
189 }
190 
readProjFile(const QString & file,bool valid,bool forceVerify)191 IGisProject* test_QMapShack::readProjFile(const QString &file, bool valid, bool forceVerify)
192 {
193     IGisProject *proj = nullptr;
194 
195     try
196     {
197         if(file.endsWith(".gpx"))
198         {
199             CGpxProject *gpxProj = new CGpxProject("a very random string to prevent loading via constructor", (CGisListWks*) nullptr);
200             gpxProj->blockUpdateItems(true);
201             CGpxProject::loadGpx(fileToPath(file), gpxProj);
202             gpxProj->blockUpdateItems(false);
203             proj = gpxProj;
204             SUBVERIFY(IGisProject::eTypeGpx == proj->getType(), "Project has invalid type");
205         }
206         else if(file.endsWith(".qms"))
207         {
208             proj = new CQmsProject(fileToPath(file), (CGisListWks*) nullptr);
209             SUBVERIFY(IGisProject::eTypeQms == proj->getType(), "Project has invalid type");
210         }
211         else if(file.endsWith(".slf"))
212         {
213             CSlfProject *slfProj = new CSlfProject("a very random string to prevent loading via constructor", false);
214             proj = slfProj;
215             CSlfReader::readFile(fileToPath(file), slfProj);
216             SUBVERIFY(IGisProject::eTypeSlf == proj->getType(), "Project has invalid type");
217         }
218         else if(file.endsWith(".fit"))
219         {
220             proj = new CFitProject(fileToPath(file), (CGisListWks*) nullptr);
221             SUBVERIFY(IGisProject::eTypeFit == proj->getType(), "Project has invalid type");
222         }
223         else
224         {
225             SUBVERIFY(false, "Internal error: Can't read project file `" + file + "`");
226         }
227     }
228     catch(QString &errormsg)
229     {
230         SUBVERIFY(!valid, "Expected `" + file + "` to be valid, error while reading: " + errormsg);
231         delete proj;
232         proj = nullptr;
233     }
234 
235     SUBVERIFY(valid || nullptr == proj, "File is neither valid, nor an exception was thrown");
236 
237     if(nullptr != proj)
238     {
239         const QString &projPath = fileToPath(file);
240         SUBVERIFY(QFile(projPath + ".xml").exists() || !forceVerify, "Can't verify file `" + file + "`, .xml does not exist");
241 
242         if(QFile(projPath + ".xml").exists())
243         {
244             verify(file, *proj);
245         }
246     }
247 
248     return proj;
249 }
250 
251 QTEST_MAIN(test_QMapShack)
252