Merge lp:~roadmr/ubuntu/oneiric/checkbox/0.13 into lp:ubuntu/oneiric/checkbox

Proposed by Daniel Manrique
Status: Superseded
Proposed branch: lp:~roadmr/ubuntu/oneiric/checkbox/0.13
Merge into: lp:ubuntu/oneiric/checkbox
Diff against target: 18858 lines (+9936/-3266)
93 files modified
checkbox/application.py (+5/-5)
checkbox/contrib/persist.py (+5/-4)
checkbox/dispatcher.py (+201/-0)
checkbox/lib/bit.py (+5/-4)
checkbox/lib/config.py (+15/-2)
checkbox/lib/conversion.py (+131/-42)
checkbox/lib/dmi.py (+169/-18)
checkbox/lib/safe.py (+5/-0)
checkbox/lib/template.py (+1/-1)
checkbox/message.py (+4/-3)
checkbox/parsers/cpuinfo.py (+30/-41)
checkbox/parsers/cputable (+40/-0)
checkbox/parsers/cputable.py (+42/-0)
checkbox/parsers/deferred.py (+27/-0)
checkbox/parsers/dmidecode.py (+123/-0)
checkbox/parsers/meminfo.py (+46/-0)
checkbox/parsers/submission.py (+474/-383)
checkbox/parsers/udevadm.py (+178/-317)
checkbox_cli/cli_interface.py (+9/-1)
checkbox_gtk/gtk_interface.py (+4/-4)
data/whitelists/default.whitelist (+1/-0)
debian/changelog (+59/-0)
debian/po/ro.po (+118/-0)
examples/checkbox.ini (+1/-1)
gtk/checkbox-gtk.ui (+37/-37)
jobs/apport.txt.in (+0/-5)
jobs/audio.txt.in (+44/-24)
jobs/autotest.txt.in (+4/-2)
jobs/bluetooth.txt.in (+64/-52)
jobs/camera.txt.in (+19/-12)
jobs/codecs.txt.in (+16/-38)
jobs/cpu.txt.in (+3/-3)
jobs/daemons.txt.in (+11/-11)
jobs/disk.txt.in (+6/-5)
jobs/evolution.txt.in (+0/-26)
jobs/fingerprint.txt.in (+24/-20)
jobs/firewire.txt.in (+9/-8)
jobs/gcalctool.txt.in (+0/-52)
jobs/gedit.txt.in (+0/-22)
jobs/gnome-terminal.txt.in (+0/-12)
jobs/graphics.txt.in (+63/-51)
jobs/hibernate.txt.in (+9/-7)
jobs/info.txt.in (+47/-27)
jobs/input.txt.in (+15/-7)
jobs/install.txt.in (+1/-1)
jobs/keys.txt.in (+58/-45)
jobs/local.txt.in (+41/-6)
jobs/ltp.txt.in (+3/-2)
jobs/mago.txt.in (+4/-2)
jobs/mediacard.txt.in (+106/-84)
jobs/memory.txt.in (+8/-5)
jobs/miscellanea.txt.in (+19/-8)
jobs/monitor.txt.in (+43/-24)
jobs/networking.txt.in (+31/-32)
jobs/optical.txt.in (+56/-42)
jobs/panel_clock_test.txt.in (+18/-15)
jobs/panel_reboot.txt.in (+8/-6)
jobs/pcmcia-pcix.txt.in (+6/-3)
jobs/peripheral.txt.in (+27/-22)
jobs/phoronix.txt.in (+4/-2)
jobs/power-management.txt.in (+32/-18)
jobs/qa_regression.txt.in (+4/-2)
jobs/resource.txt.in (+15/-0)
jobs/screenshot.txt.in (+0/-13)
jobs/server-services.txt.in (+12/-12)
jobs/stress.txt.in (+18/-10)
jobs/suspend.txt.in (+52/-31)
jobs/usb.txt.in (+55/-36)
jobs/user_apps.txt.in (+337/-110)
jobs/wireless.txt.in (+9/-9)
plugins/apport_prompt.py (+1/-1)
plugins/launchpad_report.py (+6/-4)
plugins/persist_info.py (+2/-1)
plugins/resource_info.py (+14/-1)
plugins/system_info.py (+2/-2)
po/POTFILES.in (+0/-6)
po/ca@valencia.po (+2897/-0)
po/el.po (+229/-293)
po/en_GB.po (+240/-240)
po/gd.po (+2606/-0)
po/ja.po (+297/-335)
po/lt.po (+294/-357)
po/tr.po (+217/-177)
scripts/connect_wireless (+1/-1)
scripts/cpuinfo_resource (+13/-17)
scripts/dmi_resource (+55/-0)
scripts/hal_resource (+0/-5)
scripts/meminfo_resource (+15/-27)
scripts/package_resource (+6/-0)
scripts/udev_resource (+2/-2)
scripts/usb_test (+5/-3)
setup.cfg (+1/-7)
setup.py (+2/-0)
To merge this branch: bzr merge lp:~roadmr/ubuntu/oneiric/checkbox/0.13
Reviewer Review Type Date Requested Status
Ubuntu Sponsors Team Pending
Review via email: mp+82719@code.launchpad.net

This proposal has been superseded by a proposal from 2011-11-24.

To post a comment you must log in.
Revision history for this message
Sebastien Bacher (seb128) wrote :

Thank you for your work, I'm sponsoring it to precise but I can't set the merge request to merged since you targetted oneiric which is the wrong serie and launchpad will not let me edit it, could you do that?

Unmerged revisions

36. By Daniel Manrique

New upstream release (LP: #892268):
* Generate a submission.xml file that contains all device and attachment
* Write the report before reporting the validation error.
* Changed device.product to dmi.product for the formfactor (LP: #875312)
* Use gettext for string (LP: #869267)
* Move progress indicator to main checkbox dialog instead of a
  transient window (LP: #868995)
* Ignore malformed dpkg entries in package_resource (LP: #794747)
* Reset window title after finishing a manual test (LP: #874690)
* Handle "@" in locale names (as in ca@valencia).
* Went through all the job files and:
  * Updated descriptions to match Unity UI structure
  * Added descriptions where necessary
  * Added further details to some descriptions
  * Moved some jobs to more appropriate files
  * Fixed job names in older job files to match new naming scheme
    (suite/testname)
  * Added jobs to local.txt to ensure all job files are now parsed
    (this allows easier addition of existing tests to whitelists)
  * Changed remaining manual job descriptions to match the new format
* Updated CD and DVD write tests to be more clear about when to skip
  them (LP: #772794)
* Rewrote all job descriptions to match OEM QA syntax
* Fix the code that assigns keys in checkbox-cli so that it never assigns
  keys which have other uses. (LP: #877467)
* Show details of unmet job requirements (LP: #855852)
* Ensure that connect_wireless chooses a wireless connection from the list
  of available connections (LP: #877752)
* Have the bluetooth/detect tests require a device with the category
  BLUETOOTH to run, thus preventing the test from failing on systems with
  no Bluetooth device (LP: #862322)
* Rename attachment jobs to not have a forward slash in their name
  (LP: #887964)
* Guard against trying to write files to logical partitions on USB sticks
  (which will obviously fail) in usb_test (LP: #887049)
* Make the OpenGL test ignore the return value of glxgears and improve
  the test description (LP: #890725)
* Allow input/mouse test to run if a TOUCH device is present
  (LP: #886129)
* Broken job dependencies fixed (LP: #888447)
* Regex support when specifying blacklists and whitelists on the
  commandline (LP: #588647)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'checkbox/application.py'
2--- checkbox/application.py 2011-02-14 18:19:27 +0000
3+++ checkbox/application.py 2011-11-18 18:04:27 +0000
4@@ -81,13 +81,13 @@
5 default=[],
6 help=_("Configuration override parameters."))
7 parser.add_option("-b", "--blacklist",
8- help=_("Shorthand for --config=checkbox/plugins/jobs_info/blacklist."))
9+ help=_("Shorthand for --config=.*/jobs_info/blacklist."))
10 parser.add_option("-B", "--blacklist-file",
11- help=_("Shorthand for --config=checkbox/plugins/jobs_info/blacklist_file."))
12+ help=_("Shorthand for --config=.*/jobs_info/blacklist_file."))
13 parser.add_option("-w", "--whitelist",
14- help=_("Shorthand for --config=checkbox/plugins/jobs_info/whitelist."))
15+ help=_("Shorthand for --config=.*/jobs_info/whitelist."))
16 parser.add_option("-W", "--whitelist-file",
17- help=_("Shorthand for --config=checkbox/plugins/jobs_info/whitelist_file."))
18+ help=_("Shorthand for --config=.*/jobs_info/whitelist_file."))
19 return parser.parse_args(args)
20
21 def create_application(self, args=sys.argv):
22@@ -102,7 +102,7 @@
23
24 # Replace shorthands
25 for shorthand in "blacklist", "blacklist_file", "whitelist", "whitelist_file":
26- key = "checkbox/plugins/jobs_info/%s" % shorthand
27+ key = ".*/jobs_info/%s" % shorthand
28 value = getattr(options, shorthand)
29 if value:
30 options.config.append("=".join([key, value]))
31
32=== modified file 'checkbox/contrib/persist.py'
33--- checkbox/contrib/persist.py 2011-02-14 18:19:27 +0000
34+++ checkbox/contrib/persist.py 2011-11-18 18:04:27 +0000
35@@ -23,6 +23,7 @@
36 import re
37 import posixpath
38
39+from checkbox.lib.safe import safe_close
40
41 __all__ = ["Persist", "MemoryBackend", "PickleBackend", "BPickleBackend",
42 "path_string_to_tuple", "path_tuple_to_string", "RootedPersist",
43@@ -517,14 +518,14 @@
44 try:
45 return self._pickle.load(file)
46 finally:
47- file.close()
48+ safe_close(file)
49
50 def save(self, filepath, map):
51 file = open(filepath, "w")
52 try:
53 self._pickle.dump(map, file, 2)
54 finally:
55- file.close()
56+ safe_close(file)
57
58
59 class BPickleBackend(Backend):
60@@ -538,11 +539,11 @@
61 try:
62 return self._bpickle.loads(file.read())
63 finally:
64- file.close()
65+ safe_close(file)
66
67 def save(self, filepath, map):
68 file = open(filepath, "w")
69 try:
70 file.write(self._bpickle.dumps(map))
71 finally:
72- file.close()
73+ safe_close(file)
74
75=== added file 'checkbox/dispatcher.py'
76--- checkbox/dispatcher.py 1970-01-01 00:00:00 +0000
77+++ checkbox/dispatcher.py 2011-11-18 18:04:27 +0000
78@@ -0,0 +1,201 @@
79+# Copyright 2010-2011 Canonical Ltd. This software is licensed under the
80+# GNU Affero General Public License version 3 (see the file LICENSE).
81+
82+__metaclass__ = type
83+
84+__all__ = [
85+ "Dispatcher",
86+ "DispatcherList",
87+ "DispatcherQueue",
88+ ]
89+
90+import logging
91+
92+from itertools import product
93+
94+
95+class Event:
96+ """Event payload containing the positional and keywoard arguments
97+ passed to the handler in the event listener."""
98+
99+ def __init__(self, type, *args, **kwargs):
100+ self.type = type
101+ self.args = args
102+ self.kwargs = kwargs
103+
104+
105+class Listener:
106+ """Event listener notified when events are published by the dispatcher."""
107+
108+ def __init__(self, event_type, handler, count):
109+ self.event_type = event_type
110+ self.handler = handler
111+ self.count = count
112+
113+ def notify(self, event):
114+ """Notify the handler with the payload of the event.
115+
116+ :param event: The event containint the payload for the handler.
117+ """
118+ if self.count is None or self.count:
119+ self.handler(*event.args, **event.kwargs)
120+ if self.count:
121+ self.count -= 1
122+
123+
124+class ListenerList(Listener):
125+ """Event listener notified for lists of events."""
126+
127+ def __init__(self, *args, **kwargs):
128+ super(ListenerList, self).__init__(*args, **kwargs)
129+ self.event_types = set(self.event_type)
130+ self.kwargs = {}
131+
132+ def notify(self, event):
133+ """Only notify the handler when all the events for this listener
134+ have been published by the dispatcher. When duplicate events
135+ occur, the latest event is preserved and the previous one are
136+ overwritten until all events have been published.
137+ """
138+ if self.count is None or self.count:
139+ self.kwargs[event.type] = event.args[0]
140+ if self.event_types.issubset(self.kwargs):
141+ self.handler(**self.kwargs)
142+ if self.count:
143+ self.count -= 1
144+
145+
146+class ListenerQueue(ListenerList):
147+
148+ def notify(self, event):
149+ """Only notify the handler when all the events for this listener
150+ have been published by the dispatcher. Duplicate events are enqueued
151+ and dequeued only when all events have been published.
152+ """
153+ arg = event.args[0]
154+ queue = self.kwargs.setdefault(event.type, [])
155+
156+ # Strip duplicates from the queue.
157+ if arg not in queue:
158+ queue.append(arg)
159+
160+ # Once the queue has handler has been called, the queue
161+ # then behaves like a list using the latest events.
162+ if self.event_types.issubset(self.kwargs):
163+ self.notify = notify = super(ListenerQueue, self).notify
164+ keys = self.kwargs.keys()
165+ for values in product(*self.kwargs.values()):
166+ self.kwargs = dict(zip(keys, values))
167+ notify(event)
168+
169+
170+class Dispatcher:
171+ """Register handlers and publish events for them identified by strings."""
172+
173+ listener_factory = Listener
174+
175+ def __init__(self, listener_factory=None):
176+ self._event_listeners = {}
177+
178+ if listener_factory is not None:
179+ self.listener_factory = listener_factory
180+
181+ def registerHandler(self, event_type, handler, count=None):
182+ """Register an event handler and return its listener.
183+
184+ :param event_type: The name of the event type to handle.
185+ :param handler: The function handling the given event type.
186+ :param count: Optionally, the number times to call the handler.
187+ """
188+ listener = self.listener_factory(event_type, handler, count)
189+
190+ listeners = self._event_listeners.setdefault(event_type, [])
191+ listeners.append(listener)
192+
193+ return listener
194+
195+ def unregisterHandler(self, handler):
196+ """Unregister a handler.
197+
198+ :param handler: The handler to unregister.
199+ """
200+ for event_type, listeners in self._event_listeners.items():
201+ listeners = [
202+ listener for listener in listeners
203+ if listener.handler == handler]
204+ if listeners:
205+ self._event_listeners[event_type] = listeners
206+ else:
207+ del self._event_listeners[event_type]
208+
209+ def unregisterListener(self, listener, event_type=None):
210+ """Unregister a listener.
211+
212+ :param listener: The listener of the handler to unregister.
213+ :param event_type: Optionally, the event_type to unregister.
214+ """
215+ if event_type is None:
216+ event_type = listener.event_type
217+
218+ self._event_listeners[event_type].remove(listener)
219+ if not self._event_listeners[event_type]:
220+ del self._event_listeners[event_type]
221+
222+ def publishEvent(self, event_type, *args, **kwargs):
223+ """Publish an event of a given type and notify all listeners.
224+
225+ :param event_type: The name of the event type to publish.
226+ :param args: Positional arguments to pass to the registered handlers.
227+ :param kwargs: Keyword arguments to pass to the registered handlers.
228+ """
229+ if event_type in self._event_listeners:
230+ event = Event(event_type, *args, **kwargs)
231+ for listener in list(self._event_listeners[event_type]):
232+ try:
233+ listener.notify(event)
234+ if listener.count is not None and not listener.count:
235+ self.unregisterListener(listener)
236+ except:
237+ logging.exception(
238+ "Error running event handler for %r with args %r %r",
239+ event_type, args, kwargs)
240+
241+
242+class DispatcherList(Dispatcher):
243+ """
244+ Register handlers and publish events for them identified by lists
245+ of strings.
246+ """
247+
248+ listener_factory = ListenerList
249+
250+ def registerHandler(self, event_types, handler, count=None):
251+ """See Dispatcher."""
252+ if not isinstance(event_types, (list, tuple,)):
253+ event_types = (event_types,)
254+
255+ listener = self.listener_factory(event_types, handler, count)
256+ for event_type in event_types:
257+ listeners = self._event_listeners.setdefault(event_type, [])
258+ listeners.append(listener)
259+
260+ return listener
261+
262+ def unregisterListener(self, listener):
263+ """See Dispatcher."""
264+ for event_type in listener.event_types:
265+ super(DispatcherList, self).unregisterListener(
266+ listener, event_type)
267+
268+ def publishEvent(self, event_type, arg):
269+ """See Dispatcher."""
270+ super(DispatcherList, self).publishEvent(event_type, arg)
271+
272+
273+class DispatcherQueue(DispatcherList):
274+ """
275+ Register handlers and publish events for them identified by lists
276+ of strings in queue order.
277+ """
278+
279+ listener_factory = ListenerQueue
280
281=== modified file 'checkbox/lib/bit.py'
282--- checkbox/lib/bit.py 2011-02-14 18:19:27 +0000
283+++ checkbox/lib/bit.py 2011-11-18 18:04:27 +0000
284@@ -36,10 +36,11 @@
285
286 return bitcount
287
288-def test_bit(bit, bitmask):
289- bits_per_long = calcsize("l") * 8
290- offset = bit % bits_per_long
291- long = int(bit / bits_per_long)
292+def test_bit(bit, bitmask, bits=None):
293+ if bits is None:
294+ bits = calcsize("l") * 8
295+ offset = bit % bits
296+ long = int(bit / bits)
297 if long >= len(bitmask):
298 return 0
299 return (bitmask[long] >> offset) & 1
300
301=== modified file 'checkbox/lib/config.py'
302--- checkbox/lib/config.py 2011-07-01 11:37:27 +0000
303+++ checkbox/lib/config.py 2011-11-18 18:04:27 +0000
304@@ -122,10 +122,23 @@
305 raise Exception, "Invalid config string: %s" % config
306
307 (name, option, value) = match.groups()
308- if not self._parser.has_section(name):
309+
310+ # Match section names
311+ name_regex = re.compile(name)
312+ sections = [section for section in self._parser.sections()
313+ if name_regex.match(section)]
314+
315+ if not sections:
316 self._parser.add_section(name)
317+ sections.append(name)
318
319- self._parser.set(name, option, value)
320+ for section in sections:
321+ logging.debug('Setting configuration parameter: '
322+ '%(section)s/%(option)s = %(value)s'
323+ % {'section': section,
324+ 'option': option,
325+ 'value': value})
326+ self._parser.set(section, option, value)
327
328 def read_file(self, file, filename="<stream>"):
329 logging.info("Reading configurations from: %s", filename)
330
331=== modified file 'checkbox/lib/conversion.py'
332--- checkbox/lib/conversion.py 2009-01-20 18:55:20 +0000
333+++ checkbox/lib/conversion.py 2011-11-18 18:04:27 +0000
334@@ -18,48 +18,110 @@
335 #
336 import re
337
338-
339-def string_to_type(value):
340- conversion_table = (
341- ("(yes|true)", lambda v: True),
342- ("(no|false)", lambda v: False),
343- ("\d+", lambda v: int(v.group(0))),
344- ("\d+\.\d+", lambda v: float(v.group(0))),
345- ("(\d+) ?([kmgt]?b?)", lambda v: int(v.group(1))),
346- ("(\d+\.\d+) ?([kmgt]?b?)", lambda v: float(v.group(1))),
347- ("(\d+) ?([kmgt]?hz?)", lambda v: int(v.group(1))),
348- ("(\d+\.\d+) ?([kmgt]?hz?)", lambda v: float(v.group(1))))
349-
350- multiplier_table = (
351- ("b", 1),
352- ("kb?", 1024),
353- ("mb?", 1024 * 1024),
354- ("gb?", 1024 * 1024 * 1024),
355- ("tb?", 1024 * 1024 * 1024 * 1024),
356- ("hz", 1),
357- ("khz?", 1024),
358- ("mhz?", 1024 * 1024),
359- ("ghz?", 1024 * 1024 * 1024),
360- ("thz?", 1024 * 1024 * 1024 * 1024))
361-
362- if isinstance(value, basestring):
363- for regex, conversion in conversion_table:
364- match = re.match("^%s$" % regex, value, re.IGNORECASE)
365- if match:
366- value = conversion(match)
367- if len(match.groups()) < 2:
368- return value
369-
370- unit = match.group(2)
371- for regex, multiplier in multiplier_table:
372- match = re.match("^%s$" % regex, unit, re.IGNORECASE)
373- if match:
374- value *= multiplier
375- return value
376- else:
377- raise Exception, "Unknown multiplier: %s" % unit
378-
379- return value
380+from dateutil import tz
381+from datetime import (
382+ datetime,
383+ timedelta,
384+ )
385+
386+
387+DATETIME_RE = re.compile(r"""
388+ ^(?P<year>\d\d\d\d)-?(?P<month>\d\d)-?(?P<day>\d\d)
389+ T(?P<hour>\d\d):?(?P<minute>\d\d):?(?P<second>\d\d)
390+ (?:\.(?P<second_fraction>\d{0,6}))?
391+ (?P<tz>
392+ (?:(?P<tz_sign>[-+])(?P<tz_hour>\d\d):(?P<tz_minute>\d\d))
393+ | Z)?$
394+ """, re.VERBOSE)
395+
396+TYPE_FORMATS = (
397+ (r"(yes|true)", lambda v: True),
398+ (r"(no|false)", lambda v: False),
399+ (r"-?\d+", lambda v: int(v.group(0))),
400+ (r"-?\d+\.\d+", lambda v: float(v.group(0))),
401+ (r"(-?\d+) ?([kmgt]?b?)", lambda v: int(v.group(1))),
402+ (r"(-?\d+\.\d+) ?([kmgt]?b?)", lambda v: float(v.group(1))),
403+ (r"(-?\d+) ?([kmgt]?hz)", lambda v: int(v.group(1))),
404+ (r"(-?\d+\.\d+) ?([kmgt]?hz)", lambda v: float(v.group(1))))
405+TYPE_FORMATS = tuple(
406+ (re.compile(r"^%s$" % pattern, re.IGNORECASE), format)
407+ for pattern, format in TYPE_FORMATS)
408+
409+TYPE_MULTIPLIERS = (
410+ (r"b", 1),
411+ (r"kb?", 1024),
412+ (r"mb?", 1024 * 1024),
413+ (r"gb?", 1024 * 1024 * 1024),
414+ (r"tb?", 1024 * 1024 * 1024 * 1024),
415+ (r"hz", 1),
416+ (r"khz?", 1024),
417+ (r"mhz?", 1024 * 1024),
418+ (r"ghz?", 1024 * 1024 * 1024),
419+ (r"thz?", 1024 * 1024 * 1024 * 1024))
420+TYPE_MULTIPLIERS = tuple(
421+ (re.compile(r"^%s$" % pattern, re.IGNORECASE), multiplier)
422+ for pattern, multiplier in TYPE_MULTIPLIERS)
423+
424+
425+def datetime_to_string(dt):
426+ """Return a consistent string representation for a given datetime.
427+
428+ :param dt: The datetime object.
429+ """
430+ return dt.isoformat()
431+
432+def string_to_datetime(string):
433+ """Return a datetime object from a consistent string representation.
434+
435+ :param string: The string representation.
436+ """
437+ # we cannot use time.strptime: this function accepts neither fractions
438+ # of a second nor a time zone given e.g. as '+02:30'.
439+ match = DATETIME_RE.match(string)
440+
441+ # The Relax NG schema allows a leading minus sign and year numbers
442+ # with more than four digits, which are not "covered" by _time_regex.
443+ if not match:
444+ raise ValueError("Datetime with unreasonable value: %s" % string)
445+
446+ time_parts = match.groupdict()
447+
448+ year = int(time_parts['year'])
449+ month = int(time_parts['month'])
450+ day = int(time_parts['day'])
451+ hour = int(time_parts['hour'])
452+ minute = int(time_parts['minute'])
453+ second = int(time_parts['second'])
454+ second_fraction = time_parts['second_fraction']
455+ if second_fraction is not None:
456+ milliseconds = second_fraction + '0' * (6 - len(second_fraction))
457+ milliseconds = int(milliseconds)
458+ else:
459+ milliseconds = 0
460+
461+ # The Relax NG validator accepts leap seconds, but the datetime
462+ # constructor rejects them. The time values submitted by the HWDB
463+ # client are not necessarily very precise, hence we can round down
464+ # to 59.999999 seconds without losing any real precision.
465+ if second > 59:
466+ second = 59
467+ milliseconds = 999999
468+
469+ dt = datetime(
470+ year, month, day, hour, minute, second, milliseconds,
471+ tzinfo=tz.tzutc())
472+
473+ tz_sign = time_parts['tz_sign']
474+ tz_hour = time_parts['tz_hour']
475+ tz_minute = time_parts['tz_minute']
476+ if tz_sign in ('-', '+'):
477+ delta = timedelta(hours=int(tz_hour), minutes=int(tz_minute))
478+ if tz_sign == '-':
479+ dt = dt + delta
480+ else:
481+ dt = dt - delta
482+
483+ return dt
484
485 def sizeof_bytes(bytes):
486 for x in ["bytes", "KB", "MB", "GB", "TB"]:
487@@ -78,3 +140,30 @@
488 hertz /= 1000.0
489
490 return string
491+
492+def string_to_type(string):
493+ """Return a typed representation for the given string.
494+
495+ The result might be a bool, int or float. The string might also be
496+ supplemented by a multiplier like KB which would return an int or
497+ float multiplied by 1024 for example.
498+
499+ :param string: The string representation.
500+ """
501+ if isinstance(string, basestring):
502+ for regex, formatter in TYPE_FORMATS:
503+ match = regex.match(string)
504+ if match:
505+ string = formatter(match)
506+ if len(match.groups()) > 1:
507+ unit = match.group(2)
508+ for regex, multiplier in TYPE_MULTIPLIERS:
509+ match = regex.match(unit)
510+ if match:
511+ string *= multiplier
512+ break
513+ else:
514+ raise ValueError("Unknown multiplier: %s" % unit)
515+ break
516+
517+ return string
518
519=== modified file 'checkbox/lib/dmi.py'
520--- checkbox/lib/dmi.py 2009-09-08 23:01:38 +0000
521+++ checkbox/lib/dmi.py 2011-11-18 18:04:27 +0000
522@@ -16,13 +16,15 @@
523 # You should have received a copy of the GNU General Public License
524 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
525 #
526+import os
527+
528
529 # See also 3.3.4.1 of the "System Management BIOS Reference Specification,
530 # Version 2.6.1" (Preliminary Standard) document, available from
531 # http://www.dmtf.org/standards/smbios.
532 class Dmi:
533 chassis = (
534- ("Undefined", "unknown"), # 0x00
535+ ("Undefined", "unknown"), # 0x00
536 ("Other", "unknown"),
537 ("Unknown", "unknown"),
538 ("Desktop", "desktop"),
539@@ -53,22 +55,171 @@
540 ("Blade", "server"),
541 ("Blade Enclosure", "unknown"))
542
543- chassis_names = [c[0] for c in chassis]
544- chassis_types = [c[1] for c in chassis]
545+ chassis_names = tuple(c[0] for c in chassis)
546+ chassis_types = tuple(c[1] for c in chassis)
547 chassis_name_to_type = dict(chassis)
548
549-
550-class DmiNotAvailable(object):
551- def __init__(self, function):
552- self._function = function
553-
554- def __get__(self, instance, cls=None):
555- self._instance = instance
556- return self
557-
558- def __call__(self, *args, **kwargs):
559- name = self._function(self._instance, *args, **kwargs)
560- if name == "Not Available":
561- name = None
562-
563- return name
564+ type_names = (
565+ "BIOS", # 0x00
566+ "System",
567+ "Base Board",
568+ "Chassis",
569+ "Processor",
570+ "Memory Controller",
571+ "Memory Module",
572+ "Cache",
573+ "Port Connector",
574+ "System Slots",
575+ "On Board Devices",
576+ "OEM Strings",
577+ "System Configuration Options",
578+ "BIOS Language",
579+ "Group Associations",
580+ "System Event Log",
581+ "Physical Memory Array",
582+ "Memory Device",
583+ "32-bit Memory Error",
584+ "Memory Array Mapped Address",
585+ "Memory Device Mapped Address",
586+ "Built-in Pointing Device",
587+ "Portable Battery",
588+ "System Reset",
589+ "Hardware Security",
590+ "System Power Controls",
591+ "Voltage Probe",
592+ "Cooling Device",
593+ "Temperature Probe",
594+ "Electrical Current Probe",
595+ "Out-of-band Remote Access",
596+ "Boot Integrity Services",
597+ "System Boot",
598+ "64-bit Memory Error",
599+ "Management Device",
600+ "Management Device Component",
601+ "Management Device Threshold Data",
602+ "Memory Channel",
603+ "IPMI Device",
604+ "Power Supply",
605+ )
606+
607+
608+class DmiDevice:
609+
610+ bus = "dmi"
611+ driver = None
612+ product_id = None
613+ vendor_id = None
614+
615+ _product_blacklist = (
616+ "<BAD INDEX>",
617+ "N/A",
618+ "Not Available",
619+ "INVALID",
620+ "OEM",
621+ "Product Name",
622+ "System Product Name",
623+ "To be filled by O.E.M.",
624+ "To Be Filled By O.E.M.",
625+ "To Be Filled By O.E.M. by More String",
626+ "Unknown",
627+ "Uknown",
628+ "Unknow",
629+ "xxxxxxxxxxxxxx",
630+ )
631+ _vendor_blacklist = (
632+ "<BAD INDEX>",
633+ "Not Available",
634+ "OEM",
635+ "OEM Manufacturer",
636+ "System manufacturer",
637+ "System Manufacturer",
638+ "System Name",
639+ "To be filled by O.E.M.",
640+ "To Be Filled By O.E.M.",
641+ "To Be Filled By O.E.M. by More String",
642+ "Unknow", # XXX This is correct mispelling
643+ "Unknown",
644+ )
645+ _serial_blacklist = (
646+ "0",
647+ "00000000",
648+ "00 00 00 00 00 00 00 00",
649+ "0123456789",
650+ "Base Board Serial Number",
651+ "Chassis Serial Number",
652+ "N/A",
653+ "None",
654+ "Not Applicable",
655+ "Not Available",
656+ "Not Specified",
657+ "OEM",
658+ "System Serial Number",
659+ )
660+ _version_blacklist = (
661+ "-1",
662+ "<BAD INDEX>",
663+ "N/A",
664+ "None",
665+ "Not Applicable",
666+ "Not Available",
667+ "Not Specified",
668+ "OEM",
669+ "System Version",
670+ "Unknown",
671+ "x.x",
672+ )
673+
674+ def __init__(self, attributes, category):
675+ self._attributes = attributes
676+ self.category = category
677+
678+ @property
679+ def path(self):
680+ path = "/devices/virtual/dmi/id"
681+ return os.path.join(path, self.category.lower())
682+
683+ @property
684+ def product(self):
685+ if self.category == "CHASSIS":
686+ type_string = self._attributes.get("chassis_type", "0")
687+ try:
688+ type_index = int(type_string)
689+ return Dmi.chassis_names[type_index]
690+ except ValueError:
691+ return type_string
692+
693+ for name in "name", "version":
694+ attribute = "%s_%s" % (self.category.lower(), name)
695+ product = self._attributes.get(attribute)
696+ if product and product not in self._product_blacklist:
697+ return product
698+
699+ return None
700+
701+ @property
702+ def vendor(self):
703+ for name in "manufacturer", "vendor":
704+ attribute = "%s_%s" % (self.category.lower(), name)
705+ vendor = self._attributes.get(attribute)
706+ if vendor and vendor not in self._vendor_blacklist:
707+ return vendor
708+
709+ return None
710+
711+ @property
712+ def serial(self):
713+ attribute = "%s_serial" % self.category.lower()
714+ serial = self._attributes.get(attribute)
715+ if serial and serial not in self._serial_blacklist:
716+ return serial
717+
718+ return None
719+
720+ @property
721+ def version(self):
722+ attribute = "%s_version" % self.category.lower()
723+ version = self._attributes.get(attribute)
724+ if version and version not in self._version_blacklist:
725+ return version
726+
727+ return None
728
729=== modified file 'checkbox/lib/safe.py'
730--- checkbox/lib/safe.py 2009-03-17 09:46:16 +0000
731+++ checkbox/lib/safe.py 2011-11-18 18:04:27 +0000
732@@ -94,3 +94,8 @@
733 md5sum = digest.hexdigest()
734
735 return md5sum
736+
737+def safe_close(file):
738+ file.flush()
739+ os.fsync(file.fileno())
740+ file.close()
741
742=== modified file 'checkbox/lib/template.py'
743--- checkbox/lib/template.py 2010-04-06 14:17:46 +0000
744+++ checkbox/lib/template.py 2011-11-18 18:04:27 +0000
745@@ -95,7 +95,7 @@
746 if line.startswith("#"):
747 continue
748
749- match = re.search(r"^([-_.A-Za-z0-9]*):\s?(.*)", line)
750+ match = re.search(r"^([-_.A-Za-z0-9@]*):\s?(.*)", line)
751 if match:
752 _save(field, value, extended)
753 field = match.groups()[0].lower()
754
755=== modified file 'checkbox/message.py'
756--- checkbox/message.py 2010-03-09 16:58:36 +0000
757+++ checkbox/message.py 2011-11-18 18:04:27 +0000
758@@ -22,7 +22,7 @@
759 import posixpath
760
761 from checkbox.contrib import bpickle
762-
763+from checkbox.lib.safe import safe_close
764
765 HELD = "h"
766 BROKEN = "b"
767@@ -215,7 +215,7 @@
768 try:
769 return file.read()
770 finally:
771- file.close()
772+ safe_close(file)
773
774 def _get_flags(self, path):
775 basename = posixpath.basename(path)
776@@ -253,7 +253,8 @@
777
778 file = open(filename + ".tmp", "w")
779 file.write(message_data)
780- file.close()
781+ safe_close(file)
782+
783 os.rename(filename + ".tmp", filename)
784
785 # For now we use the inode as the message id, as it will work
786
787=== modified file 'checkbox/parsers/cpuinfo.py'
788--- checkbox/parsers/cpuinfo.py 2011-06-13 14:22:39 +0000
789+++ checkbox/parsers/cpuinfo.py 2011-11-18 18:04:27 +0000
790@@ -16,24 +16,26 @@
791 # You should have received a copy of the GNU General Public License
792 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
793 #
794-import os
795 import re
796
797+from os import uname
798+
799 from checkbox.lib.conversion import string_to_type
800-from checkbox.parsers.utils import implement_from_dict
801-
802-
803-class CpuinfoParser(object):
804-
805- def __init__(self, stream, uname=None):
806+
807+
808+class CpuinfoParser:
809+ """Parser for the /proc/cpuinfo file."""
810+
811+ def __init__(self, stream, machine=None):
812 self.stream = stream
813- self.uname = uname or os.uname()[4].lower()
814+ self.machine = machine or uname()[4].lower()
815
816 def getAttributes(self):
817 count = 0
818 attributes = {}
819 cpuinfo = self.stream.read()
820- for block in cpuinfo.split("\n\n"):
821+ for block in re.split(r"\n{2,}", cpuinfo):
822+ block = block.strip()
823 if not block:
824 continue
825
826@@ -54,19 +56,23 @@
827
828 attributes[key.lower()] = value
829
830- attributes["count"] = count
831+ if attributes:
832+ attributes["count"] = count
833+
834 return attributes
835
836 def run(self, result):
837- uname = self.uname
838 attributes = self.getAttributes()
839+ if not attributes:
840+ return
841
842 # Default values
843+ machine = self.machine
844 processor = {
845- "platform": uname,
846+ "platform": machine,
847 "count": 1,
848- "type": self.uname,
849- "model": self.uname,
850+ "type": machine,
851+ "model": machine,
852 "model_number": "",
853 "model_version": "",
854 "model_revision": "",
855@@ -113,7 +119,6 @@
856 "type": "platform",
857 "model": "cpu",
858 "model_version": "revision",
859- "vendor": "machine",
860 "speed": "clock"},
861 ("sparc64", "sparc",): {
862 "count": "ncpus probed",
863@@ -126,7 +131,7 @@
864 bogompips_string = attributes.get("bogomips", "0.0")
865 processor["bogomips"] = int(round(float(bogompips_string)))
866 for platform, conversion in platform_to_conversion.iteritems():
867- if uname in platform:
868+ if machine in platform:
869 for pkey, ckey in conversion.iteritems():
870 if isinstance(ckey, (list, tuple)):
871 processor[pkey] = "/".join([attributes[k]
872@@ -134,13 +139,11 @@
873 elif ckey in attributes:
874 processor[pkey] = attributes[ckey]
875
876- # Adjust platform and vendor
877- if uname[0] == "i" and uname[-2:] == "86":
878- processor["platform"] = "i386"
879- elif uname[:5] == "alpha":
880- processor["platform"] = "alpha"
881- elif uname[:5] == "sparc":
882- processor["vendor"] = "sun"
883+ # Adjust platform
884+ if machine[0] == "i" and machine[-2:] == "86":
885+ processor["platform"] = u"i386"
886+ elif machine[:5] == "alpha":
887+ processor["platform"] = u"alpha"
888
889 # Adjust cache
890 if processor["cache"]:
891@@ -148,14 +151,14 @@
892
893 # Adjust speed
894 try:
895- if uname[:5] == "alpha":
896+ if machine[:5] == "alpha":
897 speed = processor["speed"].split()[0]
898 processor["speed"] = int(round(float(speed))) / 1000000
899- elif uname[:5] == "sparc":
900+ elif machine[:5] == "sparc":
901 speed = processor["speed"]
902 processor["speed"] = int(round(float(speed))) / 2
903 else:
904- if uname[:3] == "ppc":
905+ if machine[:3] == "ppc":
906 # String is appended with "mhz"
907 speed = processor["speed"][:-3]
908 else:
909@@ -174,18 +177,4 @@
910 if processor["count"] == 0:
911 processor["count"] = 1
912
913- for key, value in processor.iteritems():
914- camel_case = key.replace("_", " ").title().replace(" ", "")
915- method_name = "set%s" % camel_case
916- method = getattr(result, method_name)
917- method(value)
918-
919-
920-class CpuinfoResult(dict):
921-
922- def __getattr__(self, name):
923- name = re.sub(r"([A-Z])", "_\\1", name)[4:].lower()
924- def setAll(value):
925- self[name] = value
926-
927- return setAll
928+ result.setProcessor(processor)
929
930=== added file 'checkbox/parsers/cputable'
931--- checkbox/parsers/cputable 1970-01-01 00:00:00 +0000
932+++ checkbox/parsers/cputable 2011-11-18 18:04:27 +0000
933@@ -0,0 +1,40 @@
934+# This file contains the table of known CPU names.
935+#
936+# Architecture names are formed as a combination of the system name
937+# (from ostable) and CPU name (from this table) after mapping from
938+# the Debian triplet (from triplettable). A list of architecture
939+# names in the Debian ‘sid’ distribution can be found in the archtable
940+# file.
941+#
942+# Column 1 is the Debian name for the CPU, used to form the cpu part in
943+# the Debian triplet.
944+# Column 2 is the GNU name for the CPU, used to output build and host
945+# targets in ‘dpkg-architecture’.
946+# Column 3 is an extended regular expression used to match against the
947+# CPU part of the output of the GNU config.guess script.
948+# Column 4 is the size (in bits) of the integers/pointers
949+# Column 5 is the endianness (byte ordering in numbers)
950+#
951+# <Debian name> <GNU name> <config.guess regex> <Bits> <Endianness>
952+i386 i686 (i[3456]86|pentium) 32 little
953+ia64 ia64 ia64 64 little
954+alpha alpha alpha.* 64 little
955+amd64 x86_64 x86_64 64 little
956+armeb armeb arm.*b 32 big
957+arm arm arm.* 32 little
958+avr32 avr32 avr32 32 big
959+hppa hppa hppa.* 32 big
960+m32r m32r m32r 32 big
961+m68k m68k m68k 32 big
962+mips mips mips(eb)? 32 big
963+mipsel mipsel mipsel 32 little
964+powerpc powerpc (powerpc|ppc) 32 big
965+ppc64 powerpc64 (powerpc|ppc)64 64 big
966+s390 s390 s390 32 big
967+s390x s390x s390x 64 big
968+sh3 sh3 sh3 32 little
969+sh3eb sh3eb sh3eb 32 big
970+sh4 sh4 sh4 32 little
971+sh4eb sh4eb sh4eb 32 big
972+sparc sparc sparc 32 big
973+sparc64 sparc64 sparc64 64 big
974
975=== added file 'checkbox/parsers/cputable.py'
976--- checkbox/parsers/cputable.py 1970-01-01 00:00:00 +0000
977+++ checkbox/parsers/cputable.py 2011-11-18 18:04:27 +0000
978@@ -0,0 +1,42 @@
979+#
980+# This file is part of Checkbox.
981+#
982+# Copyright 2011 Canonical Ltd.
983+#
984+# Checkbox is free software: you can redistribute it and/or modify
985+# it under the terms of the GNU General Public License as published by
986+# the Free Software Foundation, either version 3 of the License, or
987+# (at your option) any later version.
988+#
989+# Checkbox is distributed in the hope that it will be useful,
990+# but WITHOUT ANY WARRANTY; without even the implied warranty of
991+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
992+# GNU General Public License for more details.
993+#
994+# You should have received a copy of the GNU General Public License
995+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
996+#
997+import re
998+
999+
1000+CPUTABLE_RE = re.compile(
1001+ r"^(?!\#)(?P<debian_name>\S+)"
1002+ r"\s+(?P<gnu_name>\S+)"
1003+ r"\s+(?P<regex>\S+)"
1004+ r"\s+(?P<bits>\d+)"
1005+ r"\s+(?P<endianness>big|little)")
1006+
1007+
1008+class CputableParser:
1009+ """Parser for the /usr/share/dpkg/cputable file."""
1010+
1011+ def __init__(self, stream):
1012+ self.stream = stream
1013+
1014+ def run(self, result):
1015+ for line in self.stream.readlines():
1016+ match = CPUTABLE_RE.match(line)
1017+ if match:
1018+ cpu = match.groupdict()
1019+ cpu["bits"] = int(cpu["bits"])
1020+ result.addCpu(cpu)
1021
1022=== added file 'checkbox/parsers/deferred.py'
1023--- checkbox/parsers/deferred.py 1970-01-01 00:00:00 +0000
1024+++ checkbox/parsers/deferred.py 2011-11-18 18:04:27 +0000
1025@@ -0,0 +1,27 @@
1026+#
1027+# This file is part of Checkbox.
1028+#
1029+# Copyright 2011 Canonical Ltd.
1030+#
1031+# Checkbox is free software: you can redistribute it and/or modify
1032+# it under the terms of the GNU General Public License as published by
1033+# the Free Software Foundation, either version 3 of the License, or
1034+# (at your option) any later version.
1035+#
1036+# Checkbox is distributed in the hope that it will be useful,
1037+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1038+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1039+# GNU General Public License for more details.
1040+#
1041+# You should have received a copy of the GNU General Public License
1042+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1043+#
1044+class DeferredParser:
1045+ """Parser for deferred dispatching of events."""
1046+
1047+ def __init__(self, dispatcher, event_type="result"):
1048+ self.dispatcher = dispatcher
1049+ self.event_type = event_type
1050+
1051+ def run(self, result):
1052+ self.dispatcher.publishEvent(self.event_type, result)
1053
1054=== added file 'checkbox/parsers/dmidecode.py'
1055--- checkbox/parsers/dmidecode.py 1970-01-01 00:00:00 +0000
1056+++ checkbox/parsers/dmidecode.py 2011-11-18 18:04:27 +0000
1057@@ -0,0 +1,123 @@
1058+#
1059+# This file is part of Checkbox.
1060+#
1061+# Copyright 2011 Canonical Ltd.
1062+#
1063+# Checkbox is free software: you can redistribute it and/or modify
1064+# it under the terms of the GNU General Public License as published by
1065+# the Free Software Foundation, either version 3 of the License, or
1066+# (at your option) any later version.
1067+#
1068+# Checkbox is distributed in the hope that it will be useful,
1069+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1070+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1071+# GNU General Public License for more details.
1072+#
1073+# You should have received a copy of the GNU General Public License
1074+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1075+#
1076+import re
1077+
1078+from string import (
1079+ hexdigits,
1080+ uppercase,
1081+ )
1082+
1083+from checkbox.lib.dmi import (
1084+ Dmi,
1085+ DmiDevice,
1086+ )
1087+
1088+
1089+HANDLE_RE = re.compile(
1090+ r"^Handle (?P<handle>0x[%s]{4}), "
1091+ r"DMI type (?P<type>\d+), "
1092+ r"(?P<size>\d+) bytes$"
1093+ % hexdigits)
1094+KEY_VALUE_RE = re.compile(
1095+ r"^\t(?P<key>[%s].+):( (?P<value>.+))?$"
1096+ % uppercase)
1097+
1098+
1099+class DmidecodeParser:
1100+ """Parser for the dmidecode command."""
1101+
1102+ _key_map = {
1103+ "ID": "serial",
1104+ "Manufacturer": "vendor",
1105+ "Product Name": "name",
1106+ "Serial Number": "serial",
1107+ "Type": "type",
1108+ "Vendor": "vendor",
1109+ "Version": "version",
1110+ }
1111+
1112+ def __init__(self, stream):
1113+ self.stream = stream
1114+
1115+ def _parseKey(self, key):
1116+ return self._key_map.get(key)
1117+
1118+ def _parseValue(self, value):
1119+ if value is not None:
1120+ value = value.strip()
1121+ if not value:
1122+ value = None
1123+
1124+ return value
1125+
1126+ def run(self, result):
1127+ output = self.stream.read()
1128+ for record in re.split(r"\n{2,}", output):
1129+ record = record.strip()
1130+ # Skip empty records
1131+ if not record:
1132+ continue
1133+
1134+ # Skip header record
1135+ lines = record.split("\n")
1136+ line = lines.pop(0)
1137+ if line.startswith("#"):
1138+ continue
1139+
1140+ # Skip records with an unsupported handle
1141+ match = HANDLE_RE.match(line)
1142+ if not match:
1143+ continue
1144+
1145+ # Skip records that are empty or inactive
1146+ if not lines or lines.pop(0) == "Inactive":
1147+ continue
1148+
1149+ # Skip disabled entries and end-of-table marker
1150+ type_index = int(match.group("type"))
1151+ if type_index >= len(Dmi.type_names):
1152+ continue
1153+
1154+ category = Dmi.type_names[type_index]
1155+ category = category.upper().split(" ")[-1]
1156+ if category not in (
1157+ "BOARD", "BIOS", "CHASSIS", "PROCESSOR", "SYSTEM"):
1158+ continue
1159+
1160+ # Parse attributes
1161+ attributes = {}
1162+ for line in lines:
1163+ # Skip lines with an unsupported key/value pair
1164+ match = KEY_VALUE_RE.match(line)
1165+ if not match:
1166+ continue
1167+
1168+ # Skip lines with an unsupported key
1169+ key = self._parseKey(match.group("key"))
1170+ if not key:
1171+ continue
1172+
1173+ key = "%s_%s" % (category.lower(), key)
1174+ value = self._parseValue(match.group("value"))
1175+ attributes[key] = value
1176+
1177+ device = DmiDevice(attributes, category)
1178+ result.addDmiDevice(device)
1179+
1180+ return result
1181
1182=== added file 'checkbox/parsers/meminfo.py'
1183--- checkbox/parsers/meminfo.py 1970-01-01 00:00:00 +0000
1184+++ checkbox/parsers/meminfo.py 2011-11-18 18:04:27 +0000
1185@@ -0,0 +1,46 @@
1186+#
1187+# This file is part of Checkbox.
1188+#
1189+# Copyright 2011 Canonical Ltd.
1190+#
1191+# Checkbox is free software: you can redistribute it and/or modify
1192+# it under the terms of the GNU General Public License as published by
1193+# the Free Software Foundation, either version 3 of the License, or
1194+# (at your option) any later version.
1195+#
1196+# Checkbox is distributed in the hope that it will be useful,
1197+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1198+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1199+# GNU General Public License for more details.
1200+#
1201+# You should have received a copy of the GNU General Public License
1202+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1203+#
1204+import re
1205+
1206+
1207+class MeminfoParser:
1208+ """Parser for the /proc/meminfo file."""
1209+
1210+ def __init__(self, stream):
1211+ self.stream = stream
1212+
1213+ def run(self, result):
1214+ key_value_pattern = re.compile(r"(?P<key>.*):\s+(?P<value>.*)")
1215+ meminfo_map = {
1216+ "MemTotal": "total",
1217+ "SwapTotal": "swap"}
1218+
1219+ meminfo = {}
1220+ for line in self.stream.readlines():
1221+ line = line.strip()
1222+ match = key_value_pattern.match(line)
1223+ if match:
1224+ key = match.group("key")
1225+ if key in meminfo_map:
1226+ key = meminfo_map[key]
1227+ value = match.group("value")
1228+ (integer, factor) = value.split()
1229+ meminfo[key] = int(integer) * 1024
1230+
1231+ result.setMemory(meminfo)
1232
1233=== modified file 'checkbox/parsers/submission.py'
1234--- checkbox/parsers/submission.py 2011-06-13 14:22:39 +0000
1235+++ checkbox/parsers/submission.py 2011-11-18 18:04:27 +0000
1236@@ -16,430 +16,521 @@
1237 # You should have received a copy of the GNU General Public License
1238 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
1239 #
1240-import re
1241-
1242-import logging
1243-
1244-from datetime import (
1245- datetime,
1246- timedelta,
1247- )
1248-from dateutil import tz
1249-
1250-from checkbox.parsers.device import DeviceResult
1251-from checkbox.parsers.udev import UdevParser
1252-from checkbox.parsers.utils import implement_from_dict
1253-
1254 try:
1255 import xml.etree.cElementTree as etree
1256 except ImportError:
1257 import cElementTree as etree
1258
1259-
1260-_time_regex = re.compile(r"""
1261- ^(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d)
1262- T(?P<hour>\d\d):(?P<minute>\d\d):(?P<second>\d\d)
1263- (?:\.(?P<second_fraction>\d{0,6}))?
1264- (?P<tz>
1265- (?:(?P<tz_sign>[-+])(?P<tz_hour>\d\d):(?P<tz_minute>\d\d))
1266- | Z)?$
1267- """,
1268- re.VERBOSE)
1269-
1270-_xml_illegal_regex = re.compile(
1271- u"([\u0000-\u0008\u000b-\u000c\u000e-\u001f\ufffe-\uffff])"
1272- + u"|([%s-%s][^%s-%s])|([^%s-%s][%s-%s])|([%s-%s]$)|(^[%s-%s])" % (
1273- unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff),
1274- unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff),
1275- unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff)))
1276-
1277-
1278-class HALDevice(object):
1279-
1280- def __init__(self, id, udi, properties):
1281- self.id = id
1282- self.udi = udi
1283- self.properties = properties
1284-
1285-
1286-class SubmissionStream(object):
1287-
1288- default_size = 4096
1289-
1290- def __init__(self, stream):
1291- self.stream = stream
1292- self._buffer = ""
1293- self._buffers = []
1294-
1295- def read(self, size=None):
1296- if size is None:
1297- size = self.default_size
1298-
1299- info_start_regex = re.compile("^<info .*>$")
1300- info_end_regex = re.compile("^</info>$")
1301-
1302- in_info = False
1303- length = sum(len(buffer) for buffer in self._buffers)
1304-
1305- while length < size:
1306- try:
1307- buffer = self.stream.next()
1308- except StopIteration:
1309- break
1310-
1311- if not in_info:
1312- if info_start_regex.match(buffer):
1313- in_info = True
1314- self._buffer += "".join(self._buffers)
1315- self._buffers = [buffer]
1316- else:
1317- length += len(buffer)
1318- self._buffers.append(buffer)
1319- else:
1320- self._buffers.append(buffer)
1321- if info_end_regex.match(buffer):
1322- in_info = False
1323-
1324- buffer = "".join(self._buffers)
1325- self._buffers = []
1326-
1327- if not _xml_illegal_regex.search(buffer):
1328- length += len(buffer)
1329- self._buffer += buffer
1330-
1331- if self._buffers:
1332- self._buffer += "".join(self._buffers)
1333- self._buffers = []
1334-
1335- if not self._buffer:
1336- return None
1337-
1338- data = self._buffer[:size]
1339- self._buffers = [self._buffer[size:]]
1340- self._buffer = ""
1341-
1342- return data
1343-
1344-
1345-class SubmissionParser(object):
1346-
1347- default_name = "unknown"
1348-
1349- def __init__(self, stream, name=None):
1350- self.stream = SubmissionStream(stream)
1351- self.name = name or self.default_name
1352+from StringIO import StringIO
1353+from logging import getLogger
1354+from pkg_resources import resource_string
1355+
1356+from checkbox.lib.conversion import string_to_datetime
1357+
1358+from checkbox import parsers
1359+from checkbox.dispatcher import DispatcherQueue
1360+from checkbox.parsers.cpuinfo import CpuinfoParser
1361+from checkbox.parsers.cputable import CputableParser
1362+from checkbox.parsers.deferred import DeferredParser
1363+from checkbox.parsers.dmidecode import DmidecodeParser
1364+from checkbox.parsers.meminfo import MeminfoParser
1365+from checkbox.parsers.udevadm import UdevadmParser
1366+from checkbox.job import (FAIL, PASS, UNINITIATED, UNRESOLVED,
1367+ UNSUPPORTED, UNTESTED)
1368+
1369+
1370+class SubmissionResult:
1371+
1372+ def __init__(self, test_run_factory, **kwargs):
1373+ self.test_run_factory = test_run_factory
1374+ self.test_run_kwargs = kwargs
1375+ self.dispatcher = DispatcherQueue()
1376+
1377+ # Register handlers to incrementally add information
1378+ register = self.dispatcher.registerHandler
1379+ register(("cpu", "architecture",), self.addCpuArchitecture)
1380+ register(("identifier",), self.addIdentifier)
1381+ register(("test_run", "attachment",), self.addAttachment)
1382+ register(("test_run", "device",), self.addDeviceState)
1383+ register(("test_run", "distribution",), self.setDistribution)
1384+ register(("test_run", "package_version",), self.addPackageVersion)
1385+ register(("test_run", "test_result",), self.addTestResult)
1386+
1387+ # Register handlers to set information once
1388+ register(("architecture",), self.setArchitecture, count=1)
1389+ register(
1390+ ("cpuinfo", "machine", "cpuinfo_result",),
1391+ self.setCpuinfo, count=1)
1392+ register(
1393+ ("meminfo", "meminfo_result",),
1394+ self.setMeminfo, count=1)
1395+ register(
1396+ ("project", "series",),
1397+ self.setTestRun, count=1)
1398+ register(
1399+ ("test_run", "architecture",),
1400+ self.setArchitectureState, count=1)
1401+ register(
1402+ ("test_run", "memory",),
1403+ self.setMemoryState, count=1)
1404+ register(
1405+ ("test_run", "processor",),
1406+ self.setProcessorState, count=1)
1407+ register(
1408+ ("udevadm", "bits", "udevadm_result",),
1409+ self.setUdevadm, count=1)
1410+
1411+ # Publish events passed as keyword arguments
1412+ if "project" in kwargs:
1413+ self.dispatcher.publishEvent("project", kwargs.pop("project"))
1414+ self.dispatcher.publishEvent("series", kwargs.pop("series", None))
1415+
1416+ def addAttachment(self, test_run, attachment):
1417+ test_run.addAttachment(**attachment)
1418+
1419+ def addContext(self, text, command=None):
1420+ if text.strip() == "Command not found.":
1421+ return
1422+
1423+ self.dispatcher.publishEvent(
1424+ "attachment", {"name": command, "content": text })
1425+
1426+ parsers = {
1427+ "cat /proc/cpuinfo": self.parseCpuinfo,
1428+ "cat /proc/meminfo": self.parseMeminfo,
1429+ "dmidecode": DmidecodeParser,
1430+ "udevadm info --export-db": self.parseUdevadm,
1431+ }
1432+ parser = parsers.get(command)
1433+ if parser:
1434+ if not isinstance(text, unicode):
1435+ text = text.decode("utf-8")
1436+ stream = StringIO(text)
1437+ p = parser(stream)
1438+ p.run(self)
1439+
1440+ def addCpu(self, cpu):
1441+ self.dispatcher.publishEvent("cpu", cpu)
1442+
1443+ def addCpuArchitecture(self, cpu, architecture):
1444+ if cpu["debian_name"] == architecture:
1445+ self.dispatcher.publishEvent("machine", cpu["gnu_name"])
1446+ self.dispatcher.publishEvent("bits", cpu["bits"])
1447+
1448+ def addDevice(self, device):
1449+ self.dispatcher.publishEvent("device", device)
1450+
1451+ def addDeviceState(self, test_run, device):
1452+ test_run.addDeviceState(
1453+ bus_name=device.bus, category_name=device.category,
1454+ product_name=device.product, vendor_name=device.vendor,
1455+ product_id=device.product_id, vendor_id=device.vendor_id,
1456+ subproduct_id=device.subproduct_id,
1457+ subvendor_id=device.subvendor_id,
1458+ driver_name=device.driver, path=device.path)
1459+
1460+ def addDmiDevice(self, device):
1461+ if device.serial:
1462+ self.dispatcher.publishEvent("identifier", device.serial)
1463+
1464+ if device.category in ("BOARD", "SYSTEM") \
1465+ and device.vendor != device.product \
1466+ and device.product is not None:
1467+ self.dispatcher.publishEvent("model", device.product)
1468+ self.dispatcher.publishEvent("make", device.vendor)
1469+ self.dispatcher.publishEvent("version", device.version)
1470+
1471+ def addIdentifier(self, identifier):
1472+ try:
1473+ self.identifiers.append(identifier)
1474+ except AttributeError:
1475+ self.identifiers = [identifier]
1476+ self.dispatcher.publishEvent("identifiers", self.identifiers)
1477+
1478+ def addPackage(self, package):
1479+ package_version = {
1480+ "name": package["name"],
1481+ "version": package["properties"]["version"],
1482+ }
1483+ self.dispatcher.publishEvent("package_version", package_version)
1484+
1485+ def addPackageVersion(self, test_run, package_version):
1486+ test_run.addPackageVersion(**package_version)
1487+
1488+ def addQuestion(self, question):
1489+ answer_to_status = {
1490+ "fail": FAIL,
1491+ "no": FAIL,
1492+ "pass": PASS,
1493+ "skip": UNTESTED,
1494+ "uninitiated": UNINITIATED,
1495+ "unresolved": UNRESOLVED,
1496+ "unsupported": UNSUPPORTED,
1497+ "untested": UNTESTED,
1498+ "yes": PASS,
1499+ }
1500+
1501+ test_result = dict(
1502+ name=question["name"],
1503+ output=question["comment"],
1504+ status=answer_to_status[question["answer"]["value"]],
1505+ )
1506+ test_result.update(self.test_run_kwargs)
1507+ self.dispatcher.publishEvent("test_result", test_result)
1508+
1509+ def addTestResult(self, test_run, test_result):
1510+ test_run.addTestResult(**test_result)
1511+
1512+ def addSummary(self, name, value):
1513+ if name == "architecture":
1514+ self.dispatcher.publishEvent("architecture", value)
1515+ elif name == "distribution":
1516+ self.dispatcher.publishEvent("project", value)
1517+ elif name == "distroseries":
1518+ self.dispatcher.publishEvent("series", value)
1519+
1520+ def parseCpuinfo(self, cpuinfo):
1521+ self.dispatcher.publishEvent("cpuinfo", cpuinfo)
1522+ return DeferredParser(self.dispatcher, "cpuinfo_result")
1523+
1524+ def parseMeminfo(self, meminfo):
1525+ self.dispatcher.publishEvent("meminfo", meminfo)
1526+ return DeferredParser(self.dispatcher, "meminfo_result")
1527+
1528+ def parseUdevadm(self, udevadm):
1529+ self.dispatcher.publishEvent("udevadm", udevadm)
1530+ return DeferredParser(self.dispatcher, "udevadm_result")
1531+
1532+ def setArchitecture(self, architecture):
1533+ string = resource_string(parsers.__name__, "cputable")
1534+ stream = StringIO(string.decode("utf-8"))
1535+ parser = CputableParser(stream)
1536+ parser.run(self)
1537+
1538+ def setArchitectureState(self, test_run, architecture):
1539+ test_run.setArchitectureState(architecture)
1540+
1541+ def setCpuinfo(self, cpuinfo, machine, cpuinfo_result):
1542+ parser = CpuinfoParser(cpuinfo, machine)
1543+ parser.run(cpuinfo_result)
1544+
1545+ def setMeminfo(self, meminfo, meminfo_result):
1546+ parser = MeminfoParser(meminfo)
1547+ parser.run(meminfo_result)
1548+
1549+ def setDistribution(self, test_run, distribution):
1550+ test_run.setDistribution(**distribution)
1551+
1552+ def setLSBRelease(self, lsb_release):
1553+ self.dispatcher.publishEvent("distribution", lsb_release)
1554+
1555+ def setMemory(self, memory):
1556+ self.dispatcher.publishEvent("memory", memory)
1557+
1558+ def setMemoryState(self, test_run, memory):
1559+ test_run.setMemoryState(**memory)
1560+
1561+ def setProcessor(self, processor):
1562+ self.dispatcher.publishEvent("processor", processor)
1563+
1564+ def setProcessorState(self, test_run, processor):
1565+ test_run.setProcessorState(
1566+ platform_name=processor["platform"],
1567+ make=processor["type"], model=processor["model"],
1568+ model_number=processor["model_number"],
1569+ model_version=processor["model_version"],
1570+ model_revision=processor["model_revision"],
1571+ cache=processor["cache"], other=processor["other"],
1572+ bogomips=processor["bogomips"], speed=processor["speed"],
1573+ count=processor["count"])
1574+
1575+ def setTestRun(self, project, series):
1576+ test_run = self.test_run_factory(
1577+ **self.test_run_kwargs)
1578+ self.dispatcher.publishEvent("test_run", test_run)
1579+
1580+ def setUdevadm(self, udevadm, bits, udevadm_result):
1581+ parser = UdevadmParser(udevadm, bits)
1582+ parser.run(udevadm_result)
1583+
1584+
1585+class SubmissionParser:
1586+
1587+ def __init__(self, file):
1588+ self.file = file
1589+ self.logger = getLogger()
1590
1591 def _getClient(self, node):
1592- return "_".join([node.get('name'), node.get('version')])
1593+ """Return a dictionary with the name and version of the client."""
1594+ return {
1595+ "name": node.get("name"),
1596+ "version": node.get("version"),
1597+ }
1598
1599 def _getProperty(self, node):
1600- """Parse a <property> node.
1601-
1602- :return: (name, (value, type)) of a property.
1603- """
1604- return (node.get('name'), self._getValueAttribute(node))
1605+ """Return the (name, value) of a property."""
1606+ return (node.get("name"), self._getValueAsType(node))
1607
1608 def _getProperties(self, node):
1609- """Parse <property> sub-nodes of node.
1610-
1611- :return: A dictionary, where each key is the name of a property;
1612- the values are the tuples (value, type) of a property.
1613- """
1614+ """Return a dictionary of properties."""
1615 properties = {}
1616 for child in node.getchildren():
1617+ assert child.tag == "property", \
1618+ "Unexpected tag <%s>, expected <property>" % child.tag
1619 name, value = self._getProperty(child)
1620- if name in properties:
1621- raise ValueError(
1622- '<property name="%s"> found more than once in <%s>'
1623- % (name, node.tag))
1624 properties[name] = value
1625
1626 return properties
1627
1628- def _getValueAttribute(self, node):
1629- """Return (value, type) of a <property> or <value> node."""
1630- type_ = node.get('type')
1631- if type_ in ('dbus.Boolean', 'bool'):
1632- value = node.text.strip() == 'True'
1633-
1634- elif type_ in ('str', 'dbus.String', 'dbus.UTF8String'):
1635+ def _getValueAsType(self, node):
1636+ """Return value of a node as the type attribute."""
1637+ type_ = node.get("type")
1638+ if type_ in ("bool",):
1639 value = node.text.strip()
1640-
1641- elif type_ in ('dbus.Byte', 'dbus.Int16', 'dbus.Int32', 'dbus.Int64',
1642- 'dbus.UInt16', 'dbus.UInt32', 'dbus.UInt64', 'int',
1643- 'long'):
1644- value = int(node.text.strip())
1645-
1646- elif type_ in ('dbus.Double', 'float'):
1647- value = float(node.text.strip())
1648-
1649- elif type_ in ('dbus.Array', 'list'):
1650- value = [self._getValueAttribute(child)
1651- for child in node.getchildren()]
1652-
1653- elif type_ in ('dbus.Dictionary', 'dict'):
1654- value = dict((child.get('name'), self._getValueAttribute(child))
1655- for child in node.getchildren())
1656-
1657+ assert value in ("True", "False",), \
1658+ "Unexpected boolean value '%s' in <%s>" % (value, node.tag)
1659+ return value == "True"
1660+ elif type_ in ("str",):
1661+ return unicode(node.text.strip())
1662+ elif type_ in ("int", "long",):
1663+ return int(node.text.strip())
1664+ elif type_ in ("float",):
1665+ return float(node.text.strip())
1666+ elif type_ in ("list",):
1667+ return list(self._getValueAsType(child)
1668+ for child in node.getchildren())
1669+ elif type_ in ("dict",):
1670+ return dict((child.get("name"), self._getValueAsType(child))
1671+ for child in node.getchildren())
1672 else:
1673- # This should not happen.
1674 raise AssertionError(
1675- 'Unexpected <property> or <value> type: %s' % type_)
1676-
1677- return value
1678-
1679- def _getValueAttributeAsBoolean(self, node):
1680+ "Unexpected type '%s' in <%s>" % (type_, node.tag))
1681+
1682+ def _getValueAsBoolean(self, node):
1683 """Return the value of the attribute "value" as a boolean."""
1684- return node.attrib['value'] == "True"
1685-
1686- def _getValueAttributeAsString(self, node):
1687+ value = node.attrib["value"]
1688+ assert value in ("True", "False",), \
1689+ "Unexpected boolean value '%s' in tag <%s>" % (value, node.tag)
1690+ return value == "True"
1691+
1692+ def _getValueAsDatetime(self, node):
1693+ """Return the value of the attribute "value" as a datetime."""
1694+ string = node.attrib["value"]
1695+ return string_to_datetime(string)
1696+
1697+ def _getValueAsString(self, node):
1698 """Return the value of the attribute "value"."""
1699- return node.attrib['value']
1700-
1701- def _getValueAttributeAsDateTime(self, node):
1702- """Convert a "value" attribute into a datetime object."""
1703- time_text = node.get('value')
1704-
1705- # we cannot use time.strptime: this function accepts neither fractions
1706- # of a second nor a time zone given e.g. as '+02:30'.
1707- match = _time_regex.search(time_text)
1708-
1709- if match is None:
1710- raise ValueError(
1711- 'Timestamp with unreasonable value: %s' % time_text)
1712-
1713- time_parts = match.groupdict()
1714-
1715- year = int(time_parts['year'])
1716- month = int(time_parts['month'])
1717- day = int(time_parts['day'])
1718- hour = int(time_parts['hour'])
1719- minute = int(time_parts['minute'])
1720- second = int(time_parts['second'])
1721- second_fraction = time_parts['second_fraction']
1722- if second_fraction is not None:
1723- milliseconds = second_fraction + '0' * (6 - len(second_fraction))
1724- milliseconds = int(milliseconds)
1725- else:
1726- milliseconds = 0
1727-
1728- if second > 59:
1729- second = 59
1730- milliseconds = 999999
1731-
1732- timestamp = datetime(year, month, day, hour, minute, second,
1733- milliseconds, tzinfo=tz.tzutc())
1734-
1735- tz_sign = time_parts['tz_sign']
1736- tz_hour = time_parts['tz_hour']
1737- tz_minute = time_parts['tz_minute']
1738- if tz_sign in ('-', '+'):
1739- delta = timedelta(hours=int(tz_hour), minutes=int(tz_minute))
1740- if tz_sign == '-':
1741- timestamp = timestamp + delta
1742- else:
1743- timestamp = timestamp - delta
1744-
1745- return timestamp
1746-
1747- def _parseHAL(self, result, node):
1748- result.startDevices()
1749- for child in node.getchildren():
1750- id = int(child.get('id'))
1751- udi = child.get('udi')
1752- properties = self._getProperties(child)
1753- device = HALDevice(id, udi, properties)
1754- result.addDevice(device)
1755-
1756- result.endDevices()
1757-
1758- def _parseInfo(self, result, node):
1759- command = node.attrib['command']
1760- if command == "udevadm info --export-db":
1761- self._parseUdev(result, node)
1762-
1763- result.addInfo(command, node.text)
1764-
1765- def _parseUdev(self, result, node):
1766- result.startDevices()
1767-
1768- stream = StringIO(node.text)
1769- udev = UdevParser(stream)
1770- udev.run(result)
1771-
1772- result.endDevices()
1773-
1774- def _parseProcessors(self, result, node):
1775- result.startProcessors()
1776-
1777- for child in node.getchildren():
1778- id = int(child.get('id'))
1779- name = child.get('name')
1780- properties = self._getProperties(child)
1781- result.addProcessor(id, name, properties)
1782-
1783- result.endProcessors()
1784-
1785- def _parseRoot(self, result, node):
1786- parsers = {
1787- "summary": self._parseSummary,
1788- "hardware": self._parseHardware,
1789- "software": self._parseSoftware,
1790- "questions": self._parseQuestions,
1791- "context": self._parseContext,
1792- }
1793-
1794- for child in node.getchildren():
1795- parser = parsers.get(child.tag, self._parseNone)
1796- parser(result, child)
1797-
1798- def _parseSummary(self, result, node):
1799- parsers = {
1800- 'live_cd': self._getValueAttributeAsBoolean,
1801- 'system_id': self._getValueAttributeAsString,
1802- 'distribution': self._getValueAttributeAsString,
1803- 'distroseries': self._getValueAttributeAsString,
1804- 'architecture': self._getValueAttributeAsString,
1805- 'private': self._getValueAttributeAsBoolean,
1806- 'contactable': self._getValueAttributeAsBoolean,
1807- 'date_created': self._getValueAttributeAsDateTime,
1808- 'client': self._getClient,
1809- 'kernel-release': self._getValueAttributeAsString,
1810- }
1811-
1812- for child in node.getchildren():
1813- parser = parsers.get(child.tag, self._parseNone)
1814- value = parser(child)
1815- result.addSummary(child.tag, value)
1816-
1817- def _parseHardware(self, result, node):
1818- parsers = {
1819- 'hal': self._parseHAL,
1820- 'processors': self._parseProcessors,
1821- 'udev': self._parseUdev,
1822- }
1823-
1824- for child in node.getchildren():
1825- parser = parsers.get(child.tag, self._parseNone)
1826- parser(result, child)
1827-
1828- def _parseLSBRelease(self, result, node):
1829+ return unicode(node.attrib["value"])
1830+
1831+ def parseContext(self, result, node):
1832+ """Parse the <context> part of a submission."""
1833+ duplicates = set()
1834+ for child in node.getchildren():
1835+ assert child.tag == "info", \
1836+ "Unexpected tag <%s>, expected <info>" % child.tag
1837+ command = child.get("command")
1838+ if command not in duplicates:
1839+ duplicates.add(command)
1840+ result.addContext(child.text, command)
1841+ else:
1842+ self.logger.debug(
1843+ "Duplicate command found in tag <info>: %s" % command)
1844+
1845+ def parseHardware(self, result, node):
1846+ """Parse the <hardware> section of a submission."""
1847+ parsers = {
1848+ "dmi": DmidecodeParser,
1849+ "processors": self.parseProcessors,
1850+ "udev": result.parseUdevadm,
1851+ }
1852+
1853+ for child in node.getchildren():
1854+ parser = parsers.get(child.tag)
1855+ if parser:
1856+ if child.getchildren():
1857+ parser(result, child)
1858+ else:
1859+ text = child.text
1860+ if not isinstance(text, unicode):
1861+ text = text.decode("utf-8")
1862+ stream = StringIO(text)
1863+ p = parser(stream)
1864+ p.run(result)
1865+ else:
1866+ self.logger.debug(
1867+ "Unsupported tag <%s> in <hardware>" % child.tag)
1868+
1869+ def parseLSBRelease(self, result, node):
1870+ """Parse the <lsbrelease> part of a submission."""
1871 properties = self._getProperties(node)
1872- result.setDistribution(**properties)
1873-
1874- def _parsePackages(self, result, node):
1875- result.startPackages()
1876-
1877- for child in node.getchildren():
1878- id = int(child.get('id'))
1879- name = child.get('name')
1880+ result.setLSBRelease(properties)
1881+
1882+ def parsePackages(self, result, node):
1883+ """Parse the <packages> part of a submission."""
1884+ for child in node.getchildren():
1885+ assert child.tag == "package", \
1886+ "Unexpected tag <%s>, expected <package>" % child.tag
1887+
1888+ package = {
1889+ "name": child.get("name"),
1890+ "properties": self._getProperties(child),
1891+ }
1892+ result.addPackage(package)
1893+
1894+ def parseProcessors(self, result, node):
1895+ """Parse the <processors> part of a submission."""
1896+ processors = []
1897+ for child in node.getchildren():
1898+ assert child.tag == "processor", \
1899+ "Unexpected tag <%s>, expected <processor>" % child.tag
1900+
1901+ # Convert lists to space separated strings.
1902 properties = self._getProperties(child)
1903-
1904- result.addPackage(id, name, properties)
1905-
1906- result.endPackages()
1907-
1908- def _parseXOrg(self, result, node):
1909- drivers = {}
1910- for child in node.getchildren():
1911- info = dict(child.attrib)
1912- if 'device' in info:
1913- info['device'] = int(info['device'])
1914-
1915- name = info['name']
1916- if name in drivers:
1917- raise ValueError(
1918- '<driver name="%s"> appears more than once in <xorg>'
1919- % name)
1920-
1921- drivers[name] = info
1922-
1923- version = node.get('version')
1924- result.addXorg(version, drivers)
1925-
1926- def _parseSoftware(self, result, node):
1927- parsers = {
1928- 'lsbrelease': self._parseLSBRelease,
1929- 'packages': self._parsePackages,
1930- 'xorg': self._parseXOrg,
1931- }
1932-
1933- for child in node.getchildren():
1934- parser = parsers.get(child.tag, self._parseNone)
1935- parser(result, child)
1936-
1937- def _parseQuestions(self, result, node):
1938- result.startQuestions()
1939-
1940- for child in node.getchildren():
1941- question = {'name': child.get('name')}
1942- plugin = child.get('plugin', None)
1943+ for key, value in properties.iteritems():
1944+ if key in ("bogomips", "cache", "count", "speed",):
1945+ properties[key] = int(value)
1946+ elif isinstance(value, list):
1947+ properties[key] = u" ".join(value)
1948+ processors.append(properties)
1949+
1950+ # Check if /proc/cpuinfo was parsed already.
1951+ if any("platform" in processor for processor in processors):
1952+ result.setProcessor(processors[0])
1953+ else:
1954+ lines = []
1955+ for processor in processors:
1956+ # Convert some keys with underscores to spaces instead.
1957+ for key, value in processor.iteritems():
1958+ if "_" in key and key != "vendor_id":
1959+ key = key.replace("_", " ")
1960+
1961+ lines.append(u"%s: %s" % (key, value))
1962+
1963+ lines.append(u"")
1964+
1965+ stream = StringIO(u"\n".join(lines))
1966+ parser = result.parseCpuinfo(stream)
1967+ parser.run(result)
1968+
1969+ def parseQuestions(self, result, node):
1970+ """Parse the <questions> part of a submission."""
1971+ for child in node.getchildren():
1972+ assert child.tag == "question", \
1973+ "Unexpected tag <%s>, expected <question>" % child.tag
1974+ question = {
1975+ "name": child.get("name"),
1976+ "targets": [],
1977+ }
1978+ plugin = child.get("plugin", None)
1979 if plugin is not None:
1980- question['plugin'] = plugin
1981- question['targets'] = targets = []
1982+ question["plugin"] = plugin
1983+
1984 answer_choices = []
1985-
1986 for sub_node in child.getchildren():
1987 sub_tag = sub_node.tag
1988- if sub_tag == 'answer':
1989- question['answer'] = answer = {}
1990- answer['type'] = sub_node.get('type')
1991- if answer['type'] == 'multiple_choice':
1992- question['answer_choices'] = answer_choices
1993- unit = sub_node.get('unit', None)
1994+ if sub_tag == "answer":
1995+ question["answer"] = answer = {}
1996+ answer["type"] = sub_node.get("type")
1997+ if answer["type"] == "multiple_choice":
1998+ question["answer_choices"] = answer_choices
1999+ unit = sub_node.get("unit", None)
2000 if unit is not None:
2001- answer['unit'] = unit
2002- answer['value'] = sub_node.text.strip()
2003- elif sub_tag == 'answer_choices':
2004+ answer["unit"] = unit
2005+ answer["value"] = sub_node.text.strip()
2006+
2007+ elif sub_tag == "answer_choices":
2008 for value_node in sub_node.getchildren():
2009 answer_choices.append(
2010- self._getValueAttribute(value_node))
2011- elif sub_tag == 'target':
2012- target = {'id': int(sub_node.get('id'))}
2013- target['drivers'] = drivers = []
2014+ self._getValueAsType(value_node))
2015+
2016+ elif sub_tag == "target":
2017+ # The Relax NG schema ensures that the attribute
2018+ # id exists and that it is an integer
2019+ target = {"id": int(sub_node.get("id"))}
2020+ target["drivers"] = drivers = []
2021 for driver_node in sub_node.getchildren():
2022 drivers.append(driver_node.text.strip())
2023- targets.append(target)
2024- elif sub_tag in('comment', 'command'):
2025+ question["targets"].append(target)
2026+
2027+ elif sub_tag in ("comment", "command",):
2028 data = sub_node.text
2029 if data is not None:
2030 question[sub_tag] = data.strip()
2031
2032+ else:
2033+ raise AssertionError(
2034+ "Unexpected tag <%s> in <question>" % sub_tag)
2035+
2036 result.addQuestion(question)
2037
2038- result.endQuestions()
2039-
2040- def _parseContext(self, result, node):
2041- parsers = {
2042- 'info': self._parseInfo,
2043- }
2044-
2045- for child in node.getchildren():
2046- parser = parsers.get(child.tag, self._parseNone)
2047- parser(result, child)
2048-
2049- def _parseNone(self, result, node):
2050- pass
2051-
2052- def run(self, result):
2053+ def parseSoftware(self, result, node):
2054+ """Parse the <software> section of a submission."""
2055+ parsers = {
2056+ "lsbrelease": self.parseLSBRelease,
2057+ "packages": self.parsePackages,
2058+ }
2059+
2060+ for child in node.getchildren():
2061+ parser = parsers.get(child.tag)
2062+ if parser:
2063+ parser(result, child)
2064+ else:
2065+ self.logger.debug(
2066+ "Unsupported tag <%s> in <software>" % child.tag)
2067+
2068+ def parseSummary(self, result, node):
2069+ """Parse the <summary> section of a submission."""
2070+ parsers = {
2071+ "architecture": self._getValueAsString,
2072+ "client": self._getClient,
2073+ "contactable": self._getValueAsBoolean,
2074+ "date_created": self._getValueAsDatetime,
2075+ "distribution": self._getValueAsString,
2076+ "distroseries": self._getValueAsString,
2077+ "kernel-release": self._getValueAsString,
2078+ "live_cd": self._getValueAsBoolean,
2079+ "private": self._getValueAsBoolean,
2080+ "system_id": self._getValueAsString,
2081+ }
2082+
2083+ for child in node.getchildren():
2084+ parser = parsers.get(child.tag)
2085+ if parser:
2086+ value = parser(child)
2087+ result.addSummary(child.tag, value)
2088+ else:
2089+ self.logger.debug(
2090+ "Unsupported tag <%s> in <summary>" % child.tag)
2091+
2092+ def parseRoot(self, result, node):
2093+ """Parse the <system> root of a submission."""
2094+ parsers = {
2095+ "context": self.parseContext,
2096+ "hardware": self.parseHardware,
2097+ "questions": self.parseQuestions,
2098+ "software": self.parseSoftware,
2099+ "summary": self.parseSummary,
2100+ }
2101+
2102+ # Iterate over the root children, "summary" first
2103+ for child in node.getchildren():
2104+ parser = parsers.get(child.tag)
2105+ if parser:
2106+ parser(result, child)
2107+ else:
2108+ self.logger.debug(
2109+ "Unsupported tag <%s> in <system>" % child.tag)
2110+
2111+ def run(self, test_run_factory, **kwargs):
2112 parser = etree.XMLParser()
2113
2114- try:
2115- tree = etree.parse(self.stream, parser=parser)
2116- except SyntaxError, error:
2117- logging.error(error)
2118- return
2119-
2120+ tree = etree.parse(self.file, parser=parser)
2121 root = tree.getroot()
2122 if root.tag != "system":
2123- logging.error("Root node is not '<system>'")
2124- return
2125-
2126- self._parseRoot(result, root)
2127-
2128-
2129-SubmissionResult = implement_from_dict("SubmissionResult", [
2130- "startDevices", "endDevices", "addDevice", "startPackages",
2131- "endPackages", "addPackage", "startProcessors", "endProcessors",
2132- "addProcessor", "startQuestions", "endQuestions", "addQuestion",
2133- "addInfo", "addSummary", "addXorg", "setDistribution"], DeviceResult)
2134+ raise AssertionError(
2135+ "Unexpected tag <%s> at root, expected <system>" % root.tag)
2136+
2137+ result = SubmissionResult(test_run_factory, **kwargs)
2138+ self.parseRoot(result, root)
2139+
2140+ return result
2141
2142=== renamed file 'checkbox/parsers/udev.py' => 'checkbox/parsers/udevadm.py'
2143--- checkbox/parsers/udev.py 2011-09-14 21:16:02 +0000
2144+++ checkbox/parsers/udevadm.py 2011-11-18 18:04:27 +0000
2145@@ -16,38 +16,78 @@
2146 # You should have received a copy of the GNU General Public License
2147 # along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
2148 #
2149-import os
2150 import re
2151 import string
2152-import posixpath
2153-
2154-from curses.ascii import isprint
2155-
2156-from checkbox.lib.bit import get_bitmask, test_bit
2157-from checkbox.lib.dmi import Dmi, DmiNotAvailable
2158+
2159+from checkbox.lib.bit import (
2160+ get_bitmask,
2161+ test_bit,
2162+ )
2163 from checkbox.lib.input import Input
2164 from checkbox.lib.pci import Pci
2165 from checkbox.lib.usb import Usb
2166
2167
2168-class UdevDevice(object):
2169- __slots__ = ("_environment", "_attributes", "_stack")
2170-
2171- def __init__(self, environment, attributes, stack=[]):
2172- super(UdevDevice, self).__init__()
2173+PCI_RE = re.compile(
2174+ r"^pci:"
2175+ r"v(?P<vendor_id>[%(hex)s]{8})"
2176+ r"d(?P<product_id>[%(hex)s]{8})"
2177+ r"sv(?P<subvendor_id>[%(hex)s]{8})"
2178+ r"sd(?P<subproduct_id>[%(hex)s]{8})"
2179+ r"bc(?P<class>[%(hex)s]{2})"
2180+ r"sc(?P<subclass>[%(hex)s]{2})"
2181+ r"i(?P<interface>[%(hex)s]{2})"
2182+ % {"hex": string.hexdigits})
2183+PNP_RE = re.compile(
2184+ r"^acpi:"
2185+ r"(?P<vendor_name>[%(upper)s]{3})"
2186+ r"(?P<product_id>[%(hex)s]{4}):"
2187+ % {"upper": string.uppercase, "hex": string.hexdigits})
2188+USB_RE = re.compile(
2189+ r"^usb:"
2190+ r"v(?P<vendor_id>[%(hex)s]{4})"
2191+ r"p(?P<product_id>[%(hex)s]{4})"
2192+ r"d(?P<revision>[%(hex)s]{4})"
2193+ r"dc(?P<class>[%(hex)s]{2})"
2194+ r"dsc(?P<subclass>[%(hex)s]{2})"
2195+ r"dp(?P<protocol>[%(hex)s]{2})"
2196+ r"ic(?P<interface_class>[%(hex)s]{2})"
2197+ r"isc(?P<interface_subclass>[%(hex)s]{2})"
2198+ r"ip(?P<interface_protocol>[%(hex)s]{2})"
2199+ % {"hex": string.hexdigits})
2200+SCSI_RE = re.compile(
2201+ r"^scsi:"
2202+ r"t-0x(?P<type>[%(hex)s]{2})"
2203+ % {"hex": string.hexdigits})
2204+
2205+
2206+class UdevadmDevice:
2207+ __slots__ = ("_environment", "_bits", "_stack",)
2208+
2209+ def __init__(self, environment, bits=None, stack=[]):
2210 self._environment = environment
2211- self._attributes = attributes
2212+ self._bits = bits
2213 self._stack = stack
2214
2215 @property
2216 def bus(self):
2217- return self._environment.get("SUBSYSTEM")
2218+ # Change the bus from 'acpi' to 'pnp' for some devices
2219+ if PNP_RE.match(self._environment.get("MODALIAS", "")) \
2220+ and self.path.endswith(":00"):
2221+ return "pnp"
2222+
2223+ # Change the bus from 'block' to parent
2224+ if self._environment.get("DEVTYPE") == "disk" and self._stack:
2225+ return self._stack[-1]._environment.get("SUBSYSTEM")
2226+
2227+ bus = self._environment.get("SUBSYSTEM")
2228+ if bus == "pnp":
2229+ return None
2230+
2231+ return bus
2232
2233 @property
2234 def category(self):
2235- if "sys_vendor" in self._attributes:
2236- return "SYSTEM"
2237-
2238 if "IFINDEX" in self._environment:
2239 return "NETWORK"
2240
2241@@ -69,7 +109,10 @@
2242 return "NETWORK"
2243
2244 if class_id == Pci.BASE_CLASS_DISPLAY:
2245- return "VIDEO"
2246+ if subclass_id == Pci.CLASS_DISPLAY_VGA:
2247+ return "VIDEO"
2248+ else:
2249+ return "OTHER"
2250
2251 if class_id == Pci.BASE_CLASS_SERIAL \
2252 and subclass_id == Pci.CLASS_SERIAL_USB:
2253@@ -113,13 +156,9 @@
2254 or subclass_id == Pci.CLASS_BRIDGE_CARDBUS):
2255 return "SOCKET"
2256
2257- if "bInterfaceClass" in self._attributes:
2258- interface_class = int(
2259- self._attributes["bInterfaceClass"], 16)
2260- interface_subclass = int(
2261- self._attributes["bInterfaceSubClass"], 16)
2262- interface_protocol = int(
2263- self._attributes["bInterfaceProtocol"], 16)
2264+ if "TYPE" in self._environment and "INTERFACE" in self._environment:
2265+ interface_class, interface_subclass, interface_protocol = (
2266+ int(i) for i in self._environment["INTERFACE"].split("/"))
2267
2268 if interface_class == Usb.BASE_CLASS_AUDIO:
2269 return "AUDIO"
2270@@ -143,6 +182,25 @@
2271 else:
2272 return "WIRELESS"
2273
2274+ if "KEY" in self._environment:
2275+ key = self._environment["KEY"].strip("=")
2276+ bitmask = get_bitmask(key)
2277+
2278+ for i in range(Input.KEY_Q, Input.KEY_P + 1):
2279+ if not test_bit(i, bitmask, self._bits):
2280+ break
2281+ else:
2282+ return "KEYBOARD"
2283+
2284+ if test_bit(Input.KEY_CAMERA, bitmask, self._bits):
2285+ return "CAPTURE"
2286+
2287+ if test_bit(Input.BTN_TOUCH, bitmask, self._bits):
2288+ return "TOUCH"
2289+
2290+ if test_bit(Input.BTN_MOUSE, bitmask, self._bits):
2291+ return "MOUSE"
2292+
2293 if "ID_TYPE" in self._environment:
2294 id_type = self._environment["ID_TYPE"]
2295
2296@@ -155,22 +213,6 @@
2297 if id_type == "video":
2298 return "VIDEO"
2299
2300- if "KEY" in self._environment:
2301- key = self._environment["KEY"].strip("=")
2302- bitmask = get_bitmask(key)
2303-
2304- for i in range(Input.KEY_Q, Input.KEY_P + 1):
2305- if not test_bit(i, bitmask):
2306- break
2307- else:
2308- return "KEYBOARD"
2309-
2310- if test_bit(Input.BTN_TOUCH, bitmask):
2311- return "TOUCH"
2312-
2313- if test_bit(Input.BTN_MOUSE, bitmask):
2314- return "MOUSE"
2315-
2316 if "DEVTYPE" in self._environment:
2317 devtype = self._environment["DEVTYPE"]
2318 if devtype == "disk":
2319@@ -181,8 +223,10 @@
2320 return "FLOPPY"
2321
2322 if devtype == "scsi_device":
2323- type = int(self._attributes.get("type", "-1"))
2324- # Check for FLASH drives, see /lib/udev/rules.d/80-udisks.rules
2325+ match = SCSI_RE.match(self._environment.get("MODALIAS", ""))
2326+ type = int(match.group("type"), 16) if match else -1
2327+
2328+ # Check FLASH drives, see /lib/udev/rules.d/80-udisks.rules
2329 if type in (0, 7, 14) \
2330 and not any(d.driver == "rts_pstor" for d in self._stack):
2331 return "DISK"
2332@@ -216,8 +260,11 @@
2333 if "DRIVER" in self._environment:
2334 return self._environment["DRIVER"]
2335
2336- if "ID_USB_DRIVER" in self._environment:
2337- return self._environment["ID_USB_DRIVER"]
2338+ # Check parent device for driver
2339+ if self._stack:
2340+ parent = self._stack[-1]
2341+ if "DRIVER" in parent._environment:
2342+ return parent._environment["DRIVER"]
2343
2344 return None
2345
2346@@ -228,48 +275,36 @@
2347 @property
2348 def product_id(self):
2349 # pci
2350- if "PCI_ID" in self._environment:
2351- vendor_id, product_id = self._environment["PCI_ID"].split(":")
2352- return int(product_id, 16)
2353-
2354- # usb interface
2355- if "PRODUCT" in self._environment \
2356- and self._environment.get("DEVTYPE") == "usb_interface":
2357- vendor_id, product_id, revision \
2358- = self._environment["PRODUCT"].split("/")
2359- return int(product_id, 16)
2360-
2361- # usb device and ieee1394
2362- for attribute in "idProduct", "model_id":
2363- if attribute in self._attributes:
2364- return int(self._attributes[attribute], 16)
2365+ match = PCI_RE.match(self._environment.get("MODALIAS", ""))
2366+ if match:
2367+ return int(match.group("product_id"), 16)
2368+
2369+ # usb
2370+ match = USB_RE.match(self._environment.get("MODALIAS", ""))
2371+ if match:
2372+ return int(match.group("product_id"), 16)
2373
2374 # pnp
2375- if "id" in self._attributes:
2376- match = re.match(r"^(?P<vendor_name>.*)(?P<product_id>[%s]{4})$"
2377- % string.hexdigits, self._attributes["id"])
2378- if match:
2379- return int(match.group("product_id"), 16)
2380+ match = PNP_RE.match(self._environment.get("MODALIAS", ""))
2381+ if match:
2382+ product_id = int(match.group("product_id"), 16)
2383+ # Ignore interrupt controllers
2384+ if product_id > 0x0100:
2385+ return product_id
2386
2387 return None
2388
2389 @property
2390 def vendor_id(self):
2391 # pci
2392- if "PCI_ID" in self._environment:
2393- vendor_id, product_id = self._environment["PCI_ID"].split(":")
2394- return int(vendor_id, 16)
2395-
2396- # usb interface
2397- if "PRODUCT" in self._environment \
2398- and self._environment.get("DEVTYPE") == "usb_interface":
2399- vendor_id, product_id, revision \
2400- = self._environment["PRODUCT"].split("/")
2401- return int(vendor_id, 16)
2402-
2403- # usb device
2404- if "idVendor" in self._attributes:
2405- return int(self._attributes["idVendor"], 16)
2406+ match = PCI_RE.match(self._environment.get("MODALIAS", ""))
2407+ if match:
2408+ return int(match.group("vendor_id"), 16)
2409+
2410+ # usb
2411+ match = USB_RE.match(self._environment.get("MODALIAS", ""))
2412+ if match:
2413+ return int(match.group("vendor_id"), 16)
2414
2415 return None
2416
2417@@ -299,16 +334,19 @@
2418 if element in self._environment:
2419 return self._environment[element].strip('"')
2420
2421- for attribute in ("description",
2422- "model_name_kv",
2423- "model",
2424- "product_name"):
2425- if attribute in self._attributes:
2426- return self._attributes[attribute]
2427+ # disk
2428+ if self._environment.get("DEVTYPE") == "scsi_device":
2429+ for device in reversed(self._stack):
2430+ if device._environment.get("ID_BUS") == "usb":
2431+ return decode_id(device._environment["ID_MODEL_ENC"])
2432+
2433+ if self._environment.get("DEVTYPE") == "disk" \
2434+ and self._environment.get("ID_BUS") == "ata":
2435+ return decode_id(self._environment["ID_MODEL_ENC"])
2436
2437 # floppy
2438 if self.driver == "floppy":
2439- return "Platform Device"
2440+ return u"Platform Device"
2441
2442 return None
2443
2444@@ -320,168 +358,28 @@
2445 if "POWER_SUPPLY_MANUFACTURER" in self._environment:
2446 return self._environment["POWER_SUPPLY_MANUFACTURER"]
2447
2448- if "vendor" in self._attributes:
2449- vendor = self._attributes["vendor"]
2450- if not re.match(r"^0x[%s]{4}$" % string.hexdigits, vendor):
2451- return vendor
2452-
2453- # dmi
2454- if "sys_vendor" in self._attributes:
2455- return self._attributes["sys_vendor"]
2456-
2457 # pnp
2458- if "id" in self._attributes:
2459- match = re.match(r"^(?P<vendor_name>.*)(?P<product_id>[%s]{4})$"
2460- % string.hexdigits, self._attributes["id"])
2461- if match:
2462- return match.group("vendor_name")
2463-
2464- return None
2465-
2466-
2467-class UdevLocalDevice(UdevDevice):
2468-
2469- @property
2470- def bus(self):
2471- sys_path = posixpath.join(
2472- "/sys%s" % self._environment["DEVPATH"], "subsystem")
2473- if posixpath.islink(sys_path):
2474- link = os.readlink(sys_path)
2475- if "/" in link:
2476- return posixpath.basename(link)
2477-
2478- return None
2479-
2480- @property
2481- def vendor_id(self):
2482- vendor_id = super(UdevLocalDevice, self).vendor_id
2483- if vendor_id is not None:
2484- return vendor_id
2485-
2486- # ieee1394
2487- vendor_id_path = posixpath.join(self.path, "../vendor_id")
2488- if posixpath.exists(vendor_id_path):
2489- vendor_id = open(vendor_id_path, "r").read().strip()
2490- return int(vendor_id, 16)
2491-
2492- return None
2493-
2494- @property
2495- def product(self):
2496- product = super(UdevLocalDevice, self).product
2497- if product is not None:
2498- return product
2499-
2500- # sound
2501- bus = self.bus
2502- if bus == "sound":
2503- device = posixpath.basename(self._environment["DEVPATH"])
2504- match = re.match(
2505- r"(card|controlC|hwC|midiC)(?P<card>\d+)", device)
2506- if match:
2507- card = match.group("card")
2508- in_card = False
2509- file = open("/proc/asound/cards", "r")
2510- for line in file.readlines():
2511- line = line.strip()
2512- match = re.match(r"(?P<card>\d+) \[", line)
2513- if match:
2514- in_card = match.group("card") == card
2515-
2516- if in_card:
2517- match = re.match(r"""(?P<name>.*) """
2518- """at (?P<address>0x[%s]{8}) """
2519- """irq (?P<irq>\d+)""" % string.hexdigits, line)
2520- if match:
2521- return match.group("name")
2522-
2523- path = None
2524- match = re.match(
2525- r"pcmC(?P<card>\d+)D(?P<device>\d+)(?P<type>\w)", device)
2526- if match:
2527- path = "/proc/asound/card%s/pcm%s%c/info" % match.groups()
2528-
2529- match = re.match(
2530- r"(dsp|adsp|midi|amidi|audio|mixer)(?P<card>\d+)?", device)
2531- if match:
2532- card = match.group("card") or 0
2533- path = "/proc/asound/card%s/pcm0p/info" % card
2534-
2535- if path and posixpath.exists(path):
2536- file = open(path, "r")
2537- for line in file.readlines():
2538- match = re.match(r"name: (?P<name>.*)", line)
2539- if match:
2540- return match.group("name")
2541-
2542- return None
2543-
2544- @property
2545- def vendor(self):
2546- vendor = super(UdevLocalDevice, self).vendor
2547- if vendor is not None:
2548- return vendor
2549-
2550- # ieee1394
2551- vendor_path = posixpath.join(self.path, "../vendor_oui")
2552- if posixpath.exists(vendor_path):
2553- return open(vendor_path, "r").read().strip()
2554-
2555- return None
2556-
2557-
2558-class UdevDmiDevice(UdevDevice):
2559-
2560- def __init__(self, environment, attributes, category):
2561- super(UdevDmiDevice, self).__init__(environment, attributes)
2562- self._category = category
2563-
2564- @property
2565- def category(self):
2566- return self._category
2567-
2568- @property
2569- def path(self):
2570- path = super(UdevDmiDevice, self).path
2571- return posixpath.join(path, self._category.lower())
2572-
2573- @property
2574- def product(self):
2575- if self._category == "CHASSIS":
2576- type_string = self._attributes.get("chassis_type", "0")
2577- type_index = int(type_string)
2578- return Dmi.chassis_names[type_index]
2579-
2580- for name in "name", "version":
2581- attribute = "%s_%s" % (self._category.lower(), name)
2582- product = self._attributes.get(attribute)
2583- if product and product != "Not Available":
2584- return product
2585-
2586- return None
2587-
2588- @DmiNotAvailable
2589- def _getVendor(self):
2590- attribute = "%s_vendor" % self._category.lower()
2591- if attribute in self._attributes:
2592- return self._attributes[attribute]
2593-
2594- return None
2595-
2596- @property
2597- def vendor(self):
2598- return self._getVendor()
2599-
2600-
2601-class UdevParser(object):
2602- """udevadm parser."""
2603-
2604- device_factory = UdevDevice
2605- dmi_device_factory = UdevDmiDevice
2606-
2607- def __init__(self, stream):
2608+ match = PNP_RE.match(self._environment.get("MODALIAS", ""))
2609+ if match:
2610+ return match.group("vendor_name")
2611+
2612+ # disk
2613+ if self._environment.get("DEVTYPE") == "scsi_device":
2614+ for device in reversed(self._stack):
2615+ if device._environment.get("ID_BUS") == "usb":
2616+ return decode_id(device._environment["ID_VENDOR_ENC"])
2617+
2618+ return None
2619+
2620+
2621+class UdevadmParser:
2622+ """Parser for the udevadm command."""
2623+
2624+ device_factory = UdevadmDevice
2625+
2626+ def __init__(self, stream, bits=None):
2627 self.stream = stream
2628- self.stack = []
2629+ self.bits = bits
2630
2631 def _ignoreDevice(self, device):
2632 # Ignore devices without bus information
2633@@ -499,9 +397,8 @@
2634 and device.subvendor_id is None)):
2635 return True
2636
2637- # Ignore virtual devices except for dmi information
2638- if device.bus != "dmi" \
2639- and "virtual" in device.path.split(posixpath.sep):
2640+ # Ignore ACPI devices
2641+ if device.bus == "acpi":
2642 return True
2643
2644 return False
2645@@ -510,96 +407,60 @@
2646 return {}
2647
2648 def run(self, result):
2649- line_pattern = re.compile(r"(?P<key>\w):\s*(?P<value>.*)")
2650+ # Some attribute lines have a space character after the
2651+ # ':', others don't have it (see udevadm-info.c).
2652+ line_pattern = re.compile(r"(?P<key>[A-Z]):\s*(?P<value>.*)")
2653 multi_pattern = re.compile(r"(?P<key>[^=]+)=(?P<value>.*)")
2654
2655+ stack = []
2656 output = self.stream.read()
2657- for record in output.split("\n\n"):
2658+ for record in re.split("\n{2,}", output):
2659+ record = record.strip()
2660 if not record:
2661 continue
2662
2663 # Determine path and environment
2664 path = None
2665+ element = None
2666 environment = {}
2667 for line in record.split("\n"):
2668- if not line:
2669+ line_match = line_pattern.match(line)
2670+ if not line_match:
2671+ if environment:
2672+ # Append to last environment element
2673+ environment[element] += line
2674 continue
2675
2676- match = line_pattern.match(line)
2677- if not match:
2678- raise Exception(
2679- "Device line not supported: %s" % line)
2680-
2681- key = match.group("key")
2682- value = match.group("value")
2683+ key = line_match.group("key")
2684+ value = line_match.group("value")
2685
2686 if key == "P":
2687 path = value
2688 elif key == "E":
2689- match = multi_pattern.match(value)
2690- if not match:
2691+ key_match = multi_pattern.match(value)
2692+ if not key_match:
2693 raise Exception(
2694 "Device property not supported: %s" % value)
2695- environment[match.group("key")] = match.group("value")
2696+ element = key_match.group("key")
2697+ environment[element] = key_match.group("value")
2698
2699 # Update stack
2700- while self.stack:
2701- if self.stack[-1].path + "/" in path:
2702+ while stack:
2703+ if stack[-1].path + "/" in path:
2704 break
2705- self.stack.pop()
2706+ stack.pop()
2707
2708 # Set default DEVPATH
2709 environment.setdefault("DEVPATH", path)
2710
2711- # Determine attributes
2712- attributes = self.getAttributes(path)
2713-
2714- if path == "/devices/virtual/dmi/id":
2715- device = self.device_factory(environment, attributes)
2716- if not self._ignoreDevice(device):
2717- result.addDevice(device)
2718- for category in "BIOS", "BOARD", "CHASSIS":
2719- device = self.dmi_device_factory(
2720- environment, attributes, category)
2721- if not self._ignoreDevice(device):
2722- result.addDevice(device)
2723- else:
2724- device = self.device_factory(environment, attributes, self.stack)
2725- if not self._ignoreDevice(device):
2726- result.addDevice(device)
2727-
2728- self.stack.append(device)
2729-
2730-
2731-class UdevLocalParser(UdevParser):
2732-
2733- device_factory = UdevLocalDevice
2734-
2735- def getAttributes(self, path):
2736- attributes = {}
2737- sys_path = "/sys%s" % path
2738- try:
2739- names = os.listdir(sys_path)
2740- except OSError:
2741- return attributes
2742-
2743- for name in names:
2744- name_path = posixpath.join(sys_path, name)
2745- if name[0] == "." \
2746- or name in ["dev", "uevent"] \
2747- or posixpath.isdir(name_path) \
2748- or posixpath.islink(name_path):
2749- continue
2750-
2751- try:
2752- value = open(name_path, "r").read().strip()
2753- except IOError:
2754- continue
2755-
2756- value = value.split("\n")[0]
2757- if [c for c in value if not isprint(c)]:
2758- continue
2759-
2760- attributes[name] = value
2761-
2762- return attributes
2763+ device = self.device_factory(environment, self.bits, list(stack))
2764+ if not self._ignoreDevice(device):
2765+ result.addDevice(device)
2766+
2767+ stack.append(device)
2768+
2769+
2770+def decode_id(id):
2771+ encoded_id = id.encode("utf-8")
2772+ decoded_id = encoded_id.decode("string-escape").decode("utf-8")
2773+ return decoded_id.strip()
2774
2775=== modified file 'checkbox_cli/cli_interface.py'
2776--- checkbox_cli/cli_interface.py 2011-06-13 14:22:39 +0000
2777+++ checkbox_cli/cli_interface.py 2011-11-18 18:04:27 +0000
2778@@ -145,7 +145,15 @@
2779
2780 def add_option(self, option, key=None):
2781 if key is None:
2782- for key in option.lower()+string.lowercase:
2783+ keys = option.lower() + \
2784+ string.ascii_letters + \
2785+ string.digits + \
2786+ string.punctuation
2787+
2788+ keys = keys.replace(' ','')
2789+ keys = keys.replace('+', '')
2790+
2791+ for key in keys:
2792 if key not in self.keys:
2793 break
2794 self.keys.append(key)
2795
2796=== modified file 'checkbox_gtk/gtk_interface.py'
2797--- checkbox_gtk/gtk_interface.py 2011-09-29 13:12:01 +0000
2798+++ checkbox_gtk/gtk_interface.py 2011-11-18 18:04:27 +0000
2799@@ -102,10 +102,8 @@
2800 image_head.connect("expose-event",self.draw_image_head)
2801
2802 # Set wait transient for dialog
2803- self._wait = self._get_widget("window_wait")
2804- self._wait.set_transient_for(self._dialog)
2805- self._wait.realize()
2806- self._wait.get_window().set_functions(Gdk.WMFunction.MOVE)
2807+ self._wait = self._get_widget("box_wait")
2808+ self._wait.hide()
2809
2810 # Set shorthand for notebook
2811 self._notebook = self._get_widget("notebook_main")
2812@@ -539,6 +537,8 @@
2813 test["data"] = self._get_text_view("text_view_comment")
2814 test["status"] = self._get_radio_button(BUTTON_TO_STATUS)
2815
2816+ self._set_main_title()
2817+
2818 def show_info(self, text, options=[], default=None):
2819 message_dialog = Gtk.MessageDialog(parent=self._dialog,
2820 type=Gtk.MessageType.INFO,
2821
2822=== modified file 'data/whitelists/default.whitelist'
2823--- data/whitelists/default.whitelist 2011-09-29 13:12:01 +0000
2824+++ data/whitelists/default.whitelist 2011-11-18 18:04:27 +0000
2825@@ -1,6 +1,7 @@
2826 cdimage
2827 cpuinfo
2828 device
2829+dmi
2830 dpkg
2831 gconf
2832 lsb
2833
2834=== modified file 'debian/changelog'
2835--- debian/changelog 2011-09-29 13:12:01 +0000
2836+++ debian/changelog 2011-11-18 18:04:27 +0000
2837@@ -1,3 +1,62 @@
2838+checkbox (0.13) precise; urgency=low
2839+
2840+ New upstream release (LP: #892268):
2841+
2842+ [Marc Tardif]
2843+ * Generate a submission.xml file that contains all device and attachment
2844+ * Write the report before reporting the validation error.
2845+ * Changed device.product to dmi.product for the formfactor (LP: #875312)
2846+
2847+ [Daniel Manrique]
2848+ * Use gettext for string (LP: #869267)
2849+ * Move progress indicator to main checkbox dialog instead of a
2850+ transient window (LP: #868995)
2851+ * Ignore malformed dpkg entries in package_resource (LP: #794747)
2852+ * Reset window title after finishing a manual test (LP: #874690)
2853+ * Handle "@" in locale names (as in ca@valencia).
2854+
2855+ [Jeff Lane]
2856+ * Went through all the job files and:
2857+ * Updated descriptions to match Unity UI structure
2858+ * Added descriptions where necessary
2859+ * Added further details to some descriptions
2860+ * Moved some jobs to more appropriate files
2861+ * Fixed job names in older job files to match new naming scheme
2862+ (suite/testname)
2863+ * Added jobs to local.txt to ensure all job files are now parsed
2864+ (this allows easier addition of existing tests to whitelists)
2865+ * Changed remaining manual job descriptions to match the new format
2866+ * Updated CD and DVD write tests to be more clear about when to skip
2867+ them (LP: #772794)
2868+
2869+ [Ara Pulido]
2870+ * Rewrote all job descriptions to match OEM QA syntax
2871+
2872+ [Brendan Donegan]
2873+ * Fix the code that assigns keys in checkbox-cli so that it never assigns
2874+ keys which have other uses. (LP: #877467)
2875+ * Show details of unmet job requirements (LP: #855852)
2876+ * Ensure that connect_wireless chooses a wireless connection from the list
2877+ of available connections (LP: #877752)
2878+ * Have the bluetooth/detect tests require a device with the category
2879+ BLUETOOTH to run, thus preventing the test from failing on systems with
2880+ no Bluetooth device (LP: #862322)
2881+ * Rename attachment jobs to not have a forward slash in their name
2882+ (LP: #887964)
2883+ * Guard against trying to write files to logical partitions on USB sticks
2884+ (which will obviously fail) in usb_test (LP: #887049)
2885+ * Make the OpenGL test ignore the return value of glxgears and improve
2886+ the test description (LP: #890725)
2887+ * Allow input/mouse test to run if a TOUCH device is present
2888+ (LP: #886129)
2889+
2890+ [ Javier Collado ]
2891+ * Broken job dependencies fixed (LP: #888447)
2892+ * Regex support when specifying blacklists and whitelists on the
2893+ commandline (LP: #588647)
2894+
2895+ -- Daniel Manrique <daniel.manrique@canonical.com> Thu, 18 Nov 2011 12:46:21 -0500
2896+
2897 checkbox (0.12.8) oneiric; urgency=low
2898
2899 New upstream release (LP: #862579):
2900
2901=== added file 'debian/po/ro.po'
2902--- debian/po/ro.po 1970-01-01 00:00:00 +0000
2903+++ debian/po/ro.po 2011-11-18 18:04:27 +0000
2904@@ -0,0 +1,118 @@
2905+# Romanian translation for checkbox
2906+# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
2907+# This file is distributed under the same license as the checkbox package.
2908+# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
2909+#
2910+msgid ""
2911+msgstr ""
2912+"Project-Id-Version: checkbox\n"
2913+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
2914+"POT-Creation-Date: 2011-03-29 15:19+0200\n"
2915+"PO-Revision-Date: 2011-10-27 20:38+0000\n"
2916+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
2917+"Language-Team: Romanian <ro@li.org>\n"
2918+"MIME-Version: 1.0\n"
2919+"Content-Type: text/plain; charset=UTF-8\n"
2920+"Content-Transfer-Encoding: 8bit\n"
2921+"X-Launchpad-Export-Date: 2011-10-28 05:11+0000\n"
2922+"X-Generator: Launchpad (build 14197)\n"
2923+
2924+#. Type: boolean
2925+#. Description
2926+#: ../checkbox.templates:1001
2927+msgid "Enable bug reporting by default? "
2928+msgstr ""
2929+
2930+#. Type: boolean
2931+#. Description
2932+#: ../checkbox.templates:1001
2933+msgid ""
2934+"If this option is set to Yes, then checkbox will ask if the user wants to "
2935+"file a bug for failing tests, even if apport is not enabled."
2936+msgstr ""
2937+
2938+#. Type: string
2939+#. Description
2940+#: ../checkbox.templates:2001
2941+msgid "Default package to report bugs against:"
2942+msgstr ""
2943+
2944+#. Type: string
2945+#. Description
2946+#: ../checkbox.templates:2001
2947+msgid ""
2948+"When filing a new bug through checkbox, if it does not guess the package, "
2949+"the default package that the bug will be file against."
2950+msgstr ""
2951+
2952+#. Type: string
2953+#. Description
2954+#: ../checkbox.templates:3001
2955+msgid "Test suite blacklist:"
2956+msgstr ""
2957+
2958+#. Type: string
2959+#. Description
2960+#: ../checkbox.templates:3001
2961+msgid "List of job files to never run when testing with checkbox."
2962+msgstr ""
2963+
2964+#. Type: string
2965+#. Description
2966+#: ../checkbox.templates:4001
2967+msgid "Test suite whitelist:"
2968+msgstr ""
2969+
2970+#. Type: string
2971+#. Description
2972+#: ../checkbox.templates:4001
2973+msgid "List of jobs to run when testing with checkbox."
2974+msgstr ""
2975+
2976+#. Type: string
2977+#. Description
2978+#: ../checkbox.templates:5001
2979+msgid "Transport URL:"
2980+msgstr ""
2981+
2982+#. Type: string
2983+#. Description
2984+#: ../checkbox.templates:5001
2985+msgid "URL where to send submissions."
2986+msgstr ""
2987+
2988+#. Type: string
2989+#. Description
2990+#: ../checkbox.templates:6001
2991+msgid "Launchpad E-mail:"
2992+msgstr ""
2993+
2994+#. Type: string
2995+#. Description
2996+#: ../checkbox.templates:6001
2997+msgid "E-mail address used to sign in to Launchpad."
2998+msgstr ""
2999+
3000+#. Type: string
3001+#. Description
3002+#: ../checkbox.templates:7001
3003+msgid "HTTP Proxy:"
3004+msgstr ""
3005+
3006+#. Type: string
3007+#. Description
3008+#: ../checkbox.templates:7001
3009+msgid "HTTP proxy to use instead of the one specified in environment."
3010+msgstr ""
3011+
3012+#. Type: string
3013+#. Description
3014+#: ../checkbox.templates:8001
3015+msgid "HTTPS Proxy:"
3016+msgstr ""
3017+
3018+#. Type: string
3019+#. Description
3020+#: ../checkbox.templates:8001
3021+msgid "HTTPS proxy to use instead of the one specified in environment."
3022+msgstr ""
3023
3024=== modified file 'examples/checkbox.ini'
3025--- examples/checkbox.ini 2010-03-09 16:58:36 +0000
3026+++ examples/checkbox.ini 2011-11-18 18:04:27 +0000
3027@@ -22,5 +22,5 @@
3028 [checkbox/plugins/jobs_info]
3029
3030 # Blacklist of jobs that are potentially dangerous
3031-blacklist = autotest ltp phoronix qa-regression-testing
3032+blacklist = autotest/full_suite ltp phoronix qa-regression-testing
3033 clone06.1 ioctl03.1 lchown02.* swapon03.1
3034
3035=== modified file 'gtk/checkbox-gtk.ui'
3036--- gtk/checkbox-gtk.ui 2011-06-13 14:22:39 +0000
3037+++ gtk/checkbox-gtk.ui 2011-11-18 18:04:27 +0000
3038@@ -374,6 +374,43 @@
3039 <property name="position">2</property>
3040 </packing>
3041 </child>
3042+ <child>
3043+ <object class="GtkBox" id="box_wait">
3044+ <property name="visible">True</property>
3045+ <property name="can_focus">False</property>
3046+ <property name="orientation">vertical</property>
3047+ <child>
3048+ <object class="GtkLabel" id="label_wait">
3049+ <property name="visible">True</property>
3050+ <property name="can_focus">False</property>
3051+ </object>
3052+ <packing>
3053+ <property name="expand">False</property>
3054+ <property name="fill">False</property>
3055+ <property name="position">0</property>
3056+ </packing>
3057+ </child>
3058+ <child>
3059+ <object class="GtkProgressBar" id="progressbar_wait">
3060+ <property name="visible">True</property>
3061+ <property name="can_focus">False</property>
3062+ <property name="pulse_step">0.10000000149</property>
3063+ </object>
3064+ <packing>
3065+ <property name="expand">False</property>
3066+ <property name="fill">False</property>
3067+ <property name="position">1</property>
3068+ </packing>
3069+ </child>
3070+ </object>
3071+ <packing>
3072+ <property name="expand">False</property>
3073+ <property name="fill">True</property>
3074+ <property name="position">3</property>
3075+ </packing>
3076+ </child>
3077+
3078+
3079 <child internal-child="action_area">
3080 <object class="GtkHButtonBox" id="dialog-action_area">
3081 <property name="visible">True</property>
3082@@ -452,41 +489,4 @@
3083 <action-widget response="1">button_next</action-widget>
3084 </action-widgets>
3085 </object>
3086- <object class="GtkWindow" id="window_wait">
3087- <property name="title" translatable="yes"></property>
3088- <property name="border_width">6</property>
3089- <property name="resizable">False</property>
3090- <property name="modal">True</property>
3091- <property name="window_position">center-on-parent</property>
3092- <property name="type_hint">dialog</property>
3093- <property name="skip_taskbar_hint">True</property>
3094- <property name="skip_pager_hint">True</property>
3095- <child>
3096- <object class="GtkVBox" id="vbox_wait">
3097- <property name="visible">True</property>
3098- <property name="spacing">12</property>
3099- <child>
3100- <object class="GtkLabel" id="label_wait">
3101- <property name="visible">True</property>
3102- </object>
3103- <packing>
3104- <property name="expand">False</property>
3105- <property name="fill">False</property>
3106- <property name="position">0</property>
3107- </packing>
3108- </child>
3109- <child>
3110- <object class="GtkProgressBar" id="progressbar_wait">
3111- <property name="visible">True</property>
3112- <property name="pulse_step">0.10000000149</property>
3113- </object>
3114- <packing>
3115- <property name="expand">False</property>
3116- <property name="fill">False</property>
3117- <property name="position">1</property>
3118- </packing>
3119- </child>
3120- </object>
3121- </child>
3122- </object>
3123 </interface>
3124
3125=== removed file 'jobs/apport.txt.in'
3126--- jobs/apport.txt.in 2011-07-01 11:37:27 +0000
3127+++ jobs/apport.txt.in 1970-01-01 00:00:00 +0000
3128@@ -1,5 +0,0 @@
3129-plugin: shell
3130-name: apport-directory
3131-requires: package.name == 'apport'
3132-command: ! test -d /var/crash || test $(find /var/crash -type f | wc -l) -eq 0
3133-_description: Test that the /var/crash directory doesn't contain anything.
3134
3135=== modified file 'jobs/audio.txt.in'
3136--- jobs/audio.txt.in 2011-09-01 12:23:07 +0000
3137+++ jobs/audio.txt.in 2011-11-18 18:04:27 +0000
3138@@ -1,15 +1,10 @@
3139-plugin: manual
3140+plugin: shell
3141 name: audio/list_devices
3142 requires:
3143 device.category == 'AUDIO'
3144 package.name == 'alsa-base'
3145 command: cat /proc/asound/cards
3146-_description:
3147- Detecting your sound device(s):
3148- .
3149- $output
3150- .
3151- Is this correct?
3152+_description: Test to detect audio devices
3153
3154 plugin: manual
3155 name: audio/playback_auto
3156@@ -19,9 +14,14 @@
3157 package.name == 'alsa-base' and package.name == 'python-gst0.10'
3158 command: gst_pipeline_test -t 2 'audiotestsrc wave=sine freq=512 ! audioconvert ! audioresample ! gconfaudiosink'
3159 _description:
3160- Select Test to play a sound on the automatically detected playback device.
3161- .
3162- Did you hear a sound and was that sound free of any distortion, clicks or other strange noises?
3163+ PURPOSE:
3164+ This test will check that internal speakers work correctly
3165+ STEPS:
3166+ 1. Make sure that no external speakers or headphones are connected
3167+ If testing a desktop, external speakers are allowed
3168+ 2. Click the Test button to play a brief tone on your audio device
3169+ VERIFICATION:
3170+ Did you hear a tone?
3171
3172 plugin: manual
3173 name: audio/playback_headphones
3174@@ -31,11 +31,13 @@
3175 package.name == 'alsa-base' and package.name == 'python-gst0.10'
3176 command: gst_pipeline_test -t 2 'audiotestsrc wave=sine freq=512 ! audioconvert ! audioresample ! gconfaudiosink'
3177 _description:
3178- Please connect a pair of headphones to your audio device.
3179- .
3180- Select Test to play a sound on the automatically detected playback device.
3181- .
3182- Did you hear a sound through the headphones and did the sound play without any distortion, clicks or other strange noises from your headphones?
3183+ PURPOSE:
3184+ This test will check that headphones connector works correctly
3185+ STEPS:
3186+ 1. Connect a pair of headphones to your audio device
3187+ 2. Click the Test button to play a sound to your audio device
3188+ VERIFICATION:
3189+ Did you hear a sound through the headphones and did the sound play without any distortion, clicks or other strange noises from your headphones?
3190
3191 plugin: manual
3192 name: audio/alsa_record_playback_internal
3193@@ -45,9 +47,14 @@
3194 package.name == 'alsa-base'
3195 command: alsa_record_playback
3196 _description:
3197- Disconnect any external microphones that you have plugged in. Select Test, then speak into your internal microphone. After a few seconds, your speech will be played back to you.
3198- .
3199- Did you hear your speech played back?
3200+ PURPOSE:
3201+ This test will check that recording sound using the onboard microphone works correctly
3202+ STEPS:
3203+ 1. Disconnect any external microphones that you have plugged in
3204+ 2. Click "Test", then speak into your internal microphone
3205+ 3. After a few seconds, your speech will be played back to you.
3206+ VERIFICATION:
3207+ Did you hear your speech played back?
3208
3209 plugin: manual
3210 name: audio/alsa_record_playback_external
3211@@ -57,9 +64,14 @@
3212 package.name == 'alsa-base'
3213 command: alsa_record_playback
3214 _description:
3215- Connect a microphone to your microphone port. Select Test, then speak into the microphone. After a few seconds, your speech will be played back to you.
3216- .
3217- Did you hear your speech played back?
3218+ PURPOSE:
3219+ This test will check that recording sound using an external microphone works correctly
3220+ STEPS:
3221+ 1. Connect a microphone to your microphone port
3222+ 2. Click "Test", then speak into the external microphone
3223+ 3. After a few seconds, your speech will be played back to you
3224+ VERIFICATION:
3225+ Did you hear your speech played back?
3226
3227 plugin: manual
3228 name: audio/alsa_record_playback_usb
3229@@ -68,9 +80,17 @@
3230 package.name == 'alsa-base'
3231 command: alsa_record_playback
3232 _description:
3233- Connect a USB audio device to your system. Then open the volume control application by left-clicking on the speaker icon in the panel and selecting "Sound Preferences". Select the "Input" tab and choose your USB device. Select the "Output" tab and choose your USB device. When you are done, select Test, then speak into the microphone. After a few seconds, your speech will be played back to you.
3234- .
3235- Did you hear your speech played back?
3236+ PURPOSE:
3237+ This test will check that a USB audio device works correctly
3238+ STEPS:
3239+ 1. Connect a USB audio device to your system
3240+ 2. Open the volume control application by left-clicking on the speaker icon in the panel and selecting "Sound Settings"
3241+ 3. Select the "Input" tab and choose your USB device
3242+ 4. Select the "Output" tab and choose your USB device
3243+ 5. Click "Test", then speak into the microphone
3244+ 6. After a few seconds, your speech will be played back to you
3245+ VERIFICATION:
3246+ Did you hear your speech played back through the USB headphones?
3247
3248 plugin: shell
3249 name: audio/alsa_record_playback_automated
3250
3251=== modified file 'jobs/autotest.txt.in'
3252--- jobs/autotest.txt.in 2010-03-09 16:58:36 +0000
3253+++ jobs/autotest.txt.in 2011-11-18 18:04:27 +0000
3254@@ -1,6 +1,8 @@
3255-name: autotest
3256+name: autotest/full_suite
3257 plugin: remote
3258-_description: Autotest suite (destructive)
3259 user: root
3260 timeout: 1200
3261 command: autotest_suite
3262+_description:
3263+ PURPOSE:
3264+ This test will attempt to install and run the Autotest Suite. These tests can be destructive, so this test is blacklisted by default.
3265
3266=== modified file 'jobs/bluetooth.txt.in'
3267--- jobs/bluetooth.txt.in 2011-09-01 12:23:07 +0000
3268+++ jobs/bluetooth.txt.in 2011-11-18 18:04:27 +0000
3269@@ -1,88 +1,100 @@
3270 plugin: shell
3271 name: bluetooth/detect
3272-requires: package.name == 'bluez'
3273+requires:
3274+ package.name == 'bluez'
3275+ device.category == 'BLUETOOTH'
3276 command: hcitool dev | tail -n+2 | awk '{print $2}' |grep -E "^([0-9a-fA-F]{2}\:){5}[0-9a-fA-F]{2}$"
3277 _description:
3278- Output address of detected Bluetooth device, if any, or exits with error if none is found.
3279+ This test will detect your Bluetooth device and output the device's hardware address. If no device is found, the test will exit with an error.
3280
3281 plugin: shell
3282 name: bluetooth/detect-output
3283-requires: package.name == 'bluez'
3284+requires:
3285+ package.name == 'bluez'
3286+ device.category == 'BLUETOOTH'
3287 command: hcitool dev | tail -n+2 | awk '{print $2}'; hcitool dev | tail -n+2 | awk '{print $2}' > $CHECKBOX_DATA/bluetooth_address
3288 _description:
3289- Automated test to store output in checkbox report
3290+ Automated test to store bluetooth device information in checkbox report
3291
3292 plugin: manual
3293 name: bluetooth/browse-files
3294 depends: bluetooth/detect
3295 _description:
3296- Bluetooth browse files procedure:
3297- 1.- Enable bluetooth on any mobile device (PDA, smartphone, etc.)
3298- 2.- Click on the bluetooth icon in the menu bar
3299- 3.- Select 'Setup new device'
3300- 3.- Look for the device in the list and select it
3301- 4.- In the device write the PIN code automatically chosen by the wizard
3302- 5.- The device should pair with the computer
3303- 6.- Right-click on the bluetooth icon and select browse files
3304- 7.- Authorize the computer to browse the files in the device if needed
3305- 8.- You should be able to browse the files
3306- .
3307- Did all the steps work?
3308+ PURPOSE:
3309+ This test will check that bluetooth connection works correctly
3310+ STEPS:
3311+ 1. Enable bluetooth on any mobile device (PDA, smartphone, etc.)
3312+ 2. Click on the bluetooth icon in the menu bar
3313+ 3. Select 'Setup new device'
3314+ 4. Look for the device in the list and select it
3315+ 5. In the device write the PIN code automatically chosen by the wizard
3316+ 6. The device should pair with the computer
3317+ 7. Right-click on the bluetooth icon and select browse files
3318+ 8. Authorize the computer to browse the files in the device if needed
3319+ 9. You should be able to browse the files
3320+ VERIFICATION:
3321+ Did all the steps work?
3322
3323 plugin: manual
3324 name: bluetooth/file-transfer
3325 depends: bluetooth/browse-files bluetooth/detect
3326 _description:
3327- Bluetooth file transfer procedure:
3328- 1.- Make sure that you're able to browse the files in your mobile device
3329- 2.- Copy a file from the computer to the mobile device
3330- 3.- Verify that the file was correctly copied
3331- 4.- Copy a file from the mobile device to the computer
3332- 5.- Verify that the file was correctly copied
3333- .
3334- Did all the steps work?
3335+ PURPOSE:
3336+ This test will check that you can transfer information through a bluetooth connection
3337+ STEPS:
3338+ 1. Make sure that you're able to browse the files in your mobile device
3339+ 2. Copy a file from the computer to the mobile device
3340+ 3. Copy a file from the mobile device to the computer
3341+ VERIFICATION:
3342+ Were all files copied correctly?
3343
3344 plugin: manual
3345 name: bluetooth/audio
3346 depends: bluetooth/detect
3347-requires: package.name == 'alsa-utils'
3348 command: arecord -d 5 -D bluetooth -f S16_LE | aplay -D bluetooth -f S16_LE
3349 _description:
3350- Bluetooth audio procedure:
3351- 1.- Enable the bluetooth headset
3352- 2.- Click on the bluetooth icon in the menu bar
3353- 3.- Select 'Setup new device'
3354- 4.- Look for the device in the list and select it
3355- 5.- In the device write the PIN code automatically chosen by the wizard
3356- 6.- The device should pair with the computer
3357- 7.- Select Test to record for five seconds and reproduce in the bluetooth device
3358- .
3359- Did all the steps work?
3360+ PURPOSE:
3361+ This test will check that you can record and hear audio using a bluetooth audio device
3362+ STEPS:
3363+ 1. Enable the bluetooth headset
3364+ 2. Click on the bluetooth icon in the menu bar
3365+ 3. Select 'Setup new device'
3366+ 4. Look for the device in the list and select it
3367+ 5. In the device write the PIN code automatically chosen by the wizard
3368+ 6. The device should pair with the computer
3369+ 7. Click "Test" to record for five seconds and reproduce in the bluetooth device
3370+ VERIFICATION:
3371+ Did you hear the sound you recorded in the bluetooth
3372
3373 plugin: manual
3374 name: bluetooth/keyboard
3375 command: keyboard_test
3376 depends: bluetooth/detect
3377 _description:
3378- Bluetooth keyboard procedure:
3379- 1.- Enable the bluetooth keyboard
3380- 2.- Click on the bluetooth icon in the menu bar
3381- 3.- Select 'Setup new device'
3382- 4.- Look for the device in the list and select it
3383- 5.- Select Test to enter text
3384- .
3385- Did all the steps work?
3386+ PURPOSE:
3387+ This test will check that you can use a bluetooth keyboard
3388+ STEPS:
3389+ 1. Enable the bluetooth keyboard
3390+ 2. Click on the bluetooth icon in the menu bar
3391+ 3. Select 'Setup new device'
3392+ 4. Look for the device in the list and select it
3393+ 5. Click "Test"
3394+ 6. Enter some text
3395+ VERIFICATION:
3396+ Were you able to enter some text with the bluetooth keyboard?
3397
3398 plugin: manual
3399 name: bluetooth/mouse
3400 depends: bluetooth/detect
3401 _description:
3402- Bluetooth mouse procedure:
3403- 1.- Enable the bluetooth mouse
3404- 2.- Click on the bluetooth icon in the menu bar
3405- 3.- Select 'Setup new device'
3406- 4.- Look for the device in the list and select it
3407- 5.- Move the mouse around the screen
3408- 6.- Perform some single/double/right click operations
3409- .
3410- Did all the steps work?
3411+ PURPOSE:
3412+ This test will check that you can use a bluetooth mouse
3413+ STEPS:
3414+ 1. Enable the bluetooth mouse
3415+ 2. Click on the bluetooth icon in the menu bar
3416+ 3. Select 'Setup new device'
3417+ 4. Look for the device in the list and select it
3418+ 5. Move the mouse around the screen
3419+ 6. Perform some single/double/right click operations
3420+ VERIFICATION:
3421+ Did the mouse work as expected?
3422
3423=== modified file 'jobs/camera.txt.in'
3424--- jobs/camera.txt.in 2011-07-01 11:37:27 +0000
3425+++ jobs/camera.txt.in 2011-11-18 18:04:27 +0000
3426@@ -4,7 +4,7 @@
3427 package.name == 'xawtv'
3428 device.category == 'CAPTURE'
3429 command: camera_test -t detect
3430-_description: Automated test case that attempts to detect a camera
3431+_description: This Automated test attempts to detect a camera.
3432
3433 plugin: manual
3434 name: camera/display
3435@@ -14,9 +14,12 @@
3436 device.category == 'CAPTURE'
3437 command: camera_test -t display
3438 _description:
3439- Select Test to display a video capture from the camera
3440- .
3441- Did you see the video capture?
3442+ PURPOSE:
3443+ This test will check that the built-in camera works
3444+ STEPS:
3445+ 1. Click on Test to display a video capture from the camera
3446+ VERIFICATION:
3447+ Did you see the video capture?
3448
3449 plugin: manual
3450 name: camera/still
3451@@ -28,9 +31,12 @@
3452 device.category == 'CAPTURE'
3453 command: camera_test -t still
3454 _description:
3455- Select Test to display a still image from the camera
3456- .
3457- Did you see the image?
3458+ PURPOSE:
3459+ This test will check that the built-in camera works
3460+ STEPS:
3461+ 1. Click on Test to display a still image from the camera
3462+ VERIFICATION:
3463+ Did you see the image?
3464
3465 plugin: manual
3466 name: camera/video
3467@@ -41,8 +47,9 @@
3468 device.category == 'CAPTURE'
3469 command: camera_test -t video
3470 _description:
3471- Select Test to capture video to a file and open it in totem.
3472- Please make sure that both audio and video is captured.
3473- .
3474- Did you see/hear the capture?
3475-
3476+ PURPOSE:
3477+ This test will check that you can capture video with the built-in camera
3478+ STEPS:
3479+ 1. Click on Test to capture video to a file (it will automatically open in Totem)
3480+ VERIFICATION:
3481+ Did you see and hear the capture?
3482
3483=== modified file 'jobs/codecs.txt.in'
3484--- jobs/codecs.txt.in 2011-07-01 11:37:27 +0000
3485+++ jobs/codecs.txt.in 2011-11-18 18:04:27 +0000
3486@@ -1,51 +1,29 @@
3487 plugin: manual
3488-name: codecs-ogg-vorbis
3489+name: codecs/ogg-vorbis
3490 requires:
3491 package.name == 'gstreamer0.10-plugins-base'
3492 package.name == 'totem' and package.name == 'ubuntu-sounds'
3493 command: totem /usr/share/sounds/ubuntu/stereo/system-ready.ogg
3494 _description:
3495- Select Test to play an Ogg Vorbis file (.ogg)
3496- .
3497- (Please close Movie Player to proceed.)
3498- .
3499- Did the sample play correctly?
3500+ PURPOSE:
3501+ This test will verify your system's ability to play Ogg Vorbis audio files.
3502+ STEPS:
3503+ 1. Click Test to play an Ogg Vorbis file (.ogg)
3504+ 2. Please close the player to proceed.
3505+ VERIFICATION:
3506+ Did the sample play correctly?
3507
3508 plugin: manual
3509-name: codecs-wav
3510+name: codecs/wav
3511 requires:
3512 package.name == 'gstreamer0.10-plugins-good'
3513 package.name == 'totem' and package.name == 'alsa-utils'
3514 command: totem /usr/share/sounds/alsa/Noise.wav
3515 _description:
3516- Select Test to play a Wave Audio format file (.wav)
3517- .
3518- (Please close Movie Player to proceed.)
3519- .
3520- Did the sample play correctly?
3521-
3522-plugin: manual
3523-name: media-pause-audio
3524-requires:
3525- package.name == 'totem'
3526- package.name == 'gstreamer0.10-plugins-base'
3527-command: totem $CHECKBOX_SHARE/data/FrustrationBlues-ColinRoss.oga
3528-_description:
3529- Select 'Test' to play some audio, and try pausing and resuming playback while the it is playing.
3530- .
3531- (Please close Movie Player to proceed.)
3532- .
3533- Did the audio play and pause as expected?
3534-
3535-plugin: manual
3536-name: media-pause-video
3537-requires:
3538- package.name == 'totem'
3539- package.name == 'gstreamer0.10-plugins-base'
3540-command: totem $CHECKBOX_SHARE/data/UbuntuIsHumanity.ogv
3541-_description:
3542- Select 'Test' to play a video, and try pausing and resuming playback while the video is playing.
3543- .
3544- (Please close Movie Player to proceed.)
3545- .
3546- Did the video play and pause as expected?
3547+ PURPOSE:
3548+ This test will verify your system's ability to play Wave Audio files.
3549+ STEPS:
3550+ 1. Select Test to play a Wave Audio format file (.wav)
3551+ 2. Please close the player to proceed.
3552+ VERIFICATION:
3553+ Did the sample play correctly?
3554
3555=== modified file 'jobs/cpu.txt.in'
3556--- jobs/cpu.txt.in 2011-08-10 21:09:56 +0000
3557+++ jobs/cpu.txt.in 2011-11-18 18:04:27 +0000
3558@@ -6,7 +6,7 @@
3559 user: root
3560 command: fwts_test -c -l $CHECKBOX_DATA/cpu_scaling_test.log
3561 _description:
3562- Test the CPU scaling capabilities using Colin King's Firmware Test Suite tool.
3563+ Test the CPU scaling capabilities using Firmware Test Suite (fwts cpufreq).
3564
3565 plugin: shell
3566 name: cpu/clocktest
3567@@ -28,7 +28,7 @@
3568 requires: int(cpuinfo.count) > 1 and (cpuinfo.platform == 'i386' or cpuinfo.platform == 'x86_64')
3569 command: cpu_topology
3570 _description:
3571- Checks cpu topology for accuracy
3572+ This test checks cpu topology for accuracy
3573
3574 plugin: shell
3575 name: cpu/frequency_governors
3576@@ -36,4 +36,4 @@
3577 user: root
3578 command: nice -n -20 cpu_scaling_test
3579 _description:
3580- Checks that CPU frequency governors are obeyed when set.
3581+ This test checks that CPU frequency governors are obeyed when set.
3582
3583=== modified file 'jobs/daemons.txt.in'
3584--- jobs/daemons.txt.in 2011-07-01 11:37:27 +0000
3585+++ jobs/daemons.txt.in 2011-11-18 18:04:27 +0000
3586@@ -1,59 +1,59 @@
3587 plugin: shell
3588-name: atd
3589+name: daemons/atd
3590 requires: package.name == 'at'
3591 command: pgrep -f '/usr/sbin/atd' >/dev/null
3592 _description: Test if the atd daemon is running when the package is installed.
3593
3594 plugin: shell
3595-name: cron
3596+name: daemons/cron
3597 requires: package.name == 'cron'
3598 command: pgrep -f '/usr/sbin/cron' >/dev/null
3599 _description: Test if the cron daemon is running when the package is installed.
3600
3601 plugin: shell
3602-name: cupsd
3603+name: daemons/cupsd
3604 requires: package.name == 'cupsys'
3605 command: pgrep -f '/usr/sbin/cupsd' >/dev/null
3606 _description: Test if the cupsd daemon is running when the package is installed.
3607
3608 plugin: shell
3609-name: getty
3610+name: daemons/getty
3611 requires: package.name == 'util-linux'
3612 command: pgrep -f '/sbin/getty' >/dev/null
3613 _description: Test if the getty daemon is running when the package is installed.
3614
3615 plugin: shell
3616-name: init
3617+name: daemons/init
3618 requires: package.name == 'upstart'
3619 command: pgrep -f '/sbin/init' >/dev/null
3620 _description: Test if the init daemon is running when the package is installed.
3621
3622 plugin: shell
3623-name: klogd
3624+name: daemons/klogd
3625 requires: package.name == 'klogd'
3626 command: pgrep -f '/sbin/klogd' >/dev/null
3627 _description: Test if the klogd daemon is running when the package is installed.
3628
3629 plugin: shell
3630-name: nmbd
3631+name: daemons/nmbd
3632 requires: package.name == 'samba'
3633 command: pgrep -f '/usr/sbin/nmbd' >/dev/null
3634 _description: Test if the nmbd daemon is running when the package is installed.
3635
3636 plugin: shell
3637-name: smbd
3638+name: daemons/smbd
3639 requires: package.name == 'samba'
3640 command: pgrep -f '/usr/sbin/smbd' >/dev/null
3641 _description: Test if the smbd daemon is running when the package is installed.
3642
3643 plugin: shell
3644-name: syslogd
3645+name: daemons/syslogd
3646 requires: package.name == 'syslogd'
3647 command: pgrep -f '/sbin/syslogd' >/dev/null
3648 _description: Test if the syslogd daemon is running when the package is installed.
3649
3650 plugin: shell
3651-name: udevd
3652+name: daemons/udevd
3653 requires:
3654 package.name == 'udevd'
3655 package.name == 'linux'
3656@@ -61,7 +61,7 @@
3657 _description: Test if the udevd daemon is running when the package is installed.
3658
3659 plugin: shell
3660-name: winbindd
3661+name: daemons/winbindd
3662 requires: package.name == 'winbind'
3663 command: pgrep -f '/usr/sbin/winbindd' >/dev/null
3664 _description: Test if the winbindd daemon is running when the package is installed.
3665
3666=== modified file 'jobs/disk.txt.in'
3667--- jobs/disk.txt.in 2011-09-14 21:16:02 +0000
3668+++ jobs/disk.txt.in 2011-11-18 18:04:27 +0000
3669@@ -14,7 +14,7 @@
3670 requires: device.path == "$path" and package.name == 'linux'
3671 user: root
3672 command: hdparm -tT /dev/`ls /sys$path/block` | sed 's/:.*= */ = /' | grep -v "^$"
3673- description: Benchmark for $path
3674+ description: This test runs hdparm timing tests as a benchmark for $path
3675 EOF
3676
3677 plugin: local
3678@@ -29,7 +29,8 @@
3679 plugin: shell
3680 name: disk/smart_`ls /sys$path/block`
3681 requires: device.path == "$path"
3682- description: SMART test for $product
3683+ description:
3684+ This tests the SMART capabilities for $product (Note that this test will not work against removable media (USB, Firewire) and hardware RAID)
3685 user: root
3686 command: disk_smart -b /dev/`ls /sys$path/block` -s 130
3687 EOF
3688@@ -44,7 +45,7 @@
3689 plugin: shell
3690 name: disk/max_diskspace_used_`ls /sys$path/block`
3691 requires: device.path == "$path"
3692- description: Max disk space test for $product
3693+ description: Maximum disk space test for $product
3694 user: root
3695 command: max_diskspace_used `ls /sys$path/block`
3696 EOF
3697@@ -61,7 +62,7 @@
3698 requires:
3699 device.path == "$path"
3700 package.name == 'linux'
3701- description: Disk performance for $product
3702+ description: Disk performance test for $product
3703 user: root
3704 command: disk_read_performance_test `ls /sys$path/block`
3705 EOF
3706@@ -79,6 +80,6 @@
3707 requires:
3708 device.path == "$path"
3709 package.name == 'linux'
3710- description: I/O stress test for $product
3711+ description: Disk I/O stress test for $product
3712 command: storage_test `ls /sys$path/block`
3713 EOF
3714
3715=== removed file 'jobs/evolution.txt.in'
3716--- jobs/evolution.txt.in 2011-07-01 11:37:27 +0000
3717+++ jobs/evolution.txt.in 1970-01-01 00:00:00 +0000
3718@@ -1,26 +0,0 @@
3719-plugin: manual
3720-name: applications-evolution-pop3
3721-requires: package.name == "evolution"
3722-command: evolution
3723-_description:
3724- Click the "Test" button to launch Evolution, then configure it to connect to a POP3 account.
3725- .
3726- Were you able to receive and read e-mail correctly?
3727-
3728-plugin: manual
3729-name: applications-evolution-imap
3730-requires: package.name == "evolution"
3731-command: evolution
3732-_description:
3733- Click the "Test" button to launch Evolution, then configure it to connect to a IMAP account.
3734- .
3735- Were you able to receive and read e-mail correctly?
3736-
3737-plugin: manual
3738-name: applications-evolution-smtp
3739-requires: package.name == "evolution"
3740-command: evolution
3741-_description:
3742- Click the "Test" button to launch Evolution, then configure it to connect to a SMTP account.
3743- .
3744- Were you able to send e-mail without errors?
3745
3746=== modified file 'jobs/fingerprint.txt.in'
3747--- jobs/fingerprint.txt.in 2011-08-10 21:09:56 +0000
3748+++ jobs/fingerprint.txt.in 2011-11-18 18:04:27 +0000
3749@@ -1,27 +1,31 @@
3750 plugin: manual
3751 name: fingerprint/login
3752 _description:
3753- Prerequisites: This test case assumes that there's a testing account from which test cases are run and a personal account that the tester uses to verify the fingerprint reader
3754- .
3755- Fingerprint login verification procedure:
3756- 1.- Click on the user switcher applet.
3757- 2.- Select your user name.
3758- 3.- A window should appear that provides the ability to login either typing your password or using fingerprint authentication.
3759- 4.- Use the fingerprint reader to login.
3760- 5.- Click on the user switcher applet.
3761- 6.- Select the testing account to continue running tests.
3762- .
3763- Did the authentication procedure work correctly?
3764+ PURPOSE:
3765+ This test will verify that a fingerprint reader will work properly for logging into your system.
3766+ PREREQUISITES:
3767+ This test case assumes that there's a testing account from which test cases are run and a personal account that the tester uses to verify the fingerprint reader
3768+ STEPS:
3769+ 1. Click on the user switcher applet.
3770+ 2. Select your user name.
3771+ 3. A window should appear that provides the ability to login either typing your password or using fingerprint authentication.
3772+ 4. Use the fingerprint reader to login.
3773+ 5. Click on the user switcher applet.
3774+ 6. Select the testing account to continue running tests.
3775+ VERIFICATION:
3776+ Did the authentication procedure work correctly?
3777
3778 plugin: manual
3779 name: fingerprint/unlock
3780 _description:
3781- Fingerprint unlock verification procedure:
3782- 1.- Click on the user switcher applet.
3783- 2.- Select 'Lock screen'.
3784- 3.- Press any key or move the mouse.
3785- 4.- A window should appear that provides the ability to unlock either typing your password or using fingerprint authentication.
3786- 5.- Use the fingerprint reader to unlock.
3787- 6.- Your screen should be unlocked.
3788- .
3789- Did the authentication procedure work correctly?
3790+ PURPOSE:
3791+ This test will verify that a fingerprint reader can be used to unlock a locked system.
3792+ STEPS:
3793+ 1. Click on the user switcher applet.
3794+ 2. Select 'Lock screen'.
3795+ 3. Press any key or move the mouse.
3796+ 4. A window should appear that provides the ability to unlock either typing your password or using fingerprint authentication.
3797+ 5. Use the fingerprint reader to unlock.
3798+ 6. Your screen should be unlocked.
3799+ VERIFICATION:
3800+ Did the authentication procedure work correctly?
3801
3802=== modified file 'jobs/firewire.txt.in'
3803--- jobs/firewire.txt.in 2011-08-10 21:09:56 +0000
3804+++ jobs/firewire.txt.in 2011-11-18 18:04:27 +0000
3805@@ -1,11 +1,12 @@
3806 plugin: manual
3807 name: firewire/hdd
3808 _description:
3809- Firewire HDD verification procedure:
3810- 1.- Plug a Firewire HDD into the computer.
3811- 2.- A window should be opened asking which action should be performed (open folder, photo manager, etc).
3812- 3.- Copy some files from your internal HDD to the firewire HDD.
3813- 4.- Copy some files from the firewire HDD to your internal HDD.
3814- .
3815- Do the copy operations work as expected?
3816-
3817+ PURPOSE:
3818+ This test will check that the firewire port works
3819+ STEPS:
3820+ 1. Plug a Firewire HDD into the computer
3821+ 2. Either a window asking which action should be performed (open folder, photo manager, etc) or a file browser will open.
3822+ 3. Copy some files from your internal HDD to the firewire HDD
3823+ 4. Copy some files from the firewire HDD to your internal HDD
3824+ VERIFICATION:
3825+ Do the copy operations work as expected?
3826
3827=== removed file 'jobs/gcalctool.txt.in'
3828--- jobs/gcalctool.txt.in 2011-07-01 11:37:27 +0000
3829+++ jobs/gcalctool.txt.in 1970-01-01 00:00:00 +0000
3830@@ -1,52 +0,0 @@
3831-plugin: manual
3832-name: applications-gcalctool
3833-requires: package.name == "gcalctool"
3834-command: gcalctool
3835-_description:
3836- Click the "Test" button to open the calculator.
3837- .
3838- Did it launch correctly?
3839-
3840-plugin: manual
3841-name: applications-gcalctool-functions
3842-depends: applications-gcalctool
3843-requires: package.name == "gcalctool"
3844-command: gcalctool
3845-_description:
3846- Click the "Test" button to open the calculator and perform:
3847- .
3848- 1. Simple math functions (+,-,/,*)
3849- 2. Nested math functions ((,))
3850- 3. Fractional math
3851- 4. Decimal math
3852- .
3853- Did the functions perform as expected?
3854-
3855-plugin: manual
3856-name: applications-gcalctool-memory
3857-depends: applications-gcalctool
3858-requires: package.name == "gcalctool"
3859-command: gcalctool
3860-_description:
3861- Click the "Test" button to open the calculator and perform:
3862- .
3863- 1. Memory set
3864- 2. Memory reset
3865- 3. Memory last clear
3866- 4. Memory clear
3867- .
3868- Did the functions perform as expected?
3869-
3870-plugin: manual
3871-name: applications-gcalctool-clipboard
3872-depends: applications-gcalctool
3873-requires: package.name == "gcalctool"
3874-command: gcalctool
3875-_description:
3876- Click the "Test" button to open the calculator and perform:
3877- .
3878- 1. Cut
3879- 2. Copy
3880- 3. Paste
3881- .
3882- Did the functions perform as expected?
3883
3884=== removed file 'jobs/gedit.txt.in'
3885--- jobs/gedit.txt.in 2011-07-01 11:37:27 +0000
3886+++ jobs/gedit.txt.in 1970-01-01 00:00:00 +0000
3887@@ -1,22 +0,0 @@
3888-plugin: manual
3889-name: applications-gedit
3890-requires: package.name == "gedit"
3891-command: gedit
3892-_description:
3893- Click the "Test" button to open gedit.
3894- .
3895- Enter some text and save the file (make a note of the file name you use), then close gedit.
3896- .
3897- Did this perform as expected?
3898-
3899-plugin: manual
3900-name: applications-gedit-read
3901-depends: applications-gedit
3902-requires: package.name == "gedit"
3903-command: gedit
3904-_description:
3905- Click the "Test" button to open gedit, and re-open the file you created previously.
3906- .
3907- Edit then save the file, then close gedit.
3908- .
3909- Did this perform as expected?
3910
3911=== removed file 'jobs/gnome-terminal.txt.in'
3912--- jobs/gnome-terminal.txt.in 2011-07-01 11:37:27 +0000
3913+++ jobs/gnome-terminal.txt.in 1970-01-01 00:00:00 +0000
3914@@ -1,12 +0,0 @@
3915-plugin: manual
3916-name: applications-gnome-terminal
3917-requires: package.name == "gnome-terminal"
3918-command: gnome-terminal
3919-_description:
3920- Click the "Test" button to open Terminal.
3921- .
3922- Type 'ls' and press enter. You should see a list of files and folder in your home directory.
3923- .
3924- Close the terminal window.
3925- .
3926- Did this perform as expected?
3927
3928=== modified file 'jobs/graphics.txt.in'
3929--- jobs/graphics.txt.in 2011-08-10 21:09:56 +0000
3930+++ jobs/graphics.txt.in 2011-11-18 18:04:27 +0000
3931@@ -1,12 +1,8 @@
3932-plugin: manual
3933+plugin: shell
3934 name: graphics/xorg-version
3935 requires: package.name == "x11-utils"
3936 command: xdpyinfo | grep "^X.Org version" | cut -d ':' -f 2 | tr -d ' '
3937-_description:
3938- 2d graphics appears to be working, your running X.Org version is:
3939- $output
3940- .
3941- Is this correct?
3942+_description: Test to output the Xorg version
3943
3944 plugin: shell
3945 name: graphics/xorg-version-output
3946@@ -19,41 +15,40 @@
3947 name: graphics/gtkperf
3948 depends: graphics/xorg-version-output
3949 requires: package.name == 'gtkperf'
3950-command: gtkperf -a
3951+command: gtkperf -a | grep "Total time:"
3952 _description:
3953 Run gtkperf to make sure that GTK based test cases work
3954- .
3955- In the future add the returned time as a benchmark result to the checkbox report
3956
3957 plugin: manual
3958 name: graphics/resolution-change
3959 depends: graphics/xorg-version-output
3960 _description:
3961- Display resolution change procedure:
3962- 1.- Open System->Preferences->Monitors
3963- 2.- Select a new resolution from the dropdown list
3964- 3.- Click on Apply
3965- 4.- The resolution should change
3966- 5.- Select the original resolution from the dropdown list
3967- 6.- Click on Apply
3968- 7.- The resolution should change again
3969- .
3970- Did the resolution change as expected?
3971+ PURPOSE:
3972+ This test will verify that the GUI is usable after manually changing resolution
3973+ STEPS:
3974+ 1. Open the Displays application
3975+ 2. Select a new resolution from the dropdown list
3976+ 3. Click on Apply
3977+ 4. Select the original resolution from the dropdown list
3978+ 5. Click on Apply
3979+ VERIFICATION:
3980+ Did the resolution change as expected?
3981
3982 plugin: manual
3983 name: graphics/rotation
3984 depends: graphics/xorg-version-output
3985 _description:
3986- Display rotation verification procedure:
3987- 1.- Open System->Preferences->Monitors
3988- 2.- Select a new rotation value from the dropdown list
3989- 3.- Click on Apply
3990- 4.- The display should be rotated according to the new configuration value
3991- 5.- Click on Restore Previous Configuration
3992- 6.- The display configuration change should be reverted
3993- 7.- Repeat 2-6 for different rotation values
3994- .
3995- Did the display rotation change as expected?
3996+ PURPOSE:
3997+ This test will test display rotation
3998+ STEPS:
3999+ 1. Open the Displays application
4000+ 2. Select a new rotation value from the dropdown list
4001+ 3. Click on Apply
4002+ 4. Click on Restore Previous Configuration
4003+ 5. Click on Apply
4004+ 6. Repeat 2-5 for different rotation values
4005+ VERIFICATION:
4006+ Did the display rotation change as expected?
4007
4008 plugin: shell
4009 name: graphics/xorg-process
4010@@ -72,7 +67,7 @@
4011 requires: package.name == 'xorg' and device.driver == 'i915'
4012 user: root
4013 command: xorg_memory_test xeyes
4014-_description: Test that X does not leak memory when running programs.
4015+_description: Test that X does not leak memory when running programs on systems with intel based graphics.
4016
4017 plugin: manual
4018 name: graphics/resolution
4019@@ -80,11 +75,14 @@
4020 device.category == 'VIDEO'
4021 command: resolution_test
4022 _description:
4023- This display is using the following resolution:
4024- .
4025- $output
4026- .
4027- Is this acceptable for your display?
4028+ PURPOSE:
4029+ This test will verify the default display resolution
4030+ STEPS:
4031+ 1. This display is using the following resolution:
4032+ INFO:
4033+ $output
4034+ VERIFICATION:
4035+ Is this acceptable for your display?
4036
4037 plugin: shell
4038 name: graphics/minimum_resolution
4039@@ -102,30 +100,39 @@
4040 requires: package.name == 'xorg' and package.name == 'python-gst0.10'
4041 command: gst_pipeline_test -t 2 'videotestsrc ! ffmpegcolorspace ! gconfvideosink'
4042 _description:
4043- Select Test to display a video test.
4044- .
4045- Do you see color bars and static?
4046+ PURPOSE:
4047+ This test will test the default display
4048+ STEPS:
4049+ 1. Click "Test" to display a video test.
4050+ VERIFICATION:
4051+ Do you see color bars and static?
4052
4053 plugin: manual
4054 name: graphics/xrandr_detect_modes
4055 requires: package.name == 'xorg'
4056 command: xrandr
4057 _description:
4058- The following screens and video modes have been detected on your system:
4059- .
4060- $output
4061- .
4062- Is this correct?
4063+ PURPOSE:
4064+ This test checks the detected video modes
4065+ STEPS:
4066+ 1. The following screens and video modes have been detected on your system
4067+ INFO:
4068+ $output
4069+ VERIFICATION:
4070+ Are those correct?
4071
4072 plugin: manual
4073 name: graphics/cycle_resolution
4074 requires: package.name == 'xorg'
4075-depends: xrandr_detect_modes
4076+depends: graphics/xrandr_detect_modes
4077 command: xrandr_cycle
4078 _description:
4079- Select Test to cycle through the detected video modes for your system.
4080- .
4081- Did the screen appear to be working for each mode?
4082+ PURPOSE:
4083+ This test cycles through the detected video modes
4084+ STEPS:
4085+ 1. Click "Test" to start cycling through the video modes
4086+ VERIFICATION:
4087+ Did the screen appear to be working for each mode?
4088
4089 plugin: shell
4090 name: graphics/compiz_check
4091@@ -136,8 +143,13 @@
4092 plugin: manual
4093 name: graphics/glxgears
4094 requires: package.name == 'mesa-utils'
4095-command: glxgears
4096+command: glxgears; true
4097 _description:
4098- Select Test to execute glxgears to ensure that minimal 3d graphics support is in place.
4099- .
4100- Did the 3d animation appear?
4101+ PURPOSE:
4102+ This test tests the basic 3D capabilities of your video card
4103+ STEPS:
4104+ 1. Click "Test" to execute an OpenGL demo. Press ESC at any time to close.
4105+ 2. Verify that the animation is not jerky or slow.
4106+ VERIFICATION:
4107+ 1. Did the 3d animation appear?
4108+ 2. Was the animation free from slowness/jerkiness?
4109
4110=== modified file 'jobs/hibernate.txt.in'
4111--- jobs/hibernate.txt.in 2011-09-01 12:23:07 +0000
4112+++ jobs/hibernate.txt.in 2011-11-18 18:04:27 +0000
4113@@ -5,10 +5,12 @@
4114 user: root
4115 command: sleep_test -s disk -w 120 --debug
4116 _description:
4117- This will check to make sure your system can successfully hibernate (if supported).
4118- .
4119- Select Test to begin. The system will hibernate and should wake itself within 5 minutes. If your system does not wake itself after 5 minutes, please press the power button to wake the system manually. If the system fails to resume from hibernate, please restart System Testing and mark this test as Failed.
4120- .
4121- Did the system successfully hibernate and did it work properly after waking up?
4122-
4123-
4124+ PURPOSE:
4125+ This test will check to make sure your system can successfully hibernate (if supported)
4126+ STEPS:
4127+ 1. Click on Test
4128+ 2. The system will hibernate and should wake itself within 5 minutes
4129+ 3. If your system does not wake itself after 5 minutes, please press the power button to wake the system manually
4130+ 4. If the system fails to resume from hibernate, please restart System Testing and mark this test as Failed
4131+ VERIFICATION:
4132+ Did the system successfully hibernate and did it work properly after waking up?
4133
4134=== modified file 'jobs/info.txt.in'
4135--- jobs/info.txt.in 2011-09-14 21:16:02 +0000
4136+++ jobs/info.txt.in 2011-11-18 18:04:27 +0000
4137@@ -1,44 +1,57 @@
4138-
4139-name: info/codecs_attachment
4140+name: codecs_attachment
4141 plugin: attachment
4142 requires: device.driver == 'HDA Intel'
4143 command: cat /proc/asound/card*/codec#*
4144+_description: Attaches a report of installed codecs for Intel HDA
4145
4146-name: info/cpuinfo_attachment
4147+name: cpuinfo_attachment
4148 plugin: attachment
4149 command: cat /proc/cpuinfo
4150+_description: Attaches a report of CPU information
4151
4152-name: info/dmesg_attachment
4153+name: dmesg_attachment
4154 plugin: attachment
4155 command: cat /var/log/dmesg | ansi_parser
4156+_description: Attaches a copy of /var/log/dmesg to the test results
4157
4158-name: info/dmi_attachment
4159+name: dmi_attachment
4160 plugin: attachment
4161 command: grep -r . /sys/class/dmi/id/ 2>/dev/null
4162+_description: Attaches info on DMI
4163
4164-name: info/dmidecode_attachment
4165+name: dmidecode_attachment
4166 plugin: attachment
4167 user: root
4168 command: dmidecode
4169+_description: Attaches dmidecode output
4170
4171-name: info/lspci_attachment
4172+name: lspci_attachment
4173 plugin: attachment
4174 command: lspci -vvnn
4175-
4176-name: info/modprobe_attachment
4177+_description: Attaches very verbose lspci output.
4178+
4179+name: meminfo_attachment
4180+plugin: attachment
4181+command: cat /proc/meminfo
4182+
4183+name: modprobe_attachment
4184 plugin: attachment
4185 command: find /etc/modprobe.* -name \*.conf | xargs cat
4186+_description: Attaches the contents of the various modprobe conf files.
4187
4188-name: info/modules_attachment
4189+name: modules_attachment
4190 plugin: attachment
4191 command: cat /etc/modules
4192+_description: Attaches the contents of the /etc/modules file.
4193
4194-name: info/sysctl_attachment
4195+name: sysctl_attachment
4196 plugin: attachment
4197 command: find /etc/sysctl.* -name \*.conf | xargs cat
4198+_description: attaches the contents of various sysctl config files.
4199
4200-name: info/sysfs_attachment
4201+name: sysfs_attachment
4202 plugin: attachment
4203+_description: Attaches a report of sysfs attributes.
4204 command:
4205 for i in `udevadm info --export-db | sed -n 's/^P: //p'`; do
4206 echo "P: $i"
4207@@ -46,19 +59,22 @@
4208 echo
4209 done
4210
4211-name: info/udev_attachment
4212+name: udev_attachment
4213 plugin: attachment
4214 command: udevadm info --export-db
4215+_description: Attaches a dump of the udev database showing system hardware information.
4216
4217-name: info/gcov_attachment
4218+name: gcov_attachment
4219 plugin: attachment
4220 requires: package.name == 'lcov'
4221 user: root
4222 command: gcov_tarball
4223+_description: Attaches a tarball of gcov data if present.
4224
4225-name: info/lsmod_attachment
4226+name: lsmod_attachment
4227 plugin: attachment
4228 command: lsmod
4229+_description: Attaches a list of the currently running kernel modules.
4230
4231 plugin: shell
4232 name: info/screenshot
4233@@ -66,25 +82,25 @@
4234 package.name == 'xorg'
4235 package.name == 'imagemagick'
4236 command: import -window root ${CHECKBOX_DATA}/screenshot.png
4237-_description:
4238- Captures a screenshot.
4239+_description: Captures a screenshot.
4240
4241 plugin: attachment
4242-name: info/screenshot.png
4243+name: screenshot.png
4244 depends: screenshot
4245 command: cat ${CHECKBOX_DATA}/screenshot.png
4246+_description: Attaches the screenshot captured in info/screenshot.
4247
4248 plugin: attachment
4249-name: info/fwts_log
4250+name: fwts_log
4251 depends: fwts_test
4252-_description:
4253- Gather log from the firmware test suite run
4254+_description: Gather log from the Firmware Test Suite run.
4255 command:
4256 cat $CHECKBOX_DATA/fwts_results.log
4257
4258 plugin: attachment
4259-name: info/acpi_sleep_attachment
4260-command: cat /proc/acpi/sleep
4261+name: acpi_sleep_attachment
4262+command: [ -e /proc/acpi/sleep ] && cat /proc/acpi/sleep
4263+_description: Attaches the contents of /proc/acpi/sleep if it exists.
4264
4265 plugin: local
4266 name: info/bootchart
4267@@ -111,25 +127,29 @@
4268 EOF
4269
4270 plugin: attachment
4271-name: info/bootchart.png
4272+name: bootchart.png
4273 depends: info/bootchart
4274 requires: (package.name == 'bootchart' and float(lsb.release) < 9.04) or package.name == 'pybootchartgui'
4275+_description: Attaches the bootchart png file for bootchart runs
4276 command:
4277 file=`ls /var/log/bootchart/*.png 2>/dev/null | tail -1`; \
4278 [ -e "$file" ] && cat "$file"
4279
4280 plugin: attachment
4281-name: info/bootchart.tgz
4282+name: bootchart.tgz
4283 depends: info/bootchart
4284 requires: package.name == 'bootchart' and float(lsb.release) >= 9.04
4285+_description: Attaches the bootchart log for bootchart test runs.
4286 command:
4287 file=`ls /var/log/bootchart/*.tgz 2>/dev/null | tail -1`; \
4288 [ -e "$file" ] && cat "$file"
4289
4290 plugin: attachment
4291-name: info/installer_bootchart.tgz
4292+name: installer_bootchart.tgz
4293 command: [ -e /var/log/installer/bootchart.tgz ] && cat /var/log/installer/bootchart.tgz
4294+_description: installs the installer bootchart tarball if it exists.
4295
4296 plugin: attachment
4297-name: info/installer_debug.gz
4298+name: installer_debug.gz
4299 command: [ -e /var/log/installer/debug ] && gzip -9 -c /var/log/installer/debug
4300+_description: Attaches the installer debug log if it exists.
4301
4302=== modified file 'jobs/input.txt.in'
4303--- jobs/input.txt.in 2011-08-10 21:09:56 +0000
4304+++ jobs/input.txt.in 2011-11-18 18:04:27 +0000
4305@@ -1,16 +1,24 @@
4306 plugin: manual
4307 name: input/mouse
4308-requires: device.category == 'MOUSE'
4309+requires: device.category == 'MOUSE' or device.category == 'TOUCH'
4310 _description:
4311- Moving the mouse should move the cursor on the screen.
4312- .
4313- Is your mouse working properly?
4314+ PURPOSE:
4315+ This test will test your pointing device
4316+ STEPS:
4317+ 1. Move the cursor using the pointing device or touch the screen.
4318+ 2. Perform some single/double/right click operations.
4319+ VERIFICATION:
4320+ Did the pointing device work as expected?
4321
4322 plugin: manual
4323 name: input/keyboard
4324 command: keyboard_test
4325 requires: device.category == 'KEYBOARD'
4326 _description:
4327- Select Test to open a text area where to type keys on your keyboard.
4328- .
4329- Is your keyboard working properly?
4330+ PURPOSE:
4331+ This test will test your keyboard
4332+ STEPS:
4333+ 1. Click on Test
4334+ 2. On the open text area, use your keyboard to type something
4335+ VERIFICATION:
4336+ Is your keyboard working properly?
4337
4338=== modified file 'jobs/install.txt.in'
4339--- jobs/install.txt.in 2011-09-01 12:23:07 +0000
4340+++ jobs/install.txt.in 2011-11-18 18:04:27 +0000
4341@@ -4,4 +4,4 @@
4342 user: root
4343 command: apt-get -d -y --force-yes dist-upgrade && true || false
4344 _description:
4345- Tests to see that apt can access repositories and get updates (does not install updates)
4346+ Tests to see that apt can access repositories and get updates (does not install updates). This is done to confirm that you could recover from an incomplete or broken update.
4347
4348=== modified file 'jobs/keys.txt.in'
4349--- jobs/keys.txt.in 2011-09-01 12:23:07 +0000
4350+++ jobs/keys.txt.in 2011-11-18 18:04:27 +0000
4351@@ -1,73 +1,86 @@
4352 plugin: manual
4353 name: keys/brightness
4354-requires: device.product in ['Notebook','Laptop','Portable']
4355+requires: dmi.product in ['Notebook','Laptop','Portable']
4356 _description:
4357- Press the brightness buttons on the keyboard. A status window should \
4358- appear and the brightness should change.
4359- .
4360- Do the buttons work?
4361+ PURPOSE:
4362+ This test will test the brightness key
4363+ STEPS:
4364+ 1. Press the brightness buttons on the keyboard
4365+ VERIFICATION:
4366+ Did the brightness change following to your key presses?
4367
4368 plugin: manual
4369 name: keys/volume
4370 _description:
4371- Press the volume buttons on the keyboard. A status window should \
4372- appear and the volume should change.
4373- .
4374- Do the buttons work?
4375+ PURPOSE:
4376+ This test will test the volume keys
4377+ STEPS:
4378+ 1. Press the volume buttons on the keyboard
4379+ VERIFICATION:
4380+ Did the volume change following to your key presses?
4381
4382 plugin: manual
4383 name: keys/mute
4384-requires: device.product in ['Notebook','Laptop','Portable']
4385+requires: dmi.product in ['Notebook','Laptop','Portable']
4386 _description:
4387- Press the mute key on the keyboard. A status window should appear \
4388- and the volume should mute/unmute when pressed multiple times.
4389- .
4390- Does the key work?
4391+ PURPOSE:
4392+ This test will test the mute key
4393+ STEPS:
4394+ 1. Press the mute button on the keyboard
4395+ VERIFICATION:
4396+ Did the volume mute following your key presses?
4397
4398 plugin: manual
4399 name: keys/sleep
4400-requires: device.product in ['Notebook','Laptop','Portable']
4401+requires: dmi.product in ['Notebook','Laptop','Portable']
4402 depends: suspend/suspend_advanced
4403 _description:
4404- Press the sleep key on the keyboard. The computer should suspend and, \
4405- after pressing the power button, resume successfully.
4406- .
4407- Does the key work?
4408+ PURPOSE:
4409+ This test will test the sleep key
4410+ STEPS:
4411+ 1. Press the sleep key on the keyboard
4412+ 2. Wake your system up by pressing the power button
4413+ VERIFICATION:
4414+ Did the system go to sleep after pressing the sleep key?
4415
4416 plugin: manual
4417 name: keys/battery-info
4418-requires: device.product in ['Notebook','Laptop','Portable']
4419+requires: dmi.product in ['Notebook','Laptop','Portable']
4420 _description:
4421- Press the battery information key on the keyboard. A status window \
4422- should appear and the amount of battery remaining should be displayed.
4423- .
4424- Does the key work?
4425+ PURPOSE:
4426+ This test will test the battery information key
4427+ STEPS:
4428+ 1. Press the battery information key on the keyboard
4429+ VERIFICATION:
4430+ Did a notification appear showing the battery status?
4431
4432 plugin: manual
4433 name: keys/wireless
4434-requires: device.product in ['Notebook','Laptop','Portable']
4435+requires: dmi.product in ['Notebook','Laptop','Portable']
4436 _description:
4437- Press the wireless networking key on the keyboard. The bluetooth icon \
4438- and the network connnection should go down if connected through the \
4439- wifi interface.
4440- .
4441- Press the same key again and check that bluetooth icon is again \
4442- displayed and that the network connection is re-established \
4443- automatically.
4444- .
4445- Does the key work?
4446+ PURPOSE:
4447+ This test will test the wireless key
4448+ STEPS:
4449+ 1. Press the wireless key on the keyboard
4450+ 2. Press the same key again
4451+ VERIFICATION:
4452+ Did the wireless go off on the first press and on again on the second?
4453
4454 plugin: manual
4455 name: keys/media-control
4456-requires: device.category == 'KEYBOARD'
4457+requires:
4458+ device.category == 'KEYBOARD'
4459+ package.name == 'totem'
4460+ package.name == 'gstreamer0.10-plugins-base'
4461+command: totem /usr/share/example-content/Ubuntu_Free_Culture_Showcase/*
4462 _description:
4463- The keyboard may have dedicated keys for controlling media as follows:
4464- .
4465- * Play/Pause
4466- * Stop
4467- * Forward
4468- * Backward (Rewind)
4469- .
4470- Play a media file and press each key in turn.
4471- .
4472- Do the keys work as expected?
4473+ PURPOSE:
4474+ This test will test the media keys of your keyboard
4475+ STEPS:
4476+ 1. Click test to launch the media player
4477+ 2. Press the play/pause key on the keyboard
4478+ 3. Press the forward key on the keyboard
4479+ 4. Press the backward key on the keyboard
4480+ 5. Press stop key on the keyboard
4481+ VERIFICATION:
4482+ Do the keys work as expected?
4483
4484=== modified file 'jobs/local.txt.in'
4485--- jobs/local.txt.in 2011-09-01 12:23:07 +0000
4486+++ jobs/local.txt.in 2011-11-18 18:04:27 +0000
4487@@ -3,6 +3,11 @@
4488 _description: Audio tests
4489 command: cat $CHECKBOX_SHARE/jobs/audio.txt*
4490
4491+name: __autotest__
4492+plugin: local
4493+_description: Autotest Suite tests
4494+command: cat $CHECKBOX_SHARE/jobs/autotest.txt*
4495+
4496 name: __bluetooth__
4497 plugin: local
4498 _description: Bluetooth tests
4499@@ -13,7 +18,7 @@
4500 _description: Camera tests
4501 command: cat $CHECKBOX_SHARE/jobs/camera.txt*
4502
4503-name: codecs
4504+name: __codecs__
4505 plugin: local
4506 _description: Codec tests
4507 command: cat $CHECKBOX_SHARE/jobs/codecs.txt*
4508@@ -23,7 +28,7 @@
4509 _description: CPU tests
4510 command: cat $CHECKBOX_SHARE/jobs/cpu.txt*
4511
4512-name: daemon
4513+name: __daemons__
4514 plugin: local
4515 _description: System Daemon tests
4516 command: cat $CHECKBOX_SHARE/jobs/daemons.txt*
4517@@ -53,6 +58,11 @@
4518 _description: Graphics tests
4519 command: cat $CHECKBOX_SHARE/jobs/graphics.txt*
4520
4521+name: __hibernate__
4522+plugin: local
4523+_description: Hibernation tests
4524+command: cat $CHECKBOX_SHARE/jobs/hibernate.txt*
4525+
4526 name: __info__
4527 plugin: local
4528 _description: Informational tests
4529@@ -73,10 +83,15 @@
4530 _description: Hotkey tests
4531 command: cat $CHECKBOX_SHARE/jobs/keys.txt*
4532
4533-name: __hibernate__
4534-plugin: local
4535-_description: Hibernation tests
4536-command: cat $CHECKBOX_SHARE/jobs/hibernate.txt*
4537+name: __ltp__
4538+plugin: local
4539+_description: Linux Test Project tests
4540+command: cat $CHECKBOX_SHARE/jobs/ltp.txt*
4541+
4542+name: __mago__
4543+plugin: local
4544+_description: Mago Automated Desktop Testing
4545+command: cat $CHECKBOX_SHARE/jobs/mago.txt*
4546
4547 name: __mediacard__
4548 plugin: local
4549@@ -108,6 +123,11 @@
4550 _description: Optical Drive tests
4551 command: cat $CHECKBOX_SHARE/jobs/optical.txt*
4552
4553+name: __panel_clock__
4554+plugin: local
4555+_description: Panel Clock Verification tests
4556+command: cat $CHECKBOX_SHARE/jobs/panel_clock_test.txt*
4557+
4558 name: __pcmcia-pcix__
4559 plugin: local
4560 _description: PCMCIA/PCIX Card tests
4561@@ -118,11 +138,26 @@
4562 _description: Peripheral tests
4563 command: cat $CHECKBOX_SHARE/jobs/peripheral.txt*
4564
4565+name: __phoronix__
4566+plugin: local
4567+_description: Phoronix Test Suite tests
4568+command: cat $CHECKBOX_SHARE/jobs/peripheral.txt*
4569+
4570 name: __power-management__
4571 plugin: local
4572 _description: Power Management tests
4573 command: cat $CHECKBOX_SHARE/jobs/power-management.txt*
4574
4575+name: __qa__
4576+plugin: local
4577+_description: QA Regression tests
4578+command: cat $CHECKBOX_SHARE/jobs/qa_regression.txt*
4579+
4580+name: __server-services__
4581+plugin: local
4582+_description: Server Services checks
4583+command: cat $CHECKBOX_SHARE/jobs/server-services.txt*
4584+
4585 name: __suspend__
4586 plugin: local
4587 _description: Suspend tests
4588
4589=== modified file 'jobs/ltp.txt.in'
4590--- jobs/ltp.txt.in 2010-03-09 16:58:36 +0000
4591+++ jobs/ltp.txt.in 2011-11-18 18:04:27 +0000
4592@@ -1,6 +1,7 @@
4593-name: ltp
4594+name: ltp/syscalls
4595 plugin: remote
4596-_description: Linux Test Project
4597+_description:
4598+ This test installs and runs Linux Test Project syscalls test. This can be destructive, thus this test is blacklisted by default.
4599 requires:
4600 package.alias == 'linux'
4601 package.name == 'cvs'
4602
4603=== modified file 'jobs/mago.txt.in'
4604--- jobs/mago.txt.in 2011-07-01 11:37:27 +0000
4605+++ jobs/mago.txt.in 2011-11-18 18:04:27 +0000
4606@@ -1,8 +1,10 @@
4607 plugin: remote
4608-name: mago
4609-_description: Automated desktop testing
4610+name: mago/suite
4611 requires:
4612 package.name == 'bzr'
4613 package.name == 'python-ldtp'
4614 desktop.gnome.interface.accessibility == True
4615 command: mago_suite
4616+_description:
4617+ PURPOSE:
4618+ This test installs and runs the Mago Automated Desktop Testing suite.
4619
4620=== modified file 'jobs/mediacard.txt.in'
4621--- jobs/mediacard.txt.in 2011-09-29 13:12:01 +0000
4622+++ jobs/mediacard.txt.in 2011-11-18 18:04:27 +0000
4623@@ -1,137 +1,159 @@
4624 plugin: manual
4625 name: mediacard/sd
4626 _description:
4627- Secure Digital (SD) media card support verification:
4628- 1.- Plug a SD media card into the computer.
4629- 2.- An icon should appear on the desktop and in the "Places" menu at the top of the screen.
4630- 3.- Right click on the deskop icon and select "Safely Remove Drive".
4631- 4.- The icon should disappear of both the deskop and the "Places" menu.
4632- .
4633- Does the icon automatically appear/disappear?
4634+ PURPOSE:
4635+ This test will check your system Secure Digital (SD) media card support
4636+ STEPS:
4637+ 1. Plug a SD media card into the computer
4638+ 2. An icon should appear on the Launcher
4639+ 3. Right click on the Launcher icon and select "Safely Remove Drive"
4640+ 4. The icon should disappear from the Launcher
4641+ VERIFICATION:
4642+ Does the icon automatically appear/disappear?
4643
4644 plugin: manual
4645 name: mediacard/sd_after_suspend
4646 depends: suspend/suspend_advanced mediacard/sd
4647 _description:
4648- Secure Digital (SD) media card support re-verification:
4649- 1.- Plug a SD media card into the computer.
4650- 2.- An icon should appear on the desktop and in the "Places" menu at the top of the screen.
4651- 3.- Right click on the deskop icon and select "Safely Remove Drive".
4652- 4.- The icon should disappear of both the deskop and the "Places" menu.
4653- .
4654- Does the icon automatically appear/disappear?
4655+ PURPOSE:
4656+ This test will check your system Secure Digital (SD) media card support after suspend
4657+ STEPS:
4658+ 1. Plug a SD media card into the computer
4659+ 2. An icon should appear on the Launcher
4660+ 3. Right click on the Launcher icon and select "Safely Remove Drive"
4661+ 4. The icon should disappear from the Launcher
4662+ VERIFICATION:
4663+ Does the icon automatically appear/disappear?
4664
4665 plugin: manual
4666 name: mediacard/sdhc
4667 _description:
4668- Secure Digital High Capacity (SDHC) media card support verification:
4669- 1.- Plug a SDHC media card into the computer.
4670- 2.- An icon should appear on the desktop and in the "Places" menu at the top of the screen.
4671- 3.- Right click on the deskop icon and select "Safely Remove Drive".
4672- 4.- The icon should disappear of both the deskop and the "Places" menu.
4673- .
4674- Does the icon automatically appear/disappear?
4675+ PURPOSE:
4676+ This test will check your system Secure Digital High Capacity (SDHC) media card support
4677+ STEPS:
4678+ 1. Plug a SDHC media card into the computer
4679+ 2. An icon should appear on the Launcher
4680+ 3. Right click on the Launcher icon and select "Safely Remove Drive"
4681+ 4. The icon should disappear from the Launcher
4682+ VERIFICATION:
4683+ Does the icon automatically appear/disappear?
4684
4685 plugin: manual
4686 name: mediacard/sdhc_after_suspend
4687 depends: suspend/suspend_advanced mediacard/sdhc
4688 _description:
4689- Secure Digital High Capacity (SDHC) media card support re-verification:
4690- 1.- Plug a SDHC media card into the computer.
4691- 2.- An icon should appear on the desktop and in the "Places" menu at the top of the screen.
4692- 3.- Right click on the deskop icon and select "Safely Remove Drive".
4693- 4.- The icon should disappear of both the deskop and the "Places" menu.
4694- .
4695- Does the icon automatically appear/disappear?
4696+ This test will check your system Secure Digital High Capacity (SDHC) media card support after suspend
4697+ STEPS:
4698+ 1. Plug a SDHC media card into the computer
4699+ 2. An icon should appear on the Launcher
4700+ 3. Right click on the Launcher icon and select "Safely Remove Drive"
4701+ 4. The icon should disappear from the Launcher
4702+ VERIFICATION:
4703+ Does the icon automatically appear/disappear?
4704
4705 plugin: manual
4706 name: mediacard/mmc
4707 _description:
4708- Multi Media Card (MMC) media card support verification:
4709- 1.- Plug a MMC media card into the computer.
4710- 2.- An icon should appear on the desktop and in the "Places" menu at the top of the screen.
4711- 3.- Right click on the deskop icon and select "Safely Remove Drive".
4712- 4.- The icon should disappear of both the deskop and the "Places" menu.
4713- .
4714- Does the icon automatically appear/disappear?
4715+ PURPOSE:
4716+ This test will check your system Multi Media Card (MMC) media card support
4717+ STEPS:
4718+ 1. Plug a MMC media card into the computer
4719+ 2. An icon should appear on the Launcher
4720+ 3. Right click on the Launcher icon and select "Safely Remove Drive"
4721+ 4. The icon should disappear from the Launcher
4722+ VERIFICATION:
4723+ Does the icon automatically appear/disappear?
4724
4725 plugin: manual
4726 name: mediacard/mmc_after_suspend
4727 depends: suspend/suspend_advanced mediacard/mmc
4728 _description:
4729- Multi Media Card (MMC) media card support re-verification:
4730- 1.- Plug a MMC media card into the computer.
4731- 2.- An icon should appear on the desktop and in the "Places" menu at the top of the screen.
4732- 3.- Right click on the deskop icon and select "Safely Remove Drive".
4733- 4.- The icon should disappear of both the deskop and the "Places" menu.
4734- .
4735- Does the icon automatically appear/disappear?
4736+ PURPOSE:
4737+ This test will check your system Multi Media Card (MMC) media card support after suspend
4738+ STEPS:
4739+ 1. Plug a MMC media card into the computer
4740+ 2. An icon should appear on the Launcher
4741+ 3. Right click on the Launcher icon and select "Safely Remove Drive"
4742+ 4. The icon should disappear from the Launcher
4743+ VERIFICATION:
4744+ Does the icon automatically appear/disappear?
4745
4746 plugin: manual
4747 name: mediacard/ms
4748 _description:
4749- Memory Stick (MS) media card support verification:
4750- 1.- Plug a MS media card into the computer.
4751- 2.- An icon should appear on the desktop and in the "Places" menu at the top of the screen.
4752- 3.- Right click on the deskop icon and select "Safely Remove Drive".
4753- 4.- The icon should disappear of both the deskop and the "Places" menu.
4754- .
4755- Does the icon automatically appear/disappear?
4756+ PURPOSE:
4757+ This test will check your system Memory Stick (MS) media card support
4758+ STEPS:
4759+ 1. Plug a MS media card into the computer
4760+ 2. An icon should appear on the Launcher
4761+ 3. Right click on the Launcher icon and select "Safely Remove Drive"
4762+ 4. The icon should disappear from the Launcher
4763+ VERIFICATION:
4764+ Does the icon automatically appear/disappear?
4765
4766 plugin: manual
4767 name: mediacard/ms_after_suspend
4768 depends: suspend/suspend_advanced mediacard/ms
4769 _description:
4770- Memory Stick (MS) media card support re-verification:
4771- 1.- Plug a MS media card into the computer.
4772- 2.- An icon should appear on the desktop and in the "Places" menu at the top of the screen.
4773- 3.- Right click on the deskop icon and select "Safely Remove Drive".
4774- 4.- The icon should disappear of both the deskop and the "Places" menu.
4775- .
4776- Does the icon automatically appear/disappear?
4777+ PURPOSE:
4778+ This test will check your system Memory Stick (MS) media card support after suspend
4779+ STEPS:
4780+ 1. Plug a MS media card into the computer
4781+ 2. An icon should appear on the Launcher
4782+ 3. Right click on the Launcher icon and select "Safely Remove Drive"
4783+ 4. The icon should disappear from the Launcher
4784+ VERIFICATION:
4785+ Does the icon automatically appear/disappear?
4786
4787 plugin: manual
4788 name: mediacard/msp
4789 _description:
4790- Memory Stick Pro (MSP) media card support verification:
4791- 1.- Plug a MSP media card into the computer.
4792- 2.- An icon should appear on the desktop and in the "Places" menu at the top of the screen.
4793- 3.- Right click on the deskop icon and select "Safely Remove Drive".
4794- 4.- The icon should disappear of both the deskop and the "Places" menu.
4795- .
4796- Does the icon automatically appear/disappear?
4797+ PURPOSE:
4798+ This test will check your system Memory Stick Pro (MSP) media card support
4799+ STEPS:
4800+ 1. Plug a MSP media card into the computer
4801+ 2. An icon should appear on the Launcher
4802+ 3. Right click on the Launcher icon and select "Safely Remove Drive"
4803+ 4. The icon should disappear from the Launcher
4804+ VERIFICATION:
4805+ Does the icon automatically appear/disappear?
4806
4807 plugin: manual
4808 name: mediacard/msp_after_suspend
4809 depends: suspend/suspend_advanced mediacard/msp
4810 _description:
4811- Memory Stick Pro (MSP) media card support re-verification:
4812- 1.- Plug a MSP media card into the computer.
4813- 2.- An icon should appear on the desktop and in the "Places" menu at the top of the screen.
4814- 3.- Right click on the deskop icon and select "Safely Remove Drive".
4815- 4.- The icon should disappear of both the deskop and the "Places" menu.
4816- .
4817- Does the icon automatically appear/disappear?
4818+ PURPOSE:
4819+ This test will check your system Memory Stick Pro (MSP) media card support after suspend
4820+ STEPS:
4821+ 1. Plug a MSP media card into the computer
4822+ 2. An icon should appear on the Launcher
4823+ 3. Right click on the Launcher icon and select "Safely Remove Drive"
4824+ 4. The icon should disappear from the Launcher
4825+ VERIFICATION:
4826+ Does the icon automatically appear/disappear?
4827
4828 plugin: manual
4829 name: mediacard/cf
4830 _description:
4831- Compact Flash (CF) media card support verification:
4832- 1.- Plug a CF media card into the computer.
4833- 2.- An icon should appear on the desktop and in the "Places" menu at the top of the screen.
4834- 3.- Right click on the deskop icon and select "Safely Remove Drive".
4835- 4.- The icon should disappear of both the deskop and the "Places" menu.
4836- .
4837- Does the icon automatically appear/disappear?
4838+ PURPOSE:
4839+ This test will check your system Compact Flash (CF) media card support
4840+ STEPS:
4841+ 1. Plug a CF media card into the computer
4842+ 2. An icon should appear on the Launcher
4843+ 3. Right click on the Launcher icon and select "Safely Remove Drive"
4844+ 4. The icon should disappear from the Launcher
4845+ VERIFICATION:
4846+ Does the icon automatically appear/disappear?
4847
4848 plugin: manual
4849 name: mediacard/cf_after_suspend
4850 depends: suspend/suspend_advanced mediacard/cf
4851 _description:
4852- Compact Flash (CF) media card support re-verification:
4853- 1.- Plug a CF media card into the computer.
4854- 2.- An icon should appear on the desktop and in the "Places" menu at the top of the screen.
4855- 3.- Right click on the deskop icon and select "Safely Remove Drive".
4856- 4.- The icon should disappear of both the deskop and the "Places" menu.
4857- .
4858- Does the icon automatically appear/disappear?
4859+ This test will check your system Compact Flash (CF) media card support after suspend
4860+ STEPS:
4861+ 1. Plug a CF media card into the computer
4862+ 2. An icon should appear on the Launcher
4863+ 3. Right click on the Launcher icon and select "Safely Remove Drive"
4864+ 4. The icon should disappear from the Launcher
4865+ VERIFICATION:
4866+ Does the icon automatically appear/disappear?
4867
4868=== modified file 'jobs/memory.txt.in'
4869--- jobs/memory.txt.in 2011-09-14 21:16:02 +0000
4870+++ jobs/memory.txt.in 2011-11-18 18:04:27 +0000
4871@@ -2,11 +2,14 @@
4872 name: memory/info
4873 command: memory_info
4874 _description:
4875- The following amount of memory was detected:
4876- .
4877- $output
4878- .
4879- Is this correct?
4880+ PURPOSE:
4881+ This test checks the amount of memory that is detected
4882+ STEPS:
4883+ 1. Click Test to see the amount of detected memory
4884+ INFO:
4885+ $output
4886+ VERIFICATION:
4887+ Is the amount of detected memory correct?
4888
4889 plugin: shell
4890 name: memory/check
4891
4892=== modified file 'jobs/miscellanea.txt.in'
4893--- jobs/miscellanea.txt.in 2011-09-29 13:12:01 +0000
4894+++ jobs/miscellanea.txt.in 2011-11-18 18:04:27 +0000
4895@@ -6,11 +6,12 @@
4896 user: root
4897 command: cycle_vts
4898 _description:
4899- Select Test to switch to another virtual terminal and then back to X. Your screen will change temporarily to a text console and then switch back to your current session.
4900- .
4901- Note that this test may require you to enter your password.
4902- .
4903- Did the screen change temporarily to a text console?
4904+ PURPOSE:
4905+ This test will check that the system can switch to a virtual terminal and back to X
4906+ STEPS:
4907+ 1. Click "Test" to switch to another virtual terminal and then back to X
4908+ VERIFICATION:
4909+ Did your screen change temporarily to a text console and then switch back to your current session?
4910
4911 plugin: shell
4912 name: miscellanea/fwts_test
4913@@ -18,7 +19,7 @@
4914 package.name == 'linux'
4915 package.name == 'fwts'
4916 _description:
4917- Run Colin King's Firmware Test Suite automated tests.
4918+ Run Firmware Test Suite (fwts) automated tests.
4919 command:
4920 fwts_test -l $CHECKBOX_DATA/fwts_results.log
4921
4922@@ -39,6 +40,16 @@
4923 plugin: manual
4924 name: miscellanea/is_laptop
4925 _description:
4926- Is your system a laptop (or netbook)?
4927+ PURPOSE:
4928+ This is to determine if we need to run tests specific to portable computers that may not apply to desktops.
4929+ STEPS:
4930+ Select "Yes" if your system is a laptop or netbook. Otherwise, select "No"
4931+
4932+plugin: shell
4933+name: miscellanea/apport-directory
4934+requires: package.name == 'apport'
4935+command: ! test -d /var/crash || test $(find /var/crash -type f | wc -l) -eq 0
4936+_description:
4937+ This test checks /var/crash to see if there are any crash reports present.
4938 .
4939- Selecting Yes here will allow us to run tests specific to portable computers that may not apply to desktops.
4940+ If there are, this test will fail.
4941
4942=== modified file 'jobs/monitor.txt.in'
4943--- jobs/monitor.txt.in 2011-09-14 21:16:02 +0000
4944+++ jobs/monitor.txt.in 2011-11-18 18:04:27 +0000
4945@@ -1,52 +1,71 @@
4946 plugin: manual
4947 name: monitor/vga
4948 _description:
4949- If your system does not have a VGA port, please skip this test.
4950- .
4951- Connect a display (if not already connected) to the VGA port on your system. Is the image displayed correctly?
4952+ PURPOSE:
4953+ This test will check your VGA port. Skip if your system does not have a VGA port.
4954+ STEPS:
4955+ 1. Connect a display (if not already connected) to the VGA port on your system
4956+ VERIFICATION:
4957+ Was the desktop displayed correctly on both screens?
4958
4959 plugin: manual
4960 name: monitor/dvi
4961 _description:
4962- If your system does not have a DVI port, please skip this test.
4963- .
4964- Connect a display (if not already connected) to the DVI port on your system. Is the image displayed correctly?
4965+ PURPOSE:
4966+ This test will check your DVI port. Skip if your system does not have a DVI port
4967+ STEPS:
4968+ 1. Connect a display (if not already connected) to the DVI port on your system
4969+ VERIFICATION:
4970+ Was the desktop displayed correctly on both screens?
4971
4972 plugin: manual
4973 name: monitor/displayport
4974 _description:
4975- If your system does not have DisplayPort, please skip this test.
4976- .
4977- Connect a display (if not already connected) to the DisplayPort on your system. Is the image displayed correctly?
4978+ PURPOSE:
4979+ This test will check your DisplayPort port. Skip if your system does not have a DisplayPort port
4980+ STEPS:
4981+ 1. Connect a display (if not already connected) to the DisplayPort port on your system
4982+ VERIFICATION:
4983+ Was the desktop displayed correctly on both screens?
4984
4985 plugin: manual
4986 name: monitor/hdmi
4987 _description:
4988- If your system does not have a HDMI port, please skip this test.
4989- .
4990- Connect a display (if not already connected) to the HDMI port on your system. Is the image displayed correctly?
4991+ PURPOSE:
4992+ This test will check your HDMI port. Skip if your system does not have a HDMI port
4993+ STEPS:
4994+ 1. Connect a display (if not already connected) to the HDMI port on your system
4995+ VERIFICATION:
4996+ Was the desktop displayed correctly on both screens?
4997
4998 plugin: manual
4999 name: monitor/svideo
5000 _description:
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches