diff mbox series

[RFC,1/1] support/testing/tests/package/test_criu.py: new runtime test

Message ID 20230915221444.1686011-1-ju.o@free.fr
State Changes Requested
Headers show
Series [RFC,1/1] support/testing/tests/package/test_criu.py: new runtime test | expand

Commit Message

Julien Olivain Sept. 15, 2023, 10:14 p.m. UTC
Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Cc: Marcus Folkesson <marcus.folkesson@gmail.com>
Signed-off-by: Julien Olivain <ju.o@free.fr>
---
This test was suggested by Thomas in [1] during the criu package
proposal in [2]. criu is not accepted yet. This is a first version
of the test for review. Minor rework is expected to align the the
final criu package patch.

Tested on branch master at commit c89d7a2, on top of criu patch [2],
with command:

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

[1] https://lists.buildroot.org/pipermail/buildroot/2023-September/674567.html
[2] https://patchwork.ozlabs.org/project/buildroot/patch/20230910193725.1435827-1-marcus.folkesson@gmail.com/
---
 DEVELOPERS                                    |   2 +
 support/testing/tests/package/test_criu.py    | 127 ++++++++++++++++++
 .../package/test_criu/linux-criu.fragment     |   1 +
 .../rootfs-overlay/root/criu-loop.sh          |  15 +++
 4 files changed, 145 insertions(+)
 create mode 100644 support/testing/tests/package/test_criu.py
 create mode 100644 support/testing/tests/package/test_criu/linux-criu.fragment
 create mode 100755 support/testing/tests/package/test_criu/rootfs-overlay/root/criu-loop.sh

Comments

Yann E. MORIN Nov. 1, 2023, 9:24 p.m. UTC | #1
Julien, All,

On 2023-09-16 00:14 +0200, Julien Olivain spake thusly:
> Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
> Cc: Marcus Folkesson <marcus.folkesson@gmail.com>
> Signed-off-by: Julien Olivain <ju.o@free.fr>
> ---
> This test was suggested by Thomas in [1] during the criu package
> proposal in [2]. criu is not accepted yet. This is a first version
> of the test for review. Minor rework is expected to align the the
> final criu package patch.

Thanks, these runtime tests you submit are very important!

[--SNIP--]
> diff --git a/support/testing/tests/package/test_criu.py b/support/testing/tests/package/test_criu.py
> new file mode 100644
> index 0000000000..af2def85e1
> --- /dev/null
> +++ b/support/testing/tests/package/test_criu.py
> @@ -0,0 +1,127 @@
[--SNIP--]
> +class TestCriu(infra.basetest.BRTest):
> +    # Criu needs to recompile a Kernel, to enable
> +    # CONFIG_CHECKPOINT_RESTORE=y
> +    config = \
> +        """
> +        BR2_aarch64=y
> +        BR2_TOOLCHAIN_EXTERNAL=y
> +        BR2_TARGET_GENERIC_GETTY_PORT="ttyAMA0"
> +        BR2_LINUX_KERNEL=y
> +        BR2_LINUX_KERNEL_CUSTOM_VERSION=y
> +        BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.1.53"
> +        BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
> +        BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/qemu/aarch64-virt/linux.config"
> +        BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="{}"
> +        BR2_LINUX_KERNEL_NEEDS_HOST_OPENSSL=y
> +        BR2_PACKAGE_CRIU=y
> +        BR2_PACKAGE_HOST_PYTHON3=y

No longer needed, criu selects it now.

[--SNIP--]
> +    def test_run(self):
[--SNIP--]
> diff --git a/support/testing/tests/package/test_criu/rootfs-overlay/root/criu-loop.sh b/support/testing/tests/package/test_criu/rootfs-overlay/root/criu-loop.sh
> new file mode 100755
> index 0000000000..6a1f72b442
> --- /dev/null
> +++ b/support/testing/tests/package/test_criu/rootfs-overlay/root/criu-loop.sh
> @@ -0,0 +1,15 @@
> +#! /bin/sh
> +set -eu
> +echo "Start"
> +for LOOP in $(seq 6) ; do
> +    echo "Loop $LOOP"
> +    if [ "$LOOP" -eq 3 ] ; then
> +        touch /tmp/wait-flag
> +    fi
> +    while [ -e /tmp/wait-flag ] ; do
> +        sleep 1
> +        echo "Waiting..."
> +    done
> +    sleep 1
> +done
> +echo "Done"

This loop looks dubious. I think we can do simpler:

Here's shat the shell script would look like:

    #! /bin/sh
    set -eu

    echo "Start"
    touch /tmp/wait-flag

    while [ -e /tmp/wait-flag ] ; do
        sleep 1
        echo "Waiting..."
    done

    touch /tmp/stop-flag
    echo "Done"


And the python scriopt would basivally do:

    spawn script
    wait for /tmp/wait-flag
    suspend process
    check it no longer exists
    resume process
    check it exists again
    remove the wait-flag
    wait "a bit"
    check the stop-flag exists

By using the stop flag, you know the script is still alive: it went past
the wait-falg and informed us that it is still runing by touching
another file.

Regards,
Yann E. MORIN.
diff mbox series

Patch

diff --git a/DEVELOPERS b/DEVELOPERS
index d6e6a98202..7220e66af3 100644
--- a/DEVELOPERS
+++ b/DEVELOPERS
@@ -1723,6 +1723,8 @@  F:	support/testing/tests/package/test_acpica/
 F:	support/testing/tests/package/test_brotli.py
 F:	support/testing/tests/package/test_bzip2.py
 F:	support/testing/tests/package/test_compressor_base.py
+F:	support/testing/tests/package/test_criu.py
+F:	support/testing/tests/package/test_criu/
 F:	support/testing/tests/package/test_ddrescue.py
 F:	support/testing/tests/package/test_ddrescue/
 F:	support/testing/tests/package/test_dos2unix.py
diff --git a/support/testing/tests/package/test_criu.py b/support/testing/tests/package/test_criu.py
new file mode 100644
index 0000000000..af2def85e1
--- /dev/null
+++ b/support/testing/tests/package/test_criu.py
@@ -0,0 +1,127 @@ 
+import os
+import time
+
+import infra.basetest
+
+
+class TestCriu(infra.basetest.BRTest):
+    # Criu needs to recompile a Kernel, to enable
+    # CONFIG_CHECKPOINT_RESTORE=y
+    config = \
+        """
+        BR2_aarch64=y
+        BR2_TOOLCHAIN_EXTERNAL=y
+        BR2_TARGET_GENERIC_GETTY_PORT="ttyAMA0"
+        BR2_LINUX_KERNEL=y
+        BR2_LINUX_KERNEL_CUSTOM_VERSION=y
+        BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.1.53"
+        BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
+        BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/qemu/aarch64-virt/linux.config"
+        BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="{}"
+        BR2_LINUX_KERNEL_NEEDS_HOST_OPENSSL=y
+        BR2_PACKAGE_CRIU=y
+        BR2_PACKAGE_HOST_PYTHON3=y
+        BR2_ROOTFS_OVERLAY="{}"
+        BR2_TARGET_ROOTFS_CPIO=y
+        BR2_TARGET_ROOTFS_CPIO_GZIP=y
+        # BR2_TARGET_ROOTFS_TAR is not set
+        """.format(
+            infra.filepath("tests/package/test_criu/linux-criu.fragment"),
+            infra.filepath("tests/package/test_criu/rootfs-overlay")
+        )
+    # Define constants for the test
+    test_script_name = "criu-loop.sh"
+    test_script_path = f"/root/{test_script_name}"
+    test_flag_file = "/tmp/wait-flag"
+    test_log = "/tmp/criu-loop.log"
+    pidof_cmd = f"pidof {test_script_name}"
+    checkpoint_dir = "/root/checkpoint"
+    tail1_log_cmd = f"tail -1 {test_log}"
+    expected_log_end = "Done"
+
+    def assert_script_is_running(self):
+        # We check our script is running if the command "pidof"
+        # succeed to find a valid PID.
+        self.assertRunOk(self.pidof_cmd)
+
+    def assert_script_is_not_running(self):
+        # Conversely, the "pidof" command will fail if it cannot find
+        # a valid PID for our script.
+        _, exit_code = self.emulator.run(self.pidof_cmd)
+        self.assertNotEqual(exit_code, 0)
+
+    def test_run(self):
+        img = os.path.join(self.builddir, "images", "rootfs.cpio.gz")
+        kern = os.path.join(self.builddir, "images", "Image")
+        self.emulator.boot(arch="aarch64",
+                           kernel=kern,
+                           kernel_cmdline=["console=ttyAMA0"],
+                           options=["-M", "virt",
+                                    "-cpu", "cortex-a57",
+                                    "-m", "256M",
+                                    "-initrd", img])
+        self.emulator.login()
+
+        # We check the program can execute.
+        self.assertRunOk("criu --version")
+
+        # We run the "criu check" command, which validates the kernel
+        # supports the needed features. We also add verbose logs for
+        # easier debugging.
+        self.assertRunOk("criu check -v")
+
+        # We start our test script, in a new session. Note: to make
+        # sure this test will be reliable in different execution speed
+        # conditions, the script implement a simple synchronization
+        # mechanism by creating a flag file and wait for it to be
+        # removed by this test controller.
+        cmd = f"setsid {self.test_script_path}"
+        cmd += f" < /dev/null &> {self.test_log} &"
+        self.assertRunOk(cmd)
+
+        # Let the script make few loops, then create and wait on its
+        # flag file.
+        time.sleep(3)
+
+        # Our script is supposed to be running.
+        self.assert_script_is_running()
+
+        # We dump the process tree with criu.
+        self.assertRunOk(f"mkdir -p {self.checkpoint_dir}")
+        cmd = f"criu dump -D {self.checkpoint_dir} -t $({self.pidof_cmd})"
+        self.assertRunOk(cmd)
+
+        # We check some criu image files were created.
+        self.assertRunOk(f"ls -1 --color=never {self.checkpoint_dir}/*.img")
+
+        # Our script is no longer supposed to be running (it was
+        # supposed to be stopped bu criu during the dump).
+        self.assert_script_is_not_running()
+
+        # We expect the last line of the log to NOT be "Done" yet.
+        output, exit_code = self.emulator.run(self.tail1_log_cmd)
+        self.assertEqual(exit_code, 0)
+        self.assertNotEqual(output[0], self.expected_log_end)
+
+        # We restore the process image.
+        self.assertRunOk(f"criu restore -d -D {self.checkpoint_dir}")
+
+        # Our script is supposed to be running again.
+        self.assert_script_is_running()
+
+        # We remove our test flag, to let our script to end its loop
+        # (this flag was created by the test script, while we were
+        # waiting earlier).
+        self.assertRunOk(f"rm {self.test_flag_file}")
+
+        # We wait for the script to finish its loop.
+        time.sleep(5)
+
+        # Our script is supposed to have reached its end, and
+        # exited. The script is no longer supposed to be running.
+        self.assert_script_is_not_running()
+
+        # We expect the last line of the log to be the end message.
+        output, exit_code = self.emulator.run(self.tail1_log_cmd)
+        self.assertEqual(exit_code, 0)
+        self.assertEqual(output[0], self.expected_log_end)
diff --git a/support/testing/tests/package/test_criu/linux-criu.fragment b/support/testing/tests/package/test_criu/linux-criu.fragment
new file mode 100644
index 0000000000..c554a09cec
--- /dev/null
+++ b/support/testing/tests/package/test_criu/linux-criu.fragment
@@ -0,0 +1 @@ 
+CONFIG_CHECKPOINT_RESTORE=y
diff --git a/support/testing/tests/package/test_criu/rootfs-overlay/root/criu-loop.sh b/support/testing/tests/package/test_criu/rootfs-overlay/root/criu-loop.sh
new file mode 100755
index 0000000000..6a1f72b442
--- /dev/null
+++ b/support/testing/tests/package/test_criu/rootfs-overlay/root/criu-loop.sh
@@ -0,0 +1,15 @@ 
+#! /bin/sh
+set -eu
+echo "Start"
+for LOOP in $(seq 6) ; do
+    echo "Loop $LOOP"
+    if [ "$LOOP" -eq 3 ] ; then
+        touch /tmp/wait-flag
+    fi
+    while [ -e /tmp/wait-flag ] ; do
+        sleep 1
+        echo "Waiting..."
+    done
+    sleep 1
+done
+echo "Done"