"""Host-only parser regressions, including each byte boundary of actual guest logs.
This does not boot QEMU or qualify a framebuffer/performance acceptance result.
"""
import argparse
import hashlib
import json
import pathlib
import sys
ROOT = pathlib.Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / 'Tools'))
from TestDesktopUi import parse_render_stats
def expect_malformed(record):
try:
parse_render_stats(record + '\n')
except ValueError as error:
assert 'Malformed complete XFE_RENDER_STATS record' in str(error), str(error)
return str(error)
raise AssertionError('A complete malformed record was silently accepted: ' + repr(record))
def check_record(record):
assert record.endswith('\n')
parsed = parse_render_stats(record)
assert len(parsed) == 1 and parsed[0]['Frequency'] > 0
# Every prefix before the LF is still an unacknowledged serial tail. Keep
# the previous sample intact until that exact terminator has arrived.
for offset in range(len(record)):
assert parse_render_stats(record[:offset]) == [], (offset, record[:offset])
assert parse_render_stats(record + record[:offset]) == parsed, offset
assert parse_render_stats(record + record) == parsed + parsed
return len(record) * 2 + 1
def run(log_paths):
sample = ('XFE_RENDER_STATS|518311632290|Frames|2205|Partial|2143|Pixels|1053509288|Glyphs|67|Fallback|0|'
'LastRenderTicks|56330|Frequency|4447722000|MotionReuses|3|MotionPixels|3512816|ScanoutPixelsSaved|2267872')
checks = check_record(sample + '\n') + check_record(sample + '\r\n')
# Complete protocol corruption must fail even when good samples precede it.
malformed = [sample.replace('|Frequency|4447722000', '|Frequency|0'),
sample.replace('|Frequency|4447722000', ''),
sample + '|Frames|1', sample + '|Timestamp|1', sample + '|Unpaired',
sample.replace('|Frames|2205', '|Frames|'),
sample.replace('|Frames|2205', '|Frames|-1'),
sample.replace('|Frames|2205', '|Frames|+1'),
sample.replace('|Frames|2205', '|Frames|1.5'),
sample.replace('|Frames|2205', '|Frames|1e3'),
sample.replace('|Frames|2205', '|Frames|2205'),
sample.replace('|Frames|2205', '|Frames|1\v2'),
sample.replace('|Frames|2205', '|Frames|9223372036854775808'),
sample.replace('|Frames|2205', '|Frames|1\r2'),
sample.replace('|Frames|2205', '|1Invalid|2205'),
'XFE_RENDER_STATS', 'XFE_RENDER_STATS|123']
failures = [expect_malformed(record) for record in malformed]
for record in malformed:
expect_malformed(sample + '\n' + record)
assert parse_render_stats('Unrelated diagnostic\n' + sample + '\nUnfinished diagnostic') == parse_render_stats(sample + '\n')
assert parse_render_stats(sample + '|FutureCounter|12\n')[0]['FutureCounter'] == 12
logs = []
for path in log_paths:
raw = path.read_bytes()
text = raw.decode('utf-8', errors='replace')
parsed = parse_render_stats(text)
assert parsed, 'No actual complete render-stat records: ' + str(path)
records = [line for line in text.splitlines(keepends=True) if line.startswith('XFE_RENDER_STATS|') and line.endswith('\n')]
assert len(records) == len(parsed)
boundary_checks = sum(check_record(record) for record in records)
checks += boundary_checks
logs.append({'Path': str(path), 'Sha256': hashlib.sha256(raw).hexdigest(), 'CompleteRecords': len(parsed),
'EveryOffsetChecks': boundary_checks, 'LastCompleteRecord': parsed[-1],
'UnterminatedRenderStatsTailIgnored': text.rsplit('\n', 1)[-1].startswith('XFE_RENDER_STATS|')})
return {'Kind': 'HostNativeRenderStatsParserRegression', 'QemuLaunched': False, 'Passed': True,
'ParserSha256': hashlib.sha256((ROOT / 'Tools/TestDesktopUi.py').read_bytes()).hexdigest(),
'EveryOffsetChecks': checks, 'CompleteMalformedCasesRejected': len(malformed), 'MalformedDiagnostics': failures,
'RealGuestLogs': logs}
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--log', action='append', required=True, type=pathlib.Path)
parser.add_argument('--output', required=True, type=pathlib.Path)
options = parser.parse_args()
result = run([path.resolve() for path in options.log])
options.output.parent.mkdir(parents=True, exist_ok=True)
options.output.write_text(json.dumps(result, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
print(json.dumps({key: result[key] for key in ('Kind', 'Passed', 'EveryOffsetChecks', 'CompleteMalformedCasesRejected')}))
return 0
if __name__ == '__main__':
raise SystemExit(main())
"""Host-only parser regressions, including each byte boundary of actual guest logs.
This does not boot QEMU or qualify a framebuffer/performance acceptance result.
"""
import argparse
import hashlib
import json
import pathlib
import sys
ROOT = pathlib.Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / 'Tools'))
from TestDesktopUi import parse_render_stats
def expect_malformed(record):
try:
parse_render_stats(record + '\n')
except ValueError as error:
assert 'Malformed complete XFE_RENDER_STATS record' in str(error), str(error)
return str(error)
raise AssertionError('A complete malformed record was silently accepted: ' + repr(record))
def check_record(record):
assert record.endswith('\n')
parsed = parse_render_stats(record)
assert len(parsed) == 1 and parsed[0]['Frequency'] > 0
# Every prefix before the LF is still an unacknowledged serial tail. Keep
# the previous sample intact until that exact terminator has arrived.
for offset in range(len(record)):
assert parse_render_stats(record[:offset]) == [], (offset, record[:offset])
assert parse_render_stats(record + record[:offset]) == parsed, offset
assert parse_render_stats(record + record) == parsed + parsed
return len(record) * 2 + 1
def run(log_paths):
sample = ('XFE_RENDER_STATS|518311632290|Frames|2205|Partial|2143|Pixels|1053509288|Glyphs|67|Fallback|0|'
'LastRenderTicks|56330|Frequency|4447722000|MotionReuses|3|MotionPixels|3512816|ScanoutPixelsSaved|2267872')
checks = check_record(sample + '\n') + check_record(sample + '\r\n')
# Complete protocol corruption must fail even when good samples precede it.
malformed = [sample.replace('|Frequency|4447722000', '|Frequency|0'),
sample.replace('|Frequency|4447722000', ''),
sample + '|Frames|1', sample + '|Timestamp|1', sample + '|Unpaired',
sample.replace('|Frames|2205', '|Frames|'),
sample.replace('|Frames|2205', '|Frames|-1'),
sample.replace('|Frames|2205', '|Frames|+1'),
sample.replace('|Frames|2205', '|Frames|1.5'),
sample.replace('|Frames|2205', '|Frames|1e3'),
sample.replace('|Frames|2205', '|Frames|2205'),
sample.replace('|Frames|2205', '|Frames|1\v2'),
sample.replace('|Frames|2205', '|Frames|9223372036854775808'),
sample.replace('|Frames|2205', '|Frames|1\r2'),
sample.replace('|Frames|2205', '|1Invalid|2205'),
'XFE_RENDER_STATS', 'XFE_RENDER_STATS|123']
failures = [expect_malformed(record) for record in malformed]
for record in malformed:
expect_malformed(sample + '\n' + record)
assert parse_render_stats('Unrelated diagnostic\n' + sample + '\nUnfinished diagnostic') == parse_render_stats(sample + '\n')
assert parse_render_stats(sample + '|FutureCounter|12\n')[0]['FutureCounter'] == 12
logs = []
for path in log_paths:
raw = path.read_bytes()
text = raw.decode('utf-8', errors='replace')
parsed = parse_render_stats(text)
assert parsed, 'No actual complete render-stat records: ' + str(path)
records = [line for line in text.splitlines(keepends=True) if line.startswith('XFE_RENDER_STATS|') and line.endswith('\n')]
assert len(records) == len(parsed)
boundary_checks = sum(check_record(record) for record in records)
checks += boundary_checks
logs.append({'Path': str(path), 'Sha256': hashlib.sha256(raw).hexdigest(), 'CompleteRecords': len(parsed),
'EveryOffsetChecks': boundary_checks, 'LastCompleteRecord': parsed[-1],
'UnterminatedRenderStatsTailIgnored': text.rsplit('\n', 1)[-1].startswith('XFE_RENDER_STATS|')})
return {'Kind': 'HostNativeRenderStatsParserRegression', 'QemuLaunched': False, 'Passed': True,
'ParserSha256': hashlib.sha256((ROOT / 'Tools/TestDesktopUi.py').read_bytes()).hexdigest(),
'EveryOffsetChecks': checks, 'CompleteMalformedCasesRejected': len(malformed), 'MalformedDiagnostics': failures,
'RealGuestLogs': logs}
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--log', action='append', required=True, type=pathlib.Path)
parser.add_argument('--output', required=True, type=pathlib.Path)
options = parser.parse_args()
result = run([path.resolve() for path in options.log])
options.output.parent.mkdir(parents=True, exist_ok=True)
options.output.write_text(json.dumps(result, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
print(json.dumps({key: result[key] for key in ('Kind', 'Passed', 'EveryOffsetChecks', 'CompleteMalformedCasesRejected')}))
return 0
if __name__ == '__main__':
raise SystemExit(main())