[U-Boot] [PATCH v3 1/1] test/py: provide example scripts for integrating qemu

Stephen Warren swarren at wwwdotorg.org
Wed Sep 20 16:41:55 UTC 2017


On 09/19/2017 02:04 PM, Heinrich Schuchardt wrote:
> The necessary parameters for running Python tests on qemu are
> tedious to find.
> 
> The patch adds a complete example for running the Python tests for
> qemu-x86_defconfig on an X86 system.

> diff --git a/test/py/README.md b/test/py/README.md

> +#### Running tests for qemu-x86\_defconfig on an x86 system
> +
> +We build u-boot.rom with
> +
> +    export BUILD_ROM=y
> +    make mrproper
> +    make qemu-x86_defconfig
> +    make

Nit: I'd rather write that as:

BUILD_ROM=y make

That way, we don't have to set BUILD_ROM permanently in the current 
shell's environment, so it won't affect anything else that's done later 
in the shell.

To avoid polluting the source tree with build results, why not build 
with O=./build-qemu-x86, and use that as the --build-dir argument to 
test/py?

> +We create directories `$HOME/ubtest/bin` and `$HOME/ubtest/py` for the script
> +files below and `../tftp` for the tftp server.

Why not $HOME/ubtest/tftp? The meaning of "../tftp" can change depending 
on current directory.

> +File `u-boot-test-console` chmod 755
> +
> +    #!/bin/sh
> +    touch /tmp/u-boot-monitor-socket

This creates a regular file not a socket. I believe you can remove this 
command.

Ah, there's a race condition here. By the time u-boot-test-reset is run 
the first time, qemu typically hasn't run far enough to create 
/tmp/u-boot-monitor-socket, and so u-boot-test-reset fails to open it. 
If I fix that by making u-boot-test-reset wait until the control socket 
actually does exist, then test/py fails because it sees two signon 
messages from U-Boot and thinks it crashed; one from when qemu started 
and booted U-Boot, and one because the first invocation of 
u-boot-test-reset resets the system which causes another signon to 
appear. Perhaps this is why Tom Rini's test/py+qemu integration spawns a 
new instance of qemu every time u-boot-test-console is run, and make 
u-boot-test-reset a no-op.

To solve this, I was going to say:

a) Add -S to the qemu command. This causes qemu to perform all 
initialization (e.g. creating/opening the monitor socket) but doesn't 
actually allow the target to run. This prevents the undesired immediate 
boot of the system.

b) Add the following to the top of u-boot-test-reset so that it waits 
for the monitor socket to exist before attempting to use it:

for i in $(seq 50); do
   if [ -f /tmp/u-boot-monitor-socket ]; then
     break
   fi
   sleep 0.1
done

c) Make u-boot-test-reset send U-Boot command "c" to start U-Boot 
executing instead of or in additionk to "system_reset" to cause a reset.

However, none of that is necessary. u-boot-test-console is re-executed 
every time test/py wants to connect to the target, which happens (a) 
when test/py starts the first test, and (b) any time a test fails, and 
test/py wants to clear all state and reconnect to the target (e.g. the 
console command might have failed rather than the target, so the console 
command is restarted too). As such, there's no need to implement 
u-boot-test-reset at all with qemu; just make it a complete no-op. That 
way, you also don't need qemu's -monitor at all.

> +    exec qemu-system-x86_64 -bios u-boot.rom -nographic -netdev \
> +      user,id=eth0,tftp=../tftp,net=192.168.76.0/24,dhcpstart=192.168.76.9 \
> +      -device e1000,netdev=eth0 -machine pc-i440fx-2.8 \

Is machine version 2.8 strictly necessary? The qemu packaged in Ubuntu 
14.04 doesn't support that version, which restricts the usefulness of 
the example. Can version 2.0 work for example? It seems to work fine for 
me. My qemu supports the following i440fx:

qemu-system-x86_64 -machine help | grep i440fx
pc-i440fx-2.0        Standard PC (i440FX + PIIX, 1996)
pc-i440fx-1.4        Standard PC (i440FX + PIIX, 1996)
pc-i440fx-1.5        Standard PC (i440FX + PIIX, 1996)
pc                   Ubuntu 14.04 PC (i440FX + PIIX, 1996) (alias of 
pc-i440fx-trusty)
pc-i440fx-trusty     Ubuntu 14.04 PC (i440FX + PIIX, 1996) (default)
pc-i440fx-1.7        Standard PC (i440FX + PIIX, 1996)
pc-i440fx-1.5-saucy  Standard PC (i440FX + PIIX, 1996) (alias of 
pc-i440fx-1.5-qemu-kvm)
pc-i440fx-1.5-qemu-kvm Standard PC (i440FX + PIIX, 1996)
pc-i440fx-1.6        Standard PC (i440FX + PIIX, 1996)

> +File `u-boot-test-quit` chmod 755
> +
> +    #!/bin/sh
> +    echo quit | socat - UNIX-CONNECT:/tmp/u-boot-monitor-socket
> +
> +This script is called when all tests are completed. The `quit` command is
> +passed to the qemu control console to terminate the qemu session.

test/py doesn't run u-boot-test-quit. Do you have a local change that 
makes it do that? My knee-jerk reaction is that it's OK to enhance 
test/py to run such a script, but let's not document it in the example 
until it does. That said, I'd assume qemu should exit as soon as test/py 
exits since stdin will go away, so this shouldn't actually be needed in 
this case.

> +File `u-boot-test-reset` chmod 755
> +
> +    #!/bin/sh
> +    echo system_reset | socat - UNIX-CONNECT:/tmp/u-boot-monitor-socket
> +    true

As mentioned previously, the true doesn't appear to be required, and 
hides legitimate errors.

> +In the `$HOME/ubtest/py` directory we create file `u_boot_boardenv_qemu_x86.py`

Since the file mode is mentioned for all the other files, let's mention 
it here too.

> +    env__net_dhcp_server = True
> +    env__efi_loader_helloworld_file = {
> +      "fn": "helloworld.efi",
> +      "size": 4298,
> +      "crc32": "55d96ef8",
> +    }
> +
> +The parameter `env__net_dhcp_server` enables the network tests. The parameter
> +`env__efi_loader_helloworld_file` is needed to make the file `helloworld.efi`
> +available which is loaded from the tftp server in `test_efi_loader.py`.
> +
> +The fields size and crc32 have to be updated to match the actual values. The
> +`crc32` command can be used to determine the latter.

Why not have Python determine those values automatically? Here's a 
snippet from my uboot-test-hooks repo that does that. Since people will 
simply be copy-and-pasting the example, it shouldn't matter that it's 
non-trivial:

{
   "fn": file_name,
   "size": os.path.getsize(file_full),
   "crc32": hex(binascii.crc32(open(file_full, 'rb').read()) & \
     0xffffffff)[2:],
}

> +We now can run the Python tests with
> +
> +    export PATH=$HOME/ubtest/bin:/usr/bin:/bin

Let's not hard-code /usr/bin:/bin; some people may rely on binaries 
elsewhere. Also I know that the recently added gpt tests rely on sgdisk 
which is in sbin on my system at least. Also quote to avoid issues with 
spaces in path names. How about:

PATH="$HOME/ubtest/bin:$PATH"

> +    export PYTHONPATH=$HOME/ubtest/py

People might have PYTHONPATH set already (e.g. if using a virtualenv or 
having installed some packages in their home directory). How about:

PYTHONPATH="$HOME/ubtest/py:$PYTHONPATH"

> +    ./test/py/test.py --bd=qemu-x86 --build-dir=.

As above, I'd suggest setting environment variables just for the one 
command rather than permanently changing them:

PATH="$HOME/ubtest/bin:$PATH" \
   PYTHONPATH="$HOME/ubtest/py:$PYTHONPATH" \
   ./test/py/test.py --bd=qemu-x86 --build-dir=./build-qemu-x86


More information about the U-Boot mailing list