Skip to content

Commit

Permalink
Merge pull request #1779 from buildtesters/show_test_content_in_build…
Browse files Browse the repository at this point in the history
…test_build_output

Show content of generated test, build script and output and error in 'buildtest build'
  • Loading branch information
shahzebsiddiqui authored May 20, 2024
2 parents 399b3a2 + 4a3afea commit 2eb29a5
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 46 deletions.
93 changes: 60 additions & 33 deletions buildtest/builders/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@
)
from buildtest.utils.shell import Shell, is_csh_shell
from buildtest.utils.timer import Timer
from buildtest.utils.tools import check_container_runtime, deep_get
from buildtest.utils.tools import (
check_container_runtime,
deep_get,
print_content,
print_file_content,
)


class BuilderBase(ABC):
Expand Down Expand Up @@ -282,15 +287,9 @@ def is_local_executor(self):
"""

# import issue when putting this at top of file
# from buildtest.executors.local import LocalExecutor

# return isinstance(self.buildexecutor.executors[self.executor], LocalExecutor)

return self.buildexecutor.executors[self.executor].type == "local"

def is_container_executor(self):
# from buildtest.executors.container import ContainerExecutor
return self.buildexecutor.executors[self.executor].type == "container"

def is_batch_job(self):
Expand All @@ -303,12 +302,10 @@ def is_batch_job(self):
def start(self):
"""Keep internal timer for test using class :class:`buildtest.utils.timer.Timer`. This method will start the timer for builder which is invoked upon running test."""

# self.timer = Timer()
self.timer.start()

def stop(self):
"""Stop internal timer for builder."""
# self.duration += self.timer.stop()
self.timer.stop()

def retry(self, retry):
Expand Down Expand Up @@ -372,16 +369,11 @@ def execute_run(self, cmd, timeout):

def execute_post_run_script(self):

if self.post_run_script:
if os.path.exists(self.post_run_script):
post_run = BuildTestCommand(self.post_run_script)
post_run.execute()
output = post_run.get_output()
error = post_run.get_error()
if len(output) >= 10:
output = output[-10:]

if len(error) >= 10:
error = error[-10:]
output = "".join(post_run.get_output())
error = "".join(post_run.get_error())

console.print(
f"[blue]{self}[/]: Running Post Run Script: [cyan]{self.post_run_script}[/cyan]"
Expand All @@ -390,11 +382,21 @@ def execute_post_run_script(self):
f"[blue]{self}[/]: Post run script exit code: {post_run.returncode()}"
)

console.rule(f"[blue]{self}[/]: Post Run Script Output")
console.print(f"[blue]{' '.join(output)}")
print_content(
output,
title=f"[blue]{self}[/]: Start of Post Run Output",
theme="monokai",
lexer="text",
show_last_lines=10,
)

console.rule(f"[red]{self}[/]: Post Run Script Error")
console.print(f"[red]{' '.join(error)}")
print_content(
error,
title=f"[blue]{self}[/]: Start of Post Run Error",
theme="monokai",
lexer="text",
show_last_lines=10,
)

def handle_run_result(self, command_result, timeout):
"""This method will handle the result of running test. If the test is successful we will record endtime,
Expand All @@ -404,23 +406,30 @@ def handle_run_result(self, command_result, timeout):
launch_command = command_result.get_command()
self.logger.debug(f"Running Test via command: {launch_command}")
ret = command_result.returncode()
output_msg = command_result.get_output()
err_msg = command_result.get_error()

if len(output_msg) >= 10:
output_msg = output_msg[-10:]

console.rule(f"[blue]Output Message for {self}")
console.print(f"[blue]{' '.join(output_msg)}")
output_msg = "".join(command_result.get_output())
err_msg = "".join(command_result.get_error())

print_content(
output_msg,
title=f"[blue]{self}[/]: Start of Output",
theme="monokai",
lexer="text",
show_last_lines=10,
)

if len(err_msg) >= 60:
err_msg = err_msg[-60:]
if not self._retry or ret == 0:
return command_result

console.print(f"[red]{self}: failed to submit job with returncode: {ret}")
console.rule(f"[red]Error Message for {self}")
console.print(f"[red]{' '.join(err_msg)}")

print_content(
err_msg,
title=f"[blue]{self}[/]: Start of Error",
theme="monokai",
lexer="text",
show_last_lines=30,
)

console.print(
f"[red]{self}: Detected failure in running test, will attempt to retry test: {self._retry} times"
)
Expand Down Expand Up @@ -658,6 +667,12 @@ def _write_build_script(self, modules=None, modulepurge=None, unload_modules=Non

self.build_script = dest
self.metadata["build_script"] = self.build_script
print_file_content(
file_path=self.build_script,
title=f"[blue]{self}[/]: Start of Build Script",
lexer="bash",
theme="monokai",
)

def _write_post_run_script(self):
"""This method will write the content of post run script that is run after the test is complete.
Expand All @@ -679,6 +694,12 @@ def _write_post_run_script(self):
console.print(
f"[blue]{self}[/]: Writing Post Run Script: {self.post_run_script}"
)
print_file_content(
file_path=self.post_run_script,
title=f"[blue]{self}[/]: Start of Post Run Script",
lexer="bash",
theme="monokai",
)

def _write_test(self):
"""This method is responsible for invoking ``generate_script`` that
Expand All @@ -705,6 +726,12 @@ def _write_test(self):
shutil.copy2(
self.testpath, os.path.join(self.test_root, os.path.basename(self.testpath))
)
print_file_content(
file_path=self.testpath,
title=f"[blue]{self}[/]: Start of Test Script",
lexer="bash",
theme="monokai",
)

def get_container_invocation(self):
"""This method returns a list of lines containing the container invocation"""
Expand Down
15 changes: 3 additions & 12 deletions buildtest/cli/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
import sys

from rich.pretty import pprint
from rich.syntax import Syntax
from rich.table import Column, Table

from buildtest.defaults import console
from buildtest.utils.file import read_file, resolve_path
from buildtest.utils.tools import checkColor
from buildtest.utils.file import resolve_path
from buildtest.utils.tools import checkColor, print_file_content


def print_by_query(
Expand Down Expand Up @@ -94,21 +93,13 @@ def print_by_query(
# print content of build script when 'buildtest inspect query --buildscript' is set
if buildscript:
print_file_content(
test["build_script"], "Test File: ", "shell", theme
test["build_script"], "Build Script File: ", "shell", theme
)

if buildenv:
print_file_content(test["buildenv"], "Test File: ", "text", theme)


def print_file_content(file_path, title, lexer, theme):
content = read_file(file_path)
console.rule(f"{title} {file_path}")

syntax = Syntax(content, lexer, theme=theme)
console.print(syntax)


def fetch_test_names(report, names):
"""Return a list of builders given input test names by search the report file for valid records. If test is found it will be returned as a builder name. If names
are specified without test ID then we retrieve latest record for test name. If names are specified with ID we find the first matching test record.
Expand Down
58 changes: 57 additions & 1 deletion buildtest/utils/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
from functools import reduce

from rich.color import Color, ColorParseError
from rich.syntax import Syntax

from buildtest.defaults import console
from buildtest.exceptions import BuildTestError
from buildtest.utils.file import is_dir, resolve_path
from buildtest.utils.file import is_dir, read_file, resolve_path

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -98,3 +100,57 @@ def check_container_runtime(platform, configuration):
)

return binary_path[platform]


def print_file_content(file_path, title, lexer, theme, show_last_lines=None):
"""This method will print the content of a file using Rich Syntax.
Args:
file_path (str): Specify file to print the content of.
title (str): The title to use when printing the content.
lexer (str): The lexer to use when printing the content.
theme (str): The theme to use when printing the content.
show_last_lines (int): Show last N lines of file content. If set to None, will print entire file content.
"""

content = read_file(file_path)
console.rule(title)

trimmed_content = None

if show_last_lines:
trimmed_content = content.split("\n")[-show_last_lines:]
trimmed_content = "\n".join(trimmed_content)

# if trimmed content is set, we will print a reduced content output otherwise will print entire file
content = trimmed_content or content

syntax = Syntax(content, lexer, theme=theme)
console.print(syntax)
console.rule()


def print_content(content, title, lexer, theme, show_last_lines=None):
"""This method will print the content using Rich Syntax.
Args:
content (str): Specify file to print the content of.
title (str): The title to use when printing the content.
lexer (str): The lexer to use when printing the content.
theme (str): The theme to use when printing the content.
show_last_lines (int): Show last N lines of file content. If set to None, will print entire file content.
"""

console.rule(title)

trimmed_content = None

if show_last_lines:
trimmed_content = content.split("\n")[-show_last_lines:]
trimmed_content = "\n".join(trimmed_content)

# if trimmed content is set, we will print a reduced content output otherwise will print entire file
content = trimmed_content or content

syntax = Syntax(content, lexer, theme=theme)
console.print(syntax)
console.rule()

0 comments on commit 2eb29a5

Please sign in to comment.