1# SPDX-License-Identifier: GPL-2.0+
2# Copyright (C) 2022 Huang Jianan <jnhuang95@gmail.com>
3# Author: Huang Jianan <jnhuang95@gmail.com>
4
5import os
6import pytest
7import shutil
8import subprocess
9
10EROFS_SRC_DIR = 'erofs_src_dir'
11EROFS_IMAGE_NAME = 'erofs.img'
12
13def generate_file(name, size):
14    """
15    Generates a file filled with 'x'.
16    """
17    content = 'x' * size
18    file = open(name, 'w')
19    file.write(content)
20    file.close()
21
22def make_erofs_image(build_dir):
23    """
24    Makes the EROFS images used for the test.
25
26    The image is generated at build_dir with the following structure:
27    erofs_src_dir/
28    ├── f4096
29    ├── f7812
30    ├── subdir/
31    │   └── subdir-file
32    ├── symdir -> subdir
33    └── symfile -> f5096
34    """
35    root = os.path.join(build_dir, EROFS_SRC_DIR)
36    os.makedirs(root)
37
38    # 4096: uncompressed file
39    generate_file(os.path.join(root, 'f4096'), 4096)
40
41    # 7812: Compressed file
42    generate_file(os.path.join(root, 'f7812'), 7812)
43
44    # sub-directory with a single file inside
45    subdir_path = os.path.join(root, 'subdir')
46    os.makedirs(subdir_path)
47    generate_file(os.path.join(subdir_path, 'subdir-file'), 100)
48
49    # symlink
50    os.symlink('subdir', os.path.join(root, 'symdir'))
51    os.symlink('f7812', os.path.join(root, 'symfile'))
52
53    input_path = os.path.join(build_dir, EROFS_SRC_DIR)
54    output_path = os.path.join(build_dir, EROFS_IMAGE_NAME)
55    args = ' '.join([output_path, input_path])
56    subprocess.run(['mkfs.erofs -zlz4 ' + args], shell=True, check=True,
57                   stdout=subprocess.DEVNULL)
58
59def clean_erofs_image(build_dir):
60    """
61    Deletes the image and src_dir at build_dir.
62    """
63    path = os.path.join(build_dir, EROFS_SRC_DIR)
64    shutil.rmtree(path)
65    image_path = os.path.join(build_dir, EROFS_IMAGE_NAME)
66    os.remove(image_path)
67
68def erofs_ls_at_root(u_boot_console):
69    """
70    Test if all the present files and directories were listed.
71    """
72    no_slash = u_boot_console.run_command('erofsls host 0')
73    slash = u_boot_console.run_command('erofsls host 0 /')
74    assert no_slash == slash
75
76    expected_lines = ['./', '../', '4096   f4096', '7812   f7812', 'subdir/',
77                      '<SYM>   symdir', '<SYM>   symfile', '4 file(s), 3 dir(s)']
78
79    output = u_boot_console.run_command('erofsls host 0')
80    for line in expected_lines:
81        assert line in output
82
83def erofs_ls_at_subdir(u_boot_console):
84    """
85    Test if the path resolution works.
86    """
87    expected_lines = ['./', '../', '100   subdir-file', '1 file(s), 2 dir(s)']
88    output = u_boot_console.run_command('erofsls host 0 subdir')
89    for line in expected_lines:
90        assert line in output
91
92def erofs_ls_at_symlink(u_boot_console):
93    """
94    Test if the symbolic link's target resolution works.
95    """
96    output = u_boot_console.run_command('erofsls host 0 symdir')
97    output_subdir = u_boot_console.run_command('erofsls host 0 subdir')
98    assert output == output_subdir
99
100    expected_lines = ['./', '../', '100   subdir-file', '1 file(s), 2 dir(s)']
101    for line in expected_lines:
102        assert line in output
103
104def erofs_ls_at_non_existent_dir(u_boot_console):
105    """
106    Test if the EROFS support will crash when get a nonexistent directory.
107    """
108    out_non_existent = u_boot_console.run_command('erofsls host 0 fff')
109    out_not_dir = u_boot_console.run_command('erofsls host 0 f1000')
110    assert out_non_existent == out_not_dir
111    assert '' in out_non_existent
112
113def erofs_load_files(u_boot_console, files, sizes, address):
114    """
115    Loads files and asserts their checksums.
116    """
117    build_dir = u_boot_console.config.build_dir
118    for (file, size) in zip(files, sizes):
119        out = u_boot_console.run_command('erofsload host 0 {} {}'.format(address, file))
120
121        # check if the right amount of bytes was read
122        assert size in out
123
124        # calculate u-boot file's checksum
125        out = u_boot_console.run_command('md5sum {} {}'.format(address, hex(int(size))))
126        u_boot_checksum = out.split()[-1]
127
128        # calculate original file's checksum
129        original_file_path = os.path.join(build_dir, EROFS_SRC_DIR + '/' + file)
130        out = subprocess.run(['md5sum ' + original_file_path], shell=True, check=True,
131                             capture_output=True, text=True)
132        original_checksum = out.stdout.split()[0]
133
134        # compare checksum
135        assert u_boot_checksum == original_checksum
136
137def erofs_load_files_at_root(u_boot_console):
138    """
139    Test load file from the root directory.
140    """
141    files = ['f4096', 'f7812']
142    sizes = ['4096', '7812']
143    address = '$kernel_addr_r'
144    erofs_load_files(u_boot_console, files, sizes, address)
145
146def erofs_load_files_at_subdir(u_boot_console):
147    """
148    Test load file from the subdirectory.
149    """
150    files = ['subdir/subdir-file']
151    sizes = ['100']
152    address = '$kernel_addr_r'
153    erofs_load_files(u_boot_console, files, sizes, address)
154
155def erofs_load_files_at_symlink(u_boot_console):
156    """
157    Test load file from the symlink.
158    """
159    files = ['symfile']
160    sizes = ['7812']
161    address = '$kernel_addr_r'
162    erofs_load_files(u_boot_console, files, sizes, address)
163
164def erofs_load_non_existent_file(u_boot_console):
165    """
166    Test if the EROFS support will crash when load a nonexistent file.
167    """
168    address = '$kernel_addr_r'
169    file = 'non-existent'
170    out = u_boot_console.run_command('erofsload host 0 {} {}'.format(address, file))
171    assert 'Failed to load' in out
172
173def erofs_run_all_tests(u_boot_console):
174    """
175    Runs all test cases.
176    """
177    erofs_ls_at_root(u_boot_console)
178    erofs_ls_at_subdir(u_boot_console)
179    erofs_ls_at_symlink(u_boot_console)
180    erofs_ls_at_non_existent_dir(u_boot_console)
181    erofs_load_files_at_root(u_boot_console)
182    erofs_load_files_at_subdir(u_boot_console)
183    erofs_load_files_at_symlink(u_boot_console)
184    erofs_load_non_existent_file(u_boot_console)
185
186@pytest.mark.boardspec('sandbox')
187@pytest.mark.buildconfigspec('cmd_fs_generic')
188@pytest.mark.buildconfigspec('cmd_erofs')
189@pytest.mark.buildconfigspec('fs_erofs')
190@pytest.mark.requiredtool('mkfs.erofs')
191@pytest.mark.requiredtool('md5sum')
192
193def test_erofs(u_boot_console):
194    """
195    Executes the erofs test suite.
196    """
197    build_dir = u_boot_console.config.build_dir
198
199    try:
200        # setup test environment
201        make_erofs_image(build_dir)
202        image_path = os.path.join(build_dir, EROFS_IMAGE_NAME)
203        u_boot_console.run_command('host bind 0 {}'.format(image_path))
204        # run all tests
205        erofs_run_all_tests(u_boot_console)
206    except:
207        clean_erofs_image(build_dir)
208        raise AssertionError
209
210    # clean test environment
211    clean_erofs_image(build_dir)
212