1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 Jolla Ltd.
4 ** Contact: Aaron McCarthy <aaron.mccarthy@jollamobile.com>
5 ** Copyright (C) 2016 The Qt Company Ltd.
6 ** Contact: https://www.qt.io/licensing/
7 **
8 ** This file is part of the test suite of the Qt Toolkit.
9 **
10 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
11 ** Commercial License Usage
12 ** Licensees holding valid commercial Qt licenses may use this file in
13 ** accordance with the commercial license agreement provided with the
14 ** Software or, alternatively, in accordance with the terms contained in
15 ** a written agreement between you and The Qt Company. For licensing terms
16 ** and conditions see https://www.qt.io/terms-conditions. For further
17 ** information use the contact form at https://www.qt.io/contact-us.
18 **
19 ** GNU General Public License Usage
20 ** Alternatively, this file may be used under the terms of the GNU
21 ** General Public License version 3 as published by the Free Software
22 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
23 ** included in the packaging of this file. Please review the following
24 ** information to ensure the GNU General Public License requirements will
25 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
26 **
27 ** $QT_END_LICENSE$
28 **
29 ****************************************************************************/
30
31 //TESTED_COMPONENT=src/location
32
33 #include "tst_qnmeapositioninfosource.h"
34
35 #include <QtCore/QDateTime>
36 #include <QtCore/QElapsedTimer>
37 #include <QtCore/QtNumeric>
38
39 #ifdef Q_OS_WIN
40
41 // Windows seems to require longer timeouts and step length
42 // We override the standard QTestCase related macros
43
44 #ifdef QTRY_COMPARE_WITH_TIMEOUT
45 #undef QTRY_COMPARE_WITH_TIMEOUT
46 #endif
47 #define QTRY_COMPARE_WITH_TIMEOUT(__expr, __expected, __timeout) \
48 do { \
49 const int __step = 100; \
50 const int __timeoutValue = __timeout; \
51 if ((__expr) != (__expected)) { \
52 QTest::qWait(0); \
53 } \
54 for (int __i = 0; __i < __timeoutValue && ((__expr) != (__expected)); __i+=__step) { \
55 QTest::qWait(__step); \
56 } \
57 QCOMPARE(__expr, __expected); \
58 } while (0)
59
60 #ifdef QTRY_COMPARE
61 #undef QTRY_COMPARE
62 #endif
63 #define QTRY_COMPARE(__expr, __expected) QTRY_COMPARE_WITH_TIMEOUT(__expr, __expected, 10000)
64
65 #endif
66
tst_QNmeaPositionInfoSource(QNmeaPositionInfoSource::UpdateMode mode,QObject * parent)67 tst_QNmeaPositionInfoSource::tst_QNmeaPositionInfoSource(QNmeaPositionInfoSource::UpdateMode mode, QObject *parent)
68 : QObject(parent),
69 m_mode(mode)
70 {
71 }
72
initTestCase()73 void tst_QNmeaPositionInfoSource::initTestCase()
74 {
75 qRegisterMetaType<QNmeaPositionInfoSource::UpdateMode>();
76 }
77
constructor()78 void tst_QNmeaPositionInfoSource::constructor()
79 {
80 QObject o;
81 QNmeaPositionInfoSource source(m_mode, &o);
82 QCOMPARE(source.updateMode(), m_mode);
83 QCOMPARE(source.parent(), &o);
84 }
85
supportedPositioningMethods()86 void tst_QNmeaPositionInfoSource::supportedPositioningMethods()
87 {
88 QNmeaPositionInfoSource source(m_mode);
89 QCOMPARE(source.supportedPositioningMethods(), QNmeaPositionInfoSource::SatellitePositioningMethods);
90 }
91
minimumUpdateInterval()92 void tst_QNmeaPositionInfoSource::minimumUpdateInterval()
93 {
94 QNmeaPositionInfoSource source(m_mode);
95 QCOMPARE(source.minimumUpdateInterval(), 2);
96 }
97
userEquivalentRangeError()98 void tst_QNmeaPositionInfoSource::userEquivalentRangeError()
99 {
100 QNmeaPositionInfoSource source(m_mode);
101 QVERIFY(qIsNaN(source.userEquivalentRangeError()));
102 source.setUserEquivalentRangeError(5.1);
103 QVERIFY(qFuzzyCompare(source.userEquivalentRangeError(), 5.1));
104 }
105
setUpdateInterval_delayedUpdate()106 void tst_QNmeaPositionInfoSource::setUpdateInterval_delayedUpdate()
107 {
108 // If an update interval is set, and an update is not available at a
109 // particular interval, the source should emit the next update as soon
110 // as it becomes available
111
112 QNmeaPositionInfoSource source(m_mode);
113 QNmeaPositionInfoSourceProxyFactory factory;
114 QNmeaPositionInfoSourceProxy *proxy = static_cast<QNmeaPositionInfoSourceProxy*>(factory.createProxy(&source));
115
116 QSignalSpy spyUpdate(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo)));
117 proxy->source()->setUpdateInterval(500);
118 proxy->source()->startUpdates();
119
120 QTest::qWait(600);
121 QDateTime now = QDateTime::currentDateTime();
122 proxy->feedUpdate(now);
123 QTRY_COMPARE(spyUpdate.count(), 1);
124
125 // should have gotten the update immediately, and not have needed to
126 // wait until the next interval
127 QVERIFY(now.time().msecsTo(QDateTime::currentDateTime().time()) < 200);
128 }
129
lastKnownPosition()130 void tst_QNmeaPositionInfoSource::lastKnownPosition()
131 {
132 QNmeaPositionInfoSource source(m_mode);
133 QNmeaPositionInfoSourceProxyFactory factory;
134 QNmeaPositionInfoSourceProxy *proxy = static_cast<QNmeaPositionInfoSourceProxy*>(factory.createProxy(&source));
135
136 QCOMPARE(proxy->source()->lastKnownPosition(), QGeoPositionInfo());
137
138 // source may need requestUpdate() or startUpdates() to be called to
139 // trigger reading of data channel
140 QSignalSpy spyTimeout(proxy->source(), SIGNAL(updateTimeout()));
141 proxy->source()->requestUpdate(proxy->source()->minimumUpdateInterval());
142 QTRY_COMPARE(spyTimeout.count(), 1);
143
144 // If an update is received and startUpdates() or requestUpdate() hasn't
145 // been called, it should still be available through lastKnownPosition()
146 QDateTime dt = QDateTime::currentDateTime().toUTC();
147 proxy->feedUpdate(dt);
148 QTRY_COMPARE(proxy->source()->lastKnownPosition().timestamp(), dt);
149
150 QList<QDateTime> dateTimes = createDateTimes(5);
151 for (int i=0; i<dateTimes.count(); i++) {
152 proxy->source()->requestUpdate(); // Irrelevant for this test
153 proxy->feedUpdate(dateTimes[i]);
154 QTRY_COMPARE(proxy->source()->lastKnownPosition().timestamp(), dateTimes[i]);
155 }
156
157 proxy->source()->startUpdates();
158 // if dateTimes are older than before, they will be ignored.
159 dateTimes = createDateTimes(dateTimes.last().addMSecs(100), 5);
160 for (int i=0; i<dateTimes.count(); i++) {
161 proxy->feedUpdate(dateTimes[i]);
162 QTRY_COMPARE(proxy->source()->lastKnownPosition().timestamp(), dateTimes[i]);
163 }
164 }
165
beginWithBufferedData()166 void tst_QNmeaPositionInfoSource::beginWithBufferedData()
167 {
168 // In SimulationMode, data stored in the QIODevice is read when
169 // startUpdates() or requestUpdate() is called.
170 // In RealTimeMode, all existing data in the QIODevice is ignored -
171 // only new data will be read.
172
173 QFETCH(QList<QDateTime>, dateTimes);
174 QFETCH(UpdateTriggerMethod, trigger);
175
176 QByteArray bytes;
177 for (int i=0; i<dateTimes.count(); i++)
178 bytes += QLocationTestUtils::createRmcSentence(dateTimes[i]).toLatin1();
179 QBuffer buffer;
180 buffer.setData(bytes);
181
182 QNmeaPositionInfoSource source(m_mode);
183 QSignalSpy spy(&source, SIGNAL(positionUpdated(QGeoPositionInfo)));
184 source.setDevice(&buffer);
185
186 if (trigger == StartUpdatesMethod)
187 source.startUpdates();
188 else if (trigger == RequestUpdatesMethod)
189 source.requestUpdate();
190
191 if (m_mode == QNmeaPositionInfoSource::RealTimeMode) {
192 QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 0, 300);
193 } else {
194 if (trigger == StartUpdatesMethod) {
195 QTRY_COMPARE(spy.count(), dateTimes.count());
196 for (int i=0; i<dateTimes.count(); i++)
197 QCOMPARE(spy.at(i).at(0).value<QGeoPositionInfo>().timestamp(), dateTimes[i]);
198 } else if (trigger == RequestUpdatesMethod) {
199 QTRY_COMPARE(spy.count(), 1);
200 QCOMPARE(spy.at(0).at(0).value<QGeoPositionInfo>().timestamp(), dateTimes.first());
201 }
202 }
203 }
204
beginWithBufferedData_data()205 void tst_QNmeaPositionInfoSource::beginWithBufferedData_data()
206 {
207 QTest::addColumn<QList<QDateTime> >("dateTimes");
208 QTest::addColumn<UpdateTriggerMethod>("trigger");
209
210 QList<QDateTime> dateTimes;
211 dateTimes << QDateTime::currentDateTime().toUTC();
212
213 QTest::newRow("startUpdates(), 1 update in buffer") << dateTimes << StartUpdatesMethod;
214 QTest::newRow("requestUpdate(), 1 update in buffer") << dateTimes << RequestUpdatesMethod;
215
216 for (int i=1; i<3; i++)
217 dateTimes << dateTimes[0].addMSecs(i * 100);
218 QTest::newRow("startUpdates(), multiple updates in buffer") << dateTimes << StartUpdatesMethod;
219 QTest::newRow("requestUpdate(), multiple updates in buffer") << dateTimes << RequestUpdatesMethod;
220 }
221
startUpdates()222 void tst_QNmeaPositionInfoSource::startUpdates()
223 {
224 QFETCH(QList<QDateTime>, dateTimes);
225
226 QNmeaPositionInfoSource source(m_mode);
227 QNmeaPositionInfoSourceProxyFactory factory;
228 QNmeaPositionInfoSourceProxy *proxy = static_cast<QNmeaPositionInfoSourceProxy*>(factory.createProxy(&source));
229
230 QSignalSpy spyUpdate(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo)));
231 proxy->source()->startUpdates();
232
233 for (int i=0; i<dateTimes.count(); i++)
234 proxy->feedUpdate(dateTimes[i]);
235 QTRY_COMPARE(spyUpdate.count(), dateTimes.count());
236 }
237
startUpdates_data()238 void tst_QNmeaPositionInfoSource::startUpdates_data()
239 {
240 QTest::addColumn<QList<QDateTime> >("dateTimes");
241
242 QTest::newRow("1 update") << createDateTimes(1);
243 QTest::newRow("2 updates") << createDateTimes(2);
244 QTest::newRow("10 updates") << createDateTimes(10);
245 }
246
startUpdates_withTimeout()247 void tst_QNmeaPositionInfoSource::startUpdates_withTimeout()
248 {
249 QNmeaPositionInfoSource source(m_mode);
250 QNmeaPositionInfoSourceProxyFactory factory;
251 QNmeaPositionInfoSourceProxy *proxy = static_cast<QNmeaPositionInfoSourceProxy*>(factory.createProxy(&source));
252
253 QSignalSpy spyUpdate(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo)));
254 QSignalSpy spyTimeout(proxy->source(), SIGNAL(updateTimeout()));
255
256 proxy->source()->setUpdateInterval(1000);
257 proxy->source()->startUpdates();
258
259 QDateTime dt = QDateTime::currentDateTime().toUTC();
260
261 if (m_mode == QNmeaPositionInfoSource::SimulationMode) {
262 // the first sentence primes the simulation
263 proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt).toLatin1());
264 proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt.addMSecs(10)).toLatin1());
265 proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt.addMSecs(1100)).toLatin1());
266 proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt.addMSecs(2200)).toLatin1());
267 proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt.addSecs(9)).toLatin1());
268
269 QElapsedTimer t;
270 t.start();
271
272 for (int j = 1; j < 4; ++j) {
273 QTRY_COMPARE(spyUpdate.count(), j);
274 QCOMPARE(spyTimeout.count(), 0);
275 int time = t.elapsed();
276 QVERIFY((time > j*1000 - 300) && (time < j*1000 + 300));
277 }
278
279 spyUpdate.clear();
280
281 QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() == 0) && (spyTimeout.count() == 1), 7500);
282 spyTimeout.clear();
283
284 QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() == 1) && (spyTimeout.count() == 0), 7500);
285
286 } else {
287 // dt + 900
288 QTRY_VERIFY(spyUpdate.count() == 0 && spyTimeout.count() == 0);
289
290 proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt.addSecs(1)).toLatin1());
291 // dt + 1200
292 QTRY_VERIFY(spyUpdate.count() == 1 && spyTimeout.count() == 0);
293 spyUpdate.clear();
294
295 // dt + 1900
296 QTRY_VERIFY(spyUpdate.count() == 0 && spyTimeout.count() == 0);
297 proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt.addSecs(2)).toLatin1());
298
299 // dt + 2200
300 QTRY_VERIFY(spyUpdate.count() == 1 && spyTimeout.count() == 0);
301 spyUpdate.clear();
302
303 // dt + 2900
304 QTRY_VERIFY(spyUpdate.count() == 0 && spyTimeout.count() == 0);
305 proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt.addSecs(3)).toLatin1());
306
307 // dt + 3200
308 QTRY_VERIFY(spyUpdate.count() == 1 && spyTimeout.count() == 0);
309 spyUpdate.clear();
310
311 // dt + 6900
312 QTRY_VERIFY(spyUpdate.count() == 0 && spyTimeout.count() == 1);
313 spyTimeout.clear();
314 proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt.addSecs(7)).toLatin1());
315
316 // dt + 7200
317 QTRY_VERIFY(spyUpdate.count() == 1 && spyTimeout.count() == 0);
318 spyUpdate.clear();
319 }
320 }
321
startUpdates_expectLatestUpdateOnly()322 void tst_QNmeaPositionInfoSource::startUpdates_expectLatestUpdateOnly()
323 {
324 // If startUpdates() is called and an interval has been set, if multiple'
325 // updates are in the buffer, only the latest update should be emitted
326
327 QNmeaPositionInfoSource source(m_mode);
328 QNmeaPositionInfoSourceProxyFactory factory;
329 QNmeaPositionInfoSourceProxy *proxy = static_cast<QNmeaPositionInfoSourceProxy*>(factory.createProxy(&source));
330
331 QSignalSpy spyUpdate(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo)));
332 proxy->source()->setUpdateInterval(500);
333 proxy->source()->startUpdates();
334
335 QList<QDateTime> dateTimes = createDateTimes(3);
336 for (int i=0; i<dateTimes.count(); i++)
337 proxy->feedUpdate(dateTimes[i]);
338
339 QTRY_COMPARE(spyUpdate.count(), 1);
340 QCOMPARE(spyUpdate[0][0].value<QGeoPositionInfo>().timestamp(), dateTimes.last());
341 }
342
startUpdates_waitForValidDateTime()343 void tst_QNmeaPositionInfoSource::startUpdates_waitForValidDateTime()
344 {
345 // Tests that the class does not emit an update until it receives a
346 // sentences with a valid date *and* time. All sentences before this
347 // should be ignored, and any sentences received after this that do
348 // not have a date should use the known date.
349
350 QFETCH(QByteArray, bytes);
351 QFETCH(QList<QDateTime>, dateTimes);
352 QFETCH(QList<bool>, expectHorizontalAccuracy);
353 QFETCH(QList<bool>, expectVerticalAccuracy);
354
355 QNmeaPositionInfoSource source(m_mode);
356 source.setUserEquivalentRangeError(5.1);
357 QNmeaPositionInfoSourceProxyFactory factory;
358 QNmeaPositionInfoSourceProxy *proxy = static_cast<QNmeaPositionInfoSourceProxy*>(factory.createProxy(&source));
359
360 QSignalSpy spy(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo)));
361 QObject::connect(proxy->source(), &QNmeaPositionInfoSource::positionUpdated, [](const QGeoPositionInfo &info) {
362 qDebug() << info.timestamp();
363 });
364
365 proxy->source()->startUpdates();
366 proxy->feedBytes(bytes);
367 QTest::qWait(1000); // default push delay is 20ms
368 QTRY_COMPARE(spy.count(), dateTimes.count());
369
370 for (int i=0; i<spy.count(); i++) {
371 QGeoPositionInfo pInfo = spy[i][0].value<QGeoPositionInfo>();
372
373 QCOMPARE(pInfo.timestamp(), dateTimes[i]);
374
375 // Generated GGA/GSA sentences have hard coded HDOP of 3.5, which corrisponds to a
376 // horizontal accuracy of 35.7, for the user equivalent range error of 5.1 set above.
377 QCOMPARE(pInfo.hasAttribute(QGeoPositionInfo::HorizontalAccuracy),
378 expectHorizontalAccuracy[i]);
379 if (pInfo.hasAttribute(QGeoPositionInfo::HorizontalAccuracy))
380 QVERIFY(qFuzzyCompare(pInfo.attribute(QGeoPositionInfo::HorizontalAccuracy), 35.7));
381
382 // Generated GSA sentences have hard coded VDOP of 4.0, which corrisponds to a vertical
383 // accuracy of 40.8, for the user equivalent range error of 5.1 set above.
384 QCOMPARE(pInfo.hasAttribute(QGeoPositionInfo::VerticalAccuracy),
385 expectVerticalAccuracy[i]);
386 if (pInfo.hasAttribute(QGeoPositionInfo::VerticalAccuracy))
387 QVERIFY(qFuzzyCompare(pInfo.attribute(QGeoPositionInfo::VerticalAccuracy), 40.8));
388 }
389 }
390
startUpdates_waitForValidDateTime_data()391 void tst_QNmeaPositionInfoSource::startUpdates_waitForValidDateTime_data()
392 {
393 QTest::addColumn<QByteArray>("bytes");
394 QTest::addColumn<QList<QDateTime> >("dateTimes");
395 QTest::addColumn<QList<bool> >("expectHorizontalAccuracy");
396 QTest::addColumn<QList<bool> >("expectVerticalAccuracy");
397
398 QDateTime dt = QDateTime::currentDateTime().toUTC();
399 QByteArray bytes;
400
401 // should only receive RMC sentence and the GGA sentence *after* it
402 bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(100).time()).toLatin1();
403 bytes += QLocationTestUtils::createRmcSentence(dt.addMSecs(200)).toLatin1();
404 bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(300).time()).toLatin1();
405 // The first GGA does not have date, and there's no cached date to inject, so that update will be invalid
406 QTest::newRow("Feed GGA,RMC,GGA; expect RMC, second GGA only")
407 << bytes << (QList<QDateTime>() << dt.addMSecs(200) << dt.addMSecs(300))
408 << (QList<bool>() << true << true) // accuracies are currently cached and injected in QGeoPositionInfos that do not have it
409 << (QList<bool>() << false << false);
410
411 // should not receive ZDA (has no coordinates) but should get the GGA
412 // sentence after it since it got the date/time from ZDA
413 bytes.clear();
414 bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(100).time()).toLatin1();
415 bytes += QLocationTestUtils::createZdaSentence(dt.addMSecs(200)).toLatin1();
416 bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(300).time()).toLatin1();
417 QTest::newRow("Feed GGA,ZDA,GGA; expect second GGA only")
418 << bytes << (QList<QDateTime>() << dt.addMSecs(300))
419 << (QList<bool>() << true)
420 << (QList<bool>() << false);
421
422 // Feed ZDA,GGA,GSA,GGA; expect vertical accuracy from second GGA.
423 bytes.clear();
424 bytes += QLocationTestUtils::createZdaSentence(dt.addMSecs(100)).toLatin1();
425 bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(200).time()).toLatin1();
426 bytes += QLocationTestUtils::createGsaSentence().toLatin1();
427 bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(300).time()).toLatin1();
428 if (m_mode == QNmeaPositionInfoSource::SimulationMode) {
429 QTest::newRow("Feed ZDA,GGA,GSA,GGA; expect vertical accuracy from second GGA")
430 << bytes << (QList<QDateTime>() << dt.addMSecs(200) << dt.addMSecs(300))
431 << (QList<bool>() << true << true)
432 << (QList<bool>() << true << true); // First GGA gets VDOP from GSA bundled into previous, as it has no timestamp, second GGA gets the cached value.
433 }
434
435 if (m_mode == QNmeaPositionInfoSource::SimulationMode) {
436 // In sim m_mode, should ignore sentence with a date/time before the known date/time
437 // (in real time m_mode, everything is passed on regardless)
438 bytes.clear();
439 bytes += QLocationTestUtils::createRmcSentence(dt.addMSecs(100)).toLatin1();
440 bytes += QLocationTestUtils::createRmcSentence(dt.addMSecs(-200)).toLatin1();
441 bytes += QLocationTestUtils::createRmcSentence(dt.addMSecs(200)).toLatin1();
442 QTest::newRow("Feed good RMC, RMC with bad date/time, good RMC; expect first and third RMC only")
443 << bytes << (QList<QDateTime>() << dt.addMSecs(100) << dt.addMSecs(200))
444 << (QList<bool>() << false << false)
445 << (QList<bool>() << false << false);
446 }
447 }
448
requestUpdate_waitForValidDateTime()449 void tst_QNmeaPositionInfoSource::requestUpdate_waitForValidDateTime()
450 {
451 QFETCH(QByteArray, bytes);
452 QFETCH(QList<QDateTime>, dateTimes);
453
454 QNmeaPositionInfoSource source(m_mode);
455 QNmeaPositionInfoSourceProxyFactory factory;
456 QNmeaPositionInfoSourceProxy *proxy = static_cast<QNmeaPositionInfoSourceProxy*>(factory.createProxy(&source));
457
458 QSignalSpy spy(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo)));
459 proxy->source()->requestUpdate();
460
461 proxy->feedBytes(bytes);
462 QTRY_COMPARE(spy.count(), 1);
463 QCOMPARE(spy[0][0].value<QGeoPositionInfo>().timestamp(), dateTimes[0]);
464 }
465
requestUpdate_waitForValidDateTime_data()466 void tst_QNmeaPositionInfoSource::requestUpdate_waitForValidDateTime_data()
467 {
468 startUpdates_waitForValidDateTime_data();
469 }
470
requestUpdate()471 void tst_QNmeaPositionInfoSource::requestUpdate()
472 {
473 QNmeaPositionInfoSource source(m_mode);
474 QNmeaPositionInfoSourceProxyFactory factory;
475 QNmeaPositionInfoSourceProxy *proxy = static_cast<QNmeaPositionInfoSourceProxy*>(factory.createProxy(&source));
476
477 QSignalSpy spyUpdate(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo)));
478 QSignalSpy spyTimeout(proxy->source(), SIGNAL(updateTimeout()));
479 QDateTime dt;
480
481 proxy->source()->requestUpdate(100);
482 QTRY_COMPARE(spyTimeout.count(), 1);
483 spyTimeout.clear();
484
485 dt = QDateTime::currentDateTime().toUTC();
486 proxy->feedUpdate(dt);
487 proxy->source()->requestUpdate();
488 QTRY_COMPARE(spyUpdate.count(), 1);
489 QCOMPARE(spyUpdate[0][0].value<QGeoPositionInfo>().timestamp(), dt);
490 QCOMPARE(spyTimeout.count(), 0);
491 spyUpdate.clear();
492
493 // delay the update and expect it to be emitted after 300ms
494 dt = QDateTime::currentDateTime().toUTC();
495 proxy->source()->requestUpdate(1000);
496 QTest::qWait(300);
497 proxy->feedUpdate(dt);
498 QTRY_COMPARE(spyUpdate.count(), 1);
499 QCOMPARE(spyUpdate[0][0].value<QGeoPositionInfo>().timestamp(), dt);
500 QCOMPARE(spyTimeout.count(), 0);
501 spyUpdate.clear();
502
503 // delay the update and expect updateTimeout() to be emitted
504 dt = QDateTime::currentDateTime().toUTC();
505 proxy->source()->requestUpdate(500);
506 QTest::qWait(1000);
507 proxy->feedUpdate(dt);
508 QCOMPARE(spyTimeout.count(), 1);
509 QCOMPARE(spyUpdate.count(), 0);
510 spyUpdate.clear();
511 }
512
requestUpdate_after_start()513 void tst_QNmeaPositionInfoSource::requestUpdate_after_start()
514 {
515 QNmeaPositionInfoSource source(m_mode);
516 QNmeaPositionInfoSourceProxyFactory factory;
517 QNmeaPositionInfoSourceProxy *proxy = static_cast<QNmeaPositionInfoSourceProxy*>(factory.createProxy(&source));
518
519 QSignalSpy spyUpdate(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo)));
520 QSignalSpy spyTimeout(proxy->source(), SIGNAL(updateTimeout()));
521
522 // Start updates with 500ms interval and requestUpdate() with 100ms
523 // timeout. Feed an update, and it should be emitted immediately due to
524 // the requestUpdate(). The update should not be emitted again after that
525 // (i.e. the startUpdates() interval should not cause it to be re-emitted).
526
527 QDateTime dt = QDateTime::currentDateTime().toUTC();
528 proxy->source()->setUpdateInterval(500);
529 proxy->source()->startUpdates();
530 proxy->source()->requestUpdate(100);
531 proxy->feedUpdate(dt);
532 QTRY_COMPARE(spyUpdate.count(), 1);
533 QCOMPARE(spyUpdate[0][0].value<QGeoPositionInfo>().timestamp(), dt);
534 QCOMPARE(spyTimeout.count(), 0);
535 spyUpdate.clear();
536
537 // Update has been emitted for requestUpdate(), shouldn't be emitted for startUpdates()
538 QTRY_COMPARE_WITH_TIMEOUT(spyUpdate.count(), 0, 1000);
539 }
540
testWithBadNmea()541 void tst_QNmeaPositionInfoSource::testWithBadNmea()
542 {
543 QFETCH(QByteArray, bytes);
544 QFETCH(QList<QDateTime>, dateTimes);
545 QFETCH(UpdateTriggerMethod, trigger);
546
547 QNmeaPositionInfoSource source(m_mode);
548 QNmeaPositionInfoSourceProxyFactory factory;
549 QNmeaPositionInfoSourceProxy *proxy = static_cast<QNmeaPositionInfoSourceProxy*>(factory.createProxy(&source));
550
551 QSignalSpy spy(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo)));
552 if (trigger == StartUpdatesMethod)
553 proxy->source()->startUpdates();
554 else
555 proxy->source()->requestUpdate();
556
557 proxy->feedBytes(bytes);
558 QTRY_COMPARE(spy.count(), dateTimes.count());
559 for (int i=0; i<dateTimes.count(); i++)
560 QCOMPARE(spy[i][0].value<QGeoPositionInfo>().timestamp(), dateTimes[i]);
561 }
562
testWithBadNmea_data()563 void tst_QNmeaPositionInfoSource::testWithBadNmea_data()
564 {
565 QTest::addColumn<QByteArray>("bytes");
566 QTest::addColumn<QList<QDateTime> >("dateTimes");
567 QTest::addColumn<UpdateTriggerMethod>("trigger");
568
569 QDateTime firstDateTime = QDateTime::currentDateTime().toUTC();
570 QByteArray bad = QLocationTestUtils::createRmcSentence(firstDateTime.addSecs(1)).toLatin1();
571 bad = bad.mid(bad.length()/2);
572 QDateTime lastDateTime = firstDateTime.addSecs(2);
573
574 QByteArray bytes;
575 bytes += QLocationTestUtils::createRmcSentence(firstDateTime).toLatin1();
576 bytes += bad;
577 bytes += QLocationTestUtils::createRmcSentence(lastDateTime).toLatin1();
578 QTest::newRow("requestUpdate(), bad second sentence") << bytes
579 << (QList<QDateTime>() << firstDateTime) << RequestUpdatesMethod;
580 QTest::newRow("startUpdates(), bad second sentence") << bytes
581 << (QList<QDateTime>() << firstDateTime << lastDateTime) << StartUpdatesMethod;
582 }
583