1 /***************************************************************************
2 testqgslayoutgui.cpp
3 ----------------------
4 Date : October 2017
5 Copyright : (C) 2017 Nyall Dawson
6 Email : nyall dot dawson at gmail 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
16
17
18
19 #include <QtTest/QTest>
20
21 #include "qgsmapsettings.h"
22 #include "qgslayout.h"
23 #include "qgslayoutitemshape.h"
24 #include "qgslayoutitemmap.h"
25 #include "qgslayoutmodel.h"
26 #include "qgslayoutitemcombobox.h"
27
28 #include <QApplication>
29 #include <QMainWindow>
30 #include <QtTest/QSignalSpy>
31
32
33 class TestQgsLayoutGui: public QObject
34 {
35 Q_OBJECT
36 private slots:
37 void initTestCase(); // will be called before the first testfunction is executed.
38 void cleanupTestCase(); // will be called after the last testfunction was executed.
39 void init(); // will be called before each testfunction is executed.
40 void cleanup(); // will be called after every testfunction.
41
42 void itemTypeComboBox();
43 void testProxyCrash();
44
45 private:
46
47 };
48
initTestCase()49 void TestQgsLayoutGui::initTestCase()
50 {
51 }
52
cleanupTestCase()53 void TestQgsLayoutGui::cleanupTestCase()
54 {
55 }
56
init()57 void TestQgsLayoutGui::init()
58 {
59 }
60
cleanup()61 void TestQgsLayoutGui::cleanup()
62 {
63 }
64
itemTypeComboBox()65 void TestQgsLayoutGui::itemTypeComboBox()
66 {
67 // test QgsLayoutItemComboBox
68 QgsLayout *layout = new QgsLayout( QgsProject::instance() );
69
70 // create a layout item combobox
71 QgsLayoutItemComboBox *cb = new QgsLayoutItemComboBox( nullptr, layout );
72 QgsLayoutItemComboBox *cb2 = new QgsLayoutItemComboBox( nullptr, layout );
73 cb2->setItemType( QgsLayoutItemRegistry::LayoutShape );
74
75 qRegisterMetaType<QgsLayoutItem *>();
76 const QSignalSpy spy1( cb, &QgsLayoutItemComboBox::itemChanged );
77 const QSignalSpy spy2( cb2, &QgsLayoutItemComboBox::itemChanged );
78
79 // add some items to layout
80 QgsLayoutItemMap *item1 = new QgsLayoutItemMap( layout );
81 item1->setId( "item 1" );
82 layout->addLayoutItem( item1 );
83
84 QCOMPARE( cb->count(), 1 );
85 QCOMPARE( cb2->count(), 0 );
86 QCOMPARE( cb->itemText( 0 ), QStringLiteral( "item 1" ) );
87 QCOMPARE( cb->item( 0 ), item1 );
88 QCOMPARE( cb->currentItem(), item1 );
89 QVERIFY( !cb2->currentItem() );
90
91 int expectedSpy1Count = 2;// ideally only one, but we'll settle for 2
92 int expectedSpy2Count = 0;
93 QCOMPARE( spy1.count(), 2 );
94 QCOMPARE( qvariant_cast<QObject *>( spy1.at( 1 ).at( 0 ) ), item1 );
95 QCOMPARE( spy2.count(), 0 );
96
97 QgsLayoutItemShape *item2 = new QgsLayoutItemShape( layout );
98 item2->setId( "item 2" );
99 layout->addLayoutItem( item2 );
100
101 QCOMPARE( cb->count(), 2 );
102 QCOMPARE( cb2->count(), 1 );
103 QCOMPARE( cb->itemText( 0 ), QStringLiteral( "item 1" ) );
104 QCOMPARE( cb->itemText( 1 ), QStringLiteral( "item 2" ) );
105 QCOMPARE( cb2->itemText( 0 ), QStringLiteral( "item 2" ) );
106 QCOMPARE( cb->item( 0 ), item1 );
107 QCOMPARE( cb->item( 1 ), item2 );
108 QCOMPARE( cb2->item( 0 ), item2 );
109 QCOMPARE( cb->currentItem(), item1 );
110 QCOMPARE( cb2->currentItem(), item2 );
111
112 QCOMPARE( spy1.count(), expectedSpy1Count ); // must be unchanged from earlier
113 expectedSpy2Count = 2;// ideally only one, but we'll settle for 2
114 QCOMPARE( spy2.count(), expectedSpy2Count );
115 QCOMPARE( qvariant_cast<QObject *>( spy2.at( 1 ).at( 0 ) ), item2 );
116
117 QgsLayoutItemMap *item3 = new QgsLayoutItemMap( layout );
118 item3->setId( "item 3" );
119 layout->addLayoutItem( item3 );
120
121 QCOMPARE( cb->count(), 3 );
122 QCOMPARE( cb2->count(), 1 );
123 QCOMPARE( cb->itemText( 0 ), QStringLiteral( "item 1" ) );
124 QCOMPARE( cb->itemText( 1 ), QStringLiteral( "item 2" ) );
125 QCOMPARE( cb->itemText( 2 ), QStringLiteral( "item 3" ) );
126 QCOMPARE( cb2->itemText( 0 ), QStringLiteral( "item 2" ) );
127 QCOMPARE( cb->item( 0 ), item1 );
128 QCOMPARE( cb->item( 1 ), item2 );
129 QCOMPARE( cb->item( 2 ), item3 );
130 QCOMPARE( cb2->item( 0 ), item2 );
131 QCOMPARE( cb->currentItem(), item1 );
132 QCOMPARE( cb2->currentItem(), item2 );
133 QCOMPARE( spy1.count(), expectedSpy1Count ); // must be unchanged from earlier
134 QCOMPARE( spy2.count(), expectedSpy2Count );
135
136 item3->setId( "a" );
137 QCOMPARE( cb->itemText( 0 ), QStringLiteral( "a" ) );
138 QCOMPARE( cb->itemText( 1 ), QStringLiteral( "item 1" ) );
139 QCOMPARE( cb->itemText( 2 ), QStringLiteral( "item 2" ) );
140 item3->setId( "item 3" );
141 QCOMPARE( cb->itemText( 0 ), QStringLiteral( "item 1" ) );
142 QCOMPARE( cb->itemText( 1 ), QStringLiteral( "item 2" ) );
143 QCOMPARE( cb->itemText( 2 ), QStringLiteral( "item 3" ) );
144
145 //manually change item
146 cb->setItem( item3 );
147 expectedSpy1Count++;
148 QCOMPARE( spy1.count(), expectedSpy1Count );
149 QCOMPARE( qvariant_cast<QObject *>( spy1.at( expectedSpy1Count - 1 ).at( 0 ) ), item3 );
150 QCOMPARE( cb->currentItem(), item3 );
151
152 cb2->setItem( nullptr );
153 QVERIFY( !cb2->currentItem() );
154 expectedSpy2Count++;
155 QCOMPARE( spy2.count(), expectedSpy2Count );
156 QVERIFY( !qvariant_cast<QObject *>( spy2.at( expectedSpy2Count - 1 ).at( 0 ) ) );
157
158 // remove item
159 layout->removeLayoutItem( item3 );
160 expectedSpy1Count++;
161 QCOMPARE( spy1.count(), expectedSpy1Count );
162 QCOMPARE( qvariant_cast<QObject *>( spy1.at( expectedSpy1Count - 1 ).at( 0 ) ), item2 );
163 QCOMPARE( cb->currentItem(), item2 );
164 QCOMPARE( spy2.count(), expectedSpy2Count );
165 QCOMPARE( cb->count(), 2 );
166 QCOMPARE( cb2->count(), 1 );
167
168 layout->removeLayoutItem( item2 );
169 expectedSpy1Count += 2; // ideally just one signal, but we want at LEAST 1...
170 QCOMPARE( spy1.count(), expectedSpy1Count );
171 QCOMPARE( qvariant_cast<QObject *>( spy1.at( expectedSpy1Count - 1 ).at( 0 ) ), item1 );
172 QCOMPARE( cb->currentItem(), item1 );
173 QCOMPARE( cb->count(), 1 );
174 QCOMPARE( cb2->count(), 0 );
175 expectedSpy2Count++;
176 QCOMPARE( spy2.count(), expectedSpy2Count );
177 QVERIFY( !qvariant_cast<QObject *>( spy2.at( expectedSpy2Count - 1 ).at( 0 ) ) );
178 QVERIFY( !cb2->currentItem() );
179
180 layout->removeLayoutItem( item1 );
181 expectedSpy1Count += 2; // ideally just one signal, but we want at LEAST 1...
182 QCOMPARE( spy1.count(), expectedSpy1Count );
183 QVERIFY( !qvariant_cast<QObject *>( spy1.at( expectedSpy1Count - 1 ).at( 0 ) ) );
184 QVERIFY( !cb->currentItem() );
185 QCOMPARE( cb->count(), 0 );
186 QCOMPARE( cb2->count(), 0 );
187 QCOMPARE( spy2.count(), expectedSpy2Count );
188 }
189
testProxyCrash()190 void TestQgsLayoutGui::testProxyCrash()
191 {
192 // test for a possible crash when using QgsLayoutProxyModel and reordering items
193 QgsLayout *layout = new QgsLayout( QgsProject::instance() );
194
195 // create a layout item combobox
196 QgsLayoutItemComboBox *cb = new QgsLayoutItemComboBox( nullptr, layout );
197 QgsLayoutItemComboBox *cb2 = new QgsLayoutItemComboBox( nullptr, layout );
198 QgsLayoutItemComboBox *cb3 = new QgsLayoutItemComboBox( nullptr, layout );
199 QgsLayoutItemComboBox *cb4 = new QgsLayoutItemComboBox( nullptr, layout );
200
201 // add some items to layout
202 QgsLayoutItemMap *item1 = new QgsLayoutItemMap( layout );
203 layout->addLayoutItem( item1 );
204 QCOMPARE( cb->count(), 1 );
205 QgsLayoutItemShape *item2 = new QgsLayoutItemShape( layout );
206 layout->addLayoutItem( item2 );
207 QCOMPARE( cb->count(), 2 );
208 QgsLayoutItemMap *item3 = new QgsLayoutItemMap( layout );
209 layout->addLayoutItem( item3 );
210
211 QCOMPARE( cb->count(), 3 );
212 cb->setItemType( QgsLayoutItemRegistry::LayoutMap );
213 QCOMPARE( cb->count(), 2 );
214
215 cb4->setItemType( QgsLayoutItemRegistry::LayoutShape );
216
217 cb->setItem( item1 );
218 QCOMPARE( cb->currentItem(), item1 );
219 cb2->setItem( item3 );
220 QCOMPARE( cb2->currentItem(), item3 );
221 cb3->setItem( item2 );
222 QCOMPARE( cb3->currentItem(), item2 );
223
224 // reorder items - expect no crash!
225 // we do this by calling the private members, in order to simulate what
226 // happens when a drag and drop reorder occurs
227 layout->itemsModel()->mItemZList.removeOne( item1 );
228 layout->itemsModel()->mItemZList.insert( 1, item1 );
229 layout->itemsModel()->rebuildSceneItemList();
230
231 QCOMPARE( cb->currentItem(), item1 );
232 QCOMPARE( cb2->currentItem(), item3 );
233
234 layout->itemsModel()->mItemZList.removeOne( item1 );
235 layout->itemsModel()->mItemZList.insert( 0, item1 );
236 layout->itemsModel()->rebuildSceneItemList();
237
238 delete layout;
239 }
240
241
242 QTEST_MAIN( TestQgsLayoutGui )
243 #include "testqgslayoutgui.moc"
244