1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28
29 #include <qgeonetworkaccessmanager.h>
30 #include <qgeoroutereply_nokia.h>
31
32 #include <QtTest/QtTest>
33 #include <QDebug>
34 #include <QNetworkReply>
35 #include <QtLocation/QGeoRouteReply>
36 #include <QtLocation/QGeoServiceProvider>
37 #include <QtLocation/QGeoRoutingManager>
38 #include <QtPositioning/QGeoRectangle>
39 #include <QtLocation/QGeoManeuver>
40 #include <QtLocation/QGeoRouteSegment>
41
42 QT_USE_NAMESPACE
43
44 #define CHECK_CLOSE_E(expected, actual, e) QVERIFY((qAbs(actual - expected) <= e))
45 #define CHECK_CLOSE(expected, actual) CHECK_CLOSE_E(expected, actual, qreal(1e-6))
46
47 class MockGeoNetworkReply : public QNetworkReply
48 {
49 public:
50 MockGeoNetworkReply( QObject* parent = 0);
51 virtual void abort();
52
53 void setFile(QFile* file);
54 void complete();
55 using QNetworkReply::setRequest;
56 using QNetworkReply::setOperation;
57 using QNetworkReply::setError;
58
59 protected:
60 virtual qint64 readData(char *data, qint64 maxlen);
61 virtual qint64 writeData(const char *data, qint64 len);
62
63 private:
64 QFile* m_file;
65 };
66
MockGeoNetworkReply(QObject * parent)67 MockGeoNetworkReply::MockGeoNetworkReply(QObject* parent)
68 : QNetworkReply(parent)
69 , m_file(0)
70 {
71 setOpenMode(QIODevice::ReadOnly);
72 }
73
abort()74 void MockGeoNetworkReply::abort()
75 {}
76
readData(char * data,qint64 maxlen)77 qint64 MockGeoNetworkReply::readData(char *data, qint64 maxlen)
78 {
79 if (m_file) {
80 const qint64 read = m_file->read(data, maxlen);
81 if (read <= 0)
82 return -1;
83 return read;
84 }
85 return -1;
86 }
87
writeData(const char * data,qint64 len)88 qint64 MockGeoNetworkReply::writeData(const char *data, qint64 len)
89 {
90 Q_UNUSED(data);
91 Q_UNUSED(len);
92 return -1;
93 }
94
setFile(QFile * file)95 void MockGeoNetworkReply::setFile(QFile* file)
96 {
97 delete m_file;
98 m_file = file;
99 if (m_file)
100 m_file->setParent(this);
101 }
102
complete()103 void MockGeoNetworkReply::complete()
104 {
105 if (error() != QNetworkReply::NoError)
106 emit errorOccurred(error());
107 setFinished(true);
108 emit finished();
109 }
110
111 class MockGeoNetworkAccessManager : public QGeoNetworkAccessManager
112 {
113 public:
114 MockGeoNetworkAccessManager(QObject* parent = 0);
115 QNetworkReply* get(const QNetworkRequest& request);
116 QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data);
117
118 void setReply(MockGeoNetworkReply* reply);
119
120 private:
121 MockGeoNetworkReply* m_reply;
122 };
123
MockGeoNetworkAccessManager(QObject * parent)124 MockGeoNetworkAccessManager::MockGeoNetworkAccessManager(QObject* parent)
125 : QGeoNetworkAccessManager(parent)
126 , m_reply(0)
127 {}
128
get(const QNetworkRequest & request)129 QNetworkReply* MockGeoNetworkAccessManager::get(const QNetworkRequest& request)
130 {
131 MockGeoNetworkReply* r = m_reply;
132 m_reply = 0;
133 if (r) {
134 r->setRequest(request);
135 r->setOperation(QNetworkAccessManager::GetOperation);
136 r->setParent(0);
137 }
138
139 return r;
140 }
141
post(const QNetworkRequest & request,const QByteArray & data)142 QNetworkReply* MockGeoNetworkAccessManager::post(const QNetworkRequest &request, const QByteArray &data)
143 {
144 Q_UNUSED(request);
145 Q_UNUSED(data);
146 QTest::qFail("Not implemented", __FILE__, __LINE__);
147 return new MockGeoNetworkReply();
148 }
149
setReply(MockGeoNetworkReply * reply)150 void MockGeoNetworkAccessManager::setReply(MockGeoNetworkReply* reply)
151 {
152 delete m_reply;
153 m_reply = reply;
154 if (m_reply)
155 m_reply->setParent(this);
156 }
157
158 class tst_nokia_routing : public QObject
159 {
160 Q_OBJECT
161
162 public:
163 tst_nokia_routing();
164
165 private:
166 void calculateRoute();
167 void loadReply(const QString& filename);
168 void onReply(QGeoRouteReply* reply);
169 void verifySaneRoute(const QGeoRoute& route);
170
171 // Infrastructure slots
172 private Q_SLOTS:
173 void routingFinished(QGeoRouteReply* reply);
174 void routingError(QGeoRouteReply* reply, QGeoRouteReply::Error error, QString errorString);
175
176 // Test slots
177 private Q_SLOTS:
178 void initTestCase();
179 void cleanupTestCase();
180 void cleanup();
181 void can_compute_route_for_all_supported_travel_modes();
182 void can_compute_route_for_all_supported_travel_modes_data();
183 void can_compute_route_for_all_supported_optimizations();
184 void can_compute_route_for_all_supported_optimizations_data();
185 void can_handle_multiple_routes_in_response();
186 void can_handle_no_route_exists_case();
187 void can_handle_invalid_server_responses();
188 void can_handle_invalid_server_responses_data();
189 void can_handle_additions_to_routing_xml();
190 void foobar();
191 void foobar_data();
192
193 private:
194 QGeoServiceProvider* m_geoServiceProvider;
195 MockGeoNetworkAccessManager* m_networkManager;
196 QGeoRoutingManager* m_routingManager;
197 QGeoRouteReply* m_reply;
198 MockGeoNetworkReply* m_replyUnowned;
199 QGeoRouteRequest m_dummyRequest;
200 bool m_calculationDone;
201 bool m_expectError;
202 };
203
tst_nokia_routing()204 tst_nokia_routing::tst_nokia_routing()
205 : m_geoServiceProvider(0)
206 , m_networkManager(0)
207 , m_routingManager(0)
208 , m_reply(0)
209 , m_replyUnowned()
210 , m_calculationDone(true)
211 , m_expectError(false)
212 {
213 }
214
loadReply(const QString & filename)215 void tst_nokia_routing::loadReply(const QString& filename)
216 {
217 QFile* file = new QFile(QFINDTESTDATA(filename));
218 if (!file->open(QIODevice::ReadOnly)) {
219 delete file;
220 file = 0;
221 qDebug() << filename;
222 QTest::qFail("Failed to open file", __FILE__, __LINE__);
223 }
224
225 m_replyUnowned = new MockGeoNetworkReply();
226 m_replyUnowned->setFile(file);
227 m_networkManager->setReply(m_replyUnowned);
228 }
229
calculateRoute()230 void tst_nokia_routing::calculateRoute()
231 {
232 QVERIFY2(m_replyUnowned, "No reply set");
233 m_calculationDone = false;
234 m_routingManager->calculateRoute(m_dummyRequest);
235 m_replyUnowned->complete();
236 m_replyUnowned = 0;
237 // Timeout of 200ms is required for slow targets (e.g. Qemu)
238 QTRY_VERIFY_WITH_TIMEOUT(m_calculationDone, 200);
239 }
240
onReply(QGeoRouteReply * reply)241 void tst_nokia_routing::onReply(QGeoRouteReply* reply)
242 {
243 QVERIFY(reply);
244 //QVERIFY(0 == m_reply);
245 m_reply = reply;
246 if (m_reply)
247 m_reply->setParent(0);
248 m_calculationDone = true;
249 }
250
verifySaneRoute(const QGeoRoute & route)251 void tst_nokia_routing::verifySaneRoute(const QGeoRoute& route)
252 {
253 QVERIFY(route.distance() > 0);
254 QVERIFY(route.travelTime() > 0);
255 QVERIFY(route.travelMode() != 0);
256
257 const QGeoRectangle bounds = route.bounds();
258 QVERIFY(bounds.width() > 0);
259 QVERIFY(bounds.height() > 0);
260
261 const QList<QGeoCoordinate> path = route.path();
262 QVERIFY(path.size() >= 2);
263
264 foreach (const QGeoCoordinate& coord, path) {
265 QVERIFY(coord.isValid());
266 QVERIFY(bounds.contains(coord));
267 }
268
269 QGeoRouteSegment segment = route.firstRouteSegment();
270 bool first = true, last = false;
271
272 do {
273 const QGeoRouteSegment next = segment.nextRouteSegment();
274 last = next.isValid();
275
276 QVERIFY(segment.isValid());
277 QVERIFY(segment.distance() >= 0);
278 QVERIFY(segment.travelTime() >= 0); // times are rounded and thus may end up being zero
279
280 const QList<QGeoCoordinate> path = segment.path();
281 foreach (const QGeoCoordinate& coord, path) {
282 QVERIFY(coord.isValid());
283 if (!first && !last) {
284 QVERIFY(bounds.contains(coord)); // on pt and pedestrian
285 }
286 }
287
288 const QGeoManeuver maneuver = segment.maneuver();
289
290 if (maneuver.isValid()) {
291 QVERIFY(!maneuver.instructionText().isEmpty());
292 QVERIFY(maneuver.position().isValid());
293 if (!first && !last) {
294 QVERIFY(bounds.contains(maneuver.position())); // on pt and pedestrian
295 }
296 }
297
298 segment = next;
299 first = false;
300 } while (!last);
301 }
302
routingFinished(QGeoRouteReply * reply)303 void tst_nokia_routing::routingFinished(QGeoRouteReply* reply)
304 {
305 onReply(reply);
306 }
307
routingError(QGeoRouteReply * reply,QGeoRouteReply::Error error,QString errorString)308 void tst_nokia_routing::routingError(QGeoRouteReply* reply, QGeoRouteReply::Error error, QString errorString)
309 {
310 Q_UNUSED(error);
311
312 if (!m_expectError) {
313 QFAIL(qPrintable(errorString));
314 } else {
315 onReply(reply);
316 }
317 }
318
initTestCase()319 void tst_nokia_routing::initTestCase()
320 {
321 QStringList providers = QGeoServiceProvider::availableServiceProviders();
322 QVERIFY(providers.contains(QStringLiteral("here")));
323
324 m_networkManager = new MockGeoNetworkAccessManager();
325
326 QVariantMap parameters;
327 parameters.insert(QStringLiteral("nam"), QVariant::fromValue<void*>(m_networkManager));
328 parameters.insert(QStringLiteral("here.app_id"), "stub");
329 parameters.insert(QStringLiteral("here.token"), "stub");
330
331 m_geoServiceProvider = new QGeoServiceProvider(QStringLiteral("here"), parameters);
332 QVERIFY(m_geoServiceProvider);
333
334 m_routingManager = m_geoServiceProvider->routingManager();
335 QVERIFY(m_routingManager);
336
337 connect(m_routingManager, SIGNAL(finished(QGeoRouteReply*)),
338 this, SLOT(routingFinished(QGeoRouteReply*)));
339 connect(m_routingManager, SIGNAL(error(QGeoRouteReply*,QGeoRouteReply::Error,QString)),
340 this, SLOT(routingError(QGeoRouteReply*,QGeoRouteReply::Error,QString)));
341
342 QList<QGeoCoordinate> waypoints;
343 waypoints.push_back(QGeoCoordinate(1, 1));
344 waypoints.push_back(QGeoCoordinate(2, 2));
345 m_dummyRequest.setWaypoints(waypoints);
346 }
347
cleanupTestCase()348 void tst_nokia_routing::cleanupTestCase()
349 {
350 delete m_geoServiceProvider;
351
352 // network access manager will be deleted by plugin
353
354 m_geoServiceProvider = 0;
355 m_networkManager = 0;
356 m_routingManager = 0;
357 }
358
cleanup()359 void tst_nokia_routing::cleanup()
360 {
361 delete m_reply;
362 m_reply = 0;
363 m_replyUnowned = 0;
364 m_expectError = false;
365 }
366
can_compute_route_for_all_supported_travel_modes()367 void tst_nokia_routing::can_compute_route_for_all_supported_travel_modes()
368 {
369 QFETCH(int, travelMode);
370 QFETCH(QString, file);
371 QFETCH(qreal, distance);
372 QFETCH(int, duration);
373
374 loadReply(file);
375 calculateRoute();
376
377 QList<QGeoRoute> routes = m_reply->routes();
378 QCOMPARE(1, routes.size());
379 QGeoRoute& route = routes[0];
380 QCOMPARE(travelMode, (int)route.travelMode());
381 CHECK_CLOSE(distance, route.distance());
382 QCOMPARE(duration, route.travelTime());
383 verifySaneRoute(route);
384 }
385
can_compute_route_for_all_supported_travel_modes_data()386 void tst_nokia_routing::can_compute_route_for_all_supported_travel_modes_data()
387 {
388 QTest::addColumn<int>("travelMode");
389 QTest::addColumn<QString>("file");
390 QTest::addColumn<qreal>("distance");
391 QTest::addColumn<int>("duration");
392
393 QTest::newRow("Car") << (int)QGeoRouteRequest::CarTravel << QString("travelmode-car.xml") << (qreal)1271.0 << 243;
394 QTest::newRow("Pedestrian") << (int)QGeoRouteRequest::PedestrianTravel << QString("travelmode-pedestrian.xml") << (qreal)1107.0 << 798;
395 QTest::newRow("Public Transport") << (int)QGeoRouteRequest::PublicTransitTravel << QString("travelmode-public-transport.xml") << (qreal)1388.0 << 641;
396 }
397
can_compute_route_for_all_supported_optimizations()398 void tst_nokia_routing::can_compute_route_for_all_supported_optimizations()
399 {
400 QFETCH(int, optimization);
401 QFETCH(QString, file);
402 QFETCH(qreal, distance);
403 QFETCH(int, duration);
404 m_dummyRequest.setRouteOptimization((QGeoRouteRequest::RouteOptimization)optimization);
405 loadReply(file);
406 calculateRoute();
407 QList<QGeoRoute> routes = m_reply->routes();
408 QCOMPARE(1, routes.size());
409 QGeoRoute& route = routes[0];
410 CHECK_CLOSE(distance, route.distance());
411 QCOMPARE(duration, route.travelTime());
412 verifySaneRoute(route);
413 }
414
can_compute_route_for_all_supported_optimizations_data()415 void tst_nokia_routing::can_compute_route_for_all_supported_optimizations_data()
416 {
417 QTest::addColumn<int>("optimization");
418 QTest::addColumn<QString>("file");
419 QTest::addColumn<qreal>("distance");
420 QTest::addColumn<int>("duration");
421
422 QTest::newRow("Shortest") << (int)QGeoRouteRequest::ShortestRoute << QString("optim-shortest.xml") << qreal(1177.0) << 309;
423 QTest::newRow("Fastest") << (int)QGeoRouteRequest::FastestRoute << QString("optim-fastest.xml") << qreal(1271.0) << 243;
424 }
425
can_handle_multiple_routes_in_response()426 void tst_nokia_routing::can_handle_multiple_routes_in_response()
427 {
428 loadReply(QStringLiteral("multiple-routes-in-response.xml"));
429 calculateRoute();
430 QList<QGeoRoute> routes = m_reply->routes();
431 QCOMPARE(2, routes.size());
432
433 verifySaneRoute(routes[0]);
434 verifySaneRoute(routes[1]);
435 }
436
can_handle_no_route_exists_case()437 void tst_nokia_routing::can_handle_no_route_exists_case()
438 {
439 loadReply(QStringLiteral("error-no-route.xml"));
440 calculateRoute();
441 QCOMPARE(QGeoRouteReply::NoError, m_reply->error());
442 QList<QGeoRoute> routes = m_reply->routes();
443 QCOMPARE(0, routes.size());
444 }
445
can_handle_additions_to_routing_xml()446 void tst_nokia_routing::can_handle_additions_to_routing_xml()
447 {
448 loadReply(QStringLiteral("littered-with-new-tags.xml"));
449 calculateRoute();
450 QCOMPARE(QGeoRouteReply::NoError, m_reply->error());
451 QList<QGeoRoute> routes = m_reply->routes();
452 QVERIFY(routes.size() > 0);
453 }
454
can_handle_invalid_server_responses()455 void tst_nokia_routing::can_handle_invalid_server_responses()
456 {
457 QFETCH(QString, file);
458
459 m_expectError = true;
460
461 loadReply(file);
462 calculateRoute();
463 QCOMPARE(QGeoRouteReply::ParseError, m_reply->error());
464 }
465
can_handle_invalid_server_responses_data()466 void tst_nokia_routing::can_handle_invalid_server_responses_data()
467 {
468 QTest::addColumn<QString>("file");
469
470 QTest::newRow("Trash") << QString("invalid-response-trash.xml");
471 QTest::newRow("Half way through") << QString("invalid-response-half-way-through.xml");
472 QTest::newRow("No <CalculateRoute> tag") << QString("invalid-response-no-calculateroute-tag.xml");
473 }
474
foobar()475 void tst_nokia_routing::foobar()
476 {
477 QFETCH(int, code);
478
479 m_expectError = true;
480 m_replyUnowned = new MockGeoNetworkReply();
481 m_replyUnowned->setError(static_cast<QNetworkReply::NetworkError>(code), QStringLiteral("Test error"));
482 m_networkManager->setReply(m_replyUnowned);
483 calculateRoute();
484 QCOMPARE(QGeoRouteReply::CommunicationError, m_reply->error());
485 }
486
foobar_data()487 void tst_nokia_routing::foobar_data()
488 {
489 QTest::addColumn<int>("code");
490
491 QTest::newRow("QNetworkReply::ConnectionRefusedError") << int(QNetworkReply::ConnectionRefusedError);
492 QTest::newRow("QNetworkReply::RemoteHostClosedError") << int(QNetworkReply::RemoteHostClosedError);
493 QTest::newRow("QNetworkReply::HostNotFoundError") << int(QNetworkReply::HostNotFoundError);
494 QTest::newRow("QNetworkReply::TimeoutError") << int(QNetworkReply::TimeoutError);
495 QTest::newRow("QNetworkReply::OperationCanceledError") << int(QNetworkReply::OperationCanceledError);
496 QTest::newRow("QNetworkReply::SslHandshakeFailedError") << int(QNetworkReply::SslHandshakeFailedError);
497 QTest::newRow("QNetworkReply::TemporaryNetworkFailureError") << int(QNetworkReply::TemporaryNetworkFailureError);
498 QTest::newRow("QNetworkReply::ProxyConnectionRefusedError") << int(QNetworkReply::ProxyConnectionRefusedError);
499 QTest::newRow("QNetworkReply::ProxyConnectionClosedError") << int(QNetworkReply::ProxyConnectionClosedError);
500 QTest::newRow("QNetworkReply::ProxyNotFoundError") << int(QNetworkReply::ProxyNotFoundError);
501 QTest::newRow("QNetworkReply::ProxyTimeoutError") << int(QNetworkReply::ProxyTimeoutError);
502 QTest::newRow("QNetworkReply::ProxyAuthenticationRequiredError") << int(QNetworkReply::ProxyAuthenticationRequiredError);
503 QTest::newRow("QNetworkReply::ContentAccessDenied") << int(QNetworkReply::ContentAccessDenied);
504 QTest::newRow("QNetworkReply::ContentOperationNotPermittedError") << int(QNetworkReply::ContentOperationNotPermittedError);
505 QTest::newRow("QNetworkReply::ContentNotFoundError") << int(QNetworkReply::ContentNotFoundError);
506 QTest::newRow("QNetworkReply::ContentReSendError") << int(QNetworkReply::ContentReSendError);
507 QTest::newRow("QNetworkReply::ProtocolUnknownError") << int(QNetworkReply::ProtocolUnknownError);
508 QTest::newRow("QNetworkReply::ProtocolInvalidOperationError") << int(QNetworkReply::ProtocolInvalidOperationError);
509 QTest::newRow("QNetworkReply::UnknownNetworkError") << int(QNetworkReply::UnknownNetworkError);
510 QTest::newRow("QNetworkReply::UnknownProxyError") << int(QNetworkReply::UnknownProxyError);
511 QTest::newRow("QNetworkReply::ProxyAuthenticationRequiredError") << int(QNetworkReply::ProxyAuthenticationRequiredError);
512 QTest::newRow("QNetworkReply::ProtocolFailure") << int(QNetworkReply::ProtocolFailure);
513 }
514
515
516 QTEST_MAIN(tst_nokia_routing)
517
518 #include "tst_routing.moc"
519