blob: 36532069dbe9242fdb47b310ed204b8bb4e3b3f6 [file] [log] [blame]
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
r"""
Class describing simulation results
"""
import collections
import re
from Testplan import Result
_REGEX_REMOVE = [
# Remove UVM time.
re.compile(r'@\s+[\d.]+\s+[np]s: '),
re.compile(r'\[[\d.]+\s+[np]s\] '),
# Remove assertion time.
re.compile(r'\(time [\d.]+ [PF]S\) '),
# Remove leading spaces.
re.compile(r'^\s+'),
# Remove extra white spaces.
re.compile(r'\s+(?=\s)'),
]
_REGEX_STRIP = [
# Strip TB instance name.
re.compile(r'[\w_]*top\.\S+\.(\w+)'),
# Strip assertion.
re.compile(r'(?<=Assertion )\S+\.(\w+)'),
]
# Regular expression for a separator: EOL or some of punctuation marks.
_SEPARATOR_RE = '($|[ ,.:;])'
_REGEX_STAR = [
# Replace hex numbers with 0x (needs to be called before other numbers).
re.compile(r'0x\s*[\da-fA-F]+'),
# Replace hex numbers with 'h (needs to be called before other numbers).
re.compile(r'\'h\s*[\da-fA-F]+'),
# Floating point numbers at the beginning of a word, example "10.1ns".
# (needs to be called before other numbers).
re.compile(r'(?<=[^a-zA-Z0-9])\d+\.\d+'),
# Replace all isolated numbers. Isolated numbers are numbers surrounded by
# special symbols, for example ':' or '+' or '_', excluding parenthesis.
# So a number with a letter or a round bracket on any one side, is
# considered non-isolated number and is not starred by these expressions.
re.compile(r'(?<=[^a-zA-Z0-9\(\)])\d+(?=($|[^a-zA-Z0-9\(\)]))'),
# Replace numbers surrounded by parenthesis after a space and followed by a
# separator.
re.compile(r'(?<= \()\s*\d+\s*(?=\)%s)' % _SEPARATOR_RE),
# Replace hex/decimal numbers after an equal sign or a semicolon and
# followed by a separator. Uses look-behind pattern which need a
# fixed width, thus the apparent redundancy.
re.compile(r'(?<=[\w\]][=:])[\da-fA-F]+(?=%s)' % _SEPARATOR_RE),
re.compile(r'(?<=[\w\]][=:] )[\da-fA-F]+(?=%s)' % _SEPARATOR_RE),
re.compile(r'(?<=[\w\]] [=:])[\da-fA-F]+(?=%s)' % _SEPARATOR_RE),
re.compile(r'(?<=[\w\]] [=:] )[\da-fA-F]+(?=%s)' % _SEPARATOR_RE),
# Replace decimal number at the beginning of the word.
re.compile(r'(?<= )\d+(?=\S)'),
# Remove decimal number at end of the word and before '=' or '[' or
# ',' or '.' or '('.
re.compile(r'(?<=\S)\d+(?=($|[ =\[,\.\(]))'),
# Replace the instance string.
re.compile(r'(?<=instance)\s*=\s*\S+'),
]
class SimResults:
'''An object wrapping up a table of results for some tests
self.table is a list of Result objects, each of which
corresponds to one or more runs of the test with a given name.
self.buckets contains a dictionary accessed by the failure signature,
holding all failing tests with the same signature.
'''
def __init__(self, items, results):
self.table = []
self.buckets = collections.defaultdict(list)
self._name_to_row = {}
for item in items:
self._add_item(item, results)
def _add_item(self, item, results):
'''Recursively add a single item to the table of results'''
status = results[item]
if status == "F":
bucket = self._bucketize(item.launcher.fail_msg.message)
self.buckets[bucket].append(
(item, item.launcher.fail_msg.line_number,
item.launcher.fail_msg.context))
# Runs get added to the table directly
if item.target == "run":
self._add_run(item, status)
def _add_run(self, item, status):
'''Add an entry to table for item'''
row = self._name_to_row.get(item.name)
if row is None:
row = Result(item.name)
self.table.append(row)
self._name_to_row[item.name] = row
if status == 'P':
row.passing += 1
row.total += 1
def _bucketize(self, fail_msg):
bucket = fail_msg
# Remove stuff.
for regex in _REGEX_REMOVE:
bucket = regex.sub('', bucket)
# Strip stuff.
for regex in _REGEX_STRIP:
bucket = regex.sub(r'\g<1>', bucket)
# Replace with '*'.
for regex in _REGEX_STAR:
bucket = regex.sub('*', bucket)
return bucket