Merge lp:~kissiel/checkbox/sa-in-cbt into lp:checkbox
- sa-in-cbt
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Zygmunt Krynicki |
Approved revision: | 3997 |
Merged at revision: | 3970 |
Proposed branch: | lp:~kissiel/checkbox/sa-in-cbt |
Merge into: | lp:checkbox |
Diff against target: |
958 lines (+248/-396) 5 files modified
checkbox-touch/checkbox-touch.qml (+39/-32) checkbox-touch/components/CheckboxTouchApplication.qml (+4/-4) checkbox-touch/py/checkbox_touch.py (+135/-327) checkbox-touch/tests/autopilot/checkbox_touch/test_checkbox_touch.py (+33/-30) plainbox/plainbox/impl/session/assistant.py (+37/-3) |
To merge this branch: | bzr merge lp:~kissiel/checkbox/sa-in-cbt |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Zygmunt Krynicki (community) | Approve | ||
Review via email: mp+269634@code.launchpad.net |
Commit message
Description of the change
This MR brings a small revolution to Checkbox {Touch,Converged}.
It replaces all the internal logic related to running plainbox core with calls to SessionAssistant.
It also smuggles some fixes to SessionAssistant along the way.
Then, it makes Autopilot happy with the New Order,
to finally clean up pep8 issues, embetter existing and add missing docstrings.
a6b1280 checkbox-touch: run run_test_activity for ALL plugins
e30f8f1 checkbox-touch: [move2SA] use SA in __init__
7de6bda checkbox-touch: [move2SA] use SA in start_session
861c36a checkbox-touch: [move2SA] use SA in is_session_
bedf971 checkbox-touch: [move2SA] use SA in get_testplans
7e1e3a2 checkbox-touch: [move2SA] use SA in remember_testplan
037f79a checkbox-touch: [move2SA] use SA in get_categories
a853d86 checkbox-touch: [move2SA] use SA in remember_categories
9e08a76 checkbox-touch: [move2SA] use SA in get_available tests
2fe8eeb checkbox-touch: [move2SA] use SA in get_rerun_
2fcc534 checkbox-touch: [move2SA] use SA in remember_tests
3a6c5cb checkbox-touch: [move2SA] use SA in get_next_test
d8fc496 checkbox-touch: [move2SA] use SA in register_
2fd2af9 checkbox-touch: [move2SA] use SA in run_test_activity
952d973 checkbox-touch: [move2SA] use SA in get_results
225b491 checkbox-touch: [move2SA] use SA in export_results
f66a3b6 checkbox-touch: add timestamp to app_blob
dfce594 checkbox-touch: [move2SA] remove _checkpoint method
d3f7f14 checkbox-touch: add a method for loading embedded providers
85e9416 checkbox-
e507f79 plainbox:
a0e3297 plainbox:
37153b4 checkbox-touch: move embedded providers stuff to CBTApp.__init__
41c813e plainbox:
73adaa6 plainbox:
db9146c checkbox-
d24664e checkbox-touch: remove unnecessary methods
e45942c checkbox-touch: remove import of no longer used modules
dc61ce2 checkbox-touch: fix PEP8 issues
70148a8 checkbox-touch: upgrade docstrings
Preview Diff
1 | === modified file 'checkbox-touch/checkbox-touch.qml' | |||
2 | --- checkbox-touch/checkbox-touch.qml 2015-08-18 12:59:01 +0000 | |||
3 | +++ checkbox-touch/checkbox-touch.qml 2015-08-31 12:00:21 +0000 | |||
4 | @@ -155,7 +155,7 @@ | |||
5 | 155 | "checkbox_touch" : applicationVersion, | 155 | "checkbox_touch" : applicationVersion, |
6 | 156 | "plainbox" : plainboxVersion | 156 | "plainbox" : plainboxVersion |
7 | 157 | }; | 157 | }; |
9 | 158 | resumeOrStartSession(appSettings["providersDir"]); | 158 | resumeOrStartSession(); |
10 | 159 | } | 159 | } |
11 | 160 | onSessionReady: { | 160 | onSessionReady: { |
12 | 161 | welcomePage.enableButton() | 161 | welcomePage.enableButton() |
13 | @@ -163,7 +163,8 @@ | |||
14 | 163 | Component.onCompleted: { | 163 | Component.onCompleted: { |
15 | 164 | // register to py.initiated signal | 164 | // register to py.initiated signal |
16 | 165 | py.onInitiated.connect(function() { | 165 | py.onInitiated.connect(function() { |
18 | 166 | construct("checkbox_touch.create_app_object", []); | 166 | construct("checkbox_touch.create_app_object", [ |
19 | 167 | appSettings["providersDir"]]); | ||
20 | 167 | }); | 168 | }); |
21 | 168 | } | 169 | } |
22 | 169 | } | 170 | } |
23 | @@ -267,14 +268,14 @@ | |||
24 | 267 | 268 | ||
25 | 268 | ResumeSessionPage { | 269 | ResumeSessionPage { |
26 | 269 | id: resumeSessionPage | 270 | id: resumeSessionPage |
29 | 270 | onRerunLast: app.resumeSession(true, appSettings["providersDir"], processNextTest) | 271 | onRerunLast: app.resumeSession(true, processNextTest) |
30 | 271 | onContinueSession: app.resumeSession(false, appSettings["providersDir"], processNextTest) | 272 | onContinueSession: app.resumeSession(false, processNextTest) |
31 | 272 | resumeText: i18n.tr("Checkbox did not finish completely.\nDo you want \ | 273 | resumeText: i18n.tr("Checkbox did not finish completely.\nDo you want \ |
32 | 273 | to rerun last test, continue to the next test, or restart from the beginning?") | 274 | to rerun last test, continue to the next test, or restart from the beginning?") |
33 | 274 | onRestartSession: { | 275 | onRestartSession: { |
34 | 275 | pageStack.clear(); | 276 | pageStack.clear(); |
35 | 276 | pageStack.push(welcomePage); | 277 | pageStack.push(welcomePage); |
37 | 277 | app.startSession(appSettings["providersDir"]); | 278 | app.startSession(); |
38 | 278 | } | 279 | } |
39 | 279 | } | 280 | } |
40 | 280 | 281 | ||
41 | @@ -407,10 +408,10 @@ | |||
42 | 407 | } else { | 408 | } else { |
43 | 408 | if (result.errors_encountered) { | 409 | if (result.errors_encountered) { |
44 | 409 | ErrorLogic.showError(mainView, i18n.tr("Could not resume session."), | 410 | ErrorLogic.showError(mainView, i18n.tr("Could not resume session."), |
46 | 410 | app.startSession(appSettings["providersDir"]), | 411 | app.startSession(), |
47 | 411 | i18n.tr("Start new session")); | 412 | i18n.tr("Start new session")); |
48 | 412 | } else { | 413 | } else { |
50 | 413 | app.startSession(appSettings["providersDir"]); | 414 | app.startSession(); |
51 | 414 | } | 415 | } |
52 | 415 | } | 416 | } |
53 | 416 | }); | 417 | }); |
54 | @@ -502,7 +503,7 @@ | |||
55 | 502 | resultsPage.endTesting.connect(function() { | 503 | resultsPage.endTesting.connect(function() { |
56 | 503 | pageStack.clear(); | 504 | pageStack.clear(); |
57 | 504 | app.clearSession(function() { | 505 | app.clearSession(function() { |
59 | 505 | app.startSession(appSettings["providersDir"]); | 506 | app.startSession(); |
60 | 506 | pageStack.push(welcomePage); | 507 | pageStack.push(welcomePage); |
61 | 507 | }); | 508 | }); |
62 | 508 | }); | 509 | }); |
63 | @@ -535,13 +536,15 @@ | |||
64 | 535 | } | 536 | } |
65 | 536 | 537 | ||
66 | 537 | function performManualTest(test) { | 538 | function performManualTest(test) { |
74 | 538 | var manualIntroPage = Qt.createComponent(Qt.resolvedUrl("components/ManualIntroPage.qml")).createObject(); | 539 | runTestActivity(test, function(test) { |
75 | 539 | manualIntroPage.test = test | 540 | var manualIntroPage = Qt.createComponent(Qt.resolvedUrl("components/ManualIntroPage.qml")).createObject(); |
76 | 540 | manualIntroPage.testDone.connect(completeTest); | 541 | manualIntroPage.test = test |
77 | 541 | manualIntroPage.continueClicked.connect(function() { showVerificationScreen(test); }); | 542 | manualIntroPage.testDone.connect(completeTest); |
78 | 542 | manualIntroPage.__customHeaderContents = progressHeader; | 543 | manualIntroPage.continueClicked.connect(function() { showVerificationScreen(test); }); |
79 | 543 | progressHeader.update(test); | 544 | manualIntroPage.__customHeaderContents = progressHeader; |
80 | 544 | pageStack.push(manualIntroPage); | 545 | progressHeader.update(test); |
81 | 546 | pageStack.push(manualIntroPage); | ||
82 | 547 | }); | ||
83 | 545 | } | 548 | } |
84 | 546 | 549 | ||
85 | 547 | function performUserInteractVerifyTest(test) { | 550 | function performUserInteractVerifyTest(test) { |
86 | @@ -592,25 +595,29 @@ | |||
87 | 592 | } | 595 | } |
88 | 593 | 596 | ||
89 | 594 | function performQmlTest(test) { | 597 | function performQmlTest(test) { |
98 | 595 | var comp = Qt.createComponent(Qt.resolvedUrl("components/QmlNativePage.qml")) | 598 | runTestActivity(test, function(test) { |
99 | 596 | console.log(comp.errorString()); | 599 | var comp = Qt.createComponent(Qt.resolvedUrl("components/QmlNativePage.qml")) |
100 | 597 | var qmlNativePage = comp.createObject(); | 600 | console.log(comp.errorString()); |
101 | 598 | qmlNativePage.test = test | 601 | var qmlNativePage = comp.createObject(); |
102 | 599 | qmlNativePage.testDone.connect(completeTest); | 602 | qmlNativePage.test = test |
103 | 600 | qmlNativePage.__customHeaderContents = progressHeader; | 603 | qmlNativePage.testDone.connect(completeTest); |
104 | 601 | progressHeader.update(test); | 604 | qmlNativePage.__customHeaderContents = progressHeader; |
105 | 602 | pageStack.push(qmlNativePage); | 605 | progressHeader.update(test); |
106 | 606 | pageStack.push(qmlNativePage); | ||
107 | 607 | }); | ||
108 | 603 | } | 608 | } |
109 | 604 | function performConfinedQmlTest(test) { | 609 | function performConfinedQmlTest(test) { |
119 | 605 | var comp = Qt.createComponent(Qt.resolvedUrl("components/QmlConfinedPage.qml")) | 610 | runTestActivity(test, function(test) { |
120 | 606 | console.log(comp.errorString()); | 611 | var comp = Qt.createComponent(Qt.resolvedUrl("components/QmlConfinedPage.qml")) |
121 | 607 | var qmlNativePage = comp.createObject(); | 612 | console.log(comp.errorString()); |
122 | 608 | qmlNativePage.test = test | 613 | var qmlNativePage = comp.createObject(); |
123 | 609 | qmlNativePage.applicationVersion = app.applicationVersion; | 614 | qmlNativePage.test = test |
124 | 610 | qmlNativePage.testDone.connect(completeTest); | 615 | qmlNativePage.applicationVersion = app.applicationVersion; |
125 | 611 | qmlNativePage.__customHeaderContents = progressHeader; | 616 | qmlNativePage.testDone.connect(completeTest); |
126 | 612 | progressHeader.update(test); | 617 | qmlNativePage.__customHeaderContents = progressHeader; |
127 | 613 | pageStack.push(qmlNativePage); | 618 | progressHeader.update(test); |
128 | 619 | pageStack.push(qmlNativePage); | ||
129 | 620 | }); | ||
130 | 614 | } | 621 | } |
131 | 615 | 622 | ||
132 | 616 | function showVerificationScreen(test) { | 623 | function showVerificationScreen(test) { |
133 | 617 | 624 | ||
134 | === modified file 'checkbox-touch/components/CheckboxTouchApplication.qml' | |||
135 | --- checkbox-touch/components/CheckboxTouchApplication.qml 2015-07-29 20:50:05 +0000 | |||
136 | +++ checkbox-touch/components/CheckboxTouchApplication.qml 2015-08-31 12:00:21 +0000 | |||
137 | @@ -45,8 +45,8 @@ | |||
138 | 45 | // Starts session in plainbox and runs all necessary setup actions. | 45 | // Starts session in plainbox and runs all necessary setup actions. |
139 | 46 | // Calling this function will signal sessionReady() once it's finished | 46 | // Calling this function will signal sessionReady() once it's finished |
140 | 47 | // doing setup. | 47 | // doing setup. |
143 | 48 | function startSession(providersDir) { | 48 | function startSession() { |
144 | 49 | request("start_session", [providersDir], function(result) { | 49 | request("start_session", [], function(result) { |
145 | 50 | sessionDir = result['session_dir']; | 50 | sessionDir = result['session_dir']; |
146 | 51 | sessionReady(); | 51 | sessionReady(); |
147 | 52 | }, function(error) { | 52 | }, function(error) { |
148 | @@ -57,8 +57,8 @@ | |||
149 | 57 | i18n.tr("Quit")); | 57 | i18n.tr("Quit")); |
150 | 58 | }); | 58 | }); |
151 | 59 | } | 59 | } |
154 | 60 | function resumeSession(rerunLastTest, providersDir, continuation) { | 60 | function resumeSession(rerunLastTest, continuation) { |
155 | 61 | request("resume_session", [rerunLastTest, providersDir], function(result) { | 61 | request("resume_session", [rerunLastTest], function(result) { |
156 | 62 | if (!result["session_id"]) { | 62 | if (!result["session_id"]) { |
157 | 63 | pageStack.pop(); | 63 | pageStack.pop(); |
158 | 64 | ErrorLogic.showError(mainView, | 64 | ErrorLogic.showError(mainView, |
159 | 65 | 65 | ||
160 | === modified file 'checkbox-touch/py/checkbox_touch.py' | |||
161 | --- checkbox-touch/py/checkbox_touch.py 2015-07-31 13:07:18 +0000 | |||
162 | +++ checkbox-touch/py/checkbox_touch.py 2015-08-31 12:00:21 +0000 | |||
163 | @@ -32,7 +32,6 @@ | |||
164 | 32 | import abc | 32 | import abc |
165 | 33 | import collections | 33 | import collections |
166 | 34 | import datetime | 34 | import datetime |
167 | 35 | import itertools | ||
168 | 36 | import json | 35 | import json |
169 | 37 | import logging | 36 | import logging |
170 | 38 | import os | 37 | import os |
171 | @@ -43,23 +42,10 @@ | |||
172 | 43 | 42 | ||
173 | 44 | from plainbox.abc import IJobResult | 43 | from plainbox.abc import IJobResult |
174 | 45 | from plainbox.impl import pod | 44 | from plainbox.impl import pod |
175 | 46 | from plainbox.impl.applogic import PlainBoxConfig | ||
176 | 47 | from plainbox.impl.clitools import ToolBase | 45 | from plainbox.impl.clitools import ToolBase |
177 | 48 | from plainbox.impl.commands.inv_run import SilentUI | 46 | from plainbox.impl.commands.inv_run import SilentUI |
178 | 49 | from plainbox.impl.result import JobResultBuilder | 47 | from plainbox.impl.result import JobResultBuilder |
192 | 50 | from plainbox.impl.runner import JobRunner | 48 | from plainbox.impl.session.assistant import SessionAssistant |
180 | 51 | from plainbox.impl.secure.origin import Origin | ||
181 | 52 | from plainbox.impl.secure.qualifiers import FieldQualifier | ||
182 | 53 | from plainbox.impl.secure.qualifiers import OperatorMatcher | ||
183 | 54 | from plainbox.impl.secure.qualifiers import select_jobs | ||
184 | 55 | from plainbox.impl.session import SessionManager | ||
185 | 56 | from plainbox.impl.session import SessionMetaData | ||
186 | 57 | from plainbox.impl.session import SessionPeekHelper | ||
187 | 58 | from plainbox.impl.session import SessionResumeError | ||
188 | 59 | from plainbox.impl.session.storage import SessionStorageRepository | ||
189 | 60 | from plainbox.impl.unit.job import JobDefinition | ||
190 | 61 | from plainbox.impl.unit.validators import compute_value_map | ||
191 | 62 | from plainbox.public import get_providers | ||
193 | 63 | import plainbox | 49 | import plainbox |
194 | 64 | 50 | ||
195 | 65 | from embedded_providers import EmbeddedProvider1PlugInCollection | 51 | from embedded_providers import EmbeddedProvider1PlugInCollection |
196 | @@ -149,25 +135,22 @@ | |||
197 | 149 | 135 | ||
198 | 150 | __version__ = (1, 2, 1, 'final', 0) | 136 | __version__ = (1, 2, 1, 'final', 0) |
199 | 151 | 137 | ||
201 | 152 | def __init__(self): | 138 | def __init__(self, providers_dir): |
202 | 153 | if plainbox.__version__ < (0, 22): | 139 | if plainbox.__version__ < (0, 22): |
203 | 154 | raise SystemExit("plainbox 0.22 required, you have {}".format( | 140 | raise SystemExit("plainbox 0.22 required, you have {}".format( |
204 | 155 | ToolBase.format_version_tuple(plainbox.__version__))) | 141 | ToolBase.format_version_tuple(plainbox.__version__))) |
218 | 156 | # adjust_logging(logging.INFO, ['checkbox.touch'], True) | 142 | self.assistant = SessionAssistant('checkbox-converged') |
219 | 157 | self.manager = None | 143 | self.ui = CheckboxTouchUI() |
220 | 158 | self.context = None | 144 | self.index = 0 |
208 | 159 | self.runner = None | ||
209 | 160 | self.index = 0 # NOTE: next test index | ||
210 | 161 | # NOTE: This may also have "" representing None | ||
211 | 162 | self.desired_category_ids = frozenset() | ||
212 | 163 | self.desired_test_ids = frozenset() | ||
213 | 164 | self.test_plan_id = "" | ||
214 | 165 | self.resume_candidate_storage = None | ||
215 | 166 | self.session_storage_repo = None | ||
216 | 167 | self.timestamp = datetime.datetime.utcnow().isoformat() | ||
217 | 168 | self.config = PlainBoxConfig() | ||
221 | 169 | self._password = None | 145 | self._password = None |
223 | 170 | self.ui = CheckboxTouchUI() | 146 | self._timestamp = None |
224 | 147 | self._latest_session = None | ||
225 | 148 | self.resume_candidate_storage = None | ||
226 | 149 | self.assistant.use_alternate_repository( | ||
227 | 150 | self._get_app_cache_directory()) | ||
228 | 151 | self.assistant.select_providers( | ||
229 | 152 | '*', | ||
230 | 153 | additional_providers=self._get_embedded_providers(providers_dir)) | ||
231 | 171 | 154 | ||
232 | 172 | def __repr__(self): | 155 | def __repr__(self): |
233 | 173 | return "app" | 156 | return "app" |
234 | @@ -182,186 +165,101 @@ | |||
235 | 182 | } | 165 | } |
236 | 183 | 166 | ||
237 | 184 | @view | 167 | @view |
277 | 185 | def start_session(self, providers_dir): | 168 | def start_session(self): |
278 | 186 | if self.manager is not None: | 169 | """Start a new session.""" |
279 | 187 | _logger.warning("start_session() should not be called twice!") | 170 | self.assistant.start_new_session('Checkbox Converged session') |
280 | 188 | else: | 171 | self._timestamp = datetime.datetime.utcnow().isoformat() |
242 | 189 | self._init_session_storage_repo() | ||
243 | 190 | self.manager = SessionManager.create(self.session_storage_repo) | ||
244 | 191 | self.manager.add_local_device_context() | ||
245 | 192 | self.context = self.manager.default_device_context | ||
246 | 193 | # Add some all providers into the context | ||
247 | 194 | for provider in self._get_default_providers(providers_dir): | ||
248 | 195 | self.context.add_provider(provider) | ||
249 | 196 | # Fill in the meta-data | ||
250 | 197 | self.context.state.metadata.app_id = 'checkbox-touch' | ||
251 | 198 | self.context.state.metadata.title = 'Checkbox Touch Session' | ||
252 | 199 | self.context.state.metadata.flags.add('bootstrapping') | ||
253 | 200 | # Checkpoint the session so that we have something to see | ||
254 | 201 | self._checkpoint() | ||
255 | 202 | |||
256 | 203 | # Prepare custom execution controller list | ||
257 | 204 | from plainbox.impl.ctrl import UserJobExecutionController | ||
258 | 205 | from sudo_with_pass_ctrl import \ | ||
259 | 206 | RootViaSudoWithPassExecutionController | ||
260 | 207 | controllers = [ | ||
261 | 208 | RootViaSudoWithPassExecutionController( | ||
262 | 209 | self.context.provider_list, self._password_provider), | ||
263 | 210 | UserJobExecutionController(self.context.provider_list), | ||
264 | 211 | ] | ||
265 | 212 | self.runner = JobRunner( | ||
266 | 213 | self.manager.storage.location, | ||
267 | 214 | self.context.provider_list, | ||
268 | 215 | # TODO: tie this with well-known-dirs helper | ||
269 | 216 | os.path.join(self.manager.storage.location, 'io-logs'), | ||
270 | 217 | execution_ctrl_list=controllers) | ||
271 | 218 | app_cache_dir = self._get_app_cache_directory() | ||
272 | 219 | if not os.path.exists(app_cache_dir): | ||
273 | 220 | os.makedirs(app_cache_dir) | ||
274 | 221 | with open(os.path.join(app_cache_dir, 'session_id'), | ||
275 | 222 | 'w') as f: | ||
276 | 223 | f.write(self.manager.storage.id) | ||
281 | 224 | return { | 172 | return { |
284 | 225 | 'session_id': self.manager.storage.id, | 173 | 'session_id': self.assistant.get_session_id(), |
285 | 226 | 'session_dir': self.manager.storage.location | 174 | 'session_dir': self.assistant.get_session_dir() |
286 | 227 | } | 175 | } |
287 | 228 | 176 | ||
288 | 229 | @view | 177 | @view |
304 | 230 | def resume_session(self, rerun_last_test, providers_dir): | 178 | def resume_session(self, rerun_last_test): |
305 | 231 | all_units = list(itertools.chain( | 179 | """ |
306 | 232 | *[p.unit_list for p in self._get_default_providers( | 180 | Resume latest sesssion. |
307 | 233 | providers_dir)])) | 181 | |
308 | 234 | try: | 182 | :param rerun_last_test: |
309 | 235 | self.manager = SessionManager.load_session( | 183 | A bool stating whether runtime should repeat the test, that the app |
310 | 236 | all_units, self.resume_candidate_storage) | 184 | was executing when it was interrupted. |
311 | 237 | except IOError as exc: | 185 | """ |
312 | 238 | _logger.info("Exception raised when trying to resume" | 186 | metadata = self.assistant.resume_session(self._latest_session) |
298 | 239 | "session: %s", str(exc)) | ||
299 | 240 | return { | ||
300 | 241 | 'session_id': None | ||
301 | 242 | } | ||
302 | 243 | self.context = self.manager.default_device_context | ||
303 | 244 | metadata = self.context.state.metadata | ||
313 | 245 | app_blob = json.loads(metadata.app_blob.decode("UTF-8")) | 187 | app_blob = json.loads(metadata.app_blob.decode("UTF-8")) |
314 | 246 | self.runner = JobRunner( | ||
315 | 247 | self.manager.storage.location, | ||
316 | 248 | self.context.provider_list, | ||
317 | 249 | os.path.join(self.manager.storage.location, 'io-logs')) | ||
318 | 250 | self.index = app_blob['index_in_run_list'] | 188 | self.index = app_blob['index_in_run_list'] |
322 | 251 | self._init_test_plan_id(app_blob['test_plan_id']) | 189 | self.test_plan_id = app_blob['test_plan_id'] |
323 | 252 | _logger.error(self.context.state.run_list) | 190 | self.assistant.select_test_plan(self.test_plan_id) |
324 | 253 | _logger.error(self.index) | 191 | self.assistant.bootstrap() |
325 | 192 | |||
326 | 254 | if not rerun_last_test: | 193 | if not rerun_last_test: |
327 | 255 | # Skip current test | 194 | # Skip current test |
328 | 256 | test = self.get_next_test()['result'] | 195 | test = self.get_next_test()['result'] |
329 | 257 | test['outcome'] = 'skip' | 196 | test['outcome'] = 'skip' |
330 | 258 | self.register_test_result(test) | 197 | self.register_test_result(test) |
331 | 259 | return { | 198 | return { |
333 | 260 | 'session_id': self.manager.storage.id | 199 | 'session_id': self._latest_session |
334 | 261 | } | 200 | } |
335 | 262 | 201 | ||
336 | 263 | @view | 202 | @view |
337 | 264 | def clear_session(self): | 203 | def clear_session(self): |
341 | 265 | self.manager = None | 204 | """Reset app-custom state info about the session.""" |
339 | 266 | self.context = None | ||
340 | 267 | self.runner = None | ||
342 | 268 | self.index = 0 | 205 | self.index = 0 |
349 | 269 | self.timestamp = datetime.datetime.utcnow().isoformat() | 206 | self._timestamp = datetime.datetime.utcnow().isoformat() |
344 | 270 | self.desired_category_ids = frozenset() | ||
345 | 271 | self.desired_test_ids = frozenset() | ||
346 | 272 | self.resume_candidate_storage = None | ||
347 | 273 | self.session_storage_repo = None | ||
348 | 274 | os.unlink(os.path.join(self._get_app_cache_directory(), 'session_id')) | ||
350 | 275 | 207 | ||
351 | 276 | @view | 208 | @view |
352 | 277 | def is_session_resumable(self): | 209 | def is_session_resumable(self): |
387 | 278 | """ | 210 | """Check whether there is a session that can be resumed.""" |
388 | 279 | Checks whether given session is resumable | 211 | for session_id, session_md in self.assistant.get_resumable_sessions(): |
389 | 280 | """ | 212 | # we're interested in the latest session only, this is why we |
390 | 281 | resumable = False | 213 | # return early |
391 | 282 | try: | 214 | self._latest_session = session_id |
392 | 283 | with open(os.path.join(self._get_app_cache_directory(), | 215 | return { |
393 | 284 | 'session_id')) as f: | 216 | 'resumable': True, |
394 | 285 | session_id = f.readline().rstrip('\n') | 217 | 'error_encountered': False, |
395 | 286 | except (OSError, IOError): | 218 | } |
396 | 287 | session_id = None | 219 | else: |
397 | 288 | self._init_session_storage_repo() | 220 | return { |
398 | 289 | for storage in self.session_storage_repo.get_storage_list(): | 221 | 'resumable': False, |
399 | 290 | data = storage.load_checkpoint() | 222 | 'error_encountered': False, |
400 | 291 | if len(data) == 0: | 223 | } |
367 | 292 | continue | ||
368 | 293 | try: | ||
369 | 294 | metadata = SessionPeekHelper().peek(data) | ||
370 | 295 | if (metadata.app_id == 'checkbox-touch' | ||
371 | 296 | and storage.id == session_id | ||
372 | 297 | and SessionMetaData.FLAG_INCOMPLETE in | ||
373 | 298 | metadata.flags): | ||
374 | 299 | self.resume_candidate_storage = storage | ||
375 | 300 | resumable = True | ||
376 | 301 | except SessionResumeError as exc: | ||
377 | 302 | _logger.info("Exception raised when trying to resume" | ||
378 | 303 | "session: %s", str(exc)) | ||
379 | 304 | return { | ||
380 | 305 | 'resumable': False, | ||
381 | 306 | 'errors_encountered': True | ||
382 | 307 | } | ||
383 | 308 | return { | ||
384 | 309 | 'resumable': resumable, | ||
385 | 310 | 'errors_encountered': False | ||
386 | 311 | } | ||
401 | 312 | 224 | ||
402 | 313 | @view | 225 | @view |
403 | 314 | def get_testplans(self): | 226 | def get_testplans(self): |
405 | 315 | all_units = self.manager.default_device_context.unit_list | 227 | """Get the list of available test plans.""" |
406 | 228 | test_plan_units = [self.assistant.get_test_plan(tp_id) for tp_id in | ||
407 | 229 | self.assistant.get_test_plans()] | ||
408 | 316 | return { | 230 | return { |
409 | 317 | 'testplan_info_list': [{ | 231 | 'testplan_info_list': [{ |
412 | 318 | "mod_id": unit.id, | 232 | "mod_id": tp.id, |
413 | 319 | "mod_name": unit.name, | 233 | "mod_name": tp.name, |
414 | 320 | "mod_selected": False, | 234 | "mod_selected": False, |
416 | 321 | } for unit in all_units if unit.Meta.name == 'test plan'] | 235 | } for tp in test_plan_units] |
417 | 322 | } | 236 | } |
418 | 323 | 237 | ||
419 | 324 | @view | 238 | @view |
420 | 325 | def remember_testplan(self, test_plan_id): | 239 | def remember_testplan(self, test_plan_id): |
424 | 326 | self.context.invalidate_shared('potential_job_list') | 240 | """Pick the test plan as the one in force.""" |
425 | 327 | self.context.invalidate_shared('potential_category_map') | 241 | self.test_plan_id = test_plan_id |
426 | 328 | self._init_test_plan_id(test_plan_id) | 242 | self.assistant.select_test_plan(test_plan_id) |
427 | 243 | self.assistant.bootstrap() | ||
428 | 329 | 244 | ||
429 | 330 | @view | 245 | @view |
430 | 331 | def get_categories(self): | 246 | def get_categories(self): |
442 | 332 | """ | 247 | """Get categories selection data.""" |
432 | 333 | Get categories selection data. | ||
433 | 334 | """ | ||
434 | 335 | potential_job_list = self.context.compute_shared( | ||
435 | 336 | 'potential_job_list', select_jobs, | ||
436 | 337 | self.context.state.job_list, [self.test_plan.get_qualifier()]) | ||
437 | 338 | potential_category_map = self.context.compute_shared( | ||
438 | 339 | 'potential_category_map', | ||
439 | 340 | self.test_plan.get_effective_category_map, potential_job_list) | ||
440 | 341 | id_map = self.context.compute_shared( | ||
441 | 342 | 'id_map', compute_value_map, self.context, 'id') | ||
443 | 343 | category_info_list = [{ | 248 | category_info_list = [{ |
444 | 344 | "mod_id": category.id, | 249 | "mod_id": category.id, |
445 | 345 | "mod_name": category.name, | 250 | "mod_name": category.name, |
446 | 346 | "mod_selected": True, | 251 | "mod_selected": True, |
447 | 347 | } for category in ( | 252 | } for category in ( |
450 | 348 | id_map[category_id][0] | 253 | self.assistant.get_category(category_id) |
451 | 349 | for category_id in set(potential_category_map.values()) | 254 | for category_id in self.assistant.get_participating_categories() |
452 | 350 | )] | 255 | )] |
457 | 351 | category_info_list.sort(key=lambda ci: ci['mod_name']) | 256 | return {'category_info_list': category_info_list} |
454 | 352 | return { | ||
455 | 353 | 'category_info_list': category_info_list | ||
456 | 354 | } | ||
458 | 355 | 257 | ||
459 | 356 | @view | 258 | @view |
460 | 357 | def remember_categories(self, selected_id_list): | 259 | def remember_categories(self, selected_id_list): |
468 | 358 | """ | 260 | """Save category selection.""" |
469 | 359 | Save category selection | 261 | _logger.info("Selected categories: %s", selected_id_list) |
470 | 360 | """ | 262 | self.assistant.filter_jobs_by_categories(selected_id_list) |
464 | 361 | self.desired_category_ids = frozenset(selected_id_list) | ||
465 | 362 | self.context.invalidate_shared('subset_job_list') | ||
466 | 363 | self.context.invalidate_shared('effective_category_map') | ||
467 | 364 | _logger.info("Selected categories: %s", self.desired_category_ids) | ||
471 | 365 | 263 | ||
472 | 366 | @view | 264 | @view |
473 | 367 | def get_available_tests(self): | 265 | def get_available_tests(self): |
474 | @@ -371,89 +269,65 @@ | |||
475 | 371 | The response object will contain only tests with category matching | 269 | The response object will contain only tests with category matching |
476 | 372 | previously set list. Tests are sorted by (category, name) | 270 | previously set list. Tests are sorted by (category, name) |
477 | 373 | """ | 271 | """ |
497 | 374 | subset_job_list = self.context.compute_shared( | 272 | category_names = { |
498 | 375 | 'subset_job_list', select_jobs, | 273 | cat_id: self.assistant.get_category(cat_id).tr_name() for |
499 | 376 | self.context.state.job_list, [ | 274 | cat_id in self.assistant.get_participating_categories()} |
500 | 377 | # Select everything the test plan selected | 275 | job_units = [self.assistant.get_job(job_id) for job_id in |
501 | 378 | self.test_plan.get_qualifier(), | 276 | self.assistant.get_static_todo_list()] |
483 | 379 | # Except jobs not matching the selected group of categories | ||
484 | 380 | FieldQualifier( | ||
485 | 381 | JobDefinition.Meta.fields.category_id, | ||
486 | 382 | OperatorMatcher(not_contains, self.desired_category_ids), | ||
487 | 383 | Origin.get_caller_origin(), inclusive=False), | ||
488 | 384 | ]) | ||
489 | 385 | effective_category_map = self.context.compute_shared( | ||
490 | 386 | 'effective_category_map', | ||
491 | 387 | self.test_plan.get_effective_category_map, subset_job_list) | ||
492 | 388 | for job_id, effective_category_id in effective_category_map.items(): | ||
493 | 389 | job_state = self.context.state.job_state_map[job_id] | ||
494 | 390 | job_state.effective_category_id = effective_category_id | ||
495 | 391 | id_map = self.context.compute_shared( | ||
496 | 392 | 'id_map', compute_value_map, self.context, 'id') | ||
502 | 393 | test_info_list = [{ | 277 | test_info_list = [{ |
506 | 394 | "mod_id": job_id, | 278 | "mod_id": job.id, |
507 | 395 | "mod_name": id_map[job_id][0].tr_summary(), | 279 | "mod_name": job.tr_summary(), |
508 | 396 | "mod_group": id_map[category_id][0].tr_name(), | 280 | "mod_group": category_names[job.category_id], |
509 | 397 | "mod_selected": True, | 281 | "mod_selected": True, |
511 | 398 | } for job_id, category_id in effective_category_map.items()] | 282 | } for job in job_units] |
512 | 399 | test_info_list.sort(key=lambda ti: (ti['mod_group'], ti['mod_name'])) | 283 | test_info_list.sort(key=lambda ti: (ti['mod_group'], ti['mod_name'])) |
516 | 400 | return { | 284 | return {'test_info_list': test_info_list} |
517 | 401 | 'test_info_list': test_info_list | 285 | |
515 | 402 | } | ||
518 | 403 | @view | 286 | @view |
519 | 404 | def get_rerun_candidates(self): | 287 | def get_rerun_candidates(self): |
520 | 288 | """Get all the tests that might be selected for rerunning.""" | ||
521 | 405 | def rerun_predicate(job_state): | 289 | def rerun_predicate(job_state): |
522 | 406 | return job_state.result.outcome in ( | 290 | return job_state.result.outcome in ( |
523 | 407 | IJobResult.OUTCOME_FAIL, IJobResult.OUTCOME_CRASH) | 291 | IJobResult.OUTCOME_FAIL, IJobResult.OUTCOME_CRASH) |
524 | 408 | id_map = self.context.compute_shared( | ||
525 | 409 | 'id_map', compute_value_map, self.context, 'id') | ||
526 | 410 | rerun_candidates = [] | 292 | rerun_candidates = [] |
529 | 411 | for job in self.manager.state.run_list: | 293 | todo_list = self.assistant.get_static_todo_list() |
530 | 412 | if rerun_predicate(self.manager.state.job_state_map[job.id]): | 294 | job_units = {job_id: self.assistant.get_job(job_id) for job_id |
531 | 295 | in todo_list} | ||
532 | 296 | job_states = {job_id: self.assistant.get_job_state(job_id) for job_id | ||
533 | 297 | in todo_list} | ||
534 | 298 | category_names = { | ||
535 | 299 | cat_id: self.assistant.get_category(cat_id).tr_name() for cat_id | ||
536 | 300 | in self.assistant.get_participating_categories()} | ||
537 | 301 | for job_id, job_state in job_states.items(): | ||
538 | 302 | if rerun_predicate(job_state): | ||
539 | 413 | rerun_candidates.append({ | 303 | rerun_candidates.append({ |
543 | 414 | "mod_id": job.id, | 304 | "mod_id": job_id, |
544 | 415 | "mod_name": job.tr_summary(), | 305 | "mod_name": job_units[job_id].tr_summary(), |
545 | 416 | "mod_group": id_map[job.category_id][0].tr_name(), | 306 | "mod_group": category_names[job_units[job_id].category_id], |
546 | 417 | "mod_selected": False | 307 | "mod_selected": False |
547 | 418 | |||
548 | 419 | }) | 308 | }) |
549 | 420 | return rerun_candidates | 309 | return rerun_candidates |
550 | 421 | 310 | ||
551 | 422 | @view | 311 | @view |
552 | 423 | def remember_tests(self, selected_id_list): | 312 | def remember_tests(self, selected_id_list): |
558 | 424 | """ | 313 | """Save test selection.""" |
554 | 425 | Save test selection | ||
555 | 426 | """ | ||
556 | 427 | self.desired_test_ids = frozenset(selected_id_list) | ||
557 | 428 | _logger.info("Selected tests: %s", self.desired_test_ids) | ||
559 | 429 | self.index = 0 | 314 | self.index = 0 |
576 | 430 | self.context.invalidate_shared('desired_job_list') | 315 | self.assistant.use_alternate_selection(selected_id_list) |
577 | 431 | desired_job_list = self.context.compute_shared( | 316 | self.assistant.update_app_blob(self._get_app_blob()) |
578 | 432 | 'desired_job_list', select_jobs, | 317 | _logger.info("Selected tests: %s", selected_id_list) |
579 | 433 | self.context.state.job_list, [ | 318 | return |
564 | 434 | # Select everything the test plan selected | ||
565 | 435 | self.test_plan.get_qualifier(), | ||
566 | 436 | # Except all the jobs that weren't marked by the user | ||
567 | 437 | FieldQualifier( | ||
568 | 438 | JobDefinition.Meta.fields.id, | ||
569 | 439 | OperatorMatcher(not_contains, self.desired_test_ids), | ||
570 | 440 | Origin.get_caller_origin(), inclusive=False)]) | ||
571 | 441 | _logger.info("Desired job list: %s", desired_job_list) | ||
572 | 442 | self.context.state.update_desired_job_list(desired_job_list) | ||
573 | 443 | _logger.info("Run job list: %s", self.context.state.run_list) | ||
574 | 444 | self.context.state.metadata.flags.add('incomplete') | ||
575 | 445 | self._checkpoint() | ||
580 | 446 | 319 | ||
581 | 447 | @view | 320 | @view |
582 | 448 | def get_next_test(self): | 321 | def get_next_test(self): |
583 | 449 | """ | 322 | """ |
586 | 450 | Get next text that is scheduled to run | 323 | Get next text that is scheduled to run. |
587 | 451 | Returns test object or None if all tests are completed | 324 | |
588 | 325 | :returns: | ||
589 | 326 | Dictionary resembling JobDefinition or None if all tests are completed | ||
590 | 452 | """ | 327 | """ |
595 | 453 | if self.index < len(self.context.state.run_list): | 328 | todo_list = self.assistant.get_static_todo_list() |
596 | 454 | job = self.context.state.run_list[self.index] | 329 | if self.index < len(todo_list): |
597 | 455 | job_state = self.context.state.job_state_map[job.id] | 330 | job = self.assistant.get_job(todo_list[self.index]) |
594 | 456 | # support for description field splitted into 3 subfields | ||
598 | 457 | description = "" | 331 | description = "" |
599 | 458 | if job.tr_purpose() is not None: | 332 | if job.tr_purpose() is not None: |
600 | 459 | description = job.tr_purpose() + "\n" | 333 | description = job.tr_purpose() + "\n" |
601 | @@ -472,97 +346,65 @@ | |||
602 | 472 | "user": job.user, | 346 | "user": job.user, |
603 | 473 | "qml_file": job.qml_file, | 347 | "qml_file": job.qml_file, |
604 | 474 | "start_time": time.time(), | 348 | "start_time": time.time(), |
607 | 475 | "test_number": self.index, | 349 | "test_number": todo_list.index(job.id), |
608 | 476 | "tests_count": len(self.context.state.run_list), | 350 | "tests_count": len(todo_list), |
609 | 477 | "command": job.command, | 351 | "command": job.command, |
610 | 478 | "flags": job.get_flag_set() | 352 | "flags": job.get_flag_set() |
611 | 479 | } | 353 | } |
612 | 480 | if not job_state.can_start(): | ||
613 | 481 | test["outcome"] = "skip" | ||
614 | 482 | test["comments"] = job_state.get_readiness_description() | ||
615 | 483 | self.register_test_result(test) | ||
616 | 484 | return self.get_next_test()["result"] | ||
617 | 485 | return test | 354 | return test |
618 | 486 | else: | 355 | else: |
619 | 487 | return {} | 356 | return {} |
620 | 488 | 357 | ||
621 | 489 | @view | 358 | @view |
622 | 490 | def register_test_result(self, test): | 359 | def register_test_result(self, test): |
626 | 491 | """ | 360 | """Registers outcome of a test.""" |
624 | 492 | Registers outcome of a test | ||
625 | 493 | """ | ||
627 | 494 | _logger.info("Storing test result: %s", test) | 361 | _logger.info("Storing test result: %s", test) |
628 | 495 | job_id = test['id'] | 362 | job_id = test['id'] |
629 | 496 | job = self.context.state.job_state_map[job_id].job | ||
630 | 497 | builder_kwargs = { | 363 | builder_kwargs = { |
631 | 498 | 'outcome': test['outcome'], | 364 | 'outcome': test['outcome'], |
633 | 499 | 'comments': test.get('comments', pod.UNSET) | 365 | 'comments': test.get('comments', pod.UNSET), |
634 | 366 | 'execution_duration': time.time() - test['start_time'] | ||
635 | 500 | } | 367 | } |
636 | 501 | # some result may already have been saved if the job had some activity | ||
637 | 502 | # to run, i.e. result object is available in the test object | ||
638 | 503 | try: | 368 | try: |
639 | 369 | # if we're registering skipped test as an outcome of resuming | ||
640 | 370 | # session, the result field of the test object will be missing | ||
641 | 504 | builder_kwargs['io_log_filename'] = test['result'].io_log_filename | 371 | builder_kwargs['io_log_filename'] = test['result'].io_log_filename |
642 | 505 | except KeyError: | 372 | except KeyError: |
645 | 506 | builder_kwargs['execution_duration'] = ( | 373 | pass |
646 | 507 | time.time() - test['start_time']) | 374 | |
647 | 508 | result = JobResultBuilder(**builder_kwargs).get_result() | 375 | result = JobResultBuilder(**builder_kwargs).get_result() |
649 | 509 | self.context.state.update_job_result(job, result) | 376 | self.assistant.use_job_result(job_id, result) |
650 | 510 | self.index += 1 | 377 | self.index += 1 |
652 | 511 | self._checkpoint() | 378 | self.assistant.update_app_blob(self._get_app_blob()) |
653 | 512 | 379 | ||
654 | 513 | @view | 380 | @view |
655 | 514 | def run_test_activity(self, test): | 381 | def run_test_activity(self, test): |
675 | 515 | """ | 382 | """Run command associated with given test.""" |
676 | 516 | Run command associated with given test | 383 | plugins_handled_natively = ['qml'] |
677 | 517 | """ | 384 | res_builder = self.assistant.run_job( |
678 | 518 | job_id = test['id'] | 385 | test['id'], self.ui, test['plugin'] in plugins_handled_natively) |
679 | 519 | job_state = self.context.state.job_state_map[job_id] | 386 | test['outcome'] = res_builder.outcome |
680 | 520 | job = job_state.job | 387 | test['result'] = res_builder |
662 | 521 | self.context.state.running_job_name = job_id | ||
663 | 522 | self._checkpoint() | ||
664 | 523 | try: | ||
665 | 524 | result = self.runner.run_job(job, job_state, self.config, self.ui) | ||
666 | 525 | except OSError as exc: | ||
667 | 526 | result = JobResultBuilder( | ||
668 | 527 | outcome='fail', | ||
669 | 528 | comment=str(exc), | ||
670 | 529 | ).get_result() | ||
671 | 530 | self.context.state.running_job_name = None | ||
672 | 531 | self._checkpoint() | ||
673 | 532 | test['outcome'] = result.outcome | ||
674 | 533 | test['result'] = result | ||
681 | 534 | return test | 388 | return test |
682 | 535 | 389 | ||
683 | 536 | @view | 390 | @view |
684 | 537 | def get_results(self): | 391 | def get_results(self): |
693 | 538 | """ | 392 | """Get results object.""" |
694 | 539 | Get results object | 393 | self.assistant.finalize_session() |
695 | 540 | """ | 394 | stats = self.assistant.get_summary() |
688 | 541 | self.context.state.metadata.flags.remove('incomplete') | ||
689 | 542 | self._checkpoint() | ||
690 | 543 | stats = collections.defaultdict(int) | ||
691 | 544 | for job_state in self.context.state.job_state_map.values(): | ||
692 | 545 | stats[job_state.result.outcome] += 1 | ||
696 | 546 | return { | 395 | return { |
697 | 547 | 'totalPassed': stats[IJobResult.OUTCOME_PASS], | 396 | 'totalPassed': stats[IJobResult.OUTCOME_PASS], |
698 | 548 | 'totalFailed': stats[IJobResult.OUTCOME_FAIL], | 397 | 'totalFailed': stats[IJobResult.OUTCOME_FAIL], |
700 | 549 | 'totalSkipped': stats[IJobResult.OUTCOME_SKIP], | 398 | 'totalSkipped': stats[IJobResult.OUTCOME_SKIP] + |
701 | 399 | stats[IJobResult.OUTCOME_NOT_SUPPORTED] | ||
702 | 550 | } | 400 | } |
703 | 551 | 401 | ||
704 | 552 | @view | 402 | @view |
705 | 553 | def export_results(self, output_format, option_list): | 403 | def export_results(self, output_format, option_list): |
710 | 554 | """ | 404 | """Export results to file(s) in the user's 'Documents' directory..""" |
707 | 555 | Export results to file | ||
708 | 556 | """ | ||
709 | 557 | # Export results in the user's Documents directory | ||
711 | 558 | dirname = self._get_user_directory_documents() | 405 | dirname = self._get_user_directory_documents() |
719 | 559 | exporter = self.manager.create_exporter(output_format, option_list) | 406 | return self.assistant.export_to_file( |
720 | 560 | extension = exporter.unit.file_extension | 407 | output_format, option_list, dirname) |
714 | 561 | filename = ''.join(['submission_', self.timestamp, '.', extension]) | ||
715 | 562 | output_file = os.path.join(dirname, filename) | ||
716 | 563 | with open(output_file, 'wb') as stream: | ||
717 | 564 | exporter.dump_from_session_manager(self.manager, stream) | ||
718 | 565 | return output_file | ||
721 | 566 | 408 | ||
722 | 567 | def _get_user_directory_documents(self): | 409 | def _get_user_directory_documents(self): |
723 | 568 | xdg_config_home = os.environ.get('XDG_CONFIG_HOME') or \ | 410 | xdg_config_home = os.environ.get('XDG_CONFIG_HOME') or \ |
724 | @@ -601,11 +443,6 @@ | |||
725 | 601 | raise IOError("{} exists and is not a directory".format(path)) | 443 | raise IOError("{} exists and is not a directory".format(path)) |
726 | 602 | return path | 444 | return path |
727 | 603 | 445 | ||
728 | 604 | |||
729 | 605 | def _checkpoint(self): | ||
730 | 606 | self.context.state.metadata.app_blob = self._get_app_blob() | ||
731 | 607 | self.manager.checkpoint() | ||
732 | 608 | |||
733 | 609 | def _get_app_blob(self): | 446 | def _get_app_blob(self): |
734 | 610 | """ | 447 | """ |
735 | 611 | Get json dump of with app-specific blob | 448 | Get json dump of with app-specific blob |
736 | @@ -615,51 +452,22 @@ | |||
737 | 615 | 'version': 1, | 452 | 'version': 1, |
738 | 616 | 'test_plan_id': self.test_plan_id, | 453 | 'test_plan_id': self.test_plan_id, |
739 | 617 | 'index_in_run_list': self.index, | 454 | 'index_in_run_list': self.index, |
740 | 455 | 'session_timestamp': self._timestamp, | ||
741 | 618 | }).encode("UTF-8") | 456 | }).encode("UTF-8") |
742 | 619 | 457 | ||
773 | 620 | def _init_test_plan_id(self, test_plan_id): | 458 | def _get_embedded_providers(self, providers_dir): |
774 | 621 | """ | 459 | """ |
775 | 622 | Validates and stores test_plan_id | 460 | Get providers included with the app |
746 | 623 | """ | ||
747 | 624 | if not isinstance(test_plan_id, str): | ||
748 | 625 | raise TypeError("test_plan_id must be a string") | ||
749 | 626 | # Look up the test plan with the specified identifier | ||
750 | 627 | id_map = self.context.compute_shared( | ||
751 | 628 | 'id_map', compute_value_map, self.context, 'id') | ||
752 | 629 | try: | ||
753 | 630 | test_plan = id_map[test_plan_id][0] | ||
754 | 631 | except KeyError: | ||
755 | 632 | raise ValueError( | ||
756 | 633 | "cannot find any unit with id: {!r}".format(test_plan_id)) | ||
757 | 634 | if test_plan.Meta.name != 'test plan': | ||
758 | 635 | raise ValueError( | ||
759 | 636 | "unit {!r} is not a test plan".format(test_plan_id)) | ||
760 | 637 | self.test_plan_id = test_plan_id | ||
761 | 638 | self.test_plan = test_plan | ||
762 | 639 | |||
763 | 640 | def _init_session_storage_repo(self): | ||
764 | 641 | """ | ||
765 | 642 | Init storage repository. | ||
766 | 643 | """ | ||
767 | 644 | self.session_storage_repo = SessionStorageRepository( | ||
768 | 645 | self._get_app_cache_directory()) | ||
769 | 646 | |||
770 | 647 | def _get_default_providers(self, providers_dir): | ||
771 | 648 | """ | ||
772 | 649 | Get providers | ||
776 | 650 | 461 | ||
777 | 651 | :param providers_dir: | 462 | :param providers_dir: |
778 | 652 | Path within application tree from which to load providers | 463 | Path within application tree from which to load providers |
779 | 653 | :returns: | 464 | :returns: |
780 | 654 | list of loaded providers | 465 | list of loaded providers |
781 | 655 | """ | 466 | """ |
785 | 656 | provider_list = get_providers() | 467 | provider_list = [] |
783 | 657 | # when running on ubuntu-touch device, APP_DIR env var is present | ||
784 | 658 | # and points to touch application top directory | ||
786 | 659 | app_root_dir = os.path.normpath(os.getenv( | 468 | app_root_dir = os.path.normpath(os.getenv( |
787 | 660 | 'APP_DIR', os.path.join(os.path.dirname(__file__), '..'))) | 469 | 'APP_DIR', os.path.join(os.path.dirname(__file__), '..'))) |
790 | 661 | path = os.path.join(app_root_dir, | 470 | path = os.path.join(app_root_dir, os.path.normpath(providers_dir)) |
789 | 662 | os.path.normpath(providers_dir)) | ||
791 | 663 | _logger.info("Loading all providers from %s", path) | 471 | _logger.info("Loading all providers from %s", path) |
792 | 664 | if os.path.exists(path): | 472 | if os.path.exists(path): |
793 | 665 | embedded_providers = EmbeddedProvider1PlugInCollection(path) | 473 | embedded_providers = EmbeddedProvider1PlugInCollection(path) |
794 | @@ -683,7 +491,7 @@ | |||
795 | 683 | 491 | ||
796 | 684 | 492 | ||
797 | 685 | def get_qml_logger(default_level): | 493 | def get_qml_logger(default_level): |
799 | 686 | logging_level = collections.defaultdict(lambda:logging.INFO, { | 494 | logging_level = collections.defaultdict(lambda: logging.INFO, { |
800 | 687 | "debug": logging.DEBUG, | 495 | "debug": logging.DEBUG, |
801 | 688 | "warning": logging.WARNING, | 496 | "warning": logging.WARNING, |
802 | 689 | "warn": logging.WARN, | 497 | "warn": logging.WARN, |
803 | 690 | 498 | ||
804 | === modified file 'checkbox-touch/tests/autopilot/checkbox_touch/test_checkbox_touch.py' | |||
805 | --- checkbox-touch/tests/autopilot/checkbox_touch/test_checkbox_touch.py 2015-07-23 08:25:48 +0000 | |||
806 | +++ checkbox-touch/tests/autopilot/checkbox_touch/test_checkbox_touch.py 2015-08-31 12:00:21 +0000 | |||
807 | @@ -62,41 +62,44 @@ | |||
808 | 62 | ] | 62 | ] |
809 | 63 | self.process_sequence_of_clicks_on_pages(next_steps) | 63 | self.process_sequence_of_clicks_on_pages(next_steps) |
810 | 64 | self.skip_test('manualIntroPage') | 64 | self.skip_test('manualIntroPage') |
811 | 65 | next_steps = [ | ||
812 | 66 | ('userInteractVerifyIntroPage', 'startTestButton'), | ||
813 | 67 | ('testVerificationPage', 'passButton'), | ||
814 | 68 | ('userInteractVerifyIntroPage', 'startTestButton'), | ||
815 | 69 | ('testVerificationPage', 'failButton') | ||
816 | 70 | ] | ||
817 | 71 | self.process_sequence_of_clicks_on_pages(next_steps) | ||
818 | 72 | self.skip_test('userInteractVerifyIntroPage') | ||
819 | 73 | next_steps = [ | ||
820 | 74 | ('userInteractVerifyIntroPage', 'startTestButton'), | ||
821 | 75 | ('testVerificationPage', 'passButton'), | ||
822 | 76 | ('userInteractVerifyIntroPage', 'startTestButton'), | ||
823 | 77 | ('testVerificationPage', 'failButton') | ||
824 | 78 | ] | ||
825 | 79 | self.process_sequence_of_clicks_on_pages(next_steps) | ||
826 | 80 | self.skip_test('userInteractVerifyIntroPage') | ||
827 | 81 | next_steps = [ | ||
828 | 82 | ('userInteractVerifyIntroPage', 'startTestButton'), | ||
829 | 83 | ('userInteractSummary', 'continueButton'), | ||
830 | 84 | ('userInteractVerifyIntroPage', 'startTestButton'), | ||
831 | 85 | ('userInteractSummary', 'continueButton'), | ||
832 | 86 | ] | ||
833 | 87 | self.process_sequence_of_clicks_on_pages(next_steps) | ||
834 | 88 | self.skip_test('userInteractVerifyIntroPage') | ||
835 | 89 | next_steps = [ | ||
836 | 90 | ('userInteractVerifyIntroPage', 'startTestButton'), | ||
837 | 91 | ('testVerificationPage', 'passButton'), | ||
838 | 92 | ] | ||
839 | 93 | self.process_sequence_of_clicks_on_pages(next_steps) | ||
840 | 94 | # now we use long_wait because we have a long test to wait for (>10s) | 65 | # now we use long_wait because we have a long test to wait for (>10s) |
841 | 95 | self.long_wait_select_single( | 66 | self.long_wait_select_single( |
843 | 96 | self.app, objectName='qmlNativePage', visible=True) | 67 | self.app, objectName='userInteractVerifyIntroPage', visible=True) |
844 | 68 | next_steps = [ | ||
845 | 69 | ('userInteractVerifyIntroPage', 'startTestButton'), | ||
846 | 70 | ('testVerificationPage', 'passButton'), | ||
847 | 71 | ] | ||
848 | 72 | self.process_sequence_of_clicks_on_pages(next_steps) | ||
849 | 97 | next_steps = [ | 73 | next_steps = [ |
850 | 98 | ('qmlNativePage', 'continueButton'), | 74 | ('qmlNativePage', 'continueButton'), |
851 | 99 | ('qmlTestPage', 'passButton'), | 75 | ('qmlTestPage', 'passButton'), |
852 | 76 | ] | ||
853 | 77 | self.process_sequence_of_clicks_on_pages(next_steps) | ||
854 | 78 | next_steps = [ | ||
855 | 79 | ('userInteractVerifyIntroPage', 'startTestButton'), | ||
856 | 80 | ('userInteractSummary', 'continueButton'), | ||
857 | 81 | ('userInteractVerifyIntroPage', 'startTestButton'), | ||
858 | 82 | ('userInteractSummary', 'continueButton'), | ||
859 | 83 | ] | ||
860 | 84 | self.process_sequence_of_clicks_on_pages(next_steps) | ||
861 | 85 | self.skip_test('userInteractVerifyIntroPage') | ||
862 | 86 | next_steps = [ | ||
863 | 87 | ('userInteractVerifyIntroPage', 'startTestButton'), | ||
864 | 88 | ('testVerificationPage', 'passButton'), | ||
865 | 89 | ('userInteractVerifyIntroPage', 'startTestButton'), | ||
866 | 90 | ('testVerificationPage', 'failButton') | ||
867 | 91 | ] | ||
868 | 92 | self.process_sequence_of_clicks_on_pages(next_steps) | ||
869 | 93 | self.skip_test('userInteractVerifyIntroPage') | ||
870 | 94 | next_steps = [ | ||
871 | 95 | ('userInteractVerifyIntroPage', 'startTestButton'), | ||
872 | 96 | ('testVerificationPage', 'passButton'), | ||
873 | 97 | ('userInteractVerifyIntroPage', 'startTestButton'), | ||
874 | 98 | ('testVerificationPage', 'failButton') | ||
875 | 99 | ] | ||
876 | 100 | self.process_sequence_of_clicks_on_pages(next_steps) | ||
877 | 101 | self.skip_test('userInteractVerifyIntroPage') | ||
878 | 102 | next_steps = [ | ||
879 | 100 | ('rerunSelectionPage', 'continueButton') | 103 | ('rerunSelectionPage', 'continueButton') |
880 | 101 | ] | 104 | ] |
881 | 102 | self.process_sequence_of_clicks_on_pages(next_steps) | 105 | self.process_sequence_of_clicks_on_pages(next_steps) |
882 | 103 | 106 | ||
883 | === modified file 'plainbox/plainbox/impl/session/assistant.py' | |||
884 | --- plainbox/plainbox/impl/session/assistant.py 2015-08-28 13:11:25 +0000 | |||
885 | +++ plainbox/plainbox/impl/session/assistant.py 2015-08-31 12:00:21 +0000 | |||
886 | @@ -371,7 +371,10 @@ | |||
887 | 371 | self.session_available(self._manager.storage.id) | 371 | self.session_available(self._manager.storage.id) |
888 | 372 | _logger.debug("New session created: %s", title) | 372 | _logger.debug("New session created: %s", title) |
889 | 373 | UsageExpectation.of(self).allowed_calls = { | 373 | UsageExpectation.of(self).allowed_calls = { |
891 | 374 | self.select_test_plan: "select the test plan to execute" | 374 | self.select_test_plan: "select the test plan to execute", |
892 | 375 | self.get_session_id: "to get the id of currently running session", | ||
893 | 376 | self.get_session_dir: ("to get the path where current session is" | ||
894 | 377 | "stored"), | ||
895 | 375 | } | 378 | } |
896 | 376 | 379 | ||
897 | 377 | @raises(KeyError, UnexpectedMethodCall) | 380 | @raises(KeyError, UnexpectedMethodCall) |
898 | @@ -412,8 +415,9 @@ | |||
899 | 412 | execution_ctrl_list=self._execution_ctrl_list) | 415 | execution_ctrl_list=self._execution_ctrl_list) |
900 | 413 | self.session_available(self._manager.storage.id) | 416 | self.session_available(self._manager.storage.id) |
901 | 414 | _logger.debug("Session resumed: %s", session_id) | 417 | _logger.debug("Session resumed: %s", session_id) |
904 | 415 | UsageExpectation.of(self).allowed_calls = ( | 418 | UsageExpectation.of(self).allowed_calls = { |
905 | 416 | self._get_allowed_calls_in_normal_state()) | 419 | self.select_test_plan: "to save test plan selection", |
906 | 420 | } | ||
907 | 417 | return self._resume_candidates[session_id][1] | 421 | return self._resume_candidates[session_id][1] |
908 | 418 | 422 | ||
909 | 419 | @raises(UnexpectedMethodCall) | 423 | @raises(UnexpectedMethodCall) |
910 | @@ -556,6 +560,8 @@ | |||
911 | 556 | to actually allowing the user to know what jobs are available. | 560 | to actually allowing the user to know what jobs are available. |
912 | 557 | """ | 561 | """ |
913 | 558 | UsageExpectation.of(self).enforce() | 562 | UsageExpectation.of(self).enforce() |
914 | 563 | UsageExpectation.of(self).allowed_calls = ( | ||
915 | 564 | self._get_allowed_calls_in_normal_state()) | ||
916 | 559 | return [unit.id for unit in self._context.unit_list | 565 | return [unit.id for unit in self._context.unit_list |
917 | 560 | if unit.Meta.name == 'test plan'] | 566 | if unit.Meta.name == 'test plan'] |
918 | 561 | 567 | ||
919 | @@ -1036,6 +1042,32 @@ | |||
920 | 1036 | 1042 | ||
921 | 1037 | return stats | 1043 | return stats |
922 | 1038 | 1044 | ||
923 | 1045 | @raises(UnexpectedMethodCall) | ||
924 | 1046 | def finalize_session(self) -> None: | ||
925 | 1047 | """ | ||
926 | 1048 | Finish the execution of the current session. | ||
927 | 1049 | |||
928 | 1050 | :raises UnexpectedMethodCall: | ||
929 | 1051 | If the call is made at an unexpected time. Do not catch this error. | ||
930 | 1052 | It is a bug in your program. The error message will indicate what | ||
931 | 1053 | is the likely cause. | ||
932 | 1054 | |||
933 | 1055 | Mark the session as complete, which prohibits running (or rerunning) | ||
934 | 1056 | any job. | ||
935 | 1057 | """ | ||
936 | 1058 | UsageExpectation.of(self).enforce() | ||
937 | 1059 | if SessionMetaData.FLAG_SUBMITTED not in self._metadata.flags: | ||
938 | 1060 | _logger.warning("Finalizing session that hasn't been submitted " | ||
939 | 1061 | "anywhere: %s", self._manager.storage.id) | ||
940 | 1062 | self._metadata.flags.remove(SessionMetaData.FLAG_INCOMPLETE) | ||
941 | 1063 | self._manager.checkpoint() | ||
942 | 1064 | UsageExpectation.of(self).allowed_calls = { | ||
943 | 1065 | self.export_to_transport: "to export the results and send them", | ||
944 | 1066 | self.export_to_file: "to export the results to a file", | ||
945 | 1067 | self.get_resumable_sessions: "to get resume candidates", | ||
946 | 1068 | self.start_new_session: "to create a new session", | ||
947 | 1069 | } | ||
948 | 1070 | |||
949 | 1039 | @raises(KeyError, TransportError, UnexpectedMethodCall) | 1071 | @raises(KeyError, TransportError, UnexpectedMethodCall) |
950 | 1040 | def export_to_transport( | 1072 | def export_to_transport( |
951 | 1041 | self, exporter_id: str, transport: ISessionStateTransport | 1073 | self, exporter_id: str, transport: ISessionStateTransport |
952 | @@ -1186,6 +1218,8 @@ | |||
953 | 1186 | # XXX: should this be available right off the bat or should we wait | 1218 | # XXX: should this be available right off the bat or should we wait |
954 | 1187 | # until all of the mandatory jobs have been executed. | 1219 | # until all of the mandatory jobs have been executed. |
955 | 1188 | self.export_to_transport: "to export the results and send them", | 1220 | self.export_to_transport: "to export the results and send them", |
956 | 1221 | self.export_to_file: "to export the results to a file", | ||
957 | 1222 | self.finalize_session: "to mark the session as complete", | ||
958 | 1189 | } | 1223 | } |
959 | 1190 | 1224 | ||
960 | 1191 | 1225 |
let there be light