[Buildroot] [PATCH v2 1/3] support/testing/infra/emulator.py: change the shell prompt before running tests
Julien Olivain
ju.o at free.fr
Tue Jun 27 19:43:35 UTC 2023
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 at 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 at buildroot.org
>> https://lists.buildroot.org/mailman/listinfo/buildroot
Best regards,
Julien.
More information about the buildroot
mailing list