diff --git a/scripts/runtestsuite/header_template.html b/scripts/runtestsuite/header_template.html index 1676c0d5f3..5b6209c064 100644 --- a/scripts/runtestsuite/header_template.html +++ b/scripts/runtestsuite/header_template.html @@ -365,6 +365,14 @@

Setup

Maximum Number of Differing Components {max-diff-comps-count-allowed} + + Source Commit + {git-title} + + + Source Commit Hash + {git-hash} +
diff --git a/scripts/runtestsuite/runtestsuite.py b/scripts/runtestsuite/runtestsuite.py index ff035e37fc..4aa7723823 100644 --- a/scripts/runtestsuite/runtestsuite.py +++ b/scripts/runtestsuite/runtestsuite.py @@ -117,6 +117,95 @@ def write_rgba_png_file(filepath, rows): with open(filepath, 'wb') as file: writer.write(file, rows) +def git_information(path): + git_hash = git_title = "N/A" + + if is_git_installed(path) and is_git_repository(path): + hash = get_git_hash(path) + title = get_git_title(path) + if hash: git_hash = hash + if title: git_title = title + elif not is_git_installed(path): + git_hash = git_title = "N/A (Git not installed)" + else: + git_hash = git_title = "N/A (Directory not under git control)" + + return git_hash, git_title + +def is_git_installed(directory): + return command_is_valid('git -C {0} --version'.format(directory)) + +def is_git_repository(directory): + return command_is_valid('git -C {0} rev-parse --is-inside-work-tree'.format(directory)) + +def get_git_hash(directory): + return command_output('git -C {0} rev-parse HEAD'.format(directory)) + +def get_git_title(directory): + return command_output('git -C {0} rev-parse --abbrev-ref HEAD'.format(directory)) + +def command_output(command): + command_output = None + try: + command_output = subprocess.check_output(command.split()) + except OSError: + pass + return command_output + +def command_is_valid(command): + try: + command_return = subprocess.check_call( + # Ensure space separated arguments are separate elements of an array + # (required for subprocess calls) + command.split(), + # Ensure error output doesn't get printed to terminal + stdout = open(os.devnull, 'wb')) + + return command_return == 0 + except (subprocess.CalledProcessError, OSError): + pass + + return False + +# -------------------------------------------------------------------------------------------------- +# Utility class to calculate successes vs failures +# -------------------------------------------------------------------------------------------------- + +class TestSuiteRunnerResults: + def __init__(self): + self.rendered = 0 + self.successes = 0 + self.start_time = datetime.datetime.min + self.end_time = datetime.datetime.min + + def increment_rendered_count(self): + self.rendered += 1 + + def increment_success_count(self): + self.successes += 1 + + def start_timer(self): + self.start_time = datetime.datetime.now() + + def end_timer(self): + self.end_time = datetime.datetime.now() + + def success_count(self): + return self.successes + + def failure_count(self): + assert self.rendered >= self.successes + return self.rendered - self.successes + + def total_test_count(self): + return self.rendered + + def success_rate(self): + return 100.0 * self.successes / self.rendered if self.rendered > 0 else 0.0 + + def total_time(self): + return format_duration(self.end_time - self.start_time) + # -------------------------------------------------------------------------------------------------- # Utility class to log progress. @@ -184,9 +273,11 @@ class ReportWriter: def __init__(self, template_directory): self.header_template = load_file(os.path.join(template_directory, "header_template.html")) + self.stats_template = load_file(os.path.join(template_directory, "stats_template.html")) self.footer_template = load_file(os.path.join(template_directory, "footer_template.html")) self.simple_failure_template = load_file(os.path.join(template_directory, "simple_failure_template.html")) self.detailed_failure_template = load_file(os.path.join(template_directory, "detailed_failure_template.html")) + self.template_directory = template_directory def open(self, args, filepath): self.args = args @@ -195,7 +286,8 @@ def open(self, args, filepath): self.failures = 0 self.all_commands = [] - def close(self): + def close(self, results): + self.__write_stats(results) self.__write_footer() self.file.close() @@ -238,6 +330,8 @@ def report_detailed_failure(self, scene, reference_filepath, output_filepath, lo def __write_header(self, args): script_path = os.path.realpath(__file__) + git_hash, git_title = git_information(self.template_directory) + self.file.write(self.__render(self.header_template, {'test-date': CURRENT_TIME, 'python-version': utils.get_python_version(), @@ -245,8 +339,22 @@ def __write_header(self, args): 'script-version': VERSION, 'appleseed-binary-path': args.tool_path, 'max-abs-diff-allowed': VALUE_THRESHOLD, - 'max-diff-comps-count-allowed': MAX_DIFFERING_COMPONENTS})) + 'max-diff-comps-count-allowed': MAX_DIFFERING_COMPONENTS, + 'git-hash': git_hash, + 'git-title': git_title})) self.file.flush() + + def __write_stats(self, results): + total_time_text = results.total_time() + success_rate_text = "{0}%".format(results.success_rate()) + failures_text = "{0} out of {1} test scene(s)".format( + results.failure_count(), + results.total_test_count()) + + self.file.write(self.__render(self.stats_template, + {'total-time': total_time_text, + 'success-rate': success_rate_text, + 'failures': failures_text})) def __write_footer(self): self.file.write(self.__render(self.footer_template, {})) @@ -452,8 +560,8 @@ def render_test_scene(args, logger, report_writer, project_directory, project_fi # -------------------------------------------------------------------------------------------------- def render_test_scenes(script_directory, args): - rendered_scene_count = 0 - passing_scene_count = 0 + results = TestSuiteRunnerResults() + results.start_timer() logger = Logger() logger.begin_table() @@ -472,16 +580,18 @@ def render_test_scenes(script_directory, args): logger.skip_rendering(os.path.join(dirpath, filename)) continue - rendered_scene_count += 1 + results.increment_rendered_count() if render_test_scene(args, logger, report_writer, dirpath, filename): - passing_scene_count += 1 + results.increment_success_count() + + results.end_timer() - report_writer.close() + report_writer.close(results) logger.end_table() - return rendered_scene_count, passing_scene_count + return results # -------------------------------------------------------------------------------------------------- @@ -522,25 +632,20 @@ def main(): utils.print_runtime_details("runtestsuite", VERSION, os.path.realpath(__file__), CURRENT_TIME) print_configuration(args.tool_path, appleseed_args) - start_time = datetime.datetime.now() - rendered_scene_count, passing_scene_count = render_test_scenes(script_directory, args) - end_time = datetime.datetime.now() - - success = 100.0 * passing_scene_count / rendered_scene_count if rendered_scene_count > 0 else 0.0 + results = render_test_scenes(script_directory, args) print() print("Results:") print(" Success Rate : {0}{1:.2f} %{2}" - .format(colorama.Fore.RED if passing_scene_count < rendered_scene_count else colorama.Fore.GREEN, - success, + .format(colorama.Fore.RED if results.failure_count() > 0 else colorama.Fore.GREEN, + results.success_rate(), colorama.Fore.RESET)) print(" Failures : {0}{1} out of {2} test scene(s){3}" - .format(colorama.Fore.RED if passing_scene_count < rendered_scene_count else colorama.Fore.GREEN, - rendered_scene_count - passing_scene_count, - rendered_scene_count, + .format(colorama.Fore.RED if results.failure_count() > 0 else colorama.Fore.GREEN, + results.failure_count(), + results.total_test_count(), colorama.Fore.RESET)) - print(" Total Time : {0}".format(format_duration(end_time - start_time))) - + print(" Total Time : {0}".format(results.total_time())) if __name__ == "__main__": main() diff --git a/scripts/runtestsuite/stats_template.html b/scripts/runtestsuite/stats_template.html new file mode 100644 index 0000000000..b6313e828e --- /dev/null +++ b/scripts/runtestsuite/stats_template.html @@ -0,0 +1,17 @@ +
+

Summary

+ + + + + + + + + + + + + +
Success Rate{success-rate}
Failures{failures}
Total Time{total-time}
+