1 /***************************************************************************
2 testqgsfeaturelistcombobox.cpp
3
4 ---------------------
5 begin : 3.10.2017
6 copyright : (C) 2017 by Matthias Kuhn
7 email : matthias@opengis.ch
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include "qgstest.h"
18
19 #include "qgsapplication.h"
20 #include "qgsfeaturelistcombobox.h"
21 #include "qgsfilterlineedit.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsfeaturefiltermodel.h"
24 #include "qgsgui.h"
25
26 #include <memory>
27
28 #include <QLineEdit>
29 #include <QSignalSpy>
30
31 class QgsFilterLineEdit;
32
33 class TestQgsFeatureListComboBox : public QObject
34 {
35 Q_OBJECT
36 public:
37 TestQgsFeatureListComboBox() = default;
38
39 private slots:
40 void initTestCase(); // will be called before the first testfunction is executed.
41 void cleanupTestCase(); // will be called after the last testfunction was executed.
42 void init(); // will be called before each testfunction is executed.
43 void cleanup(); // will be called after every testfunction.
44
45 void testSetGetLayer();
46 void testSetGetForeignKey();
47 void testMultipleForeignKeys();
48 void testAllowNull();
49 void testValuesAndSelection();
50 void testValuesAndSelection_data();
51 void nullRepresentation();
52 void testNotExistingYetFeature();
53 void testFeatureFurtherThanFetchLimit();
54
55 private:
56
57 std::unique_ptr<QgsVectorLayer> mLayer;
58
59 friend class QgsFeatureListComboBox;
60 };
61
initTestCase()62 void TestQgsFeatureListComboBox::initTestCase()
63 {
64 QgsApplication::init();
65 QgsApplication::initQgis();
66
67 // Set up the QgsSettings environment
68 QCoreApplication::setOrganizationName( QStringLiteral( "QGIS" ) );
69 QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
70 QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST-FEATURELIST-COMBOBOX" ) );
71
72 }
73
cleanupTestCase()74 void TestQgsFeatureListComboBox::cleanupTestCase()
75 {
76 QgsApplication::exitQgis();
77 }
78
init()79 void TestQgsFeatureListComboBox::init()
80 {
81 // create layer
82 mLayer.reset( new QgsVectorLayer( QStringLiteral( "LineString?field=pk:int&field=material:string&field=diameter:int&field=raccord:string" ), QStringLiteral( "vl2" ), QStringLiteral( "memory" ) ) );
83 mLayer->setDisplayExpression( QStringLiteral( "pk" ) );
84
85 // add features
86 mLayer->startEditing();
87
88 QgsFeature ft1( mLayer->fields() );
89 ft1.setAttribute( QStringLiteral( "pk" ), 10 );
90 ft1.setAttribute( QStringLiteral( "material" ), "iron" );
91 ft1.setAttribute( QStringLiteral( "diameter" ), 120 );
92 ft1.setAttribute( QStringLiteral( "raccord" ), "brides" );
93 mLayer->addFeature( ft1 );
94
95 QgsFeature ft2( mLayer->fields() );
96 ft2.setAttribute( QStringLiteral( "pk" ), 11 );
97 ft2.setAttribute( QStringLiteral( "material" ), "iron" );
98 ft2.setAttribute( QStringLiteral( "diameter" ), 120 );
99 ft2.setAttribute( QStringLiteral( "raccord" ), "sleeve" );
100 mLayer->addFeature( ft2 );
101
102 QgsFeature ft3( mLayer->fields() );
103 ft3.setAttribute( QStringLiteral( "pk" ), 12 );
104 ft3.setAttribute( QStringLiteral( "material" ), "steel" );
105 ft3.setAttribute( QStringLiteral( "diameter" ), 120 );
106 ft3.setAttribute( QStringLiteral( "raccord" ), "collar" );
107 mLayer->addFeature( ft3 );
108
109 QgsFeatureList flist;
110 for ( int i = 13; i < 40; i++ )
111 {
112 QgsFeature f( mLayer->fields() );
113 f.setAttribute( QStringLiteral( "pk" ), i );
114 f.setAttribute( QStringLiteral( "material" ), QStringLiteral( "material_%1" ).arg( i ) );
115 f.setAttribute( QStringLiteral( "diameter" ), i );
116 f.setAttribute( QStringLiteral( "raccord" ), QStringLiteral( "raccord_%1" ).arg( i ) );
117 flist << f;
118 }
119 mLayer->addFeatures( flist );
120
121 mLayer->commitChanges();
122 }
123
cleanup()124 void TestQgsFeatureListComboBox::cleanup()
125 {
126 }
127
testSetGetLayer()128 void TestQgsFeatureListComboBox::testSetGetLayer()
129 {
130 std::unique_ptr<QgsFeatureListComboBox> cb( new QgsFeatureListComboBox() );
131
132 QVERIFY( cb->sourceLayer() == nullptr );
133 cb->setSourceLayer( mLayer.get() );
134 QCOMPARE( cb->sourceLayer(), mLayer.get() );
135 }
136
testSetGetForeignKey()137 void TestQgsFeatureListComboBox::testSetGetForeignKey()
138 {
139 QgsFeatureListComboBox cb;
140
141 Q_NOWARN_DEPRECATED_PUSH
142 QVERIFY( cb.identifierValue().isNull() );
143
144 cb.setSourceLayer( mLayer.get() );
145 cb.setDisplayExpression( "\"material\"" );
146 cb.setIdentifierField( "material" );
147
148 QSignalSpy spy( &cb, &QgsFeatureListComboBox::identifierValueChanged );
149 QTest::keyClicks( cb.lineEdit(), "ro" );
150 QTest::keyClick( cb.lineEdit(), Qt::Key_Enter );
151
152 spy.wait();
153
154 QCOMPARE( cb.identifierValue().toString(), QStringLiteral( "iron" ) );
155
156 Q_NOWARN_DEPRECATED_POP
157 }
158
testMultipleForeignKeys()159 void TestQgsFeatureListComboBox::testMultipleForeignKeys()
160 {
161 std::unique_ptr<QgsFeatureListComboBox> cb( new QgsFeatureListComboBox() );
162
163 QgsApplication::setNullRepresentation( QStringLiteral( "nope" ) );
164
165 QVERIFY( cb->identifierValues().isEmpty() );
166
167 cb->setSourceLayer( mLayer.get() );
168 cb->setIdentifierFields( QStringList() << "material" << "diameter" << "raccord" );
169 cb->setDisplayExpression( "\"material\" || ' ' || \"diameter\" || ' ' || \"raccord\"" );
170 cb->setAllowNull( true );
171
172 cb->setIdentifierValues( QVariantList() << "gold" << 777 << "rush" );
173 QCOMPARE( cb->identifierValues(), QVariantList() << "gold" << 777 << "rush" );
174
175 cb->setIdentifierValuesToNull();
176 QCOMPARE( cb->identifierValues().count(), 3 );
177 QCOMPARE( cb->identifierValues(), QVariantList() << QVariant( QVariant::Int ) << QVariant( QVariant::Int ) << QVariant( QVariant::Int ) );
178
179 cb->setIdentifierValues( QVariantList() << "silver" << 888 << "fish" );
180 QCOMPARE( cb->identifierValues(), QVariantList() << "silver" << 888 << "fish" );
181
182 cb->setIdentifierValuesToNull();
183 QCOMPARE( cb->identifierValues().count(), 3 );
184 QCOMPARE( cb->identifierValues(), QVariantList() << QVariant( QVariant::Int ) << QVariant( QVariant::Int ) << QVariant( QVariant::Int ) );
185
186 cb->setIdentifierFields( QStringList() << "material" << "raccord" );
187 cb->setDisplayExpression( "\"material\" || ' ' || \"raccord\"" );
188 cb->setAllowNull( true );
189
190 cb->setIdentifierValues( QVariantList() << "gold" << "fish" );
191 QCOMPARE( cb->identifierValues().count(), 2 );
192 QCOMPARE( cb->identifierValues(), QVariantList() << "gold" << "fish" );
193
194 cb->setIdentifierValuesToNull();
195 QCOMPARE( cb->identifierValues().count(), 2 );
196 QCOMPARE( cb->identifierValues(), QVariantList() << QVariant( QVariant::Int ) << QVariant( QVariant::Int ) );
197 }
198
testAllowNull()199 void TestQgsFeatureListComboBox::testAllowNull()
200 {
201 //QVERIFY( false );
202 // Note to self: implement this!
203 }
204
testValuesAndSelection_data()205 void TestQgsFeatureListComboBox::testValuesAndSelection_data()
206 {
207 QTest::addColumn<bool>( "allowNull" );
208
209 QTest::newRow( "allowNull=true" ) << true;
210 QTest::newRow( "allowNull=false" ) << false;
211 }
212
testValuesAndSelection()213 void TestQgsFeatureListComboBox::testValuesAndSelection()
214 {
215 QFETCH( bool, allowNull );
216
217 QgsApplication::setNullRepresentation( QStringLiteral( "nope" ) );
218 std::unique_ptr<QgsFeatureListComboBox> cb( new QgsFeatureListComboBox() );
219
220 QSignalSpy spy( cb.get(), &QgsFeatureListComboBox::identifierValueChanged );
221
222 cb->setSourceLayer( mLayer.get() );
223 cb->setAllowNull( allowNull );
224 cb->setIdentifierFields( {QStringLiteral( "raccord" )} );
225 cb->setDisplayExpression( QStringLiteral( "\"raccord\"" ) );
226
227 //check if everything is fine:
228 spy.wait();
229 QCOMPARE( cb->currentIndex(), allowNull ? cb->nullIndex() : 0 );
230 QCOMPARE( cb->currentText(), allowNull ? QStringLiteral( "nope" ) : QStringLiteral( "brides" ) );
231
232 //check if text correct, selected and if the clear button disappeared:
233 cb->mLineEdit->clearValue();
234 QCOMPARE( cb->currentIndex(), allowNull ? cb->nullIndex() : 0 );
235 QCOMPARE( cb->currentText(), allowNull ? QStringLiteral( "nope" ) : QString() );
236 QCOMPARE( cb->lineEdit()->selectedText(), allowNull ? QStringLiteral( "nope" ) : QString() );
237 QVERIFY( ! cb->mLineEdit->mClearAction );
238
239 //check if text is selected after receiving focus
240 cb->setFocus();
241 QCOMPARE( cb->currentIndex(), allowNull ? cb->nullIndex() : 0 );
242 QCOMPARE( cb->currentText(), allowNull ? QStringLiteral( "nope" ) : QString() );
243 QCOMPARE( cb->lineEdit()->selectedText(), allowNull ? QStringLiteral( "nope" ) : QString() );
244 QVERIFY( ! cb->mLineEdit->mClearAction );
245
246 //check with another entry, clear button needs to be there then:
247 QTest::keyClicks( cb.get(), QStringLiteral( "sleeve" ) );
248 spy.wait();
249 QCOMPARE( cb->currentText(), QStringLiteral( "sleeve" ) );
250 QVERIFY( cb->mLineEdit->mClearAction );
251 }
252
nullRepresentation()253 void TestQgsFeatureListComboBox::nullRepresentation()
254 {
255 QgsApplication::setNullRepresentation( QStringLiteral( "nope" ) );
256 std::unique_ptr<QgsFeatureListComboBox> cb( new QgsFeatureListComboBox() );
257
258 QgsFeatureFilterModel *model = qobject_cast<QgsFeatureFilterModel *>( cb->model() );
259 QEventLoop loop;
260 connect( model, &QgsFeatureFilterModel::filterJobCompleted, &loop, &QEventLoop::quit );
261
262 cb->setAllowNull( true );
263 cb->setSourceLayer( mLayer.get() );
264
265 loop.exec();
266 QCOMPARE( cb->lineEdit()->text(), QStringLiteral( "nope" ) );
267 QCOMPARE( cb->nullIndex(), 0 );
268 }
269
270
testNotExistingYetFeature()271 void TestQgsFeatureListComboBox::testNotExistingYetFeature()
272 {
273 // test behavior when feature list combo box identifier values references a
274 // not existing yet feature (created but not saved for instance)
275
276 std::unique_ptr<QgsFeatureListComboBox> cb( new QgsFeatureListComboBox() );
277 QgsFeatureFilterModel *model = qobject_cast<QgsFeatureFilterModel *>( cb->model() );
278 QEventLoop loop;
279 connect( model, &QgsFeatureFilterModel::filterJobCompleted, &loop, &QEventLoop::quit );
280
281 QgsApplication::setNullRepresentation( QStringLiteral( "nope" ) );
282
283 QVERIFY( cb->identifierValues().isEmpty() );
284
285 cb->setSourceLayer( mLayer.get() );
286 cb->setAllowNull( true );
287
288 cb->setIdentifierValues( QVariantList() << 42 );
289
290 loop.exec();
291 QCOMPARE( cb->currentText(), QStringLiteral( "(42)" ) );
292 }
293
testFeatureFurtherThanFetchLimit()294 void TestQgsFeatureListComboBox::testFeatureFurtherThanFetchLimit()
295 {
296 const int fetchLimit = 20;
297 QVERIFY( fetchLimit < mLayer->featureCount() );
298 std::unique_ptr<QgsFeatureListComboBox> cb( new QgsFeatureListComboBox() );
299 QgsFeatureFilterModel *model = qobject_cast<QgsFeatureFilterModel *>( cb->model() );
300 QSignalSpy spy( cb.get(), &QgsFeatureListComboBox::identifierValueChanged );
301 model->setFetchLimit( 20 );
302 model->setAllowNull( false );
303 cb->setSourceLayer( mLayer.get() );
304 cb->setIdentifierFields( {QStringLiteral( "pk" )} );
305 spy.wait();
306 QCOMPARE( model->mEntries.count(), 20 );
307 for ( int i = 0; i < 20; i++ )
308 QCOMPARE( model->mEntries.at( i ).identifierFields.at( 0 ).toInt(), i + 10 );
309 cb->setIdentifierValues( {33} );
310 spy.wait();
311 QCOMPARE( cb->lineEdit()->text(), QStringLiteral( "33" ) );
312 QCOMPARE( model->mEntries.count(), 21 );
313 QCOMPARE( model->mEntries.at( 0 ).identifierFields.at( 0 ).toInt(), 33 );
314 }
315
316 QGSTEST_MAIN( TestQgsFeatureListComboBox )
317 #include "testqgsfeaturelistcombobox.moc"
318