← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:hwdb-remove-submission-parser into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:hwdb-remove-submission-parser into launchpad:master.

Commit message:
Remove hardware DB submission parser

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/387320

We no longer accept or process submissions.

This is a very large branch and will overflow the limit for merge proposal diffs, but it's almost entirely removals.  It's probably best to look at the diff on git.launchpad.net directly.
-- 
The attached diff has been truncated due to its size.
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:hwdb-remove-submission-parser into launchpad:master.
diff --git a/lib/lp/app/stories/basics/xx-offsite-form-post.txt b/lib/lp/app/stories/basics/xx-offsite-form-post.txt
index a58154b..4b1bcc8 100644
--- a/lib/lp/app/stories/basics/xx-offsite-form-post.txt
+++ b/lib/lp/app/stories/basics/xx-offsite-form-post.txt
@@ -100,11 +100,6 @@ To support apport, we allow it for +storeblob.
 
   >>> browser.post('http://launchpad.test/+storeblob', 'x=1')
 
-Similary, we exempt the URL /+hwdb/+submit in order to allow checkbox
-to submit HWDB reports.
-
-  >>> browser.post('http://launchpad.test/+hwdb/+submit', 'x=1')
-
 To support old versions of launchpadlib, we also let POST requests
 without a REFERER header go through to +request-token and
 +access-token.
diff --git a/lib/lp/bugs/browser/bugtarget.py b/lib/lp/bugs/browser/bugtarget.py
index 45f45fa..e25187c 100644
--- a/lib/lp/bugs/browser/bugtarget.py
+++ b/lib/lp/bugs/browser/bugtarget.py
@@ -121,7 +121,6 @@ from lp.bugs.model.structuralsubscription import (
     get_structural_subscriptions_for_target,
     )
 from lp.bugs.utilities.filebugdataparser import FileBugData
-from lp.hardwaredb.interfaces.hwdb import IHWSubmissionSet
 from lp.registry.browser.product import ProductConfigureBase
 from lp.registry.interfaces.distribution import IDistribution
 from lp.registry.interfaces.distributionsourcepackage import (
@@ -652,13 +651,6 @@ class FileBugViewBase(LaunchpadFormView):
                         '%s has been subscribed to this bug.' %
                         person.displayname)
 
-        submission_set = getUtility(IHWSubmissionSet)
-        for submission_key in extra_data.hwdb_submission_keys:
-            submission = submission_set.getBySubmissionKey(
-                submission_key, self.user)
-            if submission is not None:
-                bug.linkHWSubmission(submission)
-
         # Give the user some feedback on the bug just opened.
         for notification in notifications:
             self.request.response.addNotification(notification)
diff --git a/lib/lp/bugs/doc/filebug-data-parser.txt b/lib/lp/bugs/doc/filebug-data-parser.txt
index 9d34645..39e7c38 100644
--- a/lib/lp/bugs/doc/filebug-data-parser.txt
+++ b/lib/lp/bugs/doc/filebug-data-parser.txt
@@ -187,28 +187,6 @@ automatically.
     [u'sub-one', u'sub-two']
 
 
-==== HWDB submission keys ====
-
-The HWDB-Submission key is turned into a list of strings, available
-through the hwdb_submission_keys attribute.
-
-    >>> data = FileBugData()
-    >>> parser._setDataFromHeaders(
-    ...     data, {'HWDB-Submission': 'submission-one'})
-    >>> list(data.hwdb_submission_keys)
-    [u'submission-one']
-
-Two or more submission keys may be specified, separated by a comma,
-optionally followed by space characters.
-
-    >>> data = FileBugData()
-    >>> parser._setDataFromHeaders(
-    ...     data,
-    ...     {'HWDB-Submission': ' submission-one, two\t,three ,  \nfour  '})
-    >>> data.hwdb_submission_keys
-    [u'four', u'submission-one', u'three', u'two']
-
-
 === Message Parts ===
 
 Different parts of the message gets treated differently. In short, we
diff --git a/lib/lp/bugs/interfaces/bug.py b/lib/lp/bugs/interfaces/bug.py
index dab809d..74def4b 100644
--- a/lib/lp/bugs/interfaces/bug.py
+++ b/lib/lp/bugs/interfaces/bug.py
@@ -1251,7 +1251,6 @@ class IFileBugData(Interface):
     subscribers = Attribute("The initial subscribers for the bug.")
     comments = Attribute("Comments to add to the bug.")
     attachments = Attribute("Attachments to add to the bug.")
-    hwdb_submission_keys = Attribute("HWDB submission keys for the bug.")
 
 
 class IBugMute(Interface):
diff --git a/lib/lp/bugs/model/apportjob.py b/lib/lp/bugs/model/apportjob.py
index a2f16e0..fdbc847 100644
--- a/lib/lp/bugs/model/apportjob.py
+++ b/lib/lp/bugs/model/apportjob.py
@@ -270,7 +270,6 @@ class ProcessApportBlobJob(ApportJobDerived):
                 subscribers=processed_data['subscribers'],
                 extra_description=processed_data['extra_description'],
                 comments=processed_data['comments'],
-                hwdb_submission_keys=processed_data['hwdb_submission_keys'],
                 attachments=attachment_data)
         else:
             return FileBugData()
diff --git a/lib/lp/bugs/model/bug.py b/lib/lp/bugs/model/bug.py
index 98169c6..9fa5155 100644
--- a/lib/lp/bugs/model/bug.py
+++ b/lib/lp/bugs/model/bug.py
@@ -2796,8 +2796,7 @@ class FileBugData:
 
     def __init__(self, initial_summary=None, initial_tags=None,
                  private=None, subscribers=None, extra_description=None,
-                 comments=None, attachments=None,
-                 hwdb_submission_keys=None):
+                 comments=None, attachments=None):
         if initial_tags is None:
             initial_tags = []
         if subscribers is None:
@@ -2806,8 +2805,6 @@ class FileBugData:
             comments = []
         if attachments is None:
             attachments = []
-        if hwdb_submission_keys is None:
-            hwdb_submission_keys = []
 
         self.initial_summary = initial_summary
         self.private = private
@@ -2816,7 +2813,6 @@ class FileBugData:
         self.subscribers = subscribers
         self.comments = comments
         self.attachments = attachments
-        self.hwdb_submission_keys = hwdb_submission_keys
 
     def asDict(self):
         """Return the FileBugData instance as a dict."""
diff --git a/lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.txt b/lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.txt
index 3a42491..705f57f 100644
--- a/lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.txt
+++ b/lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.txt
@@ -238,55 +238,3 @@ that will get their tags set as well.
     >>> tags = find_tag_by_id(user_browser.contents, 'bug-tags')
     >>> print(extract_text(tags))
     Tags: bar foo...
-
-
-References to HWDB submissions
-------------------------------
-
-The uploaded message may contain a header "HWDB-Submission", its value
-should be a sequence of HWDB submission keys, separated by ', *'.
-If the message contains this header, the referenced HWDB submissions are
-linked to the new bug.
-
-Our test data contains such a header, mentioning two HWDB submision key.
-The database knows only the first key.
-
-    >>> extra_filebug_data_with_hwdb_submission = open(
-    ...     os.path.join(testfiles, 'extra_filebug_data_hwdb_submission.msg'),
-    ...     'rb')
-    >>> print(
-    ...     extra_filebug_data_with_hwdb_submission.read()
-    ...     .decode('UTF-8').split('\n')[2])
-    HWDB-Submission: sample-submission, non-existing-submission-key
-
-Once we submit the bug report...
-
-    >>> extra_filebug_data_with_hwdb_submission.seek(0)
-    >>> anon_browser.open('http://launchpad.test/+storeblob')
-    >>> anon_browser.getControl(name='field.blob').add_file( # Don't change!
-    ...     extra_filebug_data_with_hwdb_submission, 'not/important',
-    ...     'not.important')
-    >>> anon_browser.getControl(name='FORM_SUBMIT').click() # Don't change!
-    >>> blob_token = six.ensure_text(
-    ...     anon_browser.headers['X-Launchpad-Blob-Token'])
-    >>> process_blob(blob_token)
-
-    >>> filebug_url = (
-    ...    'http://launchpad.test/ubuntu/+source/mozilla-firefox/+filebug/'
-    ...     '%s' % blob_token)
-    >>> user_browser.open(filebug_url)
-    >>> user_browser.getControl('Summary', index=0).value = 'Another summary'
-    >>> user_browser.getControl('Continue').click()
-    >>> user_browser.getControl('Further information').value = (
-    ...     'A bug description.')
-    >>> user_browser.getControl('Submit Bug Report').click()
-
-...the existing HWDB submission is linked to the new bug.
-
-    >>> bug_id = user_browser.url.split('/')[-1]
-    >>> bug_url = '/bugs/%s' % bug_id
-    >>> linked_submissions = webservice.named_get(
-    ...     bug_url, 'getHWSubmissions').jsonBody()
-    >>> for submission in linked_submissions['entries']:
-    ...     print(submission['submission_key'])
-    sample-submission
diff --git a/lib/lp/bugs/tests/test_apportjob.py b/lib/lp/bugs/tests/test_apportjob.py
index 9f5baa7..3074097 100644
--- a/lib/lp/bugs/tests/test_apportjob.py
+++ b/lib/lp/bugs/tests/test_apportjob.py
@@ -120,10 +120,6 @@ class ProcessApportBlobJobTestCase(TestCaseWithFactory):
         self.assertEqual(
             filebug_data.comments, data_dict['comments'],
             "Values for comments do not match")
-        self.assertEqual(
-            filebug_data.hwdb_submission_keys,
-            data_dict['hwdb_submission_keys'],
-            "Values for hwdb_submission_keys do not match")
 
         # The attachments list of the data_dict dict will be of
         # the same length as the attachments list in the filebug_data
diff --git a/lib/lp/bugs/tests/testfiles/extra_filebug_data_hwdb_submission.msg b/lib/lp/bugs/tests/testfiles/extra_filebug_data_hwdb_submission.msg
deleted file mode 100644
index 5bbbdfc..0000000
--- a/lib/lp/bugs/tests/testfiles/extra_filebug_data_hwdb_submission.msg
+++ /dev/null
@@ -1,11 +0,0 @@
-MIME-Version: 1.0 
-Content-type: multipart/mixed; boundary=boundary
-HWDB-Submission: sample-submission, non-existing-submission-key
-
---boundary
-Content-disposition: inline
-Content-type: text/plain; charset=utf-8
-
-This should be added to the description.
-
---boundary--
diff --git a/lib/lp/bugs/utilities/filebugdataparser.py b/lib/lp/bugs/utilities/filebugdataparser.py
index f1266d6..7ab6540 100644
--- a/lib/lp/bugs/utilities/filebugdataparser.py
+++ b/lib/lp/bugs/utilities/filebugdataparser.py
@@ -88,10 +88,6 @@ class FileBugDataParser:
         if 'Subscribers' in headers:
             subscribers_string = unicode(headers['Subscribers'])
             data.subscribers = subscribers_string.lower().split()
-        if 'HWDB-Submission' in headers:
-            submission_string = unicode(headers['HWDB-Submission'])
-            data.hwdb_submission_keys = sorted(
-                part.strip() for part in submission_string.split(','))
 
     def parse(self):
         """Parse the message and  return a FileBugData instance.
diff --git a/lib/lp/hardwaredb/browser/configure.zcml b/lib/lp/hardwaredb/browser/configure.zcml
index 4d06052..ed130e5 100644
--- a/lib/lp/hardwaredb/browser/configure.zcml
+++ b/lib/lp/hardwaredb/browser/configure.zcml
@@ -12,18 +12,6 @@
         for="lp.hardwaredb.interfaces.hwdb.IHWDBApplication"
         path_expression="string:+hwdb"
         parent_utility="lp.services.webapp.interfaces.ILaunchpadRoot"/>
-    <browser:page
-        for="lp.hardwaredb.interfaces.hwdb.IHWDBApplication"
-        class="lp.hardwaredb.browser.hwdb.HWDBUploadView"
-        permission="zope.Public"
-        name="+submit"
-        template="../templates/hwdb-submit-hardware-data.pt"/>
-    <browser:page
-        for="lp.hardwaredb.interfaces.hwdb.HWSubmissionsDisabledError"
-        class="lp.hardwaredb.browser.hwdb.HWDBSubmissionsDisabledView"
-        name="index.html"
-        permission="zope.Public"
-        template="../templates/hwdb-submissions-disabled.pt"/>
     <browser:url
         for="lp.hardwaredb.interfaces.hwdb.IHWSystemFingerprint"
         path_expression="string:+fingerprint/${fingerprint}"
diff --git a/lib/lp/hardwaredb/browser/hwdb.py b/lib/lp/hardwaredb/browser/hwdb.py
index bbd1ee6..03bf04c 100644
--- a/lib/lp/hardwaredb/browser/hwdb.py
+++ b/lib/lp/hardwaredb/browser/hwdb.py
@@ -7,9 +7,7 @@ __all__ = [
     'HWDBApplicationNavigation',
     'HWDBFingerprintSetView',
     'HWDBPersonSubmissionsView',
-    'HWDBSubmissionsDisabledView',
     'HWDBSubmissionTextView',
-    'HWDBUploadView',
     ]
 
 from textwrap import dedent
@@ -19,10 +17,6 @@ from zope.component import getUtility
 from zope.interface import implementer
 from zope.publisher.interfaces.browser import IBrowserPublisher
 
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
 from lp.app.errors import NotFoundError
 from lp.hardwaredb.interfaces.hwdb import (
     IHWDBApplication,
@@ -30,140 +24,18 @@ from lp.hardwaredb.interfaces.hwdb import (
     IHWDeviceSet,
     IHWDriverSet,
     IHWSubmissionDeviceSet,
-    IHWSubmissionForm,
     IHWSubmissionSet,
-    IHWSystemFingerprintSet,
     IHWVendorIDSet,
     )
-from lp.registry.interfaces.distribution import IDistributionSet
 from lp.services.webapp import (
     LaunchpadView,
     Navigation,
     stepthrough,
     )
 from lp.services.webapp.batching import BatchNavigator
-from lp.services.webapp.error import GoneView
 from lp.services.webapp.interfaces import ILaunchBag
 
 
-class HWDBUploadView(LaunchpadFormView):
-    """View class for hardware database submissions."""
-
-    schema = IHWSubmissionForm
-    label = 'Hardware Database Submission'
-    page_title = 'Submit New Data to the Launchpad Hardware Database'
-
-    @action(u'Upload', name='upload')
-    def upload_action(self, action, data):
-        """Create a record in the HWSubmission table."""
-        # We expect that the data submitted by the client contains
-        # data for all fields defined in the form. The main client
-        # which POSTs data to this URL, checkbox, sometimes omits
-        # some fields (see bug 357316). The absence of required
-        # fields is not caught by Zope's form validation -- it only
-        # checks if required fields are not empty, and does this only
-        # if these fields are present in the form data. Absent fields
-        # are not detected, so let's do that here.
-        expected_fields = set(self.schema.names())
-        submitted_fields = set(data)
-        missing_fields = expected_fields.difference(submitted_fields)
-        if len(missing_fields) > 0:
-            missing_fields = ', '.join(sorted(missing_fields))
-            self.addCustomHeader(
-                'Error: Required fields not contained in POST data: '
-                + missing_fields)
-            return
-
-        distributionset = getUtility(IDistributionSet)
-        distribution = distributionset.getByName(data['distribution'].lower())
-        if distribution is not None:
-            release = data['distroseries']
-            architecture = data['architecture']
-            try:
-                distroseries = distribution.getSeries(release)
-            except NotFoundError:
-                self.addErrorHeader("distroseries",
-                    "%s isn't a valid distribution series"
-                     % data['distroseries'])
-                return
-
-            try:
-                distroarchseries = distroseries[architecture]
-            except NotFoundError:
-                self.addErrorHeader("distroarchseries",
-                    "%s isn't a valid distribution architecture"
-                     % data['architecture'])
-                return
-        else:
-            distroarchseries = None
-
-        fingerprintset = getUtility(IHWSystemFingerprintSet)
-        fingerprint = fingerprintset.getByName(data['system'])
-        if fingerprint is None:
-            fingerprint = fingerprintset.createFingerprint(data['system'])
-
-        filesize = len(data['submission_data'])
-        submission_file = self.request.form[
-            self.widgets['submission_data'].name]
-        submission_file.seek(0)
-        # convert a filename with "path elements" to a regular filename
-        filename = submission_file.filename.replace('/', '-')
-
-        hw_submissionset = getUtility(IHWSubmissionSet)
-        hw_submissionset.createSubmission(
-            date_created=data['date_created'],
-            format=data['format'],
-            private=data['private'],
-            contactable=data['contactable'],
-            submission_key=data['submission_key'],
-            emailaddress=data['emailaddress'],
-            distroarchseries=distroarchseries,
-            raw_submission=submission_file,
-            filename=filename,
-            filesize=filesize,
-            system_fingerprint=data['system'])
-
-        self.addCustomHeader('OK data stored')
-        self.request.response.addNotification(
-            "Thank you for your submission.")
-
-    def render(self):
-        """See ILaunchpadFormView."""
-        if self.errors:
-            self.setHeadersForHWDBClient()
-        return LaunchpadFormView.render(self)
-
-    def setHeadersForHWDBClient(self):
-        """Add headers that help the HWDB client detect a successful upload.
-
-        An upload is normally not made by a regular web browser, but by the
-        HWDB client. In order to allow the client to easily detect a
-        successful as well as an failed request, add some HTTP headers
-        to the response.
-        """
-        for field in self.form_fields:
-            field_name = field.__name__
-            error = self.getFieldError(field_name)
-            if error:
-                self.addErrorHeader(field_name, error)
-
-    def addErrorHeader(self, field_name, error):
-        """Adds a header informing an error to automated clients."""
-        return self.addCustomHeader(u"Error in field '%s' - %s" %
-                                    (field_name, error))
-
-    def addCustomHeader(self, value):
-        """Adds a custom header to HWDB clients."""
-        self.request.response.setHeader(
-            u'X-Launchpad-HWDB-Submission', value)
-
-
-class HWDBSubmissionsDisabledView(GoneView):
-    """View to indicate that new submissions are disabled."""
-
-    page_title = "Hardware database submissions disabled"
-
-
 class HWDBPersonSubmissionsView(LaunchpadView):
     """View class for preseting HWDB submissions by a person."""
 
diff --git a/lib/lp/hardwaredb/doc/hwdb-submission.txt b/lib/lp/hardwaredb/doc/hwdb-submission.txt
deleted file mode 100644
index 13f3edc..0000000
--- a/lib/lp/hardwaredb/doc/hwdb-submission.txt
+++ /dev/null
@@ -1,215 +0,0 @@
-Submissions to the hardware database
-====================================
-
-The hardware database client collects information from various sources,
-and submits it in an HTTP POST request to the hardware database server.
-The POST data consists of the following fields:
-
-  * date_created (see hwdb.txt)
-  * format (see hwdb.txt)
-  * private (see hwdb.txt)
-  * contactable (see hwdb.txt)
-  * submission_key (see hwdb.txt)
-  * emailaddress (see hwdb.txt)
-  * distribution: The distribution name. The value should match a value
-    in the SQL table Distribution, column name.
-  * distroseries: The distroseries version. The value should match a value
-    in the SQL table distroseries, column version
-  * processorfamily: The name of the processorfamily. The value should
-    match a value in the SQL table Processorfamily, column name.
-  * system: The system name as returned by HAL (system.vendor, system.product)
-  * submission_data: An XML file containing the collected data. This file is
-    simply stored as a Librarian file, and parsed later by a cron job.
-
-    >>> import io
-    >>> from zope.component import getUtility
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWDBApplication
-    >>> data = io.BytesIO(b'some data.')
-    >>> data.filename = 'hardware-info'
-    >>> form={'field.date_created':    u'2007-08-01',
-    ...       'field.format':          u'VERSION_1',
-    ...       'field.private':         u'',
-    ...       'field.contactable':     u'',
-    ...       'field.submission_key':  u'unique-id-1',
-    ...       'field.emailaddress':    u'test@xxxxxxxxxxxxx',
-    ...       'field.distribution':    u'ubuntu',
-    ...       'field.distroseries':    u'5.04',
-    ...       'field.architecture':    u'i386',
-    ...       'field.system':          u'HP 6543',
-    ...       'field.submission_data': data,
-    ...       'field.actions.upload':  u'Upload'}
-
-    >>> app = getUtility(IHWDBApplication)
-    >>> submit_view = create_initialized_view(app, name='+submit', form=form)
-    >>> submit_view.errors
-    []
-
-The request created an entry in the HWDBSubmissions table.
-
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWSubmissionSet
-    >>> submission_set = getUtility(IHWSubmissionSet)
-    >>> submission = submission_set.getBySubmissionKey(u'unique-id-1')
-    >>> submission.date_created, submission.format.name
-    (datetime.datetime(2007, 8, 1, 0, 0, tzinfo=<UTC>), 'VERSION_1')
-    >>> submission.private, submission.contactable
-    (False, False)
-    >>> submission.submission_key
-    u'unique-id-1'
-    >>> submission.system_fingerprint.fingerprint
-    u'HP 6543'
-
-The submitted data is stored in raw_submission.
-
-    >>> import transaction
-    >>> transaction.commit()
-    >>> submission.raw_submission.read()
-    'some data.'
-
-A reference to distroarchseries is created for the fields distribution,
-distroseries, architecture.
-
-    >>> submission.distroarchseries.distroseries.distribution.name
-    u'ubuntu'
-    >>> submission.distroarchseries.distroseries.version
-    u'5.04'
-    >>> submission.distroarchseries.architecturetag
-    u'i386'
-
-Each submission must have a distinct submission_key, hence an attempt to submit
-identical data a second time leads to an error.
-
-    >>> submit_view = create_initialized_view(app, name='+submit', form=form)
-    >>> for error in submit_view.errors:
-    ...     print(error.doc())
-    Submission key already exists.
-
-If the field distribution contains a name which is not known in the
-Launchpad database, the distroarchseries field is None.
-
-    >>> form['field.submission_key'] = u'unique-id-2'
-    >>> invalid_form = form.copy()
-    >>> invalid_form['field.distribution'] = 'no distribution'
-    >>> submit_view = create_initialized_view(
-    ...     app, name='+submit', form=invalid_form)
-    >>> submission = submission_set.getBySubmissionKey(u'unique-id-2')
-    >>> print(submission.distroarchseries)
-    None
-
-If distribution is known, but distroseries or architecture are unknown
-to Launchpad, we refuse the submissions.
-
-    >>> form['field.submission_key'] = u'unique-id-3'
-    >>> invalid_form = form.copy()
-    >>> invalid_form['field.distroseries'] = 'no release'
-    >>> submit_view = create_initialized_view(
-    ...     app, name='+submit', form=invalid_form)
-    >>> print(submission_set.getBySubmissionKey(u'unique-id-3'))
-    None
-
-    >>> form['field.submission_key'] = u'unique-id-4'
-    >>> invalid_form = form.copy()
-    >>> invalid_form['field.architecture'] = 'no architecture'
-    >>> submit_view = create_initialized_view(
-    ...     app, name='+submit', form=invalid_form)
-    >>> print(submission_set.getBySubmissionKey(u'unique-id-4'))
-    None
-
-The `date_created` field must contain a valid date.
-
-    >>> form['field.submission_key'] = u'unique-id-5'
-    >>> invalid_form = form.copy()
-    >>> invalid_form['field.date_created'] = '2007-05-35'
-    >>> submit_view = create_initialized_view(
-    ...     app, name='+submit', form=invalid_form)
-    >>> for error in submit_view.errors:
-    ...     print(error.doc())
-    Invalid datetime data
-
-The `format` field must contain a value that matches the DBEnumeratedType
-HWDBSubmissionFormat.
-
-    >>> invalid_form = form.copy()
-    >>> invalid_form['field.format'] = u'VERSION_42'
-    >>> submit_view = create_initialized_view(
-    ...     app, name='+submit', form=invalid_form)
-    >>> for error in submit_view.errors:
-    ...     print(error.doc())
-    Invalid value
-
-The field `submission_key` may contain only ASCII data.
-
-    >>> invalid_form = form.copy()
-    >>> invalid_form['field.submission_key'] = u'wrong id \x81'
-    >>> submit_view = create_initialized_view(
-    ...     app, name='+submit', form=invalid_form)
-    >>> for error in submit_view.errors:
-    ...     print(error.doc())
-    Invalid textual data
-
-The field `emailaddress` must contain a formally valid email address.
-
-    >>> invalid_form = form.copy()
-    >>> invalid_form['field.emailaddress'] = u'beeblebrox'
-    >>> submit_view = create_initialized_view(
-    ...     app, name='+submit', form=invalid_form)
-    >>> for error in submit_view.errors:
-    ...     print(error.doc())
-    Invalid email address
-
-All fields are required.  With normal form processing, it's impossible not to
-have values for field.format, field.private, or field.contactable because
-those widgets are checkboxes and menus.
-
-    >>> for field in (
-    ...     'field.date_created', 'field.submission_key',
-    ...     'field.emailaddress', 'field.distribution',
-    ...     'field.distroseries', 'field.architecture',
-    ...     'field.system', 'field.submission_data',
-    ...     ):
-    ...     invalid_form = form.copy()
-    ...     del invalid_form[field]
-    ...     invalid_form[field] = u''
-    ...     submit_view = create_initialized_view(
-    ...         app, name='+submit', form=invalid_form)
-    ...     print(field)
-    ...     for error in submit_view.errors:
-    ...         field_name = error.field_name
-    ...         print('   ', field_name,
-    ...               submit_view.getFieldError(field_name))
-    field.date_created
-        date_created Required input is missing.
-    field.submission_key
-        submission_key Required input is missing.
-    field.emailaddress
-        emailaddress Required input is missing.
-    field.distribution
-        distribution Required input is missing.
-    field.distroseries
-        distroseries Required input is missing.
-    field.architecture
-        architecture Required input is missing.
-    field.system
-        system Required input is missing.
-    field.submission_data
-        submission_data Required input is missing.
-
-Teams can be owners of submissions.
-
-    >>> import os
-    >>> from lp.services.config import config
-    >>> team_form = form.copy()
-    >>> team_form['field.emailaddress'] = 'support@xxxxxxxxxx'
-    >>> team_form['field.submission_key'] = u'unique-id-68'
-    >>> valid_sample_data_path = os.path.join(
-    ...     config.root,
-    ...     'lib/lp/hardwaredb/scripts/tests/'
-    ...     'simple_valid_hwdb_submission.xml')
-    >>> valid_sample_data = io.BytesIO(
-    ...     open(valid_sample_data_path, 'rb').read())
-    >>> valid_sample_data.filename = 'simple_valid_hwdb_submission.xml'
-    >>> team_form['field.submission_data'] = valid_sample_data
-    >>> submit_view = create_initialized_view(
-    ...     app, name='+submit', form=team_form)
-    >>> submission = submission_set.getBySubmissionKey(u'unique-id-68')
-    >>> submission.owner.displayname
-    u'Ubuntu Team'
diff --git a/lib/lp/hardwaredb/scripts/__init__.py b/lib/lp/hardwaredb/scripts/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/lib/lp/hardwaredb/scripts/__init__.py
+++ /dev/null
diff --git a/lib/lp/hardwaredb/scripts/hardware-1_0.rng b/lib/lp/hardwaredb/scripts/hardware-1_0.rng
deleted file mode 100644
index aabe1e7..0000000
--- a/lib/lp/hardwaredb/scripts/hardware-1_0.rng
+++ /dev/null
@@ -1,539 +0,0 @@
-<?xml version="1.0" ?>
-<grammar xmlns="http://relaxng.org/ns/structure/1.0";
-         datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes";
-         xmlns:a="http://launchpad.net/annotation";>
-    <start>
-        <element name="system">
-            <attribute name="version">
-                <value>1.0</value>
-            </attribute>
-          <interleave>
-            <element name="summary">
-                <ref name="summarySection"/>
-            </element>
-            <element name="hardware">
-                <ref name="hardwareSection"/>
-            </element>
-            <element name="software">
-                <ref name="softwareSection"/>
-            </element>
-            <element name="questions">
-                <ref name="questionsSection"/>
-            </element>
-            <optional>
-                <element name="context">
-                    <ref name="contextSection"/>
-                </element>
-            </optional>
-          </interleave>
-        </element>
-    </start>
-
-    <define name="summarySection">
-        <interleave>
-            <element name="live_cd">
-                <attribute name="value">
-                    <ref name="booleanValue"/>
-                </attribute>
-                <empty/>
-            </element>
-            <element name="system_id">
-                <attribute name="value">
-                    <text/>
-                </attribute>
-                <empty/>
-            </element>
-            <element name="distribution">
-                <attribute name="value">
-                    <text/>
-                </attribute>
-                <empty/>
-            </element>
-            <element name="distroseries">
-                <attribute name="value">
-                    <text/>
-                </attribute>
-                <empty/>
-            </element>
-            <element name="architecture">
-                <attribute name="value">
-                    <text/>
-                </attribute>
-                <empty/>
-            </element>
-            <element name="private">
-                <attribute name="value">
-                    <ref name="booleanValue"/>
-                </attribute>
-                <empty/>
-            </element>
-            <element name="contactable">
-                <attribute name="value">
-                    <ref name="booleanValue"/>
-                </attribute>
-                <empty/>
-            </element>
-            <element name="date_created">
-                <attribute name="value">
-                    <data type="dateTime"/>
-                </attribute>
-                <empty/>
-            </element>
-            <element name="client">
-                <attribute name="name">
-                    <text/>
-                </attribute>
-                <attribute name="version">
-                    <text/>
-                </attribute>
-                <empty/>
-                <zeroOrMore>
-                    <element name="plugin">
-                        <attribute name="name">
-                            <text/>
-                        </attribute>
-                        <attribute name="version">
-                            <text/>
-                        </attribute>
-                    </element>
-                </zeroOrMore>
-            </element>
-            <optional>
-                <element name="kernel-release">
-                    <attribute name="value">
-                        <text/>
-                    </attribute>
-                </element>
-            </optional>
-        </interleave>
-    </define>
-
-    <define name="hardwareSection">
-        <interleave>
-            <choice>
-                <element name="hal">
-                    <attribute name="version">
-                        <text/>
-                    </attribute>
-                    <oneOrMore>
-                        <element name="device">
-                            <attribute name="id">
-                                <data type="integer">
-                                    <except>
-                                        <value/>
-                                    </except>
-                                </data>
-                            </attribute>
-                            <attribute name="udi">
-                                <text/>
-                            </attribute>
-                            <optional>
-                                <attribute name="parent">
-                                   <data type="integer"/>
-                                </attribute>
-                            </optional>
-                            <!-- XXX: Abel Deuring 2007-12-07:
-                                 specify a set of required properties? -->
-                            <oneOrMore>
-                                <ref name="property"/>
-                            </oneOrMore>
-                        </element>
-                    </oneOrMore>
-                </element>
-                <group>
-                    <interleave>
-                        <element name="udev">
-                            <text/>
-                        </element>
-                        <element name="dmi">
-                            <text/>
-                        </element>
-                        <optional>
-                            <!-- Unfortunately, the checkbox version in
-                                 Maverick and Natty do not provide sysfs
-                                 data.
-                            -->
-                            <element name="sysfs-attributes">
-                                <zeroOrMore>
-                                    <text/>
-                                </zeroOrMore>
-                            </element>
-                        </optional>
-                    </interleave>
-                </group>
-            </choice>
-            <element name="processors">
-                <oneOrMore>
-                    <element name="processor">
-                        <attribute name="id">
-                            <data type="integer">
-                                <except>
-                                    <value/>
-                                </except>
-                            </data>
-                        </attribute>
-                        <attribute name="name">
-                            <text/>
-                        </attribute>
-                         <oneOrMore>
-                             <ref name="property"/>
-                         </oneOrMore>
-                    </element>
-                </oneOrMore>
-            </element>
-            <optional>
-                <element name="aliases">
-                    <zeroOrMore>
-                        <element name="alias">
-                            <attribute name="target">
-                                <text/>
-                            </attribute>
-                            <interleave>
-                                <element name="vendor">
-                                    <text/>
-                                </element>
-                                <element name="model">
-                                    <text/>
-                                </element>
-                            </interleave>
-                        </element>
-                    </zeroOrMore>
-                </element>
-            </optional>
-        </interleave>
-    </define>
-
-    <define name="softwareSection">
-        <interleave>
-            <element name="lsbrelease">
-                <!-- XXX: Abel Deuring 2007-12-07:
-                     specify a more restrictive set of allowed
-                     and/or required properties?
-                -->
-                <oneOrMore>
-                    <ref name="property"/>
-                </oneOrMore>
-            </element>
-            <optional>
-                <element name="packages">
-                    <zeroOrMore>
-                        <element name="package">
-                            <attribute name="name"/>
-                            <attribute name="id">
-                                <data type="integer">
-                                    <except>
-                                        <value/>
-                                    </except>
-                                </data>
-                            </attribute>
-                            <oneOrMore>
-                                <ref name="property"/>
-                            </oneOrMore>
-                        </element>
-                    </zeroOrMore>
-                </element>
-            </optional>
-            <optional>
-                <element name="xorg">
-                    <attribute name="version">
-                        <text/>
-                    </attribute>
-                    <zeroOrMore>
-                        <element name="driver">
-                            <optional>
-                                <attribute name="device">
-                                    <data type="integer"/>
-                                </attribute>
-                            </optional>
-                            <attribute name="name">
-                                <text/>
-                            </attribute>
-                            <optional>
-                                <attribute name="version">
-                                    <text/>
-                                </attribute>
-                            </optional>
-                            <attribute name="class">
-                                <text/>
-                            </attribute>
-                        </element>
-                    </zeroOrMore>
-                </element>
-            </optional>
-        </interleave>
-    </define>
-
-    <define name="questionsSection">
-        <zeroOrMore>
-            <element name="question">
-                <attribute name="name">
-                    <text/>
-                </attribute>
-                <a:comment>
-                    The attribute "plugin" must be set, if the question
-                    is generated by a plugin.
-                </a:comment>
-                <optional>
-                    <attribute name="plugin">
-                        <text/>
-                    </attribute>
-                </optional>
-                <interleave>
-                    <optional>
-                        <element name="command">
-                            <text/>
-                        </element>
-                    </optional>
-                    <choice>
-                        <interleave>
-                            <element name="answer">
-                                <attribute name="type">
-                                    <value>multiple_choice</value>
-                                </attribute>
-                                <optional>
-                                    <attribute name="unit">
-                                        <text/>
-                                    </attribute>
-                                </optional>
-                                <text/>
-                            </element>
-                            <element name="answer_choices">
-                                <oneOrMore>
-                                    <ref name="value"/>
-                                </oneOrMore>
-                            </element>
-                        </interleave>
-                        <element name="answer">
-                            <attribute name="type">
-                                <value>measurement</value>
-                            </attribute>
-                            <optional>
-                                <attribute name="unit">
-                                    <text/>
-                                </attribute>
-                            </optional>
-                            <data type="decimal"/>
-                        </element>
-                    </choice>
-                    <zeroOrMore>
-                        <element name="target">
-                            <attribute name="id">
-                                <text/>
-                            </attribute>
-                            <interleave>
-                                <zeroOrMore>
-                                    <element name="driver">
-                                        <text/>
-                                    </element>
-                                </zeroOrMore>
-                            </interleave>
-                        </element>
-                    </zeroOrMore>
-                    <optional>
-                        <element name="comment">
-                            <text/>
-                        </element>
-                    </optional>
-                </interleave>
-            </element>
-        </zeroOrMore>
-    </define>
-
-    <define name="contextSection">
-        <zeroOrMore>
-            <element name="info">
-                <attribute name="command">
-                    <text/>
-                </attribute>
-                <text/>
-            </element>
-        </zeroOrMore>
-    </define>
-
-    <a:comment>
-        convenience for Python code: 'True'/'False' for boolean values
-        instead of 'true'/'false' as defined by
-        http://www.w3.org/2001/XMLSchema-datatypes .
-    </a:comment>
-    <define name="booleanValue">
-        <choice>
-            <value>True</value>
-            <value>False</value>
-        </choice>
-    </define>
-
-    <define name="propertyAndValueContent">
-        <a:comment>
-            Allowed types and values:
-            The dbus... data types are used for HAL properties; the data 
-            types are specified in 
-            http://dbus.freedesktop.org/doc/dbus-specification.html
-            The other data types are Python data types, defined in
-            http://docs.python.org/lib/types.html
-        </a:comment>
-        <choice>
-            <group>
-                <attribute name="type">
-                    <choice>
-                        <value>dbus.Boolean</value>
-                        <value>bool</value>
-                    </choice>
-                </attribute>
-                <ref name="booleanValue"/>
-            </group>
-            <group>
-                <attribute name="type">
-                    <choice>
-                        <value>dbus.String</value>
-                        <value>dbus.UTF8String</value>
-                        <value>str</value>
-                    </choice>
-                </attribute>
-                <text/>
-            </group>
-            <group>
-                <attribute name="type">
-                    <value>dbus.Byte</value>
-                </attribute>
-                <data type="unsignedByte">
-                    <except>
-                        <value/>
-                    </except>
-                </data>
-            </group>
-            <group>
-                <attribute name="type">
-                    <value>dbus.Int16</value>
-                </attribute>
-                <data type="short">
-                    <except>
-                        <value/>
-                    </except>
-                </data>
-            </group>
-            <group>
-                <attribute name="type">
-                    <value>dbus.Int32</value>
-                </attribute>
-                <data type="int">
-                    <except>
-                        <value/>
-                    </except>
-                </data>
-            </group>
-            <group>
-                <attribute name="type">
-                    <value>dbus.Int64</value>
-                </attribute>
-                <data type="long">
-                    <except>
-                        <value/>
-                    </except>
-                </data>
-            </group>
-            <group>
-                <attribute name="type">
-                    <value>dbus.UInt16</value>
-                </attribute>
-                <data type="unsignedShort">
-                    <except>
-                        <value/>
-                    </except>
-                </data>
-            </group>
-            <group>
-                <attribute name="type">
-                    <value>dbus.UInt32</value>
-                </attribute>
-                <data type="unsignedInt">
-                    <except>
-                        <value/>
-                    </except>
-                </data>
-            </group>
-            <group>
-                <attribute name="type">
-                    <value>dbus.UInt64</value>
-                </attribute>
-                <data type="unsignedLong">
-                    <except>
-                        <value/>
-                    </except>
-                </data>
-            </group>
-            <group>
-                <attribute name="type">
-                    <value>int</value>
-                </attribute>
-                <data type="long">
-                    <except>
-                        <value/>
-                    </except>
-                </data>
-            </group>
-            <group>
-                <attribute name="type">
-                    <value>long</value>
-                </attribute>
-                <data type="integer">
-                    <except>
-                        <value/>
-                    </except>
-                </data>
-            </group>
-            <group>
-                <attribute name="type">
-                    <choice>
-                        <value>dbus.Double</value>
-                        <value>float</value>
-                    </choice>
-                </attribute>
-                <data type="decimal"/>
-            </group>
-            <group>
-                <attribute name="type">
-                    <choice>
-                        <value>dbus.Array</value>
-                        <value>list</value>
-                    </choice>
-                </attribute>
-                <zeroOrMore>
-                    <element name="value">
-                        <ref name="propertyAndValueContent"/>
-                    </element>
-                </zeroOrMore>
-            </group>
-            <group>
-                <attribute name="type">
-                    <choice>
-                        <value>dbus.Dictionary</value>
-                        <value>dict</value>
-                    </choice>
-                </attribute>
-                <zeroOrMore>
-                    <element name="value">
-                        <attribute name="name">
-                            <text/>
-                        </attribute>
-                        <ref name="propertyAndValueContent"/>
-                    </element>
-                </zeroOrMore>
-            </group>
-         </choice>
-     </define>
-
-    <define name="property">
-        <element name="property">
-            <attribute name="name">
-                <text/>
-            </attribute>
-            <ref name="propertyAndValueContent"/>
-       </element>
-    </define>
-
-    <define name="value">
-        <element name="value">
-            <ref name="propertyAndValueContent"/>
-        </element>
-    </define>
-</grammar>
diff --git a/lib/lp/hardwaredb/scripts/hwdbsubmissions.py b/lib/lp/hardwaredb/scripts/hwdbsubmissions.py
deleted file mode 100644
index 07ff095..0000000
--- a/lib/lp/hardwaredb/scripts/hwdbsubmissions.py
+++ /dev/null
@@ -1,2952 +0,0 @@
-# Copyright 2009-2019 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Parse Hardware Database submissions.
-
-Base classes, intended to be used both for the commercial certification
-data and for the community test submissions.
-"""
-
-__metaclass__ = type
-__all__ = [
-           'SubmissionParser',
-          ]
-
-import bz2
-from datetime import (
-    datetime,
-    timedelta,
-    )
-import io
-from logging import getLogger
-import os
-import re
-
-import defusedxml.cElementTree as etree
-import pytz
-from zope.component import getUtility
-
-from lp.hardwaredb.interfaces.hwdb import (
-    HWBus,
-    IHWDeviceDriverLinkSet,
-    IHWDeviceSet,
-    IHWDriverSet,
-    IHWSubmissionDeviceSet,
-    IHWVendorIDSet,
-    IHWVendorNameSet,
-    )
-from lp.services.config import config
-from lp.services.propertycache import cachedproperty
-from lp.services.scripts.base import disable_oops_handler
-from lp.services.xml import RelaxNGValidator
-
-
-_relax_ng_files = {
-    '1.0': 'hardware-1_0.rng', }
-
-_time_regex = re.compile(r"""
-    ^(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d)
-    T(?P<hour>\d\d):(?P<minute>\d\d):(?P<second>\d\d)
-    (?:\.(?P<second_fraction>\d{0,6}))?
-    (?P<tz>
-        (?:(?P<tz_sign>[-+])(?P<tz_hour>\d\d):(?P<tz_minute>\d\d))
-        | Z)?$
-    """,
-    re.VERBOSE)
-
-_broken_comment_nodes_re = re.compile(br'(<comment>.*?</comment>)', re.DOTALL)
-_missing_udev_node_data = re.compile(
-    br'<info command="udevadm info --export-db">(.*?)</info>', re.DOTALL)
-_missing_dmi_node_data = re.compile(
-    br'<info command="grep -r \. /sys/class/dmi/id/ 2&gt;/dev/null">(.*?)'
-    br'</info>', re.DOTALL)
-_udev_node_exists = re.compile(
-    br'<hardware>.*?<udev>.*?</hardware>', re.DOTALL)
-_dmi_node_exists = re.compile(br'<hardware>.*?<dmi>.*?</hardware>', re.DOTALL)
-
-ROOT_UDI = '/org/freedesktop/Hal/devices/computer'
-UDEV_ROOT_PATH = '/devices/LNXSYSTM:00'
-
-# These UDIs appears in some submissions more than once.
-KNOWN_DUPLICATE_UDIS = set((
-    '/org/freedesktop/Hal/devices/ssb__null_',
-    '/org/freedesktop/Hal/devices/uinput',
-    '/org/freedesktop/Hal/devices/ignored-device',
-    ))
-
-# See include/linux/pci_ids.h in the Linux kernel sources for a complete
-# list of PCI class and subclass codes.
-PCI_CLASS_STORAGE = 1
-PCI_SUBCLASS_STORAGE_SATA = 6
-
-PCI_CLASS_BRIDGE = 6
-PCI_SUBCLASS_BRIDGE_PCI = 4
-PCI_SUBCLASS_BRIDGE_CARDBUS = 7
-
-PCI_CLASS_SERIALBUS_CONTROLLER = 12
-PCI_SUBCLASS_SERIALBUS_USB = 3
-
-DB_FORMAT_FOR_VENDOR_ID = {
-    'pci': '0x%04x',
-    'usb_device': '0x%04x',
-    'scsi': '%-8s',
-    'scsi_device': '%-8s',
-    }
-
-DB_FORMAT_FOR_PRODUCT_ID = {
-    'pci': '0x%04x',
-    'usb_device': '0x%04x',
-    'scsi': '%-16s',
-    'scsi_device': '%-16s',
-    }
-
-UDEV_USB_DEVICE_PROPERTIES = set(('DEVTYPE', 'PRODUCT', 'TYPE'))
-UDEV_USB_PRODUCT_RE = re.compile(
-    '^[0-9a-f]{1,4}/[0-9a-f]{1,4}/[0-9a-f]{1,4}$', re.I)
-UDEV_USB_TYPE_RE = re.compile('^[0-9]{1,3}/[0-9]{1,3}/[0-9]{1,3}$')
-SYSFS_SCSI_DEVICE_ATTRIBUTES = set(('vendor', 'model', 'type'))
-
-
-class SubmissionParser(object):
-    """A Parser for the submissions to the hardware database."""
-
-    def __init__(self, logger=None, record_warnings=True):
-        if logger is None:
-            logger = getLogger()
-        self.logger = logger
-        self._logged_warnings = set()
-
-        self.validator = {}
-        directory = os.path.join(
-            config.root, 'lib', 'lp', 'hardwaredb', 'scripts')
-        for version, relax_ng_filename in _relax_ng_files.items():
-            path = os.path.join(directory, relax_ng_filename)
-            self.validator[version] = RelaxNGValidator(path)
-        self._setMainSectionParsers()
-        self._setHardwareSectionParsers()
-        self._setSoftwareSectionParsers()
-        self.record_warnings = record_warnings
-
-    def _logError(self, message, submission_key, create_oops=True):
-        """Log `message` for an error in submission submission_key`."""
-        msg = 'Parsing submission %s: %s' % (submission_key, message)
-        if not create_oops:
-            with disable_oops_handler(self.logger):
-                self.logger.error(msg)
-        else:
-            self.logger.error(msg)
-
-    def _logWarning(self, message, warning_id=None):
-        """Log `message` for a warning in submission submission_key`."""
-        if not self.record_warnings:
-            return
-        if warning_id is None:
-            issue_warning = True
-        elif warning_id not in self._logged_warnings:
-            issue_warning = True
-            self._logged_warnings.add(warning_id)
-        else:
-            issue_warning = False
-        if issue_warning:
-            self.logger.warning(
-                'Parsing submission %s: %s' % (self.submission_key, message))
-
-    def fixFrequentErrors(self, submission):
-        """Fixes for frequent formal errors in the submissions.
-        """
-        # A considerable number of reports for Lucid has ESC characters
-        # in comment nodes. We don't need the comment nodes at all, so
-        # we can simply empty them.
-        submission = _broken_comment_nodes_re.sub(b'<comment/>', submission)
-
-        # Submissions from Natty don't have the nodes <dmi> and <udev>
-        # as children of the <hardware> node. Fortunately, they provide
-        # this data in
-        #
-        #    <context>
-        #        <info command="grep -r . /sys/class/dmi/id/ 2&gt;/dev/null">
-        #        ...
-        #        </info>
-        #        <info command="udevadm info --export-db">
-        #        ...
-        #        </info>
-        #    </context>
-        #
-        # We can try to find the two relevant <info> nodes inside <context>
-        # and move their content into the proper subnodes of <hardware>.
-        if _udev_node_exists.search(submission) is None:
-            mo = _missing_udev_node_data.search(submission)
-            if mo is not None:
-                missing_data = mo.group(1)
-                missing_data = b'<udev>%s</udev>\n</hardware>' % missing_data
-                submission = submission.replace(b'</hardware>', missing_data)
-        if _dmi_node_exists.search(submission) is None:
-            mo = _missing_dmi_node_data.search(submission)
-            if mo is not None:
-                missing_data = mo.group(1)
-                missing_data = b'<dmi>%s</dmi>\n</hardware>' % missing_data
-                submission = submission.replace(b'</hardware>', missing_data)
-        return submission
-
-    def _getValidatedEtree(self, submission, submission_key):
-        """Create an etree doc from the XML string submission and validate it.
-
-        :return: an `lxml.etree` instance representation of a valid
-            submission or None for invalid submissions.
-        """
-        submission = self.fixFrequentErrors(submission)
-        try:
-            tree = etree.parse(io.BytesIO(submission), forbid_dtd=True)
-        except SyntaxError as error_value:
-            self._logError(error_value, submission_key)
-            return None
-
-        submission_doc = tree.getroot()
-        if submission_doc.tag != 'system':
-            self._logError("root node is not '<system>'", submission_key)
-            return None
-        version = submission_doc.attrib.get('version', None)
-        if not version in self.validator.keys():
-            self._logError(
-                'invalid submission format version: %s' % repr(version),
-                submission_key)
-            return None
-
-        validator = self.validator[version]
-        if not validator.validate(submission):
-            self._logError(
-                'Relax NG validation failed.\n%s' % validator.error_log,
-                submission_key,
-                create_oops=False)
-            return None
-        return submission_doc
-
-    def _getValueAttributeAsBoolean(self, node):
-        """Return the value of the attribute "value" as a boolean."""
-        value = node.attrib['value']
-        # Paranoia check: The Relax NG validation already ensures that the
-        # attribute value is either 'True' or 'False'.
-        assert value in ('True', 'False'), (
-            'Parsing submission %s: Boolean value for attribute "value" '
-            'expected in tag <%s>' % (self.submission_key, node.tag))
-        return value == 'True'
-
-    def _getValueAttributeAsString(self, node):
-        """Return the value of the attribute "value"."""
-        # The Relax NG validation ensures that the attribute exists.
-        return node.attrib['value']
-
-    def _getValueAttributeAsDateTime(self, time_node):
-        """Convert a "value" attribute into a datetime object."""
-        time_text = time_node.get('value')
-
-        # we cannot use time.strptime: this function accepts neither fractions
-        # of a second nor a time zone given e.g. as '+02:30'.
-        mo = _time_regex.search(time_text)
-
-        # The Relax NG schema allows a leading minus sign and year numbers
-        # with more than four digits, which are not "covered" by _time_regex.
-        if mo is None:
-            raise ValueError(
-                'Timestamp with unreasonable value: %s' % time_text)
-
-        time_parts = mo.groupdict()
-
-        year = int(time_parts['year'])
-        month = int(time_parts['month'])
-        day = int(time_parts['day'])
-        hour = int(time_parts['hour'])
-        minute = int(time_parts['minute'])
-        second = int(time_parts['second'])
-        second_fraction = time_parts['second_fraction']
-        if second_fraction is not None:
-            milliseconds = second_fraction + '0' * (6 - len(second_fraction))
-            milliseconds = int(milliseconds)
-        else:
-            milliseconds = 0
-
-        # The Relax NG validator accepts leap seconds, but the datetime
-        # constructor rejects them. The time values submitted by the HWDB
-        # client are not necessarily very precise, hence we can round down
-        # to 59.999999 seconds without losing any real precision.
-        if second > 59:
-            second = 59
-            milliseconds = 999999
-
-        timestamp = datetime(year, month, day, hour, minute, second,
-                             milliseconds, tzinfo=pytz.timezone('utc'))
-
-        tz_sign = time_parts['tz_sign']
-        tz_hour = time_parts['tz_hour']
-        tz_minute = time_parts['tz_minute']
-        if tz_sign in ('-', '+'):
-            delta = timedelta(hours=int(tz_hour), minutes=int(tz_minute))
-            if tz_sign == '-':
-                timestamp = timestamp + delta
-            else:
-                timestamp = timestamp - delta
-        return timestamp
-
-    def _getClientData(self, client_node):
-        """Parse the <client> node in the <summary> section.
-
-        :return: A dictionary with keys 'name', 'version', 'plugins'.
-                 Name and version describe the client program that
-                 produced the submission. Pugins is a list with one
-                 entry per client plugin; each entry is dictionary with
-                 the keys 'name' and 'version'.
-        """
-        result = {'name': client_node.get('name'),
-                  'version': client_node.get('version')}
-        plugins = result['plugins'] = []
-        for node in client_node.getchildren():
-            # Ensured by the Relax NG validation: The only allowed sub-tag
-            # of <client> is <plugin>, which has the attributes 'name' and
-            # 'version'.
-            plugins.append({'name': node.get('name'),
-                            'version': node.get('version')})
-        return result
-
-    _parse_summary_section = {
-        'live_cd': _getValueAttributeAsBoolean,
-        'system_id': _getValueAttributeAsString,
-        'distribution': _getValueAttributeAsString,
-        'distroseries': _getValueAttributeAsString,
-        'architecture': _getValueAttributeAsString,
-        'private': _getValueAttributeAsBoolean,
-        'contactable': _getValueAttributeAsBoolean,
-        'date_created': _getValueAttributeAsDateTime,
-        'client': _getClientData,
-        'kernel-release': _getValueAttributeAsString,
-        }
-
-    def _parseSummary(self, summary_node):
-        """Parse the <summary> part of a submission.
-
-        :return: A dictionary with the keys 'live_cd', 'system_id',
-                 'distribution', 'distroseries', 'architecture',
-                 'private', 'contactable', 'date_created', 'client'.
-                 See the sample XML file tests/hardwaretest.xml for
-                 detailed description of the values.
-        """
-        summary = {}
-        # The Relax NG validation ensures that we have exactly those
-        # sub-nodes that are listed in _parse_summary_section.
-        for node in summary_node.getchildren():
-            parser = self._parse_summary_section[node.tag]
-            summary[node.tag] = parser(self, node)
-        return summary
-
-    def _getValueAndType(self, node):
-        """Return (value, type) of a <property> or <value> node."""
-        type_ = node.get('type')
-        if type_ in ('dbus.Boolean', 'bool'):
-            value = node.text.strip()
-            # Pure paranoia: The Relax NG validation ensures that <property>
-            # and <value> tags have only the allowed values.
-            assert value in ('True', 'False'), (
-                'Parsing submission %s: Invalid bool value for <property> or '
-                    '<value>: %s' % (self.submission_key, repr(value)))
-            return (value == 'True', type_)
-        elif type_ in ('str', 'dbus.String', 'dbus.UTF8String'):
-            return (node.text.strip(), type_)
-        elif type_ in ('dbus.Byte', 'dbus.Int16', 'dbus.Int32', 'dbus.Int64',
-                       'dbus.UInt16', 'dbus.UInt32', 'dbus.UInt64', 'int',
-                       'long'):
-            value = node.text.strip()
-            return (int(value), type_)
-        elif type_ in ('dbus.Double', 'float'):
-            value = node.text.strip()
-            return (float(value), type_)
-        elif type_ in ('dbus.Array', 'list'):
-            value = []
-            for sub_node in node.getchildren():
-                value.append(self._getValueAndType(sub_node))
-            return (value, type_)
-        elif type_ in ('dbus.Dictionary', 'dict'):
-            value = {}
-            for sub_node in node.getchildren():
-                value[sub_node.get('name')] = self._getValueAndType(sub_node)
-            return (value, type_)
-        else:
-            # This should not happen: The Relax NG validation ensures
-            # that we have only those values for type_ that appear in
-            # the if/elif expressions above.
-            raise AssertionError(
-                'Parsing submission %s: Unexpected <property> or <value> '
-                    'type: %s' % (self.submission_key, type_))
-
-    def _parseProperty(self, property_node):
-        """Parse a <property> node.
-
-        :return: (name, (value, type)) of a property.
-        """
-        return (property_node.get('name'),
-                self._getValueAndType(property_node))
-
-    def _parseProperties(self, properties_node):
-        """Parse <property> sub-nodes of properties_node.
-
-        :return: A dictionary, where each key is the name of a property;
-                 the values are the tuples (value, type) of a property.
-        """
-        properties = {}
-        for property_node in properties_node.getchildren():
-            # Paranoia check: The Relax NG schema ensures that a node
-            # with <property> sub-nodes has no other sub-nodes
-            assert property_node.tag == 'property', (
-            'Parsing submission %s: Found <%s> node, expected <property>'
-                % (self.submission_key, property_node.tag))
-            property_name, property_value = self._parseProperty(property_node)
-            if property_name in properties.keys():
-                raise ValueError(
-                    '<property name="%s"> found more than once in <%s>'
-                    % (property_name, properties_node.tag))
-            properties[property_name] = property_value
-        return properties
-
-    def _parseDevice(self, device_node):
-        """Parse a HAL <device> node.
-
-        :return: A dictionary d with the keys 'id', 'udi', 'parent',
-                 'properties'. d['id'] is an ID of the device d['udi']
-                 is the HAL UDI of the device; d['properties'] is a
-                 dictionary with the properties of the device (see
-                 _parseProperties for details).
-        """
-        # The Relax NG validation ensures that the attributes "id" and
-        # "udi" exist; it also ensures that "id" contains an integer.
-        device_data = {'id': int(device_node.get('id')),
-                       'udi': device_node.get('udi')}
-        parent = device_node.get('parent', None)
-        if parent is not None:
-            parent = int(parent.strip())
-        device_data['parent'] = parent
-        device_data['properties'] = self._parseProperties(device_node)
-        return device_data
-
-    def _parseHAL(self, hal_node):
-        """Parse the <hal> section of a submission.
-
-        :return: A list, where each entry is the result of a _parseDevice
-                 call.
-        """
-        # The Relax NG validation ensures that <hal> has the attribute
-        # "version"
-        hal_data = {'version': hal_node.get('version')}
-        hal_data['devices'] = devices = []
-        for device_node in hal_node.getchildren():
-            # Pure paranoia: The Relax NG validation ensures already
-            # that we have only <device> tags within <hal>
-            assert device_node.tag == 'device', (
-                'Parsing submission %s: Unexpected tag <%s> in <hal>'
-                % (self.submission_key, device_node.tag))
-            devices.append(self._parseDevice(device_node))
-        return hal_data
-
-    def _parseProcessors(self, processors_node):
-        """Parse the <processors> node.
-
-        :return: A list of dictionaries, where each dictionary d contains
-                 the data of a <processor> node. The dictionary keys are
-                 'id', 'name', 'properties'. d['id'] is an ID of a
-                 <processor> node, d['name'] its name, and d['properties']
-                 contains the properties of a processor (see
-                 _parseProperties for details).
-        """
-        result = []
-        for processor_node in processors_node.getchildren():
-            # Pure paranoia: The Relax NG valiation ensures already
-            # the we have only <processor> as sub-tags of <processors>.
-            assert processor_node.tag == 'processor', (
-                'Parsing submission %s: Unexpected tag <%s> in <processors>'
-                   % (self.submission_key, processors_node.tag))
-            processor = {}
-            # The RelaxNG validation ensures that the attribute "id" exists
-            # and that it contains an integer.
-            processor['id'] = int(processor_node.get('id'))
-            processor['name'] = processor_node.get('name')
-            processor['properties'] = self._parseProperties(processor_node)
-            result.append(processor)
-        return result
-
-    def _parseAliases(self, aliases_node):
-        """Parse the <aliases> node.
-
-        :return: A list of dictionaries, where each dictionary d has the
-                 keys 'id', 'vendor', 'model'. d['id'] is the ID of a
-                 HAL device; d['vendor'] is an alternative vendor name of
-                 the device; d['model'] is an alternative model name.
-
-                 See tests/hardwaretest.xml more more details.
-        """
-        aliases = []
-        for alias_node in aliases_node.getchildren():
-            # Pure paranoia: The Relax NG valiation ensures already
-            # the we have only <alias> tags within <aliases>
-            assert alias_node.tag == 'alias', (
-                'Parsing submission %s: Unexpected tag <%s> in <aliases>'
-                    % (self.submission_key, alias_node.tag))
-            # The RelaxNG validation ensures that the attribute "target"
-            # exists and that it contains an integer.
-            alias = {'target': int(alias_node.get('target'))}
-            for sub_node in alias_node.getchildren():
-                # The Relax NG svalidation ensures that we have exactly
-                # two subnodes: <vendor> and <model>
-                alias[sub_node.tag] = sub_node.text.strip()
-            aliases.append(alias)
-        return aliases
-
-    def _parseUdev(self, udev_node):
-        """Parse the <udev> node.
-
-        :return: A list of dictionaries, where each dictionary
-            describes a udev device.
-
-        The <udev> node contains the output produced by
-        "udevadm info --export-db". Each entry of the dictionaries
-        represents the data of the key:value pairs as they appear
-        in this data. The value of d['S'] is a list of strings,
-        the value s['E'] is a dictionary containing the key=value
-        pairs of the "E:" lines.
-        """
-        # We get the plain text as produced by "udevadm info --export-db"
-        # This data looks like:
-        #
-        # P: /devices/LNXSYSTM:00
-        # E: UDEV_LOG=3
-        # E: DEVPATH=/devices/LNXSYSTM:00
-        # E: MODALIAS=acpi:LNXSYSTM:
-        #
-        # P: /devices/LNXSYSTM:00/ACPI_CPU:00
-        # E: UDEV_LOG=3
-        # E: DEVPATH=/devices/LNXSYSTM:00/ACPI_CPU:00
-        # E: DRIVER=processor
-        # E: MODALIAS=acpi:ACPI_CPU:
-        #
-        # Data for different devices is separated by empty lines.
-        # Each line for a device consists of key:value pairs.
-        # The following keys are defined:
-        #
-        # A: udev_device_get_num_fake_partitions()
-        # E: udev_device_get_properties_list_entry()
-        # L: the device link priority (udev_device_get_devlink_priority())
-        # N: the device node file name (udev_device_get_devnode())
-        # P: the device path (udev_device_get_devpath())
-        # R: udev_device_get_ignore_remove()
-        # S: udev_get_dev_path()
-        # W: udev_device_get_watch_handle()
-        #
-        # The key P is always present; the keys A, L, N, R, W appear at
-        # most once per device; the keys E and S may appear more than
-        # once.
-        # The values of the E records have the format "key=value"
-        #
-        # See also the libudev reference manual:
-        # http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/
-        # and the udev file udevadm-info.c, function print_record()
-
-        udev_data = udev_node.text.split('\n')
-        devices = []
-        device = None
-        line_number = 0
-        device_id = 0
-
-        for line_number, line in enumerate(udev_data):
-            if len(line) == 0:
-                device = None
-                continue
-            record = line.split(':', 1)
-            if len(record) != 2:
-                self._logError(
-                    'Line %i in <udev>: No valid key:value data: %r'
-                    % (line_number, line),
-                    self.submission_key)
-                return None
-
-            key, value = record
-            if device is None:
-                device_id += 1
-                device = {
-                    'E': {},
-                    'S': [],
-                    'id': device_id,
-                    }
-                devices.append(device)
-            # Some attribute lines have a space character after the
-            # ':', others don't have it (see udevadm-info.c).
-            value = value.lstrip()
-
-            if key == 'E':
-                property_data = value.split('=', 1)
-                if len(property_data) != 2:
-                    self._logError(
-                        'Line %i in <udev>: Property without valid key=value '
-                        'data: %r' % (line_number, line),
-                        self.submission_key)
-                    return None
-                property_key, property_value = property_data
-                device['E'][property_key] = property_value
-            elif key == 'S':
-                device['S'].append(value)
-            else:
-                if key in device:
-                    self._logWarning(
-                        'Line %i in <udev>: Duplicate attribute key: %r'
-                        % (line_number, line),
-                        self.submission_key)
-                device[key] = value
-        return devices
-
-    def _parseDmi(self, dmi_node):
-        """Parse the <dmi> node.
-
-        :return: A dictionary containing the key:value pairs of the DMI data.
-        """
-        dmi_data = {}
-        dmi_text = dmi_node.text.strip().split('\n')
-        for line_number, line in enumerate(dmi_text):
-            record = line.split(':', 1)
-            if len(record) != 2:
-                self._logError(
-                    'Line %i in <dmi>: No valid key:value data: %r'
-                    % (line_number, line),
-                    self.submission_key)
-                return None
-            dmi_data[record[0]] = record[1]
-        return dmi_data
-
-    def _parseSysfsAttributes(self, sysfs_node):
-        """Parse the <sysfs-attributes> node.
-
-        :return: A dictionary {path: attrs, ...} where path is the
-            path is the path of a sysfs directory, and where attrs
-            is a dictionary containing attribute names and values.
-
-        A sample of the input data:
-
-        P: /devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
-        A: modalias=input:b0019v0000p0001e0000-e0,1,k74,ramlsfw
-        A: uniq=
-        A: phys=LNXPWRBN/button/input0
-        A: name=Power Button
-
-        P: /devices/LNXSYSTM:00/device:00/PNP0A08:00/device:03/input/input8
-        A: modalias=input:b0019v0000p0006e0000-e0,1,kE0,E1,E3,F0,F1
-        A: uniq=
-        A: phys=/video/input0
-        A: name=Video Bus
-
-        Data for different devices is separated by empty lines. The data
-        for each device starts with a line 'P: /devices/LNXSYSTM...',
-        specifying the sysfs path of a device, followed by zero or more
-        lines of the form 'A: key=value'
-        """
-        sysfs_lines = sysfs_node.text.split('\n')
-        sysfs_data = {}
-        attributes = None
-
-        for line_number, line in enumerate(sysfs_lines):
-            if len(line) == 0:
-                attributes = None
-                continue
-            record = line.split(': ', 1)
-            if len(record) != 2:
-                self._logError(
-                    'Line %i in <sysfs-attributes>: No valid key:value data: '
-                    '%r' % (line_number, line),
-                    self.submission_key)
-                return None
-
-            key, value = record
-            if key == 'P':
-                if attributes is not None:
-                    self._logError(
-                        "Line %i in <sysfs-attributes>: duplicate 'P' line "
-                        "found: %r" % (line_number, line),
-                        self.submission_key)
-                    return None
-                attributes = {}
-                sysfs_data[value] = attributes
-            elif key == 'A':
-                if attributes is None:
-                    self._logError(
-                        "Line %i in <sysfs-attributes>: Block for a device "
-                        "does not start with 'P:': %r" % (line_number, line),
-                        self.submission_key)
-                    return None
-                attribute_data = value.split('=', 1)
-                if len(attribute_data) != 2:
-                    self._logError(
-                        'Line %i in <sysfs-attributes>: Attribute line does '
-                        'not contain key=value data: %r'
-                        % (line_number, line),
-                        self.submission_key)
-                    return None
-                attributes[attribute_data[0]] = attribute_data[1]
-            else:
-                self._logError(
-                    'Line %i in <sysfs-attributes>: Unexpected key: %r'
-                    % (line_number, line),
-                    self.submission_key)
-                return None
-        return sysfs_data
-
-    def _setHardwareSectionParsers(self):
-        self._parse_hardware_section = {
-            'hal': self._parseHAL,
-            'processors': self._parseProcessors,
-            'aliases': self._parseAliases,
-            'udev': self._parseUdev,
-            'dmi': self._parseDmi,
-            'sysfs-attributes': self._parseSysfsAttributes,
-            }
-
-    def _parseHardware(self, hardware_node):
-        """Parse the <hardware> part of a submission.
-
-        :return: A dictionary with the keys 'hal', 'processors', 'aliases',
-                 where the values are the parsing results of _parseHAL,
-                 _parseProcessors, _parseAliases.
-        """
-        # Submissions from checkbox for Lucid, Maverick and Natty
-        # unfortunately do not contain a <sysfs-attributes> node.
-        # A default value here allows us to mark these submissions.
-        # See also bug 835103.
-        hardware_data = {
-            'sysfs-attributes': None,
-            }
-        for node in hardware_node.getchildren():
-            parser = self._parse_hardware_section[node.tag]
-            result = parser(node)
-            if result is None:
-                return None
-            hardware_data[node.tag] = result
-        return hardware_data
-
-    def _parseLSBRelease(self, lsb_node):
-        """Parse the <lsb_release> part of a submission.
-
-        :return: A dictionary with the content of the <properta> nodes
-                 within the <lsb> node. See tests/hardwaretest.xml for
-                 details.
-        """
-        return self._parseProperties(lsb_node)
-
-    def _parsePackages(self, packages_node):
-        """Parse the <packages> part of a submission.
-
-        :return: A dictionary with one entry per <package> sub-node.
-                 The key is the package name, the value a dictionary
-                 containing the content of the <property> nodes within
-                 <package>. See tests/hardwaretest.xml for more details.
-        """
-        packages = {}
-        for package_node in packages_node.getchildren():
-            # Pure paranoia: The Relax NG validation ensures already
-            # that we have only <package> tags within <packages>.
-            assert package_node.tag == 'package', (
-                'Parsing submission %s: Unexpected tag <%s> in <packages>'
-                % (self.submission_key, package_node.tag))
-            package_name = package_node.get('name')
-            if package_name in packages.keys():
-                raise ValueError(
-                    '<package name="%s"> appears more than once in <packages>'
-                    % package_name)
-            # The RelaxNG validation ensures that the attribute "id" exists
-            # and that it contains an integer.
-            package_data = {'id': int(package_node.get('id'))}
-            package_data['properties'] = self._parseProperties(package_node)
-            packages[package_name] = package_data
-        return packages
-
-    def _parseXOrg(self, xorg_node):
-        """Parse the <xorg> part of a submission.
-
-        :return: A dictionary with the keys 'version' and 'drivers'.
-                 d['version'] is the xorg version; d['drivers'] is
-                 a dictionary with one entry for each <driver> sub-node,
-                 where the key is the driver name, the value is a dictionary
-                 containing the attributes of the <driver> node. See
-                 tests/hardwaretest.xml for more details.
-        """
-        xorg_data = {'version': xorg_node.get('version')}
-        xorg_data['drivers'] = xorg_drivers = {}
-        for driver_node in xorg_node.getchildren():
-            # Pure paranoia: The Relax NG validation ensures already
-            # that we have only <driver> tags within <xorg>.
-            assert driver_node.tag == 'driver', (
-                'Parsing submission %s: Unexpected tag <%s> in <xorg>'
-                    % (self.submission_key, driver_node.tag))
-            driver_info = dict(driver_node.attrib)
-            if 'device' in driver_info:
-                # The Relax NG validation ensures that driver_info['device']
-                # consists of only digits, if present.
-                driver_info['device'] = int(driver_info['device'])
-            driver_name = driver_info['name']
-            if driver_name in xorg_drivers.keys():
-                raise ValueError(
-                    '<driver name="%s"> appears more than once in <xorg>'
-                    % driver_name)
-            xorg_drivers[driver_name] = driver_info
-        return xorg_data
-
-    _parse_software_section = {
-        'lsbrelease': _parseLSBRelease,
-        'packages': _parsePackages,
-        'xorg': _parseXOrg}
-
-    def _setSoftwareSectionParsers(self):
-        self._parse_software_section = {
-            'lsbrelease': self._parseLSBRelease,
-            'packages': self._parsePackages,
-            'xorg': self._parseXOrg}
-
-    def _parseSoftware(self, software_node):
-        """Parse the <software> section of a submission.
-
-        :return: A dictionary with the keys 'lsbrelease', 'packages',
-                 'xorg', containing the parsing results of the respective
-                 sub-nodes. The key 'lsbrelease' exists always; 'xorg'
-                 and 'packages' are optional. See _parseLSBRelease,
-                 _parsePackages, _parseXOrg for more details.
-        """
-        software_data = {}
-        for node in software_node.getchildren():
-            parser = self._parse_software_section[node.tag]
-            result = parser(node)
-            software_data[node.tag] = result
-        # The nodes <packages> and <xorg> are optional. Ensure that
-        # we have dummy entries in software_data for these nodes, if
-        # the nodes do not appear in a submission in order to avoid
-        # KeyErrors elsewhere in this module.
-        for node_name in ('packages', 'xorg'):
-            if node_name not in software_data:
-                software_data[node_name] = {}
-        return software_data
-
-    def _parseQuestions(self, questions_node):
-        """Parse the <questions> part of a submission.
-
-        :return: A list, where each entry is a dictionary containing
-                 the parsing result of the <question> sub-nodes.
-
-                 Content of a list entry d (see tests/hardwaretest.xml
-                 for a more detailed description):
-                 d['name']:
-                        The name of a question. (Always present)
-                 d['plugin']:
-                        The name of the client plugin which is
-                        "responsible" for the question. (Optional)
-                 d['targets']:
-                        A list, where each entry is a dicitionary
-                        describing a target device for this question.
-                        This list is always present, but may be empty.
-
-                        The contents of each list entry t is:
-
-                        t['id']:
-                                The ID of a HAL <device> node of a
-                                target device.
-                        t['drivers']:
-                                A list of driver names, possibly empty.
-                 d['answer']:
-                        The answer to this question. The value is a
-                        dictionary a:
-                        a['value']:
-                                The value of the answer. (Always present)
-
-                                For questions of type muliple_choice,
-                                the value should match one of the
-                                entries of the answer_choices list,
-
-                                For questions of type measurement, the
-                                value is a numerical value.
-                        a['type']:
-                                This is either 'multiple_choice' or
-                                'measurement'. (Always present)
-                        a['unit']:
-                                The unit of a measurement value.
-                                (Optional)
-                 d['answer_choices']:
-                        A list of choices from which the user can select
-                        an answer. This list is always present, but should
-                        be empty for questions of type measurement.
-                 d['command']:
-                        The command line of a test script which was
-                        run for this question. (Optional)
-                 d['comment']:
-                        A comment the user has typed when running the
-                        client. (Optional)
-
-                 A consistency check of the content of d is done in
-                 method _checkSubmissionConsistency.
-        """
-        questions = []
-        for question_node in questions_node.getchildren():
-            # Pure paranoia: The Relax NG validation ensures already
-            # that we have only <driver> tags within <xorg>
-            assert question_node.tag == 'question', (
-                'Parsing submission %s: Unexpected tag <%s> in <questions>'
-                % (self.submission_key, question_node.tag))
-            question = {'name': question_node.get('name')}
-            plugin = question_node.get('plugin', None)
-            if plugin is not None:
-                question['plugin'] = plugin
-            question['targets'] = targets = []
-            answer_choices = []
-
-            for sub_node in question_node.getchildren():
-                sub_tag = sub_node.tag
-                if sub_tag == 'answer':
-                    question['answer'] = answer = {}
-                    answer['type'] = sub_node.get('type')
-                    if answer['type'] == 'multiple_choice':
-                        question['answer_choices'] = answer_choices
-                    unit = sub_node.get('unit', None)
-                    if unit is not None:
-                        answer['unit'] = unit
-                    answer['value'] = sub_node.text.strip()
-                elif sub_tag == 'answer_choices':
-                    for value_node in sub_node.getchildren():
-                        answer_choices.append(
-                            self._getValueAndType(value_node))
-                elif sub_tag == 'target':
-                    # The Relax NG schema ensures that the attribute
-                    # id exists and that it is an integer
-                    target = {'id': int(sub_node.get('id'))}
-                    target['drivers'] = drivers = []
-                    for driver_node in sub_node.getchildren():
-                        drivers.append(driver_node.text.strip())
-                    targets.append(target)
-                elif sub_tag in('comment', 'command'):
-                    data = sub_node.text
-                    if data is not None:
-                        question[sub_tag] = data.strip()
-                else:
-                    # This should not happen: The Relax NG validation
-                    # ensures that we have only those tags which appear
-                    # in the if/elif expressions.
-                    raise AssertionError(
-                        'Parsing submission %s: Unexpected node <%s> in '
-                        '<question>' % (self.submission_key, sub_tag))
-            questions.append(question)
-        return questions
-
-    def _parseContext(self, context_node):
-        """Parse the <context> part of a submission.
-
-        We don't do anything real right now, but simply log a warning
-        that this submission contains a <context> section, so that
-        we can parse it again later, once we have the SQL tables needed
-        to store the data.
-        """
-        self._logWarning('Submission contains unprocessed <context> data.')
-        return {}
-
-    def _setMainSectionParsers(self):
-        self._parse_system = {
-            'summary': self._parseSummary,
-            'hardware': self._parseHardware,
-            'software': self._parseSoftware,
-            'questions': self._parseQuestions,
-            'context': self._parseContext,
-            }
-
-    def parseMainSections(self, submission_doc):
-        # The RelaxNG validation ensures that submission_doc has exactly
-        # four sub-nodes and that the names of the sub-nodes appear in the
-        # keys of self._parse_system.
-        submission_data = {}
-        try:
-            for node in submission_doc.getchildren():
-                parser = self._parse_system[node.tag]
-                result = parser(node)
-                if result is None:
-                    return None
-                submission_data[node.tag] = result
-        except ValueError as value:
-            self._logError(value, self.submission_key)
-            return None
-        return submission_data
-
-    def parseSubmission(self, submission, submission_key):
-        """Parse the data of a HWDB submission.
-
-        :return: A dictionary with the keys 'summary', 'hardware',
-                 'software', 'questions'. See _parseSummary,
-                 _parseHardware, _parseSoftware, _parseQuestions for
-                 the content.
-        """
-        self.submission_key = submission_key
-        submission_doc = self._getValidatedEtree(submission, submission_key)
-        if submission_doc is None:
-            return None
-
-        return self.parseMainSections(submission_doc)
-
-    def _findDuplicates(self, all_ids, test_ids):
-        """Search for duplicate elements in test_ids.
-
-        :return: A set of those elements in the sequence test_ids that
-        are elements of the set all_ids or that appear more than once
-        in test_ids.
-
-        all_ids is updated with test_ids.
-        """
-        duplicates = set()
-        # Note that test_ids itself may contain an ID more than once.
-        for test_id in test_ids:
-            if test_id in all_ids:
-                duplicates.add(test_id)
-            else:
-                all_ids.add(test_id)
-        return duplicates
-
-    def findDuplicateIDs(self, parsed_data):
-        """Return the set of duplicate IDs.
-
-        The IDs of devices, processors and software packages should be
-        unique; this method returns a list of duplicate IDs found in a
-        submission.
-        """
-        all_ids = set()
-        if 'hal' in parsed_data['hardware']:
-            duplicates = self._findDuplicates(
-                all_ids,
-                [device['id']
-                 for device in parsed_data['hardware']['hal']['devices']])
-        else:
-            duplicates = self._findDuplicates(
-                all_ids,
-                [device['P']
-                 for device in parsed_data['hardware']['udev']])
-        duplicates.update(self._findDuplicates(
-            all_ids,
-            [processor['id']
-             for processor in parsed_data['hardware']['processors']]))
-        duplicates.update(self._findDuplicates(
-            all_ids,
-            [package['id']
-             for package in parsed_data['software']['packages'].values()]))
-        return duplicates
-
-    def _getIDMap(self, parsed_data):
-        """Return a dictionary ID -> devices, processors and packages."""
-        id_map = {}
-        if 'hal' in parsed_data['hardware']:
-            hal_devices = parsed_data['hardware']['hal']['devices']
-            for device in hal_devices:
-                id_map[device['id']] = device
-        else:
-            udev_devices = parsed_data['hardware']['udev']
-            for device in udev_devices:
-                id_map[device['P']] = device
-
-        for processor in parsed_data['hardware']['processors']:
-            id_map[processor['id']] = processor
-
-        for package in parsed_data['software']['packages'].values():
-            id_map[package['id']] = package
-
-        return id_map
-
-    def findInvalidIDReferences(self, parsed_data):
-        """Return the set of invalid references to IDs.
-
-        The sub-tag <target> of <question> references a device, processor
-        of package node by its ID; the submission must contain a <device>,
-        <processor> or <software> tag with this ID. This method returns a
-        set of those IDs mentioned in <target> nodes that have no
-        corresponding device or processor node.
-        """
-        id_device_map = self._getIDMap(parsed_data)
-        known_ids = set(id_device_map.keys())
-        questions = parsed_data['questions']
-        target_lists = [question['targets'] for question in questions]
-        all_targets = []
-        for target_list in target_lists:
-            all_targets.extend(target_list)
-        all_target_ids = set(target['id'] for target in all_targets)
-        return all_target_ids.difference(known_ids)
-
-    def getUDIDeviceMap(self, devices):
-        """Return a dictionary which maps UDIs to HAL devices.
-
-        Also check, if a UDI is used more than once.
-
-        Generally, a duplicate UDI indicates a bad or bogus submission,
-        but we have some UDIs where the duplicate UDI is caused by a
-        bug in HAL, see
-        http://lists.freedesktop.org/archives/hal/2009-April/013250.html
-        In these cases, we simply remove the duplicates, otherwise, a
-        ValueError is raised.
-        """
-        udi_device_map = {}
-        duplicates = []
-        for index in range(len(devices)):
-            device = devices[index]
-            udi = device['udi']
-            if udi in udi_device_map:
-                if 'info.parent' in device['properties']:
-                    parent_udi = device['properties']['info.parent'][0]
-                else:
-                    parent_udi = None
-                if (udi in KNOWN_DUPLICATE_UDIS or
-                    parent_udi in KNOWN_DUPLICATE_UDIS):
-                    duplicates.append(index)
-                    continue
-                else:
-                    raise ValueError('Duplicate UDI: %s' % device['udi'])
-            else:
-                udi_device_map[udi] = device
-        duplicates.reverse()
-        for index in duplicates:
-            devices.pop(index)
-        return udi_device_map
-
-    def _getIDUDIMaps(self, devices):
-        """Return two mappings describing the relation between IDs and UDIs.
-
-        :return: two dictionaries id_to_udi and udi_to_id, where
-                 id_2_udi has IDs as keys and UDI as values, and where
-                 udi_to_id has UDIs as keys and IDs as values.
-        """
-        id_to_udi = {}
-        udi_to_id = {}
-        for device in devices:
-            id = device['id']
-            udi = device['udi']
-            id_to_udi[id] = udi
-            udi_to_id[udi] = id
-        return id_to_udi, udi_to_id
-
-    def getUDIChildren(self, udi_device_map):
-        """Build lists of all children of a UDI.
-
-        :return: A dictionary that maps UDIs to lists of children.
-
-        If any info.parent property points to a non-existing existing
-        device, a ValueError is raised.
-        """
-        # Each HAL device references its parent device (HAL attribute
-        # info.parent), except for the "root node", which has no parent.
-        children = {}
-        known_udis = set(udi_device_map.keys())
-        for device in udi_device_map.values():
-            parent_property = device['properties'].get('info.parent', None)
-            if parent_property is not None:
-                parent = parent_property[0]
-                if not parent in known_udis:
-                    raise ValueError(
-                        'Unknown parent UDI %s in <device id="%s">'
-                        % (parent, device['id']))
-                if parent in children:
-                    children[parent].append(device)
-                else:
-                    children[parent] = [device]
-            else:
-                # A node without a parent is a root node. Only one root node
-                # is allowed, which must have the UDI
-                # "/org/freedesktop/Hal/devices/computer".
-                # Other nodes without a parent UDI indicate an error, as well
-                # as a non-existing root node.
-                if device['udi'] != ROOT_UDI:
-                    raise ValueError(
-                        'root device node found with unexpected UDI: '
-                        '<device id="%s" udi="%s">' % (device['id'],
-                                                       device['udi']))
-
-        if not ROOT_UDI in children:
-            raise ValueError('No root device found')
-        return children
-
-    def _removeChildren(self, udi, udi_test):
-        """Remove recursively all children of the device named udi."""
-        if udi in udi_test:
-            children = udi_test[udi]
-            for child in children:
-                self._removeChildren(child['udi'], udi_test)
-            del udi_test[udi]
-
-    def checkHALDevicesParentChildConsistency(self, udi_children):
-        """Ensure that HAL devices are represented in exactly one tree.
-
-        :return: A list of those UDIs that are not "connected" to the root
-                 node /org/freedesktop/Hal/devices/computer
-
-        HAL devices "know" their parent device; each device has a parent,
-        except the root element. This means that it is possible to traverse
-        all existing devices, beginning at the root node.
-
-        Several inconsistencies are possible:
-
-        (a) we may have more than one root device (i.e., one without a
-            parent)
-        (b) we may have no root element
-        (c) circular parent/child references may exist.
-
-        (a) and (b) are already checked in _getUDIChildren; this method
-        implements (c),
-        """
-        # If we build a copy of udi_children and if we remove, starting at
-        # the root UDI, recursively all children from this copy, we should
-        # get a dictionary, where all values are empty lists. Any remaining
-        # nodes must have circular parent references.
-
-        udi_test = {}
-        for udi, children in udi_children.items():
-            udi_test[udi] = children[:]
-        self._removeChildren(ROOT_UDI, udi_test)
-        return udi_test.keys()
-
-    def checkUdevDictsHavePathKey(self, udev_nodes):
-        """Ensure that each udev dictionary has a 'P' key.
-
-        The 'P' (path) key identifies a device.
-        """
-        for node in udev_nodes:
-            if not 'P' in node:
-                self._logError('udev node found without a "P" key',
-                               self.submission_key)
-                return False
-        return True
-
-    PCI_PROPERTIES = set(
-        ('PCI_CLASS', 'PCI_ID', 'PCI_SUBSYS_ID', 'PCI_SLOT_NAME'))
-    pci_class_re = re.compile('^[0-9a-f]{1,6}$', re.I)
-    pci_id_re = re.compile('^[0-9a-f]{4}:[0-9a-f]{4}$', re.I)
-
-    def checkUdevPciProperties(self, udev_data):
-        """Validation of udev PCI devices.
-
-        :param udev_data: A list of dicitionaries describing udev
-             devices, as returned by _parseUdev()
-        :return: True if all checks pass, else False.
-
-        Each PCI device must have the properties PCI_CLASS, PCI_ID,
-        PCI_SUBSYS_ID, PCI_SLOT_NAME. Non-PCI devices must not have
-        them.
-
-        The value of PCI class must be a 24 bit integer in
-        hexadecimal representation.
-
-        The values of PCI_ID and PCI_SUBSYS_ID must be two 16 bit
-        integers, separated by a ':'.
-        """
-        for device in udev_data:
-            properties = device['E']
-            property_names = set(properties)
-            existing_pci_properties = property_names.intersection(
-                self.PCI_PROPERTIES)
-            subsystem = device['E'].get('SUBSYSTEM')
-            if subsystem is None:
-                self._logError(
-                    'udev device without SUBSYSTEM property found.',
-                    self.submission_key)
-                return False
-            if subsystem == 'pci':
-                # Check whether any of the standard pci properties were
-                # missing.
-                if existing_pci_properties != self.PCI_PROPERTIES:
-                    missing_properties = self.PCI_PROPERTIES.difference(
-                            existing_pci_properties)
-
-                    self._logError(
-                        'PCI udev device without required PCI properties: '
-                            '%r %r'
-                            % (missing_properties, device['P']),
-                        self.submission_key)
-                    return False
-                # Ensure that the pci class and ids for this device are
-                # formally valid.
-                if self.pci_class_re.search(properties['PCI_CLASS']) is None:
-                    self._logError(
-                        'Invalid udev PCI class: %r %r'
-                            % (properties['PCI_CLASS'], device['P']),
-                        self.submission_key)
-                    return False
-                for pci_id in (properties['PCI_ID'],
-                               properties['PCI_SUBSYS_ID']):
-                    if self.pci_id_re.search(pci_id) is None:
-                        self._logError(
-                            'Invalid udev PCI device ID: %r %r'
-                                % (pci_id, device['P']),
-                            self.submission_key)
-                        return False
-            else:
-                if len(existing_pci_properties) > 0:
-                    self._logError(
-                        'Non-PCI udev device with PCI properties: %r %r'
-                            % (existing_pci_properties, device['P']),
-                        self.submission_key)
-                    return False
-        return True
-
-    def checkUdevUsbProperties(self, udev_data):
-        """Validation of udev USB devices.
-
-        USB devices must either have the three properties DEVTYPE
-        (value 'usb_device' or 'usb_interface'), PRODUCT and TYPE,
-        or they must have none of them.
-
-        PRODUCT must be a tuple of three integers in hexadecimal
-        representation, separates by '/'. TYPE must be a a tuple of
-        three integers in decimal representation, separated by '/'.
-        usb_interface nodes must additionally have a property
-        INTERFACE, containing three integers in the same format as
-        TYPE.
-        """
-        for device in udev_data:
-            subsystem = device['E'].get('SUBSYSTEM')
-            if subsystem != 'usb':
-                continue
-            properties = device['E']
-            property_names = set(properties)
-            existing_usb_properties = property_names.intersection(
-                UDEV_USB_DEVICE_PROPERTIES)
-
-            if len(existing_usb_properties) == 0:
-                continue
-
-            if existing_usb_properties != UDEV_USB_DEVICE_PROPERTIES:
-                missing_properties = UDEV_USB_DEVICE_PROPERTIES.difference(
-                    existing_usb_properties)
-                self._logError(
-                    'USB udev device found without required properties: %r %r'
-                    % (missing_properties, device['P']),
-                    self.submission_key)
-                return False
-            if UDEV_USB_PRODUCT_RE.search(properties['PRODUCT']) is None:
-                self._logError(
-                    'USB udev device found with invalid product ID: %r %r'
-                    % (properties['PRODUCT'], device['P']),
-                    self.submission_key)
-                return False
-            if UDEV_USB_TYPE_RE.search(properties['TYPE']) is None:
-                self._logError(
-                    'USB udev device found with invalid type data: %r %r'
-                    % (properties['TYPE'], device['P']),
-                    self.submission_key)
-                return False
-
-            device_type = properties['DEVTYPE']
-            if device_type not in ('usb_device', 'usb_interface'):
-                self._logError(
-                    'USB udev device found with invalid udev type data: %r %r'
-                    % (device_type, device['P']),
-                    self.submission_key)
-                return False
-            if device_type == 'usb_interface':
-                interface_type = properties.get('INTERFACE')
-                if interface_type is None:
-                    self._logError(
-                        'USB interface udev device found without INTERFACE '
-                        'property: %r'
-                        % device['P'],
-                        self.submission_key)
-                    return False
-                if UDEV_USB_TYPE_RE.search(interface_type) is None:
-                    self._logError(
-                        'USB Interface udev device found with invalid '
-                        'INTERFACE property: %r %r'
-                        % (interface_type, device['P']),
-                        self.submission_key)
-                    return False
-        return True
-
-    def checkUdevScsiProperties(self, udev_data, sysfs_data):
-        """Validation of udev SCSI devices.
-
-        Each udev node where SUBSYSTEM is 'scsi' should have the
-        property DEVTYPE; nodes where DEVTYPE is 'scsi_device'
-        should have a corresponding sysfs node, and this node should
-        define the attributes 'vendor', 'model', 'type'.
-        """
-        # Broken submissions from Lucid, Maverick and Natty. We'll have
-        # to deal with incomplete data for SCSI devices in this case if
-        # we don't want to drop the entire submission, so just pretend
-        # that things are fine.
-        # See also bug 835103.
-        if sysfs_data is None:
-            return True
-        for device in udev_data:
-            subsystem = device['E'].get('SUBSYSTEM')
-            if subsystem != 'scsi':
-                continue
-            properties = device['E']
-            if 'DEVTYPE' not in properties:
-                self._logError(
-                    'SCSI udev node found without DEVTYPE property: %r'
-                    % device['P'],
-                    self.submission_key)
-                return False
-            if properties['DEVTYPE'] == 'scsi_device':
-                device_path = device['P']
-                if device_path not in sysfs_data:
-                    self._logError(
-                        'SCSI udev device node found without related '
-                        'sysfs record: %r' % device_path,
-                        self.submission_key)
-                    return False
-                sysfs_attributes = sysfs_data[device_path]
-                sysfs_attribute_names = set(sysfs_attributes)
-                if SYSFS_SCSI_DEVICE_ATTRIBUTES.intersection(
-                    sysfs_attribute_names) != SYSFS_SCSI_DEVICE_ATTRIBUTES:
-                    missing_attributes = (
-                        SYSFS_SCSI_DEVICE_ATTRIBUTES.difference(
-                            sysfs_attribute_names))
-                    self._logError(
-                        'SCSI udev device found without required sysfs '
-                        'attributes: %r %r'
-                        % (missing_attributes, device_path),
-                        self.submission_key)
-                    return False
-        return True
-
-    def checkUdevDmiData(self, dmi_data):
-        """Consistency check for DMI data.
-
-        All keys of the dictionary dmi_data should start with
-        '/sys/class/dmi/id/'.
-        """
-        for dmi_key in dmi_data:
-            if not dmi_key.startswith('/sys/class/dmi/id/'):
-                self._logError(
-                    'Invalid DMI key: %r' % dmi_key, self.submission_key)
-                return False
-        return True
-
-    def checkConsistentUdevDeviceData(self, udev_data, sysfs_data, dmi_data):
-        """Consistency checks for udev data."""
-        return (
-            self.checkUdevDictsHavePathKey(udev_data) and
-            self.checkUdevPciProperties(udev_data) and
-            self.checkUdevUsbProperties(udev_data) and
-            self.checkUdevScsiProperties(udev_data, sysfs_data) and
-            self.checkUdevDmiData(dmi_data))
-
-    def checkConsistency(self, parsed_data):
-        """Run consistency checks on the submitted data.
-
-        :return: True, if the data looks consistent, otherwise False.
-        :param: parsed_data: parsed submission data, as returned by
-                             parseSubmission
-        """
-        if ('udev' in parsed_data['hardware']
-            and not self.checkConsistentUdevDeviceData(
-                parsed_data['hardware']['udev'],
-                parsed_data['hardware']['sysfs-attributes'],
-                parsed_data['hardware']['dmi'],)):
-            return False
-        duplicate_ids = self.findDuplicateIDs(parsed_data)
-        if duplicate_ids:
-            self._logError('Duplicate IDs found: %s' % duplicate_ids,
-                           self.submission_key)
-            return False
-
-        invalid_id_references = self.findInvalidIDReferences(parsed_data)
-        if invalid_id_references:
-            self._logError(
-                'Invalid ID references found: %s' % invalid_id_references,
-                self.submission_key)
-            return False
-
-        if 'hal' in parsed_data['hardware']:
-            try:
-                udi_device_map = self.getUDIDeviceMap(
-                    parsed_data['hardware']['hal']['devices'])
-                udi_children = self.getUDIChildren(udi_device_map)
-            except ValueError as value:
-                self._logError(value, self.submission_key)
-                return False
-
-            circular = self.checkHALDevicesParentChildConsistency(
-                udi_children)
-            if circular:
-                self._logError('Found HAL devices with circular parent/child '
-                               'relationship: %s' % circular,
-                               self.submission_key)
-                return False
-
-        return True
-
-    def buildDeviceList(self, parsed_data):
-        """Create a list of devices from a submission."""
-        if 'hal' in parsed_data['hardware']:
-            return self.buildHalDeviceList(parsed_data)
-        else:
-            return self.buildUdevDeviceList(parsed_data)
-
-    def buildHalDeviceList(self, parsed_data):
-        """Create a list of devices from the HAL data of a submission."""
-        self.devices = {}
-        for hal_data in parsed_data['hardware']['hal']['devices']:
-            udi = hal_data['udi']
-            self.devices[udi] = HALDevice(hal_data['id'], udi,
-                                          hal_data['properties'], self)
-        for device in self.devices.values():
-            parent_udi = device.parent_udi
-            if parent_udi is not None:
-                self.devices[parent_udi].addChild(device)
-        return True
-
-    def buildUdevDeviceList(self, parsed_data):
-        """Create a list of devices from the udev data of a submission."""
-        self.devices = {}
-        sysfs_data = parsed_data['hardware']['sysfs-attributes']
-        dmi_data = parsed_data['hardware']['dmi']
-        for udev_data in parsed_data['hardware']['udev']:
-            device_path = udev_data['P']
-            if sysfs_data is not None:
-                sysfs_data_for_device = sysfs_data.get(device_path)
-            else:
-                # broken Lucid, Maverick and Natty submissions.
-                # See also bug 835103.
-                sysfs_data_for_device = None
-            if device_path == UDEV_ROOT_PATH:
-                device = UdevDevice(
-                    self, udev_data, sysfs_data=sysfs_data_for_device,
-                    dmi_data=dmi_data)
-            else:
-                device = UdevDevice(
-                    self, udev_data, sysfs_data=sysfs_data_for_device)
-            self.devices[device_path] = device
-
-        # The parent-child relations are derived from the path names of
-        # the devices. If A and B are the path names of two devices,
-        # the device with path name A is an ancestor of the device with
-        # path name B, iff B.startswith(A). If C is the set of the path
-        # names of all ancestors of A, the element with the longest path
-        # name belongs to the parent of A.
-        #
-        # There is one exception to this rule: The root node has the
-        # the path name '/devices/LNXSYSTM:00', while the path names
-        # of PCI devices start with '/devices/pci'. We'll temporarily
-        # change the path name of the root device so that the rule
-        # holds for all devices.
-        if UDEV_ROOT_PATH not in self.devices:
-            self._logError('No udev root device defined', self.submission_key)
-            return False
-        self.devices['/devices'] = self.devices[UDEV_ROOT_PATH]
-        del self.devices[UDEV_ROOT_PATH]
-
-        path_names = sorted(self.devices, key=len, reverse=True)
-        for path_index, path_name in enumerate(path_names[:-1]):
-            # Ensure that the last ancestor of each device is our
-            # root node.
-            if not path_name.startswith('/devices'):
-                self._logError(
-                    'Invalid device path name: %r' % path_name,
-                    self.submission_key)
-                return False
-            for parent_path in path_names[path_index + 1:]:
-                if path_name.startswith(parent_path):
-                    self.devices[parent_path].addChild(
-                        self.devices[path_name])
-                    break
-        self.devices[UDEV_ROOT_PATH] = self.devices['/devices']
-        del self.devices['/devices']
-        return True
-
-    @cachedproperty
-    def kernel_package_name(self):
-        """The kernel package name for the submission."""
-        if ROOT_UDI in self.devices:
-            root_hal_device = self.devices[ROOT_UDI]
-            kernel_version = root_hal_device.getProperty(
-                'system.kernel.version')
-        else:
-            kernel_version = self.parsed_data['summary'].get('kernel-release')
-        if kernel_version is None:
-            self._logWarning(
-                'Submission does not provide property system.kernel.version '
-                'for /org/freedesktop/Hal/devices/computer or a summary '
-                'sub-node <kernel-release>.')
-            return None
-        kernel_package_name = 'linux-image-' + kernel_version
-        packages = self.parsed_data['software']['packages']
-        # The submission is not required to provide any package data...
-        if packages and kernel_package_name not in packages:
-            # ...but if we have it, we want it to be consistent with
-            # the HAL root node property.
-            self._logWarning(
-                'Inconsistent kernel version data: According to HAL the '
-                'kernel is %s, but the submission does not know about a '
-                'kernel package %s'
-                % (kernel_version, kernel_package_name))
-            return None
-        return kernel_package_name
-
-    def processSubmission(self, submission):
-        """Process a submisson.
-
-        :return: True, if the submission could be sucessfully processed,
-            otherwise False.
-        :param submission: An IHWSubmission instance.
-        """
-        raw_submission = submission.raw_submission
-        # This script runs once per day and can need a few hours to run.
-        # Short-lived Librarian server failures or a server restart should
-        # not break this script, so let's wait for 10 minutes for a
-        # response from the Librarian.
-        raw_submission.open(timeout=600)
-        submission_data = raw_submission.read(timeout=600)
-        raw_submission.close()
-
-        # We assume that the data has been sent bzip2-compressed,
-        # but this is not checked when the data is submitted.
-        expanded_data = None
-        try:
-            expanded_data = bz2.decompress(submission_data)
-        except IOError:
-            # An IOError is raised, if the data is not BZip2-compressed.
-            # We assume in this case that valid uncompressed data has been
-            # submitted. If this assumption is wrong, parseSubmission()
-            # or checkConsistency() will complain, hence we don't check
-            # anything else here.
-            pass
-        if expanded_data is not None:
-            submission_data = expanded_data
-
-        parsed_data = self.parseSubmission(
-            submission_data, submission.submission_key)
-        if parsed_data is None:
-            return False
-        self.parsed_data = parsed_data
-        if not self.checkConsistency(parsed_data):
-            return False
-        if not self.buildDeviceList(parsed_data):
-            return False
-        self.root_device.createDBData(submission, None)
-        return True
-
-    @property
-    def root_device(self):
-        """The HALDevice of UdevDevice node of the root device."""
-        # checkConsistency ensures that we have either a device with the
-        # key ROOT_UDI or a device with the key UDEV_ROOT_PATH.
-        if ROOT_UDI in self.devices:
-            return self.devices[ROOT_UDI]
-        else:
-            return self.devices[UDEV_ROOT_PATH]
-
-
-class BaseDevice:
-    """A base class to represent device data from HAL and udev."""
-
-    def __init__(self, parser):
-        self.children = []
-        self.parser = parser
-        self.parent = None
-
-    # Translation of the HAL info.bus/info.subsystem property and the
-    # udev property SUBSYSTEM to HWBus enumerated buses.
-    subsystem_hwbus = {
-        'pcmcia': HWBus.PCMCIA,
-        'usb_device': HWBus.USB,
-        'ide': HWBus.IDE,
-        'serio': HWBus.SERIAL,
-        }
-
-    def addChild(self, child):
-        """Add a child device and set the child's parent."""
-        assert type(child) == type(self)
-        self.children.append(child)
-        child.parent = self
-
-    # Translation of subclasses of the PCI class storage to HWBus
-    # enumerated buses. The Linux kernel accesses IDE and SATA disks
-    # and CDROM drives via the SCSI system; we want to know the real bus
-    # of the drive. See for example the file include/linux/pci_ids.h
-    # in the Linux kernel sources for a list of PCI device classes and
-    # subclasses. Note that the subclass 4 (RAID) is missing. While it
-    # may make sense to declare a RAID storage class for PCI devices,
-    # "RAID" does not tell us anything about the bus of the storage
-    # devices.
-    pci_storage_subclass_hwbus = {
-        0: HWBus.SCSI,
-        1: HWBus.IDE,
-        2: HWBus.FLOPPY,
-        3: HWBus.IPI,  # Intelligent Peripheral Interface.
-        5: HWBus.ATA,
-        6: HWBus.SATA,
-        7: HWBus.SAS,
-        }
-
-    @property
-    def device_id(self):
-        """A unique ID for this device."""
-        raise NotImplementedError
-
-    @property
-    def pci_class(self):
-        """The PCI device class of the device or None for Non-PCI devices."""
-        raise NotImplementedError
-
-    @property
-    def pci_subclass(self):
-        """The PCI device sub-class of the device or None for Non-PCI devices.
-        """
-        raise NotImplementedError
-
-    @property
-    def usb_vendor_id(self):
-        """The USB vendor ID of the device or None for Non-USB devices."""
-        raise NotImplementedError
-
-    @property
-    def usb_product_id(self):
-        """The USB product ID of the device or None for Non-USB devices."""
-        raise NotImplementedError
-
-    @property
-    def scsi_vendor(self):
-        """The SCSI vendor name of the device or None for Non-SCSI devices."""
-        raise NotImplementedError
-
-    @property
-    def scsi_model(self):
-        """The SCSI model name of the device or None for Non-SCSI devices."""
-        raise NotImplementedError
-
-    @property
-    def vendor(self):
-        """The vendor of this device."""
-        raise NotImplementedError
-
-    @property
-    def product(self):
-        """The vendor of this device."""
-        raise NotImplementedError
-
-    @property
-    def vendor_id(self):
-        """The vendor ID of this device."""
-        raise NotImplementedError
-
-    @property
-    def product_id(self):
-        """The product ID of this device."""
-        raise NotImplementedError
-
-    @property
-    def vendor_id_for_db(self):
-        """The vendor ID in the representation needed for the HWDB tables.
-
-        USB and PCI IDs are represented in the database in hexadecimal,
-        while the IDs provided by HAL are integers.
-
-        The SCSI vendor name is right-padded with spaces to 8 bytes.
-        """
-        bus = self.raw_bus
-        format = DB_FORMAT_FOR_VENDOR_ID.get(bus)
-        if format is None:
-            return self.vendor_id
-        else:
-            return format % self.vendor_id
-
-    @property
-    def product_id_for_db(self):
-        """The product ID in the representation needed for the HWDB tables.
-
-        USB and PCI IDs are represented in the database in hexadecimal,
-        while the IDs provided by HAL are integers.
-
-        The SCSI product name is right-padded with spaces to 16 bytes.
-        """
-        bus = self.raw_bus
-        format = DB_FORMAT_FOR_PRODUCT_ID.get(bus)
-        if format is None:
-            return self.product_id
-        else:
-            return format % self.product_id
-
-    @property
-    def driver_name(self):
-        """The name of the driver contolling this device. May be None."""
-        raise NotImplementedError
-
-    @property
-    def scsi_controller(self):
-        """Return the SCSI host controller for this device."""
-        raise NotImplementedError
-
-    def translateScsiBus(self):
-        """Return the real bus of a device where raw_bus=='scsi'.
-
-        The kernel uses the SCSI layer to access storage devices
-        connected via the USB, IDE, SATA buses. See `is_real_device`
-        for more details. This method determines the real bus
-        of a device accessed via the kernel's SCSI subsystem.
-        """
-        scsi_controller = self.scsi_controller
-        if scsi_controller is None:
-            return None
-
-        scsi_controller_bus = scsi_controller.raw_bus
-        if scsi_controller_bus == 'pci':
-            if (scsi_controller.pci_class != PCI_CLASS_STORAGE):
-                # This is not a storage class PCI device? This
-                # indicates a bug somewhere in HAL or in the hwdb
-                # client, or a fake submission.
-                device_class = scsi_controller.pci_class
-                self.parser._logWarning(
-                    'A (possibly fake) SCSI device %s is connected to '
-                    'PCI device %s that has the PCI device class %s; '
-                    'expected class 1 (storage).'
-                    % (self.device_id, scsi_controller.device_id,
-                       device_class))
-                return None
-            pci_subclass = scsi_controller.pci_subclass
-            return self.pci_storage_subclass_hwbus.get(pci_subclass)
-        elif scsi_controller_bus in ('usb', 'usb_interface'):
-            # USB storage devices have the following HAL device hierarchy:
-            # - HAL node for the USB device. info.bus == 'usb_device',
-            #   device class == 0, device subclass == 0
-            # - HAL node for the USB storage interface. info.bus == 'usb',
-            #   interface class 8, interface subclass 6
-            #   (see http://www.usb.org/developers/devclass_docs
-            #   /usb_msc_overview_1.2.pdf)
-            # - HAL node for the (fake) SCSI host. raw_bus is None
-            # - HAL node for the (fake) SCSI device. raw_bus == 'scsi'
-            # - HAL node for the mass storage device
-            #
-            # Physically, the storage device can be:
-            # (1) a genuine USB device, like a memory stick
-            # (2) a IDE/SATA hard disk, connected to a USB -> SATA/IDE
-            #     bridge
-            # (3) a card reader
-            # There is no formal way to distinguish cases (1) and (2):
-            # The device and interface classes are in both cases
-            # identical; the only way to figure out, if we have a USB
-            # hard disk enclosure or a USB memory stick would be to
-            # look at the vendor or product names, or to look up some
-            # external data sources. Of course, we can also ask the
-            # submitter in the future.
-            #
-            # The cases (1) and (2) differ from (3) in the property
-            # the property storage.removable. For (1) and (2), this
-            # property is False, for (3) it is True. Since we do not
-            # store at present any device characteristics in the HWDB,
-            # so there is no point to distinguish between (1), (2) on
-            # one side and (3) on the other side. Distinguishing
-            # between (1) and (2) might be more interesting, because
-            # a hard disk is clearly a separate device, but as written,
-            # it is hard to distinguish between (1) and (2)
-            #
-            # To sum up: we cannot get any interesting and reliable
-            # information about the details of USB storage device,
-            # so we'll treat those devices as "black boxes".
-            return None
-        else:
-            return HWBus.SCSI
-
-    def translatePciBus(self):
-        # Cardbus (aka PCCard, sometimes also incorrectly called
-        # PCMCIA) devices are treated as PCI devices by the kernel.
-        # We can detect PCCards by checking that the parent device
-        # is a PCI bridge (device class 6) for the Cardbus (device
-        # subclass 7).
-        # XXX Abel Deuring 2005-05-14 How can we detect ExpressCards?
-        # I do not have any such card at present...
-        parent_class = self.parent.pci_class
-        parent_subclass = self.parent.pci_subclass
-        if (parent_class == PCI_CLASS_BRIDGE
-            and parent_subclass == PCI_SUBCLASS_BRIDGE_CARDBUS):
-            return HWBus.PCCARD
-        else:
-            return HWBus.PCI
-
-    @property
-    def is_root_device(self):
-        """Return True is this is the root node of all devicese, else False.
-        """
-        raise NotImplementedError
-
-    @property
-    def raw_bus(self):
-        """Return the device bus as specified by HAL or udev."""
-        raise NotImplementedError
-
-    @property
-    def real_bus(self):
-        """Return the bus this device connects to on the host side.
-
-        :return: A bus as enumerated in HWBus or None, if the bus
-            cannot be determined.
-        """
-        device_bus = self.raw_bus
-        result = self.subsystem_hwbus.get(device_bus)
-        if result is not None:
-            return result
-
-        if device_bus in ('scsi', 'scsi_device'):
-            return self.translateScsiBus()
-        elif device_bus == 'pci':
-            return self.translatePciBus()
-        elif self.is_root_device:
-            # The computer itself. In Hardy, HAL provides no info.bus
-            # for the machine itself; older versions set info.bus to
-            # 'unknown', hence it is better to use the machine's
-            # UDI.
-            return HWBus.SYSTEM
-        else:
-            self.parser._logWarning(
-                'Unknown bus %r for device %s' % (device_bus, self.device_id))
-            return None
-
-    @property
-    def is_real_device(self):
-        """True, if the HAL device correspends to a real device.
-
-        In many cases HAL has more than one device entry for the
-        same physical device. We are only interested in real, physical,
-        devices but not in the fine details, how HAL represents different
-        aspects of them.
-
-        For example, the HAL device node hiearchy for a SATA disk and
-        its host controller looks like this:
-
-        HAL device node of the host controller
-            udi: .../pci_8086_27c5
-            HAL properties:
-                info.bus: pci
-                pci.device_class: 1 (storage)
-                pci.device_subclass: 6 (SATA)
-                info.linux.driver: ahci
-
-        HAL device node of the "output aspect" of the host controller
-            udi: .../pci_8086_27c5_scsi_host
-            HAL properties:
-                info.bus: n/a
-                info.driver: n/a
-                info.parent: .../pci_8086_27c5
-
-        HAL device node of a hard disk.
-            udi: .../pci_8086_27c5_scsi_host_scsi_device_lun0
-            HAL properties:
-                info.bus: scsi
-                info.driver: sd
-                info.parent: .../pci_8086_27c5_scsi_host
-
-        HAL device node of the "storage aspect" of the hard disk
-            udi: .../storage_serial_1ATA_Hitachi_HTS541616J9SA00_SB...
-            HAL properties
-                info.driver: n/a
-                info.parent: .../pci_8086_27c5_scsi_host_scsi_device_lun0
-
-        HAL device node of a disk partition:
-            udi: .../volume_uuid_0ee803cf_...
-            HAL properties
-                info.driver: n/a
-                info.parent: .../storage_serial_1ATA_Hitachi_HTS541616J...
-
-        (optionally more nodes for more partitions)
-
-        HAL device node of the "generic SCSI aspect" of the hard disk:
-            udi: .../pci_8086_27c5_scsi_host_scsi_device_lun0_scsi_generic
-                info.driver: n/a
-                info.parent: .../pci_8086_27c5_scsi_host_scsi_device_lun0
-
-        This disk is _not_ a SCSI disk, but a SATA disk. In other words,
-        the SCSI details are in this case just an artifact of the Linux
-        kernel, which uses its SCSI subsystem as a "central hub" to access
-        IDE, SATA, USB, IEEE1394 storage devices. The only interesting
-        detail for us is that the sd driver is involved in accesses to the
-        disk.
-
-        Heuristics:
-
-        - Most real devices have the property info.bus; we consider only
-          those devices to be real which have this property set.
-
-        - As written above, the SCSI bus often appears as an artifact;
-          for PCI host controllers, their properties pci.device_class
-          and pci.device_subclass tell us if we have a real SCSI host
-          controller: pci.device_class == 1 means a storage controller,
-          pci.device_subclass == 0 means a SCSI controller. This works
-          too for PCCard controllers, which use the PCI device class
-          numbers too.
-
-        - The value "usb_device" of the HAL property info.bus identifies
-          USB devices, with one exception: The USB host controller, which
-          itself has an info.bus property with the value "pci", has a
-          sub-device with info.bus='usb_device' for its "output aspect".
-          These sub-devices can be identified by the device class their
-          parent and by their USB vendor/product IDs, which are 0:0.
-
-        Several info.bus/info.subsystem values always relate to HAL nodes
-        which describe only "aspects" of physical devcies which are
-        represented by other HAL nodes:
-
-          - bus is None for a number of "virtual components", like
-            /org/freedesktop/Hal/devices/computer_alsa_timer or
-            /org/freedesktop/Hal/devices/computer_oss_sequencer, so
-            we ignore them. (The real sound devices appear with
-            other UDIs in HAL.)
-
-            XXX Abel Deuring 20080425: This ignores a few components
-            like laptop batteries or the CPU, where info.bus is None.
-            Since these components are not the most important ones
-            for the HWDB, we'll ignore them for now. Bug 237038.
-
-          - 'disk' is used udev submissions for a node related to the
-            sd or sr driver of (real or fake) SCSI block devices.
-
-          - info.bus == 'drm' is used by the HAL for the direct
-            rendering interface of a graphics card.
-
-          - info.bus == 'dvb' is used by HAL for the "input aspect"
-            of DVB receivers
-
-          - info.bus == 'memstick_host' is used by HAL for the "output aspect"
-            of memory sticks.
-
-          - info.bus == 'net' is used by the HAL version in
-            Intrepid for the "output aspects" of network devices.
-
-          - 'partition' is used in udev submissions for a node
-            related to disk partition
-
-          - 'scsi_disk' is used in udev submissions for a sub-node of
-            the real device node.
-
-            info.bus == 'scsi_generic' is used by the HAL version in
-            Intrepid for a HAL node representing the generic
-            interface of a SCSI device.
-
-            info.bus == 'scsi_host' is used by the HAL version in
-            Intrepid for real and "fake" SCSI host controllers.
-            (On Hardy, these nodes have no info.bus property.)
-            HAL nodes with this bus value are sub-nodes for the
-            "SCSI aspect" of another HAL node which represents the
-            real device.
-
-            'scsi_target' is used in udev data for SCSI target nodes,
-            the parent of a SCSI device (or LUN) node.
-
-            'spi_transport' (SCSI Parallel Transport) is used in
-            udev data for a sub-node of real SCSI devices.
-
-            info.bus == 'sound' is used by the HAL version in
-            Intrepid for "aspects" of sound devices.
-
-            info.bus == 'ssb' is used for "aspects" of Broadcom
-            Ethernet and WLAN devices, but like 'usb', they do not
-            represent separate devices.
-
-            info.bus == 'tty' is used for the "output aspect"
-            of serial output devices (RS232, modems etc). It appears
-            for USB and PCI devices as well as for legacy devices
-            like the 8250/16450/16550 controllers.
-
-            info.bus == 'usb' is used for end points of USB devices;
-            the root node of a USB device has info.bus == 'usb_device'.
-
-            'usb_interface' is used in udv submissions for interface
-            nodes of USB devices.
-
-            info.bus == 'video4linux' is used for the "input aspect"
-            of video devices.
-
-            'ac97' is used in submissions with udev data for a sub-node
-            of sound devices.
-
-            'hid' is used in submissions with udev data for a sub-node
-            of USB input devices.
-
-            'drm_minor', 'pci_express', 'tifm_adapter', 'gameport',
-            'spi_host', 'tifm', 'wlan' are used in submissions with
-            udev data for sub-nodes of PCI devices.
-
-            'pcmcia_socket' is used in submissions with udev data for
-            a sub-node of PC Card and PCMCIA bridges.
-
-            'ieee80211'  is used in submissions with udev data for
-            sub-nodes IEEE 802.11 WLAN devices.
-
-            'host', 'link' are used in submissions with udev data for
-            sub.nodes of bluetooth devices.
-
-            'usb_host' and 'usbmon' are used in submissions with udev
-            data for sub-nodes of USB controllers.
-
-            'usb_endpoint', 'usb-serial', 'lirc' are used in
-            submissions with udev data for sub-nodes of USB devices.
-
-            'enclosure' is used in submissions with udev data for a
-            sub.node of SCSI devices.
-
-            'graphics' is used  in submissions with udev data for a
-            sub-node of graphics cards.
-
-            'hwmon' is is used  in submissions with udev data in
-            many sub-nodes.
-
-            'sas_phy', 'sas_device', 'sas_end_device', 'sas_port',
-            'sas_host' is used in submissions with udev data for
-            details of SAS controllers.
-
-            'mISDN' is used  in submissions with udev data for the
-            I/O aspects of ISDN adapters.
-
-            'pvrusb2' is used  in submissions with udev data for the
-            input aspect of some DVB adapters.
-
-            'memstick' is used  in submissions with udev data for the
-            I/O aspect of memory stick controllers.
-
-            'bttv-sub' is used  in submissions with udev data for the
-            I/O aspects of some TV receivers.
-
-            'scsi_tape' is used  in submissions with udev data for
-            details of SCSI tape drives.
-            """
-        # The root node is always a real device, but its raw_bus
-        # property can have different values: None or 'Unknown' in
-        # submissions with HAL data, 'acpi' for submissions with udev
-        # data.
-        if self.is_root_device:
-            return True
-
-        bus = self.raw_bus
-        # This set of buses is only used once; it's easier to have it
-        # here than to put it elsewhere and have to document its
-        # location and purpose.
-        if bus in (None, 'ac97', 'bttv-sub', 'disk', 'drm', 'drm_minor',
-                   'dvb', 'enclosure', 'gameport', 'graphics', 'hid', 'host',
-                   'hwmon', 'ieee80211', 'link', 'lirc', 'mISDN', 'memstick',
-                   'memstick_host', 'net', 'partition', 'pci_express',
-                   'pcmcia_socket', 'pvrusb2', 'sas_device', 'sas_end_device',
-                   'sas_host', 'sas_phy', 'sas_port', 'scsi_disk',
-                   'scsi_generic', 'scsi_host', 'scsi_tape', 'scsi_target',
-                   'sound', 'spi_host', 'spi_transport', 'ssb', 'tifm',
-                   'tifm_adapter', 'tty', 'usb', 'usb-serial', 'usb_endpoint',
-                   'usb_host', 'usb_interface', 'usbmon', 'video4linux',
-                   'wlan'):
-            return False
-        elif bus == 'usb_device':
-            vendor_id = self.usb_vendor_id
-            product_id = self.usb_product_id
-            if vendor_id == 0 and product_id == 0:
-                # double-check: The parent device should be a PCI host
-                # controller, identifiable by its device class and subclass.
-                # XXX Abel Deuring 2008-04-28 Bug=237039: This ignores other
-                # possible bridges, like ISA->USB..
-                parent = self.parent
-                parent_bus = parent.raw_bus
-                parent_class = parent.pci_class
-                parent_subclass = parent.pci_subclass
-                if (parent_bus == 'pci'
-                    and parent_class == PCI_CLASS_SERIALBUS_CONTROLLER
-                    and parent_subclass == PCI_SUBCLASS_SERIALBUS_USB):
-                    return False
-                else:
-                    self.parser._logWarning(
-                        'USB device found with vendor ID==0, product ID==0, '
-                        'where the parent device does not look like a USB '
-                        'host controller: %s' % self.device_id)
-                    return False
-            return True
-        elif bus in ('scsi', 'scsi_device'):
-            # Ensure consistency with HALDevice.real_bus
-            return self.real_bus is not None
-        else:
-            return True
-
-    def getRealChildren(self):
-        """Return the list of real child devices of this devices.
-
-        The list of real child devices consists of the direct child
-        devices of this device where child.is_real_device == True, and
-        of the (recursively collected) list of real sub-devices of
-        those child devices where child.is_real_device == False.
-        """
-        result = []
-        for sub_device in self.children:
-            if sub_device.is_real_device:
-                # XXX Abel Deuring 2008-05-06: IEEE1394 devices are a bit
-                # nasty: The standard does not define any specification
-                # for product IDs or product names, hence HAL often
-                # uses the value 0 for the property ieee1394.product_id
-                # and a value like "Unknown (0x00d04b)" for
-                # ieee.product, where 0x00d04b is the vendor ID. I have
-                # currently no idea how to find or generate something
-                # that could be used as the product ID, so IEEE1394
-                # devices are at present simply dropped from the list of
-                # devices. Otherwise, we'd pollute the HWDB with
-                # unreliable data. Bug 237044.
-                if sub_device.raw_bus != 'ieee1394':
-                    result.append(sub_device)
-            else:
-                result.extend(sub_device.getRealChildren())
-        return result
-
-    @property
-    def has_reliable_data(self):
-        """Can this device be stored in the HWDB?
-
-        Devices are identifed by (bus, vendor_id, product_id).
-        At present we cannot generate reliable vendor and/or product
-        IDs for devices with the following values of the HAL
-        property info.bus resp. info.subsystem.
-
-        info.bus == 'backlight' is used by the HAL version in
-        Intrepid for the LC display. Useful vendor and product names
-        are not available.
-
-        info.bus == 'bluetooth': HAL does not provide any vendor/product
-        ID data, so we can't store these devices in HWDevice.
-
-        info.bus == 'input' is used by the HAL version in
-        Intrepid for quite different devices like keyboards, mice,
-        special laptop switches and buttons, sometimes with odd
-        product names like "Video Bus".
-
-        info.bus == 'misc' and info.bus == 'unknown' are obviously
-        not very useful, except for the computer itself, which has
-        the bus 'unknown'.
-
-        info.bus in ('mmc', 'mmc_host') is used for SD/MMC cards resp.
-        the "output aspect" of card readers. We do not not have at
-        present enough background information to properly extract a
-        vendor and product ID from these cards.
-
-        info.bus == 'platform' is used for devices like the i8042
-        which controls keyboard and mouse; HAL has no vendor
-        information for these devices, so there is no point to
-        treat them as real devices.
-
-        info.bus == 'pnp' is used for components like the ancient
-        AT DMA controller or the keyboard. Like for the bus
-        'platform', HAL does not provide any vendor data.
-
-        info.bus == 'power_supply' is used by the HAL version in
-        Intrepid for AC adapters an laptop batteries. We don't have
-        at present enough information about possible problems with
-        missing vendor/product information in order to store the
-        data reliably in the HWDB.
-
-        raw_bus == 'acpi' is used in udev data for the main system,
-        for CPUs, power supply etc. Except for the main sytsem, none
-        of them provides a vendor or product id, so we ignore them.
-
-        raw_bus == 'video_output', 'thermal', 'vtconsole', 'bdi',
-        'mem', 'ppp', 'vc', 'dmi', 'hidraw', 'hwmon', 'heci', 'rfkill',
-        'i2c-adapter', 'ttm', 'ppdev', 'printer', 'cardman_4040', 'msr',
-        'ieee1394_protocol', 'dahdi', 'atm', 'asus_oled', 'pktcdvd' is
-        used in submissions with udev data for virtual devices.
-
-        'pci_bus' is used in submissions with udev data for a node
-        describing a PCI bus.
-
-        'leds' is used in submissions with udev data to describe LEDs.
-
-        XXX Abel Deuring 2008-05-06: IEEE1394 devices are a bit
-        nasty: The standard does not define any specification
-        for product IDs or product names, hence HAL often uses
-        the value 0 for the property ieee1394.product_id and a
-        value like "Unknown (0x00d04b)" for ieee.product, where
-        0x00d04b is the vendor ID. I have currently no idea how
-        to find or generate something that could be used as the
-        product ID, so IEEE1394 devices are at present simply
-        not stored in the HWDB. Otherwise, we'd pollute the HWDB
-        with unreliable data. Bug #237044.
-
-        While PCMCIA devices have a manufacturer ID, at least its
-        value as provided by HAL in pcmcia.manf_id it is not very
-        reliable. The HAL property pcmcia.prod_id1 is too not
-        reliable. Sometimes it contains a useful vendor name like
-        "O2Micro" or "ATMEL", but sometimes useless values like
-        "IEEE 802.11b". See for example
-        drivers/net/wireless/atmel_cs.c in the Linux kernel sources.
-
-        Provided that a device is not excluded by the above criteria,
-        ensure that we have vendor ID, product ID and product name.
-        """
-        bus = self.raw_bus
-        if bus in ('unknown', 'acpi') and not self.is_root_device:
-            # The root node is course a real device; storing data
-            # about other devices with the bus "unkown" is pointless.
-            return False
-        if bus in ('asus_oled', 'atm', 'backlight', 'bdi', 'bluetooth',
-                    'cardman_4040', 'dahdi', 'dmi', 'heci', 'hidraw', 'hwmon',
-                   'i2c-adapter', 'ieee1394', 'ieee1394_protocol', 'input',
-                   'leds', 'mem', 'misc', 'mmc', 'mmc_host', 'msr', 'pci_bus',
-                   'pcmcia', 'pktcdvd', 'platform', 'pnp', 'power_supply',
-                   'ppdev', 'ppp', 'printer', 'rfkill', 'thermal', 'ttm',
-                   'vc', 'video_output', 'vtconsole'):
-            return False
-
-        # We identify devices by bus, vendor ID and product ID;
-        # additionally, we need a product name. If any of these
-        # are not available, we can't store information for this
-        # device.
-        if (self.real_bus is None or self.vendor_id is None
-            or self.product_id is None or self.product is None):
-            # Many IDE devices don't provide useful vendor and product
-            # data. We don't want to clutter the log with warnings
-            # about this problem -- there is nothing we can do to fix
-            # it.
-            if self.real_bus != HWBus.IDE:
-                self.parser._logWarning(
-                    'A %s that is supposed to be a real device does '
-                    'not provide bus, vendor ID, product ID or product name: '
-                    '%r %r %r %r %s'
-                    % (self.__class__.__name__, self.real_bus, self.vendor_id,
-                       self.product_id, self.product, self.device_id),
-                    self.parser.submission_key)
-            return False
-        return True
-
-    def getScsiVendorAndModelName(self):
-        """Separate vendor and model name of SCSI decvices.
-
-        SCSI devcies are identified by an 8 charcter vendor name
-        and an 16 character model name. The Linux kernel use the
-        the SCSI command set to access block devices connected
-        via USB, IEEE1394 and ATA buses too.
-
-        For ATA disks, the Linux kernel sets the vendor name to "ATA"
-        and prepends the model name with the real vendor name, but only
-        if the combined length if not larger than 16. Otherwise the
-        real vendor name is omitted.
-
-        This method provides a safe way to retrieve the  the SCSI vendor
-        and model name.
-
-        If the vendor name is 'ATA', and if the model name contains
-        at least one ' ' character, the string before the first ' ' is
-        returned as the vendor name, and the string after the first
-        ' ' is returned as the model name.
-
-        In all other cases, vendor and model name are returned unmodified.
-        """
-        vendor = self.scsi_vendor
-        if vendor == 'ATA':
-            # The assumption below that the vendor name does not
-            # contain any spaces is not necessarily correct, but
-            # it is hard to find a better heuristic to separate
-            # the vendor name from the product name.
-            splitted_name = self.scsi_model.split(' ', 1)
-            if len(splitted_name) == 2:
-                return {
-                    'vendor': splitted_name[0],
-                    'product': splitted_name[1],
-                    }
-        return {
-            'vendor': vendor,
-            'product': self.scsi_model,
-            }
-
-    def getDriver(self):
-        """Return the HWDriver instance associated with this device.
-
-        Create a HWDriver record, if it does not already exist.
-        """
-        # HAL and the HWDB client know at present only about kernel
-        # drivers, so there is currently no need to search for
-        # for user space printer drivers, for example.
-        if self.driver_name is not None:
-            db_driver_set = getUtility(IHWDriverSet)
-            return db_driver_set.getOrCreate(
-                self.parser.kernel_package_name, self.driver_name)
-        else:
-            return None
-
-    def ensureVendorIDVendorNameExists(self):
-        """Ensure that a useful HWVendorID record for self.vendor_id exists.
-
-        A vendor ID is associated with a vendor name. For many devices
-        we rely on the information from the submission to create this
-        association in the HWVendorID table.
-
-        We do _not_ use the submitted vendor name for USB, PCI and
-        PCCard devices, because we can get them from independent
-        sources. See l/c/l/doc/hwdb-device-tables.txt.
-        """
-        bus = self.real_bus
-        if (self.vendor is not None and
-            bus not in (HWBus.PCI, HWBus.PCCARD, HWBus.USB)):
-            hw_vendor_id_set = getUtility(IHWVendorIDSet)
-            hw_vendor_id = hw_vendor_id_set.getByBusAndVendorID(
-                bus, self.vendor_id_for_db)
-            if hw_vendor_id is None:
-                hw_vendor_name_set = getUtility(IHWVendorNameSet)
-                hw_vendor_name = hw_vendor_name_set.getByName(self.vendor)
-                if hw_vendor_name is None:
-                    hw_vendor_name = hw_vendor_name_set.create(self.vendor)
-                hw_vendor_id_set.create(
-                    self.real_bus, self.vendor_id_for_db, hw_vendor_name)
-
-    def createDBData(self, submission, parent_submission_device):
-        """Create HWDB records for this HAL device and its children.
-
-        A HWDevice record for (bus, vendor ID, product ID) of this
-        device and a HWDeviceDriverLink record (device, None) are
-        created, if they do not already exist.
-
-        A HWSubmissionDevice record is created for (HWDeviceDriverLink,
-        submission).
-
-        HWSubmissionDevice records and missing HWDeviceDriverLink
-        records for known drivers of this device are created.
-
-        createDBData is called recursively for all real child devices.
-
-        This method may only be called, if self.real_device == True.
-        """
-        assert self.is_real_device, ('HALDevice.createDBData must be called '
-                                     'for real devices only.')
-        if not self.has_reliable_data:
-            return
-        bus = self.real_bus
-        vendor_id = self.vendor_id_for_db
-        product_id = self.product_id_for_db
-        product_name = self.product
-
-        self.ensureVendorIDVendorNameExists()
-
-        db_device = getUtility(IHWDeviceSet).getOrCreate(
-            bus, vendor_id, product_id, product_name)
-        # Create a HWDeviceDriverLink record without an associated driver
-        # for each real device. This will allow us to relate tests and
-        # bugs to a device in general as well as to a specific
-        # combination of a device and a driver.
-        device_driver_link = getUtility(IHWDeviceDriverLinkSet).getOrCreate(
-            db_device, None)
-        submission_device = getUtility(IHWSubmissionDeviceSet).create(
-            device_driver_link, submission, parent_submission_device,
-            self.id)
-        self.createDBDriverData(submission, db_device, submission_device)
-
-    def createDBDriverData(self, submission, db_device, submission_device):
-        """Create HWDB records for drivers of this device and its children.
-
-        This method creates HWDeviceDriverLink and HWSubmissionDevice
-        records for this device and its children.
-        """
-        driver = self.getDriver()
-        if driver is not None:
-            device_driver_link_set = getUtility(IHWDeviceDriverLinkSet)
-            device_driver_link = device_driver_link_set.getOrCreate(
-                db_device, driver)
-            submission_device = getUtility(IHWSubmissionDeviceSet).create(
-                device_driver_link, submission, submission_device, self.id)
-        for sub_device in self.children:
-            if sub_device.is_real_device:
-                sub_device.createDBData(submission, submission_device)
-            else:
-                sub_device.createDBDriverData(submission, db_device,
-                                              submission_device)
-
-
-class HALDevice(BaseDevice):
-    """The representation of a HAL device node."""
-
-    def __init__(self, id, udi, properties, parser):
-        """HALDevice constructor.
-
-        :param id: The ID of the HAL device in the submission data as
-            specified in <device id=...>.
-        :type id: int
-        :param udi: The UDI of the HAL device.
-        :type udi: string
-        :param properties: The HAL properties of the device.
-        :type properties: dict
-        :param parser: The parser processing a submission.
-        :type parser: SubmissionParser
-        """
-        super(HALDevice, self).__init__(parser)
-        self.id = id
-        self.udi = udi
-        self.properties = properties
-
-    def getProperty(self, property_name):
-        """Return the HAL property property_name.
-
-        Note that there is no check of the property type.
-        """
-        if property_name not in self.properties:
-            return None
-        name, type_ = self.properties[property_name]
-        return name
-
-    @property
-    def parent_udi(self):
-        """The UDI of the parent device."""
-        return self.getProperty('info.parent')
-
-    @property
-    def device_id(self):
-        """See `BaseDevice`."""
-        return self.udi
-
-    @property
-    def pci_class(self):
-        """See `BaseDevice`."""
-        return self.getProperty('pci.device_class')
-
-    @property
-    def pci_subclass(self):
-        """The PCI device sub-class of the device or None for Non-PCI devices.
-        """
-        return self.getProperty('pci.device_subclass')
-
-    @property
-    def usb_vendor_id(self):
-        """See `BaseDevice`."""
-        return self.getProperty('usb_device.vendor_id')
-
-    @property
-    def usb_product_id(self):
-        """See `BaseDevice`."""
-        return self.getProperty('usb_device.product_id')
-
-    @property
-    def scsi_vendor(self):
-        """See `BaseDevice`."""
-        return self.getProperty('scsi.vendor')
-
-    @property
-    def scsi_model(self):
-        """See `BaseDevice`."""
-        return self.getProperty('scsi.model')
-
-    @property
-    def driver_name(self):
-        """See `BaseDevice`."""
-        return self.getProperty('info.linux.driver')
-
-    @property
-    def raw_bus(self):
-        """See `BaseDevice`."""
-        # Older versions of HAL stored this value in the property
-        # info.bus; newer versions store it in info.subsystem.
-        #
-        # Note that info.bus is gone for all devices except the
-        # USB bus. For USB devices, the property info.bus returns more
-        # detailed data: info.subsystem has the value 'usb' for all
-        # HAL nodes belonging to USB devices, while info.bus has the
-        # value 'usb_device' for the root node of a USB device, and the
-        # value 'usb' for sub-nodes of a USB device. We use these
-        # different value to to find the root USB device node, hence
-        # try to read info.bus first.
-        result = self.getProperty('info.bus')
-        if result is not None:
-            return result
-        return self.getProperty('info.subsystem')
-
-    @property
-    def is_root_device(self):
-        """See `BaseDevice`."""
-        return self.udi == ROOT_UDI
-
-    def getVendorOrProduct(self, type_):
-        """Return the vendor or product of this device.
-
-        :return: The vendor or product data for this device.
-        :param type_: 'vendor' or 'product'
-        """
-        # HAL does not store vendor data very consistently. Try to find
-        # the data in several places.
-        assert type_ in ('vendor', 'product'), (
-            'Unexpected value of type_: %r' % type_)
-
-        bus = self.raw_bus
-        if self.udi == ROOT_UDI:
-            # HAL sets info.product to "Computer", provides no property
-            # info.vendor and raw_bus is "unknown", hence the logic
-            # below does not work properly.
-            return self.getProperty('system.hardware.' + type_)
-        elif bus == 'scsi':
-            return self.getScsiVendorAndModelName()[type_]
-        else:
-            result = self.getProperty('info.' + type_)
-            if result is None:
-                if bus is None:
-                    return None
-                else:
-                    return self.getProperty('%s.%s' % (bus, type_))
-            else:
-                return result
-
-    @property
-    def vendor(self):
-        """See `BaseDevice`."""
-        return self.getVendorOrProduct('vendor')
-
-    @property
-    def product(self):
-        """See `BaseDevice`."""
-        return self.getVendorOrProduct('product')
-
-    def getVendorOrProductID(self, type_):
-        """Return the vendor or product ID for this device.
-
-        :return: The vendor or product ID for this device.
-        :param type_: 'vendor' or 'product'
-        """
-        assert type_ in ('vendor', 'product'), (
-            'Unexpected value of type_: %r' % type_)
-        bus = self.raw_bus
-        if self.udi == ROOT_UDI:
-            # HAL does not provide IDs for a system itself, we use the
-            # vendor resp. product name instead.
-            return self.getVendorOrProduct(type_)
-        elif bus is None:
-            return None
-        elif bus == 'scsi' or self.udi == ROOT_UDI:
-            # The SCSI specification does not distinguish between a
-            # vendor/model ID and vendor/model name: the SCSI INQUIRY
-            # command returns an 8 byte string as the vendor name and
-            # a 16 byte string as the model name. We use these strings
-            # as the vendor/product name as well as the vendor/product
-            # ID.
-            #
-            # Similary, HAL does not provide a vendor or product ID
-            # for the host system itself, so we use the vendor resp.
-            # product name as the vendor/product ID for systems too.
-            return self.getVendorOrProduct(type_)
-        else:
-            return self.getProperty('%s.%s_id' % (bus, type_))
-
-    @property
-    def vendor_id(self):
-        """See `BaseDevice`."""
-        return self.getVendorOrProductID('vendor')
-
-    @property
-    def product_id(self):
-        """See `BaseDevice`."""
-        return self.getVendorOrProductID('product')
-
-    @property
-    def scsi_controller(self):
-        """See `BaseDevice`."""
-        # While SCSI devices from valid submissions should have a
-        # parent and a grandparent, we can't be sure for bogus or
-        # broken submissions.
-        if self.raw_bus != 'scsi':
-            return None
-        parent = self.parent
-        if parent is None:
-            self.parser._logWarning(
-                'Found SCSI device without a parent: %s.' % self.device_id)
-            return None
-        grandparent = parent.parent
-        if grandparent is None:
-            self.parser._logWarning(
-                'Found SCSI device without a grandparent: %s.'
-                % self.device_id)
-            return None
-        return grandparent
-
-
-class UdevDevice(BaseDevice):
-    """The representation of a udev device node."""
-
-    def __init__(self, parser, udev_data, sysfs_data=None, dmi_data=None):
-        """HALDevice constructor.
-
-        :param udevdata: The udev data for this device
-        :param sysfs_data: sysfs data for this device.
-        :param parser: The parser processing a submission.
-        :type parser: SubmissionParser
-        """
-        super(UdevDevice, self).__init__(parser)
-        self.udev = udev_data
-        self.sysfs = sysfs_data
-        self.dmi = dmi_data
-
-    @property
-    def device_id(self):
-        """See `BaseDevice`."""
-        return self.udev['P']
-
-    @property
-    def root_device_ids(self):
-        """The vendor and product IDs of the root device."""
-        return {
-            'vendor': self.dmi.get('/sys/class/dmi/id/sys_vendor'),
-            'product': self.dmi.get('/sys/class/dmi/id/product_name')
-            }
-
-    @property
-    def is_pci(self):
-        """True, if this is a PCI device, else False."""
-        return self.udev['E'].get('SUBSYSTEM') == 'pci'
-
-    @property
-    def pci_class_info(self):
-        """Parse the udev property PCI_SUBSYS_ID.
-
-        :return: (PCI class, PCI sub-class, version) for a PCI device
-            or (None, None, None) for other devices.
-        """
-        if self.is_pci:
-            # SubmissionParser.checkConsistentUdevDeviceData() ensures
-            # that PCI_CLASS is a 24 bit integer in hexadecimal
-            # representation.
-            # Bits 16..23 of the number are the man PCI class,
-            # bits 8..15 are the sub-class, bits 0..7 are the version.
-            class_info = int(self.udev['E']['PCI_CLASS'], 16)
-            return (class_info >> 16, (class_info >> 8) & 0xFF,
-                    class_info & 0xFF)
-        else:
-            return (None, None, None)
-
-    @property
-    def pci_class(self):
-        """See `BaseDevice`."""
-        return self.pci_class_info[0]
-
-    @property
-    def pci_subclass(self):
-        """See `BaseDevice`."""
-        return self.pci_class_info[1]
-
-    @property
-    def pci_ids(self):
-        """The PCI vendor and product IDs.
-
-        :return: A dictionary containing the vendor and product IDs.
-            The IDs are set to None for Non-PCI devices.
-        """
-        if self.is_pci:
-            # SubmissionParser.checkUdevPciProperties() ensures that
-            # each PCI device has the property PCI_ID and that is
-            # consists of two 4-digit hexadecimal numbers, separated
-            # by a ':'.
-            id_string = self.udev['E']['PCI_ID']
-            ids = id_string.split(':')
-            return {
-                'vendor': int(ids[0], 16),
-                'product': int(ids[1], 16),
-                }
-        else:
-            return  {
-                'vendor': None,
-                'product': None,
-                }
-
-    @property
-    def is_usb(self):
-        """True, if this is a USB device, else False."""
-        return self.udev['E'].get('SUBSYSTEM') == 'usb'
-
-    @property
-    def usb_ids(self):
-        """The vendor ID, product ID, product version for USB devices.
-
-        :return: A dictionary containing the vendor and product IDs and
-            the product version for USB devices.
-            The IDs are set to None for Non-USB devices.
-        """
-        if self.is_usb:
-            # udev represents USB device IDs as strings
-            # vendor_id/product_id/version, where each part is
-            # a hexadecimal number.
-            # SubmissionParser.checkUdevUsbProperties() ensures that
-            # the string PRODUCT is in the format required below.
-            product_info = self.udev['E']['PRODUCT'].split('/')
-            return {
-                'vendor': int(product_info[0], 16),
-                'product': int(product_info[1], 16),
-                'version': int(product_info[2], 16),
-                }
-        else:
-            return {
-                'vendor': None,
-                'product': None,
-                'version': None,
-                }
-
-    @property
-    def usb_vendor_id(self):
-        """See `BaseDevice`."""
-        return self.usb_ids['vendor']
-
-    @property
-    def usb_product_id(self):
-        """See `BaseDevice`."""
-        return self.usb_ids['product']
-
-    @property
-    def is_scsi_device(self):
-        """True, if this is a SCSI device, else False."""
-        # udev sets the property SUBSYSTEM to "scsi" for a number of
-        # different nodes: SCSI hosts, SCSI targets and SCSI devices.
-        # They are distiguished by the property DEVTYPE.
-
-        # Hack for broken submissions from Lucid, Maverick and Natty:
-        # If we don't have sysfs information, pretend that no SCSI
-        # related node corresponds to a real device.
-        # See also bug 835103.
-        if self.sysfs is None:
-            return False
-        properties = self.udev['E']
-        return (properties.get('SUBSYSTEM') == 'scsi' and
-                properties.get('DEVTYPE') == 'scsi_device')
-
-    @property
-    def scsi_vendor(self):
-        """The SCSI vendor name of the device or None for Non-SCSI devices."""
-        if self.is_scsi_device:
-            # SubmissionParser.checkUdevScsiProperties() ensures that
-            # each SCSI device has a record in self.sysfs and that
-            # the attribute 'vendor' exists.
-            return self.sysfs['vendor']
-        else:
-            return None
-
-    @property
-    def scsi_model(self):
-        """The SCSI model name of the device or None for Non-SCSI devices."""
-        if self.is_scsi_device:
-            # SubmissionParser.checkUdevScsiProperties() ensures that
-            # each SCSI device has a record in self.sysfs and that
-            # the attribute 'model' exists.
-            return self.sysfs['model']
-        else:
-            return None
-
-    @property
-    def raw_bus(self):
-        """See `BaseDevice`."""
-        # udev specifies the property SUBSYSTEM for most devices;
-        # some devices have additionally the more specific property
-        # DEVTYPE. DEVTYPE is preferable.
-        # The root device has the subsystem/bus value "acpi", which
-        # is a bit nonsensical.
-        if self.is_root_device:
-            return None
-        properties = self.udev['E']
-        devtype = properties.get('DEVTYPE')
-        if devtype is not None:
-            return devtype
-        subsystem = properties.get('SUBSYSTEM')
-        # A real mess: The main node of a SCSI device has
-        # SUBSYSTEM = 'scsi' and DEVTYPE = 'scsi_device', while
-        # a sub-node has SUBSYSTEM='scsi_device'. We don't want
-        # the two to be confused. The latter node is not of any
-        # interest for us, so we return None. This ensures that
-        # is_real_device returns False for the sub-node.
-        if subsystem != 'scsi_device':
-            return subsystem
-        else:
-            return None
-
-    @property
-    def is_root_device(self):
-        """See `BaseDevice`."""
-        return self.udev['P'] == UDEV_ROOT_PATH
-
-    def getVendorOrProduct(self, type_):
-        """Return the vendor or product of this device.
-
-        :return: The vendor or product data for this device.
-        :param type_: 'vendor' or 'product'
-        """
-        assert type_ in ('vendor', 'product'), (
-            'Unexpected value of type_: %r' % type_)
-
-        bus = self.raw_bus
-        if self.is_root_device:
-            # udev does not known about any product information for
-            # the root device. We use DMI data instead.
-            return self.root_device_ids[type_]
-        elif bus == 'scsi_device':
-            return self.getScsiVendorAndModelName()[type_]
-        elif bus in ('pci', 'usb_device'):
-            # XXX Abel Deuring 2009-10-13, bug 450480: udev does not
-            # provide human-readable vendor and product names for
-            # USB and PCI devices. We should retrieve these from
-            # http://www.linux-usb.org/usb.ids and
-            # http://pciids.sourceforge.net/v2.2/pci.ids
-            return 'Unknown'
-        else:
-            # We don't process yet other devices than complete systems,
-            # PCI, USB devices and those devices that are represented
-            # in udev as SCSI devices: real SCSI devices, and
-            # IDE/ATA/SATA devices.
-            return None
-
-    @property
-    def vendor(self):
-        """See `BaseDevice`."""
-        return self.getVendorOrProduct('vendor')
-
-    @property
-    def product(self):
-        """See `BaseDevice`."""
-        return self.getVendorOrProduct('product')
-
-    def getVendorOrProductID(self, type_):
-        """Return the vendor or product ID of this device.
-
-        :return: The vendor or product ID for this device.
-        :param type_: 'vendor' or 'product'
-        """
-        assert type_ in ('vendor', 'product'), (
-            'Unexpected value of type_: %r' % type_)
-
-        bus = self.raw_bus
-        if self.is_root_device:
-            # udev does not known about any product information for
-            # the root device. We use DMI data instead.
-            if type_ == 'vendor':
-                return self.dmi.get('/sys/class/dmi/id/sys_vendor')
-            else:
-                return self.dmi.get('/sys/class/dmi/id/product_name')
-        elif bus == 'scsi_device':
-            return self.getScsiVendorAndModelName()[type_]
-        elif bus == 'pci':
-            return self.pci_ids[type_]
-        elif bus == 'usb_device':
-            return self.usb_ids[type_]
-        else:
-            # We don't process yet other devices than complete systems,
-            # PCI, USB devices and those devices that are represented
-            # in udev as SCSI devices: real SCSI devices, and
-            # IDE/ATA/SATA devices.
-            return None
-
-    @property
-    def vendor_id(self):
-        """See `BaseDevice`."""
-        return self.getVendorOrProductID('vendor')
-
-    @property
-    def product_id(self):
-        """See `BaseDevice`."""
-        return self.getVendorOrProductID('product')
-
-    @property
-    def driver_name(self):
-        """See `BaseDevice`."""
-        return self.udev['E'].get('DRIVER')
-
-    @property
-    def scsi_controller(self):
-        """See `BaseDevice`."""
-        if self.raw_bus != 'scsi_device':
-            return None
-
-        # While SCSI devices from valid submissions should have four
-        # ancestors, we can't be sure for bogus or broken submissions.
-        try:
-            controller = self.parent.parent.parent
-        except AttributeError:
-            controller = None
-        if controller is None:
-            self.parser._logWarning(
-                'Found a SCSI device without a sufficient number of '
-                'ancestors: %s' % self.device_id)
-            return None
-        return controller
-
-    @property
-    def id(self):
-        return self.udev['id']
diff --git a/lib/lp/hardwaredb/scripts/tests/__init__.py b/lib/lp/hardwaredb/scripts/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/lib/lp/hardwaredb/scripts/tests/__init__.py
+++ /dev/null
diff --git a/lib/lp/hardwaredb/scripts/tests/hardwaretest-natty.xml b/lib/lp/hardwaredb/scripts/tests/hardwaretest-natty.xml
deleted file mode 100644
index d09d178..0000000
--- a/lib/lp/hardwaredb/scripts/tests/hardwaretest-natty.xml
+++ /dev/null
@@ -1,473 +0,0 @@
-<?xml version="1.0" ?>
-<system version="1.0">
-
-  <!-- Reports coming from the checkbox version in Natty do not
-       have the tags <udev> and <dmi> inside <hardware>. Instead,
-       they store the data expected in these tags in the tags
-
-       <info command="udevadm info - -export-db"> and
-       <info command="grep -r . /sys/class/dmi/id/ 2&gt;/dev/null">
-
-       inside <
-  -->
-  <!-- summary: generic information about the submission -->
-  <summary>
-
-    <!-- live_cd: Was this submission made on a system running an Ubuntu Live
-             CD or on a regular Ubuntu/Linux installation?
-    -->
-    <live_cd value="False"/>
-
-    <!-- system_id: A hash of the "system identifier". This value is intended
-             to identify the tested computer model; the value should
-             be derived from the properties
-             system.product, system.vendor of the HAL UDI
-             /org/freedesktop/Hal/devices/computer.
-    -->
-    <system_id value="f982bb1ab536469cebfd6eaadcea0ffc"/>
-
-    <!-- distribution, distroseries: These values are retrieved from
-             /etc/lsb-release, parameters DISTRIB_ID and DISTRIB_RELEASE.
-    -->
-    <distribution value="Ubuntu"/>
-    <distroseries value="7.04"/>
-
-    <!-- architecture: The processor architecture of the operating system.
-    -->
-    <architecture value="amd64"/>
-
-    <!-- private: If False, this submission is publicly accessible from
-             Launchpad, else it is only accesible by the submitter, by
-             Launchpad administrators and by scripts running with
-             administrator rights. Submissions marked "private" should
-             only be used to gather statistical data.
-    -->
-    <private value="False"/>
-
-    <!-- contactable: If True, the owner agrees to be contacted by other
-             persons about devices which appear in their submission.
-             Example of a use case: Developers can ask device owners
-             to perform tests.
-    -->
-    <contactable value="False"/>
-
-    <!-- date_created: Date and time (UTC) of the submission.
-    -->
-    <date_created value="2007-09-28T16:09:20.126842"/>
-
-    <!-- client: The name and version of the program that created the
-             submission data.
-    -->
-    <client name="hwtest" version="0.9">
-
-      <!-- plugin: name and version of a plugin used by the client.
-               This tag may appear more than once.
-      -->
-      <plugin name="architecture_info" version="1.1"/>
-      <plugin name="find_network_controllers" version="2.34"/>
-      <plugin name="internet_ping" version="1.1"/>
-      <plugin name="harddisk_speed" version="0.7"/>
-    </client>
-
-    <!-- The kernel name and version, as shown by "uname -r"
-    -->
-    <kernel-release value="2.6.28-14-generic"/>
-  </summary>
-
-  <!-- hardware: data about the hardware the submission was made on.
-  -->
-  <hardware>
-
-    <!-- udev: The output of running "udevadm info - -export-db" -->
-
-
-    <!-- Additional data for SCSI devices: vendor, model, type
-
-         For each udev node which has DEVTYPE=scsi_device, we need
-         the content of the sysfs files vendor, model, type. The data
-         is stored in the same format as the DMI data:
-         /path/to/file:filecontent
-    -->
-    <sysfs-attributes>
-P: /devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
-A: modalias=input:b0019v0000p0001e0000-e0,1,k74,ramlsfw
-A: uniq=
-A: phys=LNXPWRBN/button/input0
-A: name=Power Button
-
-P: /devices/LNXSYSTM:00/device:00/PNP0A08:00/device:03/input/input8
-A: modalias=input:b0019v0000p0006e0000-e0,1,kE0,E1,E3,F0,F1,F2,F3,F4,F5,ramlsfw
-A: uniq=
-A: phys=/video/input0
-A: name=Video Bus
-</sysfs-attributes>
-
-    <!-- processors: Data about processors installed in a system.
-             The data is retrieved from /proc/cpuinfo.
-    -->
-    <processors>
-
-      <!-- processor: Data from /proc/cpuinfo about a single processor.
-      -->
-      <processor id="123" name="0">
-
-        <!-- property: The data of one line of /proc/cpuinfo.
-                 attribute name: The name of the property
-                     (the text left of the ':' in a line of /proc/cpuinfo)
-                 attribute type: A Python type appropriate for the value.
-        -->
-        <property name="wp" type="bool">
-                True
-        </property>
-        <property name="flags" type="list">
-          <value type="str">
-                  fpu
-          </value>
-          <value type="str">
-                  vme
-          </value>
-          <value type="str">
-                  de
-          </value>
-        </property>
-        <property name="cpu_mhz" type="float">
-                1000.0
-        </property>
-      </processor>
-    </processors>
-
-      <!-- aliases: optional data provided by the user:
-               The name of a peripheral, PCI card etc as shown by a label on
-               the device. OEM devices are often sold under different names
-               by different vendors; having a set of alias names for a device
-               allows users of the HWDB to search for information by these
-               "marketing names".
-      -->
-    <aliases>
-      <!-- alias: The "label name" of a device or system.
-               attribute target: The sysfs path of a device as given
-               in <udev>.
-      -->
-      <alias target="/devices/pci0000:00/0000:00:1f.1/host3/target3:0:0/3:0:0:0">
-
-        <!-- vendor: The vendor name shown on the device label.
-        -->
-        <vendor>Medion</vendor>
-
-        <!-- model: The model name of shown on the label.
-        -->
-        <model>QuickPrint 9876</model>
-      </alias>
-    </aliases>
-  </hardware>
-
-  <!-- software: Data about the software installed on the system.
-  -->
-  <software>
-
-    <!-- lsbrelease: The data from /etc/lsb-release.
-    -->
-    <lsbrelease>
-
-      <!-- property: the data from one line of /etc/lsb-release.
-               attribute type: A Python type appropriate for this
-                   property (str).
-      -->
-      <property name="release" type="str">
-              7.04
-      </property>
-      <property name="codename" type="str">
-              feisty
-      </property>
-      <property name="distributor-id" type="str">
-              Ubuntu
-      </property>
-      <property name="description" type="str">
-              Ubuntu 7.04
-      </property>
-      <property name="dict_example" type="dict">
-          <value name="a" type="str">value for key a</value>
-          <value name="b" type="int">1234</value>
-      </property>
-    </lsbrelease>
-
-    <!-- packages: Data about the installed software packages.
-    -->
-    <packages>
-
-      <!-- package: Data about a single package.
-               The <property> sub-tags contain the DEB properties
-               "name", "priority", "section", "source", "version",
-               "installed_size", "size", "summary".
-
-               XXX Abel Deuring 2007-12-12: What about submissions
-               from RPM-based Linux versions? (And "exotic" variants
-               like Gentoo?)
-      -->
-      <package name="metacity" id="200">
-        <property name="installed_size" type="int">
-                868352
-        </property>
-        <property name="section" type="str">
-                x11
-        </property>
-        <property name="summary" type="str">
-                A lightweight GTK2 based Window Manager
-        </property>
-        <property name="priority" type="str">
-                optional
-        </property>
-        <property name="source" type="str">
-                metacity
-        </property>
-        <property name="version" type="str">
-                1:2.18.2-0ubuntu1.1
-        </property>
-        <property name="size" type="int">
-                429128
-        </property>
-      </package>
-    </packages>
-    <!-- Information extracted from Xorg.0.log.
-         HAL does not provide any information about Xorg drivers, so
-         we retrieve that from the xserver's log file.
-    -->
-    <xorg version="1.3.0">
-      <!-- driver: Data about a driver.
-               (optional)
-               attribute name: The name of the driver.
-               attribute version: The version of the driver.
-               attribute class: The module class of the driver
-               attribute device: The ID of a device driven by this driver.
-      -->
-      <driver name="fglrx" version="1.23" class="X.Org Video Driver"
-              device="12"/>
-    </xorg>
-  </software>
-
-  <!-- questions: User's answers to questions asked by the client.
-  -->
-  <questions>
-
-    <!-- question: Data of a question.
-             attribute name: The unique name of the question.
-             attribute plugin: The name of the plugin which asked
-                 the question.
-             attribute version: The version of the question.
-             attribute type: Allowed values are "manual" and "automatic".
-                 A "manual" question requires user input for the answer;
-                 an "automatic" question gets the answer automatically.
-    -->
-    <question name="detected_network_controllers"
-              plugin="find_network_controllers">
-
-      <!-- target: Information about a device or software package the
-           question is about. The attribute "id" is the sysfs path of
-           a device as given in <udev>, or the ID of a software package
-           node.
-           This node may appear multiple times.
-       -->
-      <target id="/devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0/usb_endpoint/usbdev3.1_ep81">
-        <!-- driver: The driver which controls the target device. This tag
-                 may appear more than once.
-
-                 While we are working on a project called "Hardware Database",
-                 we are not that much interested in the question, if a device
-                 works "as such", but if their Linux driver(s) work.
-
-                 It is not in every case possible to identify the used driver
-                 from HAL data, so we need another way to add this information.
-                 (example: HAL does not know, which driver is used for the
-                 graphics card.)
-
-                 Example for multiple drivers: Some scanners have a SCSI _and_
-                 a USB interface; if such a scanner is tested, we not only want
-                 to know, which Sane backend is used, but also, which interface
-                 is used.
-
-                 Also, it might be interesting to know for many USB 2.0 devices,
-                 if a USB 1 (uhci_hcd or ohci_hcd driver) or the USB 2 driver
-                 (ehci_hcd) was used. A USB 1 driver might for example explain
-                 latency problems.
-        -->
-        <driver>ipw3945</driver>
-      </target>
-
-      <!-- ID of the 88E8055 PCI-E Gigabit Ethernet Controller -->
-      <target id="/devices/pci0000:00/0000:00:1f.1"/>
-
-      <!-- command: The command line of an external command required to
-               ask this question.
-      -->
-      <command/>
-
-      <!-- answer: The answer to the question. Two types of answers are
-               defined, "multiple_choice" and "measurement". (See below
-               for an example of the latter.)
-               attribute type: Must be "multiple_choice" or "measurement".
-      -->
-      <answer type="multiple_choice">pass</answer>
-
-      <!-- answer_choices: The list of possible choices.
-               The data should only be used for consistency
-               checks and to detect variants of the question.
-      -->
-      <answer_choices>
-        <value type="str">fail</value>
-        <value type="str">pass</value>
-        <value type="str">skip</value>
-      </answer_choices>
-
-      <!-- A user comment about the device or about the test.
-      -->
-      <comment>
-        The WLAN adapter drops the connection very frequently.
-      </comment>
-    </question>
-
-    <question name="internet_ping"
-              plugin="internet_ping">
-      <target id="/devices/pci0000:00/0000:00:1f.1"/>
-      <command/>
-      <answer type="multiple_choice">pass</answer>
-      <answer_choices>
-          <value type="str">fail</value>
-          <value type="str">pass</value>
-          <value type="str">skip</value>
-      </answer_choices>
-    </question>
-
-    <!-- example for a "measurement question"
-    -->
-    <question name="harddisk_speed"
-              plugin="harddisk_speed">
-      <target id="/devices/pci0000:00/0000:00:1f.1/host3/target3:0:0/3:0:0:0"/>
-      <command>hdparm -t /dev/sda</command>
-      <!-- answer: The answer to a "measurement question".
-               attribute type: See above.
-               attribute unit: The unit of the result of the measurement.
-                   XXX Abel Deuring 2007-12-12 bug=175978 We should
-                   enumerate a list of allowed units, in order to avoid
-                   multiple units for the same dimension. e.g., B/sec,
-                   MB/sec or inch, cm, foot.
-
-                   For dimensionless values, the attribute unit is omitted.
-
-                   "Percentage" and similar "convenience pseudo-units" like
-                   ppm are _not_ allowed; instead a dimensionless
-                   value must be used, where 0 is equivalent 0% and 1.0 is
-                   equivalent to 100%.
-      -->
-      <answer type="measurement" unit="MB/sec">38.4</answer>
-    </question>
-  </questions>
-  <!-- miscellaneous additional text data.
-  -->
-  <context>
-    <info command="udevadm info --export-db">P: /devices/LNXSYSTM:00
-E: UDEV_LOG=3
-E: DEVPATH=/devices/LNXSYSTM:00
-E: MODALIAS=acpi:LNXSYSTM:
-
-P: /devices/pci0000:00/0000:00:1a.0
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1a.0
-E: DRIVER=uhci_hcd
-E: PCI_CLASS=C0300
-E: PCI_ID=8086:2834
-E: PCI_SUBSYS_ID=17AA:20AA
-E: PCI_SLOT_NAME=0000:00:1a.0
-E: MODALIAS=pci:v00008086d00002834sv000017AAsd000020AAbc0Csc03i00
-
-P: /devices/pci0000:00/0000:00:1a.0/usb3
-N: bus/usb/003/001
-S: char/189:256
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb3
-E: MAJOR=189
-E: MINOR=256
-E: DEVTYPE=usb_device
-E: DRIVER=usb
-E: DEVICE=/proc/bus/usb/003/001
-E: PRODUCT=1d6b/1/206
-E: TYPE=9/0/0
-E: BUSNUM=003
-E: DEVNUM=001
-E: DEVNAME=/dev/bus/usb/003/001
-E: DEVLINKS=/dev/char/189:256
-
-P: /devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0
-E: DEVTYPE=usb_interface
-E: DRIVER=hub
-E: DEVICE=/proc/bus/usb/003/001
-E: PRODUCT=1d6b/1/206
-E: TYPE=9/0/0
-E: INTERFACE=9/0/0
-E: MODALIAS=usb:v1D6Bp0001d0206dc09dsc00dp00ic09isc00ip00
-
-P: /devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0/usb_endpoint/usbdev3.1_ep81
-N: usbdev3.1_ep81
-S: char/252:4
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0/usb_endpoint/usbdev3.1_ep81
-E: MAJOR=252
-E: MINOR=4
-E: DEVNAME=/dev/usbdev3.1_ep81
-E: DEVLINKS=/dev/char/252:4
-
-P: /devices/pci0000:00/0000:00:1f.1
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1f.1
-E: DRIVER=ata_piix
-E: PCI_CLASS=1018A
-E: PCI_ID=8086:2850
-E: PCI_SUBSYS_ID=17AA:20A6
-E: PCI_SLOT_NAME=0000:00:1f.1
-E: MODALIAS=pci:v00008086d00002850sv000017AAsd000020A6bc01sc01i8a
-
-P: /devices/pci0000:00/0000:00:1f.1/host3
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1f.1/host3
-E: DEVTYPE=scsi_host
-
-P: /devices/pci0000:00/0000:00:1f.1/host3/scsi_host/host3
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1f.1/host3/scsi_host/host3
-
-P: /devices/pci0000:00/0000:00:1f.1/host3/target3:0:0
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1f.1/host3/target3:0:0
-E: DEVTYPE=scsi_target
-
-P: /devices/pci0000:00/0000:00:1f.1/host3/target3:0:0/3:0:0:0
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1f.1/host3/target3:0:0/3:0:0:0
-E: DEVTYPE=scsi_device
-E: DRIVER=sr
-E: MODALIAS=scsi:t-0x05
-</info>
-
-    <!-- The content of publicly accessible files in /sys/class/dmi/id/
-         format: filename:content
-         as for example generated by "grep -r . /sys/class/dmi/id/"
-    -->
-
-    <info command="grep -r . /sys/class/dmi/id/ 2&gt;/dev/null">/sys/class/dmi/id/bios_vendor:LENOVO
-/sys/class/dmi/id/bios_version:7LETB9WW (2.19 )
-/sys/class/dmi/id/bios_date:06/06/2008
-/sys/class/dmi/id/sys_vendor:LENOVO
-/sys/class/dmi/id/product_name:6457BAG
-/sys/class/dmi/id/product_version:ThinkPad T61
-/sys/class/dmi/id/board_vendor:LENOVO
-/sys/class/dmi/id/board_name:6457BAG
-/sys/class/dmi/id/board_version:Not Available
-/sys/class/dmi/id/chassis_vendor:LENOVO
-/sys/class/dmi/id/chassis_type:10
-/sys/class/dmi/id/chassis_version:Not Available
-/sys/class/dmi/id/chassis_asset_tag:No Asset Information
-/sys/class/dmi/id/modalias:dmi:bvnLENOVO:bvr7LETB9WW(2.19)
-</info>
-  </context>
-</system>
diff --git a/lib/lp/hardwaredb/scripts/tests/hardwaretest-udev.xml b/lib/lp/hardwaredb/scripts/tests/hardwaretest-udev.xml
deleted file mode 100644
index 3cd340f..0000000
--- a/lib/lp/hardwaredb/scripts/tests/hardwaretest-udev.xml
+++ /dev/null
@@ -1,462 +0,0 @@
-<?xml version="1.0" ?>
-<system version="1.0">
-
-  <!-- summary: generic information about the submission -->
-  <summary>
-
-    <!-- live_cd: Was this submission made on a system running an Ubuntu Live
-             CD or on a regular Ubuntu/Linux installation?
-    -->
-    <live_cd value="False"/>
-
-    <!-- system_id: A hash of the "system identifier". This value is intended
-             to identify the tested computer model; the value should
-             be derived from the properties
-             system.product, system.vendor of the HAL UDI
-             /org/freedesktop/Hal/devices/computer.
-    -->
-    <system_id value="f982bb1ab536469cebfd6eaadcea0ffc"/>
-
-    <!-- distribution, distroseries: These values are retrieved from
-             /etc/lsb-release, parameters DISTRIB_ID and DISTRIB_RELEASE.
-    -->
-    <distribution value="Ubuntu"/>
-    <distroseries value="7.04"/>
-
-    <!-- architecture: The processor architecture of the operating system.
-    -->
-    <architecture value="amd64"/>
-
-    <!-- private: If False, this submission is publicly accessible from
-             Launchpad, else it is only accesible by the submitter, by
-             Launchpad administrators and by scripts running with
-             administrator rights. Submissions marked "private" should
-             only be used to gather statistical data.
-    -->
-    <private value="False"/>
-
-    <!-- contactable: If True, the owner agrees to be contacted by other
-             persons about devices which appear in their submission.
-             Example of a use case: Developers can ask device owners
-             to perform tests.
-    -->
-    <contactable value="False"/>
-
-    <!-- date_created: Date and time (UTC) of the submission.
-    -->
-    <date_created value="2007-09-28T16:09:20.126842"/>
-
-    <!-- client: The name and version of the program that created the
-             submission data.
-    -->
-    <client name="hwtest" version="0.9">
-
-      <!-- plugin: name and version of a plugin used by the client.
-               This tag may appear more than once.
-      -->
-      <plugin name="architecture_info" version="1.1"/>
-      <plugin name="find_network_controllers" version="2.34"/>
-      <plugin name="internet_ping" version="1.1"/>
-      <plugin name="harddisk_speed" version="0.7"/>
-    </client>
-
-    <!-- The kernel name and version, as shown by "uname -r"
-    -->
-    <kernel-release value="2.6.28-14-generic"/>
-  </summary>
-
-  <!-- hardware: data about the hardware the submission was made on.
-  -->
-  <hardware>
-
-    <!-- udev: The output of running "udevadm info - -export-db" -->
-
-    <udev>P: /devices/LNXSYSTM:00
-E: UDEV_LOG=3
-E: DEVPATH=/devices/LNXSYSTM:00
-E: MODALIAS=acpi:LNXSYSTM:
-
-P: /devices/pci0000:00/0000:00:1a.0
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1a.0
-E: DRIVER=uhci_hcd
-E: PCI_CLASS=C0300
-E: PCI_ID=8086:2834
-E: PCI_SUBSYS_ID=17AA:20AA
-E: PCI_SLOT_NAME=0000:00:1a.0
-E: MODALIAS=pci:v00008086d00002834sv000017AAsd000020AAbc0Csc03i00
-
-P: /devices/pci0000:00/0000:00:1a.0/usb3
-N: bus/usb/003/001
-S: char/189:256
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb3
-E: MAJOR=189
-E: MINOR=256
-E: DEVTYPE=usb_device
-E: DRIVER=usb
-E: DEVICE=/proc/bus/usb/003/001
-E: PRODUCT=1d6b/1/206
-E: TYPE=9/0/0
-E: BUSNUM=003
-E: DEVNUM=001
-E: DEVNAME=/dev/bus/usb/003/001
-E: DEVLINKS=/dev/char/189:256
-
-P: /devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0
-E: DEVTYPE=usb_interface
-E: DRIVER=hub
-E: DEVICE=/proc/bus/usb/003/001
-E: PRODUCT=1d6b/1/206
-E: TYPE=9/0/0
-E: INTERFACE=9/0/0
-E: MODALIAS=usb:v1D6Bp0001d0206dc09dsc00dp00ic09isc00ip00
-
-P: /devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0/usb_endpoint/usbdev3.1_ep81
-N: usbdev3.1_ep81
-S: char/252:4
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0/usb_endpoint/usbdev3.1_ep81
-E: MAJOR=252
-E: MINOR=4
-E: DEVNAME=/dev/usbdev3.1_ep81
-E: DEVLINKS=/dev/char/252:4
-
-P: /devices/pci0000:00/0000:00:1f.1
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1f.1
-E: DRIVER=ata_piix
-E: PCI_CLASS=1018A
-E: PCI_ID=8086:2850
-E: PCI_SUBSYS_ID=17AA:20A6
-E: PCI_SLOT_NAME=0000:00:1f.1
-E: MODALIAS=pci:v00008086d00002850sv000017AAsd000020A6bc01sc01i8a
-
-P: /devices/pci0000:00/0000:00:1f.1/host3
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1f.1/host3
-E: DEVTYPE=scsi_host
-
-P: /devices/pci0000:00/0000:00:1f.1/host3/scsi_host/host3
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1f.1/host3/scsi_host/host3
-
-P: /devices/pci0000:00/0000:00:1f.1/host3/target3:0:0
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1f.1/host3/target3:0:0
-E: DEVTYPE=scsi_target
-
-P: /devices/pci0000:00/0000:00:1f.1/host3/target3:0:0/3:0:0:0
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1f.1/host3/target3:0:0/3:0:0:0
-E: DEVTYPE=scsi_device
-E: DRIVER=sr
-E: MODALIAS=scsi:t-0x05
-</udev>
-
-    <!-- The content of publicly accessible files in /sys/class/dmi/id/
-         format: filename:content
-         as for example generated by "grep -r . /sys/class/dmi/id/"
-    -->
-
-    <dmi>/sys/class/dmi/id/bios_vendor:LENOVO
-/sys/class/dmi/id/bios_version:7LETB9WW (2.19 )
-/sys/class/dmi/id/bios_date:06/06/2008
-/sys/class/dmi/id/sys_vendor:LENOVO
-/sys/class/dmi/id/product_name:6457BAG
-/sys/class/dmi/id/product_version:ThinkPad T61
-/sys/class/dmi/id/board_vendor:LENOVO
-/sys/class/dmi/id/board_name:6457BAG
-/sys/class/dmi/id/board_version:Not Available
-/sys/class/dmi/id/chassis_vendor:LENOVO
-/sys/class/dmi/id/chassis_type:10
-/sys/class/dmi/id/chassis_version:Not Available
-/sys/class/dmi/id/chassis_asset_tag:No Asset Information
-/sys/class/dmi/id/modalias:dmi:bvnLENOVO:bvr7LETB9WW(2.19)
-</dmi>
-
-    <!-- Additional data for SCSI devices: vendor, model, type
-
-         For each udev node which has DEVTYPE=scsi_device, we need
-         the content of the sysfs files vendor, model, type. The data
-         is stored in the same format as the DMI data:
-         /path/to/file:filecontent
-    -->
-    <sysfs-attributes>
-P: /devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
-A: modalias=input:b0019v0000p0001e0000-e0,1,k74,ramlsfw
-A: uniq=
-A: phys=LNXPWRBN/button/input0
-A: name=Power Button
-
-P: /devices/LNXSYSTM:00/device:00/PNP0A08:00/device:03/input/input8
-A: modalias=input:b0019v0000p0006e0000-e0,1,kE0,E1,E3,F0,F1,F2,F3,F4,F5,ramlsfw
-A: uniq=
-A: phys=/video/input0
-A: name=Video Bus
-</sysfs-attributes>
-
-    <!-- processors: Data about processors installed in a system.
-             The data is retrieved from /proc/cpuinfo.
-    -->
-    <processors>
-
-      <!-- processor: Data from /proc/cpuinfo about a single processor.
-      -->
-      <processor id="123" name="0">
-
-        <!-- property: The data of one line of /proc/cpuinfo.
-                 attribute name: The name of the property
-                     (the text left of the ':' in a line of /proc/cpuinfo)
-                 attribute type: A Python type appropriate for the value.
-        -->
-        <property name="wp" type="bool">
-                True
-        </property>
-        <property name="flags" type="list">
-          <value type="str">
-                  fpu
-          </value>
-          <value type="str">
-                  vme
-          </value>
-          <value type="str">
-                  de
-          </value>
-        </property>
-        <property name="cpu_mhz" type="float">
-                1000.0
-        </property>
-      </processor>
-    </processors>
-
-      <!-- aliases: optional data provided by the user:
-               The name of a peripheral, PCI card etc as shown by a label on
-               the device. OEM devices are often sold under different names
-               by different vendors; having a set of alias names for a device
-               allows users of the HWDB to search for information by these
-               "marketing names".
-      -->
-    <aliases>
-      <!-- alias: The "label name" of a device or system.
-               attribute target: The sysfs path of a device as given
-               in <udev>.
-      -->
-      <alias target="/devices/pci0000:00/0000:00:1f.1/host3/target3:0:0/3:0:0:0">
-
-        <!-- vendor: The vendor name shown on the device label.
-        -->
-        <vendor>Medion</vendor>
-
-        <!-- model: The model name of shown on the label.
-        -->
-        <model>QuickPrint 9876</model>
-      </alias>
-    </aliases>
-  </hardware>
-
-  <!-- software: Data about the software installed on the system.
-  -->
-  <software>
-
-    <!-- lsbrelease: The data from /etc/lsb-release.
-    -->
-    <lsbrelease>
-
-      <!-- property: the data from one line of /etc/lsb-release.
-               attribute type: A Python type appropriate for this
-                   property (str).
-      -->
-      <property name="release" type="str">
-              7.04
-      </property>
-      <property name="codename" type="str">
-              feisty
-      </property>
-      <property name="distributor-id" type="str">
-              Ubuntu
-      </property>
-      <property name="description" type="str">
-              Ubuntu 7.04
-      </property>
-      <property name="dict_example" type="dict">
-          <value name="a" type="str">value for key a</value>
-          <value name="b" type="int">1234</value>
-      </property>
-    </lsbrelease>
-
-    <!-- packages: Data about the installed software packages.
-    -->
-    <packages>
-
-      <!-- package: Data about a single package.
-               The <property> sub-tags contain the DEB properties
-               "name", "priority", "section", "source", "version",
-               "installed_size", "size", "summary".
-
-               XXX Abel Deuring 2007-12-12: What about submissions
-               from RPM-based Linux versions? (And "exotic" variants
-               like Gentoo?)
-      -->
-      <package name="metacity" id="200">
-        <property name="installed_size" type="int">
-                868352
-        </property>
-        <property name="section" type="str">
-                x11
-        </property>
-        <property name="summary" type="str">
-                A lightweight GTK2 based Window Manager
-        </property>
-        <property name="priority" type="str">
-                optional
-        </property>
-        <property name="source" type="str">
-                metacity
-        </property>
-        <property name="version" type="str">
-                1:2.18.2-0ubuntu1.1
-        </property>
-        <property name="size" type="int">
-                429128
-        </property>
-      </package>
-    </packages>
-    <!-- Information extracted from Xorg.0.log.
-         HAL does not provide any information about Xorg drivers, so
-         we retrieve that from the xserver's log file.
-    -->
-    <xorg version="1.3.0">
-      <!-- driver: Data about a driver.
-               (optional)
-               attribute name: The name of the driver.
-               attribute version: The version of the driver.
-               attribute class: The module class of the driver
-               attribute device: The ID of a device driven by this driver.
-      -->
-      <driver name="fglrx" version="1.23" class="X.Org Video Driver"
-              device="12"/>
-    </xorg>
-  </software>
-
-  <!-- questions: User's answers to questions asked by the client.
-  -->
-  <questions>
-
-    <!-- question: Data of a question.
-             attribute name: The unique name of the question.
-             attribute plugin: The name of the plugin which asked
-                 the question.
-             attribute version: The version of the question.
-             attribute type: Allowed values are "manual" and "automatic".
-                 A "manual" question requires user input for the answer;
-                 an "automatic" question gets the answer automatically.
-    -->
-    <question name="detected_network_controllers"
-              plugin="find_network_controllers">
-
-      <!-- target: Information about a device or software package the
-           question is about. The attribute "id" is the sysfs path of
-           a device as given in <udev>, or the ID of a software package
-           node.
-           This node may appear multiple times.
-       -->
-      <target id="/devices/pci0000:00/0000:00:1a.0/usb3/3-0:1.0/usb_endpoint/usbdev3.1_ep81">
-        <!-- driver: The driver which controls the target device. This tag
-                 may appear more than once.
-
-                 While we are working on a project called "Hardware Database",
-                 we are not that much interested in the question, if a device
-                 works "as such", but if their Linux driver(s) work.
-
-                 It is not in every case possible to identify the used driver
-                 from HAL data, so we need another way to add this information.
-                 (example: HAL does not know, which driver is used for the
-                 graphics card.)
-
-                 Example for multiple drivers: Some scanners have a SCSI _and_
-                 a USB interface; if such a scanner is tested, we not only want
-                 to know, which Sane backend is used, but also, which interface
-                 is used.
-
-                 Also, it might be interesting to know for many USB 2.0 devices,
-                 if a USB 1 (uhci_hcd or ohci_hcd driver) or the USB 2 driver
-                 (ehci_hcd) was used. A USB 1 driver might for example explain
-                 latency problems.
-        -->
-        <driver>ipw3945</driver>
-      </target>
-
-      <!-- ID of the 88E8055 PCI-E Gigabit Ethernet Controller -->
-      <target id="/devices/pci0000:00/0000:00:1f.1"/>
-
-      <!-- command: The command line of an external command required to
-               ask this question.
-      -->
-      <command/>
-
-      <!-- answer: The answer to the question. Two types of answers are
-               defined, "multiple_choice" and "measurement". (See below
-               for an example of the latter.)
-               attribute type: Must be "multiple_choice" or "measurement".
-      -->
-      <answer type="multiple_choice">pass</answer>
-
-      <!-- answer_choices: The list of possible choices.
-               The data should only be used for consistency
-               checks and to detect variants of the question.
-      -->
-      <answer_choices>
-        <value type="str">fail</value>
-        <value type="str">pass</value>
-        <value type="str">skip</value>
-      </answer_choices>
-
-      <!-- A user comment about the device or about the test.
-      -->
-      <comment>
-        The WLAN adapter drops the connection very frequently.
-      </comment>
-    </question>
-
-    <question name="internet_ping"
-              plugin="internet_ping">
-      <target id="/devices/pci0000:00/0000:00:1f.1"/>
-      <command/>
-      <answer type="multiple_choice">pass</answer>
-      <answer_choices>
-          <value type="str">fail</value>
-          <value type="str">pass</value>
-          <value type="str">skip</value>
-      </answer_choices>
-    </question>
-
-    <!-- example for a "measurement question"
-    -->
-    <question name="harddisk_speed"
-              plugin="harddisk_speed">
-      <target id="/devices/pci0000:00/0000:00:1f.1/host3/target3:0:0/3:0:0:0"/>
-      <command>hdparm -t /dev/sda</command>
-      <!-- answer: The answer to a "measurement question".
-               attribute type: See above.
-               attribute unit: The unit of the result of the measurement.
-                   XXX Abel Deuring 2007-12-12 bug=175978 We should
-                   enumerate a list of allowed units, in order to avoid
-                   multiple units for the same dimension. e.g., B/sec,
-                   MB/sec or inch, cm, foot.
-
-                   For dimensionless values, the attribute unit is omitted.
-
-                   "Percentage" and similar "convenience pseudo-units" like
-                   ppm are _not_ allowed; instead a dimensionless
-                   value must be used, where 0 is equivalent 0% and 1.0 is
-                   equivalent to 100%.
-      -->
-      <answer type="measurement" unit="MB/sec">38.4</answer>
-    </question>
-  </questions>
-  <!-- miscellaneous additional text data.
-  -->
-</system>
diff --git a/lib/lp/hardwaredb/scripts/tests/hardwaretest.xml b/lib/lp/hardwaredb/scripts/tests/hardwaretest.xml
deleted file mode 100644
index 35d24b6..0000000
--- a/lib/lp/hardwaredb/scripts/tests/hardwaretest.xml
+++ /dev/null
@@ -1,426 +0,0 @@
-<?xml version="1.0" ?>
-<system version="1.0">
-
-  <!-- summary: generic information about the submission -->
-  <summary>
-
-    <!-- live_cd: Was this submission made on a system running an Ubuntu Live
-             CD or on a regular Ubuntu/Linux installation?
-    -->
-    <live_cd value="False"/>
-
-    <!-- system_id: A hash of the "system identifier". This value is intended
-             to identify the tested computer model; the value should 
-             be derived from the properties 
-             system.product, system.vendor of the HAL UDI 
-             /org/freedesktop/Hal/devices/computer.
-    -->
-    <system_id value="f982bb1ab536469cebfd6eaadcea0ffc"/>
-
-    <!-- distribution, distroseries: These values are retrieved from
-             /etc/lsb-release, parameters DISTRIB_ID and DISTRIB_RELEASE.
-    -->
-    <distribution value="Ubuntu"/>
-    <distroseries value="7.04"/>
-
-    <!-- architecture: The processor architecture of the operating system.
-    -->
-    <architecture value="amd64"/>
-
-    <!-- private: If False, this submission is publicly accessible from
-             Launchpad, else it is only accesible by the submitter, by 
-             Launchpad administrators and by scripts running with 
-             administrator rights. Submissions marked "private" should
-             only be used to gather statistical data.
-    -->
-    <private value="False"/>
-
-    <!-- contactable: If True, the owner agrees to be contacted by other
-             persons about devices which appear in their submission. 
-             Example of a use case: Developers can ask device owners
-             to perform tests.
-    -->
-    <contactable value="False"/>
-
-    <!-- date_created: Date and time (UTC) of the submission.
-    -->
-    <date_created value="2007-09-28T16:09:20.126842"/>
-
-    <!-- client: The name and version of the program that created the
-             submission data.
-    -->
-    <client name="hwtest" version="0.9">
- 
-      <!-- plugin: name and version of a plugin used by the client.
-               This tag may appear more than once.
-      -->
-      <plugin name="architecture_info" version="1.1"/>
-      <plugin name="find_network_controllers" version="2.34"/>
-      <plugin name="internet_ping" version="1.1"/>
-      <plugin name="harddisk_speed" version="0.7"/>
-    </client>
-  </summary>
-
-  <!-- hardware: data about the hardware the submission was made on.
-  -->
-  <hardware>
-
-    <!-- hal: data collected from HAL.
-             attribute version: The version of the HAL daemon.
-    -->
-    <hal version="0.5.8.1">
-
-      <!-- device: The data of a HAL device object.
-               attribute id: A unique identifier created by th client.
-               attribute udi: the HAL UDI of the device. Privacy sensitive
-                   UDIs (e.g., those containing UUID, MAC addresses, serial
-                   numbers) should be obscured.
-               attribute parent: The ID of the parent HAL node of this
-                   device
-      -->
-      <device id="0" parent="130" udi="/org/freedesktop/Hal/devices/platform_bluetooth">
-        <!-- property: A HAL device property.
-                 attribute name: The name of the property.
-                 attribute type: The DBus type of the property.
-
-                 The value of a property is stored as CTEXT, except
-                 for properties of the dictionary- and list-like 
-                 types.
-
-                 For list-like types, the values are stored in sub-tags
-                 <value>, which have the required attribute "type".
-
-                 For dictionary-like types, the values are stored in
-                 sub-tags value, which have the required attributes
-                 "type" and "name".
-
-                 The "type" attribute contains the DBus type of a value.
-        -->
-        <property name="info.parent" type="dbus.String">
-                /org/freedesktop/Hal/devices/computer
-        </property>
-        <property name="info.bus" type="dbus.String">
-                platform
-        </property>
-        <property name="button.has_state" type="dbus.Boolean">
-                False
-        </property>
-        <property name="info.capabilities" type="dbus.Array">
-          <value type="dbus.String">
-                button
-          </value>
-        </property>
-        <property name="linux.acpi_type" type="dbus.Int32">
-                11
-        </property>
-
-        <property name="storage.size" type="dbus.UInt64">
-                0
-        </property>
-        <!-- pure theory/for possible new feature in future HAL versions: 
-             The data type dbus.Dictionary is at present (Nov 2007) not 
-             used by HAL.
-        -->
-        <property name="test.only" type="dbus.Dictionary">
-            <value name="foo" type="dbus.String">bar</value>
-            <value name="blah" type="dbus.Int32">1234</value>
-        </property>
-      </device>
-      <device id="130" udi="/org/freedesktop/Hal/devices/computer">
-        <property name="info.bus" type="dbus.String">
-            unknown
-        </property>
-      </device>
-    </hal>
-
-    <!-- processors: Data about processors installed in a system.
-             The data is retrieved from /proc/cpuinfo.
-    -->
-    <processors>
-
-      <!-- processor: Data from /proc/cpuinfo about a single processor.
-      -->
-      <processor id="123" name="0">
-
-        <!-- property: The data of one line of /proc/cpuinfo.
-                 attribute name: The name of the property
-                     (the text left of the ':' in a line of /proc/cpuinfo)
-                 attribute type: A Python type appropriate for the value.
-        -->
-        <property name="wp" type="bool">
-                True
-        </property>
-        <property name="flags" type="list">
-          <value type="str">
-                  fpu
-          </value>
-          <value type="str">
-                  vme
-          </value>
-          <value type="str">
-                  de
-          </value>
-        </property>
-        <property name="cpu_mhz" type="float">
-                1000.0
-        </property>
-      </processor>
-    </processors>
-
-      <!-- aliases: optional data provided by the user:
-               The name of a peripheral, PCI card etc as shown by a label on
-               the device. OEM devices are often sold under different names
-               by different vendors; having a set of alias names for a device
-               allows users of the HWDB to search for information by these
-               "marketing names".
-      -->
-    <aliases>
-      <!-- alias: The "label name" of a device or system. 
-               attribute target: The ID of the HAL node of the device
-               or system.
-      -->
-      <alias target="65">
-
-        <!-- vendor: The vendor name shown on the device label.
-        -->
-        <vendor>Medion</vendor>
-
-        <!-- model: The model name of shown on the label.
-        -->
-        <model>QuickPrint 9876</model>
-      </alias>
-    </aliases>
-  </hardware>
-
-  <!-- software: Data about the software installed on the system.
-  -->
-  <software>
-
-    <!-- lsbrelease: The data from /etc/lsb-release.
-    -->
-    <lsbrelease>
-
-      <!-- property: the data from one line of /etc/lsb-release.
-               attribute type: A Python type appropriate for this
-                   property (str).
-      -->
-      <property name="release" type="str">
-              7.04
-      </property>
-      <property name="codename" type="str">
-              feisty
-      </property>
-      <property name="distributor-id" type="str">
-              Ubuntu
-      </property>
-      <property name="description" type="str">
-              Ubuntu 7.04
-      </property>
-      <property name="dict_example" type="dict">
-          <value name="a" type="str">value for key a</value>
-          <value name="b" type="int">1234</value>
-      </property>
-    </lsbrelease>
-
-    <!-- packages: Data about the installed software packages.
-    -->
-    <packages>
-
-      <!-- package: Data about a single package.
-               The <property> sub-tags contain the DEB properties
-               "name", "priority", "section", "source", "version",
-               "installed_size", "size", "summary".
-
-               XXX Abel Deuring 2007-12-12: What about submissions 
-               from RPM-based Linux versions? (And "exotic" variants
-               like Gentoo?)
-      -->
-      <package name="metacity" id="200">
-        <property name="installed_size" type="int">
-                868352
-        </property>
-        <property name="section" type="str">
-                x11
-        </property>
-        <property name="summary" type="str">
-                A lightweight GTK2 based Window Manager
-        </property>
-        <property name="priority" type="str">
-                optional
-        </property>
-        <property name="source" type="str">
-                metacity
-        </property>
-        <property name="version" type="str">
-                1:2.18.2-0ubuntu1.1
-        </property>
-        <property name="size" type="int">
-                429128
-        </property>
-      </package>
-    </packages>
-    <!-- Information extracted from Xorg.0.log.
-         HAL does not provide any information about Xorg drivers, so 
-         we retrieve that from the xserver's log file.
-    -->
-    <xorg version="1.3.0">
-      <!-- driver: Data about a driver.
-               (optional)
-               attribute name: The name of the driver.
-               attribute version: The version of the driver.
-               attribute class: The module class of the driver
-               attribute device: The ID of a device driven by this driver.
-      -->
-      <driver name="fglrx" version="1.23" class="X.Org Video Driver"
-              device="12"/>
-    </xorg>
-  </software>
-
-  <!-- questions: User's answers to questions asked by the client.
-  -->
-  <questions>
-
-    <!-- question: Data of a question.
-             attribute name: The unique name of the question.
-             attribute plugin: The name of the plugin which asked
-                 the question.
-             attribute version: The version of the question.
-             attribute type: Allowed values are "manual" and "automatic".
-                 A "manual" question requires user input for the answer;
-                 an "automatic" question gets the answer automatically.
-    -->
-    <question name="detected_network_controllers"
-              plugin="find_network_controllers">
-
-      <!-- target: Information about a device or software package the
-           question is about. The attribute "id" is the ID of a HAL device
-           node or of a software package.
-           This node may appear multiple times.
-       -->
-      <target id="42">
-        <!-- driver: The driver which controls the target device. This tag
-                 may appear more than once. 
-
-                 While we are working on a project called "Hardware Database",
-                 we are not that much interested in the question, if a device 
-                 works "as such", but if their Linux driver(s) work.
-
-                 It is not in every case possible to identify the used driver
-                 from HAL data, so we need another way to add this information.
-                 (example: HAL does not know, which driver is used for the 
-                 graphics card.)
-
-                 Example for multiple drivers: Some scanners have a SCSI _and_
-                 a USB interface; if such a scanner is tested, we not only want
-                 to know, which Sane backend is used, but also, which interface
-                 is used.
-
-                 Also, it might be interesting to know for many USB 2.0 devices,
-                 if a USB 1 (uhci_hcd or ohci_hcd driver) or the USB 2 driver
-                 (ehci_hcd) was used. A USB 1 driver might for example explain 
-                 latency problems.
-        -->
-        <driver>ipw3945</driver>
-      </target>
-
-      <!-- ID of the 88E8055 PCI-E Gigabit Ethernet Controller -->
-      <target id="43"/>
-
-      <!-- command: The command line of an external command required to
-               ask this question.
-      -->
-      <command/>
-
-      <!-- answer: The answer to the question. Two types of answers are
-               defined, "multiple_choice" and "measurement". (See below
-               for an example of the latter.)
-               attribute type: Must be "multiple_choice" or "measurement".
-      -->
-      <answer type="multiple_choice">pass</answer>
-
-      <!-- answer_choices: The list of possible choices.
-               The data should only be used for consistency
-               checks and to detect variants of the question.
-      -->
-      <answer_choices>
-        <value type="str">fail</value>
-        <value type="str">pass</value>
-        <value type="str">skip</value>
-      </answer_choices>
-
-      <!-- A user comment about the device or about the test.
-      -->
-      <comment>
-        The WLAN adapter drops the connection very frequently.
-      </comment>
-    </question>
-
-    <question name="internet_ping"
-              plugin="internet_ping">
-      <target id="23"/>
-      <command/>
-      <answer type="multiple_choice">pass</answer>
-      <answer_choices>
-          <value type="str">fail</value>
-          <value type="str">pass</value>
-          <value type="str">skip</value>
-      </answer_choices>
-    </question>
-
-    <!-- example for a "measurement question"
-    -->
-    <question name="harddisk_speed"
-              plugin="harddisk_speed">
-      <target id="87"/>
-      <command>hdparm -t /dev/sda</command>
-      <!-- answer: The answer to a "measurement question".
-               attribute type: See above.
-               attribute unit: The unit of the result of the measurement.
-                   XXX Abel Deuring 2007-12-12 bug=175978 We should 
-                   enumerate a list of allowed units, in order to avoid
-                   multiple units for the same dimension. e.g., B/sec, 
-                   MB/sec or inch, cm, foot. 
-
-                   For dimensionless values, the attribute unit is omitted.
-
-                   "Percentage" and similar "convenience pseudo-units" like
-                   ppm are _not_ allowed; instead a dimensionless
-                   value must be used, where 0 is equivalent 0% and 1.0 is
-                   equivalent to 100%.
-      -->
-      <answer type="measurement" unit="MB/sec">38.4</answer>
-    </question>
-  </questions>
-  <!-- miscellaneous additional text data.
-  -->
-  <context>
-    <!-- info: The text output of the command specified in the "command"
-             attribute. The "commmand" attribute must contain the complote
-             command line used to produce the text output, including
-             parameters used to invoke a program.
-    -->
-    <info command="dmidecode">
-# dmidecode 2.9
-SMBIOS 2.4 present.
-73 structures occupying 2436 bytes.
-Table at 0x000E0010.
-
-Handle 0x0000, DMI type 0, 24 bytes
-BIOS Information
-        Vendor: LENOVO
-    </info>
-
-    <info command="lspci -vvn">
-00:00.0 0600: 8086:2a00 (rev 0c)
-        Subsystem: 17aa:20b1
-        Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop-
-        Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- &gt;TAbort- &lt;TAbort-
-        Latency: 0
-        Capabilities: [e0] Vendor Specific Information &gt;?&lt;
-        Kernel modules: intel-agp
-
-00:01.0 0604: 8086:2a01 (rev 0c)
-        Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV-
-    </info>
-  </context>
-</system>
diff --git a/lib/lp/hardwaredb/scripts/tests/test_hwdb_submission_parser.py b/lib/lp/hardwaredb/scripts/tests/test_hwdb_submission_parser.py
deleted file mode 100644
index e8b5e36..0000000
--- a/lib/lp/hardwaredb/scripts/tests/test_hwdb_submission_parser.py
+++ /dev/null
@@ -1,2618 +0,0 @@
-# Copyright 2009-2019 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Tests of the HWDB submissions parser."""
-
-from __future__ import absolute_import, print_function, unicode_literals
-
-from datetime import datetime
-import io
-import logging
-import os
-from textwrap import dedent
-from xml.etree.cElementTree import Element
-
-import defusedxml.cElementTree as etree
-import pytz
-from zope.testing.loghandler import Handler
-
-from lp.hardwaredb.scripts.hwdbsubmissions import (
-    ROOT_UDI,
-    SubmissionParser,
-    )
-from lp.services.config import config
-from lp.testing import (
-    TestCase,
-    validate_mock_class,
-    )
-from lp.testing.layers import BaseLayer
-
-
-class SubmissionParserTestParseSoftware(SubmissionParser):
-    """A Variant used to test SubmissionParser._parseSoftware.
-
-    This class can be used to test the regular case of
-    submission data.
-    """
-
-    def __init__(self, test, logger=None):
-        super(SubmissionParserTestParseSoftware, self).__init__(logger)
-        self.test = test
-
-    def _parseLSBRelease(self, node):
-        self.test.assertEqual(node.tag, 'lsbrelease')
-        return 'parsed lsb release'
-
-    def _parsePackages(self, node):
-        self.test.assertEqual(node.tag, 'packages')
-        return 'parsed packages'
-
-    def _parseXOrg(self, node):
-        self.test.assertEqual(node.tag, 'xorg')
-        return 'parsed xorg'
-
-
-class SubmissionParserTestParseSoftwareNoXorgNode(SubmissionParser):
-    """A Variant used to test SubmissionParser._parseSoftware.
-
-    This class is intended to test submission data that does not contain
-    a <xorg> node.
-    """
-
-    def __init__(self, test, logger=None):
-        super(SubmissionParserTestParseSoftwareNoXorgNode, self).__init__(
-            logger)
-        self.test = test
-
-    def _parseLSBRelease(self, node):
-        self.test.assertEqual(node.tag, 'lsbrelease')
-        return 'parsed lsb release'
-
-    def _parsePackages(self, node):
-        self.test.assertEqual(node.tag, 'packages')
-        return 'parsed packages'
-
-
-class SubmissionParserTestParseSoftwareNoPackagesNode(SubmissionParser):
-    """A Variant used to test SubmissionParser._parseSoftware.
-
-    This class is intended to test submission data that does not contain
-    a <packages> node.
-    """
-
-    def __init__(self, test, logger=None):
-        super(SubmissionParserTestParseSoftwareNoPackagesNode, self).__init__(
-            logger)
-        self.test = test
-
-    def _parseLSBRelease(self, node):
-        self.test.assertEqual(node.tag, 'lsbrelease')
-        return 'parsed lsb release'
-
-    def _parseXOrg(self, node):
-        self.test.assertEqual(node.tag, 'xorg')
-        return 'parsed xorg'
-
-
-class TestHWDBSubmissionParser(TestCase):
-    """Tests of the HWDB submission parser."""
-
-    layer = BaseLayer
-
-    def setUp(self):
-        """Setup the test environment."""
-        super(TestHWDBSubmissionParser, self).setUp()
-        self.log = logging.getLogger('test_hwdb_submission_parser')
-        self.log.setLevel(logging.INFO)
-        self.handler = Handler(self)
-        self.handler.add(self.log.name)
-        self.udev_root_device = {
-            'P': '/devices/LNXSYSTM:00',
-            'E': {'SUBSYSTEM': 'acpi'},
-            }
-        self.udev_pci_device = {
-            'P': '/devices/pci0000:00/0000:00:1f.2',
-            'E': {
-                'SUBSYSTEM': 'pci',
-                'PCI_CLASS': '10601',
-                'PCI_ID': '8086:27C5',
-                'PCI_SUBSYS_ID': '10CF:1387',
-                'PCI_SLOT_NAME': '0000:00:1f.2',
-                }
-            }
-        self.udev_usb_device = {
-            'P': '/devices/pci0000:00/0000:00:1d.1/usb3/3-2',
-            'E': {
-                'SUBSYSTEM': 'usb',
-                'DEVTYPE': 'usb_device',
-                'PRODUCT': '46d/a01/1013',
-                'TYPE': '0/0/0',
-                },
-            }
-        self.udev_usb_interface = {
-            'P': '/devices/pci0000:00/0000:00:1d.1/usb3/3-2/3-2:1.1',
-            'E': {
-                'SUBSYSTEM': 'usb',
-                'DEVTYPE': 'usb_interface',
-                'PRODUCT': '46d/a01/1013',
-                'TYPE': '0/0/0',
-                'INTERFACE': '1/2/0',
-                },
-            }
-
-        self.udev_scsi_device = {
-            'P': '/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/4:0:0:0',
-            'E': {
-                'SUBSYSTEM': 'scsi',
-                'DEVTYPE': 'scsi_device',
-                },
-            }
-
-        self.sysfs_scsi_device = {
-            'vendor': 'MATSHITA',
-            'model': 'DVD-RAM UJ-841S',
-            'type': '5',
-            }
-
-    def getTimestampETreeNode(self, time_string):
-        """Return an Elementtree node for an XML tag with a timestamp."""
-        return Element('date_created', value=time_string)
-
-    def testTimeConversion(self):
-        """Test of the conversion of a "time string" into datetime object."""
-        # Year, month, day, hour, minute, second are required.
-        # We assume that such a value without timezone information is UTC.
-        parser = SubmissionParser(self.log)
-        utc_tz = pytz.timezone('UTC')
-
-        time_node = self.getTimestampETreeNode('2008-01-02T03:04:05')
-        self.assertEqual(parser._getValueAttributeAsDateTime(time_node),
-                         datetime(2008, 1, 2, 3, 4, 5, tzinfo=utc_tz))
-
-        # The timezone value 'Z' means UTC
-        time_node = self.getTimestampETreeNode('2008-01-02T03:04:05Z')
-        self.assertEqual(parser._getValueAttributeAsDateTime(time_node),
-                         datetime(2008, 1, 2, 3, 4, 5, tzinfo=utc_tz))
-
-        # A time zone offset is added to the given time, so that the resulting
-        # time stamp is in UTC.
-        time_node = self.getTimestampETreeNode('2008-01-02T03:04:05+01:00')
-        self.assertEqual(parser._getValueAttributeAsDateTime(time_node),
-                         datetime(2008, 1, 2, 2, 4, 5, tzinfo=utc_tz))
-
-        time_node = self.getTimestampETreeNode('2008-01-02T03:04:05-01:00')
-        self.assertEqual(parser._getValueAttributeAsDateTime(time_node),
-                         datetime(2008, 1, 2, 4, 4, 5, tzinfo=utc_tz))
-
-        # time values may be given with microsecond resolution.
-        time_node = self.getTimestampETreeNode('2008-01-02T03:04:05.123')
-        self.assertEqual(parser._getValueAttributeAsDateTime(time_node),
-                         datetime(2008, 1, 2, 3, 4, 5, 123000, tzinfo=utc_tz))
-
-        time_node = self.getTimestampETreeNode('2008-01-02T03:04:05.123456')
-        self.assertEqual(parser._getValueAttributeAsDateTime(time_node),
-                         datetime(2008, 1, 2, 3, 4, 5, 123456, tzinfo=utc_tz))
-
-        # The time zone offset may be given with "minute resolution".
-        time_node = self.getTimestampETreeNode('2008-01-02T03:04:05+00:01')
-        self.assertEqual(parser._getValueAttributeAsDateTime(time_node),
-                         datetime(2008, 1, 2, 3, 3, 5, tzinfo=utc_tz))
-
-        time_node = self.getTimestampETreeNode('2008-01-02T03:04:05-00:01')
-        self.assertEqual(parser._getValueAttributeAsDateTime(time_node),
-                         datetime(2008, 1, 2, 3, 5, 5, tzinfo=utc_tz))
-
-        # Leap seconds are rounded down to 59.999999 seconds.
-        time_node = self.getTimestampETreeNode('2008-01-02T23:59:60.999')
-        self.assertEqual(parser._getValueAttributeAsDateTime(time_node),
-                         datetime(2008, 1, 2, 23, 59, 59, 999999,
-                                  tzinfo=utc_tz))
-
-        # "Negative" time values raise a ValueError.
-        time_node = self.getTimestampETreeNode('-1000-01-02/03:04:05')
-        parser.submission_key = 'testing negative time stamps'
-        self.assertRaises(
-            ValueError, parser._getValueAttributeAsDateTime, time_node)
-
-        # Time values with years values with five or more digits raise
-        # a ValueError.
-        time_node = self.getTimestampETreeNode('12345-01-02/03:04:05')
-        parser.submission_key = 'testing negative time stamps'
-        self.assertRaises(
-            ValueError, parser._getValueAttributeAsDateTime, time_node)
-
-    def testSummary(self):
-        node = etree.fromstring("""
-            <summary>
-                <live_cd value="False"/>
-                <system_id value="f982bb1ab536469cebfd6eaadcea0ffc"/>
-                <distribution value="Ubuntu"/>
-                <distroseries value="7.04"/>
-                <architecture value="amd64"/>
-                <private value="False"/>
-                <contactable value="False"/>
-                <date_created value="2007-09-28T16:09:20.126842"/>
-                <client name="hwtest" version="0.9">
-                    <plugin name="architecture_info" version="1.1"/>
-                    <plugin name="find_network_controllers" version="2.34"/>
-                </client>
-            </summary>
-            """)
-        parser = SubmissionParser(self.log)
-        summary = parser._parseSummary(node)
-        expected_data = {
-            'live_cd': False,
-            'system_id': 'f982bb1ab536469cebfd6eaadcea0ffc',
-            'distribution': 'Ubuntu',
-            'distroseries': '7.04',
-            'architecture': 'amd64',
-            'private': False,
-            'contactable': False,
-            'date_created': datetime(2007, 9, 28, 16, 9, 20, 126842,
-                                     tzinfo=pytz.UTC),
-            'client': {
-                'name': 'hwtest',
-                'version': '0.9',
-                'plugins': [
-                    {'name': 'architecture_info',
-                     'version': '1.1'},
-                    {'name': 'find_network_controllers',
-                     'version': '2.34'}]},
-            }
-        self.assertEqual(
-            summary, expected_data,
-            'SubmissionParser.parseSummary returned an unexpected result')
-
-    def testSummaryNodeWithKernelRelease(self):
-        """The <summary> node may contain the sub-node <kernel-release>."""
-        node = etree.fromstring("""
-            <summary>
-                <live_cd value="False"/>
-                <system_id value="f982bb1ab536469cebfd6eaadcea0ffc"/>
-                <distribution value="Ubuntu"/>
-                <distroseries value="7.04"/>
-                <architecture value="amd64"/>
-                <private value="False"/>
-                <contactable value="False"/>
-                <date_created value="2007-09-28T16:09:20.126842"/>
-                <client name="hwtest" version="0.9">
-                    <plugin name="architecture_info" version="1.1"/>
-                    <plugin name="find_network_controllers" version="2.34"/>
-                </client>
-                <kernel-release value="2.6.28-15-generic"/>
-            </summary>
-            """)
-        parser = SubmissionParser(self.log)
-        summary = parser._parseSummary(node)
-        expected_data = {
-            'live_cd': False,
-            'system_id': 'f982bb1ab536469cebfd6eaadcea0ffc',
-            'distribution': 'Ubuntu',
-            'distroseries': '7.04',
-            'architecture': 'amd64',
-            'private': False,
-            'contactable': False,
-            'date_created': datetime(2007, 9, 28, 16, 9, 20, 126842,
-                                     tzinfo=pytz.UTC),
-            'client': {
-                'name': 'hwtest',
-                'version': '0.9',
-                'plugins': [
-                    {
-                        'name': 'architecture_info',
-                        'version': '1.1',
-                        },
-                    {
-                        'name': 'find_network_controllers',
-                        'version': '2.34'
-                        }
-                    ]
-                },
-            'kernel-release': '2.6.28-15-generic',
-            }
-        self.assertEqual(
-            summary, expected_data,
-            'SubmissionParser.parseSummary returned an unexpected result')
-
-    def _runPropertyTest(self, xml):
-        parser = SubmissionParser(self.log)
-        node = etree.fromstring(xml)
-        return parser._parseProperty(node)
-
-    def testBooleanPropertyTypes(self):
-        """Test the parsing result for a boolean property."""
-        for property_type in ('bool', 'dbus.Boolean'):
-            for value in (True, False):
-                xml = ('<property type="%s" name="foo">%s</property>'
-                       % (property_type, value))
-                result = self._runPropertyTest(xml)
-                self.assertEqual(
-                    result, ('foo', (value, property_type)),
-                    'Invalid parsing result for boolean property type %s, '
-                        'expected %s, got %s'
-                    % (property_type, value, result))
-
-    def testStringPropertyTypes(self):
-        """String properties are converted into (name, (value, type))."""
-        xml_template = '<property type="%s" name="foo">some text</property>'
-        for property_type in ('str', 'dbus.String', 'dbus.UTF8String'):
-            xml = xml_template % property_type
-            result = self._runPropertyTest(xml)
-            self.assertEqual(
-                result, ('foo', ('some text', property_type)),
-                'Invalid parsing result for string property type %s, '
-                'expected "some text", got "%s"'
-                    % (property_type, result))
-
-    def testStringPropertyEncoding(self):
-        """Different encodings are properly handled."""
-        xml_template = u'''<?xml version="1.0" encoding="%s"?>
-                           <property type="str" name="foo">%s</property>'''
-        umlaut = u'\xe4'
-        parser = SubmissionParser()
-        for encoding in ('utf-8', 'iso-8859-1'):
-            xml = (xml_template % (encoding, umlaut)).encode(encoding)
-            tree = etree.parse(io.BytesIO(xml))
-            node = tree.getroot()
-            result = parser._parseProperty(node)
-            self.assertEqual(result, ('foo', (umlaut, 'str')),
-                'Invalid parsing result for string encoding %s, '
-                'expected an umlaut (\xe4), got %s'
-                    % (encoding, repr(result)))
-
-    def testIntegerPropertyTypes(self):
-        """Int properties are converted into (name, (value, type_string)).
-
-        type(value) is int or long, depending on the value.
-        """
-        xml_template = '<property name="inttest" type="%s">123</property>'
-        for property_type in ('dbus.Byte', 'dbus.Int16', 'dbus.Int32',
-                              'dbus.Int64', 'dbus.UInt16', 'dbus.UInt32',
-                              'dbus.UInt64', 'int', 'long'):
-            xml = xml_template % property_type
-            result = self._runPropertyTest(xml)
-            self.assertEqual(result, ('inttest', (123, property_type)),
-                             'Invalid parsing result for integer property '
-                             'type %s' % property_type)
-        # If the value is too large for an int, a Python long is returned.
-        xml = """
-            <property name="inttest" type="long">
-                12345678901234567890
-            </property>"""
-        properties = self._runPropertyTest(xml)
-        self.assertEqual(properties,
-                         ('inttest', (12345678901234567890L, 'long')),
-                         'Invalid parsing result for integer property with '
-                             'a large value')
-
-    def testFloatPropertyTypes(self):
-        """Float properties are converted into ('name', (value, type_string)).
-
-        type(value) is float.
-        """
-        xml_template = ('<property name="floattest" type="%s">'
-                            '1.25</property>')
-        for property_type in ('dbus.Double', 'float'):
-            xml = xml_template % property_type
-            result = self._runPropertyTest(xml)
-            self.assertEqual(result, ('floattest', (1.25, property_type)),
-                             'Invalid parsing result for float property'
-                             'type: %s' % property_type)
-
-    def testListPropertyTypes(self):
-        """List properties are converted into ('name', a_list).
-
-        a_list is a Python list, where the list elements represent the
-        values of the <value> sub-nodes of the <property>.
-        """
-        xml_template = """
-            <property name="listtest" type="%s">
-                <value type="int">1</value>
-                <value type="str">a</value>
-                <value type="list">
-                    <value type="int">2</value>
-                    <value type="float">3.4</value>
-                </value>
-                <value type="dict">
-                    <value name="one" type="int">2</value>
-                    <value name="two" type="str">b</value>
-                </value>
-            </property>
-            """
-        for property_type in ('dbus.Array', 'list'):
-            xml = xml_template % property_type
-            result = self._runPropertyTest(xml)
-            self.assertEqual(result,
-                             ('listtest', ([(1, 'int'),
-                                            ('a', 'str'),
-                                            ([(2, 'int'),
-                                              (3.4, 'float')], 'list'),
-                                            ({'one': (2, 'int'),
-                                              'two': ('b', 'str')}, 'dict')],
-                                           property_type)),
-                             'Invalid parsing result for list property: '
-                             '%s' % xml)
-
-    def testDictPropertyTypes(self):
-        """Dict properties are converted into ('name', a_dict).
-
-        a_dict is a Python dictionary, where the items represent the
-        values of the <value> sub-nodes of the <property>.
-        """
-        xml_template = """
-            <property name="dicttest" type="%s">
-                <value name="one" type="int">1</value>
-                <value name="two" type="str">a</value>
-                <value name="three" type="list">
-                    <value type="int">2</value>
-                    <value type="float">3.4</value>
-                </value>
-                <value name="four" type="dict">
-                    <value name="five" type="int">2</value>
-                    <value name="six" type="str">b</value>
-                </value>
-            </property>
-            """
-        for property_type in ('dbus.Dictionary', 'dict'):
-            xml = xml_template % property_type
-            result = self._runPropertyTest(xml)
-            self.assertEqual(
-                result,
-                ('dicttest', ({'one': (1, 'int'),
-                               'two': ('a', 'str'),
-                               'three': ([(2, 'int'),
-                                          (3.4, 'float')], 'list'),
-                               'four': ({'five': (2, 'int'),
-                                         'six': ('b', 'str')}, 'dict')},
-                              property_type)),
-                'Invalid parsing result for dict property: %s' % xml)
-
-    def testProperties(self):
-        """A set of properties is converted into a dictionary."""
-        node = etree.fromstring("""
-            <container>
-                <property name="one" type="int">1</property>
-                <property name="two" type="str">a</property>
-            </container>
-            """)
-        parser = SubmissionParser(self.log)
-        result = parser._parseProperties(node)
-        self.assertEqual(result,
-                         {'one': (1, 'int'),
-                          'two': ('a', 'str')},
-                         'Invalid parsing result for a property set')
-
-        # Duplicate property names raise a ValueError
-        node = etree.fromstring("""
-            <container>
-                <property name="one" type="int">1</property>
-                <property name="one" type="str">a</property>
-            </container>
-            """)
-        self.assertRaises(ValueError, parser._parseProperties, node)
-
-    def testDevice(self):
-        """A device node is converted into a dictionary."""
-        test = self
-
-        def _parseProperties(self, node):
-            test.assertTrue(isinstance(self, SubmissionParser))
-            test.assertEqual(node.tag, 'device')
-            return 'parsed properties'
-        parser = SubmissionParser(self.log)
-        parser._parseProperties = lambda node: _parseProperties(parser, node)
-
-        node = etree.fromstring("""
-            <device id="2" udi="/org/freedesktop/Hal/devices/acpi_CPU0"
-                    parent="1">
-                <property name="info.product" type="str">
-                    Intel(R) Core(TM)2 CPU
-                </property>
-            </device>
-            """)
-        result = parser._parseDevice(node)
-        self.assertEqual(result,
-                         {'id': 2,
-                          'udi': '/org/freedesktop/Hal/devices/acpi_CPU0',
-                          'parent': 1,
-                          'properties': 'parsed properties'},
-                         'Invalid parsing result for <device> (2)')
-
-        # the attribute "parent" may be omitted.
-        node = etree.fromstring("""
-            <device id="1" udi="/org/freedesktop/Hal/devices/computer">
-                <property name="info.product" type="str">Computer</property>
-            </device>
-            """)
-        result = parser._parseDevice(node)
-        self.assertEqual(result,
-                         {'id': 1,
-                          'udi': ROOT_UDI,
-                          'parent': None,
-                          'properties': 'parsed properties'},
-                         'Invalid parsing result for <device> (1)')
-
-    def testHal(self):
-        """The <hal> node is converted into a Python dict."""
-        test = self
-
-        def _parseDevice(self, node):
-            test.assertTrue(isinstance(self, SubmissionParser))
-            test.assertEqual(node.tag, 'device')
-            return 'parsed device node'
-        parser = SubmissionParser(self.log)
-        parser._parseDevice = lambda node: _parseDevice(parser, node)
-
-        node = etree.fromstring("""
-            <hal version="0.5.9.1">
-                <device/>
-                <device/>
-            </hal>
-            """)
-        result = parser._parseHAL(node)
-        self.assertEqual(result,
-                         {'version': '0.5.9.1',
-                          'devices': ['parsed device node',
-                                      'parsed device node']},
-                         'Invalid parsing result for <hal>')
-
-    def testProcessors(self):
-        """The <processors> node is converted into a Python list.
-
-        The list elements represent the <processor> nodes.
-        """
-        test = self
-
-        def _parseProperties(self, node):
-            test.assertTrue(isinstance(self, SubmissionParser))
-            test.assertEqual(node.tag, 'processor')
-            return 'parsed properties'
-        parser = SubmissionParser(self.log)
-        parser._parseProperties = lambda node: _parseProperties(parser, node)
-
-        node = etree.fromstring("""
-            <processors>
-                <processor id="123" name="0">
-                    <property/>
-                </processor>
-                <processor id="124" name="1">
-                    <property/>
-                </processor>
-            </processors>
-            """)
-        result = parser._parseProcessors(node)
-        self.assertEqual(result,
-                         [{'id': 123,
-                           'name': '0',
-                           'properties': 'parsed properties'},
-                          {'id': 124,
-                           'name': '1',
-                           'properties': 'parsed properties'}],
-                         'Invalid parsing result for <processors>')
-
-    def testAliases(self):
-        """The <aliases> node is converted into a Python list.
-
-        The list elements represent the <alias> nodes.
-        """
-        parser = SubmissionParser(self.log)
-        node = etree.fromstring("""
-            <aliases>
-                <alias target="1">
-                    <vendor>Artec</vendor>
-                    <model>Ultima 2000</model>
-                </alias>
-                <alias target="2">
-                    <vendor>Medion</vendor>
-                    <model>MD 4394</model>
-                </alias>
-            </aliases>
-            """)
-        result = parser._parseAliases(node)
-        self.assertEqual(result,
-                         [{'target': 1,
-                           'vendor': 'Artec',
-                           'model': 'Ultima 2000'},
-                          {'target': 2,
-                           'vendor': 'Medion',
-                           'model': 'MD 4394'}],
-                         'Invalid parsing result for <aliases>')
-
-    def testUdev(self):
-        """The content of the <udev> node is converted into a list of dicts.
-        """
-        parser = SubmissionParser(self.log)
-        node = etree.fromstring("""
-<udev>P: /devices/LNXSYSTM:00
-E: UDEV_LOG=3
-E: DEVPATH=/devices/LNXSYSTM:00
-E: MODALIAS=acpi:LNXSYSTM:
-
-P: /devices/pci0000:00/0000:00:1a.0
-E: UDEV_LOG=3
-E: DEVPATH=/devices/pci0000:00/0000:00:1a.0
-S: char/189:256
-</udev>
-""")
-        result = parser._parseUdev(node)
-        self.assertEqual(
-            [
-                {
-                    'P': '/devices/LNXSYSTM:00',
-                    'E': {
-                        'UDEV_LOG': '3',
-                        'DEVPATH': '/devices/LNXSYSTM:00',
-                        'MODALIAS': 'acpi:LNXSYSTM:',
-                        },
-                    'S': [],
-                    'id': 1,
-                    },
-                {
-                    'P': '/devices/pci0000:00/0000:00:1a.0',
-                    'E': {
-                        'UDEV_LOG': '3',
-                        'DEVPATH': '/devices/pci0000:00/0000:00:1a.0',
-                        },
-                    'S': ['char/189:256'],
-                    'id': 2,
-                    },
-                ],
-            result,
-            'Invalid parsing result for <udev>')
-
-    def testUdevLineWithoutColon(self):
-        """<udev> nodes with lines not in key: value format are rejected."""
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'Detect udev lines not in key:value format'
-        node = etree.fromstring("""
-<udev>P: /devices/LNXSYSTM:00
-bad line
-</udev>
-""")
-        result = parser._parseUdev(node)
-        self.assertEqual(
-            None, result,
-            'Invalid parsing result for a <udev> node with a line not having '
-            'the key: value format.')
-        self.assertErrorMessage(
-            parser.submission_key,
-            "Line 1 in <udev>: No valid key:value data: 'bad line'")
-
-    def testUdevPropertyLineWithoutEqualSign(self):
-        """<udev> nodes with lines not in key: value format are rejected."""
-        parser = SubmissionParser(self.log)
-        parser.submission_key = (
-            'Detect udev property lines not in key=value format')
-        node = etree.fromstring("""
-<udev>P: /devices/LNXSYSTM:00
-E: bad property
-</udev>
-""")
-        result = parser._parseUdev(node)
-        self.assertEqual(
-            None, result,
-            'Invalid parsing result for a <udev> node with a property line '
-            'not having the key=value format.')
-        self.assertErrorMessage(
-            parser.submission_key,
-            "Line 1 in <udev>: Property without valid key=value data: "
-            "'E: bad property'")
-
-    def testUdevDataWithDuplicateKey(self):
-        """<udev> nodes with lines not in key: value format are rejected."""
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'Detect duplactae attributes in udev data'
-        node = etree.fromstring("""
-<udev>P: /devices/LNXSYSTM:00
-W:1
-W:2
-</udev>
-""")
-        result = parser._parseUdev(node)
-        self.assertEqual(
-            [
-                {
-                    'P': '/devices/LNXSYSTM:00',
-                    'E': {},
-                    'S': [],
-                    'W': '2',
-                    'id': 1,
-                    },
-                ],
-            result,
-            'Invalid parsing result for a <udev> node with a duplicate '
-            'attribute.')
-        self.assertWarningMessage(
-            parser.submission_key,
-            "Line 2 in <udev>: Duplicate attribute key: 'W:2'")
-
-    def testDmi(self):
-        """The content of the <udev> node is converted into a dictionary."""
-        parser = SubmissionParser(self.log)
-        node = etree.fromstring("""<dmi>/sys/class/dmi/id/bios_vendor:LENOVO
-/sys/class/dmi/id/bios_version:7LETB9WW (2.19 )
-/sys/class/dmi/id/sys_vendor:LENOVO
-/sys/class/dmi/id/modalias:dmi:bvnLENOVO:bvr7LETB9WW
-</dmi>""")
-        result = parser._parseDmi(node)
-        self.assertEqual(
-            {
-                '/sys/class/dmi/id/bios_vendor': 'LENOVO',
-                '/sys/class/dmi/id/bios_version': '7LETB9WW (2.19 )',
-                '/sys/class/dmi/id/sys_vendor': 'LENOVO',
-                '/sys/class/dmi/id/modalias': 'dmi:bvnLENOVO:bvr7LETB9WW',
-                },
-            result,
-            'Invalid parsing result for <dmi>.')
-
-    def testDmiInvalidData(self):
-        """<dmi> nodes with lines not in key:value format are rejected."""
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'Invalid DMI data'
-        node = etree.fromstring("""<dmi>/sys/class/dmi/id/bios_vendor:LENOVO
-invalid line
-</dmi>""")
-        result = parser._parseDmi(node)
-        self.assertEqual(
-            None, result,
-            '<dmi> node with invalid data not deteced.')
-        self.assertErrorMessage(
-            parser.submission_key,
-            "Line 1 in <dmi>: No valid key:value data: 'invalid line'")
-
-    def testSysfsAttributes(self):
-        """Test of SubmissionParser._parseSysfsAttributes().
-
-        The content of the <sys-attributes> node is converted into
-        a dictionary.
-        """
-        parser = SubmissionParser(self.log)
-        node = etree.fromstring(dedent("""
-            <sysfs-attributes>
-            P: /devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
-            A: modalias=input:b0019v0000p0001e0000-e0,1,k74
-            A: uniq=
-            A: phys=LNXPWRBN/button/input0
-            A: name=Power Button
-
-            P: /devices/LNXSYSTM:00/device:00/PNP0A08:00/device:03
-            A: uniq=
-            A: phys=/video/input0
-            A: name=Video Bus
-            </sysfs-attributes>
-            """))
-        result = parser._parseSysfsAttributes(node)
-        self.assertEqual(
-            {
-                '/devices/LNXSYSTM:00/LNXPWRBN:00/input/input0': {
-                    'modalias': 'input:b0019v0000p0001e0000-e0,1,k74',
-                    'uniq': '',
-                    'phys': 'LNXPWRBN/button/input0',
-                    'name': 'Power Button',
-                    },
-                '/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:03': {
-                    'uniq': '',
-                    'phys': '/video/input0',
-                    'name': 'Video Bus',
-                    },
-
-                },
-            result,
-            'Invalid parsing result of <sysfs-attributes> node.')
-
-    def testSysfsAttributesLineWithoutKeyValueData(self):
-        """Test of SubmissionParser._parseSysfsAttributes().
-
-        Lines not in key: value format are rejected.
-        """
-        parser = SubmissionParser(self.log)
-        parser.submission_key = (
-            'Detect <sysfs-attributes> lines not in key:value format')
-        node = etree.fromstring(dedent("""
-            <sysfs-attributes>
-            P: /devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
-            A: modalias=input:b0019v0000p0001e0000-e0,1,k74
-            invalid line
-            </sysfs-attributes>
-            """))
-        result = parser._parseSysfsAttributes(node)
-        self.assertEqual(
-            None, result,
-            'Invalid parsing result of a <sysfs-attributes> node containing '
-            'a line not in key:value format.')
-        self.assertErrorMessage(
-            parser.submission_key,
-            "Line 3 in <sysfs-attributes>: No valid key:value data: "
-            "'invalid line'")
-
-    def testSysfsAttributesDuplicatePLine(self):
-        """Test of SubmissionParser._parseSysfsAttributes().
-
-        A line starting with "P:" must be the first line of a device block.
-        """
-        parser = SubmissionParser(self.log)
-        parser.submission_key = (
-            'Detect <sysfs-attributes> node with duplicate P: line')
-        node = etree.fromstring(dedent("""
-            <sysfs-attributes>
-            P: /devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
-            A: modalias=input:b0019v0000p0001e0000-e0,1,k74
-            P: /devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
-            </sysfs-attributes>
-            """))
-        result = parser._parseSysfsAttributes(node)
-        self.assertEqual(
-            None, result,
-            'Invalid parsing result of a <sysfs-attributes> node containing '
-            'a duplicate P: line.')
-        self.assertErrorMessage(
-            parser.submission_key,
-            "Line 3 in <sysfs-attributes>: duplicate 'P' line found: "
-            "'P: /devices/LNXSYSTM:00/LNXPWRBN:00/input/input0'")
-
-    def testSysfsAttributesNoPLineAtDeviceStart(self):
-        """Test of SubmissionParser._parseSysfsAttributes().
-
-        The data for a device must start with a "P:" line.
-        """
-        parser = SubmissionParser(self.log)
-        parser.submission_key = (
-            'Detect <sysfs-attributes> node without leading P: line')
-        node = etree.fromstring(dedent("""
-            <sysfs-attributes>
-            A: modalias=input:b0019v0000p0001e0000-e0,1,k74
-            </sysfs-attributes>
-            """))
-        result = parser._parseSysfsAttributes(node)
-        self.assertEqual(
-            None, result,
-            'Invalid parsing result of a <sysfs-attributes> node where a '
-            'device block does not start with a "P": line.')
-        self.assertErrorMessage(
-            parser.submission_key,
-            "Line 1 in <sysfs-attributes>: Block for a device does not "
-            "start with 'P:': "
-            "'A: modalias=input:b0019v0000p0001e0000-e0,1,k74'")
-
-    def testSysfsAttributesNoAttributeKeyValue(self):
-        """Test of SubmissionParser._parseSysfsAttributes().
-
-        A line starting with "A:" must be in key=value format.
-        """
-        parser = SubmissionParser(self.log)
-        parser.submission_key = (
-            'Detect <sysfs-attributes> node with A: line not in key=value '
-            'format')
-        node = etree.fromstring(dedent("""
-            <sysfs-attributes>
-            P: /devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
-            A: equal sign is missing
-            </sysfs-attributes>
-            """))
-        result = parser._parseSysfsAttributes(node)
-        self.assertEqual(
-            None, result,
-            'Invalid parsing result of a <sysfs-attributes> node with A: '
-            'line not in key=value format.')
-        self.assertErrorMessage(
-            parser.submission_key,
-            "Line 2 in <sysfs-attributes>: Attribute line does not contain "
-            "key=value data: 'A: equal sign is missing'")
-
-    def testSysfsAttributesInvalidMainKey(self):
-        """Test of SubmissionParser._parseSysfsAttributes().
-
-        All lines must start with "P:" or "A:".
-        """
-        parser = SubmissionParser(self.log)
-        parser.submission_key = (
-            'Detect <sysfs-attributes> node with invalid main key.')
-        node = etree.fromstring(dedent("""
-            <sysfs-attributes>
-            P: /devices/LNXSYSTM:00/LNXPWRBN:00/input/input0
-            X: an invalid line
-            </sysfs-attributes>
-            """))
-        result = parser._parseSysfsAttributes(node)
-        self.assertEqual(
-            None, result,
-            'Invalid parsing result of a <sysfs-attributes> node containg '
-            'a line that does not start with "A:" or "P:".')
-        self.assertErrorMessage(
-            parser.submission_key,
-            "Line 2 in <sysfs-attributes>: Unexpected key: "
-            "'X: an invalid line'")
-
-    class MockSubmissionParserParseHardwareTest(SubmissionParser):
-        """A SubmissionParser variant for testing checkCOnsistentData()
-
-        All "method substitutes" return a valid result.
-        """
-
-        def __init__(self, logger=None, record_warnings=True):
-            super(self.__class__, self).__init__(logger)
-            self.hal_result = 'parsed HAL data'
-            self.processors_result = 'parsed processor data'
-            self.aliases_result = 'parsed alias data'
-            self.udev_result = 'parsed udev data'
-            self.dmi_result = 'parsed DMI data'
-            self.sysfs_result = 'parsed sysfs data'
-
-        def _parseHAL(self, hal_node):
-            """See `SubmissionParser`."""
-            return self.hal_result
-
-        def _parseProcessors(self, processors_node):
-            """See `SubmissionParser`."""
-            return self.processors_result
-
-        def _parseAliases(self, aliases_node):
-            """See `SubmissionParser`."""
-            return self.aliases_result
-
-        def _parseUdev(self, udev_node):
-            """See `SubmissionParser`."""
-            return self.udev_result
-
-        def _parseDmi(self, dmi_node):
-            """See `SubmissionParser`."""
-            return self.dmi_result
-
-        def _parseSysfsAttributes(self, sysfs_node):
-            """See `SubmissionParser`."""
-            return self.sysfs_result
-
-    validate_mock_class(MockSubmissionParserParseHardwareTest)
-
-    def testHardware(self):
-        """The <hardware> tag is converted into a dictionary."""
-        parser = self.MockSubmissionParserParseHardwareTest(self.log)
-
-        node = etree.fromstring("""
-            <hardware>
-                <hal/>
-                <processors/>
-                <aliases/>
-                <udev/>
-                <dmi/>
-                <sysfs-attributes/>
-            </hardware>
-            """)
-        result = parser._parseHardware(node)
-        self.assertEqual({
-            'hal': 'parsed HAL data',
-            'processors': 'parsed processor data',
-            'aliases': 'parsed alias data',
-            'udev': 'parsed udev data',
-            'dmi': 'parsed DMI data',
-            'sysfs-attributes': 'parsed sysfs data',
-            },
-            result,
-            'Invalid parsing result for <hardware>')
-
-    def testHardware_no_sysfs_node(self):
-        """If teh <sysfs-attributes> node is missing, parseHardware()
-        returns a dicitionary where the entry for this node is None.
-        """
-        parser = self.MockSubmissionParserParseHardwareTest(self.log)
-
-        node = etree.fromstring("""
-            <hardware>
-                <hal/>
-                <processors/>
-                <aliases/>
-                <udev/>
-                <dmi/>
-            </hardware>
-            """)
-        result = parser._parseHardware(node)
-        self.assertEqual({
-            'hal': 'parsed HAL data',
-            'processors': 'parsed processor data',
-            'aliases': 'parsed alias data',
-            'udev': 'parsed udev data',
-            'dmi': 'parsed DMI data',
-            'sysfs-attributes': None,
-            },
-            result,
-            'Invalid parsing result for <hardware>')
-
-    def test_parseHardware_sub_parsers_fail(self):
-        """Test of SubmissionParser._parseHardware().
-
-        If one of the sub-parsers returns None, _parseHardware() returns
-        None.
-        """
-        node = etree.fromstring("""
-            <hardware>
-               <hal/>
-                <processors/>
-                <aliases/>
-                <udev/>
-                <dmi/>
-                <sysfs-attributes/>
-            </hardware>
-            """)
-
-        submission_parser = self.MockSubmissionParserParseHardwareTest()
-        submission_parser.hal_result = None
-        self.assertIs(None, submission_parser._parseHardware(node))
-
-        submission_parser = self.MockSubmissionParserParseHardwareTest()
-        submission_parser.processors_result = None
-        self.assertIs(None, submission_parser._parseHardware(node))
-
-        submission_parser = self.MockSubmissionParserParseHardwareTest()
-        submission_parser.aliases_result = None
-        self.assertIs(None, submission_parser._parseHardware(node))
-
-        submission_parser = self.MockSubmissionParserParseHardwareTest()
-        submission_parser.udev_result = None
-        self.assertIs(None, submission_parser._parseHardware(node))
-
-        submission_parser = self.MockSubmissionParserParseHardwareTest()
-        submission_parser.dmi_result = None
-        self.assertIs(None, submission_parser._parseHardware(node))
-
-        submission_parser = self.MockSubmissionParserParseHardwareTest()
-        submission_parser.sysfs_result = None
-        self.assertIs(None, submission_parser._parseHardware(node))
-
-    def testLsbRelease(self):
-        """The <lsbrelease> node is converted into a Python dictionary.
-
-        Each dict item represents a <property> sub-node.
-        """
-        node = etree.fromstring("""
-            <lsbrelease>
-                <property name="release" type="str">
-                    7.04
-                </property>
-                <property name="codename" type="str">
-                    feisty
-                </property>
-                <property name="distributor-id" type="str">
-                    Ubuntu
-                </property>
-                <property name="description" type="str">
-                    Ubuntu 7.04
-                </property>
-            </lsbrelease>
-            """)
-        parser = SubmissionParser(self.log)
-        result = parser._parseLSBRelease(node)
-        self.assertEqual(result,
-                         {'distributor-id': ('Ubuntu', 'str'),
-                          'release': ('7.04', 'str'),
-                          'codename': ('feisty', 'str'),
-                          'description': ('Ubuntu 7.04', 'str')},
-                         'Invalid parsing result for <lsbrelease>')
-
-    def testPackages(self):
-        """The <packages> node is converted into a Python dictionary.
-
-        Each dict item represents a <package> sub-node as
-        (package_name, package_data), where package_data
-        is a dictionary representing the <property> sub-nodes of a
-        <package> node.
-        """
-        node = etree.fromstring("""
-            <packages>
-                <package name="metacity" id="1">
-                    <property name="installed_size" type="int">
-                        868352
-                    </property>
-                    <property name="section" type="str">
-                        x11
-                    </property>
-                    <property name="summary" type="str">
-                        A lightweight GTK2 based Window Manager
-                    </property>
-                    <property name="priority" type="str">
-                        optional
-                    </property>
-                    <property name="source" type="str">
-                        metacity
-                    </property>
-                    <property name="version" type="str">
-                        1:2.18.2-0ubuntu1.1
-                    </property>
-                    <property name="size" type="int">
-                        429128
-                    </property>
-                </package>
-            </packages>
-            """)
-        parser = SubmissionParser(self.log)
-        result = parser._parsePackages(node)
-        self.assertEqual(result,
-                         {'metacity':
-                          {'id': 1,
-                           'properties':
-                            {'installed_size': (868352, 'int'),
-                             'priority': ('optional', 'str'),
-                             'section': ('x11', 'str'),
-                             'size': (429128, 'int'),
-                             'source': ('metacity', 'str'),
-                             'summary':
-                                 ('A lightweight GTK2 based Window Manager',
-                                  'str'),
-                             'version': ('1:2.18.2-0ubuntu1.1', 'str')}}},
-                         'Invalid parsing result for <packages>')
-
-    def testDuplicatePackage(self):
-        """Two <package> nodes with the same name are rejected."""
-        node = etree.fromstring("""
-            <packages>
-                <package name="foo" id="1">
-                    <property name="size" type="int">10000</property>
-                </package>
-                <package name="foo" id="1">
-                    <property name="size" type="int">10000</property>
-                </package>
-            </packages>
-            """)
-        self.assertRaises(ValueError, SubmissionParser()._parsePackages, node)
-
-    def testXorg(self):
-        """The <xorg> node is converted into a Python dictionary."""
-        node = etree.fromstring("""
-            <xorg version="1.1">
-                <driver name="fglrx" version="1.23"
-                        class="X.Org Video Driver" device="12"/>
-                <driver name="kbd" version="1.2.1"
-                        class="X.Org XInput driver" device="15"/>
-
-            </xorg>
-            """)
-        parser = SubmissionParser(self.log)
-        result = parser._parseXOrg(node)
-        self.assertEqual(result,
-                         {'version': '1.1',
-                         'drivers': {'fglrx': {'name': 'fglrx',
-                                               'version': '1.23',
-                                               'class': 'X.Org Video Driver',
-                                               'device': 12},
-                                     'kbd': {'name': 'kbd',
-                                             'version': '1.2.1',
-                                             'class': 'X.Org XInput driver',
-                                             'device': 15}}},
-                         'Invalid parsing result for <xorg>')
-
-    def testDuplicateXorgDriver(self):
-        """Two <driver> nodes in <xorg> with the same name are rejected."""
-        node = etree.fromstring("""
-            <xorg>
-                <driver name="mouse" class="X.Org XInput driver"/>
-                <driver name="mouse" class="X.Org XInput driver"/>
-            </xorg>
-            """)
-        self.assertRaises(ValueError, SubmissionParser()._parseXOrg, node)
-
-    def test_parseSoftware(self):
-        """Test SubmissionParser._parseSoftware
-
-        Ensure that all sub-parsers are properly called.
-        """
-        parser = SubmissionParserTestParseSoftware(self)
-
-        node = etree.fromstring("""
-            <software>
-                <lsbrelease/>
-                <packages/>
-                <xorg/>
-            </software>
-            """)
-        result = parser._parseSoftware(node)
-        self.assertEqual(result,
-                         {'lsbrelease': 'parsed lsb release',
-                          'packages': 'parsed packages',
-                          'xorg': 'parsed xorg'},
-                         'Invalid parsing result for <software>')
-
-    def test_parseSoftware_without_xorg_node(self):
-        """Test SubmissionParser._parseSoftware
-
-        Ensure that _parseSoftware creates an entry in its
-        result for <xorg> even if the submitted data does not
-        contains this node.
-        """
-        parser = SubmissionParserTestParseSoftwareNoXorgNode(self)
-
-        node = etree.fromstring("""
-            <software>
-                <lsbrelease/>
-                <packages/>
-            </software>
-            """)
-        result = parser._parseSoftware(node)
-        self.assertEqual(
-            result,
-            {
-                'lsbrelease': 'parsed lsb release',
-                'packages': 'parsed packages',
-                'xorg': {},
-            },
-            'Invalid parsing result for <software> without <xorg> sub-node')
-
-    def test_parseSoftware_without_packages_node(self):
-        """Test SubmissionParser._parseSoftware
-
-        Ensure that _parseSoftware creates an entry in its
-        result for <packages> even if the submitted data does not
-        contains this node.
-        """
-        parser = SubmissionParserTestParseSoftwareNoPackagesNode(self)
-
-        node = etree.fromstring("""
-            <software>
-                <lsbrelease/>
-                <xorg/>
-            </software>
-            """)
-        result = parser._parseSoftware(node)
-        self.assertEqual(
-            result,
-            {
-                'lsbrelease': 'parsed lsb release',
-                'packages': {},
-                'xorg': 'parsed xorg',
-            },
-            'Invalid parsing result for <software> without <packages> '
-            'sub-node')
-
-    def testMultipleChoiceQuestion(self):
-        """The <questions> node is converted into a Python dictionary."""
-        node = etree.fromstring("""
-            <questions>
-                <question name="detected_network_controllers"
-                          plugin="find_network_controllers">
-                    <target id="42">
-                        <driver>ipw3945</driver>
-                    </target>
-                    <target id="43"/>
-                    <command/>
-                    <answer type="multiple_choice">pass</answer>
-                    <answer_choices>
-                        <value type="str">fail</value>
-                        <value type="str">pass</value>
-                        <value type="str">skip</value>
-                    </answer_choices>
-                    <comment>
-                        The WLAN adapter drops the connection very frequently.
-                    </comment>
-                </question>
-            </questions>
-            """)
-        parser = SubmissionParser()
-        result = parser._parseQuestions(node)
-        self.assertEqual(
-            result,
-            [{'name': 'detected_network_controllers',
-              'plugin': 'find_network_controllers',
-              'targets': [{'id': 42,
-                           'drivers': ['ipw3945']},
-                          {'id': 43,
-                           'drivers': []}],
-              'answer': {'type': 'multiple_choice',
-                         'value': 'pass'},
-              'answer_choices': [('fail', 'str'),
-                                 ('pass', 'str'),
-                                 ('skip', 'str')],
-              'comment': 'The WLAN adapter drops the connection very '
-                         'frequently.'}],
-            'Invalid parsing result for multiple choice question')
-
-    def testMeasurementQuestion(self):
-        """The <questions> node is converted into a Python dictionary."""
-        node = etree.fromstring("""
-            <questions>
-                <question name="harddisk_speed"
-                          plugin="harddisk_speed">
-                    <target id="87"/>
-                    <command>hdparm -t /dev/sda</command>
-                    <answer type="measurement" unit="MB/sec">38.4</answer>
-                </question>
-            </questions>
-            """)
-        parser = SubmissionParser()
-        result = parser._parseQuestions(node)
-        self.assertEqual(
-            result,
-            [{
-              'name': 'harddisk_speed',
-              'plugin': 'harddisk_speed',
-              'answer': {'type': 'measurement',
-                         'value': '38.4',
-                         'unit': 'MB/sec'},
-              'targets': [{'drivers': [],
-                           'id': 87}],
-              'command': 'hdparm -t /dev/sda'}],
-            'Invalid parsing result for measurement question')
-
-    def testContext(self):
-        """The content of the <context> node is currently not processed.
-
-        Instead, a log warning is issued.
-        """
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'Test of <context> parsing'
-        node = etree.fromstring('<context/>')
-        parser._parseContext(node)
-        self.assertEqual({}, parser._parseContext(node))
-        self.assertWarningMessage(
-            parser.submission_key,
-            'Submission contains unprocessed <context> data.')
-
-    class MockSubmissionParserMainParserTest(SubmissionParser):
-        """A SubmissionParser variant for testing checkCOnsistentData()
-
-        All "method substitutes" return a valid result.
-        """
-
-        def __init__(self, logger=None, record_warnings=True):
-            SubmissionParser.__init__(self, logger)
-            self.summary_result = 'parsed summary'
-            self.hardware_result = 'parsed hardware'
-            self.software_result = 'parsed software'
-            self.questions_result = 'parsed questions'
-            self.context_result = 'parsed context'
-
-        def _parseSummary(self, summary_node):
-            """See `SubmissionParser`."""
-            return self.summary_result
-
-        def _parseHardware(self, hardware_node):
-            """See `SubmissionParser`."""
-            return self.hardware_result
-
-        def _parseSoftware(self, software_node):
-            """See `SubmissionParser`."""
-            return self.software_result
-
-        def _parseQuestions(self, questions_node):
-            """See `SubmissionParser`."""
-            return self.questions_result
-
-        def _parseContext(self, context_node):
-            """See `SubmissionParser`."""
-            return self.context_result
-
-    validate_mock_class(MockSubmissionParserMainParserTest)
-
-    def testMainParser(self):
-        """Test SubmissionParser.parseMainSections
-
-        Ensure that all sub-parsers are properly called.
-        """
-        parser = self.MockSubmissionParserMainParserTest()
-
-        node = etree.fromstring("""
-            <system>
-                <summary/>
-                <hardware/>
-                <software/>
-                <questions/>
-                <context/>
-            </system>
-            """)
-
-        expected_data = {
-            'summary': 'parsed summary',
-            'hardware': 'parsed hardware',
-            'software': 'parsed software',
-            'questions': 'parsed questions',
-            'context': 'parsed context',
-            }
-
-        result = parser.parseMainSections(node)
-        self.assertEqual(result, expected_data,
-            'SubmissionParser.parseSubmission returned an unexpected result')
-
-        parser = self.MockSubmissionParserMainParserTest()
-        parser.summary_result = None
-        self.assertIs(None, parser.parseMainSections(node))
-
-        parser = self.MockSubmissionParserMainParserTest()
-        parser.hardware_result = None
-        self.assertIs(None, parser.parseMainSections(node))
-
-        parser = self.MockSubmissionParserMainParserTest()
-        parser.software_result = None
-        self.assertIs(None, parser.parseMainSections(node))
-
-        parser = self.MockSubmissionParserMainParserTest()
-        parser.questions_result = None
-        self.assertIs(None, parser.parseMainSections(node))
-
-        parser = self.MockSubmissionParserMainParserTest()
-        parser.context_result = None
-        self.assertIs(None, parser.parseMainSections(node))
-
-    def testSubmissionParser(self):
-        """Test the entire parser."""
-        sample_data_path = os.path.join(
-            config.root, 'lib', 'lp', 'hardwaredb', 'scripts',
-            'tests', 'hardwaretest.xml')
-        sample_data = open(sample_data_path, 'rb').read()
-        parser = SubmissionParser()
-        result = parser.parseSubmission(sample_data, 'parser test 1')
-        self.assertNotEqual(result, None,
-                            'Valid submission data rejected by '
-                            'SubmissionParser.parseSubmission')
-
-        # parseSubmission returns None, if the submitted data is not
-        # well-formed XML...
-        result = parser.parseSubmission(
-            sample_data.replace(b'<summary', b'<inconsitent_opening_tag'),
-            'parser test 2')
-        self.assertEqual(result, None,
-                         'Not-well-formed XML data accepted by '
-                         'SubmissionParser.parseSubmission')
-
-        # ...or if RelaxNG validation fails...
-        result = parser.parseSubmission(
-            sample_data.replace(b'<summary', b'<summary foo="bar"'),
-            'parser test 3')
-        self.assertEqual(result, None,
-                         'XML data that does pass the Relax NG validation '
-                         'accepted by SubmissionParser.parseSubmission')
-
-        # ...or if the parser detects an inconsistency, like a
-        # property set containing two properties with the same name.
-        result = parser.parseSubmission(
-            sample_data.replace(
-                b'<property name="info.parent"',
-                b"""<property name="info.parent" type="dbus.String">
-                       foo
-                   </property>
-                   <property name="info.parent"
-                """,
-                1),
-            'parser test 4')
-        self.assertEqual(result, None,
-                         'XML data that does pass the Relax NG validation '
-                         'accepted by SubmissionParser.parseSubmission')
-
-    def testFindDuplicates(self):
-        """Test of SubmissionParser._findDuplicates."""
-        # If all_ids is empty before the call of _findDuplicates, all
-        # elements of test_ids is copied to all_ids. Since test_ids does
-        # not contains duplicates, the return value of _findDuplicates
-        # is empty.
-        all_ids = set()
-        test_ids = [1, 2, 3]
-        parser = SubmissionParser()
-        result = parser._findDuplicates(all_ids, test_ids)
-        self.assertEqual(result, set(),
-                         '_findDuplicates found duplicates where none exist')
-        self.assertEqual(all_ids, set((1, 2, 3)),
-                         '_findDuplicates did not update all_ids properly'
-                         'with unique elements (1, 2, 3)')
-
-        # An element that appears both in all_ids and test_ids is included
-        # in the return value.
-        test_ids = [3, 4]
-        result = parser._findDuplicates(all_ids, test_ids)
-        self.assertEqual(result, set((3,)),
-                         '_findDuplicates did not detect an element in '
-                         'test_ids which already existed in all_ids')
-        self.assertEqual(all_ids, set((1, 2, 3, 4)),
-                         '_findDuplicates did not update all_ids with '
-                         'test_ids (3, 4)')
-
-        # If an element exists twice in test_ids, it is detected as a
-        # duplicate.
-        test_ids = [5, 5]
-        result = parser._findDuplicates(all_ids, test_ids)
-        self.assertEqual(result, set((5,)),
-                         '_findDuplicates did not detect a element which '
-                         'exists twice in test_ids')
-        self.assertEqual(all_ids, set((1, 2, 3, 4, 5)),
-                         '_findDuplicates did not update all_ids with a '
-                         'duplicate element of test_ids')
-
-    def testFindDuplicateIDs(self):
-        """SubmissionParser.findDuplicateIDs lists duplicate IDS.
-
-        The IDs of HAL devices, processors and packages should be
-        unique.
-        """
-        devices = [{'id': 1},
-                   {'id': 2}]
-        processors = [{'id': 3},
-                      {'id': 4}]
-        packages = {'bzr': {'id': 5},
-                    'python-dev': {'id': 6}}
-        submission = {
-            'hardware': {
-                'hal': {'devices': devices},
-                'processors': processors},
-            'software': {'packages': packages}}
-
-        parser = SubmissionParser()
-        duplicates = parser.findDuplicateIDs(submission)
-        self.assertEqual(
-            duplicates, set(),
-            'Duplicate IDs detected, where no duplicates exist.')
-
-        for duplicate_entry in ({'id': 1},
-                                {'id': 3},
-                                {'id': 5}):
-            devices.append(duplicate_entry)
-            duplicates = parser.findDuplicateIDs(submission)
-            self.assertEqual(
-                duplicates, set((duplicate_entry['id'],)),
-                'Duplicate ID %i in HAL devices not detected.'
-                % duplicate_entry['id'])
-            devices.pop()
-
-            processors.append(duplicate_entry)
-            duplicates = parser.findDuplicateIDs(submission)
-            self.assertEqual(
-                duplicates, set((duplicate_entry['id'],)),
-                'Duplicate ID %i in processors not detected.'
-                % duplicate_entry['id'])
-            processors.pop()
-
-            packages['python-xml'] = duplicate_entry
-            duplicates = parser.findDuplicateIDs(submission)
-            self.assertEqual(
-                duplicates, set((duplicate_entry['id'],)),
-                'Duplicate ID %i in packages not detected.'
-                % duplicate_entry['id'])
-            del packages['python-xml']
-
-    def testFindDuplicateIDsUdev(self):
-        """SubmissionParser.findDuplicateIDs lists duplicate IDS.
-
-        The IDs of udev devices, processors and packages should be
-        unique.
-        """
-        udev = [
-            {'P': '/devices/LNXSYSTM:00'},
-            {'P': '/devices/LNXSYSTM:00/ACPI_CPU:00'},
-            ]
-        sysfs_attributes = [
-            {'P': '/devices/LNXSYSTM:00'},
-            ]
-        processors = [
-            {'id': 1},
-            {'id': 2},
-            ]
-        packages = {
-            'bzr': {'id': 4},
-            'python-dev': {'id': 6},
-            }
-        submission = {
-            'hardware': {
-                'udev': udev,
-                'sysfs-attributes': sysfs_attributes,
-                'processors': processors,
-                },
-            'software': {
-                'packages': packages
-                }
-            }
-
-        parser = SubmissionParser()
-        duplicates = parser.findDuplicateIDs(submission)
-        self.assertEqual(
-            set(), duplicates,
-            'Duplicate IDs for udev submission detected, where no duplicates '
-            'exist.')
-
-    def testFindDuplicateIDsDuplicateUdevNode(self):
-        """SubmissionParser.findDuplicateIDs lists duplicate IDS.
-
-        Two udev dictionaries with the same device['P'] value are
-        invalid.
-        """
-        udev = [
-            {'P': '/devices/LNXSYSTM:00'},
-            {'P': '/devices/LNXSYSTM:00'},
-            ]
-        sysfs_attributes = [
-            {'P': '/devices/LNXSYSTM:00'},
-            ]
-        processors = [
-            {'id': 1},
-            {'id': 2},
-            ]
-        packages = {
-            'bzr': {'id': 4},
-            'python-dev': {'id': 6},
-            }
-        submission = {
-            'hardware': {
-                'udev': udev,
-                'sysfs-attributes': sysfs_attributes,
-                'processors': processors,
-                },
-            'software': {
-                'packages': packages
-                }
-            }
-
-        parser = SubmissionParser()
-        duplicates = parser.findDuplicateIDs(submission)
-        self.assertEqual(
-            set(('/devices/LNXSYSTM:00', )), duplicates,
-            'Duplicate udev nodes not detected.')
-
-    def testIDMap(self):
-        """Test of SubmissionParser._getIDMap."""
-        devices = [{'id': 1},
-                   {'id': 2}]
-        processors = [{'id': 3},
-                      {'id': 4}]
-        packages = {'bzr': {'id': 5},
-                    'python-dev': {'id': 6}}
-        submission = {
-            'hardware': {
-                'hal': {'devices': devices},
-                'processors': processors},
-            'software': {'packages': packages}}
-
-        parser = SubmissionParser()
-        result = parser._getIDMap(submission)
-        self.assertEqual(result,
-                         {1: devices[0],
-                          2: devices[1],
-                          3: processors[0],
-                          4: processors[1],
-                          5: packages['bzr'],
-                          6: packages['python-dev']},
-                         'Invalid result of SubmissionParser._getIDMap')
-
-    def testIDMapUdev(self):
-        """Test of SubmissionParser._getIDMap.
-
-        Variant for submissions with udev data.
-        """
-        devices = [
-            {'P': '/devices/LNXSYSTM:00'},
-            {'P': '/devices/LNXSYSTM:00/ACPI_CPU:00'},
-            ]
-        processors = [
-            {'id': 3},
-            {'id': 4},
-            ]
-        packages = {
-            'bzr': {'id': 5},
-            }
-        submission = {
-            'hardware': {
-                'udev': devices,
-                'processors': processors,
-                },
-            'software': {
-                'packages': packages
-                }
-            }
-
-        parser = SubmissionParser()
-        result = parser._getIDMap(submission)
-        self.assertEqual(
-            {
-                '/devices/LNXSYSTM:00': devices[0],
-                '/devices/LNXSYSTM:00/ACPI_CPU:00': devices[1],
-                3: processors[0],
-                4: processors[1],
-                5: packages['bzr'],
-                },
-            result,
-            'Invalid result of SubmissionParser._getIDMap')
-
-    def testInvalidIDReferencesUdev(self):
-        """Test of SubmissionParser.checkIDReferences.
-
-        Variant for submissions containing udev data.
-        """
-        devices = [{'id': 1},
-                   {'id': 2}]
-        processors = [{'id': 3},
-                      {'id': 4}]
-        packages = {'bzr': {'id': 5},
-                    'python-dev': {'id': 6}}
-        processors = [{'id': 3},
-                      {'id': 4}]
-        questions = [{'targets': [{'id': 1}]},
-                     {'targets': [{'id': 2},
-                                  {'id': 3}]}]
-        submission = {
-            'hardware': {
-                'hal': {'devices': devices},
-                        'processors': processors},
-            'software': {'packages': packages},
-            'questions': questions}
-        parser = SubmissionParser()
-        invalid_ids = parser.findInvalidIDReferences(submission)
-        self.assertEqual(invalid_ids, set(),
-                         'Invalid ID references detected where none exist')
-
-        questions.append({'targets': [{'id': -1}]})
-        invalid_ids = parser.findInvalidIDReferences(submission)
-        self.assertEqual(invalid_ids, set([-1]),
-                         'Invalid ID reference not detected')
-
-    DEVICE_2_UDI = '/org/freedesktop/Hal/devices/acpi_AC'
-    DEVICE_3_UDI = '/org/freedesktop/Hal/devices/pci_8086_27c5'
-    DEVICE_4_UDI = '/org/freedesktop/Hal/devices/usb_device_0_0_0000_00_1d_7'
-    _udi_device_test_data = [
-        {'udi': ROOT_UDI,
-          'properties': {}},
-         {'udi': DEVICE_2_UDI,
-          'properties': {
-              'info.parent': (ROOT_UDI,
-                              'dbus.String')}}]
-
-    def testUDIDeviceMap(self):
-        """Test the creation of the mapping UDI -> device."""
-        SSB_UDI = '/org/freedesktop/Hal/devices/ssb__null_'
-        SSB_CHILD_UDI = '/org/freedesktop/Hal/devices/net_00_1a_73_a3_8f_a4_0'
-        device1 = {
-              'id': 1,
-              'udi': ROOT_UDI,
-              }
-        device2 = {
-            'id': 2,
-            'udi': self.DEVICE_2_UDI,
-            'properties': {
-                'info.parent': (ROOT_UDI, 'str'),
-                },
-            }
-        device3 = {
-            'id': 3,
-            'udi': SSB_UDI,
-            'properties': {
-                'info.parent': (ROOT_UDI, 'str'),
-                },
-            }
-        device4 = {
-            'id': 4,
-            'udi': SSB_CHILD_UDI,
-            'properties': {
-                'info.parent': (SSB_UDI, 'str'),
-                },
-            }
-
-        devices = [device1, device2]
-
-        parser = SubmissionParser()
-        udi_devices = parser.getUDIDeviceMap(devices)
-        self.assertEqual(udi_devices,
-                         {ROOT_UDI: device1,
-                          self.DEVICE_2_UDI: device2},
-                         'Invalid result of SubmissionParser.getUDIDeviceMap')
-
-        # Generally, duplicate UDIs raise a ValueError.
-        devices.append(device2)
-        self.assertRaises(ValueError, parser.getUDIDeviceMap, devices)
-
-        # Exceptions are devices with certain UDIs which are known
-        # to appear sometimes more than once in HWDB submissions.
-        devices = [device1, device2, device3, device4, device3, device4]
-        udi_devices = parser.getUDIDeviceMap(devices)
-        self.assertEqual(
-            udi_devices,
-            {
-                ROOT_UDI: device1,
-                self.DEVICE_2_UDI: device2,
-                SSB_UDI: device3,
-                SSB_CHILD_UDI: device4,
-                },
-            'Unexpected result of processing a device list with duplicate '
-            'SSB UDIs')
-        self.assertEqual(
-            devices, [device1, device2, device3, device4],
-            'Unexpected list of devices after removing duplicates.')
-
-    def testIDUDIMaps(self):
-        """Test of SubmissionParser._getIDUDIMaps."""
-        device1 = {'id': 1,
-                   'udi': ROOT_UDI}
-        device2 = {'id': 2,
-                   'udi': self.DEVICE_2_UDI}
-        devices = [device1, device2]
-
-        parser = SubmissionParser()
-        id_to_udi, udi_to_id = parser._getIDUDIMaps(devices)
-        self.assertEqual(id_to_udi,
-                         {1: ROOT_UDI,
-                          2: self.DEVICE_2_UDI},
-                         '_getIDUDIMaps returned invalid ID -> UDI map')
-        self.assertEqual(udi_to_id,
-                         {ROOT_UDI: 1,
-                          self.DEVICE_2_UDI: 2},
-                         '_getIDUDIMaps returned invalid UDI -> ID map')
-
-    def testUDIChildren(self):
-        """Test of SubmissionParser.getUDIChildren."""
-        device1 = {'id': 1,
-                   'udi': ROOT_UDI,
-                   'properties': {}}
-        device2 = {'id': 2,
-                   'udi': self.DEVICE_2_UDI,
-                   'properties':
-                       {'info.parent':
-                            (ROOT_UDI, 'str')}}
-        device3 = {'id': 3,
-                   'udi': self.DEVICE_3_UDI,
-                   'properties':
-                       {'info.parent':
-                            (ROOT_UDI, 'str')}}
-        device4 = {'id': 4,
-                   'udi': self.DEVICE_4_UDI,
-                   'properties':
-                       {'info.parent':
-                            (self.DEVICE_2_UDI,
-                             'str')}}
-        devices = [device1, device2, device3, device4]
-
-        parser = SubmissionParser()
-        udi_device_map = parser.getUDIDeviceMap(devices)
-        udi_children = parser.getUDIChildren(udi_device_map)
-        expected_data = {ROOT_UDI: [device2, device3],
-                         self.DEVICE_2_UDI: [device4]}
-
-        # The order of the children lists returned by getUDIChildren
-        # depends on the order of dict.items(), hence sort the children
-        # lists before comparing them.
-        for children in udi_children.values():
-            children.sort()
-        for children in expected_data.values():
-            children.sort()
-
-        self.assertEqual(udi_children, expected_data,
-                         'Invalid result of SubmissionParser.getUDIChildren')
-
-    def testUDIDeviceMapInvalidRootNode(self):
-        """The root node of the devices must have a special UDI.
-
-        getUDIChildren ensures that the only device without an info.parent
-        property has the UDI /org/freedesktop/Hal/devices/computer (ROOT_UDI).
-        """
-        device1 = {'id': 1,
-                   'udi': 'invalid_root_node',
-                   'properties': {}}
-        device2 = {'id': 2,
-                   'udi': self.DEVICE_2_UDI,
-                   'properties':
-                       {'info.parent':
-                            ('invalid_root_node', 'str')}}
-        devices = [device1, device2]
-
-        parser = SubmissionParser()
-        udi_device_map = parser.getUDIDeviceMap(devices)
-        self.assertRaises(ValueError, parser.getUDIChildren, udi_device_map)
-
-    def testUDIDeviceMapMissingRootNode(self):
-        """If no root node exists, getUDIChildren raises a ValueError."""
-        device1 = {'id': 1,
-                   'udi': self.DEVICE_2_UDI,
-                   'properties':
-                       {'info.parent':
-                            (self.DEVICE_3_UDI, 'str')}}
-        device2 = {'id': 2,
-                   'udi': self.DEVICE_3_UDI,
-                   'properties':
-                       {'info.parent':
-                            (self.DEVICE_2_UDI, 'str')}}
-        devices = [device1, device2]
-
-        parser = SubmissionParser()
-        udi_device_map = parser.getUDIDeviceMap(devices)
-        self.assertRaises(ValueError, parser.getUDIChildren, udi_device_map)
-
-    CIRCULAR_UDI_1 = '/org/freedesktop/Hal/devices/nonsense_1'
-    CIRCULAR_UDI_2 = '/org/freedesktop/Hal/devices/nonsense_2'
-
-    def testParentChildInconsistency(self):
-        """Test of SubmissionParser.checkHALDevicesParentChildConsistency."""
-        device1 = {'id': 1,
-                   'udi': ROOT_UDI,
-                   'properties': {}}
-        device2 = {'id': 2,
-                   'udi': self.DEVICE_2_UDI,
-                   'properties':
-                       {'info.parent':
-                            (ROOT_UDI, 'str')}}
-        circular_device1 = {
-            'id': 3,
-            'udi': self.CIRCULAR_UDI_1,
-                   'properties':
-                       {'info.parent':
-                            (self.CIRCULAR_UDI_2, 'str')}}
-        circular_device2 = {
-            'id': 4,
-            'udi': self.CIRCULAR_UDI_2,
-                   'properties':
-                       {'info.parent':
-                            (self.CIRCULAR_UDI_1, 'str')}}
-        devices = [device1, device2, circular_device1, circular_device2]
-        parser = SubmissionParser()
-        udi_device_map = parser.getUDIDeviceMap(devices)
-        udi_children = parser.getUDIChildren(udi_device_map)
-        circular_udis = sorted(parser.checkHALDevicesParentChildConsistency(
-            udi_children))
-        self.assertEqual(circular_udis,
-                         [self.CIRCULAR_UDI_1, self.CIRCULAR_UDI_2],
-                         'Circular parent/child relationship in UDIs not '
-                         'detected')
-
-    def testCheckUdevDictsHavePathKey(self):
-        """Test of SubmissionParser.checkNodesHavePathKey()"""
-        # Each dict for a udev device must have a 'P' key.
-        parser = SubmissionParser(self.log)
-        devices = [
-            {'P': '/devices/LNXSYSTM:00'},
-            {'P': '/devices/LNXSYSTM:00/ACPI_CPU:00'},
-            ]
-        self.assertTrue(parser.checkUdevDictsHavePathKey(devices))
-
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'Submission having udev data without "P" key'
-        devices = [
-            {'P': '/devices/LNXSYSTM:00'},
-            {},
-            ]
-        self.assertFalse(parser.checkUdevDictsHavePathKey(devices))
-
-        self.assertErrorMessage(
-            parser.submission_key, 'udev node found without a "P" key')
-
-    def testCheckUdevPciProperties(self):
-        """Test of SubmissionParser.checkUdevPciProperties()."""
-        # udev PCI devices must have the properties PCI_CLASS, PCI_ID,
-        # PCI_SUBSYS_ID, PCI_SLOT_NAME; other devices must not have
-        # these properties.
-        parser = SubmissionParser()
-        self.assertTrue(parser.checkUdevPciProperties(
-            [self.udev_root_device, self.udev_pci_device]))
-
-    def testCheckUdevPciPropertiesNonPciDeviceWithPciProperties(self):
-        """Test of SubmissionParser.checkUdevPciProperties().
-
-        A non-PCI device having PCI properties makes a submission invalid.
-        """
-        self.udev_root_device['E']['PCI_SLOT_NAME'] = '0000:00:1f.2'
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'invalid non-PCI device'
-        self.assertFalse(parser.checkUdevPciProperties(
-            [self.udev_root_device, self.udev_pci_device]))
-        self.assertErrorMessage(
-            parser.submission_key,
-            "Non-PCI udev device with PCI properties: set([u'PCI_SLOT_NAME']) "
-            "u'/devices/LNXSYSTM:00'")
-
-    def testCheckUdevPciPropertiesPciDeviceWithoutRequiredProperties(self):
-        """Test of SubmissionParser.checkUdevPciProperties().
-
-        A PCI device not having a required PCI property makes a submission
-        invalid.
-        """
-        del self.udev_pci_device['E']['PCI_CLASS']
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'invalid PCI device'
-        self.assertFalse(parser.checkUdevPciProperties(
-            [self.udev_root_device, self.udev_pci_device]))
-        self.assertErrorMessage(
-            parser.submission_key,
-            "PCI udev device without required PCI properties: "
-            "set(['PCI_CLASS']) u'/devices/pci0000:00/0000:00:1f.2'")
-
-    def testCheckUdevPciPropertiesPciDeviceWithNonIntegerPciClass(self):
-        """Test of SubmissionParser.checkUdevPciProperties().
-
-        A PCI device with a non-integer class value makes a submission
-        invalid.
-        """
-        self.udev_pci_device['E']['PCI_CLASS'] = 'not-an-integer'
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'invalid PCI class value'
-        self.assertFalse(parser.checkUdevPciProperties(
-            [self.udev_root_device, self.udev_pci_device]))
-        self.assertErrorMessage(
-            parser.submission_key,
-            "Invalid udev PCI class: u'not-an-integer' "
-            "u'/devices/pci0000:00/0000:00:1f.2'")
-
-    def testCheckUdevPciPropertiesPciDeviceWithInvalidPciClassValue(self):
-        """Test of SubmissionParser.checkUdevPciProperties().
-
-        A PCI device with invalid class data makes a submission
-        invalid.
-        """
-        self.udev_pci_device['E']['PCI_CLASS'] = '1234567'
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'too large PCI class value'
-        self.assertFalse(parser.checkUdevPciProperties(
-            [self.udev_root_device, self.udev_pci_device]))
-        self.assertErrorMessage(
-            parser.submission_key,
-            "Invalid udev PCI class: u'1234567' "
-            "u'/devices/pci0000:00/0000:00:1f.2'")
-
-    def testCheckUdevPciPropertiesPciDeviceWithInvalidDeviceID(self):
-        """Test of SubmissionParser.checkUdevPciProperties().
-
-        A PCI device with an invalid device ID makes a submission
-        invalid.
-        """
-        self.udev_pci_device['E']['PCI_ID'] = 'not-an-id'
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'invalid PCI ID'
-        self.assertFalse(parser.checkUdevPciProperties(
-            [self.udev_root_device, self.udev_pci_device]))
-        self.assertErrorMessage(
-            parser.submission_key,
-            "Invalid udev PCI device ID: u'not-an-id' "
-            "u'/devices/pci0000:00/0000:00:1f.2'")
-
-    def testCheckUdevPciPropertiesPciDeviceWithInvalidSubsystemID(self):
-        """Test of SubmissionParser.checkUdevPciProperties().
-
-        A PCI device with an invalid subsystem ID makes a submission
-        invalid.
-        """
-        self.udev_pci_device['E']['PCI_SUBSYS_ID'] = 'not-a-subsystem-id'
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'invalid PCI subsystem ID'
-        self.assertFalse(parser.checkUdevPciProperties(
-            [self.udev_root_device, self.udev_pci_device]))
-        self.assertErrorMessage(
-            parser.submission_key,
-            "Invalid udev PCI device ID: u'not-a-subsystem-id' "
-            "u'/devices/pci0000:00/0000:00:1f.2'")
-
-    def testCheckUdevUsbProperties(self):
-        """Test of SubmissionParser.checkUdevUsbProperties().
-
-        udev nodes for USB devices must define the three properties
-        DEVTYPE, PRODUCT, TYPE or none of them.
-        """
-        parser = SubmissionParser()
-        self.assertTrue(parser.checkUdevUsbProperties(
-            [self.udev_root_device, self.udev_usb_device,
-             self.udev_usb_interface]))
-
-        for property_name in ('DEVTYPE', 'PRODUCT', 'TYPE'):
-            del self.udev_usb_device['E'][property_name]
-        self.assertTrue(parser.checkUdevUsbProperties(
-            [self.udev_root_device, self.udev_usb_device,
-             self.udev_usb_interface]))
-
-    def testCheckUdevUsbProperties_missing_required_property(self):
-        """Test of SubmissionParser.checkUdevUsbProperties().
-
-        A USB device where some but not all of the properties DEVTYPE,
-        PRODUCT, TYPE are defined makes a submission invalid.
-        """
-        for property_name in ('DEVTYPE', 'PRODUCT', 'TYPE'):
-            saved_property = self.udev_usb_device['E'].pop(property_name)
-            parser = SubmissionParser(self.log)
-            parser.submission_key = (
-                'USB device without %s property' % property_name)
-            self.assertFalse(parser.checkUdevUsbProperties(
-                [self.udev_root_device, self.udev_usb_device]))
-            self.assertErrorMessage(
-                parser.submission_key,
-                "USB udev device found without required properties: "
-                "set(['%s']) u'/devices/pci0000:00/0000:00:1d.1/usb3/3-2'"
-                % property_name)
-            self.udev_usb_device['E'][property_name] = saved_property
-
-    def testCheckUdevUsbProperties_with_invalid_product_id(self):
-        """Test of SubmissionParser.checkUdevUsbProperties().
-
-        A USB device with an invalid product ID makes a submission
-        invalid.
-        """
-        self.udev_usb_device['E']['PRODUCT'] = 'not-a-valid-usb-product-id'
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'USB device with invalid product ID'
-        self.assertFalse(parser.checkUdevUsbProperties(
-            [self.udev_root_device, self.udev_usb_device]))
-        self.assertErrorMessage(
-            parser.submission_key,
-            "USB udev device found with invalid product ID: "
-            "u'not-a-valid-usb-product-id' "
-            "u'/devices/pci0000:00/0000:00:1d.1/usb3/3-2'")
-
-    def testCheckUdevUsbProperties_with_invalid_type_data(self):
-        """Test of SubmmissionParser.checkUdevUsbProperties().
-
-        A USB device with invalid type data makes a submission invalid.
-        """
-        self.udev_usb_device['E']['TYPE'] = 'no-type'
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'USB device with invalid type data'
-        self.assertFalse(parser.checkUdevUsbProperties(
-            [self.udev_root_device, self.udev_usb_device]))
-        self.assertErrorMessage(
-            parser.submission_key,
-            "USB udev device found with invalid type data: u'no-type' "
-            "u'/devices/pci0000:00/0000:00:1d.1/usb3/3-2'")
-
-    def testCheckUdevUsbProperties_with_invalid_devtype(self):
-        """Test of SubmmissionParser.checkUdevUsbProperties().
-
-        A udev USB device must have DEVTYPE set to 'usb_device' or
-        'usb_interface'.
-        """
-        self.udev_usb_device['E']['DEVTYPE'] = 'nonsense'
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'USB device with invalid DEVTYPE'
-        self.assertFalse(parser.checkUdevUsbProperties(
-            [self.udev_root_device, self.udev_usb_device]))
-        self.assertErrorMessage(
-            parser.submission_key,
-            "USB udev device found with invalid udev type data: u'nonsense' "
-            "u'/devices/pci0000:00/0000:00:1d.1/usb3/3-2'")
-
-    def testCheckUdevUsbProperties_interface_without_interface_property(self):
-        """Test of SubmmissionParser.checkUdevUsbProperties().
-
-        A udev USB device for a USB interface have the property INTERFACE.
-        """
-        del self.udev_usb_interface['E']['INTERFACE']
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'USB interface without INTERFACE property'
-        self.assertFalse(parser.checkUdevUsbProperties(
-            [self.udev_root_device, self.udev_usb_interface]))
-        self.assertErrorMessage(
-            parser.submission_key,
-            "USB interface udev device found without INTERFACE property: "
-            "u'/devices/pci0000:00/0000:00:1d.1/usb3/3-2/3-2:1.1'")
-
-    def testCheckUdevUsbProperties_interface_invalid_interface_property(self):
-        """Test of SubmmissionParser.checkUdevUsbProperties().
-
-        The INTERFACE proeprty of A udev USB device for a USB interface
-        must have value in the format main_class/sub_class/version
-        """
-        self.udev_usb_interface['E']['INTERFACE'] = 'nonsense'
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'USB interface with invalid INTERFACE data'
-        self.assertFalse(parser.checkUdevUsbProperties(
-            [self.udev_root_device, self.udev_usb_interface]))
-        self.assertErrorMessage(
-            parser.submission_key,
-            "USB Interface udev device found with invalid INTERFACE "
-            "property: u'nonsense' "
-            "u'/devices/pci0000:00/0000:00:1d.1/usb3/3-2/3-2:1.1'")
-
-    def testCheckUdevScsiProperties(self):
-        """Test of SubmissionParser.checkUdevScsiProperties()."""
-        parser = SubmissionParser()
-        sysfs_data = {
-            self.udev_scsi_device['P']: self.sysfs_scsi_device,
-            }
-        self.assertTrue(
-            parser.checkUdevScsiProperties(
-                [self.udev_root_device, self.udev_scsi_device], sysfs_data))
-
-    def testCheckUdevScsiProperties_data_is_none(self):
-        """Test of SubmissionParser.checkUdevScsiProperties().
-
-        checkUdevScsiProperties() even if no sysfs properties are
-        available.
-        """
-        parser = SubmissionParser()
-        self.assertTrue(
-            parser.checkUdevScsiProperties(
-                [self.udev_root_device, self.udev_scsi_device], None))
-
-    def testCheckUdevScsiProperties_missing_devtype(self):
-        """Test of SubmissionParser.checkUdevScsiProperties().
-
-        Each udev SCSI node must define the DEVTYPE property.
-        """
-        del self.udev_scsi_device['E']['DEVTYPE']
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'udev SCSI device without DEVTYPE'
-        sysfs_data = {
-            self.udev_scsi_device['P']: self.sysfs_scsi_device,
-            }
-        self.assertFalse(
-            parser.checkUdevScsiProperties(
-                [self.udev_root_device, self.udev_scsi_device], sysfs_data))
-        self.assertErrorMessage(
-            parser.submission_key,
-            "SCSI udev node found without DEVTYPE property: "
-            "u'/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/4:0:0:0'")
-
-    def testCheckUdevScsiProperties_no_sysfs_data(self):
-        """Test of SubmissionParser.checkUdevScsiProperties().
-
-        Each udev SCSI node must have a corresponding sysfs node.
-        """
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'udev SCSI device without sysfs data'
-        sysfs_data = {}
-        self.assertFalse(
-            parser.checkUdevScsiProperties(
-                [self.udev_root_device, self.udev_scsi_device], sysfs_data))
-        self.assertErrorMessage(
-            parser.submission_key,
-            "SCSI udev device node found without related sysfs record: "
-            "u'/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/4:0:0:0'")
-
-    def testCheckUdevScsiProperties_missing_sysfs_attributes(self):
-        """Test of SubmissionParser.checkUdevScsiProperties().
-
-        Each sysfs node for a udev SCSI node must have a the attribues
-        vendor, model and type.
-        """
-        del self.sysfs_scsi_device['model']
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'udev SCSI device with incomplete sysfs data'
-        sysfs_data = {
-            self.udev_scsi_device['P']: self.sysfs_scsi_device,
-            }
-        self.assertFalse(
-            parser.checkUdevScsiProperties(
-                [self.udev_root_device, self.udev_scsi_device], sysfs_data))
-        self.assertErrorMessage(
-            parser.submission_key,
-            "SCSI udev device found without required sysfs attributes: "
-            "set(['model']) "
-            "u'/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/4:0:0:0'")
-
-    class UdevTestSubmissionParser(SubmissionParser):
-        """A variant of SubmissionParser that shortcuts udev related tests.
-
-        All shortcut methods return True.
-        """
-        def checkUdevDictsHavePathKey(self, udev_nodes):
-            """See `SubmissionParser`."""
-            return True
-
-        def checkUdevPciProperties(self, udev_data):
-            """See `SubmissionParser`."""
-            return True
-
-        def checkUdevUsbProperties(self, udev_data):
-            """See `SubmissionParser`."""
-            return True
-
-        def checkUdevScsiProperties(self, udev_data, sysfs_data):
-            """See `SubmissionParser`."""
-            return True
-
-        def checkUdevDmiData(self, dmi_data):
-            """See `SubmissionParser`."""
-            return True
-
-    validate_mock_class(UdevTestSubmissionParser)
-
-    def testCheckConsistentUdevDeviceData(self):
-        """Test of SubmissionParser.checkConsistentUdevDeviceData(),"""
-        parser = self.UdevTestSubmissionParser()
-        self.assertTrue(parser.checkConsistentUdevDeviceData(
-            None, None, None))
-
-    def testCheckConsistentUdevDeviceData_invalid_path_data(self):
-        """Test of SubmissionParser.checkConsistentUdevDeviceData(),
-
-        Detection of invalid path data lets the check fail.
-        """
-        class SubmissionParserUdevPathCheckFails(
-            self.UdevTestSubmissionParser):
-            """A SubmissionPaser where checkUdevDictsHavePathKey() fails."""
-
-            def checkUdevDictsHavePathKey(self, udev_nodes):
-                """See `SubmissionParser`."""
-                return False
-
-        validate_mock_class(SubmissionParserUdevPathCheckFails)
-
-        parser = SubmissionParserUdevPathCheckFails()
-        self.assertFalse(parser.checkConsistentUdevDeviceData(
-            None, None, None))
-
-    def testCheckConsistentUdevDeviceData_invalid_pci_data(self):
-        """Test of SubmissionParser.checkConsistentUdevDeviceData(),
-
-        Detection of invalid PCI data lets the check fail.
-        """
-        class SubmissionParserUdevPciCheckFails(
-            self.UdevTestSubmissionParser):
-            """A SubmissionPaser where checkUdevPciProperties() fails."""
-
-            def checkUdevPciProperties(self, udev_data):
-                """See `SubmissionParser`."""
-                return False
-
-        validate_mock_class(SubmissionParserUdevPciCheckFails)
-
-        parser = SubmissionParserUdevPciCheckFails()
-        self.assertFalse(parser.checkConsistentUdevDeviceData(
-            None, None, None))
-
-    def testCheckConsistentUdevDeviceData_invalid_usb_data(self):
-        """Test of SubmissionParser.checkConsistentUdevDeviceData(),
-
-        Detection of invalid USB data lets the check fail.
-        """
-        class SubmissionParserUdevUsbCheckFails(
-            self.UdevTestSubmissionParser):
-            """A SubmissionPaser where checkUdevUsbProperties() fails."""
-
-            def checkUdevUsbProperties(self, udev_data):
-                """See `SubmissionParser`."""
-                return False
-
-        validate_mock_class(SubmissionParserUdevUsbCheckFails)
-
-        parser = SubmissionParserUdevUsbCheckFails()
-        self.assertFalse(parser.checkConsistentUdevDeviceData(
-            None, None, None))
-
-    def testCheckConsistentUdevDeviceData_invalid_scsi_data(self):
-        """Test of SubmissionParser.checkConsistentUdevDeviceData(),
-
-        Detection of invalid SCSI data lets the check fail.
-        """
-        class SubmissionParserUdevUsbCheckFails(
-            self.UdevTestSubmissionParser):
-            """A SubmissionPaser where checkUdevScsiProperties() fails."""
-
-            def checkUdevScsiProperties(self, udev_data, sysfs_data):
-                """See `SubmissionParser`."""
-                return False
-
-        validate_mock_class(SubmissionParserUdevUsbCheckFails)
-
-        parser = SubmissionParserUdevUsbCheckFails()
-        self.assertFalse(parser.checkConsistentUdevDeviceData(
-            None, None, None))
-
-    def testCheckConsistentUdevDeviceData_invalid_dmi_data(self):
-        """Test of SubmissionParser.checkConsistentUdevDeviceData(),
-
-        Detection of invalid DMI data lets the check fail.
-        """
-        class SubmissionParserUdevUsbCheckFails(
-            self.UdevTestSubmissionParser):
-            """A SubmissionPaser where checkUdevDmiData() fails."""
-
-            def checkUdevDmiData(self, dmi_data):
-                """See `SubmissionParser`."""
-                return False
-
-        validate_mock_class(SubmissionParserUdevUsbCheckFails)
-
-        parser = SubmissionParserUdevUsbCheckFails()
-        self.assertFalse(parser.checkConsistentUdevDeviceData(
-            None, None, None))
-
-    class MockSubmissionParser(SubmissionParser):
-        """A SubmissionParser variant for testing checkCOnsistentData()
-
-        All "method substitutes" return a valid result.
-        """
-
-        def findDuplicateIDs(self, parsed_data):
-            return set()
-
-        def findInvalidIDReferences(self, parsed_data):
-            return set()
-
-        def getUDIDeviceMap(self, devices):
-            return {}
-
-        def getUDIChildren(self, udi_device_map):
-            return {}
-
-        def checkHALDevicesParentChildConsistency(self, udi_children):
-            return []
-
-        def checkConsistentUdevDeviceData(
-            self, udev_data, sysfs_data, dmi_data):
-            return True
-
-    validate_mock_class(MockSubmissionParser)
-
-    def assertErrorMessage(self, submission_key, log_message):
-        """Search for message in the log entries for submission_key.
-
-        assertErrorMessage requires that
-        (a) a log message starts with "Parsing submisson <submission_key>:"
-        (b) the error message passed as the parameter message appears
-            in a log string that matches (a)
-        (c) result, which is supposed to contain an object representing
-            the result of parsing a submission, is None.
-        (d) the log level is ERROR.
-
-        If all four criteria match, assertErrormessage does not raise any
-        exception.
-        """
-        expected_message = ('Parsing submission %s: %s'
-                            % (submission_key, log_message))
-        for r in self.handler.records:
-            if r.levelno != logging.ERROR:
-                continue
-            candidate = r.getMessage()
-            if candidate == expected_message:
-                return
-        raise AssertionError('No log message found: %s' % expected_message)
-
-    def assertWarningMessage(self, submission_key, log_message):
-        """Search for message in the log entries for submission_key.
-
-        assertErrorMessage requires that
-        (a) a log message starts with "Parsing submisson <submission_key>:"
-        (b) the error message passed as the parameter message appears
-            in a log string that matches (a)
-        (c) result, which is supposed to contain an object representing
-            the result of parsing a submission, is None.
-        (d) the log level is WARNING.
-
-        If all four criteria match, assertWarningMessage does not raise any
-        exception.
-        """
-        expected_message = ('Parsing submission %s: %s'
-                            % (submission_key, log_message))
-        for r in self.handler.records:
-            if r.levelno != logging.WARNING:
-                continue
-            candidate = r.getMessage()
-            if candidate == expected_message:
-                return
-        raise AssertionError('No log message found: %s' % expected_message)
-
-    def testConsistencyCheck(self):
-        """Test of SubmissionParser.checkConsistency."""
-        parser = self.MockSubmissionParser()
-        result = parser.checkConsistency({'hardware':
-                                              {'hal': {'devices': []}}})
-        self.assertEqual(result, True,
-                         'checkConsistency failed, but all partial checks '
-                         'succeeded')
-
-    def testConsistencyCheckValidUdevData(self):
-        """Test of SubmissionParser.checkConsistency."""
-        parser = self.MockSubmissionParser()
-        self.assertTrue(parser.checkConsistency(
-            {
-                'hardware': {
-                    'udev': None,
-                    'sysfs-attributes': None,
-                    'dmi': None,
-                    }
-                }
-            ))
-
-    def testConsistencyCheck_invalid_udev_data(self):
-        """Test of SubmissionParser.checkConsistency."""
-        class MockSubmissionParserBadUdevDeviceData(
-            self.MockSubmissionParser):
-            """A parser where checkConsistentUdevDeviceData() fails."""
-
-            def checkConsistentUdevDeviceData(self, udev_data, sysfs_data,
-                                              dmi_data):
-                return False
-
-        validate_mock_class(MockSubmissionParserBadUdevDeviceData)
-
-        parser = MockSubmissionParserBadUdevDeviceData()
-        self.assertFalse(parser.checkConsistency(
-            {
-                'hardware': {
-                    'udev': None,
-                    'sysfs-attributes': None,
-                    'dmi': None,
-                    }
-                }
-            ))
-
-    def testConsistencyCheckWithDuplicateIDs(self):
-        """SubmissionParser.checkConsistency detects duplicate IDs."""
-        class MockSubmissionParserDuplicateIds(
-            self.MockSubmissionParser):
-            """A parser where findDuplicateIDs() fails."""
-
-            def findDuplicateIDs(self, parsed_data):
-                return set([1])
-
-        validate_mock_class(MockSubmissionParserDuplicateIds)
-
-        parser = MockSubmissionParserDuplicateIds(self.log)
-        parser.submission_key = 'Consistency check detects duplicate IDs'
-        result = parser.checkConsistency({'hardware':
-                                              {'hal': {'devices': []}}})
-        self.assertEqual(result, False,
-                         'checkConsistency did not detect duplicate IDs')
-        self.assertErrorMessage('Consistency check detects duplicate IDs',
-                                'Duplicate IDs found: set([1])')
-
-    def testConsistencyCheckWithInvalidIDReferences(self):
-        """SubmissionParser.checkConsistency detects invalid ID references."""
-        class MockSubmissionParserInvalidIDReferences(
-            self.MockSubmissionParser):
-            """A parser where findInvalidIDReferences() fails."""
-            def findInvalidIDReferences(self, parsed_data):
-                return set([1])
-
-        validate_mock_class(MockSubmissionParserInvalidIDReferences)
-
-        parser = MockSubmissionParserInvalidIDReferences(self.log)
-        parser.submission_key = 'Consistency check detects invalid ID refs'
-        result = parser.checkConsistency({'hardware':
-                                              {'hal': {'devices': []}}})
-        self.assertEqual(result, False,
-                         'checkConsistency did not detect invalid ID refs')
-        self.assertErrorMessage('Consistency check detects invalid ID refs',
-                                'Invalid ID references found: set([1])')
-
-    def testConsistencyCheckWithDuplicateUDI(self):
-        """SubmissionParser.checkConsistency detects duplicate UDIs."""
-        class MockSubmissionParserUDIDeviceMapFails(
-            self.MockSubmissionParser):
-            """A parser where getUDIDeviceMap() fails."""
-
-            def getUDIDeviceMap(self, devices):
-                raise ValueError(
-                    'Duplicate UDI: /org/freedesktop/Hal/devices/computer')
-
-        validate_mock_class(MockSubmissionParserUDIDeviceMapFails)
-
-        parser = MockSubmissionParserUDIDeviceMapFails(self.log)
-        parser.submission_key = 'Consistency check detects invalid ID refs'
-        result = parser.checkConsistency({'hardware':
-                                              {'hal': {'devices': []}}})
-        self.assertEqual(result, False,
-                         'checkConsistency did not detect duplicate UDIs')
-        self.assertErrorMessage(
-            'Consistency check detects invalid ID refs',
-            'Duplicate UDI: /org/freedesktop/Hal/devices/computer')
-
-    def testConsistencyCheckChildUDIWithoutParent(self):
-        """SubmissionParser.checkConsistency detects "orphaned" devices."""
-        class MockSubmissionParserUDIChildrenFails(
-            self.MockSubmissionParser):
-            """A parser where getUDIChildren() fails."""
-
-            def getUDIChildren(self, udi_device_map):
-                raise ValueError('Unknown parent UDI /foo in <device id="3">')
-
-        validate_mock_class(MockSubmissionParserUDIChildrenFails)
-
-        parser = MockSubmissionParserUDIChildrenFails(self.log)
-        parser.submission_key = 'Consistency check detects invalid ID refs'
-        result = parser.checkConsistency({'hardware':
-                                              {'hal': {'devices': []}}})
-        self.assertEqual(result, False,
-                         'checkConsistency did not detect orphaned devices')
-        self.assertErrorMessage(
-            'Consistency check detects invalid ID refs',
-            'Unknown parent UDI /foo in <device id="3">')
-
-    def testConsistencyCheckCircularParentChildRelation(self):
-        """SubmissionParser.checkConsistency detects "orphaned" devices."""
-        class MockSubmissionParserHALDevicesParentChildConsistency(
-            self.MockSubmissionParser):
-            """A parser where checkHALDevicesParentChildConsistency() fails.
-            """
-
-            def checkHALDevicesParentChildConsistency(self, udi_children):
-                return ['/foo', '/bar']
-
-        validate_mock_class(
-            MockSubmissionParserHALDevicesParentChildConsistency)
-
-        parser = MockSubmissionParserHALDevicesParentChildConsistency(
-            self.log)
-        parser.submission_key = ('Consistency check detects circular '
-                                 'parent-child relationships')
-        result = parser.checkConsistency({'hardware':
-                                              {'hal': {'devices': []}}})
-        self.assertEqual(result, False,
-                         'checkConsistency did not detect circular parent/'
-                             'child relationship')
-        self.assertErrorMessage(
-            'Consistency check detects circular parent-child relationships',
-            "Found HAL devices with circular parent/child "
-                "relationship: [u'/foo', u'/bar']")
diff --git a/lib/lp/hardwaredb/scripts/tests/test_hwdb_submission_processing.py b/lib/lp/hardwaredb/scripts/tests/test_hwdb_submission_processing.py
deleted file mode 100644
index e0961d5..0000000
--- a/lib/lp/hardwaredb/scripts/tests/test_hwdb_submission_processing.py
+++ /dev/null
@@ -1,5373 +0,0 @@
-# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Tests of the HWDB submissions parser."""
-
-from __future__ import absolute_import, print_function, unicode_literals
-
-import bz2
-from copy import deepcopy
-from datetime import datetime
-import io
-import logging
-import os
-
-import pytz
-from zope.component import getUtility
-from zope.testing.loghandler import Handler
-
-from lp.hardwaredb.interfaces.hwdb import (
-    HWBus,
-    HWSubmissionFormat,
-    IHWDeviceDriverLinkSet,
-    IHWDeviceSet,
-    IHWDriverSet,
-    IHWSubmissionDeviceSet,
-    IHWSubmissionSet,
-    IHWVendorIDSet,
-    IHWVendorNameSet,
-    )
-from lp.hardwaredb.scripts.hwdbsubmissions import (
-    HALDevice,
-    PCI_CLASS_BRIDGE,
-    PCI_CLASS_SERIALBUS_CONTROLLER,
-    PCI_CLASS_STORAGE,
-    PCI_SUBCLASS_BRIDGE_CARDBUS,
-    PCI_SUBCLASS_BRIDGE_PCI,
-    PCI_SUBCLASS_SERIALBUS_USB,
-    PCI_SUBCLASS_STORAGE_SATA,
-    SubmissionParser,
-    UdevDevice,
-    )
-from lp.services.config import config
-from lp.testing import (
-    TestCase,
-    validate_mock_class,
-    )
-from lp.testing.dbuser import switch_dbuser
-from lp.testing.layers import (
-    BaseLayer,
-    LaunchpadZopelessLayer,
-    )
-
-
-def evaluate_property(value):
-    """Evaluate a property.
-
-    This function does nothing in itself; passing it a property evaluates the
-    property.  But it lets the code express that the evaluation is all that's
-    needed, without assigning to an unused variable etc.
-    """
-    return value
-
-
-class TestCaseHWDB(TestCase):
-    """Common base class for HWDB processing tests."""
-
-    layer = BaseLayer
-
-    PCI_SUBCLASS_STORAGE_SCSI = 0
-
-    UDI_COMPUTER = '/org/freedesktop/Hal/devices/computer'
-    UDI_SATA_CONTROLLER = '/org/freedesktop/Hal/devices/pci_8086_27c5'
-    UDI_SATA_CONTROLLER_SCSI = ('/org/freedesktop/Hal/devices/'
-                               'pci_8086_27c5_scsi_host')
-    UDI_SATA_DISK = ('org/freedesktop/Hal/devices/'
-                     'pci_8086_27c5_scsi_host_scsi_device_lun0')
-    UDI_USB_CONTROLLER_PCI_SIDE = '/org/freedesktop/Hal/devices/pci_8086_27cc'
-    UDI_USB_CONTROLLER_USB_SIDE = ('/org/freedesktop/Hal/devices/'
-                                   'usb_device_0_0_0000_00_1d_7')
-    UDI_USB_CONTROLLER_USB_SIDE_RAW = ('/org/freedesktop/Hal/devices/'
-                                   'usb_device_0_0_0000_00_1d_7_usbraw')
-    UDI_USB_STORAGE = '/org/freedesktop/Hal/devices/usb_device_1307_163_07'
-    UDI_USB_STORAGE_IF0 = ('/org/freedesktop/Hal/devices/'
-                           'usb_device_1307_163_07_if0')
-    UDI_USB_STORAGE_SCSI_HOST = ('/org/freedesktop/Hal/devices/'
-                                 'usb_device_1307_163_07_if0scsi_host')
-    UDI_USB_STORAGE_SCSI_DEVICE = ('/org/freedesktop/Hal/devices/'
-                                   'usb_device_1307_163_07_if0'
-                                   'scsi_host_scsi_device_lun0')
-    UDI_USB_HUB = '/org/freedesktop/Hal/devices/usb_device_409_5a_noserial'
-    UDI_USB_HUB_IF0 = ('/org/freedesktop/Hal/devices/'
-                       'usb_dev_409_5a_noserial_if0')
-    UDI_PCI_PCI_BRIDGE = '/org/freedesktop/Hal/devices/pci_8086_2448'
-    UDI_PCI_PCCARD_BRIDGE = '/org/freedesktop/Hal/devices/pci_1217_7134'
-    UDI_PCCARD_DEVICE = '/org/freedesktop/Hal/devices/pci_9004_6075'
-
-    UDI_SCSI_CONTROLLER_PCI_SIDE = (
-        '/org/freedesktop/Hal/devices/pci_9004_6075')
-    UDI_SCSI_CONTROLLER_SCSI_SIDE = (
-        '/org/freedesktop/Hal/devices/pci_9004_6075_scsi_host')
-    UDI_SCSI_DISK = '/org/freedesktop/Hal/devices/scsi_disk'
-
-    PCI_VENDOR_ID_INTEL = 0x8086
-    PCI_VENDOR_ID_ADAPTEC = 0x9004
-    PCI_PROD_ID_PCI_PCCARD_BRIDGE = 0x7134
-    PCI_PROD_ID_PCCARD_DEVICE = 0x6075
-    PCI_PROD_ID_USB_CONTROLLER = 0x27cc
-    PCI_PROD_ID_AIC1480 = 0x6075
-
-    USB_VENDOR_ID_NEC = 0x0409
-    USB_PROD_ID_NEC_HUB = 0x005a
-
-    USB_VENDOR_ID_USBEST = 0x1307
-    USB_PROD_ID_USBBEST_MEMSTICK = 0x0163
-
-    KERNEL_VERSION = '2.6.24-19-generic'
-    KERNEL_PACKAGE = 'linux-image-' + KERNEL_VERSION
-
-    def setUp(self):
-        """Setup the test environment."""
-        super(TestCaseHWDB, self).setUp()
-        self.log = logging.getLogger('test_hwdb_submission_parser')
-        self.log.setLevel(logging.INFO)
-        self.handler = Handler(self)
-        self.handler.add(self.log.name)
-
-    def assertWarningMessage(self, submission_key, log_message):
-        """Search for message in the log entries for submission_key.
-
-        :raise: AssertionError if no log message exists that starts with
-            "Parsing submission <submission_key>:" and that contains
-            the text passed as the parameter message.
-        """
-        expected_message = 'Parsing submission %s: %s' % (
-            submission_key, log_message)
-
-        for record in self.handler.records:
-            if record.levelno != logging.WARNING:
-                continue
-            candidate = record.getMessage()
-            if candidate == expected_message:
-                return
-        raise AssertionError('No log message found: %s' % expected_message)
-
-    def assertErrorMessage(self, submission_key, log_message):
-        """Search for log_message in the log entries for submission_key.
-
-        :raise: AssertionError if no log message exists that starts with
-            "Parsing submission <submission_key>:" and that contains
-            the text passed as the parameter message.
-        """
-        expected_message = 'Parsing submission %s: %s' % (
-            submission_key, log_message)
-
-        for record in self.handler.records:
-            if record.levelno != logging.ERROR:
-                continue
-            candidate = record.getMessage()
-            if candidate == expected_message:
-                return
-        raise AssertionError('No log message found: %s' % expected_message)
-
-
-class TestHWDBSubmissionProcessing(TestCaseHWDB):
-    """Tests for processing of HWDB submissions."""
-
-    def test_buildDeviceList(self):
-        """Test of SubmissionParser.buildDeviceList()."""
-        class MockSubmissionParser(SubmissionParser):
-            """A SubmissionParser variant for testing."""
-
-            def __init__(self, hal_result, udev_result):
-                super(MockSubmissionParser, self).__init__()
-                self.hal_result = hal_result
-                self.udev_result = udev_result
-
-            def buildHalDeviceList(self, parsed_data):
-                """See `SubmissionParser`."""
-                return self.hal_result
-
-            def buildUdevDeviceList(self, parsed_data):
-                """See `SubmissionParser`."""
-                return self.udev_result
-
-        parsed_data_hal = {
-            'hardware': {'hal': None}
-            }
-
-        parser = MockSubmissionParser(True, True)
-        self.assertTrue(parser.buildDeviceList(parsed_data_hal))
-
-        parser = MockSubmissionParser(False, True)
-        self.assertFalse(parser.buildDeviceList(parsed_data_hal))
-
-        parsed_data_udev = {
-            'hardware': {'udev': None}
-            }
-        parser = MockSubmissionParser(True, True)
-        self.assertTrue(parser.buildDeviceList(parsed_data_udev))
-
-        parser = MockSubmissionParser(True, False)
-        self.assertFalse(parser.buildDeviceList(parsed_data_udev))
-
-    def test_buildHalDeviceList(self):
-        """Test the creation of list HALDevice instances for a submission."""
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {},
-                },
-            {
-                'id': 2,
-                'udi': self.UDI_SATA_CONTROLLER,
-                'properties': {
-                    'info.parent': (self.UDI_COMPUTER, 'str')
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        self.assertEqual(len(parser.devices), len(devices),
-                         'Numbers of devices in parser.devices and in '
-                         'sample data are different')
-        root_device = parser.devices[self.UDI_COMPUTER]
-        self.assertEqual(root_device.id, 1,
-                         'Unexpected value of root device ID.')
-        self.assertEqual(root_device.udi, self.UDI_COMPUTER,
-                         'Unexpected value of root device UDI.')
-        self.assertEqual(root_device.properties,
-                         devices[0]['properties'],
-                         'Unexpected properties of root device.')
-        child_device = parser.devices[self.UDI_SATA_CONTROLLER]
-        self.assertEqual(child_device.id, 2,
-                         'Unexpected value of child device ID.')
-        self.assertEqual(child_device.udi, self.UDI_SATA_CONTROLLER,
-                         'Unexpected value of child device UDI.')
-        self.assertEqual(child_device.properties,
-                         devices[1]['properties'],
-                         'Unexpected properties of child device.')
-
-        parent = parser.devices[self.UDI_COMPUTER]
-        child = parser.devices[self.UDI_SATA_CONTROLLER]
-        self.assertEqual(parent.children, [child],
-                         'Child missing in parent.children.')
-        self.assertEqual(child.parent, parent,
-                         'Invalid value of child.parent.')
-
-    def makeUdevDeviceParsedData(self, paths, sysfs_data=None):
-        """Build test data that can be passed to buildUdevDevice()."""
-        def makeUdevDevice(path):
-            """Make a trivial UdevInstance with the given device path."""
-            return {
-                'P': path,
-                'E': {}
-                }
-        udev_device_data = [makeUdevDevice(path) for path in paths]
-        if sysfs_data is None:
-            sysfs_data = {}
-        parsed_data = {
-            'hardware': {
-                'udev': udev_device_data,
-                'sysfs-attributes': sysfs_data,
-                'dmi': {'/sys/class/dmi/id/sys_vendor': 'FUJITSU SIEMENS'},
-                }
-            }
-        return parsed_data
-
-    def test_buildUdevDeviceList(self):
-        """Test the creation of UdevDevice instances for a submission."""
-        root_device_path = '/devices/LNXSYSTM:00'
-        pci_pci_bridge_path = '/devices/pci0000:00/0000:00:1c.0'
-        pci_ethernet_controller_path = (
-            '/devices/pci0000:00/0000:00:1c.0/0000:02:00.0')
-        pci_usb_controller_path = '/devices/pci0000:00/0000:00:1d.7'
-        pci_usb_controller_usb_hub_path = (
-            '/devices/pci0000:00/0000:00:1d.7/usb1')
-        usb_storage_device_path = '/devices/pci0000:00/0000:00:1d.7/usb1/1-1'
-        udev_paths = (
-            root_device_path, pci_pci_bridge_path,
-            pci_ethernet_controller_path, pci_usb_controller_path,
-            pci_usb_controller_usb_hub_path, usb_storage_device_path,
-            )
-
-        parsed_data = self.makeUdevDeviceParsedData(udev_paths)
-        parser = SubmissionParser()
-        self.assertTrue(parser.buildUdevDeviceList(parsed_data))
-
-        self.assertEqual(len(udev_paths), len(parser.devices))
-        for path in udev_paths:
-            self.assertEqual(path, parser.devices[path].device_id)
-
-        devices = parser.devices
-
-        root_device = parser.devices[root_device_path]
-        expected_children = set(
-            (devices[pci_pci_bridge_path], devices[pci_usb_controller_path]))
-        self.assertEqual(expected_children, set(root_device.children))
-
-        pci_pci_bridge = devices[pci_pci_bridge_path]
-        self.assertEqual(
-            [devices[pci_ethernet_controller_path]], pci_pci_bridge.children)
-
-        usb_controller = devices[pci_usb_controller_path]
-        usb_hub = devices[pci_usb_controller_usb_hub_path]
-        self.assertEqual([usb_hub], usb_controller.children)
-
-        usb_storage = devices[usb_storage_device_path]
-        self.assertEqual([usb_storage], usb_hub.children)
-
-    def test_buildUdevDeviceList_root_node_has_dmi_data(self):
-        """The root node of a udev submissions has DMI data."""
-        root_device_path = '/devices/LNXSYSTM:00'
-        pci_pci_bridge_path = '/devices/pci0000:00/0000:00:1c.0'
-        udev_paths = (root_device_path, pci_pci_bridge_path)
-
-        parsed_data = self.makeUdevDeviceParsedData(udev_paths)
-        parser = SubmissionParser()
-        parser.buildUdevDeviceList(parsed_data)
-
-        self.assertEqual(
-            {'/sys/class/dmi/id/sys_vendor': 'FUJITSU SIEMENS'},
-            parser.devices[root_device_path].dmi)
-
-        self.assertIs(None, parser.devices[pci_pci_bridge_path].dmi)
-
-    def test_buildUdevDeviceList_sysfs_data(self):
-        """Optional sysfs data is passed to UdevDevice instances."""
-        root_device_path = '/devices/LNXSYSTM:00'
-        pci_pci_bridge_path = '/devices/pci0000:00/0000:00:1c.0'
-        pci_ethernet_controller_path = (
-            '/devices/pci0000:00/0000:00:1c.0/0000:02:00.0')
-
-        udev_paths = (
-            root_device_path, pci_pci_bridge_path,
-            pci_ethernet_controller_path)
-
-        sysfs_data = {
-            pci_pci_bridge_path: 'sysfs-data',
-            }
-
-        parsed_data = self.makeUdevDeviceParsedData(udev_paths, sysfs_data)
-        parser = SubmissionParser()
-        parser.buildUdevDeviceList(parsed_data)
-
-        self.assertEqual(
-            'sysfs-data', parser.devices[pci_pci_bridge_path].sysfs)
-
-        self.assertIs(
-            None, parser.devices[pci_ethernet_controller_path].sysfs)
-
-    def test_buildUdevDeviceList_no_sysfs_data(self):
-        """Sysfs data is not required (maverick and natty submissions)."""
-        root_device_path = '/devices/LNXSYSTM:00'
-        pci_pci_bridge_path = '/devices/pci0000:00/0000:00:1c.0'
-        pci_ethernet_controller_path = (
-            '/devices/pci0000:00/0000:00:1c.0/0000:02:00.0')
-
-        udev_paths = (
-            root_device_path, pci_pci_bridge_path,
-            pci_ethernet_controller_path)
-
-        sysfs_data = None
-
-        parsed_data = self.makeUdevDeviceParsedData(udev_paths, sysfs_data)
-        parser = SubmissionParser()
-        parser.buildUdevDeviceList(parsed_data)
-
-        self.assertIs(
-            None, parser.devices[pci_pci_bridge_path].sysfs)
-
-        self.assertIs(
-            None, parser.devices[pci_ethernet_controller_path].sysfs)
-
-    def test_buildUdevDeviceList_invalid_device_path(self):
-        """Test the creation of UdevDevice instances for a submission.
-
-        All device paths must start with '/devices'. Any other
-        device path makes the submission invalid.
-        """
-        root_device_path = '/devices/LNXSYSTM:00'
-        bad_device_path = '/nonsense'
-        udev_paths = (root_device_path, bad_device_path)
-
-        parsed_data = self.makeUdevDeviceParsedData(udev_paths)
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'udev device with invalid path'
-        self.assertFalse(parser.buildUdevDeviceList(parsed_data))
-        self.assertErrorMessage(
-            parser.submission_key, "Invalid device path name: u'/nonsense'")
-
-    def test_buildUdevDeviceList_missing_root_device(self):
-        """Test the creation of UdevDevice instances for a submission.
-
-        Each submission must contain a udev node for the root device.
-        """
-        pci_pci_bridge_path = '/devices/pci0000:00/0000:00:1c.0'
-        udev_paths = (pci_pci_bridge_path, )
-
-        parsed_data = self.makeUdevDeviceParsedData(udev_paths)
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'no udev root device'
-        self.assertFalse(parser.buildUdevDeviceList(parsed_data))
-        self.assertErrorMessage(
-            parser.submission_key, "No udev root device defined")
-
-    def test_kernel_package_name_hal_data(self):
-        """Test of SubmissionParser.kernel_package_name.
-
-        Regular case.
-        """
-        parser = SubmissionParser(self.log)
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {
-                    'system.kernel.version': (self.KERNEL_VERSION, 'str'),
-                    },
-                },
-            ]
-        parser.parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            'software': {
-                'packages': {
-                    self.KERNEL_PACKAGE: {},
-                    },
-                },
-            }
-        parser.buildDeviceList(parser.parsed_data)
-        kernel_package = parser.kernel_package_name
-        self.assertEqual(
-            self.KERNEL_PACKAGE, kernel_package,
-            'Unexpected value of SubmissionParser.kernel_package_name. '
-            'Expected linux-image-2.6.24-19-generic, got %r' % kernel_package)
-
-        self.assertEqual(
-            0, len(self.handler.records),
-            'One or more warning messages were logged by '
-            'SubmissionParser.kernel_package_name, where zero was expected.')
-
-    def test_kernel_package_hal_data_name_inconsistent(self):
-        """Test of SubmissionParser.kernel_package_name.
-
-        Test a name inconsistency.
-        """
-        parser = SubmissionParser(self.log)
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {
-                    'system.kernel.version': (self.KERNEL_VERSION, 'str'),
-                    },
-                },
-            ]
-        parser.parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            'software': {
-                'packages': {
-                    'linux-image-from-obscure-external-source': {},
-                    },
-                },
-            }
-        parser.submission_key = 'Test of inconsistent kernel package name'
-        parser.buildDeviceList(parser.parsed_data)
-        kernel_package = parser.kernel_package_name
-        self.assertIs(None, kernel_package)
-        self.assertWarningMessage(
-            parser.submission_key,
-            'Inconsistent kernel version data: According to HAL the '
-            'kernel is 2.6.24-19-generic, but the submission does not '
-            'know about a kernel package linux-image-2.6.24-19-generic')
-        # The warning appears only once per submission, even if the
-        # property kernel_package_name is accessed more than once.
-        num_warnings = len(self.handler.records)
-        evaluate_property(parser.kernel_package_name)
-        self.assertEqual(
-            num_warnings, len(self.handler.records),
-            'Warning for missing HAL property system.kernel.version '
-            'repeated.')
-
-    def test_kernel_package_name_hal_data_no_kernel_version_in_hal_data(self):
-        """Test of SubmissionParser.kernel_package_name.
-
-        Test without HAL property system.kernel.version.
-        """
-        parser = SubmissionParser(self.log)
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {},
-                },
-            ]
-        parser.parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            'software': {
-                'packages': {
-                    'linux-image-from-obscure-external-source': {},
-                    },
-                },
-            }
-        parser.submission_key = 'Test: missing property system.kernel.version'
-        parser.buildDeviceList(parser.parsed_data)
-        self.assertIs(None, parser.kernel_package_name)
-        self.assertWarningMessage(
-            parser.submission_key,
-            'Submission does not provide property system.kernel.version '
-            'for /org/freedesktop/Hal/devices/computer or a summary '
-            'sub-node <kernel-release>.')
-        # The warning appears only once per submission, even if the
-        # property kernel_package_name is accessed more than once.
-        num_warnings = len(self.handler.records)
-        evaluate_property(parser.kernel_package_name)
-        self.assertEqual(
-            num_warnings, len(self.handler.records),
-            'Warning for missing HAL property system.kernel.version '
-            'repeated.')
-
-    def test_kernel_package_name_hal_data_no_package_data(self):
-        """Test of SubmissionParser.kernel_package_name.
-
-        Test without any package data. In this case,
-        SubmissionParser.kernel_package_name is the value of the property
-        system.kernel.version if the root HAL device. No further checks
-        are done.
-        """
-        parser = SubmissionParser(self.log)
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {
-                    'system.kernel.version': (self.KERNEL_VERSION, 'str'),
-                    },
-                },
-            ]
-        parser.parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            'software': {
-                'packages': {},
-                },
-            }
-        parser.submission_key = 'Test: missing property system.kernel.version'
-        parser.buildDeviceList(parser.parsed_data)
-        kernel_package = parser.kernel_package_name
-        self.assertEqual(
-            self.KERNEL_PACKAGE, kernel_package,
-            'Unexpected result of SubmissionParser.getKernelPackageName, '
-            'test without any package data. Expected None, got %r'
-            % kernel_package)
-
-    def test_kernel_package_name_udev_data(self):
-        """Test of SubmissionParser.kernel_package_name for udev data.
-
-        Variant for udev data, regular case.
-        """
-        parser = SubmissionParser(self.log)
-        parser.parsed_data = {
-            'hardware': {
-                'udev': [
-                    {'P': '/devices/LNXSYSTM:00'}
-                    ],
-                'sysfs-attributes': {},
-                'dmi': {},
-                },
-            'software': {
-                'packages': {
-                    self.KERNEL_PACKAGE: {},
-                    },
-                },
-            'summary': {
-                'kernel-release': self.KERNEL_VERSION,
-                },
-            }
-        parser.buildDeviceList(parser.parsed_data)
-        kernel_package = parser.kernel_package_name
-        self.assertEqual(
-            self.KERNEL_PACKAGE, kernel_package,
-            'Unexpected value of SubmissionParser.kernel_package_name. '
-            'Expected linux-image-2.6.24-19-generic, got %r' % kernel_package)
-
-        self.assertEqual(
-            0, len(self.handler.records),
-            'One or more warning messages were logged by '
-            'SubmissionParser.kernel_package_name, where zero was expected.')
-
-    def test_kernel_package_udev_data_name_inconsistent(self):
-        """Test of SubmissionParser.kernel_package_name.
-
-        Variant for udev data, name inconsistency.
-        """
-        parser = SubmissionParser(self.log)
-        parser.parsed_data = {
-            'hardware': {
-                'udev': [
-                    {'P': '/devices/LNXSYSTM:00'}
-                    ],
-                'sysfs-attributes': {},
-                'dmi': {},
-                },
-            'software': {
-                'packages': {
-                    'linux-image-from-obscure-external-source': {},
-                    },
-                },
-            'summary': {
-                'kernel-release': self.KERNEL_VERSION,
-                },
-            }
-        parser.submission_key = 'Test of inconsistent kernel package name'
-        parser.buildDeviceList(parser.parsed_data)
-        kernel_package = parser.kernel_package_name
-        self.assertIs(None, kernel_package)
-        self.assertWarningMessage(
-            parser.submission_key,
-            'Inconsistent kernel version data: According to HAL the '
-            'kernel is 2.6.24-19-generic, but the submission does not '
-            'know about a kernel package linux-image-2.6.24-19-generic')
-        # The warning appears only once per submission, even if the
-        # property kernel_package_name is accessed more than once.
-        num_warnings = len(self.handler.records)
-        evaluate_property(parser.kernel_package_name)
-        self.assertEqual(
-            num_warnings, len(self.handler.records),
-            'Warning for missing HAL property system.kernel.version '
-            'repeated.')
-
-    def test_kernel_package_name_udev_data_no_kernel_version_in_summary(self):
-        """Test of SubmissionParser.kernel_package_name.
-
-        Test without the summary sub-node <kernel-release>.
-        """
-        parser = SubmissionParser(self.log)
-        parser.parsed_data = {
-            'hardware': {
-                'udev': [
-                    {'P': '/devices/LNXSYSTM:00'}
-                    ],
-                'sysfs-attributes': {},
-                'dmi': {},
-                },
-            'software': {
-                'packages': {
-                    self.KERNEL_PACKAGE: {},
-                    },
-                },
-            'summary': {},
-            }
-        parser.submission_key = 'Test: missing property system.kernel.version'
-        parser.buildDeviceList(parser.parsed_data)
-        self.assertIs(None, parser.kernel_package_name)
-        self.assertWarningMessage(
-            parser.submission_key,
-            'Submission does not provide property system.kernel.version '
-            'for /org/freedesktop/Hal/devices/computer or a summary '
-            'sub-node <kernel-release>.')
-        # The warning appears only once per submission, even if the
-        # property kernel_package_name is accessed more than once.
-        num_warnings = len(self.handler.records)
-        evaluate_property(parser.kernel_package_name)
-        self.assertEqual(
-            num_warnings, len(self.handler.records),
-            'Warning for missing HAL property system.kernel.version '
-            'repeated.')
-
-    def test_kernel_package_name_udev_data_no_package_data(self):
-        """Test of SubmissionParser.kernel_package_name.
-
-        Variant for udev data, test without any package data. In this case,
-        SubmissionParser.kernel_package_name is the value of the property
-        system.kernel.version if the root HAL device. No further checks
-        are done.
-        """
-        parser = SubmissionParser(self.log)
-        parser.parsed_data = {
-            'hardware': {
-                'udev': [
-                    {'P': '/devices/LNXSYSTM:00'},
-                    ],
-                'sysfs-attributes': {},
-                'dmi': {},
-                },
-            'software': {
-                'packages': {},
-                },
-            'summary': {
-                'kernel-release': self.KERNEL_VERSION,
-                },
-            }
-        parser.submission_key = 'Test: missing property system.kernel.version'
-        parser.buildDeviceList(parser.parsed_data)
-        kernel_package = parser.kernel_package_name
-        self.assertEqual(
-            self.KERNEL_PACKAGE, kernel_package,
-            'Unexpected result of SubmissionParser.getKernelPackageName, '
-            'test without any package data. Expected None, got %r'
-            % kernel_package)
-
-    def testHALDeviceConstructor(self):
-        """Test of the HALDevice constructor."""
-        properties = {
-            'info.bus': ('scsi', 'str'),
-            }
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-
-        self.assertEqual(device.id, 1, 'Unexpected device ID')
-        self.assertEqual(device.udi, '/some/udi/path',
-                         'Unexpected device UDI.')
-        self.assertEqual(device.properties, properties,
-                         'Unexpected device properties.')
-        self.assertEqual(device.parser, parser,
-                         'Unexpected device parser.')
-
-    def testHALDeviceGetProperty(self):
-        """Test of HALDevice.getProperty."""
-        properties = {
-            'info.bus': ('scsi', 'str'),
-            }
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-
-        # HALDevice.getProperty returns the value of a HAL property.
-        # Note that the property type is _not_ returned
-        self.assertEqual(device.getProperty('info.bus'), 'scsi',
-            'Unexpected result of calling HALDevice.getProperty.')
-        # If a property of the given name does not exist, None is returned.
-        self.assertEqual(device.getProperty('does-not-exist'), None,
-            'Unexpected result of calling HALDevice.getProperty for a '
-            'non-existing property.')
-
-    def testHALDeviceParentUDI(self):
-        """Test of HALDevice.parent_udi."""
-        properties = {
-            'info.bus': ('scsi', 'str'),
-            'info.parent': ('/another/udi', 'str'),
-            }
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(device.parent_udi, '/another/udi',
-                         'Unexpected value of HALDevice.parent_udi.')
-
-        properties = {
-            'info.bus': ('scsi', 'str'),
-            }
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(device.parent_udi, None,
-                         'Unexpected value of HALDevice.parent_udi, '
-                         'when no parent information available.')
-
-    def testHALDeviceDeviceId(self):
-        """Test of HALDevice.device_id."""
-        properties = {}
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(
-            '/some/udi/path', device.device_id,
-            'Unexpected value of HALDevice.device_id')
-
-    def testHALDevicePciClass(self):
-        """Test of HALDevice.pci_class."""
-        properties = {
-            'pci.device_class': (1, 'int'),
-            }
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(
-             1, device.pci_class,
-            'Unexpected value of HALDevice.pci_class.')
-
-        properties = {}
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(
-            None, device.pci_class,
-            'Unexpected value of HALDevice.pci_class for Non-PCI device.')
-
-    def testHALDevicePciSubClass(self):
-        """Test of HALDevice.pci_subclass."""
-        properties = {
-            'pci.device_subclass': (1, 'int'),
-            }
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(
-             1, device.pci_subclass,
-            'Unexpected value of HALDevice.pci_subclass.')
-
-        properties = {}
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(
-             None, device.pci_subclass,
-            'Unexpected value of HALDevice.pci_sub_class for Non-PCI device.')
-
-    def testHALDeviceUsbVendorId(self):
-        """Test of HALDevice.usb_vendor_id."""
-        properties = {
-            'usb_device.vendor_id': (1, 'int'),
-            }
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(
-             1, device.usb_vendor_id,
-            'Unexpected value of HALDevice.usb_vendor_id.')
-
-        properties = {}
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(
-             None, device.usb_vendor_id,
-            'Unexpected value of HALDevice.usb_vendor_id for Non-USB device.')
-
-    def testHALDeviceUsbProductId(self):
-        """Test of HALDevice.usb_product_id."""
-        properties = {
-            'usb_device.product_id': (1, 'int'),
-            }
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(
-             1, device.usb_product_id,
-            'Unexpected value of HALDevice.usb_product_id.')
-
-        properties = {}
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(
-             None, device.usb_product_id,
-            'Unexpected value of HALDevice.usb_product_id for Non-USB '
-            'device.')
-
-    def testHALDeviceScsiVendor(self):
-        """Test of HALDevice.scsi_vendor."""
-        properties = {
-            'scsi.vendor': ('SEAGATE', 'string'),
-            }
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(
-             'SEAGATE', device.scsi_vendor,
-            'Unexpected value of HALDevice.scsi_vendor.')
-
-        properties = {}
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(
-             None, device.scsi_vendor,
-            'Unexpected value of HALDevice.scsi_vendor for Non-SCSI device.')
-
-    def testHALDeviceScsiModel(self):
-        """Test of HALDevice.scsi_model."""
-        properties = {
-            'scsi.model': ('ST1234567', 'string'),
-            }
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(
-             'ST1234567', device.scsi_model,
-            'Unexpected value of HALDevice.scsi_model.')
-
-        properties = {}
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(
-             None, device.scsi_model,
-            'Unexpected value of HALDevice.scsi_model for Non-SCSI device.')
-
-    def testHALDeviceDriverName(self):
-        """Test of HALDevice.driver_name."""
-        properties = {
-            'info.linux.driver': ('ahci', 'string'),
-            }
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(
-             'ahci', device.driver_name,
-            'Unexpected value of HALDevice.driver_name.')
-
-        properties = {}
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(
-             None, device.driver_name,
-            'Unexpected value of HALDevice.driver_name for Non-SCSI device.')
-
-    def testHalDeviceRawBus(self):
-        """test of HALDevice.raw_bus."""
-        properties = {
-            'info.bus': ('scsi', 'str'),
-            'info.parent': ('/another/udi', 'str'),
-            }
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(device.raw_bus, 'scsi',
-                         'Unexpected value of HALDevice.raw_bus for '
-                         'HAL property info.bus.')
-
-        properties = {
-            'info.subsystem': ('scsi', 'str'),
-            'info.parent': ('/another/udi', 'str'),
-            }
-        parser = SubmissionParser(self.log)
-        device = HALDevice(1, '/some/udi/path', properties, parser)
-        self.assertEqual(device.raw_bus, 'scsi',
-                         'Unexpected value of HALDevice.raw_bus for '
-                         'HAL property info.bus.')
-
-    def test_HALDevice_scsi_controller_usb_storage_device(self):
-        """test of HALDevice.scsi_controller.
-
-        The physical device is a USB storage device.
-        """
-        devices = [
-            # The main node of the USB storage device.
-            {
-                'id': 1,
-                'udi': self.UDI_USB_STORAGE,
-                'properties': {
-                    'info.bus': ('usb_device', 'str'),
-                    },
-                },
-            # The storage interface of the USB device.
-            {
-                'id': 2,
-                'udi': self.UDI_USB_STORAGE_IF0,
-                'properties': {
-                    'info.bus': ('usb', 'str'),
-                    'info.parent': (self.UDI_USB_STORAGE, 'str'),
-                    },
-                },
-            # The fake SCSI host of the storage device. Note that HAL does
-            # _not_ provide the info.bus property.
-            {
-                'id': 3,
-                'udi': self.UDI_USB_STORAGE_SCSI_HOST,
-                'properties': {
-                    'info.parent': (self.UDI_USB_STORAGE_IF0, 'str'),
-                    },
-                },
-            # The fake SCSI disk.
-            {
-                'id': 3,
-                'udi': self.UDI_USB_STORAGE_SCSI_DEVICE,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'info.parent': (self.UDI_USB_STORAGE_SCSI_HOST, 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        parser = SubmissionParser()
-        parser.buildHalDeviceList(parsed_data)
-
-        usb_fake_scsi_disk = parser.devices[self.UDI_USB_STORAGE_SCSI_DEVICE]
-        usb_main_device = parser.devices[self.UDI_USB_STORAGE_IF0]
-        self.assertEqual(usb_main_device, usb_fake_scsi_disk.scsi_controller)
-
-    def test_HALDevice_scsi_controller_pci_controller(self):
-        """test of HALDevice.scsi_controller.
-
-        Variant for a SCSI device connected to a PCI controller.
-        """
-        devices = [
-            # The PCI host controller.
-            {
-                'id': 1,
-                'udi': self.UDI_SATA_CONTROLLER,
-                'properties': {
-                    'info.bus': ('pci', 'str'),
-                    'pci.device_class': (PCI_CLASS_STORAGE, 'int'),
-                    'pci.device_subclass': (PCI_SUBCLASS_STORAGE_SATA,
-                                            'int'),
-                    },
-                },
-            # The (fake or real) SCSI host of the storage device.
-            {
-                'id': 2,
-                'udi': self.UDI_SATA_CONTROLLER_SCSI,
-                'properties': {
-                    'info.parent': (self.UDI_SATA_CONTROLLER, 'str'),
-                    },
-                },
-            # The (possibly fake) SCSI disk.
-            {
-                'id': 3,
-                'udi': self.UDI_SATA_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'info.parent': (self.UDI_SATA_CONTROLLER_SCSI, 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        parser = SubmissionParser()
-        parser.buildHalDeviceList(parsed_data)
-
-        scsi_device = parser.devices[self.UDI_SATA_DISK]
-        controller = parser.devices[self.UDI_SATA_CONTROLLER]
-        self.assertEqual(controller, scsi_device.scsi_controller)
-
-    def test_HALDevice_scsi_controller_non_scsi_device(self):
-        """test of HALDevice.scsi_controller.
-
-        Variant for non-SCSI devices.
-        """
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {},
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        parser = SubmissionParser()
-        parser.buildHalDeviceList(parsed_data)
-
-        device = parser.devices[self.UDI_COMPUTER]
-        self.assertEqual(None, device.scsi_controller)
-
-    def test_HALDevice_scsi_controller_no_grandparent(self):
-        """test of HALDevice.scsi_controller.
-
-        Variant for a SCSI device without a grandparent device.
-        """
-        devices = [
-            # The (fake or real) SCSI host of the storage device.
-            {
-                'id': 1,
-                'udi': self.UDI_SATA_CONTROLLER_SCSI,
-                'properties': {},
-                },
-            # The (possibly fake) SCSI disk.
-            {
-                'id': 2,
-                'udi': self.UDI_SATA_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'info.parent': (self.UDI_SATA_CONTROLLER_SCSI, 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'SCSI device without grandparent device'
-        parser.buildHalDeviceList(parsed_data)
-
-        scsi_device = parser.devices[self.UDI_SATA_DISK]
-        self.assertEqual(None, scsi_device.scsi_controller)
-        self.assertWarningMessage(
-            parser.submission_key,
-            "Found SCSI device without a grandparent: %s."
-            % self.UDI_SATA_DISK)
-
-    def test_HALDevice_scsi_controller_no_parent(self):
-        """test of HALDevice.scsi_controller.
-
-        Variant for a SCSI device without a parent device.
-        """
-        devices = [
-            # The (possibly fake) SCSI disk.
-            {
-                'id': 1,
-                'udi': self.UDI_SATA_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'SCSI device without parent device'
-        parser.buildHalDeviceList(parsed_data)
-
-        scsi_device = parser.devices[self.UDI_SATA_DISK]
-        self.assertEqual(None, scsi_device.scsi_controller)
-        self.assertWarningMessage(
-            parser.submission_key,
-            "Found SCSI device without a parent: %s." % self.UDI_SATA_DISK)
-
-    def testHALDeviceGetRealBus(self):
-        """Test of HALDevice.real_bus, generic case.
-
-        For most buses as "seen" by HAL, HALDevice.real_bus returns a
-        unique HWBus value.
-        """
-        for hal_bus, real_bus in (('usb_device', HWBus.USB),
-                                  ('pcmcia', HWBus.PCMCIA),
-                                  ('ide', HWBus.IDE),
-                                  ('serio', HWBus.SERIAL),
-                                 ):
-            UDI_TEST_DEVICE = '/org/freedesktop/Hal/devices/test_device'
-            devices = [
-                {
-                    'id': 1,
-                    'udi': UDI_TEST_DEVICE,
-                    'properties': {
-                        'info.bus': (hal_bus, 'str'),
-                        },
-                    },
-                ]
-            parsed_data = {
-                'hardware': {
-                    'hal': {
-                        'devices': devices,
-                        },
-                    },
-                }
-            parser = SubmissionParser(self.log)
-            parser.buildHalDeviceList(parsed_data)
-            test_device = parser.devices[UDI_TEST_DEVICE]
-            test_bus = test_device.real_bus
-            self.assertEqual(test_bus, real_bus,
-                             'Unexpected result of HALDevice.real_bus for '
-                             'HAL bus %s: %s.' % (hal_bus, test_bus.title))
-
-    def testHALDeviceGetRealBusSystem(self):
-        """Test of HALDevice.real_bus, for the tested machine itself."""
-
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {
-                    'info.bus': ('unknown', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        test_device = parser.devices[self.UDI_COMPUTER]
-        test_bus = test_device.real_bus
-        self.assertEqual(test_bus, HWBus.SYSTEM,
-                         'Unexpected result of HALDevice.real_bus for '
-                         'a system: %s' % test_bus.title)
-
-    def testHALDeviceGetRealBusScsiUsb(self):
-        """Test of HALDevice.real_bus for info.bus=='scsi' and a USB device.
-
-        Memory sticks, card readers and USB->IDE/SATA adapters use SCSI
-        emulation; HALDevice.real_bus treats these devices as "black boxes",
-        and thus returns None.
-        """
-        devices = [
-            # The main node of the USB storage device.
-            {
-                'id': 1,
-                'udi': self.UDI_USB_STORAGE,
-                'properties': {
-                    'info.bus': ('usb_device', 'str'),
-                    },
-                },
-            # The storage interface of the USB device.
-            {
-                'id': 2,
-                'udi': self.UDI_USB_STORAGE_IF0,
-                'properties': {
-                    'info.bus': ('usb', 'str'),
-                    'info.parent': (self.UDI_USB_STORAGE, 'str'),
-                    },
-                },
-            # The fake SCSI host of the storage device. Note that HAL does
-            # _not_ provide the info.bus property.
-            {
-                'id': 3,
-                'udi': self.UDI_USB_STORAGE_SCSI_HOST,
-                'properties': {
-                    'info.parent': (self.UDI_USB_STORAGE_IF0, 'str'),
-                    },
-                },
-            # The fake SCSI disk.
-            {
-                'id': 3,
-                'udi': self.UDI_USB_STORAGE_SCSI_DEVICE,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'info.parent': (self.UDI_USB_STORAGE_SCSI_HOST, 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-
-        usb_fake_scsi_disk = parser.devices[self.UDI_USB_STORAGE_SCSI_DEVICE]
-        self.assertEqual(usb_fake_scsi_disk.real_bus, None,
-            'Unexpected result of HALDevice.real_bus for the fake SCSI '
-            'disk HAL node of a USB storage device bus.')
-
-    def testHALDeviceGetRealBusScsiPci(self):
-        """Test of HALDevice.real_bus for info.bus=='scsi'.
-
-        Many non-SCSI devices support the SCSI command, and the Linux
-        kernel can treat them like SCSI devices. The real bus of these
-        devices can be found by looking at the PCI class and subclass
-        of the host controller of the possibly fake SCSI device.
-        The real bus of these device can be IDE, ATA, SATA or SCSI.
-        """
-        devices = [
-            # The PCI host controller.
-            {
-                'id': 1,
-                'udi': self.UDI_SATA_CONTROLLER,
-                'properties': {
-                    'info.bus': ('pci', 'str'),
-                    'pci.device_class': (PCI_CLASS_STORAGE, 'int'),
-                    'pci.device_subclass': (PCI_SUBCLASS_STORAGE_SATA,
-                                            'int'),
-                    },
-                },
-            # The fake SCSI host of the storage device. Note that HAL does
-            # _not_ provide the info.bus property.
-            {
-                'id': 2,
-                'udi': self.UDI_SATA_CONTROLLER_SCSI,
-                'properties': {
-                    'info.parent': (self.UDI_SATA_CONTROLLER, 'str'),
-                    },
-                },
-            # The (possibly fake) SCSI disk.
-            {
-                'id': 3,
-                'udi': self.UDI_SATA_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'info.parent': (self.UDI_SATA_CONTROLLER_SCSI, 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        pci_subclass_bus = (
-            (0, HWBus.SCSI),
-            (1, HWBus.IDE),
-            (2, HWBus.FLOPPY),
-            (3, HWBus.IPI),
-            (4, None),  # subclass RAID is ignored.
-            (5, HWBus.ATA),
-            (6, HWBus.SATA),
-            (7, HWBus.SAS),
-            )
-
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-
-        for device_subclass, expected_bus in pci_subclass_bus:
-            devices[0]['properties']['pci.device_subclass'] = (
-                device_subclass, 'int')
-            fake_scsi_disk = parser.devices[self.UDI_SATA_DISK]
-            found_bus = fake_scsi_disk.real_bus
-            self.assertEqual(found_bus, expected_bus,
-                'Unexpected result of HWDevice.real_bus for PCI storage '
-                'class device, subclass %i: %r.' % (device_subclass,
-                                                    found_bus))
-
-    def testHALDeviceGetRealBusScsiDeviceWithoutGrandparent(self):
-        """Test of HALDevice.real_bus for a device without a grandparent."""
-        devices = [
-            # A SCSI host conrtoller.
-            {
-                'id': 2,
-                'udi': self.UDI_SATA_CONTROLLER_SCSI,
-                'properties': {},
-                },
-            # A SCSI disk.
-            {
-                'id': 3,
-                'udi': self.UDI_SCSI_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'info.parent': (self.UDI_SATA_CONTROLLER_SCSI, 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'Test SCSI disk without a grandparent'
-        parser.buildHalDeviceList(parsed_data)
-        scsi_disk = parser.devices[self.UDI_SCSI_DISK]
-        bus = scsi_disk.real_bus
-        self.assertEqual(bus, None,
-            'Unexpected result of HALDevice.real_bus for a SCSI device '
-            'without a grandparent. Expected None, got %r' % bus)
-        self.assertWarningMessage(parser.submission_key,
-            'Found SCSI device without a grandparent: %s.'
-             % self.UDI_SCSI_DISK)
-
-    def testHALDeviceGetRealBusScsiDeviceWithoutParent(self):
-        """Test of HALDevice.real_bus for a device without a parent."""
-        devices = [
-            {
-                'id': 3,
-                'udi': self.UDI_SCSI_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'Test SCSI disk without a parent'
-        parser.buildHalDeviceList(parsed_data)
-        scsi_disk = parser.devices[self.UDI_SCSI_DISK]
-        bus = scsi_disk.real_bus
-        self.assertEqual(bus, None,
-            'Unexpected result of HALDevice.real_bus for a SCSI device '
-            'without a parent. Expected None, got %r' % bus)
-        self.assertWarningMessage(parser.submission_key,
-            'Found SCSI device without a parent: %s.'
-             % self.UDI_SCSI_DISK)
-
-    def testHALDeviceGetRealBusScsiDeviceWithBogusPciGrandparent(self):
-        """Test of HALDevice.real_bus for a device with a bogus grandparent.
-
-        The PCI device class must be PCI_CLASS_STORAGE.
-        """
-        devices = [
-            # The PCI host controller. The PCI device class is invalid.
-            {
-                'id': 1,
-                'udi': self.UDI_SATA_CONTROLLER,
-                'properties': {
-                    'info.bus': ('pci', 'str'),
-                    'pci.device_class': (-1, 'int'),
-                    'pci.device_subclass': (PCI_SUBCLASS_STORAGE_SATA, 'int'),
-                    },
-                },
-            # The fake SCSI host of the storage device. Note that HAL does
-            # _not_ provide the info.bus property.
-            {
-                'id': 2,
-                'udi': self.UDI_SATA_CONTROLLER_SCSI,
-                'properties': {
-                    'info.parent': (self.UDI_SATA_CONTROLLER, 'str'),
-                    },
-                },
-            # The (possibly fake) SCSI disk.
-            {
-                'id': 3,
-                'udi': self.UDI_SATA_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'info.parent': (self.UDI_SATA_CONTROLLER_SCSI, 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.submission_key = (
-            'Test SCSI disk with invalid controller device class')
-        parser.buildHalDeviceList(parsed_data)
-        scsi_disk = parser.devices[self.UDI_SATA_DISK]
-        bus = scsi_disk.real_bus
-        self.assertEqual(bus, None,
-            'Unexpected result of HALDevice.real_bus for a SCSI device '
-            'without a parent. Expected None, got %r' % bus)
-        self.assertWarningMessage(parser.submission_key,
-            'A (possibly fake) SCSI device %s is connected to PCI device '
-            '%s that has the PCI device class -1; expected class 1 (storage).'
-             % (self.UDI_SATA_DISK, self.UDI_SATA_CONTROLLER))
-
-    def testHALDeviceGetRealBusPci(self):
-        """Test of HALDevice.real_bus for info.bus=='pci'.
-
-        If info.bus == 'pci', we may have a real PCI device or a PCCard.
-        """
-        # possible parent device for the tested device,
-        parent_devices = [
-            # The host itself.
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {
-                    'info.bus': ('unknown', 'str'),
-                    },
-                },
-            # A PCI->PCI bridge.
-            {
-                'id': 2,
-                'udi': self.UDI_PCI_PCI_BRIDGE,
-                'properties': {
-                    'info.parent': (self.UDI_COMPUTER, 'str'),
-                    'info.bus': ('pci', 'str'),
-                    'pci.device_class': (PCI_CLASS_BRIDGE, 'int'),
-                    'pci.device_subclass': (PCI_SUBCLASS_BRIDGE_PCI, 'int'),
-                    },
-                },
-            # A PCI->PCCard bridge.
-            {
-                'id': 3,
-                'udi': self.UDI_PCI_PCCARD_BRIDGE,
-                'properties': {
-                    'info.parent': (self.UDI_PCI_PCI_BRIDGE, 'str'),
-                    'info.bus': ('pci', 'str'),
-                    'pci.device_class': (PCI_CLASS_BRIDGE, 'int'),
-                    'pci.device_subclass': (PCI_SUBCLASS_BRIDGE_CARDBUS,
-                                            'int'),
-                    },
-                },
-        ]
-        tested_device = {
-            'id': 4,
-            'udi': self.UDI_PCCARD_DEVICE,
-            'properties': {
-                'info.bus': ('pci', 'str'),
-                },
-            }
-        parsed_data = {
-            'hardware': {
-                'hal': {},
-                },
-            }
-        expected_result_for_parent_device = {
-            self.UDI_COMPUTER: HWBus.PCI,
-            self.UDI_PCI_PCI_BRIDGE: HWBus.PCI,
-            self.UDI_PCI_PCCARD_BRIDGE: HWBus.PCCARD,
-            }
-
-        parser = SubmissionParser(self.log)
-
-        for parent_device in parent_devices:
-            devices = parent_devices[:]
-            tested_device['properties']['info.parent'] = (
-                parent_device['udi'], 'str')
-            devices.append(tested_device)
-            parsed_data['hardware']['hal']['devices'] = devices
-            parser.buildHalDeviceList(parsed_data)
-            tested_hal_device = parser.devices[self.UDI_PCCARD_DEVICE]
-            found_bus = tested_hal_device.real_bus
-            expected_bus = expected_result_for_parent_device[
-                parent_device['udi']]
-            self.assertEqual(found_bus, expected_bus,
-                             'Unexpected result of HWDevice.real_bus for a '
-                             'PCI or PCCard device: Expected %r, got %r.'
-                             % (expected_bus, found_bus))
-
-    def testHALDeviceGetRealBusUnknown(self):
-        """Test of HALDevice.real_bus for unknown values of info.bus."""
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_PCCARD_DEVICE,
-                'properties': {
-                    'info.bus': ('nonsense', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'Test of unknown bus name'
-        parser.buildHalDeviceList(parsed_data)
-        found_bus = parser.devices[self.UDI_PCCARD_DEVICE].real_bus
-        self.assertEqual(found_bus, None,
-                         'Unexpected result of HWDevice.real_bus for an '
-                         'unknown bus name: Expected None, got %r.'
-                         % found_bus)
-        self.assertWarningMessage(
-            parser.submission_key,
-            "Unknown bus u'nonsense' for device " + self.UDI_PCCARD_DEVICE)
-
-    def test_HALDevice_is_root_device_for_root_device(self):
-        """Test of HALDevice.is_root_device for the root device."""
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {},
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        parser = SubmissionParser()
-        parser.submission_key = 'Test of HALDevice.is_root_device'
-        parser.buildHalDeviceList(parsed_data)
-        self.assertTrue(parser.devices[self.UDI_COMPUTER].is_root_device)
-
-    def test_HALDevice_is_root_device_for_non_root_device(self):
-        """Test of HALDevice.is_root_device for a non-root device."""
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_PCCARD_DEVICE,
-                'properties': {},
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        parser = SubmissionParser()
-        parser.submission_key = 'Test of HALDevice.is_root_device'
-        parser.buildHalDeviceList(parsed_data)
-        self.assertFalse(
-            parser.devices[self.UDI_PCCARD_DEVICE].is_root_device)
-
-    def renameInfoBusToInfoSubsystem(self, devices):
-        """Rename the property info.bus in a device list to info.subsystem.
-
-        Older HAL version store the device bus in the property info.bus;
-        newer versions store the bus in info.subsystem.
-
-        The parameter devices is a list of dictionaries as used in the
-        methods below. This method replaces all dictionary entries with
-        the key info.bus by entries with the key info.subsystem in order
-        to allow easy testing of both variants.
-        """
-        for device in devices:
-            if 'info.bus' in device['properties']:
-                bus = device['properties']['info.bus']
-                device['properties']['info.subsystem'] = bus
-                del device['properties']['info.bus']
-
-    def testHALDeviceRealDeviceRegularBus(self):
-        """Test of HALDevice.is_real_device: regular info.bus property.
-
-        See below for exceptions, if info.bus == 'usb_device' or if
-        info.bus == 'usb'.
-        """
-        # If a HAL device has the property info.bus, it is considered
-        # to be a real device.
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_USB_CONTROLLER_PCI_SIDE,
-                'properties': {
-                    'info.bus': ('pci', 'str'),
-                    'pci.device_class': (PCI_CLASS_SERIALBUS_CONTROLLER,
-                                         'int'),
-                    'pci.device_subclass': (PCI_SUBCLASS_SERIALBUS_USB,
-                                            'int'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        device = parser.devices[self.UDI_USB_CONTROLLER_PCI_SIDE]
-        self.assertTrue(device.is_real_device,
-                        'Device with info.bus property not treated as a '
-                        'real device.')
-        self.renameInfoBusToInfoSubsystem(devices)
-        parser.buildHalDeviceList(parsed_data)
-        device = parser.devices[self.UDI_USB_CONTROLLER_PCI_SIDE]
-        self.assertTrue(device.is_real_device,
-                        'Device with info.subsystem property not treated as '
-                        'a real device.')
-
-    def testHALDeviceRealDeviceNoBus(self):
-        """Test of HALDevice.is_real_device: No info.bus property."""
-        UDI_HAL_STORAGE_DEVICE = '/org/freedesktop/Hal/devices/storage...'
-        devices = [
-            {
-                'id': 1,
-                'udi': UDI_HAL_STORAGE_DEVICE,
-                'properties': {},
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        device = parser.devices[UDI_HAL_STORAGE_DEVICE]
-        self.assertFalse(device.is_real_device,
-                         'Device without info.bus property treated as a '
-                         'real device')
-
-    def testHALDeviceRealDeviceHALBusValueIgnored(self):
-        """Test of HALDevice.is_real_device: ignored values of info.bus.
-
-        A HAL device is considered to not be a real device, if its
-        info.bus proerty is 'drm', 'dvb', 'memstick_host', 'net',
-        'scsi_generic', 'scsi_host', 'sound', 'ssb', 'tty', 'usb'
-        or 'video4linux'.
-        """
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_USB_HUB_IF0,
-                'properties': {
-                    'info.bus': ('usb', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        properties = devices[0]['properties']
-        parser = SubmissionParser(self.log)
-
-        ignored_buses = (
-             'ac97', 'bttv-sub', 'disk', 'drm', 'drm_minor', 'dvb',
-            'enclosure', 'gameport', 'graphics', 'hid', 'host', 'hwmon',
-            'ieee80211', 'link', 'lirc', 'mISDN', 'memstick', 'memstick_host',
-            'net', 'partition', 'pci_express', 'pcmcia_socket', 'pvrusb2',
-            'sas_device', 'sas_end_device', 'sas_host', 'sas_phy', 'sas_port',
-            'scsi_disk', 'scsi_generic', 'scsi_host', 'scsi_tape',
-            'scsi_target', 'sound', 'spi_host', 'spi_transport', 'ssb',
-            'tifm', 'tifm_adapter', 'tty', 'usb', 'usb-serial',
-            'usb_endpoint', 'usb_host', 'usb_interface', 'usbmon',
-            'video4linux', 'wlan')
-        for tested_bus in ignored_buses:
-            properties['info.bus'] = (tested_bus, 'str')
-            parser.buildHalDeviceList(parsed_data)
-            device = parser.devices[self.UDI_USB_HUB_IF0]
-            self.assertFalse(
-                device.is_real_device,
-                'Device with info.bus=%s treated as a real device'
-                % tested_bus)
-
-        del properties['info.bus']
-        for tested_bus in ignored_buses:
-            properties['info.subsystem'] = (tested_bus, 'str')
-            parser.buildHalDeviceList(parsed_data)
-            device = parser.devices[self.UDI_USB_HUB_IF0]
-            self.assertFalse(
-                device.is_real_device,
-                'Device with info.subsystem=%s treated as a real device'
-                % tested_bus)
-
-    def runTestHALDeviceRealDeviceScsiDevicesPciController(
-        self, devices, bus_property_name):
-        """Test of HALDevice.is_real_device: info.bus == 'scsi'.
-
-        The (fake or real) SCSI device is connected to a PCI controller.
-        Though the real bus may not be SCSI, all devices for the busses
-        SCSI, IDE, ATA, SATA, SAS are treated as real devices.
-        """
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        pci_subclass_bus = (
-            (0, True),  # a real SCSI controller
-            (1, True),  # an IDE device
-            (4, False),  # subclass RAID is ignored.
-            (5, True),  # an ATA device
-            (6, True),  # a SATA device
-            (7, True),  # a SAS device
-            )
-
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-
-        for device_subclass, expected_is_real in pci_subclass_bus:
-            devices[0]['properties']['pci.device_subclass'] = (
-                device_subclass, 'int')
-            scsi_device = parser.devices[self.UDI_SATA_DISK]
-            found_is_real = scsi_device.is_real_device
-            self.assertEqual(found_is_real, expected_is_real,
-                'Unexpected result of HWDevice.is_real_device for a HAL SCSI '
-                'connected to PCI controller, subclass %i: %r; testing '
-                'property %s'
-                % (device_subclass, found_is_real, bus_property_name))
-
-    def testHALDeviceRealDeviceScsiDevicesPciController(self):
-        """Test of HALDevice.is_real_device: info.bus == 'scsi'.
-
-        The (fake or real) SCSI device is connected to a PCI controller.
-        Though the real bus may not be SCSI, all devices for the busses
-        SCSI, IDE, ATA, SATA, SAS are treated as real devices.
-        """
-        devices = [
-            # The PCI host controller.
-            {
-                'id': 1,
-                'udi': self.UDI_SATA_CONTROLLER,
-                'properties': {
-                    'info.bus': ('pci', 'str'),
-                    'pci.device_class': (PCI_CLASS_STORAGE, 'int'),
-                    'pci.device_subclass': (PCI_SUBCLASS_STORAGE_SATA, 'int'),
-                    },
-                },
-            # The (possibly fake) SCSI host of the storage device.
-            {
-                'id': 3,
-                'udi': self.UDI_SATA_CONTROLLER_SCSI,
-                'properties': {
-                    'info.parent': (self.UDI_SATA_CONTROLLER,
-                                    'str'),
-                    },
-                },
-            # The (possibly fake) SCSI disk.
-            {
-                'id': 3,
-                'udi': self.UDI_SATA_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'info.parent': (self.UDI_SATA_CONTROLLER_SCSI, 'str'),
-                    },
-                },
-            ]
-
-        self.runTestHALDeviceRealDeviceScsiDevicesPciController(
-            devices, 'info.bus')
-        self.renameInfoBusToInfoSubsystem(devices)
-        self.runTestHALDeviceRealDeviceScsiDevicesPciController(
-            devices, 'info.subsystem')
-
-    def testHALDeviceRealDeviceScsiDeviceUsbStorage(self):
-        """Test of HALDevice.is_real_device: info.bus == 'scsi'.
-
-        USB storage devices are treated as SCSI devices by HAL;
-        we do not consider them to be real devices.
-        """
-        devices = [
-            # The main node of the USB storage device.
-            {
-                'id': 1,
-                'udi': self.UDI_USB_STORAGE,
-                'properties': {
-                    'info.bus': ('usb_device', 'str'),
-                    },
-                },
-            # The storage interface of the USB device.
-            {
-                'id': 2,
-                'udi': self.UDI_USB_STORAGE_IF0,
-                'properties': {
-                    'info.bus': ('usb', 'str'),
-                    'info.parent': (self.UDI_USB_STORAGE, 'str'),
-                    },
-                },
-            # The fake SCSI host of the storage device. Note that HAL does
-            # _not_ provide the info.bus property.
-            {
-                'id': 3,
-                'udi': self.UDI_USB_STORAGE_SCSI_HOST,
-                'properties': {
-                    'info.parent': (self.UDI_USB_STORAGE_IF0, 'str'),
-                    },
-                },
-            # The fake SCSI disk.
-            {
-                'id': 3,
-                'udi': self.UDI_USB_STORAGE_SCSI_DEVICE,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'info.parent': (self.UDI_USB_STORAGE_SCSI_HOST, 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-
-        scsi_device = parser.devices[self.UDI_USB_STORAGE_SCSI_DEVICE]
-        self.assertFalse(scsi_device.is_real_device,
-            'Unexpected result of HWDevice.is_real_device for a HAL SCSI '
-            'device as a subdevice of a USB storage device.')
-
-        self.renameInfoBusToInfoSubsystem(devices)
-        scsi_device = parser.devices[self.UDI_USB_STORAGE_SCSI_DEVICE]
-        self.assertFalse(scsi_device.is_real_device,
-            'Unexpected result of HWDevice.is_real_device for a HAL SCSI '
-            'device as a subdevice of a USB storage device.')
-
-    def testHALDeviceRealDeviceRootDevice(self):
-        """Test of HALDevice.is_real_device for the root node."""
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {},
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        device = parser.devices[self.UDI_COMPUTER]
-        self.assertTrue(device.is_real_device,
-                        'Root device not treated as a real device')
-
-    def testHALDeviceRealChildren(self):
-        """Test of HALDevice.getRealChildren."""
-        # An excerpt of a real world HAL device tree. We have three "real"
-        # devices, and two "unreal" devices (ID 3 and 4)
-        #
-        # the host itself. Treated as a real device.
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {}
-                },
-            # A PCI->USB bridge.
-            {
-                'id': 2,
-                'udi': self.UDI_USB_CONTROLLER_PCI_SIDE,
-                'properties': {
-                    'info.parent': (self.UDI_COMPUTER, 'str'),
-                    'info.bus': ('pci', 'str'),
-                    'pci.device_class': (PCI_CLASS_SERIALBUS_CONTROLLER,
-                                         'int'),
-                    'pci.device_subclass': (PCI_SUBCLASS_SERIALBUS_USB,
-                                            'int'),
-                 }
-            },
-            # The "output aspect" of the PCI->USB bridge. Not a real
-            # device.
-            {
-                'id': 3,
-                'udi': self.UDI_USB_CONTROLLER_USB_SIDE,
-                'properties': {
-                    'info.parent': (self.UDI_USB_CONTROLLER_PCI_SIDE, 'str'),
-                    'info.bus': ('usb_device', 'str'),
-                    'usb_device.vendor_id': (0, 'int'),
-                    'usb_device.product_id': (0, 'int'),
-                    },
-                },
-            # The HAL node for raw USB data access of the bridge. Not a
-            # real device.
-            {
-                'id': 4,
-                'udi': self.UDI_USB_CONTROLLER_USB_SIDE_RAW,
-                'properties': {
-                    'info.parent': (self.UDI_USB_CONTROLLER_USB_SIDE, 'str'),
-                    },
-                },
-            # The HAL node of a USB device connected to the bridge.
-            {
-                'id': 5,
-                'udi': self.UDI_USB_HUB,
-                'properties': {
-                    'info.parent': (self.UDI_USB_CONTROLLER_USB_SIDE, 'str'),
-                    'info.bus': ('usb_device', 'str'),
-                    'usb_device.vendor_id': (self.USB_VENDOR_ID_NEC, 'int'),
-                    'usb_device.product_id': (self.USB_PROD_ID_NEC_HUB,
-                                              'int'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-
-        # The PCI-USB bridge is a child of the system.
-        root_device = parser.devices[self.UDI_COMPUTER]
-        pci_usb_bridge = parser.devices[self.UDI_USB_CONTROLLER_PCI_SIDE]
-        self.assertEqual(root_device.getRealChildren(), [pci_usb_bridge],
-                         'Unexpected list of real children of the root '
-                         'device')
-
-        # The "output aspect" of the PCI->USB bridge and the node for
-        # raw USB access do not appear as childs of the PCI->USB bridge,
-        # but the node for the USB device is considered to be a child
-        # of the bridge.
-
-        usb_device = parser.devices[self.UDI_USB_HUB]
-        self.assertEqual(pci_usb_bridge.getRealChildren(), [usb_device],
-                         'Unexpected list of real children of the PCI-> '
-                         'USB bridge')
-
-    def testHasReliableDataRegularCase(self):
-        """Test of HALDevice.has_reliable_data, regular case."""
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {}
-                },
-            {
-                'id': 2,
-                'udi': self.UDI_SATA_CONTROLLER,
-                'properties': {
-                    'info.parent': (self.UDI_COMPUTER, 'str'),
-                    'info.bus': ('pci', 'str'),
-                    'pci.vendor_id': (self.PCI_VENDOR_ID_INTEL, 'int'),
-                    'pci.product_id': (self.PCI_PROD_ID_PCI_PCCARD_BRIDGE,
-                                       'int'),
-                    'info.product': ('Intel PCCard bridge 1234', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        device = parser.devices[self.UDI_SATA_CONTROLLER]
-        self.assertTrue(
-            device.has_reliable_data,
-            'Regular device treated as not having reliable data.')
-
-    def testHasReliableDataNotProcessible(self):
-        """Test of HALDevice.has_reliable_data, bus without reliable data."""
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SATA_CONTROLLER,
-                'properties': {},
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        properties = devices[0]['properties']
-        for bus in ('asus_oled', 'atm', 'backlight', 'bdi', 'bluetooth',
-                    'cardman_4040', 'dahdi', 'dmi', 'heci', 'hidraw',
-                    'hwmon', 'i2c-adapter', 'ieee1394', 'ieee1394_protocol',
-                    'input', 'leds', 'mem', 'misc', 'mmc', 'mmc_host', 'msr',
-                    'pci_bus', 'pcmcia', 'pktcdvd', 'platform', 'pnp',
-                    'power_supply', 'ppdev', 'ppp', 'printer', 'rfkill',
-                    'thermal', 'ttm', 'vc', 'video_output', 'vtconsole'):
-            properties['info.bus'] = (bus, 'str')
-            parser.buildHalDeviceList(parsed_data)
-            device = parser.devices[self.UDI_SATA_CONTROLLER]
-            self.assertFalse(device.has_reliable_data,
-                'Device with bus=%s treated as having reliable data.' % bus)
-
-    def testHasReliableDataRootDevice(self):
-        """Test of HALDevice.has_reliable_data, root device.
-
-        The root device has the info.subsystem or info.bus property set
-        to 'unknown'. While we treat other devices with ths bus value
-        as useless, the root device is real.
-        """
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {
-                    'info.subsystem': ('unknown', 'str'),
-                    'system.hardware.vendor': ('FUJITSU SIEMENS', 'str'),
-                    'system.hardware.product': ('LIFEBOOK E8210', 'str'),
-                },
-            },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        device = parser.devices[self.UDI_COMPUTER]
-        self.assertTrue(
-            device.has_reliable_data,
-            "Root device not treated as having reliable data.")
-
-    def testHasReliableDataForInsuffientData(self):
-        """Test of HALDevice.has_reliable_data, insufficent device data.
-
-        Test for a HAL device that should be processible but does
-        not provide enough data. Aside from a bus, we need a vendor ID,
-        a product ID and a product name.
-        """
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {}
-                },
-            {
-                'id': 2,
-                'udi': self.UDI_SATA_CONTROLLER,
-                'properties': {
-                    'info.parent': (self.UDI_COMPUTER, 'str'),
-                    'info.bus': ('pci', 'str'),
-                    'pci.vendor_id': (self.PCI_VENDOR_ID_INTEL, 'int'),
-                    'pci.product_id': (self.PCI_PROD_ID_PCI_PCCARD_BRIDGE,
-                                       'int'),
-                    'info.product': ('Intel PCCard bridge 1234', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        missing_data_log_message = (
-            ('pci.vendor_id',
-             "A HALDevice that is supposed to be a real device does not "
-             "provide bus, vendor ID, product ID or product name: <DBItem "
-             "HWBus.PCI, (1) PCI> None 28980 u'Intel PCCard bridge 1234' "
-             "/org/freedesktop/Hal/devices/pci_8086_27c5"
-             ),
-            ('pci.product_id',
-             "A HALDevice that is supposed to be a real device does not "
-             "provide bus, vendor ID, product ID or product name: "
-             "<DBItem HWBus.PCI, (1) PCI> 32902 None u'Intel PCCard bridge "
-             "1234' /org/freedesktop/Hal/devices/pci_8086_27c5"
-             ),
-            ('info.product',
-             "A HALDevice that is supposed to be a real device does not "
-             "provide bus, vendor ID, product ID or product name: "
-             "<DBItem HWBus.PCI, (1) PCI> 32902 28980 None "
-             "/org/freedesktop/Hal/devices/pci_8086_27c5"
-             ),
-            )
-
-        for (missing_data, expected_log_message) in missing_data_log_message:
-            test_parsed_data = deepcopy(parsed_data)
-            test_device = test_parsed_data['hardware']['hal']['devices'][1]
-            del test_device['properties'][missing_data]
-
-            parser = SubmissionParser(self.log)
-            submission_key = 'test_missing_%s' % missing_data
-            parser.submission_key = submission_key
-            parser.buildHalDeviceList(test_parsed_data)
-            device = parser.devices[self.UDI_SATA_CONTROLLER]
-            self.assertFalse(
-                device.has_reliable_data,
-                'Device with missing property %s treated as having reliable'
-                'data.' % missing_data)
-            self.assertWarningMessage(submission_key, expected_log_message)
-
-    def testHasReliableDataIDEDevice(self):
-        """Test of HALDevice.has_reliable_data, for IDE devices.
-
-        Many IDE devices do not provide vendor and product IDs. This is
-        a known problem and hence not worth a log message.
-        """
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {}
-                },
-            {
-                'id': 2,
-                'udi': self.UDI_SATA_CONTROLLER,
-                'properties': {
-                    'info.parent': (self.UDI_COMPUTER, 'str'),
-                    'info.bus': ('pci', 'str'),
-                    'pci.vendor_id': (self.PCI_VENDOR_ID_INTEL, 'int'),
-                    'pci.product_id': (self.PCI_PROD_ID_PCI_PCCARD_BRIDGE,
-                                       'int'),
-                    'info.product': ('Intel PCCard bridge 1234', 'str'),
-                    },
-                },
-            {
-                'id': 3,
-                'udi': self.UDI_SATA_DISK,
-                'properties': {
-                    'info.parent': (self.UDI_SATA_CONTROLLER, 'str'),
-                    'info.bus': ('ide', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        device = parser.devices[self.UDI_SATA_DISK]
-        self.assertFalse(
-            device.has_reliable_data,
-            'IDE Device with missing properties vendor ID, product ID, '
-            'product name treated as having reliabledata.')
-        self.assertEqual(
-            len(self.handler.records), 0,
-            'Warning messages exist for processing an IDE device where '
-            'no messages are expected.')
-
-    def testHALDeviceSCSIVendorModelNameRegularCase(self):
-        """Test of HALDevice.getScsiVendorAndModelName, regular case."""
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SCSI_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'scsi.vendor': ('SHARP', 'str'),
-                    'scsi.model': ('JX250 SCSI', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        device = parser.devices[self.UDI_SCSI_DISK]
-        vendor_model = device.getScsiVendorAndModelName()
-        self.assertEqual(
-            {
-                'vendor': 'SHARP',
-                'product': 'JX250 SCSI',
-                },
-            vendor_model,
-            'Unexpected result of HWDevice.getScsiVendorAndModelName '
-            'for a regular SCSI device. Expected vendor name SHARP, got %r.'
-            % vendor_model)
-
-    def testHALDeviceSCSIVendorModelNameATADiskShortModelName(self):
-        """Test of HALDevice.getScsiVendorAndModelName, ATA disk (1).
-
-        Test of an ATA disk with a short model name. The Linux kenrel
-        sets the vendor name to "ATA" and inserts the real vendor
-        name into the model string.
-        """
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SCSI_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'scsi.vendor': ('ATA', 'str'),
-                    'scsi.model': ('Hitachi HTS54161', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        device = parser.devices[self.UDI_SCSI_DISK]
-        vendor_model = device.getScsiVendorAndModelName()
-        self.assertEqual(
-            {
-                'vendor': 'Hitachi',
-                'product': 'HTS54161',
-                },
-            vendor_model,
-            'Unexpected result of HWDevice.getScsiVendorAndModelName '
-            'for an ATA SCSI device: %r.'
-            % vendor_model)
-
-    def testHALDeviceSCSIVendorModelNameATADiskLongModelName(self):
-        """Test of HALDevice.getScsiVendorAndModelName, ATA disk (2).
-
-        Test of an ATA disk with a short model name. The Linux kenrel
-        sets the vendor name to "ATA" and ignores the real vendor name,
-        """
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SCSI_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'scsi.vendor': ('ATA', 'str'),
-                    'scsi.model': ('HTC426060G9AT00', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        device = parser.devices[self.UDI_SCSI_DISK]
-        vendor_product = device.getScsiVendorAndModelName()
-        self.assertEqual(
-            {
-                'vendor': 'ATA',
-                'product': 'HTC426060G9AT00',
-                },
-            vendor_product,
-            'Unexpected result of HWDevice.getScsiVendorAndModelName '
-            'for a reguale SCSI device: %r.'
-            % vendor_product)
-
-    def testHALDeviceVendorFromInfoVendor(self):
-        """Test of HALDevice.vendor, regular case.
-
-        The value is copied from info.vendor, if available."""
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SATA_CONTROLLER,
-                'properties': {
-                    'info.bus': ('pci', 'str'),
-                    'info.vendor': ('Intel Corporation', 'str'),
-                    'pci.vendor': ('should not be used', 'str'),
-                    }
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_vendor = parser.devices[self.UDI_SATA_CONTROLLER].vendor
-        self.assertEqual(found_vendor, 'Intel Corporation',
-                         'Unexpected result of HWDevice.vendor. '
-                         'Expected Intel Corporation, got %r.'
-                         % found_vendor)
-
-    def testHALDeviceVendorFromBusVendor(self):
-        """Test of HALDevice.vendor, value copied from ${bus}.vendor.
-
-        If the property info.vendor does not exist, ${bus}.vendor
-        is tried.
-        """
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SATA_CONTROLLER,
-                'properties': {
-                    'info.bus': ('pci', 'str'),
-                    'pci.vendor': ('Intel Corporation', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_vendor = parser.devices[self.UDI_SATA_CONTROLLER].vendor
-        self.assertEqual(found_vendor, 'Intel Corporation',
-                         'Unexpected result of HWDevice.vendor, '
-                         'if info.vendor does not exist. '
-                         'Expected Intel Corporation, got %r.'
-                         % found_vendor)
-
-    def testHALDeviceVendorScsi(self):
-        """Test of HALDevice.vendor for SCSI devices: regular case."""
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SCSI_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'scsi.vendor': ('SEAGATE', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_vendor = parser.devices[self.UDI_SCSI_DISK].vendor
-        self.assertEqual(found_vendor, 'SEAGATE',
-                         'Unexpected result of HWDevice.vendor '
-                         'for SCSI device. Expected SEAGATE, got %r.'
-                         % found_vendor)
-
-    def testHALDeviceVendorScsiAta(self):
-        """Test of HALDevice.vendor for SCSI devices: fake IDE/SATA disks."""
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SCSI_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'scsi.vendor': ('ATA', 'str'),
-                    'scsi.model': ('Hitachi HTS54161', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_vendor = parser.devices[self.UDI_SCSI_DISK].vendor
-        self.assertEqual(found_vendor, 'Hitachi',
-                         'Unexpected result of HWDevice.vendor, for fake '
-                         'SCSI device. Expected Hitachi, got %r.'
-                         % found_vendor)
-
-    def testHALDeviceVendorSystem(self):
-        """Test of HALDevice.vendor for the machine itself."""
-        # HAL does not provide info.vendor for the root UDI
-        # /org/freedesktop/Hal/devices/computer, hence HALDevice.vendor
-        # reads the vendor name from system.vendor
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {
-                    'info.bus': ('unknown', 'str'),
-                    'system.hardware.vendor': ('FUJITSU SIEMENS', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_vendor = parser.devices[self.UDI_COMPUTER].vendor
-        self.assertEqual(found_vendor, 'FUJITSU SIEMENS',
-                         'Unexpected result of HWDevice.vendor for a '
-                         'system. Expected FUJITSU SIEMENS, got %r.'
-                         % found_vendor)
-
-    def testHALDeviceProductFromInfoProduct(self):
-        """Test of HALDevice.product, regular case.
-
-        The value is copied from info.product, if available."""
-        # The product name is copied from the HAL property info.product,
-        # if it is avaliable.
-        devices = [
-             {
-                'id': 1,
-                'udi': self.UDI_SATA_CONTROLLER,
-                'properties': {
-                    'info.bus': ('pci', 'str'),
-                    'info.product': ('82801GBM/GHM SATA AHCI Controller',
-                                     'str'),
-                    'pci.product': ('should not be used', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_product = parser.devices[self.UDI_SATA_CONTROLLER].product
-        self.assertEqual(found_product, '82801GBM/GHM SATA AHCI Controller',
-                         'Unexpected result of HWDevice.product. '
-                         'Expected 82801GBM/GHM SATA AHCI Controller, got %r.'
-                         % found_product)
-
-    def testHALDeviceProductFromBusProduct(self):
-        """Test of HALDevice.product, value copied from ${bus}.product.
-
-        If the property info.product does not exist, ${bus}.product
-        """
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SATA_CONTROLLER,
-                'properties': {
-                    'info.bus': ('pci', 'str'),
-                    'pci.product': ('82801GBM/GHM SATA AHCI Controller',
-                                    'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_product = parser.devices[self.UDI_SATA_CONTROLLER].product
-        self.assertEqual(found_product, '82801GBM/GHM SATA AHCI Controller',
-                         'Unexpected result of HWDevice.product, '
-                         'if info.product does not exist. '
-                         'Expected 82801GBM/GHM SATA AHCI Controller, got %r.'
-                         % found_product)
-
-    def testHALDeviceProductScsi(self):
-        """Test of HALDevice.product for SCSI devices: regular case."""
-        # The name of SCSI device is copied from the property scsi.model.
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SCSI_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'scsi.vendor': ('SEAGATE', 'str'),
-                    'scsi.model': ('ST36530N', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_product = parser.devices[self.UDI_SCSI_DISK].product
-        self.assertEqual(found_product, 'ST36530N',
-                         'Unexpected result of HWDevice.product '
-                         'for SCSI device. Expected ST36530N, got %r.'
-                         % found_product)
-
-    def testHALDeviceProductScsiAta(self):
-        """Test of HALDevice.product for SCSI devices: fake IDE/SATA disks."""
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SCSI_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'scsi.vendor': ('ATA', 'str'),
-                    'scsi.model': ('Hitachi HTS54161', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_product = parser.devices[self.UDI_SCSI_DISK].product
-        self.assertEqual(found_product, 'HTS54161',
-                         'Unexpected result of HWDevice.product, for fake '
-                         'SCSI device. Expected HTS54161, got %r.'
-                         % found_product)
-
-    def testHALDeviceProductSystem(self):
-        """Test of HALDevice.product for the machine itself."""
-        # HAL sets info.product to "Computer" for the root UDI
-        # /org/freedesktop/Hal/devices/computer, hence HALDevice.product
-        # reads the product name from system.product.
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {
-                    'info.bus': ('unknown', 'str'),
-                    'system.hardware.product': ('LIFEBOOK E8210', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_product = parser.devices[self.UDI_COMPUTER].product
-        self.assertEqual(found_product, 'LIFEBOOK E8210',
-                         'Unexpected result of HWDevice.product, '
-                         'if info.product does not exist. '
-                         'Expected LIFEBOOK E8210, got %r.'
-                         % found_product)
-
-    def testHALDeviceVendorId(self):
-        """Test of HALDevice.vendor_id.
-
-        Many buses have a numerical vendor ID. Except for the special
-        cases tested below, HWDevice.vendor_id returns the HAL property
-        ${bus}.vendor_id.
-        """
-        devices = [
-             {
-                'id': 1,
-                'udi': self.UDI_SATA_CONTROLLER,
-                'properties': {
-                    'info.bus': ('pci', 'str'),
-                    'pci.vendor_id': (self.PCI_VENDOR_ID_INTEL, 'int'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_vendor_id = parser.devices[
-            self.UDI_SATA_CONTROLLER].vendor_id
-        self.assertEqual(found_vendor_id, self.PCI_VENDOR_ID_INTEL,
-                         'Unexpected result of HWDevice.vendor_id. '
-                         'Expected 0x8086, got 0x%x.'
-                         % found_vendor_id)
-
-    def testHALDeviceVendorIdScsi(self):
-        """Test of HALDevice.vendor_id for SCSI devices.
-
-        The SCSI specification does not know about a vendor ID,
-        we use the vendor string as returned by INQUIRY command
-        as the ID.
-        """
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SCSI_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'scsi.vendor': ('SEAGATE', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_vendor_id = parser.devices[self.UDI_SCSI_DISK].vendor_id
-        self.assertEqual(found_vendor_id, 'SEAGATE',
-                         'Unexpected result of HWDevice.vendor_id for a. '
-                         'SCSI device. Expected SEAGATE, got %r.'
-                         % found_vendor_id)
-
-    def testHALDeviceVendorIdScsiAta(self):
-        """Test of HALDevice.vendor_id for SCSI devices: fake IDE/SATA disks.
-        """
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SCSI_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'scsi.vendor': ('ATA', 'str'),
-                    'scsi.model': ('Hitachi HTS54161', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_vendor_id = parser.devices[self.UDI_SCSI_DISK].vendor_id
-        self.assertEqual(found_vendor_id, 'Hitachi',
-                         'Unexpected result of HWDevice.vendor_id for a. '
-                         'fake SCSI device. Expected Hitachi, got %r.'
-                         % found_vendor_id)
-
-    def testHALDeviceVendorIdSystem(self):
-        """Test of HALDevice.vendor_id for the machine itself."""
-        # HAL does not provide the property info.vendor_id for the
-        # root UDI /org/freedesktop/Hal/devices/computer. We use
-        # HALDevice.vendor instead.
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {
-                    'info.bus': ('unknown', 'str'),
-                    'system.hardware.vendor': ('FUJITSU SIEMENS', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_vendor_id = parser.devices[self.UDI_COMPUTER].vendor_id
-        self.assertEqual(found_vendor_id, 'FUJITSU SIEMENS',
-                         'Unexpected result of HWDevice.vendor_id for a '
-                         'system. Expected FUJITSU SIEMENS, got %r.'
-                         % found_vendor_id)
-
-    def testHALDeviceProductId(self):
-        """Test of HALDevice.product_id.
-
-        Many buses have a numerical product ID. Except for the special
-        cases tested below, HWDevice.product_id returns the HAL property
-        ${bus}.product_id.
-        """
-        devices = [
-             {
-                'id': 1,
-                'udi': self.UDI_SATA_CONTROLLER,
-                'properties': {
-                    'info.bus': ('pci', 'str'),
-                    'pci.product_id': (0x27c5, 'int'),
-                    },
-                },
-             ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_product_id = parser.devices[self.UDI_SATA_CONTROLLER].product_id
-        self.assertEqual(found_product_id, 0x27c5,
-                         'Unexpected result of HWDevice.product_id. '
-                         'Expected 0x27c5, got 0x%x.'
-                         % found_product_id)
-
-    def testHALDeviceProductIdScsi(self):
-        """Test of HALDevice.product_id for SCSI devices.
-
-        The SCSI specification does not know about a product ID,
-        we use the product string as returned by INQUIRY command
-        as the ID.
-        """
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SCSI_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'scsi.vendor': ('SEAGATE', 'str'),
-                    'scsi.model': ('ST36530N', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_product_id = parser.devices[self.UDI_SCSI_DISK].product_id
-        self.assertEqual(found_product_id, 'ST36530N',
-                         'Unexpected result of HWDevice.product_id for a. '
-                         'SCSI device. Expected ST35630N, got %r.'
-                         % found_product_id)
-
-    def testHALDeviceProductIdScsiAta(self):
-        """Test of HALDevice.product_id for SCSI devices: fake IDE/SATA disks.
-        """
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SCSI_DISK,
-                'properties': {
-                    'info.bus': ('scsi', 'str'),
-                    'scsi.vendor': ('ATA', 'str'),
-                    'scsi.model': ('Hitachi HTS54161', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_product_id = parser.devices[self.UDI_SCSI_DISK].product_id
-        self.assertEqual(found_product_id, 'HTS54161',
-                         'Unexpected result of HWDevice.product_id for a. '
-                         'fake SCSI device. Expected HTS54161, got %r.'
-                         % found_product_id)
-
-    def testHALDeviceProductIdSystem(self):
-        """Test of HALDevice.product_id for the machine itself."""
-        # HAL does not provide info.product_id for the root UDI
-        # /org/freedesktop/Hal/devices/computer. We use
-        # HALDevice.product instead.
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {
-                    'info.bus': ('unknown', 'str'),
-                    'system.hardware.product': ('LIFEBOOK E8210', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_product_id = parser.devices[self.UDI_COMPUTER].product_id
-        self.assertEqual(found_product_id, 'LIFEBOOK E8210',
-                         'Unexpected result of HWDevice.product_id for a '
-                         'system. Expected LIFEBOOK E8210, got %r.'
-                         % found_product_id)
-
-    def testVendorIDForDB(self):
-        """Test of HALDevice.vendor_id_for_db."""
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SATA_DISK,
-                'properties': {},
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        properties = devices[0]['properties']
-        parser = SubmissionParser(self.log)
-        # SCSI vendor names have a length of exactly 8 bytes; we use
-        # this format for HWDevice.bus_product_id too.
-        testdata = (('pci', (0x123, 'int'), '0x0123'),
-                    ('usb_device', (0x234, 'int'), '0x0234'),
-                    ('scsi', ('SEAGATE', 'str'), 'SEAGATE '),
-                    )
-        for bus, vendor_id, expected_vendor_id in testdata:
-            properties['info.bus'] = (bus, 'str')
-            if bus == 'scsi':
-                properties['%s.vendor' % bus] = vendor_id
-            else:
-                properties['%s.vendor_id' % bus] = vendor_id
-            parser.buildHalDeviceList(parsed_data)
-            found_vendor_id = parser.devices[
-                self.UDI_SATA_DISK].vendor_id_for_db
-            self.assertEqual(found_vendor_id, expected_vendor_id,
-                'Unexpected result of HWDevice.vendor_id_for_db for bus '
-                '"%s". Expected %r, got %r.'
-                % (bus, expected_vendor_id, found_vendor_id))
-
-    def testVendorIDForDBSystem(self):
-        """Test of HALDevice.vendor_id_for_db."""
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {
-                    'system.hardware.vendor': ('FUJITSU SIEMENS', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_vendor_id = parser.devices[self.UDI_COMPUTER].vendor_id_for_db
-        self.assertEqual(found_vendor_id, 'FUJITSU SIEMENS',
-            'Unexpected result of HWDevice.vendor_id_for_db for system. '
-            'Expected FUJITSU SIEMENS, got %r.' % found_vendor_id)
-
-    def testProductIDForDB(self):
-        """Test of HALDevice.product_id_for_db."""
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_SATA_DISK,
-                'properties': {},
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        properties = devices[0]['properties']
-        parser = SubmissionParser(self.log)
-        # SCSI product names (called "model" in the SCSI specifications)
-        # have a length of exactly 16 bytes; we use this format for
-        # HWDevice.bus_product_id too.
-        testdata = (('pci', (0x123, 'int'), '0x0123'),
-                    ('usb_device', (0x234, 'int'), '0x0234'),
-                    ('scsi', ('ST1234567890', 'str'), 'ST1234567890    '),
-                   )
-        for bus, product_id, expected_product_id in testdata:
-            properties['info.bus'] = (bus, 'str')
-            if bus == 'scsi':
-                properties['%s.model' % bus] = product_id
-            else:
-                properties['%s.product_id' % bus] = product_id
-            parser.buildHalDeviceList(parsed_data)
-            found_product_id = parser.devices[
-                self.UDI_SATA_DISK].product_id_for_db
-            self.assertEqual(found_product_id, expected_product_id,
-                'Unexpected result of HWDevice.product_id_for_db for bus '
-                '"%s". Expected %r, got %r.'
-                % (bus, expected_product_id, found_product_id))
-
-    def testProductIDForDBSystem(self):
-        """Test of HALDevice.product_id_for_db."""
-        devices = [
-            {
-                'id': 1,
-                'udi': self.UDI_COMPUTER,
-                'properties': {
-                    'system.hardware.product': ('E8210', 'str'),
-                    },
-                },
-            ]
-        parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': devices,
-                    },
-                },
-            }
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(parsed_data)
-        found_product_id = parser.devices[self.UDI_COMPUTER].product_id_for_db
-        self.assertEqual(found_product_id, 'E8210',
-            'Unexpected result of HWDevice.product_id_for_db for system. '
-            'Expected FUJITSU SIEMENS, got %r.' % found_product_id)
-
-
-class TestHALDeviceUSBDevices(TestCaseHWDB):
-    """Tests for HALDevice.is_real_device: USB devices."""
-
-    def setUp(self):
-        """Setup the test environment."""
-        super(TestHALDeviceUSBDevices, self).setUp()
-        self.usb_controller_pci_side = {
-            'id': 1,
-            'udi': self.UDI_USB_CONTROLLER_PCI_SIDE,
-            'properties': {
-                'info.bus': ('pci', 'str'),
-                'pci.device_class': (PCI_CLASS_SERIALBUS_CONTROLLER, 'int'),
-                'pci.device_subclass': (PCI_SUBCLASS_SERIALBUS_USB, 'int'),
-                },
-            }
-        self.usb_controller_usb_side = {
-            'id': 2,
-            'udi': self.UDI_USB_CONTROLLER_USB_SIDE,
-            'properties': {
-                'info.parent': (self.UDI_USB_CONTROLLER_PCI_SIDE, 'str'),
-                'info.bus': ('usb_device', 'str'),
-                'usb_device.vendor_id': (0, 'int'),
-                'usb_device.product_id': (0, 'int'),
-                },
-            }
-        self.usb_storage_device = {
-            'id': 3,
-            'udi': self.UDI_USB_STORAGE,
-            'properties': {
-                'info.parent': (self.UDI_USB_CONTROLLER_USB_SIDE, 'str'),
-                'info.bus': ('usb_device', 'str'),
-                'usb_device.vendor_id': (self.USB_VENDOR_ID_USBEST, 'int'),
-                'usb_device.product_id': (self.USB_PROD_ID_USBBEST_MEMSTICK,
-                                          'int'),
-                },
-            }
-        self.parsed_data = {
-            'hardware': {
-                'hal': {
-                    'devices': [
-                        self.usb_controller_pci_side,
-                        self.usb_controller_usb_side,
-                        self.usb_storage_device,
-                        ],
-                    },
-                },
-            }
-
-    def renameInfoBusToInfoSubsystem(self):
-        for device in self.parsed_data['hardware']['hal']['devices']:
-            properties = device['properties']
-            properties['info.subsystem'] = properties['info.bus']
-            del properties['info.bus']
-
-    def testUSBDeviceRegularCase(self):
-        """Test of HALDevice.is_real_device: info.bus == 'usb_device'."""
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(self.parsed_data)
-        device = parser.devices[self.UDI_USB_STORAGE]
-        self.assertTrue(
-            device.is_real_device,
-            'Testing info.bus property: Regular USB Device not treated '
-            'as a real device.')
-
-        self.renameInfoBusToInfoSubsystem()
-        parser.buildHalDeviceList(self.parsed_data)
-        device = parser.devices[self.UDI_USB_STORAGE]
-        self.assertTrue(
-            device.is_real_device,
-            'Testing info.subsystem property: Regular USB Device not treated '
-            'as a real device.')
-
-    def testUSBHostController(self):
-        """Test of HALDevice.is_real_device: info.bus == 'usb_device'.
-
-        Special case: vendor ID and product ID of the device are zero;
-        the parent device is a PCI/USB host controller.
-        """
-
-        parser = SubmissionParser(self.log)
-        parser.buildHalDeviceList(self.parsed_data)
-        device = parser.devices[self.UDI_USB_CONTROLLER_USB_SIDE]
-        self.assertFalse(
-            device.is_real_device,
-            'Testing info.bus property: USB Device with vendor/product '
-            'ID 0:0 property treated as a real device.')
-
-        self.renameInfoBusToInfoSubsystem()
-        parser.buildHalDeviceList(self.parsed_data)
-        device = parser.devices[self.UDI_USB_CONTROLLER_USB_SIDE]
-        self.assertFalse(
-            device.is_real_device,
-            'Testing info.subsystem property: USB Device with vendor/product '
-            'ID 0:0 property treated as a real device.')
-
-    def testUSBHostControllerInvalidParentClass(self):
-        """Test of HALDevice.is_real_device: info.bus == 'usb_device'.
-
-        Special case: vendor ID and product ID of the device are zero;
-        the parent device cannot be identified as a PCI/USB host
-        controller: Wrong PCI device class of the parent device.
-        """
-        parent_properties = self.usb_controller_pci_side['properties']
-        parent_properties['pci.device_class'] = (PCI_CLASS_STORAGE, 'int')
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'USB device test 1'
-        parser.buildHalDeviceList(self.parsed_data)
-        device = parser.devices[self.UDI_USB_CONTROLLER_USB_SIDE]
-        self.assertFalse(
-            device.is_real_device,
-            'Testing info.bus property: USB Device with vendor/product '
-            'ID 0:0 property treated as a real device.')
-        self.assertWarningMessage(
-            parser.submission_key,
-            'USB device found with vendor ID==0, product ID==0, where the '
-            'parent device does not look like a USB host controller: '
-            + self.UDI_USB_CONTROLLER_USB_SIDE)
-
-        self.renameInfoBusToInfoSubsystem()
-        parser.buildHalDeviceList(self.parsed_data)
-        device = parser.devices[self.UDI_USB_CONTROLLER_USB_SIDE]
-        self.assertFalse(
-            device.is_real_device,
-            'Testing info.subsystem property: USB Device with vendor/product '
-            'ID 0:0 property treated as a real device.')
-        self.assertWarningMessage(
-            parser.submission_key,
-            'USB device found with vendor ID==0, product ID==0, where the '
-            'parent device does not look like a USB host controller: '
-            + self.UDI_USB_CONTROLLER_USB_SIDE)
-
-    def testUSBHostControllerInvalidParentSubClass(self):
-        """Test of HALDevice.is_real_device: info.bus == 'usb_device'.
-
-        Special case: vendor ID and product ID of the device are zero;
-        the parent device cannot be identified as a PCI/USB host
-        controller: Wrong PCI device subclass of the parent device.
-        """
-        parent_properties = self.usb_controller_pci_side['properties']
-        parent_properties['pci.device_subclass'] = (1, 'int')
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'USB device test 2'
-        parser.buildHalDeviceList(self.parsed_data)
-        device = parser.devices[self.UDI_USB_CONTROLLER_USB_SIDE]
-        self.assertFalse(
-            device.is_real_device,
-            'Testing info.bus property: USB Device with vendor/product '
-            'ID 0:0 property treated as a real device.')
-        self.assertWarningMessage(
-            parser.submission_key,
-            'USB device found with vendor ID==0, product ID==0, where the '
-            'parent device does not look like a USB host controller: '
-            + self.UDI_USB_CONTROLLER_USB_SIDE)
-
-        self.renameInfoBusToInfoSubsystem()
-        parser.buildHalDeviceList(self.parsed_data)
-        device = parser.devices[self.UDI_USB_CONTROLLER_USB_SIDE]
-        self.assertFalse(
-            device.is_real_device,
-            'Testing info.subsystem property: USB Device with vendor/product '
-            'ID 0:0 property treated as a real device.')
-        self.assertWarningMessage(
-            parser.submission_key,
-            'USB device found with vendor ID==0, product ID==0, where the '
-            'parent device does not look like a USB host controller: '
-            + self.UDI_USB_CONTROLLER_USB_SIDE)
-
-    def testUSBHostControllerUnexpectedParentBus(self):
-        """Test of HALDevice.is_real_device: info.bus == 'usb_device'.
-
-        Special case: vendor ID and product ID of the device are zero;
-        the parent device cannot be identified as a PCI/USB host
-        controller: Wrong bus of the parent device.
-        """
-        parent_properties = self.usb_controller_pci_side['properties']
-        parent_properties['info.bus'] = ('not pci', 'str')
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'USB device test 3'
-        parser.buildHalDeviceList(self.parsed_data)
-        device = parser.devices[self.UDI_USB_CONTROLLER_USB_SIDE]
-        self.assertFalse(
-            device.is_real_device,
-            'Testing info.bus property: USB Device with vendor/product '
-            'ID 0:0 property treated as a real device.')
-        self.assertWarningMessage(
-            parser.submission_key,
-            'USB device found with vendor ID==0, product ID==0, where the '
-            'parent device does not look like a USB host controller: '
-            + self.UDI_USB_CONTROLLER_USB_SIDE)
-
-        # All other devices which have an info.bus property return True
-        # for HALDevice.is_real_device. The USB host controller in the
-        # test data is an example.
-        device = parser.devices[self.UDI_USB_CONTROLLER_PCI_SIDE]
-        self.assertTrue(
-            device.is_real_device,
-            'Testing info.bus property: Device with existing info.bus '
-            'property not treated as a real device.')
-
-        self.renameInfoBusToInfoSubsystem()
-        parser.buildHalDeviceList(self.parsed_data)
-        device = parser.devices[self.UDI_USB_CONTROLLER_USB_SIDE]
-        self.assertFalse(
-            device.is_real_device,
-            'Testing info.subsystem property: USB Device with vendor/product '
-            'ID 0:0 property treated as a real device.')
-        self.assertWarningMessage(
-            parser.submission_key,
-            'USB device found with vendor ID==0, product ID==0, where the '
-            'parent device does not look like a USB host controller: '
-            + self.UDI_USB_CONTROLLER_USB_SIDE)
-
-        device = parser.devices[self.UDI_USB_CONTROLLER_PCI_SIDE]
-        self.assertTrue(
-            device.is_real_device,
-            'Testing info.subsystem property: Device with existing info.bus '
-            'property not treated as a real device.')
-
-
-class TestUdevDevice(TestCaseHWDB):
-    """Tests of class UdevDevice."""
-
-    def setUp(self):
-        """Setup the test environment."""
-        super(TestUdevDevice, self).setUp()
-        self.root_device = {
-            'P': '/devices/LNXSYSTM:00',
-            'E': {
-                'UDEV_LOG': '3',
-                'DEVPATH': '/devices/LNXSYSTM:00',
-                'MODALIAS': 'acpi:LNXSYSTM:',
-                'SUBSYSTEM': 'acpi',
-                },
-            'id': 1,
-            }
-
-        self.root_device_dmi_data = {
-            '/sys/class/dmi/id/sys_vendor': 'FUJITSU SIEMENS',
-            '/sys/class/dmi/id/product_name': 'LIFEBOOK E8210',
-            }
-
-        self.usb_device_data = {
-            'P': '/devices/pci0000:00/0000:00:1d.1/usb3/3-2',
-            'E': {
-                'SUBSYSTEM': 'usb',
-                'DEVTYPE': 'usb_device',
-                'PRODUCT': '46d/a01/1013',
-                'TYPE': '0/0/0',
-                'DRIVER': 'usb',
-                },
-            }
-
-        self.pci_pccard_bridge_path = (
-            '/devices/pci0000:00/0000:00:1e.0/0000:08:03.0')
-        self.pci_pccard_bridge = {
-            'P': self.pci_pccard_bridge_path,
-            'E': {
-                'DRIVER': 'yenta_cardbus',
-                'PCI_CLASS': '60700',
-                'PCI_ID': '1217:7134',
-                'PCI_SUBSYS_ID': '10CF:131E',
-                'PCI_SLOT_NAME': '0000:08:03.0',
-                'SUBSYSTEM': 'pci',
-                }
-            }
-
-        self.pccard_scsi_controller_path = (
-            '/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/0000:09:00.0')
-        self.pccard_scsi_controller_data = {
-            'P': self.pccard_scsi_controller_path,
-            'E': {
-                'DRIVER': 'aic7xxx',
-                'PCI_CLASS': '10000',
-                'PCI_ID': '9004:6075',
-                'PCI_SUBSYS_ID': '9004:7560',
-                'SUBSYSTEM': 'pci',
-                },
-            }
-
-        self.pci_scsi_controller_scsi_side_1 = {
-            'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/'
-                  '0000:09:00.0/host6'),
-            'E': {
-                'DEVTYPE': 'scsi_host',
-                'SUBSYSTEM': 'scsi',
-                },
-            }
-
-        self.pci_bridge_pccard_hierarchy_data = [
-            {'udev_data': self.root_device},
-            {'udev_data': self.pci_pccard_bridge},
-            {'udev_data': self.pccard_scsi_controller_data},
-            ]
-
-        self.pci_scsi_controller_scsi_side_2 = {
-            'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/'
-                  '0000:09:00.0/host6/scsi_host/host6'),
-            'E': {
-                'SUBSYSTEM': 'scsi_host',
-                },
-            }
-
-        self.scsi_scanner_target_data = {
-            'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/'
-                  '0000:09:00.0/host6/target6:0:1'),
-            'E': {
-                'DEVTYPE': 'scsi_target',
-                'SUBSYSTEM': 'scsi'
-                },
-            }
-
-        self.scsi_scanner_device_path = (
-            '/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/0000:09:00.0/'
-            'host6/target6:0:1/6:0:1:0')
-        self.scsi_scanner_device_data = {
-            'P': self.scsi_scanner_device_path,
-            'E': {
-                'DEVTYPE': 'scsi_device',
-                'SUBSYSTEM': 'scsi',
-                },
-            }
-
-        self.scsi_scanner_device_sysfs_data = {
-            'vendor': 'FUJITSU',
-            'model': 'fi-5120Cdj',
-            'type': '6',
-            }
-
-        self.scsi_scanner_device_data_2 = {
-            'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/'
-                  '0000:09:00.0/host6/target6:0:1/6:0:1:0/scsi_device/'
-                  '6:0:1:0'),
-            'E': {
-                'SUBSYSTEM': 'scsi_device',
-                },
-        }
-
-        self.scsi_scanner_scsi_generic = {
-            'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/'
-                  '0000:09:00.0/host6/target6:0:1/6:0:1:0/scsi_generic/sg2'),
-            'E': {
-                'SUBSYSTEM': 'scsi_generic',
-                },
-            }
-
-        self.scsi_scanner_spi = {
-            'P': ('/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/'
-                  '0000:09:00.0/host6/target6:0:1/spi_transport/target6:0:1'),
-            'E': {
-                'SUBSYSTEM': 'spi_transport',
-                },
-            }
-
-        self.scsi_device_hierarchy_data = [
-            {'udev_data': self.pccard_scsi_controller_data},
-            {'udev_data': self.pci_scsi_controller_scsi_side_1},
-            {'udev_data': self.pci_scsi_controller_scsi_side_2},
-            {'udev_data': self.scsi_scanner_target_data},
-            {
-                'udev_data': self.scsi_scanner_device_data,
-                'sysfs_data': self.scsi_scanner_device_sysfs_data,
-                },
-            {'udev_data': self.scsi_scanner_device_data_2},
-            {'udev_data': self.scsi_scanner_scsi_generic},
-            {'udev_data': self.scsi_scanner_spi},
-            ]
-
-        self.pci_ide_controller_path = '/devices/pci0000:00/0000:00:1f.1'
-        self.pci_ide_controller = {
-            'P': self.pci_ide_controller_path,
-            'E': {
-                'DRIVER': 'ata_piix',
-                'PCI_CLASS': '1018A',
-                'PCI_ID': '8086:27DF',
-                'PCI_SUBSYS_ID': '10CF:1385',
-                'SUBSYSTEM': 'pci',
-                },
-            }
-
-        self.pci_ide_controller_scsi_side_1 = {
-            'P': '/devices/pci0000:00/0000:00:1f.1/host4',
-            'E': {
-                'DEVTYPE': 'scsi_host',
-                'SUBSYSTEM': 'scsi',
-                },
-            }
-
-        self.pci_ide_controller_scsi_side_2 = {
-            'P': '/devices/pci0000:00/0000:00:1f.1/host4/scsi_host/host4',
-            'E': {
-                'SUBSYSTEM': 'scsi_host',
-                },
-            }
-
-        self.ide_device_target_data = {
-            'P': '/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0',
-            'E': {
-                'DEVTYPE': 'scsi_target',
-                'SUBSYSTEM': 'scsi',
-                },
-            }
-
-        self.ide_cdrom_device_path = (
-            '/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/4:0:0:0')
-        self.ide_cdrom_device_data = {
-             'P': self.ide_cdrom_device_path,
-             'E': {
-                 'SUBSYSTEM': 'scsi',
-                 'DEVTYPE': 'scsi_device',
-                 'DRIVER': 'sr',
-                 },
-             }
-
-        self.ide_cdrom_device_sysfs_data = {
-             'vendor': 'MATSHITA',
-             'model': 'DVD-RAM UJ-841S',
-             'type': '5',
-             }
-
-        self.ide_cdrom_sr_data = {
-            'P': ('/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/'
-                  '4:0:0:0/block/sr0'),
-            'E': {
-                'DEVTYPE': 'disk',
-                'SUBSYSTEM': 'block',
-                },
-            }
-
-        self.ide_cdrom_device_data_2 = {
-            'P': ('/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/'
-                  '4:0:0:0/scsi_device/4:0:0:0'),
-            'E': {
-                'SUBSYSTEM': 'scsi_device',
-                },
-            }
-
-        self.ide_cdrom_scsi_generic_data = {
-            'P': ('/devices/pci0000:00/0000:00:1f.1/host4/target4:0:0/'
-                  '4:0:0:0/scsi_generic/sg1'),
-            'E': {
-                'SUBSYSTEM': 'scsi_generic',
-                },
-            }
-
-        self.ide_device_hierarchy_data = [
-            {'udev_data': self.pci_ide_controller},
-            {'udev_data': self.pci_ide_controller_scsi_side_1},
-            {'udev_data': self.pci_ide_controller_scsi_side_2},
-            {'udev_data': self.ide_device_target_data},
-            {
-                'udev_data': self.ide_cdrom_device_data,
-                'sysfs_data': self.ide_cdrom_device_sysfs_data,
-                },
-            {'udev_data': self.ide_cdrom_sr_data},
-            {'udev_data': self.ide_cdrom_device_data_2},
-            {'udev_data': self.ide_cdrom_scsi_generic_data},
-            ]
-
-        self.pci_sata_controller_path = '/devices/pci0000:00/0000:00:1f.2'
-        self.pci_sata_controller = {
-            'P': self.pci_sata_controller_path,
-            'E': {
-                'PCI_CLASS': '10602',
-                'PCI_ID': '8086:27C5',
-                'PCI_SUBSYS_ID': '10CF:1387',
-                'PCI_SLOT_NAME': '0000:00:1f.2',
-                'SUBSYSTEM': 'pci',
-                'DRIVER': 'ahci',
-                }
-            }
-
-        self.pci_sata_controller_scsi_side_1 = {
-            'P': '/devices/pci0000:00/0000:00:1f.2/host0',
-            'E': {
-                'DEVTYPE': 'scsi_host',
-                'SUBSYSTEM': 'scsi',
-                },
-            }
-
-        self.pci_sata_controller_scsi_side_2 = {
-            'P': '/devices/pci0000:00/0000:00:1f.2/host0/scsi_host/host0',
-            'E': {
-                'SUBSYSTEM': 'scsi_host',
-                },
-            }
-
-        self.sata_disk_target_data = {
-            'P': '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0',
-            'E': {
-                'DEVTYPE': 'scsi_target',
-                'SUBSYSTEM': 'scsi',
-                },
-            }
-
-        self.sata_disk_device_path = (
-            '/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0')
-        self.sata_disk_device_data = {
-            'P': self.sata_disk_device_path,
-            'E': {
-                'DEVTYPE': 'scsi_device',
-                'DRIVER': 'sd',
-                'SUBSYSTEM': 'scsi',
-                },
-            }
-
-        self.sata_disk_device_sysfs_data = {
-            'vendor': 'ATA',
-            'model': 'Hitachi HTS54251',
-            'type': '0',
-            }
-
-        self.sata_disk_device_data_2 = {
-            'P': ('/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/'
-                  '0:0:0:0/scsi_device/0:0:0:0'),
-            'E': {
-                'SUBSYSTEM': 'scsi_device',
-                },
-            }
-
-        self.sata_disk_device_scsi_disk_data = {
-            'P': ('/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/'
-                  '0:0:0:0/scsi_disk/0:0:0:0'),
-            'E': {
-                'SUBSYSTEM': 'scsi_disk',
-                },
-            }
-
-        self.sata_disk_device_scsi_generic_data = {
-            'P': ('/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/'
-                  '0:0:0:0/scsi_generic/sg0'),
-            'E': {
-                'SUBSYSTEM': 'scsi_generic'
-                },
-            }
-
-        self.sata_disk_block_data = {
-            'P': ('/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/'
-                  '0:0:0:0/block/sda'),
-            'E': {
-                'DEVTYPE': 'disk',
-                'SUBSYSTEM': 'block',
-                },
-            }
-
-        self.sata_disk_partition_data = {
-            'P': ('/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/'
-                  '0:0:0:0/block/sda/sda1'),
-            'E': {
-                'DEVTYPE': 'partition',
-                'SUBSYSTEM': 'block',
-                },
-            }
-
-        self.sata_device_hierarchy_data = [
-            {'udev_data': self.pci_sata_controller},
-            {'udev_data': self.pci_sata_controller_scsi_side_1},
-            {'udev_data': self.pci_sata_controller_scsi_side_2},
-            {'udev_data': self.sata_disk_target_data},
-            {'udev_data': self.sata_disk_device_data},
-            {
-                'udev_data': self.sata_disk_device_data,
-                'sysfs_data': self.sata_disk_device_sysfs_data,
-                },
-            {'udev_data': self.sata_disk_device_data_2},
-            {'udev_data': self.sata_disk_device_scsi_disk_data},
-            {'udev_data': self.sata_disk_device_scsi_generic_data},
-            {'udev_data': self.sata_disk_block_data},
-            {'udev_data': self.sata_disk_partition_data},
-             ]
-
-        self.usb_storage_usb_device_path = (
-            '/devices/pci0000:00/0000:00:1d.7/usb1/1-1')
-        self.usb_storage_usb_device_data = {
-            'P': self.usb_storage_usb_device_path,
-            'E': {
-                'DEVTYPE': 'usb_device',
-                'DRIVER': 'usb',
-                'PRODUCT': '1307/163/100',
-                'TYPE': '0/0/0',
-                'SUBSYSTEM': 'usb',
-                },
-            }
-
-        self.usb_storage_usb_interface = {
-            'P': '/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0',
-            'E': {
-                'DRIVER': 'usb-storage',
-                'PRODUCT': '1307/163/100',
-                'TYPE': '0/0/0',
-                'INTERFACE': '8/6/80',
-                'DEVTYPE': 'usb_interface',
-                'SUBSYSTEM': 'usb',
-                },
-            }
-
-        self.usb_storage_scsi_host_1 = {
-            'P': '/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7',
-            'E': {
-                'DEVTYPE': 'scsi_host',
-                'SUBSYSTEM': 'scsi',
-                },
-            }
-
-        self.usb_storage_scsi_host_2 = {
-            'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
-                  'scsi_host/host7'),
-            'E': {
-                'SUBSYSTEM': 'scsi_host',
-                },
-            }
-
-        self.usb_storage_scsi_target = {
-            'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
-                  'target7:0:0'),
-            'E': {
-                'DEVTYPE': 'scsi_target',
-                'SUBSYSTEM': 'scsi',
-                },
-            }
-
-        self.usb_storage_scsi_device_path = (
-            '/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
-            'target7:0:0/7:0:0:0')
-        self.usb_storage_scsi_device = {
-            'P': self.usb_storage_scsi_device_path,
-            'E': {
-                'DEVTYPE': 'scsi_device',
-                'DRIVER': 'sd',
-                'SUBSYSTEM': 'scsi',
-                },
-            }
-
-        self.usb_storage_scsi_device_sysfs = {
-            'vendor': 'Ut163',
-            'model': 'USB2FlashStorage',
-            'type': '0',
-            }
-
-        self.usb_storage_scsi_device_2 = {
-            'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
-                  'target7:0:0/7:0:0:0/scsi_device/7:0:0:0'),
-            'E': {
-                'SUBSYSTEM': 'scsi_device',
-                },
-            }
-
-        self.usb_storage_scsi_disk = {
-            'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
-                  'target7:0:0/7:0:0:0/scsi_disk/7:0:0:0'),
-            'E': {
-                'SUBSYSTEM': 'scsi_disk',
-                },
-            }
-
-        self.usb_storage_scsi_generic = {
-            'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
-                  'target7:0:0/7:0:0:0/scsi_generic/sg3'),
-            'E': {
-                'SUBSYSTEM': 'scsi_generic',
-                },
-            }
-
-        self.usb_storage_block_device_data = {
-            'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
-                  'target7:0:0/7:0:0:0/block/sdb'),
-            'E': {
-                'DEVTYPE': 'disk',
-                'SUBSYSTEM': 'block',
-                },
-            }
-
-        self.usb_storage_block_partition_data = {
-            'P': ('/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/host7/'
-                  'target7:0:0/7:0:0:0/block/sdb/sdb1'),
-            'E': {
-                'DEVTYPE': 'partition',
-                'SUBSYSTEM': 'block',
-                },
-            }
-
-        self.usb_storage_hierarchy_data = [
-            {'udev_data': self.usb_storage_usb_device_data},
-            {'udev_data': self.usb_storage_usb_interface},
-            {'udev_data': self.usb_storage_scsi_host_1},
-            {'udev_data': self.usb_storage_scsi_host_2},
-            {'udev_data': self.usb_storage_scsi_target},
-            {
-                'udev_data': self.usb_storage_scsi_device,
-                'sysfs_data': self.usb_storage_scsi_device_sysfs,
-                },
-            {'udev_data': self.usb_storage_scsi_device_2},
-            {'udev_data': self.usb_storage_scsi_disk},
-            {'udev_data': self.usb_storage_scsi_generic},
-            {'udev_data': self.usb_storage_block_device_data},
-            {'udev_data': self.usb_storage_block_partition_data},
-            ]
-
-        self.usb_hub_path = '/devices/pci0000:00/0000:00:1d.0/usb2'
-        self.usb_hub = {
-            'P': self.usb_hub_path,
-            'E': {
-                'DEVTYPE': 'usb_device',
-                'DRIVER': 'usb',
-                'PRODUCT': '0/0/0',
-                'TYPE': '9/0/0',
-                'SUBSYSTEM': 'usb',
-                },
-            }
-
-        self.usb_hub_with_odd_parent_hierarchy_data = [
-            {'udev_data': self.root_device},
-            {'udev_data': self.usb_hub},
-            ]
-
-        self.no_subsystem_device_data = {
-            'P': '/devices/pnp0/00:00',
-            'E': {}
-            }
-
-        self.cpu_device_data = {
-            'P': '/devices/LNXSYSTM:00/LNXCPU:00',
-            'E': {
-                'DRIVER': 'processor',
-                'SUBSYSTEM': 'acpi',
-                },
-            }
-
-        self.platform_device_data = {
-            'P': '/devices/platform/dock.0',
-            'E': {
-                'SUBSYSTEM': 'platform',
-                },
-            }
-
-    def test_device_device_id(self):
-        """Test of UdevDevice.device_id."""
-        device = UdevDevice(None, self.pci_sata_controller)
-        self.assertEqual(
-            '/devices/pci0000:00/0000:00:1f.2', device.device_id,
-            'Unexpected value of UdevDevice.device_id.')
-
-    def test_root_device_ids(self):
-        device = UdevDevice(
-            None, self.root_device, None, self.root_device_dmi_data)
-        self.assertEqual(
-            {
-                'vendor': 'FUJITSU SIEMENS',
-                'product': 'LIFEBOOK E8210',
-                },
-            device.root_device_ids)
-
-        device = UdevDevice(
-            None, self.root_device, None, {})
-        self.assertEqual(
-            {
-                'vendor': None,
-                'product': None,
-                },
-            device.root_device_ids)
-
-    def test_is_pci(self):
-        """Test of UdevDevice.is_pci."""
-        device = UdevDevice(None, self.pci_sata_controller)
-        self.assertTrue(device.is_pci)
-
-        device = UdevDevice(None, self.root_device)
-        self.assertFalse(device.is_pci)
-
-    def test_pci_class_info(self):
-        """Test of UdevDevice.pci_class_info"""
-        device = UdevDevice(None, self.pci_sata_controller)
-        self.assertEqual(
-            (1, 6, 2), device.pci_class_info,
-            'Invalid value of UdevDevice.pci_class_info for PCI device.')
-
-        device = UdevDevice(None, self.root_device)
-        self.assertEqual(
-            (None, None, None), device.pci_class_info,
-            'Invalid value of UdevDevice.pci_class_info for Non-PCI device.')
-
-    def test_pci_class(self):
-        """Test of UdevDevice.pci_class"""
-        device = UdevDevice(None, self.pci_sata_controller)
-        self.assertEqual(
-            1, device.pci_class,
-            'Invalid value of UdevDevice.pci_class for PCI device.')
-
-        device = UdevDevice(None, self.root_device)
-        self.assertEqual(
-            None, device.pci_class,
-            'Invalid value of UdevDevice.pci_class for Non-PCI device.')
-
-    def test_pci_subclass(self):
-        """Test of UdevDevice.pci_subclass"""
-        device = UdevDevice(None, self.pci_sata_controller)
-        self.assertEqual(
-            6, device.pci_subclass,
-            'Invalid value of UdevDevice.pci_class for PCI device.')
-
-        device = UdevDevice(None, self.root_device)
-        self.assertEqual(
-            None, device.pci_class,
-            'Invalid value of UdevDevice.pci_class for Non-PCI device.')
-
-    def test_pci_ids(self):
-        """Test of UdevDevice.pci_ids"""
-        device = UdevDevice(None, self.pci_sata_controller)
-        self.assertEqual(
-            {'vendor': 0x8086,
-             'product': 0x27C5,
-             },
-            device.pci_ids,
-            'Invalid value of UdevDevice.pci_ids for PCI device.')
-
-        device = UdevDevice(None, self.usb_device_data)
-        self.assertEqual(
-            {'vendor': None,
-             'product': None,
-             },
-            device.pci_ids,
-            'Invalid value of UdevDevice.pci_ids for Non-PCI device.')
-
-    def test_is_usb(self):
-        """Test of UdevDevice.is_usb"""
-        device = UdevDevice(None, self.usb_device_data)
-        self.assertTrue(device.is_usb)
-
-        device = UdevDevice(None, self.pci_sata_controller)
-        self.assertFalse(device.is_usb)
-
-    def test_usb_ids(self):
-        """Test of UdevDevice.usb_ids"""
-        device = UdevDevice(None, self.usb_device_data)
-        self.assertEqual(
-            {
-                'vendor': 0x46d,
-                'product': 0xa01,
-                'version': 0x1013,
-                },
-            device.usb_ids,
-            'Invalid value of UdevDevice.usb_ids for USB device.')
-
-        device = UdevDevice(None, self.root_device)
-        self.assertEqual(
-            {
-                'vendor': None,
-                'product': None,
-                'version': None,
-                },
-            device.usb_ids,
-            'Invalid value of UdevDevice.usb_ids for Non-USB device.')
-
-    def test_usb_vendor_id(self):
-        """Test of UdevDevice.usb_vendor_id"""
-        device = UdevDevice(None, self.usb_device_data)
-        self.assertEqual(
-            0x46d, device.usb_vendor_id,
-            'Invalid value of UdevDevice.usb_vendor_id for USB device.')
-
-        device = UdevDevice(None, self.root_device)
-        self.assertEqual(
-            None, device.usb_vendor_id,
-            'Invalid value of UdevDevice.usb_vendor_id for Non-USB device.')
-
-    def test_usb_product_id(self):
-        """Test of UdevDevice.usb_product_id"""
-        device = UdevDevice(None, self.usb_device_data)
-        self.assertEqual(
-            0xa01, device.usb_product_id,
-            'Invalid value of UdevDevice.usb_product_id for USB device.')
-
-        device = UdevDevice(None, self.root_device)
-        self.assertEqual(
-            None, device.usb_product_id,
-            'Invalid value of UdevDevice.usb_product_id for Non-USB device.')
-
-    def test_is_scsi_device(self):
-        """Test of UdevDevice.is_scsi_device."""
-        device = UdevDevice(
-            None, self.scsi_scanner_device_data,
-            self.scsi_scanner_device_sysfs_data)
-        self.assertTrue(device.is_scsi_device)
-
-        device = UdevDevice(None, self.root_device)
-        self.assertFalse(device.is_scsi_device)
-
-    def test_is_scsi_device__no_sysfs_data(self):
-        """Test of UdevDevice.is_scsi_device.
-
-        If there is no sysfs data for a real SCSI device, is it not
-        considered as a real SCSI device.
-
-        Reason: Without sysfs data, we don't know the vendor and
-        model name, making it impossible to store data about the
-        device in the database.
-        """
-        device = UdevDevice(
-            None, self.scsi_scanner_device_data, None)
-        self.assertFalse(device.is_scsi_device)
-
-    def test_scsi_vendor(self):
-        """Test of UdevDevice.scsi_vendor."""
-        device = UdevDevice(
-            None, self.scsi_scanner_device_data,
-            self.scsi_scanner_device_sysfs_data)
-        self.assertEqual('FUJITSU', device.scsi_vendor)
-        device = UdevDevice(None, self.root_device)
-        self.assertEqual(None, device.scsi_vendor)
-
-    def test_scsi_model(self):
-        """Test of UdevDevice.scsi_model."""
-        device = UdevDevice(
-            None, self.scsi_scanner_device_data,
-            self.scsi_scanner_device_sysfs_data)
-        self.assertEqual('fi-5120Cdj', device.scsi_model)
-
-        device = UdevDevice(None, self.root_device)
-        self.assertEqual(None, device.scsi_model)
-
-    def test_raw_bus(self):
-        """Test of UdevDevice.raw_bus."""
-        device = UdevDevice(None, self.root_device)
-        self.assertEqual(None, device.raw_bus)
-
-        device = UdevDevice(None, self.pci_sata_controller)
-        self.assertEqual('pci', device.raw_bus)
-
-        device = UdevDevice(None, self.usb_device_data)
-        self.assertEqual('usb_device', device.raw_bus)
-
-        device = UdevDevice(None, self.no_subsystem_device_data)
-        self.assertEqual(None, device.raw_bus)
-
-    def test_is_root_device(self):
-        """Test of UdevDevice.is_root_device."""
-        device = UdevDevice(None, self.root_device)
-        self.assertTrue(device.is_root_device)
-
-        device = UdevDevice(None, self.pci_sata_controller)
-        self.assertFalse(device.is_root_device)
-
-    def test_getVendorOrProduct(self):
-        """Test of UdevDevice.getVendorOrProduct()."""
-        device = UdevDevice(
-            None, self.root_device, None, self.root_device_dmi_data)
-        self.assertEqual(
-            'FUJITSU SIEMENS', device.getVendorOrProduct('vendor'))
-        self.assertEqual(
-            'LIFEBOOK E8210', device.getVendorOrProduct('product'))
-        self.assertRaises(
-            AssertionError, device.getVendorOrProduct, 'nonsense')
-
-        device = UdevDevice(None, self.pci_sata_controller)
-        self.assertEqual('Unknown', device.getVendorOrProduct('vendor'))
-        self.assertEqual('Unknown', device.getVendorOrProduct('product'))
-
-        device = UdevDevice(None, self.usb_device_data)
-        self.assertEqual('Unknown', device.getVendorOrProduct('vendor'))
-        self.assertEqual('Unknown', device.getVendorOrProduct('product'))
-
-        device = UdevDevice(
-            None, self.scsi_scanner_device_data,
-            self.scsi_scanner_device_sysfs_data)
-        self.assertEqual('FUJITSU', device.getVendorOrProduct('vendor'))
-        self.assertEqual('fi-5120Cdj', device.getVendorOrProduct('product'))
-
-        device = UdevDevice(None, self.no_subsystem_device_data)
-        self.assertEqual(None, device.getVendorOrProduct('vendor'))
-        self.assertEqual(None, device.getVendorOrProduct('product'))
-
-    def test_vendor(self):
-        """Test of UdevDevice.vendor."""
-        device = UdevDevice(
-            None, self.root_device, None, self.root_device_dmi_data)
-        self.assertEqual('FUJITSU SIEMENS', device.vendor)
-
-    def test_product(self):
-        """Test of UdevDevice.product."""
-        device = UdevDevice(
-            None, self.root_device, None, self.root_device_dmi_data)
-        self.assertEqual('LIFEBOOK E8210', device.product)
-
-    def test_getVendorOrProductID(self):
-        """Test of UdevDevice.getVendorOrProduct()."""
-        device = UdevDevice(
-            None, self.root_device, None, self.root_device_dmi_data)
-        self.assertEqual(
-            'FUJITSU SIEMENS', device.getVendorOrProductID('vendor'))
-        self.assertEqual(
-            'LIFEBOOK E8210', device.getVendorOrProductID('product'))
-        self.assertRaises(
-            AssertionError, device.getVendorOrProductID, 'nonsense')
-
-        device = UdevDevice(None, self.pci_sata_controller)
-        self.assertEqual(0x8086, device.getVendorOrProductID('vendor'))
-        self.assertEqual(0x27C5, device.getVendorOrProductID('product'))
-
-        device = UdevDevice(None, self.usb_device_data)
-        self.assertEqual(0x46d, device.getVendorOrProductID('vendor'))
-        self.assertEqual(0xa01, device.getVendorOrProductID('product'))
-
-        device = UdevDevice(
-            None, self.scsi_scanner_device_data,
-            self.scsi_scanner_device_sysfs_data)
-        self.assertEqual('FUJITSU', device.getVendorOrProductID('vendor'))
-        self.assertEqual('fi-5120Cdj', device.getVendorOrProductID('product'))
-
-        device = UdevDevice(
-            None, self.no_subsystem_device_data)
-        self.assertEqual(None, device.getVendorOrProductID('vendor'))
-        self.assertEqual(None, device.getVendorOrProductID('product'))
-
-    def test_vendor_id(self):
-        """Test of UdevDevice.vendor_id."""
-        device = UdevDevice(
-            None, self.root_device, None, self.root_device_dmi_data)
-        self.assertEqual('FUJITSU SIEMENS', device.vendor_id)
-
-    def test_product_id(self):
-        """Test of UdevDevice.product_id."""
-        device = UdevDevice(
-            None, self.root_device, None, self.root_device_dmi_data)
-        self.assertEqual('LIFEBOOK E8210', device.product_id)
-
-    def test_vendor_id_for_db(self):
-        """Test of UdevDevice.vendor_id_for_db."""
-        device = UdevDevice(
-            None, self.root_device, None, self.root_device_dmi_data)
-        self.assertEqual('FUJITSU SIEMENS', device.vendor_id_for_db)
-
-        device = UdevDevice(None, self.pci_sata_controller)
-        self.assertEqual('0x8086', device.vendor_id_for_db)
-
-        device = UdevDevice(None, self.usb_device_data)
-        self.assertEqual('0x046d', device.vendor_id_for_db)
-
-        device = UdevDevice(
-            None, self.scsi_scanner_device_data,
-            self.scsi_scanner_device_sysfs_data)
-        self.assertEqual('FUJITSU ', device.vendor_id_for_db)
-
-    def test_product_id_for_db(self):
-        """Test of UdevDevice.product_id_for_db."""
-        device = UdevDevice(
-            None, self.root_device, None, self.root_device_dmi_data)
-        self.assertEqual('LIFEBOOK E8210', device.product_id_for_db)
-
-        device = UdevDevice(None, self.pci_sata_controller)
-        self.assertEqual('0x27c5', device.product_id_for_db)
-
-        device = UdevDevice(None, self.usb_device_data)
-        self.assertEqual('0x0a01', device.product_id_for_db)
-
-        device = UdevDevice(
-            None, self.scsi_scanner_device_data,
-            self.scsi_scanner_device_sysfs_data)
-        self.assertEqual('fi-5120Cdj      ', device.product_id_for_db)
-
-    def test_driver_name(self):
-        """Test of UdevDevice.driver_name."""
-        device = UdevDevice(None, self.pci_sata_controller)
-        self.assertEqual('ahci', device.driver_name)
-
-        device = UdevDevice(
-            None, self.root_device, None, self.root_device_dmi_data)
-        self.assertEqual(None, device.driver_name)
-
-    def buildUdevDeviceHierarchy(self, device_data, parser=None):
-        """Build a UdevDevice hierarchy from device_data.
-
-        :param device_data: A sequence of arguments that are passed
-            to the UdevDevice constructor. Each element must be a
-            dictionary that can be used as a **kwargs argument.
-
-            Element N of the sequence is the parent of element N+1.
-        :param parser: A SubmissionParser instance to be passed to
-            the constructor of UdevDevice.
-        """
-        devices = {}
-        for kwargs in device_data:
-            device = UdevDevice(parser, **kwargs)
-            devices[device.device_id] = device
-
-        # Build the parent-child relations so that the parent device
-        # is that device which has the longest path matching the
-        # start of the child's path.
-        #
-        # There is one exception of this rule: The root device has
-        # the path "/devices/LNXSYSTM:00", but the paths of most of
-        # our test deviies start with "/devices/pci". Well patch the
-        # index temporarily in order to find children of the root
-        # device.
-        if '/devices/LNXSYSTM:00' in devices:
-            devices['/devices'] = devices['/devices/LNXSYSTM:00']
-            del devices['/devices/LNXSYSTM:00']
-
-        device_paths = sorted(devices, key=len, reverse=True)
-        for path_index, path in enumerate(device_paths):
-            for parent_path in device_paths[path_index + 1:]:
-                if path.startswith(parent_path):
-                    devices[parent_path].addChild(devices[path])
-                    break
-        if '/devices' in devices:
-            devices['/devices/LNXSYSTM:00'] = devices['/devices']
-            del devices['/devices']
-        return devices
-
-    def test_scsi_controller(self):
-        """Test of UdevDevice.scsi_controller for a PCI controller."""
-        devices = self.buildUdevDeviceHierarchy(
-            self.scsi_device_hierarchy_data)
-        controller = devices[self.pccard_scsi_controller_path]
-        scsi_device = devices[self.scsi_scanner_device_path]
-        self.assertEqual(controller, scsi_device.scsi_controller)
-
-    def test_scsi_controller_insufficient_anchestors(self):
-        """Test of UdevDevice.scsi_controller for a PCI controller.
-
-        If a SCSI device does not have a sufficient number of ancestors,
-        UdevDevice.scsi_controller returns None.
-        """
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'UdevDevice.scsi_controller ancestor missing'
-        devices = self.buildUdevDeviceHierarchy(
-            self.scsi_device_hierarchy_data[1:], parser)
-        scsi_device = devices[self.scsi_scanner_device_path]
-        self.assertEqual(None, scsi_device.scsi_controller)
-        self.assertWarningMessage(
-            parser.submission_key,
-            'Found a SCSI device without a sufficient number of ancestors: '
-            '/devices/pci0000:00/0000:00:1e.0/0000:08:03.0/0000:09:00.0/'
-            'host6/target6:0:1/6:0:1:0')
-
-    def test_scsi_controller_no_scsi_device(self):
-        """Test of UdevDevice.scsi_controller for a PCI controller.
-
-        For non-SCSI devices, this property is None.
-        """
-        device = UdevDevice(None, self.pci_sata_controller)
-        self.assertEqual(None, device.scsi_controller)
-
-    def test_translateScsiBus_real_scsi_device(self):
-        """Test of UdevDevice.translateScsiBus() with a real SCSI device."""
-        devices = self.buildUdevDeviceHierarchy(
-            self.scsi_device_hierarchy_data)
-        scsi_device = devices[self.scsi_scanner_device_path]
-        self.assertEqual(
-            HWBus.SCSI, scsi_device.translateScsiBus())
-
-    def test_translateScsiBus_ide_device(self):
-        """Test of UdevDevice.translateScsiBus() with an IDE device."""
-        devices = self.buildUdevDeviceHierarchy(
-            self.ide_device_hierarchy_data)
-        ide_device = devices[self.ide_cdrom_device_path]
-        self.assertEqual(HWBus.IDE, ide_device.translateScsiBus())
-
-    def test_translateScsiBus_usb_device(self):
-        """Test of UdevDevice.translateScsiBus() with a USB device."""
-        devices = self.buildUdevDeviceHierarchy(
-            self.usb_storage_hierarchy_data)
-        usb_scsi_device = devices[self.usb_storage_scsi_device_path]
-        self.assertEqual(None, usb_scsi_device.translateScsiBus())
-
-    def test_translateScsiBus_non_scsi_device(self):
-        """Test of UdevDevice.translateScsiBus() for a non-SCSI device."""
-        device = UdevDevice(None, self.root_device)
-        self.assertEqual(None, device.translateScsiBus())
-
-    def test_translatePciBus(self):
-        """Test of UdevDevice.translatePciBus()."""
-        devices = self.buildUdevDeviceHierarchy(
-            self.pci_bridge_pccard_hierarchy_data)
-        pci_device = devices[self.pci_pccard_bridge_path]
-        pccard_device = devices[self.pccard_scsi_controller_path]
-        self.assertEqual(HWBus.PCI, pci_device.translatePciBus())
-        self.assertEqual(HWBus.PCCARD, pccard_device.translatePciBus())
-
-    def test_real_bus_usb_device(self):
-        """Test of UdevDevice.real_bus for a USB device."""
-        usb_device = UdevDevice(None, self.usb_device_data)
-        self.assertEqual(HWBus.USB, usb_device.real_bus)
-
-    def test_real_bus_usb_interface(self):
-        """Test of UdevDevice.real_bus for a USB interface."""
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'UdevDevice.real_bus for a not-real device'
-        usb_interface = UdevDevice(parser, self.usb_storage_usb_interface)
-        self.assertEqual(None, usb_interface.real_bus)
-        # UdevDevice.real_bus should only be accessed for real devices,
-        # which a USB is not. Hence we get a warning.
-        self.assertWarningMessage(
-            parser.submission_key,
-            "Unknown bus u'usb_interface' for device "
-            "/devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0")
-
-    def test_real_bus_pci(self):
-        """Test of UdevDevice.real_bus for PCI devices."""
-        devices = self.buildUdevDeviceHierarchy(
-            self.pci_bridge_pccard_hierarchy_data)
-        pci_device = devices[self.pci_pccard_bridge_path]
-        pccard_device = devices[self.pccard_scsi_controller_path]
-        self.assertEqual(HWBus.PCI, pci_device.real_bus)
-        self.assertEqual(HWBus.PCCARD, pccard_device.real_bus)
-
-    def test_real_bus_scsi(self):
-        """Test of UdevDevice.real_bus for a SCSI device."""
-        devices = self.buildUdevDeviceHierarchy(
-            self.scsi_device_hierarchy_data)
-        scsi_device = devices[self.scsi_scanner_device_path]
-        self.assertEqual(HWBus.SCSI, scsi_device.real_bus)
-
-    def test_real_bus_system(self):
-        """Test of UdevDevice.real_bus for a system."""
-        root_device = UdevDevice(None, self.root_device)
-        self.assertEqual(HWBus.SYSTEM, root_device.real_bus)
-
-    def test_is_real_device_root_device(self):
-        """Test of UdevDevice._is_real_device for the root device."""
-        root_device = UdevDevice(None, self.root_device)
-        self.assertTrue(root_device.is_real_device)
-
-    def test_is_real_device_pci_device(self):
-        """Test of UdevDevice._is_real_device for a PCI device."""
-        pci_device = UdevDevice(None, self.pci_sata_controller)
-        self.assertTrue(pci_device.is_real_device)
-
-    def test_is_real_device_scsi_device_related_nodes(self):
-        """Test of UdevDevice._is_real_device for SCSI related nodes.
-
-        A SCSI device and its controller are represented by several
-        nodes which describe different aspects. Only the controller
-        itself and the node representing the SCSI device are
-        considered to be real devices.
-        """
-        devices = self.buildUdevDeviceHierarchy(
-            self.scsi_device_hierarchy_data)
-        real_devices = (
-            self.pccard_scsi_controller_path, self.scsi_scanner_device_path
-            )
-        for device in devices.values():
-            self.assertEqual(
-                device.device_id in real_devices, device.is_real_device,
-                'Invalid result of UdevDevice.is_real_device for %s '
-                'Expected %s, got %s'
-                % (device.device_id, device.device_id in real_devices,
-                   device.is_real_device))
-
-    def test_is_real_device_ide_device_related_nodes(self):
-        """Test of UdevDevice._is_real_device for IDE related nodes.
-
-        An IDE device and its controller are represented by several
-        nodes which describe different aspects. Only the controller
-        itself and the node representing the IDE device are
-        considered to be real devices.
-        """
-        devices = self.buildUdevDeviceHierarchy(
-            self.ide_device_hierarchy_data)
-        real_devices = (
-            self.pci_ide_controller_path, self.ide_cdrom_device_path,
-            )
-        for device in devices.values():
-            self.assertEqual(
-                device.device_id in real_devices, device.is_real_device,
-                'Invalid result of UdevDevice.is_real_device for %s '
-                'Expected %s, got %s'
-                % (device.device_id, device.device_id in real_devices,
-                   device.is_real_device))
-
-    def test_is_real_device_ata_device_related_nodes(self):
-        """Test of UdevDevice._is_real_device for IDE related nodes.
-
-        An IDE device and its controller are represented by several
-        nodes which describe different aspects. Only the controller
-        itself and the node representing the IDE device are
-        considered to be real devices.
-        """
-        devices = self.buildUdevDeviceHierarchy(
-            self.sata_device_hierarchy_data)
-        real_devices = (
-            self.pci_sata_controller_path, self.sata_disk_device_path,
-            )
-        for device in devices.values():
-            self.assertEqual(
-                device.device_id in real_devices, device.is_real_device,
-                'Invalid result of UdevDevice.is_real_device for %s '
-                'Expected %s, got %s'
-                % (device.device_id, device.device_id in real_devices,
-                   device.is_real_device))
-
-    def test_is_real_device_usb_storage_device_related_nodes(self):
-        """Test of UdevDevice._is_real_device for USB storage related nodes.
-
-        A USB storage device is represented by several nodes which
-        describe different aspects. Only the main USB device is
-        considered to be real devices.
-        """
-        devices = self.buildUdevDeviceHierarchy(
-            self.usb_storage_hierarchy_data)
-        for device in devices.values():
-            self.assertEqual(
-                device.device_id == self.usb_storage_usb_device_path,
-                device.is_real_device,
-                'Invalid result of UdevDevice.is_real_device for %s '
-                'Expected %s, got %s'
-                % (device.device_id,
-                   device.device_id == self.usb_storage_usb_device_path,
-                   device.is_real_device))
-
-    def test_is_real_device_usb_hub_with_odd_parent(self):
-        """Test of UdevDevice._is_real_device for USB storage related nodes.
-
-        If called for USB hub node with vendor ID == 0 and product_id == 0
-        which is not the child of a PCI device, we get a warning.
-        """
-        parser = SubmissionParser(self.log)
-        parser.submission_key = (
-            'UdevDevice.is_real_device, USB hub with odd parent.')
-        devices = self.buildUdevDeviceHierarchy(
-            self.usb_hub_with_odd_parent_hierarchy_data, parser)
-        usb_hub = devices[self.usb_hub_path]
-        self.assertFalse(usb_hub.is_real_device)
-        self.assertWarningMessage(
-            parser.submission_key,
-            'USB device found with vendor ID==0, product ID==0, '
-            'where the parent device does not look like a USB '
-            'host controller: %s' % self.usb_hub_path)
-
-    def test_has_reliable_data_system(self):
-        """Test of UdevDevice.has_reliable_data for a system."""
-        root_device = UdevDevice(
-            None, self.root_device, dmi_data=self.root_device_dmi_data)
-        self.assertTrue(root_device.has_reliable_data)
-
-    def test_has_reliable_data_system_no_vendor_name(self):
-        """Test of UdevDevice.has_reliable_data for a system.
-
-        If the DMI data does not provide vendor name, has_reliable_data
-        is False.
-        """
-        del self.root_device_dmi_data['/sys/class/dmi/id/sys_vendor']
-        root_device = UdevDevice(
-            None, self.root_device, dmi_data=self.root_device_dmi_data)
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'root device without vendor name'
-        root_device.parser = parser
-        self.assertFalse(root_device.has_reliable_data)
-        self.assertWarningMessage(
-            parser.submission_key,
-            "A UdevDevice that is supposed to be a real device does not "
-            "provide bus, vendor ID, product ID or product name: "
-            "<DBItem HWBus.SYSTEM, (0) System> None u'LIFEBOOK E8210' "
-            "u'LIFEBOOK E8210' /devices/LNXSYSTM:00")
-
-    def test_has_reliable_data_system_no_product_name(self):
-        """Test of UdevDevice.has_reliable_data for a system.
-
-        If the DMI data does not provide product name, has_reliable_data
-        is False.
-        """
-        del self.root_device_dmi_data['/sys/class/dmi/id/product_name']
-        root_device = UdevDevice(
-            None, self.root_device, dmi_data=self.root_device_dmi_data)
-        parser = SubmissionParser(self.log)
-        parser.submission_key = 'root device without product name'
-        root_device.parser = parser
-        self.assertFalse(root_device.has_reliable_data)
-        self.assertWarningMessage(
-            parser.submission_key,
-            "A UdevDevice that is supposed to be a real device does not "
-            "provide bus, vendor ID, product ID or product name: "
-            "<DBItem HWBus.SYSTEM, (0) System> u'FUJITSU SIEMENS' None None "
-            "/devices/LNXSYSTM:00")
-
-    def test_has_reliable_data_acpi_device(self):
-        """Test of UdevDevice.has_reliable_data for an ACPI device.
-
-        APCI devices are considered not to have reliable data. The only
-        exception is the root device, see test_has_reliable_data_system.
-        """
-        acpi_device = UdevDevice(None, self.cpu_device_data)
-        self.assertEqual('acpi', acpi_device.raw_bus)
-        self.assertFalse(acpi_device.has_reliable_data)
-
-    def test_has_reliable_data_platform_device(self):
-        """Test of UdevDevice.has_reliable_data for a "platform" device.
-
-        devices with raw_bus == 'platform' are considered not to have
-        reliable data.
-        """
-        platform_device = UdevDevice(None, self.platform_device_data)
-        self.assertFalse(platform_device.has_reliable_data)
-
-    def test_has_reliable_data_pci_device(self):
-        """Test of UdevDevice.has_reliable_data for a PCI device."""
-        devices = self.buildUdevDeviceHierarchy(
-            self.pci_bridge_pccard_hierarchy_data)
-        pci_device = devices[self.pci_pccard_bridge_path]
-        self.assertTrue(pci_device.has_reliable_data)
-
-    def test_has_reliable_data_usb_device(self):
-        """Test of UdevDevice.has_reliable_data for a USB device."""
-        usb_device = UdevDevice(None, self.usb_storage_usb_device_data)
-        self.assertTrue(usb_device.has_reliable_data)
-
-    def test_has_reliable_data_scsi_device(self):
-        """Test of UdevDevice.has_reliable_data for a SCSI device."""
-        devices = self.buildUdevDeviceHierarchy(
-            self.scsi_device_hierarchy_data)
-        scsi_device = devices[self.scsi_scanner_device_path]
-        self.assertTrue(scsi_device.has_reliable_data)
-
-    def test_has_reliable_data_usb_interface_device(self):
-        """Test of UdevDevice.has_reliable_data for a USB interface.
-
-        UdevDevice.has_reliable_data should only be called for nodes
-        where is_rel_device is True. If called for other nodes, we
-        may get a warning because they do not provide reqired data,
-        like a bus, vendor or product ID.
-        """
-        parser = SubmissionParser(self.log)
-        parser.submission_key = (
-            'UdevDevice.has_reliable_data for a USB interface')
-        usb_interface = UdevDevice(parser, self.usb_storage_usb_interface)
-        self.assertFalse(usb_interface.has_reliable_data)
-        self.assertWarningMessage(
-            parser.submission_key,
-            'A UdevDevice that is supposed to be a real device does not '
-            'provide bus, vendor ID, product ID or product name: None None '
-            'None None /devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0')
-
-    def test_warnings_not_suppressed(self):
-        """Logging of warnings can be allowed."""
-        parser = SubmissionParser(self.log)
-        parser.submission_key = "log_with_warnings"
-        parser._logWarning("This message is logged.")
-        self.assertWarningMessage(
-            parser.submission_key, "This message is logged.")
-
-    def test_warnings_suppressed(self):
-        """Logging of warnings can be suppressed."""
-        number_of_existing_log_messages = len(self.handler.records)
-        parser = SubmissionParser(self.log, record_warnings=False)
-        parser.submission_key = "log_without_warnings"
-        parser._logWarning("This message is not logged.")
-        # No new warnings are recorded
-        self.assertEqual(
-            number_of_existing_log_messages, len(self.handler.records))
-
-    def test_device_id(self):
-        """Each UdevDevice has a property 'id'."""
-        device = UdevDevice(None, self.root_device)
-        self.assertEqual(1, device.id)
-
-
-class TestHWDBSubmissionTablePopulation(TestCaseHWDB):
-    """Tests of the HWDB popoluation with submitted data."""
-
-    layer = LaunchpadZopelessLayer
-
-    HAL_COMPUTER = {
-        'id': 1,
-        'udi': TestCaseHWDB.UDI_COMPUTER,
-        'properties': {
-            'system.hardware.vendor': ('Lenovo', 'str'),
-            'system.hardware.product': ('T41', 'str'),
-            'system.kernel.version': (TestCaseHWDB.KERNEL_VERSION, 'str'),
-            },
-        }
-
-    HAL_PCI_PCCARD_BRIDGE = {
-        'id': 2,
-        'udi': TestCaseHWDB.UDI_PCI_PCCARD_BRIDGE,
-        'properties': {
-            'info.bus': ('pci', 'str'),
-            'info.linux.driver': ('yenta_cardbus', 'str'),
-            'info.parent': (TestCaseHWDB.UDI_COMPUTER, 'str'),
-            'info.product': ('OZ711MP1/MS1 MemoryCardBus Controller', 'str'),
-            'pci.device_class': (PCI_CLASS_BRIDGE, 'int'),
-            'pci.device_subclass': (PCI_SUBCLASS_BRIDGE_CARDBUS, 'int'),
-            'pci.vendor_id': (TestCaseHWDB.PCI_VENDOR_ID_INTEL, 'int'),
-            'pci.product_id': (TestCaseHWDB.PCI_PROD_ID_PCI_PCCARD_BRIDGE,
-                               'int'),
-            },
-        }
-
-    HAL_PCCARD_DEVICE = {
-        'id': 3,
-        'udi': TestCaseHWDB.UDI_PCCARD_DEVICE,
-        'properties': {
-            'info.bus': ('pci', 'str'),
-            'info.parent': (TestCaseHWDB.UDI_PCI_PCCARD_BRIDGE, 'str'),
-            'info.product': ('ISL3890/ISL3886', 'str'),
-            'pci.device_class': (PCI_CLASS_SERIALBUS_CONTROLLER, 'int'),
-            'pci.device_subclass': (PCI_SUBCLASS_SERIALBUS_USB, 'int'),
-            'pci.vendor_id': (TestCaseHWDB.PCI_VENDOR_ID_INTEL, 'int'),
-            'pci.product_id': (TestCaseHWDB.PCI_PROD_ID_PCCARD_DEVICE, 'int'),
-            },
-        }
-
-    HAL_USB_CONTROLLER_PCI_SIDE = {
-        'id': 4,
-        'udi': TestCaseHWDB.UDI_USB_CONTROLLER_PCI_SIDE,
-        'properties': {
-            'info.bus': ('pci', 'str'),
-            'info.linux.driver': ('ehci_hcd', 'str'),
-            'info.parent': (TestCaseHWDB.UDI_COMPUTER, 'str'),
-            'info.product': ('82801G (ICH7 Family) USB2 EHCI Controller',
-                             'str'),
-            'pci.device_class': (PCI_CLASS_SERIALBUS_CONTROLLER, 'int'),
-            'pci.device_subclass': (PCI_SUBCLASS_SERIALBUS_USB, 'int'),
-            'pci.vendor_id': (TestCaseHWDB.PCI_VENDOR_ID_INTEL, 'int'),
-            'pci.product_id': (TestCaseHWDB.PCI_PROD_ID_USB_CONTROLLER,
-                               'int'),
-            },
-        }
-
-    HAL_USB_CONTROLLER_USB_SIDE = {
-        'id': 5,
-        'udi': TestCaseHWDB.UDI_USB_CONTROLLER_USB_SIDE,
-        'properties': {
-            'info.bus': ('usb_device', 'str'),
-            'info.linux.driver': ('usb', 'str'),
-            'info.parent': (TestCaseHWDB.UDI_USB_CONTROLLER_PCI_SIDE, 'str'),
-            'info.product': ('EHCI Host Controller', 'str'),
-            'usb_device.vendor_id': (0, 'int'),
-            'usb_device.product_id': (0, 'int'),
-            },
-        }
-
-    HAL_USB_STORAGE_DEVICE = {
-        'id': 6,
-        'udi': TestCaseHWDB.UDI_USB_STORAGE,
-        'properties': {
-            'info.bus': ('usb_device', 'str'),
-            'info.linux.driver': ('usb', 'str'),
-            'info.parent': (TestCaseHWDB.UDI_USB_CONTROLLER_USB_SIDE, 'str'),
-            'info.product': ('USB Mass Storage Device', 'str'),
-            'usb_device.vendor_id': (TestCaseHWDB.USB_VENDOR_ID_USBEST,
-                                     'int'),
-            'usb_device.product_id': (
-                TestCaseHWDB.USB_PROD_ID_USBBEST_MEMSTICK, 'int'),
-            },
-        }
-
-    HAL_SCSI_CONTROLLER_PCI_SIDE = {
-        'id': 7,
-        'udi': TestCaseHWDB.UDI_SCSI_CONTROLLER_PCI_SIDE,
-        'properties': {
-            'info.bus': ('pci', 'str'),
-            'info.linux.driver': ('aic7xxx', 'str'),
-            'info.parent': (TestCaseHWDB.UDI_COMPUTER, 'str'),
-            'info.product': ('AIC-1480 / APA-1480', 'str'),
-            'pci.device_class': (PCI_CLASS_STORAGE, 'int'),
-            'pci.device_subclass': (TestCaseHWDB.PCI_SUBCLASS_STORAGE_SCSI,
-                                    'int'),
-            'pci.vendor_id': (TestCaseHWDB.PCI_VENDOR_ID_ADAPTEC, 'int'),
-            'pci.product_id': (TestCaseHWDB.PCI_PROD_ID_AIC1480, 'int'),
-            },
-        }
-
-    HAL_SCSI_CONTROLLER_SCSI_SIDE = {
-        'id': 8,
-        'udi': TestCaseHWDB.UDI_SCSI_CONTROLLER_SCSI_SIDE,
-        'properties': {
-            'info.bus': ('scsi_host', 'str'),
-            'info.parent': (TestCaseHWDB.UDI_SCSI_CONTROLLER_PCI_SIDE, 'str'),
-            'info.linux.driver': ('sd', 'str'),
-            }
-        }
-
-    HAL_SCSI_STORAGE_DEVICE = {
-        'id': 9,
-        'udi': TestCaseHWDB.UDI_SCSI_DISK,
-        'properties': {
-            'info.bus': ('scsi', 'str'),
-            'info.linux.driver': ('sd', 'str'),
-            'info.parent': (TestCaseHWDB.UDI_SCSI_CONTROLLER_SCSI_SIDE,
-                            'str'),
-            'scsi.vendor': ('WDC', 'str'),
-            'scsi.model': ('WD12345678', 'str'),
-            },
-        }
-
-    parsed_data = {
-        'hardware': {
-            'hal': {},
-            },
-        'software': {
-            'packages': {
-                TestCaseHWDB.KERNEL_PACKAGE: {},
-                },
-            },
-        }
-
-    def setUp(self):
-        """Setup the test environment."""
-        super(TestHWDBSubmissionTablePopulation, self).setUp()
-        self.log = logging.getLogger('test_hwdb_submission_parser')
-        self.log.setLevel(logging.INFO)
-        self.handler = Handler(self)
-        self.handler.add(self.log.name)
-        switch_dbuser('hwdb-submission-processor')
-
-    def getLogData(self):
-        messages = [record.getMessage() for record in self.handler.records]
-        return '\n'.join(messages)
-
-    def setHALDevices(self, devices):
-        self.parsed_data['hardware']['hal']['devices'] = devices
-
-    def testGetDriverNoDriverInfo(self):
-        """Test of HALDevice.getDriver()."""
-        devices = [
-            self.HAL_COMPUTER,
-            ]
-        self.setHALDevices(devices)
-        parser = SubmissionParser(self.log)
-        parser.buildDeviceList(self.parsed_data)
-        device = parser.devices[self.UDI_COMPUTER]
-        self.assertEqual(device.getDriver(), None,
-            'HALDevice.getDriver found a driver where none is expected.')
-
-    def testGetDriverWithDriverInfo(self):
-        """Test of HALDevice.getDriver()."""
-        devices = [
-            self.HAL_COMPUTER,
-            self.HAL_PCI_PCCARD_BRIDGE,
-            ]
-        self.setHALDevices(devices)
-        parser = SubmissionParser(self.log)
-        parser.parsed_data = self.parsed_data
-        parser.buildDeviceList(self.parsed_data)
-        device = parser.devices[self.UDI_PCI_PCCARD_BRIDGE]
-        driver = device.getDriver()
-        self.assertNotEqual(driver, None,
-            'HALDevice.getDriver did not find a driver where one '
-            'is expected.')
-        self.assertEqual(driver.name, 'yenta_cardbus',
-            'Unexpected result for driver.name. Got %r, expected '
-            'yenta_cardbus.' % driver.name)
-        self.assertEqual(driver.package_name, self.KERNEL_PACKAGE,
-            'Unexpected result for driver.package_name. Got %r, expected '
-            'linux-image-2.6.24-19-generic' % driver.name)
-
-    def testEnsureVendorIDVendorNameExistsRegularCase(self):
-        """Test of ensureVendorIDVendorNameExists(self), regular case."""
-        devices = [
-            self.HAL_COMPUTER,
-            ]
-        self.setHALDevices(devices)
-        parser = SubmissionParser(self.log)
-        parser.parsed_data = self.parsed_data
-        parser.buildDeviceList(self.parsed_data)
-
-        # The database does not know yet about the vendor name
-        # 'Lenovo'...
-        vendor_name_set = getUtility(IHWVendorNameSet)
-        vendor_name = vendor_name_set.getByName('Lenovo')
-        self.assertEqual(vendor_name, None,
-                         'Expected None looking up vendor name "Lenovo" in '
-                         'HWVendorName, got %r.' % vendor_name)
-
-        # ...as well as the vendor ID (which is identical to the vendor
-        # name for systems).
-        vendor_id_set = getUtility(IHWVendorIDSet)
-        vendor_id = vendor_id_set.getByBusAndVendorID(HWBus.SYSTEM, 'Lenovo')
-        self.assertEqual(vendor_id, None,
-                         'Expected None looking up vendor ID "Lenovo" in '
-                         'HWVendorID, got %r.' % vendor_id)
-
-        # HALDevice.ensureVendorIDVendorNameExists() creates these
-        # records.
-        hal_system = parser.devices[self.UDI_COMPUTER]
-        hal_system.ensureVendorIDVendorNameExists()
-
-        vendor_name = vendor_name_set.getByName('Lenovo')
-        self.assertEqual(vendor_name.name, 'Lenovo',
-                         'Expected to find vendor name "Lenovo" in '
-                         'HWVendorName, got %r.' % vendor_name.name)
-
-        vendor_id = vendor_id_set.getByBusAndVendorID(HWBus.SYSTEM, 'Lenovo')
-        self.assertEqual(vendor_id.vendor_id_for_bus, 'Lenovo',
-                         'Expected "Lenovo" as vendor_id_for_bus, '
-                         'got %r.' % vendor_id.vendor_id_for_bus)
-        self.assertEqual(vendor_id.bus, HWBus.SYSTEM,
-                         'Expected HWBUS.SYSTEM as bus, got %s.'
-                         % vendor_id.bus.title)
-
-    def runTestEnsureVendorIDVendorNameExistsVendorNameUnknown(
-        self, devices, test_bus, test_vendor_id, test_udi):
-        """Test of ensureVendorIDVendorNameExists(self), special case.
-
-        A HWVendorID record is not created by
-        HALDevice.ensureVendorIDVendorNameExists for certain buses.
-        """
-        self.setHALDevices(devices)
-        parser = SubmissionParser(self.log)
-        parser.parsed_data = self.parsed_data
-        parser.buildDeviceList(self.parsed_data)
-
-        hal_device = parser.devices[test_udi]
-        hal_device.ensureVendorIDVendorNameExists()
-
-        vendor_id_set = getUtility(IHWVendorIDSet)
-        vendor_id = vendor_id_set.getByBusAndVendorID(
-            test_bus, test_vendor_id)
-        self.assertEqual(vendor_id, None,
-            'Expected None looking up vendor ID %s for bus %s in HWVendorID, '
-            'got %r.' % (test_vendor_id, test_bus.title, vendor_id))
-
-    def testEnsureVendorIDVendorNameExistsVendorPCI(self):
-        """Test of ensureVendorIDVendorNameExists(self), PCI bus."""
-        devices = [
-            self.HAL_COMPUTER,
-            self.HAL_PCI_PCCARD_BRIDGE
-            ]
-        self.runTestEnsureVendorIDVendorNameExistsVendorNameUnknown(
-            devices, HWBus.PCI, '0x8086', self.UDI_PCI_PCCARD_BRIDGE)
-
-    def testEnsureVendorIDVendorNameExistsVendorPCCARD(self):
-        """Test of ensureVendorIDVendorNameExists(self), PCCARD bus."""
-        devices = [
-            self.HAL_COMPUTER,
-            self.HAL_PCI_PCCARD_BRIDGE,
-            self.HAL_PCCARD_DEVICE,
-            ]
-        self.runTestEnsureVendorIDVendorNameExistsVendorNameUnknown(
-            devices, HWBus.PCCARD, '0x8086', self.UDI_PCCARD_DEVICE)
-
-    def testEnsureVendorIDVendorNameExistVendorUSB(self):
-        """Test of ensureVendorIDVendorNameExists(self), USB bus."""
-        devices = [
-            self.HAL_COMPUTER,
-            self.HAL_USB_CONTROLLER_PCI_SIDE,
-            self.HAL_USB_CONTROLLER_USB_SIDE,
-            self.HAL_USB_STORAGE_DEVICE,
-            ]
-        self.runTestEnsureVendorIDVendorNameExistsVendorNameUnknown(
-            devices, HWBus.USB, '0x1307', self.UDI_USB_STORAGE)
-
-    def testEnsureVendorIDVendorNameExistVendorSCSI(self):
-        """Test of ensureVendorIDVendorNameExists(self), SCSI bus."""
-        devices = [
-            self.HAL_COMPUTER,
-            self.HAL_SCSI_CONTROLLER_PCI_SIDE,
-            self.HAL_SCSI_CONTROLLER_SCSI_SIDE,
-            self.HAL_SCSI_STORAGE_DEVICE,
-            ]
-
-        self.setHALDevices(devices)
-        parser = SubmissionParser(self.log)
-        parser.parsed_data = self.parsed_data
-        parser.buildDeviceList(self.parsed_data)
-
-        # The database does not know yet about the vendor name
-        # 'WDC'...
-        vendor_name_set = getUtility(IHWVendorNameSet)
-        vendor_name = vendor_name_set.getByName('WDC')
-        self.assertEqual(vendor_name, None,
-                         'Expected None looking up vendor name "WDC" in '
-                         'HWVendorName, got %r.' % vendor_name)
-
-        # ...as well as the vendor ID (which is identical to the vendor
-        # name for SCSI devices).
-        vendor_id_set = getUtility(IHWVendorIDSet)
-        # Note that we must provide a string with exactly 8 characters
-        # as the vendor ID of a SCSI device.
-        vendor_id = vendor_id_set.getByBusAndVendorID(HWBus.SCSI, 'WDC     ')
-        self.assertEqual(vendor_id, None,
-                         'Expected None looking up vendor ID "WDC     " in '
-                         'HWVendorID for the SCSI bus, got %r.' % vendor_id)
-
-        # HALDevice.ensureVendorIDVendorNameExists() creates these
-        # records.
-        scsi_disk = parser.devices[self.UDI_SCSI_DISK]
-        scsi_disk.ensureVendorIDVendorNameExists()
-
-        vendor_name = vendor_name_set.getByName('WDC')
-        self.assertEqual(vendor_name.name, 'WDC',
-                         'Expected to find vendor name "WDC" in '
-                         'HWVendorName, got %r.' % vendor_name.name)
-
-        vendor_id = vendor_id_set.getByBusAndVendorID(HWBus.SCSI, 'WDC     ')
-        self.assertEqual(vendor_id.vendor_id_for_bus, 'WDC     ',
-                         'Expected "WDC     " as vendor_id_for_bus, '
-                         'got %r.' % vendor_id.vendor_id_for_bus)
-        self.assertEqual(vendor_id.bus, HWBus.SCSI,
-                         'Expected HWBUS.SCSI as bus, got %s.'
-                         % vendor_id.bus.title)
-
-    def testCreateDBDataForSimpleDevice(self):
-        """Test of HALDevice.createDBData.
-
-        Test for a HAL device without driver data.
-        """
-        devices = [
-            self.HAL_COMPUTER,
-            ]
-        self.setHALDevices(devices)
-
-        parser = SubmissionParser(self.log)
-        parser.buildDeviceList(self.parsed_data)
-
-        submission_set = getUtility(IHWSubmissionSet)
-        submission = submission_set.getBySubmissionKey('test_submission_id_1')
-
-        hal_device = parser.devices[self.UDI_COMPUTER]
-        hal_device.createDBData(submission, None)
-
-        # HALDevice.createDBData created a HWDevice record.
-        vendor_id_set = getUtility(IHWVendorIDSet)
-        vendor_id = vendor_id_set.getByBusAndVendorID(HWBus.SYSTEM, 'Lenovo')
-        hw_device_set = getUtility(IHWDeviceSet)
-        hw_device = hw_device_set.getByDeviceID(
-            hal_device.real_bus, hal_device.vendor_id,
-            hal_device.product_id)
-        self.assertEqual(hw_device.bus_vendor, vendor_id,
-            'Expected vendor ID (HWBus.SYSTEM, Lenovo) as the vendor ID, '
-            'got %s %r' % (hw_device.bus_vendor.bus,
-                           hw_device.bus_vendor.vendor_name.name))
-        self.assertEqual(hw_device.bus_product_id, 'T41',
-            'Expected product ID T41, got %r.' % hw_device.bus_product_id)
-        self.assertEqual(hw_device.name, 'T41',
-            'Expected device name T41, got %r.' % hw_device.name)
-
-        # One HWDeviceDriverLink record is created...
-        device_driver_link_set = getUtility(IHWDeviceDriverLinkSet)
-        device_driver_link = device_driver_link_set.getByDeviceAndDriver(
-            hw_device, None)
-        self.assertEqual(device_driver_link.device, hw_device,
-            'Expected HWDevice record for Lenovo T41 in HWDeviceDriverLink, '
-            'got %s %r'
-            % (device_driver_link.device.bus_vendor.bus,
-               device_driver_link.device.bus_vendor.vendor_name.name))
-        self.assertEqual(device_driver_link.driver, None,
-            'Expected None as driver in HWDeviceDriverLink')
-
-        # ...and one HWSubmissionDevice record linking the HWDeviceSriverLink
-        # to the submission.
-        submission_device_set = getUtility(IHWSubmissionDeviceSet)
-        submission_devices = submission_device_set.getDevices(submission)
-        self.assertEqual(len(list(submission_devices)), 1,
-            'Unexpected number of submission devices: %i, expected 1.'
-            % len(list(submission_devices)))
-        submission_device = submission_devices[0]
-        self.assertEqual(
-            submission_device.device_driver_link, device_driver_link,
-            'Invalid device_driver_link field in HWSubmissionDevice.')
-        self.assertEqual(
-            submission_device.parent, None,
-            'Invalid parent field in HWSubmissionDevice.')
-        self.assertEqual(
-            submission_device.hal_device_id, 1,
-            'Invalid haL-device_id field in HWSubmissionDevice.')
-
-    def testCreateDBDataForDeviceWithOneDriver(self):
-        """Test of HALDevice.createDBData.
-
-        Test of a HAL device with one driver.
-        """
-        devices = [
-            self.HAL_COMPUTER,
-            self.HAL_PCI_PCCARD_BRIDGE,
-            ]
-        self.setHALDevices(devices)
-
-        parser = SubmissionParser(self.log)
-        parser.buildDeviceList(self.parsed_data)
-        parser.parsed_data = self.parsed_data
-
-        submission_set = getUtility(IHWSubmissionSet)
-        submission = submission_set.getBySubmissionKey('test_submission_id_1')
-
-        hal_root_device = parser.devices[self.UDI_COMPUTER]
-        hal_root_device.createDBData(submission, None)
-
-        # We now have a HWDevice record for the PCCard bridge...
-        device_set = getUtility(IHWDeviceSet)
-        pccard_bridge = device_set.getByDeviceID(
-            HWBus.PCI, '0x%04x' % self.PCI_VENDOR_ID_INTEL,
-            '0x%04x' % self.PCI_PROD_ID_PCI_PCCARD_BRIDGE)
-
-        # ...and a HWDriver record for the yenta_cardbus driver.
-        driver_set = getUtility(IHWDriverSet)
-        yenta_driver = driver_set.getByPackageAndName(
-            self.KERNEL_PACKAGE, 'yenta_cardbus')
-        self.assertEqual(
-            yenta_driver.name, 'yenta_cardbus',
-            'Unexpected driver name: %r' % yenta_driver.name)
-        self.assertEqual(
-            yenta_driver.package_name, self.KERNEL_PACKAGE,
-            'Unexpected package name: %r' % yenta_driver.package_name)
-
-        # The PCCard bridge has one HWDeviceDriverLink record without
-        # an associated driver...
-        device_driver_link_set = getUtility(IHWDeviceDriverLinkSet)
-        pccard_link_no_driver = device_driver_link_set.getByDeviceAndDriver(
-            pccard_bridge, None)
-        self.assertEqual(
-            pccard_link_no_driver.device, pccard_bridge,
-            'Unexpected value of pccard_link_no_driver.device')
-        self.assertEqual(
-            pccard_link_no_driver.driver, None,
-            'Unexpected value of pccard_link_no_driver.driver')
-
-        # ...and another one with the yenta driver.
-        pccard_link_yenta = device_driver_link_set.getByDeviceAndDriver(
-            pccard_bridge, yenta_driver)
-        self.assertEqual(
-            pccard_link_yenta.device, pccard_bridge,
-