Skip to content

Commit 352238d

Browse files
committedFeb 22, 2021
Auto merge of #79979 - GuillaumeGomez:rustdoc-gui-tests, r=Mark-Simulacrum
Rustdoc gui tests This is a reopening of #70533. For this first version, there will be no screenshot comparison. Also, a big change compared to the previous version: the tests are now hosted in the rust repository directly. Since there is no image, it's pretty lightweight to say the least. So now, only remains the nodejs script to run the tests and the tests themselves. Just one thing is missing: where should I put the documentation for these tests? I'm not sure where would be the best place for that. The doc will contain important information like the documentation of the framework used and how to install it (`npm install browser-ui-test`, but still needs to be put somewhere so no one is lost). We'd also need to install the package when running the CI too. For now, it runs as long as we have nodejs installed, but I think we don't it to run in all nodejs targets? cc `@jyn514` r? `@Mark-Simulacrum`
2 parents e952db8 + 20f2497 commit 352238d

File tree

19 files changed

+312
-3
lines changed

19 files changed

+312
-3
lines changed
 

‎.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,6 @@ Session.vim
5050
.cargo
5151
!/src/test/run-make/thumb-none-qemu/example/.cargo
5252
no_llvm_build
53+
**node_modules
54+
**package-lock.json
5355
# Before adding new lines, see the comment at the top.

‎src/bootstrap/builder.rs

+1
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ impl<'a> Builder<'a> {
440440
test::CompiletestTest,
441441
test::RustdocJSStd,
442442
test::RustdocJSNotStd,
443+
test::RustdocGUI,
443444
test::RustdocTheme,
444445
test::RustdocUi,
445446
test::RustdocJson,

‎src/bootstrap/config.rs

+3
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ pub struct Config {
174174
pub mandir: Option<PathBuf>,
175175
pub codegen_tests: bool,
176176
pub nodejs: Option<PathBuf>,
177+
pub npm: Option<PathBuf>,
177178
pub gdb: Option<PathBuf>,
178179
pub python: Option<PathBuf>,
179180
pub cargo_native_static: bool,
@@ -364,6 +365,7 @@ struct Build {
364365
fast_submodules: Option<bool>,
365366
gdb: Option<String>,
366367
nodejs: Option<String>,
368+
npm: Option<String>,
367369
python: Option<String>,
368370
locked_deps: Option<bool>,
369371
vendor: Option<bool>,
@@ -654,6 +656,7 @@ impl Config {
654656
};
655657

656658
config.nodejs = build.nodejs.map(PathBuf::from);
659+
config.npm = build.npm.map(PathBuf::from);
657660
config.gdb = build.gdb.map(PathBuf::from);
658661
config.python = build.python.map(PathBuf::from);
659662
set(&mut config.low_priority, build.low_priority);

‎src/bootstrap/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,10 @@ impl Build {
637637
self.out.join(&*target.triple).join("doc")
638638
}
639639

640+
fn test_out(&self, target: TargetSelection) -> PathBuf {
641+
self.out.join(&*target.triple).join("test")
642+
}
643+
640644
/// Output directory for all documentation for a target
641645
fn compiler_doc_out(&self, target: TargetSelection) -> PathBuf {
642646
self.out.join(&*target.triple).join("compiler-doc")

‎src/bootstrap/mk/Makefile.in

+4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ check-aux:
4545
src/tools/cargo \
4646
src/tools/cargotest \
4747
$(BOOTSTRAP_ARGS)
48+
check-aux-and-gui: check-aux
49+
$(Q)$(BOOTSTRAP) test --stage 2 \
50+
src/test/rustdoc-gui \
51+
$(BOOTSTRAP_ARGS)
4852
check-bootstrap:
4953
$(Q)$(CFG_PYTHON) $(CFG_SRC_DIR)src/bootstrap/bootstrap_test.py
5054
dist:

‎src/bootstrap/sanity.rs

+7
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@ pub fn check(build: &mut Build) {
113113
.or_else(|| cmd_finder.maybe_have("node"))
114114
.or_else(|| cmd_finder.maybe_have("nodejs"));
115115

116+
build.config.npm = build
117+
.config
118+
.npm
119+
.take()
120+
.map(|p| cmd_finder.must_have(p))
121+
.or_else(|| cmd_finder.maybe_have("npm"));
122+
116123
build.config.gdb = build
117124
.config
118125
.gdb

‎src/bootstrap/test.rs

+75
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,78 @@ impl Step for RustdocJSNotStd {
688688
}
689689
}
690690

691+
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
692+
pub struct RustdocGUI {
693+
pub target: TargetSelection,
694+
pub compiler: Compiler,
695+
}
696+
697+
impl Step for RustdocGUI {
698+
type Output = ();
699+
const DEFAULT: bool = true;
700+
const ONLY_HOSTS: bool = true;
701+
702+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
703+
run.path("src/test/rustdoc-gui")
704+
}
705+
706+
fn make_run(run: RunConfig<'_>) {
707+
let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
708+
run.builder.ensure(RustdocGUI { target: run.target, compiler });
709+
}
710+
711+
fn run(self, builder: &Builder<'_>) {
712+
if let (Some(nodejs), Some(npm)) = (&builder.config.nodejs, &builder.config.npm) {
713+
builder.ensure(compile::Std { compiler: self.compiler, target: self.target });
714+
715+
// The goal here is to check if the necessary packages are installed, and if not, we
716+
// display a warning and move on.
717+
let mut command = Command::new(&npm);
718+
command.arg("list").arg("--depth=0");
719+
let lines = command
720+
.output()
721+
.map(|output| String::from_utf8_lossy(&output.stdout).to_string())
722+
.unwrap_or(String::new());
723+
if !lines.contains(&" browser-ui-test@") {
724+
println!(
725+
"warning: rustdoc-gui test suite cannot be run because npm `browser-ui-test` \
726+
dependency is missing",
727+
);
728+
println!(
729+
"If you want to install the `{0}` dependency, run `npm install {0}`",
730+
"browser-ui-test",
731+
);
732+
return;
733+
}
734+
735+
let out_dir = builder.test_out(self.target).join("rustdoc-gui");
736+
let mut command = builder.rustdoc_cmd(self.compiler);
737+
command.arg("src/test/rustdoc-gui/lib.rs").arg("-o").arg(&out_dir);
738+
builder.run(&mut command);
739+
740+
for file in fs::read_dir("src/test/rustdoc-gui").unwrap() {
741+
let file = file.unwrap();
742+
let file_path = file.path();
743+
let file_name = file.file_name();
744+
745+
if !file_name.to_str().unwrap().ends_with(".goml") {
746+
continue;
747+
}
748+
let mut command = Command::new(&nodejs);
749+
command
750+
.arg("src/tools/rustdoc-gui/tester.js")
751+
.arg("--doc-folder")
752+
.arg(out_dir.join("test_docs"))
753+
.arg("--test-file")
754+
.arg(file_path);
755+
builder.run(&mut command);
756+
}
757+
} else {
758+
builder.info("No nodejs found, skipping \"src/test/rustdoc-gui\" tests");
759+
}
760+
}
761+
}
762+
691763
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
692764
pub struct Tidy;
693765

@@ -1048,6 +1120,9 @@ note: if you're sure you want to do this, please open an issue as to why. In the
10481120
if let Some(ref nodejs) = builder.config.nodejs {
10491121
cmd.arg("--nodejs").arg(nodejs);
10501122
}
1123+
if let Some(ref npm) = builder.config.npm {
1124+
cmd.arg("--npm").arg(npm);
1125+
}
10511126

10521127
let mut flags = if is_rustdoc { Vec::new() } else { vec!["-Crpath".to_string()] };
10531128
if !is_rustdoc {

‎src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile

+22-2
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,30 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
1717
libgl1-mesa-dev \
1818
llvm-dev \
1919
libfreetype6-dev \
20-
libexpat1-dev
20+
libexpat1-dev \
21+
libexpat1-dev \
22+
gnupg \
23+
apt-utils \
24+
wget \
25+
fonts-ipafont-gothic \
26+
fonts-wqy-zenhei \
27+
fonts-thai-tlwg \
28+
fonts-kacst \
29+
fonts-freefont-ttf \
30+
libxss1 \
31+
libxtst6
32+
33+
RUN curl -sL https://nodejs.org/dist/v14.4.0/node-v14.4.0-linux-x64.tar.xz | tar -xJ
34+
ENV PATH="/node-v14.4.0-linux-x64/bin:${PATH}"
35+
36+
# Install required dependencies from browser-UI-test framework
37+
# For now, we need to use `--unsafe-perm=true` to go around an issue when npm tries
38+
# to create a new folder. For reference:
39+
# https://github.com/puppeteer/puppeteer/issues/375
40+
RUN npm install browser-ui-test -g --unsafe-perm=true
2141

2242
COPY scripts/sccache.sh /scripts/
2343
RUN sh /scripts/sccache.sh
2444

2545
ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu
26-
ENV RUST_CHECK_TARGET check-aux
46+
ENV RUST_CHECK_TARGET check-aux-and-gui

‎src/test/rustdoc-gui/basic-code.goml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
goto: file://|DOC_PATH|/index.html
2+
click: ".srclink"
3+
assert: (".line-numbers", 1)

‎src/test/rustdoc-gui/basic.goml

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
goto: file://|DOC_PATH|/index.html
2+
assert: ("#functions")
3+
goto: ./struct.Foo.html
4+
assert: ("div.type-decl")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
goto: file://|DOC_PATH|/index.html
2+
click: ".srclink"
3+
click: "#sidebar-toggle"
4+
wait-for: 500
5+
fail: true
6+
assert: ("#source-sidebar", { "left": "-300px" })

‎src/test/rustdoc-gui/lib.rs

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//! The point of this crate is to be able to have enough different "kinds" of
2+
//! documentation generated so we can test each different features.
3+
4+
#![crate_name = "test_docs"]
5+
6+
use std::fmt;
7+
8+
/// Basic function with some code examples:
9+
///
10+
/// ```
11+
/// println!("nothing fancy");
12+
/// ```
13+
///
14+
/// A failing to compile one:
15+
///
16+
/// ```compile_fail
17+
/// println!("where did my argument {} go? :'(");
18+
/// ```
19+
///
20+
/// An ignored one:
21+
///
22+
/// ```ignore (it's a test)
23+
/// Let's say I'm just some text will ya?
24+
/// ```
25+
pub fn foo() {}
26+
27+
/// Just a normal struct.
28+
pub struct Foo;
29+
30+
/// Just a normal enum.
31+
pub enum WhoLetTheDogOut {
32+
/// Woof!
33+
Woof,
34+
/// Meoooooooow...
35+
Meow,
36+
}
37+
38+
/// Who doesn't love to wrap a `format!` call?
39+
pub fn some_more_function<T: fmt::Debug>(t: &T) -> String {
40+
format!("{:?}", t)
41+
}
42+
43+
/// Woohoo! A trait!
44+
pub trait AnotherOne {
45+
/// Some func 1.
46+
fn func1();
47+
48+
/// Some func 2.
49+
fn func2();
50+
51+
/// Some func 3.
52+
fn func3();
53+
}
54+
55+
/// Check for "i" signs in lists!
56+
///
57+
/// 1. elem 1
58+
/// 2.test 1
59+
/// ```compile_fail
60+
/// fn foo() {}
61+
/// ```
62+
/// 3. elem 3
63+
/// 4. ```ignore (it's a test)
64+
/// fn foo() {}
65+
/// ```
66+
/// 5. elem 5
67+
pub fn check_list_code_block() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
goto: file://|DOC_PATH|/index.html
2+
goto: ./fn.check_list_code_block.html
3+
assert: ("pre.rust.fn")
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
goto: file://|DOC_PATH|/index.html
2+
click: "#theme-picker"
3+
click: "#theme-choices > button:first-child"
4+
wait-for: 500
5+
// should be the ayu theme so let's check the color
6+
assert: ("body", { "background-color": "rgb(15, 20, 25)" })
7+
click: "#theme-choices > button:last-child"
8+
wait-for: 500
9+
// should be the light theme so let's check the color
10+
assert: ("body", { "background-color": "rgb(255, 255, 255)" })

‎src/test/rustdoc-gui/toggle-docs.goml

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
goto: file://|DOC_PATH|/index.html
2+
click: "#toggle-all-docs"
3+
wait-for: 5000
4+
assert: ("#main > div.docblock.hidden-by-usual-hider")
5+
click: "#toggle-all-docs"
6+
wait-for: 5000
7+
assert: ("#main > div.docblock.hidden-by-usual-hider", 0)

‎src/tools/compiletest/src/common.rs

+2
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,8 @@ pub struct Config {
344344

345345
/// Path to a NodeJS executable. Used for JS doctests, emscripten and WASM tests
346346
pub nodejs: Option<String>,
347+
/// Path to a npm executable. Used for rustdoc GUI tests
348+
pub npm: Option<String>,
347349
}
348350

349351
#[derive(Debug, Clone)]

‎src/tools/compiletest/src/main.rs

+2
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
126126
.reqopt("", "llvm-components", "list of LLVM components built in", "LIST")
127127
.optopt("", "llvm-bin-dir", "Path to LLVM's `bin` directory", "PATH")
128128
.optopt("", "nodejs", "the name of nodejs", "PATH")
129+
.optopt("", "npm", "the name of npm", "PATH")
129130
.optopt("", "remote-test-client", "path to the remote test client", "PATH")
130131
.optopt(
131132
"",
@@ -264,6 +265,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
264265
linker: matches.opt_str("linker"),
265266
llvm_components: matches.opt_str("llvm-components").unwrap(),
266267
nodejs: matches.opt_str("nodejs"),
268+
npm: matches.opt_str("npm"),
267269
}
268270
}
269271

‎src/tools/compiletest/src/runtest.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1585,7 +1585,7 @@ impl<'test> TestCx<'test> {
15851585

15861586
let aux_dir = self.aux_output_dir_name();
15871587

1588-
let rustdoc_path = self.config.rustdoc_path.as_ref().expect("--rustdoc-path passed");
1588+
let rustdoc_path = self.config.rustdoc_path.as_ref().expect("--rustdoc-path not passed");
15891589
let mut rustdoc = Command::new(rustdoc_path);
15901590

15911591
rustdoc

‎src/tools/rustdoc-gui/tester.js

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// This package needs to be install:
2+
//
3+
// ```
4+
// npm install browser-ui-test
5+
// ```
6+
const path = require('path');
7+
const {Options, runTest} = require('browser-ui-test');
8+
9+
function showHelp() {
10+
console.log("rustdoc-js options:");
11+
console.log(" --doc-folder [PATH] : location of the generated doc folder");
12+
console.log(" --help : show this message then quit");
13+
console.log(" --test-file [PATH] : location of the JS test file");
14+
}
15+
16+
function parseOptions(args) {
17+
var opts = {
18+
"doc_folder": "",
19+
"test_file": "",
20+
};
21+
var correspondances = {
22+
"--doc-folder": "doc_folder",
23+
"--test-file": "test_file",
24+
};
25+
26+
for (var i = 0; i < args.length; ++i) {
27+
if (args[i] === "--doc-folder"
28+
|| args[i] === "--test-file") {
29+
i += 1;
30+
if (i >= args.length) {
31+
console.log("Missing argument after `" + args[i - 1] + "` option.");
32+
return null;
33+
}
34+
opts[correspondances[args[i - 1]]] = args[i];
35+
} else if (args[i] === "--help") {
36+
showHelp();
37+
process.exit(0);
38+
} else {
39+
console.log("Unknown option `" + args[i] + "`.");
40+
console.log("Use `--help` to see the list of options");
41+
return null;
42+
}
43+
}
44+
if (opts["test_file"].length < 1) {
45+
console.log("Missing `--test-file` option.");
46+
} else if (opts["doc_folder"].length < 1) {
47+
console.log("Missing `--doc-folder` option.");
48+
} else {
49+
return opts;
50+
}
51+
return null;
52+
}
53+
54+
function checkFile(test_file, opts, loaded, index) {
55+
const test_name = path.basename(test_file, ".js");
56+
57+
process.stdout.write('Checking "' + test_name + '" ... ');
58+
return runChecks(test_file, loaded, index);
59+
}
60+
61+
function main(argv) {
62+
var opts = parseOptions(argv.slice(2));
63+
if (opts === null) {
64+
process.exit(1);
65+
}
66+
67+
const options = new Options();
68+
try {
69+
// This is more convenient that setting fields one by one.
70+
options.parseArguments([
71+
'--no-screenshot',
72+
"--variable", "DOC_PATH", opts["doc_folder"],
73+
]);
74+
} catch (error) {
75+
console.error(`invalid argument: ${error}`);
76+
process.exit(1);
77+
}
78+
79+
runTest(opts["test_file"], options).then(out => {
80+
const [output, nb_failures] = out;
81+
console.log(output);
82+
process.exit(nb_failures);
83+
}).catch(err => {
84+
console.error(err);
85+
process.exit(1);
86+
});
87+
}
88+
89+
main(process.argv);

0 commit comments

Comments
 (0)
Please sign in to comment.