diff options
| author | Stefan Stanacar <stefanx.stanacar@intel.com> | 2013-11-27 19:08:50 +0200 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2013-12-03 17:45:50 +0000 |
| commit | 645dd61cd21b2b1c9d9c927447c68841685a1adf (patch) | |
| tree | 9dd603414037879c7092a24e959fd8171f619720 /meta/lib/oeqa/utils/commands.py | |
| parent | 1fa51bf949cf6e789b942c1b4c0321fd68a39b10 (diff) | |
| download | poky-645dd61cd21b2b1c9d9c927447c68841685a1adf.tar.gz | |
scripts/oe-selftest: script to run builds as unittest against bitbake or various scripts
The purpose of oe-selftest is to run unittest modules added from meta/lib/oeqa/selftest,
which are tests against bitbake tools.
Right now the script it's useful for simple tests like:
- "bitbake --someoption, change some metadata, bitbake X, check something" type scenarios (PR service, error output, etc)
- or "bitbake-layers <...>" type scripts and yocto-bsp tools.
This commit also adds some helper modules that the tests will use and a base class.
Also, most of the tests will have a dependency on a meta-selftest layer
which contains specially modified recipes/bbappends/include files for the purpose of the tests.
The tests themselves will usually write to ".inc" files from the layer or in conf/selftest.inc
(which is added as an include in local.conf at the start and removed at the end)
It's a simple matter or sourcing the enviroment, adding the meta-selftest layer to bblayers.conf
and running: oe-selftest to get some results. It would finish faster if at least a core-image-minimal
was built before.
[ YOCTO #4740 ]
(From OE-Core rev: 41a4f8fb005328d3a631a9036ceb6dcf75754410)
Signed-off-by: Stefan Stanacar <stefanx.stanacar@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/lib/oeqa/utils/commands.py')
| -rw-r--r-- | meta/lib/oeqa/utils/commands.py | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/meta/lib/oeqa/utils/commands.py b/meta/lib/oeqa/utils/commands.py new file mode 100644 index 0000000000..9b42620610 --- /dev/null +++ b/meta/lib/oeqa/utils/commands.py | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | # Copyright (c) 2013 Intel Corporation | ||
| 2 | # | ||
| 3 | # Released under the MIT license (see COPYING.MIT) | ||
| 4 | |||
| 5 | # DESCRIPTION | ||
| 6 | # This module is mainly used by scripts/oe-selftest and modules under meta/oeqa/selftest | ||
| 7 | # It provides a class and methods for running commands on the host in a convienent way for tests. | ||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | import os | ||
| 12 | import sys | ||
| 13 | import signal | ||
| 14 | import subprocess | ||
| 15 | import threading | ||
| 16 | import logging | ||
| 17 | |||
| 18 | class Command(object): | ||
| 19 | def __init__(self, command, bg=False, timeout=None, data=None, **options): | ||
| 20 | |||
| 21 | self.defaultopts = { | ||
| 22 | "stdout": subprocess.PIPE, | ||
| 23 | "stderr": subprocess.STDOUT, | ||
| 24 | "stdin": None, | ||
| 25 | "shell": False, | ||
| 26 | "bufsize": -1, | ||
| 27 | } | ||
| 28 | |||
| 29 | self.cmd = command | ||
| 30 | self.bg = bg | ||
| 31 | self.timeout = timeout | ||
| 32 | self.data = data | ||
| 33 | |||
| 34 | self.options = dict(self.defaultopts) | ||
| 35 | if isinstance(self.cmd, basestring): | ||
| 36 | self.options["shell"] = True | ||
| 37 | if self.data: | ||
| 38 | self.options['stdin'] = subprocess.PIPE | ||
| 39 | self.options.update(options) | ||
| 40 | |||
| 41 | self.status = None | ||
| 42 | self.output = None | ||
| 43 | self.error = None | ||
| 44 | self.thread = None | ||
| 45 | |||
| 46 | self.log = logging.getLogger("utils.commands") | ||
| 47 | |||
| 48 | def run(self): | ||
| 49 | self.process = subprocess.Popen(self.cmd, **self.options) | ||
| 50 | |||
| 51 | def commThread(): | ||
| 52 | self.output, self.error = self.process.communicate(self.data) | ||
| 53 | |||
| 54 | self.thread = threading.Thread(target=commThread) | ||
| 55 | self.thread.start() | ||
| 56 | |||
| 57 | self.log.debug("Running command '%s'" % self.cmd) | ||
| 58 | |||
| 59 | if not self.bg: | ||
| 60 | self.thread.join(self.timeout) | ||
| 61 | self.stop() | ||
| 62 | |||
| 63 | def stop(self): | ||
| 64 | if self.thread.isAlive(): | ||
| 65 | self.process.terminate() | ||
| 66 | # let's give it more time to terminate gracefully before killing it | ||
| 67 | self.thread.join(5) | ||
| 68 | if self.thread.isAlive(): | ||
| 69 | self.process.kill() | ||
| 70 | self.thread.join() | ||
| 71 | |||
| 72 | self.output = self.output.rstrip() | ||
| 73 | self.status = self.process.poll() | ||
| 74 | |||
| 75 | self.log.debug("Command '%s' returned %d as exit code." % (self.cmd, self.status)) | ||
| 76 | # logging the complete output is insane | ||
| 77 | # bitbake -e output is really big | ||
| 78 | # and makes the log file useless | ||
| 79 | if self.status: | ||
| 80 | lout = "\n".join(self.output.splitlines()[-20:]) | ||
| 81 | self.log.debug("Last 20 lines:\n%s" % lout) | ||
| 82 | |||
| 83 | |||
| 84 | class Result(object): | ||
| 85 | pass | ||
| 86 | |||
| 87 | def runCmd(command, ignore_status=False, timeout=None, **options): | ||
| 88 | |||
| 89 | result = Result() | ||
| 90 | |||
| 91 | cmd = Command(command, timeout=timeout, **options) | ||
| 92 | cmd.run() | ||
| 93 | |||
| 94 | result.command = command | ||
| 95 | result.status = cmd.status | ||
| 96 | result.output = cmd.output | ||
| 97 | result.pid = cmd.process.pid | ||
| 98 | |||
| 99 | if result.status and not ignore_status: | ||
| 100 | raise AssertionError("Command '%s' returned non-zero exit status %d:\n%s" % (command, result.status, result.output)) | ||
| 101 | |||
| 102 | return result | ||
| 103 | |||
| 104 | |||
| 105 | def bitbake(command, ignore_status=False, timeout=None, **options): | ||
| 106 | if isinstance(command, basestring): | ||
| 107 | cmd = "bitbake " + command | ||
| 108 | else: | ||
| 109 | cmd = [ "bitbake" ] + command | ||
| 110 | |||
| 111 | return runCmd(cmd, ignore_status, timeout, **options) | ||
| 112 | |||
| 113 | |||
| 114 | def get_bb_env(target=None): | ||
| 115 | if target: | ||
| 116 | return runCmd("bitbake -e %s" % target).output | ||
| 117 | else: | ||
| 118 | return runCmd("bitbake -e").output | ||
| 119 | |||
| 120 | def get_bb_var(var, target=None): | ||
| 121 | val = None | ||
| 122 | bbenv = get_bb_env(target) | ||
| 123 | for line in bbenv.splitlines(): | ||
| 124 | if line.startswith(var + "="): | ||
| 125 | val = line.split('=')[1] | ||
| 126 | val = val.replace('\"','') | ||
| 127 | break | ||
| 128 | return val | ||
| 129 | |||
| 130 | def get_test_layer(): | ||
| 131 | layers = get_bb_var("BBLAYERS").split() | ||
| 132 | testlayer = None | ||
| 133 | for l in layers: | ||
| 134 | if "/meta-selftest" in l and os.path.isdir(l): | ||
| 135 | testlayer = l | ||
| 136 | break | ||
| 137 | return testlayer | ||
