From 02ba890eb4ec21e7454a96556741cd8d7ba2f3d9 Mon Sep 17 00:00:00 2001
From: Thomas Woerner <twoerner@redhat.com>
Date: Thu, 1 Aug 2024 12:04:22 +0200
Subject: [PATCH] tests/utils.py: Shorten run_playbook for smaller traceback
 with assert

Most of the content has been moved to the new function _run_playbook to
reduce the traceback output in the case of a test failure.
---
 tests/utils.py | 117 ++++++++++++++++++++++++-------------------------
 1 file changed, 57 insertions(+), 60 deletions(-)

diff --git a/tests/utils.py b/tests/utils.py
index 89a24fc..5d8a806 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -148,11 +148,33 @@ def write_logs(result, test_name):
         log_file.write(result.stderr.decode("utf-8"))
 
 
+def _truncate(lines, charcount, minlines=0):
+    output = ""
+    line_count = 1
+    for i in range(len(lines) - 1, -1, -1):
+        if len(output) + len(lines[i]) + 1 <= charcount or \
+           line_count < minlines:
+            output = lines[i] + "\n" + output
+            line_count += 1
+        else:
+            remaining = charcount - len(output) - 1 - 4
+            if remaining > 60:
+                output = "... " + lines[i][-(remaining):] + "\n" + output
+            break
+    return output
+
+
 def _run_playbook(playbook):
     """
     Create a inventory using a temporary file and run ansible using it.
 
     The logs of the run will be placed in `tests/logs/`.
+
+    In case of failure the tail of the error message will be displayed
+    as an assertion message.
+
+    The full log of the execution will be available in the directory
+    `tests/logs/`.
     """
     with tempfile.NamedTemporaryFile() as inventory_file:
         inventory_file.write(get_inventory_content())
@@ -162,30 +184,45 @@ def _run_playbook(playbook):
         if verbose is not None:
             cmd_options.append(verbose)
         cmd = ["ansible-playbook"] + cmd_options + [playbook]
-        # pylint: disable=subprocess-run-check
         process = subprocess.run(
-            cmd, cwd=SCRIPT_DIR, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            cmd, cwd=SCRIPT_DIR, stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE, check=False
         )
     test_name = get_test_name_from_playbook_path(playbook)
     write_logs(process, test_name)
 
-    return process
+    msg = ""
+    if process.returncode != 0:
+        status_code_msg = "ansible-playbook return code: {0}".format(
+            process.returncode
+        )
 
+        _stdout = process.stdout.decode("utf8")
+        _stderr = process.stderr.decode("utf8")
+        # Truncate stdout and stderr in the way that it hopefully
+        # shows all important information. At least 15 lines of stdout
+        # (Ansible tasks) and remaining from stderr to fill up to
+        # maxlen size.
+        maxlen = 2000
+        factor = maxlen / (len(_stdout) + len(_stderr))
+        stdout = _truncate(_stdout.splitlines(),
+                           int(factor * len(_stdout)),
+                           minlines=15)
+        stderr = _truncate(_stderr.splitlines(), maxlen - len(stdout))
+
+        msg = "\n".join(
+            [
+                "",
+                "-" * 30 + " Captured stdout " + "-" * 30,
+                stdout,
+                "-" * 30 + " Captured stderr " + "-" * 30,
+                stderr
+            ]
+        )
+        msg += "-" * 30 + " Playbook Return Code " + "-" * 30 + "\n"
+        msg += status_code_msg
 
-def _truncate(lines, charcount, minlines=0):
-    output = ""
-    line_count = 1
-    for i in range(len(lines) - 1, -1, -1):
-        if len(output) + len(lines[i]) + 1 <= charcount or \
-           line_count < minlines:
-            output = lines[i] + "\n" + output
-            line_count += 1
-        else:
-            remaining = charcount - len(output) - 1 - 4
-            if remaining > 60:
-                output = "... " + lines[i][-(remaining):] + "\n" + output
-            break
-    return output
+    return process, msg
 
 
 def run_playbook(playbook, allow_failures=False):
@@ -194,50 +231,10 @@ def run_playbook(playbook, allow_failures=False):
 
     Call ansible (using _run_playbook function) and assert the result of
     the execution.
-
-    In case of failure the tail of the error message will be displayed
-    as an assertion message.
-
-    The full log of the execution will be available in the directory
-    `tests/logs/`.
     """
-    result = _run_playbook(playbook)
-
-    if allow_failures:
-        return result
-
-    status_code_msg = "ansible-playbook return code: {0}".format(
-        result.returncode
-    )
-    _stdout = result.stdout.decode("utf8")
-    _stderr = result.stderr.decode("utf8")
-    # Truncate stdout and stderr in the way that it hopefully
-    # shows all important information. At least 15 lines of stdout
-    # (Ansible tasks) and remaining from stderr to fill up to
-    # maxlen size.
-    maxlen = 2000
-    factor = maxlen / (len(_stdout) + len(_stderr))
-    stdout = _truncate(_stdout.splitlines(),
-                       int(factor * len(_stdout)),
-                       minlines=15)
-    stderr = _truncate(_stderr.splitlines(), maxlen - len(stdout))
-
-    assert_msg = "\n".join(
-        [
-            "",
-            "-" * 30 + " Captured stdout " + "-" * 30,
-            stdout,
-            "-" * 30 + " Captured stderr " + "-" * 30,
-            stderr,
-            "-" * 30 + " Playbook Return Code " + "-" * 30,
-            status_code_msg,
-        ]
-    )
-
-    # Need to get the last bytes of msg otherwise Azure
-    #   will cut it out.
-    assert result.returncode == 0, assert_msg[-2500:]
-
+    result, assert_msg = _run_playbook(playbook)
+    if not allow_failures:
+        assert result.returncode == 0, assert_msg
     return result
 
 
-- 
GitLab