1import os
2import time
3
4import infra.basetest
5
6
7class TestWeston(infra.basetest.BRTest):
8    config = \
9        """
10        BR2_aarch64=y
11        BR2_TOOLCHAIN_EXTERNAL=y
12        BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y
13        BR2_TARGET_GENERIC_GETTY_PORT="ttyAMA0"
14        BR2_ROOTFS_OVERLAY="{}"
15        BR2_PER_PACKAGE_DIRECTORIES=y
16        BR2_LINUX_KERNEL=y
17        BR2_LINUX_KERNEL_CUSTOM_VERSION=y
18        BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.1.44"
19        BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
20        BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/qemu/aarch64-virt/linux.config"
21        BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="{}"
22        BR2_PACKAGE_LIBDRM=y
23        BR2_PACKAGE_MESA3D=y
24        BR2_PACKAGE_MESA3D_GALLIUM_DRIVER_SWRAST=y
25        BR2_PACKAGE_MESA3D_LLVM=y
26        BR2_PACKAGE_MESA3D_OPENGL_EGL=y
27        BR2_PACKAGE_MESA3D_OPENGL_ES=y
28        BR2_PACKAGE_WAYLAND_UTILS=y
29        BR2_PACKAGE_WESTON=y
30        BR2_PACKAGE_WESTON_SIMPLE_CLIENTS=y
31        BR2_TARGET_ROOTFS_CPIO=y
32        BR2_TARGET_ROOTFS_CPIO_GZIP=y
33        # BR2_TARGET_ROOTFS_TAR is not set
34        """.format(
35                infra.filepath("tests/package/test_weston/overlay"),
36                infra.filepath("tests/package/test_weston/linux-vkms.fragment")
37             )
38
39    def gen_read_disp_crcs_cmd(self, count=1):
40        # DRM CRCs are exposed through a sysfs pseudo file, one measure
41        # per line. The first column is the frame number, the second
42        # column is the CRC measure. We use "head" to get the needed
43        # CRC count.
44        disp_crc_path = "/sys/kernel/debug/dri/0/crtc-0/crc/data"
45        cmd = f"head -{count} {disp_crc_path}"
46
47        # The DRM CRC sysfs pseudo file lines are terminated by '\n'
48        # and '\0'. We remove the '\0' to have a text-only output.
49        cmd += " | tr -d '\\000'"
50
51        # Finally, we drop the frame counter, and keep only the second
52        # column (CRC values)
53        cmd += " | cut -f 2 -d ' '"
54
55        return cmd
56
57    def gen_count_unique_disp_crcs_cmd(self, count=10):
58        # We get the command generating one CRC per line...
59        cmd = self.gen_read_disp_crcs_cmd(count)
60        # ...then count the number of unique values
61        cmd += " | uniq | wc -l"
62        return cmd
63
64    def start_weston(self):
65        self.assertRunOk("export XDG_RUNTIME_DIR=/tmp")
66
67        cmd = "weston"
68        cmd += " --config=/etc/weston.ini"
69        cmd += " --continue-without-input"
70        cmd += " --log=/tmp/weston.log"
71        cmd += " &> /dev/null &"
72        self.assertRunOk(cmd)
73
74        self.assertRunOk("export WAYLAND_DISPLAY=wayland-1")
75
76    def wait_for_weston(self):
77        # We wait for the wayland socket to appear...
78        wayland_socket = "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}"
79        cmd = f"while [ ! -e \"{wayland_socket}\" ] ; do sleep 1 ; done"
80        self.assertRunOk(cmd, timeout=10)
81        time.sleep(4)
82
83    def stop_weston(self):
84        cmd = "killall weston && sleep 3"
85        self.assertRunOk(cmd)
86
87    def test_run(self):
88        img = os.path.join(self.builddir, "images", "rootfs.cpio.gz")
89        kern = os.path.join(self.builddir, "images", "Image")
90        self.emulator.boot(arch="aarch64",
91                           kernel=kern,
92                           kernel_cmdline=["console=ttyAMA0"],
93                           options=["-M", "virt",
94                                    "-cpu", "cortex-a57",
95                                    "-smp", "4",
96                                    "-m", "256M",
97                                    "-initrd", img])
98        self.emulator.login()
99
100        # Check the weston binary can execute
101        self.assertRunOk("weston --version")
102
103        self.start_weston()
104        self.wait_for_weston()
105
106        # Check a simple info client can communicate with the compositor
107        self.assertRunOk("wayland-info", timeout=10)
108
109        # This test will use the Kernel VKMS DRM Display CRC support,
110        # which is exposed in debugfs. See:
111        # https://docs.kernel.org/gpu/drm-uapi.html#display-crc-support
112        self.assertRunOk("mount -t debugfs none /sys/kernel/debug/")
113
114        # We get 10 consecutive DRM frame CRCs and count how many
115        # unique CRCs we have. Since weston is supposed to run idle,
116        # we should have 10 times the same display CRC.
117        cmd = self.gen_count_unique_disp_crcs_cmd()
118        output, exit_code = self.emulator.run(cmd)
119        self.assertEqual(exit_code, 0)
120        self.assertEqual(int(output[0]), 1)
121
122        # We save the CRC value of an empty weston desktop for
123        # later...
124        cmd = self.gen_read_disp_crcs_cmd()
125        output, exit_code = self.emulator.run(cmd)
126        self.assertEqual(exit_code, 0)
127        weston_desktop_crc = int(output[0], 16)
128
129        # We start the weston-simple-egl in background...  Every
130        # rendered frame is supposed to be different (as the triangle
131        # animation is derived from the system time). Since all the
132        # rendering (client application and compositor) is in
133        # software, we sleep a bit to let those program to settle.
134        self.assertRunOk("weston-simple-egl >/dev/null 2>&1 &")
135        time.sleep(8)
136
137        # Since the weston-simple-egl client is supposed to run and
138        # display something, we are now supposed to measure a
139        # different display CRC than the one we measured when the
140        # desktop was empty.
141        cmd = self.gen_read_disp_crcs_cmd()
142        output, exit_code = self.emulator.run(cmd)
143        self.assertEqual(exit_code, 0)
144        self.assertNotEqual(int(output[0], 16), weston_desktop_crc)
145
146        # While weston-simple-egl is running, we check the VKMS DRM
147        # CRCs are now changing. We get many CRCs, one per display
148        # driver refresh (at ~60Hz). Since all the rendering is in
149        # software, we can expect a slow frame rate. In 300 captured
150        # CRCs (5s), we expect at least 5 different values (i.e. 1 fps).
151        # This guarantees the rendering pipeline is working, while we
152        # remain very permissive to slow emulation situations.
153        # Increase timeout, as the command is expected to run about 5s,
154        # which is the default timeout.
155        cmd = self.gen_count_unique_disp_crcs_cmd(300)
156        output, exit_code = self.emulator.run(cmd, timeout=10)
157        self.assertEqual(exit_code, 0)
158        self.assertGreaterEqual(int(output[0]), 5)
159
160        # We stop weston-simple-egl, and sleep a bit to let Weston do
161        # its cleanup and desktop repaint refresh...
162        self.assertRunOk("killall weston-simple-egl")
163        time.sleep(4)
164
165        # After we stopped the application, we should have the initial
166        # weston desktop background. The CRC we measure now should be
167        # the same as the one we saved earlier.
168        cmd = self.gen_read_disp_crcs_cmd()
169        output, exit_code = self.emulator.run(cmd)
170        self.assertEqual(exit_code, 0)
171        self.assertEqual(int(output[0], 16), weston_desktop_crc)
172
173        self.stop_weston()
174
175        # Now weston is supposed to be stopped,
176        # a simple client is expected to fail.
177        _, exit_code = self.emulator.run("wayland-info")
178        self.assertNotEqual(exit_code, 0)
179