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', 'centos-9', '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 += 'INIT_MANAGER = "systemd"\n'
314                self.write_config(features)
315
316                bitbake('core-image-minimal')
317
318                self.assertTrue(os.path.isfile(os.path.join(hosttestdir, "rootfs")),
319                                "rootfs state file was not created")
320
321                with runqemu('core-image-minimal') as qemu:
322                    # Make the test echo a string and search for that as
323                    # run_serial()'s status code is useless.'
324                    for filename in ("rootfs", "delayed-a", "delayed-b"):
325                        status, output = qemu.run_serial("test -f %s && echo found" % os.path.join(targettestdir, filename))
326                        self.assertIn("found", output, "%s was not present on boot" % filename)
327
328
329
330    @skipIfNotQemu()
331    def test_postinst_rootfs_and_boot_sysvinit(self):
332        """
333        Summary:        The purpose of this test case is to verify Post-installation
334                        scripts are called when rootfs is created and also test
335                        that script can be delayed to run at first boot.
336        Dependencies:   NA
337        Steps:          1. Add proper configuration to local.conf file
338                        2. Build a "core-image-minimal" image
339                        3. Verify that file created by postinst_rootfs recipe is
340                           present on rootfs dir.
341                        4. Boot the image created on qemu and verify that the file
342                           created by postinst_boot recipe is present on image.
343        Expected:       The files are successfully created during rootfs and boot
344                        time for 3 different package managers: rpm,ipk,deb and
345                        for initialization managers: sysvinit.
346
347        """
348        self.init_manager_loop("sysvinit")
349
350
351    @skipIfNotQemu()
352    def test_postinst_rootfs_and_boot_systemd(self):
353        """
354        Summary:        The purpose of this test case is to verify Post-installation
355                        scripts are called when rootfs is created and also test
356                        that script can be delayed to run at first boot.
357        Dependencies:   NA
358        Steps:          1. Add proper configuration to local.conf file
359                        2. Build a "core-image-minimal" image
360                        3. Verify that file created by postinst_rootfs recipe is
361                           present on rootfs dir.
362                        4. Boot the image created on qemu and verify that the file
363                           created by postinst_boot recipe is present on image.
364        Expected:       The files are successfully created during rootfs and boot
365                        time for 3 different package managers: rpm,ipk,deb and
366                        for initialization managers: systemd.
367
368        """
369
370        self.init_manager_loop("systemd")
371
372
373    def test_failing_postinst(self):
374        """
375        Summary:        The purpose of this test case is to verify that post-installation
376                        scripts that contain errors are properly reported.
377        Expected:       The scriptlet failure is properly reported.
378                        The file that is created after the error in the scriptlet is not present.
379        Product: oe-core
380        Author: Alexander Kanavin <alex.kanavin@gmail.com>
381        """
382
383        import oe.path
384
385        vars = get_bb_vars(("IMAGE_ROOTFS", "sysconfdir"), "core-image-minimal")
386        rootfs = vars["IMAGE_ROOTFS"]
387        self.assertIsNotNone(rootfs)
388        sysconfdir = vars["sysconfdir"]
389        self.assertIsNotNone(sysconfdir)
390        # Need to use oe.path here as sysconfdir starts with /
391        hosttestdir = oe.path.join(rootfs, sysconfdir, "postinst-test")
392
393        for classes in ("package_rpm", "package_deb", "package_ipk"):
394            with self.subTest(package_class=classes):
395                features = 'CORE_IMAGE_EXTRA_INSTALL = "postinst-rootfs-failing"\n'
396                features += 'PACKAGE_CLASSES = "%s"\n' % classes
397                self.write_config(features)
398                bb_result = bitbake('core-image-minimal', ignore_status=True)
399                self.assertGreaterEqual(bb_result.output.find("Postinstall scriptlets of ['postinst-rootfs-failing'] have failed."), 0,
400                    "Warning about a failed scriptlet not found in bitbake output: %s" %(bb_result.output))
401
402                self.assertTrue(os.path.isfile(os.path.join(hosttestdir, "rootfs-before-failure")),
403                                    "rootfs-before-failure file was not created")
404                self.assertFalse(os.path.isfile(os.path.join(hosttestdir, "rootfs-after-failure")),
405                                    "rootfs-after-failure file was created")
406
407@OETestTag("runqemu")
408class SystemTap(OESelftestTestCase):
409        """
410        Summary:        The purpose of this test case is to verify native crosstap
411                        works while talking to a target.
412        Expected:       The script should successfully connect to the qemu machine
413                        and run some systemtap examples on a qemu machine.
414        """
415
416        @classmethod
417        def setUpClass(cls):
418            super(SystemTap, cls).setUpClass()
419            cls.image = "core-image-minimal"
420
421        def default_config(self):
422            return """
423# These aren't the actual IP addresses but testexport class needs something defined
424TEST_SERVER_IP = "192.168.7.1"
425TEST_TARGET_IP = "192.168.7.2"
426
427EXTRA_IMAGE_FEATURES += "tools-profile dbg-pkgs"
428IMAGE_FEATURES:append = " ssh-server-dropbear"
429
430# enables kernel debug symbols
431KERNEL_EXTRA_FEATURES:append = " features/debug/debug-kernel.scc"
432KERNEL_EXTRA_FEATURES:append = " features/systemtap/systemtap.scc"
433
434# add systemtap run-time into target image if it is not there yet
435IMAGE_INSTALL:append = " systemtap-runtime"
436"""
437
438        def test_crosstap_helloworld(self):
439            self.write_config(self.default_config())
440            bitbake('systemtap-native')
441            systemtap_examples = os.path.join(get_bb_var("WORKDIR","systemtap-native"), "usr/share/systemtap/examples")
442            bitbake(self.image)
443
444            with runqemu(self.image) as qemu:
445                cmd = "crosstap -r root@192.168.7.2 -s %s/general/helloworld.stp " % systemtap_examples
446                result = runCmd(cmd)
447                self.assertEqual(0, result.status, 'crosstap helloworld returned a non 0 status:%s' % result.output)
448
449        def test_crosstap_pstree(self):
450            self.write_config(self.default_config())
451
452            bitbake('systemtap-native')
453            systemtap_examples = os.path.join(get_bb_var("WORKDIR","systemtap-native"), "usr/share/systemtap/examples")
454            bitbake(self.image)
455
456            with runqemu(self.image) as qemu:
457                cmd = "crosstap -r root@192.168.7.2 -s %s/process/pstree.stp" % systemtap_examples
458                result = runCmd(cmd)
459                self.assertEqual(0, result.status, 'crosstap pstree returned a non 0 status:%s' % result.output)
460
461        def test_crosstap_syscalls_by_proc(self):
462            self.write_config(self.default_config())
463
464            bitbake('systemtap-native')
465            systemtap_examples = os.path.join(get_bb_var("WORKDIR","systemtap-native"), "usr/share/systemtap/examples")
466            bitbake(self.image)
467
468            with runqemu(self.image) as qemu:
469                cmd = "crosstap -r root@192.168.7.2 -s %s/process/ syscalls_by_proc.stp" % systemtap_examples
470                result = runCmd(cmd)
471                self.assertEqual(0, result.status, 'crosstap  syscalls_by_proc returned a non 0 status:%s' % result.output)
472
473        def test_crosstap_syscalls_by_pid(self):
474            self.write_config(self.default_config())
475
476            bitbake('systemtap-native')
477            systemtap_examples = os.path.join(get_bb_var("WORKDIR","systemtap-native"), "usr/share/systemtap/examples")
478            bitbake(self.image)
479
480            with runqemu(self.image) as qemu:
481                cmd = "crosstap -r root@192.168.7.2 -s %s/process/ syscalls_by_pid.stp" % systemtap_examples
482                result = runCmd(cmd)
483                self.assertEqual(0, result.status, 'crosstap  syscalls_by_pid returned a non 0 status:%s' % result.output)
484