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