1# -*- coding: utf-8 -*-
2"""QGIS Unit tests for the attribute form
3
4
5Run with ctest -V -R PyQgsAttributeForm
6
7.. note:: This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or
10(at your option) any later version.
11
12"""
13__author__ = 'Alessandro Pasotti'
14__date__ = '2019-06-06'
15__copyright__ = 'Copyright 2019, The QGIS Project'
16
17from qgis.testing import start_app, unittest
18from qgis.core import (
19    QgsFields,
20    QgsVectorLayer,
21    QgsFeature,
22    QgsEditorWidgetSetup,
23    QgsEditFormConfig,
24    QgsAttributeEditorElement,
25)
26from qgis.gui import (
27    QgsAttributeForm,
28    QgsGui,
29    QgsEditorWidgetWrapper,
30    QgsMapCanvas
31)
32
33QGISAPP = start_app()
34
35
36class TestQgsAttributeForm(unittest.TestCase):
37
38    @classmethod
39    def setUpClass(cls):
40        cls.mCanvas = QgsMapCanvas()
41        QgsGui.editorWidgetRegistry().initEditors(cls.mCanvas)
42
43    @classmethod
44    def createLayerWithOnePoint(cls, field_type):
45        layer = QgsVectorLayer("Point?field=fld:%s" % field_type,
46                               "vl", "memory")
47        pr = layer.dataProvider()
48        f = QgsFeature()
49        assert pr.addFeatures([f])
50        assert layer.featureCount() == 1
51        return layer
52
53    @classmethod
54    def createFormWithDuplicateWidget(cls, vl, field_type, widget_type):
55        """Creates a form with two identical widgets for the same field"""
56
57        config = vl.editFormConfig()
58        config.setLayout(QgsEditFormConfig.TabLayout)
59        element = config.tabs()[0]
60        element2 = element.clone(element)
61        config.addTab(element2)
62        vl.setEditFormConfig(config)
63        vl.setEditorWidgetSetup(0, QgsEditorWidgetSetup(widget_type, {}))
64        form = QgsAttributeForm(vl, next(vl.getFeatures()))
65        assert (form.editable())
66        return form
67
68    @classmethod
69    def get_widgets_for_field(cls, vl):
70        """Get compatible widget names"""
71
72        return [k for k, v in QgsGui.editorWidgetRegistry().factories().items() if v.supportsField(vl, 0)]
73
74    @classmethod
75    def checkForm(cls, field_type, value):
76        """Creates a vector layer and an associated form with two identical widgets for the same field and test it with NULL and after setting a value
77        """
78
79        vl = cls.createLayerWithOnePoint(field_type)
80        assert (vl.startEditing())
81        for widget_type in cls.get_widgets_for_field(vl):
82            form = cls.createFormWithDuplicateWidget(vl, field_type, widget_type)
83            vl.changeAttributeValue(1, 0, value)
84            form.setFeature(next(vl.getFeatures()))
85
86    def test_duplicated_widgets(self):
87        """
88        Note: this crashed two times for datetime (see GH #29937):
89
90        - first crash with initial NULL values, because widget's clear() triggered changed()
91        - second crash when setting a value, because setDateTime() triggered changed()
92
93        There are no assertions in this test because we are looking for a crash.
94        """
95
96        field_types = {
97            'integer': 123,
98            'double': 123.45,
99            'string': 'lorem ipsum',
100            'date': '2019-01-01',
101            'time': '12:12:12',
102            'datetime': '2019-01-01',
103            'int2': 123,
104            'int4': 123,
105            'int8': 123,
106            'numeric': 123.45,
107            'decimal': 123.45,
108            'real': 123.45,
109            'double precision': 123.45,
110            'text': 'lorem ipsum',
111            'bool': True,
112            # 'binary'
113        }
114
115        for field_type, value in field_types.items():
116            self.checkForm(field_type, value)
117
118
119if __name__ == '__main__':
120    unittest.main()
121