Skip to content

Commit f5e1a73

Browse files
committed
rustdoc: Further improve chapters and sections on testing
1 parent 1652578 commit f5e1a73

File tree

8 files changed

+200
-127
lines changed

8 files changed

+200
-127
lines changed

src/SUMMARY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@
101101
- [Rustdoc internals](./rustdoc-internals.md)
102102
- [Search](./rustdoc-internals/search.md)
103103
- [The `rustdoc` test suite](./rustdoc-internals/rustdoc-test-suite.md)
104+
- [The `rustdoc-gui` test suite](./rustdoc-internals/rustdoc-gui-test-suite.md)
105+
- [The `rustdoc-json` test suite](./rustdoc-internals/rustdoc-json-test-suite.md)
104106
- [Autodiff internals](./autodiff/internals.md)
105107
- [Installation](./autodiff/installation.md)
106108
- [How to debug](./autodiff/debugging.md)

src/rustdoc-internals.md

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -270,35 +270,6 @@ in `test.rs` is the function `make_test`, which is where hand-written
270270
Some extra reading about `make_test` can be found
271271
[here](https://quietmisdreavus.net/code/2018/02/23/how-the-doctests-get-made/).
272272

273-
## Dotting i's And Crossing t's
274-
275-
So that's `rustdoc`'s code in a nutshell, but there's more things in the
276-
compiler that deal with it. Since we have the full `compiletest` suite at hand,
277-
there's a set of tests in `tests/rustdoc` that make sure the final `HTML` is
278-
what we expect in various situations. These tests also use a supplementary
279-
script, `src/etc/htmldocck.py`, that allows it to look through the final `HTML`
280-
using `XPath` notation to get a precise look at the output. The full
281-
description of all the commands available to `rustdoc` tests (e.g. [`@has`] and
282-
[`@matches`]) is in [`htmldocck.py`].
283-
284-
To use multiple crates in a `rustdoc` test, add `//@ aux-build:filename.rs`
285-
to the top of the test file. `filename.rs` should be placed in an `auxiliary`
286-
directory relative to the test file with the comment. If you need to build
287-
docs for the auxiliary file, use `//@ build-aux-docs`.
288-
289-
In addition, there are separate tests for the search index and `rustdoc`'s
290-
ability to query it. The files in `tests/rustdoc-js` each contain a
291-
different search query and the expected results, broken out by search tab.
292-
These files are processed by a script in `src/tools/rustdoc-js` and the `Node.js`
293-
runtime. These tests don't have as thorough of a writeup, but a broad example
294-
that features results in all tabs can be found in `basic.js`. The basic idea is
295-
that you match a given `QUERY` with a set of `EXPECTED` results, complete with
296-
the full item path of each item.
297-
298-
[`@has`]: https://github.com/rust-lang/rust/blob/master/src/etc/htmldocck.py#L39
299-
[`@matches`]: https://github.com/rust-lang/rust/blob/master/src/etc/htmldocck.py#L44
300-
[`htmldocck.py`]: https://github.com/rust-lang/rust/blob/master/src/etc/htmldocck.py
301-
302273
## Testing Locally
303274

304275
Some features of the generated `HTML` documentation might require local
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# The `rustdoc-gui` test suite
2+
3+
> **FIXME**: This section is a stub. Please help us flesh it out!
4+
5+
This page is about the test suite named `rustdoc-gui` used to test the "GUI" of `rustdoc` (i.e., the HTML/JS/CSS as rendered in a browser).
6+
For other rustdoc-specific test suites, see [Rustdoc test suites].
7+
8+
These use a NodeJS-based tool called [`browser-UI-test`] that uses [puppeteer] to run tests in a headless browser and check rendering and interactivity. For information on how to write this form of test, see [`tests/rustdoc-gui/README.md`][rustdoc-gui-readme] as well as [the description of the `.goml` format][goml-script]
9+
10+
[Rustdoc test suites]: ../tests/compiletest.md#rustdoc-test-suites
11+
[`browser-UI-test`]: https://github.com/GuillaumeGomez/browser-UI-test/
12+
[puppeteer]: https://pptr.dev/
13+
[rustdoc-gui-readme]: https://github.com/rust-lang/rust/blob/master/tests/rustdoc-gui/README.md
14+
[goml-script]: https://github.com/GuillaumeGomez/browser-UI-test/blob/master/goml-script.md
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# The `rustdoc-json` test suite
2+
3+
> **FIXME**: This section is a stub. It will be populated by [PR #2422](https://github.com/rust-lang/rustc-dev-guide/pull/2422/).
Lines changed: 134 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,190 @@
11
# The `rustdoc` test suite
22

3-
This page is specifically about the test suite named `rustdoc`.
4-
For other test suites used for testing rustdoc, see [Rustdoc tests](../rustdoc.md#tests).
3+
This page is about the test suite named `rustdoc` used to test the HTML output of `rustdoc`.
4+
For other rustdoc-specific test suites, see [Rustdoc test suites].
55

6-
The `rustdoc` test suite is specifically used to test the HTML output of rustdoc.
6+
Each test file in this test suite is simply a Rust source file `file.rs` sprinkled with
7+
so-called *directives* located inside normal Rust code comments.
8+
These come in two flavors: *Compiletest* and *HtmlDocCk*.
79

8-
This is achieved by means of `htmldocck.py`, a custom checker script that leverages [XPath].
10+
To learn more about the former, read [Compiletest directives].
11+
For the latter, continue reading.
912

10-
[XPath]: https://en.wikipedia.org/wiki/XPath
13+
Internally, [`compiletest`] invokes the supplementary checker script [`htmldocck.py`].
1114

12-
## Directives
13-
Directives to htmldocck are similar to those given to `compiletest` in that they take the form of `//@` comments.
15+
[Rustdoc test suites]: ../tests/compiletest.md#rustdoc-test-suites
16+
[`compiletest`]: ../tests/compiletest.md
17+
[`htmldocck.py`]: https://github.com/rust-lang/rust/blob/master/src/etc/htmldocck.py
1418

15-
In addition to the directives listed here,
16-
`rustdoc` tests also support most
17-
[compiletest directives](../tests/directives.html).
19+
## HtmlDocCk Directives
1820

19-
All `PATH`s in directives are relative to the rustdoc output directory (`build/TARGET/test/rustdoc/TESTNAME`),
20-
so it is conventional to use a `#![crate_name = "foo"]` attribute to avoid
21-
having to write a long crate name multiple times.
22-
To avoid repetition, `-` can be used in any `PATH` argument to re-use the previous `PATH` argument.
21+
Directives to HtmlDocCk are assertions that place constraints on the generated HTML.
22+
They look similar to those given to `compiletest` in that they take the form of `//@` comments
23+
but ultimately, they are completey distinct and processed by different programs.
2324

24-
All arguments take the form of quoted strings
25-
(both single and double quotes are supported),
26-
with the exception of `COUNT` and the special `-` form of `PATH`.
25+
[XPath] is used to query parts of the HTML document tree.
26+
27+
**Introductory example**:
28+
29+
```rust,ignore (illustrative)
30+
//@ has file/type.Alias.html
31+
//@ has - '//*[@class="rust item-decl"]//code' 'type Alias = Option<i32>;'
32+
pub type Alias = Option<i32>;
33+
```
34+
35+
Here, we check that documentation generated for crate `file` contains a page for the
36+
public type alias `Alias` where the code block that is found at the top contains the
37+
expected rendering of the item. The `//*[@class="rust item-decl"]//code` is an XPath
38+
expression.
2739

28-
Directives are assertions that place constraints on the generated HTML.
40+
Conventionally, you place these directives directly above the thing they are meant to test.
41+
Technically speaking however, they don't need to be as HtmlDocCk only looks for the directives.
2942

30-
All directives (except `files`) can be negated by putting a `!` in front of their name.
43+
All directives take a `PATH` argument.
44+
To avoid repetition, `-` can be passed to it to re-use the previous `PATH` argument.
45+
Since the path contains the name of the crate, it is conventional to add a
46+
`#![crate_name = "foo"]` attribute to the crate root to shorten the resulting path.
47+
48+
All arguments take the form of shell-style (single or double) quoted strings,
49+
with the exception of `COUNT` and the special `-` form of `PATH`.
50+
51+
All directives (except `files`) can be *negated* by putting a `!` in front of their name.
52+
Before you add negated directives, please read about [their caveats](#caveats).
3153

3254
Similar to shell commands,
3355
directives can extend across multiple lines if their last char is `\`.
3456
In this case, the start of the next line should be `//`, with no `@`.
3557

36-
For example, `//@ !has 'foo/struct.Bar.html'` checks that crate `foo` does not have a page for a struct named `Bar` in the crate root.
58+
Use the special string `{{channel}}` in XPaths, `PATTERN` arguments and [snapshot files](#snapshot)
59+
if you'd like to refer to the URL `https://doc.rust-lang.org/CHANNEL` where `CHANNEL` refers to the
60+
current release channel (e.g, `stable` or `nightly`).
61+
62+
Listed below are all possible directives:
63+
64+
[XPath]: https://en.wikipedia.org/wiki/XPath
3765

3866
### `has`
3967

40-
Usage 1: `//@ has PATH`
41-
Usage 2: `//@ has PATH XPATH PATTERN`
68+
> Usage 1: `//@ has PATH`
4269
43-
In the first form, `has` checks that a given file exists.
70+
Check that the file given by `PATH` exists.
4471

45-
In the second form, `has` is an alias for `matches`,
46-
except `PATTERN` is a whitespace-normalized[^1] string instead of a regex.
72+
> Usage 2: `//@ has PATH XPATH PATTERN`
4773
48-
### `matches`
74+
Checks that the text of each element / attribute / text selected by `XPATH` in the
75+
whitespace-normalized[^1] file given by `PATH` matches the
76+
(also whitespace-normalized) string `PATTERN`.
77+
78+
**Tip**: If you'd like to avoid whitespace normalization and/or if you'd like to match with a regex,
79+
use `matches` instead.
4980

50-
Usage: `//@ matches PATH XPATH PATTERN`
81+
### `hasraw`
5182

52-
Checks that the text of each element selected by `XPATH` in `PATH` matches the python-flavored regex `PATTERN`.
83+
> Usage: `//@ hasraw PATH PATTERN`
5384
54-
### `matchesraw`
85+
Checks that the contents of the whitespace-normalized[^1] file given by `PATH`
86+
matches the (also whitespace-normalized) string `PATTERN`.
5587

56-
Usage: `//@ matchesraw PATH PATTERN`
88+
**Tip**: If you'd like to avoid whitespace normalization and / or if you'd like to match with a
89+
regex, use `matchesraw` instead.
5790

58-
Checks that the contents of the file `PATH` matches the regex `PATTERN`.
91+
### `matches`
5992

60-
### `hasraw`
93+
> Usage: `//@ matches PATH XPATH PATTERN`
6194
62-
Usage: `//@ hasraw PATH PATTERN`
95+
Checks that the text of each element / attribute / text selected by `XPATH` in the
96+
file given by `PATH` matches the Python-flavored[^2] regex `PATTERN`.
6397

64-
Same as `matchesraw`, except `PATTERN` is a whitespace-normalized[^1] string instead of a regex.
98+
### `matchesraw`
99+
100+
> Usage: `//@ matchesraw PATH PATTERN`
101+
102+
Checks that the contents of the file given by `PATH` matches the
103+
Python-flavored[^2] regex `PATTERN`.
65104

66105
### `count`
67106

68-
Usage: `//@ count PATH XPATH COUNT`
107+
> Usage: `//@ count PATH XPATH COUNT`
69108
70-
Checks that there are exactly `COUNT` matches for `XPATH` within the file `PATH`.
109+
Checks that there are exactly `COUNT` matches for `XPATH` within the file given by `PATH`.
71110

72111
### `snapshot`
73112

74-
Usage: `//@ snapshot NAME PATH XPATH`
113+
> Usage: `//@ snapshot NAME PATH XPATH`
75114
76-
Creates a snapshot test named NAME.
77-
A snapshot test captures a subtree of the DOM, at the location
78-
determined by the XPath, and compares it to a pre-recorded value
79-
in a file. The file's name is the test's name with the `.rs` extension
80-
replaced with `.NAME.html`, where NAME is the snapshot's name.
115+
Checks that the element / text selected by `XPATH` in the file given by `PATH` matches the
116+
pre-recorded subtree or text (the "snapshot") in file `FILE_STEM.NAME.html` where `FILE_STEM`
117+
is the file stem of the test file.
81118

82-
htmldocck supports the `--bless` option to accept the current subtree
83-
as expected, saving it to the file determined by the snapshot's name.
84-
compiletest's `--bless` flag is forwarded to htmldocck.
119+
Pass the `--bless` option to `compiletest` to accept the current subtree/text as expected.
120+
This will overwrite the aforementioned file (or create it if it doesn't exist). It will
121+
automatically normalize the channel-dependent URL `https://doc.rust-lang.org/CHANNEL` to
122+
the special string `{{channel}}`.
85123

86124
### `has-dir`
87125

88-
Usage: `//@ has-dir PATH`
126+
> Usage: `//@ has-dir PATH`
89127
90-
Checks for the existence of directory `PATH`.
128+
Checks for the existence of the directory given by `PATH`.
91129

92130
### `files`
93131

94-
Usage: `//@ files PATH ENTRIES`
132+
> Usage: `//@ files PATH ENTRIES`
133+
134+
Checks that the directory given by `PATH` contains exactly `ENTRIES`.
135+
`ENTRIES` is a Python-like list of strings inside a quoted string.
136+
137+
**Example**: `//@ files "foo/bar" '["index.html", "sidebar-items.js"]'`
138+
139+
[^1]: Whitespace normalization means that all spans of consecutive whitespace are replaced with a single space.
140+
[^2]: They are Unicode aware (flag `UNICODE` is set), match case-sensitively and in single-line mode.
141+
142+
## Compiletest Directives (Brief)
143+
144+
As mentioned in the introduction, you also have access to [compiletest directives].
145+
Most importantly, they allow you to register auxiliary crates and
146+
to pass flags to the `rustdoc` binary under test.
147+
It's *strongly recommended* to read that chapter if you don't know anything about them yet.
148+
149+
Here are some details that are relevant to this test suite specifically:
95150

96-
Checks that the directory `PATH` contains exactly `ENTRIES`.
97-
`ENTRIES` is a python list of strings inside a quoted string,
98-
as if it were to be parsed by `eval`.
99-
(note that the list is actually parsed by `shlex.split`,
100-
so it cannot contain arbitrary python expressions).
151+
* While you can use both `//@ compile-flags` and `//@ doc-flags` to pass flags to `rustdoc`,
152+
prefer to user the latter to show intent. The former is meant for `rustc`.
153+
* Add `//@ build-aux-docs` to the test file that has auxiliary crates to not only compile the
154+
auxiliaries with `rustc` but to also document them with `rustdoc`
101155

102-
Example: `//@ files "foo/bar" '["index.html", "sidebar-items.js"]'`
156+
## Caveats
103157

104-
[^1]: Whitespace normalization means that all spans of consecutive whitespace are replaced with a single space. The files themselves are also whitespace-normalized.
158+
Testing for the absence of an element or a piece of text is quite fragile and not very future proof.
159+
160+
It's not unusual that the *shape* of the generated HTML document tree changes from time to time.
161+
This includes for example renamings of CSS classes.
162+
163+
Whenever that happens, *positive* checks will either continue to match the intended element /
164+
attribute / text if their XPath expression is general / loose enough and thus test the correct thing
165+
or they won't in which case they would fail forcing the author of the change tolook at them.
166+
167+
Compare that to *negative* checks (e.g., `//@ !has PATH XPATH PATTERN`) which won't fail if their
168+
XPath expression "no longer" matches. The author who changed "the shape" thus won't get notified and
169+
as a result someone else can unintentionally reintroduce `PATTERN` into the generated docs without
170+
the original negative check failing.
171+
172+
**Note**: Please avoid the use of *negated* checks!
173+
174+
**Tip**: If you can't avoid it, please **always** pair it with an analogous positive check in the
175+
immediate vicinity, so people changing "the shape" have a chance to notice and to update the
176+
negated check!
105177

106178
## Limitations
107-
`htmldocck.py` uses the xpath implementation from the standard library.
179+
180+
HtmlDocCk uses the XPath implementation from the Python standard library.
108181
This leads to several limitations:
182+
109183
* All `XPATH` arguments must start with `//` due to a flaw in the implementation.
110184
* Many XPath features (functions, axies, etc.) are not supported.
111185
* Only well-formed HTML can be parsed (hopefully rustdoc doesn't output mismatched tags).
112186

187+
Furthmore, compiletest [revisions] are not supported.
188+
189+
[revisions]: ../tests/compiletest.md#revisions
190+
[compiletest directives]: ../tests/directives.md

src/rustdoc.md

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -67,43 +67,29 @@ does is call the `main()` that's in this crate's `lib.rs`, though.)
6767

6868
## Code structure
6969

70-
* All paths in this section are relative to `src/librustdoc` in the rust-lang/rust repository.
70+
All paths in this section are relative to `src/librustdoc/` in the rust-lang/rust repository.
71+
7172
* Most of the HTML printing code is in `html/format.rs` and `html/render/mod.rs`.
72-
It's in a bunch of `fmt::Display` implementations and supplementary
73-
functions.
74-
* The types that got `Display` impls above are defined in `clean/mod.rs`, right
75-
next to the custom `Clean` trait used to process them out of the rustc HIR.
73+
It's in a bunch of functions returning `impl std::fmt::Display`.
74+
* The data types that get rendered by the functions mentioned above are defined in `clean/types.rs`.
75+
The functions responsible for creating them from the `HIR` and the `rustc_middle::ty` IR
76+
live in `clean/mod.rs`.
7677
* The bits specific to using rustdoc as a test harness are in
7778
`doctest.rs`.
7879
* The Markdown renderer is loaded up in `html/markdown.rs`, including functions
7980
for extracting doctests from a given block of Markdown.
8081
* Frontend CSS and JavaScript are stored in `html/static/`.
82+
* Re. JavaScript, type annotations are written using [TypeScript-flavored JSDoc]
83+
comments and an external `.d.ts` file.
84+
This way, the code itself remains plain, valid JavaScript.
85+
We only use `tsc` as a linter.
8186

82-
## Tests
87+
[TypeScript-flavored JSDoc]: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html
8388

84-
* Tests on search engine and index are located in `tests/rustdoc-js` and `tests/rustdoc-js-std`.
85-
The format is specified
86-
[in the search guide](rustdoc-internals/search.md#testing-the-search-engine).
87-
* Tests on the "UI" of rustdoc (the terminal output it produces when run) are in
88-
`tests/rustdoc-ui`
89-
* Tests on the "GUI" of rustdoc (the HTML, JS, and CSS as rendered in a browser)
90-
are in `tests/rustdoc-gui`. These use a [NodeJS tool called
91-
browser-UI-test](https://github.com/GuillaumeGomez/browser-UI-test/) that uses
92-
puppeteer to run tests in a headless browser and check rendering and
93-
interactivity. For information on how to write this form of test,
94-
see [`tests/rustdoc-gui/README.md`][rustdoc-gui-readme]
95-
as well as [the description of the `.goml` format][goml-script]
96-
* Tests on the structure of rustdoc HTML output are located in `tests/rustdoc`,
97-
where they're handled by the test runner of bootstrap and
98-
the supplementary script `src/etc/htmldocck.py`.
99-
[These tests have several extra directives available to them](./rustdoc-internals/rustdoc-test-suite.md).
100-
* Additionally, JavaScript type annotations are written using [TypeScript-flavored JSDoc]
101-
comments and an external d.ts file. The code itself is plain, valid JavaScript; we only
102-
use tsc as a linter.
89+
## Tests
10390

104-
[TypeScript-flavored JSDoc]: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html
105-
[rustdoc-gui-readme]: https://github.com/rust-lang/rust/blob/master/tests/rustdoc-gui/README.md
106-
[goml-script]: https://github.com/GuillaumeGomez/browser-UI-test/blob/master/goml-script.md
91+
`rustdoc`'s integration tests are split across several test suites.
92+
See [Rustdoc tests suites](tests/compiletest.md#rustdoc-test-suites) for more details.
10793

10894
## Constraints
10995

0 commit comments

Comments
 (0)