diff --git a/test/py/tests/test_fs/test_squashfs/sqfs_common.py b/test/py/tests/test_fs/test_squashfs/sqfs_common.py index c96f92c1d8f85cffb671f5324ca193743b6a879f..267c4b57d1b6e5b11a9dc44531d2bc362fd80e32 100644 --- a/test/py/tests/test_fs/test_squashfs/sqfs_common.py +++ b/test/py/tests/test_fs/test_squashfs/sqfs_common.py @@ -3,74 +3,203 @@ # Author: Joao Marcos Costa import os -import random -import string +import shutil import subprocess -def sqfs_get_random_letters(size): - letters = [] - for i in range(0, size): - letters.append(random.choice(string.ascii_letters)) +""" standard test images table: Each table item is a key:value pair +representing the output image name and its respective mksquashfs options. +This table should be modified only when adding support for new compression +algorithms. The 'default' case takes no options but the input and output +names, so it must be assigned with an empty string. +""" +STANDARD_TABLE = { + 'default' : '', + 'lzo_comp_frag' : '', + 'lzo_frag' : '', + 'lzo_no_frag' : '', + 'zstd_comp_frag' : '', + 'zstd_frag' : '', + 'zstd_no_frag' : '', + 'gzip_comp_frag' : '', + 'gzip_frag' : '', + 'gzip_no_frag' : '' +} - return ''.join(letters) +""" EXTRA_TABLE: Set this table's keys and values if you want to make squashfs +images with your own customized options. +""" +EXTRA_TABLE = {} -def sqfs_generate_file(path, size): - content = sqfs_get_random_letters(size) - file = open(path, "w") +# path to source directory used to make squashfs test images +SQFS_SRC_DIR = 'sqfs_src_dir' + +def get_opts_list(): + """ Combines fragmentation and compression options into a list of strings. + + opts_list's firts item is an empty string as STANDARD_TABLE's first item is + the 'default' case. + + Returns: + A list of strings whose items are formed by a compression and a + fragmentation option joined by a whitespace. + """ + # supported compression options only + comp_opts = ['-comp lzo', '-comp zstd', '-comp gzip'] + # file fragmentation options + frag_opts = ['-always-use-fragments', '-always-use-fragments -noF', '-no-fragments'] + + opts_list = [' '] + for comp_opt in comp_opts: + for frag_opt in frag_opts: + opts_list.append(' '.join([comp_opt, frag_opt])) + + return opts_list + +def init_standard_table(): + """ Initializes STANDARD_TABLE values. + + STANDARD_TABLE's keys are pre-defined, and init_standard_table() assigns + the right value for each one of them. + """ + opts_list = get_opts_list() + + for key, value in zip(STANDARD_TABLE.keys(), opts_list): + STANDARD_TABLE[key] = value + +def generate_file(file_name, file_size): + """ Generates a file filled with 'x'. + + Args: + file_name: the file's name. + file_size: the content's length and therefore the file size. + """ + content = 'x' * file_size + + file = open(file_name, 'w') file.write(content) file.close() -class Compression: - def __init__(self, name, files, sizes, block_size = 4096): - self.name = name - self.files = files - self.sizes = sizes - self.mksquashfs_opts = " -b " + str(block_size) + " -comp " + self.name - - def add_opt(self, opt): - self.mksquashfs_opts += " " + opt - - def gen_image(self, build_dir): - src = os.path.join(build_dir, "sqfs_src/") - os.mkdir(src) - for (f, s) in zip(self.files, self.sizes): - sqfs_generate_file(src + f, s) - - # the symbolic link always targets the first file - os.symlink(self.files[0], src + "sym") - - sqfs_img = os.path.join(build_dir, "sqfs-" + self.name) - i_o = src + " " + sqfs_img - opts = self.mksquashfs_opts - try: - subprocess.run(["mksquashfs " + i_o + opts], shell = True, check = True) - except: - print("mksquashfs error. Compression type: " + self.name) - raise RuntimeError - - def clean_source(self, build_dir): - src = os.path.join(build_dir, "sqfs_src/") - for f in self.files: - os.remove(src + f) - os.remove(src + "sym") - os.rmdir(src) - - def cleanup(self, build_dir): - self.clean_source(build_dir) - sqfs_img = os.path.join(build_dir, "sqfs-" + self.name) - os.remove(sqfs_img) - -files = ["blks_only", "blks_frag", "frag_only"] -sizes = [4096, 5100, 100] -gzip = Compression("gzip", files, sizes) -zstd = Compression("zstd", files, sizes) -lzo = Compression("lzo", files, sizes) - -# use fragment blocks for files larger than block_size -gzip.add_opt("-always-use-fragments") -zstd.add_opt("-always-use-fragments") - -# avoid fragments if lzo is used -lzo.add_opt("-no-fragments") - -comp_opts = [gzip, zstd, lzo] +def generate_sqfs_src_dir(build_dir): + """ Generates the source directory used to make the SquashFS images. + + The source directory is generated at build_dir, and it has the following + structure: + sqfs_src_dir/ + ├── empty-dir/ + ├── f1000 + ├── f4096 + ├── f5096 + ├── subdir/ + │   └── subdir-file + └── sym -> subdir + + 3 directories, 4 files + + The files in the root dir. are prefixed with an 'f' followed by its size. + + Args: + build_dir: u-boot's build-sandbox directory. + """ + + root = os.path.join(build_dir, SQFS_SRC_DIR) + # make root directory + os.makedirs(root) + + # 4096: minimum block size + file_name = 'f4096' + generate_file(os.path.join(root, file_name), 4096) + + # 5096: minimum block size + 1000 chars (fragment) + file_name = 'f5096' + generate_file(os.path.join(root, file_name), 5096) + + # 1000: less than minimum block size (fragment only) + file_name = 'f1000' + generate_file(os.path.join(root, file_name), 1000) + + # sub-directory with a single file inside + subdir_path = os.path.join(root, 'subdir') + os.makedirs(subdir_path) + generate_file(os.path.join(subdir_path, 'subdir-file'), 100) + + # symlink (target: sub-directory) + os.symlink('subdir', os.path.join(root, 'sym')) + + # empty directory + os.makedirs(os.path.join(root, 'empty-dir')) + +def mksquashfs(args): + """ Runs mksquashfs command. + + Args: + args: mksquashfs options (e.g.: compression and fragmentation). + """ + subprocess.run(['mksquashfs ' + args], shell=True, check=True, + stdout=subprocess.DEVNULL) + +def get_mksquashfs_version(): + """ Parses the output of mksquashfs -version. + + Returns: + mksquashfs's version as a float. + """ + out = subprocess.run(['mksquashfs -version'], shell=True, check=True, + capture_output=True, text=True) + # 'out' is: mksquashfs version X (yyyy/mm/dd) ... + return float(out.stdout.split()[2]) + +def check_mksquashfs_version(): + """ Checks if mksquashfs meets the required version. """ + + required_version = 4.4 + if get_mksquashfs_version() < required_version: + print('Error: mksquashfs is too old.') + print('Required version: {}'.format(required_version)) + raise AssertionError + +def make_all_images(build_dir): + """ Makes the SquashFS images used in the test suite. + + The image names and respective mksquashfs options are defined in STANDARD_TABLE + and EXTRA_TABLE. The destination is defined by 'build_dir'. + + Args: + build_dir: u-boot's build-sandbox directory. + """ + + init_standard_table() + input_path = os.path.join(build_dir, SQFS_SRC_DIR) + + # make squashfs images according to STANDARD_TABLE + for out, opts in zip(STANDARD_TABLE.keys(), STANDARD_TABLE.values()): + output_path = os.path.join(build_dir, out) + mksquashfs(' '.join([input_path, output_path, opts])) + + # make squashfs images according to EXTRA_TABLE + for out, opts in zip(EXTRA_TABLE.keys(), EXTRA_TABLE.values()): + output_path = os.path.join(build_dir, out) + mksquashfs(' '.join([input_path, output_path, opts])) + +def clean_all_images(build_dir): + """ Deletes the SquashFS images at build_dir. + + Args: + build_dir: u-boot's build-sandbox directory. + """ + + for image_name in STANDARD_TABLE: + image_path = os.path.join(build_dir, image_name) + os.remove(image_path) + + for image_name in EXTRA_TABLE: + image_path = os.path.join(build_dir, image_name) + os.remove(image_path) + +def clean_sqfs_src_dir(build_dir): + """ Deletes the source directory at build_dir. + + Args: + build_dir: u-boot's build-sandbox directory. + """ + path = os.path.join(build_dir, SQFS_SRC_DIR) + shutil.rmtree(path)