Skip to content

Commit

Permalink
Rollup merge of rust-lang#82311 - aDotInTheVoid:jsondocck-improvement…
Browse files Browse the repository at this point in the history
…s, r=jyn514

Jsondocck improvements

Adds 2 new commands, ``@is`` and ``@set`.`

``@is`` works like ``@has`,` except instead of checking if any value matches, it checks that there is exactly one value, and it matches. This allows more precise testing.

``@set`` gets a value, and saves it to be used later. This makes it possible to check that an item appears in the correct module.

Once this lands, the rest of the test suite can be upgraded to use these.

cc `@CraftSpider`

 `@rustbot` modify labels: +T-rustdoc +A-rustdoc-json +A-testsuite
  • Loading branch information
GuillaumeGomez authored Feb 22, 2021
2 parents b62f759 + 4c949a4 commit 6fedd91
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 27 deletions.
24 changes: 14 additions & 10 deletions src/test/rustdoc-json/nested.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
// edition:2018

// @has nested.json "$.index[*][?(@.name=='nested')].kind" \"module\"
// @has - "$.index[*][?(@.name=='nested')].inner.is_crate" true
// @is nested.json "$.index[*][?(@.name=='nested')].kind" \"module\"
// @is - "$.index[*][?(@.name=='nested')].inner.is_crate" true
// @count - "$.index[*][?(@.name=='nested')].inner.items[*]" 1

// @has nested.json "$.index[*][?(@.name=='l1')].kind" \"module\"
// @has - "$.index[*][?(@.name=='l1')].inner.is_crate" false
// @is nested.json "$.index[*][?(@.name=='l1')].kind" \"module\"
// @is - "$.index[*][?(@.name=='l1')].inner.is_crate" false
// @count - "$.index[*][?(@.name=='l1')].inner.items[*]" 2
pub mod l1 {

// @has nested.json "$.index[*][?(@.name=='l3')].kind" \"module\"
// @has - "$.index[*][?(@.name=='l3')].inner.is_crate" false
// @is nested.json "$.index[*][?(@.name=='l3')].kind" \"module\"
// @is - "$.index[*][?(@.name=='l3')].inner.is_crate" false
// @count - "$.index[*][?(@.name=='l3')].inner.items[*]" 1
// @set l3_id = - "$.index[*][?(@.name=='l3')].id"
// @has - "$.index[*][?(@.name=='l1')].inner.items[*]" $l3_id
pub mod l3 {

// @has nested.json "$.index[*][?(@.name=='L4')].kind" \"struct\"
// @has - "$.index[*][?(@.name=='L4')].inner.struct_type" \"unit\"
// @is nested.json "$.index[*][?(@.name=='L4')].kind" \"struct\"
// @is - "$.index[*][?(@.name=='L4')].inner.struct_type" \"unit\"
// @set l4_id = - "$.index[*][?(@.name=='L4')].id"
// @has - "$.index[*][?(@.name=='l3')].inner.items[*]" $l4_id
pub struct L4;
}
// @has nested.json "$.index[*][?(@.inner.span=='l3::L4')].kind" \"import\"
// @has - "$.index[*][?(@.inner.span=='l3::L4')].inner.glob" false
// @is nested.json "$.index[*][?(@.inner.span=='l3::L4')].kind" \"import\"
// @is - "$.index[*][?(@.inner.span=='l3::L4')].inner.glob" false
pub use l3::L4;
}
2 changes: 2 additions & 0 deletions src/tools/jsondocck/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub struct Cache {
root: PathBuf,
files: HashMap<PathBuf, String>,
values: HashMap<PathBuf, Value>,
pub variables: HashMap<String, Value>,
last_path: Option<PathBuf>,
}

Expand All @@ -19,6 +20,7 @@ impl Cache {
root: Path::new(doc_dir).to_owned(),
files: HashMap::new(),
values: HashMap::new(),
variables: HashMap::new(),
last_path: None,
}
}
Expand Down
71 changes: 54 additions & 17 deletions src/tools/jsondocck/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use jsonpath_lib::select;
use lazy_static::lazy_static;
use regex::{Regex, RegexBuilder};
use serde_json::Value;
use std::borrow::Cow;
use std::{env, fmt, fs};

mod cache;
Expand Down Expand Up @@ -48,13 +49,16 @@ pub struct Command {
pub enum CommandKind {
Has,
Count,
Is,
Set,
}

impl CommandKind {
fn validate(&self, args: &[String], command_num: usize, lineno: usize) -> bool {
let count = match self {
CommandKind::Has => (1..=3).contains(&args.len()),
CommandKind::Count => 3 == args.len(),
CommandKind::Count | CommandKind::Is => 3 == args.len(),
CommandKind::Set => 4 == args.len(),
};

if !count {
Expand Down Expand Up @@ -83,6 +87,8 @@ impl fmt::Display for CommandKind {
let text = match self {
CommandKind::Has => "has",
CommandKind::Count => "count",
CommandKind::Is => "is",
CommandKind::Set => "set",
};
write!(f, "{}", text)
}
Expand Down Expand Up @@ -127,6 +133,8 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
let cmd = match cmd {
"has" => CommandKind::Has,
"count" => CommandKind::Count,
"is" => CommandKind::Is,
"set" => CommandKind::Set,
_ => {
print_err(&format!("Unrecognized command name `@{}`", cmd), lineno);
errors = true;
Expand Down Expand Up @@ -180,6 +188,7 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
/// Performs the actual work of ensuring a command passes. Generally assumes the command
/// is syntactically valid.
fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
// FIXME: Be more granular about why, (e.g. syntax error, count not equal)
let result = match command.kind {
CommandKind::Has => {
match command.args.len() {
Expand All @@ -188,23 +197,15 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
// @has <path> <jsonpath> = check path exists
2 => {
let val = cache.get_value(&command.args[0])?;

match select(&val, &command.args[1]) {
Ok(results) => !results.is_empty(),
Err(_) => false,
}
let results = select(&val, &command.args[1]).unwrap();
!results.is_empty()
}
// @has <path> <jsonpath> <value> = check *any* item matched by path equals value
3 => {
let val = cache.get_value(&command.args[0])?;
match select(&val, &command.args[1]) {
Ok(results) => {
let pat: Value = serde_json::from_str(&command.args[2]).unwrap();

!results.is_empty() && results.into_iter().any(|val| *val == pat)
}
Err(_) => false,
}
let results = select(&val, &command.args[1]).unwrap();
let pat = string_to_value(&command.args[2], cache);
results.contains(&pat.as_ref())
}
_ => unreachable!(),
}
Expand All @@ -215,9 +216,37 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
let expected: usize = command.args[2].parse().unwrap();

let val = cache.get_value(&command.args[0])?;
match select(&val, &command.args[1]) {
Ok(results) => results.len() == expected,
Err(_) => false,
let results = select(&val, &command.args[1]).unwrap();
results.len() == expected
}
CommandKind::Is => {
// @has <path> <jsonpath> <value> = check *exactly one* item matched by path, and it equals value
assert_eq!(command.args.len(), 3);
let val = cache.get_value(&command.args[0])?;
let results = select(&val, &command.args[1]).unwrap();
let pat = string_to_value(&command.args[2], cache);
results.len() == 1 && results[0] == pat.as_ref()
}
CommandKind::Set => {
// @set <name> = <path> <jsonpath>
assert_eq!(command.args.len(), 4);
assert_eq!(command.args[1], "=", "Expected an `=`");
let val = cache.get_value(&command.args[2])?;
let results = select(&val, &command.args[3]).unwrap();
assert_eq!(results.len(), 1);
match results.len() {
0 => false,
1 => {
let r = cache.variables.insert(command.args[0].clone(), results[0].clone());
assert!(r.is_none(), "Name collision: {} is duplicated", command.args[0]);
true
}
_ => {
panic!(
"Got multiple results in `@set` for `{}`: {:?}",
&command.args[3], results
);
}
}
}
};
Expand Down Expand Up @@ -247,3 +276,11 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
Ok(())
}
}

fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> {
if s.starts_with("$") {
Cow::Borrowed(&cache.variables[&s[1..]])
} else {
Cow::Owned(serde_json::from_str(s).unwrap())
}
}

0 comments on commit 6fedd91

Please sign in to comment.