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