[U-Boot] [PATCH 3/3] test/py: Create tests for ext4 and fat testing on sandbox

Simon Glass sjg at chromium.org
Fri Nov 11 21:17:14 CET 2016


Hi,

On 5 November 2016 at 10:45, Stefan Brüns <stefan.bruens at 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.bruens at 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.bruens at 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?

> +
> + at 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)
> +
> +
> + at 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)
> +
> + at 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.

> + at 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'
> + at pytest.fixture(scope="class", params=["generic", "hostfs"])
> +def scenario(request):
> +    request.cls.scenario = request.param
> +    return request.param
> +
> + at 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


More information about the U-Boot mailing list