Buildroot Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [Buildroot] [PATCH v2 1/3] support/testing/infra/emulator.py: change the shell prompt before running tests
@ 2023-06-11 10:45 Julien Olivain
  2023-06-26 20:00 ` [Buildroot] [PATCH v2 2/3] support/testing/tests/package/test_dieharder.py: new runtime test Julien Olivain
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Julien Olivain @ 2023-06-11 10:45 UTC (permalink / raw)
  To: buildroot; +Cc: Julien Olivain, Ricardo Martincoski

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 protected to make sure it will not be detected as
an actual shell prompt. The prompt is encoded by single-quoting
each character (e.g. abc -> 'a''b''c').

Signed-off-by: Julien Olivain <ju.o@free.fr>
---
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 | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/support/testing/infra/emulator.py b/support/testing/infra/emulator.py
index 02cf486128..390c582e9d 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,15 @@ class Emulator(object):
         index = self.qemu.expect(["# ", pexpect.TIMEOUT])
         if index != 0:
             raise SystemError("Cannot login")
+        # Set a special shell prompt while 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 encode or escape it in some way to
+        # make sure the command echo will not be seen as a prompt
+        # itself. The prompt is encoded by single-quoting each
+        # character (e.g. abc -> 'a''b''c').
+        encoded_prompt = "''".join(self.shell_prompt)
+        self.run("export PS1='{}'".format(encoded_prompt))
         self.run("dmesg -n 1")
         # Prevent the shell from wrapping the commands at 80 columns.
         self.run("stty columns 29999")
@@ -110,13 +120,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)
 
-- 
2.41.0

_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [Buildroot] [PATCH v2 2/3] support/testing/tests/package/test_dieharder.py: new runtime test
  2023-06-11 10:45 [Buildroot] [PATCH v2 1/3] support/testing/infra/emulator.py: change the shell prompt before running tests Julien Olivain
@ 2023-06-26 20:00 ` Julien Olivain
  2023-06-26 20:01 ` [Buildroot] [PATCH v2 3/3] support/testing/tests/package/test_dmidecode.py: " Julien Olivain
  2023-06-26 21:06 ` [Buildroot] [PATCH v2 1/3] support/testing/infra/emulator.py: change the shell prompt before running tests Yann E. MORIN
  2 siblings, 0 replies; 6+ messages in thread
From: Julien Olivain @ 2023-06-26 20:00 UTC (permalink / raw)
  To: buildroot; +Cc: Julien Olivain

Signed-off-by: Julien Olivain <ju.o@free.fr>
---
 DEVELOPERS                                    |  1 +
 .../testing/tests/package/test_dieharder.py   | 40 +++++++++++++++++++
 2 files changed, 41 insertions(+)
 create mode 100644 support/testing/tests/package/test_dieharder.py

diff --git a/DEVELOPERS b/DEVELOPERS
index 7aa5980df7..1bf1969ac6 100644
--- a/DEVELOPERS
+++ b/DEVELOPERS
@@ -1737,6 +1737,7 @@ F:	support/testing/tests/package/sample_python_pyalsa.py
 F:	support/testing/tests/package/sample_python_spake2.py
 F:	support/testing/tests/package/test_ddrescue.py
 F:	support/testing/tests/package/test_ddrescue/
+F:	support/testing/tests/package/test_dieharder.py
 F:	support/testing/tests/package/test_gnupg2.py
 F:	support/testing/tests/package/test_highway.py
 F:	support/testing/tests/package/test_hwloc.py
diff --git a/support/testing/tests/package/test_dieharder.py b/support/testing/tests/package/test_dieharder.py
new file mode 100644
index 0000000000..d324a34557
--- /dev/null
+++ b/support/testing/tests/package/test_dieharder.py
@@ -0,0 +1,40 @@
+import os
+
+import infra.basetest
+
+
+class TestDieharder(infra.basetest.BRTest):
+    config = infra.basetest.BASIC_TOOLCHAIN_CONFIG + \
+        """
+        BR2_PACKAGE_DIEHARDER=y
+        BR2_TARGET_ROOTFS_CPIO=y
+        # BR2_TARGET_ROOTFS_TAR is not set
+        """
+
+    def test_run(self):
+        cpio_file = os.path.join(self.builddir, "images", "rootfs.cpio")
+        self.emulator.boot(arch="armv5",
+                           kernel="builtin",
+                           options=["-initrd", cpio_file])
+        self.emulator.login()
+
+        # Check the program can run (by showing its version)
+        self.assertRunOk("dieharder -V")
+
+        # The birthdays test on small number (1) of sample is expected to fail
+        cmd = "dieharder -g /dev/urandom -d diehard_birthdays -t 1"
+        output, exit_code = self.emulator.run(cmd)
+        self.assertEqual(exit_code, 0)
+        self.assertIn("FAILED", '\n'.join(output))
+
+        # The birthdays test on higher number (25) of sample is expected to succeed
+        cmd = "dieharder -g mt19937 -d diehard_birthdays -t 25"
+        output, exit_code = self.emulator.run(cmd, timeout=10)
+        self.assertEqual(exit_code, 0)
+        self.assertIn("PASSED", '\n'.join(output))
+
+        # The birthdays test on file /dev/zero is expected to fail
+        cmd = "dieharder -g file_input_raw -f /dev/zero -d diehard_birthdays -t 25"
+        output, exit_code = self.emulator.run(cmd, timeout=40)
+        self.assertEqual(exit_code, 0)
+        self.assertIn("FAILED", '\n'.join(output))
-- 
2.41.0

_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [Buildroot] [PATCH v2 3/3] support/testing/tests/package/test_dmidecode.py: new runtime test
  2023-06-11 10:45 [Buildroot] [PATCH v2 1/3] support/testing/infra/emulator.py: change the shell prompt before running tests Julien Olivain
  2023-06-26 20:00 ` [Buildroot] [PATCH v2 2/3] support/testing/tests/package/test_dieharder.py: new runtime test Julien Olivain
@ 2023-06-26 20:01 ` Julien Olivain
  2023-06-26 21:06 ` [Buildroot] [PATCH v2 1/3] support/testing/infra/emulator.py: change the shell prompt before running tests Yann E. MORIN
  2 siblings, 0 replies; 6+ messages in thread
From: Julien Olivain @ 2023-06-26 20:01 UTC (permalink / raw)
  To: buildroot; +Cc: Julien Olivain

Signed-off-by: Julien Olivain <ju.o@free.fr>
---
 DEVELOPERS                                    |  1 +
 .../testing/tests/package/test_dmidecode.py   | 67 +++++++++++++++++++
 2 files changed, 68 insertions(+)
 create mode 100644 support/testing/tests/package/test_dmidecode.py

diff --git a/DEVELOPERS b/DEVELOPERS
index 1bf1969ac6..a9475ec689 100644
--- a/DEVELOPERS
+++ b/DEVELOPERS
@@ -1738,6 +1738,7 @@ F:	support/testing/tests/package/sample_python_spake2.py
 F:	support/testing/tests/package/test_ddrescue.py
 F:	support/testing/tests/package/test_ddrescue/
 F:	support/testing/tests/package/test_dieharder.py
+F:	support/testing/tests/package/test_dmidecode.py
 F:	support/testing/tests/package/test_gnupg2.py
 F:	support/testing/tests/package/test_highway.py
 F:	support/testing/tests/package/test_hwloc.py
diff --git a/support/testing/tests/package/test_dmidecode.py b/support/testing/tests/package/test_dmidecode.py
new file mode 100644
index 0000000000..2363470e22
--- /dev/null
+++ b/support/testing/tests/package/test_dmidecode.py
@@ -0,0 +1,67 @@
+import os
+
+import infra.basetest
+
+
+class TestDmidecode(infra.basetest.BRTest):
+    # We use a x86_64 arch for this dmidecode test because aarch64
+    # SMBIOS is not supported in non-UEFI use-cases (and using a UEFI
+    # aarch64 would make the test longer).
+    config = \
+        """
+        BR2_x86_64=y
+        BR2_x86_corei7=y
+        BR2_TOOLCHAIN_EXTERNAL=y
+        BR2_LINUX_KERNEL=y
+        BR2_LINUX_KERNEL_CUSTOM_VERSION=y
+        BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.1.33"
+        BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
+        BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/qemu/x86_64/linux.config"
+        BR2_LINUX_KERNEL_NEEDS_HOST_LIBELF=y
+        BR2_PACKAGE_DMIDECODE=y
+        BR2_TARGET_ROOTFS_CPIO=y
+        BR2_TARGET_ROOTFS_CPIO_GZIP=y
+        # BR2_TARGET_ROOTFS_TAR is not set
+        """
+
+    def test_run(self):
+
+        # An arbitrary SMBIOS OEM string for the test
+        oem_string = "Hello Buildroot SMBIOS"
+
+        kernel = os.path.join(self.builddir, "images", "bzImage")
+        cpio_file = os.path.join(self.builddir, "images", "rootfs.cpio.gz")
+        self.emulator.boot(
+            arch="x86_64",
+            kernel=kernel,
+            kernel_cmdline=["console=ttyS0"],
+            options=["-cpu", "Nehalem", "-m", "256", "-initrd", cpio_file,
+                     "-smbios", f"type=11,value={oem_string}"],
+        )
+        self.emulator.login()
+
+        # Check the program can run
+        cmd = "dmidecode --version"
+        self.assertRunOk(cmd)
+
+        # Check a simple invocation of "dmidecode"
+        self.assertRunOk("dmidecode")
+
+        # Check a simple invocation of "biosdecode"
+        self.assertRunOk("biosdecode")
+
+        # Check dmidecode detects SMBIOS
+        cmd = "dmidecode | grep -E '^SMBIOS .* present\\.$'"
+        self.assertRunOk(cmd)
+
+        # Check the system-manufacturer is QEMU
+        cmd = "dmidecode -s system-manufacturer"
+        output, exit_code = self.emulator.run(cmd)
+        self.assertEqual(exit_code, 0)
+        self.assertEqual(output[0], "QEMU")
+
+        # Check we read back our OEM string
+        cmd = "dmidecode --oem-string 1"
+        output, exit_code = self.emulator.run(cmd)
+        self.assertEqual(exit_code, 0)
+        self.assertEqual(output[0], oem_string)
-- 
2.41.0

_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [Buildroot] [PATCH v2 1/3] support/testing/infra/emulator.py: change the shell prompt before running tests
  2023-06-11 10:45 [Buildroot] [PATCH v2 1/3] support/testing/infra/emulator.py: change the shell prompt before running tests Julien Olivain
  2023-06-26 20:00 ` [Buildroot] [PATCH v2 2/3] support/testing/tests/package/test_dieharder.py: new runtime test Julien Olivain
  2023-06-26 20:01 ` [Buildroot] [PATCH v2 3/3] support/testing/tests/package/test_dmidecode.py: " Julien Olivain
@ 2023-06-26 21:06 ` Yann E. MORIN
  2023-06-27 19:43   ` Julien Olivain
  2 siblings, 1 reply; 6+ messages in thread
From: Yann E. MORIN @ 2023-06-26 21:06 UTC (permalink / raw)
  To: Julien Olivain; +Cc: Ricardo Martincoski, buildroot

Julien, All,

On 2023-06-11 12:45 +0200, Julien Olivain spake thusly:
> 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 ###")

The most obvious issue is when testing the environment of the shell with
env, which will output PS1 itself... There is not easy way around to fix
that, of course... :-/

I was beaten by this when adding a test for bash. I eventually solved it
in another way, but took a note to find a better solution "for later"...
;-)

> 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 protected to make sure it will not be detected as
> an actual shell prompt. The prompt is encoded by single-quoting
> each character (e.g. abc -> 'a''b''c').

That's not really what join() will do. E.g. with your code:

    >>> s = "#BRTEST# "
    >>> "''".join(s)
    "#''B''R''T''E''S''T''#'' "

But we don;t really care because it is then quite improbable that some
random program will output exactly this...

I was on my side thinking about using ANSI escape sequences to carry the
information that the command was actually fnished, something along the
lines of:

    export PS1="\x1B_BR_COMMAND_FINISHED\x1B\\# "

  - \x1B_ is APC (Application Program Command),
  - \x1B\\ is ST (String Terminator), which terminates APC

Instead of APC, we could use any of:

  - \x1BP DCS, Device Control String
  - \x1BX SOS, Start Of String
  - \x1B^ PM, Privacy Message
  - or even \x1B] OSC, Operating System Command

But maybe that is a bit overkill in the end...

Anyway, your patch at least breaks tests.package.test_bash...

Regards,
Yann E. MORIN.

> Signed-off-by: Julien Olivain <ju.o@free.fr>
> ---
> 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 | 14 ++++++++++++--
>  1 file changed, 12 insertions(+), 2 deletions(-)
> 
> diff --git a/support/testing/infra/emulator.py b/support/testing/infra/emulator.py
> index 02cf486128..390c582e9d 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,15 @@ class Emulator(object):
>          index = self.qemu.expect(["# ", pexpect.TIMEOUT])
>          if index != 0:
>              raise SystemError("Cannot login")
> +        # Set a special shell prompt while 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 encode or escape it in some way to
> +        # make sure the command echo will not be seen as a prompt
> +        # itself. The prompt is encoded by single-quoting each
> +        # character (e.g. abc -> 'a''b''c').
> +        encoded_prompt = "''".join(self.shell_prompt)
> +        self.run("export PS1='{}'".format(encoded_prompt))
>          self.run("dmesg -n 1")
>          # Prevent the shell from wrapping the commands at 80 columns.
>          self.run("stty columns 29999")
> @@ -110,13 +120,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)
>  
> -- 
> 2.41.0
> 
> _______________________________________________
> buildroot mailing list
> buildroot@buildroot.org
> https://lists.buildroot.org/mailman/listinfo/buildroot

-- 
.-----------------.--------------------.------------------.--------------------.
|  Yann E. MORIN  | Real-Time Embedded | /"\ ASCII RIBBON | Erics' conspiracy: |
| +33 662 376 056 | Software  Designer | \ / CAMPAIGN     |  ___               |
| +33 561 099 427 `------------.-------:  X  AGAINST      |  \e/  There is no  |
| http://ymorin.is-a-geek.org/ | _/*\_ | / \ HTML MAIL    |   v   conspiracy.  |
'------------------------------^-------^------------------^--------------------'
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Buildroot] [PATCH v2 1/3] support/testing/infra/emulator.py: change the shell prompt before running tests
  2023-06-26 21:06 ` [Buildroot] [PATCH v2 1/3] support/testing/infra/emulator.py: change the shell prompt before running tests Yann E. MORIN
@ 2023-06-27 19:43   ` Julien Olivain
  2023-06-27 21:21     ` Yann E. MORIN
  0 siblings, 1 reply; 6+ messages in thread
From: Julien Olivain @ 2023-06-27 19:43 UTC (permalink / raw)
  To: Yann E. MORIN; +Cc: Ricardo Martincoski, buildroot

Hi Yann,

On 26/06/2023 23:06, Yann E. MORIN wrote:
> Julien, All,
> 
> On 2023-06-11 12:45 +0200, Julien Olivain spake thusly:
>> 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 ###")
> 
> The most obvious issue is when testing the environment of the shell 
> with
> env, which will output PS1 itself... There is not easy way around to 
> fix
> that, of course... :-/
> 
> I was beaten by this when adding a test for bash. I eventually solved 
> it
> in another way, but took a note to find a better solution "for 
> later"...
> ;-)
> 
>> 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 protected to make sure it will not be detected as
>> an actual shell prompt. The prompt is encoded by single-quoting
>> each character (e.g. abc -> 'a''b''c').
> 
> That's not really what join() will do. E.g. with your code:
> 
>     >>> s = "#BRTEST# "
>     >>> "''".join(s)
>     "#''B''R''T''E''S''T''#'' "

I agree doing this, we miss the first and last quote. This is exactly 
what
the next line is doing:

     self.run("export PS1='{}'".format(encoded_prompt))

Note the extra quotes. If you look in a run-test run log, there will be:

     # export PS1='#''B''R''T''E''S''T''#'' '
     #BRTEST# echo $?
     0

This was my initial intent: setting the PS1 without the local echo
begin caught by pexpect.

If you prefer any other encoding or prompt, please suggest something
else. I initially tried by temporarily disabling the local echo with
"stty -echo" but this was creating other issues...

> But we don;t really care because it is then quite improbable that some
> random program will output exactly this...
> 
> I was on my side thinking about using ANSI escape sequences to carry 
> the
> information that the command was actually fnished, something along the
> lines of:
> 
>     export PS1="\x1B_BR_COMMAND_FINISHED\x1B\\# "
> 
>   - \x1B_ is APC (Application Program Command),
>   - \x1B\\ is ST (String Terminator), which terminates APC
> 
> Instead of APC, we could use any of:
> 
>   - \x1BP DCS, Device Control String
>   - \x1BX SOS, Start Of String
>   - \x1B^ PM, Privacy Message
>   - or even \x1B] OSC, Operating System Command
> 
> But maybe that is a bit overkill in the end...
> 
> Anyway, your patch at least breaks tests.package.test_bash...

I overlooked this bash test, while testing this patch. Thanks for
pointing that out. I tried with a dozen of test cases, but not that
one.

The /etc/profile in Buildroot skeleton is overwriting PS1, see:
https://git.buildroot.org/buildroot/tree/system/skeleton/etc/profile?h=2023.05#n5

I was able to fix this test_bash by passing the PS1 in another exported
variable:

     self.assertRunOk("export BR_PS1=\"$PS1\"")
     self.emulator.qemu.sendline("bash -il")
     self.assertRunOk("export PS1=\"${BR_PS1}\"")

The "bash -il" return code is no longer tested, but I don't think it's
an issue since the rest of the test covers that.

If you agree with this workaround, I'll send an updated v3 patch series.

> 
> Regards,
> Yann E. MORIN.
> 
>> Signed-off-by: Julien Olivain <ju.o@free.fr>
>> ---
>> 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 | 14 ++++++++++++--
>>  1 file changed, 12 insertions(+), 2 deletions(-)
>> 
>> diff --git a/support/testing/infra/emulator.py 
>> b/support/testing/infra/emulator.py
>> index 02cf486128..390c582e9d 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,15 @@ class Emulator(object):
>>          index = self.qemu.expect(["# ", pexpect.TIMEOUT])
>>          if index != 0:
>>              raise SystemError("Cannot login")
>> +        # Set a special shell prompt while 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 encode or escape it in some way to
>> +        # make sure the command echo will not be seen as a prompt
>> +        # itself. The prompt is encoded by single-quoting each
>> +        # character (e.g. abc -> 'a''b''c').
>> +        encoded_prompt = "''".join(self.shell_prompt)
>> +        self.run("export PS1='{}'".format(encoded_prompt))
>>          self.run("dmesg -n 1")
>>          # Prevent the shell from wrapping the commands at 80 columns.
>>          self.run("stty columns 29999")
>> @@ -110,13 +120,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)
>> 
>> --
>> 2.41.0
>> 
>> _______________________________________________
>> buildroot mailing list
>> buildroot@buildroot.org
>> https://lists.buildroot.org/mailman/listinfo/buildroot

Best regards,

Julien.
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [Buildroot] [PATCH v2 1/3] support/testing/infra/emulator.py: change the shell prompt before running tests
  2023-06-27 19:43   ` Julien Olivain
@ 2023-06-27 21:21     ` Yann E. MORIN
  0 siblings, 0 replies; 6+ messages in thread
From: Yann E. MORIN @ 2023-06-27 21:21 UTC (permalink / raw)
  To: Julien Olivain; +Cc: Ricardo Martincoski, buildroot

Julien, All,

On 2023-06-27 21:43 +0200, Julien Olivain spake thusly:
> On 26/06/2023 23:06, Yann E. MORIN wrote:
> >On 2023-06-11 12:45 +0200, Julien Olivain spake thusly:
> >>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 ###")
[--SNIP--]
> >>an actual shell prompt. The prompt is encoded by single-quoting
> >>each character (e.g. abc -> 'a''b''c').
> >That's not really what join() will do. E.g. with your code:
> >    >>> s = "#BRTEST# "
> >    >>> "''".join(s)
> >    "#''B''R''T''E''S''T''#'' "
> I agree doing this, we miss the first and last quote. This is exactly what
> the next line is doing:
>     self.run("export PS1='{}'".format(encoded_prompt))
> Note the extra quotes. If you look in a run-test run log, there will be:
>     # export PS1='#''B''R''T''E''S''T''#'' '
>     #BRTEST# echo $?
>     0

Ah, yeah, so the prompt is not "encoded". Rather, the command setting
the prompt is obfuscated to that it is not itself detected as a prompt
being displayed.

OK, got it.

> This was my initial intent: setting the PS1 without the local echo
> begin caught by pexpect.

Yup.

> If you prefer any other encoding or prompt, please suggest something
> else. I initially tried by temporarily disabling the local echo with
> "stty -echo" but this was creating other issues...

Yeah, I think using a side channel to inform pexpect that a prompt is
actually being displayed, would eventually do the trick. That, combined
with your trick to obfuscate the command setting the prompt:

    # \033_ is APC (Application Program Command),
    # \033\ is ST (String Terminator), which terminates APC
    self.prompt_esc = '\033_BR_COMMAND_FINISHED\033\\'
    # Obfuscate the command setting the prompt, so that it is not
    # itself detected as a prompt being displayed.
    self.run("export PS1='{}# '".format("''".join(self.prompt_esc)))

And then, the emulator would be changed to expect that ANSI escape
sequence; everywhere we expect("# ") or expect(["# ",...]), we switch to
using self.prompt_esc instead or "# "

Totally untested, of course! I have a little doubt about whether the
escape ANSI sequence can be obfuscated that way, though...

[--SNIP--]
> >Anyway, your patch at least breaks tests.package.test_bash...
> The /etc/profile in Buildroot skeleton is overwriting PS1, see:
> https://git.buildroot.org/buildroot/tree/system/skeleton/etc/profile?h=2023.05#n5
> 
> I was able to fix this test_bash by passing the PS1 in another exported
> variable:
> 
>     self.assertRunOk("export BR_PS1=\"$PS1\"")
>     self.emulator.qemu.sendline("bash -il")
>     self.assertRunOk("export PS1=\"${BR_PS1}\"")
> 
> The "bash -il" return code is no longer tested, but I don't think it's
> an issue since the rest of the test covers that.
> 
> If you agree with this workaround, I'll send an updated v3 patch series.

I think we should instead tell bash to not parse the default bashrc et
al.:

    self.assertRunOk("bash -il --init-file /dev/null --rcfile /dev/null")

And this should solve the issue: PS1 is in the environment, so bash will
inherit it.

Regards,
Yann E. MORIN.

> >
> >Regards,
> >Yann E. MORIN.
> >
> >>Signed-off-by: Julien Olivain <ju.o@free.fr>
> >>---
> >>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 | 14 ++++++++++++--
> >> 1 file changed, 12 insertions(+), 2 deletions(-)
> >>
> >>diff --git a/support/testing/infra/emulator.py
> >>b/support/testing/infra/emulator.py
> >>index 02cf486128..390c582e9d 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,15 @@ class Emulator(object):
> >>         index = self.qemu.expect(["# ", pexpect.TIMEOUT])
> >>         if index != 0:
> >>             raise SystemError("Cannot login")
> >>+        # Set a special shell prompt while 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 encode or escape it in some way to
> >>+        # make sure the command echo will not be seen as a prompt
> >>+        # itself. The prompt is encoded by single-quoting each
> >>+        # character (e.g. abc -> 'a''b''c').
> >>+        encoded_prompt = "''".join(self.shell_prompt)
> >>+        self.run("export PS1='{}'".format(encoded_prompt))
> >>         self.run("dmesg -n 1")
> >>         # Prevent the shell from wrapping the commands at 80 columns.
> >>         self.run("stty columns 29999")
> >>@@ -110,13 +120,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)
> >>
> >>--
> >>2.41.0
> >>
> >>_______________________________________________
> >>buildroot mailing list
> >>buildroot@buildroot.org
> >>https://lists.buildroot.org/mailman/listinfo/buildroot
> 
> Best regards,
> 
> Julien.

-- 
.-----------------.--------------------.------------------.--------------------.
|  Yann E. MORIN  | Real-Time Embedded | /"\ ASCII RIBBON | Erics' conspiracy: |
| +33 662 376 056 | Software  Designer | \ / CAMPAIGN     |  ___               |
| +33 561 099 427 `------------.-------:  X  AGAINST      |  \e/  There is no  |
| http://ymorin.is-a-geek.org/ | _/*\_ | / \ HTML MAIL    |   v   conspiracy.  |
'------------------------------^-------^------------------^--------------------'
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2023-06-27 21:21 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-06-11 10:45 [Buildroot] [PATCH v2 1/3] support/testing/infra/emulator.py: change the shell prompt before running tests Julien Olivain
2023-06-26 20:00 ` [Buildroot] [PATCH v2 2/3] support/testing/tests/package/test_dieharder.py: new runtime test Julien Olivain
2023-06-26 20:01 ` [Buildroot] [PATCH v2 3/3] support/testing/tests/package/test_dmidecode.py: " Julien Olivain
2023-06-26 21:06 ` [Buildroot] [PATCH v2 1/3] support/testing/infra/emulator.py: change the shell prompt before running tests Yann E. MORIN
2023-06-27 19:43   ` Julien Olivain
2023-06-27 21:21     ` Yann E. MORIN

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox