|
1 | 1 | # The `rustdoc` test suite
|
2 | 2 |
|
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]. |
5 | 5 |
|
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*. |
7 | 9 |
|
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. |
9 | 12 |
|
10 |
| -[XPath]: https://en.wikipedia.org/wiki/XPath |
| 13 | +Internally, [`compiletest`] invokes the supplementary checker script [`htmldocck.py`]. |
11 | 14 |
|
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 |
14 | 18 |
|
15 |
| -In addition to the directives listed here, |
16 |
| -`rustdoc` tests also support most |
17 |
| -[compiletest directives](../tests/directives.html). |
| 19 | +## HtmlDocCk Directives |
18 | 20 |
|
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 are similar to those given to `compiletest` in that they take the form of `//@` comments. |
| 23 | +[XPath] is used query parts of the HTML document tree. |
23 | 24 |
|
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 | +**Introductory example**: |
| 26 | + |
| 27 | +```rust,ignore (illustrative) |
| 28 | +//@ has file/type.Alias.html |
| 29 | +//@ has - '//*[@class="rust item-decl"]//code' 'type Alias = Option<i32>;' |
| 30 | +pub type Alias = Option<i32>; |
| 31 | +``` |
27 | 32 |
|
28 |
| -Directives are assertions that place constraints on the generated HTML. |
| 33 | +Here, we check that documentation generated for crate `file` contains a page for the |
| 34 | +public type alias `Alias` where the code block that is found at the top contains the |
| 35 | +expected rendering of the item. |
29 | 36 |
|
30 |
| -All directives (except `files`) can be negated by putting a `!` in front of their name. |
| 37 | +Conventionally, you place these directives directly above the thing they are meant to test. |
| 38 | +Technically speaking however, they don't need to as HtmlDocCk only looks for the directives. |
| 39 | + |
| 40 | +All directives take a `PATH` argument. |
| 41 | +To avoid repetition, `-` can be passed to it to re-use the previous `PATH` argument. |
| 42 | +Since the path contains the name of the crate, it is conventional to add a |
| 43 | +`#![crate_name = "foo"]` attribute to shorten the resulting path. |
| 44 | + |
| 45 | +All arguments take the form of quoted strings (both single and double quotes are supported), |
| 46 | +with the exception of `COUNT` and the special `-` form of `PATH`. |
| 47 | + |
| 48 | +All directives (except `files`) can be *negated* by putting a `!` in front of their name. |
| 49 | +Before you add negated directives, please read about [their caveats](#caveats). |
31 | 50 |
|
32 | 51 | Similar to shell commands,
|
33 | 52 | directives can extend across multiple lines if their last char is `\`.
|
34 | 53 | In this case, the start of the next line should be `//`, with no `@`.
|
35 | 54 |
|
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. |
| 55 | +Use the special string `{{channel}}` in XPaths, `PATTERN` arguments and [snapshot files](#snapshot) |
| 56 | +if you'd like to refer to the URL `https://doc.rust-lang.org/CHANNEL` where `CHANNEL` refers to the |
| 57 | +current release channel (e.g, `stable` or `nightly`). |
| 58 | + |
| 59 | +Listed below are all possible directives: |
| 60 | + |
| 61 | +[XPath]: https://en.wikipedia.org/wiki/XPath |
37 | 62 |
|
38 | 63 | ### `has`
|
39 | 64 |
|
40 |
| -Usage 1: `//@ has PATH` |
41 |
| -Usage 2: `//@ has PATH XPATH PATTERN` |
| 65 | +> Usage 1: `//@ has PATH` |
42 | 66 |
|
43 |
| -In the first form, `has` checks that a given file exists. |
| 67 | +Check that the file given by `PATH` exists. |
44 | 68 |
|
45 |
| -In the second form, `has` is an alias for `matches`, |
46 |
| -except `PATTERN` is a whitespace-normalized[^1] string instead of a regex. |
| 69 | +> Usage 2: `//@ has PATH XPATH PATTERN` |
47 | 70 |
|
48 |
| -### `matches` |
| 71 | +Checks that the text of each element / attribute / text selected by `XPATH` in the |
| 72 | +whitespace-normalized[^1] file given by `PATH` matches the |
| 73 | +(also whitespace-normalized) string `PATTERN`. |
49 | 74 |
|
50 |
| -Usage: `//@ matches PATH XPATH PATTERN` |
| 75 | +**Tip**: If you'd like to avoid whitespace normalization and/or if you'd like to match with a regex, |
| 76 | +use `matches` instead. |
51 | 77 |
|
52 |
| -Checks that the text of each element selected by `XPATH` in `PATH` matches the python-flavored regex `PATTERN`. |
| 78 | +### `hasraw` |
53 | 79 |
|
54 |
| -### `matchesraw` |
| 80 | +> Usage: `//@ hasraw PATH PATTERN` |
55 | 81 |
|
56 |
| -Usage: `//@ matchesraw PATH PATTERN` |
| 82 | +Checks that the contents of the whitespace-normalized[^1] file given by `PATH` |
| 83 | +matches the (also whitespace-normalized) string `PATTERN`. |
57 | 84 |
|
58 |
| -Checks that the contents of the file `PATH` matches the regex `PATTERN`. |
| 85 | +**Tip**: If you'd like to avoid whitespace normalization and / or if you'd like to match with a |
| 86 | +regex, use `matchesraw` instead. |
59 | 87 |
|
60 |
| -### `hasraw` |
| 88 | +### `matches` |
61 | 89 |
|
62 |
| -Usage: `//@ hasraw PATH PATTERN` |
| 90 | +> Usage: `//@ matches PATH XPATH PATTERN` |
63 | 91 |
|
64 |
| -Same as `matchesraw`, except `PATTERN` is a whitespace-normalized[^1] string instead of a regex. |
| 92 | +Checks that the text of each element / attribute / text selected by `XPATH` in the |
| 93 | +file given by `PATH` matches the Python-flavored regex[^2] `PATTERN`. |
| 94 | + |
| 95 | +### `matchesraw` |
| 96 | + |
| 97 | +> Usage: `//@ matchesraw PATH PATTERN` |
| 98 | +
|
| 99 | +Checks that the contents of the file given by `PATH` matches the |
| 100 | +Python-flavored regex[^2] `PATTERN`. |
65 | 101 |
|
66 | 102 | ### `count`
|
67 | 103 |
|
68 |
| -Usage: `//@ count PATH XPATH COUNT` |
| 104 | +> Usage: `//@ count PATH XPATH COUNT` |
69 | 105 |
|
70 |
| -Checks that there are exactly `COUNT` matches for `XPATH` within the file `PATH`. |
| 106 | +Checks that there are exactly `COUNT` matches for `XPATH` within the file given by `PATH`. |
71 | 107 |
|
72 | 108 | ### `snapshot`
|
73 | 109 |
|
74 |
| -Usage: `//@ snapshot NAME PATH XPATH` |
| 110 | +> Usage: `//@ snapshot NAME PATH XPATH` |
75 | 111 |
|
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. |
| 112 | +Checks that the element / text selected by `XPATH` in the file given by `PATH` matches the |
| 113 | +pre-recorded subtree or text (the "snapshot") in file `FILE_STEM.NAME.html` where `FILE_STEM` |
| 114 | +is the file stem of the test file. |
81 | 115 |
|
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. |
| 116 | +Pass the `--bless` option to `compiletest` to accept the current subtree/text as expected. |
| 117 | +This will overwrite the aforementioned file (or create it if it doesn't exist). It will |
| 118 | +automatically normalize the channel-dependent URL `https://doc.rust-lang.org/CHANNEL` to |
| 119 | +the special string `{{channel}}`. |
85 | 120 |
|
86 | 121 | ### `has-dir`
|
87 | 122 |
|
88 |
| -Usage: `//@ has-dir PATH` |
| 123 | +> Usage: `//@ has-dir PATH` |
89 | 124 |
|
90 |
| -Checks for the existence of directory `PATH`. |
| 125 | +Checks for the existence of the directory given by `PATH`. |
91 | 126 |
|
92 | 127 | ### `files`
|
93 | 128 |
|
94 |
| -Usage: `//@ files PATH ENTRIES` |
| 129 | +> Usage: `//@ files PATH ENTRIES` |
| 130 | +
|
| 131 | +Checks that the directory given by `PATH` contains exactly `ENTRIES`. |
| 132 | +`ENTRIES` is a Python-like list of strings inside a quoted string. |
| 133 | + |
| 134 | +**Example**: `//@ files "foo/bar" '["index.html", "sidebar-items.js"]'` |
| 135 | + |
| 136 | +[^1]: Whitespace normalization means that all spans of consecutive whitespace are replaced with a single space. |
| 137 | +[^2]: They are Unicode aware (flag `UNICODE` is set), match case-sensitively and in single-line mode. |
| 138 | + |
| 139 | +## Compiletest Directives (Brief) |
| 140 | + |
| 141 | +As mentioned in the introduction, you also have access to [compiletest directives]. |
| 142 | +Most importantly, they allow you to register auxiliary crates and |
| 143 | +to pass flags to the `rustdoc` binary under test. |
| 144 | +It's *strongly recommended* to read that chapter if you don't know anything about them yet. |
| 145 | + |
| 146 | +Here are some details that are relevant to this test suite specifically: |
95 | 147 |
|
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). |
| 148 | +* While you can use both `//@ compile-flags` and `//@ doc-flags` to pass flags to `rustdoc`, |
| 149 | + prefer to user the latter to show intent. The former is meant for `rustc`. |
| 150 | +* Add `//@ build-aux-docs` to the test file that has auxiliary crates to not only compile the |
| 151 | + auxiliaries with `rustc` but to also document them with `rustdoc` |
101 | 152 |
|
102 |
| -Example: `//@ files "foo/bar" '["index.html", "sidebar-items.js"]'` |
| 153 | +## Caveats |
103 | 154 |
|
104 |
| -[^1]: Whitespace normalization means that all spans of consecutive whitespace are replaced with a single space. The files themselves are also whitespace-normalized. |
| 155 | +Testing for the absence of an element or a piece of text is quite fragile and not very future proof. |
| 156 | + |
| 157 | +It's not unusual that the *shape* of the generated HTML document tree changes from time to time. |
| 158 | +This includes for example renamings of CSS classes. |
| 159 | + |
| 160 | +Whenever that happens, *positive* checks will either continue to match the intended element / |
| 161 | +attribute / text if their XPath selector is general / loose enough and thus test the |
| 162 | +correct thing or they won't in which case they would fail forcing the author of the change to |
| 163 | +look at them. |
| 164 | + |
| 165 | +Compare that to *negative* checks (e.g., `//@ !has PATH XPATH PATTERN`) which won't fail if their |
| 166 | +XPath selector "no longer" matches. The author who changed "the shape" thus won't get notified and |
| 167 | +as a result someone else can unintentionally reintroduce `PATTERN` into the generated docs without |
| 168 | +the original negative check failing. |
| 169 | + |
| 170 | +**Note**: Please avoid the use of *negated* checks! |
| 171 | + |
| 172 | +**Tip**: If you can't avoid it, please **always** pair it with an analogous positive check in the |
| 173 | +immediate vicinity, so people changing "the shape" have a chance to notice the negated check! |
105 | 174 |
|
106 | 175 | ## Limitations
|
107 |
| -`htmldocck.py` uses the xpath implementation from the standard library. |
| 176 | + |
| 177 | +HtmlDocCk uses the XPath implementation from the Python standard library. |
108 | 178 | This leads to several limitations:
|
| 179 | + |
109 | 180 | * All `XPATH` arguments must start with `//` due to a flaw in the implementation.
|
110 | 181 | * Many XPath features (functions, axies, etc.) are not supported.
|
111 | 182 | * Only well-formed HTML can be parsed (hopefully rustdoc doesn't output mismatched tags).
|
112 | 183 |
|
| 184 | +Furthmore, compiletest [revisions] are not supported. |
| 185 | + |
| 186 | +[revisions]: ../tests/compiletest.md#revisions |
| 187 | +[compiletest directives]: ../tests/directives.md |
0 commit comments