Merge lp:~brendan-donegan/ubuntu/raring/checkbox/0.15.4 into lp:ubuntu/raring/checkbox

Proposed by Brendan Donegan
Status: Merged
Merged at revision: 1880
Proposed branch: lp:~brendan-donegan/ubuntu/raring/checkbox/0.15.4
Merge into: lp:ubuntu/raring/checkbox
Diff against target: 7070 lines (+2426/-817)
170 files modified
Vagrantfile (+14/-9)
checkbox/parsers/tests/test_submission.py (+2/-2)
checkbox/parsers/udevadm.py (+21/-0)
debian/changelog (+18/-0)
debian/po/ast.po (+2/-2)
debian/po/cs.po (+2/-2)
debian/po/de.po (+2/-2)
debian/po/en_AU.po (+2/-2)
debian/po/en_GB.po (+2/-2)
debian/po/es.po (+2/-2)
debian/po/fr.po (+2/-2)
debian/po/gl.po (+2/-2)
debian/po/he.po (+2/-2)
debian/po/hu.po (+2/-2)
debian/po/id.po (+2/-2)
debian/po/it.po (+2/-2)
debian/po/ja.po (+2/-2)
debian/po/nl.po (+2/-2)
debian/po/oc.po (+2/-2)
debian/po/pl.po (+2/-2)
debian/po/pt_BR.po (+2/-2)
debian/po/ro.po (+2/-2)
debian/po/ru.po (+2/-2)
debian/po/tr.po (+2/-2)
debian/po/uk.po (+2/-2)
debian/po/zh_CN.po (+2/-2)
debian/po/zh_TW.po (+2/-2)
jobs/graphics.txt.in (+2/-1)
jobs/input.txt.in (+1/-0)
jobs/optical.txt.in (+4/-4)
jobs/suspend.txt.in (+1/-1)
plainbox/docs/changelog.rst (+32/-0)
plainbox/docs/dev/architecture.rst (+337/-0)
plainbox/docs/dev/index.rst (+14/-0)
plainbox/docs/dev/intro.rst (+238/-0)
plainbox/docs/dev/reference.rst (+154/-0)
plainbox/docs/glossary.rst (+104/-0)
plainbox/docs/index.rst (+46/-6)
plainbox/docs/usage.rst (+69/-0)
plainbox/plainbox/__init__.py (+4/-4)
plainbox/plainbox/abc.py (+2/-4)
plainbox/plainbox/impl/__init__.py (+6/-4)
plainbox/plainbox/impl/box.py (+55/-14)
plainbox/plainbox/impl/checkbox.py (+9/-9)
plainbox/plainbox/impl/commands/__init__.py (+6/-6)
plainbox/plainbox/impl/commands/selftest.py (+6/-6)
plainbox/plainbox/impl/depmgr.py (+17/-15)
plainbox/plainbox/impl/exporter/__init__.py (+20/-9)
plainbox/plainbox/impl/exporter/json.py (+5/-6)
plainbox/plainbox/impl/exporter/rfc822.py (+6/-6)
plainbox/plainbox/impl/exporter/test_init.py (+16/-12)
plainbox/plainbox/impl/exporter/text.py (+6/-6)
plainbox/plainbox/impl/integration_tests.py (+20/-4)
plainbox/plainbox/impl/job.py (+18/-8)
plainbox/plainbox/impl/mock_job.py (+36/-0)
plainbox/plainbox/impl/resource.py (+7/-7)
plainbox/plainbox/impl/result.py (+42/-12)
plainbox/plainbox/impl/rfc822.py (+10/-7)
plainbox/plainbox/impl/runner.py (+76/-19)
plainbox/plainbox/impl/session.py (+115/-52)
plainbox/plainbox/impl/test_box.py (+110/-14)
plainbox/plainbox/impl/test_job.py (+3/-3)
plainbox/plainbox/impl/test_result.py (+22/-18)
plainbox/plainbox/impl/test_runner.py (+5/-5)
plainbox/plainbox/impl/test_session.py (+188/-14)
plainbox/plainbox/impl/testing_utils.py (+18/-6)
plainbox/plainbox/impl/utils.py.moved (+0/-58)
plainbox/plainbox/public.py (+2/-2)
plainbox/plainbox/testing_utils/__init__.py (+2/-4)
plainbox/plainbox/testing_utils/cwd.py (+2/-2)
plainbox/plainbox/testing_utils/io.py (+2/-2)
plainbox/plainbox/testing_utils/testcases.py (+3/-3)
plainbox/plainbox/tests.py (+2/-4)
plainbox/plainbox/vendor/__init__.py (+28/-0)
plainbox/plainbox/vendor/extcmd/__init__.py (+2/-2)
plainbox/setup.py (+3/-0)
po/ace.po (+4/-4)
po/af.po (+4/-4)
po/am.po (+4/-4)
po/ar.po (+4/-4)
po/ast.po (+4/-4)
po/az.po (+4/-4)
po/be.po (+4/-4)
po/bg.po (+4/-4)
po/bn.po (+4/-4)
po/bo.po (+4/-4)
po/br.po (+4/-4)
po/bs.po (+4/-4)
po/ca.po (+4/-4)
po/ca@valencia.po (+4/-4)
po/ckb.po (+4/-4)
po/cs.po (+4/-4)
po/cy.po (+4/-4)
po/da.po (+4/-4)
po/de.po (+5/-5)
po/dv.po (+4/-4)
po/el.po (+4/-4)
po/en_AU.po (+4/-4)
po/en_CA.po (+4/-4)
po/en_GB.po (+4/-4)
po/eo.po (+4/-4)
po/es.po (+4/-4)
po/et.po (+4/-4)
po/eu.po (+4/-4)
po/fa.po (+4/-4)
po/fi.po (+4/-4)
po/fr.po (+4/-4)
po/ga.po (+4/-4)
po/gd.po (+4/-4)
po/gl.po (+4/-4)
po/he.po (+4/-4)
po/hi.po (+4/-4)
po/hr.po (+4/-4)
po/hu.po (+4/-4)
po/hy.po (+4/-4)
po/id.po (+4/-4)
po/is.po (+4/-4)
po/it.po (+4/-4)
po/ja.po (+4/-4)
po/jbo.po (+4/-4)
po/ka.po (+4/-4)
po/kk.po (+4/-4)
po/km.po (+4/-4)
po/kn.po (+4/-4)
po/ko.po (+4/-4)
po/ku.po (+4/-4)
po/ky.po (+4/-4)
po/lt.po (+4/-4)
po/lv.po (+4/-4)
po/mk.po (+4/-4)
po/ml.po (+4/-4)
po/mr.po (+4/-4)
po/ms.po (+4/-4)
po/my.po (+4/-4)
po/nb.po (+4/-4)
po/nds.po (+4/-4)
po/ne.po (+4/-4)
po/nl.po (+4/-4)
po/nn.po (+4/-4)
po/oc.po (+4/-4)
po/pl.po (+4/-4)
po/ps.po (+4/-4)
po/pt.po (+4/-4)
po/pt_BR.po (+4/-4)
po/ro.po (+4/-4)
po/ru.po (+4/-4)
po/sd.po (+4/-4)
po/shn.po (+4/-4)
po/si.po (+4/-4)
po/sk.po (+4/-4)
po/sl.po (+4/-4)
po/sq.po (+4/-4)
po/sr.po (+4/-4)
po/sv.po (+4/-4)
po/ta.po (+4/-4)
po/te.po (+4/-4)
po/th.po (+4/-4)
po/tr.po (+22/-7)
po/ug.po (+4/-4)
po/uk.po (+4/-4)
po/ur.po (+4/-4)
po/uz.po (+4/-4)
po/vi.po (+4/-4)
po/zh_CN.po (+4/-4)
po/zh_HK.po (+4/-4)
po/zh_TW.po (+4/-4)
scripts/network_device_info (+56/-35)
scripts/udev_resource (+1/-1)
test (+4/-0)
test-in-vagrant.sh (+9/-1)
To merge this branch: bzr merge lp:~brendan-donegan/ubuntu/raring/checkbox/0.15.4
Reviewer Review Type Date Requested Status
Daniel Manrique (community) Approve
Review via email: mp+152211@code.launchpad.net

Description of the change

checkbox (0.15.4) raring; urgency=low

  * New upstream release (LP: #1152223)

  [ Daniel Manrique ]
  * Added pipefail option to a few jobs using ansi_parser (LP: #1131598)

  [ Jeff Marcom ]
  * jobs/input.txt.in Added job requirement for accelerometer test (LP: #1135832)

  [Sylvain Pineau]
  * scripts/network_device_info, scripts/udev_resource,
    checkbox/parsers/udevadm.py: Use udev to categorise network devices instead
    of lspci (LP: #1091633)

 -- Brendan Donegan <email address hidden> Thu, 07 Mar 2013 15:43:13 +0000

To post a comment you must log in.
Revision history for this message
Daniel Manrique (roadmr) wrote :

Thanks, merged.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Vagrantfile'
2--- Vagrantfile 2013-02-22 16:26:25 +0000
3+++ Vagrantfile 2013-03-07 16:10:38 +0000
4@@ -5,24 +5,29 @@
5
6 # Define a Ubuntu Server image (cloud) for the 12.10 release (quantal)
7 config.vm.define :quantal do |quantal_config|
8- quantal_config.vm.box = "quantal-cloud-amd64"
9- quantal_config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/quantal/current/quantal-server-cloudimg-amd64-vagrant-disk1.box"
10+ quantal_config.vm.box = "quantal-cloud-i386"
11+ quantal_config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/quantal/current/quantal-server-cloudimg-i386-vagrant-disk1.box"
12 end
13
14 # Define a Ubuntu Server image (cloud) for the 12.04 release (precise)
15 config.vm.define :precise do |precise_config|
16- precise_config.vm.box = "precise-cloud-amd64"
17- precise_config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/precise/current/precise-server-cloudimg-amd64-vagrant-disk1.box"
18+ precise_config.vm.box = "precise-cloud-i386"
19+ precise_config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/precise/current/precise-server-cloudimg-i386-vagrant-disk1.box"
20 end
21
22 # For debugging and later future GUI testing
23 # config.vm.boot_mode = :gui
24
25- # Update to have the latest packages
26- # Commented out for now, we don't really need it
27- # config.vm.provision :shell, :inline => "apt-get update && apt-get dist-upgrade"
28+ # Update to have the latest packages, this is needed because the image comes
29+ # with an old (and no longer working) apt cache and links to many packages no
30+ # longer work.
31+ config.vm.provision :shell, :inline => "apt-get update && apt-get dist-upgrade --yes"
32 # Install dependencies from native packages
33- config.vm.provision :shell, :inline => "apt-get install --yes python3-setuptools python3-yaml python3-lxml"
34+ config.vm.provision :shell, :inline => "apt-get install --yes python3-setuptools python3-lxml"
35+ # Install python3-mock so that we can create mock objects for testing
36+ config.vm.provision :shell, :inline => "apt-get install --yes python3-mock"
37+ # Install policykit-1 so that we have pkexec
38+ config.vm.provision :shell, :inline => "apt-get install --yes policykit-1"
39 # Install some checkbox script dependencies:
40 # Later on those could be installed on demand to test how we behave without
41 # them but for now that's good enough. Little by little...
42@@ -38,5 +43,5 @@
43 # Develop plainbox so that we have it in $PATH
44 config.vm.provision :shell, :inline => "cd /vagrant/plainbox/ && python3 setup.py develop"
45 # Create a cool symlink so that everyone knows where to go to
46- config.vm.provision :shell, :inline => "ln -s /vagrant /home/vagrant/checkbox"
47+ config.vm.provision :shell, :inline => "ln -fs /vagrant /home/vagrant/checkbox"
48 end
49
50=== modified file 'checkbox/parsers/tests/test_submission.py'
51--- checkbox/parsers/tests/test_submission.py 2012-10-12 21:39:01 +0000
52+++ checkbox/parsers/tests/test_submission.py 2013-03-07 16:10:38 +0000
53@@ -153,13 +153,13 @@
54 """Device states can be in the udev element."""
55 result = self.getResult("submission_udev.xml")
56 self.assertTrue("device_states" in result)
57- self.assertEquals(len(result["device_states"]), 77)
58+ self.assertEquals(len(result["device_states"]), 82)
59
60 def test_device_udevadm(self):
61 """Device states can be in a udevadm info element."""
62 result = self.getResult("submission_info_udevadm.xml")
63 self.assertTrue("device_states" in result)
64- self.assertEquals(len(result["device_states"]), 77)
65+ self.assertEquals(len(result["device_states"]), 82)
66
67 def test_device_dmidecode(self):
68 """Device states can be in a dmidecode info element."""
69
70=== modified file 'checkbox/parsers/udevadm.py'
71--- checkbox/parsers/udevadm.py 2012-11-12 09:59:00 +0000
72+++ checkbox/parsers/udevadm.py 2013-03-07 16:10:38 +0000
73@@ -92,8 +92,15 @@
74 @property
75 def category(self):
76 if "IFINDEX" in self._environment:
77+ if "DEVTYPE" in self._environment:
78+ devtype = self._environment["DEVTYPE"]
79+ if devtype == "wlan":
80+ return "WIRELESS"
81 return "NETWORK"
82
83+ if self.bus == "sound":
84+ return "AUDIO"
85+
86 if self.bus == "ieee80211":
87 return "WIRELESS"
88
89@@ -383,6 +390,9 @@
90 if self.driver == "floppy":
91 return "Platform Device"
92
93+ if "ID_MODEL_FROM_DATABASE" in self._environment:
94+ return self._environment["ID_MODEL_FROM_DATABASE"]
95+
96 return None
97
98 @property
99@@ -407,6 +417,17 @@
100 and "ID_VENDOR_ENC" in self._environment:
101 return decode_id(self._environment["ID_VENDOR_ENC"])
102
103+ if "ID_VENDOR_FROM_DATABASE" in self._environment:
104+ return self._environment["ID_VENDOR_FROM_DATABASE"]
105+
106+ return None
107+
108+ @property
109+ def interface(self):
110+ if self.category in ("NETWORK", "WIRELESS") \
111+ and "INTERFACE" in self._environment:
112+ return self._environment["INTERFACE"]
113+
114 return None
115
116
117
118=== modified file 'debian/changelog'
119--- debian/changelog 2013-02-22 16:41:55 +0000
120+++ debian/changelog 2013-03-07 16:10:38 +0000
121@@ -1,7 +1,25 @@
122+checkbox (0.15.4) raring; urgency=low
123+
124+ * New upstream release (LP: #1152223)
125+
126+ [ Daniel Manrique ]
127+ * Added pipefail option to a few jobs using ansi_parser (LP: #1131598)
128+
129+ [ Jeff Marcom ]
130+ * jobs/input.txt.in Added job requirement for accelerometer test (LP: #1135832)
131+
132+ [Sylvain Pineau]
133+ * scripts/network_device_info, scripts/udev_resource,
134+ checkbox/parsers/udevadm.py: Use udev to categorise network devices instead
135+ of lspci (LP: #1091633)
136+
137+ -- Brendan Donegan <brendan.donegan@ubuntu.com> Thu, 07 Mar 2013 15:43:13 +0000
138+
139 checkbox (0.15.3) raring; urgency=low
140
141 * New upstream release (LP: #1131801)
142
143+ [ Daniel Manrique ]
144 * scripts/pts_run: modified to output the full log from phoronix-test-suite
145 (LP: #1102819)
146
147
148=== modified file 'debian/po/ast.po'
149--- debian/po/ast.po 2013-02-22 16:26:25 +0000
150+++ debian/po/ast.po 2013-03-07 16:10:38 +0000
151@@ -14,8 +14,8 @@
152 "MIME-Version: 1.0\n"
153 "Content-Type: text/plain; charset=UTF-8\n"
154 "Content-Transfer-Encoding: 8bit\n"
155-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
156-"X-Generator: Launchpad (build 16491)\n"
157+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
158+"X-Generator: Launchpad (build 16506)\n"
159
160 #. Type: string
161 #. Description
162
163=== modified file 'debian/po/cs.po'
164--- debian/po/cs.po 2013-02-22 16:26:25 +0000
165+++ debian/po/cs.po 2013-03-07 16:10:38 +0000
166@@ -14,8 +14,8 @@
167 "MIME-Version: 1.0\n"
168 "Content-Type: text/plain; charset=UTF-8\n"
169 "Content-Transfer-Encoding: 8bit\n"
170-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
171-"X-Generator: Launchpad (build 16491)\n"
172+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
173+"X-Generator: Launchpad (build 16506)\n"
174
175 #. Type: string
176 #. Description
177
178=== modified file 'debian/po/de.po'
179--- debian/po/de.po 2013-02-22 16:26:25 +0000
180+++ debian/po/de.po 2013-03-07 16:10:38 +0000
181@@ -14,8 +14,8 @@
182 "MIME-Version: 1.0\n"
183 "Content-Type: text/plain; charset=UTF-8\n"
184 "Content-Transfer-Encoding: 8bit\n"
185-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
186-"X-Generator: Launchpad (build 16491)\n"
187+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
188+"X-Generator: Launchpad (build 16506)\n"
189
190 #. Type: string
191 #. Description
192
193=== modified file 'debian/po/en_AU.po'
194--- debian/po/en_AU.po 2013-02-22 16:26:25 +0000
195+++ debian/po/en_AU.po 2013-03-07 16:10:38 +0000
196@@ -14,8 +14,8 @@
197 "MIME-Version: 1.0\n"
198 "Content-Type: text/plain; charset=UTF-8\n"
199 "Content-Transfer-Encoding: 8bit\n"
200-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
201-"X-Generator: Launchpad (build 16491)\n"
202+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
203+"X-Generator: Launchpad (build 16506)\n"
204
205 #. Type: string
206 #. Description
207
208=== modified file 'debian/po/en_GB.po'
209--- debian/po/en_GB.po 2013-02-22 16:26:25 +0000
210+++ debian/po/en_GB.po 2013-03-07 16:10:38 +0000
211@@ -14,8 +14,8 @@
212 "MIME-Version: 1.0\n"
213 "Content-Type: text/plain; charset=UTF-8\n"
214 "Content-Transfer-Encoding: 8bit\n"
215-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
216-"X-Generator: Launchpad (build 16491)\n"
217+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
218+"X-Generator: Launchpad (build 16506)\n"
219
220 #. Type: string
221 #. Description
222
223=== modified file 'debian/po/es.po'
224--- debian/po/es.po 2013-02-22 16:26:25 +0000
225+++ debian/po/es.po 2013-03-07 16:10:38 +0000
226@@ -14,8 +14,8 @@
227 "MIME-Version: 1.0\n"
228 "Content-Type: text/plain; charset=UTF-8\n"
229 "Content-Transfer-Encoding: 8bit\n"
230-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
231-"X-Generator: Launchpad (build 16491)\n"
232+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
233+"X-Generator: Launchpad (build 16506)\n"
234
235 #. Type: string
236 #. Description
237
238=== modified file 'debian/po/fr.po'
239--- debian/po/fr.po 2013-02-22 16:26:25 +0000
240+++ debian/po/fr.po 2013-03-07 16:10:38 +0000
241@@ -14,8 +14,8 @@
242 "MIME-Version: 1.0\n"
243 "Content-Type: text/plain; charset=UTF-8\n"
244 "Content-Transfer-Encoding: 8bit\n"
245-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
246-"X-Generator: Launchpad (build 16491)\n"
247+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
248+"X-Generator: Launchpad (build 16506)\n"
249
250 #. Type: string
251 #. Description
252
253=== modified file 'debian/po/gl.po'
254--- debian/po/gl.po 2013-02-22 16:26:25 +0000
255+++ debian/po/gl.po 2013-03-07 16:10:38 +0000
256@@ -14,8 +14,8 @@
257 "MIME-Version: 1.0\n"
258 "Content-Type: text/plain; charset=UTF-8\n"
259 "Content-Transfer-Encoding: 8bit\n"
260-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
261-"X-Generator: Launchpad (build 16491)\n"
262+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
263+"X-Generator: Launchpad (build 16506)\n"
264
265 #. Type: string
266 #. Description
267
268=== modified file 'debian/po/he.po'
269--- debian/po/he.po 2013-02-22 16:26:25 +0000
270+++ debian/po/he.po 2013-03-07 16:10:38 +0000
271@@ -14,8 +14,8 @@
272 "MIME-Version: 1.0\n"
273 "Content-Type: text/plain; charset=UTF-8\n"
274 "Content-Transfer-Encoding: 8bit\n"
275-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
276-"X-Generator: Launchpad (build 16491)\n"
277+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
278+"X-Generator: Launchpad (build 16506)\n"
279
280 #. Type: string
281 #. Description
282
283=== modified file 'debian/po/hu.po'
284--- debian/po/hu.po 2013-02-22 16:26:25 +0000
285+++ debian/po/hu.po 2013-03-07 16:10:38 +0000
286@@ -14,8 +14,8 @@
287 "MIME-Version: 1.0\n"
288 "Content-Type: text/plain; charset=UTF-8\n"
289 "Content-Transfer-Encoding: 8bit\n"
290-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
291-"X-Generator: Launchpad (build 16491)\n"
292+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
293+"X-Generator: Launchpad (build 16506)\n"
294
295 #. Type: string
296 #. Description
297
298=== modified file 'debian/po/id.po'
299--- debian/po/id.po 2013-02-22 16:26:25 +0000
300+++ debian/po/id.po 2013-03-07 16:10:38 +0000
301@@ -14,8 +14,8 @@
302 "MIME-Version: 1.0\n"
303 "Content-Type: text/plain; charset=UTF-8\n"
304 "Content-Transfer-Encoding: 8bit\n"
305-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
306-"X-Generator: Launchpad (build 16491)\n"
307+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
308+"X-Generator: Launchpad (build 16506)\n"
309
310 #. Type: string
311 #. Description
312
313=== modified file 'debian/po/it.po'
314--- debian/po/it.po 2013-02-22 16:26:25 +0000
315+++ debian/po/it.po 2013-03-07 16:10:38 +0000
316@@ -14,8 +14,8 @@
317 "MIME-Version: 1.0\n"
318 "Content-Type: text/plain; charset=UTF-8\n"
319 "Content-Transfer-Encoding: 8bit\n"
320-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
321-"X-Generator: Launchpad (build 16491)\n"
322+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
323+"X-Generator: Launchpad (build 16506)\n"
324
325 #. Type: string
326 #. Description
327
328=== modified file 'debian/po/ja.po'
329--- debian/po/ja.po 2013-02-22 16:26:25 +0000
330+++ debian/po/ja.po 2013-03-07 16:10:38 +0000
331@@ -14,8 +14,8 @@
332 "MIME-Version: 1.0\n"
333 "Content-Type: text/plain; charset=UTF-8\n"
334 "Content-Transfer-Encoding: 8bit\n"
335-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
336-"X-Generator: Launchpad (build 16491)\n"
337+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
338+"X-Generator: Launchpad (build 16506)\n"
339
340 #. Type: string
341 #. Description
342
343=== modified file 'debian/po/nl.po'
344--- debian/po/nl.po 2013-02-22 16:26:25 +0000
345+++ debian/po/nl.po 2013-03-07 16:10:38 +0000
346@@ -14,8 +14,8 @@
347 "MIME-Version: 1.0\n"
348 "Content-Type: text/plain; charset=UTF-8\n"
349 "Content-Transfer-Encoding: 8bit\n"
350-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
351-"X-Generator: Launchpad (build 16491)\n"
352+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
353+"X-Generator: Launchpad (build 16506)\n"
354
355 #. Type: string
356 #. Description
357
358=== modified file 'debian/po/oc.po'
359--- debian/po/oc.po 2013-02-22 16:26:25 +0000
360+++ debian/po/oc.po 2013-03-07 16:10:38 +0000
361@@ -14,8 +14,8 @@
362 "MIME-Version: 1.0\n"
363 "Content-Type: text/plain; charset=UTF-8\n"
364 "Content-Transfer-Encoding: 8bit\n"
365-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
366-"X-Generator: Launchpad (build 16491)\n"
367+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
368+"X-Generator: Launchpad (build 16506)\n"
369
370 #. Type: string
371 #. Description
372
373=== modified file 'debian/po/pl.po'
374--- debian/po/pl.po 2013-02-22 16:26:25 +0000
375+++ debian/po/pl.po 2013-03-07 16:10:38 +0000
376@@ -14,8 +14,8 @@
377 "MIME-Version: 1.0\n"
378 "Content-Type: text/plain; charset=UTF-8\n"
379 "Content-Transfer-Encoding: 8bit\n"
380-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
381-"X-Generator: Launchpad (build 16491)\n"
382+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
383+"X-Generator: Launchpad (build 16506)\n"
384
385 #. Type: string
386 #. Description
387
388=== modified file 'debian/po/pt_BR.po'
389--- debian/po/pt_BR.po 2013-02-22 16:26:25 +0000
390+++ debian/po/pt_BR.po 2013-03-07 16:10:38 +0000
391@@ -14,8 +14,8 @@
392 "MIME-Version: 1.0\n"
393 "Content-Type: text/plain; charset=UTF-8\n"
394 "Content-Transfer-Encoding: 8bit\n"
395-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
396-"X-Generator: Launchpad (build 16491)\n"
397+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
398+"X-Generator: Launchpad (build 16506)\n"
399
400 #. Type: string
401 #. Description
402
403=== modified file 'debian/po/ro.po'
404--- debian/po/ro.po 2013-02-22 16:26:25 +0000
405+++ debian/po/ro.po 2013-03-07 16:10:38 +0000
406@@ -14,8 +14,8 @@
407 "MIME-Version: 1.0\n"
408 "Content-Type: text/plain; charset=UTF-8\n"
409 "Content-Transfer-Encoding: 8bit\n"
410-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
411-"X-Generator: Launchpad (build 16491)\n"
412+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
413+"X-Generator: Launchpad (build 16506)\n"
414
415 #. Type: string
416 #. Description
417
418=== modified file 'debian/po/ru.po'
419--- debian/po/ru.po 2013-02-22 16:26:25 +0000
420+++ debian/po/ru.po 2013-03-07 16:10:38 +0000
421@@ -14,8 +14,8 @@
422 "MIME-Version: 1.0\n"
423 "Content-Type: text/plain; charset=UTF-8\n"
424 "Content-Transfer-Encoding: 8bit\n"
425-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
426-"X-Generator: Launchpad (build 16491)\n"
427+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
428+"X-Generator: Launchpad (build 16506)\n"
429
430 #. Type: string
431 #. Description
432
433=== modified file 'debian/po/tr.po'
434--- debian/po/tr.po 2013-02-22 16:26:25 +0000
435+++ debian/po/tr.po 2013-03-07 16:10:38 +0000
436@@ -14,8 +14,8 @@
437 "MIME-Version: 1.0\n"
438 "Content-Type: text/plain; charset=UTF-8\n"
439 "Content-Transfer-Encoding: 8bit\n"
440-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
441-"X-Generator: Launchpad (build 16491)\n"
442+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
443+"X-Generator: Launchpad (build 16506)\n"
444
445 #. Type: string
446 #. Description
447
448=== modified file 'debian/po/uk.po'
449--- debian/po/uk.po 2013-02-22 16:26:25 +0000
450+++ debian/po/uk.po 2013-03-07 16:10:38 +0000
451@@ -14,8 +14,8 @@
452 "MIME-Version: 1.0\n"
453 "Content-Type: text/plain; charset=UTF-8\n"
454 "Content-Transfer-Encoding: 8bit\n"
455-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
456-"X-Generator: Launchpad (build 16491)\n"
457+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
458+"X-Generator: Launchpad (build 16506)\n"
459
460 #. Type: string
461 #. Description
462
463=== modified file 'debian/po/zh_CN.po'
464--- debian/po/zh_CN.po 2013-02-22 16:26:25 +0000
465+++ debian/po/zh_CN.po 2013-03-07 16:10:38 +0000
466@@ -14,8 +14,8 @@
467 "MIME-Version: 1.0\n"
468 "Content-Type: text/plain; charset=UTF-8\n"
469 "Content-Transfer-Encoding: 8bit\n"
470-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
471-"X-Generator: Launchpad (build 16491)\n"
472+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
473+"X-Generator: Launchpad (build 16506)\n"
474
475 #. Type: string
476 #. Description
477
478=== modified file 'debian/po/zh_TW.po'
479--- debian/po/zh_TW.po 2013-02-22 16:26:25 +0000
480+++ debian/po/zh_TW.po 2013-03-07 16:10:38 +0000
481@@ -14,8 +14,8 @@
482 "MIME-Version: 1.0\n"
483 "Content-Type: text/plain; charset=UTF-8\n"
484 "Content-Transfer-Encoding: 8bit\n"
485-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
486-"X-Generator: Launchpad (build 16491)\n"
487+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
488+"X-Generator: Launchpad (build 16506)\n"
489
490 #. Type: string
491 #. Description
492
493=== modified file 'jobs/graphics.txt.in'
494--- jobs/graphics.txt.in 2012-12-12 12:59:22 +0000
495+++ jobs/graphics.txt.in 2013-03-07 16:10:38 +0000
496@@ -170,7 +170,7 @@
497 plugin: shell
498 name: graphics/screenshot
499 requires: package.name == 'fswebcam'
500-command: camera_test still --device=/dev/external_webcam -f ${CHECKBOX_DATA}/screenshot.jpg -q 2>&1 | ansi_parser
501+command: set -o pipefail; camera_test still --device=/dev/external_webcam -f ${CHECKBOX_DATA}/screenshot.jpg -q 2>&1 | ansi_parser
502 _description:
503 PURPOSE:
504 Take a screengrab of the current screen (logged on Unity desktop)
505@@ -191,6 +191,7 @@
506 command:
507 dbus-launch gsettings set org.gnome.totem repeat true
508 totem --fullscreen ${CHECKBOX_SHARE}/data/video/Ogg_Theora_Video.ogv 2>/dev/null &
509+ set -o pipefail
510 sleep 15 && camera_test still --device=/dev/external_webcam -f ${CHECKBOX_DATA}/screenshot_fullscreen_video.jpg -q 2>&1 | ansi_parser
511 sleep 5 && totem --quit 2>/dev/null
512 dbus-launch gsettings set org.gnome.totem repeat false
513
514=== modified file 'jobs/input.txt.in'
515--- jobs/input.txt.in 2013-02-22 16:26:25 +0000
516+++ jobs/input.txt.in 2013-03-07 16:10:38 +0000
517@@ -44,6 +44,7 @@
518 plugin: user-interact
519 name: input/accelerometer
520 user: root
521+requires: module.name in ['hdaps', 'hp_accel']
522 command: accelerometer_test -m
523 _description:
524 PURPOSE:
525
526=== modified file 'jobs/optical.txt.in'
527--- jobs/optical.txt.in 2012-10-11 20:48:55 +0000
528+++ jobs/optical.txt.in 2013-03-07 16:10:38 +0000
529@@ -58,7 +58,7 @@
530 name: optical/cdrom-write_`ls /sys$path/block`
531 requires: device.path == "$path"
532 user: root
533- command: optical_write_test /dev/`ls /sys$path/block` | ansi_parser
534+ command: set -o pipefail; optical_write_test /dev/`ls /sys$path/block` | ansi_parser
535 description:
536 PURPOSE:
537 This test will check your system's $product CD writing capabilities. This test requires a blank CD-R or CD+R. If you do not have a blank disk, skip this test.
538@@ -80,7 +80,7 @@
539 name: optical/cdrom-write-automated_`ls /sys$path/block`
540 requires: device.path == "$path"
541 user: root
542- command: optical_write_test /dev/`ls /sys$path/block` | ansi_parser
543+ command: set -o pipefail; optical_write_test /dev/`ls /sys$path/block` | ansi_parser
544 description:
545 This is an automated version of optical/cdrom-write. It assumes you have already inserted a data CD into your optical drive prior to running Checkbox.
546 EOF
547@@ -116,7 +116,7 @@
548 name: optical/dvd-write_`ls /sys$path/block`
549 requires: device.path == "$path"
550 user: root
551- command: optical_write_test /dev/`ls /sys$path/block` | ansi_parser
552+ command: set -o pipefail; optical_write_test /dev/`ls /sys$path/block` | ansi_parser
553 description:
554 PURPOSE:
555 This test will check your system's $product writing capabilities. This test requires a blank DVD-R or DVD+R. If you do not have a blank DVD disk, skip this test.
556@@ -140,7 +140,7 @@
557 name: optical/dvd-write-automated_`ls /sys$path/block`
558 requires: device.path == "$path"
559 user: root
560- command: optical_write_test /dev/`ls /sys$path/block` | ansi_parser
561+ command: set -o pipefail; optical_write_test /dev/`ls /sys$path/block` | ansi_parser
562 description:
563 This is an automated version of optical/dvd-write. It assumes you have already inserted a data DVD into your optical drive prior to running Checkbox.
564 EOF
565
566=== modified file 'jobs/suspend.txt.in'
567--- jobs/suspend.txt.in 2013-01-30 21:43:05 +0000
568+++ jobs/suspend.txt.in 2013-03-07 16:10:38 +0000
569@@ -790,7 +790,7 @@
570 name: suspend/screenshot_after_suspend
571 depends: suspend/suspend_advanced_auto
572 requires: package.name == 'fswebcam'
573-command: camera_test still --device=/dev/external_webcam -f ${CHECKBOX_DATA}/screenshot_after_suspend.jpg -q 2>&1 | ansi_parser
574+command: set -o pipefail; camera_test still --device=/dev/external_webcam -f ${CHECKBOX_DATA}/screenshot_after_suspend.jpg -q 2>&1 | ansi_parser
575 _description:
576 PURPOSE:
577 Take a screengrab of the current screen after suspend (logged on Unity desktop)
578
579=== added file 'plainbox/docs/changelog.rst'
580--- plainbox/docs/changelog.rst 1970-01-01 00:00:00 +0000
581+++ plainbox/docs/changelog.rst 2013-03-07 16:10:38 +0000
582@@ -0,0 +1,32 @@
583+ChangeLog
584+=========
585+
586+.. note::
587+ This changelog contains only a summary of changes. For a more accurate
588+ accounting of development history please inspect the source history
589+ directly.
590+
591+PlainBox 0.3 (Unreleased)
592+^^^^^^^^^^^^^^^^^^^^^^^^^
593+
594+* Added support for all job types (manual, user-interact, user-verify, attachment, local)
595+* Added support for running as another user
596+* Added support for creating session checkpoints and resuming testing across reboots
597+* Added support for exporting test results to JSON, plain text and XML
598+* Added support for handling binary data (eg, binary attachments)
599+* Added support for using sub-commands to the main plainbox executable
600+* Added documentation to the project
601+* Numerous internal re-factorings, changes and improvements.
602+* Improved unit and integration testing coverage
603+
604+PlainBox 0.2
605+^^^^^^^^^^^^
606+
607+* Last release made from the standalone github tree.
608+* Added support for discovering dependencies and automatic dependency
609+ resolution (for both job dependencies and resource dependencies)
610+
611+PlainBox 0.1
612+^^^^^^^^^^^^
613+
614+* Initial release
615
616=== added directory 'plainbox/docs/dev'
617=== added file 'plainbox/docs/dev/architecture.rst'
618--- plainbox/docs/dev/architecture.rst 1970-01-01 00:00:00 +0000
619+++ plainbox/docs/dev/architecture.rst 2013-03-07 16:10:38 +0000
620@@ -0,0 +1,337 @@
621+PlainBox Architecture
622+=====================
623+
624+This document explains the architecture of PlainBox internals. It should be
625+always up-to-date and accurate to the extent of the scope of this overview.
626+
627+General design considerations
628+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
629+
630+PlainBox is a reimplementation of CheckBox that replaces a reactor / event /
631+plugin architecture with a monolithic core and tightly integrated components.
632+
633+The implementation models a few of the externally-visible concepts such as
634+jobs, resources and resource programs but also has some additional design that
635+was not present in CheckBox before.
636+
637+The goal of the rewrite is to provide the right model and APIs for user
638+interfaces in order to build the kind of end-user solution that we could not
639+build with CheckBox.
640+
641+This is expressed by additional functionality that is there only to provide the
642+higher layers with the right data (failure reason, descriptions, etc.). The
643+code is also intended to be highly testable. Test coverage at the time of
644+writing this document was exceeding 80%
645+
646+The core requirement for the current phase of PlainBox development is feature
647+parity with CheckBox and gradual shift from one to another in the daily
648+responsibilities of the Hardware Certification team. Currently PlainBox
649+implements a large chunk of core / essential features from CheckBox. While not
650+all features are present the core is considered almost feature complete at this
651+stage.
652+
653+Application Skeleton
654+^^^^^^^^^^^^^^^^^^^^
655+
656+This skeleton represents a typical application based on PlainBox. It enumerates
657+the essential parts of the APIs from the point of view of an application
658+developer.
659+
660+1. Instantiate :class:`plainbox.impl.checkbox.CheckBox` then call
661+ :meth:`plainbox.impl.checkbox.CheckBox.get_builtin_jobs()` to discover all
662+ known jobs. In the future this might be replaced by a step that obtains jobs
663+ from a named provider.
664+
665+3. Instantiate :class:`plainbox.impl.runner.JobRunner` so that we can run jobs
666+
667+4. Instantiate :class:`plainbox.impl.session.SessionState` so that we can keep
668+ track of application state.
669+
670+ - Potentially restore an earlier, interrupted, testing session by calling
671+ :meth:`plainbox.impl.session.SessionState.restore()`
672+
673+ - Potentially remove an earlier, interrupted, testing session by calling
674+ :meth:`plainbox.impl.session.SessionState.discard()`
675+
676+ - Potentially start a new test session by calling
677+ :meth:`plainbox.impl.session.SessionState.open()`
678+
679+5. Allow the user to select jobs that should be executed and update session
680+ state by calling
681+ :meth:`plainbox.impl.session.SessionState.update_desired_job_list()`
682+
683+6. For each job in :attr:`plainbox.impl.SessionState.run_list`:
684+
685+ 1. Check if we want to run the job (if we have a result for it from previous
686+ runs) or if we must run it (for jobs that cannot be persisted across
687+ suspend)
688+
689+ 2. Check if the job can be started by looking at
690+ :meth:`plainbox.impl.session.JobState.can_start()`
691+
692+ - optionally query for additional data on why a job cannot be started and
693+ present that to the user.
694+
695+ - optionally abort the sequence and go to step 5 or the outer loop.
696+
697+ 3. Call :meth:`plainbox.impl.runner.JobRunner.run_job()` with the current
698+ job and store the result.
699+
700+ - optionally ask the user to perform some manipulation
701+
702+ - optionally ask the user to qualify the outcome
703+
704+ - optionally ask the user for additional comments
705+
706+ 4. Call :meth:`plainbox.impl.session.SessionState.update_job_result()` to
707+ update readiness of jobs that depend on the outcome or output of current
708+ job.
709+
710+ 5. Call :meth:`plainbox.impl.session.SessionState.checkpoint()` to ensure
711+ that testing can resume after system crash or shutdown.
712+
713+7. Instantiate the selected state exporter, for example
714+ :class:`plainbox.impl.exporters.json.JSONSessionStateExporter` so that we
715+ can use it to save test results.
716+
717+ - optionally pass configuration options to customize the subset and the
718+ presentation of the session state
719+
720+8. Call
721+ :meth:`plainbox.impl.exporters.SessionStateExporterBase.get_session_data_subset()`
722+ followed by :meth:`plainbox.impl.exporters.SessionStateExporterBase.dump()`
723+ to save results to a file.
724+
725+9. Call :meth:`plainbox.impl.session.SessionState.close()` to remove any
726+ nonvolatile temporary storage that was needed for the session.
727+
728+Essential classes
729+=================
730+
731+:class:`~plainbox.impl.session.SessionState`
732+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
733+
734+Class representing all state needed during a single program session.
735+
736+Usage
737+-----
738+
739+The general idea is that you feed the session with a list of known jobs and
740+a subset of jobs that you want to run and in return get an ordered list of
741+jobs to run.
742+
743+It is expected that the user will select / deselect and run jobs. This
744+class can react to both actions by recomputing the dependency graph and
745+updating the read states accordingly.
746+
747+As the user runs subsequent jobs the results of those jobs are exposed to
748+the session with :meth:`update_job_result()`. This can cause subsequent
749+jobs to become available (not inhibited by anything). Note that there is no
750+notification of changes at this time.
751+
752+The session does almost nothing by itself, it learns about everything by
753+observing job results coming from the job runner
754+(:class:`plainbox.impl.runner.JobRunner`) that applications need to
755+instantiate.
756+
757+Suspend and resume
758+------------------
759+
760+The session can save check-point data after each job is executed. This
761+allows the system to survive and continue after a catastrophic failure
762+(broken suspend, power failure) or continue across tests that require the
763+machine to reboot.
764+
765+.. todo::
766+
767+ Create a section on suspend/resume design
768+
769+Implementation notes
770+--------------------
771+
772+Internally it ties into :class:`plainbox.impl.depmgr.DependencySolver` for
773+resolving dependencies. The way the session objects are used allows them to
774+return various problems back to the UI level - those are all the error
775+classes from :mod:`plainbox.impl.depmgr`:
776+
777+ - :class:`plainbox.impl.depmgr.DependencyCycleError`
778+
779+ - :class:`plainbox.impl.depmgr.DependencyDuplicateError`
780+
781+ - :class:`plainbox.impl.depmgr.DependencyMissingError`
782+
783+Normally *none* of those errors should ever happen, they are only provided
784+so that we don't choke when a problem really happens. Everything is checked
785+and verified early before starting a job so typical unit and integration
786+testing should capture broken job definitions (for example, with cyclic
787+dependencies) being added to the repository.
788+
789+Implementation issues
790+---------------------
791+
792+There are two issues that are known at this time:
793+
794+* There is too much checkbox-specific knowledge which really belongs
795+ elsewhere. We are working to remove that so that non-checkbox jobs
796+ can be introduced later. There is a branch in progress that entirely
797+ removes that and moves it to a new concept called SessionController.
798+ In that design the session delegates understanding of results to a
799+ per-job session controller and exposes some APIs to alter the state
800+ that was previously internal (most notably a way to add new jobs and
801+ resources).
802+
803+* The way jobs are currently selected is unfortunate because of local jobs
804+ that can add new jobs to the system. This causes considerable complexity
805+ at the application level where the application must check if each
806+ executed job is a 'local' job and re-compute the desired_job_list. This
807+ should be replaced by a matcher function that can be passed to
808+ SessionState once so that desired_job_list is re-evaluated internally
809+ whenever job_list changes.
810+
811+
812+:class:`~plainbox.impl.job.JobDefinition`
813+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
814+
815+:term:`CheckBox` has a concept of a :term:`job`. Jobs are named units of
816+testing work that can be executed. Typical jobs range from automated CPU power
817+management checks, BIOS tests, semi-automated peripherals testing to all manual
818+validation by following a script (intended for humans).
819+
820+Jobs are distributed in plain text files, formated as a loose RFC822 documents
821+where typically a single text file contains a few dozen different jobs that
822+belong to one topic, for example, all bluetooth tests.
823+
824+Tests have a number of properties that will not be discussed in detail here,
825+they are all documented in :class:`plainbox.impl.job.JobDefinition`. From the
826+architecture point of view the four essential properties of a job are *name*,
827+*plugin* and *requires* and *depends*. Those are discussed in detail below.
828+
829+JobDefinition.name
830+------------------
831+
832+The *name* field must be unique and is referred to by other parts of the system
833+(such as whitelists). Typically jobs follow a simple naming pattern
834+'category/detail', eg, 'networking/modem_connection'. The name must be _unique_
835+and this is enforced by the core.
836+
837+JobDefinition.plugin
838+--------------------
839+
840+The *plugin* field is an archaism from CheckBox and a misnomer (as PlainBox
841+does not have any plugins). In the CheckBox architecture it would instruct the
842+core which plugin should process that job. In PlainBox it is a way to encode
843+what type of a job is being processed. There is a finite set of types that are
844+documented below.
845+
846+plugin == "shell"
847+#################
848+
849+This value is used for fully automated jobs. Everything the job needs to do is
850+automated (preparation, execution, verification) and fully handled by the
851+command that is associated with a job.
852+
853+plugin == "manual"
854+##################
855+
856+This value is used for fully manual jobs. It has no special handling in the core
857+apart from requiring a human-provided outcome (pass/fail classification)
858+
859+plugin == "local"
860+#################
861+
862+This value is used for special job generator jobs. The output of such jobs is
863+interpreted as additional jobs and is identical in effect to loading such jobs
864+from a job definition file.
865+
866+There are two practical uses for such jobs:
867+
868+* Some local jobs are used to generate a number of jobs for each object.
869+ This is needed where the tested machine may have a number of such objects
870+ and each requires unique testing. A good example is a computer where all
871+ network tests are explicitly "instantiated" for each network card
872+ present.
873+
874+ This is a valid use case but is rather unfortunate for architecture of
875+ PlainBox and there is a desire to replace it with equally-expressive
876+ pattern jobs. The advantage is that unlike local jobs (which cannot be
877+ "discovered" without enduring any potential side effects that may be
878+ caused by the job script command) pattern jobs would allow the core to
879+ determine the names of jobs that can be generated and, for example,
880+ automatically determine that a pattern job needs to be executed as a
881+ dependency of a phantom (yet undetermined) job with a given name.
882+
883+ The solution with "pattern" jobs may be executed in future phases of
884+ PlainBox development. Currently there is no support for that at all.
885+
886+ Currently PlainBox cannot determine job dependencies across local jobs.
887+ That is, unless a local job is explicitly requested (in the desired job
888+ list) PlainBox will not be able to run a job that is generated by a local
889+ job at all and will treat it as if that job never existed.
890+
891+* Some local jobs are used to create a form of informal "category".
892+ Typically all such jobs have a leading and trailing double underscore,
893+ for example '__audio__'. This is currently being used by CheckBox for
894+ building a hierarchical tree of tests that the user may select.
895+
896+ Since this has the same flaws as described above (for pattern jobs) it
897+ will likely be replaced by an explicit category field that can be
898+ specified each job.
899+
900+plugin == "resource"
901+####################
902+
903+This value is used for special "data" or "environment" jobs. Their output is
904+parsed as a list of RFC822 records and is kept by the core during a testing session.
905+
906+They are primarily used to determine if a given job can be started. For
907+example, a particular bluetooth test may use the _requires_ field to indicate
908+that it depends (via a resource dependency) on a job that enumerates devices
909+and that one of those devices must be a bluetooth device.
910+
911+plugin == "user-interact"
912+#########################
913+
914+For all intents and purposes it is equivalent to "manual". The actual
915+difference is that a user is expected to perform some physical manipulation
916+before an automated test.
917+
918+plugin == "user-verify"
919+#######################
920+
921+For all intents and purposes it is equivalent to "manual". The actual
922+difference is that a user is expected to perform manual verification after an
923+automated test.
924+
925+JobDefinition.depends
926+---------------------
927+
928+The *depends* field is used to express dependencies between two jobs. If job A
929+has depends on job B then A cannot start if B is not both finished and
930+successful. PlainBox understands this dependency and can automatically sort and
931+execute jobs in proper order. In many places of the code this is referred to as
932+a "direct dependency" (in contrast to "resource dependency")
933+
934+The actual syntax is not strictly specified, PlainBox interprets this field as
935+a list of tokens delimited by comma or any whitespace (including newlines).
936+
937+A job may depend on any number of other jobs. There are a number of failure
938+modes associated with this feature, all of which are detected and handled by
939+PlainBox. Typically they only arise when during CheckBox job development
940+(editing actual job files) and are always a sign of a human error. No released
941+version of CheckBox or PlainBox should ever encounter any of those issues.
942+
943+The actual problems are:
944+
945+* dependency cycles, where job either directly or indirectly depends on
946+ itself
947+
948+* missing dependencies where some job refers to a job that is not defined
949+ anywhere.
950+
951+* duplicate jobs where two jobs with the same name (but different
952+ definition) are being introduced to the system.
953+
954+In all of those cases the core removes the offending job and tries to work
955+regardless of the problem. This is intended more as a development aid rather
956+than a reliability feature as no released versions of either project should
957+cause this problem.
958
959=== added file 'plainbox/docs/dev/index.rst'
960--- plainbox/docs/dev/index.rst 1970-01-01 00:00:00 +0000
961+++ plainbox/docs/dev/index.rst 2013-03-07 16:10:38 +0000
962@@ -0,0 +1,14 @@
963+Developers
964+==========
965+
966+The PlainBox project hopes to be a friendly developer environment. We invested
967+in a lot of tools to make your life easier. Despite being a business-centric
968+software project we welcome and encourage contributions from both Canonical and
969+Community members.
970+
971+.. toctree::
972+ :maxdepth: 3
973+
974+ intro.rst
975+ architecture.rst
976+ reference.rst
977
978=== added file 'plainbox/docs/dev/intro.rst'
979--- plainbox/docs/dev/intro.rst 1970-01-01 00:00:00 +0000
980+++ plainbox/docs/dev/intro.rst 2013-03-07 16:10:38 +0000
981@@ -0,0 +1,238 @@
982+Getting started with development
983+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
984+
985+PlainBox uses python3 for development. The core is really system independent
986+but you will need Ubuntu to really make the best of it and experience it as we
987+do. We encourage everyone to use the most recent Ubuntu release for
988+development. Usually this brings the best, most recent tools without having to
989+search for software on the Internet.
990+
991+PlainBox has almost no dependencies itself, almost, because we depend on the
992+mighty :term:`CheckBox` project to provide us with a lot of existing
993+infrastructure. Testing PlainBox requires additional packages and some
994+non-packaged software. You will typically want to install it and take advantage
995+of the integration we provide.
996+
997+.. note::
998+
999+ If you are working with the source please be aware that PlainBox requires
1000+ an installed copy of CheckBox. CheckBox in turns is has many scripts that
1001+ depend on various system packages, including python packages that cannot be
1002+ installed from pypi. If you were planning on using :command:`virtualenv`
1003+ then please make sure to create it with the ``--system-site-packages``
1004+ option.
1005+
1006+Get the tools
1007+-------------
1008+
1009+First, you need to install the basic development tools:
1010+
1011+.. code-block:: bash
1012+
1013+ $ sudo apt-get install bzr python3-setuptools python3-dev python3-doc python3-sphinx python-virtualenv
1014+
1015+While developing PlainBox you will often need to run potentially dangerous
1016+commands on your system, such as asking it to suspend and wake up
1017+automatically. We also need to support a range of Ubuntu releases, going all
1018+the way back to Ubuntu 12.04. This may cause compatibility issues that are
1019+unnoticed all until they hit our CI system. To minimize this PlainBox uses
1020+:term:`Vagrant` to create lightweight execution environments that transparently
1021+share your source tree and allow you to quickly create and share testing
1022+environment that can be deployed by any developer in minutes. Vagrant uses
1023+:term:`VirtualBox` and while both are packaged in Ubuntu, unless you are
1024+running Ubuntu 13.04 you should download and install the software from their
1025+upstream projects.
1026+
1027+If you are running Ubuntu 13.04
1028+
1029+.. code-block:: bash
1030+
1031+ $ sudo apt-get install vagrant
1032+
1033+If you are running earlier version of Ubuntu follow those two links to get started:
1034+
1035+ * http://downloads.vagrantup.com/
1036+ * https://www.virtualbox.org/wiki/Downloads
1037+
1038+If you have not installed VirtualBox before, you must add yourself to the
1039+``vboxusers`` group, log out and log back in again.
1040+
1041+.. code-block:: bash
1042+
1043+ $ sudo usermod -G vboxusers -a $USER
1044+
1045+Get the source
1046+--------------
1047+
1048+Now that you have all the tools, you can get the source:
1049+
1050+.. code-block:: bash
1051+
1052+ $ bzr branch lp:checkbox
1053+
1054+.. note::
1055+ If you would rather use ``git`` you can also do that (and in fact, some of
1056+ us already do). Head to `git-lp homepage <http://zyga.github.com/git-lp/>`_
1057+ and follow the guide there to use git-lp with this project.
1058+
1059+Initialize virtualenv
1060+---------------------
1061+
1062+PlainBox will use a few unpackaged and bleeding-edge releases from :term:`pypi`
1063+those are installed by additional script. By default the script assumes you
1064+have a /ramdisk directory but you can pass any path as an argument for an
1065+alternate location.
1066+
1067+.. code-block:: bash
1068+
1069+ $ ./mk-venv.sh
1070+
1071+After everything is set up you can activate the virtualenv environment with the
1072+dot command. Note that there *is* a space between the dot and the forward
1073+slash. You can repeat this command in as many shells as you like.
1074+
1075+.. code-block:: bash
1076+
1077+ $ . /ramdisk/venv/bin/activate
1078+
1079+Once virtualenv is activated your shell prompt will be changed to reflect that.
1080+You should now be able to run :command:`plainbox --help` to ensure everything
1081+is working properly.
1082+
1083+Initialize vagrant
1084+------------------
1085+
1086+Vagrant allows us to ship a tiny text file :file:`Vagrantfile` that describes
1087+the development and testing environment. This file tells :command:`vagrant` how
1088+to prepare a virtual machine for testing. If you never used it before you may
1089+want to keep a tab open on `vagrant getting started guide
1090+<http:`http://docs.vagrantup.com/v1/docs/getting-started/index.html>`_
1091+
1092+We did all the hard work so that you don't have to, to get everything ready
1093+just run one command:
1094+
1095+.. code-block:: bash
1096+
1097+ $ vagrant up
1098+
1099+This will download vanilla Ubuntu cloud images, initialize VirtualBox,
1100+provision virtual machines (one for each supported Ubuntu release) and allow
1101+you to ssh into them for testing with one command.
1102+
1103+This will take a moment, depending on the speed of your network. Once that is
1104+done you should be able to log into, say, ``precise`` and run
1105+:command:`plainbox --help` to see if everything is all right.
1106+
1107+.. code-block:: bash
1108+
1109+ $ vagrant ssh precise
1110+ vagrant@vagrant-ubuntu-precise-32:~$ plainbox --help
1111+ usage: plainbox [-h] [-v] {run,special,self-test} ...
1112+
1113+ positional arguments:
1114+ {run,special,self-test}
1115+ run run a test job
1116+ special special/internal commands
1117+ self-test run integration tests
1118+
1119+ optional arguments:
1120+ -h, --help show this help message and exit
1121+ -v, --version show program's version number and exit
1122+ $ exit
1123+
1124+Running PlainBox tests
1125+^^^^^^^^^^^^^^^^^^^^^^
1126+
1127+PlainBox is designed to be testable so it would be silly if it was hard to run
1128+tests. Actually, there are many different ways to run tests. They all run the
1129+same code so don't worry.
1130+
1131+To test the current code you are working on you can:
1132+
1133+- Run the :command:`./test-in-vagrant.sh` from the plainbox directory. This
1134+ will take the longes but will go over *all* the tests on *all* the supported
1135+ versions of Ubuntu. It will run CheckBox unit-tests, PlainBox unit-tests and
1136+ it will even run integration tests that actually execute jobs.
1137+
1138+- Run :command:`plainbox self-test --unit-tests` or
1139+ :command:`plainbox self-test --integration-tests`. This will execute all the
1140+ tests right on your machine, without any virtualization (well, unless you do
1141+ that after running :command:`vagrant ssh`). Typically you would run unit
1142+ tests while being in a ``virtualenv`` with the ``plainbox`` package in
1143+ development mode, as created by running :command:`python setup.py develop`
1144+
1145+- Run :command:`./setup.py test` this will install any required test
1146+ dependencies from pypi and run unit tests.
1147+
1148+- Run the script :command:`test-with-coverage.sh` while being in a virtualenv.
1149+ This will also compute testing code coverage and is very much recommended
1150+ while working on new code and tests.
1151+
1152+Submitting Patches
1153+^^^^^^^^^^^^^^^^^^
1154+
1155+We use `Launchpad <https://launchpad.net>`_ for most of our project management.
1156+All code changes should be submitted as merge requests. Launchpad has
1157+`extensive documentation <https://help.launchpad.net/>`_ on how to use various
1158+facilities it provides.
1159+
1160+In general we are open to contributions but we reserve the right to reject
1161+patches if they don't fit into the needs of the :term:`Hardware Certification`.
1162+If you have an idea go and talk to us on :abbr:`IRC (Internet Relay Chat)` on
1163+the `#ubuntu-quality <irc://freenode.net:8001/#ubuntu-quality>`_ channel.
1164+
1165+We have some basic rules patch acceptance:
1166+
1167+0. Be prepare to alter your changes.
1168+
1169+ This is a meta-rule. One of the points of code reviews is to improve the
1170+ proposal. That implies the proposal may need to change. You must be prepared
1171+ and able to change your code after getting feedback.
1172+
1173+ To do that efficiently you must structure your work in a way where each
1174+ committed change works for you rather than against you. The rules listed
1175+ below are a reflection of this.
1176+
1177+1. Each patch should be a single logical change that can be applied.
1178+
1179+ Don't clump lots of changes into one big patch. That will only delay review,
1180+ make accepting feedback difficult and annoying. This may mean that the history
1181+ has many small patches that can land in trunk in a FIFO mode. The oldest patch
1182+ of your branch may be allowed to land and should make sense. This has
1183+ implications on how general code editing should be performed. If you break some
1184+ APIs then firsts introduce a working replacement, then change usage of the API
1185+ and lastly remove any dead code.
1186+
1187+2. Don't keep junk patches in your branch.
1188+
1189+ Don't keep patches such as "fix typo" in your branch, that makes the review
1190+ process more difficult as some reviewers will read your patches one by one.
1191+ This is especially important if your changes are substantial.
1192+
1193+3. Don't merge with trunk, rebase on trunk.
1194+
1195+ This way you can keep your local delta as a collection of meaningful,
1196+ readable patches. Reading the full diff and following the complex merge
1197+ history (especially for long-lived branches) is difficult in practice.
1198+
1199+4. Keep unrelated changes in separate branches.
1200+
1201+ If you ware working on something and found a bug that needed immediate
1202+ fixing, typo or anything else that is small and quick to fix, do it. Then
1203+ take that patch out of your development branch and into a dedicated branch
1204+ and propose it. As the small change is reviewed and lands you can remove
1205+ that patch from your development branch.
1206+
1207+ This is intended to help both the developer and the reviewer. Seemingly
1208+ trivial patches may turn out to be more complicated than initially assumed
1209+ (and may have their own feedback cycle and iterations). The reviewer can
1210+ focus on logical changes and not on a collection of unrelated alterations.
1211+ Lastly we may need to apply some fixes to other supported branches and
1212+ release those.
1213+
1214+5. Don't propose untested code.
1215+
1216+ We generally like tests for new code. This is not a super-strict requirement
1217+ but unless writing tests is incredibly hard we'd rather wait. If testing is
1218+ hard we'd rather invest some time in refactoring the code or building
1219+ required support infrastructure.
1220
1221=== added file 'plainbox/docs/dev/reference.rst'
1222--- plainbox/docs/dev/reference.rst 1970-01-01 00:00:00 +0000
1223+++ plainbox/docs/dev/reference.rst 2013-03-07 16:10:38 +0000
1224@@ -0,0 +1,154 @@
1225+.. _code_reference:
1226+
1227+.. toctree::
1228+ :maxdepth: 2
1229+
1230+Code reference
1231+==============
1232+
1233+.. note::
1234+
1235+ Unless stated otherwise all API is unstable. PlainBox does not offer
1236+ general API stability at this time.
1237+
1238+.. automodule:: plainbox
1239+ :members:
1240+ :undoc-members:
1241+ :show-inheritance:
1242+
1243+.. automodule:: plainbox.public
1244+ :members:
1245+ :undoc-members:
1246+ :show-inheritance:
1247+
1248+.. automodule:: plainbox.abc
1249+ :members:
1250+ :undoc-members:
1251+ :show-inheritance:
1252+
1253+.. automodule:: plainbox.tests
1254+ :members:
1255+ :undoc-members:
1256+ :show-inheritance:
1257+
1258+.. automodule:: plainbox.testing_utils
1259+ :members:
1260+ :undoc-members:
1261+ :show-inheritance:
1262+
1263+.. automodule:: plainbox.testing_utils.cwd
1264+ :members:
1265+ :undoc-members:
1266+ :show-inheritance:
1267+
1268+.. automodule:: plainbox.testing_utils.io
1269+ :members:
1270+ :undoc-members:
1271+ :show-inheritance:
1272+
1273+.. automodule:: plainbox.testing_utils.testcases
1274+ :members:
1275+ :undoc-members:
1276+ :show-inheritance:
1277+
1278+.. automodule:: plainbox.vendor
1279+ :members:
1280+
1281+.. automodule:: plainbox.vendor.extcmd
1282+ :members:
1283+ :undoc-members:
1284+ :show-inheritance:
1285+
1286+.. automodule:: plainbox.impl
1287+ :members:
1288+ :undoc-members:
1289+ :show-inheritance:
1290+
1291+.. automodule:: plainbox.impl.commands
1292+ :members:
1293+ :undoc-members:
1294+ :show-inheritance:
1295+
1296+.. automodule:: plainbox.impl.commands.selftest
1297+ :members:
1298+ :undoc-members:
1299+ :show-inheritance:
1300+
1301+.. automodule:: plainbox.impl.exporter
1302+ :members:
1303+ :undoc-members:
1304+ :show-inheritance:
1305+
1306+.. automodule:: plainbox.impl.exporter.json
1307+ :members:
1308+ :undoc-members:
1309+ :show-inheritance:
1310+
1311+.. automodule:: plainbox.impl.exporter.rfc822
1312+ :members:
1313+ :undoc-members:
1314+ :show-inheritance:
1315+
1316+.. automodule:: plainbox.impl.exporter.text
1317+ :members:
1318+ :undoc-members:
1319+ :show-inheritance:
1320+
1321+.. automodule:: plainbox.impl.box
1322+ :members:
1323+ :undoc-members:
1324+ :show-inheritance:
1325+
1326+.. automodule:: plainbox.impl.checkbox
1327+ :members:
1328+ :undoc-members:
1329+ :show-inheritance:
1330+
1331+.. automodule:: plainbox.impl.depmgr
1332+ :members:
1333+ :undoc-members:
1334+ :show-inheritance:
1335+
1336+.. automodule:: plainbox.impl.integration_tests
1337+ :members:
1338+ :undoc-members:
1339+ :show-inheritance:
1340+
1341+.. automodule:: plainbox.impl.job
1342+ :members:
1343+ :undoc-members:
1344+ :show-inheritance:
1345+
1346+.. automodule:: plainbox.impl.mock_job
1347+ :members:
1348+ :undoc-members:
1349+ :show-inheritance:
1350+
1351+.. automodule:: plainbox.impl.resource
1352+ :members:
1353+ :undoc-members:
1354+ :show-inheritance:
1355+
1356+.. automodule:: plainbox.impl.result
1357+ :members:
1358+ :undoc-members:
1359+ :show-inheritance:
1360+
1361+.. automodule:: plainbox.impl.rfc822
1362+ :members:
1363+ :show-inheritance:
1364+
1365+.. automodule:: plainbox.impl.runner
1366+ :members:
1367+ :undoc-members:
1368+ :show-inheritance:
1369+
1370+.. automodule:: plainbox.impl.session
1371+ :members:
1372+ :undoc-members:
1373+ :show-inheritance:
1374+
1375+.. automodule:: plainbox.impl.testing_utils
1376+ :members:
1377+ :undoc-members:
1378+ :show-inheritance:
1379
1380=== added file 'plainbox/docs/glossary.rst'
1381--- plainbox/docs/glossary.rst 1970-01-01 00:00:00 +0000
1382+++ plainbox/docs/glossary.rst 2013-03-07 16:10:38 +0000
1383@@ -0,0 +1,104 @@
1384+Glossary
1385+========
1386+
1387+.. glossary::
1388+
1389+ hardware certification
1390+
1391+ A process of ensuring that a specific device works well with Ubuntu.
1392+ For more details see our certification program:
1393+
1394+ http://www.canonical.com/engineering-services/certification/hardware-certification
1395+
1396+ hardware certification team
1397+
1398+ A team inside Canonical working on :term:`Hardware Certification`.
1399+
1400+ CheckBox
1401+
1402+ CheckBox is a hardware testing tool developed by Canonical for
1403+ certifying hardware with Ubuntu. CheckBox is free software and is
1404+ available at http://launchpad.net/checkbox. The ``checkbox`` package is
1405+ pre-installed on all Ubuntu systems
1406+
1407+ PlainBox
1408+
1409+ PlainBox is a rewrite of CheckBox with the aim of improving internal
1410+ architecture, testability, robustness, quality and speed. It is
1411+ currently under active development. It is not pre-installed on Ubuntu.
1412+ It is developed inside CheckBox code repository.
1413+
1414+ white list
1415+
1416+ White lists are text files used by CheckBox to select Jobs for
1417+ execution. They can include simple regular expressions to match and
1418+ pick many similar jobs at once.
1419+
1420+ job
1421+
1422+ Jobs are smallest units of testing that can be performed by either
1423+ CheckBox or PlainBox. All jobs have an unique name. There are many
1424+ types of jobs, some are fully automated others are fully manual. Some
1425+ jobs are only an implementation detail and a part of the internal
1426+ architecture of CheckBox
1427+
1428+ resources
1429+
1430+ Resources are collections of key-value data sets that are generated by
1431+ special resource jobs. They are extensively used to indicate hardware
1432+ or software dependencies. For example a bluetooth test may indicate it
1433+ requires bluetooth hardware and appropriate software packages
1434+ installed.
1435+
1436+ requirement program
1437+
1438+ Requirement programs are small (one to few lines) programs that use a
1439+ subset of python to execute some code against resources. They are what
1440+ actually describes the relationship of a Job to some Resources. For
1441+ example a resource program ``package.name == "bluez"`` indicates that
1442+ at least one resource generated by the ``package`` job has a key
1443+ ``name`` equal to the string ``bluez``.
1444+
1445+ attachment
1446+
1447+ Attachments are a special type of a Job that can creates an attachment
1448+ record in the submission.xml file. They are commonly used to include
1449+ basic system information files and output of certain commands which can
1450+ aid in system certification.
1451+
1452+ certification website
1453+
1454+ The website https://certification.canonical.com/
1455+
1456+ Canonical ID
1457+
1458+ A number assigned to the specific device (laptop, desktop or server) by
1459+ Canonical. This number is used on the Certification Website and by the
1460+ Hardware Certification Team. It is an internal bookkeeping identifier
1461+ used in our labs.
1462+
1463+ Secure ID
1464+
1465+ An identifier, similar to Canonical ID, used for hardware
1466+ certification. This identifier is used when interacting with the
1467+ Certification Website, it does not reveal anything about the actual
1468+ hardware (like the manufacturer name or device name)
1469+
1470+ pypi
1471+
1472+ The Python Package Index where any developer can share their python
1473+ programs and libraries. Pypi is available at:
1474+ https://pypi.python.org/pypi.
1475+
1476+ Vagrant
1477+
1478+ Vagrant is command line program intended for software developers to
1479+ quickly create portable virtual environments for testing their software
1480+ in a production operating system. Vagrant is free software and is
1481+ available at http://www.vagrantup.com/
1482+
1483+ VirtualBox
1484+
1485+ VirtualBox is a free, powerful desktop vitalization software.
1486+ VirtualBox is available in the Ubuntu Software Center and at
1487+ https://www.virtualbox.org/
1488
1489=== modified file 'plainbox/docs/index.rst'
1490--- plainbox/docs/index.rst 2013-02-22 16:26:25 +0000
1491+++ plainbox/docs/index.rst 2013-03-07 16:10:38 +0000
1492@@ -3,15 +3,56 @@
1493 You can adapt this file completely to your liking, but it should at least
1494 contain the root `toctree` directive.
1495
1496-Welcome to PlainBox's documentation!
1497-====================================
1498-
1499-Contents:
1500+PlainBox
1501+========
1502+
1503+:term:`PlainBox` is a hardware testing tool useful for certifying laptops,
1504+desktops and servers with Ubuntu. It is a replacement for the current
1505+certification tool, :term:`CheckBox`.
1506+
1507+PlainBox *complements* CheckBox. It uses all the hardware test definitions,
1508+scripts and libraries from CheckBox. PlainBox is currently in **alpha** stages,
1509+having mostly but not entirely complete core and a developer-centric command
1510+line interface.
1511+
1512+Installation
1513+^^^^^^^^^^^^
1514+
1515+PlainBox can be installed from a :abbr:`PPA (Personal Package Archive)`
1516+(recommended) or :abbr:`pypi (python package index)` on Ubuntu Precise (12.04)
1517+or newer.
1518+
1519+.. code-block:: bash
1520+
1521+ $ sudo add-apt-repository ppa:checkbox-dev/ppa && sudo apt-get update && sudo apt-get install plainbox
1522+
1523+
1524+Testing your hardware
1525+^^^^^^^^^^^^^^^^^^^^^
1526+
1527+To test your hardware with the default set of tests run this command.
1528+
1529+.. code-block:: bash
1530+
1531+ $ plainbox run --whitelist=default --output-format=xml --output-file=submission.xml
1532+
1533+The :file:`submission.xml` you get in the end can be submitted to the
1534+:term:`certification website`. For more details see :ref:`usage`
1535+
1536+.. todo::
1537+ This example is bad because it is fake. We should improve the core so that
1538+ a whitelist can referred to by name alone (not by a full path).
1539+
1540+Table of contents
1541+=================
1542
1543 .. toctree::
1544 :maxdepth: 2
1545
1546-
1547+ usage.rst
1548+ dev/index.rst
1549+ glossary.rst
1550+ changelog.rst
1551
1552 Indices and tables
1553 ==================
1554@@ -19,4 +60,3 @@
1555 * :ref:`genindex`
1556 * :ref:`modindex`
1557 * :ref:`search`
1558-
1559
1560=== added file 'plainbox/docs/usage.rst'
1561--- plainbox/docs/usage.rst 1970-01-01 00:00:00 +0000
1562+++ plainbox/docs/usage.rst 2013-03-07 16:10:38 +0000
1563@@ -0,0 +1,69 @@
1564+.. _usage:
1565+
1566+Usage
1567+=====
1568+
1569+Currently :term:`PlainBox` has no graphical user interface. To use it you need
1570+to use the command line.
1571+
1572+Basically there is just one command that does everything we can do so far, that
1573+is :command:`plainbox run`. It has a number of options that tell it which
1574+:term:`job` to run and what to do with results.
1575+
1576+PlainBox has built-in help system so running :command:`plainbox run --help`
1577+will give you instant information about all the various arguments and options
1578+that are available. This document is not intended to replace that.
1579+
1580+Running a specific job
1581+^^^^^^^^^^^^^^^^^^^^^^
1582+
1583+To run a specific :term:`job` pass it to the ``--include-pattern`` or ``-i``
1584+option.
1585+
1586+For example, to run the ``cpu/scaling_test`` job:
1587+
1588+.. code-block:: bash
1589+
1590+ $ plainbox run -i cpu/scaling_test
1591+
1592+.. note::
1593+
1594+ The option ``-i`` can be provided any number of times.
1595+
1596+Running jobs related to a specific area
1597+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1598+
1599+PlainBox has no concept of job categories but you can simulate that by
1600+running all jobs that follow a specific naming pattern. For example, to run
1601+all of the USB tests you can run the following command:
1602+
1603+.. code-block:: bash
1604+
1605+ $ plainbox run -i 'usb/'
1606+
1607+Running a white list
1608+^^^^^^^^^^^^^^^^^^^^
1609+
1610+To run a :term:`white list` pass the ``--whitelist`` or ``-w`` option.
1611+
1612+For example, to run the default white list run:
1613+
1614+.. code-block:: bash
1615+
1616+ $ plainbox run -w default
1617+
1618+Saving test results as XML
1619+^^^^^^^^^^^^^^^^^^^^^^^^^^
1620+
1621+To send test results to the :term:`certification website` you need to pass two
1622+additional options:
1623+
1624+1. ``--output-format=xml``
1625+2. ``--output-file=NAME`` where *NAME* is a file name
1626+
1627+For example, to get the default certification tests ready to be submitted
1628+run this command:
1629+
1630+.. code-block:: bash
1631+
1632+ $ plainbox run -w default -f xml -o submission.xml
1633
1634=== modified file 'plainbox/plainbox/__init__.py'
1635--- plainbox/plainbox/__init__.py 2012-11-29 00:59:03 +0000
1636+++ plainbox/plainbox/__init__.py 2013-03-07 16:10:38 +0000
1637@@ -18,13 +18,13 @@
1638 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1639
1640 """
1641-plainbox
1642-========
1643+:mod:`plainbox` -- main package
1644+===============================
1645
1646 Simple checkbox redesign, without the complex message passing
1647
1648-All public API is in the 'public' module.
1649-All abstract base classes are in the 'abc' module.
1650+All public API is in :mod:`plainbox.public`.
1651+All abstract base classes are in :mod:`plainbox.abc`.
1652 """
1653
1654 import sys
1655
1656=== modified file 'plainbox/plainbox/abc.py'
1657--- plainbox/plainbox/abc.py 2012-11-29 18:40:46 +0000
1658+++ plainbox/plainbox/abc.py 2013-03-07 16:10:38 +0000
1659@@ -18,10 +18,8 @@
1660 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1661
1662 """
1663-plainbox.abc
1664-============
1665-
1666-Abstract base classes used by plainbox
1667+:mod:`plainbox.abc` -- abstract base classes
1668+============================================
1669
1670 Those classes are actually implemented in the plainbox.impl package. This
1671 module is here so that the essential API concepts are in a single spot and are
1672
1673=== modified file 'plainbox/plainbox/impl/__init__.py'
1674--- plainbox/plainbox/impl/__init__.py 2012-11-19 10:45:46 +0000
1675+++ plainbox/plainbox/impl/__init__.py 2013-03-07 16:10:38 +0000
1676@@ -18,10 +18,12 @@
1677 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1678
1679 """
1680-plainbox.impl
1681-=============
1682-
1683- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
1684+:mod:`plainbox.impl` -- implementation package
1685+==============================================
1686+
1687+.. warning::
1688+
1689+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
1690 """
1691
1692 from functools import wraps
1693
1694=== modified file 'plainbox/plainbox/impl/box.py'
1695--- plainbox/plainbox/impl/box.py 2013-02-22 16:26:25 +0000
1696+++ plainbox/plainbox/impl/box.py 2013-03-07 16:10:38 +0000
1697@@ -18,12 +18,12 @@
1698 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1699
1700 """
1701-plainbox.impl.box
1702-=================
1703-
1704-Internal implementation of plainbox
1705-
1706- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
1707+:mod:`plainbox.impl.box` -- command line interface
1708+==================================================
1709+
1710+.. warning::
1711+
1712+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
1713 """
1714
1715 from argparse import ArgumentParser
1716@@ -34,6 +34,7 @@
1717 from logging import getLogger
1718 from os.path import join
1719 import argparse
1720+import re
1721 import sys
1722
1723 from plainbox import __version__ as version
1724@@ -71,10 +72,16 @@
1725 group.add_argument(
1726 '-i', '--include-pattern', action="append",
1727 metavar='PATTERN', default=[], dest='include_pattern_list',
1728- help="Run jobs matching the given pattern")
1729+ help=("Run jobs matching the given regular expression. Matches "
1730+ "from the start to the end of the line."))
1731+ group.add_argument(
1732+ '-x', '--exclude-pattern', action="append",
1733+ metavar="PATTERN", default=[], dest='exclude_pattern_list',
1734+ help=("Do not run jobs matching the given regular expression. "
1735+ "Matches from the start to the end of the line."))
1736 # TODO: Find a way to handle the encoding of the file
1737 group.add_argument(
1738- '-W', '--whitelist',
1739+ '-w', '--whitelist',
1740 metavar="WHITELIST",
1741 type=FileType("rt"),
1742 help="Load whitelist containing run patterns")
1743@@ -88,18 +95,36 @@
1744 def _get_matching_job_list(self, ns, job_list):
1745 # Find jobs that matched patterns
1746 matching_job_list = []
1747+ # Pre-seed the include pattern list with data read from
1748+ # the whitelist file.
1749 if ns.whitelist:
1750- ns.include_pattern_list.extend([pattern.strip() for pattern in
1751- ns.whitelist.readlines()])
1752+ ns.include_pattern_list.extend([
1753+ pattern.strip()
1754+ for pattern in ns.whitelist.readlines()])
1755+ # Decide which of the known jobs to include
1756 for job in job_list:
1757- for pattern in ns.include_pattern_list:
1758- if fnmatch(job.name, pattern):
1759- matching_job_list.append(job)
1760+ # Reject all jobs that match any of the exclude
1761+ # patterns, matching strictly from the start to
1762+ # the end of the line.
1763+ for pattern in ns.exclude_pattern_list:
1764+ if re.match(r"^{pattern}$".format(pattern=pattern), job.name):
1765 break
1766+ else:
1767+ # Then accept (include) all job that matches
1768+ # any of include patterns, matching strictly
1769+ # from the start to the end of the line.
1770+ for pattern in ns.include_pattern_list:
1771+ if re.match(r"^{pattern}$".format(pattern=pattern),
1772+ job.name):
1773+ matching_job_list.append(job)
1774+ break
1775 return matching_job_list
1776
1777
1778 class SpecialCommand(PlainBoxCommand, CheckBoxCommandMixIn):
1779+ """
1780+ Implementation of ``$ plainbox special``
1781+ """
1782
1783 def invoked(self, ns):
1784 job_list = self.get_job_list(ns)
1785@@ -261,6 +286,17 @@
1786 raise SystemExit(str(exc))
1787 return exporter
1788
1789+ def ask_for_resume(self, prompt=None, allowed=None):
1790+ # FIXME: Add support/callbacks for a GUI
1791+ if prompt is None:
1792+ prompt = "Do you want to resume the previous session [Y/n]? "
1793+ if allowed is None:
1794+ allowed = ('', 'y', 'Y', 'n', 'N')
1795+ answer = None
1796+ while answer not in allowed:
1797+ answer = input(prompt)
1798+ return False if answer in ('n', 'N') else True
1799+
1800 def _run_jobs(self, ns, job_list, exporter):
1801 # Compute the run list, this can give us notification about problems in
1802 # the selected jobs. Currently we just display each problem
1803@@ -268,8 +304,13 @@
1804 print("[ Analyzing Jobs ]".center(80, '='))
1805 # Create a session that handles most of the stuff needed to run jobs
1806 session = SessionState(job_list)
1807- self._update_desired_job_list(session, matching_job_list)
1808 with session.open():
1809+ if session.previous_session_file():
1810+ if self.ask_for_resume():
1811+ session.resume()
1812+ else:
1813+ session.clean()
1814+ self._update_desired_job_list(session, matching_job_list)
1815 if (sys.stdin.isatty() and sys.stdout.isatty() and not
1816 ns.not_interactive):
1817 outcome_callback = self.ask_for_outcome
1818
1819=== modified file 'plainbox/plainbox/impl/checkbox.py'
1820--- plainbox/plainbox/impl/checkbox.py 2013-02-22 16:26:25 +0000
1821+++ plainbox/plainbox/impl/checkbox.py 2013-03-07 16:10:38 +0000
1822@@ -18,12 +18,12 @@
1823 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1824
1825 """
1826-plainbox.impl.checkbox
1827-======================
1828-
1829-Internal implementation of plainbox
1830-
1831- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
1832+:mod:`plainbox.impl.checkbox` -- CheckBox integration
1833+=====================================================
1834+
1835+.. warning::
1836+
1837+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
1838 """
1839
1840 import io
1841@@ -102,7 +102,7 @@
1842 """
1843 Return the required value of CHECKBOX_SHARE environment variable.
1844
1845- ..note::
1846+ .. note::
1847 This variable is only required by one script.
1848 It would be nice to remove this later on.
1849 """
1850@@ -119,7 +119,7 @@
1851 This entry is required for CheckBox scripts to import the correct
1852 CheckBox python libraries.
1853
1854- ..note::
1855+ .. note::
1856 The result may be None
1857 """
1858 # NOTE: When CheckBox is installed then all the scripts should not use
1859@@ -159,7 +159,7 @@
1860 """
1861 Return an absolute path of the scripts directory
1862
1863- ..note::
1864+ .. note::
1865 The scripts may not work without setting PYTHONPATH and
1866 CHECKBOX_SHARE.
1867 """
1868
1869=== modified file 'plainbox/plainbox/impl/commands/__init__.py'
1870--- plainbox/plainbox/impl/commands/__init__.py 2013-01-30 21:43:05 +0000
1871+++ plainbox/plainbox/impl/commands/__init__.py 2013-03-07 16:10:38 +0000
1872@@ -18,12 +18,12 @@
1873 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1874
1875 """
1876-plainbox.impl.commands
1877-======================
1878-
1879-Internal implementation of plainbox
1880-
1881- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
1882+:mod:`plainbox.impl.commands` -- shared code for plainbox sub-commands
1883+======================================================================
1884+
1885+.. warning::
1886+
1887+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
1888 """
1889
1890 from abc import abstractmethod, ABCMeta
1891
1892=== modified file 'plainbox/plainbox/impl/commands/selftest.py'
1893--- plainbox/plainbox/impl/commands/selftest.py 2013-02-08 17:02:12 +0000
1894+++ plainbox/plainbox/impl/commands/selftest.py 2013-03-07 16:10:38 +0000
1895@@ -19,12 +19,12 @@
1896 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1897
1898 """
1899-plainbox.impl.commands.selftest
1900-===============================
1901-
1902-Internal implementation of plainbox
1903-
1904- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
1905+:mod:`plainbox.impl.commands.selftest` -- selftest sub-command
1906+==============================================================
1907+
1908+.. warning::
1909+
1910+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
1911 """
1912 from unittest.runner import TextTestRunner
1913
1914
1915=== modified file 'plainbox/plainbox/impl/depmgr.py'
1916--- plainbox/plainbox/impl/depmgr.py 2012-11-29 18:59:46 +0000
1917+++ plainbox/plainbox/impl/depmgr.py 2013-03-07 16:10:38 +0000
1918@@ -18,12 +18,12 @@
1919 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1920
1921 """
1922-plainbox.impl.depmgr
1923-====================
1924-
1925-Internal implementation of plainbox
1926-
1927- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
1928+:mod:`plainbox.impl.depmgr` -- dependency solver
1929+================================================
1930+
1931+.. warning::
1932+
1933+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
1934 """
1935
1936 from abc import ABCMeta
1937@@ -148,15 +148,17 @@
1938 @classmethod
1939 def resolve_dependencies(cls, job_list, visit_list=None):
1940 """
1941- Solve the dependency graph expressed as a list of job definitions. The
1942- visit_list, if specified, allows to consider only a part of the graph
1943- while still having access and knowledge of all jobs.
1944-
1945- Returns the solution (a list of jobs to execute in order)
1946-
1947- raises DependencyCycleError if a cyclic dependency is present.
1948-
1949- raises DependencyMissingErorr if a required job does not exist.
1950+ Solve the dependency graph expressed as a list of job definitions.
1951+
1952+ :param list job_list: list of known jobs
1953+ :param list visit_list: (optional) list of jobs to solve
1954+
1955+ The visit_list, if specified, allows to consider only a part of the
1956+ graph while still having access and knowledge of all jobs.
1957+
1958+ :returns list: the solution (a list of jobs to execute in order)
1959+ :raises DependencyCycleError: if a cyclic dependency is present.
1960+ :raises DependencyMissingErorr: if a required job does not exist.
1961 """
1962 return cls(job_list)._solve(visit_list)
1963
1964
1965=== modified file 'plainbox/plainbox/impl/exporter/__init__.py'
1966--- plainbox/plainbox/impl/exporter/__init__.py 2013-02-22 16:26:25 +0000
1967+++ plainbox/plainbox/impl/exporter/__init__.py 2013-03-07 16:10:38 +0000
1968@@ -18,10 +18,12 @@
1969 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1970
1971 """
1972-plainbox.impl.exporter
1973-======================
1974-
1975- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
1976+:mod:`plainbox.impl.exporter` -- shared code for session state exporters
1977+========================================================================
1978+
1979+.. warning::
1980+
1981+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
1982 """
1983
1984 from abc import ABCMeta, abstractmethod
1985@@ -38,11 +40,6 @@
1986 class classproperty:
1987 """
1988 Class property.
1989-
1990- @property
1991- @classmethod
1992- def foo(cls):
1993- ...
1994 """
1995 # I wish it was in the standard library or that the composition worked
1996
1997@@ -75,6 +72,7 @@
1998 OPTION_WITH_DESIRED_JOB_LIST = 'with-job-list'
1999 OPTION_WITH_RESOURCE_MAP = 'with-resource-map'
2000 OPTION_WITH_JOB_DEFS = 'with-job-defs'
2001+ OPTION_WITH_ATTACHMENTS = 'with-attachments'
2002
2003 SUPPORTED_OPTION_LIST = (
2004 OPTION_WITH_IO_LOG,
2005@@ -84,6 +82,7 @@
2006 OPTION_WITH_JOB_LIST,
2007 OPTION_WITH_RESOURCE_MAP,
2008 OPTION_WITH_JOB_DEFS,
2009+ OPTION_WITH_ATTACHMENTS,
2010 )
2011
2012 def __init__(self, option_list=None):
2013@@ -133,6 +132,8 @@
2014 # TODO: turn session._resource_map to a public property
2015 for resource_name, resource_list in session._resource_map.items()
2016 }
2017+ if self.OPTION_WITH_ATTACHMENTS in self._option_list:
2018+ data['attachment_map'] = {}
2019 for job_name, job_state in session.job_state_map.items():
2020 if job_state.result.outcome is None:
2021 continue
2022@@ -151,6 +152,16 @@
2023 continue
2024 data['result_map'][job_name][prop] = \
2025 getattr(job_state.result.job, prop)
2026+
2027+ # Add Attachements if requested
2028+ if job_state.result.job.plugin == 'attachment':
2029+ if self.OPTION_WITH_ATTACHMENTS in self._option_list:
2030+ raw_bytes = b''.join((record[2] for record in
2031+ job_state.result.io_log if record[1] == 'stdout'))
2032+ data['attachment_map'][job_name] = \
2033+ base64.standard_b64encode(raw_bytes).decode('ASCII')
2034+ continue # Don't add attachments IO logs to the result_map
2035+
2036 # Add IO log if requested
2037 if self.OPTION_WITH_IO_LOG in self._option_list:
2038 # If requested, squash the IO log so that only textual data is
2039
2040=== modified file 'plainbox/plainbox/impl/exporter/json.py'
2041--- plainbox/plainbox/impl/exporter/json.py 2012-12-10 12:38:50 +0000
2042+++ plainbox/plainbox/impl/exporter/json.py 2013-03-07 16:10:38 +0000
2043@@ -18,12 +18,11 @@
2044 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
2045
2046 """
2047-plainbox.impl.exporter.json
2048-===========================
2049-
2050-Internal implementation of plainbox
2051-
2052- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
2053+:mod:`plainbox.impl.exporter.json` -- JSON exporter
2054+===================================================
2055+
2056+.. warning::
2057+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
2058 """
2059
2060 import json
2061
2062=== modified file 'plainbox/plainbox/impl/exporter/rfc822.py'
2063--- plainbox/plainbox/impl/exporter/rfc822.py 2013-01-04 13:31:02 +0000
2064+++ plainbox/plainbox/impl/exporter/rfc822.py 2013-03-07 16:10:38 +0000
2065@@ -18,12 +18,12 @@
2066 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
2067
2068 """
2069-plainbox.impl.exporter.rfc822
2070-===========================
2071-
2072-Internal implementation of plainbox
2073-
2074- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
2075+:mod:`plainbox.impl.exporter.rfc822` -- RFC822 exporter
2076+=======================================================
2077+
2078+.. warning::
2079+
2080+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
2081 """
2082
2083
2084
2085=== modified file 'plainbox/plainbox/impl/exporter/test_init.py'
2086--- plainbox/plainbox/impl/exporter/test_init.py 2013-02-22 16:26:25 +0000
2087+++ plainbox/plainbox/impl/exporter/test_init.py 2013-03-07 16:10:38 +0000
2088@@ -24,6 +24,7 @@
2089 Test definitions for plainbox.impl.exporter module
2090 """
2091
2092+from tempfile import TemporaryDirectory
2093 from unittest import TestCase
2094
2095 from plainbox.impl.exporter import SessionStateExporterBase
2096@@ -31,7 +32,7 @@
2097 from plainbox.impl.session import SessionState
2098 from plainbox.impl.job import JobDefinition
2099 from plainbox.impl.result import JobResult, IOLogRecord
2100-from plainbox.impl.testing_utils import make_job, make_job_result
2101+from plainbox.impl.testing_utils import make_io_log, make_job, make_job_result
2102
2103
2104 class ClassPropertyTests(TestCase):
2105@@ -95,7 +96,7 @@
2106 }
2107 self.assertEqual(data, expected_data)
2108
2109- def make_realistic_test_session(self):
2110+ def make_realistic_test_session(self, session_dir):
2111 # Create a more realistic session with two jobs but with richer set
2112 # of data in the actual jobs and results.
2113 job_a = JobDefinition({
2114@@ -115,17 +116,17 @@
2115 'job': job_a,
2116 'outcome': 'pass',
2117 'return_code': 0,
2118- 'io_log': (
2119- IOLogRecord(0, 'stdout', b'testing\n'),
2120- )
2121+ 'io_log': make_io_log(
2122+ (IOLogRecord(0, 'stdout', b'testing\n'),),
2123+ session_dir)
2124 })
2125 result_b = JobResult({
2126 'job': job_b,
2127 'outcome': 'pass',
2128 'return_code': 0,
2129- 'io_log': (
2130- IOLogRecord(0, 'stdout', b'ready: yes\n'),
2131- )
2132+ 'io_log': make_io_log(
2133+ (IOLogRecord(0, 'stdout', b'ready: yes\n'),),
2134+ session_dir)
2135 })
2136 session.update_job_result(job_a, result_a)
2137 session.update_job_result(job_b, result_b)
2138@@ -139,10 +140,11 @@
2139 # - OPTION_FLATTEN_IO_LOG
2140 # The implementation favours SQUASH_IO_LOG
2141 # and thus the code below tests that option
2142- exporter = self.TestSessionStateExporter(
2143- self.TestSessionStateExporter.supported_option_list)
2144- session = self.make_realistic_test_session()
2145- data = exporter.get_session_data_subset(session)
2146+ with TemporaryDirectory() as scratch_dir:
2147+ exporter = self.TestSessionStateExporter(
2148+ self.TestSessionStateExporter.supported_option_list)
2149+ session = self.make_realistic_test_session(scratch_dir)
2150+ data = exporter.get_session_data_subset(session)
2151 expected_data = {
2152 'job_list': ['job_a', 'job_b'],
2153 'run_list': ['job_b', 'job_a'],
2154@@ -166,6 +168,8 @@
2155 'command': 'echo ready: yes',
2156 'io_log': ['cmVhZHk6IHllcwo='],
2157 }
2158+ },
2159+ 'attachment_map': {
2160 }
2161 }
2162 # This is just to make debugging easier
2163
2164=== modified file 'plainbox/plainbox/impl/exporter/text.py'
2165--- plainbox/plainbox/impl/exporter/text.py 2012-12-10 12:38:50 +0000
2166+++ plainbox/plainbox/impl/exporter/text.py 2013-03-07 16:10:38 +0000
2167@@ -18,12 +18,12 @@
2168 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
2169
2170 """
2171-plainbox.impl.exporter.text
2172-===========================
2173-
2174-Internal implementation of plainbox
2175-
2176- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
2177+:mod:`plainbox.impl.exporter.text` -- plain text exporter
2178+=========================================================
2179+
2180+.. warning::
2181+
2182+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
2183 """
2184
2185
2186
2187=== modified file 'plainbox/plainbox/impl/integration_tests.py'
2188--- plainbox/plainbox/impl/integration_tests.py 2013-02-22 16:26:25 +0000
2189+++ plainbox/plainbox/impl/integration_tests.py 2013-03-07 16:10:38 +0000
2190@@ -18,16 +18,20 @@
2191 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
2192
2193 """
2194-plainbox.impl.integration_tests
2195-===============================
2196-
2197-Integration tests for checkbox scripts
2198+:mod:`plainbox.impl.integration_tests` -- integration tests
2199+===========================================================
2200+
2201+.. warning::
2202+
2203+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
2204 """
2205
2206 from tempfile import TemporaryDirectory
2207 from unittest import TestCase
2208 import json
2209 import os
2210+import shutil
2211+import tempfile
2212
2213 from pkg_resources import resource_filename, resource_isdir, resource_listdir
2214
2215@@ -41,6 +45,14 @@
2216
2217 parameter_names = ('job_name',)
2218
2219+ def setUp(self):
2220+ # session data are kept in XDG_CACHE_HOME/plainbox/.session
2221+ # To avoid resuming a real session, we have to select a temporary
2222+ # location instead
2223+ self._sandbox = tempfile.mkdtemp()
2224+ self._env = os.environ
2225+ os.environ['XDG_CACHE_HOME'] = self._sandbox
2226+
2227 @classmethod
2228 def _gen_job_name_values(cls, package='plainbox', root='data/'):
2229 """
2230@@ -94,3 +106,7 @@
2231 expected_result = json.load(stream)
2232 # Check that results match expected values
2233 self.assertEqual(actual_result, expected_result)
2234+
2235+ def tearDown(self):
2236+ shutil.rmtree(self._sandbox)
2237+ os.environ = self._env
2238
2239=== modified file 'plainbox/plainbox/impl/job.py'
2240--- plainbox/plainbox/impl/job.py 2013-02-22 16:26:25 +0000
2241+++ plainbox/plainbox/impl/job.py 2013-03-07 16:10:38 +0000
2242@@ -18,12 +18,12 @@
2243 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
2244
2245 """
2246-plainbox.impl.job
2247-=================
2248-
2249-Internal implementation of plainbox
2250-
2251- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
2252+:mod:`plainbox.impl.job` -- job definition
2253+==========================================
2254+
2255+.. warning::
2256+
2257+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
2258 """
2259
2260 import collections
2261@@ -41,6 +41,11 @@
2262
2263
2264 class JobDefinition(IJobDefinition):
2265+ """
2266+ Job definition class.
2267+
2268+ Thin wrapper around the RFC822 record that defines a checkbox job definition
2269+ """
2270
2271 @property
2272 def plugin(self):
2273@@ -120,10 +125,15 @@
2274 def _get_persistance_subset(self):
2275 state = {}
2276 state['data'] = {}
2277- state['data']['plugin'] = self.plugin
2278- state['data']['name'] = self.name
2279+ for key, value in self._data.items():
2280+ state['data'][key] = value
2281 return state
2282
2283+ def __eq__(self, other):
2284+ if not isinstance(other, JobDefinition):
2285+ return False
2286+ return self.get_checksum() == other.get_checksum()
2287+
2288 def get_resource_program(self):
2289 """
2290 Return a ResourceProgram based on the 'requires' expression.
2291
2292=== added file 'plainbox/plainbox/impl/mock_job.py'
2293--- plainbox/plainbox/impl/mock_job.py 1970-01-01 00:00:00 +0000
2294+++ plainbox/plainbox/impl/mock_job.py 2013-03-07 16:10:38 +0000
2295@@ -0,0 +1,36 @@
2296+# This file is part of Checkbox.
2297+#
2298+# Copyright 2013 Canonical Ltd.
2299+# Written by:
2300+# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
2301+#
2302+# Checkbox is free software: you can redistribute it and/or modify
2303+# it under the terms of the GNU General Public License as published by
2304+# the Free Software Foundation, either version 3 of the License, or
2305+# (at your option) any later version.
2306+#
2307+# Checkbox is distributed in the hope that it will be useful,
2308+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2309+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2310+# GNU General Public License for more details.
2311+#
2312+# You should have received a copy of the GNU General Public License
2313+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
2314+
2315+"""
2316+:mod:`plainbox.impl.mock_job` -- mock jobs
2317+==========================================
2318+"""
2319+
2320+from mock import Mock
2321+
2322+from plainbox.impl.job import JobDefinition
2323+
2324+
2325+def MockJobDefinition(name,*args, **kwargs):
2326+ """
2327+ Mock for JobDefinition class
2328+ """
2329+ job = Mock(*args, spec_set=JobDefinition, **kwargs)
2330+ job.name = name
2331+ return job
2332
2333=== modified file 'plainbox/plainbox/impl/resource.py'
2334--- plainbox/plainbox/impl/resource.py 2013-02-22 16:26:25 +0000
2335+++ plainbox/plainbox/impl/resource.py 2013-03-07 16:10:38 +0000
2336@@ -18,12 +18,12 @@
2337 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
2338
2339 """
2340-plainbox.impl.resource
2341-======================
2342-
2343-Internal implementation of plainbox
2344-
2345- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
2346+:mod:`plainbox.impl.resource` -- job resources
2347+==============================================
2348+
2349+.. warning::
2350+
2351+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
2352 """
2353
2354 import ast
2355@@ -221,7 +221,7 @@
2356 """
2357 A NodeVisitor subclass used to analyze requirement expressions.
2358
2359- ..warning::
2360+ .. warning::
2361
2362 Implementation of this class requires understanding of
2363 some of the lower levels of python. The general idea is
2364
2365=== modified file 'plainbox/plainbox/impl/result.py'
2366--- plainbox/plainbox/impl/result.py 2013-02-22 16:26:25 +0000
2367+++ plainbox/plainbox/impl/result.py 2013-03-07 16:10:38 +0000
2368@@ -18,16 +18,19 @@
2369 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
2370
2371 """
2372-plainbox.impl.result
2373-====================
2374-
2375-Internal implementation of plainbox
2376-
2377- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
2378+:mod:`plainbox.impl.result` -- job result
2379+=========================================
2380+
2381+.. warning::
2382+
2383+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
2384 """
2385
2386 from collections import namedtuple
2387+import base64
2388+import json
2389 import logging
2390+import os
2391
2392 from plainbox.abc import IJobResult
2393
2394@@ -48,6 +51,9 @@
2395
2396
2397 class JobResult(IJobResult):
2398+ """
2399+ Result of running a JobDefinition.
2400+ """
2401
2402 # The outcome of a job is a one-word classification how how it ran. There
2403 # are several values that were not used in the original implementation but
2404@@ -112,7 +118,11 @@
2405
2406 @property
2407 def io_log(self):
2408- return self._data.get('io_log', ())
2409+ if os.path.exists(self._data.get('io_log', '')):
2410+ with open(self._data.get('io_log')) as f:
2411+ return json.load(f, cls=IoLogDecoder)
2412+ else:
2413+ return ()
2414
2415 @property
2416 def return_code(self):
2417@@ -121,11 +131,8 @@
2418 def _get_persistance_subset(self):
2419 state = {}
2420 state['data'] = {}
2421- state['data']['job'] = self._data['job']
2422- state['data']['outcome'] = self._data.get('outcome', None)
2423- state['data']['comments'] = self._data.get('comments')
2424- state['data']['return_code'] = self._data.get('return_code')
2425- # io_log are stored on disk, see session.jobs_io_log_dir
2426+ for key, value in self._data.items():
2427+ state['data'][key] = value
2428 return state
2429
2430 @classmethod
2431@@ -134,3 +141,26 @@
2432 Create a JobResult instance from JSON record
2433 """
2434 return cls(record['data'])
2435+
2436+
2437+class IoLogEncoder(json.JSONEncoder):
2438+ """
2439+ JSON Serialize helper to encode binary io logs
2440+ """
2441+
2442+ def default(self, obj):
2443+ return base64.standard_b64encode(obj).decode('ASCII')
2444+
2445+
2446+class IoLogDecoder(json.JSONDecoder):
2447+ """
2448+ JSON Decoder helper for io logs objects
2449+ """
2450+
2451+ def decode(self, obj):
2452+ return tuple([IOLogRecord(
2453+ # io logs namedtuple are recorded as list in json, using _asdict()
2454+ # would require too much space for little benefit.
2455+ # IOLogRecord are re created using the list ordering
2456+ log[0], log[1], base64.standard_b64decode(log[2].encode('ASCII')))
2457+ for log in super().decode(obj)])
2458
2459=== modified file 'plainbox/plainbox/impl/rfc822.py'
2460--- plainbox/plainbox/impl/rfc822.py 2012-12-18 00:26:03 +0000
2461+++ plainbox/plainbox/impl/rfc822.py 2013-03-07 16:10:38 +0000
2462@@ -18,12 +18,14 @@
2463 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
2464
2465 """
2466-plainbox.impl.rfc822
2467-====================
2468+:mod:`plainbox.impl.rfc822` -- RFC822 parser
2469+============================================
2470
2471 Implementation of rfc822 serializer and deserializer.
2472
2473- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
2474+.. warning::
2475+
2476+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
2477 """
2478
2479 import logging
2480@@ -37,10 +39,11 @@
2481 """
2482 Simple class for tracking where something came from
2483
2484- It has three attributes:
2485- filename - the name of the file
2486- line_start - the number of the line where the record begins
2487- line_end - the number of the line where the record ends
2488+ :ivar filename: the name of the file
2489+
2490+ :ivar line_start: the number of the line where the record begins
2491+
2492+ :ivar line_end: the number of the line where the record ends
2493 """
2494
2495 __slots__ = ['filename', 'line_start', 'line_end']
2496
2497=== modified file 'plainbox/plainbox/impl/runner.py'
2498--- plainbox/plainbox/impl/runner.py 2013-02-22 16:26:25 +0000
2499+++ plainbox/plainbox/impl/runner.py 2013-03-07 16:10:38 +0000
2500@@ -18,16 +18,17 @@
2501 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
2502
2503 """
2504-plainbox.impl.runner
2505-====================
2506-
2507-Internal implementation of plainbox
2508-
2509- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
2510+:mod:`plainbox.impl.runner` -- job runner
2511+=========================================
2512+
2513+.. warning::
2514+
2515+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
2516 """
2517
2518 import collections
2519 import datetime
2520+import json
2521 import logging
2522 import os
2523 import string
2524@@ -35,7 +36,7 @@
2525 from plainbox.vendor import extcmd
2526
2527 from plainbox.abc import IJobRunner
2528-from plainbox.impl.result import JobResult, IOLogRecord
2529+from plainbox.impl.result import JobResult, IOLogRecord, IoLogEncoder
2530
2531 logger = logging.getLogger("plainbox.runner")
2532
2533@@ -50,6 +51,15 @@
2534 return ''.join(c if c in valid_chars else '_' for c in _string)
2535
2536
2537+def io_log_write(log, stream):
2538+ """
2539+ JSON call to serialize io_log objects to disk
2540+ """
2541+ json.dump(
2542+ log, stream, ensure_ascii=False, indent=None, cls=IoLogEncoder,
2543+ separators=(',', ':'))
2544+
2545+
2546 class CommandIOLogBuilder(extcmd.DelegateBase):
2547 """
2548 Delegate for extcmd that builds io_log entries.
2549@@ -146,15 +156,24 @@
2550 def __init__(self, prompt):
2551 self._prompt = prompt
2552 self._lineno = collections.defaultdict(int)
2553+ self._abort = False
2554
2555 def on_line(self, stream_name, line):
2556+ if self._abort:
2557+ return
2558 self._lineno[stream_name] += 1
2559- print("(job {}, <{}:{:05}>) {}".format(
2560- self._prompt, stream_name, self._lineno[stream_name],
2561- line.rstrip()))
2562+ try:
2563+ print("(job {}, <{}:{:05}>) {}".format(
2564+ self._prompt, stream_name, self._lineno[stream_name],
2565+ line.decode('UTF-8').rstrip()))
2566+ except UnicodeDecodeError:
2567+ self._abort = True
2568
2569
2570 class JobRunner(IJobRunner):
2571+ """
2572+ Runner for jobs - executes jobs and produces results
2573+ """
2574
2575 def __init__(self, session_dir, jobs_io_log_dir,
2576 command_io_delegate=None, outcome_callback=None):
2577@@ -173,6 +192,9 @@
2578 self._outcome_callback = outcome_callback
2579
2580 def run_job(self, job):
2581+ """
2582+ Run the specified job an return the result
2583+ """
2584 logger.info("Running %r", job)
2585 func_name = "_plugin_" + job.plugin.replace('-', '_')
2586 try:
2587@@ -189,6 +211,8 @@
2588 def _plugin_shell(self, job):
2589 return self._just_run_command(job)
2590
2591+ _plugin_attachment = _plugin_shell
2592+
2593 def _plugin_resource(self, job):
2594 return self._just_run_command(job)
2595
2596@@ -227,7 +251,7 @@
2597 'io_log': io_log
2598 })
2599
2600- def _get_script_env(self, job):
2601+ def _get_script_env(self, job, only_changes=True):
2602 """
2603 Compute the environment the script will be executed in
2604 """
2605@@ -237,7 +261,17 @@
2606 env['LANG'] = 'C.UTF-8'
2607 # Allow the job to customize anything
2608 job.modify_execution_environment(env, self._session_dir)
2609- return env
2610+ # If a differential environment is requested return only the subset
2611+ # that has been altered.
2612+ #
2613+ # XXX: This will effectively give the root user our PATH which _may_ be
2614+ # good bud _might_ be dangerous. This will need some peer review.
2615+ if only_changes:
2616+ return {key: value
2617+ for key, value in env.items()
2618+ if key not in os.environ or os.environ[key] != value}
2619+ else:
2620+ return env
2621
2622 def _run_command(self, job):
2623 """
2624@@ -278,7 +312,7 @@
2625 # Send the third copy to the output writer that writes everything to
2626 # disk.
2627 delegate = extcmd.Chain([
2628- extcmd.Decode(ui_io_delegate),
2629+ ui_io_delegate,
2630 io_log_builder,
2631 output_writer])
2632 logger.debug("job[%s] extcmd delegate: %r", job.name, delegate)
2633@@ -291,14 +325,37 @@
2634 # threads although all callbacks will be fired from a single
2635 # thread (which is _not_ the main thread)
2636 logger.debug("job[%s] starting command: %s", job.name, job.command)
2637- return_code = logging_popen.call(
2638- # XXX: sadly using /bin/sh results in broken output
2639- # XXX: maybe run it both ways and raise exceptions on differences?
2640- ['bash', '-c', job.command],
2641- env=self._get_script_env(job))
2642+ # XXX: sadly using /bin/sh results in broken output
2643+ # XXX: maybe run it both ways and raise exceptions on differences?
2644+ cmd = ['bash', '-c', job.command]
2645+ if job.user is not None:
2646+ # When the job requires to run as root then elevate our permissions
2647+ # via pkexec(1). Since pkexec resets environment we need to somehow
2648+ # pass the extra things we require. To do that we use the env(1)
2649+ # command and pass it the list of changed environment variables.
2650+ #
2651+ # The whole pkexec and env part gets prepended to the command we
2652+ # were supposed to run.
2653+ cmd = ['pkexec', '--user', job.user, 'env'] + [
2654+ "{key}={value}".format(key=key, value=value)
2655+ for key, value in self._get_script_env(
2656+ job, only_changes=True
2657+ ).items()
2658+ ] + cmd
2659+ logging.debug("job[%s] executing %r", job.name, cmd)
2660+ return_code = logging_popen.call(cmd)
2661+ else:
2662+ logging.debug("job[%s] executing %r", job.name, cmd)
2663+ return_code = logging_popen.call(
2664+ cmd, env=self._get_script_env(job))
2665 logger.debug("job[%s] command return code: %r",
2666 job.name, return_code)
2667 # XXX: Perhaps handle process dying from signals here
2668 # When the process is killed proc.returncode is not set
2669 # and another (cannot remember now) attribute is set
2670- return return_code, io_log_builder.io_log
2671+ fjson = os.path.join(self._jobs_io_log_dir, "{}.json".format(slug))
2672+ with open(fjson, "wt") as stream:
2673+ io_log_write(io_log_builder.io_log, stream)
2674+ stream.flush()
2675+ os.fsync(stream.fileno())
2676+ return return_code, fjson
2677
2678=== modified file 'plainbox/plainbox/impl/session.py'
2679--- plainbox/plainbox/impl/session.py 2013-02-22 16:26:25 +0000
2680+++ plainbox/plainbox/impl/session.py 2013-03-07 16:10:38 +0000
2681@@ -18,12 +18,12 @@
2682 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
2683
2684 """
2685-plainbox.impl.session
2686-=====================
2687-
2688-Internal implementation of plainbox
2689-
2690- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
2691+:mod:`plainbox.impl.session` -- session state handling
2692+======================================================
2693+
2694+.. warning::
2695+
2696+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
2697 """
2698 import json
2699 import logging
2700@@ -165,7 +165,7 @@
2701 """
2702 Class representing the state of a job in a session.
2703
2704- Contains two basic properties of each job (either of which can be None):
2705+ Contains two basic properties of each job:
2706
2707 * the readiness_inhibitor_list that prevent the job form starting
2708 * the result (outcome) of the run (IJobResult)
2709@@ -223,7 +223,7 @@
2710 return self._result
2711
2712 def fset(self, value):
2713- if value.job is not self.job:
2714+ if value.job.get_checksum() != self.job.get_checksum():
2715 raise ValueError("result job does not match")
2716 self._result = value
2717
2718@@ -278,53 +278,79 @@
2719 """
2720 Class representing all state needed during a single program session.
2721
2722+ This is the central glue/entry-point for applications. It connects user
2723+ intents to the rest of the system / plumbing and keeps all of the state in
2724+ one place.
2725+
2726 The set of utility methods and properties allow applications to easily
2727 handle the lower levels of dependencies, resources and ready states.
2728
2729- Once instantiated with a list of known jobs it is ready to react to
2730- UI-driven changes. It is expected that the user will select / unselect
2731- and run jobs. This class can react to both actions by recomputing the
2732- dependency graph and updating the read states accordingly.
2733-
2734- Ready states (one for each job) allow the UI to take simple decisions
2735- (either can or cannot run)
2736+ SessionState has the following instance variables, all of which are
2737+ currently exposed as properties.
2738+
2739+ :ivar list job_list: A list of all known jobs
2740+
2741+ Not all the jobs from this list are going to be executed (or selected
2742+ for execution) by the user.
2743+
2744+ It may change at runtime because of local jobs. Note that in upcoming
2745+ changes this will start out empty and will be changeable dynamically.
2746+ It can still change due to local jobs but there is no API yes.
2747+
2748+ :ivar dict job_state_map: mapping that tracks the state of each job
2749+
2750+ Mapping from job name to :class:`JobState`. This basically has the test
2751+ result and the inhibitor of each job. It also serves as a
2752+ :attr:`plainbox.impl.job.JobDefinition.name`-> job lookup helper.
2753+
2754+ Directly exposed with the intent to fuel part of the UI. This is a way
2755+ to get at the readiness state, result and readiness inhibitors, if any.
2756+
2757+ XXX: this can loose data job_list has jobs with the same name. It would
2758+ be better to use job id as the keys here. A separate map could be used
2759+ for the name->job lookup. This will be fixed when session controller
2760+ branch lands in trunk as then jobs are dynamically added to the system
2761+ one at a time and proper error conditions can be detected and reported.
2762+
2763+ :ivar list desired_job_list: subset of jobs selected for execution
2764+
2765+ This is used to compute :attr:`run_list`. It can only be changed by
2766+ calling :meth:`update_desired_job_list()` which returns meaningful
2767+ values so this is not a settable property.
2768+
2769+ :ivar list run_list: sorted list of jobs to execute
2770+
2771+ This is basically a superset of desired_job_list and a subset of
2772+ job_list that is topologically sorted to allowing all desired jobs to
2773+ run. This property is updated whenever desired_job_list is changed.
2774+
2775+ :ivar dict resource_map: all known resources
2776+
2777+ A mapping from resource name to a list of
2778+ :class:`plainbox.impl.resource.Resource` objects. This encapsulates all
2779+ "knowledge" about the system plainbox is running on.
2780+
2781+ It is needed to compute job readiness (as it stores resource data
2782+ needed by resource programs). It is also available to exporters.
2783+
2784+ This is computed internally from the output of checkbox resource jobs,
2785+ it can only be changed by calling :meth:`update_job_result()`
2786 """
2787
2788 session_data_filename = 'session.json'
2789
2790 def __init__(self, job_list):
2791- # The original list of job that the system knows about.
2792- # Not all jobs from this list are going to be executed
2793- # (or selected for execution) by the user.
2794 self._job_list = job_list
2795- # State of each job, see JobState for details but it basically
2796- # has the test result and the inhibitor of each job. It also serves
2797- # as a job.name -> job lookup helper.
2798- #
2799- # Directly exposed with the intent to fuel part of the UI.
2800- #
2801- # XXX: this can loose data job_list has jobs with the same name. It
2802- # would be better to use job id as the keys here. A separate map could
2803- # be used for the name->job lookup.
2804 self._job_state_map = {job.name: JobState(job)
2805 for job in self._job_list}
2806- # A subset of job_list that was selected by the user for execution.
2807- # Used to compute run_list. Can be changed at will during lifetime
2808- # of this object
2809 self._desired_job_list = []
2810- # Copy of desired_job_list that was topologically sorted by the
2811- # dependency solver. Jobs must run in this order (although not all jobs
2812- # may actually run or will actually be successful)
2813 self._run_list = []
2814- # A collection of known resources. Mapping resource job name to a list
2815- # of resource objects. Needed to compute task readiness (as it stores
2816- # resource data needed by resource programs). Currently not exposed
2817- # outside of this class.
2818 self._resource_map = {}
2819 # Temporary directory used as 'scratch space' for running jobs. Removed
2820 # entirely when session is terminated. Internally this is exposed as
2821 # $CHECKBOX_DATA to script environment.
2822 self._session_dir = None
2823+
2824 # Directory used to store jobs IO logs.
2825 self._jobs_io_log_dir = None
2826
2827@@ -355,28 +381,34 @@
2828 if self._session_dir is None:
2829 xdg_cache_home = os.environ.get('XDG_CACHE_HOME') or \
2830 os.path.join(os.path.expanduser('~'), '.cache')
2831- temp_dir = os.path.join(xdg_cache_home, 'plainbox')
2832- if not os.path.isdir(temp_dir):
2833- os.makedirs(temp_dir)
2834- self._session_dir = tempfile.mkdtemp(dir=temp_dir)
2835+ self._session_dir = os.path.join(xdg_cache_home, 'plainbox')
2836+ if not os.path.isdir(self._session_dir):
2837+ os.makedirs(self._session_dir)
2838 if self._jobs_io_log_dir is None:
2839 self._jobs_io_log_dir = os.path.join(self._session_dir, 'io-logs')
2840 if not os.path.isdir(self._jobs_io_log_dir):
2841 os.makedirs(self._jobs_io_log_dir)
2842 return self
2843
2844- def close(self):
2845+ def clean(self):
2846 """
2847- Close the session and remove temporary disk state.
2848-
2849- This function removes the directory created by .open() and all the data
2850- that was placed there. It is automatically called by __exit__, the
2851- context manager exit function. Care should be taken to ensure that all
2852- session data, particularly attachments, were saved before.
2853+ Clean the session directory.
2854 """
2855 if self._session_dir is not None:
2856 shutil.rmtree(self._session_dir)
2857 self._session_dir = None
2858+ self._jobs_io_log_dir = None
2859+ self.open()
2860+
2861+ def close(self):
2862+ """
2863+ Close the session.
2864+
2865+ It is automatically called by __exit__, the context manager exit
2866+ function.
2867+ """
2868+ self._session_dir = None
2869+ self._jobs_io_log_dir = None
2870
2871 def update_desired_job_list(self, desired_job_list):
2872 """
2873@@ -405,7 +437,7 @@
2874 # resources or runtime complexity.
2875 try:
2876 self._run_list = DependencySolver.resolve_dependencies(
2877- self._job_list, desired_job_list)
2878+ self._job_list, self._desired_job_list)
2879 except DependencyError as exc:
2880 # When a dependency error is detected remove the affected job
2881 # form _desired_job_list and try again.
2882@@ -457,11 +489,22 @@
2883 # Update all job readiness state
2884 self._recompute_job_readiness()
2885
2886+ def previous_session_file(self):
2887+ """
2888+ Check the filesystem for previous session data
2889+ Returns the full pathname to the session file if it exists
2890+ """
2891+ session_filename = os.path.join(self._session_dir,
2892+ self.session_data_filename)
2893+ if os.path.exists(session_filename):
2894+ return session_filename
2895+ else:
2896+ return None
2897+
2898 def persistent_save(self):
2899 """
2900 Save to disk the minimum needed to resume plainbox where it stopped
2901 """
2902-
2903 # Ensure an atomic update of the session file:
2904 # - create a new temp file (on the same file system!)
2905 # - write data to the temp file
2906@@ -472,7 +515,6 @@
2907 # directory containing the file has also reached disk.
2908 # For that an explicit fsync() on a file descriptor for the directory
2909 # is also needed.
2910-
2911 filename = os.path.join(self._session_dir,
2912 self.session_data_filename)
2913
2914@@ -493,6 +535,28 @@
2915 os.fsync(session_dir_fd)
2916 os.close(session_dir_fd)
2917
2918+ def resume(self):
2919+ """
2920+ Erase the job_state_map and desired_job_list with the saved ones
2921+ """
2922+ with open(self.previous_session_file(), 'r') as f:
2923+ previous_session = json.load(
2924+ f, object_hook=SessionStateEncoder().dict_to_object)
2925+ self._job_state_map = previous_session._job_state_map
2926+ desired_job_list = []
2927+ for job in previous_session._desired_job_list:
2928+ if job in self._job_list:
2929+ desired_job_list.extend(
2930+ [j for j in self._job_list if j == job])
2931+ elif (previous_session._job_state_map[job.name].result.outcome !=
2932+ JobResult.OUTCOME_NONE):
2933+ # Keep jobs results from the previous session without a
2934+ # definition in the current job_list only if they have
2935+ # a valid result
2936+ desired_job_list.append(job)
2937+ self.update_desired_job_list(desired_job_list)
2938+ # FIXME: Restore io_logs from files
2939+
2940 def _process_resource_result(self, result):
2941 new_resource_list = []
2942 for record in self._gen_rfc822_records_from_io_log(result):
2943@@ -686,7 +750,6 @@
2944 job_state.readiness_inhibitor_list.append(inhibitor)
2945
2946 def __enter__(self):
2947- self.open()
2948 return self
2949
2950 def __exit__(self, *args):
2951
2952=== modified file 'plainbox/plainbox/impl/test_box.py'
2953--- plainbox/plainbox/impl/test_box.py 2013-02-22 16:26:25 +0000
2954+++ plainbox/plainbox/impl/test_box.py 2013-03-07 16:10:38 +0000
2955@@ -24,15 +24,91 @@
2956 Test definitions for plainbox.impl.box module
2957 """
2958
2959+import os
2960+import shutil
2961+import tempfile
2962+
2963+from inspect import cleandoc
2964+from mock import Mock
2965 from unittest import TestCase
2966-from inspect import cleandoc
2967-
2968
2969 from plainbox import __version__ as version
2970-from plainbox.impl.box import main
2971+from plainbox.impl.box import main, CheckBoxCommandMixIn
2972+from plainbox.impl.mock_job import MockJobDefinition
2973 from plainbox.testing_utils.io import TestIO
2974
2975
2976+class MiscTests(TestCase):
2977+
2978+ def setUp(self):
2979+ self.job_foo = MockJobDefinition(name='foo')
2980+ self.job_bar = MockJobDefinition(name='bar')
2981+ self.obj = CheckBoxCommandMixIn(Mock(name="checkbox"))
2982+
2983+ def test_matching_job_list(self):
2984+ # Nothing gets selected automatically
2985+ ns = Mock()
2986+ ns.whitelist = None
2987+ ns.include_pattern_list = []
2988+ ns.exclude_pattern_list = []
2989+ observed = self.obj._get_matching_job_list(ns, [
2990+ self.job_foo, self.job_bar])
2991+ self.assertEqual(observed, [])
2992+
2993+ def test_matching_job_list_including(self):
2994+ # Including jobs with glob pattern works
2995+ ns = Mock()
2996+ ns.whitelist = None
2997+ ns.include_pattern_list = ['f.+']
2998+ ns.exclude_pattern_list = []
2999+ observed = self.obj._get_matching_job_list(ns, [
3000+ self.job_foo, self.job_bar])
3001+ self.assertEqual(observed, [self.job_foo])
3002+
3003+ def test_matching_job_list_excluding(self):
3004+ # Excluding jobs with glob pattern works
3005+ ns = Mock()
3006+ ns.whitelist = None
3007+ ns.include_pattern_list = ['.+']
3008+ ns.exclude_pattern_list = ['f.+']
3009+ observed = self.obj._get_matching_job_list(ns, [
3010+ self.job_foo, self.job_bar])
3011+ self.assertEqual(observed, [self.job_bar])
3012+
3013+ def test_matching_job_list_whitelist(self):
3014+ # whitelists contain list of include patterns
3015+ # that are read and interpreted as usual
3016+ whitelist = Mock()
3017+ whitelist.readlines.return_value = ['foo']
3018+ ns = Mock()
3019+ ns.whitelist = whitelist
3020+ ns.include_pattern_list = []
3021+ ns.exclude_pattern_list = []
3022+ observed = self.obj._get_matching_job_list(ns, [
3023+ self.job_foo, self.job_bar])
3024+ self.assertEqual(observed, [self.job_foo])
3025+
3026+ def test_no_prefix_matching_including(self):
3027+ # Include patterns should only match whole job name
3028+ ns = Mock()
3029+ ns.whitelist = None
3030+ ns.include_pattern_list = ['fo', 'ba.+']
3031+ ns.exclude_pattern_list = []
3032+ observed = self.obj._get_matching_job_list(ns, [self.job_foo,
3033+ self.job_bar])
3034+ self.assertEqual(observed, [self.job_bar])
3035+
3036+ def test_no_prefix_matching_excluding(self):
3037+ # Exclude patterns should only match whole job name
3038+ ns = Mock()
3039+ ns.whitelist = None
3040+ ns.include_pattern_list = ['.+']
3041+ ns.exclude_pattern_list = ['fo', 'ba.+']
3042+ observed = self.obj._get_matching_job_list(ns, [self.job_foo,
3043+ self.job_bar])
3044+ self.assertEqual(observed, [self.job_foo])
3045+
3046+
3047 class TestMain(TestCase):
3048
3049 def test_version(self):
3050@@ -85,7 +161,7 @@
3051 self.maxDiff = None
3052 expected = """
3053 usage: plainbox special [-h] (-j | -e | -d) [--dot-resources] [-i PATTERN]
3054- [-W WHITELIST]
3055+ [-x PATTERN] [-w WHITELIST]
3056
3057 optional arguments:
3058 -h, --help show this help message and exit
3059@@ -97,8 +173,12 @@
3060
3061 job definition options:
3062 -i PATTERN, --include-pattern PATTERN
3063- Run jobs matching the given pattern
3064- -W WHITELIST, --whitelist WHITELIST
3065+ Run jobs matching the given regular expression.
3066+ Matches from the start to the end of the line.
3067+ -x PATTERN, --exclude-pattern PATTERN
3068+ Do not run jobs matching the given regular expression.
3069+ Matches from the start to the end of the line.
3070+ -w WHITELIST, --whitelist WHITELIST
3071 Load whitelist containing run patterns
3072 """
3073 self.assertEqual(io.combined, cleandoc(expected) + "\n")
3074@@ -110,7 +190,7 @@
3075 self.assertEqual(call.exception.args, (2,))
3076 expected = """
3077 usage: plainbox special [-h] (-j | -e | -d) [--dot-resources] [-i PATTERN]
3078- [-W WHITELIST]
3079+ [-x PATTERN] [-w WHITELIST]
3080 plainbox special: error: one of the arguments -j/--list-jobs -e/--list-expressions -d/--dot is required
3081 """
3082 self.assertEqual(io.combined, cleandoc(expected) + "\n")
3083@@ -130,7 +210,7 @@
3084 def test_run_list_jobs_with_filtering(self):
3085 with TestIO() as io:
3086 with self.assertRaises(SystemExit) as call:
3087- main(['special', '--include-pattern=usb3*', '--list-jobs'])
3088+ main(['special', '--include-pattern=usb3.+', '--list-jobs'])
3089 self.assertEqual(call.exception.args, (0,))
3090 # Test that usb3 insertion test was listed but the usb (2.0) test was not
3091 self.assertIn("usb3/insert", io.stdout.splitlines())
3092@@ -181,6 +261,14 @@
3093
3094 class TestRun(TestCase):
3095
3096+ def setUp(self):
3097+ # session data are kept in XDG_CACHE_HOME/plainbox/.session
3098+ # To avoid resuming a real session, we have to select a temporary
3099+ # location instead
3100+ self._sandbox = tempfile.mkdtemp()
3101+ self._env = os.environ
3102+ os.environ['XDG_CACHE_HOME'] = self._sandbox
3103+
3104 def test_help(self):
3105 with TestIO(combined=True) as io:
3106 with self.assertRaises(SystemExit) as call:
3107@@ -189,7 +277,7 @@
3108 self.maxDiff = None
3109 expected = """
3110 usage: plainbox run [-h] [--not-interactive] [-n] [-f FORMAT] [-p OPTIONS]
3111- [-o FILE] [-i PATTERN] [-W WHITELIST]
3112+ [-o FILE] [-i PATTERN] [-x PATTERN] [-w WHITELIST]
3113
3114 optional arguments:
3115 -h, --help show this help message and exit
3116@@ -211,8 +299,12 @@
3117
3118 job definition options:
3119 -i PATTERN, --include-pattern PATTERN
3120- Run jobs matching the given pattern
3121- -W WHITELIST, --whitelist WHITELIST
3122+ Run jobs matching the given regular expression.
3123+ Matches from the start to the end of the line.
3124+ -x PATTERN, --exclude-pattern PATTERN
3125+ Do not run jobs matching the given regular expression.
3126+ Matches from the start to the end of the line.
3127+ -w WHITELIST, --whitelist WHITELIST
3128 Load whitelist containing run patterns
3129 """
3130 self.assertEqual(io.combined, cleandoc(expected) + "\n")
3131@@ -246,8 +338,12 @@
3132 self.assertEqual(call.exception.args, (0,))
3133 expected = """
3134 Each format may support a different set of options
3135- json: with-io-log, squash-io-log, flatten-io-log, with-run-list, with-job-list, with-resource-map, with-job-defs, machine-json
3136- rfc822: with-io-log, squash-io-log, flatten-io-log, with-run-list, with-job-list, with-resource-map, with-job-defs
3137- text: with-io-log, squash-io-log, flatten-io-log, with-run-list, with-job-list, with-resource-map, with-job-defs
3138+ json: with-io-log, squash-io-log, flatten-io-log, with-run-list, with-job-list, with-resource-map, with-job-defs, with-attachments, machine-json
3139+ rfc822: with-io-log, squash-io-log, flatten-io-log, with-run-list, with-job-list, with-resource-map, with-job-defs, with-attachments
3140+ text: with-io-log, squash-io-log, flatten-io-log, with-run-list, with-job-list, with-resource-map, with-job-defs, with-attachments
3141 """
3142 self.assertEqual(io.combined, cleandoc(expected) + "\n")
3143+
3144+ def tearDown(self):
3145+ shutil.rmtree(self._sandbox)
3146+ os.environ = self._env
3147
3148=== modified file 'plainbox/plainbox/impl/test_job.py'
3149--- plainbox/plainbox/impl/test_job.py 2013-02-22 16:26:25 +0000
3150+++ plainbox/plainbox/impl/test_job.py 2013-03-07 16:10:38 +0000
3151@@ -239,8 +239,7 @@
3152 job_enc = job._get_persistance_subset()
3153 self.assertEqual(job_enc['data']['plugin'], job.plugin)
3154 self.assertEqual(job_enc['data']['name'], job.name)
3155- with self.assertRaises(KeyError):
3156- job_enc['requires']
3157+ self.assertEqual(job_enc['data']['requires'], job.requires)
3158 with self.assertRaises(KeyError):
3159 job_enc['depends']
3160 with self.assertRaises(KeyError):
3161@@ -280,7 +279,8 @@
3162 "plugin": "user-verify"
3163 }
3164 }"""
3165- job_dec = json.loads(raw_json, object_hook=SessionStateEncoder().dict_to_object)
3166+ job_dec = json.loads(raw_json,
3167+ object_hook=SessionStateEncoder().dict_to_object)
3168 self.assertIsInstance(job_dec, JobDefinition)
3169 self.assertEqual(job_dec.name, "camera/still")
3170 self.assertEqual(job_dec.plugin, "user-verify")
3171
3172=== modified file 'plainbox/plainbox/impl/test_result.py'
3173--- plainbox/plainbox/impl/test_result.py 2013-02-22 16:26:25 +0000
3174+++ plainbox/plainbox/impl/test_result.py 2013-03-07 16:10:38 +0000
3175@@ -25,10 +25,11 @@
3176 """
3177 import json
3178
3179+from tempfile import TemporaryDirectory
3180 from unittest import TestCase
3181
3182 from plainbox.impl.result import JobResult
3183-from plainbox.impl.testing_utils import make_job
3184+from plainbox.impl.testing_utils import make_io_log, make_job
3185 from plainbox.impl.session import SessionStateEncoder
3186
3187
3188@@ -50,22 +51,24 @@
3189 self.assertIsNone(result.return_code)
3190
3191 def test_everything(self):
3192- result = JobResult({
3193- 'job': self.job,
3194- 'outcome': JobResult.OUTCOME_PASS,
3195- 'comments': "it said blah",
3196- 'io_log': ((0, 'stdout', b'blah\n'),),
3197- 'return_code': 0
3198- })
3199- self.assertEqual(str(result), "A: pass")
3200- self.assertEqual(repr(result), (
3201- "<JobResult job:<JobDefinition name:'A' plugin:'dummy'>"
3202- " outcome:'pass'>"))
3203- self.assertIs(result.job, self.job)
3204- self.assertEqual(result.outcome, JobResult.OUTCOME_PASS)
3205- self.assertEqual(result.comments, "it said blah")
3206- self.assertEqual(result.io_log, ((0, 'stdout', b'blah\n'),))
3207- self.assertEqual(result.return_code, 0)
3208+ with TemporaryDirectory() as scratch_dir:
3209+ result = JobResult({
3210+ 'job': self.job,
3211+ 'outcome': JobResult.OUTCOME_PASS,
3212+ 'comments': "it said blah",
3213+ 'io_log': make_io_log(((0, 'stdout', b'blah\n'),),
3214+ scratch_dir),
3215+ 'return_code': 0
3216+ })
3217+ self.assertEqual(str(result), "A: pass")
3218+ self.assertEqual(repr(result), (
3219+ "<JobResult job:<JobDefinition name:'A' plugin:'dummy'>"
3220+ " outcome:'pass'>"))
3221+ self.assertIs(result.job, self.job)
3222+ self.assertEqual(result.outcome, JobResult.OUTCOME_PASS)
3223+ self.assertEqual(result.comments, "it said blah")
3224+ self.assertEqual(result.io_log, ((0, 'stdout', b'blah\n'),))
3225+ self.assertEqual(result.return_code, 0)
3226
3227 def test_encode(self):
3228 result = JobResult({
3229@@ -99,7 +102,8 @@
3230 "return_code": 0
3231 }
3232 }"""
3233- result_dec = json.loads(raw_json, object_hook=SessionStateEncoder().dict_to_object)
3234+ result_dec = json.loads(raw_json,
3235+ object_hook=SessionStateEncoder().dict_to_object)
3236 self.assertIsInstance(result_dec, JobResult)
3237 self.assertEqual(result_dec.job.name, "__audio__")
3238 self.assertEqual(result_dec.outcome, JobResult.OUTCOME_PASS)
3239
3240=== modified file 'plainbox/plainbox/impl/test_runner.py'
3241--- plainbox/plainbox/impl/test_runner.py 2013-02-22 16:26:25 +0000
3242+++ plainbox/plainbox/impl/test_runner.py 2013-03-07 16:10:38 +0000
3243@@ -73,11 +73,11 @@
3244 with TestIO(combined=False) as io:
3245 obj = FallbackCommandOutputPrinter("example")
3246 # Whatever gets printed by the job...
3247- obj.on_line('stdout', 'line 1\n')
3248- obj.on_line('stderr', 'line 1\n')
3249- obj.on_line('stdout', 'line 2\n')
3250- obj.on_line('stdout', 'line 3\n')
3251- obj.on_line('stderr', 'line 2\n')
3252+ obj.on_line('stdout', b'line 1\n')
3253+ obj.on_line('stderr', b'line 1\n')
3254+ obj.on_line('stdout', b'line 2\n')
3255+ obj.on_line('stdout', b'line 3\n')
3256+ obj.on_line('stderr', b'line 2\n')
3257 # Gets printed to stdout _only_, stderr is combined with stdout here
3258 self.assertEqual(io.stdout, (
3259 "(job example, <stdout:00001>) line 1\n"
3260
3261=== modified file 'plainbox/plainbox/impl/test_session.py'
3262--- plainbox/plainbox/impl/test_session.py 2013-02-22 16:26:25 +0000
3263+++ plainbox/plainbox/impl/test_session.py 2013-03-07 16:10:38 +0000
3264@@ -25,18 +25,22 @@
3265 """
3266
3267 import json
3268+import os
3269+import tempfile
3270+import shutil
3271
3272+from tempfile import TemporaryDirectory
3273 from unittest import TestCase
3274
3275+from plainbox.impl.depmgr import DependencyMissingError
3276 from plainbox.impl.resource import Resource
3277 from plainbox.impl.result import JobResult
3278 from plainbox.impl.session import JobReadinessInhibitor
3279 from plainbox.impl.session import JobState
3280 from plainbox.impl.session import SessionState
3281+from plainbox.impl.session import SessionStateEncoder
3282 from plainbox.impl.session import UndesiredJobReadinessInhibitor
3283-from plainbox.impl.session import SessionStateEncoder
3284-from plainbox.impl.testing_utils import make_job
3285-from plainbox.impl.testing_utils import make_job_result
3286+from plainbox.impl.testing_utils import make_io_log, make_job, make_job_result
3287
3288
3289 class JobReadinessInhibitorTests(TestCase):
3290@@ -236,7 +240,8 @@
3291 }
3292 }
3293 }"""
3294- job_dec = json.loads(raw_json, object_hook=SessionStateEncoder().dict_to_object)
3295+ job_dec = json.loads(raw_json,
3296+ object_hook=SessionStateEncoder().dict_to_object)
3297 self.assertIsInstance(job_dec, JobState)
3298 self.assertEqual(repr(job_dec._result),
3299 ("<JobResult job:<JobDefinition name:'X'"
3300@@ -271,6 +276,23 @@
3301 self.assertIsNone(self.session_state.session_dir)
3302
3303
3304+class RegressionTests(TestCase):
3305+ # Tests for bugfixes
3306+
3307+ def test_crash_in_update_desired_job_list(self):
3308+ # This checks if a DependencyError can cause crash
3309+ # update_desired_job_list() with a ValueError, in certain conditions.
3310+ A = make_job('A', depends='X')
3311+ L = make_job('L', plugin='local')
3312+ session = SessionState([A, L])
3313+ problems = session.update_desired_job_list([A, L])
3314+ # We should get exactly one DependencyMissingError related to job A and
3315+ # the undefined job X (that is presumably defined by the local job L)
3316+ self.assertEqual(len(problems), 1)
3317+ self.assertIsInstance(problems[0], DependencyMissingError)
3318+ self.assertIs(problems[0].affected_job, A)
3319+
3320+
3321 class SessionStateSpecialTests(TestCase):
3322
3323 # NOTE: those tests are essential. They allow testing the behavior of
3324@@ -326,6 +348,7 @@
3325 self.job_Y = make_job("Y")
3326 self.job_list = [self.job_A, self.job_R, self.job_X, self.job_Y]
3327 self.session = SessionState(self.job_list)
3328+ self.scratch_dir = TemporaryDirectory()
3329
3330 def job_state(self, name):
3331 # A helper function to avoid overly long expressions
3332@@ -391,7 +414,8 @@
3333 # session.
3334 result_R = JobResult({
3335 'job': self.job_R,
3336- 'io_log': ((0, 'stdout', b"attr: value\n"),)
3337+ 'io_log': make_io_log(((0, 'stdout', b"attr: value\n"),),
3338+ self.scratch_dir)
3339 })
3340 self.session.update_job_result(self.job_R, result_R)
3341 # The most obvious thing that can happen, is that the result is simply
3342@@ -439,12 +463,13 @@
3343 # another proper record in that order.
3344 result_R = JobResult({
3345 'job': self.job_R,
3346- 'io_log': (
3347- (0, 'stdout', b"attr: value-1\n"),
3348- (1, 'stdout', b"\n"),
3349- (1, 'stdout', b"I-sound-like-a-broken-record\n"),
3350- (1, 'stdout', b"\n"),
3351- (1, 'stdout', b"attr: value-2\n"))
3352+ 'io_log': make_io_log((
3353+ (0, 'stdout', b"attr: value-1\n"),
3354+ (1, 'stdout', b"\n"),
3355+ (1, 'stdout', b"I-sound-like-a-broken-record\n"),
3356+ (1, 'stdout', b"\n"),
3357+ (1, 'stdout', b"attr: value-2\n")),
3358+ self.scratch_dir)
3359 })
3360 # Since we cannot control the output of scripts and people indeed make
3361 # mistakes a warning is issued but no exception is raised to the
3362@@ -521,7 +546,8 @@
3363 self.session.update_desired_job_list([self.job_A])
3364 result_R = JobResult({
3365 'job': self.job_R,
3366- 'io_log': ((0, 'stdout', b'attr: wrong value\n'),)
3367+ 'io_log': make_io_log(((0, 'stdout', b'attr: wrong value\n'),),
3368+ self.scratch_dir)
3369 })
3370 self.session.update_job_result(self.job_R, result_R)
3371 # Now A is inhibited by FAILED_RESOURCE
3372@@ -538,7 +564,8 @@
3373 # presented to a session that has some resources from that job already.
3374 result_R_old = JobResult({
3375 'job': self.job_R,
3376- 'io_log': ((0, 'stdout', b"attr: old value\n"),)
3377+ 'io_log': make_io_log(((0, 'stdout', b"attr: old value\n"),),
3378+ self.scratch_dir)
3379 })
3380 self.session.update_job_result(self.job_R, result_R_old)
3381 # So here the old result is stored into a new 'R' resource
3382@@ -547,7 +574,8 @@
3383 # Now we present the second result for the same job
3384 result_R_new = JobResult({
3385 'job': self.job_R,
3386- 'io_log': ((0, 'stdout', b"attr: new value\n"),)
3387+ 'io_log': make_io_log(((0, 'stdout', b"attr: new value\n"),),
3388+ self.scratch_dir)
3389 })
3390 self.session.update_job_result(self.job_R, result_R_new)
3391 # What should happen here is that the R resource is entirely replaced
3392@@ -557,3 +585,149 @@
3393 self.assertEqual(self.session._resource_map, expected_after)
3394
3395 # TODO: add tests for local jobs
3396+
3397+ def tearDown(self):
3398+ self.scratch_dir.cleanup()
3399+
3400+
3401+class SessionStateLocalStorageTests(TestCase):
3402+
3403+ def setUp(self):
3404+ # session data are kept in XDG_CACHE_HOME/plainbox/.session
3405+ # To avoid resuming a real session, we have to select a temporary
3406+ # location instead
3407+ self._sandbox = tempfile.mkdtemp()
3408+ self._env = os.environ
3409+ os.environ['XDG_CACHE_HOME'] = self._sandbox
3410+
3411+ def job_state(self, name):
3412+ # A helper function to avoid overly long expressions
3413+ return self.session.job_state_map[name]
3414+
3415+ def test_persistent_save(self):
3416+ self.job_A = make_job("A")
3417+ self.job_list = [self.job_A]
3418+ self.session = SessionState(self.job_list)
3419+ result_A = JobResult({
3420+ 'job': self.job_A,
3421+ 'outcome': JobResult.OUTCOME_PASS,
3422+ 'comments': 'All good',
3423+ 'return_code': 0,
3424+ 'io_log': ((0, 'stdout', "Success !\n"),)
3425+ })
3426+ session_json_text = """{
3427+ "_job_state_map": {
3428+ "A": {
3429+ "_job": {
3430+ "data": {
3431+ "name": "A",
3432+ "plugin": "dummy",
3433+ "requires": null,
3434+ "depends": null
3435+ },
3436+ "_class_id": "JOB_DEFINITION"
3437+ },
3438+ "_result": {
3439+ "data": {
3440+ "job": {
3441+ "data": {
3442+ "name": "A",
3443+ "plugin": "dummy",
3444+ "requires": null,
3445+ "depends": null
3446+ },
3447+ "_class_id": "JOB_DEFINITION"
3448+ },
3449+ "outcome": "pass",
3450+ "return_code": 0,
3451+ "comments": "All good",
3452+ "io_log": [
3453+ [
3454+ 0,
3455+ "stdout",
3456+ "Success !\\n"
3457+ ]
3458+ ]
3459+ },
3460+ "_class_id": "JOB_RESULT"
3461+ },
3462+ "_class_id": "JOB_STATE"
3463+ }
3464+ },
3465+ "_desired_job_list": [
3466+ {
3467+ "data": {
3468+ "name": "A",
3469+ "plugin": "dummy",
3470+ "requires": null,
3471+ "depends": null
3472+ },
3473+ "_class_id": "JOB_DEFINITION"
3474+ }
3475+ ],
3476+ "_class_id": "SESSION_STATE"
3477+ }"""
3478+ self.session.open()
3479+ self.session.update_desired_job_list([self.job_A])
3480+ self.session.update_job_result(self.job_A, result_A)
3481+ self.session.persistent_save()
3482+ session_file = self.session.previous_session_file()
3483+ self.session.close()
3484+ self.assertIsNotNone(session_file)
3485+ with open(session_file) as f:
3486+ raw_json = json.load(f)
3487+ self.maxDiff = None
3488+ self.assertEqual(raw_json, json.loads(session_json_text))
3489+
3490+ def test_resume_session(self):
3491+ # All of the tests below are using one session. The session has four
3492+ # jobs, Job A depends on a resource provided by job R which has no
3493+ # dependencies at all. Both Job X and Y depend on job A.
3494+ #
3495+ # A -(resource dependency)-> R
3496+ #
3497+ # X -(direct dependency) -> A
3498+ #
3499+ # Y -(direct dependency) -> A
3500+ self.job_A = make_job("A", requires="R.attr == 'value'")
3501+ self.job_A_expr = self.job_A.get_resource_program().expression_list[0]
3502+ self.job_R = make_job("R", plugin="resource")
3503+ self.job_X = make_job("X", depends='A')
3504+ self.job_Y = make_job("Y", depends='A')
3505+ self.job_list = [self.job_A, self.job_R, self.job_X, self.job_Y]
3506+ # Create a new session (session_dir is empty)
3507+ self.session = SessionState(self.job_list)
3508+ result_R = JobResult({
3509+ 'job': self.job_R,
3510+ 'io_log': make_io_log(((0, 'stdout', b"attr: value\n"),),
3511+ self._sandbox)
3512+ })
3513+ result_A = JobResult({
3514+ 'job': self.job_A,
3515+ 'outcome': JobResult.OUTCOME_PASS
3516+ })
3517+ result_X = JobResult({
3518+ 'job': self.job_X,
3519+ 'outcome': JobResult.OUTCOME_PASS
3520+ })
3521+ # Job Y can't start as it requires job A
3522+ self.assertFalse(self.job_state('Y').can_start())
3523+ self.session.update_desired_job_list([self.job_X, self.job_Y])
3524+ self.session.open()
3525+ self.session.update_job_result(self.job_R, result_R)
3526+ self.session.update_job_result(self.job_A, result_A)
3527+ self.session.update_job_result(self.job_X, result_X)
3528+ self.session.persistent_save()
3529+ self.session.close()
3530+ # Create a new session (session_dir should contain session data)
3531+ self.session = SessionState(self.job_list)
3532+ self.session.open()
3533+ # Resume the previous session
3534+ self.session.resume()
3535+ # This time job Y can start
3536+ self.assertTrue(self.job_state('Y').can_start())
3537+ self.session.close()
3538+
3539+ def tearDown(self):
3540+ shutil.rmtree(self._sandbox)
3541+ os.environ = self._env
3542
3543=== modified file 'plainbox/plainbox/impl/testing_utils.py'
3544--- plainbox/plainbox/impl/testing_utils.py 2013-01-30 21:43:05 +0000
3545+++ plainbox/plainbox/impl/testing_utils.py 2013-03-07 16:10:38 +0000
3546@@ -18,19 +18,31 @@
3547 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
3548
3549 """
3550-plainbox.impl.testing_utils
3551-===========================
3552-
3553-Internal implementation of plainbox
3554-
3555- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
3556+:mod:`plainbox.impl.testing_utils` -- plainbox specific test tools
3557+==================================================================
3558+
3559+.. warning::
3560+
3561+ THIS MODULE DOES NOT HAVE STABLE PUBLIC API
3562 """
3563
3564 import inspect
3565+from tempfile import NamedTemporaryFile
3566
3567 from plainbox.impl.job import JobDefinition
3568 from plainbox.impl.result import JobResult
3569 from plainbox.impl.rfc822 import Origin
3570+from plainbox.impl.runner import io_log_write
3571+
3572+
3573+def make_io_log(io_log, io_log_dir):
3574+ """
3575+ Make the io logs serialization to json and return the saved file pathname
3576+ WARNING: The caller has to remove the file once done with it!
3577+ """
3578+ with NamedTemporaryFile(mode='w+t', delete=False) as stream:
3579+ io_log_write(io_log, stream)
3580+ return stream.name
3581
3582
3583 def make_job(name, plugin="dummy", requires=None, depends=None):
3584
3585=== removed file 'plainbox/plainbox/impl/utils.py.moved'
3586--- plainbox/plainbox/impl/utils.py.moved 2013-02-22 16:26:25 +0000
3587+++ plainbox/plainbox/impl/utils.py.moved 1970-01-01 00:00:00 +0000
3588@@ -1,58 +0,0 @@
3589-# This file is part of Checkbox.
3590-#
3591-# Copyright 2012 Canonical Ltd.
3592-# Written by:
3593-# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
3594-#
3595-# Checkbox is free software: you can redistribute it and/or modify
3596-# it under the terms of the GNU General Public License as published by
3597-# the Free Software Foundation, either version 3 of the License, or
3598-# (at your option) any later version.
3599-#
3600-# Checkbox is distributed in the hope that it will be useful,
3601-# but WITHOUT ANY WARRANTY; without even the implied warranty of
3602-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3603-# GNU General Public License for more details.
3604-#
3605-# You should have received a copy of the GNU General Public License
3606-# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
3607-
3608-"""
3609-plainbox.impl.utils
3610-===================
3611-
3612-Internal implementation of plainbox
3613-
3614- * THIS MODULE DOES NOT HAVE STABLE PUBLIC API *
3615-"""
3616-
3617-from io import TextIOWrapper
3618-from logging import getLogger
3619-
3620-from plainbox.impl.job import JobDefinition
3621-from plainbox.impl.rfc822 import load_rfc822_records
3622-
3623-
3624-logger = getLogger("plainbox.utils")
3625-
3626-
3627-def load(somewhere):
3628- if isinstance(somewhere, str):
3629- # Load data from a file with the given name
3630- filename = somewhere
3631- with open(filename, 'rt', encoding='UTF-8') as stream:
3632- return load(stream)
3633- if isinstance(somewhere, TextIOWrapper):
3634- stream = somewhere
3635- logger.debug("Loading jobs definitions from %r...", stream.name)
3636- record_list = load_rfc822_records(stream)
3637- job_list = []
3638- for record in record_list:
3639- job = JobDefinition.from_rfc822_record(record)
3640- logger.debug("Loaded %r", job)
3641- job_list.append(job)
3642- return job_list
3643- else:
3644- raise TypeError(
3645- "Unsupported type of 'somewhere': {!r}".format(
3646- type(somewhere)))
3647
3648=== modified file 'plainbox/plainbox/public.py'
3649--- plainbox/plainbox/public.py 2013-02-22 16:26:25 +0000
3650+++ plainbox/plainbox/public.py 2013-03-07 16:10:38 +0000
3651@@ -18,8 +18,8 @@
3652 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
3653
3654 """
3655-plainbox.public
3656-===============
3657+:mod:`plainbox.public` -- public, stable API
3658+============================================
3659
3660 Public, high-level API for third party developers.
3661
3662
3663=== modified file 'plainbox/plainbox/testing_utils/__init__.py'
3664--- plainbox/plainbox/testing_utils/__init__.py 2013-01-30 21:43:05 +0000
3665+++ plainbox/plainbox/testing_utils/__init__.py 2013-03-07 16:10:38 +0000
3666@@ -18,8 +18,6 @@
3667 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
3668
3669 """
3670-plainbox.testing_utils
3671-======================
3672-
3673-Testing utilities for internals of plainbox
3674+:mod:`plainbox.testing_utils` - generic testing utilities
3675+=========================================================
3676 """
3677
3678=== modified file 'plainbox/plainbox/testing_utils/cwd.py'
3679--- plainbox/plainbox/testing_utils/cwd.py 2013-01-30 21:43:05 +0000
3680+++ plainbox/plainbox/testing_utils/cwd.py 2013-03-07 16:10:38 +0000
3681@@ -18,8 +18,8 @@
3682 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
3683
3684 """
3685-plainbox.testing_utils.cwd
3686-==========================
3687+:mod:`plainbox.testing_utils.cwd` -- tools for testing in another directory
3688+===========================================================================
3689
3690 Implementation of context managers for working in another directory
3691 """
3692
3693=== modified file 'plainbox/plainbox/testing_utils/io.py'
3694--- plainbox/plainbox/testing_utils/io.py 2013-01-30 21:43:05 +0000
3695+++ plainbox/plainbox/testing_utils/io.py 2013-03-07 16:10:38 +0000
3696@@ -18,8 +18,8 @@
3697 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
3698
3699 """
3700-plainbox.testing_utils.io
3701-=========================
3702+:mod:`plainbox.testing_utils.io` -- tools for testing IO
3703+========================================================
3704
3705 Implementation of context managers for observing IO
3706 """
3707
3708=== modified file 'plainbox/plainbox/testing_utils/testcases.py'
3709--- plainbox/plainbox/testing_utils/testcases.py 2013-01-30 21:43:05 +0000
3710+++ plainbox/plainbox/testing_utils/testcases.py 2013-03-07 16:10:38 +0000
3711@@ -18,8 +18,8 @@
3712 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
3713
3714 """
3715-plainbox.testing_utils.testcases
3716-================================
3717+:mod:`plainbox.testing_utils.testcases` -- additional TestCase classes
3718+======================================================================
3719
3720 Implementation of additional TestCase classes that aid in testing.
3721 """
3722@@ -114,7 +114,7 @@
3723 parameter values, otherwise this test will behave as if it never existed
3724 (analogous how multiplication by zero works).
3725
3726- ..note::
3727+ .. note::
3728 Technical note for tinkerers and subclass authors. Python unittest
3729 framework is pretty annoying to work with or extend. In practice you
3730 should always keep the source code (of a particular python version)
3731
3732=== modified file 'plainbox/plainbox/tests.py'
3733--- plainbox/plainbox/tests.py 2013-02-08 17:02:12 +0000
3734+++ plainbox/plainbox/tests.py 2013-03-07 16:10:38 +0000
3735@@ -18,10 +18,8 @@
3736 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
3737
3738 """
3739-plainbox.tests
3740-==============
3741-
3742-Auxiliary test loader for plainbox
3743+:mod:`plainbox.tests` -- auxiliary test loaders for plainbox
3744+============================================================
3745 """
3746
3747 from unittest.loader import defaultTestLoader
3748
3749=== modified file 'plainbox/plainbox/vendor/__init__.py'
3750--- plainbox/plainbox/vendor/__init__.py 2012-11-29 18:59:46 +0000
3751+++ plainbox/plainbox/vendor/__init__.py 2013-03-07 16:10:38 +0000
3752@@ -0,0 +1,28 @@
3753+# This file is part of Checkbox.
3754+#
3755+# Copyright 2013 Canonical Ltd.
3756+# Written by:
3757+# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
3758+#
3759+# Checkbox is free software: you can redistribute it and/or modify
3760+# it under the terms of the GNU General Public License as published by
3761+# the Free Software Foundation, either version 3 of the License, or
3762+# (at your option) any later version.
3763+#
3764+# Checkbox is distributed in the hope that it will be useful,
3765+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3766+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3767+# GNU General Public License for more details.
3768+#
3769+# You should have received a copy of the GNU General Public License
3770+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
3771+
3772+"""
3773+:mod:`plainbox.vendor` -- vendorized packages
3774+=============================================
3775+
3776+This module contains external packages that were vendorized (shipped with a
3777+tree of another project) to simplify dependency management. There is no problem
3778+with expressing those dependencies at pypi level but it would be annoying to
3779+have to first package and introduce them to Ubuntu.
3780+"""
3781
3782=== modified file 'plainbox/plainbox/vendor/extcmd/__init__.py'
3783--- plainbox/plainbox/vendor/extcmd/__init__.py 2013-02-22 16:26:25 +0000
3784+++ plainbox/plainbox/vendor/extcmd/__init__.py 2013-03-07 16:10:38 +0000
3785@@ -18,8 +18,8 @@
3786 # along with this program. If not, see <http://www.gnu.org/licenses/>.
3787
3788 """
3789-extcmd - subprocess with advanced output processing
3790-===================================================
3791+:mod:`plainbox.vendor.extcmd` - subprocess with advanced output processing
3792+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3793
3794 Unlike subprocess, which just gives you a lump of output at the end, extcmd
3795 allows you to get callbacks (via a delegate class) on all IO.
3796
3797=== modified file 'plainbox/setup.py'
3798--- plainbox/setup.py 2013-02-22 16:26:25 +0000
3799+++ plainbox/setup.py 2013-03-07 16:10:38 +0000
3800@@ -31,6 +31,9 @@
3801 author_email="zygmunt.krynicki@canonical.com",
3802 license="GPLv3+",
3803 description="Simple replacement for checkbox",
3804+ tests_require=[
3805+ 'mock',
3806+ ],
3807 entry_points={
3808 'console_scripts': [
3809 'plainbox=plainbox.public:main',
3810
3811=== modified file 'po/ace.po'
3812--- po/ace.po 2013-02-22 16:26:25 +0000
3813+++ po/ace.po 2013-03-07 16:10:38 +0000
3814@@ -14,8 +14,8 @@
3815 "MIME-Version: 1.0\n"
3816 "Content-Type: text/plain; charset=UTF-8\n"
3817 "Content-Transfer-Encoding: 8bit\n"
3818-"X-Launchpad-Export-Date: 2013-02-15 04:31+0000\n"
3819-"X-Generator: Launchpad (build 16491)\n"
3820+"X-Launchpad-Export-Date: 2013-02-23 04:38+0000\n"
3821+"X-Generator: Launchpad (build 16506)\n"
3822
3823 #. Title of the user interface
3824 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
3825@@ -964,7 +964,7 @@
3826 msgstr ""
3827
3828 #. description
3829-#: ../jobs/input.txt.in:23
3830+#: ../jobs/input.txt.in:22
3831 msgid ""
3832 "PURPOSE:\n"
3833 " This test will test your pointing device\n"
3834@@ -976,7 +976,7 @@
3835 msgstr ""
3836
3837 #. description
3838-#: ../jobs/input.txt.in:36
3839+#: ../jobs/input.txt.in:35
3840 msgid ""
3841 "PURPOSE:\n"
3842 " This test will test your keyboard\n"
3843
3844=== modified file 'po/af.po'
3845--- po/af.po 2013-02-22 16:26:25 +0000
3846+++ po/af.po 2013-03-07 16:10:38 +0000
3847@@ -14,8 +14,8 @@
3848 "MIME-Version: 1.0\n"
3849 "Content-Type: text/plain; charset=UTF-8\n"
3850 "Content-Transfer-Encoding: 8bit\n"
3851-"X-Launchpad-Export-Date: 2013-02-15 04:31+0000\n"
3852-"X-Generator: Launchpad (build 16491)\n"
3853+"X-Launchpad-Export-Date: 2013-02-23 04:38+0000\n"
3854+"X-Generator: Launchpad (build 16506)\n"
3855
3856 #. Title of the user interface
3857 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
3858@@ -964,7 +964,7 @@
3859 msgstr ""
3860
3861 #. description
3862-#: ../jobs/input.txt.in:23
3863+#: ../jobs/input.txt.in:22
3864 msgid ""
3865 "PURPOSE:\n"
3866 " This test will test your pointing device\n"
3867@@ -976,7 +976,7 @@
3868 msgstr ""
3869
3870 #. description
3871-#: ../jobs/input.txt.in:36
3872+#: ../jobs/input.txt.in:35
3873 msgid ""
3874 "PURPOSE:\n"
3875 " This test will test your keyboard\n"
3876
3877=== modified file 'po/am.po'
3878--- po/am.po 2013-02-22 16:26:25 +0000
3879+++ po/am.po 2013-03-07 16:10:38 +0000
3880@@ -14,8 +14,8 @@
3881 "MIME-Version: 1.0\n"
3882 "Content-Type: text/plain; charset=UTF-8\n"
3883 "Content-Transfer-Encoding: 8bit\n"
3884-"X-Launchpad-Export-Date: 2013-02-15 04:31+0000\n"
3885-"X-Generator: Launchpad (build 16491)\n"
3886+"X-Launchpad-Export-Date: 2013-02-23 04:38+0000\n"
3887+"X-Generator: Launchpad (build 16506)\n"
3888
3889 #. Title of the user interface
3890 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
3891@@ -964,7 +964,7 @@
3892 msgstr ""
3893
3894 #. description
3895-#: ../jobs/input.txt.in:23
3896+#: ../jobs/input.txt.in:22
3897 msgid ""
3898 "PURPOSE:\n"
3899 " This test will test your pointing device\n"
3900@@ -976,7 +976,7 @@
3901 msgstr ""
3902
3903 #. description
3904-#: ../jobs/input.txt.in:36
3905+#: ../jobs/input.txt.in:35
3906 msgid ""
3907 "PURPOSE:\n"
3908 " This test will test your keyboard\n"
3909
3910=== modified file 'po/ar.po'
3911--- po/ar.po 2013-02-22 16:26:25 +0000
3912+++ po/ar.po 2013-03-07 16:10:38 +0000
3913@@ -14,8 +14,8 @@
3914 "MIME-Version: 1.0\n"
3915 "Content-Type: text/plain; charset=UTF-8\n"
3916 "Content-Transfer-Encoding: 8bit\n"
3917-"X-Launchpad-Export-Date: 2013-02-15 04:31+0000\n"
3918-"X-Generator: Launchpad (build 16491)\n"
3919+"X-Launchpad-Export-Date: 2013-02-23 04:38+0000\n"
3920+"X-Generator: Launchpad (build 16506)\n"
3921
3922 #. Title of the user interface
3923 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
3924@@ -1139,7 +1139,7 @@
3925 msgstr ""
3926
3927 #. description
3928-#: ../jobs/input.txt.in:23
3929+#: ../jobs/input.txt.in:22
3930 msgid ""
3931 "PURPOSE:\n"
3932 " This test will test your pointing device\n"
3933@@ -1151,7 +1151,7 @@
3934 msgstr ""
3935
3936 #. description
3937-#: ../jobs/input.txt.in:36
3938+#: ../jobs/input.txt.in:35
3939 msgid ""
3940 "PURPOSE:\n"
3941 " This test will test your keyboard\n"
3942
3943=== modified file 'po/ast.po'
3944--- po/ast.po 2013-02-22 16:26:25 +0000
3945+++ po/ast.po 2013-03-07 16:10:38 +0000
3946@@ -14,8 +14,8 @@
3947 "MIME-Version: 1.0\n"
3948 "Content-Type: text/plain; charset=UTF-8\n"
3949 "Content-Transfer-Encoding: 8bit\n"
3950-"X-Launchpad-Export-Date: 2013-02-15 04:31+0000\n"
3951-"X-Generator: Launchpad (build 16491)\n"
3952+"X-Launchpad-Export-Date: 2013-02-23 04:38+0000\n"
3953+"X-Generator: Launchpad (build 16506)\n"
3954 "Language: ast\n"
3955
3956 #. Title of the user interface
3957@@ -1324,7 +1324,7 @@
3958 msgstr "Axunta'l rexistru de depuración del instalador si existe."
3959
3960 #. description
3961-#: ../jobs/input.txt.in:23
3962+#: ../jobs/input.txt.in:22
3963 msgid ""
3964 "PURPOSE:\n"
3965 " This test will test your pointing device\n"
3966@@ -1343,7 +1343,7 @@
3967 " ¿Comportóse'l preséu de punteru s'esperaba?"
3968
3969 #. description
3970-#: ../jobs/input.txt.in:36
3971+#: ../jobs/input.txt.in:35
3972 msgid ""
3973 "PURPOSE:\n"
3974 " This test will test your keyboard\n"
3975
3976=== modified file 'po/az.po'
3977--- po/az.po 2013-02-22 16:26:25 +0000
3978+++ po/az.po 2013-03-07 16:10:38 +0000
3979@@ -14,8 +14,8 @@
3980 "MIME-Version: 1.0\n"
3981 "Content-Type: text/plain; charset=UTF-8\n"
3982 "Content-Transfer-Encoding: 8bit\n"
3983-"X-Launchpad-Export-Date: 2013-02-15 04:31+0000\n"
3984-"X-Generator: Launchpad (build 16491)\n"
3985+"X-Launchpad-Export-Date: 2013-02-23 04:38+0000\n"
3986+"X-Generator: Launchpad (build 16506)\n"
3987
3988 #. Title of the user interface
3989 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
3990@@ -967,7 +967,7 @@
3991 msgstr ""
3992
3993 #. description
3994-#: ../jobs/input.txt.in:23
3995+#: ../jobs/input.txt.in:22
3996 msgid ""
3997 "PURPOSE:\n"
3998 " This test will test your pointing device\n"
3999@@ -979,7 +979,7 @@
4000 msgstr ""
4001
4002 #. description
4003-#: ../jobs/input.txt.in:36
4004+#: ../jobs/input.txt.in:35
4005 msgid ""
4006 "PURPOSE:\n"
4007 " This test will test your keyboard\n"
4008
4009=== modified file 'po/be.po'
4010--- po/be.po 2013-02-22 16:26:25 +0000
4011+++ po/be.po 2013-03-07 16:10:38 +0000
4012@@ -14,8 +14,8 @@
4013 "MIME-Version: 1.0\n"
4014 "Content-Type: text/plain; charset=UTF-8\n"
4015 "Content-Transfer-Encoding: 8bit\n"
4016-"X-Launchpad-Export-Date: 2013-02-15 04:31+0000\n"
4017-"X-Generator: Launchpad (build 16491)\n"
4018+"X-Launchpad-Export-Date: 2013-02-23 04:38+0000\n"
4019+"X-Generator: Launchpad (build 16506)\n"
4020
4021 #. Title of the user interface
4022 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4023@@ -971,7 +971,7 @@
4024 msgstr ""
4025
4026 #. description
4027-#: ../jobs/input.txt.in:23
4028+#: ../jobs/input.txt.in:22
4029 msgid ""
4030 "PURPOSE:\n"
4031 " This test will test your pointing device\n"
4032@@ -983,7 +983,7 @@
4033 msgstr ""
4034
4035 #. description
4036-#: ../jobs/input.txt.in:36
4037+#: ../jobs/input.txt.in:35
4038 msgid ""
4039 "PURPOSE:\n"
4040 " This test will test your keyboard\n"
4041
4042=== modified file 'po/bg.po'
4043--- po/bg.po 2013-02-22 16:26:25 +0000
4044+++ po/bg.po 2013-03-07 16:10:38 +0000
4045@@ -14,8 +14,8 @@
4046 "MIME-Version: 1.0\n"
4047 "Content-Type: text/plain; charset=UTF-8\n"
4048 "Content-Transfer-Encoding: 8bit\n"
4049-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4050-"X-Generator: Launchpad (build 16491)\n"
4051+"X-Launchpad-Export-Date: 2013-02-23 04:38+0000\n"
4052+"X-Generator: Launchpad (build 16506)\n"
4053
4054 #. Title of the user interface
4055 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4056@@ -968,7 +968,7 @@
4057 msgstr ""
4058
4059 #. description
4060-#: ../jobs/input.txt.in:23
4061+#: ../jobs/input.txt.in:22
4062 msgid ""
4063 "PURPOSE:\n"
4064 " This test will test your pointing device\n"
4065@@ -980,7 +980,7 @@
4066 msgstr ""
4067
4068 #. description
4069-#: ../jobs/input.txt.in:36
4070+#: ../jobs/input.txt.in:35
4071 msgid ""
4072 "PURPOSE:\n"
4073 " This test will test your keyboard\n"
4074
4075=== modified file 'po/bn.po'
4076--- po/bn.po 2013-02-22 16:26:25 +0000
4077+++ po/bn.po 2013-03-07 16:10:38 +0000
4078@@ -14,8 +14,8 @@
4079 "MIME-Version: 1.0\n"
4080 "Content-Type: text/plain; charset=UTF-8\n"
4081 "Content-Transfer-Encoding: 8bit\n"
4082-"X-Launchpad-Export-Date: 2013-02-15 04:31+0000\n"
4083-"X-Generator: Launchpad (build 16491)\n"
4084+"X-Launchpad-Export-Date: 2013-02-23 04:38+0000\n"
4085+"X-Generator: Launchpad (build 16506)\n"
4086
4087 #. Title of the user interface
4088 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4089@@ -1215,7 +1215,7 @@
4090 msgstr "ইনস্টলার ডিবাগ লগ বিদ্যমান থাকলে সংযুক্ত করা হয়।"
4091
4092 #. description
4093-#: ../jobs/input.txt.in:23
4094+#: ../jobs/input.txt.in:22
4095 msgid ""
4096 "PURPOSE:\n"
4097 " This test will test your pointing device\n"
4098@@ -1234,7 +1234,7 @@
4099 " পয়েন্টকৃত ডিভাইসটি কি প্রত্যাশিত ভাবে কাজ করছে?"
4100
4101 #. description
4102-#: ../jobs/input.txt.in:36
4103+#: ../jobs/input.txt.in:35
4104 msgid ""
4105 "PURPOSE:\n"
4106 " This test will test your keyboard\n"
4107
4108=== modified file 'po/bo.po'
4109--- po/bo.po 2013-02-22 16:26:25 +0000
4110+++ po/bo.po 2013-03-07 16:10:38 +0000
4111@@ -14,8 +14,8 @@
4112 "MIME-Version: 1.0\n"
4113 "Content-Type: text/plain; charset=UTF-8\n"
4114 "Content-Transfer-Encoding: 8bit\n"
4115-"X-Launchpad-Export-Date: 2013-02-15 04:34+0000\n"
4116-"X-Generator: Launchpad (build 16491)\n"
4117+"X-Launchpad-Export-Date: 2013-02-23 04:41+0000\n"
4118+"X-Generator: Launchpad (build 16506)\n"
4119
4120 #. Title of the user interface
4121 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4122@@ -964,7 +964,7 @@
4123 msgstr ""
4124
4125 #. description
4126-#: ../jobs/input.txt.in:23
4127+#: ../jobs/input.txt.in:22
4128 msgid ""
4129 "PURPOSE:\n"
4130 " This test will test your pointing device\n"
4131@@ -976,7 +976,7 @@
4132 msgstr ""
4133
4134 #. description
4135-#: ../jobs/input.txt.in:36
4136+#: ../jobs/input.txt.in:35
4137 msgid ""
4138 "PURPOSE:\n"
4139 " This test will test your keyboard\n"
4140
4141=== modified file 'po/br.po'
4142--- po/br.po 2013-02-22 16:26:25 +0000
4143+++ po/br.po 2013-03-07 16:10:38 +0000
4144@@ -14,8 +14,8 @@
4145 "MIME-Version: 1.0\n"
4146 "Content-Type: text/plain; charset=UTF-8\n"
4147 "Content-Transfer-Encoding: 8bit\n"
4148-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4149-"X-Generator: Launchpad (build 16491)\n"
4150+"X-Launchpad-Export-Date: 2013-02-23 04:38+0000\n"
4151+"X-Generator: Launchpad (build 16506)\n"
4152
4153 #. Title of the user interface
4154 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4155@@ -964,7 +964,7 @@
4156 msgstr ""
4157
4158 #. description
4159-#: ../jobs/input.txt.in:23
4160+#: ../jobs/input.txt.in:22
4161 msgid ""
4162 "PURPOSE:\n"
4163 " This test will test your pointing device\n"
4164@@ -976,7 +976,7 @@
4165 msgstr ""
4166
4167 #. description
4168-#: ../jobs/input.txt.in:36
4169+#: ../jobs/input.txt.in:35
4170 msgid ""
4171 "PURPOSE:\n"
4172 " This test will test your keyboard\n"
4173
4174=== modified file 'po/bs.po'
4175--- po/bs.po 2013-02-22 16:26:25 +0000
4176+++ po/bs.po 2013-03-07 16:10:38 +0000
4177@@ -14,8 +14,8 @@
4178 "MIME-Version: 1.0\n"
4179 "Content-Type: text/plain; charset=UTF-8\n"
4180 "Content-Transfer-Encoding: 8bit\n"
4181-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4182-"X-Generator: Launchpad (build 16491)\n"
4183+"X-Launchpad-Export-Date: 2013-02-23 04:38+0000\n"
4184+"X-Generator: Launchpad (build 16506)\n"
4185
4186 #. Title of the user interface
4187 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4188@@ -1271,7 +1271,7 @@
4189 msgstr "Prilaže instalerski debug dnevnik ako postoji."
4190
4191 #. description
4192-#: ../jobs/input.txt.in:23
4193+#: ../jobs/input.txt.in:22
4194 msgid ""
4195 "PURPOSE:\n"
4196 " This test will test your pointing device\n"
4197@@ -1290,7 +1290,7 @@
4198 "    Da li radi pokazivački uređaj kao što je očekivano?"
4199
4200 #. description
4201-#: ../jobs/input.txt.in:36
4202+#: ../jobs/input.txt.in:35
4203 msgid ""
4204 "PURPOSE:\n"
4205 " This test will test your keyboard\n"
4206
4207=== modified file 'po/ca.po'
4208--- po/ca.po 2013-02-22 16:26:25 +0000
4209+++ po/ca.po 2013-03-07 16:10:38 +0000
4210@@ -14,8 +14,8 @@
4211 "MIME-Version: 1.0\n"
4212 "Content-Type: text/plain; charset=UTF-8\n"
4213 "Content-Transfer-Encoding: 8bit\n"
4214-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4215-"X-Generator: Launchpad (build 16491)\n"
4216+"X-Launchpad-Export-Date: 2013-02-23 04:38+0000\n"
4217+"X-Generator: Launchpad (build 16506)\n"
4218
4219 #~ msgid "$output"
4220 #~ msgstr "$resultat"
4221@@ -989,7 +989,7 @@
4222 msgstr ""
4223
4224 #. description
4225-#: ../jobs/input.txt.in:23
4226+#: ../jobs/input.txt.in:22
4227 msgid ""
4228 "PURPOSE:\n"
4229 " This test will test your pointing device\n"
4230@@ -1001,7 +1001,7 @@
4231 msgstr ""
4232
4233 #. description
4234-#: ../jobs/input.txt.in:36
4235+#: ../jobs/input.txt.in:35
4236 msgid ""
4237 "PURPOSE:\n"
4238 " This test will test your keyboard\n"
4239
4240=== modified file 'po/ca@valencia.po'
4241--- po/ca@valencia.po 2013-02-22 16:26:25 +0000
4242+++ po/ca@valencia.po 2013-03-07 16:10:38 +0000
4243@@ -14,8 +14,8 @@
4244 "MIME-Version: 1.0\n"
4245 "Content-Type: text/plain; charset=UTF-8\n"
4246 "Content-Transfer-Encoding: 8bit\n"
4247-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
4248-"X-Generator: Launchpad (build 16491)\n"
4249+"X-Launchpad-Export-Date: 2013-02-23 04:42+0000\n"
4250+"X-Generator: Launchpad (build 16506)\n"
4251
4252 #. Title of the user interface
4253 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4254@@ -986,7 +986,7 @@
4255 msgstr ""
4256
4257 #. description
4258-#: ../jobs/input.txt.in:23
4259+#: ../jobs/input.txt.in:22
4260 msgid ""
4261 "PURPOSE:\n"
4262 " This test will test your pointing device\n"
4263@@ -998,7 +998,7 @@
4264 msgstr ""
4265
4266 #. description
4267-#: ../jobs/input.txt.in:36
4268+#: ../jobs/input.txt.in:35
4269 msgid ""
4270 "PURPOSE:\n"
4271 " This test will test your keyboard\n"
4272
4273=== modified file 'po/ckb.po'
4274--- po/ckb.po 2013-02-22 16:26:25 +0000
4275+++ po/ckb.po 2013-03-07 16:10:38 +0000
4276@@ -14,8 +14,8 @@
4277 "MIME-Version: 1.0\n"
4278 "Content-Type: text/plain; charset=UTF-8\n"
4279 "Content-Transfer-Encoding: 8bit\n"
4280-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
4281-"X-Generator: Launchpad (build 16491)\n"
4282+"X-Launchpad-Export-Date: 2013-02-23 04:41+0000\n"
4283+"X-Generator: Launchpad (build 16506)\n"
4284
4285 #. Title of the user interface
4286 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4287@@ -964,7 +964,7 @@
4288 msgstr ""
4289
4290 #. description
4291-#: ../jobs/input.txt.in:23
4292+#: ../jobs/input.txt.in:22
4293 msgid ""
4294 "PURPOSE:\n"
4295 " This test will test your pointing device\n"
4296@@ -976,7 +976,7 @@
4297 msgstr ""
4298
4299 #. description
4300-#: ../jobs/input.txt.in:36
4301+#: ../jobs/input.txt.in:35
4302 msgid ""
4303 "PURPOSE:\n"
4304 " This test will test your keyboard\n"
4305
4306=== modified file 'po/cs.po'
4307--- po/cs.po 2013-02-22 16:26:25 +0000
4308+++ po/cs.po 2013-03-07 16:10:38 +0000
4309@@ -14,8 +14,8 @@
4310 "MIME-Version: 1.0\n"
4311 "Content-Type: text/plain; charset=UTF-8\n"
4312 "Content-Transfer-Encoding: 8bit\n"
4313-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4314-"X-Generator: Launchpad (build 16491)\n"
4315+"X-Launchpad-Export-Date: 2013-02-23 04:38+0000\n"
4316+"X-Generator: Launchpad (build 16506)\n"
4317
4318 #: ../gtk/checkbox-gtk.ui.h:2 ../checkbox_gtk/gtk_interface.py:561
4319 msgid "_Test"
4320@@ -1215,7 +1215,7 @@
4321 msgstr "Existuje-li, připojí ladicí záznam instalátoru"
4322
4323 #. description
4324-#: ../jobs/input.txt.in:23
4325+#: ../jobs/input.txt.in:22
4326 msgid ""
4327 "PURPOSE:\n"
4328 " This test will test your pointing device\n"
4329@@ -1234,7 +1234,7 @@
4330 " Fungovalo dotykové zařízení dle očekávání?"
4331
4332 #. description
4333-#: ../jobs/input.txt.in:36
4334+#: ../jobs/input.txt.in:35
4335 msgid ""
4336 "PURPOSE:\n"
4337 " This test will test your keyboard\n"
4338
4339=== modified file 'po/cy.po'
4340--- po/cy.po 2013-02-22 16:26:25 +0000
4341+++ po/cy.po 2013-03-07 16:10:38 +0000
4342@@ -14,8 +14,8 @@
4343 "MIME-Version: 1.0\n"
4344 "Content-Type: text/plain; charset=UTF-8\n"
4345 "Content-Transfer-Encoding: 8bit\n"
4346-"X-Launchpad-Export-Date: 2013-02-15 04:34+0000\n"
4347-"X-Generator: Launchpad (build 16491)\n"
4348+"X-Launchpad-Export-Date: 2013-02-23 04:41+0000\n"
4349+"X-Generator: Launchpad (build 16506)\n"
4350
4351 #. Title of the user interface
4352 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4353@@ -964,7 +964,7 @@
4354 msgstr ""
4355
4356 #. description
4357-#: ../jobs/input.txt.in:23
4358+#: ../jobs/input.txt.in:22
4359 msgid ""
4360 "PURPOSE:\n"
4361 " This test will test your pointing device\n"
4362@@ -976,7 +976,7 @@
4363 msgstr ""
4364
4365 #. description
4366-#: ../jobs/input.txt.in:36
4367+#: ../jobs/input.txt.in:35
4368 msgid ""
4369 "PURPOSE:\n"
4370 " This test will test your keyboard\n"
4371
4372=== modified file 'po/da.po'
4373--- po/da.po 2013-02-22 16:26:25 +0000
4374+++ po/da.po 2013-03-07 16:10:38 +0000
4375@@ -14,8 +14,8 @@
4376 "MIME-Version: 1.0\n"
4377 "Content-Type: text/plain; charset=UTF-8\n"
4378 "Content-Transfer-Encoding: 8bit\n"
4379-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4380-"X-Generator: Launchpad (build 16491)\n"
4381+"X-Launchpad-Export-Date: 2013-02-23 04:38+0000\n"
4382+"X-Generator: Launchpad (build 16506)\n"
4383 "X-Poedit-Language: Danish\n"
4384
4385 #. Title of the user interface
4386@@ -1131,7 +1131,7 @@
4387 msgstr ""
4388
4389 #. description
4390-#: ../jobs/input.txt.in:23
4391+#: ../jobs/input.txt.in:22
4392 msgid ""
4393 "PURPOSE:\n"
4394 " This test will test your pointing device\n"
4395@@ -1143,7 +1143,7 @@
4396 msgstr ""
4397
4398 #. description
4399-#: ../jobs/input.txt.in:36
4400+#: ../jobs/input.txt.in:35
4401 msgid ""
4402 "PURPOSE:\n"
4403 " This test will test your keyboard\n"
4404
4405=== modified file 'po/de.po'
4406--- po/de.po 2013-02-22 16:26:25 +0000
4407+++ po/de.po 2013-03-07 16:10:38 +0000
4408@@ -14,8 +14,8 @@
4409 "MIME-Version: 1.0\n"
4410 "Content-Type: text/plain; charset=UTF-8\n"
4411 "Content-Transfer-Encoding: 8bit\n"
4412-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4413-"X-Generator: Launchpad (build 16491)\n"
4414+"X-Launchpad-Export-Date: 2013-02-23 04:39+0000\n"
4415+"X-Generator: Launchpad (build 16506)\n"
4416
4417 #: ../gtk/checkbox-gtk.ui.h:2 ../checkbox_gtk/gtk_interface.py:561
4418 msgid "_Test"
4419@@ -83,7 +83,7 @@
4420
4421 #: ../gtk/checkbox-gtk.ui.h:5
4422 msgid "_Skip this test"
4423-msgstr "Test _überspringen"
4424+msgstr "_Diesen Test überspringen"
4425
4426 #: ../gtk/checkbox-gtk.ui.h:6 ../checkbox_cli/cli_interface.py:460
4427 #: ../checkbox_urwid/urwid_interface.py:289
4428@@ -1425,7 +1425,7 @@
4429 "als Anlage bei, falls vorhanden."
4430
4431 #. description
4432-#: ../jobs/input.txt.in:23
4433+#: ../jobs/input.txt.in:22
4434 msgid ""
4435 "PURPOSE:\n"
4436 " This test will test your pointing device\n"
4437@@ -1445,7 +1445,7 @@
4438 " Arbeitete das Zeigegerät so, wie es vorgesehen ist?"
4439
4440 #. description
4441-#: ../jobs/input.txt.in:36
4442+#: ../jobs/input.txt.in:35
4443 msgid ""
4444 "PURPOSE:\n"
4445 " This test will test your keyboard\n"
4446
4447=== modified file 'po/dv.po'
4448--- po/dv.po 2013-02-22 16:26:25 +0000
4449+++ po/dv.po 2013-03-07 16:10:38 +0000
4450@@ -14,8 +14,8 @@
4451 "MIME-Version: 1.0\n"
4452 "Content-Type: text/plain; charset=UTF-8\n"
4453 "Content-Transfer-Encoding: 8bit\n"
4454-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4455-"X-Generator: Launchpad (build 16491)\n"
4456+"X-Launchpad-Export-Date: 2013-02-23 04:38+0000\n"
4457+"X-Generator: Launchpad (build 16506)\n"
4458
4459 #. Title of the user interface
4460 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4461@@ -964,7 +964,7 @@
4462 msgstr ""
4463
4464 #. description
4465-#: ../jobs/input.txt.in:23
4466+#: ../jobs/input.txt.in:22
4467 msgid ""
4468 "PURPOSE:\n"
4469 " This test will test your pointing device\n"
4470@@ -976,7 +976,7 @@
4471 msgstr ""
4472
4473 #. description
4474-#: ../jobs/input.txt.in:36
4475+#: ../jobs/input.txt.in:35
4476 msgid ""
4477 "PURPOSE:\n"
4478 " This test will test your keyboard\n"
4479
4480=== modified file 'po/el.po'
4481--- po/el.po 2013-02-22 16:26:25 +0000
4482+++ po/el.po 2013-03-07 16:10:38 +0000
4483@@ -14,8 +14,8 @@
4484 "MIME-Version: 1.0\n"
4485 "Content-Type: text/plain; charset=utf-8\n"
4486 "Content-Transfer-Encoding: 8bit\n"
4487-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4488-"X-Generator: Launchpad (build 16491)\n"
4489+"X-Launchpad-Export-Date: 2013-02-23 04:39+0000\n"
4490+"X-Generator: Launchpad (build 16506)\n"
4491
4492 #: ../gtk/checkbox-gtk.ui.h:2 ../checkbox_gtk/gtk_interface.py:561
4493 msgid "_Test"
4494@@ -1389,7 +1389,7 @@
4495 msgstr "Επισύναψη της καταγραφής αποσφαλμάτωσης του εγκαταστάτη αν υπάρχει."
4496
4497 #. description
4498-#: ../jobs/input.txt.in:23
4499+#: ../jobs/input.txt.in:22
4500 msgid ""
4501 "PURPOSE:\n"
4502 " This test will test your pointing device\n"
4503@@ -1409,7 +1409,7 @@
4504 " Λειτούργησε η συσκευή κατάδειξης όπως θα περιμένατε;"
4505
4506 #. description
4507-#: ../jobs/input.txt.in:36
4508+#: ../jobs/input.txt.in:35
4509 msgid ""
4510 "PURPOSE:\n"
4511 " This test will test your keyboard\n"
4512
4513=== modified file 'po/en_AU.po'
4514--- po/en_AU.po 2013-02-22 16:26:25 +0000
4515+++ po/en_AU.po 2013-03-07 16:10:38 +0000
4516@@ -14,8 +14,8 @@
4517 "MIME-Version: 1.0\n"
4518 "Content-Type: text/plain; charset=UTF-8\n"
4519 "Content-Transfer-Encoding: 8bit\n"
4520-"X-Launchpad-Export-Date: 2013-02-15 04:34+0000\n"
4521-"X-Generator: Launchpad (build 16491)\n"
4522+"X-Launchpad-Export-Date: 2013-02-23 04:41+0000\n"
4523+"X-Generator: Launchpad (build 16506)\n"
4524
4525 #. Title of the user interface
4526 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4527@@ -1254,7 +1254,7 @@
4528 msgstr "Attaches the installer debug log if it exists."
4529
4530 #. description
4531-#: ../jobs/input.txt.in:23
4532+#: ../jobs/input.txt.in:22
4533 msgid ""
4534 "PURPOSE:\n"
4535 " This test will test your pointing device\n"
4536@@ -1273,7 +1273,7 @@
4537 " Did the pointing device work as expected?"
4538
4539 #. description
4540-#: ../jobs/input.txt.in:36
4541+#: ../jobs/input.txt.in:35
4542 msgid ""
4543 "PURPOSE:\n"
4544 " This test will test your keyboard\n"
4545
4546=== modified file 'po/en_CA.po'
4547--- po/en_CA.po 2013-02-22 16:26:25 +0000
4548+++ po/en_CA.po 2013-03-07 16:10:38 +0000
4549@@ -14,8 +14,8 @@
4550 "MIME-Version: 1.0\n"
4551 "Content-Type: text/plain; charset=UTF-8\n"
4552 "Content-Transfer-Encoding: 8bit\n"
4553-"X-Launchpad-Export-Date: 2013-02-15 04:35+0000\n"
4554-"X-Generator: Launchpad (build 16491)\n"
4555+"X-Launchpad-Export-Date: 2013-02-23 04:41+0000\n"
4556+"X-Generator: Launchpad (build 16506)\n"
4557
4558 #. Title of the user interface
4559 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4560@@ -964,7 +964,7 @@
4561 msgstr ""
4562
4563 #. description
4564-#: ../jobs/input.txt.in:23
4565+#: ../jobs/input.txt.in:22
4566 msgid ""
4567 "PURPOSE:\n"
4568 " This test will test your pointing device\n"
4569@@ -976,7 +976,7 @@
4570 msgstr ""
4571
4572 #. description
4573-#: ../jobs/input.txt.in:36
4574+#: ../jobs/input.txt.in:35
4575 msgid ""
4576 "PURPOSE:\n"
4577 " This test will test your keyboard\n"
4578
4579=== modified file 'po/en_GB.po'
4580--- po/en_GB.po 2013-02-22 16:26:25 +0000
4581+++ po/en_GB.po 2013-03-07 16:10:38 +0000
4582@@ -14,8 +14,8 @@
4583 "MIME-Version: 1.0\n"
4584 "Content-Type: text/plain; charset=UTF-8\n"
4585 "Content-Transfer-Encoding: 8bit\n"
4586-"X-Launchpad-Export-Date: 2013-02-15 04:34+0000\n"
4587-"X-Generator: Launchpad (build 16491)\n"
4588+"X-Launchpad-Export-Date: 2013-02-23 04:41+0000\n"
4589+"X-Generator: Launchpad (build 16506)\n"
4590
4591 #~ msgid "Do you see color bars and static?"
4592 #~ msgstr "Do you see colour bars and static?"
4593@@ -1257,7 +1257,7 @@
4594 msgstr "Attaches the installer debug log if it exists."
4595
4596 #. description
4597-#: ../jobs/input.txt.in:23
4598+#: ../jobs/input.txt.in:22
4599 msgid ""
4600 "PURPOSE:\n"
4601 " This test will test your pointing device\n"
4602@@ -1276,7 +1276,7 @@
4603 " Did the pointing device work as expected?"
4604
4605 #. description
4606-#: ../jobs/input.txt.in:36
4607+#: ../jobs/input.txt.in:35
4608 msgid ""
4609 "PURPOSE:\n"
4610 " This test will test your keyboard\n"
4611
4612=== modified file 'po/eo.po'
4613--- po/eo.po 2013-02-22 16:26:25 +0000
4614+++ po/eo.po 2013-03-07 16:10:38 +0000
4615@@ -14,8 +14,8 @@
4616 "MIME-Version: 1.0\n"
4617 "Content-Type: text/plain; charset=UTF-8\n"
4618 "Content-Transfer-Encoding: 8bit\n"
4619-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4620-"X-Generator: Launchpad (build 16491)\n"
4621+"X-Launchpad-Export-Date: 2013-02-23 04:39+0000\n"
4622+"X-Generator: Launchpad (build 16506)\n"
4623
4624 #: ../checkbox/application.py:66
4625 msgid "Usage: checkbox [OPTIONS]"
4626@@ -1017,7 +1017,7 @@
4627 msgstr ""
4628
4629 #. description
4630-#: ../jobs/input.txt.in:23
4631+#: ../jobs/input.txt.in:22
4632 msgid ""
4633 "PURPOSE:\n"
4634 " This test will test your pointing device\n"
4635@@ -1029,7 +1029,7 @@
4636 msgstr ""
4637
4638 #. description
4639-#: ../jobs/input.txt.in:36
4640+#: ../jobs/input.txt.in:35
4641 msgid ""
4642 "PURPOSE:\n"
4643 " This test will test your keyboard\n"
4644
4645=== modified file 'po/es.po'
4646--- po/es.po 2013-02-22 16:26:25 +0000
4647+++ po/es.po 2013-03-07 16:10:38 +0000
4648@@ -14,8 +14,8 @@
4649 "MIME-Version: 1.0\n"
4650 "Content-Type: text/plain; charset=UTF-8\n"
4651 "Content-Transfer-Encoding: 8bit\n"
4652-"X-Launchpad-Export-Date: 2013-02-15 04:34+0000\n"
4653-"X-Generator: Launchpad (build 16491)\n"
4654+"X-Launchpad-Export-Date: 2013-02-23 04:41+0000\n"
4655+"X-Generator: Launchpad (build 16506)\n"
4656
4657 #: ../checkbox/application.py:70
4658 msgid "Print version information and exit."
4659@@ -1418,7 +1418,7 @@
4660 msgstr "Adjunta el registro de depuración del instalador si existe."
4661
4662 #. description
4663-#: ../jobs/input.txt.in:23
4664+#: ../jobs/input.txt.in:22
4665 msgid ""
4666 "PURPOSE:\n"
4667 " This test will test your pointing device\n"
4668@@ -1438,7 +1438,7 @@
4669 " ¿Se comportó el dispositivo de puntero como se esperaba?"
4670
4671 #. description
4672-#: ../jobs/input.txt.in:36
4673+#: ../jobs/input.txt.in:35
4674 msgid ""
4675 "PURPOSE:\n"
4676 " This test will test your keyboard\n"
4677
4678=== modified file 'po/et.po'
4679--- po/et.po 2013-02-22 16:26:25 +0000
4680+++ po/et.po 2013-03-07 16:10:38 +0000
4681@@ -14,8 +14,8 @@
4682 "MIME-Version: 1.0\n"
4683 "Content-Type: text/plain; charset=UTF-8\n"
4684 "Content-Transfer-Encoding: 8bit\n"
4685-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4686-"X-Generator: Launchpad (build 16491)\n"
4687+"X-Launchpad-Export-Date: 2013-02-23 04:39+0000\n"
4688+"X-Generator: Launchpad (build 16506)\n"
4689
4690 #. Title of the user interface
4691 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4692@@ -964,7 +964,7 @@
4693 msgstr ""
4694
4695 #. description
4696-#: ../jobs/input.txt.in:23
4697+#: ../jobs/input.txt.in:22
4698 msgid ""
4699 "PURPOSE:\n"
4700 " This test will test your pointing device\n"
4701@@ -976,7 +976,7 @@
4702 msgstr ""
4703
4704 #. description
4705-#: ../jobs/input.txt.in:36
4706+#: ../jobs/input.txt.in:35
4707 msgid ""
4708 "PURPOSE:\n"
4709 " This test will test your keyboard\n"
4710
4711=== modified file 'po/eu.po'
4712--- po/eu.po 2013-02-22 16:26:25 +0000
4713+++ po/eu.po 2013-03-07 16:10:38 +0000
4714@@ -14,8 +14,8 @@
4715 "MIME-Version: 1.0\n"
4716 "Content-Type: text/plain; charset=UTF-8\n"
4717 "Content-Transfer-Encoding: 8bit\n"
4718-"X-Launchpad-Export-Date: 2013-02-15 04:31+0000\n"
4719-"X-Generator: Launchpad (build 16491)\n"
4720+"X-Launchpad-Export-Date: 2013-02-23 04:38+0000\n"
4721+"X-Generator: Launchpad (build 16506)\n"
4722
4723 #. Title of the user interface
4724 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4725@@ -964,7 +964,7 @@
4726 msgstr ""
4727
4728 #. description
4729-#: ../jobs/input.txt.in:23
4730+#: ../jobs/input.txt.in:22
4731 msgid ""
4732 "PURPOSE:\n"
4733 " This test will test your pointing device\n"
4734@@ -976,7 +976,7 @@
4735 msgstr ""
4736
4737 #. description
4738-#: ../jobs/input.txt.in:36
4739+#: ../jobs/input.txt.in:35
4740 msgid ""
4741 "PURPOSE:\n"
4742 " This test will test your keyboard\n"
4743
4744=== modified file 'po/fa.po'
4745--- po/fa.po 2013-02-22 16:26:25 +0000
4746+++ po/fa.po 2013-03-07 16:10:38 +0000
4747@@ -14,8 +14,8 @@
4748 "MIME-Version: 1.0\n"
4749 "Content-Type: text/plain; charset=UTF-8\n"
4750 "Content-Transfer-Encoding: 8bit\n"
4751-"X-Launchpad-Export-Date: 2013-02-15 04:33+0000\n"
4752-"X-Generator: Launchpad (build 16491)\n"
4753+"X-Launchpad-Export-Date: 2013-02-23 04:40+0000\n"
4754+"X-Generator: Launchpad (build 16506)\n"
4755
4756 #. Title of the user interface
4757 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4758@@ -964,7 +964,7 @@
4759 msgstr ""
4760
4761 #. description
4762-#: ../jobs/input.txt.in:23
4763+#: ../jobs/input.txt.in:22
4764 msgid ""
4765 "PURPOSE:\n"
4766 " This test will test your pointing device\n"
4767@@ -976,7 +976,7 @@
4768 msgstr ""
4769
4770 #. description
4771-#: ../jobs/input.txt.in:36
4772+#: ../jobs/input.txt.in:35
4773 msgid ""
4774 "PURPOSE:\n"
4775 " This test will test your keyboard\n"
4776
4777=== modified file 'po/fi.po'
4778--- po/fi.po 2013-02-22 16:26:25 +0000
4779+++ po/fi.po 2013-03-07 16:10:38 +0000
4780@@ -14,8 +14,8 @@
4781 "MIME-Version: 1.0\n"
4782 "Content-Type: text/plain; charset=UTF-8\n"
4783 "Content-Transfer-Encoding: 8bit\n"
4784-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4785-"X-Generator: Launchpad (build 16491)\n"
4786+"X-Launchpad-Export-Date: 2013-02-23 04:39+0000\n"
4787+"X-Generator: Launchpad (build 16506)\n"
4788
4789 #. Title of the user interface
4790 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4791@@ -1240,7 +1240,7 @@
4792 msgstr ""
4793
4794 #. description
4795-#: ../jobs/input.txt.in:23
4796+#: ../jobs/input.txt.in:22
4797 msgid ""
4798 "PURPOSE:\n"
4799 " This test will test your pointing device\n"
4800@@ -1260,7 +1260,7 @@
4801 " Toimiko osoitinlaite odotetulla tavalla?"
4802
4803 #. description
4804-#: ../jobs/input.txt.in:36
4805+#: ../jobs/input.txt.in:35
4806 msgid ""
4807 "PURPOSE:\n"
4808 " This test will test your keyboard\n"
4809
4810=== modified file 'po/fr.po'
4811--- po/fr.po 2013-02-22 16:26:25 +0000
4812+++ po/fr.po 2013-03-07 16:10:38 +0000
4813@@ -13,8 +13,8 @@
4814 "MIME-Version: 1.0\n"
4815 "Content-Type: text/plain; charset=UTF-8\n"
4816 "Content-Transfer-Encoding: 8bit\n"
4817-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4818-"X-Generator: Launchpad (build 16491)\n"
4819+"X-Launchpad-Export-Date: 2013-02-23 04:39+0000\n"
4820+"X-Generator: Launchpad (build 16506)\n"
4821
4822 #. Title of the user interface
4823 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4824@@ -1363,7 +1363,7 @@
4825 msgstr "Joint le journal de débogage de l'installateur s'il existe."
4826
4827 #. description
4828-#: ../jobs/input.txt.in:23
4829+#: ../jobs/input.txt.in:22
4830 msgid ""
4831 "PURPOSE:\n"
4832 " This test will test your pointing device\n"
4833@@ -1383,7 +1383,7 @@
4834 " Est-ce que le périphérique de pointage a fonctionné correctement ?"
4835
4836 #. description
4837-#: ../jobs/input.txt.in:36
4838+#: ../jobs/input.txt.in:35
4839 msgid ""
4840 "PURPOSE:\n"
4841 " This test will test your keyboard\n"
4842
4843=== modified file 'po/ga.po'
4844--- po/ga.po 2013-02-22 16:26:25 +0000
4845+++ po/ga.po 2013-03-07 16:10:38 +0000
4846@@ -14,8 +14,8 @@
4847 "MIME-Version: 1.0\n"
4848 "Content-Type: text/plain; charset=UTF-8\n"
4849 "Content-Transfer-Encoding: 8bit\n"
4850-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4851-"X-Generator: Launchpad (build 16491)\n"
4852+"X-Launchpad-Export-Date: 2013-02-23 04:39+0000\n"
4853+"X-Generator: Launchpad (build 16506)\n"
4854
4855 #. Title of the user interface
4856 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4857@@ -964,7 +964,7 @@
4858 msgstr ""
4859
4860 #. description
4861-#: ../jobs/input.txt.in:23
4862+#: ../jobs/input.txt.in:22
4863 msgid ""
4864 "PURPOSE:\n"
4865 " This test will test your pointing device\n"
4866@@ -976,7 +976,7 @@
4867 msgstr ""
4868
4869 #. description
4870-#: ../jobs/input.txt.in:36
4871+#: ../jobs/input.txt.in:35
4872 msgid ""
4873 "PURPOSE:\n"
4874 " This test will test your keyboard\n"
4875
4876=== modified file 'po/gd.po'
4877--- po/gd.po 2013-02-22 16:26:25 +0000
4878+++ po/gd.po 2013-03-07 16:10:38 +0000
4879@@ -14,8 +14,8 @@
4880 "MIME-Version: 1.0\n"
4881 "Content-Type: text/plain; charset=UTF-8\n"
4882 "Content-Transfer-Encoding: 8bit\n"
4883-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4884-"X-Generator: Launchpad (build 16491)\n"
4885+"X-Launchpad-Export-Date: 2013-02-23 04:39+0000\n"
4886+"X-Generator: Launchpad (build 16506)\n"
4887
4888 #. Title of the user interface
4889 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4890@@ -1295,7 +1295,7 @@
4891 msgstr "A' ceangal log dì-bhugaich an stàlaichear ma tha e ann."
4892
4893 #. description
4894-#: ../jobs/input.txt.in:23
4895+#: ../jobs/input.txt.in:22
4896 msgid ""
4897 "PURPOSE:\n"
4898 " This test will test your pointing device\n"
4899@@ -1315,7 +1315,7 @@
4900 " An do dh'obraich an innleachd comharraich mar a bha dùil?"
4901
4902 #. description
4903-#: ../jobs/input.txt.in:36
4904+#: ../jobs/input.txt.in:35
4905 msgid ""
4906 "PURPOSE:\n"
4907 " This test will test your keyboard\n"
4908
4909=== modified file 'po/gl.po'
4910--- po/gl.po 2013-02-22 16:26:25 +0000
4911+++ po/gl.po 2013-03-07 16:10:38 +0000
4912@@ -15,8 +15,8 @@
4913 "MIME-Version: 1.0\n"
4914 "Content-Type: text/plain; charset=UTF-8\n"
4915 "Content-Transfer-Encoding: 8bit\n"
4916-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4917-"X-Generator: Launchpad (build 16491)\n"
4918+"X-Launchpad-Export-Date: 2013-02-23 04:39+0000\n"
4919+"X-Generator: Launchpad (build 16506)\n"
4920
4921 #. Title of the user interface
4922 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4923@@ -1285,7 +1285,7 @@
4924 msgstr "Anexa o rexistro de depuración do instalador se existe."
4925
4926 #. description
4927-#: ../jobs/input.txt.in:23
4928+#: ../jobs/input.txt.in:22
4929 msgid ""
4930 "PURPOSE:\n"
4931 " This test will test your pointing device\n"
4932@@ -1305,7 +1305,7 @@
4933 " Comportouse o dispositivo de punteiro como se agardaba?"
4934
4935 #. description
4936-#: ../jobs/input.txt.in:36
4937+#: ../jobs/input.txt.in:35
4938 msgid ""
4939 "PURPOSE:\n"
4940 " This test will test your keyboard\n"
4941
4942=== modified file 'po/he.po'
4943--- po/he.po 2013-02-22 16:26:25 +0000
4944+++ po/he.po 2013-03-07 16:10:38 +0000
4945@@ -14,8 +14,8 @@
4946 "MIME-Version: 1.0\n"
4947 "Content-Type: text/plain; charset=UTF-8\n"
4948 "Content-Transfer-Encoding: 8bit\n"
4949-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4950-"X-Generator: Launchpad (build 16491)\n"
4951+"X-Launchpad-Export-Date: 2013-02-23 04:39+0000\n"
4952+"X-Generator: Launchpad (build 16506)\n"
4953
4954 #: ../gtk/checkbox-gtk.ui.h:4
4955 msgid "_No"
4956@@ -1095,7 +1095,7 @@
4957 msgstr ""
4958
4959 #. description
4960-#: ../jobs/input.txt.in:23
4961+#: ../jobs/input.txt.in:22
4962 msgid ""
4963 "PURPOSE:\n"
4964 " This test will test your pointing device\n"
4965@@ -1107,7 +1107,7 @@
4966 msgstr ""
4967
4968 #. description
4969-#: ../jobs/input.txt.in:36
4970+#: ../jobs/input.txt.in:35
4971 msgid ""
4972 "PURPOSE:\n"
4973 " This test will test your keyboard\n"
4974
4975=== modified file 'po/hi.po'
4976--- po/hi.po 2013-02-22 16:26:25 +0000
4977+++ po/hi.po 2013-03-07 16:10:38 +0000
4978@@ -14,8 +14,8 @@
4979 "MIME-Version: 1.0\n"
4980 "Content-Type: text/plain; charset=UTF-8\n"
4981 "Content-Transfer-Encoding: 8bit\n"
4982-"X-Launchpad-Export-Date: 2013-02-15 04:32+0000\n"
4983-"X-Generator: Launchpad (build 16491)\n"
4984+"X-Launchpad-Export-Date: 2013-02-23 04:39+0000\n"
4985+"X-Generator: Launchpad (build 16506)\n"
4986
4987 #. Title of the user interface
4988 #: ../gtk/checkbox-gtk.ui.h:1 ../qt/checkbox-qt.desktop.in.h:1
4989@@ -966,7 +966,7 @@
4990 msgstr ""
4991
4992 #. description
4993-#: ../jobs/input.txt.in:23
4994+#: ../jobs/input.txt.in:22
4995 msgid ""
4996 "PURPOSE:\n"
4997 " This test will test your pointing device\n"
4998@@ -978,7 +978,7 @@
4999 msgstr ""
5000
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches