Revision control

Copy as Markdown

// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QTimer>
#include <QSignalSpy>
#include <QtPositioning/QNmeaSatelliteInfoSource>
#include "../../utils/qlocationtestutils_p.h"
class DataFeeder : public QIODevice
{
Q_OBJECT
public:
DataFeeder(QNmeaSatelliteInfoSource *parent)
: QIODevice(parent), m_parentMode(parent->updateMode())
{
}
void setMessages(const QList<QByteArray> &messages)
{
m_messages = messages;
m_canReadLine = true;
m_messageIndex = 0;
if (m_parentMode == QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode)
emit readyRead();
}
signals:
void messageSent();
protected:
qint64 readData(char *data, qint64 maxSize) override
{
if (maxSize == 0 || m_messageIndex < 0 || m_messageIndex >= m_messages.size())
return 0;
m_canReadLine = false;
QByteArray bytes = m_messages.at(m_messageIndex);
qint64 sz = qMin(qint64(bytes.size()), maxSize);
memcpy(data, bytes.constData(), sz);
m_messageIndex++;
emit messageSent();
QTimer::singleShot(10, this, &DataFeeder::onTimer);
return sz;
}
qint64 writeData(const char *, qint64) override { return -1; }
qint64 bytesAvailable() const override { return 1024 + QIODevice::bytesAvailable(); }
bool canReadLine() const override
{
return m_canReadLine && (m_messageIndex >= 0) && (m_messageIndex < m_messages.size());
}
private slots:
void onTimer()
{
m_canReadLine = true;
if (m_parentMode == QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode)
emit readyRead();
}
private:
QNmeaSatelliteInfoSource::UpdateMode m_parentMode;
QList<QByteArray> m_messages;
qsizetype m_messageIndex = 0;
bool m_canReadLine = true; // To read each line separately
};
class tst_QNmeaSatelliteInfoSource : public QObject
{
Q_OBJECT
private slots:
void backendProperty();
void backendProperty_data();
void parseDataStream();
void parseDataStream_data();
private:
QGeoSatelliteInfo createSatelliteInfo(QGeoSatelliteInfo::SatelliteSystem system, int id,
int snr);
};
void tst_QNmeaSatelliteInfoSource::backendProperty()
{
QFETCH(int, simulationRate);
QFETCH(int, updateInterval);
QFETCH(QList<QByteArray>, messages);
QFETCH(int, timeout);
QFETCH(int, desiredMessagesSent);
QNmeaSatelliteInfoSource source(QNmeaSatelliteInfoSource::UpdateMode::SimulationMode);
auto feeder = new DataFeeder(&source);
source.setDevice(feeder);
QSignalSpy messageSentSpy(feeder, &DataFeeder::messageSent);
QSignalSpy inViewSpy(&source, &QNmeaSatelliteInfoSource::satellitesInViewUpdated);
source.setBackendProperty(QNmeaSatelliteInfoSource::SimulationUpdateInterval, simulationRate);
source.setUpdateInterval(updateInterval);
source.startUpdates();
feeder->setMessages(messages);
QTRY_VERIFY_WITH_TIMEOUT(inViewSpy.size() == 1, timeout);
QCOMPARE(source.backendProperty(QNmeaSatelliteInfoSource::SimulationUpdateInterval).toInt(),
simulationRate);
QCOMPARE(source.updateInterval(), updateInterval);
QCOMPARE(messageSentSpy.size(), desiredMessagesSent);
}
void tst_QNmeaSatelliteInfoSource::backendProperty_data()
{
QTest::addColumn<int>("simulationRate");
QTest::addColumn<int>("updateInterval");
QTest::addColumn<QList<QByteArray>>("messages");
QTest::addColumn<int>("timeout");
QTest::addColumn<int>("desiredMessagesSent");
const auto simpleGpsGsvMessage = QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GPGSV,1,1,4,05,,,25,07,,,,08,,,,13,,,36*")
.toLatin1();
const QList<QByteArray> msgs(10, simpleGpsGsvMessage);
// here updateInterval should not be a multiple of simulationRate because
// this will make the test flacky due to timer precision.
QTest::addRow("Simulation rate smaller than updateInterval") << 200 << 500 << msgs << 550 << 2;
QTest::addRow("Simulation rate larger than updateInterval") << 200 << 100 << msgs << 300 << 1;
// Here we do not really have problems with timer precision, because even if
// the updateInterval() timer expires earlier, we will not get any update
// until the real data comes.
QTest::addRow("Simulation rate equals updateInterval") << 200 << 200 << msgs << 300 << 1;
}
void tst_QNmeaSatelliteInfoSource::parseDataStream()
{
QFETCH(QNmeaSatelliteInfoSource::UpdateMode, mode);
QFETCH(QList<QByteArray>, messages);
QFETCH(QList<QGeoSatelliteInfo>, desiredInView);
QFETCH(QList<QGeoSatelliteInfo>, desiredInUse);
QNmeaSatelliteInfoSource source(mode);
if (mode == QNmeaSatelliteInfoSource::UpdateMode::SimulationMode)
QVERIFY(source.setBackendProperty(QNmeaSatelliteInfoSource::SimulationUpdateInterval, 10));
auto feeder = new DataFeeder(&source);
source.setDevice(feeder);
QSignalSpy messageSentSpy(feeder, &DataFeeder::messageSent);
QSignalSpy inViewSpy(&source, &QNmeaSatelliteInfoSource::satellitesInViewUpdated);
QSignalSpy inUseSpy(&source, &QNmeaSatelliteInfoSource::satellitesInUseUpdated);
source.startUpdates();
feeder->setMessages(messages);
QTRY_VERIFY_WITH_TIMEOUT(messageSentSpy.size() == messages.size(), 10000);
if (!desiredInView.isEmpty()) {
QVERIFY(!inViewSpy.isEmpty());
const auto inView = inViewSpy.back().at(0).value<QList<QGeoSatelliteInfo>>();
QCOMPARE(inView, desiredInView);
}
if (!desiredInUse.isEmpty()) {
QVERIFY(!inUseSpy.isEmpty());
const auto inUse = inUseSpy.back().at(0).value<QList<QGeoSatelliteInfo>>();
QCOMPARE(inUse, desiredInUse);
}
}
void tst_QNmeaSatelliteInfoSource::parseDataStream_data()
{
QTest::addColumn<QNmeaSatelliteInfoSource::UpdateMode>("mode");
QTest::addColumn<QList<QByteArray>>("messages");
QTest::addColumn<QList<QGeoSatelliteInfo>>("desiredInView");
QTest::addColumn<QList<QGeoSatelliteInfo>>("desiredInUse");
// We will use only satId and SNR, to simplify objects for comparison
// one line GPS
const auto simpleGpsGsvMessage = QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GPGSV,1,1,4,05,,,25,07,,,,08,,,,13,,,36*")
.toLatin1();
const auto simpleGpsGsaMessage = QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GPGSA,A,3,05,13,,,,,,,,,,,50.95,50.94,1.00*")
.toLatin1();
const auto simpleGpsInView =
QList<QGeoSatelliteInfo> { createSatelliteInfo(QGeoSatelliteInfo::GPS, 5, 25),
createSatelliteInfo(QGeoSatelliteInfo::GPS, 7, -1),
createSatelliteInfo(QGeoSatelliteInfo::GPS, 8, -1),
createSatelliteInfo(QGeoSatelliteInfo::GPS, 13, 36) };
const auto simpleGpsInUse =
QList<QGeoSatelliteInfo> { createSatelliteInfo(QGeoSatelliteInfo::GPS, 5, 25),
createSatelliteInfo(QGeoSatelliteInfo::GPS, 13, 36) };
QTest::newRow("realtime GPS") << QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode
<< QList<QByteArray> { simpleGpsGsvMessage, simpleGpsGsaMessage }
<< simpleGpsInView << simpleGpsInUse;
QTest::newRow("simulation GPS")
<< QNmeaSatelliteInfoSource::UpdateMode::SimulationMode
<< QList<QByteArray> { simpleGpsGsvMessage, simpleGpsGsaMessage } << simpleGpsInView
<< simpleGpsInUse;
// multi line GPS
const auto complexGpsGsvMessage1 = QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GPGSV,2,1,8,05,,,22,07,,,,08,,,,13,,,18*")
.toLatin1();
const auto complexGpsGsvMessage2 =
QLocationTestUtils::addNmeaChecksumAndBreaks("$GPGSV,2,2,8,14,,,,15,,,18,18,,,,20,,,*")
.toLatin1();
const auto complexGpsGsaMessage = QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GPGSA,A,3,05,13,15,,,,,,,,,,50.95,50.94,1.00*")
.toLatin1();
const auto complexGpsInView =
QList<QGeoSatelliteInfo> { createSatelliteInfo(QGeoSatelliteInfo::GPS, 5, 22),
createSatelliteInfo(QGeoSatelliteInfo::GPS, 7, -1),
createSatelliteInfo(QGeoSatelliteInfo::GPS, 8, -1),
createSatelliteInfo(QGeoSatelliteInfo::GPS, 13, 18),
createSatelliteInfo(QGeoSatelliteInfo::GPS, 14, -1),
createSatelliteInfo(QGeoSatelliteInfo::GPS, 15, 18),
createSatelliteInfo(QGeoSatelliteInfo::GPS, 18, -1),
createSatelliteInfo(QGeoSatelliteInfo::GPS, 20, -1) };
const auto complexGpsInUse =
QList<QGeoSatelliteInfo> { createSatelliteInfo(QGeoSatelliteInfo::GPS, 5, 22),
createSatelliteInfo(QGeoSatelliteInfo::GPS, 13, 18),
createSatelliteInfo(QGeoSatelliteInfo::GPS, 15, 18) };
QTest::newRow("realtime multi-line GPS")
<< QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode
<< QList<QByteArray> { complexGpsGsvMessage1, complexGpsGsvMessage2,
complexGpsGsaMessage }
<< complexGpsInView << complexGpsInUse;
QTest::newRow("simulation multi-line GPS")
<< QNmeaSatelliteInfoSource::UpdateMode::SimulationMode
<< QList<QByteArray> { complexGpsGsvMessage1, complexGpsGsvMessage2,
complexGpsGsaMessage }
<< complexGpsInView << complexGpsInUse;
// one line GPS & GLONASS
const auto simpleGlnsGsvMessage = QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GLGSV,1,1,4,65,,,,66,,,,71,,,20,72,,,28*")
.toLatin1();
const auto gnSimpleGlnsGsaMessage = QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GNGSA,A,3,71,72,,,,,,,,,,,50.95,50.94,1.00*")
.toLatin1();
const auto gnSimpleGpsGsaMessage = QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GNGSA,A,3,05,13,,,,,,,,,,,50.95,50.94,1.00*")
.toLatin1();
const auto simpleGlnsInView =
QList<QGeoSatelliteInfo> { createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 65, -1),
createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 66, -1),
createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 71, 20),
createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 72, 28) };
const auto simpleGlnsInUse =
QList<QGeoSatelliteInfo> { createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 71, 20),
createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 72, 28) };
const auto simpleGpsGlnsInView = simpleGpsInView + simpleGlnsInView;
const auto simpleGpsGlnsInUse = simpleGpsInUse + simpleGlnsInUse;
QTest::newRow("realtime GPS & GLONASS")
<< QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode
<< QList<QByteArray> { simpleGpsGsvMessage, simpleGlnsGsvMessage, gnSimpleGpsGsaMessage,
gnSimpleGlnsGsaMessage }
<< simpleGpsGlnsInView << simpleGpsGlnsInUse;
QTest::newRow("simulation GPS & GLONASS")
<< QNmeaSatelliteInfoSource::UpdateMode::SimulationMode
<< QList<QByteArray> { simpleGpsGsvMessage, simpleGlnsGsvMessage, gnSimpleGpsGsaMessage,
gnSimpleGlnsGsaMessage }
<< simpleGpsGlnsInView << simpleGpsGlnsInUse;
// multi line GPS & GLONASS
const auto complexGlnsGsvMessage1 = QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GLGSV,3,1,10,65,,,,66,,,,71,,,20,72,,,28*")
.toLatin1();
const auto complexGlnsGsvMessage2 =
QLocationTestUtils::addNmeaChecksumAndBreaks("$GLGSV,3,2,10,73,,,,74,,,,75,,,33,81,,,*")
.toLatin1();
const auto complexGlnsGsvMessage3 =
QLocationTestUtils::addNmeaChecksumAndBreaks("$GLGSV,3,3,10,82,,,,83,,,*").toLatin1();
const auto gnComplexGlnsGsaMessage = QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GNGSA,A,3,71,72,75,,,,,,,,,,50.95,50.94,1.00*")
.toLatin1();
const auto gnComplexGpsGsaMessage = QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GNGSA,A,3,05,13,15,,,,,,,,,,50.95,50.94,1.00*")
.toLatin1();
const auto complexGlnsInView =
QList<QGeoSatelliteInfo> { createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 65, -1),
createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 66, -1),
createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 71, 20),
createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 72, 28),
createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 73, -1),
createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 74, -1),
createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 75, 33),
createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 81, -1),
createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 82, -1),
createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 83, -1) };
const auto complexGlnsInUse =
QList<QGeoSatelliteInfo> { createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 71, 20),
createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 72, 28),
createSatelliteInfo(QGeoSatelliteInfo::GLONASS, 75, 33) };
const auto complexGpsGlnsInView = complexGpsInView + complexGlnsInView;
const auto complexGpsGlnsInUse = complexGpsInUse + complexGlnsInUse;
QTest::newRow("realtime multi-line GPS & GLONASS")
<< QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode
<< QList<QByteArray> { complexGpsGsvMessage1, complexGpsGsvMessage2,
complexGlnsGsvMessage1, complexGlnsGsvMessage2,
complexGlnsGsvMessage3, gnComplexGpsGsaMessage,
gnComplexGlnsGsaMessage }
<< complexGpsGlnsInView << complexGpsGlnsInUse;
QTest::newRow("simulation multi-line GPS & GLONASS")
<< QNmeaSatelliteInfoSource::UpdateMode::SimulationMode
<< QList<QByteArray> { complexGpsGsvMessage1, complexGpsGsvMessage2,
complexGlnsGsvMessage1, complexGlnsGsvMessage2,
complexGlnsGsvMessage3, gnComplexGpsGsaMessage,
gnComplexGlnsGsaMessage }
<< complexGpsGlnsInView << complexGpsGlnsInUse;
// multi-line GPS & GLONASS: GSA before GSV
QTest::newRow("realtime multi-line GPS & GLONASS reverse")
<< QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode
<< QList<QByteArray> { gnComplexGlnsGsaMessage, gnComplexGpsGsaMessage,
complexGlnsGsvMessage1, complexGlnsGsvMessage2,
complexGlnsGsvMessage3, complexGpsGsvMessage1,
complexGpsGsvMessage2 }
<< complexGpsGlnsInView << complexGpsGlnsInUse;
QTest::newRow("simulation multi-line GPS & GLONASS reverse")
<< QNmeaSatelliteInfoSource::UpdateMode::SimulationMode
<< QList<QByteArray> { gnComplexGlnsGsaMessage, gnComplexGpsGsaMessage,
complexGlnsGsvMessage1, complexGlnsGsvMessage2,
complexGlnsGsvMessage3, complexGpsGsvMessage1,
complexGpsGsvMessage2 }
<< complexGpsGlnsInView << complexGpsGlnsInUse;
// multi-line GSP & GLONASS: mixed order
QTest::newRow("realtime multi-line GPS & GLONASS mixed")
<< QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode
<< QList<QByteArray> { gnComplexGlnsGsaMessage, complexGlnsGsvMessage1,
complexGlnsGsvMessage2, complexGlnsGsvMessage3,
complexGpsGsvMessage1, complexGpsGsvMessage2,
gnComplexGpsGsaMessage }
<< complexGpsGlnsInView << complexGpsGlnsInUse;
QTest::newRow("simulation multi-line GPS & GLONASS mixed")
<< QNmeaSatelliteInfoSource::UpdateMode::SimulationMode
<< QList<QByteArray> { gnComplexGlnsGsaMessage, complexGlnsGsvMessage1,
complexGlnsGsvMessage2, complexGlnsGsvMessage3,
complexGpsGsvMessage1, complexGpsGsvMessage2,
gnComplexGpsGsaMessage }
<< complexGpsGlnsInView << complexGpsGlnsInUse;
// one line GPS & GLONASS: empty GNGSA for GPS
const auto gnEmptyGpsGsaMessage = QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GNGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*")
.toLatin1();
QTest::newRow("realtime GSP & GLONASS: empty GSA for GPS")
<< QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode
<< QList<QByteArray> { simpleGpsGsvMessage, simpleGlnsGsvMessage,
gnSimpleGlnsGsaMessage, gnEmptyGpsGsaMessage }
<< simpleGpsGlnsInView << simpleGlnsInUse;
QTest::newRow("simulation GSP & GLONASS: empty GSA for GPS")
<< QNmeaSatelliteInfoSource::UpdateMode::SimulationMode
<< QList<QByteArray> { simpleGpsGsvMessage, simpleGlnsGsvMessage,
gnSimpleGlnsGsaMessage, gnEmptyGpsGsaMessage }
<< simpleGpsGlnsInView << simpleGlnsInUse;
// one line GPS & GLONASS: empty $GPGSV
const auto emptyGpsGsvMessage =
QLocationTestUtils::addNmeaChecksumAndBreaks("$GPGSV,1,1,0,,,,,,,,,,,,,,,,*")
.toLatin1();
QTest::newRow("realtime GPS & GLONASS: empty $GPGSV")
<< QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode
<< QList<QByteArray> { emptyGpsGsvMessage, simpleGlnsGsvMessage, gnSimpleGlnsGsaMessage,
gnSimpleGpsGsaMessage }
<< simpleGlnsInView << simpleGlnsInUse;
QTest::newRow("simulation GPS & GLONASS: empty $GPGSV")
<< QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode
<< QList<QByteArray> { emptyGpsGsvMessage, simpleGlnsGsvMessage, gnSimpleGlnsGsaMessage,
gnSimpleGpsGsaMessage }
<< simpleGlnsInView << simpleGlnsInUse;
// one line GPS & GLONASS: empty GNGSA in the middle
// In this test we send the following sequence:
// 1. $GPGSV - valid GPS in view
// 2. $GLGSV - valid GLONASS in view
// 3. $GNGSA - valid GPS in use
// 4. $GNGSA - valid GLONASS in use
// 5. $GPGSV - valid GPS in view
// 6. $GNGSA - empty GNGSA (meaning empty GPS)
// 7. $GPGSV - valid GPS in view: at this point GPS in use will be cleared
// As a result, we will see valid GPS & GLONASS in view, but only GLONASS
// in use.
QTest::newRow("realtime GPS & GLONASS: empty $GNGSA in the middle")
<< QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode
<< QList<QByteArray> { simpleGpsGsvMessage, simpleGlnsGsvMessage,
gnSimpleGpsGsaMessage, gnSimpleGlnsGsaMessage,
simpleGpsGsvMessage, gnEmptyGpsGsaMessage,
simpleGpsGsvMessage }
<< simpleGpsGlnsInView << simpleGlnsInUse;
QTest::newRow("simulation GPS & GLONASS: empty $GNGSA in the middle")
<< QNmeaSatelliteInfoSource::UpdateMode::SimulationMode
<< QList<QByteArray> { simpleGpsGsvMessage, simpleGlnsGsvMessage,
gnSimpleGpsGsaMessage, gnSimpleGlnsGsaMessage,
simpleGpsGsvMessage, gnEmptyGpsGsaMessage,
simpleGpsGsvMessage }
<< simpleGpsGlnsInView << simpleGlnsInUse;
// BEIDOU signals for below test cases are just synthesized based on the
// NMEA protocol description. They were NEVER checked on real hardware,
// as we do not have one.
// multi-line GPS, GLONASS & BEIDOU
const auto complexBduGsvMessage1 = QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GBGSV,2,1,7,201,,,,202,,,,203,,,20,204,,,28*")
.toLatin1();
const auto complexBduGsvMessage2 =
QLocationTestUtils::addNmeaChecksumAndBreaks("$GBGSV,2,2,7,205,,,,206,,,,207,,,33,,,,*")
.toLatin1();
const auto gnComplexBduGsaMessage = QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GNGSA,A,3,203,204,207,,,,,,,,,,50.95,50.94,1.00*")
.toLatin1();
const auto complexBduInView =
QList<QGeoSatelliteInfo> { createSatelliteInfo(QGeoSatelliteInfo::BEIDOU, 201, -1),
createSatelliteInfo(QGeoSatelliteInfo::BEIDOU, 202, -1),
createSatelliteInfo(QGeoSatelliteInfo::BEIDOU, 203, 20),
createSatelliteInfo(QGeoSatelliteInfo::BEIDOU, 204, 28),
createSatelliteInfo(QGeoSatelliteInfo::BEIDOU, 205, -1),
createSatelliteInfo(QGeoSatelliteInfo::BEIDOU, 206, -1),
createSatelliteInfo(QGeoSatelliteInfo::BEIDOU, 207, 33) };
const auto complexBduInUse =
QList<QGeoSatelliteInfo> { createSatelliteInfo(QGeoSatelliteInfo::BEIDOU, 203, 20),
createSatelliteInfo(QGeoSatelliteInfo::BEIDOU, 204, 28),
createSatelliteInfo(QGeoSatelliteInfo::BEIDOU, 207, 33) };
const auto complexGpsGlnsBduInView = complexGpsInView + complexGlnsInView + complexBduInView;
const auto complexGpsGlnsBduInUse = complexGpsInUse + complexGlnsInUse + complexBduInUse;
QTest::newRow("realtime GPS, GLONASS & BEIDOU")
<< QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode
<< QList<QByteArray> { complexGpsGsvMessage1, complexGpsGsvMessage2,
complexGlnsGsvMessage1, complexGlnsGsvMessage2,
complexGlnsGsvMessage3, complexBduGsvMessage1,
complexBduGsvMessage2, gnComplexGpsGsaMessage,
gnComplexGlnsGsaMessage, gnComplexBduGsaMessage }
<< complexGpsGlnsBduInView << complexGpsGlnsBduInUse;
QTest::newRow("simulation GPS, GLONASS & BEIDOU")
<< QNmeaSatelliteInfoSource::UpdateMode::SimulationMode
<< QList<QByteArray> { complexGpsGsvMessage1, complexGpsGsvMessage2,
complexGlnsGsvMessage1, complexGlnsGsvMessage2,
complexGlnsGsvMessage3, complexBduGsvMessage1,
complexBduGsvMessage2, gnComplexGpsGsaMessage,
gnComplexGlnsGsaMessage, gnComplexBduGsaMessage }
<< complexGpsGlnsBduInView << complexGpsGlnsBduInUse;
// multi-line GPS, GLONASS & BEIDOU: GSA before GSV
QTest::newRow("realtime GPS, GLONASS & BEIDOU reverse")
<< QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode
<< QList<QByteArray> { gnComplexGpsGsaMessage, gnComplexGlnsGsaMessage,
gnComplexBduGsaMessage, complexGpsGsvMessage1,
complexGpsGsvMessage2, complexGlnsGsvMessage1,
complexGlnsGsvMessage2, complexGlnsGsvMessage3,
complexBduGsvMessage1, complexBduGsvMessage2 }
<< complexGpsGlnsBduInView << complexGpsGlnsBduInUse;
QTest::newRow("simulation GPS, GLONASS & BEIDOU reverse")
<< QNmeaSatelliteInfoSource::UpdateMode::SimulationMode
<< QList<QByteArray> { gnComplexGpsGsaMessage, gnComplexGlnsGsaMessage,
gnComplexBduGsaMessage, complexGpsGsvMessage1,
complexGpsGsvMessage2, complexGlnsGsvMessage1,
complexGlnsGsvMessage2, complexGlnsGsvMessage3,
complexBduGsvMessage1, complexBduGsvMessage2 }
<< complexGpsGlnsBduInView << complexGpsGlnsBduInUse;
// multi-line GPS, GLONASS & BEIDOU: mixed order
QTest::newRow("realtime GPS, GLONASS & BEIDOU mixed")
<< QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode
<< QList<QByteArray> { gnComplexGpsGsaMessage, complexGlnsGsvMessage1,
complexGlnsGsvMessage2, complexGlnsGsvMessage3,
complexBduGsvMessage1, complexBduGsvMessage2,
gnComplexGlnsGsaMessage, gnComplexBduGsaMessage,
complexGpsGsvMessage1, complexGpsGsvMessage2 }
<< complexGpsGlnsBduInView << complexGpsGlnsBduInUse;
QTest::newRow("simulation GPS, GLONASS & BEIDOU mixed")
<< QNmeaSatelliteInfoSource::UpdateMode::SimulationMode
<< QList<QByteArray> { gnComplexGpsGsaMessage, complexGlnsGsvMessage1,
complexGlnsGsvMessage2, complexGlnsGsvMessage3,
complexBduGsvMessage1, complexBduGsvMessage2,
gnComplexGlnsGsaMessage, gnComplexBduGsaMessage,
complexGpsGsvMessage1, complexGpsGsvMessage2 }
<< complexGpsGlnsBduInView << complexGpsGlnsBduInUse;
// Malformed input data
struct ModeInfo {
QNmeaSatelliteInfoSource::UpdateMode mode;
QByteArray name;
};
const std::array<ModeInfo, 2> modes = {
ModeInfo{ QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode, "realtime" },
ModeInfo{ QNmeaSatelliteInfoSource::UpdateMode::SimulationMode, "simulation" },
};
for (const auto &mode : modes) {
const auto gpsGsvNegativeTotalNumMessage =
QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GPGSV,-1,1,4,05,,,25,07,,,,08,,,,13,,,36*").toLatin1();
const auto emptyGpsGsaMessage =
QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GPGSA,A,1,,,,,,,,,,,,,50.95,50.94,1.00*").toLatin1();
QTest::addRow("%s GPS negative total messages", mode.name.data())
<< mode.mode
<< QList<QByteArray>{ gpsGsvNegativeTotalNumMessage, emptyGpsGsaMessage }
<< QList<QGeoSatelliteInfo>() << QList<QGeoSatelliteInfo>();
const auto gpsGsvFirstMessage =
QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GPGSV,10,1,8,05,,,25,07,,,,08,,,,13,,,36*").toLatin1();
const auto gpsGsvNegativeSentenceNumMessage =
QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GPGSV,10,-1,8,05,,,25,07,,,,08,,,,13,,,36*").toLatin1();
QTest::addRow("%s GPS negative sentence num", mode.name.data())
<< mode.mode
<< QList<QByteArray>{ gpsGsvFirstMessage,
gpsGsvNegativeSentenceNumMessage,
emptyGpsGsaMessage }
<< QList<QGeoSatelliteInfo>() << QList<QGeoSatelliteInfo>();
const auto gpsGsvNegativeTotalSatMessage =
QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GPGSV,10,1,-4,05,,,25,07,,,,08,,,,13,,,36*").toLatin1();
QTest::addRow("%s GPS negative total satellite num", mode.name.data())
<< mode.mode
<< QList<QByteArray>{ gpsGsvNegativeTotalSatMessage,
emptyGpsGsaMessage }
<< QList<QGeoSatelliteInfo>() << QList<QGeoSatelliteInfo>();
const auto gpsGsvSentenceGreaterThanTotalMessage =
QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GPGSV,10,11,8,05,,,25,07,,,,08,,,,13,,,36*").toLatin1();
QTest::addRow("%s GPS sentence num greater than total num", mode.name.data())
<< mode.mode
<< QList<QByteArray>{ gpsGsvFirstMessage,
gpsGsvSentenceGreaterThanTotalMessage,
emptyGpsGsaMessage }
<< QList<QGeoSatelliteInfo>() << QList<QGeoSatelliteInfo>();
const auto gpsGsvTooLargeTotalNumMessage =
QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GPGSV,1001,1,4,05,,,25,07,,,,08,,,,13,,,36*").toLatin1();
QTest::addRow("%s GPS total messages too large", mode.name.data())
<< mode.mode
<< QList<QByteArray>{ gpsGsvTooLargeTotalNumMessage,
emptyGpsGsaMessage }
<< QList<QGeoSatelliteInfo>() << QList<QGeoSatelliteInfo>();
const auto gpsGsvSecondMessage =
QLocationTestUtils::addNmeaChecksumAndBreaks(
"$GPGSV,10,2,2000,05,,,25,07,,,,08,,,,13,,,36*").toLatin1();
// Craft a message that repeatedly sends the second sentence, thus
// potentially triggering an OOM condition by endlessly accumulating
// the list of parsed satellites. In this test we send only 300
// sentences, that represents 1200 satellites.
// We should stop after 1000 accumulated satellites, clear the list and
// discard all follow-up messages.
QList<QByteArray> repeatedSecondMessage = { gpsGsvFirstMessage };
repeatedSecondMessage.append(QList<QByteArray>(300, gpsGsvSecondMessage));
repeatedSecondMessage.append(emptyGpsGsaMessage);
QTest::addRow("%s GPS repeated sentence DDoS", mode.name.data())
<< mode.mode
<< repeatedSecondMessage
<< QList<QGeoSatelliteInfo>() << QList<QGeoSatelliteInfo>();
}
}
QGeoSatelliteInfo
tst_QNmeaSatelliteInfoSource::createSatelliteInfo(QGeoSatelliteInfo::SatelliteSystem system, int id,
int snr)
{
QGeoSatelliteInfo info;
info.setSatelliteSystem(system);
info.setSatelliteIdentifier(id);
info.setSignalStrength(snr);
info.setAttribute(QGeoSatelliteInfo::Azimuth, 0.0);
info.setAttribute(QGeoSatelliteInfo::Elevation, 0.0);
return info;
}
#include "tst_nmeasatelliteinfosource.moc"
QTEST_GUILESS_MAIN(tst_QNmeaSatelliteInfoSource)