1import os
2
3import infra.basetest
4
5
6class TestSed(infra.basetest.BRTest):
7    config = infra.basetest.BASIC_TOOLCHAIN_CONFIG + \
8        """
9        BR2_PACKAGE_BUSYBOX_SHOW_OTHERS=y
10        BR2_PACKAGE_SED=y
11        BR2_TARGET_ROOTFS_CPIO=y
12        # BR2_TARGET_ROOTFS_TAR is not set
13        """
14
15    def check_gnu_sed(self):
16        in_file = "testfile.txt"
17
18        # We create a test file for this test.
19        self.assertRunOk(f"echo 'This is a test' > {in_file}")
20
21        # Check we have the GNU sed by testing a GNU extension likely
22        # not present in other implementation. See:
23        # https://www.gnu.org/software/sed/manual/sed.html#Extended-Commands
24        # Note: we cannot search for "GNU sed" in sed --version,
25        # because busybox sed --version outputs: "This is not GNU sed
26        # version 4.0". The 'F' and 'Q' sed commands are known to be
27        # unimplemented in BusyBox 1.36.1.
28        expected_code = 123
29        sed_script = f"F;Q {expected_code}"
30        cmd = f"sed '{sed_script}' {in_file}"
31        output, exit_code = self.emulator.run(cmd)
32        self.assertEqual(exit_code, expected_code)
33        self.assertEqual(output, [in_file])
34
35    def check_sed_substitute(self):
36        testfile_num = 5
37
38        # We create few different test files for this test.
39        cmd = f'for i in $(seq {testfile_num}) ; do '
40        cmd += 'echo "=== $i Hello ===" > file$i.txt ; '
41        cmd += 'done'
42        self.assertRunOk(cmd)
43
44        # We reformat file content, in-place.
45        sed_script = "s/^=== \\([0-9]*\\) \\(Hello\\) ===$/\\2 \\1/"
46        cmd = f"sed -i '{sed_script}' file[0-9]*.txt"
47        self.assertRunOk(cmd)
48
49        # We substitute numbers with the string "Buildroot". We use an
50        # extended regular expression (with the '+'), so we test with
51        # the '-r' option.
52        sed_script = "s/[0-9]+/Buildroot/g"
53        cmd = f"sed -r -i '{sed_script}' file[0-9]*.txt"
54        self.assertRunOk(cmd)
55
56        # Our previous text manipulations are expected to end up with
57        # the "Hello Buildroot" string in all files.
58        cmd = "cat file[0-9]*.txt"
59        output, exit_code = self.emulator.run(cmd)
60        self.assertEqual(exit_code, 0)
61        self.assertEqual(output, ["Hello Buildroot"] * testfile_num)
62
63    def check_sed_line_count(self):
64        # We use the '=' command to count lines.
65        line_count = 1234
66        cmd = f"seq {line_count} | sed -n '$='"
67        output, exit_code = self.emulator.run(cmd)
68        self.assertEqual(exit_code, 0)
69        self.assertEqual(int(output[0]), line_count)
70
71    def check_sed_line_address(self):
72        input_file = "strings.txt"
73        expected_file = "expected.txt"
74
75        # We create simple data for this test.
76        strings = ["one", "two", "three", "four", "five"]
77        content = '\\n'.join(strings)
78        cmd = f"echo -e \"{content}\" > {input_file}"
79        self.assertRunOk(cmd)
80
81        # The manipulation in this tests are expected to extract the
82        # first and last of the input. We create the expected data for
83        # comparison.
84        expected_output = [strings[0], strings[-1]]
85        content = '\\n'.join(expected_output)
86        cmd = f"echo -e \"{content}\" > {expected_file}"
87        self.assertRunOk(cmd)
88
89        # We remove lines between strings "two" and "four" included.
90        cmd = f"sed '/two/,/four/d' {input_file} > output1.txt"
91        self.assertRunOk(cmd)
92
93        # We check output is the same as the expected data.
94        cmd = f"cmp {expected_file} output1.txt"
95        self.assertRunOk(cmd)
96
97        # We redo the same manipulation using line number addresses.
98        cmd = f"sed -n '1p;5p' {input_file} > output2.txt"
99        self.assertRunOk(cmd)
100
101        # We check again output is correct.
102        cmd = f"cmp {expected_file} output2.txt"
103        self.assertRunOk(cmd)
104
105    def test_run(self):
106        cpio_file = os.path.join(self.builddir, "images", "rootfs.cpio")
107        self.emulator.boot(arch="armv5",
108                           kernel="builtin",
109                           options=["-initrd", cpio_file])
110        self.emulator.login()
111
112        # Check the program can execute
113        self.assertRunOk("sed --version")
114
115        self.check_gnu_sed()
116        self.check_sed_substitute()
117        self.check_sed_line_count()
118        self.check_sed_line_address()
119