test/py: fs: add extended write operation test
[oweals/u-boot.git] / test / py / tests / test_fs / conftest.py
1 # SPDX-License-Identifier:      GPL-2.0+
2 # Copyright (c) 2018, Linaro Limited
3 # Author: Takahiro Akashi <takahiro.akashi@linaro.org>
4
5 import os
6 import os.path
7 import pytest
8 import re
9 from subprocess import call, check_call, check_output, CalledProcessError
10 from fstest_defs import *
11
12 supported_fs_basic = ['fat16', 'fat32', 'ext4']
13 supported_fs_ext = ['fat16', 'fat32']
14
15 #
16 # Filesystem test specific setup
17 #
18 def pytest_addoption(parser):
19     parser.addoption('--fs-type', action='append', default=None,
20         help='Targeting Filesystem Types')
21
22 def pytest_configure(config):
23     global supported_fs_basic
24     global supported_fs_ext
25
26     def intersect(listA, listB):
27         return  [x for x in listA if x in listB]
28
29     supported_fs = config.getoption('fs_type')
30     if supported_fs:
31         print("*** FS TYPE modified: %s" % supported_fs)
32         supported_fs_basic =  intersect(supported_fs, supported_fs_basic)
33         supported_fs_ext =  intersect(supported_fs, supported_fs_ext)
34
35 def pytest_generate_tests(metafunc):
36     if 'fs_obj_basic' in metafunc.fixturenames:
37         metafunc.parametrize('fs_obj_basic', supported_fs_basic,
38             indirect=True, scope='module')
39     if 'fs_obj_ext' in metafunc.fixturenames:
40         metafunc.parametrize('fs_obj_ext', supported_fs_ext,
41             indirect=True, scope='module')
42
43 #
44 # Helper functions
45 #
46 def fstype_to_ubname(fs_type):
47     if re.match('fat', fs_type):
48         return 'fat'
49     else:
50         return fs_type
51
52 def check_ubconfig(config, fs_type):
53     if not config.buildconfig.get('config_cmd_%s' % fs_type, None):
54         pytest.skip('.config feature "CMD_%s" not enabled' % fs_type.upper())
55     if not config.buildconfig.get('config_%s_write' % fs_type, None):
56         pytest.skip('.config feature "%s_WRITE" not enabled'
57         % fs_type.upper())
58
59 def mk_fs(config, fs_type, size, id):
60     fs_img = '%s.%s.img' % (id, fs_type)
61     fs_img = config.persistent_data_dir + '/' + fs_img
62
63     if fs_type == 'fat16':
64         mkfs_opt = '-F 16'
65     elif fs_type == 'fat32':
66         mkfs_opt = '-F 32'
67     else:
68         mkfs_opt = ''
69
70     if re.match('fat', fs_type):
71         fs_lnxtype = 'vfat'
72     else:
73         fs_lnxtype = fs_type
74
75     count = (size + 1048576 - 1) / 1048576
76
77     try:
78         check_call('rm -f %s' % fs_img, shell=True)
79         check_call('dd if=/dev/zero of=%s bs=1M count=%d'
80             % (fs_img, count), shell=True)
81         check_call('mkfs.%s %s %s'
82             % (fs_lnxtype, mkfs_opt, fs_img), shell=True)
83         return fs_img
84     except CalledProcessError:
85         call('rm -f %s' % fs_img, shell=True)
86         raise
87
88 # from test/py/conftest.py
89 def tool_is_in_path(tool):
90     for path in os.environ["PATH"].split(os.pathsep):
91         fn = os.path.join(path, tool)
92         if os.path.isfile(fn) and os.access(fn, os.X_OK):
93             return True
94     return False
95
96 fuse_mounted = False
97
98 def mount_fs(fs_type, device, mount_point):
99     global fuse_mounted
100
101     fuse_mounted = False
102     try:
103         if tool_is_in_path('guestmount'):
104             fuse_mounted = True
105             check_call('guestmount -a %s -m /dev/sda %s'
106                 % (device, mount_point), shell=True)
107         else:
108             mount_opt = "loop,rw"
109             if re.match('fat', fs_type):
110                 mount_opt += ",umask=0000"
111
112             check_call('sudo mount -o %s %s %s'
113                 % (mount_opt, device, mount_point), shell=True)
114
115             # may not be effective for some file systems
116             check_call('sudo chmod a+rw %s' % mount_point, shell=True)
117     except CalledProcessError:
118         raise
119
120 def umount_fs(fs_type, mount_point):
121     if fuse_mounted:
122         call('sync')
123         call('guestunmount %s' % mount_point, shell=True)
124     else:
125         call('sudo umount %s' % mount_point, shell=True)
126
127 #
128 # Fixture for basic fs test
129 #     derived from test/fs/fs-test.sh
130 #
131 # NOTE: yield_fixture was deprecated since pytest-3.0
132 @pytest.yield_fixture()
133 def fs_obj_basic(request, u_boot_config):
134     fs_type = request.param
135     fs_img = ''
136
137     fs_ubtype = fstype_to_ubname(fs_type)
138     check_ubconfig(u_boot_config, fs_ubtype)
139
140     mount_dir = u_boot_config.persistent_data_dir + '/mnt'
141
142     small_file = mount_dir + '/' + SMALL_FILE
143     big_file = mount_dir + '/' + BIG_FILE
144
145     try:
146
147         # 3GiB volume
148         fs_img = mk_fs(u_boot_config, fs_type, 0xc0000000, '3GB')
149
150         # Mount the image so we can populate it.
151         check_call('mkdir -p %s' % mount_dir, shell=True)
152         mount_fs(fs_type, fs_img, mount_dir)
153
154         # Create a subdirectory.
155         check_call('mkdir %s/SUBDIR' % mount_dir, shell=True)
156
157         # Create big file in this image.
158         # Note that we work only on the start 1MB, couple MBs in the 2GB range
159         # and the last 1 MB of the huge 2.5GB file.
160         # So, just put random values only in those areas.
161         check_call('dd if=/dev/urandom of=%s bs=1M count=1'
162             % big_file, shell=True)
163         check_call('dd if=/dev/urandom of=%s bs=1M count=2 seek=2047'
164             % big_file, shell=True)
165         check_call('dd if=/dev/urandom of=%s bs=1M count=1 seek=2499'
166             % big_file, shell=True)
167
168         # Create a small file in this image.
169         check_call('dd if=/dev/urandom of=%s bs=1M count=1'
170             % small_file, shell=True)
171
172         # Delete the small file copies which possibly are written as part of a
173         # previous test.
174         # check_call('rm -f "%s.w"' % MB1, shell=True)
175         # check_call('rm -f "%s.w2"' % MB1, shell=True)
176
177         # Generate the md5sums of reads that we will test against small file
178         out = check_output(
179             'dd if=%s bs=1M skip=0 count=1 2> /dev/null | md5sum'
180             % small_file, shell=True)
181         md5val = [ out.split()[0] ]
182
183         # Generate the md5sums of reads that we will test against big file
184         # One from beginning of file.
185         out = check_output(
186             'dd if=%s bs=1M skip=0 count=1 2> /dev/null | md5sum'
187             % big_file, shell=True)
188         md5val.append(out.split()[0])
189
190         # One from end of file.
191         out = check_output(
192             'dd if=%s bs=1M skip=2499 count=1 2> /dev/null | md5sum'
193             % big_file, shell=True)
194         md5val.append(out.split()[0])
195
196         # One from the last 1MB chunk of 2GB
197         out = check_output(
198             'dd if=%s bs=1M skip=2047 count=1 2> /dev/null | md5sum'
199             % big_file, shell=True)
200         md5val.append(out.split()[0])
201
202         # One from the start 1MB chunk from 2GB
203         out = check_output(
204             'dd if=%s bs=1M skip=2048 count=1 2> /dev/null | md5sum'
205             % big_file, shell=True)
206         md5val.append(out.split()[0])
207
208         # One 1MB chunk crossing the 2GB boundary
209         out = check_output(
210             'dd if=%s bs=512K skip=4095 count=2 2> /dev/null | md5sum'
211             % big_file, shell=True)
212         md5val.append(out.split()[0])
213
214         umount_fs(fs_type, mount_dir)
215     except CalledProcessError:
216         pytest.skip('Setup failed for filesystem: ' + fs_type)
217         return
218     else:
219         yield [fs_ubtype, fs_img, md5val]
220     finally:
221         umount_fs(fs_type, mount_dir)
222         call('rmdir %s' % mount_dir, shell=True)
223         if fs_img:
224             call('rm -f %s' % fs_img, shell=True)
225
226 #
227 # Fixture for extended fs test
228 #
229 # NOTE: yield_fixture was deprecated since pytest-3.0
230 @pytest.yield_fixture()
231 def fs_obj_ext(request, u_boot_config):
232     fs_type = request.param
233     fs_img = ''
234
235     fs_ubtype = fstype_to_ubname(fs_type)
236     check_ubconfig(u_boot_config, fs_ubtype)
237
238     mount_dir = u_boot_config.persistent_data_dir + '/mnt'
239
240     min_file = mount_dir + '/' + MIN_FILE
241     tmp_file = mount_dir + '/tmpfile'
242
243     try:
244
245         # 128MiB volume
246         fs_img = mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
247
248         # Mount the image so we can populate it.
249         check_call('mkdir -p %s' % mount_dir, shell=True)
250         mount_fs(fs_type, fs_img, mount_dir)
251
252         # Create a test directory
253         check_call('mkdir %s/dir1' % mount_dir, shell=True)
254
255         # Create a small file and calculate md5
256         check_call('dd if=/dev/urandom of=%s bs=1K count=20'
257             % min_file, shell=True)
258         out = check_output(
259             'dd if=%s bs=1K 2> /dev/null | md5sum'
260             % min_file, shell=True)
261         md5val = [ out.split()[0] ]
262
263         # Calculate md5sum of Test Case 4
264         check_call('dd if=%s of=%s bs=1K count=20'
265             % (min_file, tmp_file), shell=True)
266         check_call('dd if=%s of=%s bs=1K seek=5 count=20'
267             % (min_file, tmp_file), shell=True)
268         out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum'
269             % tmp_file, shell=True)
270         md5val.append(out.split()[0])
271
272         # Calculate md5sum of Test Case 5
273         check_call('dd if=%s of=%s bs=1K count=20'
274             % (min_file, tmp_file), shell=True)
275         check_call('dd if=%s of=%s bs=1K seek=5 count=5'
276             % (min_file, tmp_file), shell=True)
277         out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum'
278             % tmp_file, shell=True)
279         md5val.append(out.split()[0])
280
281         # Calculate md5sum of Test Case 7
282         check_call('dd if=%s of=%s bs=1K count=20'
283             % (min_file, tmp_file), shell=True)
284         check_call('dd if=%s of=%s bs=1K seek=20 count=20'
285             % (min_file, tmp_file), shell=True)
286         out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum'
287             % tmp_file, shell=True)
288         md5val.append(out.split()[0])
289
290         check_call('rm %s' % tmp_file, shell=True)
291         umount_fs(fs_type, mount_dir)
292     except CalledProcessError:
293         pytest.skip('Setup failed for filesystem: ' + fs_type)
294         return
295     else:
296         yield [fs_ubtype, fs_img, md5val]
297     finally:
298         umount_fs(fs_type, mount_dir)
299         call('rmdir %s' % mount_dir, shell=True)
300         if fs_img:
301             call('rm -f %s' % fs_img, shell=True)