Skip to content

Commit 90c443a

Browse files
Add USB mass storage test
1 parent 5c97244 commit 90c443a

File tree

6 files changed

+897
-1
lines changed

6 files changed

+897
-1
lines changed

TESTS/host_tests/pyusb_msd.py

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
"""
2+
Copyright (c) 2019, Arm Limited and affiliates.
3+
SPDX-License-Identifier: Apache-2.0
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
"""
17+
18+
from mbed_host_tests import BaseHostTest
19+
import time
20+
import psutil
21+
import tempfile
22+
import os
23+
import platform
24+
import subprocess
25+
import sys
26+
27+
28+
class PyusbMSDTest(BaseHostTest):
29+
"""Host side test for USB MSD class."""
30+
31+
__result = None
32+
MOUNT_WAIT_TIME = 25 # in [s]
33+
initial_disk_list = None
34+
msd_disk = None
35+
36+
def _callback_record_disk_list(self, key, value, timestamp):
37+
"""Records msd disks list.
38+
39+
It will be used to find new disks mounted during test.
40+
"""
41+
self.initial_disk_list = set(MSDUtils.disks())
42+
self.report_success()
43+
44+
def _callback_check_file_exist(self, key, value, timestamp):
45+
"""Check if file exist.
46+
47+
"""
48+
folder_name, file_name, file_content = value.split(' ')
49+
file_path = os.path.join(self.msd_disk.mountpoint, folder_name, file_name)
50+
try:
51+
file = open(file_path, 'r')
52+
line = file.readline()
53+
file.close()
54+
time.sleep(2) # wait for msd communication done
55+
if line == file_content:
56+
self.send_kv("exist", "0")
57+
return
58+
self.report_error("file content invalid")
59+
except IOError as err:
60+
self.log('{} !!!'.format(err))
61+
self.send_kv("non-exist", "0")
62+
63+
def _callback_delete_files(self, key, value, timestamp):
64+
"""Delete test file.
65+
66+
"""
67+
dir_name, file_name = value.split(' ')
68+
69+
try:
70+
os.remove(os.path.join(self.msd_disk.mountpoint, dir_name, file_name))
71+
except:
72+
self.report_error("delete files")
73+
return
74+
time.sleep(2) # wait for msd communication done
75+
self.report_success()
76+
77+
def _callback_check_if_mounted(self, key, value, timestamp):
78+
"""Check if disk was mounted.
79+
80+
"""
81+
wait_time = self.MOUNT_WAIT_TIME
82+
while wait_time != 0:
83+
disk_list = set(MSDUtils.disks()) - self.initial_disk_list
84+
if len(disk_list) == 1: # one new MSD disk found
85+
self.msd_disk = disk_list.pop()
86+
time.sleep(2) # wait for msd communication done
87+
self.report_success()
88+
return
89+
elif len(disk_list) > 1:
90+
self.log('detected more then one new USB disk!!!!!!: {}'.format(disk_list))
91+
wait_time -= 1
92+
time.sleep(1) # wait 1s and try again
93+
self.report_error("mount check")
94+
95+
def _callback_check_if_not_mounted(self, key, value, timestamp):
96+
"""Check if disk was unmouted.
97+
98+
"""
99+
wait_time = self.MOUNT_WAIT_TIME
100+
while wait_time != 0:
101+
disk_list = set(MSDUtils.disks()) - self.initial_disk_list
102+
if len(disk_list) == 0:
103+
self.msd_disk = None
104+
time.sleep(2) # wait for msd communication done
105+
self.report_success()
106+
return
107+
wait_time -= 1
108+
time.sleep(1) # wait 1s and try again
109+
self.report_error("unmount check")
110+
111+
def _callback_get_mounted_fs_size(self, key, value, timestamp):
112+
"""Record visible filesystem size.
113+
114+
"""
115+
stats = psutil.disk_usage(self.msd_disk.mountpoint)
116+
self.send_kv("{}".format(stats.total), "0")
117+
118+
def _callback_unmount(self, key, value, timestamp):
119+
"""Disk unmount.
120+
121+
"""
122+
if MSDUtils.unmount(self.msd_disk):
123+
self.report_success()
124+
else:
125+
self.report_error("unmount")
126+
127+
def setup(self):
128+
self.register_callback('record_disk_list', self._callback_record_disk_list)
129+
self.register_callback('check_if_mounted', self._callback_check_if_mounted)
130+
self.register_callback('check_if_not_mounted', self._callback_check_if_not_mounted)
131+
self.register_callback('get_mounted_fs_size', self._callback_get_mounted_fs_size)
132+
self.register_callback('check_file_exist', self._callback_check_file_exist)
133+
self.register_callback('delete_files', self._callback_delete_files)
134+
self.register_callback('unmount', self._callback_unmount)
135+
136+
def report_success(self):
137+
self.send_kv("passed", "0")
138+
139+
def report_error(self, msg):
140+
self.log('{} failed !!!'.format(msg))
141+
self.send_kv("failed", "0")
142+
143+
def result(self):
144+
return self.__result
145+
146+
def teardown(self):
147+
pass
148+
149+
150+
class MSDUtils(object):
151+
@staticmethod
152+
def disks():
153+
system_name = platform.system()
154+
if system_name == "Windows":
155+
return MSDUtils._disks_windows()
156+
elif system_name == "Linux":
157+
return MSDUtils._disks_linux()
158+
elif system_name == "Darwin":
159+
return MSDUtils._disks_mac()
160+
return []
161+
162+
@staticmethod
163+
def unmount(disk):
164+
system_name = platform.system()
165+
if system_name == "Windows":
166+
return MSDUtils._unmount_windows(disk)
167+
elif system_name == "Linux":
168+
return MSDUtils._unmount_linux(disk)
169+
elif system_name == "Darwin":
170+
return MSDUtils._unmount_mac(disk)
171+
return False
172+
173+
@staticmethod
174+
def _unmount_windows(disk):
175+
176+
tmp_file = tempfile.NamedTemporaryFile(suffix='.ps1', delete=False)
177+
try:
178+
# create unmount script
179+
tmp_file.write('$disk_leter=$args[0]\n')
180+
tmp_file.write('$driveEject = New-Object -comObject Shell.Application\n')
181+
tmp_file.write('$driveEject.Namespace(17).ParseName($disk_leter).InvokeVerb("Eject")\n')
182+
# close to allow open by other process
183+
tmp_file.close()
184+
185+
try_cout = 10
186+
while try_cout:
187+
p = subprocess.Popen(["powershell.exe", tmp_file.name + " " + disk.mountpoint], stdout=sys.stdout)
188+
p.communicate()
189+
try_cout -= 1
190+
disks = set(MSDUtils._disks_windows())
191+
if disk not in disks:
192+
return True
193+
time.sleep(1)
194+
finally:
195+
os.remove(tmp_file.name)
196+
197+
return False
198+
199+
@staticmethod
200+
def _unmount_linux(disk):
201+
os.system("umount " + disk.mountpoint)
202+
disks = set(MSDUtils._disks_linux())
203+
if disk not in disks:
204+
return True
205+
return False
206+
207+
@staticmethod
208+
def _unmount_mac(disk):
209+
os.system("diskutil unmount " + disk.mountpoint)
210+
disks = set(MSDUtils._disks_mac())
211+
if disk not in disks:
212+
return True
213+
return False
214+
215+
@staticmethod
216+
def _disks_windows():
217+
msd_disks = []
218+
disks = psutil.disk_partitions()
219+
for disk in disks:
220+
opts = disk.opts.split(",")
221+
for opt in opts:
222+
if opt == "removable":
223+
msd_disks.append(disk)
224+
return msd_disks
225+
226+
@staticmethod
227+
def _disks_linux():
228+
msd_disks = []
229+
disks = psutil.disk_partitions()
230+
for disk in disks:
231+
if "/dev/sd" in disk.device and "/media/" in disk.mountpoint:
232+
msd_disks.append(disk)
233+
return msd_disks
234+
235+
@staticmethod
236+
def _disks_mac():
237+
msd_disks = []
238+
disks = psutil.disk_partitions()
239+
for disk in disks:
240+
if "/dev/disk" in disk.device and "/Volumes/" in disk.mountpoint:
241+
msd_disks.append(disk)
242+
return msd_disks

TESTS/usb_device/msd/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# USB mass storage test user guide
2+
3+
To run the tests-usb_device-msd test you need device with:
4+
- at least 70kB of RAM
5+
- and/or with flash block device support
6+
7+
Test run command (for GCC_ARM and k64f target):
8+
```bash
9+
mbed test -t GCC_ARM -m k64f -n tests-usb_device-msd --app-config ./TESTS/usb_device/msd/mbed_app.json
10+
```

TESTS/usb_device/msd/TestUSBMSD.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (c) 2019, Arm Limited and affiliates.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#ifndef Test_USBMSD_H
18+
#define Test_USBMSD_H
19+
20+
#include "USBMSD.h"
21+
22+
class TestUSBMSD: public USBMSD {
23+
public:
24+
TestUSBMSD(BlockDevice *bd, bool connect_blocking = true, uint16_t vendor_id = 0x0703, uint16_t product_id = 0x0104, uint16_t product_release = 0x0001)
25+
: USBMSD(bd, connect_blocking, vendor_id, product_id, product_release)
26+
{
27+
28+
}
29+
30+
virtual ~TestUSBMSD()
31+
{
32+
33+
}
34+
35+
uint32_t get_read_counter()
36+
{
37+
return read_counter;
38+
}
39+
40+
uint32_t get_program_counter()
41+
{
42+
return program_counter;
43+
}
44+
45+
void reset_counters()
46+
{
47+
read_counter = program_counter = erase_counter = 0;
48+
}
49+
50+
static volatile uint32_t read_counter, program_counter, erase_counter;
51+
52+
protected:
53+
virtual int disk_read(uint8_t *data, uint64_t block, uint8_t count)
54+
{
55+
read_counter++;
56+
return USBMSD::disk_read(data, block, count);
57+
}
58+
59+
virtual int disk_write(const uint8_t *data, uint64_t block, uint8_t count)
60+
{
61+
erase_counter++;
62+
program_counter++;
63+
64+
return USBMSD::disk_write(data, block, count);
65+
}
66+
};
67+
68+
volatile uint32_t TestUSBMSD::read_counter = 0;
69+
volatile uint32_t TestUSBMSD::program_counter = 0;
70+
volatile uint32_t TestUSBMSD::erase_counter = 0;
71+
72+
#endif // Test_USBMSD_H

0 commit comments

Comments
 (0)