blob: 833c95af9c1612d646044ac16b50615fe1ebe781 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qgeonetworkaccessmanager.h>
#include <qgeoroutereply_nokia.h>
#include <QtTest/QtTest>
#include <QDebug>
#include <QNetworkReply>
#include <QtLocation/QGeoRouteReply>
#include <QtLocation/QGeoServiceProvider>
#include <QtLocation/QGeoRoutingManager>
#include <QtPositioning/QGeoRectangle>
#include <QtLocation/QGeoManeuver>
#include <QtLocation/QGeoRouteSegment>
QT_USE_NAMESPACE
#define CHECK_CLOSE_E(expected, actual, e) QVERIFY((qAbs(actual - expected) <= e))
#define CHECK_CLOSE(expected, actual) CHECK_CLOSE_E(expected, actual, qreal(1e-6))
class MockGeoNetworkReply : public QNetworkReply
{
public:
MockGeoNetworkReply( QObject* parent = 0);
virtual void abort();
void setFile(QFile* file);
void complete();
using QNetworkReply::setRequest;
using QNetworkReply::setOperation;
using QNetworkReply::setError;
protected:
virtual qint64 readData(char *data, qint64 maxlen);
virtual qint64 writeData(const char *data, qint64 len);
private:
QFile* m_file;
};
MockGeoNetworkReply::MockGeoNetworkReply(QObject* parent)
: QNetworkReply(parent)
, m_file(0)
{
setOpenMode(QIODevice::ReadOnly);
}
void MockGeoNetworkReply::abort()
{}
qint64 MockGeoNetworkReply::readData(char *data, qint64 maxlen)
{
if (m_file) {
const qint64 read = m_file->read(data, maxlen);
if (read <= 0)
return -1;
return read;
}
return -1;
}
qint64 MockGeoNetworkReply::writeData(const char *data, qint64 len)
{
Q_UNUSED(data);
Q_UNUSED(len);
return -1;
}
void MockGeoNetworkReply::setFile(QFile* file)
{
delete m_file;
m_file = file;
if (m_file)
m_file->setParent(this);
}
void MockGeoNetworkReply::complete()
{
if (error() != QNetworkReply::NoError)
emit error(error());
setFinished(true);
emit finished();
}
class MockGeoNetworkAccessManager : public QGeoNetworkAccessManager
{
public:
MockGeoNetworkAccessManager(QObject* parent = 0);
QNetworkReply* get(const QNetworkRequest& request);
QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data);
void setReply(MockGeoNetworkReply* reply);
private:
MockGeoNetworkReply* m_reply;
};
MockGeoNetworkAccessManager::MockGeoNetworkAccessManager(QObject* parent)
: QGeoNetworkAccessManager(parent)
, m_reply(0)
{}
QNetworkReply* MockGeoNetworkAccessManager::get(const QNetworkRequest& request)
{
MockGeoNetworkReply* r = m_reply;
m_reply = 0;
if (r) {
r->setRequest(request);
r->setOperation(QNetworkAccessManager::GetOperation);
r->setParent(0);
}
return r;
}
QNetworkReply* MockGeoNetworkAccessManager::post(const QNetworkRequest &request, const QByteArray &data)
{
Q_UNUSED(request);
Q_UNUSED(data);
QTest::qFail("Not implemented", __FILE__, __LINE__);
return new MockGeoNetworkReply();
}
void MockGeoNetworkAccessManager::setReply(MockGeoNetworkReply* reply)
{
delete m_reply;
m_reply = reply;
if (m_reply)
m_reply->setParent(this);
}
class tst_nokia_routing : public QObject
{
Q_OBJECT
public:
tst_nokia_routing();
private:
void calculateRoute();
void loadReply(const QString& filename);
void onReply(QGeoRouteReply* reply);
void verifySaneRoute(const QGeoRoute& route);
// Infrastructure slots
private Q_SLOTS:
void routingFinished(QGeoRouteReply* reply);
void routingError(QGeoRouteReply* reply, QGeoRouteReply::Error error, QString errorString);
// Test slots
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();
void cleanup();
void can_compute_route_for_all_supported_travel_modes();
void can_compute_route_for_all_supported_travel_modes_data();
void can_compute_route_for_all_supported_optimizations();
void can_compute_route_for_all_supported_optimizations_data();
void can_handle_multiple_routes_in_response();
void can_handle_no_route_exists_case();
void can_handle_invalid_server_responses();
void can_handle_invalid_server_responses_data();
void can_handle_additions_to_routing_xml();
void foobar();
void foobar_data();
private:
QGeoServiceProvider* m_geoServiceProvider;
MockGeoNetworkAccessManager* m_networkManager;
QGeoRoutingManager* m_routingManager;
QGeoRouteReply* m_reply;
MockGeoNetworkReply* m_replyUnowned;
QGeoRouteRequest m_dummyRequest;
bool m_calculationDone;
bool m_expectError;
};
tst_nokia_routing::tst_nokia_routing()
: m_geoServiceProvider(0)
, m_networkManager(0)
, m_routingManager(0)
, m_reply(0)
, m_replyUnowned()
, m_calculationDone(true)
, m_expectError(false)
{
}
void tst_nokia_routing::loadReply(const QString& filename)
{
QFile* file = new QFile(QFINDTESTDATA(filename));
if (!file->open(QIODevice::ReadOnly)) {
delete file;
file = 0;
qDebug() << filename;
QTest::qFail("Failed to open file", __FILE__, __LINE__);
}
m_replyUnowned = new MockGeoNetworkReply();
m_replyUnowned->setFile(file);
m_networkManager->setReply(m_replyUnowned);
}
void tst_nokia_routing::calculateRoute()
{
QVERIFY2(m_replyUnowned, "No reply set");
m_calculationDone = false;
m_routingManager->calculateRoute(m_dummyRequest);
m_replyUnowned->complete();
m_replyUnowned = 0;
// Timeout of 200ms is required for slow targets (e.g. Qemu)
QTRY_VERIFY_WITH_TIMEOUT(m_calculationDone, 200);
}
void tst_nokia_routing::onReply(QGeoRouteReply* reply)
{
QVERIFY(reply);
//QVERIFY(0 == m_reply);
m_reply = reply;
if (m_reply)
m_reply->setParent(0);
m_calculationDone = true;
}
void tst_nokia_routing::verifySaneRoute(const QGeoRoute& route)
{
QVERIFY(route.distance() > 0);
QVERIFY(route.travelTime() > 0);
QVERIFY(route.travelMode() != 0);
const QGeoRectangle bounds = route.bounds();
QVERIFY(bounds.width() > 0);
QVERIFY(bounds.height() > 0);
const QList<QGeoCoordinate> path = route.path();
QVERIFY(path.size() >= 2);
foreach (const QGeoCoordinate& coord, path) {
QVERIFY(coord.isValid());
QVERIFY(bounds.contains(coord));
}
QGeoRouteSegment segment = route.firstRouteSegment();
bool first = true, last = false;
do {
const QGeoRouteSegment next = segment.nextRouteSegment();
last = next.isValid();
QVERIFY(segment.isValid());
QVERIFY(segment.distance() >= 0);
QVERIFY(segment.travelTime() >= 0); // times are rounded and thus may end up being zero
const QList<QGeoCoordinate> path = segment.path();
foreach (const QGeoCoordinate& coord, path) {
QVERIFY(coord.isValid());
if (!first && !last) {
QVERIFY(bounds.contains(coord)); // on pt and pedestrian
}
}
const QGeoManeuver maneuver = segment.maneuver();
if (maneuver.isValid()) {
QVERIFY(!maneuver.instructionText().isEmpty());
QVERIFY(maneuver.position().isValid());
if (!first && !last) {
QVERIFY(bounds.contains(maneuver.position())); // on pt and pedestrian
}
}
segment = next;
first = false;
} while (!last);
}
void tst_nokia_routing::routingFinished(QGeoRouteReply* reply)
{
onReply(reply);
}
void tst_nokia_routing::routingError(QGeoRouteReply* reply, QGeoRouteReply::Error error, QString errorString)
{
Q_UNUSED(error);
if (!m_expectError) {
QFAIL(qPrintable(errorString));
} else {
onReply(reply);
}
}
void tst_nokia_routing::initTestCase()
{
QStringList providers = QGeoServiceProvider::availableServiceProviders();
QVERIFY(providers.contains(QStringLiteral("here")));
m_networkManager = new MockGeoNetworkAccessManager();
QVariantMap parameters;
parameters.insert(QStringLiteral("nam"), QVariant::fromValue<void*>(m_networkManager));
parameters.insert(QStringLiteral("here.app_id"), "stub");
parameters.insert(QStringLiteral("here.token"), "stub");
m_geoServiceProvider = new QGeoServiceProvider(QStringLiteral("here"), parameters);
QVERIFY(m_geoServiceProvider);
m_routingManager = m_geoServiceProvider->routingManager();
QVERIFY(m_routingManager);
connect(m_routingManager, SIGNAL(finished(QGeoRouteReply*)),
this, SLOT(routingFinished(QGeoRouteReply*)));
connect(m_routingManager, SIGNAL(error(QGeoRouteReply*,QGeoRouteReply::Error,QString)),
this, SLOT(routingError(QGeoRouteReply*,QGeoRouteReply::Error,QString)));
QList<QGeoCoordinate> waypoints;
waypoints.push_back(QGeoCoordinate(1, 1));
waypoints.push_back(QGeoCoordinate(2, 2));
m_dummyRequest.setWaypoints(waypoints);
}
void tst_nokia_routing::cleanupTestCase()
{
delete m_geoServiceProvider;
// network access manager will be deleted by plugin
m_geoServiceProvider = 0;
m_networkManager = 0;
m_routingManager = 0;
}
void tst_nokia_routing::cleanup()
{
delete m_reply;
m_reply = 0;
m_replyUnowned = 0;
m_expectError = false;
}
void tst_nokia_routing::can_compute_route_for_all_supported_travel_modes()
{
QFETCH(int, travelMode);
QFETCH(QString, file);
QFETCH(qreal, distance);
QFETCH(int, duration);
loadReply(file);
calculateRoute();
QList<QGeoRoute> routes = m_reply->routes();
QCOMPARE(1, routes.size());
QGeoRoute& route = routes[0];
QCOMPARE(travelMode, (int)route.travelMode());
CHECK_CLOSE(distance, route.distance());
QCOMPARE(duration, route.travelTime());
verifySaneRoute(route);
}
void tst_nokia_routing::can_compute_route_for_all_supported_travel_modes_data()
{
QTest::addColumn<int>("travelMode");
QTest::addColumn<QString>("file");
QTest::addColumn<qreal>("distance");
QTest::addColumn<int>("duration");
QTest::newRow("Car") << (int)QGeoRouteRequest::CarTravel << QString("travelmode-car.xml") << (qreal)1271.0 << 243;
QTest::newRow("Pedestrian") << (int)QGeoRouteRequest::PedestrianTravel << QString("travelmode-pedestrian.xml") << (qreal)1107.0 << 798;
QTest::newRow("Public Transport") << (int)QGeoRouteRequest::PublicTransitTravel << QString("travelmode-public-transport.xml") << (qreal)1388.0 << 641;
}
void tst_nokia_routing::can_compute_route_for_all_supported_optimizations()
{
QFETCH(int, optimization);
QFETCH(QString, file);
QFETCH(qreal, distance);
QFETCH(int, duration);
m_dummyRequest.setRouteOptimization((QGeoRouteRequest::RouteOptimization)optimization);
loadReply(file);
calculateRoute();
QList<QGeoRoute> routes = m_reply->routes();
QCOMPARE(1, routes.size());
QGeoRoute& route = routes[0];
CHECK_CLOSE(distance, route.distance());
QCOMPARE(duration, route.travelTime());
verifySaneRoute(route);
}
void tst_nokia_routing::can_compute_route_for_all_supported_optimizations_data()
{
QTest::addColumn<int>("optimization");
QTest::addColumn<QString>("file");
QTest::addColumn<qreal>("distance");
QTest::addColumn<int>("duration");
QTest::newRow("Shortest") << (int)QGeoRouteRequest::ShortestRoute << QString("optim-shortest.xml") << qreal(1177.0) << 309;
QTest::newRow("Fastest") << (int)QGeoRouteRequest::FastestRoute << QString("optim-fastest.xml") << qreal(1271.0) << 243;
}
void tst_nokia_routing::can_handle_multiple_routes_in_response()
{
loadReply(QStringLiteral("multiple-routes-in-response.xml"));
calculateRoute();
QList<QGeoRoute> routes = m_reply->routes();
QCOMPARE(2, routes.size());
verifySaneRoute(routes[0]);
verifySaneRoute(routes[1]);
}
void tst_nokia_routing::can_handle_no_route_exists_case()
{
loadReply(QStringLiteral("error-no-route.xml"));
calculateRoute();
QCOMPARE(QGeoRouteReply::NoError, m_reply->error());
QList<QGeoRoute> routes = m_reply->routes();
QCOMPARE(0, routes.size());
}
void tst_nokia_routing::can_handle_additions_to_routing_xml()
{
loadReply(QStringLiteral("littered-with-new-tags.xml"));
calculateRoute();
QCOMPARE(QGeoRouteReply::NoError, m_reply->error());
QList<QGeoRoute> routes = m_reply->routes();
QVERIFY(routes.size() > 0);
}
void tst_nokia_routing::can_handle_invalid_server_responses()
{
QFETCH(QString, file);
m_expectError = true;
loadReply(file);
calculateRoute();
QCOMPARE(QGeoRouteReply::ParseError, m_reply->error());
}
void tst_nokia_routing::can_handle_invalid_server_responses_data()
{
QTest::addColumn<QString>("file");
QTest::newRow("Trash") << QString("invalid-response-trash.xml");
QTest::newRow("Half way through") << QString("invalid-response-half-way-through.xml");
QTest::newRow("No <CalculateRoute> tag") << QString("invalid-response-no-calculateroute-tag.xml");
}
void tst_nokia_routing::foobar()
{
QFETCH(int, code);
m_expectError = true;
m_replyUnowned = new MockGeoNetworkReply();
m_replyUnowned->setError(static_cast<QNetworkReply::NetworkError>(code), QStringLiteral("Test error"));
m_networkManager->setReply(m_replyUnowned);
calculateRoute();
QCOMPARE(QGeoRouteReply::CommunicationError, m_reply->error());
}
void tst_nokia_routing::foobar_data()
{
QTest::addColumn<int>("code");
QTest::newRow("QNetworkReply::ConnectionRefusedError") << int(QNetworkReply::ConnectionRefusedError);
QTest::newRow("QNetworkReply::RemoteHostClosedError") << int(QNetworkReply::RemoteHostClosedError);
QTest::newRow("QNetworkReply::HostNotFoundError") << int(QNetworkReply::HostNotFoundError);
QTest::newRow("QNetworkReply::TimeoutError") << int(QNetworkReply::TimeoutError);
QTest::newRow("QNetworkReply::OperationCanceledError") << int(QNetworkReply::OperationCanceledError);
QTest::newRow("QNetworkReply::SslHandshakeFailedError") << int(QNetworkReply::SslHandshakeFailedError);
QTest::newRow("QNetworkReply::TemporaryNetworkFailureError") << int(QNetworkReply::TemporaryNetworkFailureError);
QTest::newRow("QNetworkReply::ProxyConnectionRefusedError") << int(QNetworkReply::ProxyConnectionRefusedError);
QTest::newRow("QNetworkReply::ProxyConnectionClosedError") << int(QNetworkReply::ProxyConnectionClosedError);
QTest::newRow("QNetworkReply::ProxyNotFoundError") << int(QNetworkReply::ProxyNotFoundError);
QTest::newRow("QNetworkReply::ProxyTimeoutError") << int(QNetworkReply::ProxyTimeoutError);
QTest::newRow("QNetworkReply::ProxyAuthenticationRequiredError") << int(QNetworkReply::ProxyAuthenticationRequiredError);
QTest::newRow("QNetworkReply::ContentAccessDenied") << int(QNetworkReply::ContentAccessDenied);
QTest::newRow("QNetworkReply::ContentOperationNotPermittedError") << int(QNetworkReply::ContentOperationNotPermittedError);
QTest::newRow("QNetworkReply::ContentNotFoundError") << int(QNetworkReply::ContentNotFoundError);
QTest::newRow("QNetworkReply::ContentReSendError") << int(QNetworkReply::ContentReSendError);
QTest::newRow("QNetworkReply::ProtocolUnknownError") << int(QNetworkReply::ProtocolUnknownError);
QTest::newRow("QNetworkReply::ProtocolInvalidOperationError") << int(QNetworkReply::ProtocolInvalidOperationError);
QTest::newRow("QNetworkReply::UnknownNetworkError") << int(QNetworkReply::UnknownNetworkError);
QTest::newRow("QNetworkReply::UnknownProxyError") << int(QNetworkReply::UnknownProxyError);
QTest::newRow("QNetworkReply::ProxyAuthenticationRequiredError") << int(QNetworkReply::ProxyAuthenticationRequiredError);
QTest::newRow("QNetworkReply::ProtocolFailure") << int(QNetworkReply::ProtocolFailure);
}
QTEST_MAIN(tst_nokia_routing)
#include "tst_routing.moc"