Skip to content
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

Merged
merged 44 commits into from
Jun 17, 2022
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
e22f65a
codegen: Composite fields docs
lexnv Apr 21, 2022
40f041c
codegen: Propagate docs for Event type
lexnv Apr 21, 2022
b097bc1
codegen: Propagate docs for calls module
lexnv Apr 21, 2022
8a7382d
subxt: Update polkadot.rs
lexnv Apr 21, 2022
70dfd68
codegen: Propagate documentation for `TypeDefGenKind::Enum` kind
lexnv Apr 21, 2022
d0bdbad
subxt: Update polkadot.rs
lexnv Apr 21, 2022
06eb385
codegen/tests: Recursively obtain raw metadata documentation
lexnv Apr 21, 2022
e25ac4b
codegen/tests: Generate runtime interface from node metadata
lexnv Apr 21, 2022
ea06e05
codegen/tests: Obtain documentation from generated runtime API
lexnv Apr 21, 2022
d6e6317
codegen/tests: Match raw documentation with the generated one
lexnv Apr 21, 2022
9ae68dd
Revert not longer needed "codegen: Composite fields docs"
lexnv Apr 22, 2022
96c2d6e
codegen/tests: Improve test regex documentation
lexnv Apr 22, 2022
03f5f74
subxt: Add integration-tests feature flag
lexnv Apr 22, 2022
45b84eb
subxt: Guard integration tests under feature flag
lexnv Apr 22, 2022
fbdfdf6
test-runtime: Place build.rs under feature flag
lexnv Apr 22, 2022
c9ad19b
subxt: Pass `integration-tests` feature to `test-runtime`
lexnv Apr 22, 2022
8e5f38b
CI: Use `integration-tests` feature to run all tests
lexnv Apr 22, 2022
3087ada
Merge remote-tracking branch 'origin/master' into 351_test_feature
lexnv Apr 28, 2022
057f075
subxt: Rely on `#[cfg(feature = "integration-tests")]` for integration
lexnv Apr 28, 2022
e596c68
subxt/metadata: Manually construct test metadata
lexnv Apr 28, 2022
c192f17
artifacts: Move scale binary blob to dedicated folder
lexnv Apr 28, 2022
680bc05
examples: Update path to metadata blob
lexnv Apr 28, 2022
3a1a692
metadata: Rely on artifact metadata blob for benches
lexnv Apr 28, 2022
2461c3b
metadata: Remove `test-runtime` dependency
lexnv Apr 28, 2022
3d9b9ab
examples: Modify runtime path for `custom_type_derives`
lexnv Apr 28, 2022
06c587c
Merge remote-tracking branch 'origin/master' into 351_test_feature
lexnv Apr 28, 2022
1e0522d
subxt: Remove tests folder
lexnv Apr 28, 2022
e73c763
test-runtime: Remove `integration-tests` feature flag
lexnv Apr 28, 2022
e309328
integration-tests: Add an integration test crate for subxt
lexnv Apr 28, 2022
77818e0
subxt: Remove `test-runtime` dependency
lexnv Apr 28, 2022
8c1c681
subxt: Add comment for feature flags
lexnv Apr 28, 2022
cd91831
integration-tests: Trim dependencies
lexnv Apr 28, 2022
bc0f719
integration-tests: Move dependencies under dev
lexnv Apr 28, 2022
bce0562
Revert "CI: Use `integration-tests` feature to run all tests"
lexnv Apr 28, 2022
959b8b5
Merge remote-tracking branch 'origin/351_test_feature' into 503_doc_t…
lexnv Apr 28, 2022
bfba931
codegen: Move documentation test to integration crate
lexnv Apr 28, 2022
c1f4979
codegen_documentation: Add license + fmt
lexnv Apr 28, 2022
a4a208a
Merge remote-tracking branch 'origin/master' into 503_doc_tests
lexnv May 17, 2022
ea1ad75
Merge remote-tracking branch 'origin/master' into 503_doc_tests
lexnv Jun 14, 2022
efbd0c5
Update `polkadot.rs`
lexnv Jun 14, 2022
d201044
Merge remote-tracking branch 'origin/master' into 503_doc_tests
lexnv Jun 17, 2022
57321c9
Move test under `testing` folder
lexnv Jun 17, 2022
86c250b
Merge remote-tracking branch 'origin/master' into 503_doc_tests
lexnv Jun 17, 2022
293a90a
Update testing/integration-tests/src/codegen/codegen_documentation.rs
lexnv Jun 17, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions codegen/src/api/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,11 @@ pub fn generate_calls(
})
.unzip();

let call_ty = type_gen.resolve_type(call.ty.id());
let docs = call_ty.docs();

quote! {
#( #[doc = #docs ] )*
pub mod calls {
use super::root_mod;
use super::#types_mod_ident;
Expand Down
3 changes: 3 additions & 0 deletions codegen/src/api/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,11 @@ pub fn generate_events(
}
});
let event_type = type_gen.resolve_type_path(event.ty.id(), &[]);
let event_ty = type_gen.resolve_type(event.ty.id());
let docs = event_ty.docs();

quote! {
#( #[doc = #docs ] )*
pub type Event = #event_type;
pub mod events {
use super::#types_mod_ident;
Expand Down
8 changes: 8 additions & 0 deletions codegen/src/types/type_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ pub struct TypeDefGen {
derives: Derives,
/// The kind of type to be generated.
ty_kind: TypeDefGenKind,
/// Type documentation.
ty_docs: TokenStream,
}

impl TypeDefGen {
Expand Down Expand Up @@ -119,10 +121,14 @@ impl TypeDefGen {
_ => TypeDefGenKind::BuiltIn,
};

let docs = ty.docs();
let ty_docs = quote! { #( #[doc = #docs ] )* };

Self {
type_params,
derives,
ty_kind,
ty_docs,
}
}
}
Expand Down Expand Up @@ -152,8 +158,10 @@ impl quote::ToTokens for TypeDefGen {
let enum_ident = format_ident!("{}", type_name);
let type_params = &self.type_params;
let derives = &self.derives;
let docs = &self.ty_docs;
let ty_toks = quote! {
#derives
#docs
pub enum #enum_ident #type_params {
#( #variants, )*
}
Expand Down
3 changes: 3 additions & 0 deletions integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features =
frame-metadata = "15.0.0"
futures = "0.3.13"
hex = "0.4.3"
regex = "1.5"
scale-info = { version = "2.0.0", features = ["bit-vec"] }
sp-core = { version = "6.0.0", default-features = false }
sp-keyring = "6.0.0"
sp-runtime = "6.0.0"
subxt = { version = "0.21.0", path = "../subxt" }
subxt-codegen = { version = "0.21.0", path = "../codegen" }
syn = "1.0.58"
test-runtime = { path = "../test-runtime" }
tokio = { version = "1.8", features = ["macros", "time"] }
tracing = "0.1.34"
Expand Down
119 changes: 119 additions & 0 deletions integration-tests/src/codegen/codegen_documentation.rs
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 `(.*?)`.
Copy link
Collaborator

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:

let re = Regex::new(r#"\# \[doc = "[^\n]*"\]"#).unwrap();

To just explicitly forbid actual newlines from appearing and force the match to be on one line, for a bit of added reliability?

Copy link
Collaborator Author

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 ]:

			The [event](https://docs.substrate.io/v3/runtime/events-and-errors) emitted
			by this pallet.
# <weight>
- `O(1)`.

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 🤔

//
// 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("\\\"", "\"")
Copy link
Collaborator

Choose a reason for hiding this comment

The 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 cargo doc --open for instance?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The \n and \t cases originate from the substrate generation of the documentation

event_item.attrs.push(syn::parse_quote!(
			#[doc = r"
			The [event](https://docs.substrate.io/v3/runtime/events-and-errors) emitted
			by this pallet.
			"]
		));

leading to a single line

#[doc = "\n\t\t\tThe [event](https://docs.substrate.io/v3/runtime/events-and-errors) emitted\n\t\t\tby this pallet.\n\t\t\t"]

However, those lines are rendered correctly by the cargo doc --open.

Copy link
Collaborator

Choose a reason for hiding this comment

The 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
);
}
}
2 changes: 2 additions & 0 deletions integration-tests/src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@
#[rustfmt::skip]
#[allow(clippy::all)]
mod polkadot;

mod codegen_documentation;
Loading