summaryrefslogtreecommitdiff
path: root/poky/meta/lib/oeqa
diff options
context:
space:
mode:
Diffstat (limited to 'poky/meta/lib/oeqa')
-rw-r--r--poky/meta/lib/oeqa/core/runner.py2
-rw-r--r--poky/meta/lib/oeqa/core/target/serial.py315
-rw-r--r--poky/meta/lib/oeqa/core/target/ssh.py16
-rw-r--r--poky/meta/lib/oeqa/runtime/cases/parselogs-ignores-mipsarch.txt6
-rw-r--r--poky/meta/lib/oeqa/runtime/cases/parselogs-ignores-qemuall.txt8
-rw-r--r--poky/meta/lib/oeqa/runtime/cases/parselogs.py2
-rw-r--r--poky/meta/lib/oeqa/runtime/cases/scp.py2
-rw-r--r--poky/meta/lib/oeqa/runtime/cases/ssh.py31
-rw-r--r--poky/meta/lib/oeqa/runtime/cases/systemd.py17
-rw-r--r--poky/meta/lib/oeqa/runtime/context.py12
-rw-r--r--poky/meta/lib/oeqa/sdk/cases/autotools.py6
-rw-r--r--poky/meta/lib/oeqa/sdk/cases/cmake.py4
-rw-r--r--poky/meta/lib/oeqa/sdk/cases/gcc.py4
-rw-r--r--poky/meta/lib/oeqa/sdk/cases/gtk3.py4
-rw-r--r--poky/meta/lib/oeqa/sdk/cases/kmod.py41
-rw-r--r--poky/meta/lib/oeqa/sdk/cases/makefile.py6
-rw-r--r--poky/meta/lib/oeqa/sdk/cases/meson.py4
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/bbclasses.py106
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/binutils.py2
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/buildoptions.py10
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/cve_check.py51
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/devtool.py4
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/distrodata.py20
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/fitimage.py264
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/gcc.py4
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/imagefeatures.py15
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/incompatible_lic.py6
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/locales.py2
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/meta_ide.py4
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/oescripts.py2
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/overlayfs.py41
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/package.py26
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/picolibc.py18
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/recipetool.py1
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/reproducible.py21
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/retain.py241
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/runtime_test.py5
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/rust.py113
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/spdx.py133
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/sstatetests.py24
-rw-r--r--poky/meta/lib/oeqa/selftest/context.py5
-rw-r--r--poky/meta/lib/oeqa/utils/__init__.py8
-rw-r--r--poky/meta/lib/oeqa/utils/gitarchive.py4
-rw-r--r--poky/meta/lib/oeqa/utils/postactions.py69
-rw-r--r--poky/meta/lib/oeqa/utils/qemurunner.py11
45 files changed, 1319 insertions, 371 deletions
diff --git a/poky/meta/lib/oeqa/core/runner.py b/poky/meta/lib/oeqa/core/runner.py
index a86a706bd9..b683d9b80a 100644
--- a/poky/meta/lib/oeqa/core/runner.py
+++ b/poky/meta/lib/oeqa/core/runner.py
@@ -357,7 +357,7 @@ class OETestResultJSONHelper(object):
os.makedirs(write_dir, exist_ok=True)
test_results = self._get_existing_testresults_if_available(write_dir)
test_results[result_id] = {'configuration': configuration, 'result': test_result}
- json_testresults = json.dumps(test_results, sort_keys=True, indent=4)
+ json_testresults = json.dumps(test_results, sort_keys=True, indent=1)
self._write_file(write_dir, self.testresult_filename, json_testresults)
if has_bb:
bb.utils.unlockfile(lf)
diff --git a/poky/meta/lib/oeqa/core/target/serial.py b/poky/meta/lib/oeqa/core/target/serial.py
new file mode 100644
index 0000000000..7c2cd8b248
--- /dev/null
+++ b/poky/meta/lib/oeqa/core/target/serial.py
@@ -0,0 +1,315 @@
+#
+# SPDX-License-Identifier: MIT
+#
+
+import base64
+import logging
+import os
+from threading import Lock
+from . import OETarget
+
+class OESerialTarget(OETarget):
+
+ def __init__(self, logger, target_ip, server_ip, server_port=0,
+ timeout=300, serialcontrol_cmd=None, serialcontrol_extra_args=None,
+ serialcontrol_ps1=None, serialcontrol_connect_timeout=None,
+ machine=None, **kwargs):
+ if not logger:
+ logger = logging.getLogger('target')
+ logger.setLevel(logging.INFO)
+ filePath = os.path.join(os.getcwd(), 'remoteTarget.log')
+ fileHandler = logging.FileHandler(filePath, 'w', 'utf-8')
+ formatter = logging.Formatter(
+ '%(asctime)s.%(msecs)03d %(levelname)s: %(message)s',
+ '%H:%M:%S')
+ fileHandler.setFormatter(formatter)
+ logger.addHandler(fileHandler)
+
+ super(OESerialTarget, self).__init__(logger)
+
+ if serialcontrol_ps1:
+ self.target_ps1 = serialcontrol_ps1
+ elif machine:
+ # fallback to a default value which assumes root@machine
+ self.target_ps1 = f'root@{machine}:.*# '
+ else:
+ raise ValueError("Unable to determine shell command prompt (PS1) format.")
+
+ if not serialcontrol_cmd:
+ raise ValueError("Unable to determine serial control command.")
+
+ if serialcontrol_extra_args:
+ self.connection_script = f'{serialcontrol_cmd} {serialcontrol_extra_args}'
+ else:
+ self.connection_script = serialcontrol_cmd
+
+ if serialcontrol_connect_timeout:
+ self.connect_timeout = serialcontrol_connect_timeout
+ else:
+ self.connect_timeout = 10 # default to 10s connection timeout
+
+ self.default_command_timeout = timeout
+ self.ip = target_ip
+ self.server_ip = server_ip
+ self.server_port = server_port
+ self.conn = None
+ self.mutex = Lock()
+
+ def start(self, **kwargs):
+ pass
+
+ def stop(self, **kwargs):
+ pass
+
+ def get_connection(self):
+ if self.conn is None:
+ self.conn = SerialConnection(self.connection_script,
+ self.target_ps1,
+ self.connect_timeout,
+ self.default_command_timeout)
+
+ return self.conn
+
+ def run(self, cmd, timeout=None):
+ """
+ Runs command on target over the provided serial connection.
+ The first call will open the connection, and subsequent
+ calls will re-use the same connection to send new commands.
+
+ command: Command to run on target.
+ timeout: <value>: Kill command after <val> seconds.
+ None: Kill command default value seconds.
+ 0: No timeout, runs until return.
+ """
+ # Lock needed to avoid multiple threads running commands concurrently
+ # A serial connection can only be used by one caller at a time
+ with self.mutex:
+ conn = self.get_connection()
+
+ self.logger.debug(f"[Running]$ {cmd}")
+ # Run the command, then echo $? to get the command's return code
+ try:
+ output = conn.run_command(cmd, timeout)
+ status = conn.run_command("echo $?")
+ self.logger.debug(f" [stdout]: {output}")
+ self.logger.debug(f" [ret code]: {status}\n\n")
+ except SerialTimeoutException as e:
+ self.logger.debug(e)
+ output = ""
+ status = 255
+
+ # Return to $HOME after each command to simulate a stateless SSH connection
+ conn.run_command('cd "$HOME"')
+
+ return (int(status), output)
+
+ def copyTo(self, localSrc, remoteDst):
+ """
+ Copies files by converting them to base 32, then transferring
+ the ASCII text to the target, and decoding it in place on the
+ target.
+
+ On a 115k baud serial connection, this method transfers at
+ roughly 30kbps.
+ """
+ with open(localSrc, 'rb') as file:
+ data = file.read()
+
+ b32 = base64.b32encode(data).decode('utf-8')
+
+ # To avoid shell line limits, send a chunk at a time
+ SPLIT_LEN = 512
+ lines = [b32[i:i+SPLIT_LEN] for i in range(0, len(b32), SPLIT_LEN)]
+
+ with self.mutex:
+ conn = self.get_connection()
+
+ filename = os.path.basename(localSrc)
+ TEMP = f'/tmp/{filename}.b32'
+
+ # Create or empty out the temp file
+ conn.run_command(f'echo -n "" > {TEMP}')
+
+ for line in lines:
+ conn.run_command(f'echo -n {line} >> {TEMP}')
+
+ # Check to see whether the remoteDst is a directory
+ is_directory = conn.run_command(f'[[ -d {remoteDst} ]]; echo $?')
+ if int(is_directory) == 0:
+ # append the localSrc filename to the end of remoteDst
+ remoteDst = os.path.join(remoteDst, filename)
+
+ conn.run_command(f'base32 -d {TEMP} > {remoteDst}')
+ conn.run_command(f'rm {TEMP}')
+
+ return 0, 'Success'
+
+ def copyFrom(self, remoteSrc, localDst):
+ """
+ Copies files by converting them to base 32 on the target, then
+ transferring the ASCII text to the host. That text is then
+ decoded here and written out to the destination.
+
+ On a 115k baud serial connection, this method transfers at
+ roughly 30kbps.
+ """
+ with self.mutex:
+ b32 = self.get_connection().run_command(f'base32 {remoteSrc}')
+
+ data = base64.b32decode(b32.replace('\r\n', ''))
+
+ # If the local path is a directory, get the filename from
+ # the remoteSrc path and append it to localDst
+ if os.path.isdir(localDst):
+ filename = os.path.basename(remoteSrc)
+ localDst = os.path.join(localDst, filename)
+
+ with open(localDst, 'wb') as file:
+ file.write(data)
+
+ return 0, 'Success'
+
+ def copyDirTo(self, localSrc, remoteDst):
+ """
+ Copy recursively localSrc directory to remoteDst in target.
+ """
+
+ for root, dirs, files in os.walk(localSrc):
+ # Create directories in the target as needed
+ for d in dirs:
+ tmpDir = os.path.join(root, d).replace(localSrc, "")
+ newDir = os.path.join(remoteDst, tmpDir.lstrip("/"))
+ cmd = "mkdir -p %s" % newDir
+ self.run(cmd)
+
+ # Copy files into the target
+ for f in files:
+ tmpFile = os.path.join(root, f).replace(localSrc, "")
+ dstFile = os.path.join(remoteDst, tmpFile.lstrip("/"))
+ srcFile = os.path.join(root, f)
+ self.copyTo(srcFile, dstFile)
+
+ def deleteFiles(self, remotePath, files):
+ """
+ Deletes files in target's remotePath.
+ """
+
+ cmd = "rm"
+ if not isinstance(files, list):
+ files = [files]
+
+ for f in files:
+ cmd = "%s %s" % (cmd, os.path.join(remotePath, f))
+
+ self.run(cmd)
+
+ def deleteDir(self, remotePath):
+ """
+ Deletes target's remotePath directory.
+ """
+
+ cmd = "rmdir %s" % remotePath
+ self.run(cmd)
+
+ def deleteDirStructure(self, localPath, remotePath):
+ """
+ Delete recursively localPath structure directory in target's remotePath.
+
+ This function is useful to delete a package that is installed in the
+ device under test (DUT) and the host running the test has such package
+ extracted in tmp directory.
+
+ Example:
+ pwd: /home/user/tmp
+ tree: .
+ └── work
+ ├── dir1
+ │   └── file1
+ └── dir2
+
+ localpath = "/home/user/tmp" and remotepath = "/home/user"
+
+ With the above variables this function will try to delete the
+ directory in the DUT in this order:
+ /home/user/work/dir1/file1
+ /home/user/work/dir1 (if dir is empty)
+ /home/user/work/dir2 (if dir is empty)
+ /home/user/work (if dir is empty)
+ """
+
+ for root, dirs, files in os.walk(localPath, topdown=False):
+ # Delete files first
+ tmpDir = os.path.join(root).replace(localPath, "")
+ remoteDir = os.path.join(remotePath, tmpDir.lstrip("/"))
+ self.deleteFiles(remoteDir, files)
+
+ # Remove dirs if empty
+ for d in dirs:
+ tmpDir = os.path.join(root, d).replace(localPath, "")
+ remoteDir = os.path.join(remotePath, tmpDir.lstrip("/"))
+ self.deleteDir(remoteDir)
+
+class SerialTimeoutException(Exception):
+ def __init__(self, msg):
+ self.msg = msg
+ def __str__(self):
+ return self.msg
+
+class SerialConnection:
+
+ def __init__(self, script, target_prompt, connect_timeout, default_command_timeout):
+ import pexpect # limiting scope to avoid build dependency
+ self.prompt = target_prompt
+ self.connect_timeout = connect_timeout
+ self.default_command_timeout = default_command_timeout
+ self.conn = pexpect.spawn('/bin/bash', ['-c', script], encoding='utf8')
+ self._seek_to_clean_shell()
+ # Disable echo to avoid the need to parse the outgoing command
+ self.run_command('stty -echo')
+
+ def _seek_to_clean_shell(self):
+ """
+ Attempts to find a clean shell, meaning it is clear and
+ ready to accept a new command. This is necessary to ensure
+ the correct output is captured from each command.
+ """
+ import pexpect # limiting scope to avoid build dependency
+ # Look for a clean shell
+ # Wait a short amount of time for the connection to finish
+ pexpect_code = self.conn.expect([self.prompt, pexpect.TIMEOUT],
+ timeout=self.connect_timeout)
+
+ # if a timeout occurred, send an empty line and wait for a clean shell
+ if pexpect_code == 1:
+ # send a newline to clear and present the shell
+ self.conn.sendline("")
+ pexpect_code = self.conn.expect(self.prompt)
+
+ def run_command(self, cmd, timeout=None):
+ """
+ Runs command on target over the provided serial connection.
+ Returns any output on the shell while the command was run.
+
+ command: Command to run on target.
+ timeout: <value>: Kill command after <val> seconds.
+ None: Kill command default value seconds.
+ 0: No timeout, runs until return.
+ """
+ import pexpect # limiting scope to avoid build dependency
+ # Convert from the OETarget defaults to pexpect timeout values
+ if timeout is None:
+ timeout = self.default_command_timeout
+ elif timeout == 0:
+ timeout = None # passing None to pexpect is infinite timeout
+
+ self.conn.sendline(cmd)
+ pexpect_code = self.conn.expect([self.prompt, pexpect.TIMEOUT], timeout=timeout)
+
+ # check for timeout
+ if pexpect_code == 1:
+ self.conn.send('\003') # send Ctrl+C
+ self._seek_to_clean_shell()
+ raise SerialTimeoutException(f'Timeout executing: {cmd} after {timeout}s')
+
+ return self.conn.before.removesuffix('\r\n')
+
diff --git a/poky/meta/lib/oeqa/core/target/ssh.py b/poky/meta/lib/oeqa/core/target/ssh.py
index 09cdd14c75..d473469384 100644
--- a/poky/meta/lib/oeqa/core/target/ssh.py
+++ b/poky/meta/lib/oeqa/core/target/ssh.py
@@ -55,14 +55,14 @@ class OESSHTarget(OETarget):
def stop(self, **kwargs):
pass
- def _run(self, command, timeout=None, ignore_status=True):
+ def _run(self, command, timeout=None, ignore_status=True, raw=False):
"""
Runs command in target using SSHProcess.
"""
self.logger.debug("[Running]$ %s" % " ".join(command))
starttime = time.time()
- status, output = SSHCall(command, self.logger, timeout)
+ status, output = SSHCall(command, self.logger, timeout, raw)
self.logger.debug("[Command returned '%d' after %.2f seconds]"
"" % (status, time.time() - starttime))
@@ -72,7 +72,7 @@ class OESSHTarget(OETarget):
return (status, output)
- def run(self, command, timeout=None, ignore_status=True):
+ def run(self, command, timeout=None, ignore_status=True, raw=False):
"""
Runs command in target.
@@ -91,7 +91,7 @@ class OESSHTarget(OETarget):
else:
processTimeout = self.timeout
- status, output = self._run(sshCmd, processTimeout, ignore_status)
+ status, output = self._run(sshCmd, processTimeout, ignore_status, raw)
self.logger.debug('Command: %s\nStatus: %d Output: %s\n' % (command, status, output))
return (status, output)
@@ -206,7 +206,7 @@ class OESSHTarget(OETarget):
remoteDir = os.path.join(remotePath, tmpDir.lstrip("/"))
self.deleteDir(remoteDir)
-def SSHCall(command, logger, timeout=None, **opts):
+def SSHCall(command, logger, timeout=None, raw=False, **opts):
def run():
nonlocal output
@@ -265,7 +265,7 @@ def SSHCall(command, logger, timeout=None, **opts):
else:
output_raw = process.communicate()[0]
- output = output_raw.decode('utf-8', errors='ignore')
+ output = output_raw if raw else output_raw.decode('utf-8', errors='ignore')
logger.debug('Data from SSH call:\n%s' % output.rstrip())
# timout or not, make sure process exits and is not hanging
@@ -292,7 +292,7 @@ def SSHCall(command, logger, timeout=None, **opts):
options = {
"stdout": subprocess.PIPE,
- "stderr": subprocess.STDOUT,
+ "stderr": subprocess.STDOUT if not raw else None,
"stdin": None,
"shell": False,
"bufsize": -1,
@@ -320,4 +320,4 @@ def SSHCall(command, logger, timeout=None, **opts):
logger.debug('Something went wrong, killing SSH process')
raise
- return (process.returncode, output.rstrip())
+ return (process.returncode, output if raw else output.rstrip())
diff --git a/poky/meta/lib/oeqa/runtime/cases/parselogs-ignores-mipsarch.txt b/poky/meta/lib/oeqa/runtime/cases/parselogs-ignores-mipsarch.txt
index 2c0bd9a247..9c2677c4cf 100644
--- a/poky/meta/lib/oeqa/runtime/cases/parselogs-ignores-mipsarch.txt
+++ b/poky/meta/lib/oeqa/runtime/cases/parselogs-ignores-mipsarch.txt
@@ -1,2 +1,8 @@
# These should be reviewed to see if they are still needed
cacheinfo: Failed to find cpu0 device node
+
+# 6.10 restructures sysctl registration such that mips
+# registers an empty table and generates harmless warnings:
+# failed when register_sysctl_sz sched_fair_sysctls to kernel
+# failed when register_sysctl_sz sched_core_sysctls to kernel
+failed when register_sysctl_sz sched
diff --git a/poky/meta/lib/oeqa/runtime/cases/parselogs-ignores-qemuall.txt b/poky/meta/lib/oeqa/runtime/cases/parselogs-ignores-qemuall.txt
index b0c0fc9ddf..143db40d63 100644
--- a/poky/meta/lib/oeqa/runtime/cases/parselogs-ignores-qemuall.txt
+++ b/poky/meta/lib/oeqa/runtime/cases/parselogs-ignores-qemuall.txt
@@ -13,6 +13,14 @@ FBIOPUT_VSCREENINFO failed, double buffering disabled
# pci 0000:00:00.0: [Firmware Bug]: reg 0x20: invalid BAR (can't size)
# pci 0000:00:00.0: [Firmware Bug]: reg 0x24: invalid BAR (can't size)
invalid BAR (can't size)
+# 6.10+ the invalid BAR warnings are of this format:
+# pci 0000:00:00.0: [Firmware Bug]: BAR 0: invalid; can't size
+# pci 0000:00:00.0: [Firmware Bug]: BAR 1: invalid; can't size
+# pci 0000:00:00.0: [Firmware Bug]: BAR 2: invalid; can't size
+# pci 0000:00:00.0: [Firmware Bug]: BAR 3: invalid; can't size
+# pci 0000:00:00.0: [Firmware Bug]: BAR 4: invalid; can't size
+# pci 0000:00:00.0: [Firmware Bug]: BAR 5: invalid; can't size
+invalid; can't size
# These should be reviewed to see if they are still needed
wrong ELF class
diff --git a/poky/meta/lib/oeqa/runtime/cases/parselogs.py b/poky/meta/lib/oeqa/runtime/cases/parselogs.py
index 6966923c94..47c77fccd5 100644
--- a/poky/meta/lib/oeqa/runtime/cases/parselogs.py
+++ b/poky/meta/lib/oeqa/runtime/cases/parselogs.py
@@ -34,7 +34,7 @@ class ParseLogsTest(OERuntimeTestCase):
log_locations = ["/var/log/", "/var/log/dmesg", "/tmp/dmesg_output.log"]
# The keywords that identify error messages in the log files
- errors = ["error", "cannot", "can't", "failed"]
+ errors = ["error", "cannot", "can't", "failed", "---[ cut here ]---", "No irq handler for vector"]
# A list of error messages that should be ignored
ignore_errors = []
diff --git a/poky/meta/lib/oeqa/runtime/cases/scp.py b/poky/meta/lib/oeqa/runtime/cases/scp.py
index ee97b8ef66..364264369a 100644
--- a/poky/meta/lib/oeqa/runtime/cases/scp.py
+++ b/poky/meta/lib/oeqa/runtime/cases/scp.py
@@ -25,7 +25,7 @@ class ScpTest(OERuntimeTestCase):
os.remove(cls.tmp_path)
@OETestDepends(['ssh.SSHTest.test_ssh'])
- @OEHasPackage(['openssh-scp'])
+ @OEHasPackage({'openssh-scp', 'openssh-sftp-server'})
def test_scp_file(self):
dst = '/tmp/test_scp_file'
diff --git a/poky/meta/lib/oeqa/runtime/cases/ssh.py b/poky/meta/lib/oeqa/runtime/cases/ssh.py
index cdbef59500..89d64430e5 100644
--- a/poky/meta/lib/oeqa/runtime/cases/ssh.py
+++ b/poky/meta/lib/oeqa/runtime/cases/ssh.py
@@ -4,6 +4,9 @@
# SPDX-License-Identifier: MIT
#
+import time
+import signal
+
from oeqa.runtime.case import OERuntimeTestCase
from oeqa.core.decorator.depends import OETestDepends
from oeqa.runtime.decorator.package import OEHasPackage
@@ -13,12 +16,22 @@ class SSHTest(OERuntimeTestCase):
@OETestDepends(['ping.PingTest.test_ping'])
@OEHasPackage(['dropbear', 'openssh-sshd'])
def test_ssh(self):
- (status, output) = self.target.run('sleep 20', timeout=2)
- msg='run() timed out but return code was zero.'
- self.assertNotEqual(status, 0, msg=msg)
- (status, output) = self.target.run('uname -a')
- self.assertEqual(status, 0, msg='SSH Test failed: %s' % output)
- (status, output) = self.target.run('cat /etc/controllerimage')
- msg = "This isn't the right image - /etc/controllerimage " \
- "shouldn't be here %s" % output
- self.assertEqual(status, 1, msg=msg)
+ for i in range(5):
+ status, output = self.target.run("uname -a", timeout=30)
+ if status == 0:
+ break
+ elif status == 255 or status == -signal.SIGTERM:
+ # ssh returns 255 only if a ssh error occurs. This could
+ # be an issue with "Connection refused" because the port
+ # isn't open yet, and this could check explicitly for that
+ # here. However, let's keep it simple and just retry for
+ # all errors a limited amount of times with a sleep to
+ # give it time for the port to open.
+ # We sometimes see -15 (SIGTERM) on slow emulation machines too, likely
+ # from boot/init not being 100% complete, retry for these too.
+ time.sleep(5)
+ continue
+ else:
+ self.fail("uname failed with \"%s\" (exit code %s)" % (output, status))
+ if status != 0:
+ self.fail("ssh failed with \"%s\" (exit code %s)" % (output, status))
diff --git a/poky/meta/lib/oeqa/runtime/cases/systemd.py b/poky/meta/lib/oeqa/runtime/cases/systemd.py
index 80fdae240a..640f28abe9 100644
--- a/poky/meta/lib/oeqa/runtime/cases/systemd.py
+++ b/poky/meta/lib/oeqa/runtime/cases/systemd.py
@@ -150,12 +150,21 @@ class SystemdServiceTests(SystemdTest):
t_thread.start()
time.sleep(1)
- status, output = self.target.run('pidof sleep')
+ status, sleep_pid = self.target.run('pidof sleep')
# cause segfault on purpose
- self.target.run('kill -SEGV %s' % output)
- self.assertEqual(status, 0, msg = 'Not able to find process that runs sleep, output : %s' % output)
+ self.target.run('kill -SEGV %s' % sleep_pid)
+ self.assertEqual(status, 0, msg = 'Not able to find process that runs sleep, output : %s' % sleep_pid)
- (status, output) = self.target.run('coredumpctl info')
+ # Give some time to systemd-coredump@.service to process the coredump
+ for x in range(20):
+ status, output = self.target.run('coredumpctl list %s' % sleep_pid)
+ if status == 0:
+ break
+ time.sleep(1)
+ else:
+ self.fail("Timed out waiting for coredump creation")
+
+ (status, output) = self.target.run('coredumpctl info %s' % sleep_pid)
self.assertEqual(status, 0, msg='MiniDebugInfo Test failed: %s' % output)
self.assertEqual('sleep_for_duration (busybox.nosuid' in output or 'xnanosleep (sleep.coreutils' in output,
True, msg='Call stack is missing minidebuginfo symbols (functions shown as "n/a"): %s' % output)
diff --git a/poky/meta/lib/oeqa/runtime/context.py b/poky/meta/lib/oeqa/runtime/context.py
index cb7227a8df..daabc44910 100644
--- a/poky/meta/lib/oeqa/runtime/context.py
+++ b/poky/meta/lib/oeqa/runtime/context.py
@@ -8,6 +8,7 @@ import os
import sys
from oeqa.core.context import OETestContext, OETestContextExecutor
+from oeqa.core.target.serial import OESerialTarget
from oeqa.core.target.ssh import OESSHTarget
from oeqa.core.target.qemu import OEQemuTarget
@@ -60,7 +61,7 @@ class OERuntimeTestContextExecutor(OETestContextExecutor):
runtime_group = self.parser.add_argument_group('runtime options')
runtime_group.add_argument('--target-type', action='store',
- default=self.default_target_type, choices=['simpleremote', 'qemu'],
+ default=self.default_target_type, choices=['simpleremote', 'qemu', 'serial'],
help="Target type of device under test, default: %s" \
% self.default_target_type)
runtime_group.add_argument('--target-ip', action='store',
@@ -108,6 +109,8 @@ class OERuntimeTestContextExecutor(OETestContextExecutor):
target = OESSHTarget(logger, target_ip, server_ip, **kwargs)
elif target_type == 'qemu':
target = OEQemuTarget(logger, server_ip, **kwargs)
+ elif target_type == 'serial':
+ target = OESerialTarget(logger, target_ip, server_ip, **kwargs)
else:
# XXX: This code uses the old naming convention for controllers and
# targets, the idea it is to leave just targets as the controller
@@ -203,8 +206,15 @@ class OERuntimeTestContextExecutor(OETestContextExecutor):
super(OERuntimeTestContextExecutor, self)._process_args(logger, args)
+ td = self.tc_kwargs['init']['td']
+
target_kwargs = {}
+ target_kwargs['machine'] = td.get("MACHINE") or None
target_kwargs['qemuboot'] = args.qemu_boot
+ target_kwargs['serialcontrol_cmd'] = td.get("TEST_SERIALCONTROL_CMD") or None
+ target_kwargs['serialcontrol_extra_args'] = td.get("TEST_SERIALCONTROL_EXTRA_ARGS") or ""
+ target_kwargs['serialcontrol_ps1'] = td.get("TEST_SERIALCONTROL_PS1") or None
+ target_kwargs['serialcontrol_connect_timeout'] = td.get("TEST_SERIALCONTROL_CONNECT_TIMEOUT") or None
self.tc_kwargs['init']['target'] = \
OERuntimeTestContextExecutor.getTarget(args.target_type,
diff --git a/poky/meta/lib/oeqa/sdk/cases/autotools.py b/poky/meta/lib/oeqa/sdk/cases/autotools.py
index 848e9392ec..4bac28f04d 100644
--- a/poky/meta/lib/oeqa/sdk/cases/autotools.py
+++ b/poky/meta/lib/oeqa/sdk/cases/autotools.py
@@ -7,6 +7,7 @@
import os
import tempfile
import subprocess
+import unittest
from oeqa.sdk.case import OESDKTestCase
from oeqa.utils.subprocesstweak import errors_have_output
@@ -16,6 +17,11 @@ class AutotoolsTest(OESDKTestCase):
"""
Check that autotools will cross-compile correctly.
"""
+ def setUp(self):
+ libc = self.td.get("TCLIBC")
+ if libc in [ 'newlib' ]:
+ raise unittest.SkipTest("AutotoolsTest class: SDK doesn't contain a supported C library")
+
def test_cpio(self):
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")
diff --git a/poky/meta/lib/oeqa/sdk/cases/cmake.py b/poky/meta/lib/oeqa/sdk/cases/cmake.py
index db7d826a38..cb0944ee99 100644
--- a/poky/meta/lib/oeqa/sdk/cases/cmake.py
+++ b/poky/meta/lib/oeqa/sdk/cases/cmake.py
@@ -19,6 +19,10 @@ class CMakeTest(OESDKTestCase):
"""
def setUp(self):
+ libc = self.td.get("TCLIBC")
+ if libc in [ 'newlib' ]:
+ raise unittest.SkipTest("CMakeTest class: SDK doesn't contain a supported C library")
+
if not (self.tc.hasHostPackage("nativesdk-cmake") or
self.tc.hasHostPackage("cmake-native")):
raise unittest.SkipTest("CMakeTest: needs cmake")
diff --git a/poky/meta/lib/oeqa/sdk/cases/gcc.py b/poky/meta/lib/oeqa/sdk/cases/gcc.py
index fc28b9c3d4..e810d2c42b 100644
--- a/poky/meta/lib/oeqa/sdk/cases/gcc.py
+++ b/poky/meta/lib/oeqa/sdk/cases/gcc.py
@@ -26,6 +26,10 @@ class GccCompileTest(OESDKTestCase):
os.path.join(self.tc.sdk_dir, f))
def setUp(self):
+ libc = self.td.get("TCLIBC")
+ if libc in [ 'newlib' ]:
+ raise unittest.SkipTest("GccCompileTest class: SDK doesn't contain a supported C library")
+
machine = self.td.get("MACHINE")
if not (self.tc.hasHostPackage("packagegroup-cross-canadian-%s" % machine) or
self.tc.hasHostPackage("^gcc-", regex=True)):
diff --git a/poky/meta/lib/oeqa/sdk/cases/gtk3.py b/poky/meta/lib/oeqa/sdk/cases/gtk3.py
index c329c4bb86..8f60d5e7da 100644
--- a/poky/meta/lib/oeqa/sdk/cases/gtk3.py
+++ b/poky/meta/lib/oeqa/sdk/cases/gtk3.py
@@ -18,6 +18,10 @@ class GTK3Test(OESDKTestCase):
Test that autotools and GTK+ 3 compiles correctly.
"""
def setUp(self):
+ libc = self.td.get("TCLIBC")
+ if libc in [ 'newlib' ]:
+ raise unittest.SkipTest("GTK3Test class: SDK doesn't contain a supported C library")
+
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")
diff --git a/poky/meta/lib/oeqa/sdk/cases/kmod.py b/poky/meta/lib/oeqa/sdk/cases/kmod.py
new file mode 100644
index 0000000000..9e8fdbcd40
--- /dev/null
+++ b/poky/meta/lib/oeqa/sdk/cases/kmod.py
@@ -0,0 +1,41 @@
+#
+# 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 KernelModuleTest(OESDKTestCase):
+ """
+ Test that out-of-tree kernel modules build.
+ """
+
+ def setUp(self):
+ if not self.tc.hasTargetPackage("kernel-devsrc"):
+ raise unittest.SkipTest("KernelModuleTest needs kernel-devsrc")
+
+ # These targets need to be built before kernel modules can be built.
+ self._run("make -j -C $OECORE_TARGET_SYSROOT/usr/src/kernel prepare scripts")
+
+
+ def test_cryptodev(self):
+ with tempfile.TemporaryDirectory(prefix="cryptodev", dir=self.tc.sdk_dir) as testdir:
+ git_url = "https://github.com/cryptodev-linux/cryptodev-linux"
+ # This is a knnown-good commit post-1.13 that builds with kernel 6.7+
+ git_sha = "bb8bc7cf60d2c0b097c8b3b0e807f805b577a53f"
+
+ sourcedir = os.path.join(testdir, "cryptodev-linux")
+ subprocess.check_output(["git", "clone", git_url, sourcedir], stderr=subprocess.STDOUT)
+ self.assertTrue(os.path.isdir(sourcedir))
+ subprocess.check_output(["git", "-C", sourcedir, "checkout", git_sha], stderr=subprocess.STDOUT)
+
+ self._run("make -C %s V=1 KERNEL_DIR=$OECORE_TARGET_SYSROOT/usr/src/kernel" % sourcedir)
+ self.check_elf(os.path.join(sourcedir, "cryptodev.ko"))
diff --git a/poky/meta/lib/oeqa/sdk/cases/makefile.py b/poky/meta/lib/oeqa/sdk/cases/makefile.py
index 2ff54ce25f..e1e2484820 100644
--- a/poky/meta/lib/oeqa/sdk/cases/makefile.py
+++ b/poky/meta/lib/oeqa/sdk/cases/makefile.py
@@ -5,6 +5,7 @@
#
import os, tempfile, subprocess
+import unittest
from oeqa.sdk.case import OESDKTestCase
from oeqa.utils.subprocesstweak import errors_have_output
errors_have_output()
@@ -13,6 +14,11 @@ class MakefileTest(OESDKTestCase):
"""
Test that "plain" compilation works, using just $CC $CFLAGS etc.
"""
+ def setUp(self):
+ libc = self.td.get("TCLIBC")
+ if libc in [ 'newlib' ]:
+ raise unittest.SkipTest("MakefileTest class: SDK doesn't contain a supported C library")
+
def test_lzip(self):
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")
diff --git a/poky/meta/lib/oeqa/sdk/cases/meson.py b/poky/meta/lib/oeqa/sdk/cases/meson.py
index be53df204a..1edf78720a 100644
--- a/poky/meta/lib/oeqa/sdk/cases/meson.py
+++ b/poky/meta/lib/oeqa/sdk/cases/meson.py
@@ -18,6 +18,10 @@ class MesonTest(OESDKTestCase):
Test that Meson builds correctly.
"""
def setUp(self):
+ libc = self.td.get("TCLIBC")
+ if libc in [ 'newlib' ]:
+ raise unittest.SkipTest("MesonTest class: SDK doesn't contain a supported C library")
+
if not (self.tc.hasHostPackage("nativesdk-meson") or
self.tc.hasHostPackage("meson-native")):
raise unittest.SkipTest("MesonTest: needs meson")
diff --git a/poky/meta/lib/oeqa/selftest/cases/bbclasses.py b/poky/meta/lib/oeqa/selftest/cases/bbclasses.py
new file mode 100644
index 0000000000..10545ebe65
--- /dev/null
+++ b/poky/meta/lib/oeqa/selftest/cases/bbclasses.py
@@ -0,0 +1,106 @@
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import get_bb_vars, bitbake
+
+class Systemd(OESelftestTestCase):
+ """
+ Tests related to the systemd bbclass.
+ """
+
+ def getVars(self, recipe):
+ self.bb_vars = get_bb_vars(
+ [
+ 'BPN',
+ 'D',
+ 'INIT_D_DIR',
+ 'prefix',
+ 'systemd_system_unitdir',
+ 'sysconfdir',
+ ],
+ recipe,
+ )
+
+ def fileExists(self, filename):
+ self.assertExists(filename.format(**self.bb_vars))
+
+ def fileNotExists(self, filename):
+ self.assertNotExists(filename.format(**self.bb_vars))
+
+ def test_systemd_in_distro(self):
+ """
+ Summary: Verify that no sysvinit files are installed when the
+ systemd distro feature is enabled, but sysvinit is not.
+ Expected: Systemd service file exists, but /etc does not.
+ Product: OE-Core
+ Author: Peter Kjellerstedt <peter.kjellerstedt@axis.com>
+ """
+
+ self.write_config("""
+DISTRO_FEATURES:append = " systemd usrmerge"
+DISTRO_FEATURES:remove = "sysvinit"
+VIRTUAL-RUNTIME_init_manager = "systemd"
+""")
+ bitbake("systemd-only systemd-and-sysvinit -c install")
+
+ self.getVars("systemd-only")
+ self.fileExists("{D}{systemd_system_unitdir}/{BPN}.service")
+
+ self.getVars("systemd-and-sysvinit")
+ self.fileExists("{D}{systemd_system_unitdir}/{BPN}.service")
+ self.fileNotExists("{D}{sysconfdir}")
+
+ def test_systemd_and_sysvinit_in_distro(self):
+ """
+ Summary: Verify that both systemd and sysvinit files are installed
+ when both the systemd and sysvinit distro features are
+ enabled.
+ Expected: Systemd service file and sysvinit initscript exist.
+ Product: OE-Core
+ Author: Peter Kjellerstedt <peter.kjellerstedt@axis.com>
+ """
+
+ self.write_config("""
+DISTRO_FEATURES:append = " systemd sysvinit usrmerge"
+VIRTUAL-RUNTIME_init_manager = "systemd"
+""")
+ bitbake("systemd-only systemd-and-sysvinit -c install")
+
+ self.getVars("systemd-only")
+ self.fileExists("{D}{systemd_system_unitdir}/{BPN}.service")
+
+ self.getVars("systemd-and-sysvinit")
+ self.fileExists("{D}{systemd_system_unitdir}/{BPN}.service")
+ self.fileExists("{D}{INIT_D_DIR}/{BPN}")
+
+ def test_sysvinit_in_distro(self):
+ """
+ Summary: Verify that no systemd service files are installed when the
+ sysvinit distro feature is enabled, but systemd is not.
+ Expected: The systemd service file does not exist, nor does /usr.
+ The sysvinit initscript exists.
+ Product: OE-Core
+ Author: Peter Kjellerstedt <peter.kjellerstedt@axis.com>
+ """
+
+ self.write_config("""
+DISTRO_FEATURES:remove = "systemd"
+DISTRO_FEATURES:append = " sysvinit usrmerge"
+VIRTUAL-RUNTIME_init_manager = "sysvinit"
+""")
+ bitbake("systemd-only systemd-and-sysvinit -c install")
+
+ self.getVars("systemd-only")
+ self.fileNotExists("{D}{systemd_system_unitdir}/{BPN}.service")
+ self.fileNotExists("{D}{prefix}")
+ self.fileNotExists("{D}{sysconfdir}")
+ self.fileExists("{D}")
+
+ self.getVars("systemd-and-sysvinit")
+ self.fileNotExists("{D}{systemd_system_unitdir}/{BPN}.service")
+ self.fileNotExists("{D}{prefix}")
+ self.fileExists("{D}{INIT_D_DIR}/{BPN}")
diff --git a/poky/meta/lib/oeqa/selftest/cases/binutils.py b/poky/meta/lib/oeqa/selftest/cases/binutils.py
index 1688eabe4e..5ff263d342 100644
--- a/poky/meta/lib/oeqa/selftest/cases/binutils.py
+++ b/poky/meta/lib/oeqa/selftest/cases/binutils.py
@@ -33,7 +33,7 @@ class BinutilsCrossSelfTest(OESelftestTestCase, OEPTestResultTestCase):
features.append('CHECK_TARGETS = "{0}"'.format(suite))
self.write_config("\n".join(features))
- recipe = "binutils-cross-testsuite"
+ recipe = "binutils-testsuite"
bb_vars = get_bb_vars(["B", "TARGET_SYS", "T"], recipe)
builddir, target_sys, tdir = bb_vars["B"], bb_vars["TARGET_SYS"], bb_vars["T"]
diff --git a/poky/meta/lib/oeqa/selftest/cases/buildoptions.py b/poky/meta/lib/oeqa/selftest/cases/buildoptions.py
index 31dafaa9c5..423c31e189 100644
--- a/poky/meta/lib/oeqa/selftest/cases/buildoptions.py
+++ b/poky/meta/lib/oeqa/selftest/cases/buildoptions.py
@@ -84,7 +84,7 @@ class SanityOptionsTest(OESelftestTestCase):
self.write_config("INHERIT:remove = \"report-error\"")
if "packages-list" not in get_bb_var("ERROR_QA"):
- self.append_config("ERROR_QA:append = \" packages-list\"")
+ self.append_config("ERROR_QA:append:pn-xcursor-transparent-theme = \" packages-list\"")
self.write_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"')
self.add_command_to_tearDown('bitbake -c clean xcursor-transparent-theme')
@@ -94,8 +94,8 @@ class SanityOptionsTest(OESelftestTestCase):
self.assertTrue(line and line.startswith("ERROR:"), msg=res.output)
self.assertEqual(res.status, 1, msg = "bitbake reported exit code %s. It should have been 1. Bitbake output: %s" % (str(res.status), res.output))
self.write_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"')
- self.append_config('ERROR_QA:remove = "packages-list"')
- self.append_config('WARN_QA:append = " packages-list"')
+ self.append_config('ERROR_QA:remove:pn-xcursor-transparent-theme = "packages-list"')
+ self.append_config('WARN_QA:append:pn-xcursor-transparent-theme = " packages-list"')
res = bitbake("xcursor-transparent-theme -f -c package")
self.delete_recipeinc('xcursor-transparent-theme')
line = self.getline(res, "QA Issue: xcursor-transparent-theme-dbg is listed in PACKAGES multiple times, this leads to packaging errors.")
@@ -173,8 +173,8 @@ class BuildhistoryTests(BuildhistoryBase):
data = load_bh(os.path.join(history_dir, 'hicolor-icon-theme-dev', 'latest'))
if 'FILELIST' in data:
- self.assertEqual(data['FILELIST'], '')
- self.assertEqual(int(data['PKGSIZE']), 0)
+ self.assertEqual(data['FILELIST'], '/usr/share/pkgconfig/default-icon-theme.pc')
+ self.assertGreater(int(data['PKGSIZE']), 0)
class ArchiverTest(OESelftestTestCase):
def test_arch_work_dir_and_export_source(self):
diff --git a/poky/meta/lib/oeqa/selftest/cases/cve_check.py b/poky/meta/lib/oeqa/selftest/cases/cve_check.py
index 60cecd1328..3dd3e89d3e 100644
--- a/poky/meta/lib/oeqa/selftest/cases/cve_check.py
+++ b/poky/meta/lib/oeqa/selftest/cases/cve_check.py
@@ -72,6 +72,54 @@ class CVECheck(OESelftestTestCase):
self.assertEqual(convert_cve_version("6.2_rc8"), "6.2-rc8")
self.assertEqual(convert_cve_version("6.2_rc31"), "6.2-rc31")
+ def test_product_match(self):
+ from oe.cve_check import has_cve_product_match
+
+ status = {}
+ status["detail"] = "ignored"
+ status["vendor"] = "*"
+ status["product"] = "*"
+ status["description"] = ""
+ status["mapping"] = ""
+
+ self.assertEqual(has_cve_product_match(status, "some_vendor:some_product"), True)
+ self.assertEqual(has_cve_product_match(status, "*:*"), True)
+ self.assertEqual(has_cve_product_match(status, "some_product"), True)
+ self.assertEqual(has_cve_product_match(status, "glibc"), True)
+ self.assertEqual(has_cve_product_match(status, "glibca"), True)
+ self.assertEqual(has_cve_product_match(status, "aglibc"), True)
+ self.assertEqual(has_cve_product_match(status, "*"), True)
+ self.assertEqual(has_cve_product_match(status, "aglibc glibc test:test"), True)
+
+ status["product"] = "glibc"
+ self.assertEqual(has_cve_product_match(status, "some_vendor:some_product"), False)
+ # The CPE in the recipe must be defined, no * accepted
+ self.assertEqual(has_cve_product_match(status, "*:*"), False)
+ self.assertEqual(has_cve_product_match(status, "*"), False)
+ self.assertEqual(has_cve_product_match(status, "some_product"), False)
+ self.assertEqual(has_cve_product_match(status, "glibc"), True)
+ self.assertEqual(has_cve_product_match(status, "glibca"), False)
+ self.assertEqual(has_cve_product_match(status, "aglibc"), False)
+ self.assertEqual(has_cve_product_match(status, "some_vendor:glibc"), True)
+ self.assertEqual(has_cve_product_match(status, "some_vendor:glibc test"), True)
+ self.assertEqual(has_cve_product_match(status, "test some_vendor:glibc"), True)
+
+ status["vendor"] = "glibca"
+ status["product"] = "glibc"
+ self.assertEqual(has_cve_product_match(status, "some_vendor:some_product"), False)
+ # The CPE in the recipe must be defined, no * accepted
+ self.assertEqual(has_cve_product_match(status, "*:*"), False)
+ self.assertEqual(has_cve_product_match(status, "*"), False)
+ self.assertEqual(has_cve_product_match(status, "some_product"), False)
+ self.assertEqual(has_cve_product_match(status, "glibc"), False)
+ self.assertEqual(has_cve_product_match(status, "glibca"), False)
+ self.assertEqual(has_cve_product_match(status, "aglibc"), False)
+ self.assertEqual(has_cve_product_match(status, "some_vendor:glibc"), False)
+ self.assertEqual(has_cve_product_match(status, "glibca:glibc"), True)
+ self.assertEqual(has_cve_product_match(status, "test:test glibca:glibc"), True)
+ self.assertEqual(has_cve_product_match(status, "test glibca:glibc"), True)
+ self.assertEqual(has_cve_product_match(status, "glibca:glibc test"), True)
+
def test_recipe_report_json(self):
config = """
@@ -217,9 +265,10 @@ CVE_CHECK_REPORT_PATCHED = "1"
# m4 CVE should not be in logrotate
self.assertNotIn("CVE-2008-1687", found_cves)
# logrotate has both Patched and Ignored CVEs
+ detail = "version-not-in-range"
self.assertIn("CVE-2011-1098", found_cves)
self.assertEqual(found_cves["CVE-2011-1098"]["status"], "Patched")
- self.assertEqual(len(found_cves["CVE-2011-1098"]["detail"]), 0)
+ self.assertEqual(found_cves["CVE-2011-1098"]["detail"], detail)
self.assertEqual(len(found_cves["CVE-2011-1098"]["description"]), 0)
detail = "not-applicable-platform"
description = "CVE is debian, gentoo or SUSE specific on the way logrotate was installed/used"
diff --git a/poky/meta/lib/oeqa/selftest/cases/devtool.py b/poky/meta/lib/oeqa/selftest/cases/devtool.py
index 432d9c9a67..8e709944a8 100644
--- a/poky/meta/lib/oeqa/selftest/cases/devtool.py
+++ b/poky/meta/lib/oeqa/selftest/cases/devtool.py
@@ -317,7 +317,7 @@ class DevtoolBase(DevtoolTestCase):
cls.sstate_conf = 'SSTATE_DIR = "%s"\n' % cls.devtool_sstate
cls.sstate_conf += ('SSTATE_MIRRORS += "file://.* file:///%s/PATH"\n'
% cls.original_sstate)
- cls.sstate_conf += ('BB_HASHSERVE_UPSTREAM = "hashserv.yocto.io:8687"\n')
+ cls.sstate_conf += ('BB_HASHSERVE_UPSTREAM = "hashserv.yoctoproject.org:8686"\n')
@classmethod
def tearDownClass(cls):
@@ -2017,7 +2017,7 @@ class DevtoolUpgradeTests(DevtoolBase):
newlines = f.readlines()
self.assertEqual(desiredlines, newlines)
- def test_devtool_upgrade_recipe_update_extra_tasks(self):
+ def test_devtool_upgrade_recipe_upgrade_extra_tasks(self):
# Check preconditions
self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
self.track_for_cleanup(self.workspacedir)
diff --git a/poky/meta/lib/oeqa/selftest/cases/distrodata.py b/poky/meta/lib/oeqa/selftest/cases/distrodata.py
index ad952c004b..7771a42e2b 100644
--- a/poky/meta/lib/oeqa/selftest/cases/distrodata.py
+++ b/poky/meta/lib/oeqa/selftest/cases/distrodata.py
@@ -20,10 +20,10 @@ class Distrodata(OESelftestTestCase):
feature = 'LICENSE_FLAGS_ACCEPTED += " commercial"\n'
self.write_config(feature)
- pkgs = oe.recipeutils.get_recipe_upgrade_status()
+ pkggroups = oe.recipeutils.get_recipe_upgrade_status()
- regressed_failures = [pkg[0] for pkg in pkgs if pkg[1] == 'UNKNOWN_BROKEN']
- regressed_successes = [pkg[0] for pkg in pkgs if pkg[1] == 'KNOWN_BROKEN']
+ regressed_failures = [pkg['pn'] for pkgs in pkggroups for pkg in pkgs if pkg['status'] == 'UNKNOWN_BROKEN']
+ regressed_successes = [pkg['pn'] for pkgs in pkggroups for pkg in pkgs if pkg['status'] == 'KNOWN_BROKEN']
msg = ""
if len(regressed_failures) > 0:
msg = msg + """
@@ -55,7 +55,7 @@ but their recipes claim otherwise by setting UPSTREAM_VERSION_UNKNOWN. Please re
return False
def is_maintainer_exception(entry):
- exceptions = ["musl", "newlib", "linux-yocto", "linux-dummy", "mesa-gl", "libgfortran", "libx11-compose-data",
+ exceptions = ["musl", "newlib", "picolibc", "linux-yocto", "linux-dummy", "mesa-gl", "libgfortran", "libx11-compose-data",
"cve-update-nvd2-native",]
for i in exceptions:
if i in entry:
@@ -115,3 +115,15 @@ The list of oe-core recipes with maintainers is empty. This may indicate that th
self.fail("""
Unable to find recipes for the following entries in maintainers.inc:
""" + "\n".join(['%s' % i for i in missing_recipes]))
+
+ def test_common_include_recipes(self):
+ """
+ Summary: Test that obtaining recipes that share includes between them returns a sane result
+ Expected: At least cmake and qemu entries are present in the output
+ Product: oe-core
+ Author: Alexander Kanavin <alex.kanavin@gmail.com>
+ """
+ recipes = oe.recipeutils.get_common_include_recipes()
+
+ self.assertIn({'qemu-system-native', 'qemu', 'qemu-native'}, recipes)
+ self.assertIn({'cmake-native', 'cmake'}, recipes)
diff --git a/poky/meta/lib/oeqa/selftest/cases/fitimage.py b/poky/meta/lib/oeqa/selftest/cases/fitimage.py
index 347c065377..0b5f4602fb 100644
--- a/poky/meta/lib/oeqa/selftest/cases/fitimage.py
+++ b/poky/meta/lib/oeqa/selftest/cases/fitimage.py
@@ -11,6 +11,51 @@ import re
class FitImageTests(OESelftestTestCase):
+ def _setup_uboot_tools_native(self):
+ """build u-boot-tools-native and return RECIPE_SYSROOT_NATIVE"""
+ bitbake("u-boot-tools-native -c addto_recipe_sysroot")
+ return get_bb_var('RECIPE_SYSROOT_NATIVE', 'u-boot-tools-native')
+
+ def _verify_fit_image_signature(self, uboot_tools_sysroot_native, fitimage_path, dtb_path, conf_name=None):
+ """Verify the signature of a fit contfiguration
+
+ The fit_check_sign utility from u-boot-tools-native is called.
+ uboot-fit_check_sign -f fitImage -k $dtb_name -c conf-$dtb_name
+ """
+ fit_check_sign_path = os.path.join(uboot_tools_sysroot_native, 'usr', 'bin', 'uboot-fit_check_sign')
+ cmd = '%s -f %s -k %s' % (fit_check_sign_path, fitimage_path, dtb_path)
+ if conf_name:
+ cmd += ' -c %s' % conf_name
+ result = runCmd(cmd)
+ self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output)
+ self.assertIn("Signature check OK", result.output)
+
+ @staticmethod
+ def _find_string_in_bin_file(file_path, search_string):
+ """find stings in a binary file
+
+ Shell equivalent: strings "$1" | grep "$2" | wc -l
+ return number of matches
+ """
+ found_positions = 0
+ with open(file_path, 'rb') as file:
+ byte = file.read(1)
+ current_position = 0
+ current_match = 0
+ while byte:
+ char = byte.decode('ascii', errors='ignore')
+ if char == search_string[current_match]:
+ current_match += 1
+ if current_match == len(search_string):
+ found_positions += 1
+ current_match = 0
+ else:
+ current_match = 0
+ current_position += 1
+ byte = file.read(1)
+ return found_positions
+
+
def test_fit_image(self):
"""
Summary: Check if FIT image and Image Tree Source (its) are built
@@ -53,10 +98,8 @@ FIT_DESC = "A model description"
fitimage_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'],
"fitImage-%s-%s" % (bb_vars['INITRAMFS_IMAGE_NAME'], bb_vars['KERNEL_FIT_LINK_NAME']))
- self.assertTrue(os.path.exists(fitimage_its_path),
- "%s image tree source doesn't exist" % (fitimage_its_path))
- self.assertTrue(os.path.exists(fitimage_path),
- "%s FIT image doesn't exist" % (fitimage_path))
+ self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path))
+ self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path))
# Check that the type, load address, entrypoint address and default
# values for kernel and ramdisk in Image Tree Source are as expected.
@@ -108,19 +151,21 @@ FIT_DESC = "A model description"
Author: Paul Eggleton <paul.eggleton@microsoft.com> based upon
work by Usama Arif <usama.arif@arm.com>
"""
+ a_comment = "a smart comment"
config = """
# Enable creation of fitImage
MACHINE = "beaglebone-yocto"
KERNEL_IMAGETYPES += " fitImage "
-KERNEL_CLASSES = " kernel-fitimage test-mkimage-wrapper "
+KERNEL_CLASSES = " kernel-fitimage "
UBOOT_SIGN_ENABLE = "1"
FIT_GENERATE_KEYS = "1"
UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest"
UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
FIT_SIGN_INDIVIDUAL = "1"
-UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
-"""
+UBOOT_MKIMAGE_SIGN_ARGS = "-c '%s'"
+""" % a_comment
+
self.write_config(config)
# fitImage is created as part of linux recipe
@@ -133,10 +178,8 @@ UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
fitimage_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'],
"fitImage-%s.bin" % (bb_vars['KERNEL_FIT_LINK_NAME']))
- self.assertTrue(os.path.exists(fitimage_its_path),
- "%s image tree source doesn't exist" % (fitimage_its_path))
- self.assertTrue(os.path.exists(fitimage_path),
- "%s FIT image doesn't exist" % (fitimage_path))
+ self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path))
+ self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path))
req_itspaths = [
['/', 'images', 'kernel-1'],
@@ -195,10 +238,8 @@ UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
self.assertEqual(value, reqvalue)
# Dump the image to see if it really got signed
- bitbake("u-boot-tools-native -c addto_recipe_sysroot")
- result = runCmd('bitbake -e u-boot-tools-native | grep ^RECIPE_SYSROOT_NATIVE=')
- recipe_sysroot_native = result.output.split('=')[1].strip('"')
- dumpimage_path = os.path.join(recipe_sysroot_native, 'usr', 'bin', 'dumpimage')
+ uboot_tools_sysroot_native = self._setup_uboot_tools_native()
+ dumpimage_path = os.path.join(uboot_tools_sysroot_native, 'usr', 'bin', 'dumpimage')
result = runCmd('%s -l %s' % (dumpimage_path, fitimage_path))
in_signed = None
signed_sections = {}
@@ -224,17 +265,15 @@ UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
value = values.get('Sign value', None)
self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section)
- # Check for UBOOT_MKIMAGE_SIGN_ARGS
- result = runCmd('bitbake -e virtual/kernel | grep ^T=')
- tempdir = result.output.split('=', 1)[1].strip().strip('')
- result = runCmd('grep "a smart comment" %s/run.do_assemble_fitimage' % tempdir, ignore_status=True)
- self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE_SIGN_ARGS value did not get used')
+ # Search for the string passed to mkimage: 1 kernel + 3 DTBs + config per DTB = 7 sections
+ # Looks like mkimage supports to add a comment but does not support to read it back.
+ found_comments = FitImageTests._find_string_in_bin_file(fitimage_path, a_comment)
+ self.assertEqual(found_comments, 7, "Expected 7 signed and commented section in the fitImage.")
- # Check for evidence of test-mkimage-wrapper class
- result = runCmd('grep "### uboot-mkimage wrapper message" %s/log.do_assemble_fitimage' % tempdir, ignore_status=True)
- self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE did not work')
- result = runCmd('grep "### uboot-mkimage signing wrapper message" %s/log.do_assemble_fitimage' % tempdir, ignore_status=True)
- self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE_SIGN did not work')
+ # Verify the signature for all configurations = DTBs
+ for dtb in ['am335x-bone.dtb', 'am335x-boneblack.dtb', 'am335x-bonegreen.dtb']:
+ self._verify_fit_image_signature(uboot_tools_sysroot_native, fitimage_path,
+ os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], dtb), 'conf-' + dtb)
def test_uboot_fit_image(self):
"""
@@ -287,10 +326,8 @@ FIT_SIGN_INDIVIDUAL = "1"
fitimage_path = os.path.join(deploy_dir_image,
"u-boot-fitImage-%s" % (machine,))
- self.assertTrue(os.path.exists(fitimage_its_path),
- "%s image tree source doesn't exist" % (fitimage_its_path))
- self.assertTrue(os.path.exists(fitimage_path),
- "%s FIT image doesn't exist" % (fitimage_path))
+ self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path))
+ self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path))
# Check that the type, load address, entrypoint address and default
# values for kernel and ramdisk in Image Tree Source are as expected.
@@ -351,7 +388,6 @@ UBOOT_ENTRYPOINT = "0x80080000"
UBOOT_FIT_DESC = "A model description"
KERNEL_IMAGETYPES += " fitImage "
KERNEL_CLASSES = " kernel-fitimage "
-INHERIT += "test-mkimage-wrapper"
UBOOT_SIGN_ENABLE = "1"
FIT_GENERATE_KEYS = "1"
UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
@@ -372,10 +408,8 @@ UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot comment'"
fitimage_path = os.path.join(deploy_dir_image,
"u-boot-fitImage-%s" % (machine,))
- self.assertTrue(os.path.exists(fitimage_its_path),
- "%s image tree source doesn't exist" % (fitimage_its_path))
- self.assertTrue(os.path.exists(fitimage_path),
- "%s FIT image doesn't exist" % (fitimage_path))
+ self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path))
+ self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path))
# Check that the type, load address, entrypoint address and default
# values for kernel and ramdisk in Image Tree Source are as expected.
@@ -425,6 +459,7 @@ UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot comment'"
work by Paul Eggleton <paul.eggleton@microsoft.com> and
Usama Arif <usama.arif@arm.com>
"""
+ a_comment = "a smart U-Boot comment"
config = """
# There's no U-boot deconfig with CONFIG_FIT_SIGNATURE yet, so we need at
# least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set
@@ -434,7 +469,6 @@ SPL_BINARY = "MLO"
# The kernel-fitimage class is a dependency even if we're only
# creating/signing the U-Boot fitImage
KERNEL_CLASSES = " kernel-fitimage"
-INHERIT += "test-mkimage-wrapper"
# Enable creation and signing of the U-Boot fitImage
UBOOT_FITIMAGE_ENABLE = "1"
SPL_SIGN_ENABLE = "1"
@@ -446,17 +480,17 @@ UBOOT_LOADADDRESS = "0x80000000"
UBOOT_DTB_LOADADDRESS = "0x82000000"
UBOOT_ARCH = "arm"
SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000"
-SPL_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot comment'"
+SPL_MKIMAGE_SIGN_ARGS = "-c '%s'"
UBOOT_EXTLINUX = "0"
UBOOT_FIT_GENERATE_KEYS = "1"
UBOOT_FIT_HASH_ALG = "sha256"
-"""
+""" % a_comment
+
self.write_config(config)
# The U-Boot fitImage is created as part of the U-Boot recipe
bitbake("virtual/bootloader")
- image_type = "core-image-minimal"
deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
machine = get_bb_var('MACHINE')
fitimage_its_path = os.path.join(deploy_dir_image,
@@ -464,10 +498,8 @@ UBOOT_FIT_HASH_ALG = "sha256"
fitimage_path = os.path.join(deploy_dir_image,
"u-boot-fitImage-%s" % (machine,))
- self.assertTrue(os.path.exists(fitimage_its_path),
- "%s image tree source doesn't exist" % (fitimage_its_path))
- self.assertTrue(os.path.exists(fitimage_path),
- "%s FIT image doesn't exist" % (fitimage_path))
+ self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path))
+ self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path))
req_itspaths = [
['/', 'images', 'uboot'],
@@ -516,10 +548,8 @@ UBOOT_FIT_HASH_ALG = "sha256"
self.assertEqual(value, reqvalue)
# Dump the image to see if it really got signed
- bitbake("u-boot-tools-native -c addto_recipe_sysroot")
- result = runCmd('bitbake -e u-boot-tools-native | grep ^RECIPE_SYSROOT_NATIVE=')
- recipe_sysroot_native = result.output.split('=')[1].strip('"')
- dumpimage_path = os.path.join(recipe_sysroot_native, 'usr', 'bin', 'dumpimage')
+ uboot_tools_sysroot_native = self._setup_uboot_tools_native()
+ dumpimage_path = os.path.join(uboot_tools_sysroot_native, 'usr', 'bin', 'dumpimage')
result = runCmd('%s -l %s' % (dumpimage_path, fitimage_path))
in_signed = None
signed_sections = {}
@@ -542,16 +572,14 @@ UBOOT_FIT_HASH_ALG = "sha256"
self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section)
# Check for SPL_MKIMAGE_SIGN_ARGS
- result = runCmd('bitbake -e virtual/bootloader | grep ^T=')
- tempdir = result.output.split('=', 1)[1].strip().strip('')
- result = runCmd('grep "a smart U-Boot comment" %s/run.do_uboot_assemble_fitimage' % tempdir, ignore_status=True)
- self.assertEqual(result.status, 0, 'SPL_MKIMAGE_SIGN_ARGS value did not get used')
+ # Looks like mkimage supports to add a comment but does not support to read it back.
+ found_comments = FitImageTests._find_string_in_bin_file(fitimage_path, a_comment)
+ self.assertEqual(found_comments, 2, "Expected 2 signed and commented section in the fitImage.")
+
+ # Verify the signature
+ self._verify_fit_image_signature(uboot_tools_sysroot_native, fitimage_path,
+ os.path.join(deploy_dir_image, 'u-boot-spl.dtb'))
- # Check for evidence of test-mkimage-wrapper class
- result = runCmd('grep "### uboot-mkimage wrapper message" %s/log.do_uboot_assemble_fitimage' % tempdir, ignore_status=True)
- self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE did not work')
- result = runCmd('grep "### uboot-mkimage signing wrapper message" %s/log.do_uboot_assemble_fitimage' % tempdir, ignore_status=True)
- self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE_SIGN did not work')
def test_sign_cascaded_uboot_fit_image(self):
"""
@@ -573,6 +601,7 @@ UBOOT_FIT_HASH_ALG = "sha256"
work by Paul Eggleton <paul.eggleton@microsoft.com> and
Usama Arif <usama.arif@arm.com>
"""
+ a_comment = "a smart cascaded U-Boot comment"
config = """
# There's no U-boot deconfig with CONFIG_FIT_SIGNATURE yet, so we need at
# least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set
@@ -588,7 +617,7 @@ UBOOT_DTB_BINARY = "u-boot.dtb"
UBOOT_ENTRYPOINT = "0x80000000"
UBOOT_LOADADDRESS = "0x80000000"
UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000"
-UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart cascaded Kernel comment'"
+UBOOT_MKIMAGE_SIGN_ARGS = "-c '%s'"
UBOOT_DTB_LOADADDRESS = "0x82000000"
UBOOT_ARCH = "arm"
SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000"
@@ -598,20 +627,18 @@ UBOOT_FIT_GENERATE_KEYS = "1"
UBOOT_FIT_HASH_ALG = "sha256"
KERNEL_IMAGETYPES += " fitImage "
KERNEL_CLASSES = " kernel-fitimage "
-INHERIT += "test-mkimage-wrapper"
UBOOT_SIGN_ENABLE = "1"
FIT_GENERATE_KEYS = "1"
UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest"
UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
FIT_SIGN_INDIVIDUAL = "1"
-"""
+""" % a_comment
self.write_config(config)
# The U-Boot fitImage is created as part of the U-Boot recipe
bitbake("virtual/bootloader")
- image_type = "core-image-minimal"
deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
machine = get_bb_var('MACHINE')
fitimage_its_path = os.path.join(deploy_dir_image,
@@ -619,10 +646,8 @@ FIT_SIGN_INDIVIDUAL = "1"
fitimage_path = os.path.join(deploy_dir_image,
"u-boot-fitImage-%s" % (machine,))
- self.assertTrue(os.path.exists(fitimage_its_path),
- "%s image tree source doesn't exist" % (fitimage_its_path))
- self.assertTrue(os.path.exists(fitimage_path),
- "%s FIT image doesn't exist" % (fitimage_path))
+ self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path))
+ self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path))
req_itspaths = [
['/', 'images', 'uboot'],
@@ -671,10 +696,8 @@ FIT_SIGN_INDIVIDUAL = "1"
self.assertEqual(value, reqvalue)
# Dump the image to see if it really got signed
- bitbake("u-boot-tools-native -c addto_recipe_sysroot")
- result = runCmd('bitbake -e u-boot-tools-native | grep ^RECIPE_SYSROOT_NATIVE=')
- recipe_sysroot_native = result.output.split('=')[1].strip('"')
- dumpimage_path = os.path.join(recipe_sysroot_native, 'usr', 'bin', 'dumpimage')
+ uboot_tools_sysroot_native = self._setup_uboot_tools_native()
+ dumpimage_path = os.path.join(uboot_tools_sysroot_native, 'usr', 'bin', 'dumpimage')
result = runCmd('%s -l %s' % (dumpimage_path, fitimage_path))
in_signed = None
signed_sections = {}
@@ -697,17 +720,13 @@ FIT_SIGN_INDIVIDUAL = "1"
self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section)
# Check for SPL_MKIMAGE_SIGN_ARGS
- result = runCmd('bitbake -e virtual/bootloader | grep ^T=')
- tempdir = result.output.split('=', 1)[1].strip().strip('')
- result = runCmd('grep "a smart cascaded U-Boot comment" %s/run.do_uboot_assemble_fitimage' % tempdir, ignore_status=True)
- self.assertEqual(result.status, 0, 'SPL_MKIMAGE_SIGN_ARGS value did not get used')
-
- # Check for evidence of test-mkimage-wrapper class
- result = runCmd('grep "### uboot-mkimage wrapper message" %s/log.do_uboot_assemble_fitimage' % tempdir, ignore_status=True)
- self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE did not work')
- result = runCmd('grep "### uboot-mkimage signing wrapper message" %s/log.do_uboot_assemble_fitimage' % tempdir, ignore_status=True)
- self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE_SIGN did not work')
+ # Looks like mkimage supports to add a comment but does not support to read it back.
+ found_comments = FitImageTests._find_string_in_bin_file(fitimage_path, a_comment)
+ self.assertEqual(found_comments, 2, "Expected 2 signed and commented section in the fitImage.")
+ # Verify the signature
+ self._verify_fit_image_signature(uboot_tools_sysroot_native, fitimage_path,
+ os.path.join(deploy_dir_image, 'u-boot-spl.dtb'))
def test_initramfs_bundle(self):
@@ -755,24 +774,24 @@ FIT_HASH_ALG = "sha256"
# fitImage is created as part of linux recipe
bitbake("virtual/kernel")
- image_type = get_bb_var('INITRAMFS_IMAGE')
- deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
- machine = get_bb_var('MACHINE')
- fitimage_its_path = os.path.join(deploy_dir_image,
- "fitImage-its-%s-%s-%s" % (image_type, machine, machine))
- fitimage_path = os.path.join(deploy_dir_image,"fitImage")
-
- self.assertTrue(os.path.exists(fitimage_its_path),
- "%s image tree source doesn't exist" % (fitimage_its_path))
- self.assertTrue(os.path.exists(fitimage_path),
- "%s FIT image doesn't exist" % (fitimage_path))
+ bb_vars = get_bb_vars([
+ 'DEPLOY_DIR_IMAGE',
+ 'FIT_HASH_ALG',
+ 'FIT_KERNEL_COMP_ALG',
+ 'INITRAMFS_IMAGE',
+ 'MACHINE',
+ 'UBOOT_ARCH',
+ 'UBOOT_ENTRYPOINT',
+ 'UBOOT_LOADADDRESS',
+ 'UBOOT_MKIMAGE_KERNEL_TYPE'
+ ],
+ 'virtual/kernel')
+ fitimage_its_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'],
+ "fitImage-its-%s-%s-%s" % (bb_vars['INITRAMFS_IMAGE'], bb_vars['MACHINE'], bb_vars['MACHINE']))
+ fitimage_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'],"fitImage")
- kernel_load = str(get_bb_var('UBOOT_LOADADDRESS'))
- kernel_entry = str(get_bb_var('UBOOT_ENTRYPOINT'))
- kernel_type = str(get_bb_var('UBOOT_MKIMAGE_KERNEL_TYPE'))
- kernel_compression = str(get_bb_var('FIT_KERNEL_COMP_ALG'))
- uboot_arch = str(get_bb_var('UBOOT_ARCH'))
- fit_hash_alg = str(get_bb_var('FIT_HASH_ALG'))
+ self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path))
+ self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path))
its_file = open(fitimage_its_path)
@@ -782,31 +801,31 @@ FIT_HASH_ALG = "sha256"
'kernel-1 {',
'description = "Linux kernel";',
'data = /incbin/("linux.bin");',
- 'type = "' + kernel_type + '";',
- 'arch = "' + uboot_arch + '";',
+ 'type = "' + str(bb_vars['UBOOT_MKIMAGE_KERNEL_TYPE']) + '";',
+ 'arch = "' + str(bb_vars['UBOOT_ARCH']) + '";',
'os = "linux";',
- 'compression = "' + kernel_compression + '";',
- 'load = <' + kernel_load + '>;',
- 'entry = <' + kernel_entry + '>;',
+ 'compression = "' + str(bb_vars['FIT_KERNEL_COMP_ALG']) + '";',
+ 'load = <' + str(bb_vars['UBOOT_LOADADDRESS']) + '>;',
+ 'entry = <' + str(bb_vars['UBOOT_ENTRYPOINT']) + '>;',
'hash-1 {',
- 'algo = "' + fit_hash_alg +'";',
+ 'algo = "' + str(bb_vars['FIT_HASH_ALG']) +'";',
'};',
'};'
]
node_str = exp_node_lines[0]
- test_passed = False
-
print ("checking kernel node\n")
+ self.assertIn(node_str, its_lines)
- if node_str in its_lines:
- node_start_idx = its_lines.index(node_str)
- node = its_lines[node_start_idx:(node_start_idx + len(exp_node_lines))]
- if node == exp_node_lines:
- print("kernel node verified")
- else:
- self.assertTrue(test_passed == True,"kernel node does not match expectation")
+ node_start_idx = its_lines.index(node_str)
+ node = its_lines[node_start_idx:(node_start_idx + len(exp_node_lines))]
+
+ # Remove the absolute path. This refers to WORKDIR which is not always predictable.
+ re_data = re.compile(r'^data = /incbin/\(.*/linux\.bin"\);$')
+ node = [re.sub(re_data, 'data = /incbin/("linux.bin");', cfg_str) for cfg_str in node]
+
+ self.assertEqual(node, exp_node_lines, "kernel node does not match expectation")
rx_configs = re.compile("^conf-.*")
its_configs = list(filter(rx_configs.match, its_lines))
@@ -822,25 +841,14 @@ FIT_HASH_ALG = "sha256"
node = its_lines[cfg_start_idx:line_idx]
print("checking configuration " + cfg_str.rstrip(" {"))
- rx_desc_line = re.compile("^description.*1 Linux kernel.*")
- if len(list(filter(rx_desc_line.match, node))) != 1:
- self.assertTrue(test_passed == True,"kernel keyword not found in the description line")
- break
- else:
- print("kernel keyword found in the description line")
+ rx_desc_line = re.compile(r'^description = ".*Linux kernel.*')
+ self.assertEqual(len(list(filter(rx_desc_line.match, node))), 1, "kernel keyword not found in the description line")
- if 'kernel = "kernel-1";' not in node:
- self.assertTrue(test_passed == True,"kernel line not found")
- break
- else:
- print("kernel line found")
+ self.assertIn('kernel = "kernel-1";', node)
- rx_sign_line = re.compile("^sign-images.*kernel.*")
- if len(list(filter(rx_sign_line.match, node))) != 1:
- self.assertTrue(test_passed == True,"kernel hash not signed")
- break
- else:
- print("kernel hash signed")
+ rx_sign_line = re.compile(r'^sign-images = .*kernel.*')
+ self.assertEqual(len(list(filter(rx_sign_line.match, node))), 1, "kernel hash not signed")
- test_passed = True
- self.assertTrue(test_passed == True,"Initramfs bundle test success")
+ # Verify the signature
+ uboot_tools_sysroot_native = self._setup_uboot_tools_native()
+ self._verify_fit_image_signature(uboot_tools_sysroot_native, fitimage_path, os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], 'am335x-bone.dtb'))
diff --git a/poky/meta/lib/oeqa/selftest/cases/gcc.py b/poky/meta/lib/oeqa/selftest/cases/gcc.py
index 89360178fe..1bda29a72b 100644
--- a/poky/meta/lib/oeqa/selftest/cases/gcc.py
+++ b/poky/meta/lib/oeqa/selftest/cases/gcc.py
@@ -37,7 +37,7 @@ class GccSelfTestBase(OESelftestTestCase, OEPTestResultTestCase):
features = []
features.append('MAKE_CHECK_TARGETS = "{0}"'.format(" ".join(targets)))
if ssh is not None:
- features.append('TOOLCHAIN_TEST_TARGET = "ssh"')
+ features.append('TOOLCHAIN_TEST_TARGET = "linux-ssh"')
features.append('TOOLCHAIN_TEST_HOST = "{0}"'.format(ssh))
features.append('TOOLCHAIN_TEST_HOST_USER = "root"')
features.append('TOOLCHAIN_TEST_HOST_PORT = "22"')
@@ -83,6 +83,8 @@ class GccSelfTestBase(OESelftestTestCase, OEPTestResultTestCase):
# validate that SSH is working
status, _ = qemu.run("uname")
self.assertEqual(status, 0)
+ qemu.run('echo "MaxStartups 75:30:100" >> /etc/ssh/sshd_config')
+ qemu.run('service sshd restart')
return self.run_check(*args, ssh=qemu.ip, **kwargs)
diff --git a/poky/meta/lib/oeqa/selftest/cases/imagefeatures.py b/poky/meta/lib/oeqa/selftest/cases/imagefeatures.py
index dc88c222bd..94d01ba116 100644
--- a/poky/meta/lib/oeqa/selftest/cases/imagefeatures.py
+++ b/poky/meta/lib/oeqa/selftest/cases/imagefeatures.py
@@ -250,12 +250,7 @@ USERADD_GID_TABLES += "files/static-group"
DISTRO_FEATURES:append = " pam opengl wayland"
# Switch to systemd
-DISTRO_FEATURES:append = " systemd usrmerge"
-VIRTUAL-RUNTIME_init_manager = "systemd"
-VIRTUAL-RUNTIME_initscripts = ""
-VIRTUAL-RUNTIME_syslog = ""
-VIRTUAL-RUNTIME_login_manager = "shadow-base"
-DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
+INIT_MANAGER = "systemd"
# Replace busybox
PREFERRED_PROVIDER_virtual/base-utils = "packagegroup-core-base-utils"
@@ -319,7 +314,7 @@ SKIP_RECIPE[busybox] = "Don't build this"
"""
config = """
DISTRO_FEATURES:append = " api-documentation"
-CORE_IMAGE_EXTRA_INSTALL = "man-pages kmod-doc"
+CORE_IMAGE_EXTRA_INSTALL = "man-pages"
"""
self.write_config(config)
bitbake("core-image-minimal")
@@ -330,7 +325,7 @@ CORE_IMAGE_EXTRA_INSTALL = "man-pages kmod-doc"
self.assertEqual(status, 1, 'Failed to run apropos: %s' % (output))
self.assertIn("iso_8859_15", output)
- # This manpage is provided by kmod
- status, output = qemu.run_serial("man --pager=cat modprobe")
+ # This manpage is provided by man-pages
+ status, output = qemu.run_serial("man --pager=cat intro")
self.assertEqual(status, 1, 'Failed to run man: %s' % (output))
- self.assertIn("force-modversion", output)
+ self.assertIn("introduction to user commands", output)
diff --git a/poky/meta/lib/oeqa/selftest/cases/incompatible_lic.py b/poky/meta/lib/oeqa/selftest/cases/incompatible_lic.py
index f4af67a239..be5484bca4 100644
--- a/poky/meta/lib/oeqa/selftest/cases/incompatible_lic.py
+++ b/poky/meta/lib/oeqa/selftest/cases/incompatible_lic.py
@@ -114,7 +114,7 @@ INCOMPATIBLE_LICENSE:pn-core-image-minimal = "GPL-3.0* LGPL-3.0*"
def test_bash_and_license(self):
self.disable_class("create-spdx")
- self.write_config(self.default_config() + '\nLICENSE:append:pn-bash = " & SomeLicense"')
+ self.write_config(self.default_config() + '\nLICENSE:append:pn-bash = " & SomeLicense"\nERROR_QA:remove:pn-bash = "license-exists"')
error_msg = "ERROR: core-image-minimal-1.0-r0 do_rootfs: Package bash cannot be installed into the image because it has incompatible license(s): GPL-3.0-or-later"
result = bitbake('core-image-minimal', ignore_status=True)
@@ -123,12 +123,12 @@ INCOMPATIBLE_LICENSE:pn-core-image-minimal = "GPL-3.0* LGPL-3.0*"
def test_bash_or_license(self):
self.disable_class("create-spdx")
- self.write_config(self.default_config() + '\nLICENSE:append:pn-bash = " | SomeLicense"')
+ self.write_config(self.default_config() + '\nLICENSE:append:pn-bash = " | SomeLicense"\nERROR_QA:remove:pn-bash = "license-exists"\nERROR_QA:remove:pn-core-image-minimal = "license-file-missing"')
bitbake('core-image-minimal')
def test_bash_license_exceptions(self):
- self.write_config(self.default_config() + '\nINCOMPATIBLE_LICENSE_EXCEPTIONS:pn-core-image-minimal = "bash:GPL-3.0-or-later"')
+ self.write_config(self.default_config() + '\nINCOMPATIBLE_LICENSE_EXCEPTIONS:pn-core-image-minimal = "bash:GPL-3.0-or-later"\nERROR_QA:remove:pn-core-image-minimal = "license-exception"')
bitbake('core-image-minimal')
diff --git a/poky/meta/lib/oeqa/selftest/cases/locales.py b/poky/meta/lib/oeqa/selftest/cases/locales.py
index 4ca8ffb7aa..ac4888ef66 100644
--- a/poky/meta/lib/oeqa/selftest/cases/locales.py
+++ b/poky/meta/lib/oeqa/selftest/cases/locales.py
@@ -14,7 +14,7 @@ class LocalesTest(OESelftestTestCase):
features = []
features.append('EXTRA_IMAGE_FEATURES = "empty-root-password allow-empty-password allow-root-login"')
features.append('IMAGE_INSTALL:append = " glibc-utils localedef"')
- features.append('GLIBC_GENERATE_LOCALES = "en_US.UTF-8 fr_FR.UTF-8"')
+ features.append('GLIBC_GENERATE_LOCALES = "en_US.UTF-8 fr_FR.UTF-8 en_US.ISO-8859-1 de_DE.UTF-8 fr_FR.ISO-8859-1 zh_HK.BIG5-HKSCS tr_TR.UTF-8"')
features.append('IMAGE_LINGUAS:append = " en-us fr-fr"')
if binary_enabled:
features.append('ENABLE_BINARY_LOCALE_GENERATION = "1"')
diff --git a/poky/meta/lib/oeqa/selftest/cases/meta_ide.py b/poky/meta/lib/oeqa/selftest/cases/meta_ide.py
index ffe0d2604d..5a17ca52ea 100644
--- a/poky/meta/lib/oeqa/selftest/cases/meta_ide.py
+++ b/poky/meta/lib/oeqa/selftest/cases/meta_ide.py
@@ -20,8 +20,8 @@ class MetaIDE(OESelftestTestCase):
bitbake('meta-ide-support')
bitbake('build-sysroots -c build_native_sysroot')
bitbake('build-sysroots -c build_target_sysroot')
- bb_vars = get_bb_vars(['MULTIMACH_TARGET_SYS', 'DEPLOY_DIR_IMAGE', 'COREBASE'])
- cls.environment_script = 'environment-setup-%s' % bb_vars['MULTIMACH_TARGET_SYS']
+ bb_vars = get_bb_vars(['MACHINE_ARCH', 'TARGET_VENDOR', 'TARGET_OS', 'DEPLOY_DIR_IMAGE', 'COREBASE'])
+ cls.environment_script = 'environment-setup-%s%s-%s' % (bb_vars['MACHINE_ARCH'], bb_vars['TARGET_VENDOR'], bb_vars['TARGET_OS'])
cls.deploydir = bb_vars['DEPLOY_DIR_IMAGE']
cls.environment_script_path = '%s/%s' % (cls.deploydir, cls.environment_script)
cls.corebasedir = bb_vars['COREBASE']
diff --git a/poky/meta/lib/oeqa/selftest/cases/oescripts.py b/poky/meta/lib/oeqa/selftest/cases/oescripts.py
index fcfe54af74..bfbc33b08d 100644
--- a/poky/meta/lib/oeqa/selftest/cases/oescripts.py
+++ b/poky/meta/lib/oeqa/selftest/cases/oescripts.py
@@ -175,7 +175,7 @@ class OEListPackageconfigTests(OESelftestTestCase):
def test_packageconfig_flags_option_all(self):
results = runCmd('%s/contrib/list-packageconfig-flags.py -a' % self.scripts_dir)
expected_endlines = []
- expected_endlines.append("pinentry-1.3.0")
+ expected_endlines.append("pinentry-1.3.1")
expected_endlines.append("PACKAGECONFIG ncurses")
expected_endlines.append("PACKAGECONFIG[qt] --enable-pinentry-qt, --disable-pinentry-qt, qtbase-native qtbase")
expected_endlines.append("PACKAGECONFIG[gtk2] --enable-pinentry-gtk2, --disable-pinentry-gtk2, gtk+ glib-2.0")
diff --git a/poky/meta/lib/oeqa/selftest/cases/overlayfs.py b/poky/meta/lib/oeqa/selftest/cases/overlayfs.py
index e31063567b..580fbdcb9c 100644
--- a/poky/meta/lib/oeqa/selftest/cases/overlayfs.py
+++ b/poky/meta/lib/oeqa/selftest/cases/overlayfs.py
@@ -5,7 +5,7 @@
#
from oeqa.selftest.case import OESelftestTestCase
-from oeqa.utils.commands import bitbake, runqemu
+from oeqa.utils.commands import bitbake, runqemu, get_bb_vars
from oeqa.core.decorator import OETestTag
from oeqa.core.decorator.data import skipIfNotMachine
@@ -466,6 +466,45 @@ IMAGE_INSTALL:append = " overlayfs-user"
line = getline_qemu(output, "Read-only file system")
self.assertTrue(line, msg=output)
+ @skipIfNotMachine("qemux86-64", "tests are qemux86-64 specific currently")
+ def test_postinst_on_target_for_read_only_rootfs(self):
+ """
+ Summary: The purpose of this test case is to verify that post-installation
+ on target scripts are executed even if using read-only rootfs when
+ read-only-rootfs-delayed-postinsts is set
+ Expected: The test files are created on first boot
+ """
+
+ import oe.path
+
+ vars = get_bb_vars(("IMAGE_ROOTFS", "sysconfdir"), "core-image-minimal")
+ sysconfdir = vars["sysconfdir"]
+ self.assertIsNotNone(sysconfdir)
+ # Need to use oe.path here as sysconfdir starts with /
+ targettestdir = os.path.join(sysconfdir, "postinst-test")
+
+ config = self.get_working_config()
+
+ args = {
+ 'OVERLAYFS_INIT_OPTION': "",
+ 'OVERLAYFS_ETC_USE_ORIG_INIT_NAME': 1,
+ 'OVERLAYFS_ROOTFS_TYPE': "ext4",
+ 'OVERLAYFS_ETC_CREATE_MOUNT_DIRS': 1
+ }
+
+ # read-only-rootfs is already set in get_working_config()
+ config += 'EXTRA_IMAGE_FEATURES += "read-only-rootfs-delayed-postinsts"\n'
+ config += 'CORE_IMAGE_EXTRA_INSTALL = "postinst-delayed-b"\n'
+
+ self.write_config(config.format(**args))
+
+ res = bitbake('core-image-minimal')
+
+ with runqemu('core-image-minimal', image_fstype='wic') as qemu:
+ for filename in ("rootfs", "delayed-a", "delayed-b"):
+ status, output = qemu.run_serial("test -f %s && echo found" % os.path.join(targettestdir, filename))
+ self.assertIn("found", output, "%s was not present on boot" % filename)
+
def get_working_config(self):
return """
# Use systemd as init manager
diff --git a/poky/meta/lib/oeqa/selftest/cases/package.py b/poky/meta/lib/oeqa/selftest/cases/package.py
index 1aa6c03f8a..38ed7173fe 100644
--- a/poky/meta/lib/oeqa/selftest/cases/package.py
+++ b/poky/meta/lib/oeqa/selftest/cases/package.py
@@ -103,11 +103,37 @@ class PackageTests(OESelftestTestCase):
dest = get_bb_var('PKGDEST', 'selftest-hardlink')
bindir = get_bb_var('bindir', 'selftest-hardlink')
+ libdir = get_bb_var('libdir', 'selftest-hardlink')
+ libexecdir = get_bb_var('libexecdir', 'selftest-hardlink')
def checkfiles():
# Recipe creates 4 hardlinked files, there is a copy in package/ and a copy in packages-split/
# so expect 8 in total.
self.assertEqual(os.stat(dest + "/selftest-hardlink" + bindir + "/hello1").st_nlink, 8)
+ self.assertEqual(os.stat(dest + "/selftest-hardlink" + libexecdir + "/hello3").st_nlink, 8)
+
+ # Check dbg version
+ # 2 items, a copy in both package/packages-split so 4
+ self.assertEqual(os.stat(dest + "/selftest-hardlink-dbg" + bindir + "/.debug/hello1").st_nlink, 4)
+ self.assertEqual(os.stat(dest + "/selftest-hardlink-dbg" + libexecdir + "/.debug/hello1").st_nlink, 4)
+
+ # Even though the libexecdir name is 'hello3' or 'hello4', that isn't the debug target name
+ self.assertEqual(os.path.exists(dest + "/selftest-hardlink-dbg" + libexecdir + "/.debug/hello3"), False)
+ self.assertEqual(os.path.exists(dest + "/selftest-hardlink-dbg" + libexecdir + "/.debug/hello4"), False)
+
+ # Check the staticdev libraries
+ # 101 items, a copy in both package/packages-split so 202
+ self.assertEqual(os.stat(dest + "/selftest-hardlink-staticdev" + libdir + "/libhello.a").st_nlink, 202)
+ self.assertEqual(os.stat(dest + "/selftest-hardlink-staticdev" + libdir + "/libhello-25.a").st_nlink, 202)
+ self.assertEqual(os.stat(dest + "/selftest-hardlink-staticdev" + libdir + "/libhello-50.a").st_nlink, 202)
+ self.assertEqual(os.stat(dest + "/selftest-hardlink-staticdev" + libdir + "/libhello-75.a").st_nlink, 202)
+
+ # Check static dbg
+ # 101 items, a copy in both package/packages-split so 202
+ self.assertEqual(os.stat(dest + "/selftest-hardlink-dbg" + libdir + "/.debug-static/libhello.a").st_nlink, 202)
+ self.assertEqual(os.stat(dest + "/selftest-hardlink-dbg" + libdir + "/.debug-static/libhello-25.a").st_nlink, 202)
+ self.assertEqual(os.stat(dest + "/selftest-hardlink-dbg" + libdir + "/.debug-static/libhello-50.a").st_nlink, 202)
+ self.assertEqual(os.stat(dest + "/selftest-hardlink-dbg" + libdir + "/.debug-static/libhello-75.a").st_nlink, 202)
# Test a sparse file remains sparse
sparsestat = os.stat(dest + "/selftest-hardlink" + bindir + "/sparsetest")
diff --git a/poky/meta/lib/oeqa/selftest/cases/picolibc.py b/poky/meta/lib/oeqa/selftest/cases/picolibc.py
new file mode 100644
index 0000000000..e40b4fc3d3
--- /dev/null
+++ b/poky/meta/lib/oeqa/selftest/cases/picolibc.py
@@ -0,0 +1,18 @@
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import bitbake, get_bb_var
+
+class PicolibcTest(OESelftestTestCase):
+
+ def test_picolibc(self):
+ compatible_machines = ['qemuarm', 'qemuarm64', 'qemuriscv32', 'qemuriscv64']
+ machine = get_bb_var('MACHINE')
+ if machine not in compatible_machines:
+ self.skipTest('This test only works with machines : %s' % ' '.join(compatible_machines))
+ self.write_config('TCLIBC = "picolibc"')
+ bitbake("picolibc-helloworld")
diff --git a/poky/meta/lib/oeqa/selftest/cases/recipetool.py b/poky/meta/lib/oeqa/selftest/cases/recipetool.py
index 42202b7831..f742dd4d64 100644
--- a/poky/meta/lib/oeqa/selftest/cases/recipetool.py
+++ b/poky/meta/lib/oeqa/selftest/cases/recipetool.py
@@ -1068,6 +1068,7 @@ class RecipetoolTests(RecipetoolBase):
d = DataConnectorCopy
d.getVar = Mock(return_value=commonlicdir)
+ d.expand = Mock(side_effect=lambda x: x)
srctree = tempfile.mkdtemp(prefix='recipetoolqa')
self.track_for_cleanup(srctree)
diff --git a/poky/meta/lib/oeqa/selftest/cases/reproducible.py b/poky/meta/lib/oeqa/selftest/cases/reproducible.py
index 97a9c3da90..92266ab66a 100644
--- a/poky/meta/lib/oeqa/selftest/cases/reproducible.py
+++ b/poky/meta/lib/oeqa/selftest/cases/reproducible.py
@@ -16,8 +16,6 @@ import os
import datetime
exclude_packages = [
- 'rust-rustdoc',
- 'rust-dbg'
]
def is_excluded(package):
@@ -135,7 +133,8 @@ class ReproducibleTests(OESelftestTestCase):
max_report_size = 250 * 1024 * 1024
# targets are the things we want to test the reproducibility of
- targets = ['core-image-minimal', 'core-image-sato', 'core-image-full-cmdline', 'core-image-weston', 'world']
+ # Have to add the virtual targets manually for now as builds may or may not include them as they're exclude from world
+ targets = ['core-image-minimal', 'core-image-sato', 'core-image-full-cmdline', 'core-image-weston', 'world', 'virtual/librpc', 'virtual/libsdl2', 'virtual/crypt']
# sstate targets are things to pull from sstate to potentially cut build/debugging time
sstate_targets = []
@@ -178,12 +177,8 @@ class ReproducibleTests(OESelftestTestCase):
self.sstate_targets = bb_vars['OEQA_REPRODUCIBLE_TEST_SSTATE_TARGETS'].split()
self.extraresults = {}
- self.extraresults.setdefault('reproducible.rawlogs', {})['log'] = ''
self.extraresults.setdefault('reproducible', {}).setdefault('files', {})
- def append_to_log(self, msg):
- self.extraresults['reproducible.rawlogs']['log'] += msg
-
def compare_packages(self, reference_dir, test_dir, diffutils_sysroot):
result = PackageCompareResults(self.oeqa_reproducible_excluded_packages)
@@ -210,7 +205,7 @@ class ReproducibleTests(OESelftestTestCase):
def write_package_list(self, package_class, name, packages):
self.extraresults['reproducible']['files'].setdefault(package_class, {})[name] = [
- {'reference': p.reference, 'test': p.test} for p in packages]
+ p.reference.split("/./")[1] for p in packages]
def copy_file(self, source, dest):
bb.utils.mkdirhier(os.path.dirname(dest))
@@ -275,9 +270,13 @@ class ReproducibleTests(OESelftestTestCase):
os.chmod(save_dir, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
self.logger.info('Non-reproducible packages will be copied to %s', save_dir)
+ # The below bug shows that a few reproducible issues are depends on build dir path length.
+ # https://bugzilla.yoctoproject.org/show_bug.cgi?id=15554
+ # So, the reproducibleA & reproducibleB directories are changed to reproducibleA & reproducibleB-extended to have different size.
+
vars_A = self.do_test_build('reproducibleA', self.build_from_sstate)
- vars_B = self.do_test_build('reproducibleB', False)
+ vars_B = self.do_test_build('reproducibleB-extended', False)
# NOTE: The temp directories from the reproducible build are purposely
# kept after the build so it can be diffed for debugging.
@@ -296,8 +295,6 @@ class ReproducibleTests(OESelftestTestCase):
self.logger.info('Reproducibility summary for %s: %s' % (c, result))
- self.append_to_log('\n'.join("%s: %s" % (r.status, r.test) for r in result.total))
-
self.write_package_list(package_class, 'missing', result.missing)
self.write_package_list(package_class, 'different', result.different)
self.write_package_list(package_class, 'different_excluded', result.different_excluded)
@@ -332,7 +329,7 @@ class ReproducibleTests(OESelftestTestCase):
# Copy jquery to improve the diffoscope output usability
self.copy_file(os.path.join(jquery_sysroot, 'usr/share/javascript/jquery/jquery.min.js'), os.path.join(package_html_dir, 'jquery.js'))
- run_diffoscope('reproducibleA', 'reproducibleB', package_html_dir, max_report_size=self.max_report_size,
+ run_diffoscope('reproducibleA', 'reproducibleB-extended', package_html_dir, max_report_size=self.max_report_size,
native_sysroot=diffoscope_sysroot, ignore_status=True, cwd=package_dir)
if fails:
diff --git a/poky/meta/lib/oeqa/selftest/cases/retain.py b/poky/meta/lib/oeqa/selftest/cases/retain.py
new file mode 100644
index 0000000000..892be45857
--- /dev/null
+++ b/poky/meta/lib/oeqa/selftest/cases/retain.py
@@ -0,0 +1,241 @@
+# Tests for retain.bbclass
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+import os
+import glob
+import fnmatch
+import oe.path
+import shutil
+import tarfile
+from oeqa.utils.commands import bitbake, get_bb_vars
+from oeqa.selftest.case import OESelftestTestCase
+
+class Retain(OESelftestTestCase):
+
+ def test_retain_always(self):
+ """
+ Summary: Test retain class with RETAIN_DIRS_ALWAYS
+ Expected: Archive written to RETAIN_OUTDIR when build of test recipe completes
+ Product: oe-core
+ Author: Paul Eggleton <paul.eggleton@microsoft.com>
+ """
+
+ test_recipe = 'quilt-native'
+
+ features = 'INHERIT += "retain"\n'
+ features += 'RETAIN_DIRS_ALWAYS = "${T}"\n'
+ self.write_config(features)
+
+ bitbake('-c clean %s' % test_recipe)
+
+ bb_vars = get_bb_vars(['RETAIN_OUTDIR', 'TMPDIR'])
+ retain_outdir = bb_vars['RETAIN_OUTDIR'] or ''
+ tmpdir = bb_vars['TMPDIR']
+ if len(retain_outdir) < 5:
+ self.fail('RETAIN_OUTDIR value "%s" is invalid' % retain_outdir)
+ if not oe.path.is_path_parent(tmpdir, retain_outdir):
+ self.fail('RETAIN_OUTDIR (%s) is not underneath TMPDIR (%s)' % (retain_outdir, tmpdir))
+ try:
+ shutil.rmtree(retain_outdir)
+ except FileNotFoundError:
+ pass
+
+ bitbake(test_recipe)
+ if not glob.glob(os.path.join(retain_outdir, '%s_temp_*.tar.gz' % test_recipe)):
+ self.fail('No output archive for %s created' % test_recipe)
+
+
+ def test_retain_failure(self):
+ """
+ Summary: Test retain class default behaviour
+ Expected: Archive written to RETAIN_OUTDIR only when build of test
+ recipe fails, and archive contents are as expected
+ Product: oe-core
+ Author: Paul Eggleton <paul.eggleton@microsoft.com>
+ """
+
+ test_recipe_fail = 'error'
+
+ features = 'INHERIT += "retain"\n'
+ self.write_config(features)
+
+ bb_vars = get_bb_vars(['RETAIN_OUTDIR', 'TMPDIR', 'RETAIN_DIRS_ALWAYS', 'RETAIN_DIRS_GLOBAL_ALWAYS'])
+ if bb_vars['RETAIN_DIRS_ALWAYS']:
+ self.fail('RETAIN_DIRS_ALWAYS is set, this interferes with the test')
+ if bb_vars['RETAIN_DIRS_GLOBAL_ALWAYS']:
+ self.fail('RETAIN_DIRS_GLOBAL_ALWAYS is set, this interferes with the test')
+ retain_outdir = bb_vars['RETAIN_OUTDIR'] or ''
+ tmpdir = bb_vars['TMPDIR']
+ if len(retain_outdir) < 5:
+ self.fail('RETAIN_OUTDIR value "%s" is invalid' % retain_outdir)
+ if not oe.path.is_path_parent(tmpdir, retain_outdir):
+ self.fail('RETAIN_OUTDIR (%s) is not underneath TMPDIR (%s)' % (retain_outdir, tmpdir))
+
+ try:
+ shutil.rmtree(retain_outdir)
+ except FileNotFoundError:
+ pass
+
+ bitbake('-c clean %s' % test_recipe_fail)
+
+ if os.path.exists(retain_outdir):
+ retain_dirlist = os.listdir(retain_outdir)
+ if retain_dirlist:
+ self.fail('RETAIN_OUTDIR should be empty without failure, contents:\n%s' % '\n'.join(retain_dirlist))
+
+ result = bitbake('-c compile %s' % test_recipe_fail, ignore_status=True)
+ if result.status == 0:
+ self.fail('Build of %s did not fail as expected' % test_recipe_fail)
+
+ archives = glob.glob(os.path.join(retain_outdir, '%s_*.tar.gz' % test_recipe_fail))
+ if not archives:
+ self.fail('No output archive for %s created' % test_recipe_fail)
+ if len(archives) > 1:
+ self.fail('More than one archive for %s created' % test_recipe_fail)
+ for archive in archives:
+ found = False
+ archive_prefix = os.path.basename(archive).split('.tar')[0]
+ expected_prefix_start = '%s_workdir' % test_recipe_fail
+ if not archive_prefix.startswith(expected_prefix_start):
+ self.fail('Archive %s name does not start with expected prefix "%s"' % (os.path.basename(archive), expected_prefix_start))
+ with tarfile.open(archive) as tf:
+ for ti in tf:
+ if not fnmatch.fnmatch(ti.name, '%s/*' % archive_prefix):
+ self.fail('File without tarball-named subdirectory within tarball %s: %s' % (os.path.basename(archive), ti.name))
+ if ti.name.endswith('/temp/log.do_compile'):
+ found = True
+ if not found:
+ self.fail('Did not find log.do_compile in output archive %s' % os.path.basename(archive))
+
+
+ def test_retain_global(self):
+ """
+ Summary: Test retain class RETAIN_DIRS_GLOBAL_* behaviour
+ Expected: Ensure RETAIN_DIRS_GLOBAL_ALWAYS always causes an
+ archive to be created, and RETAIN_DIRS_GLOBAL_FAILURE
+ only causes an archive to be created on failure.
+ Also test archive naming (with : character) as an
+ added bonus.
+ Product: oe-core
+ Author: Paul Eggleton <paul.eggleton@microsoft.com>
+ """
+
+ test_recipe = 'quilt-native'
+ test_recipe_fail = 'error'
+
+ features = 'INHERIT += "retain"\n'
+ features += 'RETAIN_DIRS_GLOBAL_ALWAYS = "${LOG_DIR};prefix=buildlogs"\n'
+ features += 'RETAIN_DIRS_GLOBAL_FAILURE = "${STAMPS_DIR}"\n'
+ self.write_config(features)
+
+ bitbake('-c clean %s' % test_recipe)
+
+ bb_vars = get_bb_vars(['RETAIN_OUTDIR', 'TMPDIR', 'STAMPS_DIR'])
+ retain_outdir = bb_vars['RETAIN_OUTDIR'] or ''
+ tmpdir = bb_vars['TMPDIR']
+ if len(retain_outdir) < 5:
+ self.fail('RETAIN_OUTDIR value "%s" is invalid' % retain_outdir)
+ if not oe.path.is_path_parent(tmpdir, retain_outdir):
+ self.fail('RETAIN_OUTDIR (%s) is not underneath TMPDIR (%s)' % (retain_outdir, tmpdir))
+ try:
+ shutil.rmtree(retain_outdir)
+ except FileNotFoundError:
+ pass
+
+ # Test success case
+ bitbake(test_recipe)
+ if not glob.glob(os.path.join(retain_outdir, 'buildlogs_*.tar.gz')):
+ self.fail('No output archive for LOG_DIR created')
+ stamps_dir = bb_vars['STAMPS_DIR']
+ if glob.glob(os.path.join(retain_outdir, '%s_*.tar.gz' % os.path.basename(stamps_dir))):
+ self.fail('Output archive for STAMPS_DIR created when it should not have been')
+
+ # Test failure case
+ result = bitbake('-c compile %s' % test_recipe_fail, ignore_status=True)
+ if result.status == 0:
+ self.fail('Build of %s did not fail as expected' % test_recipe_fail)
+ if not glob.glob(os.path.join(retain_outdir, '%s_*.tar.gz' % os.path.basename(stamps_dir))):
+ self.fail('Output archive for STAMPS_DIR not created')
+ if len(glob.glob(os.path.join(retain_outdir, 'buildlogs_*.tar.gz'))) != 2:
+ self.fail('Should be exactly two buildlogs archives in output dir')
+
+
+ def test_retain_misc(self):
+ """
+ Summary: Test retain class with RETAIN_ENABLED and RETAIN_TARBALL_SUFFIX
+ Expected: Archive written to RETAIN_OUTDIR only when RETAIN_ENABLED is set
+ and archive contents are as expected. Also test archive naming
+ (with : character) as an added bonus.
+ Product: oe-core
+ Author: Paul Eggleton <paul.eggleton@microsoft.com>
+ """
+
+ test_recipe_fail = 'error'
+
+ features = 'INHERIT += "retain"\n'
+ features += 'RETAIN_DIRS_ALWAYS = "${T}"\n'
+ features += 'RETAIN_ENABLED = "0"\n'
+ self.write_config(features)
+
+ bb_vars = get_bb_vars(['RETAIN_OUTDIR', 'TMPDIR'])
+ retain_outdir = bb_vars['RETAIN_OUTDIR'] or ''
+ tmpdir = bb_vars['TMPDIR']
+ if len(retain_outdir) < 5:
+ self.fail('RETAIN_OUTDIR value "%s" is invalid' % retain_outdir)
+ if not oe.path.is_path_parent(tmpdir, retain_outdir):
+ self.fail('RETAIN_OUTDIR (%s) is not underneath TMPDIR (%s)' % (retain_outdir, tmpdir))
+
+ try:
+ shutil.rmtree(retain_outdir)
+ except FileNotFoundError:
+ pass
+
+ bitbake('-c clean %s' % test_recipe_fail)
+ result = bitbake('-c compile %s' % test_recipe_fail, ignore_status=True)
+ if result.status == 0:
+ self.fail('Build of %s did not fail as expected' % test_recipe_fail)
+
+ if os.path.exists(retain_outdir) and os.listdir(retain_outdir):
+ self.fail('RETAIN_OUTDIR should be empty with RETAIN_ENABLED = "0"')
+
+ features = 'INHERIT += "retain"\n'
+ features += 'RETAIN_DIRS_ALWAYS = "${T};prefix=recipelogs"\n'
+ features += 'RETAIN_TARBALL_SUFFIX = "${DATETIME}-testsuffix.tar.bz2"\n'
+ features += 'RETAIN_ENABLED = "1"\n'
+ self.write_config(features)
+
+ result = bitbake('-c compile %s' % test_recipe_fail, ignore_status=True)
+ if result.status == 0:
+ self.fail('Build of %s did not fail as expected' % test_recipe_fail)
+
+ archives = glob.glob(os.path.join(retain_outdir, '%s_*-testsuffix.tar.bz2' % test_recipe_fail))
+ if not archives:
+ self.fail('No output archive for %s created' % test_recipe_fail)
+ if len(archives) != 2:
+ self.fail('Two archives for %s expected, but %d exist' % (test_recipe_fail, len(archives)))
+ recipelogs_found = False
+ workdir_found = False
+ for archive in archives:
+ contents_found = False
+ archive_prefix = os.path.basename(archive).split('.tar')[0]
+ if archive_prefix.startswith('%s_recipelogs' % test_recipe_fail):
+ recipelogs_found = True
+ if archive_prefix.startswith('%s_workdir' % test_recipe_fail):
+ workdir_found = True
+ with tarfile.open(archive, 'r:bz2') as tf:
+ for ti in tf:
+ if not fnmatch.fnmatch(ti.name, '%s/*' % (archive_prefix)):
+ self.fail('File without tarball-named subdirectory within tarball %s: %s' % (os.path.basename(archive), ti.name))
+ if ti.name.endswith('/log.do_compile'):
+ contents_found = True
+ if not contents_found:
+ # Both archives should contain this file
+ self.fail('Did not find log.do_compile in output archive %s' % os.path.basename(archive))
+ if not recipelogs_found:
+ self.fail('No archive with expected "recipelogs" prefix found')
+ if not workdir_found:
+ self.fail('No archive with expected "workdir" prefix found')
diff --git a/poky/meta/lib/oeqa/selftest/cases/runtime_test.py b/poky/meta/lib/oeqa/selftest/cases/runtime_test.py
index 13aa5f16c9..27090ae5cd 100644
--- a/poky/meta/lib/oeqa/selftest/cases/runtime_test.py
+++ b/poky/meta/lib/oeqa/selftest/cases/runtime_test.py
@@ -310,10 +310,7 @@ class Postinst(OESelftestTestCase):
features += 'IMAGE_FEATURES += "package-management empty-root-password"\n'
features += 'PACKAGE_CLASSES = "%s"\n' % classes
if init_manager == "systemd":
- features += 'DISTRO_FEATURES:append = " systemd usrmerge"\n'
- features += 'VIRTUAL-RUNTIME_init_manager = "systemd"\n'
- features += 'DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"\n'
- features += 'VIRTUAL-RUNTIME_initscripts = ""\n'
+ features += 'INIT_MANAGER = "systemd"\n'
self.write_config(features)
bitbake('core-image-minimal')
diff --git a/poky/meta/lib/oeqa/selftest/cases/rust.py b/poky/meta/lib/oeqa/selftest/cases/rust.py
index 4ccbe9867b..cbe6366f75 100644
--- a/poky/meta/lib/oeqa/selftest/cases/rust.py
+++ b/poky/meta/lib/oeqa/selftest/cases/rust.py
@@ -66,132 +66,45 @@ class RustSelfTestSystemEmulated(OESelftestTestCase, OEPTestResultTestCase):
# bless: First runs rustfmt to format the codebase,
# then runs tidy checks.
exclude_list = [
- 'compiler/rustc',
- 'compiler/rustc_interface/src/tests.rs',
- 'library/panic_abort',
- 'library/panic_unwind',
- 'library/test/src/stats/tests.rs',
- 'src/bootstrap/builder/tests.rs',
+ 'src/bootstrap',
'src/doc/rustc',
'src/doc/rustdoc',
'src/doc/unstable-book',
'src/librustdoc',
'src/rustdoc-json-types',
'src/tools/compiletest/src/common.rs',
+ 'src/tools/jsondoclint',
'src/tools/lint-docs',
+ 'src/tools/replace-version-placeholder',
'src/tools/rust-analyzer',
'src/tools/rustdoc-themes',
- 'src/tools/tidy',
+ 'src/tools/rust-installer',
+ 'src/tools/suggest-tests',
+ 'src/tools/tidy/src/',
'tests/assembly/asm/aarch64-outline-atomics.rs',
'tests/codegen/abi-main-signature-32bit-c-int.rs',
- 'tests/codegen/abi-repr-ext.rs',
- 'tests/codegen/abi-x86-interrupt.rs',
- 'tests/codegen/branch-protection.rs',
- 'tests/codegen/catch-unwind.rs',
- 'tests/codegen/cf-protection.rs',
- 'tests/codegen/enum-bounds-check-derived-idx.rs',
- 'tests/codegen/force-unwind-tables.rs',
- 'tests/codegen/intrinsic-no-unnamed-attr.rs',
- 'tests/codegen/issues/issue-103840.rs',
- 'tests/codegen/issues/issue-47278.rs',
- 'tests/codegen/issues/issue-73827-bounds-check-index-in-subexpr.rs',
- 'tests/codegen/lifetime_start_end.rs',
- 'tests/codegen/local-generics-in-exe-internalized.rs',
- 'tests/codegen/match-unoptimized.rs',
- 'tests/codegen/noalias-rwlockreadguard.rs',
- 'tests/codegen/non-terminate/nonempty-infinite-loop.rs',
- 'tests/codegen/noreturn-uninhabited.rs',
- 'tests/codegen/repr-transparent-aggregates-3.rs',
- 'tests/codegen/riscv-abi/call-llvm-intrinsics.rs',
- 'tests/codegen/riscv-abi/riscv64-lp64f-lp64d-abi.rs',
- 'tests/codegen/riscv-abi/riscv64-lp64d-abi.rs',
- 'tests/codegen/sse42-implies-crc32.rs',
+ 'tests/codegen/i128-x86-align.rs',
+ 'tests/codegen/issues/issue-122805.rs',
'tests/codegen/thread-local.rs',
- 'tests/codegen/uninit-consts.rs',
- 'tests/pretty/raw-str-nonexpr.rs',
+ 'tests/mir-opt/',
'tests/run-make',
'tests/run-make-fulldeps',
'tests/rustdoc',
'tests/rustdoc-json',
'tests/rustdoc-js-std',
- 'tests/rustdoc-ui/cfg-test.rs',
- 'tests/rustdoc-ui/check-cfg-test.rs',
- 'tests/rustdoc-ui/display-output.rs',
- 'tests/rustdoc-ui/doc-comment-multi-line-attr.rs',
- 'tests/rustdoc-ui/doc-comment-multi-line-cfg-attr.rs',
- 'tests/rustdoc-ui/doc-test-doctest-feature.rs',
- 'tests/rustdoc-ui/doctest-multiline-crate-attribute.rs',
- 'tests/rustdoc-ui/doctest-output.rs',
- 'tests/rustdoc-ui/doc-test-rustdoc-feature.rs',
- 'tests/rustdoc-ui/failed-doctest-compile-fail.rs',
- 'tests/rustdoc-ui/issue-80992.rs',
- 'tests/rustdoc-ui/issue-91134.rs',
- 'tests/rustdoc-ui/nocapture-fail.rs',
- 'tests/rustdoc-ui/nocapture.rs',
- 'tests/rustdoc-ui/no-run-flag.rs',
- 'tests/rustdoc-ui/run-directory.rs',
- 'tests/rustdoc-ui/test-no_std.rs',
- 'tests/rustdoc-ui/test-type.rs',
- 'tests/rustdoc/unit-return.rs',
'tests/ui/abi/stack-probes-lto.rs',
'tests/ui/abi/stack-probes.rs',
- 'tests/ui/array-slice-vec/subslice-patterns-const-eval-match.rs',
- 'tests/ui/asm/x86_64/sym.rs',
- 'tests/ui/associated-type-bounds/fn-apit.rs',
- 'tests/ui/associated-type-bounds/fn-dyn-apit.rs',
- 'tests/ui/associated-type-bounds/fn-wrap-apit.rs',
+ 'tests/ui/codegen/mismatched-data-layouts.rs',
'tests/ui/debuginfo/debuginfo-emit-llvm-ir-and-split-debuginfo.rs',
- 'tests/ui/drop/dynamic-drop.rs',
- 'tests/ui/empty_global_asm.rs',
- 'tests/ui/functions-closures/fn-help-with-err.rs',
- 'tests/ui/linkage-attr/issue-10755.rs',
- 'tests/ui/macros/restricted-shadowing-legacy.rs',
+ 'tests/ui-fulldeps/',
'tests/ui/process/nofile-limit.rs',
- 'tests/ui/process/process-panic-after-fork.rs',
- 'tests/ui/process/process-sigpipe.rs',
- 'tests/ui/simd/target-feature-mixup.rs',
'tests/ui/structs-enums/multiple-reprs.rs',
- 'src/tools/jsondoclint',
- 'src/tools/replace-version-placeholder',
- 'tests/codegen/abi-efiapi.rs',
- 'tests/codegen/abi-sysv64.rs',
- 'tests/codegen/align-byval.rs',
- 'tests/codegen/align-fn.rs',
- 'tests/codegen/asm-powerpc-clobbers.rs',
- 'tests/codegen/async-fn-debug-awaitee-field.rs',
- 'tests/codegen/binary-search-index-no-bound-check.rs',
- 'tests/codegen/call-metadata.rs',
- 'tests/codegen/debug-column.rs',
- 'tests/codegen/debug-limited.rs',
- 'tests/codegen/debuginfo-generic-closure-env-names.rs',
- 'tests/codegen/drop.rs',
- 'tests/codegen/dst-vtable-align-nonzero.rs',
- 'tests/codegen/enable-lto-unit-splitting.rs',
- 'tests/codegen/enum/enum-u128.rs',
- 'tests/codegen/fn-impl-trait-self.rs',
- 'tests/codegen/inherit_overflow.rs',
- 'tests/codegen/inline-function-args-debug-info.rs',
- 'tests/codegen/intrinsics/mask.rs',
- 'tests/codegen/intrinsics/transmute-niched.rs',
- 'tests/codegen/issues/issue-73258.rs',
- 'tests/codegen/issues/issue-75546.rs',
- 'tests/codegen/issues/issue-77812.rs',
- 'tests/codegen/issues/issue-98156-const-arg-temp-lifetime.rs',
- 'tests/codegen/llvm-ident.rs',
- 'tests/codegen/mainsubprogram.rs',
- 'tests/codegen/move-operands.rs',
- 'tests/codegen/repr/transparent-mips64.rs',
- 'tests/mir-opt/',
- 'tests/rustdoc-json',
- 'tests/rustdoc-ui/doc-test-rustdoc-feature.rs',
- 'tests/rustdoc-ui/no-run-flag.rs',
- 'tests/ui-fulldeps/',
- 'tests/ui/numbers-arithmetic/u128.rs'
+ 'tidyselftest'
]
exclude_fail_tests = " ".join([" --exclude " + item for item in exclude_list])
# Add exclude_fail_tests with other test arguments
- testargs = exclude_fail_tests + " --doc --no-fail-fast --bless"
+ testargs = exclude_fail_tests + " --no-fail-fast --bless"
# wrap the execution with a qemu instance.
# Tests are run with 512 tasks in parallel to execute all tests very quickly
diff --git a/poky/meta/lib/oeqa/selftest/cases/spdx.py b/poky/meta/lib/oeqa/selftest/cases/spdx.py
index 7685a81e7f..be595babb3 100644
--- a/poky/meta/lib/oeqa/selftest/cases/spdx.py
+++ b/poky/meta/lib/oeqa/selftest/cases/spdx.py
@@ -6,21 +6,26 @@
import json
import os
+import textwrap
+from pathlib import Path
from oeqa.selftest.case import OESelftestTestCase
-from oeqa.utils.commands import bitbake, get_bb_var, runCmd
+from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars, runCmd
-class SPDXCheck(OESelftestTestCase):
+class SPDX22Check(OESelftestTestCase):
@classmethod
def setUpClass(cls):
- super(SPDXCheck, cls).setUpClass()
+ super().setUpClass()
bitbake("python3-spdx-tools-native")
bitbake("-c addto_recipe_sysroot python3-spdx-tools-native")
def check_recipe_spdx(self, high_level_dir, spdx_file, target_name):
- config = """
-INHERIT += "create-spdx"
-"""
+ config = textwrap.dedent(
+ """\
+ INHERIT:remove = "create-spdx"
+ INHERIT += "create-spdx-2.2"
+ """
+ )
self.write_config(config)
deploy_dir = get_bb_var("DEPLOY_DIR")
@@ -29,7 +34,9 @@ INHERIT += "create-spdx"
# qemux86-64 creates the directory qemux86_64
machine_dir = machine_var.replace("-", "_")
- full_file_path = os.path.join(deploy_dir, "spdx", spdx_version, machine_dir, high_level_dir, spdx_file)
+ full_file_path = os.path.join(
+ deploy_dir, "spdx", spdx_version, machine_dir, high_level_dir, spdx_file
+ )
try:
os.remove(full_file_path)
@@ -44,8 +51,13 @@ INHERIT += "create-spdx"
self.assertNotEqual(report, None)
self.assertNotEqual(report["SPDXID"], None)
- python = os.path.join(get_bb_var('STAGING_BINDIR', 'python3-spdx-tools-native'), 'nativepython3')
- validator = os.path.join(get_bb_var('STAGING_BINDIR', 'python3-spdx-tools-native'), 'pyspdxtools')
+ python = os.path.join(
+ get_bb_var("STAGING_BINDIR", "python3-spdx-tools-native"),
+ "nativepython3",
+ )
+ validator = os.path.join(
+ get_bb_var("STAGING_BINDIR", "python3-spdx-tools-native"), "pyspdxtools"
+ )
result = runCmd("{} {} -i {}".format(python, validator, filename))
self.assertExists(full_file_path)
@@ -53,3 +65,106 @@ INHERIT += "create-spdx"
def test_spdx_base_files(self):
self.check_recipe_spdx("packages", "base-files.spdx.json", "base-files")
+
+
+class SPDX3CheckBase(object):
+ """
+ Base class for checking SPDX 3 based tests
+ """
+
+ def check_spdx_file(self, filename):
+ import oe.spdx30
+
+ self.assertExists(filename)
+
+ # Read the file
+ objset = oe.spdx30.SHACLObjectSet()
+ with open(filename, "r") as f:
+ d = oe.spdx30.JSONLDDeserializer()
+ d.read(f, objset)
+
+ return objset
+
+ def check_recipe_spdx(self, target_name, spdx_path, *, task=None, extraconf=""):
+ config = textwrap.dedent(
+ f"""\
+ INHERIT:remove = "create-spdx"
+ INHERIT += "{self.SPDX_CLASS}"
+ {extraconf}
+ """
+ )
+ self.write_config(config)
+
+ if task:
+ bitbake(f"-c {task} {target_name}")
+ else:
+ bitbake(target_name)
+
+ filename = spdx_path.format(
+ **get_bb_vars(
+ [
+ "DEPLOY_DIR_IMAGE",
+ "DEPLOY_DIR_SPDX",
+ "MACHINE",
+ "MACHINE_ARCH",
+ "SDKMACHINE",
+ "SDK_DEPLOY",
+ "SPDX_VERSION",
+ "TOOLCHAIN_OUTPUTNAME",
+ ],
+ target_name,
+ )
+ )
+
+ return self.check_spdx_file(filename)
+
+ def check_objset_missing_ids(self, objset):
+ if objset.missing_ids:
+ self.assertTrue(
+ False,
+ "The following SPDXIDs are unresolved:\n "
+ + "\n ".join(objset.missing_ids),
+ )
+
+
+class SPDX30Check(SPDX3CheckBase, OESelftestTestCase):
+ SPDX_CLASS = "create-spdx-3.0"
+
+ def test_base_files(self):
+ self.check_recipe_spdx(
+ "base-files",
+ "{DEPLOY_DIR_SPDX}/{MACHINE_ARCH}/packages/base-files.spdx.json",
+ )
+
+ def test_core_image_minimal(self):
+ objset = self.check_recipe_spdx(
+ "core-image-minimal",
+ "{DEPLOY_DIR_IMAGE}/core-image-minimal-{MACHINE}.rootfs.spdx.json",
+ )
+
+ # Document should be fully linked
+ self.check_objset_missing_ids(objset)
+
+ def test_core_image_minimal_sdk(self):
+ objset = self.check_recipe_spdx(
+ "core-image-minimal",
+ "{SDK_DEPLOY}/{TOOLCHAIN_OUTPUTNAME}.spdx.json",
+ task="populate_sdk",
+ )
+
+ # Document should be fully linked
+ self.check_objset_missing_ids(objset)
+
+ def test_baremetal_helloworld(self):
+ objset = self.check_recipe_spdx(
+ "baremetal-helloworld",
+ "{DEPLOY_DIR_IMAGE}/baremetal-helloworld-image-{MACHINE}.spdx.json",
+ extraconf=textwrap.dedent(
+ """\
+ TCLIBC = "baremetal"
+ """
+ ),
+ )
+
+ # Document should be fully linked
+ self.check_objset_missing_ids(objset)
diff --git a/poky/meta/lib/oeqa/selftest/cases/sstatetests.py b/poky/meta/lib/oeqa/selftest/cases/sstatetests.py
index 94ad6e38b6..fa0172dd6d 100644
--- a/poky/meta/lib/oeqa/selftest/cases/sstatetests.py
+++ b/poky/meta/lib/oeqa/selftest/cases/sstatetests.py
@@ -378,7 +378,6 @@ class SStateHashSameSigs(SStateBase):
self.write_config("""
MACHINE = "qemux86"
TMPDIR = "${TOPDIR}/tmp-sstatesamehash"
-TCLIBCAPPEND = ""
BUILD_ARCH = "x86_64"
BUILD_OS = "linux"
SDKMACHINE = "x86_64"
@@ -390,7 +389,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash"
self.write_config("""
MACHINE = "qemux86"
TMPDIR = "${TOPDIR}/tmp-sstatesamehash2"
-TCLIBCAPPEND = ""
BUILD_ARCH = "i686"
BUILD_OS = "linux"
SDKMACHINE = "i686"
@@ -426,7 +424,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash"
self.write_config("""
TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
-TCLIBCAPPEND = \"\"
NATIVELSBSTRING = \"DistroA\"
BB_SIGNATURE_HANDLER = "OEBasicHash"
""")
@@ -434,7 +431,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash"
bitbake("core-image-weston -S none")
self.write_config("""
TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
-TCLIBCAPPEND = \"\"
NATIVELSBSTRING = \"DistroB\"
BB_SIGNATURE_HANDLER = "OEBasicHash"
""")
@@ -463,17 +459,17 @@ class SStateHashSameSigs2(SStateBase):
configA = """
TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
-TCLIBCAPPEND = \"\"
MACHINE = \"qemux86-64\"
BB_SIGNATURE_HANDLER = "OEBasicHash"
"""
#OLDEST_KERNEL is arch specific so set to a different value here for testing
configB = """
TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
-TCLIBCAPPEND = \"\"
MACHINE = \"qemuarm\"
OLDEST_KERNEL = \"3.3.0\"
BB_SIGNATURE_HANDLER = "OEBasicHash"
+ERROR_QA:append = " somenewoption"
+WARN_QA:append = " someotheroption"
"""
self.sstate_common_samesigs(configA, configB, allarch=True)
@@ -484,7 +480,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash"
configA = """
TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
-TCLIBCAPPEND = \"\"
MACHINE = \"qemux86-64\"
require conf/multilib.conf
MULTILIBS = \"multilib:lib32\"
@@ -493,7 +488,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash"
"""
configB = """
TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
-TCLIBCAPPEND = \"\"
MACHINE = \"qemuarm\"
require conf/multilib.conf
MULTILIBS = \"\"
@@ -511,7 +505,6 @@ class SStateHashSameSigs3(SStateBase):
self.write_config("""
TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
-TCLIBCAPPEND = \"\"
MACHINE = \"qemux86\"
require conf/multilib.conf
MULTILIBS = "multilib:lib32"
@@ -522,7 +515,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash"
bitbake("world meta-toolchain -S none")
self.write_config("""
TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
-TCLIBCAPPEND = \"\"
MACHINE = \"qemux86copy\"
require conf/multilib.conf
MULTILIBS = "multilib:lib32"
@@ -559,7 +551,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash"
self.write_config("""
TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
-TCLIBCAPPEND = \"\"
MACHINE = \"qemux86\"
require conf/multilib.conf
MULTILIBS = "multilib:lib32"
@@ -570,7 +561,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash"
bitbake("binutils-native -S none")
self.write_config("""
TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
-TCLIBCAPPEND = \"\"
MACHINE = \"qemux86copy\"
BB_SIGNATURE_HANDLER = "OEBasicHash"
""")
@@ -598,7 +588,6 @@ class SStateHashSameSigs4(SStateBase):
self.write_config("""
TMPDIR = "${TOPDIR}/tmp-sstatesamehash"
-TCLIBCAPPEND = ""
BB_NUMBER_THREADS = "${@oe.utils.cpu_count()}"
PARALLEL_MAKE = "-j 1"
DL_DIR = "${TOPDIR}/download1"
@@ -613,7 +602,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash"
bitbake("world meta-toolchain -S none")
self.write_config("""
TMPDIR = "${TOPDIR}/tmp-sstatesamehash2"
-TCLIBCAPPEND = ""
BB_NUMBER_THREADS = "${@oe.utils.cpu_count()+1}"
PARALLEL_MAKE = "-j 2"
DL_DIR = "${TOPDIR}/download2"
@@ -724,7 +712,6 @@ class SStateFindSiginfo(SStateBase):
"""
self.write_config("""
TMPDIR = \"${TOPDIR}/tmp-sstates-findsiginfo\"
-TCLIBCAPPEND = \"\"
MACHINE = \"qemux86-64\"
require conf/multilib.conf
MULTILIBS = "multilib:lib32"
@@ -933,8 +920,7 @@ class SStateCheckObjectPresence(SStateBase):
# these get influnced by IMAGE_FSTYPES tweaks in yocto-autobuilder-helper's config.json (on x86-64)
# additionally, they depend on noexec (thus, absent stamps) package, install, etc. image tasks,
# which makes tracing other changes difficult
- exceptions += ["{}.*create_spdx".format(t) for t in targets.split()]
- exceptions += ["{}.*create_runtime_spdx".format(t) for t in targets.split()]
+ exceptions += ["{}.*create_.*spdx".format(t) for t in targets.split()]
output_l = output.splitlines()
for l in output_l:
@@ -977,7 +963,7 @@ class SStateMirrors(SStateCheckObjectPresence):
self.config_sstate(True)
self.append_config("""
MACHINE = "{}"
-BB_HASHSERVE_UPSTREAM = "hashserv.yocto.io:8687"
+BB_HASHSERVE_UPSTREAM = "hashserv.yoctoproject.org:8686"
SSTATE_MIRRORS ?= "file://.* http://cdn.jsdelivr.net/yocto/sstate/all/PATH;downloadfilename=PATH"
""".format(machine))
else:
@@ -992,12 +978,10 @@ MACHINE = "{}"
def test_cdn_mirror_qemux86_64(self):
exceptions = []
- self.run_test("qemux86-64", "core-image-minimal core-image-full-cmdline core-image-sato-sdk", exceptions, ignore_errors = True)
self.run_test("qemux86-64", "core-image-minimal core-image-full-cmdline core-image-sato-sdk", exceptions)
def test_cdn_mirror_qemuarm64(self):
exceptions = []
- self.run_test("qemuarm64", "core-image-minimal core-image-full-cmdline core-image-sato-sdk", exceptions, ignore_errors = True)
self.run_test("qemuarm64", "core-image-minimal core-image-full-cmdline core-image-sato-sdk", exceptions)
def test_local_cache_qemux86_64(self):
diff --git a/poky/meta/lib/oeqa/selftest/context.py b/poky/meta/lib/oeqa/selftest/context.py
index 99186175e5..acc3b073bd 100644
--- a/poky/meta/lib/oeqa/selftest/context.py
+++ b/poky/meta/lib/oeqa/selftest/context.py
@@ -117,8 +117,11 @@ class OESelftestTestContext(OETestContext):
newbblayers += 'BBLAYERS = "%s"\n' % ' '.join(bblayers_abspath)
f.write(newbblayers)
+ # Rewrite builddir paths seen in environment variables
for e in os.environ:
- if builddir + "/" in os.environ[e]:
+ # Rewrite paths that absolutely point inside builddir
+ # (e.g $builddir/conf/ would be rewritten but not $builddir/../bitbake/)
+ if builddir + "/" in os.environ[e] and builddir + "/" in os.path.abspath(os.environ[e]):
os.environ[e] = os.environ[e].replace(builddir + "/", newbuilddir + "/")
if os.environ[e].endswith(builddir):
os.environ[e] = os.environ[e].replace(builddir, newbuilddir)
diff --git a/poky/meta/lib/oeqa/utils/__init__.py b/poky/meta/lib/oeqa/utils/__init__.py
index 53bdcbf266..e03f7e33bb 100644
--- a/poky/meta/lib/oeqa/utils/__init__.py
+++ b/poky/meta/lib/oeqa/utils/__init__.py
@@ -96,4 +96,10 @@ def get_json_result_dir(d):
custom_json_result_dir = d.getVar("OEQA_JSON_RESULT_DIR")
if custom_json_result_dir:
json_result_dir = custom_json_result_dir
- return json_result_dir \ No newline at end of file
+ return json_result_dir
+
+def get_artefact_dir(d):
+ custom_json_result_dir = d.getVar("OEQA_ARTEFACT_DIR")
+ if custom_json_result_dir:
+ return custom_json_result_dir
+ return os.path.join(d.getVar("LOG_DIR"), 'oeqa-artefacts')
diff --git a/poky/meta/lib/oeqa/utils/gitarchive.py b/poky/meta/lib/oeqa/utils/gitarchive.py
index 10cb267dfa..a826646059 100644
--- a/poky/meta/lib/oeqa/utils/gitarchive.py
+++ b/poky/meta/lib/oeqa/utils/gitarchive.py
@@ -67,7 +67,7 @@ def git_commit_data(repo, data_dir, branch, message, exclude, notes, log):
# Remove files that are excluded
if exclude:
- repo.run_cmd(['rm', '--cached'] + [f for f in exclude], env_update)
+ repo.run_cmd(['rm', '--cached', '--ignore-unmatch'] + [f for f in exclude], env_update)
tree = repo.run_cmd('write-tree', env_update)
@@ -202,6 +202,8 @@ def gitarchive(data_dir, git_dir, no_create, bare, commit_msg_subject, commit_ms
log.info("Pushing data to remote")
data_repo.run_cmd(cmd)
+ return tag_name
+
# Container class for tester revisions
TestedRev = namedtuple('TestedRev', 'commit commit_number tags')
diff --git a/poky/meta/lib/oeqa/utils/postactions.py b/poky/meta/lib/oeqa/utils/postactions.py
index ecdddd2d40..8f787838b9 100644
--- a/poky/meta/lib/oeqa/utils/postactions.py
+++ b/poky/meta/lib/oeqa/utils/postactions.py
@@ -7,23 +7,20 @@
# Run a set of actions after tests. The runner provides internal data
# dictionary as well as test context to any action to run.
-from oeqa.utils import get_json_result_dir
-
-def create_artifacts_directory(d, tc):
- import shutil
-
- local_artifacts_dir = os.path.join(get_json_result_dir(d), "artifacts")
- if os.path.isdir(local_artifacts_dir):
- shutil.rmtree(local_artifacts_dir)
-
- os.makedirs(local_artifacts_dir)
+import datetime
+import io
+import os
+import stat
+import subprocess
+import tempfile
+from oeqa.utils import get_artefact_dir
##################################################################
# Host/target statistics
##################################################################
-def get_target_disk_usage(d, tc):
- output_file = os.path.join(get_json_result_dir(d), "artifacts", "target_disk_usage.txt")
+def get_target_disk_usage(d, tc, artifacts_list, outputdir):
+ output_file = os.path.join(outputdir, "target_disk_usage.txt")
try:
(status, output) = tc.target.run('df -h')
with open(output_file, 'w') as f:
@@ -32,10 +29,10 @@ def get_target_disk_usage(d, tc):
except Exception as e:
bb.warn(f"Can not get target disk usage: {e}")
-def get_host_disk_usage(d, tc):
+def get_host_disk_usage(d, tc, artifacts_list, outputdir):
import subprocess
- output_file = os.path.join(get_json_result_dir(d), "artifacts", "host_disk_usage.txt")
+ output_file = os.path.join(outputdir, "host_disk_usage.txt")
try:
with open(output_file, 'w') as f:
output = subprocess.run(['df', '-hl'], check=True, text=True, stdout=f, env={})
@@ -61,25 +58,21 @@ def get_artifacts_list(target, raw_list):
return result
-def retrieve_test_artifacts(target, artifacts_list, target_dir):
- local_artifacts_dir = os.path.join(target_dir, "artifacts")
- for artifact_path in artifacts_list:
- if not os.path.isabs(artifact_path):
- bb.warn(f"{artifact_path} is not an absolute path")
- continue
- try:
- dest_dir = os.path.join(local_artifacts_dir, os.path.dirname(artifact_path[1:]))
- os.makedirs(dest_dir, exist_ok=True)
- target.copyFrom(artifact_path, dest_dir)
- except Exception as e:
- bb.warn(f"Can not retrieve {artifact_path} from test target: {e}")
-
-def list_and_fetch_failed_tests_artifacts(d, tc):
- artifacts_list = get_artifacts_list(tc.target, d.getVar("TESTIMAGE_FAILED_QA_ARTIFACTS"))
+def list_and_fetch_failed_tests_artifacts(d, tc, artifacts_list, outputdir):
+ artifacts_list = get_artifacts_list(tc.target, artifacts_list)
if not artifacts_list:
bb.warn("Could not load artifacts list, skip artifacts retrieval")
- else:
- retrieve_test_artifacts(tc.target, artifacts_list, get_json_result_dir(d))
+ return
+ try:
+ cmd = "tar zcf - " + " ".join(artifacts_list)
+ (status, output) = tc.target.run(cmd, raw = True)
+ if status != 0 or not output:
+ raise Exception("Error while fetching compressed artifacts")
+ archive_name = os.path.join(outputdir, "tests_artifacts.tar.gz")
+ with open(archive_name, "wb") as f:
+ f.write(output)
+ except Exception as e:
+ bb.warn(f"Can not retrieve artifacts from test target: {e}")
##################################################################
@@ -87,12 +80,22 @@ def list_and_fetch_failed_tests_artifacts(d, tc):
##################################################################
def run_failed_tests_post_actions(d, tc):
+ artifacts = d.getVar("TESTIMAGE_FAILED_QA_ARTIFACTS")
+ # Allow all the code to be disabled by having no artifacts set, e.g. for systems with no ssh support
+ if not artifacts:
+ return
+
+ outputdir = get_artefact_dir(d)
+ os.makedirs(outputdir, exist_ok=True)
+ datestr = datetime.datetime.now().strftime('%Y%m%d')
+ outputdir = tempfile.mkdtemp(prefix='oeqa-target-artefacts-%s-' % datestr, dir=outputdir)
+ os.chmod(outputdir, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
+
post_actions=[
- create_artifacts_directory,
list_and_fetch_failed_tests_artifacts,
get_target_disk_usage,
get_host_disk_usage
]
for action in post_actions:
- action(d, tc)
+ action(d, tc, artifacts, outputdir)
diff --git a/poky/meta/lib/oeqa/utils/qemurunner.py b/poky/meta/lib/oeqa/utils/qemurunner.py
index cda43aad8c..f1c2d2b5c9 100644
--- a/poky/meta/lib/oeqa/utils/qemurunner.py
+++ b/poky/meta/lib/oeqa/utils/qemurunner.py
@@ -519,7 +519,6 @@ class QemuRunner:
except Exception as e:
self.logger.warning('Extra log data exception %s' % repr(e))
data = None
- self.thread.serial_lock.release()
return False
with self.thread.serial_lock:
@@ -822,10 +821,12 @@ class LoggingThread(threading.Thread):
self.logfunc(data, ".stdout")
elif self.serialsock and self.serialsock.fileno() == fd:
if self.serial_lock.acquire(blocking=False):
- data = self.recv(1024, self.serialsock)
- self.logger.debug("Data received serial thread %s" % data.decode('utf-8', 'replace'))
- self.logfunc(data, ".2")
- self.serial_lock.release()
+ try:
+ data = self.recv(1024, self.serialsock)
+ self.logger.debug("Data received serial thread %s" % data.decode('utf-8', 'replace'))
+ self.logfunc(data, ".2")
+ finally:
+ self.serial_lock.release()
else:
serial_registered = False
poll.unregister(self.serialsock.fileno())