summaryrefslogtreecommitdiffstats
path: root/import-layers/yocto-poky/meta/lib/oeqa
diff options
context:
space:
mode:
Diffstat (limited to 'import-layers/yocto-poky/meta/lib/oeqa')
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/__init__.py0
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/buildperf/__init__.py19
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/buildperf/base.py551
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/buildperf/test_basic.py127
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/controllers/masterimage.py44
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/oetest.py385
-rwxr-xr-ximport-layers/yocto-poky/meta/lib/oeqa/runexported.py48
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/runtime/__init__.py3
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/runtime/_ptest.py4
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/runtime/buildgalculator.py23
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/runtime/buildiptables.py2
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/runtime/buildsudoku.py28
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/runtime/connman.py2
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/runtime/files/test.py2
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/runtime/parselogs.py47
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/runtime/ping.py2
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/runtime/python.py2
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/runtime/rpm.py33
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/runtime/smart.py47
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/runtime/syslog.py13
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/runtime/systemd.py8
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/sdk/buildgalculator.py27
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/sdk/buildiptables.py2
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/sdk/buildsudoku.py26
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/sdkext/devtool.py90
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/sdkext/files/myapp_cmake/CMakeLists.txt11
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/sdkext/files/myapp_cmake/myapp.c9
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/sdkext/sdk_update.py3
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/selftest/_toaster.py4
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/selftest/base.py34
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/selftest/bbtests.py11
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/selftest/buildoptions.py91
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/selftest/devtool.py210
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/selftest/eSDK.py103
-rwxr-xr-ximport-layers/yocto-poky/meta/lib/oeqa/selftest/esdk_prepare.py75
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/selftest/imagefeatures.py27
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/selftest/liboe.py93
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/selftest/lic-checksum.py10
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/selftest/pkgdata.py56
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/selftest/recipetool.py55
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/selftest/runtime-test.py105
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/selftest/signing.py34
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/selftest/sstatetests.py75
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/selftest/wic.py25
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/targetcontrol.py28
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/utils/commands.py71
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/utils/decorators.py40
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/utils/dump.py2
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/utils/git.py68
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/utils/httpserver.py7
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/utils/logparser.py2
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/utils/package_manager.py29
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/utils/qemurunner.py90
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/utils/qemutinyrunner.py16
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/utils/sshcontrol.py116
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/utils/targetbuild.py14
-rw-r--r--import-layers/yocto-poky/meta/lib/oeqa/utils/testexport.py8
57 files changed, 2501 insertions, 556 deletions
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/__init__.py b/import-layers/yocto-poky/meta/lib/oeqa/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/__init__.py
+++ /dev/null
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/buildperf/__init__.py b/import-layers/yocto-poky/meta/lib/oeqa/buildperf/__init__.py
new file mode 100644
index 000000000..605f429ec
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/buildperf/__init__.py
@@ -0,0 +1,19 @@
+# Copyright (c) 2016, Intel Corporation.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU General Public License,
+# version 2, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+"""Build performance tests"""
+from .base import (BuildPerfTestCase,
+ BuildPerfTestLoader,
+ BuildPerfTestResult,
+ BuildPerfTestRunner,
+ KernelDropCaches,
+ runCmd2)
+from .test_basic import *
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/buildperf/base.py b/import-layers/yocto-poky/meta/lib/oeqa/buildperf/base.py
new file mode 100644
index 000000000..59dd02521
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/buildperf/base.py
@@ -0,0 +1,551 @@
+# Copyright (c) 2016, Intel Corporation.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU General Public License,
+# version 2, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+"""Build performance test base classes and functionality"""
+import glob
+import json
+import logging
+import os
+import re
+import resource
+import shutil
+import socket
+import time
+import traceback
+import unittest
+from datetime import datetime, timedelta
+from functools import partial
+from multiprocessing import Process
+from multiprocessing import SimpleQueue
+
+import oe.path
+from oeqa.utils.commands import CommandError, runCmd, get_bb_vars
+from oeqa.utils.git import GitError, GitRepo
+
+# Get logger for this module
+log = logging.getLogger('build-perf')
+
+# Our own version of runCmd which does not raise AssertErrors which would cause
+# errors to interpreted as failures
+runCmd2 = partial(runCmd, assert_error=False)
+
+
+class KernelDropCaches(object):
+ """Container of the functions for dropping kernel caches"""
+ sudo_passwd = None
+
+ @classmethod
+ def check(cls):
+ """Check permssions for dropping kernel caches"""
+ from getpass import getpass
+ from locale import getdefaultlocale
+ cmd = ['sudo', '-k', '-n', 'tee', '/proc/sys/vm/drop_caches']
+ ret = runCmd2(cmd, ignore_status=True, data=b'0')
+ if ret.output.startswith('sudo:'):
+ pass_str = getpass(
+ "\nThe script requires sudo access to drop caches between "
+ "builds (echo 3 > /proc/sys/vm/drop_caches).\n"
+ "Please enter your sudo password: ")
+ cls.sudo_passwd = bytes(pass_str, getdefaultlocale()[1])
+
+ @classmethod
+ def drop(cls):
+ """Drop kernel caches"""
+ cmd = ['sudo', '-k']
+ if cls.sudo_passwd:
+ cmd.append('-S')
+ input_data = cls.sudo_passwd + b'\n'
+ else:
+ cmd.append('-n')
+ input_data = b''
+ cmd += ['tee', '/proc/sys/vm/drop_caches']
+ input_data += b'3'
+ runCmd2(cmd, data=input_data)
+
+
+def str_to_fn(string):
+ """Convert string to a sanitized filename"""
+ return re.sub(r'(\W+)', '-', string, flags=re.LOCALE)
+
+
+class ResultsJsonEncoder(json.JSONEncoder):
+ """Extended encoder for build perf test results"""
+ unix_epoch = datetime.utcfromtimestamp(0)
+
+ def default(self, obj):
+ """Encoder for our types"""
+ if isinstance(obj, datetime):
+ # NOTE: we assume that all timestamps are in UTC time
+ return (obj - self.unix_epoch).total_seconds()
+ if isinstance(obj, timedelta):
+ return obj.total_seconds()
+ return json.JSONEncoder.default(self, obj)
+
+
+class BuildPerfTestResult(unittest.TextTestResult):
+ """Runner class for executing the individual tests"""
+ # List of test cases to run
+ test_run_queue = []
+
+ def __init__(self, out_dir, *args, **kwargs):
+ super(BuildPerfTestResult, self).__init__(*args, **kwargs)
+
+ self.out_dir = out_dir
+ # Get Git parameters
+ try:
+ self.repo = GitRepo('.')
+ except GitError:
+ self.repo = None
+ self.git_commit, self.git_commit_count, self.git_branch = \
+ self.get_git_revision()
+ self.hostname = socket.gethostname()
+ self.product = os.getenv('OE_BUILDPERFTEST_PRODUCT', 'oe-core')
+ self.start_time = self.elapsed_time = None
+ self.successes = []
+ log.info("Using Git branch:commit %s:%s (%s)", self.git_branch,
+ self.git_commit, self.git_commit_count)
+
+ def get_git_revision(self):
+ """Get git branch and commit under testing"""
+ commit = os.getenv('OE_BUILDPERFTEST_GIT_COMMIT')
+ commit_cnt = os.getenv('OE_BUILDPERFTEST_GIT_COMMIT_COUNT')
+ branch = os.getenv('OE_BUILDPERFTEST_GIT_BRANCH')
+ if not self.repo and (not commit or not commit_cnt or not branch):
+ log.info("The current working directory doesn't seem to be a Git "
+ "repository clone. You can specify branch and commit "
+ "displayed in test results with OE_BUILDPERFTEST_GIT_BRANCH, "
+ "OE_BUILDPERFTEST_GIT_COMMIT and "
+ "OE_BUILDPERFTEST_GIT_COMMIT_COUNT environment variables")
+ else:
+ if not commit:
+ commit = self.repo.rev_parse('HEAD^0')
+ commit_cnt = self.repo.run_cmd(['rev-list', '--count', 'HEAD^0'])
+ if not branch:
+ branch = self.repo.get_current_branch()
+ if not branch:
+ log.debug('Currently on detached HEAD')
+ return str(commit), str(commit_cnt), str(branch)
+
+ def addSuccess(self, test):
+ """Record results from successful tests"""
+ super(BuildPerfTestResult, self).addSuccess(test)
+ self.successes.append((test, None))
+
+ def startTest(self, test):
+ """Pre-test hook"""
+ test.base_dir = self.out_dir
+ os.mkdir(test.out_dir)
+ log.info("Executing test %s: %s", test.name, test.shortDescription())
+ self.stream.write(datetime.now().strftime("[%Y-%m-%d %H:%M:%S] "))
+ super(BuildPerfTestResult, self).startTest(test)
+
+ def startTestRun(self):
+ """Pre-run hook"""
+ self.start_time = datetime.utcnow()
+
+ def stopTestRun(self):
+ """Pre-run hook"""
+ self.elapsed_time = datetime.utcnow() - self.start_time
+ self.write_results_json()
+
+ def all_results(self):
+ result_map = {'SUCCESS': self.successes,
+ 'FAIL': self.failures,
+ 'ERROR': self.errors,
+ 'EXP_FAIL': self.expectedFailures,
+ 'UNEXP_SUCCESS': self.unexpectedSuccesses,
+ 'SKIPPED': self.skipped}
+ for status, tests in result_map.items():
+ for test in tests:
+ yield (status, test)
+
+
+ def update_globalres_file(self, filename):
+ """Write results to globalres csv file"""
+ # Map test names to time and size columns in globalres
+ # The tuples represent index and length of times and sizes
+ # respectively
+ gr_map = {'test1': ((0, 1), (8, 1)),
+ 'test12': ((1, 1), (None, None)),
+ 'test13': ((2, 1), (9, 1)),
+ 'test2': ((3, 1), (None, None)),
+ 'test3': ((4, 3), (None, None)),
+ 'test4': ((7, 1), (10, 2))}
+
+ if self.repo:
+ git_tag_rev = self.repo.run_cmd(['describe', self.git_commit])
+ else:
+ git_tag_rev = self.git_commit
+
+ values = ['0'] * 12
+ for status, (test, msg) in self.all_results():
+ if status in ['ERROR', 'SKIPPED']:
+ continue
+ (t_ind, t_len), (s_ind, s_len) = gr_map[test.name]
+ if t_ind is not None:
+ values[t_ind:t_ind + t_len] = test.times
+ if s_ind is not None:
+ values[s_ind:s_ind + s_len] = test.sizes
+
+ log.debug("Writing globalres log to %s", filename)
+ with open(filename, 'a') as fobj:
+ fobj.write('{},{}:{},{},'.format(self.hostname,
+ self.git_branch,
+ self.git_commit,
+ git_tag_rev))
+ fobj.write(','.join(values) + '\n')
+
+ def write_results_json(self):
+ """Write test results into a json-formatted file"""
+ results = {'tester_host': self.hostname,
+ 'git_branch': self.git_branch,
+ 'git_commit': self.git_commit,
+ 'git_commit_count': self.git_commit_count,
+ 'product': self.product,
+ 'start_time': self.start_time,
+ 'elapsed_time': self.elapsed_time}
+
+ tests = {}
+ for status, (test, reason) in self.all_results():
+ tests[test.name] = {'name': test.name,
+ 'description': test.shortDescription(),
+ 'status': status,
+ 'start_time': test.start_time,
+ 'elapsed_time': test.elapsed_time,
+ 'cmd_log_file': os.path.relpath(test.cmd_log_file,
+ self.out_dir),
+ 'measurements': test.measurements}
+ results['tests'] = tests
+
+ with open(os.path.join(self.out_dir, 'results.json'), 'w') as fobj:
+ json.dump(results, fobj, indent=4, sort_keys=True,
+ cls=ResultsJsonEncoder)
+
+
+ def git_commit_results(self, repo_path, branch=None, tag=None):
+ """Commit results into a Git repository"""
+ repo = GitRepo(repo_path, is_topdir=True)
+ if not branch:
+ branch = self.git_branch
+ else:
+ # Replace keywords
+ branch = branch.format(git_branch=self.git_branch,
+ tester_host=self.hostname)
+
+ log.info("Committing test results into %s %s", repo_path, branch)
+ tmp_index = os.path.join(repo_path, '.git', 'index.oe-build-perf')
+ try:
+ # Create new commit object from the new results
+ env_update = {'GIT_INDEX_FILE': tmp_index,
+ 'GIT_WORK_TREE': self.out_dir}
+ repo.run_cmd('add .', env_update)
+ tree = repo.run_cmd('write-tree', env_update)
+ parent = repo.rev_parse(branch)
+ msg = "Results of {}:{}\n".format(self.git_branch, self.git_commit)
+ git_cmd = ['commit-tree', tree, '-m', msg]
+ if parent:
+ git_cmd += ['-p', parent]
+ commit = repo.run_cmd(git_cmd, env_update)
+
+ # Update branch head
+ git_cmd = ['update-ref', 'refs/heads/' + branch, commit]
+ if parent:
+ git_cmd.append(parent)
+ repo.run_cmd(git_cmd)
+
+ # Update current HEAD, if we're on branch 'branch'
+ if repo.get_current_branch() == branch:
+ log.info("Updating %s HEAD to latest commit", repo_path)
+ repo.run_cmd('reset --hard')
+
+ # Create (annotated) tag
+ if tag:
+ # Find tags matching the pattern
+ tag_keywords = dict(git_branch=self.git_branch,
+ git_commit=self.git_commit,
+ git_commit_count=self.git_commit_count,
+ tester_host=self.hostname,
+ tag_num='[0-9]{1,5}')
+ tag_re = re.compile(tag.format(**tag_keywords) + '$')
+ tag_keywords['tag_num'] = 0
+ for existing_tag in repo.run_cmd('tag').splitlines():
+ if tag_re.match(existing_tag):
+ tag_keywords['tag_num'] += 1
+
+ tag = tag.format(**tag_keywords)
+ msg = "Test run #{} of {}:{}\n".format(tag_keywords['tag_num'],
+ self.git_branch,
+ self.git_commit)
+ repo.run_cmd(['tag', '-a', '-m', msg, tag, commit])
+
+ finally:
+ if os.path.exists(tmp_index):
+ os.unlink(tmp_index)
+
+
+class BuildPerfTestCase(unittest.TestCase):
+ """Base class for build performance tests"""
+ SYSRES = 'sysres'
+ DISKUSAGE = 'diskusage'
+ build_target = None
+
+ def __init__(self, *args, **kwargs):
+ super(BuildPerfTestCase, self).__init__(*args, **kwargs)
+ self.name = self._testMethodName
+ self.base_dir = None
+ self.start_time = None
+ self.elapsed_time = None
+ self.measurements = []
+ self.bb_vars = get_bb_vars()
+ # TODO: remove 'times' and 'sizes' arrays when globalres support is
+ # removed
+ self.times = []
+ self.sizes = []
+
+ @property
+ def out_dir(self):
+ return os.path.join(self.base_dir, self.name)
+
+ @property
+ def cmd_log_file(self):
+ return os.path.join(self.out_dir, 'commands.log')
+
+ def setUp(self):
+ """Set-up fixture for each test"""
+ if self.build_target:
+ self.log_cmd_output(['bitbake', self.build_target,
+ '-c', 'fetchall'])
+
+ def run(self, *args, **kwargs):
+ """Run test"""
+ self.start_time = datetime.now()
+ super(BuildPerfTestCase, self).run(*args, **kwargs)
+ self.elapsed_time = datetime.now() - self.start_time
+
+ def log_cmd_output(self, cmd):
+ """Run a command and log it's output"""
+ cmd_str = cmd if isinstance(cmd, str) else ' '.join(cmd)
+ log.info("Logging command: %s", cmd_str)
+ try:
+ with open(self.cmd_log_file, 'a') as fobj:
+ runCmd2(cmd, stdout=fobj)
+ except CommandError as err:
+ log.error("Command failed: %s", err.retcode)
+ raise
+
+ def measure_cmd_resources(self, cmd, name, legend, save_bs=False):
+ """Measure system resource usage of a command"""
+ def _worker(data_q, cmd, **kwargs):
+ """Worker process for measuring resources"""
+ try:
+ start_time = datetime.now()
+ ret = runCmd2(cmd, **kwargs)
+ etime = datetime.now() - start_time
+ rusage_struct = resource.getrusage(resource.RUSAGE_CHILDREN)
+ iostat = {}
+ with open('/proc/{}/io'.format(os.getpid())) as fobj:
+ for line in fobj.readlines():
+ key, val = line.split(':')
+ iostat[key] = int(val)
+ rusage = {}
+ # Skip unused fields, (i.e. 'ru_ixrss', 'ru_idrss', 'ru_isrss',
+ # 'ru_nswap', 'ru_msgsnd', 'ru_msgrcv' and 'ru_nsignals')
+ for key in ['ru_utime', 'ru_stime', 'ru_maxrss', 'ru_minflt',
+ 'ru_majflt', 'ru_inblock', 'ru_oublock',
+ 'ru_nvcsw', 'ru_nivcsw']:
+ rusage[key] = getattr(rusage_struct, key)
+ data_q.put({'ret': ret,
+ 'start_time': start_time,
+ 'elapsed_time': etime,
+ 'rusage': rusage,
+ 'iostat': iostat})
+ except Exception as err:
+ data_q.put(err)
+
+ cmd_str = cmd if isinstance(cmd, str) else ' '.join(cmd)
+ log.info("Timing command: %s", cmd_str)
+ data_q = SimpleQueue()
+ try:
+ with open(self.cmd_log_file, 'a') as fobj:
+ proc = Process(target=_worker, args=(data_q, cmd,),
+ kwargs={'stdout': fobj})
+ proc.start()
+ data = data_q.get()
+ proc.join()
+ if isinstance(data, Exception):
+ raise data
+ except CommandError:
+ log.error("Command '%s' failed, see %s for more details", cmd_str,
+ self.cmd_log_file)
+ raise
+ etime = data['elapsed_time']
+
+ measurement = {'type': self.SYSRES,
+ 'name': name,
+ 'legend': legend}
+ measurement['values'] = {'start_time': data['start_time'],
+ 'elapsed_time': etime,
+ 'rusage': data['rusage'],
+ 'iostat': data['iostat']}
+ if save_bs:
+ bs_file = self.save_buildstats(legend)
+ measurement['values']['buildstats_file'] = \
+ os.path.relpath(bs_file, self.base_dir)
+
+ self.measurements.append(measurement)
+
+ # Append to 'times' array for globalres log
+ e_sec = etime.total_seconds()
+ self.times.append('{:d}:{:02d}:{:05.2f}'.format(int(e_sec / 3600),
+ int((e_sec % 3600) / 60),
+ e_sec % 60))
+
+ def measure_disk_usage(self, path, name, legend, apparent_size=False):
+ """Estimate disk usage of a file or directory"""
+ cmd = ['du', '-s', '--block-size', '1024']
+ if apparent_size:
+ cmd.append('--apparent-size')
+ cmd.append(path)
+
+ ret = runCmd2(cmd)
+ size = int(ret.output.split()[0])
+ log.debug("Size of %s path is %s", path, size)
+ measurement = {'type': self.DISKUSAGE,
+ 'name': name,
+ 'legend': legend}
+ measurement['values'] = {'size': size}
+ self.measurements.append(measurement)
+ # Append to 'sizes' array for globalres log
+ self.sizes.append(str(size))
+
+ def save_buildstats(self, label=None):
+ """Save buildstats"""
+ def split_nevr(nevr):
+ """Split name and version information from recipe "nevr" string"""
+ n_e_v, revision = nevr.rsplit('-', 1)
+ match = re.match(r'^(?P<name>\S+)-((?P<epoch>[0-9]{1,5})_)?(?P<version>[0-9]\S*)$',
+ n_e_v)
+ if not match:
+ # If we're not able to parse a version starting with a number, just
+ # take the part after last dash
+ match = re.match(r'^(?P<name>\S+)-((?P<epoch>[0-9]{1,5})_)?(?P<version>[^-]+)$',
+ n_e_v)
+ name = match.group('name')
+ version = match.group('version')
+ epoch = match.group('epoch')
+ return name, epoch, version, revision
+
+ def bs_to_json(filename):
+ """Convert (task) buildstats file into json format"""
+ bs_json = {'iostat': {},
+ 'rusage': {},
+ 'child_rusage': {}}
+ with open(filename) as fobj:
+ for line in fobj.readlines():
+ key, val = line.split(':', 1)
+ val = val.strip()
+ if key == 'Started':
+ start_time = datetime.utcfromtimestamp(float(val))
+ bs_json['start_time'] = start_time
+ elif key == 'Ended':
+ end_time = datetime.utcfromtimestamp(float(val))
+ elif key.startswith('IO '):
+ split = key.split()
+ bs_json['iostat'][split[1]] = int(val)
+ elif key.find('rusage') >= 0:
+ split = key.split()
+ ru_key = split[-1]
+ if ru_key in ('ru_stime', 'ru_utime'):
+ val = float(val)
+ else:
+ val = int(val)
+ ru_type = 'rusage' if split[0] == 'rusage' else \
+ 'child_rusage'
+ bs_json[ru_type][ru_key] = val
+ elif key == 'Status':
+ bs_json['status'] = val
+ bs_json['elapsed_time'] = end_time - start_time
+ return bs_json
+
+ log.info('Saving buildstats in JSON format')
+ bs_dirs = sorted(os.listdir(self.bb_vars['BUILDSTATS_BASE']))
+ if len(bs_dirs) > 1:
+ log.warning("Multiple buildstats found for test %s, only "
+ "archiving the last one", self.name)
+ bs_dir = os.path.join(self.bb_vars['BUILDSTATS_BASE'], bs_dirs[-1])
+
+ buildstats = []
+ for fname in os.listdir(bs_dir):
+ recipe_dir = os.path.join(bs_dir, fname)
+ if not os.path.isdir(recipe_dir):
+ continue
+ name, epoch, version, revision = split_nevr(fname)
+ recipe_bs = {'name': name,
+ 'epoch': epoch,
+ 'version': version,
+ 'revision': revision,
+ 'tasks': {}}
+ for task in os.listdir(recipe_dir):
+ recipe_bs['tasks'][task] = bs_to_json(os.path.join(recipe_dir,
+ task))
+ buildstats.append(recipe_bs)
+
+ # Write buildstats into json file
+ postfix = '.' + str_to_fn(label) if label else ''
+ postfix += '.json'
+ outfile = os.path.join(self.out_dir, 'buildstats' + postfix)
+ with open(outfile, 'w') as fobj:
+ json.dump(buildstats, fobj, indent=4, sort_keys=True,
+ cls=ResultsJsonEncoder)
+ return outfile
+
+ def rm_tmp(self):
+ """Cleanup temporary/intermediate files and directories"""
+ log.debug("Removing temporary and cache files")
+ for name in ['bitbake.lock', 'conf/sanity_info',
+ self.bb_vars['TMPDIR']]:
+ oe.path.remove(name, recurse=True)
+
+ def rm_sstate(self):
+ """Remove sstate directory"""
+ log.debug("Removing sstate-cache")
+ oe.path.remove(self.bb_vars['SSTATE_DIR'], recurse=True)
+
+ def rm_cache(self):
+ """Drop bitbake caches"""
+ oe.path.remove(self.bb_vars['PERSISTENT_DIR'], recurse=True)
+
+ @staticmethod
+ def sync():
+ """Sync and drop kernel caches"""
+ log.debug("Syncing and dropping kernel caches""")
+ KernelDropCaches.drop()
+ os.sync()
+ # Wait a bit for all the dirty blocks to be written onto disk
+ time.sleep(3)
+
+
+class BuildPerfTestLoader(unittest.TestLoader):
+ """Test loader for build performance tests"""
+ sortTestMethodsUsing = None
+
+
+class BuildPerfTestRunner(unittest.TextTestRunner):
+ """Test loader for build performance tests"""
+ sortTestMethodsUsing = None
+
+ def __init__(self, out_dir, *args, **kwargs):
+ super(BuildPerfTestRunner, self).__init__(*args, **kwargs)
+ self.out_dir = out_dir
+
+ def _makeResult(self):
+ return BuildPerfTestResult(self.out_dir, self.stream, self.descriptions,
+ self.verbosity)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/buildperf/test_basic.py b/import-layers/yocto-poky/meta/lib/oeqa/buildperf/test_basic.py
new file mode 100644
index 000000000..7a48c1e77
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/buildperf/test_basic.py
@@ -0,0 +1,127 @@
+# Copyright (c) 2016, Intel Corporation.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU General Public License,
+# version 2, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+"""Basic set of build performance tests"""
+import os
+import shutil
+
+import oe.path
+from oeqa.buildperf import BuildPerfTestCase
+from oeqa.utils.commands import get_bb_vars
+
+
+class Test1P1(BuildPerfTestCase):
+ build_target = 'core-image-sato'
+
+ def test1(self):
+ """Measure wall clock of bitbake core-image-sato and size of tmp dir"""
+ self.rm_tmp()
+ self.rm_sstate()
+ self.rm_cache()
+ self.sync()
+ self.measure_cmd_resources(['bitbake', self.build_target], 'build',
+ 'bitbake ' + self.build_target, save_bs=True)
+ self.measure_disk_usage(self.bb_vars['TMPDIR'], 'tmpdir', 'tmpdir')
+
+
+class Test1P2(BuildPerfTestCase):
+ build_target = 'virtual/kernel'
+
+ def test12(self):
+ """Measure bitbake virtual/kernel"""
+ # Build and cleans state in order to get all dependencies pre-built
+ self.log_cmd_output(['bitbake', self.build_target])
+ self.log_cmd_output(['bitbake', self.build_target, '-c', 'cleansstate'])
+
+ self.sync()
+ self.measure_cmd_resources(['bitbake', self.build_target], 'build',
+ 'bitbake ' + self.build_target)
+
+
+class Test1P3(BuildPerfTestCase):
+ build_target = 'core-image-sato'
+
+ def test13(self):
+ """Build core-image-sato with rm_work enabled"""
+ postfile = os.path.join(self.out_dir, 'postfile.conf')
+ with open(postfile, 'w') as fobj:
+ fobj.write('INHERIT += "rm_work"\n')
+ try:
+ self.rm_tmp()
+ self.rm_sstate()
+ self.rm_cache()
+ self.sync()
+ cmd = ['bitbake', '-R', postfile, self.build_target]
+ self.measure_cmd_resources(cmd, 'build',
+ 'bitbake' + self.build_target,
+ save_bs=True)
+ self.measure_disk_usage(self.bb_vars['TMPDIR'], 'tmpdir', 'tmpdir')
+ finally:
+ os.unlink(postfile)
+
+
+class Test2(BuildPerfTestCase):
+ build_target = 'core-image-sato'
+
+ def test2(self):
+ """Measure bitbake core-image-sato -c rootfs with sstate"""
+ # Build once in order to populate sstate cache
+ self.log_cmd_output(['bitbake', self.build_target])
+
+ self.rm_tmp()
+ self.rm_cache()
+ self.sync()
+ cmd = ['bitbake', self.build_target, '-c', 'rootfs']
+ self.measure_cmd_resources(cmd, 'do_rootfs', 'bitbake do_rootfs')
+
+
+class Test3(BuildPerfTestCase):
+
+ def test3(self):
+ """Parsing time metrics (bitbake -p)"""
+ # Drop all caches and parse
+ self.rm_cache()
+ oe.path.remove(os.path.join(self.bb_vars['TMPDIR'], 'cache'), True)
+ self.measure_cmd_resources(['bitbake', '-p'], 'parse_1',
+ 'bitbake -p (no caches)')
+ # Drop tmp/cache
+ oe.path.remove(os.path.join(self.bb_vars['TMPDIR'], 'cache'), True)
+ self.measure_cmd_resources(['bitbake', '-p'], 'parse_2',
+ 'bitbake -p (no tmp/cache)')
+ # Parse with fully cached data
+ self.measure_cmd_resources(['bitbake', '-p'], 'parse_3',
+ 'bitbake -p (cached)')
+
+
+class Test4(BuildPerfTestCase):
+ build_target = 'core-image-sato'
+
+ def test4(self):
+ """eSDK metrics"""
+ self.log_cmd_output("bitbake {} -c do_populate_sdk_ext".format(
+ self.build_target))
+ self.bb_vars = get_bb_vars(None, self.build_target)
+ tmp_dir = self.bb_vars['TMPDIR']
+ installer = os.path.join(
+ self.bb_vars['SDK_DEPLOY'],
+ self.bb_vars['TOOLCHAINEXT_OUTPUTNAME'] + '.sh')
+ # Measure installer size
+ self.measure_disk_usage(installer, 'installer_bin', 'eSDK installer',
+ apparent_size=True)
+ # Measure deployment time and deployed size
+ deploy_dir = os.path.join(tmp_dir, 'esdk-deploy')
+ if os.path.exists(deploy_dir):
+ shutil.rmtree(deploy_dir)
+ self.sync()
+ self.measure_cmd_resources([installer, '-y', '-d', deploy_dir],
+ 'deploy', 'eSDK deploy')
+ self.measure_disk_usage(deploy_dir, 'deploy_dir', 'deploy dir',
+ apparent_size=True)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/controllers/masterimage.py b/import-layers/yocto-poky/meta/lib/oeqa/controllers/masterimage.py
index 522f9ebd7..9ce3bf803 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/controllers/masterimage.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/controllers/masterimage.py
@@ -24,9 +24,7 @@ from oeqa.utils import CommandError
from abc import ABCMeta, abstractmethod
-class MasterImageHardwareTarget(oeqa.targetcontrol.BaseTarget):
-
- __metaclass__ = ABCMeta
+class MasterImageHardwareTarget(oeqa.targetcontrol.BaseTarget, metaclass=ABCMeta):
supported_image_fstypes = ['tar.gz', 'tar.bz2']
@@ -199,3 +197,43 @@ class GummibootTarget(MasterImageHardwareTarget):
self.power_cycle(self.master)
# there are better ways than a timeout but this should work for now
time.sleep(120)
+
+
+class SystemdbootTarget(MasterImageHardwareTarget):
+
+ def __init__(self, d):
+ super(SystemdbootTarget, self).__init__(d)
+ # this the value we need to set in the LoaderEntryOneShot EFI variable
+ # so the system boots the 'test' bootloader label and not the default
+ # The first four bytes are EFI bits, and the rest is an utf-16le string
+ # (EFI vars values need to be utf-16)
+ # $ echo -en "test\0" | iconv -f ascii -t utf-16le | hexdump -C
+ # 00000000 74 00 65 00 73 00 74 00 00 00 |t.e.s.t...|
+ self.efivarvalue = r'\x07\x00\x00\x00\x74\x00\x65\x00\x73\x00\x74\x00\x00\x00'
+ self.deploy_cmds = [
+ 'mount -L boot /boot',
+ 'mkdir -p /mnt/testrootfs',
+ 'mount -L testrootfs /mnt/testrootfs',
+ 'modprobe efivarfs',
+ 'mount -t efivarfs efivarfs /sys/firmware/efi/efivars',
+ 'cp ~/test-kernel /boot',
+ 'rm -rf /mnt/testrootfs/*',
+ 'tar xvf ~/test-rootfs.%s -C /mnt/testrootfs' % self.image_fstype,
+ 'printf "%s" > /sys/firmware/efi/efivars/LoaderEntryOneShot-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f' % self.efivarvalue
+ ]
+
+ def _deploy(self):
+ # make sure these aren't mounted
+ self.master.run("umount /boot; umount /mnt/testrootfs; umount /sys/firmware/efi/efivars;")
+ # from now on, every deploy cmd should return 0
+ # else an exception will be thrown by sshcontrol
+ self.master.ignore_status = False
+ self.master.copy_to(self.rootfs, "~/test-rootfs." + self.image_fstype)
+ self.master.copy_to(self.kernel, "~/test-kernel")
+ for cmd in self.deploy_cmds:
+ self.master.run(cmd)
+
+ def _start(self, params=None):
+ self.power_cycle(self.master)
+ # there are better ways than a timeout but this should work for now
+ time.sleep(120)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/oetest.py b/import-layers/yocto-poky/meta/lib/oeqa/oetest.py
index 3ed5bb8c2..95d3bf72f 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/oetest.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/oetest.py
@@ -12,6 +12,8 @@ import unittest
import inspect
import subprocess
import signal
+import shutil
+import functools
try:
import bb
except ImportError:
@@ -54,18 +56,29 @@ def filterByTagExp(testsuite, tagexp):
@LogResults
class oeTest(unittest.TestCase):
+ pscmd = "ps"
longMessage = True
@classmethod
def hasPackage(self, pkg):
- for item in oeTest.tc.pkgmanifest.split('\n'):
- if re.match(pkg, item):
+ """
+ True if the full package name exists in the manifest, False otherwise.
+ """
+ return pkg in oeTest.tc.pkgmanifest
+
+ @classmethod
+ def hasPackageMatch(self, match):
+ """
+ True if match exists in the manifest as a regular expression substring,
+ False otherwise.
+ """
+ for s in oeTest.tc.pkgmanifest:
+ if re.match(match, s):
return True
return False
@classmethod
def hasFeature(self,feature):
-
if feature in oeTest.tc.imagefeatures or \
feature in oeTest.tc.distrofeatures:
return True
@@ -78,6 +91,9 @@ class oeRuntimeTest(oeTest):
super(oeRuntimeTest, self).__init__(methodName)
def setUp(self):
+ # Install packages in the DUT
+ self.tc.install_uninstall_packages(self.id())
+
# Check if test needs to run
if self.tc.sigterm:
self.fail("Got SIGTERM")
@@ -91,6 +107,9 @@ class oeRuntimeTest(oeTest):
pass
def tearDown(self):
+ # Unistall packages in the DUT
+ self.tc.install_uninstall_packages(self.id(), False)
+
res = getResults()
# If a test fails or there is an exception dump
# for QemuTarget only
@@ -117,6 +136,15 @@ class oeRuntimeTest(oeTest):
if status != 0:
return status
+class OETestCalledProcessError(subprocess.CalledProcessError):
+ def __str__(self):
+ if hasattr(self, "stderr"):
+ return "Command '%s' returned non-zero exit status %d with output %s and stderr %s" % (self.cmd, self.returncode, self.output, self.stderr)
+ else:
+ return "Command '%s' returned non-zero exit status %d with output %s" % (self.cmd, self.returncode, self.output)
+
+subprocess.CalledProcessError = OETestCalledProcessError
+
class oeSDKTest(oeTest):
def __init__(self, methodName='runTest'):
self.sdktestdir = oeSDKTest.tc.sdktestdir
@@ -124,13 +152,12 @@ class oeSDKTest(oeTest):
@classmethod
def hasHostPackage(self, pkg):
-
if re.search(pkg, oeTest.tc.hostpkgmanifest):
return True
return False
def _run(self, cmd):
- return subprocess.check_output(". %s > /dev/null; %s;" % (self.tc.sdkenv, cmd), shell=True)
+ return subprocess.check_output(". %s > /dev/null; %s;" % (self.tc.sdkenv, cmd), shell=True, stderr=subprocess.STDOUT).decode("utf-8")
class oeSDKExtTest(oeSDKTest):
def _run(self, cmd):
@@ -142,7 +169,7 @@ class oeSDKExtTest(oeSDKTest):
env['PATH'] = avoid_paths_in_environ(paths_to_avoid)
return subprocess.check_output(". %s > /dev/null;"\
- " %s;" % (self.tc.sdkenv, cmd), shell=True, env=env)
+ " %s;" % (self.tc.sdkenv, cmd), stderr=subprocess.STDOUT, shell=True, env=env).decode("utf-8")
def getmodule(pos=2):
# stack returns a list of tuples containg frame information
@@ -185,15 +212,22 @@ def custom_verbose(msg, *args, **kwargs):
_buffer_logger = ""
class TestContext(object):
- def __init__(self, d):
+ def __init__(self, d, exported=False):
self.d = d
self.testsuites = self._get_test_suites()
- self.testslist = self._get_tests_list(d.getVar("BBPATH", True).split(':'))
+
+ if exported:
+ path = [os.path.dirname(os.path.abspath(__file__))]
+ extrapath = ""
+ else:
+ path = d.getVar("BBPATH", True).split(':')
+ extrapath = "lib/oeqa"
+
+ self.testslist = self._get_tests_list(path, extrapath)
self.testsrequired = self._get_test_suites_required()
- self.filesdir = os.path.join(os.path.dirname(os.path.abspath(
- oeqa.runtime.__file__)), "files")
+ self.filesdir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "runtime/files")
self.imagefeatures = d.getVar("IMAGE_FEATURES", True).split()
self.distrofeatures = d.getVar("DISTRO_FEATURES", True).split()
@@ -212,7 +246,7 @@ class TestContext(object):
return " ".join(tcs)
# return test list by type also filter if TEST_SUITES is specified
- def _get_tests_list(self, bbpath):
+ def _get_tests_list(self, bbpath, extrapath):
testslist = []
type = self._get_test_namespace()
@@ -226,11 +260,11 @@ class TestContext(object):
continue
found = False
for p in bbpath:
- if os.path.exists(os.path.join(p, 'lib', 'oeqa', type, testname + '.py')):
+ if os.path.exists(os.path.join(p, extrapath, type, testname + ".py")):
testslist.append("oeqa." + type + "." + testname)
found = True
break
- elif os.path.exists(os.path.join(p, 'lib', 'oeqa', type, testname.split(".")[0] + '.py')):
+ elif os.path.exists(os.path.join(p, extrapath, type, testname.split(".")[0] + ".py")):
testslist.append("oeqa." + type + "." + testname)
found = True
break
@@ -239,8 +273,6 @@ class TestContext(object):
if "auto" in self.testsuites:
def add_auto_list(path):
- if not os.path.exists(os.path.join(path, '__init__.py')):
- bb.fatal('Tests directory %s exists but is missing __init__.py' % path)
files = sorted([f for f in os.listdir(path) if f.endswith('.py') and not f.startswith('_')])
for f in files:
module = 'oeqa.' + type + '.' + f[:-3]
@@ -255,6 +287,51 @@ class TestContext(object):
return testslist
+ def getTestModules(self):
+ """
+ Returns all the test modules in the testlist.
+ """
+
+ import pkgutil
+
+ modules = []
+ for test in self.testslist:
+ if re.search("\w+\.\w+\.test_\S+", test):
+ test = '.'.join(t.split('.')[:3])
+ module = pkgutil.get_loader(test)
+ modules.append(module)
+
+ return modules
+
+ def getModulefromID(self, test_id):
+ """
+ Returns the test module based on a test id.
+ """
+
+ module_name = ".".join(test_id.split(".")[:3])
+ modules = self.getTestModules()
+ for module in modules:
+ if module.name == module_name:
+ return module
+
+ return None
+
+ def getTests(self, test):
+ '''Return all individual tests executed when running the suite.'''
+ # Unfortunately unittest does not have an API for this, so we have
+ # to rely on implementation details. This only needs to work
+ # for TestSuite containing TestCase.
+ method = getattr(test, '_testMethodName', None)
+ if method:
+ # leaf case: a TestCase
+ yield test
+ else:
+ # Look into TestSuite.
+ tests = getattr(test, '_tests', [])
+ for t1 in tests:
+ for t2 in self.getTests(t1):
+ yield t2
+
def loadTests(self):
setattr(oeTest, "tc", self)
@@ -263,36 +340,20 @@ class TestContext(object):
suites = [testloader.loadTestsFromName(name) for name in self.testslist]
suites = filterByTagExp(suites, getattr(self, "tagexp", None))
- def getTests(test):
- '''Return all individual tests executed when running the suite.'''
- # Unfortunately unittest does not have an API for this, so we have
- # to rely on implementation details. This only needs to work
- # for TestSuite containing TestCase.
- method = getattr(test, '_testMethodName', None)
- if method:
- # leaf case: a TestCase
- yield test
- else:
- # Look into TestSuite.
- tests = getattr(test, '_tests', [])
- for t1 in tests:
- for t2 in getTests(t1):
- yield t2
-
# Determine dependencies between suites by looking for @skipUnlessPassed
# method annotations. Suite A depends on suite B if any method in A
# depends on a method on B.
for suite in suites:
suite.dependencies = []
suite.depth = 0
- for test in getTests(suite):
+ for test in self.getTests(suite):
methodname = getattr(test, '_testMethodName', None)
if methodname:
method = getattr(test, methodname)
depends_on = getattr(method, '_depends_on', None)
if depends_on:
for dep_suite in suites:
- if depends_on in [getattr(t, '_testMethodName', None) for t in getTests(dep_suite)]:
+ if depends_on in [getattr(t, '_testMethodName', None) for t in self.getTests(dep_suite)]:
if dep_suite not in suite.dependencies and \
dep_suite is not suite:
suite.dependencies.append(dep_suite)
@@ -314,7 +375,14 @@ class TestContext(object):
for index, suite in enumerate(suites):
set_suite_depth(suite)
suite.index = index
- suites.sort(cmp=lambda a,b: cmp((a.depth, a.index), (b.depth, b.index)))
+
+ def cmp(a, b):
+ return (a > b) - (a < b)
+
+ def cmpfunc(a, b):
+ return cmp((a.depth, a.index), (b.depth, b.index))
+
+ suites.sort(key=functools.cmp_to_key(cmpfunc))
self.suite = testloader.suiteClass(suites)
@@ -331,35 +399,24 @@ class TestContext(object):
return runner.run(self.suite)
-class ImageTestContext(TestContext):
- def __init__(self, d, target, host_dumper):
- super(ImageTestContext, self).__init__(d)
-
- self.tagexp = d.getVar("TEST_SUITES_TAGS", True)
+class RuntimeTestContext(TestContext):
+ def __init__(self, d, target, exported=False):
+ super(RuntimeTestContext, self).__init__(d, exported)
self.target = target
- self.host_dumper = host_dumper
+ self.pkgmanifest = {}
manifest = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True),
d.getVar("IMAGE_LINK_NAME", True) + ".manifest")
nomanifest = d.getVar("IMAGE_NO_MANIFEST", True)
if nomanifest is None or nomanifest != "1":
try:
with open(manifest) as f:
- self.pkgmanifest = f.read()
+ for line in f:
+ (pkg, arch, version) = line.strip().split()
+ self.pkgmanifest[pkg] = (version, arch)
except IOError as e:
bb.fatal("No package manifest file found. Did you build the image?\n%s" % e)
- else:
- self.pkgmanifest = ""
-
- self.sigterm = False
- self.origsigtermhandler = signal.getsignal(signal.SIGTERM)
- signal.signal(signal.SIGTERM, self._sigterm_exception)
-
- def _sigterm_exception(self, signum, stackframe):
- bb.warn("TestImage received SIGTERM, shutting down...")
- self.sigterm = True
- self.target.stop()
def _get_test_namespace(self):
return "runtime"
@@ -382,8 +439,223 @@ class ImageTestContext(TestContext):
return [t for t in self.d.getVar("TEST_SUITES", True).split() if t != "auto"]
def loadTests(self):
- super(ImageTestContext, self).loadTests()
- setattr(oeRuntimeTest, "pscmd", "ps -ef" if oeTest.hasPackage("procps") else "ps")
+ super(RuntimeTestContext, self).loadTests()
+ if oeTest.hasPackage("procps"):
+ oeRuntimeTest.pscmd = "ps -ef"
+
+ def extract_packages(self):
+ """
+ Find packages that will be needed during runtime.
+ """
+
+ modules = self.getTestModules()
+ bbpaths = self.d.getVar("BBPATH", True).split(":")
+
+ shutil.rmtree(self.d.getVar("TEST_EXTRACTED_DIR", True))
+ shutil.rmtree(self.d.getVar("TEST_PACKAGED_DIR", True))
+ for module in modules:
+ json_file = self._getJsonFile(module)
+ if json_file:
+ needed_packages = self._getNeededPackages(json_file)
+ self._perform_package_extraction(needed_packages)
+
+ def _perform_package_extraction(self, needed_packages):
+ """
+ Extract packages that will be needed during runtime.
+ """
+
+ import oe.path
+
+ extracted_path = self.d.getVar("TEST_EXTRACTED_DIR", True)
+ packaged_path = self.d.getVar("TEST_PACKAGED_DIR", True)
+
+ for key,value in needed_packages.items():
+ packages = ()
+ if isinstance(value, dict):
+ packages = (value, )
+ elif isinstance(value, list):
+ packages = value
+ else:
+ bb.fatal("Failed to process needed packages for %s; "
+ "Value must be a dict or list" % key)
+
+ for package in packages:
+ pkg = package["pkg"]
+ rm = package.get("rm", False)
+ extract = package.get("extract", True)
+ if extract:
+ dst_dir = os.path.join(extracted_path, pkg)
+ else:
+ dst_dir = os.path.join(packaged_path)
+
+ # Extract package and copy it to TEST_EXTRACTED_DIR
+ pkg_dir = self._extract_in_tmpdir(pkg)
+ if extract:
+
+ # Same package used for more than one test,
+ # don't need to extract again.
+ if os.path.exists(dst_dir):
+ continue
+ oe.path.copytree(pkg_dir, dst_dir)
+ shutil.rmtree(pkg_dir)
+
+ # Copy package to TEST_PACKAGED_DIR
+ else:
+ self._copy_package(pkg)
+
+ def _getJsonFile(self, module):
+ """
+ Returns the path of the JSON file for a module, empty if doesn't exitst.
+ """
+
+ module_file = module.path
+ json_file = "%s.json" % module_file.rsplit(".", 1)[0]
+ if os.path.isfile(module_file) and os.path.isfile(json_file):
+ return json_file
+ else:
+ return ""
+
+ def _getNeededPackages(self, json_file, test=None):
+ """
+ Returns a dict with needed packages based on a JSON file.
+
+
+ If a test is specified it will return the dict just for that test.
+ """
+
+ import json
+
+ needed_packages = {}
+
+ with open(json_file) as f:
+ test_packages = json.load(f)
+ for key,value in test_packages.items():
+ needed_packages[key] = value
+
+ if test:
+ if test in needed_packages:
+ needed_packages = needed_packages[test]
+ else:
+ needed_packages = {}
+
+ return needed_packages
+
+ def _extract_in_tmpdir(self, pkg):
+ """"
+ Returns path to a temp directory where the package was
+ extracted without dependencies.
+ """
+
+ from oeqa.utils.package_manager import get_package_manager
+
+ pkg_path = os.path.join(self.d.getVar("TEST_INSTALL_TMP_DIR", True), pkg)
+ pm = get_package_manager(self.d, pkg_path)
+ extract_dir = pm.extract(pkg)
+ shutil.rmtree(pkg_path)
+
+ return extract_dir
+
+ def _copy_package(self, pkg):
+ """
+ Copy the RPM, DEB or IPK package to dst_dir
+ """
+
+ from oeqa.utils.package_manager import get_package_manager
+
+ pkg_path = os.path.join(self.d.getVar("TEST_INSTALL_TMP_DIR", True), pkg)
+ dst_dir = self.d.getVar("TEST_PACKAGED_DIR", True)
+ pm = get_package_manager(self.d, pkg_path)
+ pkg_info = pm.package_info(pkg)
+ file_path = pkg_info[pkg]["filepath"]
+ shutil.copy2(file_path, dst_dir)
+ shutil.rmtree(pkg_path)
+
+ def install_uninstall_packages(self, test_id, pkg_dir, install):
+ """
+ Check if the test requires a package and Install/Unistall it in the DUT
+ """
+
+ test = test_id.split(".")[4]
+ module = self.getModulefromID(test_id)
+ json = self._getJsonFile(module)
+ if json:
+ needed_packages = self._getNeededPackages(json, test)
+ if needed_packages:
+ self._install_uninstall_packages(needed_packages, pkg_dir, install)
+
+ def _install_uninstall_packages(self, needed_packages, pkg_dir, install=True):
+ """
+ Install/Unistall packages in the DUT without using a package manager
+ """
+
+ if isinstance(needed_packages, dict):
+ packages = [needed_packages]
+ elif isinstance(needed_packages, list):
+ packages = needed_packages
+
+ for package in packages:
+ pkg = package["pkg"]
+ rm = package.get("rm", False)
+ extract = package.get("extract", True)
+ src_dir = os.path.join(pkg_dir, pkg)
+
+ # Install package
+ if install and extract:
+ self.target.connection.copy_dir_to(src_dir, "/")
+
+ # Unistall package
+ elif not install and rm:
+ self.target.connection.delete_dir_structure(src_dir, "/")
+
+class ImageTestContext(RuntimeTestContext):
+ def __init__(self, d, target, host_dumper):
+ super(ImageTestContext, self).__init__(d, target)
+
+ self.tagexp = d.getVar("TEST_SUITES_TAGS", True)
+
+ self.host_dumper = host_dumper
+
+ self.sigterm = False
+ self.origsigtermhandler = signal.getsignal(signal.SIGTERM)
+ signal.signal(signal.SIGTERM, self._sigterm_exception)
+
+ def _sigterm_exception(self, signum, stackframe):
+ bb.warn("TestImage received SIGTERM, shutting down...")
+ self.sigterm = True
+ self.target.stop()
+
+ def install_uninstall_packages(self, test_id, install=True):
+ """
+ Check if the test requires a package and Install/Unistall it in the DUT
+ """
+
+ pkg_dir = self.d.getVar("TEST_EXTRACTED_DIR", True)
+ super(ImageTestContext, self).install_uninstall_packages(test_id, pkg_dir, install)
+
+class ExportTestContext(RuntimeTestContext):
+ def __init__(self, d, target, exported=False, parsedArgs={}):
+ """
+ This class is used when exporting tests and when are executed outside OE environment.
+
+ parsedArgs can contain the following:
+ - tag: Filter test by tag.
+ """
+ super(ExportTestContext, self).__init__(d, target, exported)
+
+ tag = parsedArgs.get("tag", None)
+ self.tagexp = tag if tag != None else d.getVar("TEST_SUITES_TAGS", True)
+
+ self.sigterm = None
+
+ def install_uninstall_packages(self, test_id, install=True):
+ """
+ Check if the test requires a package and Install/Unistall it in the DUT
+ """
+
+ export_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+ extracted_dir = self.d.getVar("TEST_EXPORT_EXTRACTED_DIR", True)
+ pkg_dir = os.path.join(export_dir, extracted_dir)
+ super(ExportTestContext, self).install_uninstall_packages(test_id, pkg_dir, install)
class SDKTestContext(TestContext):
def __init__(self, d, sdktestdir, sdkenv, tcname, *args):
@@ -396,8 +668,11 @@ class SDKTestContext(TestContext):
if not hasattr(self, 'target_manifest'):
self.target_manifest = d.getVar("SDK_TARGET_MANIFEST", True)
try:
+ self.pkgmanifest = {}
with open(self.target_manifest) as f:
- self.pkgmanifest = f.read()
+ for line in f:
+ (pkg, arch, version) = line.strip().split()
+ self.pkgmanifest[pkg] = (version, arch)
except IOError as e:
bb.fatal("No package manifest file found. Did you build the sdk image?\n%s" % e)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runexported.py b/import-layers/yocto-poky/meta/lib/oeqa/runexported.py
index cc89e13c0..7e245c412 100755
--- a/import-layers/yocto-poky/meta/lib/oeqa/runexported.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runexported.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (C) 2013 Intel Corporation
@@ -30,9 +30,9 @@ except ImportError:
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "oeqa")))
-from oeqa.oetest import TestContext
+from oeqa.oetest import ExportTestContext
+from oeqa.utils.commands import runCmd, updateEnv
from oeqa.utils.sshcontrol import SSHControl
-from oeqa.utils.dump import get_host_dumper
# this isn't pretty but we need a fake target object
# for running the tests externally as we don't care
@@ -69,10 +69,6 @@ class MyDataDict(dict):
def getVar(self, key, unused = None):
return self.get(key, "")
-class ExportTestContext(TestContext):
- def __init__(self, d):
- self.d = d
-
def main():
parser = argparse.ArgumentParser()
@@ -85,6 +81,7 @@ def main():
specified in the json if that directory actually exists or it will error out.")
parser.add_argument("-l", "--log-dir", dest="log_dir", help="This sets the path for TEST_LOG_DIR. If not specified \
the current dir is used. This is used for usually creating a ssh log file and a scp test file.")
+ parser.add_argument("-a", "--tag", dest="tag", help="Only run test with specified tag.")
parser.add_argument("json", help="The json file exported by the build system", default="testdata.json", nargs='?')
args = parser.parse_args()
@@ -111,30 +108,41 @@ def main():
if not os.path.isdir(d["DEPLOY_DIR"]):
print("WARNING: The path to DEPLOY_DIR does not exist: %s" % d["DEPLOY_DIR"])
+ parsedArgs = {}
+ parsedArgs["tag"] = args.tag
+
+ extract_sdk(d)
target = FakeTarget(d)
for key in loaded["target"].keys():
setattr(target, key, loaded["target"][key])
- host_dumper = get_host_dumper(d)
- host_dumper.parent_dir = loaded["host_dumper"]["parent_dir"]
- host_dumper.cmds = loaded["host_dumper"]["cmds"]
-
target.exportStart()
- tc = ExportTestContext(d)
-
- setattr(tc, "d", d)
- setattr(tc, "target", target)
- setattr(tc, "host_dumper", host_dumper)
- for key in loaded.keys():
- if key != "d" and key != "target" and key != "host_dumper":
- setattr(tc, key, loaded[key])
-
+ tc = ExportTestContext(d, target, True, parsedArgs)
tc.loadTests()
tc.runTests()
return 0
+def extract_sdk(d):
+ """
+ Extract SDK if needed
+ """
+
+ export_dir = os.path.dirname(os.path.realpath(__file__))
+ tools_dir = d.getVar("TEST_EXPORT_SDK_DIR", True)
+ tarball_name = "%s.sh" % d.getVar("TEST_EXPORT_SDK_NAME", True)
+ tarball_path = os.path.join(export_dir, tools_dir, tarball_name)
+ extract_path = os.path.join(export_dir, "sysroot")
+ if os.path.isfile(tarball_path):
+ print ("Found SDK tarball %s. Extracting..." % tarball_path)
+ result = runCmd("%s -y -d %s" % (tarball_path, extract_path))
+ for f in os.listdir(extract_path):
+ if f.startswith("environment-setup"):
+ print("Setting up SDK environment...")
+ env_file = os.path.join(extract_path, f)
+ updateEnv(env_file)
+
if __name__ == "__main__":
try:
ret = main()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/__init__.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/__init__.py
deleted file mode 100644
index 4cf3fa76b..000000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# Enable other layers to have tests in the same named directory
-from pkgutil import extend_path
-__path__ = extend_path(__path__, __name__)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/_ptest.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/_ptest.py
index 0621028b8..71324d3da 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/_ptest.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/_ptest.py
@@ -11,7 +11,7 @@ import subprocess
def setUpModule():
if not oeRuntimeTest.hasFeature("package-management"):
skipModule("Image doesn't have package management feature")
- if not oeRuntimeTest.hasPackage("smart"):
+ if not oeRuntimeTest.hasPackage("smartpm"):
skipModule("Image doesn't have smart installed")
if "package_rpm" != oeRuntimeTest.tc.d.getVar("PACKAGE_CLASSES", True).split()[0]:
skipModule("Rpm is not the primary package manager")
@@ -105,7 +105,7 @@ class PtestRunnerTest(oeRuntimeTest):
def test_ptestrunner(self):
self.add_smart_channel()
(runnerstatus, result) = self.target.run('which ptest-runner', 0)
- cond = oeRuntimeTest.hasPackage("ptest-runner") and oeRuntimeTest.hasFeature("ptest") and oeRuntimeTest.hasPackage("-ptest") and (runnerstatus != 0)
+ cond = oeRuntimeTest.hasPackage("ptest-runner") and oeRuntimeTest.hasFeature("ptest") and oeRuntimeTest.hasPackageMatch("-ptest") and (runnerstatus != 0)
if cond:
self.install_packages(self.install_complementary("*-ptest"))
self.install_packages(['ptest-runner'])
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildgalculator.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildgalculator.py
new file mode 100644
index 000000000..28ba29e5c
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildgalculator.py
@@ -0,0 +1,23 @@
+from oeqa.oetest import oeRuntimeTest, skipModule
+from oeqa.utils.decorators import *
+from oeqa.utils.targetbuild import TargetBuildProject
+
+def setUpModule():
+ if not oeRuntimeTest.hasFeature("tools-sdk"):
+ skipModule("Image doesn't have tools-sdk in IMAGE_FEATURES")
+
+class GalculatorTest(oeRuntimeTest):
+ @skipUnlessPassed("test_ssh")
+ def test_galculator(self):
+ try:
+ project = TargetBuildProject(oeRuntimeTest.tc.target, oeRuntimeTest.tc.d,
+ "http://galculator.mnim.org/downloads/galculator-2.1.4.tar.bz2")
+ project.download_archive()
+
+ self.assertEqual(project.run_configure(), 0,
+ msg="Running configure failed")
+
+ self.assertEqual(project.run_make(), 0,
+ msg="Running make failed")
+ finally:
+ project.clean()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildiptables.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildiptables.py
index 09e252df8..bc75d0a0c 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildiptables.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildiptables.py
@@ -11,7 +11,7 @@ class BuildIptablesTest(oeRuntimeTest):
@classmethod
def setUpClass(self):
self.project = TargetBuildProject(oeRuntimeTest.tc.target, oeRuntimeTest.tc.d,
- "http://netfilter.org/projects/iptables/files/iptables-1.4.13.tar.bz2")
+ "http://downloads.yoctoproject.org/mirror/sources/iptables-1.4.13.tar.bz2")
self.project.download_archive()
@testcase(206)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildsudoku.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildsudoku.py
deleted file mode 100644
index 802b06001..000000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildsudoku.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from oeqa.oetest import oeRuntimeTest, skipModule
-from oeqa.utils.decorators import *
-from oeqa.utils.targetbuild import TargetBuildProject
-
-def setUpModule():
- if not oeRuntimeTest.hasFeature("tools-sdk"):
- skipModule("Image doesn't have tools-sdk in IMAGE_FEATURES")
-
-class SudokuTest(oeRuntimeTest):
-
- @classmethod
- def setUpClass(self):
- self.project = TargetBuildProject(oeRuntimeTest.tc.target, oeRuntimeTest.tc.d,
- "http://downloads.sourceforge.net/project/sudoku-savant/sudoku-savant/sudoku-savant-1.3/sudoku-savant-1.3.tar.bz2")
- self.project.download_archive()
-
- @testcase(207)
- @skipUnlessPassed("test_ssh")
- def test_sudoku(self):
- self.assertEqual(self.project.run_configure(), 0,
- msg="Running configure failed")
-
- self.assertEqual(self.project.run_make(), 0,
- msg="Running make failed")
-
- @classmethod
- def tearDownClass(self):
- self.project.clean()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/connman.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/connman.py
index bd9dba3bd..003fefe2c 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/connman.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/connman.py
@@ -27,5 +27,5 @@ class ConnmanTest(oeRuntimeTest):
def test_connmand_running(self):
(status, output) = self.target.run(oeRuntimeTest.pscmd + ' | grep [c]onnmand')
if status != 0:
- print self.service_status("connman")
+ print(self.service_status("connman"))
self.fail("No connmand process running")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/files/test.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/files/test.py
index f3a2273c5..f389225d7 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/files/test.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/files/test.py
@@ -3,4 +3,4 @@ import os
os.system('touch /tmp/testfile.python')
a = 9.01e+21 - 9.01e+21 + 0.01
-print "the value of a is %s" % a
+print("the value of a is %s" % a)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/parselogs.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/parselogs.py
index dec9ebe87..8efe2d1de 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/parselogs.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/parselogs.py
@@ -55,18 +55,25 @@ x86_common = [
'Could not enable PowerButton event',
'probe of LNXPWRBN:00 failed with error -22',
'pmd_set_huge: Cannot satisfy',
+ 'failed to setup card detect gpio',
+ 'amd_nb: Cannot enumerate AMD northbridges',
+ 'failed to retrieve link info, disabling eDP',
] + common_errors
qemux86_common = [
'wrong ELF class',
"fail to add MMCONFIG information, can't access extended PCI configuration space under this bridge.",
"can't claim BAR ",
+ 'amd_nb: Cannot enumerate AMD northbridges',
+ 'uvesafb: 5000 ms task timeout, infinitely waiting',
+ 'tsc: HPET/PMTIMER calibration failed',
] + common_errors
ignore_errors = {
'default' : common_errors,
'qemux86' : [
'Failed to access perfctr msr (MSR',
+ 'pci 0000:00:00.0: [Firmware Bug]: reg 0x..: invalid BAR (can\'t size)',
] + qemux86_common,
'qemux86-64' : qemux86_common,
'qemumips' : [
@@ -81,16 +88,28 @@ ignore_errors = {
'host side 80-wire cable detection failed, limiting max speed',
'mode "640x480" test failed',
'Failed to load module "glx"',
+ 'can\'t handle BAR above 4GB',
+ 'Cannot reserve Legacy IO',
] + common_errors,
'qemuarm' : [
'mmci-pl18x: probe of fpga:05 failed with error -22',
'mmci-pl18x: probe of fpga:0b failed with error -22',
- 'Failed to load module "glx"'
+ 'Failed to load module "glx"',
+ 'OF: amba_device_add() failed (-19) for /amba/smc@10100000',
+ 'OF: amba_device_add() failed (-19) for /amba/mpmc@10110000',
+ 'OF: amba_device_add() failed (-19) for /amba/sctl@101e0000',
+ 'OF: amba_device_add() failed (-19) for /amba/watchdog@101e1000',
+ 'OF: amba_device_add() failed (-19) for /amba/sci@101f0000',
+ 'OF: amba_device_add() failed (-19) for /amba/ssp@101f4000',
+ 'OF: amba_device_add() failed (-19) for /amba/fpga/sci@a000',
+ 'Failed to initialize \'/amba/timer@101e3000\': -22',
+ 'jitterentropy: Initialization failed with host not compliant with requirements: 2',
] + common_errors,
'qemuarm64' : [
'Fatal server error:',
'(EE) Server terminated with error (1). Closing log file.',
'dmi: Firmware registration failed.',
+ 'irq: type mismatch, failed to map hwirq-27 for /intc',
] + common_errors,
'emenlow' : [
'[Firmware Bug]: ACPI: No _BQC method, cannot determine initial brightness',
@@ -110,11 +129,19 @@ ignore_errors = {
'(EE) Failed to load module psbdrv',
'(EE) open /dev/fb0: No such file or directory',
'(EE) AIGLX: reverting to software rendering',
+ 'dmi: Firmware registration failed.',
+ 'ioremap error for 0x78',
] + x86_common,
'intel-corei7-64' : x86_common,
'crownbay' : x86_common,
'genericx86' : x86_common,
- 'genericx86-64' : x86_common,
+ 'genericx86-64' : [
+ 'Direct firmware load for i915',
+ 'Failed to load firmware i915',
+ 'Failed to fetch GuC',
+ 'Failed to initialize GuC',
+ 'The driver is built-in, so to load the firmware you need to',
+ ] + x86_common,
'edgerouter' : [
'Fatal server error:',
] + common_errors,
@@ -153,6 +180,9 @@ class ParseLogsTest(oeRuntimeTest):
def getMachine(self):
return oeRuntimeTest.tc.d.getVar("MACHINE", True)
+ def getWorkdir(self):
+ return oeRuntimeTest.tc.d.getVar("WORKDIR", True)
+
#get some information on the CPU of the machine to display at the beginning of the output. This info might be useful in some cases.
def getHardwareInfo(self):
hwi = ""
@@ -190,16 +220,19 @@ class ParseLogsTest(oeRuntimeTest):
#copy the log files to be parsed locally
def transfer_logs(self, log_list):
- target_logs = 'target_logs'
+ workdir = self.getWorkdir()
+ self.target_logs = workdir + '/' + 'target_logs'
+ target_logs = self.target_logs
if not os.path.exists(target_logs):
os.makedirs(target_logs)
+ bb.utils.remove(self.target_logs + "/*")
for f in log_list:
self.target.copy_from(f, target_logs)
#get the local list of logs
def get_local_log_list(self, log_locations):
self.transfer_logs(self.getLogList(log_locations))
- logs = [ os.path.join('target_logs',f) for f in os.listdir('target_logs') if os.path.isfile(os.path.join('target_logs',f)) ]
+ logs = [ os.path.join(self.target_logs, f) for f in os.listdir(self.target_logs) if os.path.isfile(os.path.join(self.target_logs, f)) ]
return logs
#build the grep command to be used with filters and exclusions
@@ -238,7 +271,7 @@ class ParseLogsTest(oeRuntimeTest):
result = None
thegrep = self.build_grepcmd(errors, ignore_errors, log)
try:
- result = subprocess.check_output(thegrep, shell=True)
+ result = subprocess.check_output(thegrep, shell=True).decode("utf-8")
except:
pass
if (result is not None):
@@ -246,7 +279,7 @@ class ParseLogsTest(oeRuntimeTest):
rez = result.splitlines()
for xrez in rez:
try:
- grep_output = subprocess.check_output(['grep', '-F', xrez, '-B', str(lines_before), '-A', str(lines_after), log])
+ grep_output = subprocess.check_output(['grep', '-F', xrez, '-B', str(lines_before), '-A', str(lines_after), log]).decode("utf-8")
except:
pass
results[log.replace('target_logs/','')][xrez]=grep_output
@@ -262,7 +295,7 @@ class ParseLogsTest(oeRuntimeTest):
self.write_dmesg()
log_list = self.get_local_log_list(self.log_locations)
result = self.parse_logs(self.errors, self.ignore_errors, log_list)
- print self.getHardwareInfo()
+ print(self.getHardwareInfo())
errcount = 0
for log in result:
self.msg += "Log: "+log+"\n"
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/ping.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/ping.py
index 80c460161..0f2744792 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/ping.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/ping.py
@@ -14,7 +14,7 @@ class PingTest(oeRuntimeTest):
endtime = time.time() + 60
while count < 5 and time.time() < endtime:
proc = subprocess.Popen("ping -c 1 %s" % self.target.ip, shell=True, stdout=subprocess.PIPE)
- output += proc.communicate()[0]
+ output += proc.communicate()[0].decode("utf-8")
if proc.poll() == 0:
count += 1
else:
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/python.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/python.py
index 26edb7a9b..29a231c7c 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/python.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/python.py
@@ -4,7 +4,7 @@ from oeqa.oetest import oeRuntimeTest, skipModule
from oeqa.utils.decorators import *
def setUpModule():
- if not oeRuntimeTest.hasPackage("python"):
+ if not oeRuntimeTest.hasPackage("python-core"):
skipModule("No python package in the image")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/rpm.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/rpm.py
index 624c515aa..7f514ca00 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/rpm.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/rpm.py
@@ -51,12 +51,32 @@ class RpmInstallRemoveTest(oeRuntimeTest):
@testcase(1096)
@skipUnlessPassed('test_ssh')
def test_rpm_query_nonroot(self):
- (status, output) = self.target.run('useradd test1')
- self.assertTrue(status == 0, msg="Failed to create new user: " + output)
- (status, output) = self.target.run('sudo -u test1 id')
- self.assertTrue('(test1)' in output, msg="Failed to execute as new user")
- (status, output) = self.target.run('sudo -u test1 rpm -qa')
- self.assertEqual(status, 0, msg="status: %s. Cannot run rpm -qa: %s" % (status, output))
+
+ def set_up_test_user(u):
+ (status, output) = self.target.run("id -u %s" % u)
+ if status == 0:
+ pass
+ else:
+ (status, output) = self.target.run("useradd %s" % u)
+ self.assertTrue(status == 0, msg="Failed to create new user: " + output)
+
+ def exec_as_test_user(u):
+ (status, output) = self.target.run("su -c id %s" % u)
+ self.assertTrue("({0})".format(u) in output, msg="Failed to execute as new user")
+ (status, output) = self.target.run("su -c \"rpm -qa\" %s " % u)
+ self.assertEqual(status, 0, msg="status: %s. Cannot run rpm -qa: %s" % (status, output))
+
+ def unset_up_test_user(u):
+ (status, output) = self.target.run("userdel -r %s" % u)
+ self.assertTrue(status == 0, msg="Failed to erase user: %s" % output)
+
+ tuser = 'test1'
+
+ try:
+ set_up_test_user(tuser)
+ exec_as_test_user(tuser)
+ finally:
+ unset_up_test_user(tuser)
@testcase(195)
@skipUnlessPassed('test_rpm_install')
@@ -98,4 +118,3 @@ class RpmInstallRemoveTest(oeRuntimeTest):
@classmethod
def tearDownClass(self):
oeRuntimeTest.tc.target.run('rm -f /tmp/rpm-doc.rpm')
-
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/smart.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/smart.py
index 126d61463..6cdb10d63 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/smart.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/smart.py
@@ -1,5 +1,7 @@
import unittest
import re
+import oe
+import subprocess
from oeqa.oetest import oeRuntimeTest, skipModule
from oeqa.utils.decorators import *
from oeqa.utils.httpserver import HTTPService
@@ -7,7 +9,7 @@ from oeqa.utils.httpserver import HTTPService
def setUpModule():
if not oeRuntimeTest.hasFeature("package-management"):
skipModule("Image doesn't have package management feature")
- if not oeRuntimeTest.hasPackage("smart"):
+ if not oeRuntimeTest.hasPackage("smartpm"):
skipModule("Image doesn't have smart installed")
if "package_rpm" != oeRuntimeTest.tc.d.getVar("PACKAGE_CLASSES", True).split()[0]:
skipModule("Rpm is not the primary package manager")
@@ -53,9 +55,50 @@ class SmartBasicTest(SmartTest):
class SmartRepoTest(SmartTest):
@classmethod
+ def create_index(self, arg):
+ index_cmd = arg
+ try:
+ bb.note("Executing '%s' ..." % index_cmd)
+ result = subprocess.check_output(index_cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8")
+ except subprocess.CalledProcessError as e:
+ return("Index creation command '%s' failed with return code %d:\n%s" %
+ (e.cmd, e.returncode, e.output.decode("utf-8")))
+ if result:
+ bb.note(result)
+ return None
+
+ @classmethod
def setUpClass(self):
self.repolist = []
- self.repo_server = HTTPService(oeRuntimeTest.tc.d.getVar('DEPLOY_DIR', True), oeRuntimeTest.tc.target.server_ip)
+
+ # Index RPMs
+ rpm_createrepo = bb.utils.which(os.getenv('PATH'), "createrepo")
+ index_cmds = []
+ rpm_dirs_found = False
+ archs = (oeRuntimeTest.tc.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS', True) or "").replace('-', '_').split()
+ for arch in archs:
+ rpm_dir = os.path.join(oeRuntimeTest.tc.d.getVar('DEPLOY_DIR_RPM', True), arch)
+ idx_path = os.path.join(oeRuntimeTest.tc.d.getVar('WORKDIR', True), 'rpm', arch)
+ db_path = os.path.join(oeRuntimeTest.tc.d.getVar('WORKDIR', True), 'rpmdb', arch)
+ if not os.path.isdir(rpm_dir):
+ continue
+ if os.path.exists(db_path):
+ bb.utils.remove(dbpath, True)
+ lockfilename = oeRuntimeTest.tc.d.getVar('DEPLOY_DIR_RPM', True) + "/rpm.lock"
+ lf = bb.utils.lockfile(lockfilename, False)
+ oe.path.copyhardlinktree(rpm_dir, idx_path)
+ # Full indexes overload a 256MB image so reduce the number of rpms
+ # in the feed. Filter to p* since we use the psplash packages and
+ # this leaves some allarch and machine arch packages too.
+ bb.utils.remove(idx_path + "*/[a-oq-z]*.rpm")
+ bb.utils.unlockfile(lf)
+ index_cmds.append("%s --dbpath %s --update -q %s" % (rpm_createrepo, db_path, idx_path))
+ rpm_dirs_found = True
+ # Create repodata¬
+ result = oe.utils.multiprocess_exec(index_cmds, self.create_index)
+ if result:
+ bb.fatal('%s' % ('\n'.join(result)))
+ self.repo_server = HTTPService(oeRuntimeTest.tc.d.getVar('WORKDIR', True), oeRuntimeTest.tc.target.server_ip)
self.repo_server.start()
@classmethod
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/syslog.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/syslog.py
index 2601dd9ea..8f550329e 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/syslog.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/syslog.py
@@ -9,7 +9,6 @@ def setUpModule():
class SyslogTest(oeRuntimeTest):
@testcase(201)
- @skipUnlessPassed("test_syslog_help")
def test_syslog_running(self):
(status,output) = self.target.run(oeRuntimeTest.pscmd + ' | grep -i [s]yslogd')
self.assertEqual(status, 0, msg="no syslogd process, ps output: %s" % self.target.run(oeRuntimeTest.pscmd)[1])
@@ -19,8 +18,16 @@ class SyslogTestConfig(oeRuntimeTest):
@testcase(1149)
@skipUnlessPassed("test_syslog_running")
def test_syslog_logger(self):
- (status,output) = self.target.run('logger foobar && test -e /var/log/messages && grep foobar /var/log/messages || logread | grep foobar')
- self.assertEqual(status, 0, msg="Test log string not found in /var/log/messages. Output: %s " % output)
+ (status, output) = self.target.run('logger foobar')
+ self.assertEqual(status, 0, msg="Can't log into syslog. Output: %s " % output)
+
+ (status, output) = self.target.run('grep foobar /var/log/messages')
+ if status != 0:
+ if oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager", "") == "systemd":
+ (status, output) = self.target.run('journalctl -o cat | grep foobar')
+ else:
+ (status, output) = self.target.run('logread | grep foobar')
+ self.assertEqual(status, 0, msg="Test log string not found in /var/log/messages or logread. Output: %s " % output)
@testcase(1150)
@skipUnlessPassed("test_syslog_running")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/systemd.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/systemd.py
index 2b2f10d71..8de799cd6 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/systemd.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/systemd.py
@@ -57,7 +57,7 @@ class SystemdBasicTests(SystemdTest):
self.systemctl('--version')
@testcase(551)
- @skipUnlessPassed('test_system_basic')
+ @skipUnlessPassed('test_systemd_basic')
def test_systemd_list(self):
self.systemctl('list-unit-files')
@@ -153,7 +153,7 @@ class SystemdJournalTests(SystemdTest):
if check_match: break
# put the startup time in the test log
if check_match:
- print "%s" % check_match
+ print("%s" % check_match)
else:
self.skipTest("Error at obtaining the boot time from journalctl")
boot_time_sec = 0
@@ -174,5 +174,5 @@ class SystemdJournalTests(SystemdTest):
self.skipTest("Error when parsing time from boot string")
#Assert the target boot time against systemd's unit start timeout
if boot_time_sec > systemd_TimeoutStartSec:
- print "Target boot time %s exceeds systemd's TimeoutStartSec %s"\
- %(boot_time_sec, systemd_TimeoutStartSec)
+ print("Target boot time %s exceeds systemd's TimeoutStartSec %s"\
+ %(boot_time_sec, systemd_TimeoutStartSec))
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildgalculator.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildgalculator.py
new file mode 100644
index 000000000..dc2fa9ce1
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildgalculator.py
@@ -0,0 +1,27 @@
+from oeqa.oetest import oeSDKTest, skipModule
+from oeqa.utils.decorators import *
+from oeqa.utils.targetbuild import SDKBuildProject
+
+def setUpModule():
+ if not (oeSDKTest.hasPackage("gtk+3") or oeSDKTest.hasPackage("libgtk-3.0")):
+ skipModule("Image doesn't have gtk+3 in manifest")
+
+class GalculatorTest(oeSDKTest):
+ def test_galculator(self):
+ try:
+ project = SDKBuildProject(oeSDKTest.tc.sdktestdir + "/galculator/",
+ oeSDKTest.tc.sdkenv, oeSDKTest.tc.d,
+ "http://galculator.mnim.org/downloads/galculator-2.1.4.tar.bz2")
+
+ project.download_archive()
+
+ # regenerate configure to get support for --with-libtool-sysroot
+ legacy_preconf=("autoreconf -i -f -I ${OECORE_TARGET_SYSROOT}/usr/share/aclocal -I m4;")
+
+ self.assertEqual(project.run_configure(extra_cmds=legacy_preconf),
+ 0, msg="Running configure failed")
+
+ self.assertEqual(project.run_make(), 0,
+ msg="Running make failed")
+ finally:
+ project.clean()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildiptables.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildiptables.py
index 062e5316e..f0cb8a428 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildiptables.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildiptables.py
@@ -8,7 +8,7 @@ class BuildIptablesTest(oeSDKTest):
@classmethod
def setUpClass(self):
self.project = SDKBuildProject(oeSDKTest.tc.sdktestdir + "/iptables/", oeSDKTest.tc.sdkenv, oeSDKTest.tc.d,
- "http://netfilter.org/projects/iptables/files/iptables-1.4.13.tar.bz2")
+ "http://downloads.yoctoproject.org/mirror/sources/iptables-1.4.13.tar.bz2")
self.project.download_archive()
def test_iptables(self):
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildsudoku.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildsudoku.py
deleted file mode 100644
index dea77c659..000000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildsudoku.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from oeqa.oetest import oeSDKTest, skipModule
-from oeqa.utils.decorators import *
-from oeqa.utils.targetbuild import SDKBuildProject
-
-def setUpModule():
- if not oeSDKTest.hasPackage("gtk\+"):
- skipModule("Image doesn't have gtk+ in manifest")
-
-class SudokuTest(oeSDKTest):
-
- @classmethod
- def setUpClass(self):
- self.project = SDKBuildProject(oeSDKTest.tc.sdktestdir + "/sudoku/", oeSDKTest.tc.sdkenv, oeSDKTest.tc.d,
- "http://downloads.sourceforge.net/project/sudoku-savant/sudoku-savant/sudoku-savant-1.3/sudoku-savant-1.3.tar.bz2")
- self.project.download_archive()
-
- def test_sudoku(self):
- self.assertEqual(self.project.run_configure(), 0,
- msg="Running configure failed")
-
- self.assertEqual(self.project.run_make(), 0,
- msg="Running make failed")
-
- @classmethod
- def tearDownClass(self):
- self.project.clean()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdkext/devtool.py b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/devtool.py
index c5bb3102a..65f41f687 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/sdkext/devtool.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/devtool.py
@@ -1,32 +1,108 @@
import shutil
-
+import subprocess
+import urllib.request
from oeqa.oetest import oeSDKExtTest
from oeqa.utils.decorators import *
class DevtoolTest(oeSDKExtTest):
-
@classmethod
def setUpClass(self):
self.myapp_src = os.path.join(self.tc.sdkextfilesdir, "myapp")
self.myapp_dst = os.path.join(self.tc.sdktestdir, "myapp")
shutil.copytree(self.myapp_src, self.myapp_dst)
+ self.myapp_cmake_src = os.path.join(self.tc.sdkextfilesdir, "myapp_cmake")
+ self.myapp_cmake_dst = os.path.join(self.tc.sdktestdir, "myapp_cmake")
+ shutil.copytree(self.myapp_cmake_src, self.myapp_cmake_dst)
+
+ def _test_devtool_build(self, directory):
+ self._run('devtool add myapp %s' % directory)
+ try:
+ self._run('devtool build myapp')
+ except Exception as e:
+ print(e.output)
+ self._run('devtool reset myapp')
+ raise e
+ self._run('devtool reset myapp')
+
+ def _test_devtool_build_package(self, directory):
+ self._run('devtool add myapp %s' % directory)
+ try:
+ self._run('devtool package myapp')
+ except Exception as e:
+ print(e.output)
+ self._run('devtool reset myapp')
+ raise e
+ self._run('devtool reset myapp')
+
def test_devtool_location(self):
output = self._run('which devtool')
self.assertEqual(output.startswith(self.tc.sdktestdir), True, \
msg="Seems that devtool isn't the eSDK one: %s" % output)
-
+
@skipUnlessPassed('test_devtool_location')
def test_devtool_add_reset(self):
self._run('devtool add myapp %s' % self.myapp_dst)
self._run('devtool reset myapp')
+
+ @testcase(1473)
+ @skipUnlessPassed('test_devtool_location')
+ def test_devtool_build_make(self):
+ self._test_devtool_build(self.myapp_dst)
+
+ @testcase(1474)
+ @skipUnlessPassed('test_devtool_location')
+ def test_devtool_build_esdk_package(self):
+ self._test_devtool_build_package(self.myapp_dst)
+ @testcase(1479)
@skipUnlessPassed('test_devtool_location')
- def test_devtool_build(self):
- self._run('devtool add myapp %s' % self.myapp_dst)
- self._run('devtool build myapp')
- self._run('devtool reset myapp')
+ def test_devtool_build_cmake(self):
+ self._test_devtool_build(self.myapp_cmake_dst)
+
+ @testcase(1482)
+ @skipUnlessPassed('test_devtool_location')
+ def test_extend_autotools_recipe_creation(self):
+ req = 'https://github.com/rdfa/librdfa'
+ recipe = "bbexample"
+ self._run('devtool add %s %s' % (recipe, req) )
+ try:
+ self._run('devtool build %s' % recipe)
+ except Exception as e:
+ print(e.output)
+ self._run('devtool reset %s' % recipe)
+ raise e
+ self._run('devtool reset %s' % recipe)
+
+ @testcase(1484)
+ @skipUnlessPassed('test_devtool_location')
+ def test_devtool_kernelmodule(self):
+ docfile = 'https://github.com/umlaeute/v4l2loopback.git'
+ recipe = 'v4l2loopback-driver'
+ self._run('devtool add %s %s' % (recipe, docfile) )
+ try:
+ self._run('devtool build %s' % recipe)
+ except Exception as e:
+ print(e.output)
+ self._run('devtool reset %s' % recipe)
+ raise e
+ self._run('devtool reset %s' % recipe)
+
+ @testcase(1478)
+ @skipUnlessPassed('test_devtool_location')
+ def test_recipes_for_nodejs(self):
+ package_nodejs = "npm://registry.npmjs.org;name=winston;version=2.2.0"
+ self._run('devtool add %s ' % package_nodejs)
+ try:
+ self._run('devtool build %s ' % package_nodejs)
+ except Exception as e:
+ print(e.output)
+ self._run('devtool reset %s' % package_nodejs)
+ raise e
+ self._run('devtool reset %s '% package_nodejs)
+
@classmethod
def tearDownClass(self):
shutil.rmtree(self.myapp_dst)
+ shutil.rmtree(self.myapp_cmake_dst)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdkext/files/myapp_cmake/CMakeLists.txt b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/files/myapp_cmake/CMakeLists.txt
new file mode 100644
index 000000000..19d773dd6
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/files/myapp_cmake/CMakeLists.txt
@@ -0,0 +1,11 @@
+cmake_minimum_required (VERSION 2.6)
+project (myapp)
+# The version number.
+set (myapp_VERSION_MAJOR 1)
+set (myapp_VERSION_MINOR 0)
+
+# add the executable
+add_executable (myapp myapp.c)
+
+install(TARGETS myapp
+ RUNTIME DESTINATION bin)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdkext/files/myapp_cmake/myapp.c b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/files/myapp_cmake/myapp.c
new file mode 100644
index 000000000..f0b63f03f
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/files/myapp_cmake/myapp.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int
+main(int argc, char *argv[])
+{
+ printf("Hello world\n");
+
+ return 0;
+}
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdkext/sdk_update.py b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/sdk_update.py
index 7a2a6fe7c..2ade839c0 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/sdkext/sdk_update.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/sdk_update.py
@@ -30,9 +30,6 @@ class SdkUpdateTest(oeSDKExtTest):
def test_sdk_update_http(self):
output = self._run("devtool sdk-update \"%s\"" % self.http_url)
- def test_sdk_update_local(self):
- output = self._run("devtool sdk-update \"%s\"" % self.publish_dir)
-
@classmethod
def tearDownClass(self):
self.http_service.stop()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/_toaster.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/_toaster.py
index c424659fd..15ea9df9e 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/_toaster.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/_toaster.py
@@ -2,7 +2,7 @@ import unittest
import os
import sys
import shlex, subprocess
-import urllib, commands, time, getpass, re, json, shlex
+import urllib.request, urllib.parse, urllib.error, subprocess, time, getpass, re, json, shlex
import oeqa.utils.ftools as ftools
from oeqa.selftest.base import oeSelfTest
@@ -290,7 +290,7 @@ class Toaster_DB_Tests(ToasterSetup):
layers = Layer.objects.values('id', 'layer_index_url')
cnt_err = []
for layer in layers:
- resp = urllib.urlopen(layer['layer_index_url'])
+ resp = urllib.request.urlopen(layer['layer_index_url'])
if (resp.getcode() != 200):
cnt_err.append(layer['id'])
self.assertEqual(len(cnt_err), 0, msg = 'Errors for layer id: %s' % cnt_err)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/base.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/base.py
index e10455edc..26c93f905 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/base.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/base.py
@@ -28,17 +28,47 @@ class oeSelfTest(unittest.TestCase):
def __init__(self, methodName="runTest"):
self.builddir = os.environ.get("BUILDDIR")
self.localconf_path = os.path.join(self.builddir, "conf/local.conf")
+ self.localconf_backup = os.path.join(self.builddir, "conf/local.bk")
self.testinc_path = os.path.join(self.builddir, "conf/selftest.inc")
self.local_bblayers_path = os.path.join(self.builddir, "conf/bblayers.conf")
+ self.local_bblayers_backup = os.path.join(self.builddir,
+ "conf/bblayers.bk")
self.testinc_bblayers_path = os.path.join(self.builddir, "conf/bblayers.inc")
self.machineinc_path = os.path.join(self.builddir, "conf/machine.inc")
self.testlayer_path = oeSelfTest.testlayer_path
self._extra_tear_down_commands = []
- self._track_for_cleanup = [self.testinc_path, self.testinc_bblayers_path, self.machineinc_path]
+ self._track_for_cleanup = [
+ self.testinc_path, self.testinc_bblayers_path,
+ self.machineinc_path, self.localconf_backup,
+ self.local_bblayers_backup]
super(oeSelfTest, self).__init__(methodName)
def setUp(self):
os.chdir(self.builddir)
+ # Check if local.conf or bblayers.conf files backup exists
+ # from a previous failed test and restore them
+ if os.path.isfile(self.localconf_backup) or os.path.isfile(
+ self.local_bblayers_backup):
+ self.log.debug("Found a local.conf and/or bblayers.conf backup \
+from a previously aborted test. Restoring these files now, but tests should \
+be re-executed from a clean environment to ensure accurate results.")
+ try:
+ shutil.copyfile(self.localconf_backup, self.localconf_path)
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ raise
+ try:
+ shutil.copyfile(self.local_bblayers_backup,
+ self.local_bblayers_path)
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ raise
+ else:
+ # backup local.conf and bblayers.conf
+ shutil.copyfile(self.localconf_path, self.localconf_backup)
+ shutil.copyfile(self.local_bblayers_path,
+ self.local_bblayers_backup)
+ self.log.debug("Creating local.conf and bblayers.conf backups.")
# we don't know what the previous test left around in config or inc files
# if it failed so we need a fresh start
try:
@@ -67,7 +97,7 @@ class oeSelfTest(unittest.TestCase):
machine = custommachine
machine_conf = 'MACHINE ??= "%s"\n' % machine
self.set_machine_config(machine_conf)
- print 'MACHINE: %s' % machine
+ print('MACHINE: %s' % machine)
# tests might need their own setup
# but if they overwrite this one they have to call
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/bbtests.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/bbtests.py
index 26728a4b4..baae1e0e5 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/bbtests.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/bbtests.py
@@ -29,7 +29,7 @@ class BitbakeTests(oeSelfTest):
def test_event_handler(self):
self.write_config("INHERIT += \"test_events\"")
result = bitbake('m4-native')
- find_build_started = re.search("NOTE: Test for bb\.event\.BuildStarted(\n.*)*NOTE: Preparing RunQueue", result.output)
+ find_build_started = re.search("NOTE: Test for bb\.event\.BuildStarted(\n.*)*NOTE: Executing RunQueue Tasks", result.output)
find_build_completed = re.search("Tasks Summary:.*(\n.*)*NOTE: Test for bb\.event\.BuildCompleted", result.output)
self.assertTrue(find_build_started, msg = "Match failed in:\n%s" % result.output)
self.assertTrue(find_build_completed, msg = "Match failed in:\n%s" % result.output)
@@ -64,12 +64,15 @@ class BitbakeTests(oeSelfTest):
@testcase(108)
def test_invalid_patch(self):
+ # This patch already exists in SRC_URI so adding it again will cause the
+ # patch to fail.
self.write_recipeinc('man', 'SRC_URI += "file://man-1.5h1-make.patch"')
+ self.write_config("INHERIT_remove = \"report-error\"")
result = bitbake('man -c patch', ignore_status=True)
self.delete_recipeinc('man')
bitbake('-cclean man')
line = self.getline(result, "Function failed: patch_do_patch")
- self.assertTrue(line and line.startswith("ERROR:"), msg = "Though no man-1.5h1-make.patch file exists, bitbake didn't output any err. message. bitbake output: %s" % result.output)
+ self.assertTrue(line and line.startswith("ERROR:"), msg = "Repeated patch application didn't fail. bitbake output: %s" % result.output)
@testcase(1354)
def test_force_task_1(self):
@@ -131,6 +134,7 @@ class BitbakeTests(oeSelfTest):
self.write_recipeinc('man', data)
self.write_config("""DL_DIR = \"${TOPDIR}/download-selftest\"
SSTATE_DIR = \"${TOPDIR}/download-selftest\"
+INHERIT_remove = \"report-error\"
""")
self.track_for_cleanup(os.path.join(self.builddir, "download-selftest"))
@@ -141,7 +145,7 @@ SSTATE_DIR = \"${TOPDIR}/download-selftest\"
self.assertEqual(result.status, 1, msg="Command succeded when it should have failed. bitbake output: %s" % result.output)
self.assertTrue('Fetcher failure: Unable to find file file://invalid anywhere. The paths that were searched were:' in result.output, msg = "\"invalid\" file \
doesn't exist, yet no error message encountered. bitbake output: %s" % result.output)
- line = self.getline(result, 'Function failed: Fetcher failure for URL: \'file://invalid\'. Unable to fetch URL from any source.')
+ line = self.getline(result, 'Fetcher failure for URL: \'file://invalid\'. Unable to fetch URL from any source.')
self.assertTrue(line and line.startswith("ERROR:"), msg = "\"invalid\" file \
doesn't exist, yet fetcher didn't report any error. bitbake output: %s" % result.output)
@@ -212,6 +216,7 @@ SSTATE_DIR = \"${TOPDIR}/download-selftest\"
def test_continue(self):
self.write_config("""DL_DIR = \"${TOPDIR}/download-selftest\"
SSTATE_DIR = \"${TOPDIR}/download-selftest\"
+INHERIT_remove = \"report-error\"
""")
self.track_for_cleanup(os.path.join(self.builddir, "download-selftest"))
self.write_recipeinc('man',"\ndo_fail_task () {\nexit 1 \n}\n\naddtask do_fail_task before do_fetch\n" )
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/buildoptions.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/buildoptions.py
index 35d5dfd29..9487898b0 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/buildoptions.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/buildoptions.py
@@ -30,22 +30,6 @@ class ImageOptionsTests(oeSelfTest):
incremental_removed = re.search("NOTE: load old install solution for incremental install\nNOTE: creating new install solution for incremental install(\n.*)*NOTE: incremental removed:.*openssh-sshd-.*", log_data_removed)
self.assertTrue(incremental_removed, msg = "Match failed in:\n%s" % log_data_removed)
- @testcase(925)
- def test_rm_old_image(self):
- bitbake("core-image-minimal")
- deploydir = get_bb_var("DEPLOY_DIR_IMAGE", target="core-image-minimal")
- imagename = get_bb_var("IMAGE_LINK_NAME", target="core-image-minimal")
- deploydir_files = os.listdir(deploydir)
- track_original_files = []
- for image_file in deploydir_files:
- if imagename in image_file and os.path.islink(os.path.join(deploydir, image_file)):
- track_original_files.append(os.path.realpath(os.path.join(deploydir, image_file)))
- self.write_config("RM_OLD_IMAGE = \"1\"")
- bitbake("-C rootfs core-image-minimal")
- deploydir_files = os.listdir(deploydir)
- remaining_not_expected = [path for path in track_original_files if os.path.basename(path) in deploydir_files]
- self.assertFalse(remaining_not_expected, msg="\nThe following image files were not removed: %s" % ', '.join(map(str, remaining_not_expected)))
-
@testcase(286)
def test_ccache_tool(self):
bitbake("ccache-native")
@@ -89,8 +73,9 @@ class SanityOptionsTest(oeSelfTest):
def test_options_warnqa_errorqa_switch(self):
bitbake("xcursor-transparent-theme -ccleansstate")
+ self.write_config("INHERIT_remove = \"report-error\"")
if "packages-list" not in get_bb_var("ERROR_QA"):
- self.write_config("ERROR_QA_append = \" packages-list\"")
+ self.append_config("ERROR_QA_append = \" packages-list\"")
self.write_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"')
res = bitbake("xcursor-transparent-theme", ignore_status=True)
@@ -199,78 +184,6 @@ class BuildhistoryTests(BuildhistoryBase):
self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True)
self.run_buildhistory_operation(target, target_config="PR = \"r0\"", change_bh_location=False, expect_error=True, error_regex=error)
- @testcase(1386)
- def test_buildhistory_does_not_change_signatures(self):
- """
- Summary: Ensure that buildhistory does not change signatures
- Expected: Only 'do_rootfs' task should be rerun
- Product: oe-core
- Author: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
- AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
- """
-
- tmpdir1_name = 'tmpsig1'
- tmpdir2_name = 'tmpsig2'
- builddir = os.environ.get('BUILDDIR')
- tmpdir1 = os.path.join(builddir, tmpdir1_name)
- tmpdir2 = os.path.join(builddir, tmpdir2_name)
-
- self.track_for_cleanup(tmpdir1)
- self.track_for_cleanup(tmpdir2)
-
- features = 'TMPDIR = "%s"\n' % tmpdir1
- self.write_config(features)
- bitbake('core-image-minimal -S none -c rootfs')
-
- features = 'TMPDIR = "%s"\n' % tmpdir2
- features += 'INHERIT += "buildhistory"\n'
- self.write_config(features)
- bitbake('core-image-minimal -S none -c rootfs')
-
- def get_files(d):
- f = []
- for root, dirs, files in os.walk(d):
- for name in files:
- f.append(os.path.join(root, name))
- return f
-
- files1 = get_files(tmpdir1 + '/stamps')
- files2 = get_files(tmpdir2 + '/stamps')
- files2 = [x.replace(tmpdir2_name, tmpdir1_name) for x in files2]
-
- f1 = set(files1)
- f2 = set(files2)
- sigdiff = f1 - f2
-
- self.assertEqual(len(sigdiff), 1, 'Expected 1 signature differences. Out: %s' % list(sigdiff))
-
- unexpected_diff = []
-
- # No new signatures should appear apart from do_rootfs
- found_do_rootfs_flag = False
-
- for sig in sigdiff:
- if 'do_rootfs' in sig:
- found_do_rootfs_flag = True
- else:
- unexpected_diff.append(sig)
-
- self.assertTrue(found_do_rootfs_flag, 'Task do_rootfs did not rerun.')
- self.assertFalse(unexpected_diff, 'Found unexpected signature differences. Out: %s' % unexpected_diff)
-
-
-class BuildImagesTest(oeSelfTest):
- @testcase(563)
- def test_directfb(self):
- """
- This method is used to test the build of directfb image for arm arch.
- In essence we build a coreimagedirectfb and test the exitcode of bitbake that in case of success is 0.
- """
- self.add_command_to_tearDown('cleanup-workdir')
- self.write_config("DISTRO_FEATURES_remove = \"x11\"\nDISTRO_FEATURES_append = \" directfb\"\nMACHINE ??= \"qemuarm\"")
- res = bitbake("core-image-directfb", ignore_status=True)
- self.assertEqual(res.status, 0, "\ncoreimagedirectfb failed to build. Please check logs for further details.\nbitbake output %s" % res.output)
-
class ArchiverTest(oeSelfTest):
@testcase(926)
def test_arch_work_dir_and_export_source(self):
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/devtool.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/devtool.py
index 132a73d0e..e992dcf77 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/devtool.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/devtool.py
@@ -5,10 +5,11 @@ import re
import shutil
import tempfile
import glob
+import fnmatch
import oeqa.utils.ftools as ftools
from oeqa.selftest.base import oeSelfTest
-from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer, runqemu
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer, runqemu, get_test_layer
from oeqa.utils.decorators import testcase
class DevtoolBase(oeSelfTest):
@@ -50,7 +51,7 @@ class DevtoolBase(oeSelfTest):
missingvars = {}
- for var, value in checkvars.iteritems():
+ for var, value in checkvars.items():
if value is not None:
missingvars[var] = value
self.assertEqual(missingvars, {}, 'Some expected variables not found in recipe: %s' % checkvars)
@@ -207,12 +208,14 @@ class DevtoolTests(DevtoolBase):
tempdir = tempfile.mkdtemp(prefix='devtoolqa')
self.track_for_cleanup(tempdir)
pn = 'dbus-wait'
+ srcrev = '6cc6077a36fe2648a5f993fe7c16c9632f946517'
# We choose an https:// git URL here to check rewriting the URL works
url = 'https://git.yoctoproject.org/git/dbus-wait'
# Force fetching to "noname" subdir so we verify we're picking up the name from autoconf
# instead of the directory name
result = runCmd('git clone %s noname' % url, cwd=tempdir)
srcdir = os.path.join(tempdir, 'noname')
+ result = runCmd('git reset --hard %s' % srcrev, cwd=srcdir)
self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure.ac')), 'Unable to find configure script in source directory')
# Test devtool add
self.track_for_cleanup(self.workspacedir)
@@ -235,7 +238,7 @@ class DevtoolTests(DevtoolBase):
checkvars['S'] = '${WORKDIR}/git'
checkvars['PV'] = '0.1+git${SRCPV}'
checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/dbus-wait;protocol=https'
- checkvars['SRCREV'] = '${AUTOREV}'
+ checkvars['SRCREV'] = srcrev
checkvars['DEPENDS'] = set(['dbus'])
self._test_recipe_contents(recipefile, checkvars, [])
@@ -345,7 +348,7 @@ class DevtoolTests(DevtoolBase):
self.track_for_cleanup(self.workspacedir)
self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe)
self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
- result = runCmd('devtool add %s %s -f %s' % (testrecipe, srcdir, url))
+ result = runCmd('devtool add %s %s -a -f %s' % (testrecipe, srcdir, url))
self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created: %s' % result.output)
self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure.ac')), 'Unable to find configure.ac in source directory')
# Test devtool status
@@ -357,7 +360,7 @@ class DevtoolTests(DevtoolBase):
self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
checkvars = {}
checkvars['S'] = '${WORKDIR}/git'
- checkvars['PV'] = '1.11+git${SRCPV}'
+ checkvars['PV'] = '1.12+git${SRCPV}'
checkvars['SRC_URI'] = url
checkvars['SRCREV'] = '${AUTOREV}'
self._test_recipe_contents(recipefile, checkvars, [])
@@ -465,12 +468,11 @@ class DevtoolTests(DevtoolBase):
testrecipes = 'perf kernel-devsrc package-index core-image-minimal meta-toolchain packagegroup-core-sdk meta-ide-support'.split()
# Find actual name of gcc-source since it now includes the version - crude, but good enough for this purpose
result = runCmd('bitbake-layers show-recipes gcc-source*')
- reading = False
for line in result.output.splitlines():
- if line.startswith('=='):
- reading = True
- elif reading and not line.startswith(' '):
- testrecipes.append(line.split(':')[0])
+ # just match those lines that contain a real target
+ m = re.match('(?P<recipe>^[a-zA-Z0-9.-]+)(?P<colon>:$)', line)
+ if m:
+ testrecipes.append(m.group('recipe'))
for testrecipe in testrecipes:
# Check it's a valid recipe
bitbake('%s -e' % testrecipe)
@@ -816,28 +818,28 @@ class DevtoolTests(DevtoolBase):
# Check bbappend contents
result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
- expectedlines = ['SRCREV = "%s"\n' % result.output,
- '\n',
- 'SRC_URI = "%s"\n' % git_uri,
- '\n']
+ expectedlines = set(['SRCREV = "%s"\n' % result.output,
+ '\n',
+ 'SRC_URI = "%s"\n' % git_uri,
+ '\n'])
with open(bbappendfile, 'r') as f:
- self.assertEqual(expectedlines, f.readlines())
+ self.assertEqual(expectedlines, set(f.readlines()))
# Check we can run it again and bbappend isn't modified
result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
with open(bbappendfile, 'r') as f:
- self.assertEqual(expectedlines, f.readlines())
+ self.assertEqual(expectedlines, set(f.readlines()))
# Drop new commit and check SRCREV changes
result = runCmd('git reset HEAD^', cwd=tempsrcdir)
result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
self.assertFalse(os.path.exists(os.path.join(appenddir, testrecipe)), 'Patch directory should not be created')
result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
- expectedlines = ['SRCREV = "%s"\n' % result.output,
- '\n',
- 'SRC_URI = "%s"\n' % git_uri,
- '\n']
+ expectedlines = set(['SRCREV = "%s"\n' % result.output,
+ '\n',
+ 'SRC_URI = "%s"\n' % git_uri,
+ '\n'])
with open(bbappendfile, 'r') as f:
- self.assertEqual(expectedlines, f.readlines())
+ self.assertEqual(expectedlines, set(f.readlines()))
# Put commit back and check we can run it if layer isn't in bblayers.conf
os.remove(bbappendfile)
result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir)
@@ -846,12 +848,12 @@ class DevtoolTests(DevtoolBase):
self.assertIn('WARNING: Specified layer is not currently enabled in bblayers.conf', result.output)
self.assertFalse(os.path.exists(os.path.join(appenddir, testrecipe)), 'Patch directory should not be created')
result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
- expectedlines = ['SRCREV = "%s"\n' % result.output,
- '\n',
- 'SRC_URI = "%s"\n' % git_uri,
- '\n']
+ expectedlines = set(['SRCREV = "%s"\n' % result.output,
+ '\n',
+ 'SRC_URI = "%s"\n' % git_uri,
+ '\n'])
with open(bbappendfile, 'r') as f:
- self.assertEqual(expectedlines, f.readlines())
+ self.assertEqual(expectedlines, set(f.readlines()))
# Deleting isn't expected to work under these circumstances
@testcase(1370)
@@ -1188,3 +1190,159 @@ class DevtoolTests(DevtoolBase):
s = "Microsoft Made No Profit From Anyone's Zunes Yo"
result = runCmd("devtool --quiet selftest-reverse \"%s\"" % s)
self.assertEqual(result.output, s[::-1])
+
+ def _setup_test_devtool_finish_upgrade(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)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ # Use a "real" recipe from meta-selftest
+ recipe = 'devtool-upgrade-test1'
+ oldversion = '1.5.3'
+ newversion = '1.6.0'
+ oldrecipefile = get_bb_var('FILE', recipe)
+ recipedir = os.path.dirname(oldrecipefile)
+ result = runCmd('git status --porcelain .', cwd=recipedir)
+ if result.output.strip():
+ self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ # Check that recipe is not already under devtool control
+ result = runCmd('devtool status')
+ self.assertNotIn(recipe, result.output)
+ # Do the upgrade
+ result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, newversion))
+ # Check devtool status and make sure recipe is present
+ result = runCmd('devtool status')
+ self.assertIn(recipe, result.output)
+ self.assertIn(tempdir, result.output)
+ # Make a change to the source
+ result = runCmd('sed -i \'/^#include "pv.h"/a \\/* Here is a new comment *\\/\' src/pv/number.c', cwd=tempdir)
+ result = runCmd('git status --porcelain', cwd=tempdir)
+ self.assertIn('M src/pv/number.c', result.output)
+ result = runCmd('git commit src/pv/number.c -m "Add a comment to the code"', cwd=tempdir)
+ # Check if patch is there
+ recipedir = os.path.dirname(oldrecipefile)
+ olddir = os.path.join(recipedir, recipe + '-' + oldversion)
+ patchfn = '0001-Add-a-note-line-to-the-quick-reference.patch'
+ self.assertTrue(os.path.exists(os.path.join(olddir, patchfn)), 'Original patch file does not exist')
+ return recipe, oldrecipefile, recipedir, olddir, newversion, patchfn
+
+ def test_devtool_finish_upgrade_origlayer(self):
+ recipe, oldrecipefile, recipedir, olddir, newversion, patchfn = self._setup_test_devtool_finish_upgrade()
+ # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
+ self.assertIn('/meta-selftest/', recipedir)
+ # Try finish to the original layer
+ self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
+ result = runCmd('devtool finish %s meta-selftest' % recipe)
+ result = runCmd('devtool status')
+ self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
+ self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe)), 'Recipe directory should not exist after finish')
+ self.assertFalse(os.path.exists(oldrecipefile), 'Old recipe file should have been deleted but wasn\'t')
+ self.assertFalse(os.path.exists(os.path.join(olddir, patchfn)), 'Old patch file should have been deleted but wasn\'t')
+ newrecipefile = os.path.join(recipedir, '%s_%s.bb' % (recipe, newversion))
+ newdir = os.path.join(recipedir, recipe + '-' + newversion)
+ self.assertTrue(os.path.exists(newrecipefile), 'New recipe file should have been copied into existing layer but wasn\'t')
+ self.assertTrue(os.path.exists(os.path.join(newdir, patchfn)), 'Patch file should have been copied into new directory but wasn\'t')
+ self.assertTrue(os.path.exists(os.path.join(newdir, '0002-Add-a-comment-to-the-code.patch')), 'New patch file should have been created but wasn\'t')
+
+ def test_devtool_finish_upgrade_otherlayer(self):
+ recipe, oldrecipefile, recipedir, olddir, newversion, patchfn = self._setup_test_devtool_finish_upgrade()
+ # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
+ self.assertIn('/meta-selftest/', recipedir)
+ # Try finish to a different layer - should create a bbappend
+ # This cleanup isn't strictly necessary but do it anyway just in case it goes wrong and writes to here
+ self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
+ oe_core_dir = os.path.join(get_bb_var('COREBASE'), 'meta')
+ newrecipedir = os.path.join(oe_core_dir, 'recipes-test', 'devtool')
+ newrecipefile = os.path.join(newrecipedir, '%s_%s.bb' % (recipe, newversion))
+ self.track_for_cleanup(newrecipedir)
+ result = runCmd('devtool finish %s oe-core' % recipe)
+ result = runCmd('devtool status')
+ self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
+ self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe)), 'Recipe directory should not exist after finish')
+ self.assertTrue(os.path.exists(oldrecipefile), 'Old recipe file should not have been deleted')
+ self.assertTrue(os.path.exists(os.path.join(olddir, patchfn)), 'Old patch file should not have been deleted')
+ newdir = os.path.join(newrecipedir, recipe + '-' + newversion)
+ self.assertTrue(os.path.exists(newrecipefile), 'New recipe file should have been copied into existing layer but wasn\'t')
+ self.assertTrue(os.path.exists(os.path.join(newdir, patchfn)), 'Patch file should have been copied into new directory but wasn\'t')
+ self.assertTrue(os.path.exists(os.path.join(newdir, '0002-Add-a-comment-to-the-code.patch')), 'New patch file should have been created but wasn\'t')
+
+ def _setup_test_devtool_finish_modify(self):
+ # Check preconditions
+ self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ # Try modifying a recipe
+ self.track_for_cleanup(self.workspacedir)
+ recipe = 'mdadm'
+ oldrecipefile = get_bb_var('FILE', recipe)
+ recipedir = os.path.dirname(oldrecipefile)
+ result = runCmd('git status --porcelain .', cwd=recipedir)
+ if result.output.strip():
+ self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ result = runCmd('devtool modify %s %s' % (recipe, tempdir))
+ self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile')), 'Extracted source could not be found')
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn(recipe, result.output)
+ self.assertIn(tempdir, result.output)
+ # Make a change to the source
+ result = runCmd('sed -i \'/^#include "mdadm.h"/a \\/* Here is a new comment *\\/\' maps.c', cwd=tempdir)
+ result = runCmd('git status --porcelain', cwd=tempdir)
+ self.assertIn('M maps.c', result.output)
+ result = runCmd('git commit maps.c -m "Add a comment to the code"', cwd=tempdir)
+ for entry in os.listdir(recipedir):
+ filesdir = os.path.join(recipedir, entry)
+ if os.path.isdir(filesdir):
+ break
+ else:
+ self.fail('Unable to find recipe files directory for %s' % recipe)
+ return recipe, oldrecipefile, recipedir, filesdir
+
+ def test_devtool_finish_modify_origlayer(self):
+ recipe, oldrecipefile, recipedir, filesdir = self._setup_test_devtool_finish_modify()
+ # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
+ self.assertIn('/meta/', recipedir)
+ # Try finish to the original layer
+ self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
+ result = runCmd('devtool finish %s meta' % recipe)
+ result = runCmd('devtool status')
+ self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
+ self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe)), 'Recipe directory should not exist after finish')
+ expected_status = [(' M', '.*/%s$' % os.path.basename(oldrecipefile)),
+ ('??', '.*/.*-Add-a-comment-to-the-code.patch$')]
+ self._check_repo_status(recipedir, expected_status)
+
+ def test_devtool_finish_modify_otherlayer(self):
+ recipe, oldrecipefile, recipedir, filesdir = self._setup_test_devtool_finish_modify()
+ # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
+ self.assertIn('/meta/', recipedir)
+ relpth = os.path.relpath(recipedir, os.path.join(get_bb_var('COREBASE'), 'meta'))
+ appenddir = os.path.join(get_test_layer(), relpth)
+ self.track_for_cleanup(appenddir)
+ # Try finish to the original layer
+ self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
+ result = runCmd('devtool finish %s meta-selftest' % recipe)
+ result = runCmd('devtool status')
+ self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
+ self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe)), 'Recipe directory should not exist after finish')
+ result = runCmd('git status --porcelain .', cwd=recipedir)
+ if result.output.strip():
+ self.fail('Recipe directory for %s contains the following unexpected changes after finish:\n%s' % (recipe, result.output.strip()))
+ recipefn = os.path.splitext(os.path.basename(oldrecipefile))[0]
+ recipefn = recipefn.split('_')[0] + '_%'
+ appendfile = os.path.join(appenddir, recipefn + '.bbappend')
+ self.assertTrue(os.path.exists(appendfile), 'bbappend %s should have been created but wasn\'t' % appendfile)
+ newdir = os.path.join(appenddir, recipe)
+ files = os.listdir(newdir)
+ foundpatch = None
+ for fn in files:
+ if fnmatch.fnmatch(fn, '*-Add-a-comment-to-the-code.patch'):
+ foundpatch = fn
+ if not foundpatch:
+ self.fail('No patch file created next to bbappend')
+ files.remove(foundpatch)
+ if files:
+ self.fail('Unexpected file(s) copied next to bbappend: %s' % ', '.join(files))
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/eSDK.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/eSDK.py
new file mode 100644
index 000000000..9d5c68094
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/eSDK.py
@@ -0,0 +1,103 @@
+import unittest
+import tempfile
+import shutil
+import os
+import glob
+import logging
+import subprocess
+import oeqa.utils.ftools as ftools
+from oeqa.utils.decorators import testcase
+from oeqa.selftest.base import oeSelfTest
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var
+from oeqa.utils.httpserver import HTTPService
+
+class oeSDKExtSelfTest(oeSelfTest):
+ """
+ # Bugzilla Test Plan: 6033
+ # This code is planned to be part of the automation for eSDK containig
+ # Install libraries and headers, image generation binary feeds.
+ """
+
+ @staticmethod
+ def get_esdk_environment(env_eSDK, tmpdir_eSDKQA):
+ # XXX: at this time use the first env need to investigate
+ # what environment load oe-selftest, i586, x86_64
+ pattern = os.path.join(tmpdir_eSDKQA, 'environment-setup-*')
+ return glob.glob(pattern)[0]
+
+ @staticmethod
+ def run_esdk_cmd(env_eSDK, tmpdir_eSDKQA, cmd, postconfig=None, **options):
+ if postconfig:
+ esdk_conf_file = os.path.join(tmpdir_eSDKQA, 'conf', 'local.conf')
+ with open(esdk_conf_file, 'a+') as f:
+ f.write(postconfig)
+ if not options:
+ options = {}
+ if not 'shell' in options:
+ options['shell'] = True
+
+ runCmd("cd %s; . %s; %s" % (tmpdir_eSDKQA, env_eSDK, cmd), **options)
+
+ @staticmethod
+ def generate_eSDK(image):
+ pn_task = '%s -c populate_sdk_ext' % image
+ bitbake(pn_task)
+
+ @staticmethod
+ def get_eSDK_toolchain(image):
+ pn_task = '%s -c populate_sdk_ext' % image
+
+ sdk_deploy = get_bb_var('SDK_DEPLOY', pn_task)
+ toolchain_name = get_bb_var('TOOLCHAINEXT_OUTPUTNAME', pn_task)
+ return os.path.join(sdk_deploy, toolchain_name + '.sh')
+
+
+ @classmethod
+ def setUpClass(cls):
+ # Start to serve sstate dir
+ sstate_dir = os.path.join(os.environ['BUILDDIR'], 'sstate-cache')
+ cls.http_service = HTTPService(sstate_dir)
+ cls.http_service.start()
+
+ http_url = "127.0.0.1:%d" % cls.http_service.port
+
+ image = 'core-image-minimal'
+
+ cls.tmpdir_eSDKQA = tempfile.mkdtemp(prefix='eSDKQA')
+ oeSDKExtSelfTest.generate_eSDK(image)
+
+ # Install eSDK
+ ext_sdk_path = oeSDKExtSelfTest.get_eSDK_toolchain(image)
+ runCmd("%s -y -d \"%s\"" % (ext_sdk_path, cls.tmpdir_eSDKQA))
+
+ cls.env_eSDK = oeSDKExtSelfTest.get_esdk_environment('', cls.tmpdir_eSDKQA)
+
+ # Configure eSDK to use sstate mirror from poky
+ sstate_config="""
+SDK_LOCAL_CONF_WHITELIST = "SSTATE_MIRRORS"
+SSTATE_MIRRORS = "file://.* http://%s/PATH"
+ """ % http_url
+ with open(os.path.join(cls.tmpdir_eSDKQA, 'conf', 'local.conf'), 'a+') as f:
+ f.write(sstate_config)
+
+
+ @classmethod
+ def tearDownClass(cls):
+ shutil.rmtree(cls.tmpdir_eSDKQA)
+ cls.http_service.stop()
+
+ @testcase (1471)
+ def test_install_libraries_headers(self):
+ pn_sstate = 'bc'
+ bitbake(pn_sstate)
+ cmd = "devtool sdk-install %s " % pn_sstate
+ oeSDKExtSelfTest.run_esdk_cmd(self.env_eSDK, self.tmpdir_eSDKQA, cmd)
+
+ @testcase(1472)
+ def test_image_generation_binary_feeds(self):
+ image = 'core-image-minimal'
+ cmd = "devtool build-image %s" % image
+ oeSDKExtSelfTest.run_esdk_cmd(self.env_eSDK, self.tmpdir_eSDKQA, cmd)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/esdk_prepare.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/esdk_prepare.py
deleted file mode 100755
index 1b36a0d68..000000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/esdk_prepare.py
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/env python
-
-import shutil, tempfile
-import sys
-import os
-import imp
-import unittest
-try:
- from oeqa.utils.commands import get_bb_var
-except ImportError:
- pass
-
-# module under test
-module_file_name = "ext-sdk-prepare.py"
-module_path = ""
-
-class ExtSdkPrepareTest(unittest.TestCase):
-
- """ unit test for fix for Yocto #9019 """
-
- @classmethod
- def setUpClass(self):
- # copy module under test to temp dir
- self.test_dir = tempfile.mkdtemp()
- module_dest_path = os.path.join(self.test_dir, module_file_name)
- try:
- shutil.copy(module_path, self.test_dir)
- # load module under test
- self.test_mod = imp.load_source("", module_dest_path)
- except:
- print "error: unable to copy or load %s [src: %s, dst: %s]" % \
- (module_file_name, module_path, module_dest_path)
- sys.exit(1)
-
- def test_prepare_unexpected(self):
- # test data
- # note: pathnames have been truncated from the actual bitbake
- # output as they are not important for the test.
- test_data = (
- 'NOTE: Running noexec task 9 of 6539 (ID: 28, quilt/quilt-native_0.64.bb, do_build)\n'
- 'NOTE: Running task 10 of 6539 (ID: 29, quilt/quilt-native_0.64.bb, do_package)\n'
- 'NOTE: Running task 11 of 6539 (ID: 30, quilt/quilt-native_0.64.bb, do_rm_work)\n'
- 'NOTE: Running noexec task 6402 of 6539 (ID: 1, images/core-image-sato.bb, do_patch)\n'
- 'NOTE: Running task 6538 of 6539 (ID: 14, images/core-image-sato.bb, do_rm_work)\n'
- )
- # expected warning output
- expected = [ (' task 10 of 6539 (ID: 29, quilt/quilt-native_0.64.bb, do_package)') ]
- # recipe to test, matching test input data
- recipes = [ "core-image-sato.bb" ]
-
- # run the test
- output = self.test_mod.check_unexpected(test_data, recipes)
- self.assertEqual(output, expected)
-
- @classmethod
- def tearDownClass(self):
- # remove temp dir
- shutil.rmtree(self.test_dir)
-
-if __name__ == '__main__':
- # running from command line - i.e., not under oe-selftest
- # directory containing module under test comes from command line
- if len(sys.argv) == 2 and os.path.isdir(sys.argv[1]):
- module_path = os.path.join(sys.argv[1], module_file_name)
- suite = unittest.TestLoader().loadTestsFromTestCase(ExtSdkPrepareTest)
- unittest.TextTestRunner().run(suite)
- else:
- progname = os.path.basename(sys.argv[0])
- print "%s: missing directory path" % progname
- print "usage: %s /path/to/directory-of(ext-sdk-prepare.py)" % progname
- sys.exit(1)
-else:
- # running under oe-selftest
- # determine module source dir from COREBASE and expected path
- module_path = os.path.join(get_bb_var("COREBASE"), "meta", "files", module_file_name)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/imagefeatures.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/imagefeatures.py
index 8a53899c7..d015c4908 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/imagefeatures.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/imagefeatures.py
@@ -98,3 +98,30 @@ class ImageFeatures(oeSelfTest):
# Build a core-image-weston
bitbake('core-image-weston')
+ def test_bmap(self):
+ """
+ Summary: Check bmap support
+ Expected: 1. core-image-minimal can be build with bmap support
+ 2. core-image-minimal is sparse
+ Product: oe-core
+ Author: Ed Bartosh <ed.bartosh@linux.intel.com>
+ """
+
+ features = 'IMAGE_FSTYPES += " ext4 ext4.bmap"'
+ self.write_config(features)
+
+ image_name = 'core-image-minimal'
+ bitbake(image_name)
+
+ deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
+ link_name = get_bb_var('IMAGE_LINK_NAME', image_name)
+ image_path = os.path.join(deploy_dir_image, "%s.ext4" % link_name)
+ bmap_path = "%s.bmap" % image_path
+
+ # check if result image and bmap file are in deploy directory
+ self.assertTrue(os.path.exists(image_path))
+ self.assertTrue(os.path.exists(bmap_path))
+
+ # check if result image is sparse
+ image_stat = os.stat(image_path)
+ self.assertTrue(image_stat.st_size > image_stat.st_blocks * 512)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/liboe.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/liboe.py
new file mode 100644
index 000000000..35131eb24
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/liboe.py
@@ -0,0 +1,93 @@
+from oeqa.selftest.base import oeSelfTest
+from oeqa.utils.commands import get_bb_var, bitbake, runCmd
+import oe.path
+import glob
+import os
+import os.path
+
+class LibOE(oeSelfTest):
+ def test_copy_tree_special(self):
+ """
+ Summary: oe.path.copytree() should copy files with special character
+ Expected: 'test file with sp£c!al @nd spaces' should exist in
+ copy destination
+ Product: OE-Core
+ Author: Joshua Lock <joshua.g.lock@intel.com>
+ """
+ tmp_dir = get_bb_var('TMPDIR')
+ testloc = oe.path.join(tmp_dir, 'liboetests')
+ src = oe.path.join(testloc, 'src')
+ dst = oe.path.join(testloc, 'dst')
+ bb.utils.mkdirhier(testloc)
+ bb.utils.mkdirhier(src)
+ testfilename = 'test file with sp£c!al @nd spaces'
+
+ # create the test file and copy it
+ open(oe.path.join(src, testfilename), 'w+b').close()
+ oe.path.copytree(src, dst)
+
+ # ensure path exists in dest
+ fileindst = os.path.isfile(oe.path.join(dst, testfilename))
+ self.assertTrue(fileindst, "File with spaces doesn't exist in dst")
+
+ oe.path.remove(testloc)
+
+ def test_copy_tree_xattr(self):
+ """
+ Summary: oe.path.copytree() should preserve xattr on copied files
+ Expected: testxattr file in destination should have user.oetest
+ extended attribute
+ Product: OE-Core
+ Author: Joshua Lock <joshua.g.lock@intel.com>
+ """
+ tmp_dir = get_bb_var('TMPDIR')
+ testloc = oe.path.join(tmp_dir, 'liboetests')
+ src = oe.path.join(testloc, 'src')
+ dst = oe.path.join(testloc, 'dst')
+ bb.utils.mkdirhier(testloc)
+ bb.utils.mkdirhier(src)
+ testfilename = 'testxattr'
+
+ # ensure we have setfattr available
+ bitbake("attr-native")
+ bindir = get_bb_var('STAGING_BINDIR_NATIVE')
+
+ # create a file with xattr and copy it
+ open(oe.path.join(src, testfilename), 'w+b').close()
+ runCmd('%s/setfattr -n user.oetest -v "testing liboe" %s' % (bindir, oe.path.join(src, testfilename)))
+ oe.path.copytree(src, dst)
+
+ # ensure file in dest has user.oetest xattr
+ result = runCmd('%s/getfattr -n user.oetest %s' % (bindir, oe.path.join(dst, testfilename)))
+ self.assertIn('user.oetest="testing liboe"', result.output, 'Extended attribute not sert in dst')
+
+ oe.path.remove(testloc)
+
+ def test_copy_hardlink_tree_count(self):
+ """
+ Summary: oe.path.copyhardlinktree() shouldn't miss out files
+ Expected: src and dst should have the same number of files
+ Product: OE-Core
+ Author: Joshua Lock <joshua.g.lock@intel.com>
+ """
+ tmp_dir = get_bb_var('TMPDIR')
+ testloc = oe.path.join(tmp_dir, 'liboetests')
+ src = oe.path.join(testloc, 'src')
+ dst = oe.path.join(testloc, 'dst')
+ bb.utils.mkdirhier(testloc)
+ bb.utils.mkdirhier(src)
+ testfiles = ['foo', 'bar', '.baz', 'quux']
+
+ def touchfile(tf):
+ open(oe.path.join(src, tf), 'w+b').close()
+
+ for f in testfiles:
+ touchfile(f)
+
+ oe.path.copyhardlinktree(src, dst)
+
+ dstcnt = len(os.listdir(dst))
+ srccnt = len(os.listdir(src))
+ self.assertEquals(dstcnt, len(testfiles), "Number of files in dst (%s) differs from number of files in src(%s)." % (dstcnt, srccnt))
+
+ oe.path.remove(testloc)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/lic-checksum.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/lic-checksum.py
index cac6d8445..2e81373ae 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/lic-checksum.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/lic-checksum.py
@@ -12,20 +12,24 @@ class LicenseTests(oeSelfTest):
# the license qa to fail due to a mismatched md5sum.
@testcase(1197)
def test_nonmatching_checksum(self):
- bitbake_cmd = '-c configure emptytest'
+ bitbake_cmd = '-c populate_lic emptytest'
error_msg = 'emptytest: The new md5 checksum is 8d777f385d3dfec8815d20f7496026dc'
lic_file, lic_path = tempfile.mkstemp()
os.close(lic_file)
self.track_for_cleanup(lic_path)
- self.write_recipeinc('emptytest', 'INHIBIT_DEFAULT_DEPS = "1"')
- self.append_recipeinc('emptytest', 'LIC_FILES_CHKSUM = "file://%s;md5=d41d8cd98f00b204e9800998ecf8427e"' % lic_path)
+ self.write_recipeinc('emptytest', """
+INHIBIT_DEFAULT_DEPS = "1"
+LIC_FILES_CHKSUM = "file://%s;md5=d41d8cd98f00b204e9800998ecf8427e"
+SRC_URI = "file://%s;md5=d41d8cd98f00b204e9800998ecf8427e"
+""" % (lic_path, lic_path))
result = bitbake(bitbake_cmd)
with open(lic_path, "w") as f:
f.write("data")
+ self.write_config("INHERIT_remove = \"report-error\"")
result = bitbake(bitbake_cmd, ignore_status=True)
if error_msg not in result.output:
raise AssertionError(result.output)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/pkgdata.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/pkgdata.py
index 138b03aad..5a63f89ff 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/pkgdata.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/pkgdata.py
@@ -131,15 +131,15 @@ class OePkgdataUtilTests(oeSelfTest):
# Test recipe-space package name
result = runCmd('oe-pkgdata-util list-pkg-files zlib-dev zlib-doc')
files = splitoutput(result.output)
- self.assertIn('zlib-dev', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('zlib-doc', files.keys(), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-doc', list(files.keys()), "listed pkgs. files: %s" %result.output)
self.assertIn(os.path.join(includedir, 'zlib.h'), files['zlib-dev'])
self.assertIn(os.path.join(mandir, 'man3/zlib.3'), files['zlib-doc'])
# Test runtime package name
result = runCmd('oe-pkgdata-util list-pkg-files -r libz1 libz-dev')
files = splitoutput(result.output)
- self.assertIn('libz1', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('libz-dev', files.keys(), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz1', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
self.assertGreater(len(files['libz1']), 1)
libspec = os.path.join(base_libdir, 'libz.so.1.*')
found = False
@@ -152,12 +152,12 @@ class OePkgdataUtilTests(oeSelfTest):
# Test recipe
result = runCmd('oe-pkgdata-util list-pkg-files -p zlib')
files = splitoutput(result.output)
- self.assertIn('zlib-dbg', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('zlib-doc', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('zlib-dev', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('zlib-staticdev', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('zlib', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertNotIn('zlib-locale', files.keys(), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-dbg', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-doc', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-staticdev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertNotIn('zlib-locale', list(files.keys()), "listed pkgs. files: %s" %result.output)
# (ignore ptest, might not be there depending on config)
self.assertIn(os.path.join(includedir, 'zlib.h'), files['zlib-dev'])
self.assertIn(os.path.join(mandir, 'man3/zlib.3'), files['zlib-doc'])
@@ -165,36 +165,36 @@ class OePkgdataUtilTests(oeSelfTest):
# Test recipe, runtime
result = runCmd('oe-pkgdata-util list-pkg-files -p zlib -r')
files = splitoutput(result.output)
- self.assertIn('libz-dbg', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('libz-doc', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('libz-dev', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('libz-staticdev', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('libz1', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertNotIn('libz-locale', files.keys(), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-dbg', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-doc', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-staticdev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz1', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertNotIn('libz-locale', list(files.keys()), "listed pkgs. files: %s" %result.output)
self.assertIn(os.path.join(includedir, 'zlib.h'), files['libz-dev'])
self.assertIn(os.path.join(mandir, 'man3/zlib.3'), files['libz-doc'])
self.assertIn(os.path.join(libdir, 'libz.a'), files['libz-staticdev'])
# Test recipe, unpackaged
result = runCmd('oe-pkgdata-util list-pkg-files -p zlib -u')
files = splitoutput(result.output)
- self.assertIn('zlib-dbg', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('zlib-doc', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('zlib-dev', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('zlib-staticdev', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('zlib', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('zlib-locale', files.keys(), "listed pkgs. files: %s" %result.output) # this is the key one
+ self.assertIn('zlib-dbg', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-doc', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-staticdev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-locale', list(files.keys()), "listed pkgs. files: %s" %result.output) # this is the key one
self.assertIn(os.path.join(includedir, 'zlib.h'), files['zlib-dev'])
self.assertIn(os.path.join(mandir, 'man3/zlib.3'), files['zlib-doc'])
self.assertIn(os.path.join(libdir, 'libz.a'), files['zlib-staticdev'])
# Test recipe, runtime, unpackaged
result = runCmd('oe-pkgdata-util list-pkg-files -p zlib -r -u')
files = splitoutput(result.output)
- self.assertIn('libz-dbg', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('libz-doc', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('libz-dev', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('libz-staticdev', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('libz1', files.keys(), "listed pkgs. files: %s" %result.output)
- self.assertIn('libz-locale', files.keys(), "listed pkgs. files: %s" %result.output) # this is the key one
+ self.assertIn('libz-dbg', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-doc', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-staticdev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz1', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-locale', list(files.keys()), "listed pkgs. files: %s" %result.output) # this is the key one
self.assertIn(os.path.join(includedir, 'zlib.h'), files['libz-dev'])
self.assertIn(os.path.join(mandir, 'man3/zlib.3'), files['libz-doc'])
self.assertIn(os.path.join(libdir, 'libz.a'), files['libz-staticdev'])
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/recipetool.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/recipetool.py
index e72911b0a..db1f8deeb 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/recipetool.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/recipetool.py
@@ -1,7 +1,7 @@
import os
import logging
import tempfile
-import urlparse
+import urllib.parse
from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer
from oeqa.utils.decorators import testcase
@@ -278,7 +278,7 @@ class RecipetoolTests(RecipetoolBase):
'}\n']
_, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/etc/selftest-replaceme-patched', self.testfile, '', expectedlines, ['testfile'])
for line in output.splitlines():
- if line.startswith('WARNING: '):
+ if 'WARNING: ' in line:
self.assertIn('add-file.patch', line, 'Unexpected warning found in output:\n%s' % line)
break
else:
@@ -383,13 +383,13 @@ class RecipetoolTests(RecipetoolBase):
@testcase(1194)
def test_recipetool_create_git(self):
# Ensure we have the right data in shlibs/pkgdata
- bitbake('libpng pango libx11 libxext jpeg libxsettings-client libcheck')
+ bitbake('libpng pango libx11 libxext jpeg libcheck')
# Try adding a recipe
tempsrc = os.path.join(self.tempdir, 'srctree')
os.makedirs(tempsrc)
recipefile = os.path.join(self.tempdir, 'libmatchbox.bb')
srcuri = 'git://git.yoctoproject.org/libmatchbox'
- result = runCmd('recipetool create -o %s %s -x %s' % (recipefile, srcuri, tempsrc))
+ result = runCmd(['recipetool', 'create', '-o', recipefile, srcuri + ";rev=9f7cf8895ae2d39c465c04cc78e918c157420269", '-x', tempsrc])
self.assertTrue(os.path.isfile(recipefile), 'recipetool did not create recipe file; output:\n%s' % result.output)
checkvars = {}
checkvars['LICENSE'] = 'LGPLv2.1'
@@ -397,7 +397,7 @@ class RecipetoolTests(RecipetoolBase):
checkvars['S'] = '${WORKDIR}/git'
checkvars['PV'] = '1.11+git${SRCPV}'
checkvars['SRC_URI'] = srcuri
- checkvars['DEPENDS'] = set(['libcheck', 'libjpeg-turbo', 'libpng', 'libx11', 'libxsettings-client', 'libxext', 'pango'])
+ checkvars['DEPENDS'] = set(['libcheck', 'libjpeg-turbo', 'libpng', 'libx11', 'libxext', 'pango'])
inherits = ['autotools', 'pkgconfig']
self._test_recipe_contents(recipefile, checkvars, inherits)
@@ -442,6 +442,49 @@ class RecipetoolTests(RecipetoolBase):
inherits = ['cmake', 'python-dir', 'gettext', 'pkgconfig']
self._test_recipe_contents(recipefile, checkvars, inherits)
+ def test_recipetool_create_github(self):
+ # Basic test to see if github URL mangling works
+ temprecipe = os.path.join(self.tempdir, 'recipe')
+ os.makedirs(temprecipe)
+ recipefile = os.path.join(temprecipe, 'meson_git.bb')
+ srcuri = 'https://github.com/mesonbuild/meson'
+ result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri))
+ self.assertTrue(os.path.isfile(recipefile))
+ checkvars = {}
+ checkvars['LICENSE'] = set(['Apache-2.0'])
+ checkvars['SRC_URI'] = 'git://github.com/mesonbuild/meson;protocol=https'
+ inherits = ['setuptools']
+ self._test_recipe_contents(recipefile, checkvars, inherits)
+
+ def test_recipetool_create_github_tarball(self):
+ # Basic test to ensure github URL mangling doesn't apply to release tarballs
+ temprecipe = os.path.join(self.tempdir, 'recipe')
+ os.makedirs(temprecipe)
+ pv = '0.32.0'
+ recipefile = os.path.join(temprecipe, 'meson_%s.bb' % pv)
+ srcuri = 'https://github.com/mesonbuild/meson/releases/download/%s/meson-%s.tar.gz' % (pv, pv)
+ result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri))
+ self.assertTrue(os.path.isfile(recipefile))
+ checkvars = {}
+ checkvars['LICENSE'] = set(['Apache-2.0'])
+ checkvars['SRC_URI'] = 'https://github.com/mesonbuild/meson/releases/download/${PV}/meson-${PV}.tar.gz'
+ inherits = ['setuptools']
+ self._test_recipe_contents(recipefile, checkvars, inherits)
+
+ def test_recipetool_create_git_http(self):
+ # Basic test to check http git URL mangling works
+ temprecipe = os.path.join(self.tempdir, 'recipe')
+ os.makedirs(temprecipe)
+ recipefile = os.path.join(temprecipe, 'matchbox-terminal_git.bb')
+ srcuri = 'http://git.yoctoproject.org/git/matchbox-terminal'
+ result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri))
+ self.assertTrue(os.path.isfile(recipefile))
+ checkvars = {}
+ checkvars['LICENSE'] = set(['GPLv2'])
+ checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/matchbox-terminal;protocol=http'
+ inherits = ['pkgconfig', 'autotools']
+ self._test_recipe_contents(recipefile, checkvars, inherits)
+
class RecipetoolAppendsrcBase(RecipetoolBase):
def _try_recipetool_appendsrcfile(self, testrecipe, newfile, destfile, options, expectedlines, expectedfiles):
cmd = 'recipetool appendsrcfile %s %s %s %s %s' % (options, self.templayerdir, testrecipe, newfile, destfile)
@@ -471,7 +514,7 @@ class RecipetoolAppendsrcBase(RecipetoolBase):
'''Return the first file:// in SRC_URI for the specified recipe.'''
src_uri = get_bb_var('SRC_URI', recipe).split()
for uri in src_uri:
- p = urlparse.urlparse(uri)
+ p = urllib.parse.urlparse(uri)
if p.scheme == 'file':
return p.netloc + p.path
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/runtime-test.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/runtime-test.py
new file mode 100644
index 000000000..c2d5b45a4
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/runtime-test.py
@@ -0,0 +1,105 @@
+from oeqa.selftest.base import oeSelfTest
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, runqemu
+from oeqa.utils.decorators import testcase
+import os
+
+class TestExport(oeSelfTest):
+
+ def test_testexport_basic(self):
+ """
+ Summary: Check basic testexport functionality with only ping test enabled.
+ Expected: 1. testexport directory must be created.
+ 2. runexported.py must run without any error/exception.
+ 3. ping test must succeed.
+ Product: oe-core
+ Author: Mariano Lopez <mariano.lopez@intel.com>
+ """
+
+ features = 'INHERIT += "testexport"\n'
+ # These aren't the actual IP addresses but testexport class needs something defined
+ features += 'TEST_SERVER_IP = "192.168.7.1"\n'
+ features += 'TEST_TARGET_IP = "192.168.7.1"\n'
+ features += 'TEST_SUITES = "ping"\n'
+ self.write_config(features)
+
+ # Build tesexport for core-image-minimal
+ bitbake('core-image-minimal')
+ bitbake('-c testexport core-image-minimal')
+
+ # Verify if TEST_EXPORT_DIR was created
+ testexport_dir = get_bb_var('TEST_EXPORT_DIR', 'core-image-minimal')
+ isdir = os.path.isdir(testexport_dir)
+ self.assertEqual(True, isdir, 'Failed to create testexport dir: %s' % testexport_dir)
+
+ with runqemu('core-image-minimal') as qemu:
+ # Attempt to run runexported.py to perform ping test
+ runexported_path = os.path.join(testexport_dir, "runexported.py")
+ testdata_path = os.path.join(testexport_dir, "testdata.json")
+ cmd = "%s -t %s -s %s %s" % (runexported_path, qemu.ip, qemu.server_ip, testdata_path)
+ result = runCmd(cmd)
+ self.assertEqual(0, result.status, 'runexported.py returned a non 0 status')
+
+ # Verify ping test was succesful
+ failure = True if 'FAIL' in result.output else False
+ self.assertNotEqual(True, failure, 'ping test failed')
+
+ def test_testexport_sdk(self):
+ """
+ Summary: Check sdk functionality for testexport.
+ Expected: 1. testexport directory must be created.
+ 2. SDK tarball must exists.
+ 3. Uncompressing of tarball must succeed.
+ 4. Check if the SDK directory is added to PATH.
+ 5. Run tar from the SDK directory.
+ Product: oe-core
+ Author: Mariano Lopez <mariano.lopez@intel.com>
+ """
+
+ features = 'INHERIT += "testexport"\n'
+ # These aren't the actual IP addresses but testexport class needs something defined
+ features += 'TEST_SERVER_IP = "192.168.7.1"\n'
+ features += 'TEST_TARGET_IP = "192.168.7.1"\n'
+ features += 'TEST_SUITES = "ping"\n'
+ features += 'TEST_SUITES_TAGS = "selftest_sdk"\n'
+ features += 'TEST_EXPORT_SDK_ENABLED = "1"\n'
+ features += 'TEST_EXPORT_SDK_PACKAGES = "nativesdk-tar"\n'
+ self.write_config(features)
+
+ # Build tesexport for core-image-minimal
+ bitbake('core-image-minimal')
+ bitbake('-c testexport core-image-minimal')
+
+ # Check for SDK
+ testexport_dir = get_bb_var('TEST_EXPORT_DIR', 'core-image-minimal')
+ sdk_dir = get_bb_var('TEST_EXPORT_SDK_DIR', 'core-image-minimal')
+ tarball_name = "%s.sh" % get_bb_var('TEST_EXPORT_SDK_NAME', 'core-image-minimal')
+ tarball_path = os.path.join(testexport_dir, sdk_dir, tarball_name)
+ self.assertEqual(os.path.isfile(tarball_path), True, "Couldn't find SDK tarball: %s" % tarball_path)
+
+ # Run runexported.py
+ runexported_path = os.path.join(testexport_dir, "runexported.py")
+ testdata_path = os.path.join(testexport_dir, "testdata.json")
+ cmd = "%s %s" % (runexported_path, testdata_path)
+ result = runCmd(cmd)
+ self.assertEqual(0, result.status, 'runexported.py returned a non 0 status')
+
+
+class TestImage(oeSelfTest):
+
+ def test_testimage_install(self):
+ """
+ Summary: Check install packages functionality for testimage/testexport.
+ Expected: 1. Import tests from a directory other than meta.
+ 2. Check install/unistall of socat.
+ Product: oe-core
+ Author: Mariano Lopez <mariano.lopez@intel.com>
+ """
+
+ features = 'INHERIT += "testimage"\n'
+ features += 'TEST_SUITES = "ping ssh selftest"\n'
+ features += 'TEST_SUITES_TAGS = "selftest_package_install"\n'
+ self.write_config(features)
+
+ # Build core-image-sato and testimage
+ bitbake('core-image-full-cmdline socat')
+ bitbake('-c testimage core-image-full-cmdline')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/signing.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/signing.py
index 1babca07d..4c12d6d94 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/signing.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/signing.py
@@ -12,30 +12,22 @@ from oeqa.utils.ftools import write_file
class Signing(oeSelfTest):
gpg_dir = ""
- pub_key_name = 'key.pub'
- secret_key_name = 'key.secret'
+ pub_key_path = ""
+ secret_key_path = ""
@classmethod
def setUpClass(cls):
- # Import the gpg keys
+ # Check that we can find the gpg binary and fail early if we can't
+ if not shutil.which("gpg"):
+ raise AssertionError("This test needs GnuPG")
- cls.gpg_dir = os.path.join(cls.testlayer_path, 'files/signing/')
+ cls.gpg_home_dir = tempfile.TemporaryDirectory(prefix="oeqa-signing-")
+ cls.gpg_dir = cls.gpg_home_dir.name
- # key.secret key.pub are located in gpg_dir
- pub_key_location = cls.gpg_dir + cls.pub_key_name
- secret_key_location = cls.gpg_dir + cls.secret_key_name
- runCmd('gpg --homedir %s --import %s %s' % (cls.gpg_dir, pub_key_location, secret_key_location))
+ cls.pub_key_path = os.path.join(cls.testlayer_path, 'files', 'signing', "key.pub")
+ cls.secret_key_path = os.path.join(cls.testlayer_path, 'files', 'signing', "key.secret")
- @classmethod
- def tearDownClass(cls):
- # Delete the files generated by 'gpg --import'
-
- gpg_files = glob.glob(cls.gpg_dir + '*.gpg*')
- random_seed_file = cls.gpg_dir + 'random_seed'
- gpg_files.append(random_seed_file)
-
- for gpg_file in gpg_files:
- runCmd('rm -f ' + gpg_file)
+ runCmd('gpg --homedir %s --import %s %s' % (cls.gpg_dir, cls.pub_key_path, cls.secret_key_path))
@testcase(1362)
def test_signing_packages(self):
@@ -57,7 +49,7 @@ class Signing(oeSelfTest):
feature = 'INHERIT += "sign_rpm"\n'
feature += 'RPM_GPG_PASSPHRASE = "test123"\n'
feature += 'RPM_GPG_NAME = "testuser"\n'
- feature += 'RPM_GPG_PUBKEY = "%s%s"\n' % (self.gpg_dir, self.pub_key_name)
+ feature += 'RPM_GPG_PUBKEY = "%s"\n' % self.pub_key_path
feature += 'GPG_PATH = "%s"\n' % self.gpg_dir
self.write_config(feature)
@@ -81,8 +73,8 @@ class Signing(oeSelfTest):
# Use a temporary rpmdb
rpmdb = tempfile.mkdtemp(prefix='oeqa-rpmdb')
- runCmd('%s/rpm --define "_dbpath %s" --import %s%s' %
- (staging_bindir_native, rpmdb, self.gpg_dir, self.pub_key_name))
+ runCmd('%s/rpm --define "_dbpath %s" --import %s' %
+ (staging_bindir_native, rpmdb, self.pub_key_path))
ret = runCmd('%s/rpm --define "_dbpath %s" --checksig %s' %
(staging_bindir_native, rpmdb, pkg_deploy))
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/sstatetests.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/sstatetests.py
index acaf405ac..6642539eb 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/sstatetests.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/sstatetests.py
@@ -237,6 +237,7 @@ TMPDIR = "${TOPDIR}/tmp-sstatesamehash"
BUILD_ARCH = "x86_64"
BUILD_OS = "linux"
SDKMACHINE = "x86_64"
+PACKAGE_CLASSES = "package_rpm package_ipk package_deb"
""")
self.track_for_cleanup(topdir + "/tmp-sstatesamehash")
bitbake("core-image-sato -S none")
@@ -246,6 +247,7 @@ TMPDIR = "${TOPDIR}/tmp-sstatesamehash2"
BUILD_ARCH = "i686"
BUILD_OS = "linux"
SDKMACHINE = "i686"
+PACKAGE_CLASSES = "package_rpm package_ipk package_deb"
""")
self.track_for_cleanup(topdir + "/tmp-sstatesamehash2")
bitbake("core-image-sato -S none")
@@ -264,7 +266,7 @@ SDKMACHINE = "i686"
files2 = get_files(topdir + "/tmp-sstatesamehash2/stamps/")
files2 = [x.replace("tmp-sstatesamehash2", "tmp-sstatesamehash").replace("i686-linux", "x86_64-linux").replace("i686" + targetvendor + "-linux", "x86_64" + targetvendor + "-linux", ) for x in files2]
self.maxDiff = None
- self.assertItemsEqual(files1, files2)
+ self.assertCountEqual(files1, files2)
@testcase(1271)
@@ -298,7 +300,7 @@ NATIVELSBSTRING = \"DistroB\"
files2 = get_files(topdir + "/tmp-sstatesamehash2/stamps/")
files2 = [x.replace("tmp-sstatesamehash2", "tmp-sstatesamehash") for x in files2]
self.maxDiff = None
- self.assertItemsEqual(files1, files2)
+ self.assertCountEqual(files1, files2)
@testcase(1368)
def test_sstate_allarch_samesigs(self):
@@ -309,19 +311,48 @@ NATIVELSBSTRING = \"DistroB\"
the two MACHINE values.
"""
+ configA = """
+TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
+MACHINE = \"qemux86-64\"
+"""
+ configB = """
+TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
+MACHINE = \"qemuarm\"
+"""
+ self.sstate_allarch_samesigs(configA, configB)
+
+ def test_sstate_allarch_samesigs_multilib(self):
+ """
+ The sstate checksums of allarch multilib packages should be independent of whichever
+ MACHINE is set. Check this using bitbake -S.
+ Also, rather than duplicate the test, check nativesdk stamps are the same between
+ the two MACHINE values.
+ """
+
+ configA = """
+TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
+MACHINE = \"qemux86-64\"
+require conf/multilib.conf
+MULTILIBS = \"multilib:lib32\"
+DEFAULTTUNE_virtclass-multilib-lib32 = \"x86\"
+"""
+ configB = """
+TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
+MACHINE = \"qemuarm\"
+require conf/multilib.conf
+MULTILIBS = \"\"
+"""
+ self.sstate_allarch_samesigs(configA, configB)
+
+ def sstate_allarch_samesigs(self, configA, configB):
+
topdir = get_bb_var('TOPDIR')
targetos = get_bb_var('TARGET_OS')
targetvendor = get_bb_var('TARGET_VENDOR')
- self.write_config("""
-TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
-MACHINE = \"qemux86\"
-""")
+ self.write_config(configA)
self.track_for_cleanup(topdir + "/tmp-sstatesamehash")
bitbake("world meta-toolchain -S none")
- self.write_config("""
-TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
-MACHINE = \"qemuarm\"
-""")
+ self.write_config(configB)
self.track_for_cleanup(topdir + "/tmp-sstatesamehash2")
bitbake("world meta-toolchain -S none")
@@ -393,7 +424,7 @@ DEFAULTTUNE_virtclass-multilib-lib32 = "x86"
files2 = get_files(topdir + "/tmp-sstatesamehash2/stamps")
files2 = [x.replace("tmp-sstatesamehash2", "tmp-sstatesamehash") for x in files2]
self.maxDiff = None
- self.assertItemsEqual(files1, files2)
+ self.assertCountEqual(files1, files2)
def test_sstate_noop_samesigs(self):
@@ -411,9 +442,11 @@ PARALLEL_MAKE = "-j 1"
DL_DIR = "${TOPDIR}/download1"
TIME = "111111"
DATE = "20161111"
-INHERIT_remove = "buildstats-summary buildhistory"
+INHERIT_remove = "buildstats-summary buildhistory uninative"
+http_proxy = ""
""")
self.track_for_cleanup(topdir + "/tmp-sstatesamehash")
+ self.track_for_cleanup(topdir + "/download1")
bitbake("world meta-toolchain -S none")
self.write_config("""
TMPDIR = "${TOPDIR}/tmp-sstatesamehash2"
@@ -422,9 +455,13 @@ PARALLEL_MAKE = "-j 2"
DL_DIR = "${TOPDIR}/download2"
TIME = "222222"
DATE = "20161212"
+# Always remove uninative as we're changing proxies
+INHERIT_remove = "uninative"
INHERIT += "buildstats-summary buildhistory"
+http_proxy = "http://example.com/"
""")
self.track_for_cleanup(topdir + "/tmp-sstatesamehash2")
+ self.track_for_cleanup(topdir + "/download2")
bitbake("world meta-toolchain -S none")
def get_files(d):
@@ -439,23 +476,23 @@ INHERIT += "buildstats-summary buildhistory"
files1 = get_files(topdir + "/tmp-sstatesamehash/stamps/")
files2 = get_files(topdir + "/tmp-sstatesamehash2/stamps/")
# Remove items that are identical in both sets
- for k,v in files1.viewitems() & files2.viewitems():
+ for k,v in files1.items() & files2.items():
del files1[k]
del files2[k]
if not files1 and not files2:
# No changes, so we're done
return
- for k in files1.viewkeys() | files2.viewkeys():
+ for k in files1.keys() | files2.keys():
if k in files1 and k in files2:
- print "%s differs:" % k
- print subprocess.check_output(("bitbake-diffsigs",
+ print("%s differs:" % k)
+ print(subprocess.check_output(("bitbake-diffsigs",
topdir + "/tmp-sstatesamehash/stamps/" + k + "." + files1[k],
- topdir + "/tmp-sstatesamehash2/stamps/" + k + "." + files2[k]))
+ topdir + "/tmp-sstatesamehash2/stamps/" + k + "." + files2[k])))
elif k in files1 and k not in files2:
- print "%s in files1" % k
+ print("%s in files1" % k)
elif k not in files1 and k in files2:
- print "%s in files2" % k
+ print("%s in files2" % k)
else:
assert "shouldn't reach here"
self.fail("sstate hashes not identical.")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/wic.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/wic.py
index a569fbf74..faac11e21 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/wic.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/wic.py
@@ -49,7 +49,7 @@ class Wic(oeSelfTest):
# setUpClass being unavailable.
if not Wic.image_is_ready:
bitbake('syslinux syslinux-native parted-native gptfdisk-native '
- 'dosfstools-native mtools-native')
+ 'dosfstools-native mtools-native bmap-tools-native')
bitbake('core-image-minimal')
Wic.image_is_ready = True
@@ -276,3 +276,26 @@ class Wic(oeSelfTest):
status, output = qemu.run_serial(command)
self.assertEqual(1, status, 'Failed to run command "%s": %s' % (command, output))
self.assertEqual(output, '/dev/root /\r\n/dev/vda3 /mnt')
+
+ def test_bmap(self):
+ """Test generation of .bmap file"""
+ image = "directdisk"
+ status = runCmd("wic create %s -e core-image-minimal --bmap" % image).status
+ self.assertEqual(0, status)
+ self.assertEqual(1, len(glob(self.resultdir + "%s-*direct" % image)))
+ self.assertEqual(1, len(glob(self.resultdir + "%s-*direct.bmap" % image)))
+
+ def test_systemd_bootdisk(self):
+ """Test creation of systemd-bootdisk image"""
+ image = "systemd-bootdisk"
+ self.assertEqual(0, runCmd("wic create %s -e core-image-minimal" \
+ % image).status)
+ self.assertEqual(1, len(glob(self.resultdir + "%s-*direct" % image)))
+
+ def test_sdimage_bootpart(self):
+ """Test creation of sdimage-bootpart image"""
+ image = "sdimage-bootpart"
+ self.write_config('IMAGE_BOOT_FILES = "bzImage"\n')
+ self.assertEqual(0, runCmd("wic create %s -e core-image-minimal" \
+ % image).status)
+ self.assertEqual(1, len(glob(self.resultdir + "%s-*direct" % image)))
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/targetcontrol.py b/import-layers/yocto-poky/meta/lib/oeqa/targetcontrol.py
index 5422a617c..24669f461 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/targetcontrol.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/targetcontrol.py
@@ -43,9 +43,7 @@ def get_target_controller(d):
return controller(d)
-class BaseTarget(object):
-
- __metaclass__ = ABCMeta
+class BaseTarget(object, metaclass=ABCMeta):
supported_image_fstypes = []
@@ -68,7 +66,7 @@ class BaseTarget(object):
bb.note("SSH log file: %s" % self.sshlog)
@abstractmethod
- def start(self, params=None, ssh=True):
+ def start(self, params=None, ssh=True, extra_bootparams=None):
pass
@abstractmethod
@@ -121,12 +119,17 @@ class QemuTarget(BaseTarget):
self.image_fstype = self.get_image_fstype(d)
self.qemulog = os.path.join(self.testdir, "qemu_boot_log.%s" % self.datetime)
- self.origrootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("IMAGE_LINK_NAME", True) + '.' + self.image_fstype)
- self.rootfs = os.path.join(self.testdir, d.getVar("IMAGE_LINK_NAME", True) + '-testimage.' + self.image_fstype)
+ self.rootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("IMAGE_LINK_NAME", True) + '.' + self.image_fstype)
self.kernel = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("KERNEL_IMAGETYPE", False) + '-' + d.getVar('MACHINE', False) + '.bin')
dump_target_cmds = d.getVar("testimage_dump_target", True)
dump_host_cmds = d.getVar("testimage_dump_host", True)
dump_dir = d.getVar("TESTIMAGE_DUMP_DIR", True)
+ if d.getVar("QEMU_USE_KVM", False) is not None \
+ and d.getVar("QEMU_USE_KVM", False) == "True" \
+ and "x86" in d.getVar("MACHINE", True):
+ use_kvm = True
+ else:
+ use_kvm = False
# Log QemuRunner log output to a file
import oe.path
@@ -155,17 +158,14 @@ class QemuTarget(BaseTarget):
display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY", True),
logfile = self.qemulog,
boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT", True)),
+ use_kvm = use_kvm,
dump_dir = dump_dir,
dump_host_cmds = d.getVar("testimage_dump_host", True))
self.target_dumper = TargetDumper(dump_target_cmds, dump_dir, self.runner)
def deploy(self):
- try:
- bb.utils.mkdirhier(self.testdir)
- shutil.copyfile(self.origrootfs, self.rootfs)
- except Exception as e:
- bb.fatal("Error copying rootfs: %s" % e)
+ bb.utils.mkdirhier(self.testdir)
qemuloglink = os.path.join(self.testdir, "qemu_boot_log")
if os.path.islink(qemuloglink):
@@ -176,8 +176,8 @@ class QemuTarget(BaseTarget):
bb.note("Qemu log file: %s" % self.qemulog)
super(QemuTarget, self).deploy()
- def start(self, params=None, ssh=True):
- if self.runner.start(params, get_ip=ssh):
+ def start(self, params=None, ssh=True, extra_bootparams=None):
+ if self.runner.start(params, get_ip=ssh, extra_bootparams=extra_bootparams):
if ssh:
self.ip = self.runner.ip
self.server_ip = self.runner.server_ip
@@ -232,7 +232,7 @@ class SimpleRemoteTarget(BaseTarget):
def deploy(self):
super(SimpleRemoteTarget, self).deploy()
- def start(self, params=None, ssh=True):
+ def start(self, params=None, ssh=True, extra_bootparams=None):
if ssh:
self.connection = SSHControl(self.ip, logfile=self.sshlog, port=self.port)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/commands.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/commands.py
index 48f644129..5cd0f7477 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/commands.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/commands.py
@@ -41,7 +41,7 @@ class Command(object):
self.data = data
self.options = dict(self.defaultopts)
- if isinstance(self.cmd, basestring):
+ if isinstance(self.cmd, str):
self.options["shell"] = True
if self.data:
self.options['stdin'] = subprocess.PIPE
@@ -78,7 +78,10 @@ class Command(object):
self.process.kill()
self.thread.join()
- self.output = self.output.rstrip()
+ if not self.output:
+ self.output = ""
+ else:
+ self.output = self.output.decode("utf-8", errors='replace').rstrip()
self.status = self.process.poll()
self.log.debug("Command '%s' returned %d as exit code." % (self.cmd, self.status))
@@ -103,6 +106,7 @@ def runCmd(command, ignore_status=False, timeout=None, assert_error=True, **opti
result.command = command
result.status = cmd.status
result.output = cmd.output
+ result.error = cmd.error
result.pid = cmd.process.pid
if result.status and not ignore_status:
@@ -123,7 +127,7 @@ def bitbake(command, ignore_status=False, timeout=None, postconfig=None, **optio
else:
extra_args = ""
- if isinstance(command, basestring):
+ if isinstance(command, str):
cmd = "bitbake " + extra_args + " " + command
else:
cmd = [ "bitbake" ] + [a for a in (command + extra_args.split(" ")) if a not in [""]]
@@ -141,22 +145,45 @@ def get_bb_env(target=None, postconfig=None):
else:
return bitbake("-e", postconfig=postconfig).output
-def get_bb_var(var, target=None, postconfig=None):
- val = None
+def get_bb_vars(variables=None, target=None, postconfig=None):
+ """Get values of multiple bitbake variables"""
bbenv = get_bb_env(target, postconfig=postconfig)
+
+ var_re = re.compile(r'^(export )?(?P<var>\w+)="(?P<value>.*)"$')
+ unset_re = re.compile(r'^unset (?P<var>\w+)$')
lastline = None
+ values = {}
for line in bbenv.splitlines():
- if re.search("^(export )?%s=" % var, line):
- val = line.split('=', 1)[1]
- val = val.strip('\"')
- break
- elif re.match("unset %s$" % var, line):
- # Handle [unexport] variables
- if lastline.startswith('# "'):
- val = lastline.split('\"')[1]
- break
+ match = var_re.match(line)
+ val = None
+ if match:
+ val = match.group('value')
+ else:
+ match = unset_re.match(line)
+ if match:
+ # Handle [unexport] variables
+ if lastline.startswith('# "'):
+ val = lastline.split('"')[1]
+ if val:
+ var = match.group('var')
+ if variables is None:
+ values[var] = val
+ else:
+ if var in variables:
+ values[var] = val
+ variables.remove(var)
+ # Stop after all required variables have been found
+ if not variables:
+ break
lastline = line
- return val
+ if variables:
+ # Fill in missing values
+ for var in variables:
+ values[var] = None
+ return values
+
+def get_bb_var(var, target=None, postconfig=None):
+ return get_bb_vars([var], target, postconfig)[var]
def get_test_layer():
layers = get_bb_var("BBLAYERS").split()
@@ -196,7 +223,7 @@ def runqemu(pn, ssh=True):
tinfoil.config_data.setVar("TEST_QEMUBOOT_TIMEOUT", "1000")
import oe.recipeutils
recipefile = oe.recipeutils.pn_to_recipe(tinfoil.cooker, pn)
- recipedata = oe.recipeutils.parse_recipe(recipefile, [], tinfoil.config_data)
+ recipedata = oe.recipeutils.parse_recipe(tinfoil.cooker, recipefile, [])
# The QemuRunner log is saved out, but we need to ensure it is at the right
# log level (and then ensure that since it's a child of the BitBake logger,
@@ -237,3 +264,15 @@ def runqemu(pn, ssh=True):
qemu.stop()
except:
pass
+
+def updateEnv(env_file):
+ """
+ Source a file and update environment.
+ """
+
+ cmd = ". %s; env -0" % env_file
+ result = runCmd(cmd)
+
+ for line in result.output.split("\0"):
+ (key, _, value) = line.partition("=")
+ os.environ[key] = value
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/decorators.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/decorators.py
index 0d79223a2..25f9c54e6 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/decorators.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/decorators.py
@@ -57,6 +57,7 @@ class skipIfFailure(object):
self.testcase = testcase
def __call__(self,f):
+ @wraps(f)
def wrapped_f(*args, **kwargs):
res = getResults()
if self.testcase in (res.getFailList() or res.getErrorList()):
@@ -71,6 +72,7 @@ class skipIfSkipped(object):
self.testcase = testcase
def __call__(self,f):
+ @wraps(f)
def wrapped_f(*args, **kwargs):
res = getResults()
if self.testcase in res.getSkipList():
@@ -85,6 +87,7 @@ class skipUnlessPassed(object):
self.testcase = testcase
def __call__(self,f):
+ @wraps(f)
def wrapped_f(*args, **kwargs):
res = getResults()
if self.testcase in res.getSkipList() or \
@@ -97,11 +100,11 @@ class skipUnlessPassed(object):
return wrapped_f
class testcase(object):
-
def __init__(self, test_case):
self.test_case = test_case
def __call__(self, func):
+ @wraps(func)
def wrapped_f(*args, **kwargs):
return func(*args, **kwargs)
wrapped_f.test_case = self.test_case
@@ -112,6 +115,8 @@ class NoParsingFilter(logging.Filter):
def filter(self, record):
return record.levelno == 100
+import inspect
+
def LogResults(original_class):
orig_method = original_class.run
@@ -121,6 +126,19 @@ def LogResults(original_class):
logfile = os.path.join(os.getcwd(),'results-'+caller+'.'+timestamp+'.log')
linkfile = os.path.join(os.getcwd(),'results-'+caller+'.log')
+ def get_class_that_defined_method(meth):
+ if inspect.ismethod(meth):
+ for cls in inspect.getmro(meth.__self__.__class__):
+ if cls.__dict__.get(meth.__name__) is meth:
+ return cls
+ meth = meth.__func__ # fallback to __qualname__ parsing
+ if inspect.isfunction(meth):
+ cls = getattr(inspect.getmodule(meth),
+ meth.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0])
+ if isinstance(cls, type):
+ return cls
+ return None
+
#rewrite the run method of unittest.TestCase to add testcase logging
def run(self, result, *args, **kws):
orig_method(self, result, *args, **kws)
@@ -132,7 +150,7 @@ def LogResults(original_class):
except AttributeError:
test_case = self._testMethodName
- class_name = str(testMethod.im_class).split("'")[1]
+ class_name = str(get_class_that_defined_method(testMethod)).split("'")[1]
#create custom logging level for filtering.
custom_log_level = 100
@@ -171,10 +189,24 @@ def LogResults(original_class):
if passed:
local_log.results("Testcase "+str(test_case)+": PASSED")
+ # XXX: In order to avoid race condition when test if exists the linkfile
+ # use bb.utils.lock, the best solution is to create a unique name for the
+ # link file.
+ try:
+ import bb
+ has_bb = True
+ lockfilename = linkfile + '.lock'
+ except ImportError:
+ has_bb = False
+
+ if has_bb:
+ lf = bb.utils.lockfile(lockfilename, block=True)
# Create symlink to the current log
- if os.path.exists(linkfile):
+ if os.path.lexists(linkfile):
os.remove(linkfile)
os.symlink(logfile, linkfile)
+ if has_bb:
+ bb.utils.unlockfile(lf)
original_class.run = run
@@ -212,7 +244,7 @@ def tag(*args, **kwargs):
def wrap_ob(ob):
for name in args:
setattr(ob, __tag_prefix + name, True)
- for name, value in kwargs.iteritems():
+ for name, value in kwargs.items():
setattr(ob, __tag_prefix + name, value)
return ob
return wrap_ob
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/dump.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/dump.py
index 63a591d36..71422a9ae 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/dump.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/dump.py
@@ -3,7 +3,7 @@ import sys
import errno
import datetime
import itertools
-from commands import runCmd
+from .commands import runCmd
def get_host_dumper(d):
cmds = d.getVar("testimage_dump_host", True)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/git.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/git.py
new file mode 100644
index 000000000..ae85d2766
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/git.py
@@ -0,0 +1,68 @@
+#
+# Copyright (C) 2016 Intel Corporation
+#
+# Released under the MIT license (see COPYING.MIT)
+#
+"""Git repository interactions"""
+import os
+
+from oeqa.utils.commands import runCmd
+
+
+class GitError(Exception):
+ """Git error handling"""
+ pass
+
+class GitRepo(object):
+ """Class representing a Git repository clone"""
+ def __init__(self, path, is_topdir=False):
+ self.top_dir = self._run_git_cmd_at(['rev-parse', '--show-toplevel'],
+ path)
+ realpath = os.path.realpath(path)
+ if is_topdir and realpath != self.top_dir:
+ raise GitError("{} is not a Git top directory".format(realpath))
+
+ @staticmethod
+ def _run_git_cmd_at(git_args, cwd, **kwargs):
+ """Run git command at a specified directory"""
+ git_cmd = 'git ' if isinstance(git_args, str) else ['git']
+ git_cmd += git_args
+ ret = runCmd(git_cmd, ignore_status=True, cwd=cwd, **kwargs)
+ if ret.status:
+ cmd_str = git_cmd if isinstance(git_cmd, str) \
+ else ' '.join(git_cmd)
+ raise GitError("'{}' failed with exit code {}: {}".format(
+ cmd_str, ret.status, ret.output))
+ return ret.output.strip()
+
+ @staticmethod
+ def init(path):
+ """Initialize a new Git repository"""
+ GitRepo._run_git_cmd_at('init', cwd=path)
+ return GitRepo(path, is_topdir=True)
+
+ def run_cmd(self, git_args, env_update=None):
+ """Run Git command"""
+ env = None
+ if env_update:
+ env = os.environ.copy()
+ env.update(env_update)
+ return self._run_git_cmd_at(git_args, self.top_dir, env=env)
+
+ def rev_parse(self, revision):
+ """Do git rev-parse"""
+ try:
+ return self.run_cmd(['rev-parse', revision])
+ except GitError:
+ # Revision does not exist
+ return None
+
+ def get_current_branch(self):
+ """Get current branch"""
+ try:
+ # Strip 11 chars, i.e. 'refs/heads' from the beginning
+ return self.run_cmd(['symbolic-ref', 'HEAD'])[11:]
+ except GitError:
+ return None
+
+
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/httpserver.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/httpserver.py
index 76518d8ef..7d1233145 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/httpserver.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/httpserver.py
@@ -1,8 +1,9 @@
-import SimpleHTTPServer
+import http.server
import multiprocessing
import os
+from socketserver import ThreadingMixIn
-class HTTPServer(SimpleHTTPServer.BaseHTTPServer.HTTPServer):
+class HTTPServer(ThreadingMixIn, http.server.HTTPServer):
def server_start(self, root_dir):
import signal
@@ -10,7 +11,7 @@ class HTTPServer(SimpleHTTPServer.BaseHTTPServer.HTTPServer):
os.chdir(root_dir)
self.serve_forever()
-class HTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+class HTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
def log_message(self, format_str, *args):
pass
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/logparser.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/logparser.py
index 87b50354c..b377dcd27 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/logparser.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/logparser.py
@@ -3,7 +3,7 @@
import sys
import os
import re
-import ftools
+from . import ftools
# A parser that can be used to identify weather a line is a test result or a section statement.
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/package_manager.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/package_manager.py
new file mode 100644
index 000000000..099ecc972
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/package_manager.py
@@ -0,0 +1,29 @@
+def get_package_manager(d, root_path):
+ """
+ Returns an OE package manager that can install packages in root_path.
+ """
+ from oe.package_manager import RpmPM, OpkgPM, DpkgPM
+
+ pkg_class = d.getVar("IMAGE_PKGTYPE", True)
+ if pkg_class == "rpm":
+ pm = RpmPM(d,
+ root_path,
+ d.getVar('TARGET_VENDOR', True))
+ pm.create_configs()
+
+ elif pkg_class == "ipk":
+ pm = OpkgPM(d,
+ root_path,
+ d.getVar("IPKGCONF_TARGET", True),
+ d.getVar("ALL_MULTILIB_PACKAGE_ARCHS", True))
+
+ elif pkg_class == "deb":
+ pm = DpkgPM(d,
+ root_path,
+ d.getVar('PACKAGE_ARCHS', True),
+ d.getVar('DPKG_ARCH', True))
+
+ pm.write_index()
+ pm.update()
+
+ return pm
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/qemurunner.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/qemurunner.py
index 784cf964f..8f1b5b980 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/qemurunner.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/qemurunner.py
@@ -20,16 +20,17 @@ from oeqa.utils.dump import HostDumper
import logging
logger = logging.getLogger("BitBake.QemuRunner")
+logger.addHandler(logging.StreamHandler())
# Get Unicode non printable control chars
-control_range = range(0,32)+range(127,160)
-control_chars = [unichr(x) for x in control_range
- if unichr(x) not in string.printable]
+control_range = list(range(0,32))+list(range(127,160))
+control_chars = [chr(x) for x in control_range
+ if chr(x) not in string.printable]
re_control_char = re.compile('[%s]' % re.escape("".join(control_chars)))
class QemuRunner:
- def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, boottime, dump_dir, dump_host_cmds):
+ def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, boottime, dump_dir, dump_host_cmds, use_kvm):
# Popen object for runqemu
self.runqemu = None
@@ -49,6 +50,7 @@ class QemuRunner:
self.boottime = boottime
self.logged = False
self.thread = None
+ self.use_kvm = use_kvm
self.runqemutime = 60
self.host_dumper = HostDumper(dump_host_cmds, dump_dir)
@@ -71,7 +73,8 @@ class QemuRunner:
if self.logfile:
# It is needed to sanitize the data received from qemu
# because is possible to have control characters
- msg = re_control_char.sub('', unicode(msg, 'utf-8'))
+ msg = msg.decode("utf-8")
+ msg = re_control_char.sub('', msg)
with codecs.open(self.logfile, "a", encoding="utf-8") as f:
f.write("%s" % msg)
@@ -79,7 +82,7 @@ class QemuRunner:
import fcntl
fl = fcntl.fcntl(o, fcntl.F_GETFL)
fcntl.fcntl(o, fcntl.F_SETFL, fl | os.O_NONBLOCK)
- return os.read(o.fileno(), 1000000)
+ return os.read(o.fileno(), 1000000).decode("utf-8")
def handleSIGCHLD(self, signum, frame):
@@ -91,7 +94,7 @@ class QemuRunner:
self._dump_host()
raise SystemExit
- def start(self, qemuparams = None, get_ip = True):
+ def start(self, qemuparams = None, get_ip = True, extra_bootparams = None):
if self.display:
os.environ["DISPLAY"] = self.display
# Set this flag so that Qemu doesn't do any grabs as SDL grabs
@@ -114,12 +117,16 @@ class QemuRunner:
try:
threadsock, threadport = self.create_socket()
self.server_socket, self.serverport = self.create_socket()
- except socket.error, msg:
+ except socket.error as msg:
logger.error("Failed to create listening socket: %s" % msg[1])
return False
- self.qemuparams = 'bootparams="console=tty1 console=ttyS0,115200n8 printk.time=1" qemuparams="-serial tcp:127.0.0.1:{}"'.format(threadport)
+ bootparams = 'console=tty1 console=ttyS0,115200n8 printk.time=1'
+ if extra_bootparams:
+ bootparams = bootparams + ' ' + extra_bootparams
+
+ self.qemuparams = 'bootparams="{0}" qemuparams="-serial tcp:127.0.0.1:{1}"'.format(bootparams, threadport)
if not self.display:
self.qemuparams = 'nographic ' + self.qemuparams
if qemuparams:
@@ -128,7 +135,15 @@ class QemuRunner:
self.origchldhandler = signal.getsignal(signal.SIGCHLD)
signal.signal(signal.SIGCHLD, self.handleSIGCHLD)
- launch_cmd = 'runqemu tcpserial=%s %s %s %s' % (self.serverport, self.machine, self.rootfs, self.qemuparams)
+ launch_cmd = 'runqemu snapshot '
+ if self.use_kvm:
+ logger.info('Using kvm for runqemu')
+ launch_cmd += 'kvm '
+ else:
+ logger.info('Not using kvm for runqemu')
+ launch_cmd += 'tcpserial=%s %s %s %s' % (self.serverport, self.machine, self.rootfs, self.qemuparams)
+ logger.info('launchcmd=%s'%(launch_cmd))
+
# FIXME: We pass in stdin=subprocess.PIPE here to work around stty
# blocking at the end of the runqemu script when using this within
# oe-selftest (this makes stty error out immediately). There ought
@@ -137,12 +152,12 @@ class QemuRunner:
output = self.runqemu.stdout
#
- # We need the preexec_fn above so that all runqemu processes can easily be killed
+ # We need the preexec_fn above so that all runqemu processes can easily be killed
# (by killing their process group). This presents a problem if this controlling
- # process itself is killed however since those processes don't notice the death
+ # process itself is killed however since those processes don't notice the death
# of the parent and merrily continue on.
#
- # Rather than hack runqemu to deal with this, we add something here instead.
+ # Rather than hack runqemu to deal with this, we add something here instead.
# Basically we fork off another process which holds an open pipe to the parent
# and also is setpgrp. If/when the pipe sees EOF from the parent dieing, it kills
# the process group. This is like pctrl's PDEATHSIG but for a process group
@@ -192,7 +207,7 @@ class QemuRunner:
else:
self.ip = ips[0]
self.server_ip = ips[1]
- except IndexError, ValueError:
+ except (IndexError, ValueError):
logger.info("Couldn't get ip from qemu process arguments! Here is the qemu command line used:\n%s\nand output from runqemu:\n%s" % (cmdline, self.getOutput(output)))
self._dump_host()
self.stop()
@@ -219,6 +234,7 @@ class QemuRunner:
stopread = False
qemusock = None
bootlog = ''
+ data = b''
while time.time() < endtime and not stopread:
sread, swrite, serror = select.select(socklist, [], [], 5)
for sock in sread:
@@ -229,14 +245,19 @@ class QemuRunner:
socklist.remove(self.server_socket)
logger.info("Connection from %s:%s" % addr)
else:
- data = sock.recv(1024)
+ data = data + sock.recv(1024)
if data:
- bootlog += data
- if re.search(".* login:", bootlog):
- self.server_socket = qemusock
- stopread = True
- reachedlogin = True
- logger.info("Reached login banner")
+ try:
+ data = data.decode("utf-8", errors="surrogateescape")
+ bootlog += data
+ data = b''
+ if re.search(".* login:", bootlog):
+ self.server_socket = qemusock
+ stopread = True
+ reachedlogin = True
+ logger.info("Reached login banner")
+ except UnicodeDecodeError:
+ continue
else:
socklist.remove(sock)
sock.close()
@@ -277,13 +298,14 @@ class QemuRunner:
if hasattr(self, "origchldhandler"):
signal.signal(signal.SIGCHLD, self.origchldhandler)
if self.runqemu:
- os.kill(self.monitorpid, signal.SIGKILL)
- logger.info("Sending SIGTERM to runqemu")
- try:
- os.killpg(os.getpgid(self.runqemu.pid), signal.SIGTERM)
- except OSError as e:
- if e.errno != errno.ESRCH:
- raise
+ if hasattr(self, "monitorpid"):
+ os.kill(self.monitorpid, signal.SIGKILL)
+ logger.info("Sending SIGTERM to runqemu")
+ try:
+ os.killpg(os.getpgid(self.runqemu.pid), signal.SIGTERM)
+ except OSError as e:
+ if e.errno != errno.ESRCH:
+ raise
endtime = time.time() + self.runqemutime
while self.runqemu.poll() is None and time.time() < endtime:
time.sleep(1)
@@ -325,7 +347,7 @@ class QemuRunner:
# Walk the process tree from the process specified looking for a qemu-system. Return its [pid'cmd]
#
ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,command'], stdout=subprocess.PIPE).communicate()[0]
- processes = ps.split('\n')
+ processes = ps.decode("utf-8").split('\n')
nfields = len(processes[0].split()) - 1
pids = {}
commands = {}
@@ -354,9 +376,9 @@ class QemuRunner:
if p not in parents:
parents.append(p)
newparents = next
- #print "Children matching %s:" % str(parents)
+ #print("Children matching %s:" % str(parents))
for p in parents:
- # Need to be careful here since runqemu-internal runs "ldd qemu-system-xxxx"
+ # Need to be careful here since runqemu runs "ldd qemu-system-xxxx"
# Also, old versions of ldd (2.11) run "LD_XXXX qemu-system-xxxx"
basecmd = commands[p].split()[0]
basecmd = os.path.basename(basecmd)
@@ -370,14 +392,14 @@ class QemuRunner:
data = ''
status = 0
- self.server_socket.sendall(command)
+ self.server_socket.sendall(command.encode('utf-8'))
keepreading = True
while keepreading:
sread, _, _ = select.select([self.server_socket],[],[],5)
if sread:
answer = self.server_socket.recv(1024)
if answer:
- data += answer
+ data += answer.decode('utf-8')
# Search the prompt to stop
if re.search("[a-zA-Z0-9]+@[a-zA-Z0-9\-]+:~#", data):
keepreading = False
@@ -442,7 +464,7 @@ class LoggingThread(threading.Thread):
def stop(self):
self.logger.info("Stopping logging thread")
if self.running:
- os.write(self.writepipe, "stop")
+ os.write(self.writepipe, bytes("stop", "utf-8"))
def teardown(self):
self.logger.info("Tearing down logging thread")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/qemutinyrunner.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/qemutinyrunner.py
index 4f95101f3..d554f0dbc 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/qemutinyrunner.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/qemutinyrunner.py
@@ -13,7 +13,7 @@ import re
import socket
import select
import bb
-from qemurunner import QemuRunner
+from .qemurunner import QemuRunner
class QemuTinyRunner(QemuRunner):
@@ -50,7 +50,7 @@ class QemuTinyRunner(QemuRunner):
self.server_socket.connect(self.socketfile)
bb.note("Created listening socket for qemu serial console.")
tries = 0
- except socket.error, msg:
+ except socket.error as msg:
self.server_socket.close()
bb.fatal("Failed to create listening socket.")
tries -= 1
@@ -60,7 +60,7 @@ class QemuTinyRunner(QemuRunner):
with open(self.logfile, "a") as f:
f.write("%s" % msg)
- def start(self, qemuparams = None):
+ def start(self, qemuparams = None, ssh=True, extra_bootparams=None):
if self.display:
os.environ["DISPLAY"] = self.display
@@ -102,7 +102,7 @@ class QemuTinyRunner(QemuRunner):
bb.note("Qemu pid didn't appeared in %s seconds" % self.runqemutime)
output = self.runqemu.stdout
self.stop()
- bb.note("Output from runqemu:\n%s" % output.read())
+ bb.note("Output from runqemu:\n%s" % output.read().decode("utf-8"))
return False
return self.is_alive()
@@ -131,7 +131,7 @@ class QemuTinyRunner(QemuRunner):
# Walk the process tree from the process specified looking for a qemu-system. Return its [pid'cmd]
#
ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,command'], stdout=subprocess.PIPE).communicate()[0]
- processes = ps.split('\n')
+ processes = ps.decode("utf-8").split('\n')
nfields = len(processes[0].split()) - 1
pids = {}
commands = {}
@@ -160,11 +160,11 @@ class QemuTinyRunner(QemuRunner):
if p not in parents:
parents.append(p)
newparents = next
- #print "Children matching %s:" % str(parents)
+ #print("Children matching %s:" % str(parents))
for p in parents:
- # Need to be careful here since runqemu-internal runs "ldd qemu-system-xxxx"
+ # Need to be careful here since runqemu runs "ldd qemu-system-xxxx"
# Also, old versions of ldd (2.11) run "LD_XXXX qemu-system-xxxx"
basecmd = commands[p].split()[0]
basecmd = os.path.basename(basecmd)
if "qemu-system" in basecmd and "-serial unix" in commands[p]:
- return [int(p),commands[p]] \ No newline at end of file
+ return [int(p),commands[p]]
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/sshcontrol.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/sshcontrol.py
index 165874416..05d650255 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/sshcontrol.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/sshcontrol.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# Copyright (C) 2013 Intel Corporation
#
# Released under the MIT license (see COPYING.MIT)
@@ -51,16 +52,19 @@ class SSHProcess(object):
endtime = self.starttime + timeout
eof = False
while time.time() < endtime and not eof:
- if select.select([self.process.stdout], [], [], 5)[0] != []:
- data = os.read(self.process.stdout.fileno(), 1024)
- if not data:
- self.process.stdout.close()
- eof = True
- else:
- output += data
- self.log(data)
- endtime = time.time() + timeout
-
+ try:
+ if select.select([self.process.stdout], [], [], 5)[0] != []:
+ data = os.read(self.process.stdout.fileno(), 1024)
+ if not data:
+ self.process.stdout.close()
+ eof = True
+ else:
+ data = data.decode("utf-8")
+ output += data
+ self.log(data)
+ endtime = time.time() + timeout
+ except InterruptedError:
+ continue
# process hasn't returned yet
if not eof:
@@ -145,9 +149,97 @@ class SSHControl(object):
return self._internal_run(command, timeout, self.ignore_status)
def copy_to(self, localpath, remotepath):
- command = self.scp + [localpath, '%s@%s:%s' % (self.user, self.ip, remotepath)]
- return self._internal_run(command, ignore_status=False)
+ if os.path.islink(localpath):
+ link = os.readlink(localpath)
+ dst_dir, dst_base = os.path.split(remotepath)
+ return self.run("cd %s; ln -s %s %s" % (dst_dir, link, dst_base))
+ else:
+ command = self.scp + [localpath, '%s@%s:%s' % (self.user, self.ip, remotepath)]
+ return self._internal_run(command, ignore_status=False)
def copy_from(self, remotepath, localpath):
command = self.scp + ['%s@%s:%s' % (self.user, self.ip, remotepath), localpath]
return self._internal_run(command, ignore_status=False)
+
+ def copy_dir_to(self, localpath, remotepath):
+ """
+ Copy recursively localpath directory to remotepath in target.
+ """
+
+ for root, dirs, files in os.walk(localpath):
+ # Create directories in the target as needed
+ for d in dirs:
+ tmp_dir = os.path.join(root, d).replace(localpath, "")
+ new_dir = os.path.join(remotepath, tmp_dir.lstrip("/"))
+ cmd = "mkdir -p %s" % new_dir
+ self.run(cmd)
+
+ # Copy files into the target
+ for f in files:
+ tmp_file = os.path.join(root, f).replace(localpath, "")
+ dst_file = os.path.join(remotepath, tmp_file.lstrip("/"))
+ src_file = os.path.join(root, f)
+ self.copy_to(src_file, dst_file)
+
+
+ def delete_files(self, remotepath, files):
+ """
+ Delete files in target's remote path.
+ """
+
+ 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 delete_dir(self, remotepath):
+ """
+ Delete remotepath directory in target.
+ """
+
+ cmd = "rmdir %s" % remotepath
+ self.run(cmd)
+
+
+ def delete_dir_structure(self, localpath, remotepath):
+ """
+ Delete recursively localpath structure directory in target's remotepath.
+
+ This function is very usefult to delete a package that is installed in
+ the 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.delete_files(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.delete_dir(remotepath)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/targetbuild.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/targetbuild.py
index f850d78df..59593f5ef 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/targetbuild.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/targetbuild.py
@@ -10,18 +10,17 @@ import bb.utils
import subprocess
from abc import ABCMeta, abstractmethod
-class BuildProject():
-
- __metaclass__ = ABCMeta
+class BuildProject(metaclass=ABCMeta):
def __init__(self, d, uri, foldername=None, tmpdir="/tmp/"):
self.d = d
self.uri = uri
self.archive = os.path.basename(uri)
self.localarchive = os.path.join(tmpdir,self.archive)
- self.fname = re.sub(r'.tar.bz2|tar.gz$', '', self.archive)
if foldername:
self.fname = foldername
+ else:
+ self.fname = re.sub(r'\.tar\.bz2$|\.tar\.gz$|\.tar\.xz$', '', self.archive)
# Download self.archive to self.localarchive
def _download_archive(self):
@@ -118,10 +117,10 @@ class SDKBuildProject(BuildProject):
subprocess.check_call(cmd, shell=True)
#Change targetdir to project folder
- self.targetdir = self.targetdir + self.fname
+ self.targetdir = os.path.join(self.targetdir, self.fname)
- def run_configure(self, configure_args=''):
- return super(SDKBuildProject, self).run_configure(configure_args=(configure_args or '$CONFIGURE_FLAGS'), extra_cmds=' gnu-configize; ')
+ def run_configure(self, configure_args='', extra_cmds=' gnu-configize; '):
+ return super(SDKBuildProject, self).run_configure(configure_args=(configure_args or '$CONFIGURE_FLAGS'), extra_cmds=extra_cmds)
def run_install(self, install_args=''):
return super(SDKBuildProject, self).run_install(install_args=(install_args or "DESTDIR=%s/../install" % self.targetdir))
@@ -134,4 +133,3 @@ class SDKBuildProject(BuildProject):
def _run(self, cmd):
self.log("Running . %s; " % self.sdkenv + cmd)
return subprocess.call(". %s; " % self.sdkenv + cmd, shell=True)
-
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/testexport.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/testexport.py
index 243463bc1..57be2ca44 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/testexport.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/testexport.py
@@ -6,7 +6,7 @@
import os, re, glob as g, shutil as sh,sys
from time import sleep
-from commands import runCmd
+from .commands import runCmd
from difflib import SequenceMatcher as SM
try:
@@ -17,13 +17,13 @@ except ImportError:
pass
def plain(self, msg):
if msg:
- print msg
+ print(msg)
def warn(self, msg):
if msg:
- print "WARNING: " + msg
+ print("WARNING: " + msg)
def fatal(self, msg):
if msg:
- print "FATAL:" + msg
+ print("FATAL:" + msg)
sys.exit(1)
bb = my_log()
OpenPOWER on IntegriCloud