-
Notifications
You must be signed in to change notification settings - Fork 254
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Followup test for checking propagated documentation #514
Changes from 40 commits
e22f65a
40f041c
b097bc1
8a7382d
70dfd68
d0bdbad
06eb385
e25ac4b
ea06e05
d6e6317
9ae68dd
96c2d6e
03f5f74
45b84eb
fbdfdf6
c9ad19b
8e5f38b
3087ada
057f075
e596c68
c192f17
680bc05
3a1a692
2461c3b
3d9b9ab
06c587c
1e0522d
e73c763
e309328
77818e0
8c1c681
cd91831
bc0f719
bce0562
959b8b5
bfba931
c1f4979
a4a208a
ea1ad75
efbd0c5
d201044
57321c9
86c250b
293a90a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
// Copyright 2019-2022 Parity Technologies (UK) Ltd. | ||
// This file is part of subxt. | ||
// | ||
// subxt is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// subxt is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU General Public License | ||
// along with subxt. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
use regex::Regex; | ||
use subxt_codegen::{ | ||
DerivesRegistry, | ||
RuntimeGenerator, | ||
}; | ||
|
||
fn metadata_docs() -> Vec<String> { | ||
// Load the runtime metadata downloaded from a node via `test-runtime`. | ||
let bytes = test_runtime::METADATA; | ||
let meta: frame_metadata::RuntimeMetadataPrefixed = | ||
codec::Decode::decode(&mut &*bytes).expect("Cannot decode scale metadata"); | ||
let metadata = match meta.1 { | ||
frame_metadata::RuntimeMetadata::V14(v14) => v14, | ||
_ => panic!("Unsupported metadata version {:?}", meta.1), | ||
}; | ||
|
||
// Inspect the metadata types and collect the documentation. | ||
let mut docs = Vec::new(); | ||
for ty in metadata.types.types() { | ||
docs.extend_from_slice(ty.ty().docs()); | ||
} | ||
|
||
for pallet in metadata.pallets { | ||
if let Some(storage) = pallet.storage { | ||
for entry in storage.entries { | ||
docs.extend(entry.docs); | ||
} | ||
} | ||
// Note: Calls, Events and Errors are deduced directly to | ||
// PortableTypes which are handled above. | ||
for constant in pallet.constants { | ||
docs.extend(constant.docs); | ||
} | ||
} | ||
// Note: Extrinsics do not have associated documentation, but is implied by | ||
// associated Type. | ||
|
||
docs | ||
} | ||
|
||
fn generate_runtime_interface() -> String { | ||
// Load the runtime metadata downloaded from a node via `test-runtime`. | ||
let bytes = test_runtime::METADATA; | ||
let metadata: frame_metadata::RuntimeMetadataPrefixed = | ||
codec::Decode::decode(&mut &*bytes).expect("Cannot decode scale metadata"); | ||
|
||
// Generate a runtime interface form the provided metadata. | ||
let generator = RuntimeGenerator::new(metadata); | ||
let item_mod = syn::parse_quote!( | ||
pub mod api {} | ||
); | ||
let derives = DerivesRegistry::default(); | ||
generator.generate_runtime(item_mod, derives).to_string() | ||
} | ||
|
||
fn interface_docs() -> Vec<String> { | ||
// Generate the runtime interface from the node's metadata. | ||
// Note: the API is generated on a single line. | ||
let runtime_api = generate_runtime_interface(); | ||
|
||
// Documentation lines have the following format: | ||
// # [ doc = "Upward message is invalid XCM."] | ||
// Given the API is generated on a single line, the regex matching | ||
// must be lazy hence the `?` in the matched group `(.*?)`. | ||
// | ||
// The greedy `non-?` matching would lead to one single match | ||
// from the beginning of the first documentation tag, containing everything up to | ||
// the last documentation tag | ||
// `# [ doc = "msg"] # [ doc = "msg2"] ... api ... # [ doc = "msgN" ]` | ||
// | ||
// The `(.*?)` stands for match any character zero or more times lazily. | ||
let re = Regex::new(r#"\# \[doc = "(.*?)"\]"#).unwrap(); | ||
re.captures_iter(&runtime_api) | ||
.filter_map(|capture| { | ||
// Get the matched group (ie index 1). | ||
capture.get(1).as_ref().map(|doc| { | ||
// Generated documentation will escape special characters. | ||
// Replace escaped characters with unescaped variants for | ||
// exact matching on the raw metadata documentation. | ||
doc.as_str() | ||
.replace("\\n", "\n") | ||
.replace("\\t", "\t") | ||
.replace("\\\"", "\"") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this mean that all generated docs end up on one line? Should we split it over multiple lines. I wonder how the docs actually look when opened in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
leading to a single line
However, those lines are rendered correctly by the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perfect, thank you :) we can tidy up the extraneous space characters in a future PR then! |
||
}) | ||
}) | ||
.collect() | ||
} | ||
|
||
#[test] | ||
fn check_documentation() { | ||
// Inspect metadata recursively and obtain all associated documentation. | ||
let raw_docs = metadata_docs(); | ||
// Obtain documentation from the generated API. | ||
let runtime_docs = interface_docs(); | ||
|
||
for raw in raw_docs.iter() { | ||
assert!( | ||
runtime_docs.contains(raw), | ||
"Documentation not present in runtime API: {}", | ||
raw | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,3 +24,5 @@ | |
#[rustfmt::skip] | ||
#[allow(clippy::all)] | ||
mod polkadot; | ||
|
||
mod codegen_documentation; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess if any doc string contains
"]
we'd hit an issue here?perhaps it could also be:
To just explicitly forbid actual newlines from appearing and force the match to be on one line, for a bit of added reliability?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had a try printing all the documentation pulled from the generated file
Seems to be also handling the cases for extra
]
:Although not entirely accurate: https://regex101.com/r/TtWQqO/1 . Seems to handle multiple
]
on the same documentation line.The
^\n
didn't seem to match tho 🤔