Hi, On 5 November 2016 at 10:45, Stefan Brüns <stefan.bru...@rwth-aachen.de> wrote: > > The following checks are currently implemented: > 1. listing a directory > 2. verifying size of a file > 3. veryfying md5sum for a file region > 4. reading the beginning of a file > > Signed-off-by: Stefan Brüns <stefan.bru...@rwth-aachen.de> > --- > test/py/tests/test_fs.py | 298 > +++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 298 insertions(+) > create mode 100644 test/py/tests/test_fs.py > > diff --git a/test/py/tests/test_fs.py b/test/py/tests/test_fs.py > new file mode 100644 > index 0000000..5ac91e4 > --- /dev/null > +++ b/test/py/tests/test_fs.py > @@ -0,0 +1,298 @@ > +# Copyright (c) 2016, Stefan Bruens <stefan.bru...@rwth-aachen.de> > +# > +# SPDX-License-Identifier: GPL-2.0 > + > +# Test U-Boot's filesystem implementations
Can you add a few details here about what this tests? > + > +import hashlib > +import pytest > +import os > +import random > +import re > +import u_boot_utils as util > + > + > +mkfs_opts = { > + "fat" :'-t vfat', > + "ext4" : '-t ext4 -F', Can you please use a single quote unless you can't? That is the style used in U-Boot. > +} > + > +fs_commands = { > + "fat" : { > + 'listcmd' : 'ls', > + 'readcmd' : 'load', > + 'sizecmd' : 'size', > + 'writecmd' : 'size', > + }, > + "ext4" : { > + 'listcmd' : 'ls', > + 'readcmd' : 'load', > + 'sizecmd' : 'size', > + 'writecmd' : 'size', > + }, > +} > + > +cmd_parameters = { > + "hostfs" : { > + 'prefix' : 'host ', > + 'interface' : 'hostfs -', > + }, > + "generic" : { > + 'prefix' : '', > + 'interface' : 'host 0:0', > + }, > +} > + > +files = { > + "empty.file" : [(0, 0)], > + "1MB.file" : [(0, 1e6)], > + "1MB.sparse.file" : [(1e6-1, 1e6)], > +} > + # "2_5GB.sparse.file" : [(0, 1e6), (1e9, 1e9+1e6), (2.5e9-1e6, 2.5e9)], What is that line for? > + > +@pytest.fixture(scope="session") > +def prereq_commands(): > + from distutils.spawn import find_executable Why not import this at the top of the file? > + for command in ["mkfs", "mount", "umount"]: > + if find_executable(command) is None: > + pytest.skip('Filesystem tests, "{0}" not in > PATH'.format(command)) > + > +class FsImage: > + def __init__(self, fstype, imagename, mountpath): Please add comments as to what these params are. > + self.fstype = fstype > + self.imagename = imagename > + self.mountpath = mountpath > + self.md5s = {} > + with open(self.imagename, 'w') as fd: > + fd.truncate(0) > + fd.seek(3e9) > + fd.write(bytes([0])) > + > + def mkfs(self, log): > + mkfsopts = mkfs_opts.get(self.fstype) > + util.run_and_log(log, > + 'mkfs {0} {1}'.format(mkfsopts, self.imagename)) > + > + def create_file(self, log, filename): Please add a short comment on each non-function describing what it does and what the args are (and return value if any). > + md5sums = [] > + with open(self.mountpath + "/" + filename, 'w') as fd: I think this is better as os.path.join(self.mountpath, filename) > + for stride in files[filename]: > + length = int(stride[1] - stride[0]) > + data = bytearray(random.getrandbits(8) for _ in > xrange(length)) > + md5 = hashlib.md5(data).hexdigest() > + md5sums.append(md5) > + log.info("{0}: write {1} bytes @ {2} : {3}".format( > + filename, int(stride[1] - stride[0]), > + int(stride[0]), md5)) > + fd.seek(stride[0]) > + fd.write(data); > + self.md5s[filename] = md5sums > + > + def create_files(self, log): > + with log.section("Create initial files"): > + for filename in files: > + self.create_file(log, filename) > + log.info("Created test files in {0}".format(self.mountpath)) > + util.run_and_log(log, 'ls -la {0}'.format(self.mountpath)) > + util.run_and_log(log, 'sync {0}'.format(self.mountpath)) > + > + def mount(self, log): > + if not os.path.exists(self.mountpath): > + os.mkdir(self.mountpath) > + log.info("Mounting {0} at {1}".format(self.imagename, > self.mountpath)) > + if self.fstype == "ext4": > + cmd = 'sudo -n mount -o loop,rw {0} {1}'.format(self.imagename, > self.mountpath) > + else: > + cmd = 'sudo -n mount -o loop,rw,umask=000 {0} > {1}'.format(self.imagename, self.mountpath) > + util.run_and_log(log, cmd) > + if self.fstype == "ext4": > + cmd = 'sudo -n chmod og+rw {0}'.format(self.mountpath) > + return util.run_and_log(log, cmd) > + > + def unmount(self, log): > + log.info("Unmounting {0}".format(self.imagename)) > + cmd = 'sudo -n umount -l {0}'.format(self.mountpath) > + util.run_and_log(log, cmd, ignore_errors=True) > + > + > +@pytest.fixture(scope="module", params=["fat", "ext4"]) > +def fsimage(prereq_commands, u_boot_config, u_boot_log, request): > + datadir = u_boot_config.result_dir + '/' > + fstype = request.param > + imagename = datadir + "3GB." + fstype + ".img" > + mountpath = datadir + "mnt_" + fstype > + > + with u_boot_log.section('Create image {0}'.format(imagename)): > + fsimage = FsImage(fstype, imagename, mountpath) > + fsimage.mkfs(u_boot_log) > + > + yield fsimage > + fsimage.unmount(u_boot_log) > + > +@pytest.fixture(scope="module") > +def populated_image(fsimage, u_boot_log): > + try: > + fsimage.mount(u_boot_log) > + except Exception as e: > + pytest.skip('{0}: could not mount {1}'.format( > + fsimage.fstype, fsimage.imagename)) > + yield None > + > + fsimage.create_files(u_boot_log) > + fsimage.unmount(u_boot_log) > + yield fsimage > + > +# 'Transfer' the image to the client and make it accessible Please see other files to see how function comments should be formatted. > +@pytest.fixture(scope="function") > +def boundimage(populated_image, u_boot_console, request): > + image = populated_image > + if request.cls.scenario == "hostfs": > + image.mount(u_boot_console.log) > + image.rootpath = image.mountpath + "/" > + yield image > + image.unmount(u_boot_console.log) > + else: > + output = u_boot_console.run_command_list( > + ["host bind 0 {0}".format(image.imagename)]) > + image.rootpath = "/" > + yield image > + output = u_boot_console.run_command_list(["host bind 0 "]) > + > + > +# Dummy test to create an image file with filesystem > +# Useful to isolate fixture setup from actual tests > +def test_fs_prepare_image(u_boot_config, fsimage, request): > + if not fsimage: > + pytest.fail("Failed to create image") > + > +# Dummy test to create initial filesystem contents > +def test_fs_populate_image(populated_image, request): > + if not populated_image: > + pytest.fail("Failed create initial image content") > + > +# Scenarios: > +# hostfs: access image contents through the sandbox hostfs > +# facility, using the filesytem implementation of > +# the sandbox host, e.g. Linux kernel > +# generic: test u-boots native filesystem implementations, > +# using the 'generic' command names, e.g. 'load' > +# TODO - > +# fscommands: test u-boots native filesystem implementations, > +# using the fs specific commands, e.g. 'ext4load' > +@pytest.fixture(scope="class", params=["generic", "hostfs"]) > +def scenario(request): > + request.cls.scenario = request.param > + return request.param > + > +@pytest.mark.usefixtures("u_boot_console", "scenario") > +class TestFilesystems: > + ignore_cleanup_errors = True > + filesize_regex = re.compile("^filesize=([A-Fa-f0-9]+)") > + md5sum_regex = re.compile("^md5 for .* ==> ([A-Fa-f0-9]{32})") > + dirlist_regex = re.compile("\s+(\d+)\s+(\S+.file\S*)") Should these be self. ? > + > + def get_filesize(self, filename): > + strides = files[filename] > + return int(strides[-1][1]) > + > + def check_dirlist(self, string, filenames): > + m = self.dirlist_regex.findall(string) > + assert(m) > + for i, e in enumerate(m): > + m[i] = (int(e[0]), e[1].lower()) > + for f in filenames: > + e = (self.get_filesize(f), f.lower()) > + assert(e in m) > + > + def check_filesize(self, string, size): > + m = self.filesize_regex.match(string) > + assert(m) > + assert(int(m.group(1), 16) == size) > + > + def check_md5sum(self, string, md5): > + m = self.md5sum_regex.match(string) > + assert(m) > + assert(len(m.group(1)) == 32) > + assert(m.group(1) == md5) > + > + def run_listcmd(self, rootpath, dirname): > + cmd = "{0}{1} {2} {3}".format( > + self.fs_params.get('prefix'), > + self.fs_commands.get('listcmd'), > + self.fs_params.get('interface'), > + rootpath + dirname) > + with self.console.log.section('List "{0}"'.format(dirname)): > + output = self.console.run_command_list([cmd]) > + return output[0] > + > + def run_readcmd(self, rootpath, filename, offset, length): > + cmd = "{0}{1} {2} {3} {4} 0x{5:x} 0x{6:x}".format( > + self.fs_params.get('prefix'), > + self.fs_commands.get('readcmd'), > + self.fs_params.get('interface'), > + "0", # address > + rootpath + filename, > + length, offset) > + with self.console.log.section('Read file "{0}"'.format(filename)): > + output = self.console.run_command_list( > + [cmd, "env print filesize", > + "md5sum 0 $filesize", "env set filesize"]) > + return output[1:3] > + > + def run_sizecmd(self, rootpath, filename): > + cmd = "{0}{1} {2} {3}".format( > + self.fs_params.get('prefix'), > + self.fs_commands.get('sizecmd'), > + self.fs_params.get('interface'), > + rootpath + filename) > + with self.console.log.section('Get size of "{0}"'.format(filename)): > + output = self.console.run_command_list( > + [cmd, "env print filesize", "env set filesize"]) > + return output[1] > + > + @pytest.mark.parametrize("dirname", ["", "./"]) > + def test_fs_ls(self, boundimage, u_boot_console, dirname): > + self.console = u_boot_console > + self.fs_params = cmd_parameters.get(self.scenario) > + self.fs_commands = fs_commands.get(boundimage.fstype) > + > + output = self.run_listcmd(boundimage.rootpath, dirname) > + self.check_dirlist(output, files.keys()) > + > + @pytest.mark.parametrize("filename", files.keys()) > + def test_fs_filesize(self, boundimage, u_boot_console, filename): > + self.console = u_boot_console > + self.fs_params = cmd_parameters.get(self.scenario) > + self.fs_commands = fs_commands.get(boundimage.fstype) > + filesize = self.get_filesize(filename) > + > + output = self.run_sizecmd(boundimage.rootpath, filename) > + self.check_filesize(output, filesize) > + > + @pytest.mark.parametrize("filename", files.keys()) > + def test_fs_read(self, boundimage, u_boot_console, filename): > + self.console = u_boot_console > + self.fs_params = cmd_parameters.get(self.scenario) > + self.fs_commands = fs_commands.get(boundimage.fstype) > + md5s = boundimage.md5s[filename] > + > + for i, stride in enumerate(files[filename]): > + length = int(stride[1]) - int(stride[0]) > + output = self.run_readcmd( > + boundimage.rootpath, filename, int(stride[0]), length) > + self.check_filesize(output[0], length) > + self.console.log.info("md5: {0}".format(md5s[i])) > + self.check_md5sum(output[1], md5s[i]) > + > + @pytest.mark.parametrize("filename", files.keys()) > + def test_fs_read_head(self, boundimage, u_boot_console, filename): > + self.console = u_boot_console > + self.fs_params = cmd_parameters.get(self.scenario) > + self.fs_commands = fs_commands.get(boundimage.fstype) > + > + filesize = self.get_filesize(filename) > + filesize = min(filesize, 4e6) > + > + output = self.run_readcmd( > + boundimage.rootpath, filename, 0, filesize) > + self.check_filesize(output[0], filesize) > -- > 2.10.1 > Regards, Simon _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot