mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-09-16 10:45:45 +00:00
* assert is handled specially by the type checker so we don't want to have duplicate self.check / assert * The original reason for TestCase.check was to print the assertion message but this can be handled by checking for the AssertionError exception
216 lines
7.0 KiB
Python
216 lines
7.0 KiB
Python
import os
|
|
import sys
|
|
import re
|
|
import traceback
|
|
import mimetypes
|
|
import difflib
|
|
import shutil
|
|
from . import util
|
|
|
|
|
|
class TestFailureException(Exception):
|
|
def __init__(self, message, *args):
|
|
self.message = message
|
|
self.files = []
|
|
for a in args:
|
|
self.files.append(str(a))
|
|
|
|
def __str__(self):
|
|
return self.message
|
|
|
|
def __repr__(self):
|
|
return "<TestFailureException '{}' with files: {}>".format(self.message, repr(self.files))
|
|
|
|
|
|
class TestLogger:
|
|
def __init__(self):
|
|
self.indentation = 0
|
|
self.test_name = ''
|
|
self.outputs = [sys.stdout]
|
|
self.failed = False
|
|
self.section_failed = False
|
|
self.logged_exception = False
|
|
|
|
def subprocess_print(self, line: str):
|
|
for o in self.outputs:
|
|
o.write(line)
|
|
o.flush()
|
|
|
|
def rawprint(self, line: str, with_stdout=True):
|
|
for o in self.outputs:
|
|
if o == sys.stdout and not with_stdout:
|
|
continue
|
|
|
|
for l in line.split('\n'):
|
|
if self.indentation > 0:
|
|
o.write(self.indentation*' ')
|
|
o.write(l)
|
|
o.write('\n')
|
|
|
|
o.flush()
|
|
|
|
def add_output(self, o, header='', footer=''):
|
|
os.makedirs(os.path.dirname(o), exist_ok=True)
|
|
self.outputs.append(open(o, "a"))
|
|
|
|
def print(self, line: str, with_stdout=True):
|
|
self.rawprint('.. ' + line, with_stdout)
|
|
|
|
def comment(self, line: str):
|
|
self.rawprint('// ' + line)
|
|
|
|
def header(self, text):
|
|
self.rawprint('\n## ' + text + ' ##\n')
|
|
|
|
def indent(self):
|
|
self.indentation += 4
|
|
|
|
def dedent(self):
|
|
self.indentation -= 4
|
|
|
|
def begin_test(self, test_name: str, print_header: bool=True):
|
|
self.test_name = test_name
|
|
if print_header:
|
|
self.rawprint(">> Test {}".format(test_name))
|
|
self.indent()
|
|
|
|
self.failed = False
|
|
self.logged_exception = False
|
|
|
|
def end_test(self, test_name: str, print_footer: bool=True):
|
|
if self.failed:
|
|
self.rawprint("$$ FAILED")
|
|
self.dedent()
|
|
if print_footer:
|
|
self.rawprint("<< Test {}".format(test_name))
|
|
self.test_name = ''
|
|
|
|
def begin_section(self, name: str):
|
|
self.rawprint(">> Section {}".format(name))
|
|
self.indent()
|
|
self.section_failed = False
|
|
self.logged_exception = False
|
|
|
|
def end_section(self, name: str):
|
|
if self.section_failed:
|
|
self.rawprint("$$ FAILED")
|
|
self.dedent()
|
|
self.rawprint("<< Section {}".format(name))
|
|
|
|
def auto_section(self, name: str):
|
|
class ScopedSection():
|
|
def __init__(self, logger: TestLogger, name: str):
|
|
self.name = name
|
|
self.logger = logger
|
|
|
|
def __enter__(self):
|
|
self.logger.begin_section(name)
|
|
|
|
def __exit__(self, exc_type, exc_value, traceback):
|
|
if exc_value is not None:
|
|
self.logger.failure(exc_value)
|
|
self.logger.end_section(name)
|
|
|
|
return ScopedSection(self, name)
|
|
|
|
def inline_file(self, name: str, path: str, with_stdout: bool = False):
|
|
self.rawprint(">> Raw {}".format(name))
|
|
self.indent()
|
|
with open(path) as f:
|
|
lines = f.readlines()
|
|
for l in lines:
|
|
self.rawprint(l.strip(), with_stdout=with_stdout)
|
|
self.dedent()
|
|
self.rawprint("<< Raw {}".format(name))
|
|
|
|
def success(self, message):
|
|
self.rawprint("** " + message)
|
|
|
|
def error(self, message):
|
|
self.failed = self.section_failed = True
|
|
|
|
self.rawprint("!! " + message)
|
|
|
|
def failure(self, ex):
|
|
if self.logged_exception:
|
|
return
|
|
|
|
self.logged_exception = True
|
|
self.failed = self.section_failed = True
|
|
|
|
tb = traceback.extract_tb(sys.exc_info()[2])
|
|
|
|
if isinstance(ex, AssertionError):
|
|
assertion_line = tb[-1].line
|
|
|
|
if assertion_line is not None:
|
|
assert_msg = re.sub(r'assert (.*)', r'\1', assertion_line)
|
|
else:
|
|
assert_msg = "Unknown Assertion"
|
|
|
|
self.rawprint(f"!+ ASSERT FAILURE in {self.test_name}: {assert_msg}")
|
|
elif isinstance(ex, TestFailureException):
|
|
self.rawprint(f"!+ FAILURE in {self.test_name}: {ex!s}")
|
|
else:
|
|
self.rawprint(f"!+ FAILURE in {self.test_name}: {type(ex).__name__} {ex!s}")
|
|
|
|
self.rawprint('>> Callstack')
|
|
for frame in reversed(tb):
|
|
filename = util.sanitise_filename(frame.filename)
|
|
filename = re.sub('.*site-packages/', 'site-packages/', filename)
|
|
if filename[0] == '/':
|
|
filename = filename[1:]
|
|
self.rawprint(" File \"{}\", line {}, in {}".format(filename, frame.lineno, frame.name))
|
|
self.rawprint(" {}".format(frame.line))
|
|
self.rawprint('<< Callstack')
|
|
|
|
if isinstance(ex, TestFailureException):
|
|
file_list = []
|
|
for f in ex.files:
|
|
fname = '{}_{}'.format(self.test_name, os.path.basename(f))
|
|
if 'data' in f:
|
|
ext = fname.rfind('.')
|
|
if ext > 0:
|
|
fname = fname[0:ext] + '_ref' + fname[ext:]
|
|
if not os.path.exists(f):
|
|
continue
|
|
shutil.copyfile(f, util.get_artifact_path(fname))
|
|
file_list.append(fname)
|
|
|
|
diff_file = ''
|
|
diff = ''
|
|
|
|
# Special handling for the common case where we have two files to generate comparisons
|
|
if len(file_list) == 2:
|
|
mime = mimetypes.guess_type(ex.files[0])
|
|
|
|
if 'image' in mime[0]:
|
|
# If we have two files and they are images, a failed image comparison should have
|
|
# generated a diff.png. Grab it and include it
|
|
diff_tmp_file = util.get_tmp_path('diff.png')
|
|
if os.path.exists(diff_tmp_file):
|
|
diff_artifact = '{}_diff.png'.format(self.test_name)
|
|
shutil.move(diff_tmp_file, util.get_artifact_path(diff_artifact))
|
|
diff_file = ' ({})'.format(diff_artifact)
|
|
|
|
elif 'text' in mime[0] or 'xml' in mime[0]:
|
|
with open(ex.files[0]) as f:
|
|
fromlines = f.readlines()
|
|
with open(ex.files[1]) as f:
|
|
tolines = f.readlines()
|
|
diff = difflib.unified_diff(fromlines, tolines, fromfile=file_list[0], tofile=file_list[1])
|
|
|
|
if diff != '':
|
|
self.rawprint("=+ Compare: " + ','.join(file_list) + diff_file)
|
|
self.indent()
|
|
self.rawprint(''.join(diff).strip())
|
|
self.dedent()
|
|
self.rawprint("=- Compare")
|
|
elif len(file_list) > 0:
|
|
self.rawprint("== Compare: " + ','.join(file_list) + diff_file)
|
|
|
|
self.rawprint("!- FAILURE")
|
|
|
|
|
|
log = TestLogger()
|