diff mbox series

[v4,5/6] patchtest/selftest: Add coverage for detached and attached HEAD cases

Message ID 20251211150646.2647853-5-naftaly.ralamboarivony@smile.fr
State New
Headers show
Series [v4,1/6] patchtest: fix failure when oe-core repo is in detached HEAD | expand

Commit Message

naftaly.ralamboarivony@smile.fr Dec. 11, 2025, 3:06 p.m. UTC
From: Naftaly RALAMBOARIVONY <naftaly.ralamboarivony@smile.fr>

Extend the selftest to run against both modes to ensure correct behavior and
prevent regressions when operating in a detached HEAD environment.

Two test modes are run: Git attached and detached, via the
'run_tests()' function.

Signed-off-by: Naftaly RALAMBOARIVONY <naftaly.ralamboarivony@smile.fr>
---
changes in v4:

- Fix incorrect working directory when running Git commands on the
autobuilder : https://lists.openembedded.org/g/openembedded-core/message/227474

The run-patchtest-selftest script uses an incorrect cwd when executing
Git commands during autobuilder tests. This causes failures when the
script is launched from outside the expected repository.

To resolve this, the run_sh() function is updated to pass the cwd
argument to subprocess.check_output, ensuring all Git operations run
from the correct directory.

+ /srv/pokybuild/yocto-worker/patchtest-selftest/build/layers/openembedded-core//meta/lib/patchtest/selftest/selftest
Not a Git repository
Traceback (most recent call last):
  File "/srv/pokybuild/yocto-worker/patchtest-selftest/build/layers/openembedded-core//meta/lib/patchtest/selftest/selftest", line 198, in <module>
    counts = run_tests(patches, counts)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/srv/pokybuild/yocto-worker/patchtest-selftest/build/layers/openembedded-core//meta/lib/patchtest/selftest/selftest", line 173, in run_tests
    assert git_state['branch'] != temp_branch, f"Cannot run patchtest selftest while on branch '{temp_branch}'"
           ~~~~~~~~~^^^^^^^^^^

changes in v3:
    - Simplified printouts message in test_head_detached when test failed
    - Fixed the result message for test_head_detached
    - Incrementing xpass so it is properly included in the test results

---
 meta/lib/patchtest/selftest/selftest | 68 +++++++++++++++++++++++++++-
 1 file changed, 67 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/meta/lib/patchtest/selftest/selftest b/meta/lib/patchtest/selftest/selftest
index 92acc4d556..01a0888499 100755
--- a/meta/lib/patchtest/selftest/selftest
+++ b/meta/lib/patchtest/selftest/selftest
@@ -84,6 +84,47 @@  def analyze_result(results, patch, counts):
 
     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_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):
@@ -101,6 +142,31 @@  def test_head_attached(patches, counts):
         counts = analyze_result(results, patch_info, counts)
     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"]
+    results = test(patch_info["root"], patch_info["patch"])
+    git_st_detach_after = get_git_state()
+    counts = analyze_result(results, patch_info, counts)
+    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):
+    git_state = get_git_state()
+    counts = test_head_attached(patches, counts)
+    counts = test_head_detached(patches, counts)
+    restore_git_state(git_state)
+
+    return counts
+
 if __name__ == '__main__':
     counts = {
         "pass": 0,
@@ -118,5 +184,5 @@  if __name__ == '__main__':
     if not patches:
         print(f"Error: Unable to find patch(es) in {patchesdir}")
         sys.exit(1)
-    counts = test_head_attached(patches, counts)
+    counts = run_tests(patches, counts)
     print_results(counts)