-
Notifications
You must be signed in to change notification settings - Fork 42
/
reporting.py
111 lines (93 loc) · 4.18 KB
/
reporting.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
from array_api_tests.dtype_helpers import dtype_to_name
from array_api_tests import _array_module as xp
from array_api_tests import __version__
from collections import Counter
from types import BuiltinFunctionType, FunctionType
import dataclasses
import json
import warnings
from hypothesis.strategies import SearchStrategy
from pytest import hookimpl, fixture
try:
import pytest_jsonreport # noqa
except ImportError:
raise ImportError("pytest-json-report is required to run the array API tests")
def to_json_serializable(o):
if o in dtype_to_name:
return dtype_to_name[o]
if isinstance(o, (BuiltinFunctionType, FunctionType, type)):
return o.__name__
if dataclasses.is_dataclass(o):
return to_json_serializable(dataclasses.asdict(o))
if isinstance(o, SearchStrategy):
return repr(o)
if isinstance(o, dict):
return {to_json_serializable(k): to_json_serializable(v) for k, v in o.items()}
if isinstance(o, tuple):
if hasattr(o, '_asdict'): # namedtuple
return to_json_serializable(o._asdict())
return tuple(to_json_serializable(i) for i in o)
if isinstance(o, list):
return [to_json_serializable(i) for i in o]
if callable(o):
return repr(o)
# Ensure everything is JSON serializable. If this warning is issued, it
# means the given type needs to be added above if possible.
try:
json.dumps(o)
except TypeError:
warnings.warn(f"{o!r} (of type {type(o)}) is not JSON-serializable. Using the repr instead.")
return repr(o)
return o
@hookimpl(optionalhook=True)
def pytest_metadata(metadata):
"""
Additional global metadata for --json-report.
"""
metadata['array_api_tests_module'] = xp.__name__
metadata['array_api_tests_version'] = __version__
@fixture(autouse=True)
def add_extra_json_metadata(request, json_metadata):
"""
Additional per-test metadata for --json-report
"""
def add_metadata(name, obj):
obj = to_json_serializable(obj)
json_metadata[name] = obj
test_module = request.module.__name__
if test_module.startswith('array_api_tests.meta'):
return
test_function = request.function.__name__
assert test_function.startswith('test_'), 'unexpected test function name'
if test_module == 'array_api_tests.test_has_names':
array_api_function_name = None
else:
array_api_function_name = test_function[len('test_'):]
add_metadata('test_module', test_module)
add_metadata('test_function', test_function)
add_metadata('array_api_function_name', array_api_function_name)
if hasattr(request.node, 'callspec'):
params = request.node.callspec.params
add_metadata('params', params)
def finalizer():
# TODO: This metadata is all in the form of error strings. It might be
# nice to extract the hypothesis failing inputs directly somehow.
if hasattr(request.node, 'hypothesis_report_information'):
add_metadata('hypothesis_report_information', request.node.hypothesis_report_information)
if hasattr(request.node, 'hypothesis_statistics'):
add_metadata('hypothesis_statistics', request.node.hypothesis_statistics)
request.addfinalizer(finalizer)
@hookimpl(optionalhook=True)
def pytest_json_modifyreport(json_report):
# Deduplicate warnings. These duplicate warnings can cause the file size
# to become huge. For instance, a warning from np.bool which is emitted
# every time hypothesis runs (over a million times) causes the warnings
# JSON for a plain numpy namespace run to be over 500MB.
# This will lose information about what order the warnings were issued in,
# but that isn't particularly helpful anyway since the warning metadata
# doesn't store a full stack of where it was issued from. The resulting
# warnings will be in order of the first time each warning is issued since
# collections.Counter is ordered just like dict().
counted_warnings = Counter([frozenset(i.items()) for i in json_report.get('warnings', dict())])
deduped_warnings = [{**dict(i), 'count': counted_warnings[i]} for i in counted_warnings]
json_report['warnings'] = deduped_warnings