1 /***************************************************************************
2 testqgsvectorfilewriter.cpp
3 --------------------------------------
4 Date : Sun Sep 16 12:22:54 AKDT 2007
5 Copyright : (C) 2007 by Gary E. Sherman
6 Email : sherman at mrcc dot com
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 <QObject>
17 #include <QString>
18 #include <QStringList>
19 #include <QApplication>
20 #include <QFileInfo>
21 #include <QDir>
22
23 //qgis includes...
24 #include <qgsmaplayer.h>
25 #include <qgsvectorlayer.h>
26 #include <qgsapplication.h>
27 #include <qgsproviderregistry.h>
28 #include "qgsvectorlayerref.h"
29 #include "qgsmaplayerlistutils.h"
30
31 class TestSignalReceiver : public QObject
32 {
33 Q_OBJECT
34
35 public:
TestSignalReceiver()36 TestSignalReceiver()
37 : QObject( nullptr )
38 {}
39 QPainter::CompositionMode blendMode = QPainter::CompositionMode_SourceOver ;
40 public slots:
onBlendModeChanged(const QPainter::CompositionMode blendMode)41 void onBlendModeChanged( const QPainter::CompositionMode blendMode )
42 {
43 this->blendMode = blendMode;
44 }
45 };
46
47 /**
48 * \ingroup UnitTests
49 * This is a unit test for the QgsMapLayer class.
50 */
51 class TestQgsMapLayer : public QObject
52 {
53 Q_OBJECT
54
55 public:
56 TestQgsMapLayer() = default;
57
58 private slots:
59 void initTestCase();// will be called before the first testfunction is executed.
60 void cleanupTestCase();// will be called after the last testfunction was executed.
61 void init(); // will be called before each testfunction is executed.
62 void cleanup(); // will be called after every testfunction.
63
64 void isValid();
65 void formatName();
66
67 void setBlendMode();
68
69 void isInScaleRange_data();
70 void isInScaleRange();
71 void isInScaleRange2();
72
73 void layerRef();
74 void layerRefListUtils();
75 void layerRefResolveByIdOrNameOnly();
76 void layerRefResolveWeakly();
77
78 void styleCategories();
79
80 void notify();
81
82 private:
83 QgsVectorLayer *mpLayer = nullptr;
84 };
85
initTestCase()86 void TestQgsMapLayer::initTestCase()
87 {
88 //
89 // Runs once before any tests are run
90 //
91 // init QGIS's paths - true means that all path will be inited from prefix
92 QgsApplication::init();
93 QgsApplication::initQgis();
94 QgsApplication::showSettings();
95
96 }
97
init()98 void TestQgsMapLayer::init()
99 {
100 //create some objects that will be used in all tests...
101 //create a map layer that will be used in all tests...
102 QString myFileName( TEST_DATA_DIR ); //defined in CmakeLists.txt
103 myFileName = myFileName + "/points.shp";
104 QFileInfo myMapFileInfo( myFileName );
105 mpLayer = new QgsVectorLayer( myMapFileInfo.filePath(),
106 myMapFileInfo.completeBaseName(), QStringLiteral( "ogr" ) );
107 QgsProject::instance()->addMapLayer( mpLayer );
108 }
109
cleanup()110 void TestQgsMapLayer::cleanup()
111 {
112 QgsProject::instance()->removeAllMapLayers();
113 }
114
cleanupTestCase()115 void TestQgsMapLayer::cleanupTestCase()
116 {
117 QgsApplication::exitQgis();
118 }
119
isValid()120 void TestQgsMapLayer::isValid()
121 {
122 QVERIFY( mpLayer->isValid() );
123 }
124
formatName()125 void TestQgsMapLayer::formatName()
126 {
127 QCOMPARE( QgsMapLayer::formatLayerName( QString() ), QString() );
128 QCOMPARE( QgsMapLayer::formatLayerName( QStringLiteral( "layer" ) ), QStringLiteral( "Layer" ) );
129 QCOMPARE( QgsMapLayer::formatLayerName( QStringLiteral( "layer name" ) ), QStringLiteral( "Layer Name" ) );
130 QCOMPARE( QgsMapLayer::formatLayerName( QStringLiteral( "layer_name" ) ), QStringLiteral( "Layer Name" ) );
131 }
132
setBlendMode()133 void TestQgsMapLayer::setBlendMode()
134 {
135 TestSignalReceiver receiver;
136 QObject::connect( mpLayer, SIGNAL( blendModeChanged( const QPainter::CompositionMode ) ),
137 &receiver, SLOT( onBlendModeChanged( const QPainter::CompositionMode ) ) );
138 QCOMPARE( int( receiver.blendMode ), 0 );
139 mpLayer->setBlendMode( QPainter::CompositionMode_Screen );
140 // check the signal has been correctly emitted
141 QCOMPARE( receiver.blendMode, QPainter::CompositionMode_Screen );
142 // check accessor
143 QCOMPARE( mpLayer->blendMode(), QPainter::CompositionMode_Screen );
144 }
145
isInScaleRange_data()146 void TestQgsMapLayer::isInScaleRange_data()
147 {
148 QTest::addColumn<double>( "scale" );
149 QTest::addColumn<bool>( "isInScale" );
150
151 QTest::newRow( "in the middle" ) << 3000.0 << true;
152 QTest::newRow( "too low" ) << 1000.0 << false;
153 QTest::newRow( "too high" ) << 6000.0 << false;
154 QTest::newRow( "max is not inclusive" ) << 5000.0 << false;
155 QTest::newRow( "min is inclusive" ) << 2500.0 << true;
156 QTest::newRow( "min is inclusive even with conversion errors" ) << static_cast< double >( 1.0f / ( ( float )1.0 / 2500.0 ) ) << true;
157 }
158
isInScaleRange()159 void TestQgsMapLayer::isInScaleRange()
160 {
161 QFETCH( double, scale );
162 QFETCH( bool, isInScale );
163
164 mpLayer->setMaximumScale( 2500.0 );
165 mpLayer->setMinimumScale( 5000.0 );
166 mpLayer->setScaleBasedVisibility( true );
167 QCOMPARE( mpLayer->isInScaleRange( scale ), isInScale );
168 //always in scale range if scale based visibility is false
169 mpLayer->setScaleBasedVisibility( false );
170 QCOMPARE( mpLayer->isInScaleRange( scale ), true );
171 }
172
isInScaleRange2()173 void TestQgsMapLayer::isInScaleRange2()
174 {
175 mpLayer->setMaximumScale( 5000.0 );
176 mpLayer->setMinimumScale( 0.0 );
177 mpLayer->setScaleBasedVisibility( true );
178 QVERIFY( !mpLayer->isInScaleRange( 1000 ) );
179 QVERIFY( !mpLayer->isInScaleRange( 1 ) );
180 QVERIFY( !mpLayer->isInScaleRange( 4999 ) );
181 QVERIFY( mpLayer->isInScaleRange( 5001 ) );
182 QVERIFY( mpLayer->isInScaleRange( 15000 ) );
183
184 mpLayer->setMaximumScale( 0.0 );
185 mpLayer->setMinimumScale( 5000.0 );
186 mpLayer->setScaleBasedVisibility( true );
187 QVERIFY( mpLayer->isInScaleRange( 1000 ) );
188 QVERIFY( mpLayer->isInScaleRange( 1 ) );
189 QVERIFY( mpLayer->isInScaleRange( 4999 ) );
190 QVERIFY( !mpLayer->isInScaleRange( 5001 ) );
191 QVERIFY( !mpLayer->isInScaleRange( 15000 ) );
192 }
193
layerRef()194 void TestQgsMapLayer::layerRef()
195 {
196 // construct from layer
197 QgsVectorLayerRef ref( mpLayer );
198 QCOMPARE( ref.get(), mpLayer );
199 QCOMPARE( ref.layer.data(), mpLayer );
200 QCOMPARE( ref.layerId, mpLayer->id() );
201 QCOMPARE( ref.name, QStringLiteral( "points" ) );
202 QCOMPARE( ref.source, mpLayer->publicSource() );
203 QCOMPARE( ref.provider, QStringLiteral( "ogr" ) );
204
205 // bool operator
206 QVERIFY( ref );
207 // -> operator
208 QCOMPARE( ref->id(), mpLayer->id() );
209
210 // verify that layer matches layer
211 QVERIFY( ref.layerMatchesSource( mpLayer ) );
212
213 // create a weak reference
214 QgsVectorLayerRef ref2( mpLayer->id(), QStringLiteral( "points" ), mpLayer->publicSource(), QStringLiteral( "ogr" ) );
215 QVERIFY( !ref2 );
216 QVERIFY( !ref2.get() );
217 QVERIFY( !ref2.layer.data() );
218 QCOMPARE( ref2.layerId, mpLayer->id() );
219 QCOMPARE( ref2.name, QStringLiteral( "points" ) );
220 QCOMPARE( ref2.source, mpLayer->publicSource() );
221 QCOMPARE( ref2.provider, QStringLiteral( "ogr" ) );
222
223 // verify that weak reference matches layer
224 QVERIFY( ref2.layerMatchesSource( mpLayer ) );
225
226 // resolve layer using project
227 QCOMPARE( ref2.resolve( QgsProject::instance() ), mpLayer );
228 QVERIFY( ref2 );
229 QCOMPARE( ref2.get(), mpLayer );
230 QCOMPARE( ref2.layer.data(), mpLayer );
231 QCOMPARE( ref2.layerId, mpLayer->id() );
232 QCOMPARE( ref2.name, QStringLiteral( "points" ) );
233 QCOMPARE( ref2.source, mpLayer->publicSource() );
234 QCOMPARE( ref2.provider, QStringLiteral( "ogr" ) );
235
236 // setLayer
237 QgsVectorLayerRef ref3;
238 QVERIFY( !ref3.get() );
239 ref3.setLayer( mpLayer );
240 QCOMPARE( ref3.get(), mpLayer );
241 QCOMPARE( ref3.layer.data(), mpLayer );
242 QCOMPARE( ref3.layerId, mpLayer->id() );
243 QCOMPARE( ref3.name, QStringLiteral( "points" ) );
244 QCOMPARE( ref3.source, mpLayer->publicSource() );
245 QCOMPARE( ref3.provider, QStringLiteral( "ogr" ) );
246
247 // weak resolve
248 QgsVectorLayerRef ref4( QStringLiteral( "badid" ), QStringLiteral( "points" ), mpLayer->publicSource(), QStringLiteral( "ogr" ) );
249 QVERIFY( !ref4 );
250 QVERIFY( !ref4.resolve( QgsProject::instance() ) );
251 QCOMPARE( ref4.resolveWeakly( QgsProject::instance() ), mpLayer );
252 QCOMPARE( ref4.get(), mpLayer );
253 QCOMPARE( ref4.layer.data(), mpLayer );
254 QCOMPARE( ref4.layerId, mpLayer->id() );
255 QCOMPARE( ref4.name, QStringLiteral( "points" ) );
256 QCOMPARE( ref4.source, mpLayer->publicSource() );
257 QCOMPARE( ref4.provider, QStringLiteral( "ogr" ) );
258
259 // try resolving a bad reference
260 QgsVectorLayerRef ref5( QStringLiteral( "badid" ), QStringLiteral( "points" ), mpLayer->publicSource(), QStringLiteral( "xxx" ) );
261 QVERIFY( !ref5.get() );
262 QVERIFY( !ref5.resolve( QgsProject::instance() ) );
263 QVERIFY( !ref5.resolveWeakly( QgsProject::instance() ) );
264 }
265
layerRefListUtils()266 void TestQgsMapLayer::layerRefListUtils()
267 {
268 // conversion utils
269 QgsVectorLayer *vlA = new QgsVectorLayer( QStringLiteral( "Point" ), QStringLiteral( "a" ), QStringLiteral( "memory" ) );
270 QgsVectorLayer *vlB = new QgsVectorLayer( QStringLiteral( "Point" ), QStringLiteral( "b" ), QStringLiteral( "memory" ) );
271
272 QList<QgsMapLayer *> listRawSource;
273 listRawSource << vlA << vlB;
274
275 QList< QgsMapLayerRef > refs = _qgis_listRawToRef( listRawSource );
276 QCOMPARE( refs.at( 0 ).get(), vlA );
277 QCOMPARE( refs.at( 1 ).get(), vlB );
278
279 QList<QgsMapLayer *> raw = _qgis_listRefToRaw( refs );
280 QCOMPARE( raw, QList< QgsMapLayer *>() << vlA << vlB );
281
282 //remove layers
283 QgsVectorLayer *vlC = new QgsVectorLayer( QStringLiteral( "Point" ), QStringLiteral( "c" ), QStringLiteral( "memory" ) );
284 QgsVectorLayer *vlD = new QgsVectorLayer( QStringLiteral( "Point" ), QStringLiteral( "d" ), QStringLiteral( "memory" ) );
285 refs << QgsMapLayerRef( vlC ) << QgsMapLayerRef( vlD );
286
287 _qgis_removeLayers( refs, QList< QgsMapLayer *>() << vlB << vlD );
288 QCOMPARE( refs.size(), 2 );
289 QCOMPARE( refs.at( 0 ).get(), vlA );
290 QCOMPARE( refs.at( 1 ).get(), vlC );
291 }
292
layerRefResolveByIdOrNameOnly()293 void TestQgsMapLayer::layerRefResolveByIdOrNameOnly()
294 {
295 QgsVectorLayer *vlA = new QgsVectorLayer( QStringLiteral( "Point" ), QStringLiteral( "name" ), QStringLiteral( "memory" ) );
296 QgsVectorLayerRef ref;
297 QgsProject::instance()->addMapLayer( vlA );
298 ref.name = vlA->name();
299 QCOMPARE( ref.resolveByIdOrNameOnly( QgsProject::instance() ), vlA );
300 ref.layerId = vlA->id();
301 // Same name, different id
302 QgsVectorLayer *vlB = new QgsVectorLayer( QStringLiteral( "Point" ), QStringLiteral( "name" ), QStringLiteral( "memory" ) );
303 QgsProject::instance()->addMapLayer( vlB );
304 QCOMPARE( ref.resolveByIdOrNameOnly( QgsProject::instance() ), vlA );
305 // Remove layer A and check if B is returned (because they have the same name)
306 QgsProject::instance()->removeMapLayer( vlA );
307 QCOMPARE( ref.resolveByIdOrNameOnly( QgsProject::instance() ), vlB );
308 // Cleanup
309 QgsProject::instance()->removeAllMapLayers();
310 }
311
layerRefResolveWeakly()312 void TestQgsMapLayer::layerRefResolveWeakly()
313 {
314 QgsVectorLayer *vlA = new QgsVectorLayer( QStringLiteral( "Point" ), QStringLiteral( "name" ), QStringLiteral( "memory" ) );
315 QgsVectorLayerRef ref;
316 QgsProject::instance()->addMapLayer( vlA );
317 ref.name = vlA->name();
318 QVERIFY( ! ref.resolveWeakly( QgsProject::instance() ) );
319 QVERIFY( ref.resolveWeakly( QgsProject::instance(), QgsVectorLayerRef::MatchType::Name ) );
320
321 ref = QgsVectorLayerRef();
322 ref.name = QStringLiteral( "another name" );
323 QVERIFY( ! ref.resolveWeakly( QgsProject::instance(), QgsVectorLayerRef::MatchType::Name ) );
324 ref.provider = vlA->providerType();
325 QVERIFY( ref.resolveWeakly( QgsProject::instance(), QgsVectorLayerRef::MatchType::Provider ) );
326
327 ref = QgsVectorLayerRef();
328 ref.name = QStringLiteral( "another name" );
329 QVERIFY( ! ref.resolveWeakly( QgsProject::instance(),
330 static_cast<QgsVectorLayerRef::MatchType>( QgsVectorLayerRef::MatchType::Provider |
331 QgsVectorLayerRef::MatchType::Name ) ) );
332 ref.provider = vlA->providerType();
333 QVERIFY( ! ref.resolveWeakly( QgsProject::instance(),
334 static_cast<QgsVectorLayerRef::MatchType>( QgsVectorLayerRef::MatchType::Provider |
335 QgsVectorLayerRef::MatchType::Name ) ) );
336 ref.name = vlA->name();
337 QVERIFY( ref.resolveWeakly( QgsProject::instance(),
338 static_cast<QgsVectorLayerRef::MatchType>( QgsVectorLayerRef::MatchType::Provider |
339 QgsVectorLayerRef::MatchType::Name ) ) );
340
341 ref = QgsVectorLayerRef();
342 QVERIFY( ! ref.resolveWeakly( QgsProject::instance(),
343 static_cast<QgsVectorLayerRef::MatchType>( QgsVectorLayerRef::MatchType::Source |
344 QgsVectorLayerRef::MatchType::Name ) ) );
345 ref.source = vlA->publicSource();
346 QVERIFY( ! ref.resolveWeakly( QgsProject::instance(),
347 static_cast<QgsVectorLayerRef::MatchType>( QgsVectorLayerRef::MatchType::Source |
348 QgsVectorLayerRef::MatchType::Name ) ) );
349 ref.name = vlA->name();
350 QVERIFY( ref.resolveWeakly( QgsProject::instance(),
351 static_cast<QgsVectorLayerRef::MatchType>( QgsVectorLayerRef::MatchType::Source |
352 QgsVectorLayerRef::MatchType::Name ) ) );
353 }
354
styleCategories()355 void TestQgsMapLayer::styleCategories()
356 {
357 // control that AllStyleCategories is actually complete
358 QgsMapLayer::StyleCategories allStyleCategories = QgsMapLayer::AllStyleCategories;
359 for ( QgsMapLayer::StyleCategory category : qgsEnumMap<QgsMapLayer::StyleCategory>().keys() )
360 {
361 if ( category == QgsMapLayer::AllStyleCategories )
362 continue;
363
364 QVERIFY( allStyleCategories.testFlag( category ) );
365 }
366 }
367
notify()368 void TestQgsMapLayer::notify()
369 {
370 QgsVectorLayer *vl = new QgsVectorLayer( QStringLiteral( "Point" ), QStringLiteral( "name" ), QStringLiteral( "memory" ) );
371 QVERIFY( vl->dataProvider() );
372
373 QSignalSpy spyRepaint( vl, &QgsMapLayer::repaintRequested );
374 QSignalSpy spyDataChanged( vl, &QgsMapLayer::dataChanged );
375
376 vl->setRefreshOnNotifyEnabled( true );
377 emit vl->dataProvider()->notify( "test" );
378 QCOMPARE( spyRepaint.count(), 1 );
379 QCOMPARE( spyDataChanged.count(), 1 );
380
381 vl->setRefreshOnNotifyEnabled( false );
382 emit vl->dataProvider()->notify( "test" );
383 QCOMPARE( spyRepaint.count(), 1 );
384 QCOMPARE( spyDataChanged.count(), 1 );
385
386 vl->setRefreshOnNotifyEnabled( true );
387 vl->setRefreshOnNofifyMessage( "test" );
388 emit vl->dataProvider()->notify( "test" );
389 QCOMPARE( spyRepaint.count(), 2 );
390 QCOMPARE( spyDataChanged.count(), 2 );
391
392 vl->setRefreshOnNofifyMessage( "test" );
393 emit vl->dataProvider()->notify( "nottest" );
394 QCOMPARE( spyRepaint.count(), 2 );
395 QCOMPARE( spyDataChanged.count(), 2 );
396 }
397
398 QGSTEST_MAIN( TestQgsMapLayer )
399 #include "testqgsmaplayer.moc"
400