Rollup merge of #99474 - aDotInTheVoid:rustdoc-json-noinline-test-cleanup, r=CraftSpider

Rustdoc json tests: New @hasexact test command

Alot of the time, we wanted to assert that a module had an exact set of items. Most of the time this was done by asserting that the ```@count``` of the module was `n`, and then doing `n` ```@has``` checks on the module.

This was tedious, so often shortcuts were done.

This PR adds a new command to jsondocck to allow consistently expressing this behavior, and then uses it to clean up the tests.

``@rustbot`` modify labels: +A-rustdoc-json +A-testsuite
This commit is contained in:
Matthias Krüger 2022-08-17 12:32:47 +02:00 committed by GitHub
commit 0491fdad6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 111 additions and 39 deletions

View File

@ -4,27 +4,28 @@
// @is nested.json "$.crate_version" \"1.0.0\" // @is nested.json "$.crate_version" \"1.0.0\"
// @is - "$.index[*][?(@.name=='nested')].kind" \"module\" // @is - "$.index[*][?(@.name=='nested')].kind" \"module\"
// @is - "$.index[*][?(@.name=='nested')].inner.is_crate" true // @is - "$.index[*][?(@.name=='nested')].inner.is_crate" true
// @count - "$.index[*][?(@.name=='nested')].inner.items[*]" 1
// @set l1_id = - "$.index[*][?(@.name=='l1')].id"
// @ismany - "$.index[*][?(@.name=='nested')].inner.items[*]" $l1_id
// @is nested.json "$.index[*][?(@.name=='l1')].kind" \"module\" // @is nested.json "$.index[*][?(@.name=='l1')].kind" \"module\"
// @is - "$.index[*][?(@.name=='l1')].inner.is_crate" false // @is - "$.index[*][?(@.name=='l1')].inner.is_crate" false
// @count - "$.index[*][?(@.name=='l1')].inner.items[*]" 2
pub mod l1 { pub mod l1 {
// @is nested.json "$.index[*][?(@.name=='l3')].kind" \"module\" // @is nested.json "$.index[*][?(@.name=='l3')].kind" \"module\"
// @is - "$.index[*][?(@.name=='l3')].inner.is_crate" false // @is - "$.index[*][?(@.name=='l3')].inner.is_crate" false
// @count - "$.index[*][?(@.name=='l3')].inner.items[*]" 1
// @set l3_id = - "$.index[*][?(@.name=='l3')].id" // @set l3_id = - "$.index[*][?(@.name=='l3')].id"
// @has - "$.index[*][?(@.name=='l1')].inner.items[*]" $l3_id
pub mod l3 { pub mod l3 {
// @is nested.json "$.index[*][?(@.name=='L4')].kind" \"struct\" // @is nested.json "$.index[*][?(@.name=='L4')].kind" \"struct\"
// @is - "$.index[*][?(@.name=='L4')].inner.struct_type" \"unit\" // @is - "$.index[*][?(@.name=='L4')].inner.struct_type" \"unit\"
// @set l4_id = - "$.index[*][?(@.name=='L4')].id" // @set l4_id = - "$.index[*][?(@.name=='L4')].id"
// @has - "$.index[*][?(@.name=='l3')].inner.items[*]" $l4_id // @ismany - "$.index[*][?(@.name=='l3')].inner.items[*]" $l4_id
pub struct L4; pub struct L4;
} }
// @is nested.json "$.index[*][?(@.inner.source=='l3::L4')].kind" \"import\" // @is nested.json "$.index[*][?(@.inner.source=='l3::L4')].kind" \"import\"
// @is - "$.index[*][?(@.inner.source=='l3::L4')].inner.glob" false // @is - "$.index[*][?(@.inner.source=='l3::L4')].inner.glob" false
// @is - "$.index[*][?(@.inner.source=='l3::L4')].inner.id" $l4_id
// @set l4_use_id = - "$.index[*][?(@.inner.source=='l3::L4')].id"
pub use l3::L4; pub use l3::L4;
} }
// @ismany - "$.index[*][?(@.name=='l1')].inner.items[*]" $l3_id $l4_use_id

View File

@ -4,15 +4,20 @@
#![feature(no_core)] #![feature(no_core)]
// @is glob_extern.json "$.index[*][?(@.name=='mod1')].kind" \"module\" // @is glob_extern.json "$.index[*][?(@.name=='mod1')].kind" \"module\"
// @is glob_extern.json "$.index[*][?(@.name=='mod1')].inner.is_stripped" "true" // @is - "$.index[*][?(@.name=='mod1')].inner.is_stripped" "true"
mod mod1 { mod mod1 {
extern "C" { extern "C" {
// @has - "$.index[*][?(@.name=='public_fn')].id" // @set public_fn_id = - "$.index[*][?(@.name=='public_fn')].id"
pub fn public_fn(); pub fn public_fn();
// @!has - "$.index[*][?(@.name=='private_fn')]" // @!has - "$.index[*][?(@.name=='private_fn')]"
fn private_fn(); fn private_fn();
} }
// @ismany - "$.index[*][?(@.name=='mod1')].inner.items[*]" $public_fn_id
// @set mod1_id = - "$.index[*][?(@.name=='mod1')].id"
} }
// @is - "$.index[*][?(@.kind=='import')].inner.glob" true // @is - "$.index[*][?(@.kind=='import')].inner.glob" true
// @is - "$.index[*][?(@.kind=='import')].inner.id" $mod1_id
// @set use_id = - "$.index[*][?(@.kind=='import')].id"
// @ismany - "$.index[*][?(@.name=='glob_extern')].inner.items[*]" $use_id
pub use mod1::*; pub use mod1::*;

View File

@ -16,7 +16,7 @@ mod mod1 {
struct Mod2Private; struct Mod2Private;
} }
// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='mod2')]" // @set mod2_use_id = - "$.index[*][?(@.kind=='import' && @.inner.name=='mod2')].id"
pub use self::mod2::*; pub use self::mod2::*;
// @set m1pub_id = - "$.index[*][?(@.name=='Mod1Public')].id" // @set m1pub_id = - "$.index[*][?(@.name=='Mod1Public')].id"
@ -25,8 +25,9 @@ mod mod1 {
struct Mod1Private; struct Mod1Private;
} }
// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='mod1')]" // @set mod1_use_id = - "$.index[*][?(@.kind=='import' && @.inner.name=='mod1')].id"
pub use mod1::*; pub use mod1::*;
// @has - "$.index[*][?(@.name=='mod2')].inner.items[*]" $m2pub_id // @ismany - "$.index[*][?(@.name=='mod2')].inner.items[*]" $m2pub_id
// @has - "$.index[*][?(@.name=='mod1')].inner.items[*]" $m1pub_id // @ismany - "$.index[*][?(@.name=='mod1')].inner.items[*]" $m1pub_id $mod2_use_id
// @ismany - "$.index[*][?(@.name=='glob_private')].inner.items[*]" $mod1_use_id

View File

@ -3,7 +3,7 @@
pub mod foo { pub mod foo {
// @set bar_id = in_root_and_mod_pub.json "$.index[*][?(@.name=='Bar')].id" // @set bar_id = in_root_and_mod_pub.json "$.index[*][?(@.name=='Bar')].id"
// @has - "$.index[*][?(@.name=='foo')].inner.items[*]" $bar_id // @ismany - "$.index[*][?(@.name=='foo')].inner.items[*]" $bar_id
pub struct Bar; pub struct Bar;
} }
@ -15,6 +15,6 @@ pub use foo::Bar;
pub mod baz { pub mod baz {
// @set baz_import_id = - "$.index[*][?(@.inner.source=='crate::foo::Bar')].id" // @set baz_import_id = - "$.index[*][?(@.inner.source=='crate::foo::Bar')].id"
// @is - "$.index[*][?(@.inner.source=='crate::foo::Bar')].inner.id" $bar_id // @is - "$.index[*][?(@.inner.source=='crate::foo::Bar')].inner.id" $bar_id
// @has - "$.index[*][?(@.name=='baz')].inner.items[*]" $baz_import_id // @ismany - "$.index[*][?(@.name=='baz')].inner.items[*]" $baz_import_id
pub use crate::foo::Bar; pub use crate::foo::Bar;
} }

View File

@ -3,15 +3,13 @@
#![no_core] #![no_core]
#![feature(no_core)] #![feature(no_core)]
// @count macro.json "$.index[*][?(@.name=='macro')].inner.items[*]" 2
// @set repro_id = macro.json "$.index[*][?(@.name=='repro')].id" // @set repro_id = macro.json "$.index[*][?(@.name=='repro')].id"
// @has - "$.index[*][?(@.name=='macro')].inner.items[*]" $repro_id
#[macro_export] #[macro_export]
macro_rules! repro { macro_rules! repro {
() => {}; () => {};
} }
// @set repro2_id = macro.json "$.index[*][?(@.inner.name=='repro2')].id" // @set repro2_id = macro.json "$.index[*][?(@.inner.name=='repro2')].id"
// @has - "$.index[*][?(@.name=='macro')].inner.items[*]" $repro2_id
pub use crate::repro as repro2; pub use crate::repro as repro2;
// @ismany macro.json "$.index[*][?(@.name=='macro')].inner.items[*]" $repro_id $repro2_id

View File

@ -1,18 +1,28 @@
// aux-build:pub-struct.rs // aux-build:pub-struct.rs
// ignore-tidy-linelength
// Test for the ICE in rust/83057 // Test for the ICE in https://github.com/rust-lang/rust/issues/83057
// Am external type re-exported with different attributes shouldn't cause an error // An external type re-exported with different attributes shouldn't cause an error
#![no_core] #![no_core]
#![feature(no_core)] #![feature(no_core)]
extern crate pub_struct as foo; extern crate pub_struct as foo;
#[doc(inline)] #[doc(inline)]
// @set crate_use_id = private_twice_one_inline.json "$.index[*][?(@.docs=='Hack A')].id"
// @set foo_id = - "$.index[*][?(@.docs=='Hack A')].inner.id"
/// Hack A
pub use foo::Foo; pub use foo::Foo;
// @set bar_id = - "$.index[*][?(@.name=='bar')].id"
pub mod bar { pub mod bar {
// @is - "$.index[*][?(@.docs=='Hack B')].inner.id" $foo_id
// @set bar_use_id = - "$.index[*][?(@.docs=='Hack B')].id"
// @ismany - "$.index[*][?(@.name=='bar')].inner.items[*]" $bar_use_id
/// Hack B
pub use foo::Foo; pub use foo::Foo;
} }
// @count private_twice_one_inline.json "$.index[*][?(@.kind=='import')]" 2 // @ismany - "$.index[*][?(@.kind=='import')].id" $crate_use_id $bar_use_id
// @ismany - "$.index[*][?(@.name=='private_twice_one_inline')].inner.items[*]" $bar_id $crate_use_id

View File

@ -1,4 +1,6 @@
// Test for the ICE in rust/83720 // ignore-tidy-linelength
// Test for the ICE in https://github.com/rust-lang/rust/issues/83720
// A pub-in-private type re-exported under two different names shouldn't cause an error // A pub-in-private type re-exported under two different names shouldn't cause an error
#![no_core] #![no_core]
@ -7,11 +9,15 @@
// @is private_two_names.json "$.index[*][?(@.name=='style')].kind" \"module\" // @is private_two_names.json "$.index[*][?(@.name=='style')].kind" \"module\"
// @is private_two_names.json "$.index[*][?(@.name=='style')].inner.is_stripped" "true" // @is private_two_names.json "$.index[*][?(@.name=='style')].inner.is_stripped" "true"
mod style { mod style {
// @has - "$.index[*](?(@.name=='Color'))" // @set color_struct_id = - "$.index[*][?(@.kind=='struct' && @.name=='Color')].id"
pub struct Color; pub struct Color;
} }
// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='Color')]" // @is - "$.index[*][?(@.kind=='import' && @.inner.name=='Color')].inner.id" $color_struct_id
// @set color_export_id = - "$.index[*][?(@.kind=='import' && @.inner.name=='Color')].id"
pub use style::Color; pub use style::Color;
// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='Colour')]" // @is - "$.index[*][?(@.kind=='import' && @.inner.name=='Colour')].inner.id" $color_struct_id
// @set colour_export_id = - "$.index[*][?(@.kind=='import' && @.inner.name=='Colour')].id"
pub use style::Color as Colour; pub use style::Color as Colour;
// @ismany - "$.index[*][?(@.name=='private_two_names')].inner.items[*]" $color_export_id $colour_export_id

View File

@ -4,14 +4,14 @@
#![feature(no_core)] #![feature(no_core)]
// @set inner_id = rename_public.json "$.index[*][?(@.name=='inner')].id" // @set inner_id = rename_public.json "$.index[*][?(@.name=='inner')].id"
// @has - "$.index[*][?(@.name=='rename_public')].inner.items[*]" $inner_id
pub mod inner { pub mod inner {
// @set public_id = - "$.index[*][?(@.name=='Public')].id" // @set public_id = - "$.index[*][?(@.name=='Public')].id"
// @has - "$.index[*][?(@.name=='inner')].inner.items[*]" $public_id // @ismany - "$.index[*][?(@.name=='inner')].inner.items[*]" $public_id
pub struct Public; pub struct Public;
} }
// @set import_id = - "$.index[*][?(@.inner.name=='NewName')].id" // @set import_id = - "$.index[*][?(@.inner.name=='NewName')].id"
// @!has - "$.index[*][?(@.inner.name=='Public')]" // @!has - "$.index[*][?(@.inner.name=='Public')]"
// @has - "$.index[*][?(@.name=='rename_public')].inner.items[*]" $import_id
// @is - "$.index[*][?(@.inner.name=='NewName')].inner.source" \"inner::Public\" // @is - "$.index[*][?(@.inner.name=='NewName')].inner.source" \"inner::Public\"
pub use inner::Public as NewName; pub use inner::Public as NewName;
// @ismany - "$.index[*][?(@.name=='rename_public')].inner.items[*]" $inner_id $import_id

View File

@ -1,3 +1,5 @@
// ignore-tidy-linelength
// Regression test for <https://github.com/rust-lang/rust/issues/97432>. // Regression test for <https://github.com/rust-lang/rust/issues/97432>.
#![feature(no_core)] #![feature(no_core)]
@ -5,11 +7,17 @@
#![no_core] #![no_core]
// @has same_type_reexported_more_than_once.json // @has same_type_reexported_more_than_once.json
// @has - "$.index[*][?(@.name=='Trait')]"
pub use inner::Trait;
// @has - "$.index[*].inner[?(@.name=='Reexport')].id"
pub use inner::Trait as Reexport;
mod inner { mod inner {
// @set trait_id = - "$.index[*][?(@.name=='Trait')].id"
pub trait Trait {} pub trait Trait {}
} }
// @set export_id = - "$.index[*][?(@.inner.name=='Trait')].id"
// @is - "$.index[*][?(@.inner.name=='Trait')].inner.id" $trait_id
pub use inner::Trait;
// @set reexport_id = - "$.index[*][?(@.inner.name=='Reexport')].id"
// @is - "$.index[*][?(@.inner.name=='Reexport')].inner.id" $trait_id
pub use inner::Trait as Reexport;
// @ismany - "$.index[*][?(@.name=='same_type_reexported_more_than_once')].inner.items[*]" $reexport_id $export_id

View File

@ -10,6 +10,8 @@ mod inner {
} }
// @is - "$.index[*][?(@.kind=='import')].inner.name" \"Public\" // @is - "$.index[*][?(@.kind=='import')].inner.name" \"Public\"
// @set use_id = - "$.index[*][?(@.kind=='import')].id"
pub use inner::Public; pub use inner::Public;
// @has - "$.index[*][?(@.name=='inner')].inner.items[*]" $pub_id // @ismany - "$.index[*][?(@.name=='inner')].inner.items[*]" $pub_id
// @ismany - "$.index[*][?(@.name=='simple_private')].inner.items[*]" $use_id

View File

@ -4,15 +4,15 @@
#![feature(no_core)] #![feature(no_core)]
// @set inner_id = simple_public.json "$.index[*][?(@.name=='inner')].id" // @set inner_id = simple_public.json "$.index[*][?(@.name=='inner')].id"
// @has - "$.index[*][?(@.name=='simple_public')].inner.items[*]" $inner_id
pub mod inner { pub mod inner {
// @set public_id = - "$.index[*][?(@.name=='Public')].id" // @set public_id = - "$.index[*][?(@.name=='Public')].id"
// @has - "$.index[*][?(@.name=='inner')].inner.items[*]" $public_id // @ismany - "$.index[*][?(@.name=='inner')].inner.items[*]" $public_id
pub struct Public; pub struct Public;
} }
// @set import_id = - "$.index[*][?(@.inner.name=='Public')].id" // @set import_id = - "$.index[*][?(@.inner.name=='Public')].id"
// @has - "$.index[*][?(@.name=='simple_public')].inner.items[*]" $import_id
// @is - "$.index[*][?(@.inner.name=='Public')].inner.source" \"inner::Public\" // @is - "$.index[*][?(@.inner.name=='Public')].inner.source" \"inner::Public\"
pub use inner::Public; pub use inner::Public;
// @ismany - "$.index[*][?(@.name=='simple_public')].inner.items[*]" $import_id $inner_id

View File

@ -2,8 +2,7 @@
// @is fn_lifetime.json "$.index[*][?(@.name=='GenericFn')].kind" \"typedef\" // @is fn_lifetime.json "$.index[*][?(@.name=='GenericFn')].kind" \"typedef\"
// @count - "$.index[*][?(@.name=='GenericFn')].inner.generics.params[*]" 1 // @ismany - "$.index[*][?(@.name=='GenericFn')].inner.generics.params[*].name" \"\'a\"
// @is - "$.index[*][?(@.name=='GenericFn')].inner.generics.params[*].name" \"\'a\"
// @has - "$.index[*][?(@.name=='GenericFn')].inner.generics.params[*].kind.lifetime" // @has - "$.index[*][?(@.name=='GenericFn')].inner.generics.params[*].kind.lifetime"
// @count - "$.index[*][?(@.name=='GenericFn')].inner.generics.params[*].kind.lifetime.outlives[*]" 0 // @count - "$.index[*][?(@.name=='GenericFn')].inner.generics.params[*].kind.lifetime.outlives[*]" 0
// @count - "$.index[*][?(@.name=='GenericFn')].inner.generics.where_predicates[*]" 0 // @count - "$.index[*][?(@.name=='GenericFn')].inner.generics.where_predicates[*]" 0

View File

@ -50,6 +50,7 @@ pub enum CommandKind {
Has, Has,
Count, Count,
Is, Is,
IsMany,
Set, Set,
} }
@ -57,6 +58,7 @@ impl CommandKind {
fn validate(&self, args: &[String], command_num: usize, lineno: usize) -> bool { fn validate(&self, args: &[String], command_num: usize, lineno: usize) -> bool {
let count = match self { let count = match self {
CommandKind::Has => (1..=3).contains(&args.len()), CommandKind::Has => (1..=3).contains(&args.len()),
CommandKind::IsMany => args.len() >= 3,
CommandKind::Count | CommandKind::Is => 3 == args.len(), CommandKind::Count | CommandKind::Is => 3 == args.len(),
CommandKind::Set => 4 == args.len(), CommandKind::Set => 4 == args.len(),
}; };
@ -89,6 +91,7 @@ impl fmt::Display for CommandKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let text = match self { let text = match self {
CommandKind::Has => "has", CommandKind::Has => "has",
CommandKind::IsMany => "ismany",
CommandKind::Count => "count", CommandKind::Count => "count",
CommandKind::Is => "is", CommandKind::Is => "is",
CommandKind::Set => "set", CommandKind::Set => "set",
@ -137,6 +140,7 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
"has" => CommandKind::Has, "has" => CommandKind::Has,
"count" => CommandKind::Count, "count" => CommandKind::Count,
"is" => CommandKind::Is, "is" => CommandKind::Is,
"ismany" => CommandKind::IsMany,
"set" => CommandKind::Set, "set" => CommandKind::Set,
_ => { _ => {
print_err(&format!("Unrecognized command name `@{}`", cmd), lineno); print_err(&format!("Unrecognized command name `@{}`", cmd), lineno);
@ -227,6 +231,44 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
_ => unreachable!(), _ => unreachable!(),
} }
} }
CommandKind::IsMany => {
// @ismany <path> <jsonpath> <value>...
let (path, query, values) = if let [path, query, values @ ..] = &command.args[..] {
(path, query, values)
} else {
unreachable!("Checked in CommandKind::validate")
};
let val = cache.get_value(path)?;
let got_values = select(&val, &query).unwrap();
assert!(!command.negated, "`@!ismany` is not supported");
// Serde json doesn't implement Ord or Hash for Value, so we must
// use a Vec here. While in theory that makes setwize equality
// O(n^2), in practice n will never be large enought to matter.
let expected_values =
values.iter().map(|v| string_to_value(v, cache)).collect::<Vec<_>>();
if expected_values.len() != got_values.len() {
return Err(CkError::FailedCheck(
format!(
"Expected {} values, but `{}` matched to {} values ({:?})",
expected_values.len(),
query,
got_values.len(),
got_values
),
command,
));
};
for got_value in got_values {
if !expected_values.iter().any(|exp| &**exp == got_value) {
return Err(CkError::FailedCheck(
format!("`{}` has match {:?}, which was not expected", query, got_value),
command,
));
}
}
true
}
CommandKind::Count => { CommandKind::Count => {
// @count <path> <jsonpath> <count> = Check that the jsonpath matches exactly [count] times // @count <path> <jsonpath> <count> = Check that the jsonpath matches exactly [count] times
assert_eq!(command.args.len(), 3); assert_eq!(command.args.len(), 3);