blob: 26d1b612279b1863f79752f17d26edf9f4243a57 [file]
#!/usr/bin/env python3
# Test every patch from files folder and output error on failure
#
# Copyright (C) 2016 Intel Corporation
#
# SPDX-License-Identifier: GPL-2.0-only
import os
import subprocess
import sys
currentdir = os.path.dirname(os.path.abspath(__file__))
patchesdir = os.path.join(currentdir, 'files')
topdir = os.path.dirname(currentdir)
parentdir = os.path.dirname(topdir)
# path to the repo root
repodir = os.path.dirname(os.path.dirname(parentdir))
def print_results(counts):
total = sum(counts.values())
print("============================================================================")
print("Testsuite summary for %s" % os.path.basename(topdir))
print("============================================================================")
print("# TOTAL: " + str(total))
print("# XPASS: " + str(counts["xpass"]))
print("# XFAIL: " + str(counts["xfail"]))
print("# XSKIP: " + str(counts["xskip"]))
print("# PASS: " + str(counts["pass"]))
print("# FAIL: " + str(counts["fail"]))
print("# SKIP: " + str(counts["skip"]))
print("# ERROR: " + str(counts["error"]))
print("============================================================================")
def get_patches(patchesdir):
"""
Return a list of dict mapping test IDs to patch filenames and expected results.
"""
patch_list = []
for root, dirs, patches in os.walk(patchesdir):
for patch in patches:
part = patch.split('.')
klass, testname, expected_result = part[0], part[1], part[-1]
testid = f".{klass}.{testname}"
patch_list.append({
"testid": testid,
"patch": patch,
"expected": expected_result,
"root" : root,
})
return patch_list
def analyze_result(results, patch, counts, return_code):
testid = patch["testid"]
expected_result = str(patch["expected"])
for resultline in results.splitlines():
if testid in resultline:
result, _ = resultline.split(':', 1)
if expected_result.upper() == "FAIL" and result.upper() == "FAIL" and return_code != 0:
counts["xfail"] = counts["xfail"] + 1
print("XFAIL: %s (file: %s)" % (testid.strip("."), os.path.basename(patch["patch"])))
elif expected_result.upper() == "PASS" and result.upper() == "PASS" and return_code == 0:
counts["xpass"] = counts["xpass"] + 1
print("XPASS: %s (file: %s)" % (testid.strip("."), os.path.basename(patch["patch"])))
elif expected_result.upper() == "SKIP" and result.upper() == "SKIP" and return_code == 0:
counts["xskip"] = counts["xskip"] + 1
print("XSKIP: %s (file: %s)" % (testid.strip("."), os.path.basename(patch["patch"])))
else:
print("%s: %s (%s)" % (result.upper(), testid.strip("."),os.path.basename(patch["patch"])))
if result.upper() == "PASS":
counts["pass"] = counts["pass"] + 1
elif result.upper() == "FAIL":
counts["fail"] = counts["fail"] + 1
elif result.upper() == "SKIP":
counts["skip"] = counts["skip"] + 1
else:
print("Bad result on test %s against %s" % (testid.strip("."),os.path.basename(patch["patch"])))
counts["error"] = counts["error"] + 1
break
else:
print ("No test for=%s" % patch["patch"])
return counts
def run_sh(cmd):
"""Run a shell command and return its stdout as a stripped string."""
return subprocess.check_output(cmd, cwd=currentdir, stderr=subprocess.STDOUT, universal_newlines=True, shell=True).strip()
def get_git_state():
"""Return the current Git HEAD state (branch, commit)."""
try:
inside_repo = run_sh("git rev-parse --is-inside-work-tree")
except subprocess.CalledProcessError:
print("Not a Git repository")
return None
state = {
"branch": run_sh("git rev-parse --abbrev-ref HEAD"),
"commit": run_sh("git rev-parse HEAD"),
}
return state
def restore_git_state(git_state):
assert git_state['branch'] is not None, "Failed to restore git state, no valid branch"
if git_state['branch'] == "HEAD":
run_sh(f"git switch --detach {git_state['commit']}")
else:
run_sh(f"git switch {git_state['branch']}")
def is_git_state_same(before, after):
ret = True
for k in ("branch", "commit"):
if before[k] != after[k]:
print(f"Git state changed: {k} changed: {before[k]} -> {after[k]}")
ret = False
return ret
def git_attach_head(temp_branch):
run_sh(f"git switch -C {temp_branch}")
def git_detach_head():
run_sh("git switch --detach HEAD")
assert run_sh("git rev-parse --abbrev-ref HEAD") == "HEAD", "Failed to enter detached HEAD state"
return get_git_state()
# Once the tests are in oe-core, we can remove the testdir param and use os.path.dirname to get relative paths
def test(root, patch):
res = True
patchpath = os.path.abspath(os.path.join(root, patch))
cmd = 'patchtest --base-commit HEAD --repodir %s --testdir %s/tests --patch %s' % (repodir, topdir, patchpath)
results = subprocess.run(cmd, capture_output=True, universal_newlines=True, shell=True)
return results.returncode, results.stdout
def test_head_attached(patches, counts, branch):
git_attach_head(branch)
git_state_before = get_git_state()
for patch_info in patches:
return_code, results = test(patch_info["root"], patch_info["patch"])
counts = analyze_result(results, patch_info, counts, return_code)
git_state_after = get_git_state()
assert is_git_state_same(git_state_before, git_state_after), "Repository state changed after attached HEAD test."
return counts
def test_head_detached(patches, counts):
git_state = get_git_state()
git_st_detach_before = git_detach_head()
patch_info = patches[0]
testid = patch_info["testid"]
return_code, results = test(patch_info["root"], patch_info["patch"])
git_st_detach_after = get_git_state()
counts = analyze_result(results, patch_info, counts, return_code)
if not is_git_state_same(git_st_detach_before, git_st_detach_after):
print(" Test '%s' failed with git in detach HEAD mode: state changed after test" % testid.strip("."))
counts["error"] = counts["error"] + 1
else:
counts["xpass"] = counts["xpass"] + 1
print("XPASS: %s.test_head_detached" % os.path.basename(__file__))
return counts
def run_tests(patches, counts):
temp_branch = "test_patchtest_head_attached"
git_state = get_git_state()
assert git_state['branch'] != temp_branch, f"Cannot run patchtest selftest while on branch '{temp_branch}'"
counts = test_head_attached(patches, counts, temp_branch)
counts = test_head_detached(patches, counts)
restore_git_state(git_state)
run_sh(f"git branch -D {temp_branch}")
return counts
if __name__ == '__main__':
counts = {
"pass": 0,
"fail": 0,
"skip": 0,
"xpass": 0,
"xfail": 0,
"xskip": 0,
"error": 0,
}
results = None
patches = get_patches(patchesdir)
if not patches:
print(f"Error: Unable to find patch(es) in {patchesdir}")
sys.exit(1)
counts = run_tests(patches, counts)
print_results(counts)