Merge lp:~adeuring/launchpad/hwdb-class-udev-device-2 into lp:launchpad
- hwdb-class-udev-device-2
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Barry Warsaw |
Approved revision: | no longer in the source branch. |
Merged at revision: | not available |
Proposed branch: | lp:~adeuring/launchpad/hwdb-class-udev-device-2 |
Merge into: | lp:launchpad |
Diff against target: |
456 lines 3 files modified
lib/canonical/launchpad/scripts/hwdbsubmissions.py (+109/-4) lib/canonical/launchpad/scripts/tests/test_hwdb_submission_parser.py (+150/-7) lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py (+54/-0) |
To merge this branch: | bzr merge lp:~adeuring/launchpad/hwdb-class-udev-device-2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Barry Warsaw (community) | Approve | ||
Review via email: mp+13118@code.launchpad.net |
Commit message
Description of the change
Abel Deuring (adeuring) wrote : | # |
Barry Warsaw (barry) wrote : | # |
Hi Abel,
A rather terse review from me today, since I'm also the CHR. I have some
comments that should be easy to address. r=me, merge-conditional with their
consideration.
review approve
status approve
-Barry
=== modified file 'lib/canonical/
--- lib/canonical/
+++ lib/canonical/
> @@ -1212,11 +1212,83 @@
> return False
> return True
>
> + USB_DEVICE_
> + usb_product_re = re.compile(
> + '^[0-9a-
> + usb_type_re = re.compile(
Is there a reason these are class attributes? They probably make more sense
being module globals, with all-caps names.
> +
> + def checkUdevUsbPro
> + """Validation of udev USB devices.
> +
> + USB devices must have the properties DEVTYPE (value
> + 'usb_device' or 'usb_interface'), PRODUCT and TYPE. 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[
> + if subsystem != 'usb':
> + continue
> + properties = device['E']
> + property_names = set(properties.
Since 'properties' is a dictionary, this is more efficient:
> + existing_
> + self.USB_
> + if existing_
> + self._logError(
> + 'USB udev device found without required properties: %r %r'
> + % (self.USB_
> + existing_
> + device['P']),
This is somewhat unreadable. You should move the .difference() calculation to
above the self._logError() call and stash it in a local variable, then use the
local variable in the interpolation.
> + self.submission
> + return False
> + if self.usb_
> + self._logError(
> + 'USB udev device found with invalid product ID: %r %r'
> + % (properties[
> + self.submission
> + return False
> + if self.usb_
> + self._logError(
> + 'USB udev device found with invalid type data: %r %r'
> + % (properties[
> + ...
Preview Diff
1 | === modified file 'lib/canonical/launchpad/scripts/hwdbsubmissions.py' | |||
2 | --- lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-08 13:41:32 +0000 | |||
3 | +++ lib/canonical/launchpad/scripts/hwdbsubmissions.py 2009-10-09 15:45:23 +0000 | |||
4 | @@ -94,6 +94,11 @@ | |||
5 | 94 | 'scsi': '%-16s', | 94 | 'scsi': '%-16s', |
6 | 95 | } | 95 | } |
7 | 96 | 96 | ||
8 | 97 | UDEV_USB_DEVICE_PROPERTIES = set(('DEVTYPE', 'PRODUCT', 'TYPE')) | ||
9 | 98 | UDEV_USB_PRODUCT_RE = re.compile( | ||
10 | 99 | '^[0-9a-f]{1,4}/[0-9a-f]{1,4}/[0-9a-f]{1,4}$', re.I) | ||
11 | 100 | UDEV_USB_TYPE_RE = re.compile('^[0-9]{1,3}/[0-9]{1,3}/[0-9]{1,3}$') | ||
12 | 101 | |||
13 | 97 | class SubmissionParser(object): | 102 | class SubmissionParser(object): |
14 | 98 | """A Parser for the submissions to the hardware database.""" | 103 | """A Parser for the submissions to the hardware database.""" |
15 | 99 | 104 | ||
16 | @@ -1165,7 +1170,7 @@ | |||
17 | 1165 | """ | 1170 | """ |
18 | 1166 | for device in udev_data: | 1171 | for device in udev_data: |
19 | 1167 | properties = device['E'] | 1172 | properties = device['E'] |
21 | 1168 | property_names = set(properties.keys()) | 1173 | property_names = set(properties) |
22 | 1169 | existing_pci_properties = property_names.intersection( | 1174 | existing_pci_properties = property_names.intersection( |
23 | 1170 | self.PCI_PROPERTIES) | 1175 | self.PCI_PROPERTIES) |
24 | 1171 | subsystem = device['E'].get('SUBSYSTEM') | 1176 | subsystem = device['E'].get('SUBSYSTEM') |
25 | @@ -1212,11 +1217,78 @@ | |||
26 | 1212 | return False | 1217 | return False |
27 | 1213 | return True | 1218 | return True |
28 | 1214 | 1219 | ||
29 | 1220 | def checkUdevUsbProperties(self, udev_data): | ||
30 | 1221 | """Validation of udev USB devices. | ||
31 | 1222 | |||
32 | 1223 | USB devices must have the properties DEVTYPE (value | ||
33 | 1224 | 'usb_device' or 'usb_interface'), PRODUCT and TYPE. PRODUCT | ||
34 | 1225 | must be a tuple of three integers in hexadecimal | ||
35 | 1226 | representation, separates by '/'. TYPE must be a a tuple of | ||
36 | 1227 | three integers in decimal representation, separated by '/'. | ||
37 | 1228 | usb_interface nodes must additionally have a property | ||
38 | 1229 | INTERFACE, containing three integers in the same format as | ||
39 | 1230 | TYPE. | ||
40 | 1231 | """ | ||
41 | 1232 | for device in udev_data: | ||
42 | 1233 | subsystem = device['E'].get('SUBSYSTEM') | ||
43 | 1234 | if subsystem != 'usb': | ||
44 | 1235 | continue | ||
45 | 1236 | properties = device['E'] | ||
46 | 1237 | property_names = set(properties) | ||
47 | 1238 | existing_usb_properties = property_names.intersection( | ||
48 | 1239 | UDEV_USB_DEVICE_PROPERTIES) | ||
49 | 1240 | if existing_usb_properties != UDEV_USB_DEVICE_PROPERTIES: | ||
50 | 1241 | missing_properties = UDEV_USB_DEVICE_PROPERTIES.difference( | ||
51 | 1242 | existing_usb_properties) | ||
52 | 1243 | self._logError( | ||
53 | 1244 | 'USB udev device found without required properties: %r %r' | ||
54 | 1245 | % (missing_properties, device['P']), | ||
55 | 1246 | self.submission_key) | ||
56 | 1247 | return False | ||
57 | 1248 | if UDEV_USB_PRODUCT_RE.search(properties['PRODUCT']) is None: | ||
58 | 1249 | self._logError( | ||
59 | 1250 | 'USB udev device found with invalid product ID: %r %r' | ||
60 | 1251 | % (properties['PRODUCT'], device['P']), | ||
61 | 1252 | self.submission_key) | ||
62 | 1253 | return False | ||
63 | 1254 | if UDEV_USB_TYPE_RE.search(properties['TYPE']) is None: | ||
64 | 1255 | self._logError( | ||
65 | 1256 | 'USB udev device found with invalid type data: %r %r' | ||
66 | 1257 | % (properties['TYPE'], device['P']), | ||
67 | 1258 | self.submission_key) | ||
68 | 1259 | return False | ||
69 | 1260 | |||
70 | 1261 | device_type = properties['DEVTYPE'] | ||
71 | 1262 | if device_type not in ('usb_device', 'usb_interface'): | ||
72 | 1263 | self._logError( | ||
73 | 1264 | 'USB udev device found with invalid udev type data: %r %r' | ||
74 | 1265 | % (device_type, device['P']), | ||
75 | 1266 | self.submission_key) | ||
76 | 1267 | return False | ||
77 | 1268 | if device_type == 'usb_interface': | ||
78 | 1269 | interface_type = properties.get('INTERFACE') | ||
79 | 1270 | if interface_type is None: | ||
80 | 1271 | self._logError( | ||
81 | 1272 | 'USB interface udev device found without INTERFACE ' | ||
82 | 1273 | 'property: %r' | ||
83 | 1274 | % device['P'], | ||
84 | 1275 | self.submission_key) | ||
85 | 1276 | return False | ||
86 | 1277 | if UDEV_USB_TYPE_RE.search(interface_type) is None: | ||
87 | 1278 | self._logError( | ||
88 | 1279 | 'USB Interface udev device found with invalid ' | ||
89 | 1280 | 'INTERFACE property: %r %r' | ||
90 | 1281 | % (interface_type, device['P']), | ||
91 | 1282 | self.submission_key) | ||
92 | 1283 | return False | ||
93 | 1284 | return True | ||
94 | 1285 | |||
95 | 1215 | def checkConsistentUdevDeviceData(self, udev_data): | 1286 | def checkConsistentUdevDeviceData(self, udev_data): |
96 | 1216 | """Consistency checks for udev data.""" | 1287 | """Consistency checks for udev data.""" |
100 | 1217 | if not self.checkUdevDictsHavePathKey(udev_data): | 1288 | return ( |
101 | 1218 | return False | 1289 | self.checkUdevDictsHavePathKey(udev_data) and |
102 | 1219 | return self.checkUdevPciProperties(udev_data) | 1290 | self.checkUdevPciProperties(udev_data) and |
103 | 1291 | self.checkUdevUsbProperties(udev_data)) | ||
104 | 1220 | 1292 | ||
105 | 1221 | def checkConsistency(self, parsed_data): | 1293 | def checkConsistency(self, parsed_data): |
106 | 1222 | """Run consistency checks on the submitted data. | 1294 | """Run consistency checks on the submitted data. |
107 | @@ -2278,6 +2350,39 @@ | |||
108 | 2278 | """See `BaseDevice`.""" | 2350 | """See `BaseDevice`.""" |
109 | 2279 | return self.pci_class_info[1] | 2351 | return self.pci_class_info[1] |
110 | 2280 | 2352 | ||
111 | 2353 | @property | ||
112 | 2354 | def is_usb(self): | ||
113 | 2355 | """True, if this is a USB device, else False.""" | ||
114 | 2356 | return self.udev['E'].get('SUBSYSTEM') == 'usb' | ||
115 | 2357 | |||
116 | 2358 | @property | ||
117 | 2359 | def usb_ids(self): | ||
118 | 2360 | """The vendor ID, product ID, product version for USB devices. | ||
119 | 2361 | |||
120 | 2362 | :return: [vendor_id, product_id, version] for USB devices | ||
121 | 2363 | or [None, None, None] for other devices. | ||
122 | 2364 | """ | ||
123 | 2365 | if self.is_usb: | ||
124 | 2366 | # udev represents USB device IDs as strings | ||
125 | 2367 | # vendor_id/prodct_id/version, where each part is | ||
126 | 2368 | # as a hexdecimal number. | ||
127 | 2369 | # SubmissionParser.checkUdevUsbProperties() ensures that | ||
128 | 2370 | # the string PRODUCT is in the format required below. | ||
129 | 2371 | product_info = self.udev['E']['PRODUCT'].split('/') | ||
130 | 2372 | return [int(part, 16) for part in product_info] | ||
131 | 2373 | else: | ||
132 | 2374 | return [None, None, None] | ||
133 | 2375 | |||
134 | 2376 | @property | ||
135 | 2377 | def usb_vendor_id(self): | ||
136 | 2378 | """See `BaseDevice`.""" | ||
137 | 2379 | return self.usb_ids[0] | ||
138 | 2380 | |||
139 | 2381 | @property | ||
140 | 2382 | def usb_product_id(self): | ||
141 | 2383 | """See `BaseDevice`.""" | ||
142 | 2384 | return self.usb_ids[1] | ||
143 | 2385 | |||
144 | 2281 | 2386 | ||
145 | 2282 | class ProcessingLoop(object): | 2387 | class ProcessingLoop(object): |
146 | 2283 | """An `ITunableLoop` for processing HWDB submissions.""" | 2388 | """An `ITunableLoop` for processing HWDB submissions.""" |
147 | 2284 | 2389 | ||
148 | === modified file 'lib/canonical/launchpad/scripts/tests/test_hwdb_submission_parser.py' | |||
149 | --- lib/canonical/launchpad/scripts/tests/test_hwdb_submission_parser.py 2009-10-08 13:41:32 +0000 | |||
150 | +++ lib/canonical/launchpad/scripts/tests/test_hwdb_submission_parser.py 2009-10-09 15:45:23 +0000 | |||
151 | @@ -112,6 +112,25 @@ | |||
152 | 112 | 'PCI_SLOT_NAME': '0000:00:1f.2', | 112 | 'PCI_SLOT_NAME': '0000:00:1f.2', |
153 | 113 | } | 113 | } |
154 | 114 | } | 114 | } |
155 | 115 | self.udev_usb_device = { | ||
156 | 116 | 'P': '/devices/pci0000:00/0000:00:1d.1/usb3/3-2', | ||
157 | 117 | 'E': { | ||
158 | 118 | 'SUBSYSTEM': 'usb', | ||
159 | 119 | 'DEVTYPE': 'usb_device', | ||
160 | 120 | 'PRODUCT': '46d/a01/1013', | ||
161 | 121 | 'TYPE': '0/0/0', | ||
162 | 122 | }, | ||
163 | 123 | } | ||
164 | 124 | self.udev_usb_interface = { | ||
165 | 125 | 'P': '/devices/pci0000:00/0000:00:1d.1/usb3/3-2/3-2:1.1', | ||
166 | 126 | 'E': { | ||
167 | 127 | 'SUBSYSTEM': 'usb', | ||
168 | 128 | 'DEVTYPE': 'usb_interface', | ||
169 | 129 | 'PRODUCT': '46d/a01/1013', | ||
170 | 130 | 'TYPE': '0/0/0', | ||
171 | 131 | 'INTERFACE': '1/2/0', | ||
172 | 132 | }, | ||
173 | 133 | } | ||
174 | 115 | 134 | ||
175 | 116 | def getTimestampETreeNode(self, time_string): | 135 | def getTimestampETreeNode(self, time_string): |
176 | 117 | """Return an Elementtree node for an XML tag with a timestamp.""" | 136 | """Return an Elementtree node for an XML tag with a timestamp.""" |
177 | @@ -1788,7 +1807,7 @@ | |||
178 | 1788 | parser.submission_key, 'udev node found without a "P" key') | 1807 | parser.submission_key, 'udev node found without a "P" key') |
179 | 1789 | 1808 | ||
180 | 1790 | def testCheckUdevPciProperties(self): | 1809 | def testCheckUdevPciProperties(self): |
182 | 1791 | """Test of SubmmissionParser.checkUdevPciProperties().""" | 1810 | """Test of SubmissionParser.checkUdevPciProperties().""" |
183 | 1792 | # udev PCI devices must have the properties PCI_CLASS, PCI_ID, | 1811 | # udev PCI devices must have the properties PCI_CLASS, PCI_ID, |
184 | 1793 | # PCI_SUBSYS_ID, PCI_SLOT_NAME; other devices must not have | 1812 | # PCI_SUBSYS_ID, PCI_SLOT_NAME; other devices must not have |
185 | 1794 | # these properties. | 1813 | # these properties. |
186 | @@ -1797,7 +1816,7 @@ | |||
187 | 1797 | [self.udev_root_device, self.udev_pci_device])) | 1816 | [self.udev_root_device, self.udev_pci_device])) |
188 | 1798 | 1817 | ||
189 | 1799 | def testCheckUdevPciPropertiesNonPciDeviceWithPciProperties(self): | 1818 | def testCheckUdevPciPropertiesNonPciDeviceWithPciProperties(self): |
191 | 1800 | """Test of SubmmissionParser.checkUdevPciProperties(). | 1819 | """Test of SubmissionParser.checkUdevPciProperties(). |
192 | 1801 | 1820 | ||
193 | 1802 | A non-PCI device having PCI properties makes a submission invalid. | 1821 | A non-PCI device having PCI properties makes a submission invalid. |
194 | 1803 | """ | 1822 | """ |
195 | @@ -1812,7 +1831,7 @@ | |||
196 | 1812 | "'/devices/LNXSYSTM:00'") | 1831 | "'/devices/LNXSYSTM:00'") |
197 | 1813 | 1832 | ||
198 | 1814 | def testCheckUdevPciPropertiesPciDeviceWithoutRequiredProperties(self): | 1833 | def testCheckUdevPciPropertiesPciDeviceWithoutRequiredProperties(self): |
200 | 1815 | """Test of SubmmissionParser.checkUdevPciProperties(). | 1834 | """Test of SubmissionParser.checkUdevPciProperties(). |
201 | 1816 | 1835 | ||
202 | 1817 | A PCI device not having a required PCI property makes a submission | 1836 | A PCI device not having a required PCI property makes a submission |
203 | 1818 | invalid. | 1837 | invalid. |
204 | @@ -1828,7 +1847,7 @@ | |||
205 | 1828 | "set(['PCI_CLASS']) '/devices/pci0000:00/0000:00:1f.2'") | 1847 | "set(['PCI_CLASS']) '/devices/pci0000:00/0000:00:1f.2'") |
206 | 1829 | 1848 | ||
207 | 1830 | def testCheckUdevPciPropertiesPciDeviceWithNonIntegerPciClass(self): | 1849 | def testCheckUdevPciPropertiesPciDeviceWithNonIntegerPciClass(self): |
209 | 1831 | """Test of SubmmissionParser.checkUdevPciProperties(). | 1850 | """Test of SubmissionParser.checkUdevPciProperties(). |
210 | 1832 | 1851 | ||
211 | 1833 | A PCI device with a non-integer class value makes a submission | 1852 | A PCI device with a non-integer class value makes a submission |
212 | 1834 | invalid. | 1853 | invalid. |
213 | @@ -1844,7 +1863,7 @@ | |||
214 | 1844 | "'/devices/pci0000:00/0000:00:1f.2'") | 1863 | "'/devices/pci0000:00/0000:00:1f.2'") |
215 | 1845 | 1864 | ||
216 | 1846 | def testCheckUdevPciPropertiesPciDeviceWithInvalidPciClassValue(self): | 1865 | def testCheckUdevPciPropertiesPciDeviceWithInvalidPciClassValue(self): |
218 | 1847 | """Test of SubmmissionParser.checkUdevPciProperties(). | 1866 | """Test of SubmissionParser.checkUdevPciProperties(). |
219 | 1848 | 1867 | ||
220 | 1849 | A PCI device with invalid class data makes a submission | 1868 | A PCI device with invalid class data makes a submission |
221 | 1850 | invalid. | 1869 | invalid. |
222 | @@ -1860,7 +1879,7 @@ | |||
223 | 1860 | "'/devices/pci0000:00/0000:00:1f.2'") | 1879 | "'/devices/pci0000:00/0000:00:1f.2'") |
224 | 1861 | 1880 | ||
225 | 1862 | def testCheckUdevPciPropertiesPciDeviceWithInvalidDeviceID(self): | 1881 | def testCheckUdevPciPropertiesPciDeviceWithInvalidDeviceID(self): |
227 | 1863 | """Test of SubmmissionParser.checkUdevPciProperties(). | 1882 | """Test of SubmissionParser.checkUdevPciProperties(). |
228 | 1864 | 1883 | ||
229 | 1865 | A PCI device with an invalid device ID makes a submission | 1884 | A PCI device with an invalid device ID makes a submission |
230 | 1866 | invalid. | 1885 | invalid. |
231 | @@ -1876,7 +1895,7 @@ | |||
232 | 1876 | "'/devices/pci0000:00/0000:00:1f.2'") | 1895 | "'/devices/pci0000:00/0000:00:1f.2'") |
233 | 1877 | 1896 | ||
234 | 1878 | def testCheckUdevPciPropertiesPciDeviceWithInvalidSubsystemID(self): | 1897 | def testCheckUdevPciPropertiesPciDeviceWithInvalidSubsystemID(self): |
236 | 1879 | """Test of SubmmissionParser.checkUdevPciProperties(). | 1898 | """Test of SubmissionParser.checkUdevPciProperties(). |
237 | 1880 | 1899 | ||
238 | 1881 | A PCI device with an invalid subsystem ID makes a submission | 1900 | A PCI device with an invalid subsystem ID makes a submission |
239 | 1882 | invalid. | 1901 | invalid. |
240 | @@ -1891,6 +1910,110 @@ | |||
241 | 1891 | "Invalid udev PCI device ID: 'not-a-subsystem-id' " | 1910 | "Invalid udev PCI device ID: 'not-a-subsystem-id' " |
242 | 1892 | "'/devices/pci0000:00/0000:00:1f.2'") | 1911 | "'/devices/pci0000:00/0000:00:1f.2'") |
243 | 1893 | 1912 | ||
244 | 1913 | def testCheckUdevUsbProperties(self): | ||
245 | 1914 | """Test of SubmissionParser.checkUdevUsbProperties().""" | ||
246 | 1915 | parser = SubmissionParser() | ||
247 | 1916 | self.assertTrue(parser.checkUdevUsbProperties( | ||
248 | 1917 | [self.udev_root_device, self.udev_usb_device, | ||
249 | 1918 | self.udev_usb_interface])) | ||
250 | 1919 | |||
251 | 1920 | def testCheckUdevUsbProperties_missing_required_property(self): | ||
252 | 1921 | """Test of SubmissionParser.checkUdevUsbProperties(). | ||
253 | 1922 | |||
254 | 1923 | A USB device that does not have a required property makes a | ||
255 | 1924 | submission invalid. | ||
256 | 1925 | """ | ||
257 | 1926 | del self.udev_usb_device['E']['DEVTYPE'] | ||
258 | 1927 | parser = SubmissionParser(self.log) | ||
259 | 1928 | parser.submission_key = 'USB device without DEVTYPE property' | ||
260 | 1929 | self.assertFalse(parser.checkUdevUsbProperties( | ||
261 | 1930 | [self.udev_root_device, self.udev_usb_device])) | ||
262 | 1931 | self.assertErrorMessage( | ||
263 | 1932 | parser.submission_key, | ||
264 | 1933 | "USB udev device found without required properties: " | ||
265 | 1934 | "set(['DEVTYPE']) '/devices/pci0000:00/0000:00:1d.1/usb3/3-2'") | ||
266 | 1935 | |||
267 | 1936 | def testCheckUdevUsbProperties_with_invalid_product_id(self): | ||
268 | 1937 | """Test of SubmissionParser.checkUdevUsbProperties(). | ||
269 | 1938 | |||
270 | 1939 | A USB device with an invalid product ID makes a submission | ||
271 | 1940 | invalid. | ||
272 | 1941 | """ | ||
273 | 1942 | self.udev_usb_device['E']['PRODUCT'] = 'not-a-valid-usb-product-id' | ||
274 | 1943 | parser = SubmissionParser(self.log) | ||
275 | 1944 | parser.submission_key = 'USB device with invalid product ID' | ||
276 | 1945 | self.assertFalse(parser.checkUdevUsbProperties( | ||
277 | 1946 | [self.udev_root_device, self.udev_usb_device])) | ||
278 | 1947 | self.assertErrorMessage( | ||
279 | 1948 | parser.submission_key, | ||
280 | 1949 | "USB udev device found with invalid product ID: " | ||
281 | 1950 | "'not-a-valid-usb-product-id' " | ||
282 | 1951 | "'/devices/pci0000:00/0000:00:1d.1/usb3/3-2'") | ||
283 | 1952 | |||
284 | 1953 | def testCheckUdevUsbProperties_with_invalid_type_data(self): | ||
285 | 1954 | """Test of SubmmissionParser.checkUdevUsbProperties(). | ||
286 | 1955 | |||
287 | 1956 | A USB device with invalid type data makes a submission invalid. | ||
288 | 1957 | """ | ||
289 | 1958 | self.udev_usb_device['E']['TYPE'] = 'no-type' | ||
290 | 1959 | parser = SubmissionParser(self.log) | ||
291 | 1960 | parser.submission_key = 'USB device with invalid type data' | ||
292 | 1961 | self.assertFalse(parser.checkUdevUsbProperties( | ||
293 | 1962 | [self.udev_root_device, self.udev_usb_device])) | ||
294 | 1963 | self.assertErrorMessage( | ||
295 | 1964 | parser.submission_key, | ||
296 | 1965 | "USB udev device found with invalid type data: 'no-type' " | ||
297 | 1966 | "'/devices/pci0000:00/0000:00:1d.1/usb3/3-2'") | ||
298 | 1967 | |||
299 | 1968 | def testCheckUdevUsbProperties_with_invalid_devtype(self): | ||
300 | 1969 | """Test of SubmmissionParser.checkUdevUsbProperties(). | ||
301 | 1970 | |||
302 | 1971 | A udev USB device must have DEVTYPE set to 'usb_device' or | ||
303 | 1972 | 'usb_interface'. | ||
304 | 1973 | """ | ||
305 | 1974 | self.udev_usb_device['E']['DEVTYPE'] = 'nonsense' | ||
306 | 1975 | parser = SubmissionParser(self.log) | ||
307 | 1976 | parser.submission_key = 'USB device with invalid DEVTYPE' | ||
308 | 1977 | self.assertFalse(parser.checkUdevUsbProperties( | ||
309 | 1978 | [self.udev_root_device, self.udev_usb_device])) | ||
310 | 1979 | self.assertErrorMessage( | ||
311 | 1980 | parser.submission_key, | ||
312 | 1981 | "USB udev device found with invalid udev type data: 'nonsense' " | ||
313 | 1982 | "'/devices/pci0000:00/0000:00:1d.1/usb3/3-2'") | ||
314 | 1983 | |||
315 | 1984 | def testCheckUdevUsbProperties_interface_without_interface_property(self): | ||
316 | 1985 | """Test of SubmmissionParser.checkUdevUsbProperties(). | ||
317 | 1986 | |||
318 | 1987 | A udev USB device for a USB interface have the property INTERFACE. | ||
319 | 1988 | """ | ||
320 | 1989 | del self.udev_usb_interface['E']['INTERFACE'] | ||
321 | 1990 | parser = SubmissionParser(self.log) | ||
322 | 1991 | parser.submission_key = 'USB interface without INTERFACE property' | ||
323 | 1992 | self.assertFalse(parser.checkUdevUsbProperties( | ||
324 | 1993 | [self.udev_root_device, self.udev_usb_interface])) | ||
325 | 1994 | self.assertErrorMessage( | ||
326 | 1995 | parser.submission_key, | ||
327 | 1996 | "USB interface udev device found without INTERFACE property: " | ||
328 | 1997 | "'/devices/pci0000:00/0000:00:1d.1/usb3/3-2/3-2:1.1'") | ||
329 | 1998 | |||
330 | 1999 | def testCheckUdevUsbProperties_interface_invalid_interface_property(self): | ||
331 | 2000 | """Test of SubmmissionParser.checkUdevUsbProperties(). | ||
332 | 2001 | |||
333 | 2002 | The INTERFACE proeprty of A udev USB device for a USB interface | ||
334 | 2003 | must have value in the format main_class/sub_class/version | ||
335 | 2004 | """ | ||
336 | 2005 | self.udev_usb_interface['E']['INTERFACE'] = 'nonsense' | ||
337 | 2006 | parser = SubmissionParser(self.log) | ||
338 | 2007 | parser.submission_key = 'USB interface with invalid INTERFACE data' | ||
339 | 2008 | self.assertFalse(parser.checkUdevUsbProperties( | ||
340 | 2009 | [self.udev_root_device, self.udev_usb_interface])) | ||
341 | 2010 | self.assertErrorMessage( | ||
342 | 2011 | parser.submission_key, | ||
343 | 2012 | "USB Interface udev device found with invalid INTERFACE " | ||
344 | 2013 | "property: 'nonsense' " | ||
345 | 2014 | "'/devices/pci0000:00/0000:00:1d.1/usb3/3-2/3-2:1.1'") | ||
346 | 2015 | |||
347 | 2016 | |||
348 | 1894 | class UdevTestSubmissionParser(SubmissionParser): | 2017 | class UdevTestSubmissionParser(SubmissionParser): |
349 | 1895 | """A variant of SubmissionParser that shortcuts udev related tests. | 2018 | """A variant of SubmissionParser that shortcuts udev related tests. |
350 | 1896 | 2019 | ||
351 | @@ -1904,6 +2027,10 @@ | |||
352 | 1904 | """See `SubmissionParser`.""" | 2027 | """See `SubmissionParser`.""" |
353 | 1905 | return True | 2028 | return True |
354 | 1906 | 2029 | ||
355 | 2030 | def checkUdevUsbProperties(self, udev_data): | ||
356 | 2031 | """See `SubmissionParser`.""" | ||
357 | 2032 | return True | ||
358 | 2033 | |||
359 | 1907 | def testCheckConsistentUdevDeviceData(self): | 2034 | def testCheckConsistentUdevDeviceData(self): |
360 | 1908 | """Test of SubmissionParser.checkConsistentUdevDeviceData(),""" | 2035 | """Test of SubmissionParser.checkConsistentUdevDeviceData(),""" |
361 | 1909 | parser = self.UdevTestSubmissionParser() | 2036 | parser = self.UdevTestSubmissionParser() |
362 | @@ -1941,6 +2068,22 @@ | |||
363 | 1941 | parser = SubmissionParserUdevPciCheckFails() | 2068 | parser = SubmissionParserUdevPciCheckFails() |
364 | 1942 | self.assertFalse(parser.checkConsistentUdevDeviceData(None)) | 2069 | self.assertFalse(parser.checkConsistentUdevDeviceData(None)) |
365 | 1943 | 2070 | ||
366 | 2071 | def testCheckConsistentUdevDeviceData_invalid_usb_data(self): | ||
367 | 2072 | """Test of SubmissionParser.checkConsistentUdevDeviceData(), | ||
368 | 2073 | |||
369 | 2074 | Detection of invalid PCI data lets the check fail. | ||
370 | 2075 | """ | ||
371 | 2076 | class SubmissionParserUdevUsbCheckFails( | ||
372 | 2077 | self.UdevTestSubmissionParser): | ||
373 | 2078 | """A SubmissionPaser where checkUdevPciProperties() fails.""" | ||
374 | 2079 | |||
375 | 2080 | def checkUdevUsbProperties(self, udev_data): | ||
376 | 2081 | """See `SubmissionParser`.""" | ||
377 | 2082 | return False | ||
378 | 2083 | |||
379 | 2084 | parser = SubmissionParserUdevUsbCheckFails() | ||
380 | 2085 | self.assertFalse(parser.checkConsistentUdevDeviceData(None)) | ||
381 | 2086 | |||
382 | 1944 | def _setupConsistencyCheckParser(self): | 2087 | def _setupConsistencyCheckParser(self): |
383 | 1945 | """Prepare and return a SubmissionParser instance. | 2088 | """Prepare and return a SubmissionParser instance. |
384 | 1946 | 2089 | ||
385 | 1947 | 2090 | ||
386 | === modified file 'lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py' | |||
387 | --- lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-08 13:41:32 +0000 | |||
388 | +++ lib/canonical/launchpad/scripts/tests/test_hwdb_submission_processing.py 2009-10-09 15:45:23 +0000 | |||
389 | @@ -2617,6 +2617,16 @@ | |||
390 | 2617 | } | 2617 | } |
391 | 2618 | } | 2618 | } |
392 | 2619 | 2619 | ||
393 | 2620 | usb_device_data = { | ||
394 | 2621 | 'P': '/devices/pci0000:00/0000:00:1d.1/usb3/3-2', | ||
395 | 2622 | 'E': { | ||
396 | 2623 | 'SUBSYSTEM': 'usb', | ||
397 | 2624 | 'DEVTYPE': 'usb_device', | ||
398 | 2625 | 'PRODUCT': '46d/a01/1013', | ||
399 | 2626 | 'TYPE': '0/0/0', | ||
400 | 2627 | }, | ||
401 | 2628 | } | ||
402 | 2629 | |||
403 | 2620 | def test_device_id(self): | 2630 | def test_device_id(self): |
404 | 2621 | """Test of UdevDevice.device_id.""" | 2631 | """Test of UdevDevice.device_id.""" |
405 | 2622 | device = UdevDevice(self.pci_device_data, None, None) | 2632 | device = UdevDevice(self.pci_device_data, None, None) |
406 | @@ -2668,6 +2678,50 @@ | |||
407 | 2668 | None, device.pci_class, | 2678 | None, device.pci_class, |
408 | 2669 | 'Invalid value of UdevDevice.pci_class for Non-PCI device.') | 2679 | 'Invalid value of UdevDevice.pci_class for Non-PCI device.') |
409 | 2670 | 2680 | ||
410 | 2681 | def test_is_usb(self): | ||
411 | 2682 | """Test of UdevDevice.is_usb""" | ||
412 | 2683 | device = UdevDevice(self.usb_device_data, None, None) | ||
413 | 2684 | self.assertTrue(device.is_usb) | ||
414 | 2685 | |||
415 | 2686 | device = UdevDevice(self.pci_device_data, None, None) | ||
416 | 2687 | self.assertFalse(device.is_usb) | ||
417 | 2688 | |||
418 | 2689 | def test_usb_ids(self): | ||
419 | 2690 | """Test of UdevDevice.usb_ids""" | ||
420 | 2691 | device = UdevDevice(self.usb_device_data, None, None) | ||
421 | 2692 | self.assertEqual( | ||
422 | 2693 | [0x46d, 0xa01, 0x1013], device.usb_ids, | ||
423 | 2694 | 'Invalid value of UdevDevice.usb_ids for USB device.') | ||
424 | 2695 | |||
425 | 2696 | device = UdevDevice(self.root_device, None, None) | ||
426 | 2697 | self.assertEqual( | ||
427 | 2698 | [None, None, None], device.usb_ids, | ||
428 | 2699 | 'Invalid value of UdevDevice.usb_ids for Non-USB device.') | ||
429 | 2700 | |||
430 | 2701 | def test_usb_vendor_id(self): | ||
431 | 2702 | """Test of UdevDevice.usb_vendor_id""" | ||
432 | 2703 | device = UdevDevice(self.usb_device_data, None, None) | ||
433 | 2704 | self.assertEqual( | ||
434 | 2705 | 0x46d, device.usb_vendor_id, | ||
435 | 2706 | 'Invalid value of UdevDevice.usb_vendor_id for USB device.') | ||
436 | 2707 | |||
437 | 2708 | device = UdevDevice(self.root_device, None, None) | ||
438 | 2709 | self.assertEqual( | ||
439 | 2710 | None, device.usb_vendor_id, | ||
440 | 2711 | 'Invalid value of UdevDevice.usb_vendor_id for Non-USB device.') | ||
441 | 2712 | |||
442 | 2713 | def test_usb_product_id(self): | ||
443 | 2714 | """Test of UdevDevice.usb_product_id""" | ||
444 | 2715 | device = UdevDevice(self.usb_device_data, None, None) | ||
445 | 2716 | self.assertEqual( | ||
446 | 2717 | 0xa01, device.usb_product_id, | ||
447 | 2718 | 'Invalid value of UdevDevice.usb_product_id for USB device.') | ||
448 | 2719 | |||
449 | 2720 | device = UdevDevice(self.root_device, None, None) | ||
450 | 2721 | self.assertEqual( | ||
451 | 2722 | None, device.usb_product_id, | ||
452 | 2723 | 'Invalid value of UdevDevice.usb_product_id for Non-USB device.') | ||
453 | 2724 | |||
454 | 2671 | 2725 | ||
455 | 2672 | class TestHWDBSubmissionTablePopulation(TestCaseHWDB): | 2726 | class TestHWDBSubmissionTablePopulation(TestCaseHWDB): |
456 | 2673 | """Tests of the HWDB popoluation with submitted data.""" | 2727 | """Tests of the HWDB popoluation with submitted data.""" |
This branch adds a few USB-related properties to class UdevDevice, which will be used in the script hwdbsubmisison.py to process HWDB submission coming from the client in Karmic.
The branch also adds some sanity checks for USB-related data, to ensure that required properties exist and that their values have the correct format.
tests:
./bin/test --test= test_hwdb_ submission_ parser/ 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 _parser. py /launchpad/ scripts/ tests/test_ hwdb_submission _processing. py
lib/canonical
lib/canonical
lib/canonical
== 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
10: redefinition of unused 'etree' from line 8
== Pylint notices ==
lib/canonical/ launchpad/ scripts/ hwdbsubmissions .py cElementTree' (No module named etree)
20: [F0401] Unable to import 'xml.etree.
lib/canonical/ launchpad/ scripts/ tests/test_ hwdb_submission _parser. py cElementTree' (No module named etree)
8: [F0401] Unable to import 'xml.etree.
These complaints are not not related to my cheanges; the come from a branch which prepares LP for Python 2.5