diff options
Diffstat (limited to 'meta/lib/oeqa/sdk')
25 files changed, 786 insertions, 151 deletions
diff --git a/meta/lib/oeqa/sdk/buildtools-cases/README b/meta/lib/oeqa/sdk/buildtools-cases/README new file mode 100644 index 0000000000..d4f20faa9f --- /dev/null +++ b/meta/lib/oeqa/sdk/buildtools-cases/README @@ -0,0 +1,2 @@ +These test cases are used by buildtools-tarball, and are not used by the testsdk +class. diff --git a/meta/lib/oeqa/sdk/buildtools-cases/build.py b/meta/lib/oeqa/sdk/buildtools-cases/build.py new file mode 100644 index 0000000000..c85c32496b --- /dev/null +++ b/meta/lib/oeqa/sdk/buildtools-cases/build.py @@ -0,0 +1,32 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +import os, tempfile +import time +from oeqa.sdk.case import OESDKTestCase +from oeqa.utils.subprocesstweak import errors_have_output +errors_have_output() + +class BuildTests(OESDKTestCase): + """ + Verify that bitbake can build virtual/libc inside the buildtools. + """ + def test_libc(self): + with tempfile.TemporaryDirectory(prefix='bitbake-build-', dir=self.tc.sdk_dir) as testdir: + corebase = self.td['COREBASE'] + + self._run('. %s/oe-init-build-env %s' % (corebase, testdir)) + with open(os.path.join(testdir, 'conf', 'local.conf'), 'ta') as conf: + conf.write('\n') + conf.write('DL_DIR = "%s"\n' % self.td['DL_DIR']) + + try: + self._run('. %s/oe-init-build-env %s && bitbake virtual/libc' % (corebase, testdir)) + finally: + delay = 10 + while delay and (os.path.exists(testdir + "/bitbake.lock") or os.path.exists(testdir + "/cache/hashserv.db-wal")): + time.sleep(1) + delay = delay - 1 diff --git a/meta/lib/oeqa/sdk/buildtools-cases/gcc.py b/meta/lib/oeqa/sdk/buildtools-cases/gcc.py new file mode 100644 index 0000000000..a62c4d0bc4 --- /dev/null +++ b/meta/lib/oeqa/sdk/buildtools-cases/gcc.py @@ -0,0 +1,31 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +import os.path +from oeqa.sdk.case import OESDKTestCase + +class GccTests(OESDKTestCase): + def test_verify_specs(self): + """ + Verify that the compiler has been relocated successfully and isn't + looking in the hard-coded prefix. + """ + # Canonicalise the SDK root + sdk_base = os.path.realpath(self.tc.sdk_dir) + # Canonicalise the location of GCC + gcc_path = os.path.realpath(self._run("command -v gcc").strip()) + # Skip the test if the GCC didn't come from the buildtools, as it only + # comes with buildtools-extended-tarball. + if os.path.commonprefix((sdk_base, gcc_path)) != sdk_base: + self.skipTest("Buildtools does not provide GCC") + + # This is the prefix that GCC is build with, and should be replaced at + # installation time. + sdkpath = self.td.get("SDKPATH") + self.assertTrue(sdkpath) + + for line in self._run('gcc -dumpspecs').splitlines(): + self.assertNotIn(sdkpath, line) diff --git a/meta/lib/oeqa/sdk/buildtools-cases/https.py b/meta/lib/oeqa/sdk/buildtools-cases/https.py new file mode 100644 index 0000000000..4525e3d758 --- /dev/null +++ b/meta/lib/oeqa/sdk/buildtools-cases/https.py @@ -0,0 +1,22 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +from oeqa.sdk.case import OESDKTestCase +from oeqa.utils.subprocesstweak import errors_have_output +errors_have_output() + +class HTTPTests(OESDKTestCase): + """ + Verify that HTTPS certificates are working correctly, as this depends on + environment variables being set correctly. + """ + + def test_wget(self): + self._run('env -i wget --debug --output-document /dev/null https://yoctoproject.org/connectivity.html') + + def test_python(self): + # urlopen() returns a file-like object on success and throws an exception otherwise + self._run('python3 -c \'import urllib.request; urllib.request.urlopen("https://yoctoproject.org/connectivity.html")\'') diff --git a/meta/lib/oeqa/sdk/buildtools-cases/sanity.py b/meta/lib/oeqa/sdk/buildtools-cases/sanity.py new file mode 100644 index 0000000000..a55d456656 --- /dev/null +++ b/meta/lib/oeqa/sdk/buildtools-cases/sanity.py @@ -0,0 +1,24 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +import shutil +import os.path +from oeqa.sdk.case import OESDKTestCase + +class SanityTests(OESDKTestCase): + def test_tools(self): + """ + Test that wget and tar come from the buildtools, not the host. This + verifies that the buildtools have installed correctly. We can't check + for gcc as that is only installed by buildtools-extended. + """ + for command in ("tar", "wget"): + # Canonicalise the SDK root + sdk_base = os.path.realpath(self.tc.sdk_dir) + # Canonicalise the location of this command + tool_path = os.path.realpath(self._run("command -v %s" % command).strip()) + # Assert that the tool was found inside the SDK root + self.assertEqual(os.path.commonprefix((sdk_base, tool_path)), sdk_base) diff --git a/meta/lib/oeqa/sdk/buildtools-docs-cases/README b/meta/lib/oeqa/sdk/buildtools-docs-cases/README new file mode 100644 index 0000000000..f8edbc7dad --- /dev/null +++ b/meta/lib/oeqa/sdk/buildtools-docs-cases/README @@ -0,0 +1,2 @@ +These test cases are used by build-docs-tarball, and are not used by the testsdk +class. diff --git a/meta/lib/oeqa/sdk/buildtools-docs-cases/build.py b/meta/lib/oeqa/sdk/buildtools-docs-cases/build.py new file mode 100644 index 0000000000..6e3ee94292 --- /dev/null +++ b/meta/lib/oeqa/sdk/buildtools-docs-cases/build.py @@ -0,0 +1,19 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +import tempfile +from oeqa.sdk.case import OESDKTestCase +from oeqa.utils.subprocesstweak import errors_have_output +errors_have_output() + +class BuildTests(OESDKTestCase): + """ + Verify that our docs can build using our docs tools tarball. + """ + def test_docs_build(self): + with tempfile.TemporaryDirectory(prefix='docs-tarball-build-', dir=self.tc.sdk_dir) as testdir: + self._run('git clone git://git.yoctoproject.org/yocto-docs %s' % testdir) + self._run('cd %s/documentation && make html' % testdir) diff --git a/meta/lib/oeqa/sdk/case.py b/meta/lib/oeqa/sdk/case.py index 963aa8d358..c45882689c 100644 --- a/meta/lib/oeqa/sdk/case.py +++ b/meta/lib/oeqa/sdk/case.py @@ -1,6 +1,10 @@ +# # Copyright (C) 2016 Intel Corporation -# Released under the MIT license (see COPYING.MIT) +# +# SPDX-License-Identifier: MIT +# +import os import subprocess from oeqa.core.case import OETestCase @@ -8,5 +12,43 @@ from oeqa.core.case import OETestCase class OESDKTestCase(OETestCase): def _run(self, cmd): return subprocess.check_output(". %s > /dev/null; %s;" % \ - (self.tc.sdk_env, cmd), shell=True, + (self.tc.sdk_env, cmd), shell=True, executable="/bin/bash", stderr=subprocess.STDOUT, universal_newlines=True) + + def fetch(self, workdir, dl_dir, url, archive=None): + if not archive: + from urllib.parse import urlparse + archive = os.path.basename(urlparse(url).path) + + if dl_dir: + tarball = os.path.join(dl_dir, archive) + if os.path.exists(tarball): + return tarball + + tarball = os.path.join(workdir, archive) + subprocess.check_output(["wget", "-O", tarball, url], stderr=subprocess.STDOUT) + return tarball + + def check_elf(self, path, target_os=None, target_arch=None): + """ + Verify that the ELF binary $path matches the specified target + OS/architecture, or if not specified the currently configured MACHINE's + OS/architecture. + """ + import oe.qa, oe.elf + + if not target_os or not target_arch: + output = self._run("echo $OECORE_TARGET_OS:$OECORE_TARGET_ARCH") + target_os, target_arch = output.strip().split(":") + + machine_data = oe.elf.machine_dict(None)[target_os][target_arch] + (machine, osabi, abiversion, endian, bits) = machine_data + + elf = oe.qa.ELFFile(path) + elf.open() + + self.assertEqual(machine, elf.machine(), + "Binary was %s but expected %s" % + (oe.qa.elf_machine_to_string(elf.machine()), oe.qa.elf_machine_to_string(machine))) + self.assertEqual(bits, elf.abiSize()) + self.assertEqual(endian, elf.isLittleEndian()) diff --git a/meta/lib/oeqa/sdk/cases/assimp.py b/meta/lib/oeqa/sdk/cases/assimp.py new file mode 100644 index 0000000000..e986838aea --- /dev/null +++ b/meta/lib/oeqa/sdk/cases/assimp.py @@ -0,0 +1,45 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +import os +import subprocess +import tempfile +import unittest +from oeqa.sdk.case import OESDKTestCase + +from oeqa.utils.subprocesstweak import errors_have_output +errors_have_output() + +class BuildAssimp(OESDKTestCase): + """ + Test case to build a project using cmake. + """ + + def setUp(self): + if not (self.tc.hasHostPackage("nativesdk-cmake") or + self.tc.hasHostPackage("cmake-native")): + raise unittest.SkipTest("Needs cmake") + + def test_assimp(self): + with tempfile.TemporaryDirectory(prefix="assimp", dir=self.tc.sdk_dir) as testdir: + tarball = self.fetch(testdir, self.td["DL_DIR"], "https://github.com/assimp/assimp/archive/v5.3.1.tar.gz") + + dirs = {} + dirs["source"] = os.path.join(testdir, "assimp-5.3.1") + dirs["build"] = os.path.join(testdir, "build") + dirs["install"] = os.path.join(testdir, "install") + + subprocess.check_output(["tar", "xf", tarball, "-C", testdir], stderr=subprocess.STDOUT) + self.assertTrue(os.path.isdir(dirs["source"])) + # Apply the zlib patch https://github.com/madler/zlib/commit/a566e156b3fa07b566ddbf6801b517a9dba04fa3 + # this sed wont be needed once assimp moves its zlib copy to v1.3.1+ + self._run("sed -i '/# ifdef _FILE_OFFSET_BITS/I,+2 d' {source}/contrib/zlib/gzguts.h".format(**dirs)) + os.makedirs(dirs["build"]) + + self._run("cd {build} && cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DASSIMP_BUILD_ZLIB=ON {source}".format(**dirs)) + self._run("cmake --build {build} -- -j".format(**dirs)) + self._run("cmake --build {build} --target install -- DESTDIR={install}".format(**dirs)) + self.check_elf(os.path.join(dirs["install"], "usr", "local", "lib", "libassimp.so.5.3.0")) diff --git a/meta/lib/oeqa/sdk/cases/buildcpio.py b/meta/lib/oeqa/sdk/cases/buildcpio.py index 333dc7c226..51003b19cd 100644 --- a/meta/lib/oeqa/sdk/cases/buildcpio.py +++ b/meta/lib/oeqa/sdk/cases/buildcpio.py @@ -1,33 +1,37 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +import os +import tempfile +import subprocess import unittest + from oeqa.sdk.case import OESDKTestCase -from oeqa.sdk.utils.sdkbuildproject import SDKBuildProject +from oeqa.utils.subprocesstweak import errors_have_output +errors_have_output() class BuildCpioTest(OESDKTestCase): - td_vars = ['DATETIME'] - - @classmethod - def setUpClass(self): - dl_dir = self.td.get('DL_DIR', None) - - self.project = SDKBuildProject(self.tc.sdk_dir + "/cpio/", self.tc.sdk_env, - "https://ftp.gnu.org/gnu/cpio/cpio-2.12.tar.gz", - self.tc.sdk_dir, self.td['DATETIME'], dl_dir=dl_dir) - self.project.download_archive() - - machine = self.td.get("MACHINE") - if not self.tc.hasHostPackage("packagegroup-cross-canadian-%s" % machine): - raise unittest.SkipTest("SDK doesn't contain a cross-canadian toolchain") - + """ + Check that autotools will cross-compile correctly. + """ def test_cpio(self): - self.assertEqual(self.project.run_configure(), 0, - msg="Running configure failed") + with tempfile.TemporaryDirectory(prefix="cpio-", dir=self.tc.sdk_dir) as testdir: + tarball = self.fetch(testdir, self.td["DL_DIR"], "https://ftp.gnu.org/gnu/cpio/cpio-2.15.tar.gz") + + dirs = {} + dirs["source"] = os.path.join(testdir, "cpio-2.15") + dirs["build"] = os.path.join(testdir, "build") + dirs["install"] = os.path.join(testdir, "install") - self.assertEqual(self.project.run_make(), 0, - msg="Running make failed") + subprocess.check_output(["tar", "xf", tarball, "-C", testdir], stderr=subprocess.STDOUT) + self.assertTrue(os.path.isdir(dirs["source"])) + os.makedirs(dirs["build"]) - self.assertEqual(self.project.run_install(), 0, - msg="Running make install failed") + self._run("cd {build} && {source}/configure $CONFIGURE_FLAGS".format(**dirs)) + self._run("cd {build} && make -j".format(**dirs)) + self._run("cd {build} && make install DESTDIR={install}".format(**dirs)) - @classmethod - def tearDownClass(self): - self.project.clean() + self.check_elf(os.path.join(dirs["install"], "usr", "local", "bin", "cpio")) diff --git a/meta/lib/oeqa/sdk/cases/buildepoxy.py b/meta/lib/oeqa/sdk/cases/buildepoxy.py new file mode 100644 index 0000000000..147ee3e0ee --- /dev/null +++ b/meta/lib/oeqa/sdk/cases/buildepoxy.py @@ -0,0 +1,44 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +import os +import subprocess +import tempfile +import unittest + +from oeqa.sdk.case import OESDKTestCase +from oeqa.utils.subprocesstweak import errors_have_output +errors_have_output() + +class EpoxyTest(OESDKTestCase): + """ + Test that Meson builds correctly. + """ + def setUp(self): + if not (self.tc.hasHostPackage("nativesdk-meson") or + self.tc.hasHostPackage("meson-native")): + raise unittest.SkipTest("EpoxyTest class: SDK doesn't contain Meson") + + def test_epoxy(self): + with tempfile.TemporaryDirectory(prefix="epoxy", dir=self.tc.sdk_dir) as testdir: + tarball = self.fetch(testdir, self.td["DL_DIR"], "https://github.com/anholt/libepoxy/releases/download/1.5.3/libepoxy-1.5.3.tar.xz") + + dirs = {} + dirs["source"] = os.path.join(testdir, "libepoxy-1.5.3") + dirs["build"] = os.path.join(testdir, "build") + dirs["install"] = os.path.join(testdir, "install") + + subprocess.check_output(["tar", "xf", tarball, "-C", testdir], stderr=subprocess.STDOUT) + self.assertTrue(os.path.isdir(dirs["source"])) + os.makedirs(dirs["build"]) + + log = self._run("meson --warnlevel 1 -Degl=no -Dglx=no -Dx11=false {build} {source}".format(**dirs)) + # Check that Meson thinks we're doing a cross build and not a native + self.assertIn("Build type: cross build", log) + self._run("ninja -C {build} -v".format(**dirs)) + self._run("DESTDIR={install} ninja -C {build} -v install".format(**dirs)) + + self.check_elf(os.path.join(dirs["install"], "usr", "local", "lib", "libepoxy.so")) diff --git a/meta/lib/oeqa/sdk/cases/buildgalculator.py b/meta/lib/oeqa/sdk/cases/buildgalculator.py index 780afccc74..178f07472d 100644 --- a/meta/lib/oeqa/sdk/cases/buildgalculator.py +++ b/meta/lib/oeqa/sdk/cases/buildgalculator.py @@ -1,35 +1,46 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +import os +import subprocess +import tempfile import unittest from oeqa.sdk.case import OESDKTestCase -from oeqa.sdk.utils.sdkbuildproject import SDKBuildProject +from oeqa.utils.subprocesstweak import errors_have_output +errors_have_output() class GalculatorTest(OESDKTestCase): - td_vars = ['DATETIME'] - - @classmethod - def setUpClass(self): - if not (self.tc.hasTargetPackage("gtk\+3") or\ - self.tc.hasTargetPackage("libgtk-3.0")): + """ + Test that autotools and GTK+ 3 compiles correctly. + """ + def setUp(self): + if not (self.tc.hasTargetPackage("gtk+3", multilib=True) or \ + self.tc.hasTargetPackage("libgtk-3.0", multilib=True)): raise unittest.SkipTest("GalculatorTest class: SDK don't support gtk+3") + if not (self.tc.hasHostPackage("nativesdk-gettext-dev") or + self.tc.hasHostPackage("gettext-native")): + raise unittest.SkipTest("GalculatorTest class: SDK doesn't contain gettext") def test_galculator(self): - dl_dir = self.td.get('DL_DIR', None) - project = None - try: - project = SDKBuildProject(self.tc.sdk_dir + "/galculator/", - self.tc.sdk_env, - "http://galculator.mnim.org/downloads/galculator-2.1.4.tar.bz2", - self.tc.sdk_dir, self.td['DATETIME'], dl_dir=dl_dir) - - project.download_archive() - - # regenerate configure to get support for --with-libtool-sysroot - legacy_preconf=("autoreconf -i -f -I ${OECORE_TARGET_SYSROOT}/usr/share/aclocal -I m4;") - - self.assertEqual(project.run_configure(extra_cmds=legacy_preconf), - 0, msg="Running configure failed") - - self.assertEqual(project.run_make(), 0, - msg="Running make failed") - finally: - project.clean() + with tempfile.TemporaryDirectory(prefix="galculator", dir=self.tc.sdk_dir) as testdir: + tarball = self.fetch(testdir, self.td["DL_DIR"], "http://galculator.mnim.org/downloads/galculator-2.1.4.tar.bz2") + + dirs = {} + dirs["source"] = os.path.join(testdir, "galculator-2.1.4") + dirs["build"] = os.path.join(testdir, "build") + dirs["install"] = os.path.join(testdir, "install") + + subprocess.check_output(["tar", "xf", tarball, "-C", testdir], stderr=subprocess.STDOUT) + self.assertTrue(os.path.isdir(dirs["source"])) + os.makedirs(dirs["build"]) + + self._run("cd {source} && sed -i -e '/s_preferences.*prefs;/d' src/main.c && autoreconf -i -f -I $OECORE_TARGET_SYSROOT/usr/share/aclocal -I m4".format(**dirs)) + self._run("cd {build} && {source}/configure $CONFIGURE_FLAGS".format(**dirs)) + self._run("cd {build} && make -j".format(**dirs)) + self._run("cd {build} && make install DESTDIR={install}".format(**dirs)) + + self.check_elf(os.path.join(dirs["install"], "usr", "local", "bin", "galculator")) diff --git a/meta/lib/oeqa/sdk/cases/buildlzip.py b/meta/lib/oeqa/sdk/cases/buildlzip.py index 3a89ce8627..b4b7d85b88 100644 --- a/meta/lib/oeqa/sdk/cases/buildlzip.py +++ b/meta/lib/oeqa/sdk/cases/buildlzip.py @@ -1,36 +1,39 @@ -import unittest -from oeqa.sdk.case import OESDKTestCase -from oeqa.sdk.utils.sdkbuildproject import SDKBuildProject +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# +import os, tempfile, subprocess, unittest +from oeqa.sdk.case import OESDKTestCase +from oeqa.utils.subprocesstweak import errors_have_output +errors_have_output() class BuildLzipTest(OESDKTestCase): - td_vars = ['DATETIME'] - - @classmethod - def setUpClass(self): - dl_dir = self.td.get('DL_DIR', None) - - self.project = SDKBuildProject(self.tc.sdk_dir + "/lzip/", self.tc.sdk_env, - "http://downloads.yoctoproject.org/mirror/sources/lzip-1.19.tar.gz", - self.tc.sdk_dir, self.td['DATETIME'], dl_dir=dl_dir) - self.project.download_archive() - - machine = self.td.get("MACHINE") - - if not (self.tc.hasTargetPackage("packagegroup-cross-canadian-%s" % machine) or - self.tc.hasTargetPackage("gcc")): - raise unittest.SkipTest("SDK doesn't contain a cross-canadian toolchain") - + """ + Test that "plain" compilation works, using just $CC $CFLAGS etc. + """ def test_lzip(self): - self.assertEqual(self.project.run_configure(), 0, - msg="Running configure failed") - - self.assertEqual(self.project.run_make(), 0, - msg="Running make failed") - - self.assertEqual(self.project.run_install(), 0, - msg="Running make install failed") - - @classmethod - def tearDownClass(self): - self.project.clean() + with tempfile.TemporaryDirectory(prefix="lzip", dir=self.tc.sdk_dir) as testdir: + tarball = self.fetch(testdir, self.td["DL_DIR"], "http://downloads.yoctoproject.org/mirror/sources/lzip-1.19.tar.gz") + + dirs = {} + dirs["source"] = os.path.join(testdir, "lzip-1.19") + dirs["build"] = os.path.join(testdir, "build") + dirs["install"] = os.path.join(testdir, "install") + + subprocess.check_output(["tar", "xf", tarball, "-C", testdir], stderr=subprocess.STDOUT) + self.assertTrue(os.path.isdir(dirs["source"])) + os.makedirs(dirs["build"]) + + cmd = """cd {build} && \ + {source}/configure --srcdir {source} \ + CXX="$CXX" \ + CPPFLAGS="$CPPFLAGS" \ + CXXFLAGS="$CXXFLAGS" \ + LDFLAGS="$LDFLAGS" \ + """ + self._run(cmd.format(**dirs)) + self._run("cd {build} && make -j".format(**dirs)) + self._run("cd {build} && make install DESTDIR={install}".format(**dirs)) + self.check_elf(os.path.join(dirs["install"], "usr", "local", "bin", "lzip")) diff --git a/meta/lib/oeqa/sdk/cases/gcc.py b/meta/lib/oeqa/sdk/cases/gcc.py index d11f4b63fb..fc28b9c3d4 100644 --- a/meta/lib/oeqa/sdk/cases/gcc.py +++ b/meta/lib/oeqa/sdk/cases/gcc.py @@ -1,3 +1,9 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + import os import shutil import unittest @@ -5,6 +11,9 @@ import unittest from oeqa.core.utils.path import remove_safe from oeqa.sdk.case import OESDKTestCase +from oeqa.utils.subprocesstweak import errors_have_output +errors_have_output() + class GccCompileTest(OESDKTestCase): td_vars = ['MACHINE'] @@ -18,8 +27,8 @@ class GccCompileTest(OESDKTestCase): def setUp(self): machine = self.td.get("MACHINE") - if not (self.tc.hasTargetPackage("packagegroup-cross-canadian-%s" % machine) or - self.tc.hasTargetPackage("gcc")): + if not (self.tc.hasHostPackage("packagegroup-cross-canadian-%s" % machine) or + self.tc.hasHostPackage("^gcc-", regex=True)): raise unittest.SkipTest("GccCompileTest class: SDK doesn't contain a cross-canadian toolchain") def test_gcc_compile(self): diff --git a/meta/lib/oeqa/sdk/cases/maturin.py b/meta/lib/oeqa/sdk/cases/maturin.py new file mode 100644 index 0000000000..ea10f568b2 --- /dev/null +++ b/meta/lib/oeqa/sdk/cases/maturin.py @@ -0,0 +1,79 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +import os +import shutil +import unittest + +from oeqa.core.utils.path import remove_safe +from oeqa.sdk.case import OESDKTestCase +from oeqa.utils.subprocesstweak import errors_have_output + +errors_have_output() + + +class MaturinTest(OESDKTestCase): + def setUp(self): + if not ( + self.tc.hasHostPackage("nativesdk-python3-maturin") + or self.tc.hasHostPackage("python3-maturin-native") + ): + raise unittest.SkipTest("No python3-maturin package in the SDK") + + def test_maturin_list_python(self): + py_major = self._run("python3 -c 'import sys; print(sys.version_info.major)'") + py_minor = self._run("python3 -c 'import sys; print(sys.version_info.minor)'") + python_version = "%s.%s" % (py_major.strip(), py_minor.strip()) + cmd = "maturin list-python" + output = self._run(cmd) + self.assertRegex(output, r"^🐍 1 python interpreter found:\n") + self.assertRegex( + output, + r" - CPython %s (.+)/usr/bin/python%s$" % (python_version, python_version), + ) + + +class MaturinDevelopTest(OESDKTestCase): + @classmethod + def setUpClass(self): + targetdir = os.path.join(self.tc.sdk_dir, "guessing-game") + try: + shutil.rmtree(targetdir) + except FileNotFoundError: + pass + shutil.copytree( + os.path.join(self.tc.files_dir, "maturin/guessing-game"), targetdir + ) + + def setUp(self): + machine = self.td.get("MACHINE") + if not ( + self.tc.hasHostPackage("nativesdk-python3-maturin") + or self.tc.hasHostPackage("python3-maturin-native") + ): + raise unittest.SkipTest("No python3-maturin package in the SDK") + if not ( + self.tc.hasHostPackage("packagegroup-rust-cross-canadian-%s" % machine) + ): + raise unittest.SkipTest( + "Testing 'maturin develop' requires Rust cross-canadian in the SDK" + ) + + def test_maturin_develop(self): + """ + This test case requires: + (1) that a .venv can been created. + (2) a functional 'rustc' and 'cargo' + """ + self._run("cd %s/guessing-game; python3 -m venv .venv" % self.tc.sdk_dir) + cmd = "cd %s/guessing-game; maturin develop" % self.tc.sdk_dir + output = self._run(cmd) + self.assertRegex(output, r"🔗 Found pyo3 bindings with abi3 support for Python ≥ 3.8") + self.assertRegex(output, r"🐍 Not using a specific python interpreter") + self.assertRegex(output, r"📡 Using build options features from pyproject.toml") + self.assertRegex(output, r"Compiling guessing-game v0.1.0") + self.assertRegex(output, r"📦 Built wheel for abi3 Python ≥ 3.8") + self.assertRegex(output, r"🛠 Installed guessing-game-0.1.0") diff --git a/meta/lib/oeqa/sdk/cases/perl.py b/meta/lib/oeqa/sdk/cases/perl.py index 8085678116..8eab4442e8 100644 --- a/meta/lib/oeqa/sdk/cases/perl.py +++ b/meta/lib/oeqa/sdk/cases/perl.py @@ -1,28 +1,22 @@ -import os -import shutil -import unittest +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# -from oeqa.core.utils.path import remove_safe +import unittest from oeqa.sdk.case import OESDKTestCase +from oeqa.utils.subprocesstweak import errors_have_output +errors_have_output() + class PerlTest(OESDKTestCase): - @classmethod - def setUpClass(self): + def setUp(self): if not (self.tc.hasHostPackage("nativesdk-perl") or self.tc.hasHostPackage("perl-native")): raise unittest.SkipTest("No perl package in the SDK") - for f in ['test.pl']: - shutil.copyfile(os.path.join(self.tc.files_dir, f), - os.path.join(self.tc.sdk_dir, f)) - self.testfile = os.path.join(self.tc.sdk_dir, "test.pl") - - def test_perl_exists(self): - self._run('which perl') - - def test_perl_works(self): - self._run('perl %s' % self.testfile) - - @classmethod - def tearDownClass(self): - remove_safe(self.testfile) + def test_perl(self): + cmd = "perl -e '$_=\"Uryyb, jbeyq\"; tr/a-zA-Z/n-za-mN-ZA-M/;print'" + output = self._run(cmd) + self.assertEqual(output, "Hello, world") diff --git a/meta/lib/oeqa/sdk/cases/python.py b/meta/lib/oeqa/sdk/cases/python.py index 72dfcc72bd..5ea992b9f3 100644 --- a/meta/lib/oeqa/sdk/cases/python.py +++ b/meta/lib/oeqa/sdk/cases/python.py @@ -1,32 +1,22 @@ -import os -import shutil -import unittest +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# -from oeqa.core.utils.path import remove_safe +import subprocess, unittest from oeqa.sdk.case import OESDKTestCase -class PythonTest(OESDKTestCase): - @classmethod - def setUpClass(self): - if not (self.tc.hasHostPackage("nativesdk-python") or - self.tc.hasHostPackage("python-native")): - raise unittest.SkipTest("No python package in the SDK") +from oeqa.utils.subprocesstweak import errors_have_output +errors_have_output() - for f in ['test.py']: - shutil.copyfile(os.path.join(self.tc.files_dir, f), - os.path.join(self.tc.sdk_dir, f)) +class Python3Test(OESDKTestCase): + def setUp(self): + if not (self.tc.hasHostPackage("nativesdk-python3-core") or + self.tc.hasHostPackage("python3-core-native")): + raise unittest.SkipTest("No python3 package in the SDK") - def test_python_exists(self): - self._run('which python') - - def test_python_stdout(self): - output = self._run('python %s/test.py' % self.tc.sdk_dir) - self.assertEqual(output.strip(), "the value of a is 0.01", msg="Incorrect output: %s" % output) - - def test_python_testfile(self): - self._run('ls /tmp/testfile.python') - - @classmethod - def tearDownClass(self): - remove_safe("%s/test.py" % self.tc.sdk_dir) - remove_safe("/tmp/testfile.python") + def test_python3(self): + cmd = "python3 -c \"import codecs; print(codecs.encode('Uryyb, jbeyq', 'rot13'))\"" + output = self._run(cmd) + self.assertEqual(output, "Hello, world\n") diff --git a/meta/lib/oeqa/sdk/cases/rust.py b/meta/lib/oeqa/sdk/cases/rust.py new file mode 100644 index 0000000000..f5d437bb19 --- /dev/null +++ b/meta/lib/oeqa/sdk/cases/rust.py @@ -0,0 +1,57 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +import os +import shutil +import unittest + +from oeqa.core.utils.path import remove_safe +from oeqa.sdk.case import OESDKTestCase + +from oeqa.utils.subprocesstweak import errors_have_output +errors_have_output() + +class RustCompileTest(OESDKTestCase): + td_vars = ['MACHINE'] + + @classmethod + def setUpClass(self): + targetdir = os.path.join(self.tc.sdk_dir, "hello") + try: + shutil.rmtree(targetdir) + except FileNotFoundError: + pass + shutil.copytree(os.path.join(self.tc.sdk_files_dir, "rust/hello"), targetdir) + + def setUp(self): + machine = self.td.get("MACHINE") + if not self.tc.hasHostPackage("packagegroup-rust-cross-canadian-%s" % machine): + raise unittest.SkipTest("RustCompileTest class: SDK doesn't contain a Rust cross-canadian toolchain") + + def test_cargo_build(self): + self._run('cd %s/hello; cargo build' % self.tc.sdk_dir) + +class RustHostCompileTest(OESDKTestCase): + td_vars = ['MACHINE', 'SDK_SYS'] + + @classmethod + def setUpClass(self): + targetdir = os.path.join(self.tc.sdk_dir, "hello") + try: + shutil.rmtree(targetdir) + except FileNotFoundError: + pass + shutil.copytree(os.path.join(self.tc.sdk_files_dir, "rust/hello"), targetdir) + + def setUp(self): + machine = self.td.get("MACHINE") + if not self.tc.hasHostPackage("packagegroup-rust-cross-canadian-%s" % machine): + raise unittest.SkipTest("RustCompileTest class: SDK doesn't contain a Rust cross-canadian toolchain") + + def test_cargo_build(self): + sdksys = self.td.get("SDK_SYS") + self._run('cd %s/hello; cargo build --target %s-gnu' % (self.tc.sdk_dir, sdksys)) + self._run('cd %s/hello; cargo run --target %s-gnu' % (self.tc.sdk_dir, sdksys)) diff --git a/meta/lib/oeqa/sdk/context.py b/meta/lib/oeqa/sdk/context.py index b3d7c75183..01c38c24e6 100644 --- a/meta/lib/oeqa/sdk/context.py +++ b/meta/lib/oeqa/sdk/context.py @@ -1,15 +1,17 @@ +# # Copyright (C) 2016 Intel Corporation -# Released under the MIT license (see COPYING.MIT) +# +# SPDX-License-Identifier: MIT +# import os import sys import glob import re -from oeqa.core.context import OETestContextExecutor -from oeqa.core.threaded import OETestContextThreaded +from oeqa.core.context import OETestContext, OETestContextExecutor -class OESDKTestContext(OETestContextThreaded): +class OESDKTestContext(OETestContext): sdk_files_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "files") def __init__(self, td=None, logger=None, sdk_dir=None, sdk_env=None, @@ -21,17 +23,30 @@ class OESDKTestContext(OETestContextThreaded): self.target_pkg_manifest = target_pkg_manifest self.host_pkg_manifest = host_pkg_manifest - def _hasPackage(self, manifest, pkg): - for host_pkg in manifest.keys(): - if re.search(pkg, host_pkg): + def _hasPackage(self, manifest, pkg, regex=False): + if regex: + # do regex match + pat = re.compile(pkg) + for p in manifest.keys(): + if pat.search(p): + return True + else: + # do exact match + if pkg in manifest.keys(): return True return False - def hasHostPackage(self, pkg): - return self._hasPackage(self.host_pkg_manifest, pkg) + def hasHostPackage(self, pkg, regex=False): + return self._hasPackage(self.host_pkg_manifest, pkg, regex=regex) - def hasTargetPackage(self, pkg): - return self._hasPackage(self.target_pkg_manifest, pkg) + def hasTargetPackage(self, pkg, multilib=False, regex=False): + if multilib: + # match multilib according to sdk_env + mls = self.td.get('MULTILIB_VARIANTS', '').split() + for ml in mls: + if ('ml'+ml) in self.sdk_env: + pkg = ml + '-' + pkg + return self._hasPackage(self.target_pkg_manifest, pkg, regex=regex) class OESDKTestContextExecutor(OETestContextExecutor): _context_class = OESDKTestContext @@ -66,6 +81,9 @@ class OESDKTestContextExecutor(OETestContextExecutor): sdk_rgroup.add_argument('--sdk-dir', required=False, action='store', help='sdk installed directory') + self.parser.add_argument('-j', '--num-processes', dest='processes', action='store', + type=int, help="number of processes to execute in parallel with") + @staticmethod def _load_manifest(manifest): pkg_manifest = {} @@ -86,6 +104,7 @@ class OESDKTestContextExecutor(OETestContextExecutor): OESDKTestContextExecutor._load_manifest(args.target_manifest) self.tc_kwargs['init']['host_pkg_manifest'] = \ OESDKTestContextExecutor._load_manifest(args.host_manifest) + self.tc_kwargs['run']['processes'] = args.processes @staticmethod def _get_sdk_environs(sdk_dir): @@ -117,7 +136,7 @@ class OESDKTestContextExecutor(OETestContextExecutor): sdk_envs = OESDKTestContextExecutor._get_sdk_environs(args.sdk_dir) if not sdk_envs: raise argparse_oe.ArgumentUsageError("No available SDK "\ - "enviroments found at %s" % args.sdk_dir, self.name) + "environments found at %s" % args.sdk_dir, self.name) if args.list_sdk_env: self._display_sdk_envs(logger.info, args, sdk_envs) diff --git a/meta/lib/oeqa/sdk/files/rust/hello/Cargo.toml b/meta/lib/oeqa/sdk/files/rust/hello/Cargo.toml new file mode 100644 index 0000000000..fe619478a6 --- /dev/null +++ b/meta/lib/oeqa/sdk/files/rust/hello/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "hello" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/meta/lib/oeqa/sdk/files/rust/hello/build.rs b/meta/lib/oeqa/sdk/files/rust/hello/build.rs new file mode 100644 index 0000000000..b1a533d5df --- /dev/null +++ b/meta/lib/oeqa/sdk/files/rust/hello/build.rs @@ -0,0 +1,3 @@ +/* This is the simplest build script just to invoke host compiler + in the build process. */ +fn main() {} diff --git a/meta/lib/oeqa/sdk/files/rust/hello/src/main.rs b/meta/lib/oeqa/sdk/files/rust/hello/src/main.rs new file mode 100644 index 0000000000..a06c03f82a --- /dev/null +++ b/meta/lib/oeqa/sdk/files/rust/hello/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, OpenEmbedded world!"); +} diff --git a/meta/lib/oeqa/sdk/testmetaidesupport.py b/meta/lib/oeqa/sdk/testmetaidesupport.py new file mode 100644 index 0000000000..00ef30e82e --- /dev/null +++ b/meta/lib/oeqa/sdk/testmetaidesupport.py @@ -0,0 +1,45 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +class TestSDK(object): + def run(self, d): + import json + import logging + from oeqa.sdk.context import OESDKTestContext, OESDKTestContextExecutor + from oeqa.utils import make_logger_bitbake_compatible + + pn = d.getVar("PN") + + logger = make_logger_bitbake_compatible(logging.getLogger("BitBake")) + + sdk_dir = d.expand("${WORKDIR}/testsdk/") + bb.utils.remove(sdk_dir, True) + bb.utils.mkdirhier(sdk_dir) + + sdk_envs = OESDKTestContextExecutor._get_sdk_environs(d.getVar("DEPLOY_DIR_IMAGE")) + tdname = d.expand("${DEPLOY_DIR_IMAGE}/${PN}.testdata.json") + test_data = json.load(open(tdname, "r")) + + host_pkg_manifest = {"cmake-native":"", "gcc-cross":"", "gettext-native":"", "meson-native":"", "perl-native":"", "python3-core-native":"", } + target_pkg_manifest = {"gtk+3":""} + + for s in sdk_envs: + bb.plain("meta-ide-support based SDK testing environment: %s" % s) + + sdk_env = sdk_envs[s] + + tc = OESDKTestContext(td=test_data, logger=logger, sdk_dir=sdk_dir, + sdk_env=sdk_env, target_pkg_manifest=target_pkg_manifest, + host_pkg_manifest=host_pkg_manifest) + + tc.loadTests(OESDKTestContextExecutor.default_cases) + + results = tc.runTests() + if results: + results.logSummary(pn) + + if (not results) or (not results.wasSuccessful()): + bb.fatal('%s - FAILED' % (pn,), forcelog=True) diff --git a/meta/lib/oeqa/sdk/testsdk.py b/meta/lib/oeqa/sdk/testsdk.py new file mode 100644 index 0000000000..518b09febb --- /dev/null +++ b/meta/lib/oeqa/sdk/testsdk.py @@ -0,0 +1,141 @@ +# +# Copyright 2018 by Garmin Ltd. or its subsidiaries +# +# SPDX-License-Identifier: MIT +# + +from oeqa.sdk.context import OESDKTestContext, OESDKTestContextExecutor + +class TestSDKBase(object): + @staticmethod + def get_sdk_configuration(d, test_type): + import platform + import oe.lsb + from oeqa.utils.metadata import get_layers + configuration = {'TEST_TYPE': test_type, + 'MACHINE': d.getVar("MACHINE"), + 'SDKMACHINE': d.getVar("SDKMACHINE"), + 'IMAGE_BASENAME': d.getVar("IMAGE_BASENAME"), + 'IMAGE_PKGTYPE': d.getVar("IMAGE_PKGTYPE"), + 'STARTTIME': d.getVar("DATETIME"), + 'HOST_DISTRO': oe.lsb.distro_identifier().replace(' ', '-'), + 'LAYERS': get_layers(d.getVar("BBLAYERS"))} + return configuration + + @staticmethod + def get_sdk_result_id(configuration): + return '%s_%s_%s_%s_%s' % (configuration['TEST_TYPE'], configuration['IMAGE_BASENAME'], configuration['SDKMACHINE'], configuration['MACHINE'], configuration['STARTTIME']) + +class TestSDK(TestSDKBase): + context_executor_class = OESDKTestContextExecutor + context_class = OESDKTestContext + test_type = 'sdk' + + def get_tcname(self, d): + """ + Get the name of the SDK file + """ + return d.expand("${SDK_DEPLOY}/${TOOLCHAIN_OUTPUTNAME}.sh") + + def extract_sdk(self, tcname, sdk_dir, d): + """ + Extract the SDK to the specified location + """ + import subprocess + + try: + subprocess.check_output("cd %s; %s <<EOF\n./\nY\nEOF" % (sdk_dir, tcname), shell=True) + except subprocess.CalledProcessError as e: + bb.fatal("Couldn't install the SDK:\n%s" % e.output.decode("utf-8")) + + def setup_context(self, d): + """ + Return a dictionary of additional arguments that should be passed to + the context_class on construction + """ + return dict() + + def run(self, d): + + import os + import subprocess + import json + import logging + + from bb.utils import export_proxies + from oeqa.utils import make_logger_bitbake_compatible + from oeqa.utils import get_json_result_dir + + pn = d.getVar("PN") + logger = make_logger_bitbake_compatible(logging.getLogger("BitBake")) + + # sdk use network for download projects for build + export_proxies(d) + + # We need the original PATH for testing the eSDK, not with our manipulations + os.environ['PATH'] = d.getVar("BB_ORIGENV", False).getVar("PATH") + + tcname = self.get_tcname(d) + + if not os.path.exists(tcname): + bb.fatal("The toolchain %s is not built. Build it before running the tests: 'bitbake <image> -c populate_sdk' ." % tcname) + + tdname = d.expand("${SDK_DEPLOY}/${TOOLCHAIN_OUTPUTNAME}.testdata.json") + test_data = json.load(open(tdname, "r")) + + target_pkg_manifest = self.context_executor_class._load_manifest( + d.expand("${SDK_DEPLOY}/${TOOLCHAIN_OUTPUTNAME}.target.manifest")) + host_pkg_manifest = self.context_executor_class._load_manifest( + d.expand("${SDK_DEPLOY}/${TOOLCHAIN_OUTPUTNAME}.host.manifest")) + + processes = d.getVar("TESTIMAGE_NUMBER_THREADS") or d.getVar("BB_NUMBER_THREADS") + if processes: + try: + import testtools, subunit + except ImportError: + bb.warn("Failed to import testtools or subunit, the testcases will run serially") + processes = None + + sdk_dir = d.expand("${WORKDIR}/testimage-sdk/") + bb.utils.remove(sdk_dir, True) + bb.utils.mkdirhier(sdk_dir) + + context_args = self.setup_context(d) + + self.extract_sdk(tcname, sdk_dir, d) + + fail = False + sdk_envs = self.context_executor_class._get_sdk_environs(sdk_dir) + for s in sdk_envs: + sdk_env = sdk_envs[s] + bb.plain("SDK testing environment: %s" % s) + tc = self.context_class(td=test_data, logger=logger, sdk_dir=sdk_dir, + sdk_env=sdk_env, target_pkg_manifest=target_pkg_manifest, + host_pkg_manifest=host_pkg_manifest, **context_args) + + try: + tc.loadTests(self.context_executor_class.default_cases) + except Exception as e: + import traceback + bb.fatal("Loading tests failed:\n%s" % traceback.format_exc()) + + if processes: + result = tc.runTests(processes=int(processes)) + else: + result = tc.runTests() + + component = "%s %s" % (pn, self.context_executor_class.name) + context_msg = "%s:%s" % (os.path.basename(tcname), os.path.basename(sdk_env)) + configuration = self.get_sdk_configuration(d, self.test_type) + result.logDetails(get_json_result_dir(d), + configuration, + self.get_sdk_result_id(configuration)) + result.logSummary(component, context_msg) + + if not result.wasSuccessful(): + fail = True + + if fail: + bb.fatal("%s - FAILED - check the task log and the commands log" % pn) + + diff --git a/meta/lib/oeqa/sdk/utils/sdkbuildproject.py b/meta/lib/oeqa/sdk/utils/sdkbuildproject.py index 4e251142d7..32f5e3310d 100644 --- a/meta/lib/oeqa/sdk/utils/sdkbuildproject.py +++ b/meta/lib/oeqa/sdk/utils/sdkbuildproject.py @@ -1,5 +1,8 @@ +# # Copyright (C) 2016 Intel Corporation -# Released under the MIT license (see COPYING.MIT) +# +# SPDX-License-Identifier: MIT +# import os import subprocess @@ -20,10 +23,9 @@ class SDKBuildProject(BuildProject): BuildProject.__init__(self, uri, foldername, tmpdir=testpath, dl_dir=dl_dir) def download_archive(self): - self._download_archive() - cmd = 'tar xf %s%s -C %s' % (self.targetdir, self.archive, self.targetdir) + cmd = 'tar xf %s -C %s' % (os.path.join(self.targetdir, self.archive), self.targetdir) subprocess.check_output(cmd, shell=True) #Change targetdir to project folder @@ -42,4 +44,10 @@ class SDKBuildProject(BuildProject): def _run(self, cmd): self.log("Running . %s; " % self.sdkenv + cmd) - return subprocess.call(". %s; " % self.sdkenv + cmd, shell=True) + try: + output = subprocess.check_output(". %s; " % self.sdkenv + cmd, shell=True, + executable='/bin/bash', stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as exc: + print(exc.output.decode('utf-8')) + return exc.returncode + return 0 |