diff mbox series

[v3,1/4] support/testing/infra/emulator.py: change the shell prompt before running tests

Message ID 20230706201917.284060-1-ju.o@free.fr
State New
Headers show
Series [v3,1/4] support/testing/infra/emulator.py: change the shell prompt before running tests | expand

Commit Message

Julien Olivain July 6, 2023, 8:19 p.m. UTC
If a program has the string '# ' (i.e. the default shell prompt) in
its output, the test execution in the Buildroot runtime test infra is
failing.

This can be reproduced by adding a single line in a package test
script:

    self.assertRunOk("echo ### this is a string with hashes ###")

When executed with, for example, the command:

    support/testing/run-tests \
        -d dl -o output_folder tests.package.test_sometest

The test will fail with output:

    Traceback (most recent call last):
      File "/buildroot/support/testing/tests/package/test_sometest.py", line 20, in test_run
        self.assertRunOk("echo ### this is a string with hashes ###")
      File "/buildroot/support/testing/infra/basetest.py", line 94, in assertRunOk
        out, exit_code = self.emulator.run(cmd, timeout)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/buildroot/support/testing/infra/emulator.py", line 121, in run
        exit_code = int(exit_code)
                    ^^^^^^^^^^^^^^
    ValueError: invalid literal for int() with base 10: ''

This issue has been seen with real softwares, for example while
writing tests for the dieharder package, which creates report tables
formatted with hash characters. See:
https://github.com/eddelbuettel/dieharder/blob/3.31.1.4/libdieharder/version.c#L34
See also the output produced by a program invocation with command
"dieharder -d 0". This issue has also been seen with dmidecode,
producing comments in its output (when simply invoked with
"dmidecode"). See:
https://git.savannah.gnu.org/cgit/dmidecode.git/tree/dmioutput.c?h=dmidecode-3-5#n30

This issue could technically be seen with program producing MarkDown
formatted output (for example "# Header 1").

This issue happen because the test infra emulator.run() method expect
the prompt "# " after running a command and while getting the return
code. See:
https://git.buildroot.org/buildroot/tree/support/testing/infra/emulator.py?h=2023.05#n113

Since the string "# " is quite common, this patch changes the prompt
after the emulator.login(), by setting the PS1 variable to a string
which is less likely to appear in a normal program output. A small
caveat: since there is a command echo, the command setting the new
prompt needs to be slightly obfuscated to make sure it will not be
detected as an actual shell prompt. The simple obfuscation scheme
consist of single-quoting each character (e.g. abc -> 'a''b''c').

Signed-off-by: Julien Olivain <ju.o@free.fr>
---
Changes v2 -> v3:
- reworded the comment about the prompt obfuscation
- rewrote the prompt obfuscation with map()

Changes v1 -> v2:
- reworded commit log, to mention this issue was also seen while writing
  a test for the dmidecode package
- the patch series also introduce the new test for dmidecode
---
 support/testing/infra/emulator.py | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/support/testing/infra/emulator.py b/support/testing/infra/emulator.py
index 02cf486128..e010fb3d28 100644
--- a/support/testing/infra/emulator.py
+++ b/support/testing/infra/emulator.py
@@ -13,6 +13,7 @@  class Emulator(object):
         # can take a long time to run the emulator. Use a timeout multiplier
         # when running the tests to avoid sporadic failures.
         self.timeout_multiplier = timeout_multiplier
+        self.shell_prompt = "#BRTEST# "
 
     # Start Qemu to boot the system
     #
@@ -100,6 +101,16 @@  class Emulator(object):
         index = self.qemu.expect(["# ", pexpect.TIMEOUT])
         if index != 0:
             raise SystemError("Cannot login")
+        # We set a special shell prompt for testing. Since the
+        # standard prompt '# ' is quite generic, a normal process
+        # output could contain that string and confuse expect. When
+        # changing the prompt, we also need to slightly obfuscate it
+        # in some way to make sure the command echo will not be seen
+        # as a prompt itself. The simple obfuscation scheme just
+        # consist of single-quoting each character (for example, the
+        # string "abc" becomes 'a''b''c').
+        quoted_prompt = "".join((map(lambda c: f"'{c}'", self.shell_prompt)))
+        self.run("export PS1={}".format(quoted_prompt))
         self.run("dmesg -n 1")
         # Prevent the shell from wrapping the commands at 80 columns.
         self.run("stty columns 29999")
@@ -110,13 +121,13 @@  class Emulator(object):
         self.qemu.sendline(cmd)
         if timeout != -1:
             timeout *= self.timeout_multiplier
-        self.qemu.expect("# ", timeout=timeout)
+        self.qemu.expect(self.shell_prompt, timeout=timeout)
         # Remove double carriage return from qemu stdout so str.splitlines()
         # works as expected.
         output = self.qemu.before.replace("\r\r", "\r").splitlines()[1:]
 
         self.qemu.sendline("echo $?")
-        self.qemu.expect("# ")
+        self.qemu.expect(self.shell_prompt)
         exit_code = self.qemu.before.splitlines()[2]
         exit_code = int(exit_code)