1#
2# SPDX-License-Identifier: MIT
3#
4
5from oeqa.selftest.case import OESelftestTestCase
6from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqemu
7from oeqa.utils.sshcontrol import SSHControl
8import os
9import re
10import tempfile
11import shutil
12import oe.lsb
13from oeqa.core.decorator.data import skipIfNotQemu
14
15class TestExport(OESelftestTestCase):
16
17    @classmethod
18    def tearDownClass(cls):
19        runCmd("rm -rf /tmp/sdk")
20        super(TestExport, cls).tearDownClass()
21
22    def test_testexport_basic(self):
23        """
24        Summary: Check basic testexport functionality with only ping test enabled.
25        Expected: 1. testexport directory must be created.
26                  2. runexported.py must run without any error/exception.
27                  3. ping test must succeed.
28        Product: oe-core
29        Author: Mariano Lopez <mariano.lopez@intel.com>
30        """
31
32        features = 'INHERIT += "testexport"\n'
33        # These aren't the actual IP addresses but testexport class needs something defined
34        features += 'TEST_SERVER_IP = "192.168.7.1"\n'
35        features += 'TEST_TARGET_IP = "192.168.7.1"\n'
36        features += 'TEST_SUITES = "ping"\n'
37        self.write_config(features)
38
39        # Build tesexport for core-image-minimal
40        bitbake('core-image-minimal')
41        bitbake('-c testexport core-image-minimal')
42
43        testexport_dir = get_bb_var('TEST_EXPORT_DIR', 'core-image-minimal')
44
45        # Verify if TEST_EXPORT_DIR was created
46        isdir = os.path.isdir(testexport_dir)
47        self.assertEqual(True, isdir, 'Failed to create testexport dir: %s' % testexport_dir)
48
49        with runqemu('core-image-minimal') as qemu:
50            # Attempt to run runexported.py to perform ping test
51            test_path = os.path.join(testexport_dir, "oe-test")
52            data_file = os.path.join(testexport_dir, 'data', 'testdata.json')
53            manifest = os.path.join(testexport_dir, 'data', 'manifest')
54            cmd = ("%s runtime --test-data-file %s --packages-manifest %s "
55                   "--target-ip %s --server-ip %s --quiet"
56                  % (test_path, data_file, manifest, qemu.ip, qemu.server_ip))
57            result = runCmd(cmd)
58            # Verify ping test was succesful
59            self.assertEqual(0, result.status, 'oe-test runtime returned a non 0 status')
60
61    def test_testexport_sdk(self):
62        """
63        Summary: Check sdk functionality for testexport.
64        Expected: 1. testexport directory must be created.
65                  2. SDK tarball must exists.
66                  3. Uncompressing of tarball must succeed.
67                  4. Check if the SDK directory is added to PATH.
68                  5. Run tar from the SDK directory.
69        Product: oe-core
70        Author: Mariano Lopez <mariano.lopez@intel.com>
71        """
72
73        features = 'INHERIT += "testexport"\n'
74        # These aren't the actual IP addresses but testexport class needs something defined
75        features += 'TEST_SERVER_IP = "192.168.7.1"\n'
76        features += 'TEST_TARGET_IP = "192.168.7.1"\n'
77        features += 'TEST_SUITES = "ping"\n'
78        features += 'TEST_EXPORT_SDK_ENABLED = "1"\n'
79        features += 'TEST_EXPORT_SDK_PACKAGES = "nativesdk-tar"\n'
80        self.write_config(features)
81
82        # Build tesexport for core-image-minimal
83        bitbake('core-image-minimal')
84        bitbake('-c testexport core-image-minimal')
85
86        needed_vars = ['TEST_EXPORT_DIR', 'TEST_EXPORT_SDK_DIR', 'TEST_EXPORT_SDK_NAME']
87        bb_vars = get_bb_vars(needed_vars, 'core-image-minimal')
88        testexport_dir = bb_vars['TEST_EXPORT_DIR']
89        sdk_dir = bb_vars['TEST_EXPORT_SDK_DIR']
90        sdk_name = bb_vars['TEST_EXPORT_SDK_NAME']
91
92        # Check for SDK
93        tarball_name = "%s.sh" % sdk_name
94        tarball_path = os.path.join(testexport_dir, sdk_dir, tarball_name)
95        msg = "Couldn't find SDK tarball: %s" % tarball_path
96        self.assertEqual(os.path.isfile(tarball_path), True, msg)
97
98        # Extract SDK and run tar from SDK
99        result = runCmd("%s -y -d /tmp/sdk" % tarball_path)
100        self.assertEqual(0, result.status, "Couldn't extract SDK")
101
102        env_script = result.output.split()[-1]
103        result = runCmd(". %s; which tar" % env_script, shell=True)
104        self.assertEqual(0, result.status, "Couldn't setup SDK environment")
105        is_sdk_tar = True if "/tmp/sdk" in result.output else False
106        self.assertTrue(is_sdk_tar, "Couldn't setup SDK environment")
107
108        tar_sdk = result.output
109        result = runCmd("%s --version" % tar_sdk)
110        self.assertEqual(0, result.status, "Couldn't run tar from SDK")
111
112
113class TestImage(OESelftestTestCase):
114
115    def test_testimage_install(self):
116        """
117        Summary: Check install packages functionality for testimage/testexport.
118        Expected: 1. Import tests from a directory other than meta.
119                  2. Check install/uninstall of socat.
120        Product: oe-core
121        Author: Mariano Lopez <mariano.lopez@intel.com>
122        """
123        if get_bb_var('DISTRO') == 'poky-tiny':
124            self.skipTest('core-image-full-cmdline not buildable for poky-tiny')
125
126        features = 'INHERIT += "testimage"\n'
127        features += 'IMAGE_INSTALL_append = " libssl"\n'
128        features += 'TEST_SUITES = "ping ssh selftest"\n'
129        self.write_config(features)
130
131        # Build core-image-sato and testimage
132        bitbake('core-image-full-cmdline socat')
133        bitbake('-c testimage core-image-full-cmdline')
134
135    def test_testimage_dnf(self):
136        """
137        Summary: Check package feeds functionality for dnf
138        Expected: 1. Check that remote package feeds can be accessed
139        Product: oe-core
140        Author: Alexander Kanavin <alex.kanavin@gmail.com>
141        """
142        if get_bb_var('DISTRO') == 'poky-tiny':
143            self.skipTest('core-image-full-cmdline not buildable for poky-tiny')
144
145        features = 'INHERIT += "testimage"\n'
146        features += 'TEST_SUITES = "ping ssh dnf_runtime dnf.DnfBasicTest.test_dnf_help"\n'
147        # We don't yet know what the server ip and port will be - they will be patched
148        # in at the start of the on-image test
149        features += 'PACKAGE_FEED_URIS = "http://bogus_ip:bogus_port"\n'
150        features += 'EXTRA_IMAGE_FEATURES += "package-management"\n'
151        features += 'PACKAGE_CLASSES = "package_rpm"\n'
152
153        bitbake('gnupg-native -c addto_recipe_sysroot')
154
155        # Enable package feed signing
156        self.gpg_home = tempfile.mkdtemp(prefix="oeqa-feed-sign-")
157        self.track_for_cleanup(self.gpg_home)
158        signing_key_dir = os.path.join(self.testlayer_path, 'files', 'signing')
159        runCmd('gpgconf --list-dirs --homedir %s; gpg -v --batch --homedir %s --import %s' % (self.gpg_home, self.gpg_home, os.path.join(signing_key_dir, 'key.secret')), native_sysroot=get_bb_var("RECIPE_SYSROOT_NATIVE", "gnupg-native"), shell=True)
160        features += 'INHERIT += "sign_package_feed"\n'
161        features += 'PACKAGE_FEED_GPG_NAME = "testuser"\n'
162        features += 'PACKAGE_FEED_GPG_PASSPHRASE_FILE = "%s"\n' % os.path.join(signing_key_dir, 'key.passphrase')
163        features += 'GPG_PATH = "%s"\n' % self.gpg_home
164        features += 'PSEUDO_IGNORE_PATHS .= ",%s"\n' % self.gpg_home
165        self.write_config(features)
166
167        # Build core-image-sato and testimage
168        bitbake('core-image-full-cmdline socat')
169        bitbake('-c testimage core-image-full-cmdline')
170
171    def test_testimage_virgl_gtk_sdl(self):
172        """
173        Summary: Check host-assisted accelerate OpenGL functionality in qemu with gtk and SDL frontends
174        Expected: 1. Check that virgl kernel driver is loaded and 3d acceleration is enabled
175                  2. Check that kmscube demo runs without crashing.
176        Product: oe-core
177        Author: Alexander Kanavin <alex.kanavin@gmail.com>
178        """
179        if "DISPLAY" not in os.environ:
180            self.skipTest("virgl gtk test must be run inside a X session")
181        distro = oe.lsb.distro_identifier()
182        if distro and distro == 'debian-8':
183            self.skipTest('virgl isn\'t working with Debian 8')
184        if distro and distro == 'debian-9':
185            self.skipTest('virgl isn\'t working with Debian 9')
186        if distro and distro == 'centos-7':
187            self.skipTest('virgl isn\'t working with Centos 7')
188        if distro and distro == 'opensuseleap-15.0':
189            self.skipTest('virgl isn\'t working with Opensuse 15.0')
190
191        qemu_packageconfig = get_bb_var('PACKAGECONFIG', 'qemu-system-native')
192        qemu_distrofeatures = get_bb_var('DISTRO_FEATURES', 'qemu-system-native')
193        features = 'INHERIT += "testimage"\n'
194        if 'gtk+' not in qemu_packageconfig:
195            features += 'PACKAGECONFIG_append_pn-qemu-system-native = " gtk+"\n'
196        if 'sdl' not in qemu_packageconfig:
197            features += 'PACKAGECONFIG_append_pn-qemu-system-native = " sdl"\n'
198        if 'opengl' not in qemu_distrofeatures:
199            features += 'DISTRO_FEATURES_append = " opengl"\n'
200        features += 'TEST_SUITES = "ping ssh virgl"\n'
201        features += 'IMAGE_FEATURES_append = " ssh-server-dropbear"\n'
202        features += 'IMAGE_INSTALL_append = " kmscube"\n'
203        features_gtk = features + 'TEST_RUNQEMUPARAMS = "gtk gl"\n'
204        self.write_config(features_gtk)
205        bitbake('core-image-minimal')
206        bitbake('-c testimage core-image-minimal')
207        features_sdl = features + 'TEST_RUNQEMUPARAMS = "sdl gl"\n'
208        self.write_config(features_sdl)
209        bitbake('core-image-minimal')
210        bitbake('-c testimage core-image-minimal')
211
212    def test_testimage_virgl_headless(self):
213        """
214        Summary: Check host-assisted accelerate OpenGL functionality in qemu with egl-headless frontend
215        Expected: 1. Check that virgl kernel driver is loaded and 3d acceleration is enabled
216                  2. Check that kmscube demo runs without crashing.
217        Product: oe-core
218        Author: Alexander Kanavin <alex.kanavin@gmail.com>
219        """
220        import subprocess, os
221        try:
222            content = os.listdir("/dev/dri")
223            if len([i for i in content if i.startswith('render')]) == 0:
224                self.skipTest("No render nodes found in /dev/dri: %s" %(content))
225        except FileNotFoundError:
226            self.skipTest("/dev/dri directory does not exist; no render nodes available on this machine.")
227        try:
228            dripath = subprocess.check_output("pkg-config --variable=dridriverdir dri", shell=True)
229        except subprocess.CalledProcessError as e:
230            self.skipTest("Could not determine the path to dri drivers on the host via pkg-config.\nPlease install Mesa development files (particularly, dri.pc) on the host machine.")
231        qemu_distrofeatures = get_bb_var('DISTRO_FEATURES', 'qemu-system-native')
232        features = 'INHERIT += "testimage"\n'
233        if 'opengl' not in qemu_distrofeatures:
234            features += 'DISTRO_FEATURES_append = " opengl"\n'
235        features += 'TEST_SUITES = "ping ssh virgl"\n'
236        features += 'IMAGE_FEATURES_append = " ssh-server-dropbear"\n'
237        features += 'IMAGE_INSTALL_append = " kmscube"\n'
238        features += 'TEST_RUNQEMUPARAMS = "egl-headless"\n'
239        self.write_config(features)
240        bitbake('core-image-minimal')
241        bitbake('-c testimage core-image-minimal')
242
243class Postinst(OESelftestTestCase):
244
245    def init_manager_loop(self, init_manager):
246        import oe.path
247
248        vars = get_bb_vars(("IMAGE_ROOTFS", "sysconfdir"), "core-image-minimal")
249        rootfs = vars["IMAGE_ROOTFS"]
250        self.assertIsNotNone(rootfs)
251        sysconfdir = vars["sysconfdir"]
252        self.assertIsNotNone(sysconfdir)
253        # Need to use oe.path here as sysconfdir starts with /
254        hosttestdir = oe.path.join(rootfs, sysconfdir, "postinst-test")
255        targettestdir = os.path.join(sysconfdir, "postinst-test")
256
257        for classes in ("package_rpm", "package_deb", "package_ipk"):
258            with self.subTest(init_manager=init_manager, package_class=classes):
259                features = 'CORE_IMAGE_EXTRA_INSTALL = "postinst-delayed-b"\n'
260                features += 'IMAGE_FEATURES += "package-management empty-root-password"\n'
261                features += 'PACKAGE_CLASSES = "%s"\n' % classes
262                if init_manager == "systemd":
263                    features += 'DISTRO_FEATURES_append = " systemd"\n'
264                    features += 'VIRTUAL-RUNTIME_init_manager = "systemd"\n'
265                    features += 'DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"\n'
266                    features += 'VIRTUAL-RUNTIME_initscripts = ""\n'
267                self.write_config(features)
268
269                bitbake('core-image-minimal')
270
271                self.assertTrue(os.path.isfile(os.path.join(hosttestdir, "rootfs")),
272                                "rootfs state file was not created")
273
274                with runqemu('core-image-minimal') as qemu:
275                    # Make the test echo a string and search for that as
276                    # run_serial()'s status code is useless.'
277                    for filename in ("rootfs", "delayed-a", "delayed-b"):
278                        status, output = qemu.run_serial("test -f %s && echo found" % os.path.join(targettestdir, filename))
279                        self.assertIn("found", output, "%s was not present on boot" % filename)
280
281
282
283    @skipIfNotQemu('qemuall', 'Test only runs in qemu')
284    def test_postinst_rootfs_and_boot_sysvinit(self):
285        """
286        Summary:        The purpose of this test case is to verify Post-installation
287                        scripts are called when rootfs is created and also test
288                        that script can be delayed to run at first boot.
289        Dependencies:   NA
290        Steps:          1. Add proper configuration to local.conf file
291                        2. Build a "core-image-minimal" image
292                        3. Verify that file created by postinst_rootfs recipe is
293                           present on rootfs dir.
294                        4. Boot the image created on qemu and verify that the file
295                           created by postinst_boot recipe is present on image.
296        Expected:       The files are successfully created during rootfs and boot
297                        time for 3 different package managers: rpm,ipk,deb and
298                        for initialization managers: sysvinit.
299
300        """
301        self.init_manager_loop("sysvinit")
302
303
304    @skipIfNotQemu('qemuall', 'Test only runs in qemu')
305    def test_postinst_rootfs_and_boot_systemd(self):
306        """
307        Summary:        The purpose of this test case is to verify Post-installation
308                        scripts are called when rootfs is created and also test
309                        that script can be delayed to run at first boot.
310        Dependencies:   NA
311        Steps:          1. Add proper configuration to local.conf file
312                        2. Build a "core-image-minimal" image
313                        3. Verify that file created by postinst_rootfs recipe is
314                           present on rootfs dir.
315                        4. Boot the image created on qemu and verify that the file
316                           created by postinst_boot recipe is present on image.
317        Expected:       The files are successfully created during rootfs and boot
318                        time for 3 different package managers: rpm,ipk,deb and
319                        for initialization managers: systemd.
320
321        """
322
323        self.init_manager_loop("systemd")
324
325
326    def test_failing_postinst(self):
327        """
328        Summary:        The purpose of this test case is to verify that post-installation
329                        scripts that contain errors are properly reported.
330        Expected:       The scriptlet failure is properly reported.
331                        The file that is created after the error in the scriptlet is not present.
332        Product: oe-core
333        Author: Alexander Kanavin <alex.kanavin@gmail.com>
334        """
335
336        import oe.path
337
338        vars = get_bb_vars(("IMAGE_ROOTFS", "sysconfdir"), "core-image-minimal")
339        rootfs = vars["IMAGE_ROOTFS"]
340        self.assertIsNotNone(rootfs)
341        sysconfdir = vars["sysconfdir"]
342        self.assertIsNotNone(sysconfdir)
343        # Need to use oe.path here as sysconfdir starts with /
344        hosttestdir = oe.path.join(rootfs, sysconfdir, "postinst-test")
345
346        for classes in ("package_rpm", "package_deb", "package_ipk"):
347            with self.subTest(package_class=classes):
348                features = 'CORE_IMAGE_EXTRA_INSTALL = "postinst-rootfs-failing"\n'
349                features += 'PACKAGE_CLASSES = "%s"\n' % classes
350                self.write_config(features)
351                bb_result = bitbake('core-image-minimal', ignore_status=True)
352                self.assertGreaterEqual(bb_result.output.find("Postinstall scriptlets of ['postinst-rootfs-failing'] have failed."), 0,
353                    "Warning about a failed scriptlet not found in bitbake output: %s" %(bb_result.output))
354
355                self.assertTrue(os.path.isfile(os.path.join(hosttestdir, "rootfs-before-failure")),
356                                    "rootfs-before-failure file was not created")
357                self.assertFalse(os.path.isfile(os.path.join(hosttestdir, "rootfs-after-failure")),
358                                    "rootfs-after-failure file was created")
359
360class SystemTap(OESelftestTestCase):
361        """
362        Summary:        The purpose of this test case is to verify native crosstap
363                        works while talking to a target.
364        Expected:       The script should successfully connect to the qemu machine
365                        and run some systemtap examples on a qemu machine.
366        """
367
368        @classmethod
369        def setUpClass(cls):
370            super(SystemTap, cls).setUpClass()
371            cls.image = "core-image-minimal"
372
373        def default_config(self):
374            return """
375# These aren't the actual IP addresses but testexport class needs something defined
376TEST_SERVER_IP = "192.168.7.1"
377TEST_TARGET_IP = "192.168.7.2"
378
379EXTRA_IMAGE_FEATURES += "tools-profile dbg-pkgs"
380IMAGE_FEATURES_append = " ssh-server-dropbear"
381
382# enables kernel debug symbols
383KERNEL_EXTRA_FEATURES_append = " features/debug/debug-kernel.scc"
384KERNEL_EXTRA_FEATURES_append = " features/systemtap/systemtap.scc"
385
386# add systemtap run-time into target image if it is not there yet
387IMAGE_INSTALL_append = " systemtap-runtime"
388"""
389
390        def test_crosstap_helloworld(self):
391            self.write_config(self.default_config())
392            bitbake('systemtap-native')
393            systemtap_examples = os.path.join(get_bb_var("WORKDIR","systemtap-native"), "usr/share/systemtap/examples")
394            bitbake(self.image)
395
396            with runqemu(self.image) as qemu:
397                cmd = "crosstap -r root@192.168.7.2 -s %s/general/helloworld.stp " % systemtap_examples
398                result = runCmd(cmd)
399                self.assertEqual(0, result.status, 'crosstap helloworld returned a non 0 status:%s' % result.output)
400
401        def test_crosstap_pstree(self):
402            self.write_config(self.default_config())
403
404            bitbake('systemtap-native')
405            systemtap_examples = os.path.join(get_bb_var("WORKDIR","systemtap-native"), "usr/share/systemtap/examples")
406            bitbake(self.image)
407
408            with runqemu(self.image) as qemu:
409                cmd = "crosstap -r root@192.168.7.2 -s %s/process/pstree.stp" % systemtap_examples
410                result = runCmd(cmd)
411                self.assertEqual(0, result.status, 'crosstap pstree returned a non 0 status:%s' % result.output)
412
413        def test_crosstap_syscalls_by_proc(self):
414            self.write_config(self.default_config())
415
416            bitbake('systemtap-native')
417            systemtap_examples = os.path.join(get_bb_var("WORKDIR","systemtap-native"), "usr/share/systemtap/examples")
418            bitbake(self.image)
419
420            with runqemu(self.image) as qemu:
421                cmd = "crosstap -r root@192.168.7.2 -s %s/process/ syscalls_by_proc.stp" % systemtap_examples
422                result = runCmd(cmd)
423                self.assertEqual(0, result.status, 'crosstap  syscalls_by_proc returned a non 0 status:%s' % result.output)
424
425        def test_crosstap_syscalls_by_pid(self):
426            self.write_config(self.default_config())
427
428            bitbake('systemtap-native')
429            systemtap_examples = os.path.join(get_bb_var("WORKDIR","systemtap-native"), "usr/share/systemtap/examples")
430            bitbake(self.image)
431
432            with runqemu(self.image) as qemu:
433                cmd = "crosstap -r root@192.168.7.2 -s %s/process/ syscalls_by_pid.stp" % systemtap_examples
434                result = runCmd(cmd)
435                self.assertEqual(0, result.status, 'crosstap  syscalls_by_pid returned a non 0 status:%s' % result.output)
436
437