summaryrefslogtreecommitdiffstats
path: root/meta/lib/oeqa/utils
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib/oeqa/utils')
-rw-r--r--meta/lib/oeqa/utils/__init__.py8
-rw-r--r--meta/lib/oeqa/utils/commands.py40
-rw-r--r--meta/lib/oeqa/utils/gitarchive.py6
-rw-r--r--meta/lib/oeqa/utils/metadata.py5
-rw-r--r--meta/lib/oeqa/utils/postactions.py72
-rw-r--r--meta/lib/oeqa/utils/qemurunner.py40
-rw-r--r--meta/lib/oeqa/utils/sshcontrol.py6
-rw-r--r--meta/lib/oeqa/utils/subprocesstweak.py13
-rw-r--r--meta/lib/oeqa/utils/testexport.py10
9 files changed, 127 insertions, 73 deletions
diff --git a/meta/lib/oeqa/utils/__init__.py b/meta/lib/oeqa/utils/__init__.py
index 53bdcbf266..e03f7e33bb 100644
--- a/meta/lib/oeqa/utils/__init__.py
+++ b/meta/lib/oeqa/utils/__init__.py
@@ -96,4 +96,10 @@ def get_json_result_dir(d):
96 custom_json_result_dir = d.getVar("OEQA_JSON_RESULT_DIR") 96 custom_json_result_dir = d.getVar("OEQA_JSON_RESULT_DIR")
97 if custom_json_result_dir: 97 if custom_json_result_dir:
98 json_result_dir = custom_json_result_dir 98 json_result_dir = custom_json_result_dir
99 return json_result_dir \ No newline at end of file 99 return json_result_dir
100
101def get_artefact_dir(d):
102 custom_json_result_dir = d.getVar("OEQA_ARTEFACT_DIR")
103 if custom_json_result_dir:
104 return custom_json_result_dir
105 return os.path.join(d.getVar("LOG_DIR"), 'oeqa-artefacts')
diff --git a/meta/lib/oeqa/utils/commands.py b/meta/lib/oeqa/utils/commands.py
index 575e380017..b60a6e6c38 100644
--- a/meta/lib/oeqa/utils/commands.py
+++ b/meta/lib/oeqa/utils/commands.py
@@ -203,6 +203,8 @@ def runCmd(command, ignore_status=False, timeout=None, assert_error=True, sync=T
203 203
204 if result.status and not ignore_status: 204 if result.status and not ignore_status:
205 exc_output = result.output 205 exc_output = result.output
206 if result.error:
207 exc_output = exc_output + result.error
206 if limit_exc_output > 0: 208 if limit_exc_output > 0:
207 split = result.output.splitlines() 209 split = result.output.splitlines()
208 if len(split) > limit_exc_output: 210 if len(split) > limit_exc_output:
@@ -283,7 +285,20 @@ def get_bb_vars(variables=None, target=None, postconfig=None):
283 return values 285 return values
284 286
285def get_bb_var(var, target=None, postconfig=None): 287def get_bb_var(var, target=None, postconfig=None):
286 return get_bb_vars([var], target, postconfig)[var] 288 if postconfig:
289 return bitbake("-e %s" % target or "", postconfig=postconfig).output
290 else:
291 # Fast-path for the non-postconfig case
292 cmd = ["bitbake-getvar", "--quiet", "--value", var]
293 if target:
294 cmd.extend(["--recipe", target])
295 try:
296 return subprocess.run(cmd, check=True, text=True, stdout=subprocess.PIPE).stdout.strip()
297 except subprocess.CalledProcessError as e:
298 # We need to return None not the empty string if the variable hasn't been set.
299 if e.returncode == 1:
300 return None
301 raise
287 302
288def get_test_layer(bblayers=None): 303def get_test_layer(bblayers=None):
289 if bblayers is None: 304 if bblayers is None:
@@ -312,9 +327,26 @@ def create_temp_layer(templayerdir, templayername, priority=999, recipepathspec=
312 f.write('LAYERSERIES_COMPAT_%s = "%s"\n' % (templayername, corenames)) 327 f.write('LAYERSERIES_COMPAT_%s = "%s"\n' % (templayername, corenames))
313 328
314@contextlib.contextmanager 329@contextlib.contextmanager
315def runqemu(pn, ssh=True, runqemuparams='', image_fstype=None, launch_cmd=None, qemuparams=None, overrides={}, discard_writes=True): 330def runqemu(pn, ssh=True, runqemuparams='', image_fstype=None, launch_cmd=None, qemuparams=None, overrides={}, boot_patterns = {}, discard_writes=True):
316 """ 331 """
317 launch_cmd means directly run the command, don't need set rootfs or env vars. 332 Starts a context manager for a 'oeqa.targetcontrol.QemuTarget' resource.
333 The underlying Qemu will be booted into a shell when the generator yields
334 and stopped when the 'with' block exits.
335
336 Usage:
337
338 with runqemu('core-image-minimal') as qemu:
339 qemu.run_serial('cat /proc/cpuinfo')
340
341 Args:
342 pn (str): (image) recipe to run on
343 ssh (boolean): whether or not to enable SSH (network access)
344 runqemuparams (str): space-separated list of params to pass to 'runqemu' script (like 'nographics', 'ovmf', etc.)
345 image_fstype (str): IMAGE_FSTYPE to use
346 launch_cmd (str): directly run this command and bypass automatic runqemu parameter generation
347 overrides (dict): dict of "'<bitbake-variable>': value" pairs that allows overriding bitbake variables
348 boot_patterns (dict): dict of "'<pattern-name>': value" pairs to override default boot patterns, e.g. when not booting Linux
349 discard_writes (boolean): enables qemu -snapshot feature to prevent modifying original image
318 """ 350 """
319 351
320 import bb.tinfoil 352 import bb.tinfoil
@@ -345,7 +377,7 @@ def runqemu(pn, ssh=True, runqemuparams='', image_fstype=None, launch_cmd=None,
345 377
346 logdir = recipedata.getVar("TEST_LOG_DIR") 378 logdir = recipedata.getVar("TEST_LOG_DIR")
347 379
348 qemu = oeqa.targetcontrol.QemuTarget(recipedata, targetlogger, image_fstype) 380 qemu = oeqa.targetcontrol.QemuTarget(recipedata, targetlogger, image_fstype, boot_patterns=boot_patterns)
349 finally: 381 finally:
350 # We need to shut down tinfoil early here in case we actually want 382 # We need to shut down tinfoil early here in case we actually want
351 # to run tinfoil-using utilities with the running QEMU instance. 383 # to run tinfoil-using utilities with the running QEMU instance.
diff --git a/meta/lib/oeqa/utils/gitarchive.py b/meta/lib/oeqa/utils/gitarchive.py
index 10cb267dfa..7e1d505748 100644
--- a/meta/lib/oeqa/utils/gitarchive.py
+++ b/meta/lib/oeqa/utils/gitarchive.py
@@ -67,7 +67,7 @@ def git_commit_data(repo, data_dir, branch, message, exclude, notes, log):
67 67
68 # Remove files that are excluded 68 # Remove files that are excluded
69 if exclude: 69 if exclude:
70 repo.run_cmd(['rm', '--cached'] + [f for f in exclude], env_update) 70 repo.run_cmd(['rm', '--cached', '--ignore-unmatch'] + [f for f in exclude], env_update)
71 71
72 tree = repo.run_cmd('write-tree', env_update) 72 tree = repo.run_cmd('write-tree', env_update)
73 73
@@ -146,7 +146,7 @@ def expand_tag_strings(repo, name_pattern, msg_subj_pattern, msg_body_pattern,
146 keyws['tag_number'] = '{tag_number}' 146 keyws['tag_number'] = '{tag_number}'
147 tag_re = format_str(name_pattern, keyws) 147 tag_re = format_str(name_pattern, keyws)
148 # Replace parentheses for proper regex matching 148 # Replace parentheses for proper regex matching
149 tag_re = tag_re.replace('(', '\(').replace(')', '\)') + '$' 149 tag_re = tag_re.replace('(', r'\(').replace(')', r'\)') + '$'
150 # Inject regex group pattern for 'tag_number' 150 # Inject regex group pattern for 'tag_number'
151 tag_re = tag_re.format(tag_number='(?P<tag_number>[0-9]{1,5})') 151 tag_re = tag_re.format(tag_number='(?P<tag_number>[0-9]{1,5})')
152 152
@@ -202,6 +202,8 @@ def gitarchive(data_dir, git_dir, no_create, bare, commit_msg_subject, commit_ms
202 log.info("Pushing data to remote") 202 log.info("Pushing data to remote")
203 data_repo.run_cmd(cmd) 203 data_repo.run_cmd(cmd)
204 204
205 return tag_name
206
205# Container class for tester revisions 207# Container class for tester revisions
206TestedRev = namedtuple('TestedRev', 'commit commit_number tags') 208TestedRev = namedtuple('TestedRev', 'commit commit_number tags')
207 209
diff --git a/meta/lib/oeqa/utils/metadata.py b/meta/lib/oeqa/utils/metadata.py
index 15ec190c4a..b320df67e0 100644
--- a/meta/lib/oeqa/utils/metadata.py
+++ b/meta/lib/oeqa/utils/metadata.py
@@ -76,6 +76,10 @@ def git_rev_info(path):
76 info['commit_count'] = int(subprocess.check_output(["git", "rev-list", "--count", "HEAD"], cwd=path).decode('utf-8').strip()) 76 info['commit_count'] = int(subprocess.check_output(["git", "rev-list", "--count", "HEAD"], cwd=path).decode('utf-8').strip())
77 except subprocess.CalledProcessError: 77 except subprocess.CalledProcessError:
78 pass 78 pass
79 try:
80 info['commit_time'] = int(subprocess.check_output(["git", "show", "--no-patch", "--format=%ct", "HEAD"], cwd=path).decode('utf-8').strip())
81 except subprocess.CalledProcessError:
82 pass
79 return info 83 return info
80 try: 84 try:
81 repo = Repo(path, search_parent_directories=True) 85 repo = Repo(path, search_parent_directories=True)
@@ -83,6 +87,7 @@ def git_rev_info(path):
83 return info 87 return info
84 info['commit'] = repo.head.commit.hexsha 88 info['commit'] = repo.head.commit.hexsha
85 info['commit_count'] = repo.head.commit.count() 89 info['commit_count'] = repo.head.commit.count()
90 info['commit_time'] = repo.head.commit.committed_date
86 try: 91 try:
87 info['branch'] = repo.active_branch.name 92 info['branch'] = repo.active_branch.name
88 except TypeError: 93 except TypeError:
diff --git a/meta/lib/oeqa/utils/postactions.py b/meta/lib/oeqa/utils/postactions.py
index 8104400ac2..c69481db6c 100644
--- a/meta/lib/oeqa/utils/postactions.py
+++ b/meta/lib/oeqa/utils/postactions.py
@@ -7,35 +7,32 @@
7# Run a set of actions after tests. The runner provides internal data 7# Run a set of actions after tests. The runner provides internal data
8# dictionary as well as test context to any action to run. 8# dictionary as well as test context to any action to run.
9 9
10from oeqa.utils import get_json_result_dir 10import datetime
11 11import io
12def create_artifacts_directory(d, tc): 12import os
13 import shutil 13import stat
14 14import subprocess
15 local_artifacts_dir = os.path.join(get_json_result_dir(d), "artifacts") 15import tempfile
16 if os.path.isdir(local_artifacts_dir): 16from oeqa.utils import get_artefact_dir
17 shutil.rmtree(local_artifacts_dir)
18
19 os.makedirs(local_artifacts_dir)
20 17
21################################################################## 18##################################################################
22# Host/target statistics 19# Host/target statistics
23################################################################## 20##################################################################
24 21
25def get_target_disk_usage(d, tc): 22def get_target_disk_usage(d, tc, artifacts_list, outputdir):
26 output_file = os.path.join(get_json_result_dir(d), "artifacts", "target_disk_usage.txt") 23 output_file = os.path.join(outputdir, "target_disk_usage.txt")
27 try: 24 try:
28 (status, output) = tc.target.run('df -hl') 25 (status, output) = tc.target.run('df -h')
29 with open(output_file, 'w') as f: 26 with open(output_file, 'w') as f:
30 f.write(output) 27 f.write(output)
31 f.write("\n") 28 f.write("\n")
32 except Exception as e: 29 except Exception as e:
33 bb.warn(f"Can not get target disk usage: {e}") 30 bb.warn(f"Can not get target disk usage: {e}")
34 31
35def get_host_disk_usage(d, tc): 32def get_host_disk_usage(d, tc, artifacts_list, outputdir):
36 import subprocess 33 import subprocess
37 34
38 output_file = os.path.join(get_json_result_dir(d), "artifacts", "host_disk_usage.txt") 35 output_file = os.path.join(outputdir, "host_disk_usage.txt")
39 try: 36 try:
40 with open(output_file, 'w') as f: 37 with open(output_file, 'w') as f:
41 output = subprocess.run(['df', '-hl'], check=True, text=True, stdout=f, env={}) 38 output = subprocess.run(['df', '-hl'], check=True, text=True, stdout=f, env={})
@@ -61,25 +58,22 @@ def get_artifacts_list(target, raw_list):
61 58
62 return result 59 return result
63 60
64def retrieve_test_artifacts(target, artifacts_list, target_dir): 61def list_and_fetch_failed_tests_artifacts(d, tc, artifacts_list, outputdir):
65 local_artifacts_dir = os.path.join(target_dir, "artifacts") 62 artifacts_list = get_artifacts_list(tc.target, artifacts_list)
66 for artifact_path in artifacts_list:
67 if not os.path.isabs(artifact_path):
68 bb.warn(f"{artifact_path} is not an absolute path")
69 continue
70 try:
71 dest_dir = os.path.join(local_artifacts_dir, os.path.dirname(artifact_path[1:]))
72 os.makedirs(dest_dir, exist_ok=True)
73 target.copyFrom(artifact_path, dest_dir)
74 except Exception as e:
75 bb.warn(f"Can not retrieve {artifact_path} from test target: {e}")
76
77def list_and_fetch_failed_tests_artifacts(d, tc):
78 artifacts_list = get_artifacts_list(tc.target, d.getVar("TESTIMAGE_FAILED_QA_ARTIFACTS"))
79 if not artifacts_list: 63 if not artifacts_list:
80 bb.warn("Could not load artifacts list, skip artifacts retrieval") 64 bb.warn("Could not load artifacts list, skip artifacts retrieval")
81 else: 65 return
82 retrieve_test_artifacts(tc.target, artifacts_list, get_json_result_dir(d)) 66 try:
67 # We need gnu tar for sparse files, not busybox
68 cmd = "tar --sparse -zcf - " + " ".join(artifacts_list)
69 (status, output) = tc.target.run(cmd, raw = True)
70 if status != 0 or not output:
71 raise Exception("Error while fetching compressed artifacts")
72 archive_name = os.path.join(outputdir, "tests_artifacts.tar.gz")
73 with open(archive_name, "wb") as f:
74 f.write(output)
75 except Exception as e:
76 bb.warn(f"Can not retrieve artifacts from test target: {e}")
83 77
84 78
85################################################################## 79##################################################################
@@ -87,12 +81,22 @@ def list_and_fetch_failed_tests_artifacts(d, tc):
87################################################################## 81##################################################################
88 82
89def run_failed_tests_post_actions(d, tc): 83def run_failed_tests_post_actions(d, tc):
84 artifacts = d.getVar("TESTIMAGE_FAILED_QA_ARTIFACTS")
85 # Allow all the code to be disabled by having no artifacts set, e.g. for systems with no ssh support
86 if not artifacts:
87 return
88
89 outputdir = get_artefact_dir(d)
90 os.makedirs(outputdir, exist_ok=True)
91 datestr = datetime.datetime.now().strftime('%Y%m%d')
92 outputdir = tempfile.mkdtemp(prefix='oeqa-target-artefacts-%s-' % datestr, dir=outputdir)
93 os.chmod(outputdir, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
94
90 post_actions=[ 95 post_actions=[
91 create_artifacts_directory,
92 list_and_fetch_failed_tests_artifacts, 96 list_and_fetch_failed_tests_artifacts,
93 get_target_disk_usage, 97 get_target_disk_usage,
94 get_host_disk_usage 98 get_host_disk_usage
95 ] 99 ]
96 100
97 for action in post_actions: 101 for action in post_actions:
98 action(d, tc) 102 action(d, tc, artifacts, outputdir)
diff --git a/meta/lib/oeqa/utils/qemurunner.py b/meta/lib/oeqa/utils/qemurunner.py
index cda43aad8c..c4db0cf038 100644
--- a/meta/lib/oeqa/utils/qemurunner.py
+++ b/meta/lib/oeqa/utils/qemurunner.py
@@ -30,6 +30,8 @@ control_range = list(range(0,32))+list(range(127,160))
30control_chars = [chr(x) for x in control_range 30control_chars = [chr(x) for x in control_range
31 if chr(x) not in string.printable] 31 if chr(x) not in string.printable]
32re_control_char = re.compile('[%s]' % re.escape("".join(control_chars))) 32re_control_char = re.compile('[%s]' % re.escape("".join(control_chars)))
33# Regex to remove the ANSI (color) control codes from console strings in order to match the text only
34re_vt100 = re.compile(r'(\x1b\[|\x9b)[^@-_a-z]*[@-_a-z]|\x1b[@-_a-z]')
33 35
34def getOutput(o): 36def getOutput(o):
35 import fcntl 37 import fcntl
@@ -101,7 +103,7 @@ class QemuRunner:
101 103
102 # Only override patterns that were set e.g. login user TESTIMAGE_BOOT_PATTERNS[send_login_user] = "webserver\n" 104 # Only override patterns that were set e.g. login user TESTIMAGE_BOOT_PATTERNS[send_login_user] = "webserver\n"
103 for pattern in accepted_patterns: 105 for pattern in accepted_patterns:
104 if not self.boot_patterns[pattern]: 106 if pattern not in self.boot_patterns or not self.boot_patterns[pattern]:
105 self.boot_patterns[pattern] = default_boot_patterns[pattern] 107 self.boot_patterns[pattern] = default_boot_patterns[pattern]
106 108
107 def create_socket(self): 109 def create_socket(self):
@@ -265,12 +267,15 @@ class QemuRunner:
265 self.monitorpipe = os.fdopen(w, "w") 267 self.monitorpipe = os.fdopen(w, "w")
266 else: 268 else:
267 # child process 269 # child process
268 os.setpgrp() 270 try:
269 os.close(w) 271 os.setpgrp()
270 r = os.fdopen(r) 272 os.close(w)
271 x = r.read() 273 r = os.fdopen(r)
272 os.killpg(os.getpgid(self.runqemu.pid), signal.SIGTERM) 274 x = r.read()
273 os._exit(0) 275 os.killpg(os.getpgid(self.runqemu.pid), signal.SIGTERM)
276 finally:
277 # We must exit under all circumstances
278 os._exit(0)
274 279
275 self.logger.debug("runqemu started, pid is %s" % self.runqemu.pid) 280 self.logger.debug("runqemu started, pid is %s" % self.runqemu.pid)
276 self.logger.debug("waiting at most %d seconds for qemu pid (%s)" % 281 self.logger.debug("waiting at most %d seconds for qemu pid (%s)" %
@@ -519,7 +524,6 @@ class QemuRunner:
519 except Exception as e: 524 except Exception as e:
520 self.logger.warning('Extra log data exception %s' % repr(e)) 525 self.logger.warning('Extra log data exception %s' % repr(e))
521 data = None 526 data = None
522 self.thread.serial_lock.release()
523 return False 527 return False
524 528
525 with self.thread.serial_lock: 529 with self.thread.serial_lock:
@@ -533,7 +537,7 @@ class QemuRunner:
533 self.logger.debug("Logged in as %s in serial console" % self.boot_patterns['send_login_user'].replace("\n", "")) 537 self.logger.debug("Logged in as %s in serial console" % self.boot_patterns['send_login_user'].replace("\n", ""))
534 if netconf: 538 if netconf:
535 # configure guest networking 539 # configure guest networking
536 cmd = "ifconfig eth0 %s netmask %s up\n" % (self.ip, self.netmask) 540 cmd = "ip addr add %s/%s dev eth0\nip link set dev eth0 up\n" % (self.ip, self.netmask)
537 output = self.run_serial(cmd, raw=True)[1] 541 output = self.run_serial(cmd, raw=True)[1]
538 if re.search(r"root@[a-zA-Z0-9\-]+:~#", output): 542 if re.search(r"root@[a-zA-Z0-9\-]+:~#", output):
539 self.logger.debug("configured ip address %s", self.ip) 543 self.logger.debug("configured ip address %s", self.ip)
@@ -681,7 +685,7 @@ class QemuRunner:
681 time.sleep(0.1) 685 time.sleep(0.1)
682 answer = self.server_socket.recv(1024) 686 answer = self.server_socket.recv(1024)
683 if answer: 687 if answer:
684 data += answer.decode('utf-8') 688 data += re_vt100.sub("", answer.decode('utf-8'))
685 # Search the prompt to stop 689 # Search the prompt to stop
686 if re.search(self.boot_patterns['search_cmd_finished'], data): 690 if re.search(self.boot_patterns['search_cmd_finished'], data):
687 break 691 break
@@ -745,8 +749,10 @@ class LoggingThread(threading.Thread):
745 def threadtarget(self): 749 def threadtarget(self):
746 try: 750 try:
747 self.eventloop() 751 self.eventloop()
748 except Exception as e: 752 except Exception:
749 self.logger.warning("Exception %s in logging thread" % traceback.format_exception(e)) 753 exc_type, exc_value, exc_traceback = sys.exc_info()
754 self.logger.warning("Exception %s in logging thread" %
755 traceback.format_exception(exc_type, exc_value, exc_traceback))
750 finally: 756 finally:
751 self.teardown() 757 self.teardown()
752 758
@@ -822,10 +828,12 @@ class LoggingThread(threading.Thread):
822 self.logfunc(data, ".stdout") 828 self.logfunc(data, ".stdout")
823 elif self.serialsock and self.serialsock.fileno() == fd: 829 elif self.serialsock and self.serialsock.fileno() == fd:
824 if self.serial_lock.acquire(blocking=False): 830 if self.serial_lock.acquire(blocking=False):
825 data = self.recv(1024, self.serialsock) 831 try:
826 self.logger.debug("Data received serial thread %s" % data.decode('utf-8', 'replace')) 832 data = self.recv(1024, self.serialsock)
827 self.logfunc(data, ".2") 833 self.logger.debug("Data received serial thread %s" % data.decode('utf-8', 'replace'))
828 self.serial_lock.release() 834 self.logfunc(data, ".2")
835 finally:
836 self.serial_lock.release()
829 else: 837 else:
830 serial_registered = False 838 serial_registered = False
831 poll.unregister(self.serialsock.fileno()) 839 poll.unregister(self.serialsock.fileno())
diff --git a/meta/lib/oeqa/utils/sshcontrol.py b/meta/lib/oeqa/utils/sshcontrol.py
index 36c2ecb3db..88a61aff63 100644
--- a/meta/lib/oeqa/utils/sshcontrol.py
+++ b/meta/lib/oeqa/utils/sshcontrol.py
@@ -57,8 +57,10 @@ class SSHProcess(object):
57 if select.select([self.process.stdout], [], [], 5)[0] != []: 57 if select.select([self.process.stdout], [], [], 5)[0] != []:
58 data = os.read(self.process.stdout.fileno(), 1024) 58 data = os.read(self.process.stdout.fileno(), 1024)
59 if not data: 59 if not data:
60 self.process.stdout.close() 60 self.process.poll()
61 eof = True 61 if self.process.returncode is not None:
62 self.process.stdout.close()
63 eof = True
62 else: 64 else:
63 data = data.decode("utf-8") 65 data = data.decode("utf-8")
64 output += data 66 output += data
diff --git a/meta/lib/oeqa/utils/subprocesstweak.py b/meta/lib/oeqa/utils/subprocesstweak.py
index 3e43ed547b..1774513023 100644
--- a/meta/lib/oeqa/utils/subprocesstweak.py
+++ b/meta/lib/oeqa/utils/subprocesstweak.py
@@ -8,16 +8,11 @@ import subprocess
8class OETestCalledProcessError(subprocess.CalledProcessError): 8class OETestCalledProcessError(subprocess.CalledProcessError):
9 def __str__(self): 9 def __str__(self):
10 def strify(o): 10 def strify(o):
11 if isinstance(o, bytes): 11 return o.decode("utf-8", errors="replace") if isinstance(o, bytes) else o
12 return o.decode("utf-8", errors="replace")
13 else:
14 return o
15 12
16 s = "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) 13 s = super().__str__()
17 if hasattr(self, "output") and self.output: 14 s = s + "\nStandard Output: " + strify(self.output)
18 s = s + "\nStandard Output: " + strify(self.output) 15 s = s + "\nStandard Error: " + strify(self.stderr)
19 if hasattr(self, "stderr") and self.stderr:
20 s = s + "\nStandard Error: " + strify(self.stderr)
21 return s 16 return s
22 17
23def errors_have_output(): 18def errors_have_output():
diff --git a/meta/lib/oeqa/utils/testexport.py b/meta/lib/oeqa/utils/testexport.py
index e89d130a9c..3ab024d9e9 100644
--- a/meta/lib/oeqa/utils/testexport.py
+++ b/meta/lib/oeqa/utils/testexport.py
@@ -60,17 +60,17 @@ def process_binaries(d, params):
60 export_env = d.getVar("TEST_EXPORT_ONLY") 60 export_env = d.getVar("TEST_EXPORT_ONLY")
61 61
62 def extract_binary(pth_to_pkg, dest_pth=None): 62 def extract_binary(pth_to_pkg, dest_pth=None):
63 cpio_command = runCmd("which cpio") 63 tar_command = runCmd("which tar")
64 rpm2cpio_command = runCmd("ls /usr/bin/rpm2cpio") 64 rpm2archive_command = runCmd("ls /usr/bin/rpm2archive")
65 if (cpio_command.status != 0) and (rpm2cpio_command.status != 0): 65 if (tar_command.status != 0) and (rpm2archive_command.status != 0):
66 bb.fatal("Either \"rpm2cpio\" or \"cpio\" tools are not available on your system." 66 bb.fatal("Either \"rpm2archive\" or \"tar\" tools are not available on your system."
67 "All binaries extraction processes will not be available, crashing all related tests." 67 "All binaries extraction processes will not be available, crashing all related tests."
68 "Please install them according to your OS recommendations") # will exit here 68 "Please install them according to your OS recommendations") # will exit here
69 if dest_pth: 69 if dest_pth:
70 os.chdir(dest_pth) 70 os.chdir(dest_pth)
71 else: 71 else:
72 os.chdir("%s" % os.sep)# this is for native package 72 os.chdir("%s" % os.sep)# this is for native package
73 extract_bin_command = runCmd("%s %s | %s -idm" % (rpm2cpio_command.output, pth_to_pkg, cpio_command.output)) # semi-hardcoded because of a bug on poky's rpm2cpio 73 extract_bin_command = runCmd("%s -n %s | %s xv" % (rpm2archive_command.output, pth_to_pkg, tar_command.output)) # semi-hardcoded because of a bug on poky's rpm2cpio
74 return extract_bin_command 74 return extract_bin_command
75 75
76 if determine_if_poky_env(): # machine with poky environment 76 if determine_if_poky_env(): # machine with poky environment