Merge lp:~sylvain-pineau/checkbox/more-udisks2-spineau into lp:~zyga/checkbox/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
Reviewer Review Type Date Requested Status
Zygmunt Krynicki Pending
Review via email: mp+127260@code.launchpad.net

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
Zygmunt Krynicki (zyga) wrote :

I'll cherry-pick individual pieces and propose them separately

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 UDisks1StorageDeviceListener

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)

Subscribers

People subscribed via source and target branches