testexecutor-ui 0.6.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. testexecutor/VERSION +1 -0
  2. testexecutor/__init__.py +6 -0
  3. testexecutor/control/TestListenerApi.py +56 -0
  4. testexecutor/control/TestSuiteListener.py +249 -0
  5. testexecutor/control/__init__.py +0 -0
  6. testexecutor/model/GenericListModel.py +63 -0
  7. testexecutor/model/InstructionsModel.py +351 -0
  8. testexecutor/model/KeyValueModel.py +177 -0
  9. testexecutor/model/MultiTestWindowModel.py +184 -0
  10. testexecutor/model/ResultModel.py +314 -0
  11. testexecutor/model/TestSuiteControlModel.py +139 -0
  12. testexecutor/model/TestSuiteGroup.py +105 -0
  13. testexecutor/model/TestSuiteModel.py +301 -0
  14. testexecutor/model/__init__.py +0 -0
  15. testexecutor/model/selector/FilterGroupModel.py +90 -0
  16. testexecutor/model/selector/FilterModel.py +251 -0
  17. testexecutor/model/selector/MultiSelectDetails.py +106 -0
  18. testexecutor/model/selector/MultiSelectListModel.py +48 -0
  19. testexecutor/model/selector/__init__.py +0 -0
  20. testexecutor/ui/IdentificationWidget.qml +40 -0
  21. testexecutor/ui/InputWidget.qml +74 -0
  22. testexecutor/ui/InstructionWidget.qml +185 -0
  23. testexecutor/ui/KeyValueItem.qml +194 -0
  24. testexecutor/ui/KeyValueItemConfig.qml +41 -0
  25. testexecutor/ui/KeyValueList.qml +49 -0
  26. testexecutor/ui/MultiTestWidget.qml +48 -0
  27. testexecutor/ui/MultiTestWindow.qml +88 -0
  28. testexecutor/ui/ResultItem.qml +199 -0
  29. testexecutor/ui/ResultItemConfig.qml +46 -0
  30. testexecutor/ui/ResultList.qml +58 -0
  31. testexecutor/ui/TestSuiteConfig.qml +95 -0
  32. testexecutor/ui/TestSuiteController.qml +54 -0
  33. testexecutor/ui/TestSuiteWidget.qml +350 -0
  34. testexecutor/ui/selector/FilterItem.qml +75 -0
  35. testexecutor/ui/selector/FilterItemConfig.qml +24 -0
  36. testexecutor/ui/selector/FilterMultiSelectorItem.qml +169 -0
  37. testexecutor/ui/selector/FilterMultiSelectorItemConfig.qml +22 -0
  38. testexecutor/ui/selector/FilterMultiSelectorList.qml +50 -0
  39. testexecutor/ui/selector/HeaderConfig.qml +24 -0
  40. testexecutor/ui/selector/SectionConfig.qml +24 -0
  41. testexecutor/ui/selector/SectionDelegate.qml +85 -0
  42. testexecutor/ui/selector/SelectorHeader.qml +43 -0
  43. testexecutor/ui/selector/SuiteList.qml +150 -0
  44. testexecutor/ui/selector/SuiteSelector.qml +71 -0
  45. testexecutor/ui/selector/SuiteSelectorConfig.qml +38 -0
  46. testexecutor/ui/selector/TestConfig.qml +22 -0
  47. testexecutor/ui/selector/TestDelegate.qml +75 -0
  48. testexecutor/ui/te-64x64.ico +0 -0
  49. testexecutor_ui-0.6.1.dist-info/LICENSE +204 -0
  50. testexecutor_ui-0.6.1.dist-info/METADATA +27 -0
  51. testexecutor_ui-0.6.1.dist-info/NOTICE +3 -0
  52. testexecutor_ui-0.6.1.dist-info/RECORD +54 -0
  53. testexecutor_ui-0.6.1.dist-info/WHEEL +5 -0
  54. testexecutor_ui-0.6.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,105 @@
1
+ # Copyright 2020 Direkt, Australia
2
+ # Copyright 2021 Direkt Embedded Pty Ltd
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ """
17
+ A class which inherits QAbstractListModel to provide a list of TestSuites for a multi test runner.
18
+ Usage
19
+ # Write your MyTestSuiteListener class which inherits TestSuiteListener and provide your business logic in the callbacks
20
+ # Setup the list of suites
21
+ mySuiteGroup = TestSuiteGroup()
22
+ for i in range(4):
23
+ mySuiteGroup.addData(MyTestSuiteListener())
24
+
25
+ # Setup the qml UI
26
+ qml_file = os.path.join(ui_path, 'MultiTestWidget.qml')
27
+ url = QUrl.fromLocalFile(qml_file)
28
+ view.setSource(url)
29
+ view.setResizeMode(QQuickView.SizeRootObjectToView)
30
+ if view.status() != QQuickView.Error:
31
+ # Set the UIs Property model_list to the set of 4 TestSuiteListeners (which inherit TestSuiteModel)
32
+ view.rootContext().setContextProperty("model_list", mySuiteGroup)
33
+ view.showMaximized()
34
+ """
35
+
36
+ from PySide6.QtCore import QAbstractListModel
37
+ from PySide6.QtCore import Qt
38
+ from PySide6.QtCore import QModelIndex
39
+ from PySide6.QtCore import Signal, Property
40
+
41
+
42
+ class TestSuiteGroup(QAbstractListModel):
43
+
44
+ TestSuiteRole = Qt.UserRole
45
+ TestSuiteKey = b"testsuite"
46
+
47
+ _roles = {
48
+ TestSuiteRole: TestSuiteKey
49
+ }
50
+
51
+ def __init__(self, parent=None):
52
+ QAbstractListModel.__init__(self, parent)
53
+ self._datas = []
54
+
55
+ def addData(self, data):
56
+ index = QModelIndex()
57
+ self.beginInsertRows(index, self.rowCount(), self.rowCount())
58
+ self._datas.append(data)
59
+ self.endInsertRows()
60
+
61
+ def setData(self, index, value, role=None):
62
+ try:
63
+ data = self._datas[index.row()]
64
+ except IndexError:
65
+ return False
66
+ if role == self.TestSuiteRole:
67
+ self._datas[index.row()] = value
68
+ self.dataChanged.emit(index, index, {self.TestSuiteRole: self.TestSuiteKey})
69
+ return True
70
+
71
+ def rowCount(self, parent=QModelIndex()):
72
+ return len(self._datas)
73
+
74
+ def data(self, index, role=Qt.DisplayRole):
75
+ try:
76
+ data = self._datas[index.row()]
77
+ except IndexError:
78
+ return None
79
+
80
+ if role == self.TestSuiteRole:
81
+ return data
82
+
83
+ return None
84
+
85
+ def roleNames(self):
86
+ return self._roles
87
+
88
+ def _getactive_suites(self):
89
+ """ Getter for active_suites Property """
90
+ active = False
91
+ for item in self._datas:
92
+ active = item.active()
93
+ if active:
94
+ break
95
+ return active
96
+
97
+ active_suites_changed = Signal()
98
+ active_suites = Property(bool, _getactive_suites, None, notify=active_suites_changed)
99
+
100
+ def abortAll(self, hard=False):
101
+ for item in self._datas:
102
+ if hard:
103
+ item.close()
104
+ else:
105
+ item.suitestate = "stopping"
@@ -0,0 +1,301 @@
1
+ # Copyright 2020 Direkt, Australia
2
+ # Copyright 2021 Direkt Embedded Pty Ltd
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ # This Python file uses the following encoding: utf-8
17
+ import threading
18
+ import time
19
+ from PySide6.QtCore import QObject, Qt
20
+ from PySide6.QtCore import Signal, Property, Slot
21
+ from testexecutor.model.InstructionsModel import InstructionModel
22
+
23
+
24
+ class SuiteColours(QObject):
25
+ """
26
+ Dynamic colours that the user can set on the suite
27
+ If the colour is None/Undefined then the default colour as in
28
+ the configuration will be used.
29
+ """
30
+ def __init__(self):
31
+ QObject.__init__(self)
32
+ self.lock = threading.RLock()
33
+ self._running_colour = None
34
+ self._interaction_colour = None
35
+
36
+ def clear(self):
37
+ self.running = None
38
+ self.interaction = None
39
+
40
+ def _set_running_colour(self, _running_colour):
41
+ """ Setter for running_colour Property """
42
+ with self.lock:
43
+ self._running_colour = _running_colour
44
+ self.running_colour_changed.emit()
45
+
46
+ def _get_running_colour(self):
47
+ """ Getter for running_colour Property """
48
+ with self.lock:
49
+ col = self._running_colour
50
+ return col
51
+
52
+ running_colour_changed = Signal()
53
+ running = Property(str, _get_running_colour, _set_running_colour, notify=running_colour_changed)
54
+
55
+ def _set_interaction_colour(self, _interaction_colour):
56
+ """ Setter for interaction_colour Property """
57
+ with self.lock:
58
+ self._interaction_colour = _interaction_colour
59
+ self.interaction_colour_changed.emit()
60
+
61
+ def _get_interaction_colour(self):
62
+ """ Getter for interaction_colour Property """
63
+ with self.lock:
64
+ col = self._interaction_colour
65
+ return col
66
+
67
+ interaction_colour_changed = Signal()
68
+ interaction = Property(str, _get_interaction_colour, _set_interaction_colour,
69
+ notify=interaction_colour_changed)
70
+
71
+
72
+ class TestSuiteModel(QObject):
73
+ # these state strings must match those in TestSuiteWidget.qml
74
+ STATE_RUNNING = "running"
75
+ STATE_STARTING = "starting"
76
+ STATE_STOPPING = "stopping"
77
+ STATE_STOPPED = "stopped"
78
+ STATE_END = "end"
79
+ STATE_IDLE = "idle"
80
+ STATE_READY = "ready"
81
+ STATE_RESTART = "restart"
82
+ ACTION_CLEAR = "Clear"
83
+ ACTION_STOP = "Stop"
84
+
85
+ def __init__(self, idlist=None, resultlist=None, setid_callback=None, setstate_callback=None, title=None):
86
+ QObject.__init__(self)
87
+ self.lock = threading.RLock()
88
+ self._setidentifiers(idlist)
89
+ self._setresults(resultlist)
90
+ self._setinstructions(InstructionModel())
91
+ self._newId = None # See newid Property below
92
+ self.setid_callback = setid_callback
93
+ self._state = None # see suitestate Property below
94
+ self.setstate_callback = setstate_callback
95
+ self._title = title
96
+ self._controller = None
97
+ self._suite_colours = SuiteColours()
98
+ self.on_input.connect(self.instructions.control.onExternalInput, Qt.QueuedConnection)
99
+
100
+ def clear(self):
101
+ self.identifiers.clearData()
102
+ self.instructions.clear()
103
+ self.results.clear()
104
+ time.sleep(0.1)
105
+ self.identifiers.clearData() # Intentionally duplicate until qml corrected to clear combobox selected item
106
+ self.suitestate = self.STATE_IDLE
107
+ self.suite_colours.clear()
108
+
109
+ def setstate(self, st):
110
+ """ Setter for suitestate Property """
111
+ newstate = None
112
+ changed = False
113
+ if self.setstate_callback:
114
+ newstate = self.setstate_callback(st)
115
+ else:
116
+ newstate = st
117
+ with self.lock:
118
+ if newstate and newstate != self._state:
119
+ changed = True
120
+ if changed:
121
+ self._state = newstate
122
+ if changed:
123
+ if newstate == self.STATE_READY:
124
+ self.results.clear()
125
+ elif newstate == self.STATE_STOPPING:
126
+ self.instructions.clear()
127
+ self.result_changed.emit()
128
+ self.stateChanged.emit()
129
+
130
+ def _getstate(self):
131
+ """ Getter for suitestate Property """
132
+ state = None
133
+ with self.lock:
134
+ state = self._state
135
+ return state
136
+
137
+ stateChanged = Signal()
138
+ suitestate = Property(str, _getstate, setstate, notify=stateChanged)
139
+
140
+ def _setid(self, id):
141
+ """ Setter for newid Property """
142
+ with self.lock:
143
+ self._newId = id
144
+ # call id call back only when not active as we do not want to change ids after started
145
+ if not self.active():
146
+ if self.setid_callback:
147
+ self.setid_callback(id)
148
+ else:
149
+ self.on_input.emit(id)
150
+ self.id_changed.emit()
151
+
152
+ def _getid(self):
153
+ """ Getter for newid Property """
154
+ id = None
155
+ with self.lock:
156
+ id = self._newId
157
+ return id
158
+
159
+ id_changed = Signal()
160
+ on_input = Signal(str)
161
+ newid = Property(str, _getid, _setid, notify=id_changed)
162
+
163
+ def _setidentifiers(self, identifiers):
164
+ """ Setter for identifiers QAbstractList Property """
165
+ with self.lock:
166
+ self._idlist = identifiers
167
+ self.identifiers_changed.emit()
168
+
169
+ def _getidentifiers(self):
170
+ """ Getter for identifiers QAbstractList Property """
171
+ with self.lock:
172
+ list = self._idlist
173
+ return list
174
+
175
+ identifiers_changed = Signal()
176
+ identifiers = Property(QObject, _getidentifiers, _setidentifiers, notify=identifiers_changed)
177
+
178
+ def _setresults(self, results):
179
+ """ Setter for results QAbstractList Property """
180
+ with self.lock:
181
+ self._resultlist = results
182
+ self.results_changed.emit()
183
+
184
+ def _getresults(self):
185
+ """ Getter for results QAbstractList Property """
186
+ with self.lock:
187
+ list = self._resultlist
188
+ return list
189
+
190
+ results_changed = Signal()
191
+ results = Property(QObject, _getresults, _setresults, notify=results_changed)
192
+
193
+ def _getsuite_colours(self):
194
+ """ Getter for suite_colours Property """
195
+ with self.lock:
196
+ col = self._suite_colours
197
+ return col
198
+
199
+ suite_colours_changed = Signal()
200
+ suite_colours = Property(QObject, _getsuite_colours, None, notify=suite_colours_changed)
201
+
202
+ def _setinstructions(self, instruction_info):
203
+ """ Setter for instructions Property """
204
+ with self.lock:
205
+ self._instructions = instruction_info
206
+ self.instructions_changed.emit()
207
+
208
+ def _getinstructions(self):
209
+ """ Getter for instructions Property """
210
+ with self.lock:
211
+ instr = self._instructions
212
+ return instr
213
+
214
+ instructions_changed = Signal()
215
+ instructions = Property(QObject, _getinstructions, _setinstructions, notify=instructions_changed)
216
+
217
+ def _getresult(self):
218
+ """
219
+ Getter for result Property
220
+ Checks all test results and returns state
221
+ :return: Result.StatePass if every test passed
222
+ Result.StateFail if a single test failed within suite
223
+ Result.StateRunning if suite is still in progress
224
+ None if error occurred or no tests started
225
+ """
226
+ suite_result = None
227
+ with self.lock:
228
+ if self.results:
229
+ suite_result = self.results.overallResult()
230
+ return suite_result
231
+
232
+ result_changed = Signal()
233
+ result = Property(str, _getresult, None, notify=result_changed)
234
+
235
+ def _settitle(self, title):
236
+ """ Setter for title Property """
237
+ changed = False
238
+ with self.lock:
239
+ if self._title != title:
240
+ self._title = title
241
+ changed = True
242
+ if changed:
243
+ self.title_changed.emit()
244
+
245
+ def _gettitle(self):
246
+ """ Getter for title Property """
247
+ title = ""
248
+ with self.lock:
249
+ title = self._title
250
+ return title
251
+
252
+ title_changed = Signal()
253
+ title = Property(str, _gettitle, _settitle, notify=title_changed)
254
+
255
+ def _setcontroller(self, controller):
256
+ """ Setter for controller Property """
257
+ changed = False
258
+ with self.lock:
259
+ if self._controller != controller:
260
+ self._controller = controller
261
+ changed = True
262
+ if changed:
263
+ self.controller_changed.emit()
264
+
265
+ def _getcontroller(self):
266
+ """
267
+ Getter for controller Property
268
+ A suite can have an attached controller which can be used to define a test list and the like.
269
+ """
270
+ controller = None
271
+ with self.lock:
272
+ controller = self._controller
273
+ return controller
274
+
275
+ controller_changed = Signal()
276
+ controller = Property(QObject, _getcontroller, _setcontroller, notify=controller_changed)
277
+
278
+ @Slot()
279
+ def active(self):
280
+ activeStates = [self.STATE_STOPPING, self.STATE_STARTING, self.STATE_RUNNING]
281
+ return self.suitestate in activeStates
282
+
283
+ @Slot(str)
284
+ def action(self, action):
285
+ """
286
+ Two default actions are supported, Clear and Stop. The action string comes from the text on the control/action
287
+ button of the TestSuiteWidget.qml states, so ensure the configuration has strings which match exactly.
288
+ :param action:
289
+ :return:
290
+ """
291
+ if action == self.ACTION_CLEAR:
292
+ self.clear()
293
+ elif action == self.ACTION_STOP:
294
+ self.suitestate = self.STATE_STOPPING
295
+
296
+ def close(self):
297
+ """
298
+ API for Hard closing the suite, ensuring any processes or threads created are killed
299
+ :return:
300
+ """
301
+ pass
File without changes
@@ -0,0 +1,90 @@
1
+ # Copyright 2020 Sipke Vriend, Australia
2
+ # Copyright 2021 Direkt Embedded Pty Ltd
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ """
17
+ Model for a list of filters as FilterModel.
18
+ """
19
+
20
+ from PySide6.QtCore import QAbstractListModel
21
+ from PySide6.QtCore import Qt
22
+ from PySide6.QtCore import QModelIndex
23
+ from .FilterModel import FilterModel
24
+
25
+
26
+ class FilterGroupModel(QAbstractListModel):
27
+
28
+ FilterRole = Qt.UserRole
29
+ FilterKey = b"filter"
30
+
31
+ _roles = {
32
+ FilterRole: FilterKey
33
+ }
34
+
35
+ def __init__(self, filters=None, parent=None, onSelectedChanged=None):
36
+ QAbstractListModel.__init__(self, parent)
37
+ self._datas = []
38
+ if type(filters) == dict:
39
+ for item in filters.items():
40
+ filter = FilterModel(item[0], item[1])
41
+ if onSelectedChanged:
42
+ filter.updated_selected.connect(onSelectedChanged, Qt.QueuedConnection)
43
+ self.addData(filter)
44
+
45
+ def addData(self, data):
46
+ index = QModelIndex()
47
+ self.beginInsertRows(index, self.rowCount(), self.rowCount())
48
+ self._datas.append(data)
49
+ self.endInsertRows()
50
+
51
+ def setData(self, index, value, role):
52
+ try:
53
+ data = self._datas[index.row()]
54
+ except IndexError:
55
+ return False
56
+ if role == self.FilterRole:
57
+ self._datas[index.row()] = value
58
+ self.dataChanged.emit(index, index, {self.FilterRole: self.FilterKey})
59
+ return True
60
+
61
+ def rowCount(self, parent=QModelIndex()):
62
+ return len(self._datas)
63
+
64
+ def data(self, index, role=Qt.DisplayRole):
65
+ try:
66
+ data = self._datas[index.row()]
67
+ except IndexError:
68
+ return None
69
+
70
+ if role == self.FilterRole:
71
+ return data
72
+
73
+ return None
74
+
75
+ def roleNames(self):
76
+ return self._roles
77
+
78
+ def updateData(self, name, content_list):
79
+ """
80
+ Update an existing filter with new content. If filter with name does not exist, nothing will happen
81
+ :param name:
82
+ :param content_list:
83
+ :return:
84
+ """
85
+ rowCount = self.rowCount()
86
+ for i in range(rowCount):
87
+ existingItem = self.index(i, 0).data(self.FilterRole)
88
+ if existingItem.name == name:
89
+ existingItem.proposed.setStringList(content_list)
90
+ self.dataChanged.emit(self.index(i, 0), self.index(i, 0), self._roles)