Merge lp:~adeuring/launchpad/hwdb-class-udev-device-1 into lp:launchpad

Proposed by Abel Deuring
Status: Merged
Merged at revision: not available
Proposed branch: lp:~adeuring/launchpad/hwdb-class-udev-device-1
Merge into: lp:launchpad
Diff against target: 506 lines
3 files modified
lib/canonical/launchpad/scripts/hwdbsubmissions.py (+138/-5)
lib/canonical/launchpad/scripts/tests/test_hwdb_submission_parser.py (+181/-4)
lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py (+80/-2)
To merge this branch: bzr merge lp:~adeuring/launchpad/hwdb-class-udev-device-1
Reviewer Review Type Date Requested Status
Michael Nelson (community) code Approve
Review via email: mp+13051@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote :

This branch adds some sanity checks for HWDB submission containing udev data (as sent by the HWDB client in Karmic), and it defines a new class UdevDevice, representing a device described by udev data. (The class is quite rudimentary at present.)

The idea of class UdevDevice is to "mangle" the raw data provided in a submission so that we cana populate the HWDB tables from this data. (See the base class BaseDevice and sibling class HALDevice for details.)

A new method SubmissionParser.checkUdevPciProperties() ensures that udev data for PCI devices have certain properties and that these properties have useful data, thus allowing class UdevDevice to make some assumptions about the data it is based on, for example, that the value of the property PCI_CLASS is an integer represented as a hexdecimal string.

SubmissionParser.checkConsistency() now calls checkConsistentUdevDeviceData(). The former method also assumed that the data for each submission contains a dictionary for data from HAL, which is no longer true, so some sanity checks specific for submissions with HAL data are now skipped for submissinos with udev data.

tests:

./bin/test -t test_hwdb_submission_parser
./bin/test -t test_hwdb_submission_processing

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/canonical/launchpad/scripts/hwdbsubmissions.py
  lib/canonical/launchpad/scripts/tests/test_hwdb_submission_parser.py
  lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py

== Pyflakes notices ==

lib/canonical/launchpad/scripts/hwdbsubmissions.py
    22: redefinition of unused 'etree' from line 20

lib/canonical/launchpad/scripts/tests/test_hwdb_submission_parser.py
    11: redefinition of unused 'etree' from line 9

== Pylint notices ==

lib/canonical/launchpad/scripts/hwdbsubmissions.py
    1160: [C0301] Line too long (79/78)
    20: [F0401] Unable to import 'xml.etree.cElementTree' (No module named etree)

lib/canonical/launchpad/scripts/tests/test_hwdb_submission_parser.py
    9: [F0401] Unable to import 'xml.etree.cElementTree' (No module named etree)

These etree-related complaints are not caused by the changes in this branch.

Revision history for this message
Abel Deuring (adeuring) wrote :

oops, I missed the lint complaint about the too long line. Fixed in r9648

Revision history for this message
Michael Nelson (michael.nelson) wrote :
Download full text (22.6 KiB)

> This branch adds some sanity checks for HWDB submission containing udev data
> (as sent by the HWDB client in Karmic), and it defines a new class UdevDevice,
> representing a device described by udev data. (The class is quite rudimentary
> at present.)

Wow - I learned lots of great stuff through this branch - sorry it took me so long to review!

After we found a better way of creating test versions of some of your classes (thanks Bjorn) I've only got comments and some test suggestions left.

Great work!

> === modified file 'lib/canonical/launchpad/scripts/hwdbsubmissions.py'
> --- lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-06 08:37:11 +0000
> +++ lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-08 09:38:16 +0000
> @@ -1141,6 +1141,75 @@
> 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.
> +
> + 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 ':'.

Barry would say: could you please use the :param: etc. like you have below
for UDevDevice.

> + """
> + for device in udev_data:
> + properties = device['E']
> + property_names = set(properties.keys())

I think this method could do with a few comments in general... maybe
a lot of it is completely obvious to you, but I have to spend time
interpreting the code, for example,...

> + 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 valid.
> + if self.pci_class_re.search(properties['PCI_CLASS']) is None:
> + self._logError(
> + 'Invalid udev PCI class: %...

review: Approve (code)
Revision history for this message
Abel Deuring (adeuring) wrote :
Download full text (24.1 KiB)

Hi Michael,

thanks for your review!

On 08.10.2009 13:35, Michael Nelson wrote:
> Review: Approve code
>> This branch adds some sanity checks for HWDB submission containing udev data
>> (as sent by the HWDB client in Karmic), and it defines a new class UdevDevice,
>> representing a device described by udev data. (The class is quite rudimentary
>> at present.)
>
> Wow - I learned lots of great stuff through this branch - sorry it took me so long to review!
>
> After we found a better way of creating test versions of some of your classes (thanks Bjorn) I've only got comments and some test suggestions left.
>
> Great work!
>
>> === modified file 'lib/canonical/launchpad/scripts/hwdbsubmissions.py'
>> --- lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-06 08:37:11 +0000
>> +++ lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-08 09:38:16 +0000
>> @@ -1141,6 +1141,75 @@
>> 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.
>> +
>> + 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 ':'.
>
> Barry would say: could you please use the :param: etc. like you have below
> for UDevDevice.

Done.

>
>> + """
>> + for device in udev_data:
>> + properties = device['E']
>> + property_names = set(properties.keys())
>
> I think this method could do with a few comments in general... maybe
> a lot of it is completely obvious to you, but I have to spend time
> interpreting the code, for example,...

yeah, sorry, I forgot to mention how you can see the data yourself:

 udevadm info --export-db

produces the data we are talking about. Might have made it easier for
you to get an idea what all this is about...

>
>> + 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.

Added.

>> + 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: '
>> + ...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/scripts/hwdbsubmissions.py'
2--- lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-06 08:37:11 +0000
3+++ lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-08 13:44:12 +0000
4@@ -1141,6 +1141,83 @@
5 return False
6 return True
7
8+ PCI_PROPERTIES = set(
9+ ('PCI_CLASS', 'PCI_ID', 'PCI_SUBSYS_ID', 'PCI_SLOT_NAME'))
10+ pci_class_re = re.compile('^[0-9a-f]{1,6}$', re.I)
11+ pci_id_re = re.compile('^[0-9a-f]{4}:[0-9a-f]{4}$', re.I)
12+
13+ def checkUdevPciProperties(self, udev_data):
14+ """Validation of udev PCI devices.
15+
16+ :param udev_data: A list of dicitionaries describing udev
17+ devices, as returned by _parseUdev()
18+ :return: True if all checks pass, else False.
19+
20+ Each PCI device must have the properties PCI_CLASS, PCI_ID,
21+ PCI_SUBSYS_ID, PCI_SLOT_NAME. Non-PCI devices must not have
22+ them.
23+
24+ The value of PCI class must be a 24 bit integer in
25+ hexadecimal representation.
26+
27+ The values of PCI_ID and PCI_SUBSYS_ID must be two 16 bit
28+ integers, separated by a ':'.
29+ """
30+ for device in udev_data:
31+ properties = device['E']
32+ property_names = set(properties.keys())
33+ existing_pci_properties = property_names.intersection(
34+ self.PCI_PROPERTIES)
35+ subsystem = device['E'].get('SUBSYSTEM')
36+ if subsystem is None:
37+ self._logError(
38+ 'udev device without SUBSYSTEM property found.',
39+ self.submission_key)
40+ return False
41+ if subsystem == 'pci':
42+ # Check whether any of the standard pci properties were
43+ # missing.
44+ if existing_pci_properties != self.PCI_PROPERTIES:
45+ missing_properties = self.PCI_PROPERTIES.difference(
46+ existing_pci_properties)
47+
48+ self._logError(
49+ 'PCI udev device without required PCI properties: '
50+ '%r %r'
51+ % (missing_properties, device['P']),
52+ self.submission_key)
53+ return False
54+ # Ensure that the pci class and ids for this device are
55+ # formally valid.
56+ if self.pci_class_re.search(properties['PCI_CLASS']) is None:
57+ self._logError(
58+ 'Invalid udev PCI class: %r %r'
59+ % (properties['PCI_CLASS'], device['P']),
60+ self.submission_key)
61+ return False
62+ for pci_id in (properties['PCI_ID'],
63+ properties['PCI_SUBSYS_ID']):
64+ if self.pci_id_re.search(pci_id) is None:
65+ self._logError(
66+ 'Invalid udev PCI device ID: %r %r'
67+ % (pci_id, device['P']),
68+ self.submission_key)
69+ return False
70+ else:
71+ if len(existing_pci_properties) > 0:
72+ self._logError(
73+ 'Non-PCI udev device with PCI properties: %r %r'
74+ % (existing_pci_properties, device['P']),
75+ self.submission_key)
76+ return False
77+ return True
78+
79+ def checkConsistentUdevDeviceData(self, udev_data):
80+ """Consistency checks for udev data."""
81+ if not self.checkUdevDictsHavePathKey(udev_data):
82+ return False
83+ return self.checkUdevPciProperties(udev_data)
84+
85 def checkConsistency(self, parsed_data):
86 """Run consistency checks on the submitted data.
87
88@@ -1148,10 +1225,10 @@
89 :param: parsed_data: parsed submission data, as returned by
90 parseSubmission
91 """
92- if 'udev' in parsed_data['hardware']:
93- if not self.checkUdevDictsHavePathKey(
94- parsed_data['hardware']['udev']):
95- return False
96+ if ('udev' in parsed_data['hardware']
97+ and not self.checkConsistentUdevDeviceData(
98+ parsed_data['hardware']['udev'])):
99+ return False
100 duplicate_ids = self.findDuplicateIDs(parsed_data)
101 if duplicate_ids:
102 self._logError('Duplicate IDs found: %s' % duplicate_ids,
103@@ -1174,7 +1251,8 @@
104 self._logError(value, self.submission_key)
105 return False
106
107- circular = self.checkHALDevicesParentChildConsistency(udi_children)
108+ circular = self.checkHALDevicesParentChildConsistency(
109+ udi_children)
110 if circular:
111 self._logError('Found HAL devices with circular parent/child '
112 'relationship: %s' % circular,
113@@ -2146,6 +2224,61 @@
114 return self.getVendorOrProductID('product')
115
116
117+class UdevDevice(BaseDevice):
118+ """The representation of a udev device node."""
119+
120+ def __init__(self, udev_data, sysfs_data, parser):
121+ """HALDevice constructor.
122+
123+ :param udevdata: The udev data for this device
124+ :param sysfs_data: sysfs data for this device.
125+ :param parser: The parser processing a submission.
126+ :type parser: SubmissionParser
127+ """
128+ super(UdevDevice, self).__init__(parser)
129+ self.udev = udev_data
130+ self.sysfs = sysfs_data
131+
132+ @property
133+ def device_id(self):
134+ """See `BaseDevice`."""
135+ return self.udev['P']
136+
137+ @property
138+ def is_pci(self):
139+ """True, if this is a PCI device, else False."""
140+ return self.udev['E'].get('SUBSYSTEM') == 'pci'
141+
142+ @property
143+ def pci_class_info(self):
144+ """Parse the udev property PCI_SUBSYS_ID.
145+
146+ :return: (PCI class, PCI sub-class, version) for a PCI device
147+ or (None, None, None) for other devices.
148+ """
149+ if self.is_pci:
150+ # SubmissionParser.checkConsistentUdevDeviceData() ensures
151+ # that PCI_CLASS is a 24 bit integer in hexadecimal
152+ # representation.
153+ # Bits 16..23 of the number are the man PCI class,
154+ # bits 8..15 are the sub-class, bits 0..7 are the version.
155+ class_info = int(self.udev['E']['PCI_CLASS'], 16)
156+ return (class_info >> 16, (class_info >> 8) & 0xFF,
157+ class_info & 0xFF)
158+ else:
159+ return (None, None, None)
160+
161+ @property
162+ def pci_class(self):
163+ """See `BaseDevice`."""
164+ return self.pci_class_info[0]
165+
166+ @property
167+ def pci_subclass(self):
168+ """See `BaseDevice`."""
169+ return self.pci_class_info[1]
170+
171+
172 class ProcessingLoop(object):
173 """An `ITunableLoop` for processing HWDB submissions."""
174
175
176=== modified file 'lib/canonical/launchpad/scripts/tests/test_hwdb_submission_parser.py'
177--- lib/canonical/launchpad/scripts/tests/test_hwdb_submission_parser.py 2009-10-06 09:31:32 +0000
178+++ lib/canonical/launchpad/scripts/tests/test_hwdb_submission_parser.py 2009-10-08 13:44:12 +0000
179@@ -98,6 +98,20 @@
180 self.log.setLevel(logging.INFO)
181 self.handler = Handler(self)
182 self.handler.add(self.log.name)
183+ self.udev_root_device = {
184+ 'P': '/devices/LNXSYSTM:00',
185+ 'E': {'SUBSYSTEM': 'acpi'},
186+ }
187+ self.udev_pci_device = {
188+ 'P': '/devices/pci0000:00/0000:00:1f.2',
189+ 'E': {
190+ 'SUBSYSTEM': 'pci',
191+ 'PCI_CLASS': '10601',
192+ 'PCI_ID': '8086:27C5',
193+ 'PCI_SUBSYS_ID': '10CF:1387',
194+ 'PCI_SLOT_NAME': '0000:00:1f.2',
195+ }
196+ }
197
198 def getTimestampETreeNode(self, time_string):
199 """Return an Elementtree node for an XML tag with a timestamp."""
200@@ -1773,6 +1787,160 @@
201 self.assertErrorMessage(
202 parser.submission_key, 'udev node found without a "P" key')
203
204+ def testCheckUdevPciProperties(self):
205+ """Test of SubmmissionParser.checkUdevPciProperties()."""
206+ # udev PCI devices must have the properties PCI_CLASS, PCI_ID,
207+ # PCI_SUBSYS_ID, PCI_SLOT_NAME; other devices must not have
208+ # these properties.
209+ parser = SubmissionParser()
210+ self.assertTrue(parser.checkUdevPciProperties(
211+ [self.udev_root_device, self.udev_pci_device]))
212+
213+ def testCheckUdevPciPropertiesNonPciDeviceWithPciProperties(self):
214+ """Test of SubmmissionParser.checkUdevPciProperties().
215+
216+ A non-PCI device having PCI properties makes a submission invalid.
217+ """
218+ self.udev_root_device['E']['PCI_SLOT_NAME'] = '0000:00:1f.2'
219+ parser = SubmissionParser(self.log)
220+ parser.submission_key = 'invalid non-PCI device'
221+ self.assertFalse(parser.checkUdevPciProperties(
222+ [self.udev_root_device, self.udev_pci_device]))
223+ self.assertErrorMessage(
224+ parser.submission_key,
225+ "Non-PCI udev device with PCI properties: set(['PCI_SLOT_NAME']) "
226+ "'/devices/LNXSYSTM:00'")
227+
228+ def testCheckUdevPciPropertiesPciDeviceWithoutRequiredProperties(self):
229+ """Test of SubmmissionParser.checkUdevPciProperties().
230+
231+ A PCI device not having a required PCI property makes a submission
232+ invalid.
233+ """
234+ del self.udev_pci_device['E']['PCI_CLASS']
235+ parser = SubmissionParser(self.log)
236+ parser.submission_key = 'invalid PCI device'
237+ self.assertFalse(parser.checkUdevPciProperties(
238+ [self.udev_root_device, self.udev_pci_device]))
239+ self.assertErrorMessage(
240+ parser.submission_key,
241+ "PCI udev device without required PCI properties: "
242+ "set(['PCI_CLASS']) '/devices/pci0000:00/0000:00:1f.2'")
243+
244+ def testCheckUdevPciPropertiesPciDeviceWithNonIntegerPciClass(self):
245+ """Test of SubmmissionParser.checkUdevPciProperties().
246+
247+ A PCI device with a non-integer class value makes a submission
248+ invalid.
249+ """
250+ self.udev_pci_device['E']['PCI_CLASS'] = 'not-an-integer'
251+ parser = SubmissionParser(self.log)
252+ parser.submission_key = 'invalid PCI class value'
253+ self.assertFalse(parser.checkUdevPciProperties(
254+ [self.udev_root_device, self.udev_pci_device]))
255+ self.assertErrorMessage(
256+ parser.submission_key,
257+ "Invalid udev PCI class: 'not-an-integer' "
258+ "'/devices/pci0000:00/0000:00:1f.2'")
259+
260+ def testCheckUdevPciPropertiesPciDeviceWithInvalidPciClassValue(self):
261+ """Test of SubmmissionParser.checkUdevPciProperties().
262+
263+ A PCI device with invalid class data makes a submission
264+ invalid.
265+ """
266+ self.udev_pci_device['E']['PCI_CLASS'] = '1234567'
267+ parser = SubmissionParser(self.log)
268+ parser.submission_key = 'too large PCI class value'
269+ self.assertFalse(parser.checkUdevPciProperties(
270+ [self.udev_root_device, self.udev_pci_device]))
271+ self.assertErrorMessage(
272+ parser.submission_key,
273+ "Invalid udev PCI class: '1234567' "
274+ "'/devices/pci0000:00/0000:00:1f.2'")
275+
276+ def testCheckUdevPciPropertiesPciDeviceWithInvalidDeviceID(self):
277+ """Test of SubmmissionParser.checkUdevPciProperties().
278+
279+ A PCI device with an invalid device ID makes a submission
280+ invalid.
281+ """
282+ self.udev_pci_device['E']['PCI_ID'] = 'not-an-id'
283+ parser = SubmissionParser(self.log)
284+ parser.submission_key = 'invalid PCI ID'
285+ self.assertFalse(parser.checkUdevPciProperties(
286+ [self.udev_root_device, self.udev_pci_device]))
287+ self.assertErrorMessage(
288+ parser.submission_key,
289+ "Invalid udev PCI device ID: 'not-an-id' "
290+ "'/devices/pci0000:00/0000:00:1f.2'")
291+
292+ def testCheckUdevPciPropertiesPciDeviceWithInvalidSubsystemID(self):
293+ """Test of SubmmissionParser.checkUdevPciProperties().
294+
295+ A PCI device with an invalid subsystem ID makes a submission
296+ invalid.
297+ """
298+ self.udev_pci_device['E']['PCI_SUBSYS_ID'] = 'not-a-subsystem-id'
299+ parser = SubmissionParser(self.log)
300+ parser.submission_key = 'invalid PCI subsystem ID'
301+ self.assertFalse(parser.checkUdevPciProperties(
302+ [self.udev_root_device, self.udev_pci_device]))
303+ self.assertErrorMessage(
304+ parser.submission_key,
305+ "Invalid udev PCI device ID: 'not-a-subsystem-id' "
306+ "'/devices/pci0000:00/0000:00:1f.2'")
307+
308+ class UdevTestSubmissionParser(SubmissionParser):
309+ """A variant of SubmissionParser that shortcuts udev related tests.
310+
311+ All shortcut methods return True.
312+ """
313+ def checkUdevDictsHavePathKey(self, udev_data):
314+ """See `SubmissionParser`."""
315+ return True
316+
317+ def checkUdevPciProperties(self, udev_data):
318+ """See `SubmissionParser`."""
319+ return True
320+
321+ def testCheckConsistentUdevDeviceData(self):
322+ """Test of SubmissionParser.checkConsistentUdevDeviceData(),"""
323+ parser = self.UdevTestSubmissionParser()
324+ self.assertTrue(parser.checkConsistentUdevDeviceData(None))
325+
326+ def testCheckConsistentUdevDeviceData_invalid_path_data(self):
327+ """Test of SubmissionParser.checkConsistentUdevDeviceData(),
328+
329+ Detection of invalid path data lets the check fail.
330+ """
331+ class SubmissionParserUdevPathCheckFails(
332+ self.UdevTestSubmissionParser):
333+ """A SubmissionPaser where checkUdevDictsHavePathKey() fails."""
334+
335+ def checkUdevDictsHavePathKey(self, udev_data):
336+ """See `SubmissionParser`."""
337+ return False
338+
339+ parser = SubmissionParserUdevPathCheckFails()
340+ self.assertFalse(parser.checkConsistentUdevDeviceData(None))
341+
342+ def testCheckConsistentUdevDeviceData_invalid_pci_data(self):
343+ """Test of SubmissionParser.checkConsistentUdevDeviceData(),
344+
345+ Detection of invalid PCI data lets the check fail.
346+ """
347+ class SubmissionParserUdevPciCheckFails(
348+ self.UdevTestSubmissionParser):
349+ """A SubmissionPaser where checkUdevPciProperties() fails."""
350+
351+ def checkUdevPciProperties(self, udev_data):
352+ """See `SubmissionParser`."""
353+ return False
354+
355+ parser = SubmissionParserUdevPciCheckFails()
356+ self.assertFalse(parser.checkConsistentUdevDeviceData(None))
357+
358 def _setupConsistencyCheckParser(self):
359 """Prepare and return a SubmissionParser instance.
360
361@@ -1799,6 +1967,9 @@
362 test.assertTrue(isinstance(self, SubmissionParser))
363 return []
364
365+ def checkConsistentUdevDeviceData(self, udev_data):
366+ return True
367+
368 parser = SubmissionParser(self.log)
369 parser.findDuplicateIDs = (
370 lambda parsed_data: findDuplicateIDs(parser, parsed_data))
371@@ -1811,6 +1982,9 @@
372 parser.checkHALDevicesParentChildConsistency = (
373 lambda udi_children: checkHALDevicesParentChildConsistency(
374 parser, udi_children))
375+ parser.checkConsistentUdevDeviceData = (
376+ lambda udev_data: checkConsistentUdevDeviceData(
377+ parser, udev_data))
378 return parser
379
380 def assertErrorMessage(self, submission_key, log_message):
381@@ -1882,10 +2056,15 @@
382 }
383 ))
384
385- def testConsistencyCheckInvalidUdevData(self):
386+ def testConsistencyCheck_invalid_udev_data(self):
387 """Test of SubmissionParser.checkConsistency."""
388+ def checkConsistentUdevDeviceData(self, udev_data):
389+ return False
390+
391 parser = self._setupConsistencyCheckParser()
392- parser.submission_key = 'Consistency check with invalid udev data'
393+ parser.checkConsistentUdevDeviceData = (
394+ lambda udev_data: checkConsistentUdevDeviceData(
395+ parser, udev_data))
396 self.assertFalse(parser.checkConsistency(
397 {
398 'hardware': {
399@@ -1894,8 +2073,6 @@
400 }
401 }
402 ))
403- self.assertErrorMessage(
404- parser.submission_key, 'udev node found without a "P" key')
405
406 def testConsistencyCheckWithDuplicateIDs(self):
407 """SubmissionParser.checkConsistency detects duplicate IDs."""
408
409=== modified file 'lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py'
410--- lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-01 14:48:33 +0000
411+++ lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-08 13:44:12 +0000
412@@ -27,8 +27,8 @@
413 from canonical.launchpad.scripts.hwdbsubmissions import (
414 HALDevice, PCI_CLASS_BRIDGE, PCI_CLASS_SERIALBUS_CONTROLLER,
415 PCI_CLASS_STORAGE, PCI_SUBCLASS_BRIDGE_CARDBUS, PCI_SUBCLASS_BRIDGE_PCI,
416- PCI_SUBCLASS_SERIALBUS_USB, PCI_SUBCLASS_STORAGE_SATA, SubmissionParser,
417- process_pending_submissions)
418+ PCI_SUBCLASS_SERIALBUS_USB, PCI_SUBCLASS_STORAGE_SATA,
419+ process_pending_submissions, SubmissionParser, UdevDevice)
420 from canonical.launchpad.webapp.errorlog import ErrorReportingUtility
421 from canonical.testing import BaseLayer, LaunchpadZopelessLayer
422
423@@ -2591,6 +2591,84 @@
424 'property not treated as a real device.')
425
426
427+class TestUdevDevice(TestCase):
428+ """Tests of class UdevDevice."""
429+
430+ layer = BaseLayer
431+
432+ root_device = {
433+ 'P': '/devices/LNXSYSTM:00',
434+ 'E': {
435+ 'UDEV_LOG': '3',
436+ 'DEVPATH': '/devices/LNXSYSTM:00',
437+ 'MODALIAS': 'acpi:LNXSYSTM:',
438+ 'SUBSYSTEM': 'acpi',
439+ }
440+ }
441+
442+ pci_device_data = {
443+ 'P': '/devices/pci0000:00/0000:00:1f.2',
444+ 'E': {
445+ 'PCI_CLASS': '10602',
446+ 'PCI_ID': '8086:27C5',
447+ 'PCI_SUBSYS_ID': '10CF:1387',
448+ 'PCI_SLOT_NAME': '0000:00:1f.2',
449+ 'SUBSYSTEM': 'pci',
450+ }
451+ }
452+
453+ def test_device_id(self):
454+ """Test of UdevDevice.device_id."""
455+ device = UdevDevice(self.pci_device_data, None, None)
456+ self.assertEqual(
457+ '/devices/pci0000:00/0000:00:1f.2', device.device_id,
458+ 'Unexpected value of UdevDevice.device_id.')
459+
460+ def test_is_pci(self):
461+ """Test of UdevDevice.is_pci."""
462+ device = UdevDevice(self.pci_device_data, None, None)
463+ self.assertTrue(device.is_pci)
464+
465+ device = UdevDevice(self.root_device, None, None)
466+ self.assertFalse(device.is_pci)
467+
468+ def test_pci_class_info(self):
469+ """Test of UdevDevice.pci_class_info"""
470+ device = UdevDevice(self.pci_device_data, None, None)
471+ self.assertEqual(
472+ (1, 6, 2), device.pci_class_info,
473+ 'Invalid value of UdevDevice.pci_class_info for PCI device.')
474+
475+ device = UdevDevice(self.root_device, None, None)
476+ self.assertEqual(
477+ (None, None, None), device.pci_class_info,
478+ 'Invalid value of UdevDevice.pci_class_info for Non-PCI device.')
479+
480+ def test_pci_class(self):
481+ """Test of UdevDevice.pci_class"""
482+ device = UdevDevice(self.pci_device_data, None, None)
483+ self.assertEqual(
484+ 1, device.pci_class,
485+ 'Invalid value of UdevDevice.pci_class for PCI device.')
486+
487+ device = UdevDevice(self.root_device, None, None)
488+ self.assertEqual(
489+ None, device.pci_class,
490+ 'Invalid value of UdevDevice.pci_class for Non-PCI device.')
491+
492+ def test_pci_subclass(self):
493+ """Test of UdevDevice.pci_subclass"""
494+ device = UdevDevice(self.pci_device_data, None, None)
495+ self.assertEqual(
496+ 6, device.pci_subclass,
497+ 'Invalid value of UdevDevice.pci_class for PCI device.')
498+
499+ device = UdevDevice(self.root_device, None, None)
500+ self.assertEqual(
501+ None, device.pci_class,
502+ 'Invalid value of UdevDevice.pci_class for Non-PCI device.')
503+
504+
505 class TestHWDBSubmissionTablePopulation(TestCaseHWDB):
506 """Tests of the HWDB popoluation with submitted data."""
507