Merge lp:~adeuring/launchpad/bug-460976-noise-reduction-for-hwdb-processing-log into lp:launchpad/db-devel
- bug-460976-noise-reduction-for-hwdb-processing-log
- Merge into db-devel
Proposed by
Abel Deuring
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~adeuring/launchpad/bug-460976-noise-reduction-for-hwdb-processing-log |
Merge into: | lp:launchpad/db-devel |
Diff against target: |
671 lines 5 files modified
lib/canonical/launchpad/scripts/hwdbsubmissions.py (+69/-14) lib/canonical/launchpad/scripts/tests/test_hwdb_submission_parser.py (+121/-96) lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py (+14/-5) lib/lp/testing/__init__.py (+67/-0) lib/lp/testing/tests/test_inlinetests.py (+20/-0) |
To merge this branch: | bzr merge lp:~adeuring/launchpad/bug-460976-noise-reduction-for-hwdb-processing-log |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Eleanor Berger (community) | code | Approve | |
Review via email: mp+13950@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote : | # |
Revision history for this message
Eleanor Berger (intellectronica) : | # |
review:
Approve
(code)
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-22 11:08:18 +0000 |
3 | +++ lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-26 12:05:33 +0000 |
4 | @@ -1228,9 +1228,11 @@ |
5 | def checkUdevUsbProperties(self, udev_data): |
6 | """Validation of udev USB devices. |
7 | |
8 | - USB devices must have the properties DEVTYPE (value |
9 | - 'usb_device' or 'usb_interface'), PRODUCT and TYPE. PRODUCT |
10 | - must be a tuple of three integers in hexadecimal |
11 | + USB devices must either have the three properties DEVTYPE |
12 | + (value 'usb_device' or 'usb_interface'), PRODUCT and TYPE, |
13 | + or they must have none of them. |
14 | + |
15 | + PRODUCT must be a tuple of three integers in hexadecimal |
16 | representation, separates by '/'. TYPE must be a a tuple of |
17 | three integers in decimal representation, separated by '/'. |
18 | usb_interface nodes must additionally have a property |
19 | @@ -1245,6 +1247,10 @@ |
20 | property_names = set(properties) |
21 | existing_usb_properties = property_names.intersection( |
22 | UDEV_USB_DEVICE_PROPERTIES) |
23 | + |
24 | + if len(existing_usb_properties) == 0: |
25 | + continue |
26 | + |
27 | if existing_usb_properties != UDEV_USB_DEVICE_PROPERTIES: |
28 | missing_properties = UDEV_USB_DEVICE_PROPERTIES.difference( |
29 | existing_usb_properties) |
30 | @@ -1365,7 +1371,8 @@ |
31 | if ('udev' in parsed_data['hardware'] |
32 | and not self.checkConsistentUdevDeviceData( |
33 | parsed_data['hardware']['udev'], |
34 | - parsed_data['hardware']['sysfs-attributes'])): |
35 | + parsed_data['hardware']['sysfs-attributes'], |
36 | + parsed_data['hardware']['dmi'],)): |
37 | return False |
38 | duplicate_ids = self.findDuplicateIDs(parsed_data) |
39 | if duplicate_ids: |
40 | @@ -1962,6 +1969,40 @@ |
41 | |
42 | info.bus == 'video4linux' is used for the "input aspect" |
43 | of video devices. |
44 | + |
45 | + 'ac97' is used in submissions with udev data for a sub-node |
46 | + of sound devices. |
47 | + |
48 | + 'hid' is used in submissions with udev data for a sub-node |
49 | + of USB input devices. |
50 | + |
51 | + 'drm_minor', 'pci_express', 'tifm_adapter', 'gameport', |
52 | + 'spi_host', 'tifm', 'wlan' are used in submissions with |
53 | + udev data for sub-nodes of PCI devices. |
54 | + |
55 | + 'pcmcia_socket' is used in submissions with udev data for |
56 | + a sub-node of PC Card and PCMCIA bridges. |
57 | + |
58 | + 'ieee80211' is used in submissions with udev data for |
59 | + sub-nodes IEEE 802.11 WLAN devices. |
60 | + |
61 | + 'host', 'link' are used in submissions with udev data for |
62 | + sub.nodes of bluetooth devices. |
63 | + |
64 | + 'usb_host' and 'usbmon' are used in submissions with udev |
65 | + data for sub-nodes of USB controllers. |
66 | + |
67 | + 'usb_endpoint', 'usb-serial', 'lirc' are used in |
68 | + submissions with udev data for sub-nodes of USB devices. |
69 | + |
70 | + 'enclosure' is used in submissions with udev data for a |
71 | + sub.node of SCSI devices. |
72 | + |
73 | + 'graphics' is used in submissions with udev data for a |
74 | + sub-node of graphics cards. |
75 | + |
76 | + 'hwmon' is is used in submissions with udev data in |
77 | + many sub-nodes. |
78 | """ |
79 | # The root node is always a real device, but its raw_bus |
80 | # property can have different values: None or 'Unknown' in |
81 | @@ -1974,13 +2015,15 @@ |
82 | # This set of buses is only used once; it's easier to have it |
83 | # here than to put it elsewhere and have to document its |
84 | # location and purpose. |
85 | - if bus in (None, 'disk', 'drm', 'dvb', 'memstick_host', 'net', |
86 | - 'partition', 'scsi_disk', 'scsi_generic', 'scsi_host', |
87 | - 'scsi_target', 'sound', 'spi_transport', 'ssb', 'tty', |
88 | - 'usb', 'usb_interface', 'video4linux', ): |
89 | - # |
90 | - # The computer itself is the only HAL device without the |
91 | - # info.bus property that we treat as a real device. |
92 | + if bus in (None, 'ac97', 'disk', 'drm', 'drm_minor', 'dvb', |
93 | + 'enclosure', 'gameport', 'graphics', 'hid', 'host', |
94 | + 'hwmon', 'ieee80211', 'link', 'lirc', 'memstick_host', |
95 | + 'net', 'partition', 'pci_express', 'pcmcia_socket', |
96 | + 'scsi_disk', 'scsi_generic', 'scsi_host', 'scsi_target', |
97 | + 'sound', 'spi_host', 'spi_transport', 'ssb', 'tifm', |
98 | + 'tifm_adapter', 'tty', 'usb', 'usb-serial', 'usb_endpoint', |
99 | + 'usb_host', 'usb_interface', 'usbmon', 'video4linux', |
100 | + 'wlan'): |
101 | return False |
102 | elif bus == 'usb_device': |
103 | vendor_id = self.usb_vendor_id |
104 | @@ -2088,6 +2131,16 @@ |
105 | for CPUs, power supply etc. Except for the main sytsem, none |
106 | of them provides a vendor or product id, so we ignore them. |
107 | |
108 | + raw_bus == 'video_output', 'thermal', 'vtconsole', 'bdi', |
109 | + 'mem', 'ppp', 'vc', 'dmi', 'hidraw', 'hwmon', 'heci', 'rfkill', |
110 | + 'i2c-adapter', 'ttm', 'ppdev', 'printer' is used in submissions |
111 | + with udev data for virtual devices. |
112 | + |
113 | + 'pci_bus' is used in submissions with udev data for a node |
114 | + describing a PCI bus. |
115 | + |
116 | + 'leds' is used in submissions with udev data to describe LEDs. |
117 | + |
118 | XXX Abel Deuring 2008-05-06: IEEE1394 devices are a bit |
119 | nasty: The standard does not define any specification |
120 | for product IDs or product names, hence HAL often uses |
121 | @@ -2115,9 +2168,11 @@ |
122 | # The root node is course a real device; storing data |
123 | # about other devices with the bus "unkown" is pointless. |
124 | return False |
125 | - if bus in ('backlight', 'bluetooth', 'ieee1394', 'input', 'misc', |
126 | - 'mmc', 'mmc_host', 'pcmcia', 'platform', 'pnp', |
127 | - 'power_supply'): |
128 | + if bus in ('backlight', 'bdi', 'bluetooth', 'dmi', 'heci', 'hidraw', |
129 | + 'hwmon', 'i2c-adapter', 'ieee1394', 'input', 'leds', 'mem', |
130 | + 'misc', 'mmc', 'mmc_host', 'pci_bus', 'pcmcia', 'platform', |
131 | + 'pnp', 'power_supply', 'ppdev', 'ppp', 'printer', 'rfkill', |
132 | + 'thermal', 'ttm', 'vc', 'video_output', 'vtconsole'): |
133 | return False |
134 | |
135 | # We identify devices by bus, vendor ID and product ID; |
136 | |
137 | === modified file 'lib/canonical/launchpad/scripts/tests/test_hwdb_submission_parser.py' |
138 | --- lib/canonical/launchpad/scripts/tests/test_hwdb_submission_parser.py 2009-10-21 16:49:03 +0000 |
139 | +++ lib/canonical/launchpad/scripts/tests/test_hwdb_submission_parser.py 2009-10-26 12:05:33 +0000 |
140 | @@ -23,6 +23,7 @@ |
141 | ROOT_UDI) |
142 | from canonical.testing import BaseLayer |
143 | |
144 | +from lp.testing import validate_mock_class |
145 | |
146 | class SubmissionParserTestParseSoftware(SubmissionParser): |
147 | """A Variant used to test SubmissionParser._parseSoftware. |
148 | @@ -1928,27 +1929,41 @@ |
149 | "'/devices/pci0000:00/0000:00:1f.2'") |
150 | |
151 | def testCheckUdevUsbProperties(self): |
152 | - """Test of SubmissionParser.checkUdevUsbProperties().""" |
153 | + """Test of SubmissionParser.checkUdevUsbProperties(). |
154 | + |
155 | + udev nodes for USB devices must define the three properties |
156 | + DEVTYPE, PRODUCT, TYPE or none of them. |
157 | + """ |
158 | parser = SubmissionParser() |
159 | self.assertTrue(parser.checkUdevUsbProperties( |
160 | [self.udev_root_device, self.udev_usb_device, |
161 | self.udev_usb_interface])) |
162 | |
163 | + for property_name in ('DEVTYPE', 'PRODUCT', 'TYPE'): |
164 | + del self.udev_usb_device['E'][property_name] |
165 | + self.assertTrue(parser.checkUdevUsbProperties( |
166 | + [self.udev_root_device, self.udev_usb_device, |
167 | + self.udev_usb_interface])) |
168 | + |
169 | def testCheckUdevUsbProperties_missing_required_property(self): |
170 | """Test of SubmissionParser.checkUdevUsbProperties(). |
171 | |
172 | - A USB device that does not have a required property makes a |
173 | - submission invalid. |
174 | + A USB device where some but not all of the properties DEVTYPE, |
175 | + PRODUCT, TYPE are defined makes a submission invalid. |
176 | """ |
177 | - del self.udev_usb_device['E']['DEVTYPE'] |
178 | - parser = SubmissionParser(self.log) |
179 | - parser.submission_key = 'USB device without DEVTYPE property' |
180 | - self.assertFalse(parser.checkUdevUsbProperties( |
181 | - [self.udev_root_device, self.udev_usb_device])) |
182 | - self.assertErrorMessage( |
183 | - parser.submission_key, |
184 | - "USB udev device found without required properties: " |
185 | - "set(['DEVTYPE']) '/devices/pci0000:00/0000:00:1d.1/usb3/3-2'") |
186 | + for property_name in ('DEVTYPE', 'PRODUCT', 'TYPE'): |
187 | + saved_property = self.udev_usb_device['E'].pop(property_name) |
188 | + parser = SubmissionParser(self.log) |
189 | + parser.submission_key = ( |
190 | + 'USB device without %s property' % property_name) |
191 | + self.assertFalse(parser.checkUdevUsbProperties( |
192 | + [self.udev_root_device, self.udev_usb_device])) |
193 | + self.assertErrorMessage( |
194 | + parser.submission_key, |
195 | + "USB udev device found without required properties: " |
196 | + "set(['%s']) '/devices/pci0000:00/0000:00:1d.1/usb3/3-2'" |
197 | + % property_name) |
198 | + self.udev_usb_device['E'][property_name] = saved_property |
199 | |
200 | def testCheckUdevUsbProperties_with_invalid_product_id(self): |
201 | """Test of SubmissionParser.checkUdevUsbProperties(). |
202 | @@ -2102,7 +2117,7 @@ |
203 | |
204 | All shortcut methods return True. |
205 | """ |
206 | - def checkUdevDictsHavePathKey(self, udev_data): |
207 | + def checkUdevDictsHavePathKey(self, udev_nodes): |
208 | """See `SubmissionParser`.""" |
209 | return True |
210 | |
211 | @@ -2114,7 +2129,7 @@ |
212 | """See `SubmissionParser`.""" |
213 | return True |
214 | |
215 | - def checkUdevScsiProperties(self, udev_data, syfs_data): |
216 | + def checkUdevScsiProperties(self, udev_data, sysfs_data): |
217 | """See `SubmissionParser`.""" |
218 | return True |
219 | |
220 | @@ -2122,6 +2137,8 @@ |
221 | """See `SubmissionParser`.""" |
222 | return True |
223 | |
224 | + validate_mock_class(UdevTestSubmissionParser) |
225 | + |
226 | def testCheckConsistentUdevDeviceData(self): |
227 | """Test of SubmissionParser.checkConsistentUdevDeviceData(),""" |
228 | parser = self.UdevTestSubmissionParser() |
229 | @@ -2137,10 +2154,12 @@ |
230 | self.UdevTestSubmissionParser): |
231 | """A SubmissionPaser where checkUdevDictsHavePathKey() fails.""" |
232 | |
233 | - def checkUdevDictsHavePathKey(self, udev_data): |
234 | + def checkUdevDictsHavePathKey(self, udev_nodes): |
235 | """See `SubmissionParser`.""" |
236 | return False |
237 | |
238 | + validate_mock_class(SubmissionParserUdevPathCheckFails) |
239 | + |
240 | parser = SubmissionParserUdevPathCheckFails() |
241 | self.assertFalse(parser.checkConsistentUdevDeviceData( |
242 | None, None, None)) |
243 | @@ -2158,6 +2177,8 @@ |
244 | """See `SubmissionParser`.""" |
245 | return False |
246 | |
247 | + validate_mock_class(SubmissionParserUdevPciCheckFails) |
248 | + |
249 | parser = SubmissionParserUdevPciCheckFails() |
250 | self.assertFalse(parser.checkConsistentUdevDeviceData( |
251 | None, None, None)) |
252 | @@ -2175,6 +2196,8 @@ |
253 | """See `SubmissionParser`.""" |
254 | return False |
255 | |
256 | + validate_mock_class(SubmissionParserUdevUsbCheckFails) |
257 | + |
258 | parser = SubmissionParserUdevUsbCheckFails() |
259 | self.assertFalse(parser.checkConsistentUdevDeviceData( |
260 | None, None, None)) |
261 | @@ -2192,6 +2215,8 @@ |
262 | """See `SubmissionParser`.""" |
263 | return False |
264 | |
265 | + validate_mock_class(SubmissionParserUdevUsbCheckFails) |
266 | + |
267 | parser = SubmissionParserUdevUsbCheckFails() |
268 | self.assertFalse(parser.checkConsistentUdevDeviceData( |
269 | None, None, None)) |
270 | @@ -2209,55 +2234,38 @@ |
271 | """See `SubmissionParser`.""" |
272 | return False |
273 | |
274 | + validate_mock_class(SubmissionParserUdevUsbCheckFails) |
275 | + |
276 | parser = SubmissionParserUdevUsbCheckFails() |
277 | self.assertFalse(parser.checkConsistentUdevDeviceData( |
278 | None, None, None)) |
279 | |
280 | - def _setupConsistencyCheckParser(self): |
281 | - """Prepare and return a SubmissionParser instance. |
282 | + class MockSubmissionParser(SubmissionParser): |
283 | + """A SubmissionParser variant for testing checkCOnsistentData() |
284 | |
285 | All "method substitutes" return a valid result. |
286 | """ |
287 | - test = self |
288 | + |
289 | def findDuplicateIDs(self, parsed_data): |
290 | - test.assertTrue(isinstance(self, SubmissionParser)) |
291 | return set() |
292 | |
293 | def findInvalidIDReferences(self, parsed_data): |
294 | - test.assertTrue(isinstance(self, SubmissionParser)) |
295 | return set() |
296 | |
297 | def getUDIDeviceMap(self, devices): |
298 | - test.assertTrue(isinstance(self, SubmissionParser)) |
299 | return {} |
300 | |
301 | def getUDIChildren(self, udi_device_map): |
302 | - test.assertTrue(isinstance(self, SubmissionParser)) |
303 | return {} |
304 | |
305 | - def checkHALDevicesParentChildConsistency(self, devices): |
306 | - test.assertTrue(isinstance(self, SubmissionParser)) |
307 | + def checkHALDevicesParentChildConsistency(self, udi_children): |
308 | return [] |
309 | |
310 | - def checkConsistentUdevDeviceData(self, udev_data, sysfs_data): |
311 | + def checkConsistentUdevDeviceData( |
312 | + self, udev_data, sysfs_data, dmi_data): |
313 | return True |
314 | |
315 | - parser = SubmissionParser(self.log) |
316 | - parser.findDuplicateIDs = ( |
317 | - lambda parsed_data: findDuplicateIDs(parser, parsed_data)) |
318 | - parser.findInvalidIDReferences = ( |
319 | - lambda parsed_data: findInvalidIDReferences(parser, parsed_data)) |
320 | - parser.getUDIDeviceMap = ( |
321 | - lambda devices: getUDIDeviceMap(parser, devices)) |
322 | - parser.getUDIChildren = ( |
323 | - lambda udi_device_map: getUDIChildren(parser, udi_device_map)) |
324 | - parser.checkHALDevicesParentChildConsistency = ( |
325 | - lambda udi_children: checkHALDevicesParentChildConsistency( |
326 | - parser, udi_children)) |
327 | - parser.checkConsistentUdevDeviceData = ( |
328 | - lambda udev_data, sysfs_data: checkConsistentUdevDeviceData( |
329 | - parser, udev_data, sysfs_data)) |
330 | - return parser |
331 | + validate_mock_class(MockSubmissionParser) |
332 | |
333 | def assertErrorMessage(self, submission_key, log_message): |
334 | """Search for message in the log entries for submission_key. |
335 | @@ -2309,7 +2317,7 @@ |
336 | |
337 | def testConsistencyCheck(self): |
338 | """Test of SubmissionParser.checkConsistency.""" |
339 | - parser = self._setupConsistencyCheckParser() |
340 | + parser = self.MockSubmissionParser() |
341 | result = parser.checkConsistency({'hardware': |
342 | {'hal': {'devices': []}}}) |
343 | self.assertEqual(result, True, |
344 | @@ -2318,45 +2326,53 @@ |
345 | |
346 | def testConsistencyCheckValidUdevData(self): |
347 | """Test of SubmissionParser.checkConsistency.""" |
348 | - parser = self._setupConsistencyCheckParser() |
349 | + parser = self.MockSubmissionParser() |
350 | self.assertTrue(parser.checkConsistency( |
351 | { |
352 | 'hardware': { |
353 | - 'udev': [], |
354 | - 'sysfs-attributes': [] |
355 | + 'udev': None, |
356 | + 'sysfs-attributes': None, |
357 | + 'dmi': None, |
358 | } |
359 | } |
360 | )) |
361 | |
362 | def testConsistencyCheck_invalid_udev_data(self): |
363 | """Test of SubmissionParser.checkConsistency.""" |
364 | - def checkConsistentUdevDeviceData(self, udev_data, sysfs_data): |
365 | - return False |
366 | - |
367 | - parser = self._setupConsistencyCheckParser() |
368 | - parser.checkConsistentUdevDeviceData = ( |
369 | - lambda udev_data, sysfs_data: checkConsistentUdevDeviceData( |
370 | - parser, udev_data, sysfs_data)) |
371 | + class MockSubmissionParserBadUdevDeviceData( |
372 | + self.MockSubmissionParser): |
373 | + """A parser where checkConsistentUdevDeviceData() fails.""" |
374 | + |
375 | + def checkConsistentUdevDeviceData(self, udev_data, sysfs_data, |
376 | + dmi_data): |
377 | + return False |
378 | + |
379 | + validate_mock_class(MockSubmissionParserBadUdevDeviceData) |
380 | + |
381 | + parser = MockSubmissionParserBadUdevDeviceData() |
382 | self.assertFalse(parser.checkConsistency( |
383 | { |
384 | 'hardware': { |
385 | - 'udev': [{}], |
386 | - 'sysfs-attributes': [] |
387 | + 'udev': None, |
388 | + 'sysfs-attributes': None, |
389 | + 'dmi': None, |
390 | } |
391 | } |
392 | )) |
393 | |
394 | def testConsistencyCheckWithDuplicateIDs(self): |
395 | """SubmissionParser.checkConsistency detects duplicate IDs.""" |
396 | - test = self |
397 | - def findDuplicateIDs(self, parsed_data): |
398 | - test.assertTrue(isinstance(self, SubmissionParser)) |
399 | - return set([1]) |
400 | - |
401 | - parser = self._setupConsistencyCheckParser() |
402 | + class MockSubmissionParserDuplicateIds( |
403 | + self.MockSubmissionParser): |
404 | + """A parser where findDuplicateIDs() fails.""" |
405 | + |
406 | + def findDuplicateIDs(self, parsed_data): |
407 | + return set([1]) |
408 | + |
409 | + validate_mock_class(MockSubmissionParserDuplicateIds) |
410 | + |
411 | + parser = MockSubmissionParserDuplicateIds(self.log) |
412 | parser.submission_key = 'Consistency check detects duplicate IDs' |
413 | - parser.findDuplicateIDs = ( |
414 | - lambda parsed_data: findDuplicateIDs(parser, parsed_data)) |
415 | result = parser.checkConsistency({'hardware': |
416 | {'hal': {'devices': []}}}) |
417 | self.assertEqual(result, False, |
418 | @@ -2366,15 +2382,16 @@ |
419 | |
420 | def testConsistencyCheckWithInvalidIDReferences(self): |
421 | """SubmissionParser.checkConsistency detects invalid ID references.""" |
422 | - test = self |
423 | - def findInvalidIDReferences(self, parsed_data): |
424 | - test.assertTrue(isinstance(self, SubmissionParser)) |
425 | - return set([1]) |
426 | - |
427 | - parser = self._setupConsistencyCheckParser() |
428 | + class MockSubmissionParserInvalidIDReferences( |
429 | + self.MockSubmissionParser): |
430 | + """A parser where findInvalidIDReferences() fails.""" |
431 | + def findInvalidIDReferences(self, parsed_data): |
432 | + return set([1]) |
433 | + |
434 | + validate_mock_class(MockSubmissionParserInvalidIDReferences) |
435 | + |
436 | + parser = MockSubmissionParserInvalidIDReferences(self.log) |
437 | parser.submission_key = 'Consistency check detects invalid ID refs' |
438 | - parser.findInvalidIDReferences = ( |
439 | - lambda parsed_data: findInvalidIDReferences(parser, parsed_data)) |
440 | result = parser.checkConsistency({'hardware': |
441 | {'hal': {'devices': []}}}) |
442 | self.assertEqual(result, False, |
443 | @@ -2384,16 +2401,18 @@ |
444 | |
445 | def testConsistencyCheckWithDuplicateUDI(self): |
446 | """SubmissionParser.checkConsistency detects duplicate UDIs.""" |
447 | - test = self |
448 | - def getUDIDeviceMap(self, parsed_data): |
449 | - test.assertTrue(isinstance(self, SubmissionParser)) |
450 | - raise ValueError( |
451 | - 'Duplicate UDI: /org/freedesktop/Hal/devices/computer') |
452 | - |
453 | - parser = self._setupConsistencyCheckParser() |
454 | + class MockSubmissionParserUDIDeviceMapFails( |
455 | + self.MockSubmissionParser): |
456 | + """A parser where getUDIDeviceMap() fails.""" |
457 | + |
458 | + def getUDIDeviceMap(self, devices): |
459 | + raise ValueError( |
460 | + 'Duplicate UDI: /org/freedesktop/Hal/devices/computer') |
461 | + |
462 | + validate_mock_class(MockSubmissionParserUDIDeviceMapFails) |
463 | + |
464 | + parser = MockSubmissionParserUDIDeviceMapFails(self.log) |
465 | parser.submission_key = 'Consistency check detects invalid ID refs' |
466 | - parser.getUDIDeviceMap = ( |
467 | - lambda devices: getUDIDeviceMap(parser, devices)) |
468 | result = parser.checkConsistency({'hardware': |
469 | {'hal': {'devices': []}}}) |
470 | self.assertEqual(result, False, |
471 | @@ -2404,15 +2423,17 @@ |
472 | |
473 | def testConsistencyCheckChildUDIWithoutParent(self): |
474 | """SubmissionParser.checkConsistency detects "orphaned" devices.""" |
475 | - test = self |
476 | - def getUDIChildren(self, udi_device_map): |
477 | - test.assertTrue(isinstance(self, SubmissionParser)) |
478 | - raise ValueError('Unknown parent UDI /foo in <device id="3">') |
479 | - |
480 | - parser = self._setupConsistencyCheckParser() |
481 | + class MockSubmissionParserUDIChildrenFails( |
482 | + self.MockSubmissionParser): |
483 | + """A parser where getUDIChildren() fails.""" |
484 | + |
485 | + def getUDIChildren(self, udi_device_map): |
486 | + raise ValueError('Unknown parent UDI /foo in <device id="3">') |
487 | + |
488 | + validate_mock_class(MockSubmissionParserUDIChildrenFails) |
489 | + |
490 | + parser = MockSubmissionParserUDIChildrenFails(self.log) |
491 | parser.submission_key = 'Consistency check detects invalid ID refs' |
492 | - parser.getUDIChildren = ( |
493 | - lambda udi_device_map: getUDIChildren(parser, udi_device_map)) |
494 | result = parser.checkConsistency({'hardware': |
495 | {'hal': {'devices': []}}}) |
496 | self.assertEqual(result, False, |
497 | @@ -2423,17 +2444,21 @@ |
498 | |
499 | def testConsistencyCheckCircularParentChildRelation(self): |
500 | """SubmissionParser.checkConsistency detects "orphaned" devices.""" |
501 | - test = self |
502 | - def checkHALDevicesParentChildConsistency(self, devices): |
503 | - test.assertTrue(isinstance(self, SubmissionParser)) |
504 | - return ['/foo', '/bar'] |
505 | - |
506 | - parser = self._setupConsistencyCheckParser() |
507 | + class MockSubmissionParserHALDevicesParentChildConsistency( |
508 | + self.MockSubmissionParser): |
509 | + """A parser where checkHALDevicesParentChildConsistency() fails. |
510 | + """ |
511 | + |
512 | + def checkHALDevicesParentChildConsistency(self, udi_children): |
513 | + return ['/foo', '/bar'] |
514 | + |
515 | + validate_mock_class( |
516 | + MockSubmissionParserHALDevicesParentChildConsistency) |
517 | + |
518 | + parser = MockSubmissionParserHALDevicesParentChildConsistency( |
519 | + self.log) |
520 | parser.submission_key = ('Consistency check detects circular ' |
521 | 'parent-child relationships') |
522 | - parser.checkHALDevicesParentChildConsistency = ( |
523 | - lambda devices: checkHALDevicesParentChildConsistency( |
524 | - parser, devices)) |
525 | result = parser.checkConsistency({'hardware': |
526 | {'hal': {'devices': []}}}) |
527 | self.assertEqual(result, False, |
528 | |
529 | === modified file 'lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py' |
530 | --- lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-22 11:08:18 +0000 |
531 | +++ lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-26 12:05:33 +0000 |
532 | @@ -1672,8 +1672,14 @@ |
533 | parser = SubmissionParser(self.log) |
534 | |
535 | ignored_buses = ( |
536 | - 'drm', 'dvb', 'memstick_host', 'net', 'scsi_generic', 'scsi_host', |
537 | - 'sound', 'ssb', 'tty', 'usb', 'video4linux', ) |
538 | + 'ac97', 'disk', 'drm', 'drm_minor', 'dvb', 'enclosure', |
539 | + 'gameport', 'hid', 'host', 'ieee80211', 'link', 'lirc', |
540 | + 'memstick_host', 'net', 'partition', 'pci_express', |
541 | + 'pcmcia_socket', 'scsi_disk', 'scsi_generic', 'scsi_host', |
542 | + 'scsi_target', 'sound', 'spi_host', 'spi_transport', 'ssb', |
543 | + 'tifm', 'tifm_adapter', 'tty', 'usb', 'usb-serial', |
544 | + 'usb_endpoint', 'usb_host', 'usb_interface', 'video4linux', |
545 | + 'wlan') |
546 | for tested_bus in ignored_buses: |
547 | properties['info.bus'] = (tested_bus, 'str') |
548 | parser.buildHalDeviceList(parsed_data) |
549 | @@ -2004,9 +2010,12 @@ |
550 | } |
551 | parser = SubmissionParser(self.log) |
552 | properties = devices[0]['properties'] |
553 | - for bus in ('backlight', 'bluetooth', 'ieee1394', 'input', 'misc', |
554 | - 'mmc', 'mmc_host', 'pcmcia', 'platform', 'pnp', |
555 | - 'power_supply', 'unknown'): |
556 | + for bus in ('backlight', 'bdi', 'bluetooth', 'dmi', 'heci', 'hidraw', |
557 | + 'hwmon', 'i2c-adapter', 'ieee1394', 'input', 'leds', 'mem', |
558 | + 'misc', 'mmc', 'mmc_host', 'pci_bus', 'pcmcia', 'platform', |
559 | + 'pnp', 'power_supply', 'ppdev', 'ppp', 'printer', 'rfkill', |
560 | + 'thermal', 'ttm', 'unknown', 'vc', 'video_output', |
561 | + 'vtconsole'): |
562 | properties['info.bus'] = (bus, 'str') |
563 | parser.buildHalDeviceList(parsed_data) |
564 | device = parser.devices[self.UDI_SATA_CONTROLLER] |
565 | |
566 | === modified file 'lib/lp/testing/__init__.py' |
567 | --- lib/lp/testing/__init__.py 2009-10-23 11:07:32 +0000 |
568 | +++ lib/lp/testing/__init__.py 2009-10-26 12:05:33 +0000 |
569 | @@ -8,6 +8,7 @@ |
570 | from datetime import datetime, timedelta |
571 | from pprint import pformat |
572 | import copy |
573 | +from inspect import getargspec, getmembers, getmro, isclass, ismethod |
574 | import os |
575 | import shutil |
576 | import subprocess |
577 | @@ -793,3 +794,69 @@ |
578 | tree.unlock() |
579 | |
580 | return contents |
581 | + |
582 | +def validate_mock_class(mock_class): |
583 | + """Validate method signatures in mock classes derived from real classes. |
584 | + |
585 | + We often use mock classes in tests which are derived from real |
586 | + classes. |
587 | + |
588 | + This function ensures that methods redefined in the mock |
589 | + class have the same signature as the corresponding methods of |
590 | + the base class. |
591 | + |
592 | + >>> class A: |
593 | + ... |
594 | + ... def method_one(self, a): |
595 | + ... pass |
596 | + |
597 | + >>> |
598 | + >>> class B(A): |
599 | + ... def method_one(self, a): |
600 | + ... pass |
601 | + >>> validate_mock_class(B) |
602 | + |
603 | + If a class derived from A defines method_one with a different |
604 | + signature, we get an AssertionError. |
605 | + |
606 | + >>> class C(A): |
607 | + ... def method_one(self, a, b): |
608 | + ... pass |
609 | + >>> validate_mock_class(C) |
610 | + Traceback (most recent call last): |
611 | + ... |
612 | + AssertionError: Different method signature for method_one:... |
613 | + |
614 | + Even a parameter name must not be modified. |
615 | + |
616 | + >>> class D(A): |
617 | + ... def method_one(self, b): |
618 | + ... pass |
619 | + >>> validate_mock_class(D) |
620 | + Traceback (most recent call last): |
621 | + ... |
622 | + AssertionError: Different method signature for method_one:... |
623 | + |
624 | + If validate_mock_class() for anything but a class, we get an |
625 | + AssertionError. |
626 | + |
627 | + >>> validate_mock_class('a string') |
628 | + Traceback (most recent call last): |
629 | + ... |
630 | + AssertionError: validate_mock_class() must be called for a class |
631 | + """ |
632 | + assert isclass(mock_class), ( |
633 | + "validate_mock_class() must be called for a class") |
634 | + base_classes = getmro(mock_class) |
635 | + for name, obj in getmembers(mock_class): |
636 | + if ismethod(obj): |
637 | + for base_class in base_classes[1:]: |
638 | + if name in base_class.__dict__: |
639 | + mock_args = getargspec(obj) |
640 | + real_args = getargspec(base_class.__dict__[name]) |
641 | + if mock_args != real_args: |
642 | + raise AssertionError( |
643 | + 'Different method signature for %s: %r %r' % ( |
644 | + name, mock_args, real_args)) |
645 | + else: |
646 | + break |
647 | |
648 | === added file 'lib/lp/testing/tests/test_inlinetests.py' |
649 | --- lib/lp/testing/tests/test_inlinetests.py 1970-01-01 00:00:00 +0000 |
650 | +++ lib/lp/testing/tests/test_inlinetests.py 2009-10-26 12:05:33 +0000 |
651 | @@ -0,0 +1,20 @@ |
652 | +# Copyright 2009 Canonical Ltd. This software is licensed under the |
653 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
654 | + |
655 | +"""Run the doc string tests.""" |
656 | + |
657 | +import doctest |
658 | + |
659 | +from zope.testing.doctest import NORMALIZE_WHITESPACE, ELLIPSIS |
660 | + |
661 | +from canonical.launchpad.testing.systemdocs import LayeredDocFileSuite |
662 | +from canonical.testing import BaseLayer |
663 | +from lp import testing |
664 | + |
665 | +def test_suite(): |
666 | + suite = LayeredDocFileSuite( |
667 | + layer=BaseLayer) |
668 | + suite.addTest(doctest.DocTestSuite( |
669 | + testing, optionflags=NORMALIZE_WHITESPACE|ELLIPSIS)) |
670 | + return suite |
671 | + |
The property BaseDevice.raw_bus is used to determine if a udev/HAL node represents a real, physical device or if it defines only an "aspect" of a physical device (property BaseDevice. is_real_ device) . It is also used to determine if the submitted data contains enough details, like a vendor and product ID, for the given kind of device so that we can store it in our database (property BaseDevice. has_reliable_ data).
In submissions with udev data we have a number of raw_bus values not used in HAL for nodes that either represent physical devices for which we don't have enough data or that represent only "aspects" or real devices. This leads to lots of warnings in the processing log.
This branch update BaseDevice. has_reliable_ data and BaseDevice. is_real_ device so that udev nodes with the "new" values of baseDevice.raw_bus are treated properly and do not produced warnings in the processing log.
test: ./bin/test --test= 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: /launchpad/ scripts/ hwdbsubmissions .py /launchpad/ scripts/ tests/test_ hwdb_submission _processing. py
lib/canonical
lib/canonical
== Pyflakes notices ==
lib/canonical/ launchpad/ scripts/ hwdbsubmissions .py
22: redefinition of unused 'etree' from line 20
== Pylint notices ==
lib/canonical/ launchpad/ scripts/ hwdbsubmissions .py cElementTree' (No module named etree)
20: [F0401] Unable to import 'xml.etree.
This branch is based on lp:~adeuring/launchpad/bug-460935-hwdb-better-consistency-check-udev-usb-devices, which is reviewed but is mot yet merged. The diff against this brnach:
=== modified file 'lib/canonical/ launchpad/ scripts/ hwdbsubmissions .py' launchpad/ scripts/ hwdbsubmissions .py 2009-10-26 10:01:51 +0000 launchpad/ scripts/ hwdbsubmissions .py 2009-10-26 11:35:15 +0000
--- lib/canonical/
+++ lib/canonical/
@@ -1969,6 +1969,40 @@
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
+ ...