1 /***************************************************************************
2 testqgsgpsinformationwidget.cpp
3 --------------------------
4 Date : 2019-06-19
5 Copyright : (C) 2019 by Alessandro Pasotti
6 Email : elpaso at itopen dot it
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15 #include "qgstest.h"
16 #include <QTimeZone>
17
18 #include "qgisapp.h"
19 #include "qgsapplication.h"
20 #include "qgsvectorlayer.h"
21 #include "qgsproject.h"
22 #include "qgsmapcanvas.h"
23 #include "qgssettingsregistrycore.h"
24 #include "gps/qgsgpsinformationwidget.h"
25 #include "nmeatime.h"
26
27 /**
28 * \ingroup UnitTests
29 * This is a unit test for the GPS information widget
30 */
31 class TestQgsGpsInformationWidget : public QObject
32 {
33 Q_OBJECT
34 public:
35 TestQgsGpsInformationWidget();
36
37 private slots:
38 void initTestCase();// will be called before the first testfunction is executed.
39 void cleanupTestCase();// will be called after the last testfunction was executed.
init()40 void init() {} // will be called before each testfunction is executed.
cleanup()41 void cleanup() {} // will be called after every testfunction.
42 void testGuiSignals();
43 void testStorePreferredFields();
44 void testTimestamp();
45 void testTimestampWrite();
46 void testMultiPartLayers();
47
48 private:
49 std::unique_ptr<QgsGpsInformationWidget> prepareWidget();
50 QDateTime _testWrite( QgsVectorLayer *vlayer, QgsGpsInformationWidget *widget, const QString &fieldName, Qt::TimeSpec timeSpec, bool commit = false );
51 QgsVectorLayer *tempLayer = nullptr;
52 QgsVectorLayer *tempLayerString = nullptr;
53 QgsVectorLayer *tempLayerDateTime = nullptr;
54 QgsVectorLayer *tempLayerLineString = nullptr;
55 QgsVectorLayer *tempGpkgLayerPointString = nullptr;
56 QgisApp *mQgisApp = nullptr;
57 };
58
59 TestQgsGpsInformationWidget::TestQgsGpsInformationWidget() = default;
60
61 //runs before all tests
initTestCase()62 void TestQgsGpsInformationWidget::initTestCase()
63 {
64 // setup the test QSettings environment
65
66 QgsApplication::init();
67 QgsApplication::initQgis();
68 QgsApplication::showSettings();
69
70 QCoreApplication::setOrganizationName( QStringLiteral( "QGIS" ) );
71 QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
72 QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST" ) );
73
74 mQgisApp = new QgisApp();
75
76
77 tempLayer = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326&field=intf:int" ),
78 QStringLiteral( "vl1" ),
79 QStringLiteral( "memory" ) );
80 tempLayerString = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326&field=stringf:string&field=intf:int" ),
81 QStringLiteral( "vl2" ),
82 QStringLiteral( "memory" ) );
83 tempLayerDateTime = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326&field=datetimef:datetime&field=intf:int" ),
84 QStringLiteral( "vl3" ),
85 QStringLiteral( "memory" ) );
86 tempLayerLineString = new QgsVectorLayer( QStringLiteral( "Linestring?crs=epsg:4326&field=intf:int&field=stringf:string" ),
87 QStringLiteral( "vl4" ),
88 QStringLiteral( "memory" ) );
89
90 QgsSettingsRegistryCore::settingsDigitizingDisableEnterAttributeValuesDialog.setValue( true );
91
92 const QString tempPath = QDir::tempPath() + QStringLiteral( "/gps_timestamp.gpkg" );
93 QFile::copy( TEST_DATA_DIR + QStringLiteral( "/gps_timestamp.gpkg" ), tempPath );
94 tempGpkgLayerPointString = new QgsVectorLayer( QStringLiteral( "%1|layername=points" ).arg( tempPath ),
95 QStringLiteral( "vl4" ) );
96 Q_ASSERT( tempGpkgLayerPointString->isValid() );
97 Q_ASSERT( tempLayer->isValid() );
98 Q_ASSERT( tempLayerString->isValid() );
99 Q_ASSERT( tempLayerDateTime->isValid() );
100 Q_ASSERT( tempLayerLineString->isValid() );
101 QgsProject::instance()->addMapLayers( { tempLayer, tempLayerString, tempLayerDateTime, tempGpkgLayerPointString, tempLayerLineString } );
102 }
103
104 //runs after all tests
cleanupTestCase()105 void TestQgsGpsInformationWidget::cleanupTestCase()
106 {
107 QgsApplication::exitQgis();
108 }
109
110
prepareWidget()111 std::unique_ptr<QgsGpsInformationWidget> TestQgsGpsInformationWidget::prepareWidget()
112 {
113 QgsMapCanvas *canvas = mQgisApp->mapCanvas();
114 std::unique_ptr<QgsGpsInformationWidget> widget = std::make_unique<QgsGpsInformationWidget>( canvas );
115 // Widget config and input values
116 // 2019/06/19 12:27:34.543[UTC]
117 widget->mLastNmeaTime = { 119, 5, 19, 12, 27, 34, 543 };
118 canvas->setCurrentLayer( tempLayerString );
119 widget->mCboTimestampField->setCurrentIndex( widget->mCboTimestampField->findText( QStringLiteral( "stringf" ) ) );
120 canvas->setCurrentLayer( tempLayerDateTime );
121 widget->mCboTimestampField->setCurrentIndex( widget->mCboTimestampField->findText( QStringLiteral( "datetimef" ) ) );
122 canvas->setCurrentLayer( tempLayerLineString );
123 widget->mCboTimestampField->setCurrentIndex( widget->mCboTimestampField->findText( QStringLiteral( "stringf" ) ) );
124 canvas->setCurrentLayer( tempGpkgLayerPointString );
125 widget->mCboTimestampField->setCurrentIndex( widget->mCboTimestampField->findText( QStringLiteral( "datetimef" ) ) );
126
127 widget->mCboTimeZones->setCurrentIndex( widget->mCboTimeZones->findText( QStringLiteral( "Asia/Colombo" ) ) );
128 widget->mCbxLeapSeconds->setChecked( false );
129 widget->mLeapSeconds->setValue( 7 );
130 return widget;
131 }
132
_testWrite(QgsVectorLayer * vlayer,QgsGpsInformationWidget * widget,const QString & fieldName,Qt::TimeSpec timeSpec,bool commit)133 QDateTime TestQgsGpsInformationWidget::_testWrite( QgsVectorLayer *vlayer, QgsGpsInformationWidget *widget, const QString &fieldName, Qt::TimeSpec timeSpec, bool commit )
134 {
135 widget->mMapCanvas->setCurrentLayer( vlayer );
136 vlayer->startEditing();
137 widget->mCboTimestampField->setCurrentIndex( widget->mCboTimestampField->findText( fieldName ) );
138 widget->mCboTimestampFormat->setCurrentIndex( widget->mCboTimestampFormat->findData( timeSpec ) );
139 widget->mBtnCloseFeature_clicked();
140 const auto fids { vlayer->allFeatureIds() };
141 const auto fid { std::min_element( fids.begin(), fids.end() ) };
142 const QgsFeature f { vlayer->getFeature( *fid ) };
143 if ( commit )
144 vlayer->commitChanges();
145 else
146 vlayer->rollBack();
147 return f.attribute( fieldName ).toDateTime();
148 }
149
testGuiSignals()150 void TestQgsGpsInformationWidget::testGuiSignals()
151 {
152 QgsMapCanvas *canvas = mQgisApp->mapCanvas();
153 std::unique_ptr<QgsGpsInformationWidget> widget = prepareWidget();
154 canvas->setCurrentLayer( tempLayer );
155 QVERIFY( ! widget->mGboxTimestamp->isEnabled() );
156
157 canvas->setCurrentLayer( tempLayerString );
158 QVERIFY( widget->mGboxTimestamp->isEnabled() );
159 QVERIFY( widget->mCboTimestampField->findText( QStringLiteral( "stringf" ) ) != -1 );
160 QVERIFY( widget->mCboTimestampField->findText( QStringLiteral( "intf" ) ) == -1 );
161
162 canvas->setCurrentLayer( tempLayerDateTime );
163 QVERIFY( widget->mGboxTimestamp->isEnabled() );
164 QVERIFY( widget->mCboTimestampField->findText( QStringLiteral( "datetimef" ) ) != -1 );
165 QVERIFY( widget->mCboTimestampField->findText( QStringLiteral( "intf" ) ) == -1 );
166
167 // Check tz combo
168 widget->mCboTimestampFormat->setCurrentIndex( widget->mCboTimestampFormat->findData( Qt::TimeSpec::UTC ) );
169 QVERIFY( ! widget->mCboTimeZones->isEnabled() );
170 widget->mCboTimestampFormat->setCurrentIndex( widget->mCboTimestampFormat->findData( Qt::TimeSpec::LocalTime ) );
171 QVERIFY( ! widget->mCboTimeZones->isEnabled() );
172 widget->mCboTimestampFormat->setCurrentIndex( widget->mCboTimestampFormat->findData( Qt::TimeSpec::TimeZone ) );
173 QVERIFY( widget->mCboTimeZones->isEnabled() );
174
175 canvas->setCurrentLayer( tempLayer );
176 QVERIFY( ! widget->mGboxTimestamp->isEnabled() );
177 }
178
testStorePreferredFields()179 void TestQgsGpsInformationWidget::testStorePreferredFields()
180 {
181 std::unique_ptr<QgsGpsInformationWidget> widget = prepareWidget();
182 QgsMapCanvas *canvas = mQgisApp->mapCanvas();
183 canvas->setCurrentLayer( tempLayerDateTime );
184 int fieldIdx = tempLayerDateTime->fields().indexOf( QLatin1String( "datetimef" ) );
185 QVERIFY( fieldIdx != -1 );
186 widget->mCboTimestampField->setCurrentIndex( widget->mCboTimestampField->findText( QStringLiteral( "datetimef" ) ) );
187
188 canvas->setCurrentLayer( tempLayerString );
189 fieldIdx = tempLayerString->fields().indexOf( QLatin1String( "stringf" ) );
190 QVERIFY( fieldIdx != -1 );
191 widget->mCboTimestampField->setCurrentIndex( widget->mCboTimestampField->findText( QStringLiteral( "stringf" ) ) );
192
193 canvas->setCurrentLayer( tempLayer );
194 QVERIFY( widget->mPreferredTimestampFields.contains( tempLayerDateTime->id() ) );
195 QCOMPARE( widget->mPreferredTimestampFields[ tempLayerString->id() ], QStringLiteral( "stringf" ) );
196 QVERIFY( widget->mPreferredTimestampFields.contains( tempLayerDateTime->id() ) );
197 QCOMPARE( widget->mPreferredTimestampFields[ tempLayerDateTime->id() ], QStringLiteral( "datetimef" ) );
198 }
199
testTimestamp()200 void TestQgsGpsInformationWidget::testTimestamp()
201 {
202 std::unique_ptr<QgsGpsInformationWidget> widget = prepareWidget();
203 QgsMapCanvas *canvas = mQgisApp->mapCanvas();
204
205 QDateTime dateTime( QDate( 2019, 6, 19 ), QTime( 12, 27, 34, 543 ) );
206 dateTime.setTimeSpec( Qt::TimeSpec::UTC );
207 const QDateTime tzTime( dateTime.toTimeZone( QTimeZone( QStringLiteral( "Asia/Colombo" ).toUtf8() ) ) );
208 const QDateTime localTime( dateTime.toLocalTime() );
209
210 ///////////////////////////////////////////
211 // Test datetime layer
212 canvas->setCurrentLayer( tempLayerDateTime );
213
214 int fieldIdx { tempLayerDateTime->fields().indexOf( QLatin1String( "datetimef" ) ) };
215 widget->mCboTimestampField->setCurrentIndex( widget->mCboTimestampField->findText( QStringLiteral( "datetimef" ) ) );
216 QVERIFY( fieldIdx != -1 );
217 // UTC
218 widget->mCboTimestampFormat->setCurrentIndex( widget->mCboTimestampFormat->findData( Qt::TimeSpec::UTC ) );
219 QVariant dt = widget->timestamp( tempLayerDateTime, fieldIdx );
220 QCOMPARE( dt.toDateTime(), dateTime );
221
222 // Local time
223 widget->mCboTimestampFormat->setCurrentIndex( widget->mCboTimestampFormat->findData( Qt::TimeSpec::LocalTime ) );
224 dt = widget->timestamp( tempLayerDateTime, fieldIdx );
225 QCOMPARE( dt.toDateTime(), dateTime.toLocalTime() );
226
227 // Leap seconds
228 widget->mCbxLeapSeconds->setChecked( true );
229 dt = widget->timestamp( tempLayerDateTime, fieldIdx );
230 QCOMPARE( dt.toDateTime(), dateTime.addSecs( 7 ) );
231 widget->mCbxLeapSeconds->setChecked( false );
232
233 ///////////////////////////////////////////
234 // Test string
235 canvas->setCurrentLayer( tempLayerString );
236 fieldIdx = tempLayerString->fields().indexOf( QLatin1String( "stringf" ) );
237 widget->mCboTimestampField->setCurrentIndex( widget->mCboTimestampField->findText( QStringLiteral( "stringf" ) ) );
238
239 // UTC
240 widget->mCboTimestampFormat->setCurrentIndex( widget->mCboTimestampFormat->findData( Qt::TimeSpec::UTC ) );
241 dt = widget->timestamp( tempLayerString, fieldIdx );
242 QCOMPARE( dt.toString(), dateTime.toString( Qt::DateFormat::ISODate ) );
243
244 // Local Time (not very robust because we cannot change the system timezone and it may be GMT)
245 widget->mCboTimestampFormat->setCurrentIndex( widget->mCboTimestampFormat->findData( Qt::TimeSpec::LocalTime ) );
246 dt = widget->timestamp( tempLayerString, fieldIdx );
247 QCOMPARE( dt.toString(), localTime.toString( Qt::DateFormat::ISODate ) );
248
249 // Timezone
250 widget->mCboTimestampFormat->setCurrentIndex( widget->mCboTimestampFormat->findData( Qt::TimeSpec::TimeZone ) );
251 widget->mCboTimeZones->setCurrentIndex( widget->mCboTimeZones->findText( QStringLiteral( "Asia/Colombo" ) ) ) ;
252 dt = widget->timestamp( tempLayerString, fieldIdx );
253 QCOMPARE( dt.toString(), tzTime.toString( Qt::DateFormat::ISODate ) );
254
255 }
256
testTimestampWrite()257 void TestQgsGpsInformationWidget::testTimestampWrite()
258 {
259 std::unique_ptr<QgsGpsInformationWidget> widget = prepareWidget();
260 QDateTime dateTime( QDate( 2019, 6, 19 ), QTime( 12, 27, 34, 543 ) );
261 dateTime.setTimeSpec( Qt::TimeSpec::UTC );
262 const QDateTime tzTime( dateTime.toTimeZone( QTimeZone( QStringLiteral( "Asia/Colombo" ).toUtf8() ) ) );
263 const QDateTime localTime( dateTime.toLocalTime() );
264
265 // Test write on datetime field
266 QCOMPARE( _testWrite( tempLayerDateTime, widget.get(), QStringLiteral( "datetimef" ), Qt::TimeSpec::UTC ), dateTime );
267 QCOMPARE( _testWrite( tempLayerDateTime, widget.get(), QStringLiteral( "datetimef" ), Qt::TimeSpec::LocalTime ), localTime );
268 QCOMPARE( _testWrite( tempLayerDateTime, widget.get(), QStringLiteral( "datetimef" ), Qt::TimeSpec::TimeZone ), tzTime );
269
270 // Test write on string field
271 QCOMPARE( _testWrite( tempLayerString, widget.get(), QStringLiteral( "stringf" ), Qt::TimeSpec::UTC ).toString( Qt::DateFormat::ISODate ), dateTime.toString( Qt::DateFormat::ISODate ) );
272 QCOMPARE( _testWrite( tempLayerString, widget.get(), QStringLiteral( "stringf" ), Qt::TimeSpec::LocalTime ).toString( Qt::DateFormat::ISODate ), localTime.toString( Qt::DateFormat::ISODate ) );
273 QCOMPARE( _testWrite( tempLayerString, widget.get(), QStringLiteral( "stringf" ), Qt::TimeSpec::TimeZone ).toString( Qt::DateFormat::ISODate ), tzTime.toString( Qt::DateFormat::ISODate ) );
274
275 // Test write on line string field
276 widget->mCaptureList.push_back( QgsPoint( 1, 2 ) );
277 widget->mCaptureList.push_back( QgsPoint( 3, 4 ) );
278 QCOMPARE( _testWrite( tempLayerLineString, widget.get(), QStringLiteral( "stringf" ), Qt::TimeSpec::UTC ).toString( Qt::DateFormat::ISODate ), dateTime.toString( Qt::DateFormat::ISODate ) );
279 widget->mCaptureList.push_back( QgsPoint( 1, 2 ) );
280 widget->mCaptureList.push_back( QgsPoint( 3, 4 ) );
281 QCOMPARE( _testWrite( tempLayerLineString, widget.get(), QStringLiteral( "stringf" ), Qt::TimeSpec::LocalTime ).toString( Qt::DateFormat::ISODate ), localTime.toString( Qt::DateFormat::ISODate ) );
282 widget->mCaptureList.push_back( QgsPoint( 1, 2 ) );
283 widget->mCaptureList.push_back( QgsPoint( 3, 4 ) );
284 QCOMPARE( _testWrite( tempLayerLineString, widget.get(), QStringLiteral( "stringf" ), Qt::TimeSpec::TimeZone ).toString( Qt::DateFormat::ISODate ), tzTime.toString( Qt::DateFormat::ISODate ) );
285
286 // Write on GPKG
287 // Test write on datetime field
288 QCOMPARE( _testWrite( tempGpkgLayerPointString, widget.get(), QStringLiteral( "datetimef" ), Qt::TimeSpec::UTC, true ), dateTime );
289 QCOMPARE( _testWrite( tempGpkgLayerPointString, widget.get(), QStringLiteral( "datetimef" ), Qt::TimeSpec::LocalTime, true ), localTime );
290 QCOMPARE( _testWrite( tempGpkgLayerPointString, widget.get(), QStringLiteral( "datetimef" ), Qt::TimeSpec::TimeZone, true ), tzTime );
291
292 // Test write on string field
293 QCOMPARE( _testWrite( tempGpkgLayerPointString, widget.get(), QStringLiteral( "stringf" ),
294 Qt::TimeSpec::UTC, true ).toString( Qt::DateFormat::ISODate ), dateTime.toString( Qt::DateFormat::ISODate ) );
295 QCOMPARE( _testWrite( tempGpkgLayerPointString, widget.get(), QStringLiteral( "stringf" ),
296 Qt::TimeSpec::LocalTime, true ).toString( Qt::DateFormat::ISODate ), localTime.toString( Qt::DateFormat::ISODate ) );
297 QCOMPARE( _testWrite( tempGpkgLayerPointString, widget.get(), QStringLiteral( "stringf" ),
298 Qt::TimeSpec::TimeZone, true ).toString( Qt::DateFormat::ISODate ), tzTime.toString( Qt::DateFormat::ISODate ) );
299
300
301 }
302
testMultiPartLayers()303 void TestQgsGpsInformationWidget::testMultiPartLayers()
304 {
305 std::unique_ptr< QgsVectorLayer >multiLineString = std::make_unique< QgsVectorLayer >( QStringLiteral( "MultiLinestring?crs=epsg:4326&field=intf:int&field=stringf:string" ),
306 QStringLiteral( "vl4" ),
307 QStringLiteral( "memory" ) );
308
309 QgsMapCanvas *canvas = mQgisApp->mapCanvas();
310 std::unique_ptr<QgsGpsInformationWidget> widget = std::make_unique<QgsGpsInformationWidget>( canvas );
311 widget->mMapCanvas->setCurrentLayer( multiLineString.get() );
312 multiLineString->startEditing();
313
314 // not possible, no points
315 widget->mBtnCloseFeature_clicked();
316 QCOMPARE( multiLineString->featureCount(), 0L );
317 // need at least 2 points
318 widget->mBtnAddVertex_clicked();
319 widget->mBtnCloseFeature_clicked();
320 QCOMPARE( multiLineString->featureCount(), 0L );
321
322 widget->mBtnAddVertex_clicked();
323 widget->mBtnCloseFeature_clicked();
324 QCOMPARE( multiLineString->featureCount(), 1L );
325 QgsFeature f;
326 QVERIFY( multiLineString->getFeatures().nextFeature( f ) );
327 QCOMPARE( f.geometry().wkbType(), QgsWkbTypes::MultiLineString );
328 multiLineString->rollBack();
329
330 // multipolygon
331 std::unique_ptr< QgsVectorLayer >multiPolygon = std::make_unique< QgsVectorLayer >( QStringLiteral( "MultiPolygon?crs=epsg:4326&field=intf:int&field=stringf:string" ),
332 QStringLiteral( "vl4" ),
333 QStringLiteral( "memory" ) );
334
335 widget = std::make_unique<QgsGpsInformationWidget>( canvas );
336 widget->mMapCanvas->setCurrentLayer( multiPolygon.get() );
337 multiPolygon->startEditing();
338
339 // not possible, no points
340 widget->mBtnCloseFeature_clicked();
341 QCOMPARE( multiPolygon->featureCount(), 0L );
342
343 // need at least 3 points
344 widget->mBtnAddVertex_clicked();
345 widget->mBtnCloseFeature_clicked();
346 QCOMPARE( multiPolygon->featureCount(), 0L );
347 widget->mBtnAddVertex_clicked();
348 widget->mBtnCloseFeature_clicked();
349 QCOMPARE( multiPolygon->featureCount(), 0L );
350
351 widget->mBtnAddVertex_clicked();
352 widget->mBtnCloseFeature_clicked();
353 QCOMPARE( multiPolygon->featureCount(), 1L );
354 QVERIFY( multiPolygon->getFeatures().nextFeature( f ) );
355 QCOMPARE( f.geometry().wkbType(), QgsWkbTypes::MultiPolygon );
356 multiPolygon->rollBack();
357 }
358
359
360
361 QGSTEST_MAIN( TestQgsGpsInformationWidget )
362 #include "testqgsgpsinformationwidget.moc"
363