Merge lp:~adeuring/launchpad/bug-460976-noise-reduction-for-hwdb-processing-log into lp:launchpad/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
Reviewer Review Type Date Requested Status
Eleanor Berger (community) code Approve
Review via email: mp+13950@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote :
Download full text (8.5 KiB)

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:
  lib/canonical/launchpad/scripts/hwdbsubmissions.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

== Pylint notices ==

lib/canonical/launchpad/scripts/hwdbsubmissions.py
    20: [F0401] Unable to import 'xml.etree.cElementTree' (No module named 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'
--- lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-26 10:01:51 +0000
+++ lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-26 11:35:15 +0000
@@ -1969,6 +1969,40 @@

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

Read more...

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+

Subscribers

People subscribed via source and target branches

to status/vote changes: