]> git.proxmox.com Git - rustc.git/blame - src/test/rustdoc-json/compare.py
New upstream version 1.50.0+dfsg1
[rustc.git] / src / test / rustdoc-json / compare.py
CommitLineData
fc512014
XL
1#!/usr/bin/env python
2
3# This script can check that an expected json blob is a subset of what actually gets produced.
4# The comparison is independent of the value of IDs (which are unstable) and instead uses their
5# relative ordering to check them against eachother by looking them up in their respective blob's
6# `index` or `paths` mappings. To add a new test run `rustdoc --output-format json -o . yourtest.rs`
7# and then create `yourtest.expected` by stripping unnecessary details from `yourtest.json`. If
8# you're on windows, replace `\` with `/`.
9
10import copy
11import sys
12import json
13import types
14
15# Used instead of the string ids when used as references.
16# Not used as keys in `index` or `paths`
17class ID(str):
18 pass
19
20
21class SubsetException(Exception):
22 def __init__(self, msg, trace):
23 self.msg = msg
24 self.trace = msg
25 super().__init__("{}: {}".format(trace, msg))
26
27
28def check_subset(expected_main, actual_main, base_dir):
29 expected_index = expected_main["index"]
30 expected_paths = expected_main["paths"]
31 actual_index = actual_main["index"]
32 actual_paths = actual_main["paths"]
33 already_checked = set()
34
35 def _check_subset(expected, actual, trace):
36 expected_type = type(expected)
37 actual_type = type(actual)
38
39 if actual_type is str:
40 actual = normalize(actual).replace(base_dir, "$TEST_BASE_DIR")
41
42 if expected_type is not actual_type:
43 raise SubsetException(
44 "expected type `{}`, got `{}`".format(expected_type, actual_type), trace
45 )
46
47
48 if expected_type in (int, bool, str) and expected != actual:
49 raise SubsetException("expected `{}`, got: `{}`".format(expected, actual), trace)
50 if expected_type is dict:
51 for key in expected:
52 if key not in actual:
53 raise SubsetException(
54 "Key `{}` not found in output".format(key), trace
55 )
56 new_trace = copy.deepcopy(trace)
57 new_trace.append(key)
58 _check_subset(expected[key], actual[key], new_trace)
59 elif expected_type is list:
60 expected_elements = len(expected)
61 actual_elements = len(actual)
62 if expected_elements != actual_elements:
63 raise SubsetException(
64 "Found {} items, expected {}".format(
65 expected_elements, actual_elements
66 ),
67 trace,
68 )
69 for expected, actual in zip(expected, actual):
70 new_trace = copy.deepcopy(trace)
71 new_trace.append(expected)
72 _check_subset(expected, actual, new_trace)
73 elif expected_type is ID and expected not in already_checked:
74 already_checked.add(expected)
75 _check_subset(
76 expected_index.get(expected, {}), actual_index.get(actual, {}), trace
77 )
78 _check_subset(
79 expected_paths.get(expected, {}), actual_paths.get(actual, {}), trace
80 )
81
82 _check_subset(expected_main["root"], actual_main["root"], [])
83
84
85def rustdoc_object_hook(obj):
86 # No need to convert paths, index and external_crates keys to ids, since
87 # they are the target of resolution, and never a source itself.
88 if "id" in obj and obj["id"]:
89 obj["id"] = ID(obj["id"])
90 if "root" in obj:
91 obj["root"] = ID(obj["root"])
92 if "items" in obj:
93 obj["items"] = [ID(id) for id in obj["items"]]
94 if "variants" in obj:
95 obj["variants"] = [ID(id) for id in obj["variants"]]
96 if "fields" in obj:
97 obj["fields"] = [ID(id) for id in obj["fields"]]
98 if "impls" in obj:
99 obj["impls"] = [ID(id) for id in obj["impls"]]
100 if "implementors" in obj:
101 obj["implementors"] = [ID(id) for id in obj["implementors"]]
102 if "links" in obj:
103 obj["links"] = {s: ID(id) for s, id in obj["links"]}
104 if "variant_kind" in obj and obj["variant_kind"] == "struct":
105 obj["variant_inner"] = [ID(id) for id in obj["variant_inner"]]
106 return obj
107
108
109def main(expected_fpath, actual_fpath, base_dir):
110 print(
111 "checking that {} is a logical subset of {}".format(
112 expected_fpath, actual_fpath
113 )
114 )
115 with open(expected_fpath) as expected_file:
116 expected_main = json.load(expected_file, object_hook=rustdoc_object_hook)
117 with open(actual_fpath) as actual_file:
118 actual_main = json.load(actual_file, object_hook=rustdoc_object_hook)
119 check_subset(expected_main, actual_main, base_dir)
120 print("all checks passed")
121
122def normalize(s):
123 return s.replace('\\', '/')
124
125if __name__ == "__main__":
126 if len(sys.argv) < 4:
127 print("Usage: `compare.py expected.json actual.json test-dir`")
128 else:
129 main(sys.argv[1], sys.argv[2], normalize(sys.argv[3]))