summaryrefslogtreecommitdiffstats
path: root/meta/lib
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib')
-rw-r--r--meta/lib/oe/copy_buildsystem.py102
-rw-r--r--meta/lib/oe/image.py24
-rw-r--r--meta/lib/oe/license.py3
-rw-r--r--meta/lib/oe/lsb.py1
-rw-r--r--meta/lib/oe/package.py29
-rw-r--r--meta/lib/oe/package_manager.py36
-rw-r--r--meta/lib/oe/patch.py161
-rw-r--r--meta/lib/oe/recipeutils.py279
-rw-r--r--meta/lib/oe/rootfs.py86
-rw-r--r--meta/lib/oe/sdk.py2
-rw-r--r--meta/lib/oe/sstatesig.py5
-rw-r--r--meta/lib/oe/terminal.py67
-rw-r--r--meta/lib/oe/utils.py14
-rw-r--r--meta/lib/oeqa/runtime/_ptest.py19
-rw-r--r--meta/lib/oeqa/runtime/parselogs.py51
-rw-r--r--meta/lib/oeqa/runtime/rpm.py10
-rw-r--r--meta/lib/oeqa/runtime/smart.py48
-rw-r--r--meta/lib/oeqa/runtime/systemd.py6
-rw-r--r--meta/lib/oeqa/selftest/_toaster.py556
-rw-r--r--meta/lib/oeqa/selftest/bblayers.py33
-rw-r--r--meta/lib/oeqa/selftest/bbtests.py86
-rw-r--r--meta/lib/oeqa/selftest/buildoptions.py14
-rw-r--r--meta/lib/oeqa/selftest/devtool.py527
-rw-r--r--meta/lib/oeqa/selftest/lic-checksum.py29
-rw-r--r--meta/lib/oeqa/selftest/pkgdata.py218
-rw-r--r--meta/lib/oeqa/targetcontrol.py2
-rw-r--r--meta/lib/oeqa/utils/commands.py21
-rw-r--r--meta/lib/oeqa/utils/decorators.py108
28 files changed, 2058 insertions, 479 deletions
diff --git a/meta/lib/oe/copy_buildsystem.py b/meta/lib/oe/copy_buildsystem.py
new file mode 100644
index 0000000000..cf7fada7f0
--- /dev/null
+++ b/meta/lib/oe/copy_buildsystem.py
@@ -0,0 +1,102 @@
+# This class should provide easy access to the different aspects of the
+# buildsystem such as layers, bitbake location, etc.
+import stat
+import shutil
+
+def _smart_copy(src, dest):
+ # smart_copy will choose the correct function depending on whether the
+ # source is a file or a directory.
+ mode = os.stat(src).st_mode
+ if stat.S_ISDIR(mode):
+ shutil.copytree(src, dest, symlinks=True)
+ else:
+ shutil.copyfile(src, dest)
+ shutil.copymode(src, dest)
+
+class BuildSystem(object):
+ def __init__(self, d):
+ self.d = d
+ self.layerdirs = d.getVar('BBLAYERS', True).split()
+
+ def copy_bitbake_and_layers(self, destdir):
+ # Copy in all metadata layers + bitbake (as repositories)
+ layers_copied = []
+ bb.utils.mkdirhier(destdir)
+ layers = list(self.layerdirs)
+
+ corebase = self.d.getVar('COREBASE', True)
+ layers.append(corebase)
+
+ corebase_files = self.d.getVar('COREBASE_FILES', True).split()
+
+ # bitbake belongs in corebase so make sure it goes there
+ if "bitbake" not in corebase_files:
+ corebase_files.append("bitbake")
+ corebase_files = [corebase + '/' +x for x in corebase_files]
+
+ for layer in layers:
+ layerconf = os.path.join(layer, 'conf', 'layer.conf')
+ if os.path.exists(layerconf):
+ with open(layerconf, 'r') as f:
+ if f.readline().startswith("# ### workspace layer auto-generated by devtool ###"):
+ bb.warn("Skipping local workspace layer %s" % layer)
+ continue
+
+ # If the layer was already under corebase, leave it there
+ # since layers such as meta have issues when moved.
+ layerdestpath = destdir
+ if corebase == os.path.dirname(layer):
+ layerdestpath += '/' + os.path.basename(corebase)
+ layerdestpath += '/' + os.path.basename(layer)
+
+ layer_relative = os.path.relpath(layerdestpath,
+ destdir)
+ layers_copied.append(layer_relative)
+
+ # Treat corebase as special since it typically will contain
+ # build directories or other custom items.
+ if corebase == layer:
+ bb.utils.mkdirhier(layerdestpath)
+ for f in corebase_files:
+ f_basename = os.path.basename(f)
+ destname = os.path.join(layerdestpath, f_basename)
+ _smart_copy(f, destname)
+ else:
+ if os.path.exists(layerdestpath):
+ bb.note("Skipping layer %s, already handled" % layer)
+ else:
+ _smart_copy(layer, layerdestpath)
+
+ return layers_copied
+
+def generate_locked_sigs(sigfile, d):
+ bb.utils.mkdirhier(os.path.dirname(sigfile))
+ depd = d.getVar('BB_TASKDEPDATA', True)
+ tasks = ['%s.%s' % (v[2], v[1]) for v in depd.itervalues()]
+ bb.parse.siggen.dump_lockedsigs(sigfile, tasks)
+
+def prune_lockedsigs(allowed_tasks, excluded_targets, lockedsigs, pruned_output):
+ with open(lockedsigs, 'r') as infile:
+ bb.utils.mkdirhier(os.path.dirname(pruned_output))
+ with open(pruned_output, 'w') as f:
+ invalue = False
+ for line in infile:
+ if invalue:
+ if line.endswith('\\\n'):
+ splitval = line.strip().split(':')
+ if splitval[1] in allowed_tasks and not splitval[0] in excluded_targets:
+ f.write(line)
+ else:
+ f.write(line)
+ invalue = False
+ elif line.startswith('SIGGEN_LOCKEDSIGS'):
+ invalue = True
+ f.write(line)
+
+def create_locked_sstate_cache(lockedsigs, input_sstate_cache, output_sstate_cache, d, fixedlsbstring=""):
+ bb.note('Generating sstate-cache...')
+
+ bb.process.run("gen-lockedsig-cache %s %s %s" % (lockedsigs, input_sstate_cache, output_sstate_cache))
+ if fixedlsbstring:
+ os.rename(output_sstate_cache + '/' + d.getVar('NATIVELSBSTRING', True),
+ output_sstate_cache + '/' + fixedlsbstring)
diff --git a/meta/lib/oe/image.py b/meta/lib/oe/image.py
index 7e080b00dd..0ce303d570 100644
--- a/meta/lib/oe/image.py
+++ b/meta/lib/oe/image.py
@@ -48,11 +48,13 @@ class ImageDepGraph(object):
graph = dict()
def add_node(node):
+ base_type = self._image_base_type(node)
deps = (self.d.getVar('IMAGE_TYPEDEP_' + node, True) or "")
- if deps != "":
+ base_deps = (self.d.getVar('IMAGE_TYPEDEP_' + base_type, True) or "")
+ if deps != "" or base_deps != "":
graph[node] = deps
- for dep in deps.split():
+ for dep in deps.split() + base_deps.split():
if not dep in graph:
add_node(dep)
else:
@@ -72,6 +74,18 @@ class ImageDepGraph(object):
for item in remove_list:
self.graph.pop(item, None)
+ def _image_base_type(self, type):
+ ctypes = self.d.getVar('COMPRESSIONTYPES', True).split()
+ if type in ["vmdk", "live", "iso", "hddimg"]:
+ type = "ext3"
+ basetype = type
+ for ctype in ctypes:
+ if type.endswith("." + ctype):
+ basetype = type[:-len("." + ctype)]
+ break
+
+ return basetype
+
def _compute_dependencies(self):
"""
returns dict object of nodes with [no_of_depends_on, no_of_depended_by]
@@ -282,7 +296,11 @@ class Image(ImageDepGraph):
bb.data.update_data(localdata)
localdata.setVar('type', type)
- cmds.append("\t" + localdata.getVar("IMAGE_CMD", True))
+ image_cmd = localdata.getVar("IMAGE_CMD", True)
+ if image_cmd:
+ cmds.append("\t" + image_cmd)
+ else:
+ bb.fatal("No IMAGE_CMD defined for IMAGE_FSTYPES entry '%s' - possibly invalid type name or missing support class" % type)
cmds.append(localdata.expand("\tcd ${DEPLOY_DIR_IMAGE}"))
if type in cimages:
diff --git a/meta/lib/oe/license.py b/meta/lib/oe/license.py
index 340da61102..31ca15b574 100644
--- a/meta/lib/oe/license.py
+++ b/meta/lib/oe/license.py
@@ -25,7 +25,8 @@ class InvalidLicense(LicenseError):
def __str__(self):
return "invalid characters in license '%s'" % self.license
-license_operator = re.compile('([&|() ])')
+license_operator_chars = '&|() '
+license_operator = re.compile('([' + license_operator_chars + '])')
license_pattern = re.compile('[a-zA-Z0-9.+_\-]+$')
class LicenseVisitor(ast.NodeVisitor):
diff --git a/meta/lib/oe/lsb.py b/meta/lib/oe/lsb.py
index b53f361035..50c1d4720b 100644
--- a/meta/lib/oe/lsb.py
+++ b/meta/lib/oe/lsb.py
@@ -9,6 +9,7 @@ def release_dict():
data = {}
for line in output.splitlines():
+ if line.startswith("-e"): line = line[3:]
try:
key, value = line.split(":\t", 1)
except ValueError:
diff --git a/meta/lib/oe/package.py b/meta/lib/oe/package.py
index f8b532220a..8bc56c6e88 100644
--- a/meta/lib/oe/package.py
+++ b/meta/lib/oe/package.py
@@ -30,7 +30,8 @@ def runstrip(arg):
elif elftype & 8 or elftype & 4:
extraflags = "--remove-section=.comment --remove-section=.note"
- stripcmd = "'%s' %s '%s'" % (strip, extraflags, file)
+ # Use mv to break hardlinks
+ stripcmd = "'%s' %s '%s' -o '%s.tmp' && chown --reference='%s' '%s.tmp' && mv '%s.tmp' '%s'" % (strip, extraflags, file, file, file, file, file, file)
bb.debug(1, "runstrip: %s" % stripcmd)
ret = subprocess.call(stripcmd, shell=True)
@@ -97,3 +98,29 @@ def filedeprunner(arg):
raise e
return (pkg, provides, requires)
+
+
+def read_shlib_providers(d):
+ import re
+
+ shlib_provider = {}
+ shlibs_dirs = d.getVar('SHLIBSDIRS', True).split()
+ list_re = re.compile('^(.*)\.list$')
+ # Go from least to most specific since the last one found wins
+ for dir in reversed(shlibs_dirs):
+ bb.debug(2, "Reading shlib providers in %s" % (dir))
+ if not os.path.exists(dir):
+ continue
+ for file in os.listdir(dir):
+ m = list_re.match(file)
+ if m:
+ dep_pkg = m.group(1)
+ fd = open(os.path.join(dir, file))
+ lines = fd.readlines()
+ fd.close()
+ for l in lines:
+ s = l.strip().split(":")
+ if s[0] not in shlib_provider:
+ shlib_provider[s[0]] = {}
+ shlib_provider[s[0]][s[1]] = (dep_pkg, s[2])
+ return shlib_provider
diff --git a/meta/lib/oe/package_manager.py b/meta/lib/oe/package_manager.py
index 8609156ca2..c9a8084bf2 100644
--- a/meta/lib/oe/package_manager.py
+++ b/meta/lib/oe/package_manager.py
@@ -529,8 +529,11 @@ class PackageManager(object):
return
cmd = [bb.utils.which(os.getenv('PATH'), "oe-pkgdata-util"),
- "glob", self.d.getVar('PKGDATA_DIR', True), installed_pkgs_file,
+ "-p", self.d.getVar('PKGDATA_DIR', True), "glob", installed_pkgs_file,
globs]
+ exclude = self.d.getVar('PACKAGE_EXCLUDE_COMPLEMENTARY', True)
+ if exclude:
+ cmd.extend(['-x', exclude])
try:
bb.note("Installing complementary packages ...")
complementary_pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
@@ -575,7 +578,8 @@ class RpmPM(PackageManager):
self.fullpkglist = list()
self.deploy_dir = self.d.getVar('DEPLOY_DIR_RPM', True)
self.etcrpm_dir = os.path.join(self.target_rootfs, "etc/rpm")
- self.install_dir = os.path.join(self.target_rootfs, "install")
+ self.install_dir_name = "oe_install"
+ self.install_dir_path = os.path.join(self.target_rootfs, self.install_dir_name)
self.rpm_cmd = bb.utils.which(os.getenv('PATH'), "rpm")
self.smart_cmd = bb.utils.which(os.getenv('PATH'), "smart")
self.smart_opt = "--quiet --data-dir=" + os.path.join(target_rootfs,
@@ -676,10 +680,10 @@ class RpmPM(PackageManager):
def _search_pkg_name_in_feeds(self, pkg, feed_archs):
for arch in feed_archs:
arch = arch.replace('-', '_')
+ regex_match = re.compile(r"^%s-[^-]*-[^-]*@%s$" % \
+ (re.escape(pkg), re.escape(arch)))
for p in self.fullpkglist:
- regex_match = r"^%s-[^-]*-[^-]*@%s$" % \
- (re.escape(pkg), re.escape(arch))
- if re.match(regex_match, p) is not None:
+ if regex_match.match(p) is not None:
# First found is best match
# bb.note('%s -> %s' % (pkg, pkg + '@' + arch))
return pkg + '@' + arch
@@ -746,9 +750,9 @@ class RpmPM(PackageManager):
bb.utils.mkdirhier(self.etcrpm_dir)
# Setup temporary directory -- install...
- if os.path.exists(self.install_dir):
- bb.utils.remove(self.install_dir, True)
- bb.utils.mkdirhier(os.path.join(self.install_dir, 'tmp'))
+ if os.path.exists(self.install_dir_path):
+ bb.utils.remove(self.install_dir_path, True)
+ bb.utils.mkdirhier(os.path.join(self.install_dir_path, 'tmp'))
channel_priority = 5
platform_dir = os.path.join(self.etcrpm_dir, "platform")
@@ -835,7 +839,7 @@ class RpmPM(PackageManager):
self._invoke_smart('config --set rpm-dbpath=/var/lib/rpm')
self._invoke_smart('config --set rpm-extra-macros._var=%s' %
self.d.getVar('localstatedir', True))
- cmd = 'config --set rpm-extra-macros._tmppath=/install/tmp'
+ cmd = "config --set rpm-extra-macros._tmppath=/%s/tmp" % (self.install_dir_name)
prefer_color = self.d.getVar('RPM_PREFER_ELF_ARCH', True)
if prefer_color:
@@ -853,6 +857,7 @@ class RpmPM(PackageManager):
% prefer_color)
self._invoke_smart(cmd)
+ self._invoke_smart('config --set rpm-ignoresize=1')
# Write common configuration for host and target usage
self._invoke_smart('config --set rpm-nolinktos=1')
@@ -988,7 +993,7 @@ class RpmPM(PackageManager):
cmd += "--dbpath=/var/lib/rpm "
cmd += "--define='_cross_scriptlet_wrapper %s' " % \
self.scriptlet_wrapper
- cmd += "--define='_tmppath /install/tmp' %s" % ' '.join(pkgs)
+ cmd += "--define='_tmppath /%s/tmp' %s" % (self.install_dir_name, ' '.join(pkgs))
else:
# for pkg in pkgs:
# bb.note('Debug: What required: %s' % pkg)
@@ -1023,7 +1028,7 @@ class RpmPM(PackageManager):
bb.utils.remove(os.path.join(self.target_rootfs, 'var/lib/opkg'), True)
# remove temp directory
- bb.utils.remove(self.d.expand('${IMAGE_ROOTFS}/install'), True)
+ bb.utils.remove(self.install_dir_path, True)
def backup_packaging_data(self):
# Save the rpmlib for increment rpm image generation
@@ -1627,10 +1632,10 @@ class DpkgPM(PackageManager):
def remove(self, pkgs, with_dependencies=True):
if with_dependencies:
os.environ['APT_CONFIG'] = self.apt_conf_file
- cmd = "%s remove %s" % (self.apt_get_cmd, ' '.join(pkgs))
+ cmd = "%s purge %s" % (self.apt_get_cmd, ' '.join(pkgs))
else:
cmd = "%s --admindir=%s/var/lib/dpkg --instdir=%s" \
- " -r --force-depends %s" % \
+ " -P --force-depends %s" % \
(bb.utils.which(os.getenv('PATH'), "dpkg"),
self.target_rootfs, self.target_rootfs, ' '.join(pkgs))
@@ -1722,9 +1727,12 @@ class DpkgPM(PackageManager):
with open(self.d.expand("${STAGING_ETCDIR_NATIVE}/apt/apt.conf.sample")) as apt_conf_sample:
for line in apt_conf_sample.read().split("\n"):
match_arch = re.match(" Architecture \".*\";$", line)
+ architectures = ""
if match_arch:
for base_arch in base_arch_list:
- apt_conf.write(" Architecture \"%s\";\n" % base_arch)
+ architectures += "\"%s\";" % base_arch
+ apt_conf.write(" Architectures {%s};\n" % architectures);
+ apt_conf.write(" Architecture \"%s\";\n" % base_archs)
else:
line = re.sub("#ROOTFS#", self.target_rootfs, line)
line = re.sub("#APTCONF#", self.apt_conf_dir, line)
diff --git a/meta/lib/oe/patch.py b/meta/lib/oe/patch.py
index b085c9d6b5..f68d40f8c8 100644
--- a/meta/lib/oe/patch.py
+++ b/meta/lib/oe/patch.py
@@ -199,10 +199,108 @@ class PatchTree(PatchSet):
self.Pop(all=True)
class GitApplyTree(PatchTree):
+ patch_line_prefix = '%% original patch'
+
def __init__(self, dir, d):
PatchTree.__init__(self, dir, d)
+ @staticmethod
+ def extractPatchHeader(patchfile):
+ """
+ Extract just the header lines from the top of a patch file
+ """
+ lines = []
+ with open(patchfile, 'r') as f:
+ for line in f.readlines():
+ if line.startswith('Index: ') or line.startswith('diff -') or line.startswith('---'):
+ break
+ lines.append(line)
+ return lines
+
+ @staticmethod
+ def prepareCommit(patchfile):
+ """
+ Prepare a git commit command line based on the header from a patch file
+ (typically this is useful for patches that cannot be applied with "git am" due to formatting)
+ """
+ import tempfile
+ import re
+ author_re = re.compile('[\S ]+ <\S+@\S+\.\S+>')
+ # Process patch header and extract useful information
+ lines = GitApplyTree.extractPatchHeader(patchfile)
+ outlines = []
+ author = None
+ date = None
+ for line in lines:
+ if line.startswith('Subject: '):
+ subject = line.split(':', 1)[1]
+ # Remove any [PATCH][oe-core] etc.
+ subject = re.sub(r'\[.+?\]\s*', '', subject)
+ outlines.insert(0, '%s\n\n' % subject.strip())
+ continue
+ if line.startswith('From: ') or line.startswith('Author: '):
+ authorval = line.split(':', 1)[1].strip().replace('"', '')
+ # git is fussy about author formatting i.e. it must be Name <email@domain>
+ if author_re.match(authorval):
+ author = authorval
+ continue
+ if line.startswith('Date: '):
+ if date is None:
+ dateval = line.split(':', 1)[1].strip()
+ # Very crude check for date format, since git will blow up if it's not in the right
+ # format. Without e.g. a python-dateutils dependency we can't do a whole lot more
+ if len(dateval) > 12:
+ date = dateval
+ continue
+ if line.startswith('Signed-off-by: '):
+ authorval = line.split(':', 1)[1].strip().replace('"', '')
+ # git is fussy about author formatting i.e. it must be Name <email@domain>
+ if author_re.match(authorval):
+ author = authorval
+ outlines.append(line)
+ # Write out commit message to a file
+ with tempfile.NamedTemporaryFile('w', delete=False) as tf:
+ tmpfile = tf.name
+ for line in outlines:
+ tf.write(line)
+ # Prepare git command
+ cmd = ["git", "commit", "-F", tmpfile]
+ # git doesn't like plain email addresses as authors
+ if author and '<' in author:
+ cmd.append('--author="%s"' % author)
+ if date:
+ cmd.append('--date="%s"' % date)
+ return (tmpfile, cmd)
+
+ @staticmethod
+ def extractPatches(tree, startcommit, outdir):
+ import tempfile
+ import shutil
+ tempdir = tempfile.mkdtemp(prefix='oepatch')
+ try:
+ shellcmd = ["git", "format-patch", startcommit, "-o", tempdir]
+ out = runcmd(["sh", "-c", " ".join(shellcmd)], tree)
+ if out:
+ for srcfile in out.split():
+ patchlines = []
+ outfile = None
+ with open(srcfile, 'r') as f:
+ for line in f:
+ if line.startswith(GitApplyTree.patch_line_prefix):
+ outfile = line.split()[-1].strip()
+ continue
+ patchlines.append(line)
+ if not outfile:
+ outfile = os.path.basename(srcfile)
+ with open(os.path.join(outdir, outfile), 'w') as of:
+ for line in patchlines:
+ of.write(line)
+ finally:
+ shutil.rmtree(tempdir)
+
def _applypatch(self, patch, force = False, reverse = False, run = True):
+ import shutil
+
def _applypatchhelper(shellcmd, patch, force = False, reverse = False, run = True):
if reverse:
shellcmd.append('-R')
@@ -214,12 +312,65 @@ class GitApplyTree(PatchTree):
return runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
+ # Add hooks which add a pointer to the original patch file name in the commit message
+ reporoot = (runcmd("git rev-parse --show-toplevel".split(), self.dir) or '').strip()
+ if not reporoot:
+ raise Exception("Cannot get repository root for directory %s" % self.dir)
+ commithook = os.path.join(reporoot, '.git', 'hooks', 'commit-msg')
+ commithook_backup = commithook + '.devtool-orig'
+ applyhook = os.path.join(reporoot, '.git', 'hooks', 'applypatch-msg')
+ applyhook_backup = applyhook + '.devtool-orig'
+ if os.path.exists(commithook):
+ shutil.move(commithook, commithook_backup)
+ if os.path.exists(applyhook):
+ shutil.move(applyhook, applyhook_backup)
+ with open(commithook, 'w') as f:
+ # NOTE: the formatting here is significant; if you change it you'll also need to
+ # change other places which read it back
+ f.write('echo >> $1\n')
+ f.write('echo "%s: $PATCHFILE" >> $1\n' % GitApplyTree.patch_line_prefix)
+ os.chmod(commithook, 0755)
+ shutil.copy2(commithook, applyhook)
try:
- shellcmd = ["git", "--work-tree=.", "am", "-3", "-p%s" % patch['strippath']]
- return _applypatchhelper(shellcmd, patch, force, reverse, run)
- except CmdError:
- shellcmd = ["git", "--git-dir=.", "apply", "-p%s" % patch['strippath']]
- return _applypatchhelper(shellcmd, patch, force, reverse, run)
+ patchfilevar = 'PATCHFILE="%s"' % os.path.basename(patch['file'])
+ try:
+ shellcmd = [patchfilevar, "git", "--work-tree=.", "am", "-3", "--keep-cr", "-p%s" % patch['strippath']]
+ return _applypatchhelper(shellcmd, patch, force, reverse, run)
+ except CmdError:
+ # Need to abort the git am, or we'll still be within it at the end
+ try:
+ shellcmd = ["git", "--work-tree=.", "am", "--abort"]
+ runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
+ except CmdError:
+ pass
+ # Fall back to git apply
+ shellcmd = ["git", "--git-dir=.", "apply", "-p%s" % patch['strippath']]
+ try:
+ output = _applypatchhelper(shellcmd, patch, force, reverse, run)
+ except CmdError:
+ # Fall back to patch
+ output = PatchTree._applypatch(self, patch, force, reverse, run)
+ # Add all files
+ shellcmd = ["git", "add", "-f", "."]
+ output += runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
+ # Exclude the patches directory
+ shellcmd = ["git", "reset", "HEAD", self.patchdir]
+ output += runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
+ # Commit the result
+ (tmpfile, shellcmd) = self.prepareCommit(patch['file'])
+ try:
+ shellcmd.insert(0, patchfilevar)
+ output += runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
+ finally:
+ os.remove(tmpfile)
+ return output
+ finally:
+ os.remove(commithook)
+ os.remove(applyhook)
+ if os.path.exists(commithook_backup):
+ shutil.move(commithook_backup, commithook)
+ if os.path.exists(applyhook_backup):
+ shutil.move(applyhook_backup, applyhook)
class QuiltTree(PatchSet):
diff --git a/meta/lib/oe/recipeutils.py b/meta/lib/oe/recipeutils.py
new file mode 100644
index 0000000000..159a103719
--- /dev/null
+++ b/meta/lib/oe/recipeutils.py
@@ -0,0 +1,279 @@
+# Utility functions for reading and modifying recipes
+#
+# Some code borrowed from the OE layer index
+#
+# Copyright (C) 2013-2014 Intel Corporation
+#
+
+import sys
+import os
+import os.path
+import tempfile
+import textwrap
+import difflib
+import utils
+import shutil
+import re
+from collections import OrderedDict, defaultdict
+
+
+# Help us to find places to insert values
+recipe_progression = ['SUMMARY', 'DESCRIPTION', 'HOMEPAGE', 'BUGTRACKER', 'SECTION', 'LICENSE', 'LIC_FILES_CHKSUM', 'PROVIDES', 'DEPENDS', 'PR', 'PV', 'SRCREV', 'SRC_URI', 'S', 'do_fetch', 'do_unpack', 'do_patch', 'EXTRA_OECONF', 'do_configure', 'EXTRA_OEMAKE', 'do_compile', 'do_install', 'do_populate_sysroot', 'INITSCRIPT', 'USERADD', 'GROUPADD', 'PACKAGES', 'FILES', 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RPROVIDES', 'RREPLACES', 'RCONFLICTS', 'ALLOW_EMPTY', 'do_package', 'do_deploy']
+# Variables that sometimes are a bit long but shouldn't be wrapped
+nowrap_vars = ['SUMMARY', 'HOMEPAGE', 'BUGTRACKER']
+list_vars = ['SRC_URI', 'LIC_FILES_CHKSUM']
+meta_vars = ['SUMMARY', 'DESCRIPTION', 'HOMEPAGE', 'BUGTRACKER', 'SECTION']
+
+
+def pn_to_recipe(cooker, pn):
+ """Convert a recipe name (PN) to the path to the recipe file"""
+ import bb.providers
+
+ if pn in cooker.recipecache.pkg_pn:
+ filenames = cooker.recipecache.pkg_pn[pn]
+ best = bb.providers.findBestProvider(pn, cooker.data, cooker.recipecache, cooker.recipecache.pkg_pn)
+ return best[3]
+ else:
+ return None
+
+
+def get_unavailable_reasons(cooker, pn):
+ """If a recipe could not be found, find out why if possible"""
+ import bb.taskdata
+ taskdata = bb.taskdata.TaskData(None, skiplist=cooker.skiplist)
+ return taskdata.get_reasons(pn)
+
+
+def parse_recipe(fn, d):
+ """Parse an individual recipe"""
+ import bb.cache
+ envdata = bb.cache.Cache.loadDataFull(fn, [], d)
+ return envdata
+
+
+def get_var_files(fn, varlist, d):
+ """Find the file in which each of a list of variables is set.
+ Note: requires variable history to be enabled when parsing.
+ """
+ envdata = parse_recipe(fn, d)
+ varfiles = {}
+ for v in varlist:
+ history = envdata.varhistory.variable(v)
+ files = []
+ for event in history:
+ if 'file' in event and not 'flag' in event:
+ files.append(event['file'])
+ if files:
+ actualfile = files[-1]
+ else:
+ actualfile = None
+ varfiles[v] = actualfile
+
+ return varfiles
+
+
+def patch_recipe_file(fn, values, patch=False, relpath=''):
+ """Update or insert variable values into a recipe file (assuming you
+ have already identified the exact file you want to update.)
+ Note that some manual inspection/intervention may be required
+ since this cannot handle all situations.
+ """
+ remainingnames = {}
+ for k in values.keys():
+ remainingnames[k] = recipe_progression.index(k) if k in recipe_progression else -1
+ remainingnames = OrderedDict(sorted(remainingnames.iteritems(), key=lambda x: x[1]))
+
+ with tempfile.NamedTemporaryFile('w', delete=False) as tf:
+ def outputvalue(name):
+ rawtext = '%s = "%s"\n' % (name, values[name])
+ if name in nowrap_vars:
+ tf.write(rawtext)
+ elif name in list_vars:
+ splitvalue = values[name].split()
+ if len(splitvalue) > 1:
+ linesplit = ' \\\n' + (' ' * (len(name) + 4))
+ tf.write('%s = "%s%s"\n' % (name, linesplit.join(splitvalue), linesplit))
+ else:
+ tf.write(rawtext)
+ else:
+ wrapped = textwrap.wrap(rawtext)
+ for wrapline in wrapped[:-1]:
+ tf.write('%s \\\n' % wrapline)
+ tf.write('%s\n' % wrapped[-1])
+
+ tfn = tf.name
+ with open(fn, 'r') as f:
+ # First runthrough - find existing names (so we know not to insert based on recipe_progression)
+ # Second runthrough - make the changes
+ existingnames = []
+ for runthrough in [1, 2]:
+ currname = None
+ for line in f:
+ if not currname:
+ insert = False
+ for k in remainingnames.keys():
+ for p in recipe_progression:
+ if re.match('^%s(_prepend|_append)*[ ?:=(]' % p, line):
+ if remainingnames[k] > -1 and recipe_progression.index(p) > remainingnames[k] and runthrough > 1 and not k in existingnames:
+ outputvalue(k)
+ del remainingnames[k]
+ break
+ for k in remainingnames.keys():
+ if re.match('^%s[ ?:=]' % k, line):
+ currname = k
+ if runthrough == 1:
+ existingnames.append(k)
+ else:
+ del remainingnames[k]
+ break
+ if currname and runthrough > 1:
+ outputvalue(currname)
+
+ if currname:
+ sline = line.rstrip()
+ if not sline.endswith('\\'):
+ currname = None
+ continue
+ if runthrough > 1:
+ tf.write(line)
+ f.seek(0)
+ if remainingnames:
+ tf.write('\n')
+ for k in remainingnames.keys():
+ outputvalue(k)
+
+ with open(tfn, 'U') as f:
+ tolines = f.readlines()
+ if patch:
+ with open(fn, 'U') as f:
+ fromlines = f.readlines()
+ relfn = os.path.relpath(fn, relpath)
+ diff = difflib.unified_diff(fromlines, tolines, 'a/%s' % relfn, 'b/%s' % relfn)
+ os.remove(tfn)
+ return diff
+ else:
+ with open(fn, 'w') as f:
+ f.writelines(tolines)
+ os.remove(tfn)
+ return None
+
+def localise_file_vars(fn, varfiles, varlist):
+ """Given a list of variables and variable history (fetched with get_var_files())
+ find where each variable should be set/changed. This handles for example where a
+ recipe includes an inc file where variables might be changed - in most cases
+ we want to update the inc file when changing the variable value rather than adding
+ it to the recipe itself.
+ """
+ fndir = os.path.dirname(fn) + os.sep
+
+ first_meta_file = None
+ for v in meta_vars:
+ f = varfiles.get(v, None)
+ if f:
+ actualdir = os.path.dirname(f) + os.sep
+ if actualdir.startswith(fndir):
+ first_meta_file = f
+ break
+
+ filevars = defaultdict(list)
+ for v in varlist:
+ f = varfiles[v]
+ # Only return files that are in the same directory as the recipe or in some directory below there
+ # (this excludes bbclass files and common inc files that wouldn't be appropriate to set the variable
+ # in if we were going to set a value specific to this recipe)
+ if f:
+ actualfile = f
+ else:
+ # Variable isn't in a file, if it's one of the "meta" vars, use the first file with a meta var in it
+ if first_meta_file:
+ actualfile = first_meta_file
+ else:
+ actualfile = fn
+
+ actualdir = os.path.dirname(actualfile) + os.sep
+ if not actualdir.startswith(fndir):
+ actualfile = fn
+ filevars[actualfile].append(v)
+
+ return filevars
+
+def patch_recipe(d, fn, varvalues, patch=False, relpath=''):
+ """Modify a list of variable values in the specified recipe. Handles inc files if
+ used by the recipe.
+ """
+ varlist = varvalues.keys()
+ varfiles = get_var_files(fn, varlist, d)
+ locs = localise_file_vars(fn, varfiles, varlist)
+ patches = []
+ for f,v in locs.iteritems():
+ vals = {k: varvalues[k] for k in v}
+ patchdata = patch_recipe_file(f, vals, patch, relpath)
+ if patch:
+ patches.append(patchdata)
+
+ if patch:
+ return patches
+ else:
+ return None
+
+
+
+def copy_recipe_files(d, tgt_dir, whole_dir=False, download=True):
+ """Copy (local) recipe files, including both files included via include/require,
+ and files referred to in the SRC_URI variable."""
+ import bb.fetch2
+ import oe.path
+
+ # FIXME need a warning if the unexpanded SRC_URI value contains variable references
+
+ uris = (d.getVar('SRC_URI', True) or "").split()
+ fetch = bb.fetch2.Fetch(uris, d)
+ if download:
+ fetch.download()
+
+ # Copy local files to target directory and gather any remote files
+ bb_dir = os.path.dirname(d.getVar('FILE', True)) + os.sep
+ remotes = []
+ includes = [path for path in d.getVar('BBINCLUDED', True).split() if
+ path.startswith(bb_dir) and os.path.exists(path)]
+ for path in fetch.localpaths() + includes:
+ # Only import files that are under the meta directory
+ if path.startswith(bb_dir):
+ if not whole_dir:
+ relpath = os.path.relpath(path, bb_dir)
+ subdir = os.path.join(tgt_dir, os.path.dirname(relpath))
+ if not os.path.exists(subdir):
+ os.makedirs(subdir)
+ shutil.copy2(path, os.path.join(tgt_dir, relpath))
+ else:
+ remotes.append(path)
+ # Simply copy whole meta dir, if requested
+ if whole_dir:
+ shutil.copytree(bb_dir, tgt_dir)
+
+ return remotes
+
+
+def get_recipe_patches(d):
+ """Get a list of the patches included in SRC_URI within a recipe."""
+ patchfiles = []
+ # Execute src_patches() defined in patch.bbclass - this works since that class
+ # is inherited globally
+ patches = bb.utils.exec_flat_python_func('src_patches', d)
+ for patch in patches:
+ _, _, local, _, _, parm = bb.fetch.decodeurl(patch)
+ patchfiles.append(local)
+ return patchfiles
+
+
+def validate_pn(pn):
+ """Perform validation on a recipe name (PN) for a new recipe."""
+ reserved_names = ['forcevariable', 'append', 'prepend', 'remove']
+ if not re.match('[0-9a-z-]+', pn):
+ return 'Recipe name "%s" is invalid: only characters 0-9, a-z and - are allowed' % pn
+ elif pn in reserved_names:
+ return 'Recipe name "%s" is invalid: is a reserved keyword' % pn
+ elif pn.startswith('pn-'):
+ return 'Recipe name "%s" is invalid: names starting with "pn-" are reserved' % pn
+ return ''
+
diff --git a/meta/lib/oe/rootfs.py b/meta/lib/oe/rootfs.py
index c4735f2755..a18472aeeb 100644
--- a/meta/lib/oe/rootfs.py
+++ b/meta/lib/oe/rootfs.py
@@ -118,17 +118,18 @@ class Rootfs(object):
if self.d.getVar('USE_DEVFS', True) != "1":
self._create_devfs()
- self._uninstall_uneeded()
+ self._uninstall_unneeded()
self._insert_feed_uris()
self._run_ldconfig()
- self._generate_kernel_module_deps()
+ if self.d.getVar('USE_DEPMOD', True) != "0":
+ self._generate_kernel_module_deps()
self._cleanup()
- def _uninstall_uneeded(self):
+ def _uninstall_unneeded(self):
# Remove unneeded init script symlinks
delayed_postinsts = self._get_delayed_postinsts()
if delayed_postinsts is None:
@@ -137,34 +138,39 @@ class Rootfs(object):
self.d.getVar('IMAGE_ROOTFS', True),
"run-postinsts", "remove"])
- # Remove unneeded package-management related components
- if bb.utils.contains("IMAGE_FEATURES", "package-management",
- True, False, self.d):
- return
+ runtime_pkgmanage = bb.utils.contains("IMAGE_FEATURES", "package-management",
+ True, False, self.d)
+ if not runtime_pkgmanage:
+ # Remove components that we don't need if we're not going to install
+ # additional packages at runtime
+ if delayed_postinsts is None:
+ installed_pkgs_dir = self.d.expand('${WORKDIR}/installed_pkgs.txt')
+ pkgs_to_remove = list()
+ with open(installed_pkgs_dir, "r+") as installed_pkgs:
+ pkgs_installed = installed_pkgs.read().split('\n')
+ for pkg_installed in pkgs_installed[:]:
+ pkg = pkg_installed.split()[0]
+ if pkg in ["update-rc.d",
+ "base-passwd",
+ self.d.getVar("ROOTFS_BOOTSTRAP_INSTALL", True)
+ ]:
+ pkgs_to_remove.append(pkg)
+ pkgs_installed.remove(pkg_installed)
+
+ if len(pkgs_to_remove) > 0:
+ self.pm.remove(pkgs_to_remove, False)
+ # Update installed_pkgs.txt
+ open(installed_pkgs_dir, "w+").write('\n'.join(pkgs_installed))
- if delayed_postinsts is None:
- installed_pkgs_dir = self.d.expand('${WORKDIR}/installed_pkgs.txt')
- pkgs_to_remove = list()
- with open(installed_pkgs_dir, "r+") as installed_pkgs:
- pkgs_installed = installed_pkgs.read().split('\n')
- for pkg_installed in pkgs_installed[:]:
- pkg = pkg_installed.split()[0]
- if pkg in ["update-rc.d",
- "base-passwd",
- self.d.getVar("ROOTFS_BOOTSTRAP_INSTALL", True)
- ]:
- pkgs_to_remove.append(pkg)
- pkgs_installed.remove(pkg_installed)
-
- if len(pkgs_to_remove) > 0:
- self.pm.remove(pkgs_to_remove, False)
- # Update installed_pkgs.txt
- open(installed_pkgs_dir, "w+").write('\n'.join(pkgs_installed))
+ else:
+ self._save_postinsts()
- else:
- self._save_postinsts()
+ post_uninstall_cmds = self.d.getVar("ROOTFS_POSTUNINSTALL_COMMAND", True)
+ execute_pre_post_process(self.d, post_uninstall_cmds)
- self.pm.remove_packaging_data()
+ if not runtime_pkgmanage:
+ # Remove the package manager data files
+ self.pm.remove_packaging_data()
def _run_intercepts(self):
intercepts_dir = os.path.join(self.d.getVar('WORKDIR', True),
@@ -209,16 +215,17 @@ class Rootfs(object):
'new', '-v'])
def _generate_kernel_module_deps(self):
- kernel_abi_ver_file = os.path.join(self.d.getVar('STAGING_KERNEL_DIR', True),
+ kernel_abi_ver_file = oe.path.join(self.d.getVar('PKGDATA_DIR', True), "kernel-depmod",
'kernel-abiversion')
- if os.path.exists(kernel_abi_ver_file):
- kernel_ver = open(kernel_abi_ver_file).read().strip(' \n')
- modules_dir = os.path.join(self.image_rootfs, 'lib', 'modules', kernel_ver)
+ if not os.path.exists(kernel_abi_ver_file):
+ bb.fatal("No kernel-abiversion file found (%s), cannot run depmod, aborting" % kernel_abi_ver_file)
+
+ kernel_ver = open(kernel_abi_ver_file).read().strip(' \n')
+ modules_dir = os.path.join(self.image_rootfs, 'lib', 'modules', kernel_ver)
- bb.utils.mkdirhier(modules_dir)
+ bb.utils.mkdirhier(modules_dir)
- self._exec_shell_cmd(['depmodwrapper', '-a', '-b', self.image_rootfs,
- kernel_ver])
+ self._exec_shell_cmd(['depmodwrapper', '-a', '-b', self.image_rootfs, kernel_ver])
"""
Create devfs:
@@ -348,11 +355,11 @@ class RpmRootfs(Rootfs):
pass
def _log_check_warn(self):
- r = re.compile('(warn|Warn)')
+ r = re.compile('^(warn|Warn|NOTE: warn|NOTE: Warn)')
log_path = self.d.expand("${T}/log.do_rootfs")
with open(log_path, 'r') as log:
for line in log.read().split('\n'):
- if 'log_check' in line:
+ if 'log_check' in line or 'NOTE:' in line:
continue
m = r.search(line)
@@ -402,7 +409,10 @@ class RpmRootfs(Rootfs):
# __db.00* (Berkeley DB files that hold locks, rpm specific environment
# settings, etc.), that should not get into the final rootfs
self.pm.unlock_rpm_db()
- bb.utils.remove(self.image_rootfs + "/install", True)
+ if os.path.isdir(self.pm.install_dir_path + "/tmp") and not os.listdir(self.pm.install_dir_path + "/tmp"):
+ bb.utils.remove(self.pm.install_dir_path + "/tmp", True)
+ if os.path.isdir(self.pm.install_dir_path) and not os.listdir(self.pm.install_dir_path):
+ bb.utils.remove(self.pm.install_dir_path, True)
class DpkgRootfs(Rootfs):
diff --git a/meta/lib/oe/sdk.py b/meta/lib/oe/sdk.py
index c57a441941..a6767412c7 100644
--- a/meta/lib/oe/sdk.py
+++ b/meta/lib/oe/sdk.py
@@ -269,6 +269,8 @@ class DpkgSdk(Sdk):
bb.note("Installing TARGET packages")
self._populate_sysroot(self.target_pm, self.target_manifest)
+ self.target_pm.install_complementary(self.d.getVar('SDKIMAGE_INSTALL_COMPLEMENTARY', True))
+
execute_pre_post_process(self.d, self.d.getVar("POPULATE_SDK_POST_TARGET_COMMAND", True))
self._copy_apt_dir_to(os.path.join(self.sdk_target_sysroot, "etc", "apt"))
diff --git a/meta/lib/oe/sstatesig.py b/meta/lib/oe/sstatesig.py
index af7617ee61..62e75c25ca 100644
--- a/meta/lib/oe/sstatesig.py
+++ b/meta/lib/oe/sstatesig.py
@@ -137,13 +137,16 @@ class SignatureGeneratorOEBasicHash(bb.siggen.SignatureGeneratorBasicHash):
return
super(bb.siggen.SignatureGeneratorBasicHash, self).dump_sigtask(fn, task, stampbase, runtime)
- def dump_lockedsigs(self, sigfile=None):
+ def dump_lockedsigs(self, sigfile=None, taskfilter=None):
if not sigfile:
sigfile = os.getcwd() + "/locked-sigs.inc"
bb.plain("Writing locked sigs to %s" % sigfile)
types = {}
for k in self.runtaskdeps:
+ if taskfilter:
+ if not k in taskfilter:
+ continue
fn = k.rsplit(".",1)[0]
t = self.lockedhashfn[fn].split(" ")[1].split(":")[5]
t = 't-' + t.replace('_', '-')
diff --git a/meta/lib/oe/terminal.py b/meta/lib/oe/terminal.py
index 0a623c75b1..4f5c611615 100644
--- a/meta/lib/oe/terminal.py
+++ b/meta/lib/oe/terminal.py
@@ -2,6 +2,7 @@ import logging
import oe.classutils
import shlex
from bb.process import Popen, ExecutionError
+from distutils.version import LooseVersion
logger = logging.getLogger('BitBake.OE.Terminal')
@@ -52,9 +53,17 @@ class XTerminal(Terminal):
raise UnsupportedTerminal(self.name)
class Gnome(XTerminal):
- command = 'gnome-terminal -t "{title}" -x {command}'
+ command = 'gnome-terminal -t "{title}" --disable-factory -x {command}'
priority = 2
+ def __init__(self, sh_cmd, title=None, env=None, d=None):
+ # Check version
+ vernum = check_terminal_version("gnome-terminal")
+ if vernum and LooseVersion(vernum) >= '3.10':
+ logger.debug(1, 'Gnome-Terminal 3.10 or later does not support --disable-factory')
+ self.command = 'gnome-terminal -t "{title}" -x {command}'
+ XTerminal.__init__(self, sh_cmd, title, env, d)
+
class Mate(XTerminal):
command = 'mate-terminal -t "{title}" -x {command}'
priority = 2
@@ -63,17 +72,20 @@ class Xfce(XTerminal):
command = 'xfce4-terminal -T "{title}" -e "{command}"'
priority = 2
+class Terminology(XTerminal):
+ command = 'terminology -T="{title}" -e {command}'
+ priority = 2
+
class Konsole(XTerminal):
- command = 'konsole -T "{title}" -e {command}'
+ command = 'konsole --nofork -p tabtitle="{title}" -e {command}'
priority = 2
def __init__(self, sh_cmd, title=None, env=None, d=None):
# Check version
- vernum = check_konsole_version("konsole")
- if vernum:
- if vernum.split('.')[0] == "2":
- logger.debug(1, 'Konsole from KDE 4.x will not work as devshell, skipping')
- raise UnsupportedTerminal(self.name)
+ vernum = check_terminal_version("konsole")
+ if vernum and LooseVersion(vernum) < '2.0.0':
+ # Konsole from KDE 3.x
+ self.command = 'konsole -T "{title}" -e {command}'
XTerminal.__init__(self, sh_cmd, title, env, d)
class XTerm(XTerminal):
@@ -112,6 +124,24 @@ class TmuxRunning(Terminal):
if not os.getenv('TMUX'):
raise UnsupportedTerminal('tmux is not running')
+ if not check_tmux_pane_size('tmux'):
+ raise UnsupportedTerminal('tmux pane too small')
+
+ Terminal.__init__(self, sh_cmd, title, env, d)
+
+class TmuxNewWindow(Terminal):
+ """Open a new window in the current running tmux session"""
+ name = 'tmux-new-window'
+ command = 'tmux new-window -n "{title}" "{command}"'
+ priority = 2.70
+
+ def __init__(self, sh_cmd, title=None, env=None, d=None):
+ if not bb.utils.which(os.getenv('PATH'), 'tmux'):
+ raise UnsupportedTerminal('tmux is not installed')
+
+ if not os.getenv('TMUX'):
+ raise UnsupportedTerminal('tmux is not running')
+
Terminal.__init__(self, sh_cmd, title, env, d)
class Tmux(Terminal):
@@ -180,10 +210,27 @@ def spawn(name, sh_cmd, title=None, env=None, d=None):
if pipe.returncode != 0:
raise ExecutionError(sh_cmd, pipe.returncode, output)
-def check_konsole_version(konsole):
+def check_tmux_pane_size(tmux):
+ import subprocess as sub
+ try:
+ p = sub.Popen('%s list-panes -F "#{?pane_active,#{pane_height},}"' % tmux,
+ shell=True,stdout=sub.PIPE,stderr=sub.PIPE)
+ out, err = p.communicate()
+ size = int(out.strip())
+ except OSError as exc:
+ import errno
+ if exc.errno == errno.ENOENT:
+ return None
+ else:
+ raise
+ if size/2 >= 19:
+ return True
+ return False
+
+def check_terminal_version(terminalName):
import subprocess as sub
try:
- p = sub.Popen(['sh', '-c', '%s --version' % konsole],stdout=sub.PIPE,stderr=sub.PIPE)
+ p = sub.Popen(['sh', '-c', '%s --version' % terminalName],stdout=sub.PIPE,stderr=sub.PIPE)
out, err = p.communicate()
ver_info = out.rstrip().split('\n')
except OSError as exc:
@@ -196,6 +243,8 @@ def check_konsole_version(konsole):
for ver in ver_info:
if ver.startswith('Konsole'):
vernum = ver.split(' ')[-1]
+ if ver.startswith('GNOME Terminal'):
+ vernum = ver.split(' ')[-1]
return vernum
def distro_name():
diff --git a/meta/lib/oe/utils.py b/meta/lib/oe/utils.py
index 35442568e2..bedade292b 100644
--- a/meta/lib/oe/utils.py
+++ b/meta/lib/oe/utils.py
@@ -42,7 +42,15 @@ def version_less_or_equal(variable, checkvalue, truevalue, falsevalue, d):
return falsevalue
def both_contain(variable1, variable2, checkvalue, d):
- if d.getVar(variable1,1).find(checkvalue) != -1 and d.getVar(variable2,1).find(checkvalue) != -1:
+ val1 = d.getVar(variable1, True)
+ val2 = d.getVar(variable2, True)
+ val1 = set(val1.split())
+ val2 = set(val2.split())
+ if isinstance(checkvalue, basestring):
+ checkvalue = set(checkvalue.split())
+ else:
+ checkvalue = set(checkvalue)
+ if checkvalue.issubset(val1) and checkvalue.issubset(val2):
return checkvalue
else:
return ""
@@ -180,3 +188,7 @@ def multiprocess_exec(commands, function):
pool.terminate()
pool.join()
raise
+
+def squashspaces(string):
+ import re
+ return re.sub("\s+", " ", string).strip()
diff --git a/meta/lib/oeqa/runtime/_ptest.py b/meta/lib/oeqa/runtime/_ptest.py
index 4c58dc1d7f..81c9c43862 100644
--- a/meta/lib/oeqa/runtime/_ptest.py
+++ b/meta/lib/oeqa/runtime/_ptest.py
@@ -86,7 +86,7 @@ class PtestRunnerTest(oeRuntimeTest):
installed_pkgs.write(self.pkgs_list.list("arch"))
cmd = [bb.utils.which(os.getenv('PATH'), "oe-pkgdata-util"),
- "glob", oeRuntimeTest.tc.d.getVar('PKGDATA_DIR', True), installed_pkgs_file,
+ "-p", oeRuntimeTest.tc.d.getVar('PKGDATA_DIR', True), "glob", installed_pkgs_file,
globs]
try:
bb.note("Installing complementary packages ...")
@@ -99,26 +99,27 @@ class PtestRunnerTest(oeRuntimeTest):
return complementary_pkgs.split()
def setUp(self):
- self.buildhist_dir = oeRuntimeTest.tc.d.getVar("BUILDHISTORY_DIR_IMAGE", True)
- self.assertTrue(os.path.exists(self.buildhist_dir))
self.ptest_log = os.path.join(oeRuntimeTest.tc.d.getVar("TEST_LOG_DIR",True), "ptest-%s.log" % oeRuntimeTest.tc.d.getVar('DATETIME', True))
@skipUnlessPassed('test_ssh')
def test_ptestrunner(self):
self.add_smart_channel()
- cond = oeRuntimeTest.hasPackage("ptest-runner") and oeRuntimeTest.hasFeature("ptest") and oeRuntimeTest.hasPackage("-ptest")
- if not cond:
+ (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)
+ if cond:
self.install_packages(self.install_complementary("*-ptest"))
self.install_packages(['ptest-runner'])
- self.target.run('/usr/bin/ptest-runner > /tmp/ptest.log 2>&1', 0)
+ (runnerstatus, result) = self.target.run('/usr/bin/ptest-runner > /tmp/ptest.log 2>&1', 0)
+ #exit code is !=0 even if ptest-runner executes because some ptest tests fail.
+ self.assertTrue(runnerstatus != 127, msg="Cannot execute ptest-runner!")
self.target.copy_from('/tmp/ptest.log', self.ptest_log)
- shutil.copyfile(self.ptest_log, os.path.join(self.buildhist_dir, "ptest.log"))
+ shutil.copyfile(self.ptest_log, "ptest.log")
- result = self.parse_ptest(os.path.join(self.buildhist_dir, "ptest.log"))
+ result = self.parse_ptest("ptest.log")
log_results_to_location = "./results"
if os.path.exists(log_results_to_location):
shutil.rmtree(log_results_to_location)
os.makedirs(log_results_to_location)
- result.log_as_files(log_results_to_location, test_status = ['fail'])
+ result.log_as_files(log_results_to_location, test_status = ['pass','fail'])
diff --git a/meta/lib/oeqa/runtime/parselogs.py b/meta/lib/oeqa/runtime/parselogs.py
index 42cb1b5e6f..7721912a23 100644
--- a/meta/lib/oeqa/runtime/parselogs.py
+++ b/meta/lib/oeqa/runtime/parselogs.py
@@ -7,10 +7,26 @@ from oeqa.utils.decorators import *
errors = ["error", "cannot", "can\'t", "failed"]
common_errors = [
- '(WW) warning, (EE) error, (NI) not implemented, (??) unknown.',
- 'dma timeout',
- 'can\'t add hid device:',
- 'usbhid: probe of ',
+ "(WW) warning, (EE) error, (NI) not implemented, (??) unknown.",
+ "dma timeout",
+ "can\'t add hid device:",
+ "usbhid: probe of ",
+ "_OSC failed (AE_ERROR)",
+ "_OSC failed (AE_SUPPORT)",
+ "AE_ALREADY_EXISTS",
+ "ACPI _OSC request failed (AE_SUPPORT)",
+ "can\'t disable ASPM",
+ "Failed to load module \"vesa\"",
+ "Failed to load module vesa",
+ "Failed to load module \"modesetting\"",
+ "Failed to load module modesetting",
+ "Failed to load module \"glx\"",
+ "Failed to load module glx",
+ "[drm] Cannot find any crtc or sizes - going 1024x768",
+ "_OSC failed (AE_NOT_FOUND); disabling ASPM",
+ "Open ACPI failed (/var/run/acpid.socket) (No such file or directory)",
+ "NX (Execute Disable) protection cannot be enabled: non-PAE kernel!",
+ "hd.: possibly failed opcode"
]
x86_common = [
@@ -22,11 +38,6 @@ x86_common = [
qemux86_common = [
'Fast TSC calibration',
- '_OSC failed (AE_NOT_FOUND); disabling ASPM',
- 'Open ACPI failed (/var/run/acpid.socket) (No such file or directory)',
- 'Failed to load module "vesa"',
- 'Failed to load module "modesetting"',
- 'Failed to load module "glx"',
'wrong ELF class',
] + common_errors
@@ -39,9 +50,11 @@ ignore_errors = {
'qemux86-64' : qemux86_common,
'qemumips' : [
'Failed to load module "glx"',
+ 'pci 0000:00:00.0: [Firmware Bug]: reg 0x..: invalid BAR (can\'t size)',
] + common_errors,
'qemuppc' : [
'PCI 0000:00 Cannot reserve Legacy IO [io 0x0000-0x0fff]',
+ 'host side 80-wire cable detection failed, limiting max speed',
'mode "640x480" test failed',
'Failed to load module "glx"',
] + common_errors,
@@ -54,6 +67,19 @@ ignore_errors = {
'crownbay' : x86_common,
'genericx86' : x86_common,
'genericx86-64' : x86_common,
+ 'edgerouter' : [
+ 'Fatal server error:',
+ ] + common_errors,
+ 'minnow' : [
+ 'netlink init failed',
+ 'NETLINK INITIALIZATION FAILED',
+ ] + common_errors,
+ 'jasperforest' : [
+ 'Activated service \'org.bluez\' failed:',
+ 'Unable to find NFC netlink family',
+ 'netlink init failed',
+ 'NETLINK INITIALIZATION FAILED',
+ ] + common_errors,
}
log_locations = ["/var/log/","/var/log/dmesg", "/tmp/dmesg_output.log"]
@@ -100,9 +126,10 @@ class ParseLogsTest(oeRuntimeTest):
(status, output) = self.target.run("test -d "+str(location))
if (status == 0):
(status, output) = self.target.run("find "+str(location)+"/*.log -maxdepth 1 -type f")
- output = output.splitlines()
- for logfile in output:
- logs.append(os.path.join(location,str(logfile)))
+ if (status == 0):
+ output = output.splitlines()
+ for logfile in output:
+ logs.append(os.path.join(location,str(logfile)))
return logs
#build the grep command to be used with filters and exclusions
diff --git a/meta/lib/oeqa/runtime/rpm.py b/meta/lib/oeqa/runtime/rpm.py
index b17e8b46a8..0529992cbe 100644
--- a/meta/lib/oeqa/runtime/rpm.py
+++ b/meta/lib/oeqa/runtime/rpm.py
@@ -47,6 +47,16 @@ class RpmInstallRemoveTest(oeRuntimeTest):
(status,output) = self.target.run('rpm -e rpm-doc')
self.assertEqual(status, 0, msg="Failed to remove rpm-doc package: %s" % output)
+ @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")
+ (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" % status)
+
@classmethod
def tearDownClass(self):
oeRuntimeTest.tc.target.run('rm -f /tmp/rpm-doc.rpm')
diff --git a/meta/lib/oeqa/runtime/smart.py b/meta/lib/oeqa/runtime/smart.py
index 3b49314df7..dba3bd60d7 100644
--- a/meta/lib/oeqa/runtime/smart.py
+++ b/meta/lib/oeqa/runtime/smart.py
@@ -53,12 +53,15 @@ class SmartRepoTest(SmartTest):
@classmethod
def setUpClass(self):
+ self.repolist = []
self.repo_server = HTTPService(oeRuntimeTest.tc.d.getVar('DEPLOY_DIR', True), oeRuntimeTest.tc.target.server_ip)
self.repo_server.start()
@classmethod
def tearDownClass(self):
self.repo_server.stop()
+ for i in self.repolist:
+ oeRuntimeTest.tc.target.run('smart channel -y --remove '+str(i))
def test_smart_channel(self):
self.smart('channel', 1)
@@ -71,6 +74,7 @@ class SmartRepoTest(SmartTest):
for arch in os.listdir('%s/%s' % (self.repo_server.root_dir, image_pkgtype)):
if arch in pkgarchs:
self.smart('channel -y --add {a} type=rpm-md baseurl={u}/{a}'.format(a=arch, u=deploy_url))
+ self.repolist.append(arch)
self.smart('update')
def test_smart_channel_help(self):
@@ -119,3 +123,47 @@ class SmartRepoTest(SmartTest):
@skipUnlessPassed('test_smart_install')
def test_smart_reinstall(self):
self.smart('reinstall -y psplash-default')
+
+ @testcase(727)
+ @skipUnlessPassed('test_smart_channel_add')
+ def test_smart_remote_repo(self):
+ self.smart('update')
+ self.smart('install -y psplash')
+ self.smart('remove -y psplash')
+
+ @testcase(726)
+ def test_smart_local_dir(self):
+ self.target.run('mkdir /tmp/myrpmdir')
+ self.smart('channel --add myrpmdir type=rpm-dir path=/tmp/myrpmdir -y')
+ self.target.run('cd /tmp/myrpmdir')
+ self.smart('download psplash')
+ output = self.smart('channel --list')
+ for i in output.split("\n"):
+ if ("rpmsys" != str(i)) and ("myrpmdir" != str(i)):
+ self.smart('channel --disable '+str(i))
+ self.target.run('cd /home/root')
+ self.smart('install psplash')
+ for i in output.split("\n"):
+ if ("rpmsys" != str(i)) and ("myrpmdir" != str(i)):
+ self.smart('channel --enable '+str(i))
+ self.smart('channel --remove myrpmdir -y')
+ self.target.run("rm -rf /tmp/myrpmdir")
+
+ @testcase(718)
+ def test_smart_add_rpmdir(self):
+ self.target.run('mkdir /tmp/myrpmdir')
+ self.smart('channel --add myrpmdir type=rpm-dir path=/tmp/myrpmdir -y')
+ self.smart('channel --disable myrpmdir -y')
+ output = self.smart('channel --show myrpmdir')
+ self.assertTrue("disabled = yes" in output, msg="Failed to disable rpm dir")
+ self.smart('channel --enable myrpmdir -y')
+ output = self.smart('channel --show myrpmdir')
+ self.assertFalse("disabled = yes" in output, msg="Failed to enable rpm dir")
+ self.smart('channel --remove myrpmdir -y')
+ self.target.run("rm -rf /tmp/myrpmdir")
+
+ @testcase(731)
+ @skipUnlessPassed('test_smart_channel_add')
+ def test_smart_remove_package(self):
+ self.smart('install -y psplash')
+ self.smart('remove -y psplash') \ No newline at end of file
diff --git a/meta/lib/oeqa/runtime/systemd.py b/meta/lib/oeqa/runtime/systemd.py
index 1451698bb3..5935edd549 100644
--- a/meta/lib/oeqa/runtime/systemd.py
+++ b/meta/lib/oeqa/runtime/systemd.py
@@ -86,3 +86,9 @@ class SystemdServiceTests(SystemdTest):
self.systemctl('is-enabled', 'avahi-daemon.service', expected=1)
self.systemctl('enable', 'avahi-daemon.service')
self.systemctl('is-enabled', 'avahi-daemon.service')
+
+class SystemdJournalTests(SystemdTest):
+ @skipUnlessPassed('test_ssh')
+ def test_systemd_journal(self):
+ (status, output) = self.target.run('journalctl')
+ self.assertEqual(status, 0, output)
diff --git a/meta/lib/oeqa/selftest/_toaster.py b/meta/lib/oeqa/selftest/_toaster.py
index 1cf28a0144..2ed08d24e6 100644
--- a/meta/lib/oeqa/selftest/_toaster.py
+++ b/meta/lib/oeqa/selftest/_toaster.py
@@ -19,89 +19,89 @@ from oeqa.utils.decorators import testcase
class ToasterSetup(oeSelfTest):
def recipe_parse(self, file_path, var):
- for line in open(file_path,'r'):
- if line.find(var) > -1:
- val = line.split(" = ")[1].replace("\"", "").strip()
- return val
+ for line in open(file_path,'r'):
+ if line.find(var) > -1:
+ val = line.split(" = ")[1].replace("\"", "").strip()
+ return val
def fix_file_path(self, file_path):
- if ":" in file_path:
- file_path=file_path.split(":")[2]
- return file_path
+ if ":" in file_path:
+ file_path=file_path.split(":")[2]
+ return file_path
class Toaster_DB_Tests(ToasterSetup):
# Check if build name is unique - tc_id=795
@testcase(795)
def test_Build_Unique_Name(self):
- all_builds = Build.objects.all().count()
- distinct_builds = Build.objects.values('id').distinct().count()
- self.assertEqual(distinct_builds, all_builds, msg = 'Build name is not unique')
+ all_builds = Build.objects.all().count()
+ distinct_builds = Build.objects.values('id').distinct().count()
+ self.assertEqual(distinct_builds, all_builds, msg = 'Build name is not unique')
# Check if build coocker log path is unique - tc_id=819
@testcase(819)
def test_Build_Unique_Cooker_Log_Path(self):
- distinct_path = Build.objects.values('cooker_log_path').distinct().count()
- total_builds = Build.objects.values('id').count()
- self.assertEqual(distinct_path, total_builds, msg = 'Build coocker log path is not unique')
+ distinct_path = Build.objects.values('cooker_log_path').distinct().count()
+ total_builds = Build.objects.values('id').count()
+ self.assertEqual(distinct_path, total_builds, msg = 'Build coocker log path is not unique')
# Check if the number of errors matches the number of orm_logmessage.level entries with value 2 - tc_id=820
@testcase(820)
def test_Build_Errors_No(self):
- builds = Build.objects.values('id', 'errors_no')
- cnt_err = []
- for build in builds:
- log_mess_err_no = LogMessage.objects.filter(build = build['id'], level = 2).count()
- if (build['errors_no'] != log_mess_err_no):
- cnt_err.append(build['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err)
+ builds = Build.objects.values('id', 'errors_no')
+ cnt_err = []
+ for build in builds:
+ log_mess_err_no = LogMessage.objects.filter(build = build['id'], level = 2).count()
+ if (build['errors_no'] != log_mess_err_no):
+ cnt_err.append(build['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err)
# Check if the number of warnings matches the number of orm_logmessage.level entries with value 1 - tc=821
@testcase(821)
def test_Build_Warnings_No(self):
- builds = Build.objects.values('id', 'warnings_no')
- cnt_err = []
- for build in builds:
- log_mess_warn_no = LogMessage.objects.filter(build = build['id'], level = 1).count()
- if (build['warnings_no'] != log_mess_warn_no):
- cnt_err.append(build['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err)
+ builds = Build.objects.values('id', 'warnings_no')
+ cnt_err = []
+ for build in builds:
+ log_mess_warn_no = LogMessage.objects.filter(build = build['id'], level = 1).count()
+ if (build['warnings_no'] != log_mess_warn_no):
+ cnt_err.append(build['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err)
# Check if the build succeeded then the errors_no is 0 - tc_id=822
@testcase(822)
def test_Build_Suceeded_Errors_No(self):
- builds = Build.objects.filter(outcome = 0).values('id', 'errors_no')
- cnt_err = []
- for build in builds:
- if (build['errors_no'] != 0):
- cnt_err.append(build['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err)
+ builds = Build.objects.filter(outcome = 0).values('id', 'errors_no')
+ cnt_err = []
+ for build in builds:
+ if (build['errors_no'] != 0):
+ cnt_err.append(build['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err)
# Check if task order is unique for one build - tc=824
- @testcase(824)
+ @testcase(824)
def test_Task_Unique_Order(self):
- builds = Build.objects.values('id')
- cnt_err = []
- for build in builds:
- total_task_order = Task.objects.filter(build = build['id']).values('order').count()
- distinct_task_order = Task.objects.filter(build = build['id']).values('order').distinct().count()
- if (total_task_order != distinct_task_order):
- cnt_err.append(build['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err)
+ builds = Build.objects.values('id')
+ cnt_err = []
+ for build in builds:
+ total_task_order = Task.objects.filter(build = build['id']).values('order').count()
+ distinct_task_order = Task.objects.filter(build = build['id']).values('order').distinct().count()
+ if (total_task_order != distinct_task_order):
+ cnt_err.append(build['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err)
# Check task order sequence for one build - tc=825
@testcase(825)
def test_Task_Order_Sequence(self):
- builds = builds = Build.objects.values('id')
- cnt_err = []
- for build in builds:
- tasks = Task.objects.filter(Q(build = build['id']), ~Q(order = None), ~Q(task_name__contains = '_setscene')).values('id', 'order').order_by("order")
- cnt_tasks = 0
- for task in tasks:
- cnt_tasks += 1
- if (task['order'] != cnt_tasks):
- cnt_err.append(task['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
+ builds = builds = Build.objects.values('id')
+ cnt_err = []
+ for build in builds:
+ tasks = Task.objects.filter(Q(build = build['id']), ~Q(order = None), ~Q(task_name__contains = '_setscene')).values('id', 'order').order_by("order")
+ cnt_tasks = 0
+ for task in tasks:
+ cnt_tasks += 1
+ if (task['order'] != cnt_tasks):
+ cnt_err.append(task['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
# Check if disk_io matches the difference between EndTimeIO and StartTimeIO in build stats - tc=828
### this needs to be updated ###
@@ -110,336 +110,336 @@ class Toaster_DB_Tests(ToasterSetup):
# Check if outcome = 2 (SSTATE) then sstate_result must be 3 (RESTORED) - tc=832
@testcase(832)
def test_Task_If_Outcome_2_Sstate_Result_Must_Be_3(self):
- tasks = Task.objects.filter(outcome = 2).values('id', 'sstate_result')
- cnt_err = []
- for task in tasks:
- if (row['sstate_result'] != 3):
- cnt_err.append(task['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
+ tasks = Task.objects.filter(outcome = 2).values('id', 'sstate_result')
+ cnt_err = []
+ for task in tasks:
+ if (row['sstate_result'] != 3):
+ cnt_err.append(task['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
# Check if outcome = 1 (COVERED) or 3 (EXISTING) then sstate_result must be 0 (SSTATE_NA) - tc=833
@testcase(833)
def test_Task_If_Outcome_1_3_Sstate_Result_Must_Be_0(self):
- tasks = Task.objects.filter(outcome__in = (1, 3)).values('id', 'sstate_result')
- cnt_err = []
- for task in tasks:
- if (task['sstate_result'] != 0):
- cnt_err.append(task['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
+ tasks = Task.objects.filter(outcome__in = (1, 3)).values('id', 'sstate_result')
+ cnt_err = []
+ for task in tasks:
+ if (task['sstate_result'] != 0):
+ cnt_err.append(task['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
# Check if outcome is 0 (SUCCESS) or 4 (FAILED) then sstate_result must be 0 (NA), 1 (MISS) or 2 (FAILED) - tc=834
@testcase(834)
def test_Task_If_Outcome_0_4_Sstate_Result_Must_Be_0_1_2(self):
- tasks = Task.objects.filter(outcome__in = (0, 4)).values('id', 'sstate_result')
- cnt_err = []
- for task in tasks:
- if (task['sstate_result'] not in [0, 1, 2]):
- cnt_err.append(task['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
+ tasks = Task.objects.filter(outcome__in = (0, 4)).values('id', 'sstate_result')
+ cnt_err = []
+ for task in tasks:
+ if (task['sstate_result'] not in [0, 1, 2]):
+ cnt_err.append(task['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
# Check if task_executed = TRUE (1), script_type must be 0 (CODING_NA), 2 (CODING_PYTHON), 3 (CODING_SHELL) - tc=891
@testcase(891)
def test_Task_If_Task_Executed_True_Script_Type_0_2_3(self):
- tasks = Task.objects.filter(task_executed = 1).values('id', 'script_type')
- cnt_err = []
- for task in tasks:
- if (task['script_type'] not in [0, 2, 3]):
- cnt_err.append(task['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
+ tasks = Task.objects.filter(task_executed = 1).values('id', 'script_type')
+ cnt_err = []
+ for task in tasks:
+ if (task['script_type'] not in [0, 2, 3]):
+ cnt_err.append(task['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
# Check if task_executed = TRUE (1), outcome must be 0 (SUCCESS) or 4 (FAILED) - tc=836
@testcase(836)
def test_Task_If_Task_Executed_True_Outcome_0_4(self):
- tasks = Task.objects.filter(task_executed = 1).values('id', 'outcome')
- cnt_err = []
- for task in tasks:
- if (task['outcome'] not in [0, 4]):
- cnt_err.append(task['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
+ tasks = Task.objects.filter(task_executed = 1).values('id', 'outcome')
+ cnt_err = []
+ for task in tasks:
+ if (task['outcome'] not in [0, 4]):
+ cnt_err.append(task['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
# Check if task_executed = FALSE (0), script_type must be 0 - tc=890
@testcase(890)
def test_Task_If_Task_Executed_False_Script_Type_0(self):
- tasks = Task.objects.filter(task_executed = 0).values('id', 'script_type')
- cnt_err = []
- for task in tasks:
- if (task['script_type'] != 0):
- cnt_err.append(task['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
+ tasks = Task.objects.filter(task_executed = 0).values('id', 'script_type')
+ cnt_err = []
+ for task in tasks:
+ if (task['script_type'] != 0):
+ cnt_err.append(task['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
# Check if task_executed = FALSE (0) and build outcome = SUCCEEDED (0), task outcome must be 1 (COVERED), 2 (CACHED), 3 (PREBUILT), 5 (EMPTY) - tc=837
@testcase(837)
def test_Task_If_Task_Executed_False_Outcome_1_2_3_5(self):
- builds = Build.objects.filter(outcome = 0).values('id')
- cnt_err = []
- for build in builds:
- tasks = Task.objects.filter(build = build['id'], task_executed = 0).values('id', 'outcome')
- for task in tasks:
- if (task['outcome'] not in [1, 2, 3, 5]):
- cnt_err.append(task['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
+ builds = Build.objects.filter(outcome = 0).values('id')
+ cnt_err = []
+ for build in builds:
+ tasks = Task.objects.filter(build = build['id'], task_executed = 0).values('id', 'outcome')
+ for task in tasks:
+ if (task['outcome'] not in [1, 2, 3, 5]):
+ cnt_err.append(task['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
# Key verification - tc=888
@testcase(888)
def test_Target_Installed_Package(self):
- rows = Target_Installed_Package.objects.values('id', 'target_id', 'package_id')
- cnt_err = []
- for row in rows:
- target = Target.objects.filter(id = row['target_id']).values('id')
- package = Package.objects.filter(id = row['package_id']).values('id')
- if (not target or not package):
- cnt_err.append(row['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for target installed package id: %s' % cnt_err)
+ rows = Target_Installed_Package.objects.values('id', 'target_id', 'package_id')
+ cnt_err = []
+ for row in rows:
+ target = Target.objects.filter(id = row['target_id']).values('id')
+ package = Package.objects.filter(id = row['package_id']).values('id')
+ if (not target or not package):
+ cnt_err.append(row['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for target installed package id: %s' % cnt_err)
# Key verification - tc=889
@testcase(889)
def test_Task_Dependency(self):
- rows = Task_Dependency.objects.values('id', 'task_id', 'depends_on_id')
- cnt_err = []
- for row in rows:
- task_id = Task.objects.filter(id = row['task_id']).values('id')
- depends_on_id = Task.objects.filter(id = row['depends_on_id']).values('id')
- if (not task_id or not depends_on_id):
- cnt_err.append(row['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for task dependency id: %s' % cnt_err)
+ rows = Task_Dependency.objects.values('id', 'task_id', 'depends_on_id')
+ cnt_err = []
+ for row in rows:
+ task_id = Task.objects.filter(id = row['task_id']).values('id')
+ depends_on_id = Task.objects.filter(id = row['depends_on_id']).values('id')
+ if (not task_id or not depends_on_id):
+ cnt_err.append(row['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for task dependency id: %s' % cnt_err)
# Check if build target file_name is populated only if is_image=true AND orm_build.outcome=0 then if the file exists and its size matches the file_size value
### Need to add the tc in the test run
@testcase(1037)
def test_Target_File_Name_Populated(self):
- builds = Build.objects.filter(outcome = 0).values('id')
- for build in builds:
- targets = Target.objects.filter(build_id = build['id'], is_image = 1).values('id')
- for target in targets:
- target_files = Target_Image_File.objects.filter(target_id = target['id']).values('id', 'file_name', 'file_size')
- cnt_err = []
- for file_info in target_files:
- target_id = file_info['id']
- target_file_name = file_info['file_name']
- target_file_size = file_info['file_size']
- if (not target_file_name or not target_file_size):
- cnt_err.append(target_id)
- else:
- if (not os.path.exists(target_file_name)):
- cnt_err.append(target_id)
- else:
- if (os.path.getsize(target_file_name) != target_file_size):
- cnt_err.append(target_id)
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for target image file id: %s' % cnt_err)
+ builds = Build.objects.filter(outcome = 0).values('id')
+ for build in builds:
+ targets = Target.objects.filter(build_id = build['id'], is_image = 1).values('id')
+ for target in targets:
+ target_files = Target_Image_File.objects.filter(target_id = target['id']).values('id', 'file_name', 'file_size')
+ cnt_err = []
+ for file_info in target_files:
+ target_id = file_info['id']
+ target_file_name = file_info['file_name']
+ target_file_size = file_info['file_size']
+ if (not target_file_name or not target_file_size):
+ cnt_err.append(target_id)
+ else:
+ if (not os.path.exists(target_file_name)):
+ cnt_err.append(target_id)
+ else:
+ if (os.path.getsize(target_file_name) != target_file_size):
+ cnt_err.append(target_id)
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for target image file id: %s' % cnt_err)
# Key verification - tc=884
@testcase(884)
def test_Package_Dependency(self):
- cnt_err = []
- deps = Package_Dependency.objects.values('id', 'package_id', 'depends_on_id')
- for dep in deps:
- if (dep['package_id'] == dep['depends_on_id']):
- cnt_err.append(dep['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for package dependency id: %s' % cnt_err)
+ cnt_err = []
+ deps = Package_Dependency.objects.values('id', 'package_id', 'depends_on_id')
+ for dep in deps:
+ if (dep['package_id'] == dep['depends_on_id']):
+ cnt_err.append(dep['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for package dependency id: %s' % cnt_err)
# Check if recipe name does not start with a number (0-9) - tc=838
@testcase(838)
def test_Recipe_Name(self):
- recipes = Recipe.objects.values('id', 'name')
- cnt_err = []
- for recipe in recipes:
- if (recipe['name'][0].isdigit() is True):
- cnt_err.append(recipe['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err)
+ recipes = Recipe.objects.values('id', 'name')
+ cnt_err = []
+ for recipe in recipes:
+ if (recipe['name'][0].isdigit() is True):
+ cnt_err.append(recipe['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err)
# Check if recipe section matches the content of the SECTION variable (if set) in file_path - tc=839
@testcase(839)
def test_Recipe_DB_Section_Match_Recipe_File_Section(self):
- recipes = Recipe.objects.values('id', 'section', 'file_path')
- cnt_err = []
- for recipe in recipes:
- file_path = self.fix_file_path(recipe['file_path'])
- file_exists = os.path.isfile(file_path)
- if (not file_path or (file_exists is False)):
- cnt_err.append(recipe['id'])
- else:
- file_section = self.recipe_parse(file_path, "SECTION = ")
- db_section = recipe['section']
- if file_section:
- if (db_section != file_section):
- cnt_err.append(recipe['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err)
+ recipes = Recipe.objects.values('id', 'section', 'file_path')
+ cnt_err = []
+ for recipe in recipes:
+ file_path = self.fix_file_path(recipe['file_path'])
+ file_exists = os.path.isfile(file_path)
+ if (not file_path or (file_exists is False)):
+ cnt_err.append(recipe['id'])
+ else:
+ file_section = self.recipe_parse(file_path, "SECTION = ")
+ db_section = recipe['section']
+ if file_section:
+ if (db_section != file_section):
+ cnt_err.append(recipe['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err)
# Check if recipe license matches the content of the LICENSE variable (if set) in file_path - tc=840
@testcase(840)
def test_Recipe_DB_License_Match_Recipe_File_License(self):
- recipes = Recipe.objects.values('id', 'license', 'file_path')
- cnt_err = []
- for recipe in recipes:
- file_path = self.fix_file_path(recipe['file_path'])
- file_exists = os.path.isfile(file_path)
- if (not file_path or (file_exists is False)):
- cnt_err.append(recipe['id'])
- else:
- file_license = self.recipe_parse(file_path, "LICENSE = ")
- db_license = recipe['license']
- if file_license:
- if (db_license != file_license):
- cnt_err.append(recipe['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err)
+ recipes = Recipe.objects.values('id', 'license', 'file_path')
+ cnt_err = []
+ for recipe in recipes:
+ file_path = self.fix_file_path(recipe['file_path'])
+ file_exists = os.path.isfile(file_path)
+ if (not file_path or (file_exists is False)):
+ cnt_err.append(recipe['id'])
+ else:
+ file_license = self.recipe_parse(file_path, "LICENSE = ")
+ db_license = recipe['license']
+ if file_license:
+ if (db_license != file_license):
+ cnt_err.append(recipe['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err)
# Check if recipe homepage matches the content of the HOMEPAGE variable (if set) in file_path - tc=841
@testcase(841)
def test_Recipe_DB_Homepage_Match_Recipe_File_Homepage(self):
- recipes = Recipe.objects.values('id', 'homepage', 'file_path')
- cnt_err = []
- for recipe in recipes:
- file_path = self.fix_file_path(recipe['file_path'])
- file_exists = os.path.isfile(file_path)
- if (not file_path or (file_exists is False)):
- cnt_err.append(recipe['id'])
- else:
- file_homepage = self.recipe_parse(file_path, "HOMEPAGE = ")
- db_homepage = recipe['homepage']
- if file_homepage:
- if (db_homepage != file_homepage):
- cnt_err.append(recipe['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err)
+ recipes = Recipe.objects.values('id', 'homepage', 'file_path')
+ cnt_err = []
+ for recipe in recipes:
+ file_path = self.fix_file_path(recipe['file_path'])
+ file_exists = os.path.isfile(file_path)
+ if (not file_path or (file_exists is False)):
+ cnt_err.append(recipe['id'])
+ else:
+ file_homepage = self.recipe_parse(file_path, "HOMEPAGE = ")
+ db_homepage = recipe['homepage']
+ if file_homepage:
+ if (db_homepage != file_homepage):
+ cnt_err.append(recipe['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err)
# Check if recipe bugtracker matches the content of the BUGTRACKER variable (if set) in file_path - tc=842
@testcase(842)
def test_Recipe_DB_Bugtracker_Match_Recipe_File_Bugtracker(self):
- recipes = Recipe.objects.values('id', 'bugtracker', 'file_path')
- cnt_err = []
- for recipe in recipes:
- file_path = self.fix_file_path(recipe['file_path'])
- file_exists = os.path.isfile(file_path)
- if (not file_path or (file_exists is False)):
- cnt_err.append(recipe['id'])
- else:
- file_bugtracker = self.recipe_parse(file_path, "BUGTRACKER = ")
- db_bugtracker = recipe['bugtracker']
- if file_bugtracker:
- if (db_bugtracker != file_bugtracker):
- cnt_err.append(recipe['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err)
+ recipes = Recipe.objects.values('id', 'bugtracker', 'file_path')
+ cnt_err = []
+ for recipe in recipes:
+ file_path = self.fix_file_path(recipe['file_path'])
+ file_exists = os.path.isfile(file_path)
+ if (not file_path or (file_exists is False)):
+ cnt_err.append(recipe['id'])
+ else:
+ file_bugtracker = self.recipe_parse(file_path, "BUGTRACKER = ")
+ db_bugtracker = recipe['bugtracker']
+ if file_bugtracker:
+ if (db_bugtracker != file_bugtracker):
+ cnt_err.append(recipe['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err)
# Recipe key verification, recipe name does not depends on a recipe having the same name - tc=883
@testcase(883)
def test_Recipe_Dependency(self):
- deps = Recipe_Dependency.objects.values('id', 'recipe_id', 'depends_on_id')
- cnt_err = []
- for dep in deps:
- if (not dep['recipe_id'] or not dep['depends_on_id']):
- cnt_err.append(dep['id'])
- else:
- name = Recipe.objects.filter(id = dep['recipe_id']).values('name')
- dep_name = Recipe.objects.filter(id = dep['depends_on_id']).values('name')
- if (name == dep_name):
- cnt_err.append(dep['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe dependency id: %s' % cnt_err)
+ deps = Recipe_Dependency.objects.values('id', 'recipe_id', 'depends_on_id')
+ cnt_err = []
+ for dep in deps:
+ if (not dep['recipe_id'] or not dep['depends_on_id']):
+ cnt_err.append(dep['id'])
+ else:
+ name = Recipe.objects.filter(id = dep['recipe_id']).values('name')
+ dep_name = Recipe.objects.filter(id = dep['depends_on_id']).values('name')
+ if (name == dep_name):
+ cnt_err.append(dep['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe dependency id: %s' % cnt_err)
# Check if package name does not start with a number (0-9) - tc=846
@testcase(846)
def test_Package_Name_For_Number(self):
- packages = Package.objects.filter(~Q(size = -1)).values('id', 'name')
- cnt_err = []
- for package in packages:
- if (package['name'][0].isdigit() is True):
- cnt_err.append(package['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
+ packages = Package.objects.filter(~Q(size = -1)).values('id', 'name')
+ cnt_err = []
+ for package in packages:
+ if (package['name'][0].isdigit() is True):
+ cnt_err.append(package['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
# Check if package version starts with a number (0-9) - tc=847
@testcase(847)
def test_Package_Version_Starts_With_Number(self):
- packages = Package.objects.filter(~Q(size = -1)).values('id', 'version')
- cnt_err = []
- for package in packages:
- if (package['version'][0].isdigit() is False):
- cnt_err.append(package['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
+ packages = Package.objects.filter(~Q(size = -1)).values('id', 'version')
+ cnt_err = []
+ for package in packages:
+ if (package['version'][0].isdigit() is False):
+ cnt_err.append(package['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
# Check if package revision starts with 'r' - tc=848
@testcase(848)
def test_Package_Revision_Starts_With_r(self):
- packages = Package.objects.filter(~Q(size = -1)).values('id', 'revision')
- cnt_err = []
- for package in packages:
- if (package['revision'][0].startswith("r") is False):
- cnt_err.append(package['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
+ packages = Package.objects.filter(~Q(size = -1)).values('id', 'revision')
+ cnt_err = []
+ for package in packages:
+ if (package['revision'][0].startswith("r") is False):
+ cnt_err.append(package['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
# Check the validity of the package build_id
### TC must be added in test run
@testcase(1038)
def test_Package_Build_Id(self):
- packages = Package.objects.filter(~Q(size = -1)).values('id', 'build_id')
- cnt_err = []
- for package in packages:
- build_id = Build.objects.filter(id = package['build_id']).values('id')
- if (not build_id):
- cnt_err.append(package['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
+ packages = Package.objects.filter(~Q(size = -1)).values('id', 'build_id')
+ cnt_err = []
+ for package in packages:
+ build_id = Build.objects.filter(id = package['build_id']).values('id')
+ if (not build_id):
+ cnt_err.append(package['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
# Check the validity of package recipe_id
### TC must be added in test run
@testcase(1039)
def test_Package_Recipe_Id(self):
- packages = Package.objects.filter(~Q(size = -1)).values('id', 'recipe_id')
- cnt_err = []
- for package in packages:
- recipe_id = Recipe.objects.filter(id = package['recipe_id']).values('id')
- if (not recipe_id):
- cnt_err.append(package['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
+ packages = Package.objects.filter(~Q(size = -1)).values('id', 'recipe_id')
+ cnt_err = []
+ for package in packages:
+ recipe_id = Recipe.objects.filter(id = package['recipe_id']).values('id')
+ if (not recipe_id):
+ cnt_err.append(package['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
# Check if package installed_size field is not null
### TC must be aded in test run
@testcase(1040)
def test_Package_Installed_Size_Not_NULL(self):
- packages = Package.objects.filter(installed_size__isnull = True).values('id')
- cnt_err = []
- for package in packages:
- cnt_err.append(package['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
+ packages = Package.objects.filter(installed_size__isnull = True).values('id')
+ cnt_err = []
+ for package in packages:
+ cnt_err.append(package['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
# Check if all layers requests return exit code is 200 - tc=843
@testcase(843)
def test_Layers_Requests_Exit_Code(self):
- layers = Layer.objects.values('id', 'layer_index_url')
- cnt_err = []
- for layer in layers:
- resp = urllib.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)
+ layers = Layer.objects.values('id', 'layer_index_url')
+ cnt_err = []
+ for layer in layers:
+ resp = urllib.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)
# Check if the output of bitbake-layers show_layers matches the info from database - tc=895
@testcase(895)
def test_Layers_Show_Layers(self):
- layers = Layer.objects.values('id', 'name', 'local_path')
- cmd = commands.getoutput('bitbake-layers show_layers')
- cnt_err = []
- for layer in layers:
- if (layer['name'] or layer['local_path']) not in cmd:
- cnt_err.append(layer['id'])
- self.assertEqual(len(cnt_err), 0, msg = 'Errors for layer id: %s' % cnt_err)
+ layers = Layer.objects.values('id', 'name', 'local_path')
+ cmd = commands.getoutput('bitbake-layers show_layers')
+ cnt_err = []
+ for layer in layers:
+ if (layer['name'] or layer['local_path']) not in cmd:
+ cnt_err.append(layer['id'])
+ self.assertEqual(len(cnt_err), 0, msg = 'Errors for layer id: %s' % cnt_err)
# Check if django server starts regardless of the timezone set on the machine - tc=905
@testcase(905)
def test_Start_Django_Timezone(self):
current_path = os.getcwd()
- zonefilelist = []
+ zonefilelist = []
ZONEINFOPATH = '/usr/share/zoneinfo/'
- os.chdir("../bitbake/lib/toaster/")
- cnt_err = 0
- for filename in os.listdir(ZONEINFOPATH):
- if os.path.isfile(os.path.join(ZONEINFOPATH, filename)):
- zonefilelist.append(filename)
- for k in range(len(zonefilelist)):
- if k <= 5:
- files = zonefilelist[k]
- os.system("export TZ="+str(files)+"; python manage.py runserver > /dev/null 2>&1 &")
- time.sleep(3)
- pid = subprocess.check_output("ps aux | grep '[/u]sr/bin/python manage.py runserver' | awk '{print $2}'", shell = True)
- if pid:
- os.system("kill -9 "+str(pid))
- else:
- cnt_err.append(zonefilelist[k])
- self.assertEqual(cnt_err, 0, msg = 'Errors django server does not start with timezone: %s' % cnt_err)
+ os.chdir("../bitbake/lib/toaster/")
+ cnt_err = 0
+ for filename in os.listdir(ZONEINFOPATH):
+ if os.path.isfile(os.path.join(ZONEINFOPATH, filename)):
+ zonefilelist.append(filename)
+ for k in range(len(zonefilelist)):
+ if k <= 5:
+ files = zonefilelist[k]
+ os.system("export TZ="+str(files)+"; python manage.py runserver > /dev/null 2>&1 &")
+ time.sleep(3)
+ pid = subprocess.check_output("ps aux | grep '[/u]sr/bin/python manage.py runserver' | awk '{print $2}'", shell = True)
+ if pid:
+ os.system("kill -9 "+str(pid))
+ else:
+ cnt_err.append(zonefilelist[k])
+ self.assertEqual(cnt_err, 0, msg = 'Errors django server does not start with timezone: %s' % cnt_err)
os.chdir(current_path)
diff --git a/meta/lib/oeqa/selftest/bblayers.py b/meta/lib/oeqa/selftest/bblayers.py
index 1ead8e8671..5b046d06e1 100644
--- a/meta/lib/oeqa/selftest/bblayers.py
+++ b/meta/lib/oeqa/selftest/bblayers.py
@@ -18,26 +18,43 @@ class BitbakeLayers(oeSelfTest):
@testcase(83)
def test_bitbakelayers_showlayers(self):
- result = runCmd('bitbake-layers show_layers')
+ result = runCmd('bitbake-layers show-layers')
self.assertTrue('meta-selftest' in result.output)
@testcase(93)
def test_bitbakelayers_showappends(self):
- result = runCmd('bitbake-layers show_appends')
+ result = runCmd('bitbake-layers show-appends')
self.assertTrue('xcursor-transparent-theme_0.1.1.bbappend' in result.output, msg='xcursor-transparent-theme_0.1.1.bbappend file was not recognised')
@testcase(90)
def test_bitbakelayers_showoverlayed(self):
- result = runCmd('bitbake-layers show_overlayed')
- self.assertTrue('aspell' in result.output, msg='xcursor-transparent-theme_0.1.1.bbappend file was not recognised')
+ result = runCmd('bitbake-layers show-overlayed')
+ self.assertTrue('aspell' in result.output, msg='aspell overlayed recipe was not recognised')
@testcase(95)
def test_bitbakelayers_flatten(self):
- self.assertFalse(os.path.isdir(os.path.join(self.builddir, 'test')))
- result = runCmd('bitbake-layers flatten test')
- bb_file = os.path.join(self.builddir, 'test/recipes-graphics/xcursor-transparent-theme/xcursor-transparent-theme_0.1.1.bb')
+ testoutdir = os.path.join(self.builddir, 'test_bitbakelayers_flatten')
+ self.assertFalse(os.path.isdir(testoutdir))
+ self.track_for_cleanup(testoutdir)
+ result = runCmd('bitbake-layers flatten %s' % testoutdir)
+ bb_file = os.path.join(testoutdir, 'recipes-graphics/xcursor-transparent-theme/xcursor-transparent-theme_0.1.1.bb')
self.assertTrue(os.path.isfile(bb_file))
contents = ftools.read_file(bb_file)
find_in_contents = re.search("##### bbappended from meta-selftest #####\n(.*\n)*include test_recipe.inc", contents)
- shutil.rmtree(os.path.join(self.builddir, 'test'))
self.assertTrue(find_in_contents)
+
+ def test_bitbakelayers_add_remove(self):
+ result = runCmd('bitbake-layers show-layers')
+ self.assertNotIn('meta-skeleton', result.output, 'This test cannot run with meta-skeleton in bblayers.conf')
+ result = runCmd('bitbake-layers add-layer ../meta-skeleton')
+ result = runCmd('bitbake-layers show-layers')
+ self.assertIn('meta-skeleton', result.output)
+ result = runCmd('bitbake-layers remove-layer ../meta-skeleton')
+ result = runCmd('bitbake-layers show-layers')
+ self.assertNotIn('meta-skeleton', result.output)
+ result = runCmd('bitbake-layers add-layer ../meta-skeleton')
+ result = runCmd('bitbake-layers show-layers')
+ self.assertIn('meta-skeleton', result.output)
+ result = runCmd('bitbake-layers remove-layer */meta-skeleton')
+ result = runCmd('bitbake-layers show-layers')
+ self.assertNotIn('meta-skeleton', result.output)
diff --git a/meta/lib/oeqa/selftest/bbtests.py b/meta/lib/oeqa/selftest/bbtests.py
index 68f97bd8e3..b301d8f819 100644
--- a/meta/lib/oeqa/selftest/bbtests.py
+++ b/meta/lib/oeqa/selftest/bbtests.py
@@ -27,7 +27,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: Preparing RunQueue", 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)
@@ -71,6 +71,7 @@ class BitbakeTests(oeSelfTest):
@testcase(163)
def test_force_task(self):
bitbake('m4-native')
+ self.add_command_to_tearDown('bitbake -c clean m4-native')
result = bitbake('-C compile m4-native')
look_for_tasks = ['do_compile', 'do_install', 'do_populate_sysroot']
for task in look_for_tasks:
@@ -119,60 +120,71 @@ class BitbakeTests(oeSelfTest):
@testcase(1028)
def test_environment(self):
- self.append_config("TEST_ENV=\"localconf\"")
- result = runCmd('bitbake -e | grep TEST_ENV=')
- self.assertTrue('localconf' in result.output)
- self.remove_config("TEST_ENV=\"localconf\"")
+ self.append_config("TEST_ENV=\"localconf\"")
+ result = runCmd('bitbake -e | grep TEST_ENV=')
+ self.assertTrue('localconf' in result.output)
+ self.remove_config("TEST_ENV=\"localconf\"")
@testcase(1029)
def test_dry_run(self):
- result = runCmd('bitbake -n m4-native')
- self.assertEqual(0, result.status)
+ result = runCmd('bitbake -n m4-native')
+ self.assertEqual(0, result.status)
@testcase(1030)
def test_just_parse(self):
- result = runCmd('bitbake -p')
- self.assertEqual(0, result.status)
+ result = runCmd('bitbake -p')
+ self.assertEqual(0, result.status)
@testcase(1031)
def test_version(self):
- result = runCmd('bitbake -s | grep wget')
- find = re.search("wget *:([0-9a-zA-Z\.\-]+)", result.output)
- self.assertTrue(find)
+ result = runCmd('bitbake -s | grep wget')
+ find = re.search("wget *:([0-9a-zA-Z\.\-]+)", result.output)
+ self.assertTrue(find)
@testcase(1032)
def test_prefile(self):
- preconf = os.path.join(self.builddir, 'conf/prefile.conf')
- self.track_for_cleanup(preconf)
- ftools.write_file(preconf ,"TEST_PREFILE=\"prefile\"")
- result = runCmd('bitbake -r conf/prefile.conf -e | grep TEST_PREFILE=')
- self.assertTrue('prefile' in result.output)
- self.append_config("TEST_PREFILE=\"localconf\"")
- result = runCmd('bitbake -r conf/prefile.conf -e | grep TEST_PREFILE=')
- self.assertTrue('localconf' in result.output)
- self.remove_config("TEST_PREFILE=\"localconf\"")
+ preconf = os.path.join(self.builddir, 'conf/prefile.conf')
+ self.track_for_cleanup(preconf)
+ ftools.write_file(preconf ,"TEST_PREFILE=\"prefile\"")
+ result = runCmd('bitbake -r conf/prefile.conf -e | grep TEST_PREFILE=')
+ self.assertTrue('prefile' in result.output)
+ self.append_config("TEST_PREFILE=\"localconf\"")
+ result = runCmd('bitbake -r conf/prefile.conf -e | grep TEST_PREFILE=')
+ self.assertTrue('localconf' in result.output)
+ self.remove_config("TEST_PREFILE=\"localconf\"")
@testcase(1033)
def test_postfile(self):
- postconf = os.path.join(self.builddir, 'conf/postfile.conf')
- self.track_for_cleanup(postconf)
- ftools.write_file(postconf , "TEST_POSTFILE=\"postfile\"")
- self.append_config("TEST_POSTFILE=\"localconf\"")
- result = runCmd('bitbake -R conf/postfile.conf -e | grep TEST_POSTFILE=')
- self.assertTrue('postfile' in result.output)
- self.remove_config("TEST_POSTFILE=\"localconf\"")
+ postconf = os.path.join(self.builddir, 'conf/postfile.conf')
+ self.track_for_cleanup(postconf)
+ ftools.write_file(postconf , "TEST_POSTFILE=\"postfile\"")
+ self.append_config("TEST_POSTFILE=\"localconf\"")
+ result = runCmd('bitbake -R conf/postfile.conf -e | grep TEST_POSTFILE=')
+ self.assertTrue('postfile' in result.output)
+ self.remove_config("TEST_POSTFILE=\"localconf\"")
@testcase(1034)
def test_checkuri(self):
- result = runCmd('bitbake -c checkuri m4')
- self.assertEqual(0, result.status)
+ result = runCmd('bitbake -c checkuri m4')
+ self.assertEqual(0, result.status)
@testcase(1035)
def test_continue(self):
- self.write_recipeinc('man',"\ndo_fail_task () {\nexit 1 \n}\n\naddtask do_fail_task before do_fetch\n" )
- runCmd('bitbake -c cleanall man xcursor-transparent-theme')
- result = runCmd('bitbake man xcursor-transparent-theme -k', ignore_status=True)
- errorpos = result.output.find('ERROR: Function failed: do_fail_task')
- manver = re.search("NOTE: recipe xcursor-transparent-theme-(.*?): task do_unpack: Started", result.output)
- continuepos = result.output.find('NOTE: recipe xcursor-transparent-theme-%s: task do_unpack: Started' % manver.group(1))
- self.assertLess(errorpos,continuepos)
+ self.write_recipeinc('man',"\ndo_fail_task () {\nexit 1 \n}\n\naddtask do_fail_task before do_fetch\n" )
+ runCmd('bitbake -c cleanall man xcursor-transparent-theme')
+ result = runCmd('bitbake man xcursor-transparent-theme -k', ignore_status=True)
+ errorpos = result.output.find('ERROR: Function failed: do_fail_task')
+ manver = re.search("NOTE: recipe xcursor-transparent-theme-(.*?): task do_unpack: Started", result.output)
+ continuepos = result.output.find('NOTE: recipe xcursor-transparent-theme-%s: task do_unpack: Started' % manver.group(1))
+ self.assertLess(errorpos,continuepos)
+
+ @testcase(1119)
+ def test_non_gplv3(self):
+ data = 'INCOMPATIBLE_LICENSE = "GPLv3"'
+ conf = os.path.join(self.builddir, 'conf/local.conf')
+ ftools.append_file(conf ,data)
+ result = bitbake('readline', ignore_status=True)
+ self.assertEqual(result.status, 0)
+ self.assertFalse(os.path.isfile(os.path.join(self.builddir, 'tmp/deploy/licenses/readline/generic_GPLv3')))
+ self.assertTrue(os.path.isfile(os.path.join(self.builddir, 'tmp/deploy/licenses/readline/generic_GPLv2')))
+ ftools.remove_from_file(conf ,data) \ No newline at end of file
diff --git a/meta/lib/oeqa/selftest/buildoptions.py b/meta/lib/oeqa/selftest/buildoptions.py
index a250cae0e1..926ffe9993 100644
--- a/meta/lib/oeqa/selftest/buildoptions.py
+++ b/meta/lib/oeqa/selftest/buildoptions.py
@@ -13,16 +13,22 @@ class ImageOptionsTests(oeSelfTest):
@testcase(761)
def test_incremental_image_generation(self):
+ image_pkgtype = get_bb_var("IMAGE_PKGTYPE")
+ if image_pkgtype != 'rpm':
+ self.skipTest('Not using RPM as main package format')
bitbake("-c cleanall core-image-minimal")
self.write_config('INC_RPM_IMAGE_GEN = "1"')
self.append_config('IMAGE_FEATURES += "ssh-server-openssh"')
bitbake("core-image-minimal")
- res = runCmd("grep 'Installing openssh-sshd' %s" % (os.path.join(get_bb_var("WORKDIR", "core-image-minimal"), "temp/log.do_rootfs")), ignore_status=True)
+ log_data_file = os.path.join(get_bb_var("WORKDIR", "core-image-minimal"), "temp/log.do_rootfs")
+ log_data_created = ftools.read_file(log_data_file)
+ incremental_created = re.search("NOTE: load old install solution for incremental install\nNOTE: old install solution not exist\nNOTE: creating new install solution for incremental install(\n.*)*NOTE: Installing the following packages:.*packagegroup-core-ssh-openssh", log_data_created)
self.remove_config('IMAGE_FEATURES += "ssh-server-openssh"')
- self.assertEqual(0, res.status, msg="No match for openssh-sshd in log.do_rootfs")
+ self.assertTrue(incremental_created, msg = "Match failed in:\n%s" % log_data_created)
bitbake("core-image-minimal")
- res = runCmd("grep 'Removing openssh-sshd' %s" %(os.path.join(get_bb_var("WORKDIR", "core-image-minimal"), "temp/log.do_rootfs")),ignore_status=True)
- self.assertEqual(0, res.status, msg="openssh-sshd was not removed from image")
+ log_data_removed = ftools.read_file(log_data_file)
+ 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):
diff --git a/meta/lib/oeqa/selftest/devtool.py b/meta/lib/oeqa/selftest/devtool.py
new file mode 100644
index 0000000000..dc1cf21064
--- /dev/null
+++ b/meta/lib/oeqa/selftest/devtool.py
@@ -0,0 +1,527 @@
+import unittest
+import os
+import logging
+import re
+import shutil
+import tempfile
+import glob
+
+import oeqa.utils.ftools as ftools
+from oeqa.selftest.base import oeSelfTest
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var
+from oeqa.utils.decorators import testcase
+
+class DevtoolTests(oeSelfTest):
+
+ def test_create_workspace(self):
+ # Check preconditions
+ workspacedir = os.path.join(self.builddir, 'workspace')
+ self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ result = runCmd('bitbake-layers show-layers')
+ self.assertTrue('/workspace' not in result.output, 'This test cannot be run with a workspace layer in bblayers.conf')
+ # Try creating a workspace layer with a specific path
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ result = runCmd('devtool create-workspace %s' % tempdir)
+ self.assertTrue(os.path.isfile(os.path.join(tempdir, 'conf', 'layer.conf')))
+ result = runCmd('bitbake-layers show-layers')
+ self.assertIn(tempdir, result.output)
+ # Try creating a workspace layer with the default path
+ self.track_for_cleanup(workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ result = runCmd('devtool create-workspace')
+ self.assertTrue(os.path.isfile(os.path.join(workspacedir, 'conf', 'layer.conf')))
+ result = runCmd('bitbake-layers show-layers')
+ self.assertNotIn(tempdir, result.output)
+ self.assertIn(workspacedir, result.output)
+
+ def test_recipetool_create(self):
+ # Try adding a recipe
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ tempsrc = os.path.join(tempdir, 'srctree')
+ os.makedirs(tempsrc)
+ recipefile = os.path.join(tempdir, 'logrotate_3.8.7.bb')
+ srcuri = 'https://fedorahosted.org/releases/l/o/logrotate/logrotate-3.8.7.tar.gz'
+ result = runCmd('recipetool create -o %s %s -x %s' % (recipefile, srcuri, tempsrc))
+ self.assertTrue(os.path.isfile(recipefile))
+ checkvars = {}
+ checkvars['LICENSE'] = 'GPLv2'
+ checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=18810669f13b87348459e611d31ab760'
+ checkvars['SRC_URI'] = 'https://fedorahosted.org/releases/l/o/logrotate/logrotate-${PV}.tar.gz'
+ checkvars['SRC_URI[md5sum]'] = '99e08503ef24c3e2e3ff74cc5f3be213'
+ checkvars['SRC_URI[sha256sum]'] = 'f6ba691f40e30e640efa2752c1f9499a3f9738257660994de70a45fe00d12b64'
+ with open(recipefile, 'r') as f:
+ for line in f:
+ if '=' in line:
+ splitline = line.split('=', 1)
+ var = splitline[0].rstrip()
+ value = splitline[1].strip().strip('"')
+ if var in checkvars:
+ needvalue = checkvars.pop(var)
+ self.assertEqual(value, needvalue)
+ if line.startswith('inherit '):
+ inherits = line.split()[1:]
+
+ self.assertEqual(checkvars, {}, 'Some variables not found')
+
+ def test_recipetool_create_git(self):
+ # Ensure we have the right data in shlibs/pkgdata
+ bitbake('libpng pango libx11 libxext jpeg')
+ # Try adding a recipe
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ tempsrc = os.path.join(tempdir, 'srctree')
+ os.makedirs(tempsrc)
+ recipefile = os.path.join(tempdir, 'libmatchbox.bb')
+ srcuri = 'git://git.yoctoproject.org/libmatchbox'
+ result = runCmd('recipetool create -o %s %s -x %s' % (recipefile, srcuri, tempsrc))
+ self.assertTrue(os.path.isfile(recipefile), 'recipetool did not create recipe file; output:\n%s' % result.output)
+ checkvars = {}
+ checkvars['LICENSE'] = 'LGPLv2.1'
+ checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=7fbc338309ac38fefcd64b04bb903e34'
+ checkvars['S'] = '${WORKDIR}/git'
+ checkvars['PV'] = '1.0+git${SRCPV}'
+ checkvars['SRC_URI'] = srcuri
+ checkvars['DEPENDS'] = 'libpng pango libx11 libxext jpeg'
+ inherits = []
+ with open(recipefile, 'r') as f:
+ for line in f:
+ if '=' in line:
+ splitline = line.split('=', 1)
+ var = splitline[0].rstrip()
+ value = splitline[1].strip().strip('"')
+ if var in checkvars:
+ needvalue = checkvars.pop(var)
+ self.assertEqual(value, needvalue)
+ if line.startswith('inherit '):
+ inherits = line.split()[1:]
+
+ self.assertEqual(checkvars, {}, 'Some variables not found')
+
+ self.assertIn('autotools', inherits, 'Missing inherit of autotools')
+ self.assertIn('pkgconfig', inherits, 'Missing inherit of pkgconfig')
+
+ def test_devtool_add(self):
+ # Check preconditions
+ workspacedir = os.path.join(self.builddir, 'workspace')
+ self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ # Fetch source
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ url = 'http://www.ivarch.com/programs/sources/pv-1.5.3.tar.bz2'
+ result = runCmd('wget %s' % url, cwd=tempdir)
+ result = runCmd('tar xfv pv-1.5.3.tar.bz2', cwd=tempdir)
+ srcdir = os.path.join(tempdir, 'pv-1.5.3')
+ self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure')), 'Unable to find configure script in source directory')
+ # Test devtool add
+ self.track_for_cleanup(workspacedir)
+ self.add_command_to_tearDown('bitbake -c cleansstate pv')
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ result = runCmd('devtool add pv %s' % srcdir)
+ self.assertTrue(os.path.exists(os.path.join(workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn('pv', result.output)
+ self.assertIn(srcdir, result.output)
+ # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
+ bitbake('pv -c cleansstate')
+ # Test devtool build
+ result = runCmd('devtool build pv')
+ installdir = get_bb_var('D', 'pv')
+ self.assertTrue(installdir, 'Could not query installdir variable')
+ bindir = get_bb_var('bindir', 'pv')
+ self.assertTrue(bindir, 'Could not query bindir variable')
+ if bindir[0] == '/':
+ bindir = bindir[1:]
+ self.assertTrue(os.path.isfile(os.path.join(installdir, bindir, 'pv')), 'pv binary not found in D')
+
+ def test_devtool_add_library(self):
+ # Check preconditions
+ workspacedir = os.path.join(self.builddir, 'workspace')
+ self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ # We don't have the ability to pick up this dependency automatically yet...
+ bitbake('libusb1')
+ # Fetch source
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ url = 'http://www.intra2net.com/en/developer/libftdi/download/libftdi1-1.1.tar.bz2'
+ result = runCmd('wget %s' % url, cwd=tempdir)
+ result = runCmd('tar xfv libftdi1-1.1.tar.bz2', cwd=tempdir)
+ srcdir = os.path.join(tempdir, 'libftdi1-1.1')
+ self.assertTrue(os.path.isfile(os.path.join(srcdir, 'CMakeLists.txt')), 'Unable to find CMakeLists.txt in source directory')
+ # Test devtool add
+ self.track_for_cleanup(workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ result = runCmd('devtool add libftdi %s' % srcdir)
+ self.assertTrue(os.path.exists(os.path.join(workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn('libftdi', result.output)
+ self.assertIn(srcdir, result.output)
+ # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
+ bitbake('libftdi -c cleansstate')
+ # Test devtool build
+ result = runCmd('devtool build libftdi')
+ staging_libdir = get_bb_var('STAGING_LIBDIR', 'libftdi')
+ self.assertTrue(staging_libdir, 'Could not query STAGING_LIBDIR variable')
+ self.assertTrue(os.path.isfile(os.path.join(staging_libdir, 'libftdi1.so.2.1.0')), 'libftdi binary not found in STAGING_LIBDIR')
+ # Test devtool reset
+ stampprefix = get_bb_var('STAMP', 'libftdi')
+ result = runCmd('devtool reset libftdi')
+ result = runCmd('devtool status')
+ self.assertNotIn('libftdi', result.output)
+ self.assertTrue(stampprefix, 'Unable to get STAMP value for recipe libftdi')
+ matches = glob.glob(stampprefix + '*')
+ self.assertFalse(matches, 'Stamp files exist for recipe libftdi that should have been cleaned')
+ self.assertFalse(os.path.isfile(os.path.join(staging_libdir, 'libftdi1.so.2.1.0')), 'libftdi binary still found in STAGING_LIBDIR after cleaning')
+
+ def test_devtool_modify(self):
+ # Check preconditions
+ workspacedir = os.path.join(self.builddir, 'workspace')
+ self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ # Clean up anything in the workdir/sysroot/sstate cache
+ bitbake('mdadm -c cleansstate')
+ # Try modifying a recipe
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ self.add_command_to_tearDown('bitbake -c clean mdadm')
+ result = runCmd('devtool modify mdadm -x %s' % tempdir)
+ self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile')), 'Extracted source could not be found')
+ self.assertTrue(os.path.isdir(os.path.join(tempdir, '.git')), 'git repository for external source tree not found')
+ self.assertTrue(os.path.exists(os.path.join(workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
+ matches = glob.glob(os.path.join(workspacedir, 'appends', 'mdadm_*.bbappend'))
+ self.assertTrue(matches, 'bbappend not created')
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn('mdadm', result.output)
+ self.assertIn(tempdir, result.output)
+ # Check git repo
+ result = runCmd('git status --porcelain', cwd=tempdir)
+ self.assertEqual(result.output.strip(), "", 'Created git repo is not clean')
+ result = runCmd('git symbolic-ref HEAD', cwd=tempdir)
+ self.assertEqual(result.output.strip(), "refs/heads/devtool", 'Wrong branch in git repo')
+ # Try building
+ bitbake('mdadm')
+ # Try making (minor) modifications to the source
+ result = runCmd("sed -i 's!^\.TH.*!.TH MDADM 8 \"\" v9.999-custom!' %s" % os.path.join(tempdir, 'mdadm.8.in'))
+ bitbake('mdadm -c package')
+ pkgd = get_bb_var('PKGD', 'mdadm')
+ self.assertTrue(pkgd, 'Could not query PKGD variable')
+ mandir = get_bb_var('mandir', 'mdadm')
+ self.assertTrue(mandir, 'Could not query mandir variable')
+ if mandir[0] == '/':
+ mandir = mandir[1:]
+ with open(os.path.join(pkgd, mandir, 'man8', 'mdadm.8'), 'r') as f:
+ for line in f:
+ if line.startswith('.TH'):
+ self.assertEqual(line.rstrip(), '.TH MDADM 8 "" v9.999-custom', 'man file not modified')
+ # Test devtool reset
+ stampprefix = get_bb_var('STAMP', 'mdadm')
+ result = runCmd('devtool reset mdadm')
+ result = runCmd('devtool status')
+ self.assertNotIn('mdadm', result.output)
+ self.assertTrue(stampprefix, 'Unable to get STAMP value for recipe mdadm')
+ matches = glob.glob(stampprefix + '*')
+ self.assertFalse(matches, 'Stamp files exist for recipe mdadm that should have been cleaned')
+
+ def test_devtool_modify_invalid(self):
+ # Check preconditions
+ workspacedir = os.path.join(self.builddir, 'workspace')
+ self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ # Try modifying some recipes
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+
+ testrecipes = 'perf gcc-source kernel-devsrc package-index core-image-minimal meta-toolchain packagegroup-core-sdk meta-ide-support'.split()
+ for testrecipe in testrecipes:
+ # Check it's a valid recipe
+ bitbake('%s -e' % testrecipe)
+ # devtool extract should fail
+ result = runCmd('devtool extract %s %s' % (testrecipe, os.path.join(tempdir, testrecipe)), ignore_status=True)
+ self.assertNotEqual(result.status, 0, 'devtool extract on %s should have failed' % testrecipe)
+ self.assertNotIn('Fetching ', result.output, 'devtool extract on %s should have errored out before trying to fetch' % testrecipe)
+ self.assertIn('ERROR: ', result.output, 'devtool extract on %s should have given an ERROR' % testrecipe)
+ # devtool modify should fail
+ result = runCmd('devtool modify %s -x %s' % (testrecipe, os.path.join(tempdir, testrecipe)), ignore_status=True)
+ self.assertNotEqual(result.status, 0, 'devtool modify on %s should have failed' % testrecipe)
+ self.assertIn('ERROR: ', result.output, 'devtool modify on %s should have given an ERROR' % testrecipe)
+
+ def test_devtool_modify_git(self):
+ # Check preconditions
+ workspacedir = os.path.join(self.builddir, 'workspace')
+ self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ testrecipe = 'mkelfimage'
+ src_uri = get_bb_var('SRC_URI', testrecipe)
+ self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
+ # Clean up anything in the workdir/sysroot/sstate cache
+ bitbake('%s -c cleansstate' % testrecipe)
+ # Try modifying a recipe
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
+ result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
+ self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile')), 'Extracted source could not be found')
+ self.assertTrue(os.path.isdir(os.path.join(tempdir, '.git')), 'git repository for external source tree not found')
+ self.assertTrue(os.path.exists(os.path.join(workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
+ matches = glob.glob(os.path.join(workspacedir, 'appends', 'mkelfimage_*.bbappend'))
+ self.assertTrue(matches, 'bbappend not created')
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn(testrecipe, result.output)
+ self.assertIn(tempdir, result.output)
+ # Check git repo
+ result = runCmd('git status --porcelain', cwd=tempdir)
+ self.assertEqual(result.output.strip(), "", 'Created git repo is not clean')
+ result = runCmd('git symbolic-ref HEAD', cwd=tempdir)
+ self.assertEqual(result.output.strip(), "refs/heads/devtool", 'Wrong branch in git repo')
+ # Try building
+ bitbake(testrecipe)
+
+ def test_devtool_modify_localfiles(self):
+ # Check preconditions
+ workspacedir = os.path.join(self.builddir, 'workspace')
+ self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ testrecipe = 'lighttpd'
+ src_uri = (get_bb_var('SRC_URI', testrecipe) or '').split()
+ foundlocal = False
+ for item in src_uri:
+ if item.startswith('file://') and '.patch' not in item:
+ foundlocal = True
+ break
+ self.assertTrue(foundlocal, 'This test expects the %s recipe to fetch local files and it seems that it no longer does' % testrecipe)
+ # Clean up anything in the workdir/sysroot/sstate cache
+ bitbake('%s -c cleansstate' % testrecipe)
+ # Try modifying a recipe
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
+ result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
+ self.assertTrue(os.path.exists(os.path.join(tempdir, 'configure.ac')), 'Extracted source could not be found')
+ self.assertTrue(os.path.exists(os.path.join(workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
+ matches = glob.glob(os.path.join(workspacedir, 'appends', '%s_*.bbappend' % testrecipe))
+ self.assertTrue(matches, 'bbappend not created')
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn(testrecipe, result.output)
+ self.assertIn(tempdir, result.output)
+ # Try building
+ bitbake(testrecipe)
+
+ def test_devtool_update_recipe(self):
+ # Check preconditions
+ workspacedir = os.path.join(self.builddir, 'workspace')
+ self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ testrecipe = 'minicom'
+ recipefile = get_bb_var('FILE', testrecipe)
+ src_uri = get_bb_var('SRC_URI', testrecipe)
+ self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe)
+ result = runCmd('git status . --porcelain', cwd=os.path.dirname(recipefile))
+ self.assertEqual(result.output.strip(), "", '%s recipe is not clean' % testrecipe)
+ # First, modify a recipe
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ # (don't bother with cleaning the recipe on teardown, we won't be building it)
+ result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
+ # Check git repo
+ self.assertTrue(os.path.isdir(os.path.join(tempdir, '.git')), 'git repository for external source tree not found')
+ result = runCmd('git status --porcelain', cwd=tempdir)
+ self.assertEqual(result.output.strip(), "", 'Created git repo is not clean')
+ result = runCmd('git symbolic-ref HEAD', cwd=tempdir)
+ self.assertEqual(result.output.strip(), "refs/heads/devtool", 'Wrong branch in git repo')
+ # Add a couple of commits
+ # FIXME: this only tests adding, need to also test update and remove
+ result = runCmd('echo "Additional line" >> README', cwd=tempdir)
+ result = runCmd('git commit -a -m "Change the README"', cwd=tempdir)
+ result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir)
+ result = runCmd('git add devtool-new-file', cwd=tempdir)
+ result = runCmd('git commit -m "Add a new file"', cwd=tempdir)
+ self.add_command_to_tearDown('cd %s; rm %s/*.patch; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
+ result = runCmd('devtool update-recipe %s' % testrecipe)
+ result = runCmd('git status . --porcelain', cwd=os.path.dirname(recipefile))
+ self.assertNotEqual(result.output.strip(), "", '%s recipe should be modified' % testrecipe)
+ status = result.output.splitlines()
+ self.assertEqual(len(status), 3, 'Less/more files modified than expected. Entire status:\n%s' % result.output)
+ for line in status:
+ if line.endswith('0001-Change-the-README.patch'):
+ self.assertEqual(line[:3], '?? ', 'Unexpected status in line: %s' % line)
+ elif line.endswith('0002-Add-a-new-file.patch'):
+ self.assertEqual(line[:3], '?? ', 'Unexpected status in line: %s' % line)
+ elif re.search('%s_[^_]*.bb$' % testrecipe, line):
+ self.assertEqual(line[:3], ' M ', 'Unexpected status in line: %s' % line)
+ else:
+ raise AssertionError('Unexpected modified file in status: %s' % line)
+
+ def test_devtool_update_recipe_git(self):
+ # Check preconditions
+ workspacedir = os.path.join(self.builddir, 'workspace')
+ self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ testrecipe = 'mtd-utils'
+ recipefile = get_bb_var('FILE', testrecipe)
+ src_uri = get_bb_var('SRC_URI', testrecipe)
+ self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
+ result = runCmd('git status . --porcelain', cwd=os.path.dirname(recipefile))
+ self.assertEqual(result.output.strip(), "", '%s recipe is not clean' % testrecipe)
+ # First, modify a recipe
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ # (don't bother with cleaning the recipe on teardown, we won't be building it)
+ result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
+ # Check git repo
+ self.assertTrue(os.path.isdir(os.path.join(tempdir, '.git')), 'git repository for external source tree not found')
+ result = runCmd('git status --porcelain', cwd=tempdir)
+ self.assertEqual(result.output.strip(), "", 'Created git repo is not clean')
+ result = runCmd('git symbolic-ref HEAD', cwd=tempdir)
+ self.assertEqual(result.output.strip(), "refs/heads/devtool", 'Wrong branch in git repo')
+ # Add a couple of commits
+ # FIXME: this only tests adding, need to also test update and remove
+ result = runCmd('echo "# Additional line" >> Makefile', cwd=tempdir)
+ result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempdir)
+ result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir)
+ result = runCmd('git add devtool-new-file', cwd=tempdir)
+ result = runCmd('git commit -m "Add a new file"', cwd=tempdir)
+ self.add_command_to_tearDown('cd %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, os.path.basename(recipefile)))
+ result = runCmd('devtool update-recipe %s' % testrecipe)
+ result = runCmd('git status . --porcelain', cwd=os.path.dirname(recipefile))
+ self.assertNotEqual(result.output.strip(), "", '%s recipe should be modified' % testrecipe)
+ status = result.output.splitlines()
+ self.assertEqual(len(status), 3, 'Less/more files modified than expected. Entire status:\n%s' % result.output)
+ for line in status:
+ if line.endswith('add-exclusion-to-mkfs-jffs2-git-2.patch'):
+ self.assertEqual(line[:3], ' D ', 'Unexpected status in line: %s' % line)
+ elif line.endswith('fix-armv7-neon-alignment.patch'):
+ self.assertEqual(line[:3], ' D ', 'Unexpected status in line: %s' % line)
+ elif re.search('%s_[^_]*.bb$' % testrecipe, line):
+ self.assertEqual(line[:3], ' M ', 'Unexpected status in line: %s' % line)
+ else:
+ raise AssertionError('Unexpected modified file in status: %s' % line)
+ result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile))
+ addlines = ['SRCREV = ".*"', 'SRC_URI = "git://git.infradead.org/mtd-utils.git"']
+ removelines = ['SRCREV = ".*"', 'SRC_URI = "git://git.infradead.org/mtd-utils.git \\\\', 'file://add-exclusion-to-mkfs-jffs2-git-2.patch \\\\', 'file://fix-armv7-neon-alignment.patch \\\\', '"']
+ for line in result.output.splitlines():
+ if line.startswith('+++') or line.startswith('---'):
+ continue
+ elif line.startswith('+'):
+ matched = False
+ for item in addlines:
+ if re.match(item, line[1:].strip()):
+ matched = True
+ break
+ self.assertTrue(matched, 'Unexpected diff add line: %s' % line)
+ elif line.startswith('-'):
+ matched = False
+ for item in removelines:
+ if re.match(item, line[1:].strip()):
+ matched = True
+ break
+ self.assertTrue(matched, 'Unexpected diff remove line: %s' % line)
+
+ def test_devtool_extract(self):
+ # Check preconditions
+ workspacedir = os.path.join(self.builddir, 'workspace')
+ self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ # Try devtool extract
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ result = runCmd('devtool extract remake %s' % tempdir)
+ self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile.am')), 'Extracted source could not be found')
+ self.assertTrue(os.path.isdir(os.path.join(tempdir, '.git')), 'git repository for external source tree not found')
+
+ def test_devtool_reset_all(self):
+ # Check preconditions
+ workspacedir = os.path.join(self.builddir, 'workspace')
+ self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ testrecipe1 = 'mdadm'
+ testrecipe2 = 'cronie'
+ result = runCmd('devtool modify -x %s %s' % (testrecipe1, os.path.join(tempdir, testrecipe1)))
+ result = runCmd('devtool modify -x %s %s' % (testrecipe2, os.path.join(tempdir, testrecipe2)))
+ result = runCmd('devtool build %s' % testrecipe1)
+ result = runCmd('devtool build %s' % testrecipe2)
+ stampprefix1 = get_bb_var('STAMP', testrecipe1)
+ self.assertTrue(stampprefix1, 'Unable to get STAMP value for recipe %s' % testrecipe1)
+ stampprefix2 = get_bb_var('STAMP', testrecipe2)
+ self.assertTrue(stampprefix2, 'Unable to get STAMP value for recipe %s' % testrecipe2)
+ result = runCmd('devtool reset -a')
+ self.assertIn(testrecipe1, result.output)
+ self.assertIn(testrecipe2, result.output)
+ result = runCmd('devtool status')
+ self.assertNotIn(testrecipe1, result.output)
+ self.assertNotIn(testrecipe2, result.output)
+ matches1 = glob.glob(stampprefix1 + '*')
+ self.assertFalse(matches1, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe1)
+ matches2 = glob.glob(stampprefix2 + '*')
+ self.assertFalse(matches2, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe2)
+
+ def test_devtool_deploy_target(self):
+ # NOTE: Whilst this test would seemingly be better placed as a runtime test,
+ # unfortunately the runtime tests run under bitbake and you can't run
+ # devtool within bitbake (since devtool needs to run bitbake itself).
+ # Additionally we are testing build-time functionality as well, so
+ # really this has to be done as an oe-selftest test.
+ #
+ # Check preconditions
+ machine = get_bb_var('MACHINE')
+ if not machine.startswith('qemu'):
+ self.skipTest('This test only works with qemu machines')
+ if not os.path.exists('/etc/runqemu-nosudo'):
+ self.skipTest('You must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
+ workspacedir = os.path.join(self.builddir, 'workspace')
+ self.assertTrue(not os.path.exists(workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ import pexpect
+ # Definitions
+ testrecipe = 'mdadm'
+ testfile = '/sbin/mdadm'
+ testimage = 'oe-selftest-image'
+ testhost = '192.168.7.2'
+ testcommand = '/sbin/mdadm --help'
+ # Build an image to run
+ bitbake("%s qemu-native qemu-helper-native" % testimage)
+ deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
+ self.add_command_to_tearDown('bitbake -c clean %s' % testimage)
+ self.add_command_to_tearDown('rm -f %s/%s*' % (deploy_dir_image, testimage))
+ # Clean recipe so the first deploy will fail
+ bitbake("%s -c clean" % testrecipe)
+ # Try devtool modify
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
+ result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
+ # Test that deploy-target at this point fails (properly)
+ result = runCmd('devtool deploy-target -n %s root@%s' % (testrecipe, testhost), ignore_status=True)
+ self.assertNotEqual(result.output, 0, 'devtool deploy-target should have failed, output: %s' % result.output)
+ self.assertNotIn(result.output, 'Traceback', 'devtool deploy-target should have failed with a proper error not a traceback, output: %s' % result.output)
+ result = runCmd('devtool build %s' % testrecipe)
+ # First try a dry-run of deploy-target
+ result = runCmd('devtool deploy-target -n %s root@%s' % (testrecipe, testhost))
+ self.assertIn(' %s' % testfile, result.output)
+ # Boot the image
+ console = pexpect.spawn('runqemu %s %s qemuparams="-snapshot" nographic' % (machine, testimage))
+ console.expect("login:", timeout=120)
+ # Now really test deploy-target
+ result = runCmd('devtool deploy-target -c %s root@%s' % (testrecipe, testhost))
+ result = runCmd('ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@%s %s' % (testhost, testcommand))
+ # Test undeploy-target
+ result = runCmd('devtool undeploy-target -c %s root@%s' % (testrecipe, testhost))
+ result = runCmd('ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@%s %s' % (testhost, testcommand), ignore_status=True)
+ self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have')
+ console.close()
diff --git a/meta/lib/oeqa/selftest/lic-checksum.py b/meta/lib/oeqa/selftest/lic-checksum.py
new file mode 100644
index 0000000000..92fc3a84f1
--- /dev/null
+++ b/meta/lib/oeqa/selftest/lic-checksum.py
@@ -0,0 +1,29 @@
+import os
+import tempfile
+
+from oeqa.selftest.base import oeSelfTest
+from oeqa.utils.commands import bitbake
+from oeqa.utils import CommandError
+
+class LicenseTests(oeSelfTest):
+
+ # Verify that changing a license file that has an absolute path causes
+ # the license qa to fail due to a mismatched md5sum.
+ def test_nonmatching_checksum(self):
+ bitbake_cmd = '-c configure emptytest'
+ error_msg = 'ERROR: 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)
+ result = bitbake(bitbake_cmd)
+
+ with open(lic_path, "w") as f:
+ f.write("data")
+
+ result = bitbake(bitbake_cmd, ignore_status=True)
+ if error_msg not in result.output:
+ raise AssertionError(result.output)
diff --git a/meta/lib/oeqa/selftest/pkgdata.py b/meta/lib/oeqa/selftest/pkgdata.py
new file mode 100644
index 0000000000..34eea468e8
--- /dev/null
+++ b/meta/lib/oeqa/selftest/pkgdata.py
@@ -0,0 +1,218 @@
+import unittest
+import os
+import tempfile
+import logging
+import fnmatch
+
+import oeqa.utils.ftools as ftools
+from oeqa.selftest.base import oeSelfTest
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var
+from oeqa.utils.decorators import testcase
+
+class OePkgdataUtilTests(oeSelfTest):
+
+ @classmethod
+ def setUpClass(cls):
+ # Ensure we have the right data in pkgdata
+ logger = logging.getLogger("selftest")
+ logger.info('Running bitbake to generate pkgdata')
+ bitbake('glibc busybox zlib bash')
+
+ def test_lookup_pkg(self):
+ # Forward tests
+ result = runCmd('oe-pkgdata-util lookup-pkg "glibc busybox"')
+ self.assertEqual(result.output, 'libc6\nbusybox')
+ result = runCmd('oe-pkgdata-util lookup-pkg zlib-dev')
+ self.assertEqual(result.output, 'libz-dev')
+ result = runCmd('oe-pkgdata-util lookup-pkg nonexistentpkg', ignore_status=True)
+ self.assertEqual(result.status, 1)
+ self.assertEqual(result.output, 'ERROR: The following packages could not be found: nonexistentpkg')
+ # Reverse tests
+ result = runCmd('oe-pkgdata-util lookup-pkg -r "libc6 busybox"')
+ self.assertEqual(result.output, 'glibc\nbusybox')
+ result = runCmd('oe-pkgdata-util lookup-pkg -r libz-dev')
+ self.assertEqual(result.output, 'zlib-dev')
+ result = runCmd('oe-pkgdata-util lookup-pkg -r nonexistentpkg', ignore_status=True)
+ self.assertEqual(result.status, 1)
+ self.assertEqual(result.output, 'ERROR: The following packages could not be found: nonexistentpkg')
+
+ def test_read_value(self):
+ result = runCmd('oe-pkgdata-util read-value PN libz1')
+ self.assertEqual(result.output, 'zlib')
+ result = runCmd('oe-pkgdata-util read-value PKGSIZE bash')
+ pkgsize = int(result.output.strip())
+ self.assertGreater(pkgsize, 1)
+
+ def test_find_path(self):
+ result = runCmd('oe-pkgdata-util find-path /lib/libc.so.6')
+ self.assertEqual(result.output, 'glibc: /lib/libc.so.6')
+ result = runCmd('oe-pkgdata-util find-path /bin/bash')
+ self.assertEqual(result.output, 'bash: /bin/bash')
+ result = runCmd('oe-pkgdata-util find-path /not/exist', ignore_status=True)
+ self.assertEqual(result.status, 1)
+ self.assertEqual(result.output, 'ERROR: Unable to find any package producing path /not/exist')
+
+ def test_lookup_recipe(self):
+ result = runCmd('oe-pkgdata-util lookup-recipe "libc6-staticdev busybox"')
+ self.assertEqual(result.output, 'glibc\nbusybox')
+ result = runCmd('oe-pkgdata-util lookup-recipe libz-dbg')
+ self.assertEqual(result.output, 'zlib')
+ result = runCmd('oe-pkgdata-util lookup-recipe nonexistentpkg', ignore_status=True)
+ self.assertEqual(result.status, 1)
+ self.assertEqual(result.output, 'ERROR: The following packages could not be found: nonexistentpkg')
+
+ def test_list_pkgs(self):
+ # No arguments
+ result = runCmd('oe-pkgdata-util list-pkgs')
+ pkglist = result.output.split()
+ self.assertIn('glibc-utils', pkglist)
+ self.assertIn('zlib-dev', pkglist)
+ # No pkgspec, runtime
+ result = runCmd('oe-pkgdata-util list-pkgs -r')
+ pkglist = result.output.split()
+ self.assertIn('libc6-utils', pkglist)
+ self.assertIn('libz-dev', pkglist)
+ # With recipe specified
+ result = runCmd('oe-pkgdata-util list-pkgs -p zlib')
+ pkglist = sorted(result.output.split())
+ try:
+ pkglist.remove('zlib-ptest') # in case ptest is disabled
+ except ValueError:
+ pass
+ self.assertEqual(pkglist, ['zlib', 'zlib-dbg', 'zlib-dev', 'zlib-doc', 'zlib-staticdev'])
+ # With recipe specified, runtime
+ result = runCmd('oe-pkgdata-util list-pkgs -p zlib -r')
+ pkglist = sorted(result.output.split())
+ try:
+ pkglist.remove('libz-ptest') # in case ptest is disabled
+ except ValueError:
+ pass
+ self.assertEqual(pkglist, ['libz-dbg', 'libz-dev', 'libz-doc', 'libz-staticdev', 'libz1'])
+ # With recipe specified and unpackaged
+ result = runCmd('oe-pkgdata-util list-pkgs -p zlib -u')
+ pkglist = sorted(result.output.split())
+ self.assertIn('zlib-locale', pkglist)
+ # With recipe specified and unpackaged, runtime
+ result = runCmd('oe-pkgdata-util list-pkgs -p zlib -u -r')
+ pkglist = sorted(result.output.split())
+ self.assertIn('libz-locale', pkglist)
+ # With recipe specified and pkgspec
+ result = runCmd('oe-pkgdata-util list-pkgs -p zlib "*-d*"')
+ pkglist = sorted(result.output.split())
+ self.assertEqual(pkglist, ['zlib-dbg', 'zlib-dev', 'zlib-doc'])
+ # With recipe specified and pkgspec, runtime
+ result = runCmd('oe-pkgdata-util list-pkgs -p zlib -r "*-d*"')
+ pkglist = sorted(result.output.split())
+ self.assertEqual(pkglist, ['libz-dbg', 'libz-dev', 'libz-doc'])
+
+ def test_list_pkg_files(self):
+ def splitoutput(output):
+ files = {}
+ curpkg = None
+ for line in output.splitlines():
+ if line.startswith('\t'):
+ self.assertTrue(curpkg, 'Unexpected non-package line:\n%s' % line)
+ files[curpkg].append(line.strip())
+ else:
+ self.assertTrue(line.rstrip().endswith(':'), 'Invalid package line in output:\n%s' % line)
+ curpkg = line.split(':')[0]
+ files[curpkg] = []
+ return files
+ base_libdir = get_bb_var('base_libdir')
+ libdir = get_bb_var('libdir')
+ includedir = get_bb_var('includedir')
+ mandir = get_bb_var('mandir')
+ # 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())
+ self.assertIn('zlib-doc', files.keys())
+ 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())
+ self.assertIn('libz-dev', files.keys())
+ self.assertGreater(len(files['libz1']), 1)
+ libspec = os.path.join(base_libdir, 'libz.so.1.*')
+ found = False
+ for fileitem in files['libz1']:
+ if fnmatch.fnmatchcase(fileitem, libspec):
+ found = True
+ break
+ self.assertTrue(found, 'Could not find zlib library file %s in libz1 package file list: %s' % (libspec, files['libz1']))
+ self.assertIn(os.path.join(includedir, 'zlib.h'), files['libz-dev'])
+ # Test recipe
+ result = runCmd('oe-pkgdata-util list-pkg-files -p zlib')
+ files = splitoutput(result.output)
+ self.assertIn('zlib-dbg', files.keys())
+ self.assertIn('zlib-doc', files.keys())
+ self.assertIn('zlib-dev', files.keys())
+ self.assertIn('zlib-staticdev', files.keys())
+ self.assertIn('zlib', files.keys())
+ self.assertNotIn('zlib-locale', files.keys())
+ # (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'])
+ self.assertIn(os.path.join(libdir, 'libz.a'), files['zlib-staticdev'])
+ # Test recipe, runtime
+ result = runCmd('oe-pkgdata-util list-pkg-files -p zlib -r')
+ files = splitoutput(result.output)
+ self.assertIn('libz-dbg', files.keys())
+ self.assertIn('libz-doc', files.keys())
+ self.assertIn('libz-dev', files.keys())
+ self.assertIn('libz-staticdev', files.keys())
+ self.assertIn('libz1', files.keys())
+ self.assertNotIn('libz-locale', files.keys())
+ 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())
+ self.assertIn('zlib-doc', files.keys())
+ self.assertIn('zlib-dev', files.keys())
+ self.assertIn('zlib-staticdev', files.keys())
+ self.assertIn('zlib', files.keys())
+ self.assertIn('zlib-locale', files.keys()) # 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())
+ self.assertIn('libz-doc', files.keys())
+ self.assertIn('libz-dev', files.keys())
+ self.assertIn('libz-staticdev', files.keys())
+ self.assertIn('libz1', files.keys())
+ self.assertIn('libz-locale', files.keys()) # 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'])
+
+ def test_glob(self):
+ tempdir = tempfile.mkdtemp(prefix='pkgdataqa')
+ self.track_for_cleanup(tempdir)
+ pkglistfile = os.path.join(tempdir, 'pkglist')
+ with open(pkglistfile, 'w') as f:
+ f.write('libc6\n')
+ f.write('libz1\n')
+ f.write('busybox\n')
+ result = runCmd('oe-pkgdata-util glob %s "*-dev"' % pkglistfile)
+ desiredresult = ['libc6-dev', 'libz-dev', 'busybox-dev']
+ self.assertEqual(sorted(result.output.split()), sorted(desiredresult))
+ # The following should not error (because when we use this during rootfs construction, sometimes the complementary package won't exist)
+ result = runCmd('oe-pkgdata-util glob %s "*-nonexistent"' % pkglistfile)
+ self.assertEqual(result.output, '')
+ # Test exclude option
+ result = runCmd('oe-pkgdata-util glob %s "*-dev *-dbg" -x "^libz"' % pkglistfile)
+ resultlist = result.output.split()
+ self.assertNotIn('libz-dev', resultlist)
+ self.assertNotIn('libz-dbg', resultlist)
+
+ def test_specify_pkgdatadir(self):
+ result = runCmd('oe-pkgdata-util -p %s lookup-pkg glibc' % get_bb_var('PKGDATA_DIR'))
+ self.assertEqual(result.output, 'libc6')
diff --git a/meta/lib/oeqa/targetcontrol.py b/meta/lib/oeqa/targetcontrol.py
index cc582dd1ad..1f4770f94e 100644
--- a/meta/lib/oeqa/targetcontrol.py
+++ b/meta/lib/oeqa/targetcontrol.py
@@ -110,7 +110,7 @@ class BaseTarget(object):
class QemuTarget(BaseTarget):
- supported_image_fstypes = ['ext3']
+ supported_image_fstypes = ['ext3', 'ext4']
def __init__(self, d):
diff --git a/meta/lib/oeqa/utils/commands.py b/meta/lib/oeqa/utils/commands.py
index 802bc2f208..e8a467f4cd 100644
--- a/meta/lib/oeqa/utils/commands.py
+++ b/meta/lib/oeqa/utils/commands.py
@@ -110,11 +110,11 @@ def runCmd(command, ignore_status=False, timeout=None, assert_error=True, **opti
def bitbake(command, ignore_status=False, timeout=None, postconfig=None, **options):
if postconfig:
- postconfig_file = os.path.join(os.environ.get('BUILDDIR'), 'oeqa-post.conf')
- ftools.write_file(postconfig_file, postconfig)
- extra_args = "-R %s" % postconfig_file
+ postconfig_file = os.path.join(os.environ.get('BUILDDIR'), 'oeqa-post.conf')
+ ftools.write_file(postconfig_file, postconfig)
+ extra_args = "-R %s" % postconfig_file
else:
- extra_args = ""
+ extra_args = ""
if isinstance(command, basestring):
cmd = "bitbake " + extra_args + " " + command
@@ -122,7 +122,7 @@ def bitbake(command, ignore_status=False, timeout=None, postconfig=None, **optio
cmd = [ "bitbake" ] + [a for a in (command + extra_args.split(" ")) if a not in [""]]
try:
- return runCmd(cmd, ignore_status, timeout, **options)
+ return runCmd(cmd, ignore_status, timeout, **options)
finally:
if postconfig:
os.remove(postconfig_file)
@@ -137,11 +137,18 @@ def get_bb_env(target=None, postconfig=None):
def get_bb_var(var, target=None, postconfig=None):
val = None
bbenv = get_bb_env(target, postconfig=postconfig)
+ lastline = None
for line in bbenv.splitlines():
- if line.startswith(var + "="):
+ if line.startswith(var + "=") or line.startswith("export " + var + "="):
val = line.split('=')[1]
- val = val.replace('\"','')
+ val = val.strip('\"')
break
+ elif line.startswith("unset " + var):
+ # Handle [unexport] variables
+ if lastline.startswith('# "'):
+ val = lastline.split('\"')[1]
+ break
+ lastline = line
return val
def get_test_layer():
diff --git a/meta/lib/oeqa/utils/decorators.py b/meta/lib/oeqa/utils/decorators.py
index 7f845dbb4b..ff5f278bc1 100644
--- a/meta/lib/oeqa/utils/decorators.py
+++ b/meta/lib/oeqa/utils/decorators.py
@@ -18,14 +18,21 @@ class getResults(object):
upperf = sys._current_frames().values()[0]
while (upperf.f_globals['__name__'] != 'unittest.case'):
upperf = upperf.f_back
- self.faillist = [ seq[0]._testMethodName for seq in upperf.f_locals['result'].failures ]
- self.errorlist = [ seq[0]._testMethodName for seq in upperf.f_locals['result'].errors ]
- #ignore the _ErrorHolder objects from the skipped tests list
- self.skiplist = []
- for seq in upperf.f_locals['result'].skipped:
- try:
- self.skiplist.append(seq[0]._testMethodName)
- except: pass
+
+ def handleList(items):
+ ret = []
+ # items is a list of tuples, (test, failure) or (_ErrorHandler(), Exception())
+ for i in items:
+ s = i[0].id()
+ #Handle the _ErrorHolder objects from skipModule failures
+ if "setUpModule (" in s:
+ ret.append(s.replace("setUpModule (", "").replace(")",""))
+ else:
+ ret.append(s)
+ return ret
+ self.faillist = handleList(upperf.f_locals['result'].failures)
+ self.errorlist = handleList(upperf.f_locals['result'].errors)
+ self.skiplist = handleList(upperf.f_locals['result'].skipped)
def getFailList(self):
return self.faillist
@@ -86,14 +93,15 @@ class testcase(object):
self.test_case = test_case
def __call__(self, func):
- def wrapped_f(*args):
- return func(*args)
- wrapped_f.test_case = self.test_case
- return wrapped_f
+ def wrapped_f(*args):
+ return func(*args)
+ wrapped_f.test_case = self.test_case
+ wrapped_f.__name__ = func.__name__
+ return wrapped_f
class NoParsingFilter(logging.Filter):
def filter(self, record):
- return record.levelno == 100
+ return record.levelno == 100
def LogResults(original_class):
orig_method = original_class.run
@@ -101,51 +109,51 @@ def LogResults(original_class):
#rewrite the run method of unittest.TestCase to add testcase logging
def run(self, result, *args, **kws):
orig_method(self, result, *args, **kws)
- passed = True
- testMethod = getattr(self, self._testMethodName)
-
- #if test case is decorated then use it's number, else use it's name
- try:
- test_case = testMethod.test_case
- except AttributeError:
- test_case = self._testMethodName
-
- #create custom logging level for filtering.
- custom_log_level = 100
- logging.addLevelName(custom_log_level, 'RESULTS')
- caller = os.path.basename(sys.argv[0])
-
- def results(self, message, *args, **kws):
- if self.isEnabledFor(custom_log_level):
- self.log(custom_log_level, message, *args, **kws)
- logging.Logger.results = results
-
- logging.basicConfig(filename=os.path.join(os.getcwd(),'results-'+caller+'.log'),
+ passed = True
+ testMethod = getattr(self, self._testMethodName)
+
+ #if test case is decorated then use it's number, else use it's name
+ try:
+ test_case = testMethod.test_case
+ except AttributeError:
+ test_case = self._testMethodName
+
+ #create custom logging level for filtering.
+ custom_log_level = 100
+ logging.addLevelName(custom_log_level, 'RESULTS')
+ caller = os.path.basename(sys.argv[0])
+
+ def results(self, message, *args, **kws):
+ if self.isEnabledFor(custom_log_level):
+ self.log(custom_log_level, message, *args, **kws)
+ logging.Logger.results = results
+
+ logging.basicConfig(filename=os.path.join(os.getcwd(),'results-'+caller+'.log'),
filemode='w',
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%H:%M:%S',
level=custom_log_level)
- for handler in logging.root.handlers:
- handler.addFilter(NoParsingFilter())
- local_log = logging.getLogger(caller)
+ for handler in logging.root.handlers:
+ handler.addFilter(NoParsingFilter())
+ local_log = logging.getLogger(caller)
- #check status of tests and record it
+ #check status of tests and record it
for (name, msg) in result.errors:
- if self._testMethodName == str(name).split(' ')[0]:
- local_log.results("Testcase "+str(test_case)+": ERROR")
- local_log.results("Testcase "+str(test_case)+":\n"+msg)
- passed = False
+ if self._testMethodName == str(name).split(' ')[0]:
+ local_log.results("Testcase "+str(test_case)+": ERROR")
+ local_log.results("Testcase "+str(test_case)+":\n"+msg)
+ passed = False
for (name, msg) in result.failures:
- if self._testMethodName == str(name).split(' ')[0]:
- local_log.results("Testcase "+str(test_case)+": FAILED")
- local_log.results("Testcase "+str(test_case)+":\n"+msg)
- passed = False
+ if self._testMethodName == str(name).split(' ')[0]:
+ local_log.results("Testcase "+str(test_case)+": FAILED")
+ local_log.results("Testcase "+str(test_case)+":\n"+msg)
+ passed = False
for (name, msg) in result.skipped:
- if self._testMethodName == str(name).split(' ')[0]:
- local_log.results("Testcase "+str(test_case)+": SKIPPED")
- passed = False
- if passed:
- local_log.results("Testcase "+str(test_case)+": PASSED")
+ if self._testMethodName == str(name).split(' ')[0]:
+ local_log.results("Testcase "+str(test_case)+": SKIPPED")
+ passed = False
+ if passed:
+ local_log.results("Testcase "+str(test_case)+": PASSED")
original_class.run = run
return original_class