From: Joshua Watt <jpewhacker@gmail.com>
To: yocto@yoctoproject.org
Subject: [meta-mingw][PATCH v2 1/5] Add SDK test case framework
Date: Mon, 19 Nov 2018 15:17:48 -0600 [thread overview]
Message-ID: <20181119211752.18963-2-JPEWhacker@gmail.com> (raw)
In-Reply-To: <20181119211752.18963-1-JPEWhacker@gmail.com>
Adds the framework for testing SDKs that ties into the oeqa test
framework. This allows commands like:
$ bitbake -c testsdk ...
to be run for MinGW SDKs.
The test framework currently executes all tests under Wine in lieu of
having access to actual Windows machines.
[YOCTO #13020]
Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
---
conf/machine-sdk/i686-mingw32.conf | 1 +
conf/machine-sdk/include/mingw32-common.inc | 7 ++
conf/machine-sdk/x86_64-mingw32.conf | 1 +
lib/oeqa/sdkmingw/__init__.py | 0
lib/oeqa/sdkmingw/case.py | 87 +++++++++++++++++++++
lib/oeqa/sdkmingw/cases/__init__.py | 0
lib/oeqa/sdkmingw/context.py | 69 ++++++++++++++++
lib/oeqa/sdkmingw/testsdk.py | 42 ++++++++++
8 files changed, 207 insertions(+)
create mode 100644 lib/oeqa/sdkmingw/__init__.py
create mode 100644 lib/oeqa/sdkmingw/case.py
create mode 100644 lib/oeqa/sdkmingw/cases/__init__.py
create mode 100644 lib/oeqa/sdkmingw/context.py
create mode 100644 lib/oeqa/sdkmingw/testsdk.py
diff --git a/conf/machine-sdk/i686-mingw32.conf b/conf/machine-sdk/i686-mingw32.conf
index 5090168..fef48b5 100644
--- a/conf/machine-sdk/i686-mingw32.conf
+++ b/conf/machine-sdk/i686-mingw32.conf
@@ -1,3 +1,4 @@
SDK_ARCH = "i686"
+TESTSDK_WINEARCH = "win32"
require conf/machine-sdk/include/mingw32-common.inc
diff --git a/conf/machine-sdk/include/mingw32-common.inc b/conf/machine-sdk/include/mingw32-common.inc
index 733d092..71e8d45 100644
--- a/conf/machine-sdk/include/mingw32-common.inc
+++ b/conf/machine-sdk/include/mingw32-common.inc
@@ -26,6 +26,9 @@ SDKPKGSUFFIX = "nativesdk-mingw32"
MACHINEOVERRIDES .= ":sdkmingw32"
+TESTSDK_CLASS_NAME = "oeqa.sdkmingw.testsdk.TestSDKMinGW"
+TESTSDKEXT_CLASS_NAME = ""
+
WINDRES_mingw32 = "${HOST_PREFIX}windres --include-dir=${STAGING_INCDIR}"
RC_mingw32 = "${WINDRES}"
@@ -39,3 +42,7 @@ DISABLE_STATIC_mingw32 = ""
# disable security flags
GCCPIE_mingw32 = ""
+
+# wine and wineserver are required to test MinGW SDKs
+HOSTTOOLS += "${@'wine wineserver' if (bb.utils.contains_any('IMAGE_CLASSES', 'testsdk-mingw', True, False, d) or any(x in (d.getVar("BBINCLUDED") or "") for x in ["testsdk-mingw.bbclass"])) else ''}"
+
diff --git a/conf/machine-sdk/x86_64-mingw32.conf b/conf/machine-sdk/x86_64-mingw32.conf
index fc53822..188debc 100644
--- a/conf/machine-sdk/x86_64-mingw32.conf
+++ b/conf/machine-sdk/x86_64-mingw32.conf
@@ -1,3 +1,4 @@
SDK_ARCH = "x86_64"
+TESTSDK_WINEARCH = "win64"
require conf/machine-sdk/include/mingw32-common.inc
diff --git a/lib/oeqa/sdkmingw/__init__.py b/lib/oeqa/sdkmingw/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/lib/oeqa/sdkmingw/case.py b/lib/oeqa/sdkmingw/case.py
new file mode 100644
index 0000000..169c143
--- /dev/null
+++ b/lib/oeqa/sdkmingw/case.py
@@ -0,0 +1,87 @@
+# Copyright 2018 by Garmin Ltd. or its subsidiaries
+# Released under the MIT license (see COPYING.MIT)
+
+import subprocess
+import os
+import tempfile
+import shutil
+import bb
+
+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 OESDKMinGWTestCase(OESDKTestCase):
+ def setUp(self):
+ super().setUp()
+
+ self.test_dir = tempfile.mkdtemp(prefix=self.__class__.__name__ + '-', dir=self.tc.sdk_dir)
+ self.addCleanup(lambda: bb.utils.prunedir(self.test_dir))
+
+ self.wine_test_dir = self.tc.wine_path(self.test_dir)
+
+ def copyTestFile(self, src, dest=None):
+ dest_path = dest or os.path.join(self.test_dir, os.path.basename(src))
+ shutil.copyfile(src, dest_path)
+ self.addCleanup(lambda: remove_safe(dest_path))
+
+ def fetch(self, url, destdir=None, dl_dir=None, archive=None):
+ if not destdir:
+ destdir = self.test_dir
+
+ if not dl_dir:
+ dl_dir = self.td.get('DL_DIR', 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(destdir, archive)
+ subprocess.check_output(["wget", "-O", tarball, url])
+ return tarball
+
+
+ def _run(self, cmd):
+ import shlex
+
+ def strip_quotes(s):
+ if s[0] == '"' and s[-1] == '"':
+ return s[1:-1]
+ return s
+
+ command = ['wine', 'cmd', '/c', self.tc.wine_sdk_env, '>', 'NUL', '&&', 'cd', self.wine_test_dir, '&&']
+
+ # Perform some massaging so that commands can be written naturally in
+ # test cases. shlex.split() in Non-posix mode gets us most of the way
+ # there, but it leaves the quotes around a quoted argument, so we
+ # remove them manually.
+ command.extend(strip_quotes(s) for s in shlex.split(cmd, posix=False))
+
+ return subprocess.check_output(command, env=self.tc.get_wine_env(),
+ stderr=subprocess.STDOUT, universal_newlines=True)
+
+ def assertIsTargetElf(self, path):
+ import oe.qa
+ import oe.elf
+
+ elf = oe.qa.ELFFile(path)
+ elf.open()
+
+ if not getattr(self, 'target_os', None):
+ output = self._run("echo %OECORE_TARGET_OS%:%OECORE_TARGET_ARCH%")
+ self.target_os, self.target_arch = output.strip().split(":")
+
+ machine_data = oe.elf.machine_dict(None)[self.target_os][self.target_arch]
+ (machine, osabi, abiversion, endian, bits) = machine_data
+
+ self.assertEqual(machine, elf.machine())
+ self.assertEqual(bits, elf.abiSize())
+ self.assertEqual(endian, elf.isLittleEndian())
+
diff --git a/lib/oeqa/sdkmingw/cases/__init__.py b/lib/oeqa/sdkmingw/cases/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/lib/oeqa/sdkmingw/context.py b/lib/oeqa/sdkmingw/context.py
new file mode 100644
index 0000000..edabcbd
--- /dev/null
+++ b/lib/oeqa/sdkmingw/context.py
@@ -0,0 +1,69 @@
+# Copyright (C) 2018 by Garmin Ltd. or its subsidiaries
+# Released under the MIT license (see COPYING.MIT)
+import os
+import subprocess
+
+from oeqa.sdk.context import OESDKTestContext, OESDKTestContextExecutor
+
+from oeqa.utils.subprocesstweak import errors_have_output
+errors_have_output()
+
+class OESDKMinGWTestContext(OESDKTestContext):
+ 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, wine_prefix=None,
+ wine_arch=None, target_pkg_manifest=None, host_pkg_manifest=None):
+ super(OESDKMinGWTestContext, self).__init__(td, logger, sdk_dir, sdk_env, target_pkg_manifest, host_pkg_manifest)
+ self.wine_prefix = wine_prefix
+ self.wine_arch = wine_arch
+ self.wine_sdk_dir = self.wine_path(sdk_dir)
+ self.wine_sdk_env = self.wine_path(sdk_env)
+
+ def get_wine_env(self):
+ env = os.environ.copy()
+
+ # Turn off all Wine debug logging so it doesn't interfere with command output
+ env['WINEDEBUG'] = '-all'
+
+ env['WINEPREFIX'] = self.wine_prefix
+ env['WINEARCH'] = self.wine_arch
+
+ # Convenience variables to make test cases easier to write
+ env['SDK_DIR'] = getattr(self, 'wine_sdk_dir', '')
+
+ return env
+
+ def wine_path(self, p):
+ """
+ Converts a host POSIX path to a path in Wine
+ """
+ o = subprocess.check_output(['wine', 'winepath', '-w', p], env=self.get_wine_env())
+ return o.decode('utf-8').rstrip()
+
+
+class OESDKMinGWTestContextExecutor(OESDKTestContextExecutor):
+ _context_class = OESDKMinGWTestContext
+
+ name = 'sdk-mingw'
+ help = 'MinGW sdk test component'
+ description = 'executes MinGW sdk tests'
+
+ default_cases = [os.path.join(os.path.abspath(os.path.dirname(__file__)),
+ 'cases')]
+
+ def register_commands(self, logger, subparsers):
+ super(OESDKMinGWTestContextExecutor, self).register_commands(logger, subparsers)
+
+ wine_group = self.parser.add_argument_group('wine options')
+ wine_group.add_argument('--wine-prefix', action='store',
+ help='Wine prefix (bottle). Default is $SDK_DIR/.wine')
+ wine_group.add_argument('--wine-arch', action='store', choices=('win32', 'win64'),
+ default='win64', help='Wine architecture. Defaults to %(default)s')
+
+ def _process_args(self, logger, args):
+ super(OESDKMinGWTestContextExecutor, self)._process_args(logger, args)
+ self.tc_kwargs['init']['wine_prefix'] = args.wine_prefix or os.path.join(args.sdk_dir, '.wine')
+ self.tc_kwargs['init']['wine_arch'] = args.wine_arch
+
+_executor_class = OESDKMinGWTestContextExecutor
+
diff --git a/lib/oeqa/sdkmingw/testsdk.py b/lib/oeqa/sdkmingw/testsdk.py
new file mode 100644
index 0000000..85fe3c6
--- /dev/null
+++ b/lib/oeqa/sdkmingw/testsdk.py
@@ -0,0 +1,42 @@
+# Copyright 2018 by Garmin Ltd. or its subsidiaries
+# Released under the MIT license (see COPYING.MIT)
+
+from oeqa.sdk.testsdk import TestSDK
+from oeqa.sdkmingw.context import OESDKMinGWTestContext, OESDKMinGWTestContextExecutor
+
+class TestSDKMinGW(TestSDK):
+ context_executor_class = OESDKMinGWTestContextExecutor
+ context_class = OESDKMinGWTestContext
+
+ def get_tcname(self, d):
+ """
+ Get the name of the SDK file
+ """
+ return d.expand("${SDK_DEPLOY}/${TOOLCHAIN_OUTPUTNAME}.tar.xz")
+
+ def extract_sdk(self, tcname, sdk_dir, d):
+ """
+ Extract the SDK to the specified location
+ """
+ import subprocess
+
+ try:
+ # TODO: It would be nice to try and extract the SDK in Wine to make
+ # sure it is well formed
+ subprocess.check_output(['tar', '-xf', tcname, '-C', sdk_dir])
+ 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
+ """
+ wine_prefix = d.getVar('TESTSDK_WINEPREFIX') or d.expand('${WORKDIR}/testimage-wine/')
+ bb.utils.remove(wine_prefix, True)
+
+ return {
+ 'wine_prefix': wine_prefix,
+ 'wine_arch': d.getVar('TESTSDK_WINEARCH') or 'win64'
+ }
+
--
2.19.1
next prev parent reply other threads:[~2018-11-19 21:19 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-11-15 19:23 [meta-mingw][PATCH 0/4] Add oeqa test cases Joshua Watt
2018-11-16 11:50 ` Burton, Ross
2018-11-16 12:12 ` Joshua Watt
2018-11-19 21:17 ` [meta-mingw][PATCH v2 0/5] " Joshua Watt
2018-11-19 21:17 ` Joshua Watt [this message]
2018-11-19 21:17 ` [meta-mingw][PATCH v2 2/5] Add .gitignore Joshua Watt
2018-11-19 21:17 ` [meta-mingw][PATCH v2 3/5] classes/toolchain-scripts-mingw32: Update variables Joshua Watt
2018-11-19 21:17 ` [meta-mingw][PATCH v2 4/5] classes/toolchain-scripts-mingw32: Ensure exit code is success Joshua Watt
2018-11-19 21:17 ` [meta-mingw][PATCH v2 5/5] oeqa/sdkmingw: Add test cases Joshua Watt
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20181119211752.18963-2-JPEWhacker@gmail.com \
--to=jpewhacker@gmail.com \
--cc=yocto@yoctoproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.