1import os
2
3import infra.basetest
4
5
6class TestCompressorBase(infra.basetest.BRTest):
7    """Common class to test a data compression/decompression package.
8
9    Build an image containing the package enabled in config, start the
10    emulator, login to it. It prepares a test data file with some
11    redundancy to let compression program reduce the file size.
12
13    Each test case that inherits from this class must have:
14    __test__ = True  - to let nose2 know that it is a test case
15    config           - defconfig fragment with the packages to run the test
16    compress_cmd     - the compression program command (ex: "gzip")
17                       it can also contain arguments (ex: "gzip -9")
18    It also can have:
19    decompress_cmd   - the decompression program (ex: "gunzip")
20                       if unset, the default value is "un" appended with the
21                       value of "compress_cmd".
22    check_integrity_cmd
23                     - the integrity check command (ex: "gzip -t")
24                       in unset, the default value is "compress_cmd" appended
25                       with " -t".
26    compressed_file_ext
27                     - the file extention of compressed files created by the
28                       compress command. (ex: ".gz")
29                       if unset, the default value is a dot "." appended with
30                       the value of "compress_cmd".
31    timeout          - timeout to the compression command. Some compression
32                       program can take more time than the default value
33                       (set to 5 seconds).
34    """
35    __test__ = False
36    config = infra.basetest.BASIC_TOOLCHAIN_CONFIG + \
37        """
38        BR2_TARGET_ROOTFS_CPIO=y
39        # BR2_TARGET_ROOTFS_TAR is not set
40        """
41    compress_cmd = "compressor-unknown"
42    decompress_cmd = None
43    check_integrity_cmd = None
44    compressed_file_ext = None
45    timeout = 5
46
47    def __init__(self, names):
48        """Setup common test variables."""
49        super(TestCompressorBase, self).__init__(names)
50
51        if self.decompress_cmd is None:
52            self.decompress_cmd = "un" + self.compress_cmd
53
54        if self.check_integrity_cmd is None:
55            self.check_integrity_cmd = self.compress_cmd + " -t"
56
57        if self.compressed_file_ext is None:
58            self.compressed_file_ext = "." + self.compress_cmd
59
60        self.ref_file = "reference.bin"
61        self.test_file = "test.bin"
62        self.comp_test_file = self.test_file + self.compressed_file_ext
63
64    def login(self):
65        cpio_file = os.path.join(self.builddir, "images", "rootfs.cpio")
66        self.emulator.boot(arch="armv5",
67                           kernel="builtin",
68                           options=["-initrd", cpio_file])
69        self.emulator.login()
70
71    def test_run(self):
72        self.login()
73        self.prepare_data()
74        self.compress_test()
75        self.check_integrity_test()
76        self.uncompress_test()
77        self.compare_test()
78
79    def prepare_data(self):
80        # Create a data file composed of: 128kB of random data, then
81        # 128kB of zeroes, then a repetition of the 1st 128kB of
82        # random.
83        self.assertRunOk("dd if=/dev/urandom of=rand.bin bs=128k count=1")
84        self.assertRunOk("dd if=/dev/zero of=zeroes.bin bs=128k count=1")
85        self.assertRunOk(f"cat rand.bin zeroes.bin rand.bin > {self.ref_file}")
86
87        # Keep a copy of the reference data since
88        # compressor/decompressor programs usually unlink source data
89        # file.
90        self.assertRunOk(f"cp {self.ref_file} {self.test_file}")
91
92    def compress_test(self):
93        # Run the compression
94        self.assertRunOk(f"{self.compress_cmd} {self.test_file}", timeout=self.timeout)
95
96        # Check that the compressed file was created with the expected name
97        self.assertRunOk(f"test -e {self.comp_test_file}")
98
99        # Remove the input test file. Some compressors (like gzip) are
100        # removing it automatically by default. Some others (like lz4)
101        # are keeping those by default. We always remove it to make
102        # sure a new file will be recreated by the decompression.
103        self.assertRunOk(f"rm -f {self.test_file}")
104
105        # Check the compressed file is smaller than the input.
106        # The "ls -l" command is for simplifying debugging
107        self.assertRunOk(f"ls -l {self.ref_file} {self.comp_test_file}")
108        ref_sz_cmd = f"wc -c < {self.ref_file}"
109        comp_sz_cmd = f"wc -c < {self.comp_test_file}"
110        self.assertRunOk(f"test $({ref_sz_cmd}) -gt $({comp_sz_cmd})")
111
112    def check_integrity_test(self):
113        # Check the compressed file integrity
114        self.assertRunOk(f"{self.check_integrity_cmd} {self.comp_test_file}")
115
116    def uncompress_test(self):
117        # Run the decompression
118        self.assertRunOk(f"{self.decompress_cmd} {self.comp_test_file}")
119
120        # Check the decompressed file was created with the correct name
121        self.assertRunOk(f"test -e {self.test_file}")
122
123    def compare_test(self):
124        # Check the decompressed file is exactly the same as the
125        # reference created at the beginning
126        self.assertRunOk(f"cmp {self.test_file} {self.ref_file}")
127