Merge lp:~sylvain-pineau/checkbox/more-udisks2-spineau into lp:~zyga/checkbox/more-udisks2
- more-udisks2-spineau
- Merge into more-udisks2
Proposed by
Sylvain Pineau
Status: | Rejected |
---|---|
Rejected by: | Zygmunt Krynicki |
Proposed branch: | lp:~sylvain-pineau/checkbox/more-udisks2-spineau |
Merge into: | lp:~zyga/checkbox/more-udisks2 |
Diff against target: |
959 lines (+300/-251) 12 files modified
backend (+7/-3) checkbox/parsers/udevadm.py (+19/-0) checkbox/tests/message_files.py (+23/-14) debian/changelog (+11/-1) jobs/mediacard.txt.in (+1/-1) jobs/resource.txt.in (+2/-7) plugins/backend_info.py (+60/-21) scripts/display_resource (+84/-0) scripts/memorycard_resource (+0/-143) scripts/optical_write_test (+4/-3) scripts/removable_storage_test (+29/-20) scripts/removable_storage_watcher (+60/-38) |
To merge this branch: | bzr merge lp:~sylvain-pineau/checkbox/more-udisks2-spineau |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Zygmunt Krynicki | Pending | ||
Review via email:
|
Commit message
Description of the change
This MR basically fixes two things:
1. is_memorycard() uses the imported regexp from the udevadm parser and the Udisks1 version also benefit from it.
2. To follow the Udisks1 version behavior, memory cards detected are filtered except if --memorycard is explicitly given as argument to the script.
I'd like to see these minor changes merged in your branch before approving your version.
To post a comment you must log in.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Zygmunt Krynicki (zyga) wrote : | # |
Unmerged revisions
- 1731. By Sylvain Pineau
-
Filter memory cards if we're only interested in usb devices (same behaviour as the Udisks1 version)
- 1730. By Sylvain Pineau
-
Reused is_memory_card() for the UDisks1StorageD
eviceListener - 1729. By Sylvain Pineau
-
Merged from trunk
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'backend' |
2 | --- backend 2012-06-01 21:38:37 +0000 |
3 | +++ backend 2012-10-01 12:36:29 +0000 |
4 | @@ -35,10 +35,14 @@ |
5 | while True: |
6 | try: |
7 | message = reader.read_object() |
8 | + #"unpack" the message |
9 | + sequence, message = message |
10 | if message == "stop": |
11 | break |
12 | if message == "ping": |
13 | - writer.write_object("pong") |
14 | + #Build a tuple with the sequence number as |
15 | + #received |
16 | + writer.write_object((sequence, "pong",)) |
17 | continue |
18 | if isinstance(message, dict) and "command" in message: |
19 | job = Job(message["command"], message.get("environ"), |
20 | @@ -50,8 +54,8 @@ |
21 | status, data, duration = (FAIL, "Decode error", 0,) |
22 | else: |
23 | status, data, duration = (FAIL, "", 0,) |
24 | - |
25 | - writer.write_object((status, data, duration,)) |
26 | + #Build a tuple with sequence number |
27 | + writer.write_object((sequence, (status, data, duration,),)) |
28 | except IOError: |
29 | break |
30 | |
31 | |
32 | === modified file 'checkbox/parsers/udevadm.py' |
33 | --- checkbox/parsers/udevadm.py 2012-09-25 11:52:25 +0000 |
34 | +++ checkbox/parsers/udevadm.py 2012-10-01 12:36:29 +0000 |
35 | @@ -59,6 +59,9 @@ |
36 | r"^scsi:" |
37 | r"t-0x(?P<type>[%(hex)s]{2})" |
38 | % {"hex": string.hexdigits}) |
39 | +CARD_READER_RE = re.compile(r"SD|MMC|CF|MS|SM|xD|Card", re.I) |
40 | +GENERIC_RE = re.compile(r"Generic", re.I) |
41 | +FLASH_RE = re.compile(r"Flash", re.I) |
42 | |
43 | |
44 | class UdevadmDevice: |
45 | @@ -208,6 +211,22 @@ |
46 | if test_bit(Input.BTN_MOUSE, bitmask, self._bits): |
47 | return "MOUSE" |
48 | |
49 | + if self.driver: |
50 | + if self.driver.startswith("sdhci"): |
51 | + return "CARDREADER" |
52 | + |
53 | + if self.driver.startswith("mmc"): |
54 | + return "CARDREADER" |
55 | + |
56 | + if self.driver == "sd" and self.product: |
57 | + if any(FLASH_RE.search(k) for k in self._environment.keys()): |
58 | + return "CARDREADER" |
59 | + if any(d.bus == 'usb' for d in self._stack): |
60 | + if CARD_READER_RE.search(self.product): |
61 | + return "CARDREADER" |
62 | + if GENERIC_RE.search(self.vendor): |
63 | + return "CARDREADER" |
64 | + |
65 | if "ID_TYPE" in self._environment: |
66 | id_type = self._environment["ID_TYPE"] |
67 | |
68 | |
69 | === modified file 'checkbox/tests/message_files.py' |
70 | --- checkbox/tests/message_files.py 2012-09-19 16:12:11 +0000 |
71 | +++ checkbox/tests/message_files.py 2012-10-01 12:36:29 +0000 |
72 | @@ -32,32 +32,41 @@ |
73 | jobs_path = "./jobs" |
74 | |
75 | for filename in path_expand_recursive(jobs_path): |
76 | - if not filename.endswith("~"): |
77 | - file = open(filename, "r", encoding="utf-8") |
78 | + basename = os.path.basename(filename) |
79 | + if not basename.startswith(".") and not basename.endswith("~"): |
80 | template = TemplateI18n() |
81 | - messages += template.load_file(file, filename) |
82 | + messages += template.load_filename(filename) |
83 | + |
84 | return messages |
85 | |
86 | def setUp(self): |
87 | self.messages = self.read_jobs() |
88 | |
89 | def test_job_files_valid(self): |
90 | - messages = self.messages |
91 | - self.assertTrue(messages) |
92 | - self.assertTrue(len(messages) > 0) |
93 | + self.assertTrue(self.messages) |
94 | + self.assertTrue(len(self.messages) > 0) |
95 | |
96 | def test_all_messages_have_name(self): |
97 | - messages = self.messages |
98 | - for message in messages: |
99 | + for message in self.messages: |
100 | self.assertIn("name", message) |
101 | |
102 | def test_all_messages_have_command_or_description(self): |
103 | - messages = self.messages |
104 | - for message in messages: |
105 | + for message in self.messages: |
106 | self.assertTrue("command" in message or "description" in message) |
107 | - |
108 | + |
109 | def test_shell_jobs_have_description(self): |
110 | - messages = self.messages |
111 | - for message in messages: |
112 | - if message['plugin']=='shell': |
113 | + for message in self.messages: |
114 | + if message['plugin'] == 'shell': |
115 | self.assertTrue("description" in message, message['name']) |
116 | + |
117 | + def test_jobs_comply_with_schema(self): |
118 | + globals = {} |
119 | + exec(open("plugins/jobs_info.py").read(), globals) |
120 | + job_schema = globals["job_schema"] |
121 | + for message in self.messages: |
122 | + long_ext = "_extended" |
123 | + for long_key in list(message.keys()): |
124 | + if long_key.endswith(long_ext): |
125 | + short_key = long_key.replace(long_ext, "") |
126 | + message[short_key] = message.pop(long_key) |
127 | + job_schema.coerce(message) |
128 | |
129 | === modified file 'debian/changelog' |
130 | --- debian/changelog 2012-09-27 18:10:45 +0000 |
131 | +++ debian/changelog 2012-10-01 12:36:29 +0000 |
132 | @@ -62,6 +62,10 @@ |
133 | that all shell jobs have descriptions (LP: #1052992). |
134 | * [FEATURE] jobs/stress.txt.in: Updated some shell jobs that had no |
135 | description (LP: #1052992). |
136 | + * Added consecutive numbering to messages sent to the backend, so the |
137 | + frontend knows to discard out-of-sequence messages. (LP: #886118) |
138 | + * [FEATURE] Added a test to verify that jobs contain only keys |
139 | + declared in the schema (avoid stray keys). |
140 | |
141 | [Alberto Milone] |
142 | * [FEATURE] scripts/window_test, jobs/graphics.txt.in: Added script |
143 | @@ -161,6 +165,12 @@ |
144 | * checkbox/parsers/udevadm.py: Improved wireless devices detection. |
145 | The wireless category is now set if the subsystem is equal to ieee80211 |
146 | (LP: #855382) |
147 | + * scripts/memorycard_resource, scripts/removable_storage_test, |
148 | + scripts/removable_storage_watcher: Fixed the memorycard regexp flags and |
149 | + add the DriveVendor Udisks property to the re.search() string (LP: #1050920) |
150 | + * scripts/display_resource, jobs/resource.txt.in: Added a new display |
151 | + resource script to properly handle connector names returned by proprietary |
152 | + drivers (LP: #956139 and #992727) |
153 | |
154 | [Zygmunt Krynicki] |
155 | * Fixed simple duplicate 'the' mistakes (LP: #1040022) |
156 | @@ -172,7 +182,7 @@ |
157 | the 'device' argument (bus type) and require at least one value |
158 | * [FEATURE] scripts/removable_storage_watcher: add support for debugging |
159 | |
160 | - -- Jeff Marcom <jeff.marcom@canonical.com> Thu, 20 Sep 2012 18:03:21 -0400 |
161 | + -- Daniel Manrique <roadmr@ubuntu.com> Thu, 27 Sep 2012 13:02:24 -0400 |
162 | |
163 | checkbox (0.14.5) quantal; urgency=low |
164 | |
165 | |
166 | === modified file 'jobs/mediacard.txt.in' |
167 | --- jobs/mediacard.txt.in 2012-09-04 19:15:43 +0000 |
168 | +++ jobs/mediacard.txt.in 2012-10-01 12:36:29 +0000 |
169 | @@ -181,7 +181,7 @@ |
170 | user: root |
171 | requires: |
172 | package.name == 'udisks' |
173 | - memory_card.reader == 'detected' |
174 | + device.category == 'CARDREADER' |
175 | command: removable_storage_test --memorycard -l sdio usb scsi && removable_storage_test --memorycard sdio usb scsi |
176 | _description: |
177 | This is a fully automated version of mediacard/sd-automated and assumes that the |
178 | |
179 | === modified file 'jobs/resource.txt.in' |
180 | --- jobs/resource.txt.in 2012-09-06 09:42:43 +0000 |
181 | +++ jobs/resource.txt.in 2012-10-01 12:36:29 +0000 |
182 | @@ -76,7 +76,8 @@ |
183 | |
184 | name: display |
185 | plugin: resource |
186 | -command: for display in `xrandr | grep connected | awk '{print $1}' | grep -o ^[A-Z]* | sort | uniq`; do echo "$display: supported"; done |
187 | +command: display_resource |
188 | +description: Creates display resource info from xrandr output |
189 | |
190 | name: usb |
191 | plugin: resource |
192 | @@ -100,9 +101,3 @@ |
193 | for e in `env | sed 's/=/:/g'`; do |
194 | echo $e | awk -F':' '{print tolower($1) ": " $2}' |
195 | done |
196 | - |
197 | -name: memory_card |
198 | -plugin: resource |
199 | -user: root |
200 | -command: memorycard_resource |
201 | -description: Create resource info for memorycard readers |
202 | |
203 | === modified file 'plugins/backend_info.py' |
204 | --- plugins/backend_info.py 2012-06-22 16:33:41 +0000 |
205 | +++ plugins/backend_info.py 2012-10-01 12:36:29 +0000 |
206 | @@ -26,11 +26,12 @@ |
207 | from checkbox.lib.fifo import FifoReader, FifoWriter, create_fifo |
208 | |
209 | from checkbox.plugin import Plugin |
210 | -from checkbox.properties import Path, Float |
211 | +from checkbox.properties import Path, Float |
212 | from checkbox.job import FAIL |
213 | |
214 | from gettext import gettext as _ |
215 | |
216 | + |
217 | class BackendInfo(Plugin): |
218 | |
219 | # how long to wait for I/O from/to the backend before the call returns. |
220 | @@ -39,6 +40,37 @@ |
221 | |
222 | command = Path(default="%(checkbox_share)s/backend") |
223 | |
224 | + next_sequence = 0 |
225 | + expected_sequence = 0 |
226 | + |
227 | + def write_to_parent(self, object): |
228 | + message = (self.next_sequence, object,) |
229 | + logging.debug("Sending message with sequence number %s to backend" % |
230 | + self.next_sequence) |
231 | + self.parent_writer.write_object(message) |
232 | + self.expected_sequence = self.next_sequence |
233 | + self.next_sequence += 1 |
234 | + |
235 | + def read_from_parent(self): |
236 | + correct_sequence = False |
237 | + while not correct_sequence: |
238 | + ro = self.parent_reader.read_object() |
239 | + if ro: |
240 | + sequence, result = ro |
241 | + logging.debug("Expecting sequence number %s from backend, " |
242 | + "got sequence number %s" % |
243 | + (self.expected_sequence, sequence)) |
244 | + if (self.expected_sequence == sequence): |
245 | + correct_sequence = True |
246 | + else: |
247 | + logging.warning("Backend sent wrong sequence number, " |
248 | + "Discarding message and re-reading") |
249 | + else: |
250 | + #If we timed out, just return nothing, the rest of |
251 | + #the code knows how to handle this. |
252 | + return ro |
253 | + return result |
254 | + |
255 | def register(self, manager): |
256 | super(BackendInfo, self).register(manager) |
257 | |
258 | @@ -57,7 +89,10 @@ |
259 | |
260 | def get_root_command(self, *args): |
261 | uid = os.getuid() |
262 | - password_text = _("SYSTEM TESTING: Please enter your password. Some tests require root access to run properly. Your password will never be stored and will never be submitted with test results.") |
263 | + password_text = _("SYSTEM TESTING: Please enter your password. " |
264 | + "Some tests require root access to run properly. " |
265 | + "Your password will never be stored and will never " |
266 | + "be submitted with test results.") |
267 | password_prompt = _("PASSWORD: ") |
268 | if uid == 0: |
269 | prefix = [] |
270 | @@ -88,31 +123,35 @@ |
271 | def ping_backend(self): |
272 | if not self.parent_reader or not self.parent_writer: |
273 | return False |
274 | - self.parent_writer.write_object("ping") |
275 | - result = self.parent_reader.read_object() |
276 | + self.write_to_parent("ping") |
277 | + result = self.read_from_parent() |
278 | return result == "pong" |
279 | |
280 | - |
281 | def gather(self): |
282 | self.directory = mkdtemp(prefix="checkbox") |
283 | - child_input = create_fifo(os.path.join(self.directory, "input"), 0o600) |
284 | - child_output = create_fifo(os.path.join(self.directory, "output"), 0o600) |
285 | + child_input = create_fifo(os.path.join(self.directory, "input"), |
286 | + 0o600) |
287 | + child_output = create_fifo(os.path.join(self.directory, "output"), |
288 | + 0o600) |
289 | |
290 | self.backend_is_alive = False |
291 | - for attempt in range(1,4): |
292 | + for attempt in range(1, 4): |
293 | self.spawn_backend(child_input, child_output) |
294 | - #Only returns if I'm still the parent, so I can do parent stuff here |
295 | + #Only returns if I'm still the parent, |
296 | + #so I can do parent stuff here |
297 | self.parent_writer = FifoWriter(child_input, timeout=self.timeout) |
298 | - self.parent_reader = FifoReader(child_output, timeout=self.timeout) |
299 | + self.parent_reader = FifoReader(child_output, |
300 | + timeout=self.timeout) |
301 | if self.ping_backend(): |
302 | logging.debug("Backend responded, continuing execution.") |
303 | self.backend_is_alive = True |
304 | break |
305 | else: |
306 | - logging.debug("Backend didn't respond, trying to create again.") |
307 | + logging.debug("Backend didn't respond, " |
308 | + "trying to create again.") |
309 | |
310 | - if not self.backend_is_alive: |
311 | - logging.warning("Privileged backend not responding. " + |
312 | + if not self.backend_is_alive: |
313 | + logging.warning("Privileged backend not responding. " + |
314 | "jobs specifying user will not be run") |
315 | |
316 | def message_exec(self, message): |
317 | @@ -120,20 +159,20 @@ |
318 | if "environ" in message: |
319 | #Prepare variables to be "exported" from my environment |
320 | #to the backend's. |
321 | - backend_environ=["%s=%s" % (key, os.environ[key]) |
322 | - for key in message["environ"] |
323 | + backend_environ = ["%s=%s" % (key, os.environ[key]) |
324 | + for key in message["environ"] |
325 | if key in os.environ] |
326 | - message=dict(message) #so as to not wreck the |
327 | - #original message |
328 | - message["environ"]=backend_environ |
329 | + message = dict(message) # so as to not wreck the |
330 | + # original message |
331 | + message["environ"] = backend_environ |
332 | |
333 | if (self.backend_is_alive and not self.ping_backend()): |
334 | self.backend_is_alive = False |
335 | |
336 | if self.backend_is_alive: |
337 | - self.parent_writer.write_object(message) |
338 | + self.write_to_parent(message) |
339 | while True: |
340 | - result = self.parent_reader.read_object() |
341 | + result = self.read_from_parent() |
342 | if result: |
343 | break |
344 | else: |
345 | @@ -145,7 +184,7 @@ |
346 | self._manager.reactor.fire("message-result", *result) |
347 | |
348 | def stop(self): |
349 | - self.parent_writer.write_object("stop") |
350 | + self.write_to_parent("stop") |
351 | self.parent_writer.close() |
352 | self.parent_reader.close() |
353 | shutil.rmtree(self.directory) |
354 | |
355 | === added file 'scripts/display_resource' |
356 | --- scripts/display_resource 1970-01-01 00:00:00 +0000 |
357 | +++ scripts/display_resource 2012-10-01 12:36:29 +0000 |
358 | @@ -0,0 +1,84 @@ |
359 | +#!/usr/bin/python3 |
360 | + |
361 | +import re |
362 | +import sys |
363 | +import subprocess |
364 | + |
365 | +CONNECTOR_RE = re.compile( |
366 | + r"\n(?P<Name>[\w\-]+)" |
367 | + r"(?:\s+.*?" |
368 | + r"SignalFormat:\s+(?P<SignalFormat>[\w\-_]+)\s+.*?" |
369 | + r"ConnectorType:\s+(?P<ConnectorType>[\w\-_]+))?", re.S) |
370 | +SVIDEO_RE = re.compile(r"s\-?video|din|cv", re.I) |
371 | +DP_RE = re.compile(r"dp|displayport", re.I) |
372 | + |
373 | + |
374 | +def main(): |
375 | + try: |
376 | + xrandr_output = subprocess.check_output( |
377 | + ["xrandr", "-q", "--verbose"], universal_newlines=True) |
378 | + except subprocess.CalledProcessError: |
379 | + return 0 |
380 | + |
381 | + xrandr_output = "\n" + ''.join(xrandr_output.splitlines(True)[1:]) |
382 | + supported_connections = dict() |
383 | + |
384 | + for m in CONNECTOR_RE.finditer(xrandr_output): |
385 | + name = m.group('Name').lower() |
386 | + signal_format = connector_type = '' # RandR 1.3 only |
387 | + if m.group('SignalFormat'): |
388 | + signal_format = m.group('SignalFormat').lower() |
389 | + if m.group('ConnectorType'): |
390 | + connector_type = m.group('ConnectorType').lower() |
391 | + |
392 | + if name.startswith('vga'): |
393 | + supported_connections['vga'] = 'supported' |
394 | + |
395 | + elif name.startswith('dvi'): |
396 | + supported_connections['dvi'] = 'supported' |
397 | + |
398 | + elif DP_RE.match(name): |
399 | + # HDMI uses TMDS links, DisplayPort (DP) uses LVDS pairs (lanes) |
400 | + # to transmit micro data packets. |
401 | + # Reported by NVIDIA proprietary drivers (version >= 302.17) |
402 | + if signal_format == 'tmds': |
403 | + supported_connections['hdmi'] = 'supported' |
404 | + else: |
405 | + supported_connections['dp'] = 'supported' |
406 | + |
407 | + elif name.startswith('hdmi'): |
408 | + supported_connections['hdmi'] = 'supported' |
409 | + |
410 | + elif SVIDEO_RE.match(name): |
411 | + supported_connections['svideo'] = 'supported' |
412 | + |
413 | + elif name.startswith('rca'): |
414 | + supported_connections['rca'] = 'supported' |
415 | + |
416 | + elif name.startswith('tv'): |
417 | + if SVIDEO_RE.match(signal_format): |
418 | + supported_connections['svideo'] = 'supported' |
419 | + else: |
420 | + supported_connections['tv'] = 'supported' |
421 | + |
422 | + # ATI/AMD proprietary FGLRX graphics driver codenames: |
423 | + elif name.startswith('crt') or name.startswith('dfp'): |
424 | + if connector_type.startswith('dvi'): |
425 | + supported_connections['dvi'] = 'supported' |
426 | + elif connector_type.startswith('vga'): |
427 | + supported_connections['vga'] = 'supported' |
428 | + elif DP_RE.match(connector_type) and DP_RE.match(signal_format): |
429 | + supported_connections['dp'] = 'supported' |
430 | + else: |
431 | + # HDMI ports may appear as unknown |
432 | + # (for both signal format and connector type). |
433 | + supported_connections['hdmi'] = 'supported' |
434 | + |
435 | + for connector in supported_connections.keys(): |
436 | + print("%s: supported" % connector) |
437 | + |
438 | + return 0 |
439 | + |
440 | + |
441 | +if __name__ == "__main__": |
442 | + sys.exit(main()) |
443 | |
444 | === removed file 'scripts/memorycard_resource' |
445 | --- scripts/memorycard_resource 2012-09-05 20:09:10 +0000 |
446 | +++ scripts/memorycard_resource 1970-01-01 00:00:00 +0000 |
447 | @@ -1,143 +0,0 @@ |
448 | -#!/usr/bin/python3 |
449 | -# |
450 | -# This file is part of Checkbox. |
451 | -# |
452 | -# Copyright 2012 Canonical Ltd. |
453 | -# |
454 | -# Checkbox is free software: you can redistribute it and/or modify |
455 | -# it under the terms of the GNU General Public License as published by |
456 | -# the Free Software Foundation, either version 3 of the License, or |
457 | -# (at your option) any later version. |
458 | -# |
459 | -# Checkbox is distributed in the hope that it will be useful, |
460 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
461 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
462 | -# GNU General Public License for more details. |
463 | -# |
464 | -# You should have received a copy of the GNU General Public License |
465 | -# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
466 | -# |
467 | - |
468 | -import re |
469 | -import sys |
470 | -import fcntl |
471 | -import struct |
472 | -import ctypes |
473 | -from glob import glob |
474 | -from subprocess import call |
475 | - |
476 | - |
477 | -class sg_io_hdr(ctypes.Structure): |
478 | - """ |
479 | - Do ioctl's using Linux generic SCSI interface. |
480 | - """ |
481 | - SG_IO = 0x2285 |
482 | - |
483 | - _fields_ = [("interface_id", ctypes.c_int), |
484 | - ("dxfer_direction", ctypes.c_int), |
485 | - ("cmd_len", ctypes.c_ubyte), |
486 | - ("mx_sb_len", ctypes.c_ubyte), |
487 | - ("iovec_count", ctypes.c_ushort), |
488 | - ("dxfer_len", ctypes.c_int), |
489 | - ("dxferp", ctypes.c_char_p), |
490 | - ("cmdp", ctypes.c_char_p), |
491 | - ("sbp", ctypes.c_char_p), |
492 | - ("timeout", ctypes.c_uint), |
493 | - ("flags", ctypes.c_uint), |
494 | - ("pack_id", ctypes.c_int), |
495 | - ("usr_ptr", ctypes.c_char_p), |
496 | - ("status", ctypes.c_ubyte), |
497 | - ("masked_status", ctypes.c_ubyte), |
498 | - ("msg_status", ctypes.c_ubyte), |
499 | - ("sb_len_wr", ctypes.c_ubyte), |
500 | - ("host_status", ctypes.c_ushort), |
501 | - ("driver_status", ctypes.c_ushort), |
502 | - ("resid", ctypes.c_int), |
503 | - ("duration", ctypes.c_uint), |
504 | - ("info", ctypes.c_uint)] |
505 | - |
506 | - def __init__(self): |
507 | - self.interface_id = ord('S') |
508 | - self.dxfer_direction = 0 |
509 | - self.cmd_len = 0 |
510 | - self.mx_sb_len = 0 |
511 | - self.iovec_count = 0 |
512 | - self.dxfer_len = 0 |
513 | - self.dxferp = None |
514 | - self.cmdp = None |
515 | - self.timeout = 20000 |
516 | - self.flags = 0 |
517 | - self.pack_id = 0 |
518 | - self.usr_ptr = None |
519 | - self.status = 0 |
520 | - self.masked_status = 0 |
521 | - self.msg_status = 0 |
522 | - self.sb_len_wr = 0 |
523 | - self.host_status = 0 |
524 | - self.driver_status = 0 |
525 | - self.resid = 0 |
526 | - self.duration = 0 |
527 | - self.info = 0 |
528 | - |
529 | - |
530 | -def _get_sub_page(size, control_block, device): |
531 | - """ |
532 | - ioctl to retrieve a sub-page from the device. |
533 | - """ |
534 | - io_hdr = sg_io_hdr() |
535 | - io_hdr.dxfer_direction = -3 |
536 | - io_hdr.cmd_len = len(control_block) |
537 | - io_hdr.cmdp = control_block |
538 | - page_buffer = ctypes.create_string_buffer(size) |
539 | - io_hdr.dxfer_len = size |
540 | - io_hdr.dxferp = ctypes.cast(page_buffer, ctypes.c_char_p) |
541 | - |
542 | - i = -1 |
543 | - try: |
544 | - with open(device, 'r') as fd: |
545 | - i = fcntl.ioctl(fd, sg_io_hdr.SG_IO, io_hdr) |
546 | - except IOError: |
547 | - return None |
548 | - |
549 | - if i < 0: |
550 | - raise IOError("SG_IO ioctl error") |
551 | - |
552 | - if io_hdr.status != 0x00: |
553 | - raise IOError("io_hdr status is: %x" % io_hdr.status) |
554 | - |
555 | - if io_hdr.resid > 0: |
556 | - retsize = size - io_hdr.resid |
557 | - else: |
558 | - retsize = size |
559 | - |
560 | - return page_buffer[0:retsize] |
561 | - |
562 | - |
563 | -def inquire(device): |
564 | - """ |
565 | - Issue a SCSI INQUIRY command |
566 | - """ |
567 | - fields = '>BBBBBBBB8s16s4s20sBB8HH' |
568 | - fields_len = struct.calcsize(fields) |
569 | - control_block = struct.pack("BBBBBB", 0x12, 0, 0, 0, fields_len, 0) |
570 | - try: |
571 | - return _get_sub_page(fields_len, control_block, device)[8:32].decode() |
572 | - except TypeError: |
573 | - return '' |
574 | - |
575 | - |
576 | -def main(): |
577 | - pattern = re.compile('SD|MMC|CF|MS|SM|xD|Card|Generic') |
578 | - matches = [dev for dev in glob('/dev/sg*') if |
579 | - pattern.search(inquire(dev), re.I)] |
580 | - |
581 | - if matches or not call( |
582 | - "grep -q 'SDHCI controller found' /var/log/kern.log*", shell=True): |
583 | - print('reader: detected') |
584 | - else: |
585 | - print('reader: none') |
586 | - |
587 | - return 0 |
588 | - |
589 | -if __name__ == "__main__": |
590 | - sys.exit(main()) |
591 | |
592 | === modified file 'scripts/optical_write_test' |
593 | --- scripts/optical_write_test 2012-09-24 14:39:56 +0000 |
594 | +++ scripts/optical_write_test 2012-10-01 12:36:29 +0000 |
595 | @@ -68,14 +68,14 @@ |
596 | while true; do |
597 | sleep $INTERVAL |
598 | SLEEP_COUNT=`expr $SLEEP_COUNT + $INTERVAL` |
599 | - |
600 | + |
601 | mount $OPTICAL_DRIVE 2>&1 |egrep -q "already mounted" |
602 | rt=$? |
603 | if [ $rt -eq 0 ]; then |
604 | echo "Drive appears to be mounted now" |
605 | break |
606 | fi |
607 | - |
608 | + |
609 | # If they exceed the timeout limit, make a best effort to load the tray |
610 | # in the next steps |
611 | if [ $SLEEP_COUNT -ge $TIMEOUT ]; then |
612 | @@ -84,6 +84,7 @@ |
613 | fi |
614 | done |
615 | |
616 | + |
617 | echo "Deleting original data files ..." |
618 | rm -rf $SAMPLE_FILE |
619 | if [ ! -z "$(mount | grep $OPTICAL_DRIVE)" ]; then |
620 | @@ -130,7 +131,7 @@ |
621 | OPTICAL_DRIVE=$(readlink -f $1) |
622 | else |
623 | OPTICAL_DRIVE='/dev/sr0' |
624 | -fi |
625 | +fi |
626 | |
627 | create_working_dirs || failed "Failed to create working directories" |
628 | get_sample_data || failed "Failed to copy sample data" |
629 | |
630 | === modified file 'scripts/removable_storage_test' |
631 | --- scripts/removable_storage_test 2012-09-06 09:27:13 +0000 |
632 | +++ scripts/removable_storage_test 2012-10-01 12:36:29 +0000 |
633 | @@ -4,7 +4,6 @@ |
634 | import sys |
635 | import random |
636 | import os |
637 | -import re |
638 | import tempfile |
639 | import hashlib |
640 | import argparse |
641 | @@ -12,6 +11,7 @@ |
642 | import threading |
643 | import time |
644 | import collections |
645 | +from checkbox.parsers.udevadm import CARD_READER_RE, GENERIC_RE, FLASH_RE |
646 | |
647 | |
648 | class ActionTimer(): |
649 | @@ -142,7 +142,6 @@ |
650 | self.rem_disks_nm = {} |
651 | self.rem_disks_memory_cards = {} |
652 | self.rem_disks_memory_cards_nm = {} |
653 | - pattern = re.compile('SD|MMC|CF|MS|SM|xD|Card|Generic') |
654 | for dev in ud_manager.EnumerateDevices(): |
655 | device_obj = bus.get_object("org.freedesktop.UDisks", dev) |
656 | device_props = dbus.Interface(device_obj, dbus.PROPERTIES_IFACE) |
657 | @@ -150,20 +149,26 @@ |
658 | if not device_props.Get(udisks, "DeviceIsDrive"): |
659 | dev_bus = device_props.Get(udisks, "DriveConnectionInterface") |
660 | if dev_bus in args.device: |
661 | - parent_model = '' |
662 | + parent_model = parent_vendor = '' |
663 | if device_props.Get(udisks, "DeviceIsPartition"): |
664 | parent_obj = bus.get_object( |
665 | "org.freedesktop.UDisks", |
666 | device_props.Get(udisks, "PartitionSlave")) |
667 | parent_props = dbus.Interface( |
668 | parent_obj, dbus.PROPERTIES_IFACE) |
669 | - parent_model=parent_props.Get(udisks, "DriveModel") |
670 | + parent_model = parent_props.Get(udisks, "DriveModel") |
671 | + parent_vendor = parent_props.Get(udisks, "DriveVendor") |
672 | + parent_media = parent_props.Get(udisks, "DriveMedia") |
673 | if args.memorycard: |
674 | - if dev_bus != 'sdio' and not pattern.search( |
675 | - parent_model, re.I): |
676 | + if (dev_bus != 'sdio' |
677 | + and not FLASH_RE.search(parent_media) |
678 | + and not CARD_READER_RE.search(parent_model) |
679 | + and not GENERIC_RE.search(parent_vendor)): |
680 | continue |
681 | else: |
682 | - if pattern.search(parent_model, re.I): |
683 | + if (FLASH_RE.search(parent_media) |
684 | + or CARD_READER_RE.search(parent_model) |
685 | + or GENERIC_RE.search(parent_vendor)): |
686 | continue |
687 | dev_file = str(device_props.Get(udisks, "DeviceFile")) |
688 | dev_speed = str(device_props.Get(udisks, |
689 | @@ -178,7 +183,6 @@ |
690 | self.rem_disks_nm[dev_file] = None |
691 | self.rem_disks_memory_cards_nm[dev_file] = None |
692 | |
693 | - |
694 | def mount(self): |
695 | passed_mount = {} |
696 | |
697 | @@ -379,7 +383,8 @@ |
698 | print(" %s : %s : %s bits/s" % |
699 | (disk, mount_point, supported_speed), |
700 | end="") |
701 | - if (args.min_speed and int(args.min_speed) > int(supported_speed)): |
702 | + if (args.min_speed |
703 | + and int(args.min_speed) > int(supported_speed)): |
704 | print(" (Will not test it, speed is below %s bits/s)" % |
705 | args.min_speed, end="") |
706 | |
707 | @@ -387,9 +392,10 @@ |
708 | |
709 | print('-' * 20) |
710 | |
711 | - disks_eligible = {disk: disks_all[disk] for disk in disks_all \ |
712 | - if not args.min_speed or \ |
713 | - int(test.rem_disks_speed[disk]) >= int(args.min_speed) } |
714 | + disks_eligible = {disk: disks_all[disk] for disk in disks_all |
715 | + if not args.min_speed or |
716 | + int(test.rem_disks_speed[disk]) |
717 | + >= int(args.min_speed)} |
718 | write_sizes = [] |
719 | test_files = {} |
720 | # Generate our data file(s) |
721 | @@ -441,8 +447,9 @@ |
722 | test.clean_up(file) |
723 | total_write_time = sum(write_times) |
724 | avg_write_time = total_write_time / args.count |
725 | - avg_write_speed = ( |
726 | - total_write_size / total_write_time) / 1024 / 1024 |
727 | + avg_write_speed = (( |
728 | + total_write_size / total_write_time) |
729 | + / 1024 / 1024) |
730 | iteration_write_times.append(total_write_time) |
731 | print("\t[Iteration %s] Average Speed: %0.4f" |
732 | % (iteration, avg_write_speed)) |
733 | @@ -470,25 +477,27 @@ |
734 | "but there were errors" % args.count) |
735 | return 1 |
736 | elif len(disks_eligible) == 0: |
737 | - print("ERROR: No %s disks with speed higher than %s bits/s" % |
738 | - (args.device, args.min_speed), file=sys.stderr) |
739 | + print("ERROR: No %s disks with speed higher than %s bits/s" |
740 | + % (args.device, args.min_speed), file=sys.stderr) |
741 | return 1 |
742 | |
743 | else: |
744 | #Pass is not assured! |
745 | - if not args.pass_speed or avg_write_speed >= args.pass_speed: |
746 | + if (not args.pass_speed or |
747 | + avg_write_speed >= args.pass_speed): |
748 | return 0 |
749 | else: |
750 | print("FAIL: Average speed was lower than desired " |
751 | "pass speed of %s MB/s" % args.pass_speed) |
752 | return 1 |
753 | else: |
754 | - print("ERROR: No device being mounted successfully for testing, " |
755 | - "aborting", file=sys.stderr) |
756 | + print("ERROR: No device being mounted successfully " |
757 | + "for testing, aborting", file=sys.stderr) |
758 | return 1 |
759 | |
760 | else: # If we don't have removable drives attached and mounted |
761 | - print("ERROR: No removable drives were detected, aborting", file=sys.stderr) |
762 | + print("ERROR: No removable drives were detected, aborting", |
763 | + file=sys.stderr) |
764 | return 1 |
765 | |
766 | if __name__ == '__main__': |
767 | |
768 | === modified file 'scripts/removable_storage_watcher' |
769 | --- scripts/removable_storage_watcher 2012-09-27 20:29:22 +0000 |
770 | +++ scripts/removable_storage_watcher 2012-10-01 12:36:29 +0000 |
771 | @@ -5,7 +5,6 @@ |
772 | import copy |
773 | import dbus |
774 | import logging |
775 | -import re |
776 | import sys |
777 | |
778 | from dbus.exceptions import DBusException |
779 | @@ -13,6 +12,12 @@ |
780 | |
781 | from checkbox.dbus import connect_to_system_bus |
782 | from checkbox.dbus.udisks2 import UDisks2Model, UDisks2Observer |
783 | +from checkbox.parsers.udevadm import CARD_READER_RE, GENERIC_RE, FLASH_RE |
784 | + |
785 | +# Subset of device properties for Udisks1 |
786 | +Properties = collections.namedtuple( |
787 | + "Properties", |
788 | + "file bus speed model vendor media") |
789 | |
790 | # Delta record that encapsulates difference: |
791 | # delta_dir -- directon of the difference, either DELTA_DIR_PLUS or |
792 | @@ -78,18 +83,21 @@ |
793 | return "{}{}B".format(size // factor, prefix.strip()) |
794 | |
795 | |
796 | -def is_memory_card(vendor, model, udisks2_media): |
797 | +def is_memory_card(vendor, model, media): |
798 | """ |
799 | Check if the device seems to be a memory card |
800 | |
801 | - This is rather fuzzy, sadly udev and udisks2 don't do a very good job and |
802 | + This is rather fuzzy, sadly udev and udisks don't do a very good job and |
803 | mostly don't specify the "media" property (it has a few useful values, such |
804 | as flash_cf, flash_ms, flash_sm, flash_sd, flash_sdhc, flash_sdxc and |
805 | flash_mmc but I have yet to see a device that reports such values) |
806 | """ |
807 | + # Treat any media that starts with 'flash' as a memory card |
808 | + if media is not None and FLASH_RE.search(media): |
809 | + return True |
810 | # Treat any device that match model name to the following regular |
811 | # expression as a memory card reader. |
812 | - if re.search('SD|MMC|CF|MS|SM|xD|Card', model, re.I): |
813 | + if CARD_READER_RE.search(model): |
814 | return True |
815 | # Treat any device that contains the word 'Generic' in the vendor string as |
816 | # a memory card reader. |
817 | @@ -97,10 +105,7 @@ |
818 | # XXX: This seems odd but strangely enough seems to gets the job done. I |
819 | # guess if I should start filing tons of bugs/patches on udev/udisks2 to |
820 | # just have a few more rules and make this rule obsolete. |
821 | - if re.match('Generic', vendor, re.I): |
822 | - return True |
823 | - # Treat any udisks2_media that starts with 'flash' as a memory card |
824 | - if udisks2_media is not None and udisks2_media.startswith('flash'): |
825 | + if GENERIC_RE.search(vendor): |
826 | return True |
827 | return False |
828 | |
829 | @@ -157,33 +162,36 @@ |
830 | desired_bus_devices = [ |
831 | device |
832 | for device in changed_devices |
833 | - if device[1] in self._devices] |
834 | + if device.bus in self._devices] |
835 | logging.debug("Desired bus devices: %s", desired_bus_devices) |
836 | - pattern = re.compile('SD|MMC|CF|MS|SM|xD|Card|Generic') |
837 | for dev in desired_bus_devices: |
838 | + # Ensure it is a media card reader if this was explicitly requested |
839 | + drive_is_reader = is_memory_card(dev.vendor, dev.model, dev.media) |
840 | if self._memorycard: |
841 | - if dev[1] != 'sdio' and not pattern.search(dev[3], re.I): |
842 | - logging.debug("The device does not seem to be a memory" |
843 | - " card (dev[1]: %r, dev[3]: %r), skipping", |
844 | - dev[1], dev[3]) |
845 | + if not drive_is_reader: |
846 | + logging.warning("The device does not seem to be a memory" |
847 | + " card (bus: %r, model: %r), skipping", |
848 | + dev.bus, dev.model) |
849 | return |
850 | - print(message % {'bus': 'memory card', 'file': dev[0]}) |
851 | + print(message % {'bus': 'memory card', 'file': dev.file}) |
852 | else: |
853 | - if pattern.search(dev[3], re.I): |
854 | - logging.debug("The device seems to be a memory" |
855 | - " card %s (dev[3]: %r), skipping", |
856 | - pattern, dev[3]) |
857 | + if drive_is_reader: |
858 | + logging.warning("The device seems to be a memory" |
859 | + " card (bus: %r (model: %r), skipping", |
860 | + dev.bus, dev.model) |
861 | return |
862 | - print(message % {'bus': dev[1], 'file': dev[0]}) |
863 | + print(message % {'bus': dev.bus, 'file': dev.file}) |
864 | if self._minimum_speed: |
865 | - if dev[2] >= self._minimum_speed: |
866 | + if dev.speed >= self._minimum_speed: |
867 | print("with speed of %(speed)s bits/s " |
868 | "higher than %(min_speed)s bits/s" % |
869 | - {'speed': dev[2], 'min_speed': self._minimum_speed}) |
870 | + {'speed': dev.speed, |
871 | + 'min_speed': self._minimum_speed}) |
872 | else: |
873 | print("ERROR: speed of %(speed)s bits/s lower " |
874 | "than %(min_speed)s bits/s" % |
875 | - {'speed': dev[2], 'min_speed': self._minimum_speed}) |
876 | + {'speed': dev.speed, |
877 | + 'min_speed': self._minimum_speed}) |
878 | self._error = True |
879 | logging.debug("Device matches requirements, exiting event loop") |
880 | self._loop.quit() |
881 | @@ -257,11 +265,11 @@ |
882 | udisks = 'org.freedesktop.UDisks.Device' |
883 | _device_file = device_props.Get(udisks, |
884 | "DeviceFile") |
885 | - _device = device_props.Get(udisks, |
886 | + _bus = device_props.Get(udisks, |
887 | "DriveConnectionInterface") |
888 | _speed = device_props.Get(udisks, |
889 | "DriveConnectionSpeed") |
890 | - _parent_model = '' |
891 | + _parent_model = _parent_vendor = '' |
892 | |
893 | if device_props.Get(udisks, "DeviceIsPartition"): |
894 | parent_obj = self._bus.get_object( |
895 | @@ -270,13 +278,17 @@ |
896 | parent_props = dbus.Interface( |
897 | parent_obj, dbus.PROPERTIES_IFACE) |
898 | _parent_model = parent_props.Get(udisks, "DriveModel") |
899 | + _parent_vendor = parent_props.Get(udisks, "DriveVendor") |
900 | + _parent_media = parent_props.Get(udisks, "DriveMedia") |
901 | |
902 | if not device_props.Get(udisks, "DeviceIsDrive"): |
903 | - device = ( |
904 | + device = Properties( |
905 | str(_device_file), |
906 | - str(_device), |
907 | + str(_bus), |
908 | int(_speed), |
909 | - str(_parent_model)) |
910 | + str(_parent_model), |
911 | + str(_parent_vendor), |
912 | + str(_parent_media)) |
913 | existing_devices.append(device) |
914 | |
915 | except dbus.DBusException: |
916 | @@ -443,8 +455,8 @@ |
917 | # argument uses somewhat different strings. In particular we need to |
918 | # map 'ieee1394' to 'firewire' |
919 | # |
920 | - # See: http://udisks.freedesktop.org/docs/latest/gdbus-org.freedesktop.UDisks2.Drive.html#gdbus-property-org-freedesktop-UDisks2-Drive.ConnectionBus |
921 | - |
922 | + # See: http://goo.gl/jaUAa |
923 | + # |
924 | # TODO: Report old unsupported possibilities that are still offered by |
925 | # the command-line interface. We also need samples to understand how |
926 | # UDisks2 behaves in practice on various devices. |
927 | @@ -655,14 +667,24 @@ |
928 | drive_is_reader = is_memory_card( |
929 | drive_props['Vendor'], drive_props['Model'], |
930 | drive_props['Media']) |
931 | - if self._desired_memory_card and not drive_is_reader: |
932 | - logging.warning( |
933 | - "The object %s belongs to drive %s that does not seem to" |
934 | - " be a media reader", object_block_device, |
935 | - drive_object_path) |
936 | - # Ignore this object so that we don't spam the user twice |
937 | - self._ignored_objects.add(object_path) |
938 | - continue |
939 | + if self._desired_memory_card: |
940 | + if not drive_is_reader: |
941 | + logging.warning( |
942 | + "The object %s belongs to drive %s that does not seem" |
943 | + " to be a media reader", object_block_device, |
944 | + drive_object_path) |
945 | + # Ignore this object so that we don't spam the user twice |
946 | + self._ignored_objects.add(object_path) |
947 | + continue |
948 | + else: |
949 | + if drive_is_reader: |
950 | + logging.warning( |
951 | + "The object %s belongs to drive %s that seems to be a" |
952 | + " media reader", object_block_device, |
953 | + drive_object_path) |
954 | + # Ignore this object so that we don't spam the user twice |
955 | + self._ignored_objects.add(object_path) |
956 | + continue |
957 | # TODO: Add support for detecting USB3 |
958 | # Yay, success |
959 | results.add(object_block_device) |
I'll cherry-pick individual pieces and propose them separately