Skip to content

Commit c8b4e3c

Browse files
P1n3appl3jyn514
authored andcommitted
Add expected output tests
1 parent 8156b9a commit c8b4e3c

File tree

8 files changed

+567
-1
lines changed

8 files changed

+567
-1
lines changed

src/librustdoc/json/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//! docs for usage and details.
66
77
mod conversions;
8-
mod types;
8+
pub mod types;
99

1010
use std::cell::RefCell;
1111
use std::fs::File;

src/test/run-make-fulldeps/rustdoc-json/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
tests: *.rs
44
$(RUSTDOC) $< -o $(TMPDIR) --output-format json
55
$(PYTHON) check_missing_items.py $(TMPDIR)/$(basename $<).json
6+
$(PYTHON) compare.py $(basename $<).expected $(TMPDIR)/$(basename $<).json

src/test/run-make-fulldeps/rustdoc-json/check_missing_items.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
#!/usr/bin/env python
22

3+
# This test ensures that every ID in the produced json actually resolves to an item either in `index` or `paths`.
4+
# It DOES NOT check that the structure of the produced json is actually in any way correct,
5+
# for example an empty map would pass.
6+
37
import sys
48
import json
59

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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`.
8+
9+
import sys
10+
import json
11+
import types
12+
13+
# Used instead of the string ids when used as references.
14+
# Not used as keys in `index` or `paths`
15+
class ID(str):
16+
pass
17+
18+
19+
class SubsetException(Exception):
20+
def __init__(self, msg, trace):
21+
self.msg = msg
22+
self.trace = msg
23+
super().__init__("{}: {}".format(trace, msg))
24+
25+
26+
def check_subset(expected_main, actual_main):
27+
expected_index = expected_main["index"]
28+
expected_paths = expected_main["paths"]
29+
actual_index = actual_main["index"]
30+
actual_paths = actual_main["paths"]
31+
already_checked = set()
32+
33+
def _check_subset(expected, actual, trace):
34+
expected_type = type(expected)
35+
actual_type = type(actual)
36+
if expected_type is not actual_type:
37+
raise SubsetException(
38+
"expected type `{}`, got `{}`".format(expected_type, actual_type), trace
39+
)
40+
if expected_type in (str, int, bool) and expected != actual:
41+
raise SubsetException("expected `{}`, got: `{}`".format(expected, actual), trace)
42+
if expected_type is dict:
43+
for key in expected:
44+
if key not in actual:
45+
raise SubsetException("Key `{}` not found in output".format(key))
46+
new_trace = trace.copy()
47+
new_trace.append(key)
48+
_check_subset(expected[key], actual[key], new_trace)
49+
elif expected_type is list:
50+
expected_elements = len(expected)
51+
actual_elements = len(actual)
52+
if expected_elements != actual_elements:
53+
raise SubsetException(
54+
"Found {} items, expected {}".format(expected_elements, actual_elements)
55+
)
56+
for expected, actual in zip(expected, actual):
57+
new_trace = trace.copy()
58+
new_trace.append(expected)
59+
_check_subset(expected, actual, new_trace)
60+
elif expected_type is ID and expected not in already_checked:
61+
already_checked.add(expected)
62+
_check_subset(expected_index.get(expected, {}), actual_index.get(actual, {}), trace)
63+
_check_subset(expected_paths.get(expected, {}), actual_paths.get(actual, {}), trace)
64+
65+
_check_subset(expected_main, actual_main, [])
66+
67+
68+
def rustdoc_object_hook(obj):
69+
# No need to convert paths, index and external_crates keys to ids, since
70+
# they are the target of resolution, and never a source itself.
71+
if "id" in obj:
72+
obj["id"] = ID(id)
73+
if "root" in obj:
74+
obj["root"] = ID(id)
75+
if "items" in obj:
76+
obj["items"] = [ID(id) for id in obj["items"]]
77+
if "variants" in obj:
78+
obj["variants"] = [ID(id) for id in obj["variants"]]
79+
if "fields" in obj:
80+
obj["fields"] = [ID(id) for id in obj["fields"]]
81+
if "impls" in obj:
82+
obj["impls"] = [ID(id) for id in obj["impls"]]
83+
if "implementors" in obj:
84+
obj["implementors"] = [ID(id) for id in obj["implementors"]]
85+
if "links" in obj:
86+
obj["links"] = {s: ID(id) for s, id in obj["links"]}
87+
if "variant_kind" in obj and obj["variant_kind"] == "struct":
88+
obj["variant_inner"] = [ID(id) for id in obj["variant_inner"]]
89+
return obj
90+
91+
92+
def main(expected_fpath, actual_fpath):
93+
print("checking that {} is a logical subset of {}".format(expected_fpath, actual_fpath))
94+
with open(expected_fpath) as expected_file:
95+
expected_main = json.load(expected_file, object_hook=rustdoc_object_hook)
96+
with open(actual_fpath) as actual_file:
97+
actual_main = json.load(actual_file, object_hook=rustdoc_object_hook)
98+
check_subset(expected_main, actual_main)
99+
print("all checks passed")
100+
101+
102+
if __name__ == "__main__":
103+
if len(sys.argv) < 3:
104+
print("Usage: `compare.py expected.json actual.json`")
105+
main(sys.argv[1], sys.argv[2])
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
{
2+
"root": "0:0",
3+
"version": null,
4+
"includes_private": false,
5+
"index": {
6+
"0:6": {
7+
"crate_id": 0,
8+
"name": "bar",
9+
"source": {
10+
"filename": "modules.rs",
11+
"begin": [
12+
5,
13+
4
14+
],
15+
"end": [
16+
7,
17+
5
18+
]
19+
},
20+
"visibility": "public",
21+
"docs": "",
22+
"links": {},
23+
"attrs": [],
24+
"deprecation": null,
25+
"kind": "module",
26+
"inner": {
27+
"is_crate": false,
28+
"items": [
29+
"0:7"
30+
]
31+
}
32+
},
33+
"0:3": {
34+
"crate_id": 0,
35+
"name": null,
36+
"source": {
37+
"filename": "modules.rs",
38+
"begin": [
39+
1,
40+
0
41+
],
42+
"end": [
43+
1,
44+
27
45+
]
46+
},
47+
"visibility": "public",
48+
"docs": "",
49+
"links": {},
50+
"attrs": [],
51+
"deprecation": null,
52+
"kind": "import",
53+
"inner": {
54+
"source": "foo::bar",
55+
"name": "foobar",
56+
"id": "0:6",
57+
"glob": false
58+
}
59+
},
60+
"0:0": {
61+
"crate_id": 0,
62+
"name": "modules",
63+
"source": {
64+
"filename": "modules.rs",
65+
"begin": [
66+
1,
67+
0
68+
],
69+
"end": [
70+
8,
71+
1
72+
]
73+
},
74+
"visibility": "public",
75+
"docs": "",
76+
"links": {},
77+
"attrs": [],
78+
"deprecation": null,
79+
"kind": "module",
80+
"inner": {
81+
"is_crate": true,
82+
"items": [
83+
"0:3",
84+
"0:4",
85+
"0:5"
86+
]
87+
}
88+
},
89+
"0:5": {
90+
"crate_id": 0,
91+
"name": "foo",
92+
"source": {
93+
"filename": "modules.rs",
94+
"begin": [
95+
4,
96+
0
97+
],
98+
"end": [
99+
8,
100+
1
101+
]
102+
},
103+
"visibility": "public",
104+
"docs": "",
105+
"links": {},
106+
"attrs": [],
107+
"deprecation": null,
108+
"kind": "module",
109+
"inner": {
110+
"is_crate": false,
111+
"items": [
112+
"0:6"
113+
]
114+
}
115+
},
116+
"0:4": {
117+
"crate_id": 0,
118+
"name": null,
119+
"source": {
120+
"filename": "modules.rs",
121+
"begin": [
122+
2,
123+
0
124+
],
125+
"end": [
126+
2,
127+
23
128+
]
129+
},
130+
"visibility": "public",
131+
"docs": "",
132+
"links": {},
133+
"attrs": [],
134+
"deprecation": null,
135+
"kind": "import",
136+
"inner": {
137+
"source": "foobar::baz",
138+
"name": "baz",
139+
"id": "0:7",
140+
"glob": true
141+
}
142+
},
143+
"0:7": {
144+
"crate_id": 0,
145+
"name": "baz",
146+
"source": {
147+
"filename": "modules.rs",
148+
"begin": [
149+
6,
150+
8
151+
],
152+
"end": [
153+
6,
154+
22
155+
]
156+
},
157+
"visibility": "public",
158+
"docs": "",
159+
"links": {},
160+
"attrs": [],
161+
"deprecation": null,
162+
"kind": "module",
163+
"inner": {
164+
"is_crate": false,
165+
"items": []
166+
}
167+
}
168+
},
169+
"paths": {},
170+
"format_version": 1
171+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
pub use foo::bar as foobar;
2+
pub use foobar::baz::*;
3+
4+
pub mod foo {
5+
pub mod bar {
6+
pub mod baz {}
7+
}
8+
}

0 commit comments

Comments
 (0)