Merge lp:~kissiel/checkbox/confined-qml-jobs into lp:checkbox

Proposed by Maciej Kisielewski
Status: Merged
Approved by: Zygmunt Krynicki
Approved revision: 3914
Merged at revision: 3915
Proposed branch: lp:~kissiel/checkbox/confined-qml-jobs
Merge into: lp:checkbox
Diff against target: 531 lines (+463/-3)
7 files modified
checkbox-touch/checkbox-touch.qml (+15/-1)
checkbox-touch/components/QmlConfinedPage.qml (+160/-0)
checkbox-touch/confinement/generate-confinement.py (+111/-0)
checkbox-touch/confinement/plainbox-confined-shell.qml (+130/-0)
checkbox-touch/confinement/plainbox_confined_shell.py (+43/-0)
checkbox-touch/manifest.json (+1/-1)
checkbox-touch/py/checkbox_touch.py (+3/-1)
To merge this branch: bzr merge lp:~kissiel/checkbox/confined-qml-jobs
Reviewer Review Type Date Requested Status
Zygmunt Krynicki (community) Approve
Review via email: mp+265640@code.launchpad.net

Description of the change

This MR brings support for the confined tests!

3e61d6f checkbox-touch: make test quasi-object forward flags
442be29 checkbox-touch: make test quasi-object forward partial_id
abc3808 checkbox-touch: change version to 1.2 to match what plainbox reports
18700ec checkbox-touch: add shell for running confined tests
ecc05b6 checkbox-touch: add QML confined page component
91e2524 checkbox-touch: add function that runs confined qml jobs
33e2a62 checkbox-touch: check for 'confined' flag and act appropriately
3ced2e1 checkbox-touch: add confinement generator

To post a comment you must log in.
Revision history for this message
Zygmunt Krynicki (zyga) wrote :

I had a review session with Maciek earlier today. Let's merge it.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'checkbox-touch/checkbox-touch.qml'
2--- checkbox-touch/checkbox-touch.qml 2015-07-23 08:25:48 +0000
3+++ checkbox-touch/checkbox-touch.qml 2015-07-23 11:16:26 +0000
4@@ -456,7 +456,10 @@
5 performUserInteractTest(test);
6 break;
7 case 'qml':
8- performQmlTest(test);
9+ if (test.flags.indexOf("confined") > -1)
10+ performConfinedQmlTest(test);
11+ else
12+ performQmlTest(test);
13 break;
14 default:
15 test.outcome = "skip";
16@@ -590,6 +593,17 @@
17 progressHeader.update(test);
18 pageStack.push(qmlNativePage);
19 }
20+ function performConfinedQmlTest(test) {
21+ var comp = Qt.createComponent(Qt.resolvedUrl("components/QmlConfinedPage.qml"))
22+ console.log(comp.errorString());
23+ var qmlNativePage = comp.createObject();
24+ qmlNativePage.test = test
25+ qmlNativePage.applicationVersion = app.applicationVersion;
26+ qmlNativePage.testDone.connect(completeTest);
27+ qmlNativePage.__customHeaderContents = progressHeader;
28+ progressHeader.update(test);
29+ pageStack.push(qmlNativePage);
30+ }
31
32 function showVerificationScreen(test) {
33 var verificationPage = Qt.createComponent(Qt.resolvedUrl("components/TestVerificationPage.qml")).createObject();
34
35=== added file 'checkbox-touch/components/QmlConfinedPage.qml'
36--- checkbox-touch/components/QmlConfinedPage.qml 1970-01-01 00:00:00 +0000
37+++ checkbox-touch/components/QmlConfinedPage.qml 2015-07-23 11:16:26 +0000
38@@ -0,0 +1,160 @@
39+/*
40+ * This file is part of Checkbox
41+ *
42+ * Copyright 2015 Canonical Ltd.
43+ *
44+ * Authors:
45+ * - Maciej Kisielewski <maciej.kisielewski@canonical.com>
46+ *
47+ * This program is free software; you can redistribute it and/or modify
48+ * it under the terms of the GNU General Public License as published by
49+ * the Free Software Foundation; version 3.
50+ *
51+ * This program is distributed in the hope that it will be useful,
52+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
53+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
54+ * GNU General Public License for more details.
55+ *
56+ * You should have received a copy of the GNU General Public License
57+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
58+ */
59+
60+/*! \brief Page for Qml native test
61+*/
62+
63+import QtQuick 2.0
64+import Ubuntu.Components 1.1
65+import QtQuick.Layouts 1.1
66+import "ConfirmationLogic.js" as ConfirmationLogic
67+import Ubuntu.Content 1.1
68+
69+Page {
70+ id: qmlNativePage
71+ property var test: { "name": "", "description": "", "test_number": 0, "tests_count": 0}
72+ property var applicationVersion: ""
73+
74+ signal testDone(var test);
75+
76+ objectName: "qmlNativePage"
77+ title: i18n.tr("Test Description")
78+
79+ /* testingShell serves as the interface to the external world from the
80+ * qml-test. */
81+ Object {
82+ id: testingShell
83+ property string name: "Checkbox-touch qml confined shell"
84+ property alias pageStack: qmlNativePage.pageStack
85+ property string sessionDir: app.sessionDir
86+ function getTest() {
87+ return test;
88+ }
89+ }
90+
91+ Connections {
92+ target: ContentHub
93+ onImportRequested: {
94+ var resultFile = String(transfer.items[0].url).replace(
95+ 'file://', '');
96+ var xhr = new XMLHttpRequest;
97+ xhr.open("GET", resultFile);
98+ xhr.onreadystatechange = function() {
99+ if (xhr.readyState === XMLHttpRequest.DONE) {
100+ try {
101+ var result = JSON.parse(xhr.responseText);
102+ test["outcome"] = result["outcome"];
103+ testDone(test);
104+ } catch (x) {
105+ console.error("ERROR when parsing result json obj.")
106+ }
107+ }
108+ }
109+ xhr.send();
110+ transfer.finalize();
111+ }
112+ }
113+
114+ head {
115+ actions: [
116+ Action {
117+ id: skipAction
118+ iconName: "media-seek-forward"
119+ text: i18n.tr("Skip")
120+ onTriggered: {
121+ var confirmationOptions = {
122+ question : i18n.tr("Do you really want to skip this test?"),
123+ remember : true,
124+ }
125+ ConfirmationLogic.confirmRequest(qmlNativePage,
126+ confirmationOptions, function(res) {
127+ if (res) {
128+ test["outcome"] = "skip";
129+ testDone(test);
130+ }
131+ });
132+ }
133+ }
134+ ]
135+ }
136+
137+ ColumnLayout {
138+ id: body
139+ spacing: units.gu(3)
140+ anchors {
141+ fill: parent
142+ topMargin: units.gu(3)
143+ bottomMargin: units.gu(3)
144+ leftMargin: units.gu(1)
145+ rightMargin: units.gu(1)
146+ }
147+
148+ Label {
149+ fontSize: "large"
150+ Layout.fillWidth: true
151+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
152+ text: test["name"]
153+ font.bold: true
154+ }
155+
156+ Flickable {
157+ Layout.fillWidth: true
158+ Layout.fillHeight: true
159+ contentHeight: childrenRect.height
160+ flickableDirection: Flickable.VerticalFlick
161+ clip: true
162+ Label {
163+ fontSize: "medium"
164+ anchors.fill: parent
165+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
166+ text: test["description"]
167+ }
168+ }
169+
170+ ColumnLayout {
171+ id: busyIndicatorGroup
172+ visible: false
173+ Layout.fillWidth: true
174+ Layout.fillHeight: true
175+
176+ Label {
177+ fontSize: "large"
178+ Layout.fillWidth: true
179+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
180+ text: i18n.tr("Waiting for the test to finish")
181+ font.bold: true
182+ }
183+ }
184+
185+ LatchButton {
186+ objectName: "continueButton"
187+ color: UbuntuColors.green
188+ Layout.fillWidth: true
189+ text: i18n.tr("Continue")
190+ onClicked: {
191+ busyIndicatorGroup.visible = true;
192+ var appName = "com.ubuntu.checkbox_" + test["partial_id"] + "_" + applicationVersion + ".desktop";
193+ console.log("Launching " + appName);
194+ Qt.openUrlExternally("application:///" + appName);
195+ }
196+ }
197+ }
198+}
199
200=== added directory 'checkbox-touch/confinement'
201=== added file 'checkbox-touch/confinement/generate-confinement.py'
202--- checkbox-touch/confinement/generate-confinement.py 1970-01-01 00:00:00 +0000
203+++ checkbox-touch/confinement/generate-confinement.py 2015-07-23 11:16:26 +0000
204@@ -0,0 +1,111 @@
205+#!/usr/bin/env python3
206+# This file is part of Checkbox.
207+#
208+# Copyright 2015 Canonical Ltd.
209+# Written by:
210+# Maciej Kisielewski <maciej.kisielewski@canonical.com>
211+#
212+# Checkbox is free software: you can redistribute it and/or modify
213+# it under the terms of the GNU General Public License version 3,
214+# as published by the Free Software Foundation.
215+#
216+# Checkbox is distributed in the hope that it will be useful,
217+# but WITHOUT ANY WARRANTY; without even the implied warranty of
218+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
219+# GNU General Public License for more details.
220+#
221+# You should have received a copy of the GNU General Public License
222+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
223+import argparse
224+import os
225+import string
226+import sys
227+
228+
229+CONTENT_HUB = """{
230+ "destination": [ "documents"],
231+ "source": [ "documents"],
232+ "share": [ "documents"]
233+}"""
234+
235+APPARMOR = """{
236+ "policy_groups": [
237+ "networking",
238+ "webview",
239+ "content_exchange",
240+ "content_exchange_source"
241+ ],
242+ "policy_version": 1.2
243+}"""
244+
245+
246+DESKTOP = """[Desktop Entry]
247+Name=Checkbox-${partial_id}
248+Comment=${partial_id} - confined test from Checkbox
249+Exec=qmlscene -I lib/py/plainbox/data/plainbox-qml-modules/ -I providers/${provider_name}/data/ --checkbox-name=${full_checkbox_name} --job ../providers/${provider_name}/data/${qml_file} $$@ confinement/plainbox-confined-shell.qml
250+Icon=checkbox-touch.svg
251+Terminal=false
252+Type=Application
253+X-Ubuntu-Touch=true
254+"""
255+
256+HOOK = """
257+"${partial_id}": {
258+ "apparmor": "providers/${provider_name}/data/confined/${partial_id}.apparmor",
259+ "desktop": "providers/${provider_name}/data/confined/${partial_id}.desktop",
260+ "content-hub": "providers/${provider_name}/data/confined/${partial_id}-ch.json"
261+}
262+"""
263+
264+def generate_confinement(provider_name, partial_id, full_checkbox_name, qml_file):
265+ # generate content-hub file
266+ target_dir = os.path.join('data', 'confined')
267+ if not os.path.exists(target_dir):
268+ os.makedirs(target_dir)
269+
270+ content_hub_path = os.path.join(target_dir, partial_id + '-ch.json')
271+ with open(content_hub_path, "wt") as f:
272+ f.write(CONTENT_HUB)
273+
274+ # generate apparmor file
275+ apparmor_path = os.path.join(target_dir, partial_id + '.apparmor')
276+ with open(apparmor_path, "wt") as f:
277+ f.write(APPARMOR)
278+
279+
280+ # generate desktop file
281+ desktop_path = os.path.join(target_dir, partial_id + '.desktop')
282+ template = string.Template(DESKTOP)
283+ with open(desktop_path, "wt") as f:
284+ f.write(template.substitute(partial_id=partial_id, provider_name=provider_name, full_checkbox_name=full_checkbox_name, qml_file=qml_file))
285+
286+def generate_hook(provider_name, partial_id):
287+ return string.Template(HOOK).substitute(
288+ provider_name=provider_name, partial_id=partial_id)
289+
290+
291+
292+def main():
293+ parser = argparse.ArgumentParser(
294+ description="Generate confinement files for Checkbox")
295+ parser.add_argument('--checkbox_version', action='store', type=str)
296+ parser.add_argument('job_ids', nargs='+')
297+ args = parser.parse_args()
298+ checkbox_name = "com.ubuntu.checkbox_checkbox-touch_" + args.checkbox_version
299+
300+ # check if current dir looks like a provider - very dumb heuristic
301+ if not os.path.exists('manage.py'):
302+ sys.exit("Current directory doesn't look like a plainbox provider")
303+ provider_name = os.path.split(os.getcwd())[-1]
304+
305+ hooks = ''
306+ for job in args.job_ids:
307+ generate_confinement(provider_name, job, checkbox_name, job + '.qml')
308+ hooks += generate_hook(provider_name, job)
309+
310+ print("Add following hooks to your checkbox-touch.manifest:")
311+ print(hooks)
312+
313+
314+if __name__ == '__main__':
315+ main()
316
317=== added file 'checkbox-touch/confinement/plainbox-confined-shell.qml'
318--- checkbox-touch/confinement/plainbox-confined-shell.qml 1970-01-01 00:00:00 +0000
319+++ checkbox-touch/confinement/plainbox-confined-shell.qml 2015-07-23 11:16:26 +0000
320@@ -0,0 +1,130 @@
321+/*
322+ * This file is part of Checkbox
323+ *
324+ * Copyright 2015 Canonical Ltd.
325+ *
326+ * Authors:
327+ * - Maciej Kisielewski <maciej.kisielewski@canonical.com>
328+ *
329+ * This program is free software; you can redistribute it and/or modify
330+ * it under the terms of the GNU General Public License as published by
331+ * the Free Software Foundation; version 3.
332+ *
333+ * This program is distributed in the hope that it will be useful,
334+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
335+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
336+ * GNU General Public License for more details.
337+ *
338+ * You should have received a copy of the GNU General Public License
339+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
340+ */
341+/*! \brief QML standalone shell for confined tests
342+*/
343+import QtQuick 2.0
344+import Ubuntu.Components 0.1
345+import Ubuntu.Content 1.1
346+import io.thp.pyotherside 1.2
347+
348+MainView {
349+ id: mainView
350+ width: units.gu(100)
351+ height: units.gu(75)
352+
353+ // information and functionality passed to qml job component
354+ property var testingShell: {
355+ "name": "Checkbox-touch qml confined shell",
356+ "pageStack": pageStack
357+ }
358+ property var activeTransfer;
359+
360+ Arguments {
361+ id: args
362+ Argument {
363+ name: "job"
364+ help: "QML-native job to run"
365+ required: true
366+ valueNames: ["PATH"]
367+ }
368+ Argument {
369+ name: "checkbox-name"
370+ help: "Qualified name of Checkbox app to send results to"
371+ required: true
372+ valueNames: ["checkbox-touch-x.x.x"]
373+ }
374+ }
375+
376+ Loader {
377+ id: loader
378+ anchors.fill: parent
379+ onStatusChanged: {
380+ if (loader.status === Loader.Error) {
381+ testDone({'outcome': 'crash'});
382+ }
383+ }
384+ onLoaded: loader.item.testDone.connect(testDone)
385+ }
386+ Python {
387+ id: py
388+ Component.onCompleted: {
389+ addImportPath('./confinement/');
390+ importModule('os', function() {});
391+ importModule('plainbox_confined_shell', function() {
392+ loader.setSource(args.values['job'],
393+ {'testingShell': testingShell});
394+ });
395+ }
396+ }
397+
398+ function testDone(res) {
399+ loader.active = false;
400+ endPage.visible = true;
401+ var transfer = checkboxPeer.request()
402+ py.call("plainbox_confined_shell.obj_to_file", [res, 'com.ubuntu.checkbox', 'res.json'], function(resJson) {
403+ console.log('Result file availble @ ' + resJson);
404+ mainView.activeTransfer = checkboxPeer.request()
405+ mainView.activeTransfer.items = [ contentItemComponent.createObject(
406+ mainView, {url: resJson})]
407+ mainView.activeTransfer.state = ContentTransfer.Charged;
408+ });
409+
410+ }
411+ Page {
412+ id: endPage
413+ visible: false
414+ anchors.fill: parent
415+ Label {
416+ anchors.fill: parent
417+ text: i18n.tr("sending results - you should not see this page :-)")
418+ }
419+ }
420+ ContentPeer {
421+ id: checkboxPeer
422+ appId: args.values['checkbox-name']
423+ contentType: ContentType.Documents
424+ handler: ContentHandler.Destination
425+ }
426+ Connections {
427+ target: mainView.activeTransfer
428+ onStateChanged: {
429+ if (mainView.activeTransfer.state === ContentTransfer.Finalized) {
430+ var resultFile = String(mainView.activeTransfer.items[0].url).replace(
431+ 'file://', '');
432+ console.log("Transfer completed; removing result file");
433+ py.call('os.unlink', [resultFile], function() {
434+ Qt.quit();
435+ });
436+ }
437+ }
438+ }
439+
440+ Component {
441+ id: contentItemComponent
442+ ContentItem {
443+ }
444+ }
445+
446+ PageStack {
447+ id: pageStack
448+ }
449+
450+}
451
452=== added file 'checkbox-touch/confinement/plainbox_confined_shell.py'
453--- checkbox-touch/confinement/plainbox_confined_shell.py 1970-01-01 00:00:00 +0000
454+++ checkbox-touch/confinement/plainbox_confined_shell.py 2015-07-23 11:16:26 +0000
455@@ -0,0 +1,43 @@
456+# This file is part of Checkbox.
457+#
458+# Copyright 2015 Canonical Ltd.
459+# Written by:
460+# Maciej Kisielewski <maciej.kisielewski@canonical.com>
461+#
462+# Checkbox is free software: you can redistribute it and/or modify
463+# it under the terms of the GNU General Public License version 3,
464+# as published by the Free Software Foundation.
465+#
466+# Checkbox is distributed in the hope that it will be useful,
467+# but WITHOUT ANY WARRANTY; without even the implied warranty of
468+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
469+# GNU General Public License for more details.
470+#
471+# You should have received a copy of the GNU General Public License
472+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
473+
474+import json
475+import os
476+
477+
478+def obj_to_file(obj, consumer_name, filename):
479+ """
480+ Stringify object, write it to file and return path to the file.
481+
482+ :param object:
483+ Object to be stringified
484+ :param consumer_name:
485+ Name of the app that will consume the generated file
486+ :param filename:
487+ Name of the file to write to (just the basename)
488+ :returns:
489+ Path to the written file
490+ """
491+ s = json.dumps(obj)
492+ dir_path = os.path.join(os.environ['XDG_RUNTIME_DIR'], consumer_name)
493+ if not os.path.exists(dir_path):
494+ os.makedirs(dir_path)
495+ out_path = os.path.join(dir_path, filename)
496+ with open(out_path, "wt") as f:
497+ f.write(s)
498+ return out_path
499
500=== modified file 'checkbox-touch/manifest.json'
501--- checkbox-touch/manifest.json 2015-06-24 13:44:55 +0000
502+++ checkbox-touch/manifest.json 2015-07-23 11:16:26 +0000
503@@ -11,7 +11,7 @@
504 "maintainer": "Zygmunt Krynicki <zkrynicki@ubuntu.com>",
505 "name": "com.ubuntu.checkbox",
506 "title": "Checkbox",
507- "version": "1.2.0",
508+ "version": "1.2",
509 "x-source": {
510 "vcs-bzr": "lp:checkbox",
511 "vcs-bzr-revno": "checkbox-touch-v1.2.0"
512
513=== modified file 'checkbox-touch/py/checkbox_touch.py'
514--- checkbox-touch/py/checkbox_touch.py 2015-07-23 08:25:48 +0000
515+++ checkbox-touch/py/checkbox_touch.py 2015-07-23 11:16:26 +0000
516@@ -468,12 +468,14 @@
517 job.tr_verification() is not None else description,
518 "plugin": job.plugin,
519 "id": job.id,
520+ "partial_id": job.partial_id,
521 "user": job.user,
522 "qml_file": job.qml_file,
523 "start_time": time.time(),
524 "test_number": self.index,
525 "tests_count": len(self.context.state.run_list),
526- "command": job.command
527+ "command": job.command,
528+ "flags": job.get_flag_set()
529 }
530 if not job_state.can_start():
531 test["outcome"] = "skip"

Subscribers

People subscribed via source and target branches