1# -*- coding: utf-8 -*-
2"""QGIS Unit tests for QgsFieldModel
3
4.. note:: This program is free software; you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation; either version 2 of the License, or
7(at your option) any later version.
8"""
9__author__ = 'Nyall Dawson'
10__date__ = '14/11/2016'
11__copyright__ = 'Copyright 2016, The QGIS Project'
12
13import qgis  # NOQA
14
15from qgis.core import (QgsField,
16                       QgsFields,
17                       QgsVectorLayer,
18                       QgsFieldModel,
19                       QgsFieldProxyModel,
20                       QgsEditorWidgetSetup,
21                       QgsProject,
22                       QgsVectorLayerJoinInfo,
23                       QgsFieldConstraints)
24from qgis.PyQt.QtCore import QVariant, Qt, QModelIndex
25
26from qgis.testing import start_app, unittest
27
28start_app()
29
30
31def create_layer():
32    layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer",
33                           "addfeat", "memory")
34    layer.setEditorWidgetSetup(0, QgsEditorWidgetSetup('Hidden', {}))
35    layer.setEditorWidgetSetup(1, QgsEditorWidgetSetup('ValueMap', {}))
36    assert layer.isValid()
37    return layer
38
39
40def create_model():
41    l = create_layer()
42    m = QgsFieldModel()
43    m.setLayer(l)
44    return l, m
45
46
47class TestQgsFieldModel(unittest.TestCase):
48
49    def testGettersSetters(self):
50        """ test model getters/setters """
51        l = create_layer()
52        m = QgsFieldModel()
53
54        self.assertFalse(m.layer())
55        m.setLayer(l)
56        self.assertEqual(m.layer(), l)
57
58        m.setAllowExpression(True)
59        self.assertTrue(m.allowExpression())
60        m.setAllowExpression(False)
61        self.assertFalse(m.allowExpression())
62
63        m.setAllowEmptyFieldName(True)
64        self.assertTrue(m.allowEmptyFieldName())
65        m.setAllowEmptyFieldName(False)
66        self.assertFalse(m.allowEmptyFieldName())
67
68        fields = QgsFields()
69        fields.append(QgsField('test1', QVariant.String))
70        fields.append(QgsField('test2', QVariant.String))
71        m.setFields(fields)
72        self.assertIsNone(m.layer())
73        self.assertEqual(m.fields(), fields)
74
75    def testIndexFromName(self):
76        l, m = create_model()
77        i = m.indexFromName('fldtxt')
78        self.assertTrue(i.isValid())
79        self.assertEqual(i.row(), 0)
80        i = m.indexFromName('fldint')
81        self.assertTrue(i.isValid())
82        self.assertEqual(i.row(), 1)
83        i = m.indexFromName('not a field')
84        self.assertFalse(i.isValid())
85
86        # test with alias
87        i = m.indexFromName('text field')
88        self.assertFalse(i.isValid())
89        l.setFieldAlias(0, 'text field')
90        i = m.indexFromName('text field')
91        self.assertTrue(i.isValid())
92        self.assertEqual(i.row(), 0)
93        i = m.indexFromName('int field')
94        self.assertFalse(i.isValid())
95        l.setFieldAlias(1, 'int field')
96        i = m.indexFromName('int field')
97        self.assertTrue(i.isValid())
98        self.assertEqual(i.row(), 1)
99
100        # should be case insensitive
101        i = m.indexFromName('FLDTXT')
102        self.assertTrue(i.isValid())
103        self.assertEqual(i.row(), 0)
104        i = m.indexFromName('FLDINT')
105        self.assertTrue(i.isValid())
106        self.assertEqual(i.row(), 1)
107
108        # try with expression
109        m.setAllowExpression(True)
110        i = m.indexFromName('not a field')
111        # still not valid - needs expression set first
112        self.assertFalse(i.isValid())
113        m.setExpression('not a field')
114        i = m.indexFromName('not a field')
115        self.assertTrue(i.isValid())
116        self.assertEqual(i.row(), 2)
117
118        # try with null
119        i = m.indexFromName(None)
120        self.assertFalse(i.isValid())
121        m.setAllowEmptyFieldName(True)
122        i = m.indexFromName(None)
123        self.assertTrue(i.isValid())
124        self.assertEqual(i.row(), 0)
125        # when null is shown, all other rows should be offset
126        self.assertEqual(m.indexFromName('fldtxt').row(), 1)
127        self.assertEqual(m.indexFromName('fldint').row(), 2)
128        self.assertEqual(m.indexFromName('not a field').row(), 3)
129        self.assertEqual(m.indexFromName('FLDTXT').row(), 1)
130        self.assertEqual(m.indexFromName('FLDINT').row(), 2)
131
132    def testIsField(self):
133        l, m = create_model()
134        self.assertTrue(m.isField('fldtxt'))
135        self.assertTrue(m.isField('fldint'))
136        self.assertFalse(m.isField(None))
137        self.assertFalse(m.isField('an expression'))
138
139    def testRowCount(self):
140        l, m = create_model()
141        self.assertEqual(m.rowCount(), 2)
142        m.setAllowEmptyFieldName(True)
143        self.assertEqual(m.rowCount(), 3)
144        m.setAllowExpression(True)
145        m.setExpression('not a field')
146        self.assertEqual(m.rowCount(), 4)
147        m.setExpression('not a field')
148        self.assertEqual(m.rowCount(), 4)
149        m.setExpression('not a field 2')
150        self.assertEqual(m.rowCount(), 4)
151        m.removeExpression()
152        self.assertEqual(m.rowCount(), 3)
153
154    def testFieldNameRole(self):
155        l, m = create_model()
156        self.assertEqual(m.data(m.indexFromName('fldtxt'), QgsFieldModel.FieldNameRole), 'fldtxt')
157        self.assertEqual(m.data(m.indexFromName('fldint'), QgsFieldModel.FieldNameRole), 'fldint')
158        self.assertFalse(m.data(m.indexFromName('an expression'), QgsFieldModel.FieldNameRole))
159        self.assertFalse(m.data(m.indexFromName(None), QgsFieldModel.FieldNameRole))
160        m.setAllowExpression(True)
161        m.setExpression('an expression')
162        self.assertFalse(m.data(m.indexFromName('an expression'), QgsFieldModel.FieldNameRole))
163        m.setAllowEmptyFieldName(True)
164        self.assertFalse(m.data(m.indexFromName(None), QgsFieldModel.FieldNameRole))
165
166    def testExpressionRole(self):
167        l, m = create_model()
168        self.assertEqual(m.data(m.indexFromName('fldtxt'), QgsFieldModel.ExpressionRole), 'fldtxt')
169        self.assertEqual(m.data(m.indexFromName('fldint'), QgsFieldModel.ExpressionRole), 'fldint')
170        self.assertFalse(m.data(m.indexFromName('an expression'), QgsFieldModel.ExpressionRole))
171        self.assertFalse(m.data(m.indexFromName(None), QgsFieldModel.ExpressionRole))
172        m.setAllowExpression(True)
173        m.setExpression('an expression')
174        self.assertEqual(m.data(m.indexFromName('an expression'), QgsFieldModel.ExpressionRole), 'an expression')
175        m.setAllowEmptyFieldName(True)
176        self.assertFalse(m.data(m.indexFromName(None), QgsFieldModel.ExpressionRole))
177
178    def testFieldIndexRole(self):
179        l, m = create_model()
180        self.assertEqual(m.data(m.indexFromName('fldtxt'), QgsFieldModel.FieldIndexRole), 0)
181        self.assertEqual(m.data(m.indexFromName('fldint'), QgsFieldModel.FieldIndexRole), 1)
182        self.assertFalse(m.data(m.indexFromName('an expression'), QgsFieldModel.FieldIndexRole))
183        self.assertFalse(m.data(m.indexFromName(None), QgsFieldModel.FieldIndexRole))
184        m.setAllowExpression(True)
185        m.setExpression('an expression')
186        self.assertFalse(m.data(m.indexFromName('an expression'), QgsFieldModel.FieldIndexRole))
187        m.setAllowEmptyFieldName(True)
188        self.assertFalse(m.data(m.indexFromName(None), QgsFieldModel.FieldIndexRole))
189
190    def testIsExpressionRole(self):
191        l, m = create_model()
192        self.assertFalse(m.data(m.indexFromName('fldtxt'), QgsFieldModel.IsExpressionRole))
193        self.assertFalse(m.data(m.indexFromName('fldint'), QgsFieldModel.IsExpressionRole))
194        self.assertFalse(m.data(m.indexFromName('an expression'), QgsFieldModel.IsExpressionRole))
195        self.assertFalse(m.data(m.indexFromName(None), QgsFieldModel.IsExpressionRole))
196        m.setAllowExpression(True)
197        m.setExpression('an expression')
198        self.assertTrue(m.data(m.indexFromName('an expression'), QgsFieldModel.IsExpressionRole))
199        m.setAllowEmptyFieldName(True)
200        self.assertFalse(m.data(m.indexFromName(None), QgsFieldModel.IsExpressionRole))
201
202    def testExpressionValidityRole(self):
203        l, m = create_model()
204        self.assertTrue(m.data(m.indexFromName('fldtxt'), QgsFieldModel.ExpressionValidityRole))
205        self.assertTrue(m.data(m.indexFromName('fldint'), QgsFieldModel.ExpressionValidityRole))
206        self.assertFalse(m.data(m.indexFromName('an expression'), QgsFieldModel.ExpressionValidityRole))
207        self.assertFalse(m.data(m.indexFromName(None), QgsFieldModel.ExpressionValidityRole))
208        m.setAllowExpression(True)
209        m.setExpression('an expression')
210        self.assertFalse(m.data(m.indexFromName('an expression'), QgsFieldModel.ExpressionValidityRole))
211        m.setAllowEmptyFieldName(True)
212        self.assertTrue(m.data(m.indexFromName(None), QgsFieldModel.ExpressionValidityRole))
213
214    def testFieldTypeRole(self):
215        l, m = create_model()
216        self.assertEqual(m.data(m.indexFromName('fldtxt'), QgsFieldModel.FieldTypeRole), QVariant.String)
217        self.assertEqual(m.data(m.indexFromName('fldint'), QgsFieldModel.FieldTypeRole), QVariant.Int)
218        self.assertFalse(m.data(m.indexFromName('an expression'), QgsFieldModel.FieldTypeRole))
219        self.assertFalse(m.data(m.indexFromName(None), QgsFieldModel.FieldTypeRole))
220        m.setAllowExpression(True)
221        m.setExpression('an expression')
222        self.assertFalse(m.data(m.indexFromName('an expression'), QgsFieldModel.FieldTypeRole))
223        m.setAllowEmptyFieldName(True)
224        self.assertFalse(m.data(m.indexFromName(None), QgsFieldModel.FieldTypeRole))
225
226    def testFieldOriginRole(self):
227        l, m = create_model()
228        self.assertEqual(m.data(m.indexFromName('fldtxt'), QgsFieldModel.FieldOriginRole), QgsFields.OriginProvider)
229        self.assertEqual(m.data(m.indexFromName('fldint'), QgsFieldModel.FieldOriginRole), QgsFields.OriginProvider)
230        self.assertFalse(m.data(m.indexFromName('an expression'), QgsFieldModel.FieldOriginRole))
231        self.assertFalse(m.data(m.indexFromName(None), QgsFieldModel.FieldOriginRole))
232        m.setAllowExpression(True)
233        m.setExpression('an expression')
234        self.assertFalse(m.data(m.indexFromName('an expression'), QgsFieldModel.FieldOriginRole))
235        m.setAllowEmptyFieldName(True)
236        self.assertFalse(m.data(m.indexFromName(None), QgsFieldModel.FieldOriginRole))
237
238    def testIsEmptyRole(self):
239        l, m = create_model()
240        self.assertFalse(m.data(m.indexFromName('fldtxt'), QgsFieldModel.IsEmptyRole), QgsFields.OriginProvider)
241        self.assertFalse(m.data(m.indexFromName('fldint'), QgsFieldModel.IsEmptyRole), QgsFields.OriginProvider)
242        self.assertFalse(m.data(m.indexFromName('an expression'), QgsFieldModel.IsEmptyRole))
243        self.assertFalse(m.data(m.indexFromName(None), QgsFieldModel.IsEmptyRole))
244        m.setAllowExpression(True)
245        m.setExpression('an expression')
246        self.assertFalse(m.data(m.indexFromName('an expression'), QgsFieldModel.IsEmptyRole))
247        m.setAllowEmptyFieldName(True)
248        self.assertTrue(m.data(m.indexFromName(None), QgsFieldModel.IsEmptyRole))
249
250    def testDisplayRole(self):
251        l, m = create_model()
252        self.assertEqual(m.data(m.indexFromName('fldtxt'), Qt.DisplayRole), 'fldtxt')
253        self.assertEqual(m.data(m.indexFromName('fldint'), Qt.DisplayRole), 'fldint')
254        self.assertFalse(m.data(m.indexFromName('an expression'), Qt.DisplayRole))
255        self.assertFalse(m.data(m.indexFromName(None), Qt.DisplayRole))
256        m.setAllowExpression(True)
257        m.setExpression('an expression')
258        self.assertEqual(m.data(m.indexFromName('an expression'), Qt.DisplayRole), 'an expression')
259        m.setAllowEmptyFieldName(True)
260        self.assertFalse(m.data(m.indexFromName(None), Qt.DisplayRole))
261
262    def testManualFields(self):
263        _, m = create_model()
264        fields = QgsFields()
265        fields.append(QgsField('f1', QVariant.String))
266        fields.append(QgsField('f2', QVariant.String))
267        m.setFields(fields)
268        self.assertEqual(m.rowCount(), 2)
269        self.assertEqual(m.data(m.index(0, 0, QModelIndex()), Qt.DisplayRole), 'f1')
270        self.assertEqual(m.data(m.index(1, 0, QModelIndex()), Qt.DisplayRole), 'f2')
271
272    def testEditorWidgetTypeRole(self):
273        l, m = create_model()
274        self.assertEqual(m.data(m.indexFromName('fldtxt'), QgsFieldModel.EditorWidgetType), 'Hidden')
275        self.assertEqual(m.data(m.indexFromName('fldint'), QgsFieldModel.EditorWidgetType), 'ValueMap')
276        self.assertIsNone(m.data(m.indexFromName('an expression'), QgsFieldModel.EditorWidgetType))
277        self.assertIsNone(m.data(m.indexFromName(None), QgsFieldModel.EditorWidgetType))
278        m.setAllowExpression(True)
279        m.setExpression('an expression')
280        self.assertIsNone(m.data(m.indexFromName('an expression'), QgsFieldModel.EditorWidgetType))
281        m.setAllowEmptyFieldName(True)
282        self.assertIsNone(m.data(m.indexFromName(None), QgsFieldModel.EditorWidgetType))
283
284    def testJoinedFieldIsEditableRole(self):
285        layer = QgsVectorLayer("Point?field=id_a:integer",
286                               "addfeat", "memory")
287        layer2 = QgsVectorLayer("Point?field=id_b:integer&field=value_b",
288                                "addfeat", "memory")
289        QgsProject.instance().addMapLayers([layer, layer2])
290
291        # editable join
292        join_info = QgsVectorLayerJoinInfo()
293        join_info.setTargetFieldName("id_a")
294        join_info.setJoinLayer(layer2)
295        join_info.setJoinFieldName("id_b")
296        join_info.setPrefix("B_")
297        join_info.setEditable(True)
298        join_info.setUpsertOnEdit(True)
299        layer.addJoin(join_info)
300
301        m = QgsFieldModel()
302        m.setLayer(layer)
303
304        self.assertIsNone(m.data(m.indexFromName('id_a'), QgsFieldModel.JoinedFieldIsEditable))
305        self.assertTrue(m.data(m.indexFromName('B_value_b'), QgsFieldModel.JoinedFieldIsEditable))
306        self.assertIsNone(m.data(m.indexFromName('an expression'), QgsFieldModel.JoinedFieldIsEditable))
307        self.assertIsNone(m.data(m.indexFromName(None), QgsFieldModel.JoinedFieldIsEditable))
308        m.setAllowExpression(True)
309        m.setExpression('an expression')
310        self.assertIsNone(m.data(m.indexFromName('an expression'), QgsFieldModel.JoinedFieldIsEditable))
311        m.setAllowEmptyFieldName(True)
312        self.assertIsNone(m.data(m.indexFromName(None), QgsFieldModel.JoinedFieldIsEditable))
313
314        proxy_m = QgsFieldProxyModel()
315        proxy_m.setFilters(QgsFieldProxyModel.AllTypes | QgsFieldProxyModel.HideReadOnly)
316        proxy_m.sourceFieldModel().setLayer(layer)
317        self.assertEqual(proxy_m.rowCount(), 2)
318        self.assertEqual(proxy_m.data(proxy_m.index(0, 0)), 'id_a')
319        self.assertEqual(proxy_m.data(proxy_m.index(1, 0)), 'B_value_b')
320
321        # not editable join
322        layer3 = QgsVectorLayer("Point?field=id_a:integer",
323                                "addfeat", "memory")
324        QgsProject.instance().addMapLayers([layer3])
325        join_info = QgsVectorLayerJoinInfo()
326        join_info.setTargetFieldName("id_a")
327        join_info.setJoinLayer(layer2)
328        join_info.setJoinFieldName("id_b")
329        join_info.setPrefix("B_")
330        join_info.setEditable(False)
331
332        layer3.addJoin(join_info)
333        m = QgsFieldModel()
334        m.setLayer(layer3)
335
336        self.assertIsNone(m.data(m.indexFromName('id_a'), QgsFieldModel.JoinedFieldIsEditable))
337        self.assertFalse(m.data(m.indexFromName('B_value_b'), QgsFieldModel.JoinedFieldIsEditable))
338        self.assertIsNone(m.data(m.indexFromName('an expression'), QgsFieldModel.JoinedFieldIsEditable))
339        self.assertIsNone(m.data(m.indexFromName(None), QgsFieldModel.JoinedFieldIsEditable))
340        m.setAllowExpression(True)
341        m.setExpression('an expression')
342        self.assertIsNone(m.data(m.indexFromName('an expression'), QgsFieldModel.JoinedFieldIsEditable))
343        m.setAllowEmptyFieldName(True)
344        self.assertIsNone(m.data(m.indexFromName(None), QgsFieldModel.JoinedFieldIsEditable))
345
346        proxy_m = QgsFieldProxyModel()
347        proxy_m.sourceFieldModel().setLayer(layer3)
348        proxy_m.setFilters(QgsFieldProxyModel.AllTypes | QgsFieldProxyModel.HideReadOnly)
349        self.assertEqual(proxy_m.rowCount(), 1)
350        self.assertEqual(proxy_m.data(proxy_m.index(0, 0)), 'id_a')
351
352    def testFieldIsWidgetEditableRole(self):
353        l, m = create_model()
354        self.assertTrue(m.data(m.indexFromName('fldtxt'), QgsFieldModel.FieldIsWidgetEditable))
355        self.assertTrue(m.data(m.indexFromName('fldint'), QgsFieldModel.FieldIsWidgetEditable))
356        self.assertFalse(m.data(m.indexFromName('an expression'), QgsFieldModel.FieldIsWidgetEditable))
357        self.assertFalse(m.data(m.indexFromName(None), QgsFieldModel.FieldIsWidgetEditable))
358        m.setAllowExpression(True)
359        m.setExpression('an expression')
360        self.assertTrue(m.data(m.indexFromName('an expression'), QgsFieldModel.FieldIsWidgetEditable))
361        m.setAllowEmptyFieldName(True)
362        self.assertTrue(m.data(m.indexFromName(None), QgsFieldModel.FieldIsWidgetEditable))
363
364        editFormConfig = l.editFormConfig()
365        idx = l.fields().indexOf('fldtxt')
366        # Make fldtxt readOnly
367        editFormConfig.setReadOnly(idx, True)
368        l.setEditFormConfig(editFormConfig)
369        # It's read only, so the widget is NOT editable
370        self.assertFalse(m.data(m.indexFromName('fldtxt'), QgsFieldModel.FieldIsWidgetEditable))
371
372    def testFieldTooltip(self):
373        f = QgsField('my_string', QVariant.String, 'string')
374        self.assertEqual(QgsFieldModel.fieldToolTip(f), "<b>my_string</b><br><font style='font-family:monospace; white-space: nowrap;'>string NULL</font>")
375        f.setAlias('my alias')
376        self.assertEqual(QgsFieldModel.fieldToolTip(f), "<b>my alias</b> (my_string)<br><font style='font-family:monospace; white-space: nowrap;'>string NULL</font>")
377        f.setLength(20)
378        self.assertEqual(QgsFieldModel.fieldToolTip(f), "<b>my alias</b> (my_string)<br><font style='font-family:monospace; white-space: nowrap;'>string(20) NULL</font>")
379        f = QgsField('my_real', QVariant.Double, 'real', 8, 3)
380        self.assertEqual(QgsFieldModel.fieldToolTip(f), "<b>my_real</b><br><font style='font-family:monospace; white-space: nowrap;'>real(8, 3) NULL</font>")
381        f.setComment('Comment text')
382        self.assertEqual(QgsFieldModel.fieldToolTip(f), "<b>my_real</b><br><font style='font-family:monospace; white-space: nowrap;'>real(8, 3) NULL</font><br><em>Comment text</em>")
383
384    def testFieldTooltipExtended(self):
385        layer = QgsVectorLayer("Point?", "tooltip", "memory")
386        f = QgsField('my_real', QVariant.Double, 'real', 8, 3, 'Comment text')
387        layer.addExpressionField('1+1', f)
388        layer.updateFields()
389        self.assertEqual(QgsFieldModel.fieldToolTipExtended(QgsField('my_string', QVariant.String, 'string'), layer), '')
390        self.assertEqual(QgsFieldModel.fieldToolTipExtended(f, layer), "<b>my_real</b><br><font style='font-family:monospace; white-space: nowrap;'>real(8, 3) NULL</font><br><em>Comment text</em><br><font style='font-family:monospace;'>1+1</font>")
391        f.setAlias('my alias')
392        constraints = f.constraints()
393        constraints.setConstraint(QgsFieldConstraints.ConstraintUnique)
394        f.setConstraints(constraints)
395        self.assertEqual(QgsFieldModel.fieldToolTipExtended(f, layer), "<b>my alias</b> (my_real)<br><font style='font-family:monospace; white-space: nowrap;'>real(8, 3) NULL UNIQUE</font><br><em>Comment text</em><br><font style='font-family:monospace;'>1+1</font>")
396
397
398if __name__ == '__main__':
399    unittest.main()
400