test/py: use " for docstrings
[oweals/u-boot.git] / test / py / tests / test_ums.py
1 # Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
2 #
3 # SPDX-License-Identifier: GPL-2.0
4
5 # Test U-Boot's "ums" command. The test starts UMS in U-Boot, waits for USB
6 # device enumeration on the host, reads a small block of data from the UMS
7 # block device, optionally mounts a partition and performs filesystem-based
8 # read/write tests, and finally aborts the "ums" command in U-Boot.
9
10 import os
11 import os.path
12 import pytest
13 import re
14 import time
15 import u_boot_utils
16
17 """
18 Note: This test relies on:
19
20 a) boardenv_* to contain configuration values to define which USB ports are
21 available for testing. Without this, this test will be automatically skipped.
22 For example:
23
24 # Leave this list empty if you have no block_devs below with writable
25 # partitions defined.
26 env__mount_points = (
27     "/mnt/ubtest-mnt-p2371-2180-na",
28 )
29
30 env__usb_dev_ports = (
31     {
32         "tgt_usb_ctlr": "0",
33         "host_ums_dev_node": "/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0",
34     },
35 )
36
37 env__block_devs = (
38     # eMMC; always present
39     {
40         "type": "mmc",
41         "id": "0",
42         # The following two properties are optional.
43         # If present, the partition will be mounted and a file written-to and
44         # read-from it. If missing, only a simple block read test will be
45         # performed.
46         "writable_fs_partition": 1,
47         "writable_fs_subdir": "tmp/",
48     },
49     # SD card; present since I plugged one in
50     {
51         "type": "mmc",
52         "id": "1"
53     },
54 )
55
56 b) udev rules to set permissions on devices nodes, so that sudo is not
57 required. For example:
58
59 ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", KERNELS=="3-13", MODE:="666"
60
61 (You may wish to change the group ID instead of setting the permissions wide
62 open. All that matters is that the user ID running the test can access the
63 device.)
64
65 c) /etc/fstab entries to allow the block device to be mounted without requiring
66 root permissions. For example:
67
68 /dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0-part1 /mnt/ubtest-mnt-p2371-2180-na ext4 noauto,user,nosuid,nodev
69
70 This entry is only needed if any block_devs above contain a
71 writable_fs_partition value.
72 """
73
74 @pytest.mark.buildconfigspec('cmd_usb_mass_storage')
75 def test_ums(u_boot_console, env__usb_dev_port, env__block_devs):
76     """Test the "ums" command; the host system must be able to enumerate a UMS
77     device when "ums" is running, block and optionally file I/O are tested,
78     and this device must disappear when "ums" is aborted.
79
80     Args:
81         u_boot_console: A U-Boot console connection.
82         env__usb_dev_port: The single USB device-mode port specification on
83             which to run the test. See the file-level comment above for
84             details of the format.
85         env__block_devs: The list of block devices that the target U-Boot
86             device has attached. See the file-level comment above for details
87             of the format.
88
89     Returns:
90         Nothing.
91     """
92
93     have_writable_fs_partition = 'writable_fs_partition' in env__block_devs[0]
94     if not have_writable_fs_partition:
95         # If 'writable_fs_subdir' is missing, we'll skip all parts of the
96         # testing which mount filesystems.
97         u_boot_console.log.warning(
98             'boardenv missing "writable_fs_partition"; ' +
99             'UMS testing will be limited.')
100
101     tgt_usb_ctlr = env__usb_dev_port['tgt_usb_ctlr']
102     host_ums_dev_node = env__usb_dev_port['host_ums_dev_node']
103
104     # We're interested in testing USB device mode on each port, not the cross-
105     # product of that with each device. So, just pick the first entry in the
106     # device list here. We'll test each block device somewhere else.
107     tgt_dev_type = env__block_devs[0]['type']
108     tgt_dev_id = env__block_devs[0]['id']
109     if have_writable_fs_partition:
110         mount_point = u_boot_console.config.env['env__mount_points'][0]
111         mount_subdir = env__block_devs[0]['writable_fs_subdir']
112         part_num = env__block_devs[0]['writable_fs_partition']
113         host_ums_part_node = '%s-part%d' % (host_ums_dev_node, part_num)
114     else:
115         host_ums_part_node = host_ums_dev_node
116
117     test_f = u_boot_utils.PersistentRandomFile(u_boot_console, 'ums.bin',
118         1024 * 1024);
119     if have_writable_fs_partition:
120         mounted_test_fn = mount_point + '/' + mount_subdir + test_f.fn
121
122     def start_ums():
123         """Start U-Boot's ums shell command.
124
125         This also waits for the host-side USB enumeration process to complete.
126
127         Args:
128             None.
129
130         Returns:
131             Nothing.
132         """
133
134         u_boot_console.log.action(
135             'Starting long-running U-Boot ums shell command')
136         cmd = 'ums %s %s %s' % (tgt_usb_ctlr, tgt_dev_type, tgt_dev_id)
137         u_boot_console.run_command(cmd, wait_for_prompt=False)
138         u_boot_console.wait_for(re.compile('UMS: LUN.*[\r\n]'))
139         fh = u_boot_utils.wait_until_open_succeeds(host_ums_part_node)
140         u_boot_console.log.action('Reading raw data from UMS device')
141         fh.read(4096)
142         fh.close()
143
144     def mount():
145         """Mount the block device that U-Boot exports.
146
147         Args:
148             None.
149
150         Returns:
151             Nothing.
152         """
153
154         u_boot_console.log.action('Mounting exported UMS device')
155         cmd = ('/bin/mount', host_ums_part_node)
156         u_boot_utils.run_and_log(u_boot_console, cmd)
157
158     def umount(ignore_errors):
159         """Unmount the block device that U-Boot exports.
160
161         Args:
162             ignore_errors: Ignore any errors. This is useful if an error has
163                 already been detected, and the code is performing best-effort
164                 cleanup. In this case, we do not want to mask the original
165                 error by "honoring" any new errors.
166
167         Returns:
168             Nothing.
169         """
170
171         u_boot_console.log.action('Unmounting UMS device')
172         cmd = ('/bin/umount', host_ums_part_node)
173         u_boot_utils.run_and_log(u_boot_console, cmd, ignore_errors)
174
175     def stop_ums(ignore_errors):
176         """Stop U-Boot's ums shell command from executing.
177
178         This also waits for the host-side USB de-enumeration process to
179         complete.
180
181         Args:
182             ignore_errors: Ignore any errors. This is useful if an error has
183                 already been detected, and the code is performing best-effort
184                 cleanup. In this case, we do not want to mask the original
185                 error by "honoring" any new errors.
186
187         Returns:
188             Nothing.
189         """
190
191         u_boot_console.log.action(
192             'Stopping long-running U-Boot ums shell command')
193         u_boot_console.ctrlc()
194         u_boot_utils.wait_until_file_open_fails(host_ums_part_node,
195             ignore_errors)
196
197     ignore_cleanup_errors = True
198     try:
199         start_ums()
200         if not have_writable_fs_partition:
201             # Skip filesystem-based testing if not configured
202             return
203         try:
204             mount()
205             u_boot_console.log.action('Writing test file via UMS')
206             cmd = ('rm', '-f', mounted_test_fn)
207             u_boot_utils.run_and_log(u_boot_console, cmd)
208             if os.path.exists(mounted_test_fn):
209                 raise Exception('Could not rm target UMS test file')
210             cmd = ('cp', test_f.abs_fn, mounted_test_fn)
211             u_boot_utils.run_and_log(u_boot_console, cmd)
212             ignore_cleanup_errors = False
213         finally:
214             umount(ignore_errors=ignore_cleanup_errors)
215     finally:
216         stop_ums(ignore_errors=ignore_cleanup_errors)
217
218     ignore_cleanup_errors = True
219     try:
220         start_ums()
221         try:
222             mount()
223             u_boot_console.log.action('Reading test file back via UMS')
224             read_back_hash = u_boot_utils.md5sum_file(mounted_test_fn)
225             cmd = ('rm', '-f', mounted_test_fn)
226             u_boot_utils.run_and_log(u_boot_console, cmd)
227             ignore_cleanup_errors = False
228         finally:
229             umount(ignore_errors=ignore_cleanup_errors)
230     finally:
231         stop_ums(ignore_errors=ignore_cleanup_errors)
232
233     written_hash = test_f.content_hash
234     assert(written_hash == read_back_hash)