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

Add proper test Custom values #1147

Merged
merged 16 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ members = [
"testing/test-runtime",
"testing/integration-tests",
"testing/ui-tests",
"testing/generate-custom-metadata",
"macro",
"metadata",
"signer",
Expand Down
Binary file added artifacts/metadata_with_custom_values.scale
Binary file not shown.
7 changes: 4 additions & 3 deletions codegen/src/api/custom_values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use heck::ToSnakeCase as _;
use subxt_metadata::{CustomValueMetadata, Metadata};

use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use quote::quote;

/// Generate the custom values mod, if there are any custom values in the metadata. Else returns None.
pub fn generate_custom_values<'a>(
Expand Down Expand Up @@ -47,14 +47,15 @@ fn generate_custom_value_fn(
if fn_names_taken.contains(&fn_name) {
return None;
}
let fn_name_ident = format_ident!("{fn_name}");
// if the fn_name would be an invalid ident, return None:
jsdw marked this conversation as resolved.
Show resolved Hide resolved
let fn_name_ident = syn::parse_str::<syn::Ident>(&fn_name).ok()?;
fn_names_taken.insert(fn_name);

let custom_value_hash = custom_value.hash();
let return_ty = type_gen.resolve_type_path(custom_value.type_id());

Some(quote!(
pub fn #fn_name_ident() -> #crate_path::custom_values::StaticAddress<#return_ty> {
pub fn #fn_name_ident(&self) -> #crate_path::custom_values::StaticAddress<#return_ty> {
#crate_path::custom_values::StaticAddress::new_static(#name, [#(#custom_value_hash,)*])
}
))
Expand Down
18 changes: 18 additions & 0 deletions testing/generate-custom-metadata/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "generate-custom-metadata"
authors.workspace = true
edition.workspace = true
version.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
documentation.workspace = true
homepage.workspace = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
subxt = { workspace = true, features = ["native"] }
scale-info = { workspace = true, features = ["bit-vec"] }
frame-metadata = { workspace = true }
codec = { package = "parity-scale-codec", workspace = true, features = ["derive", "bit-vec"] }
4 changes: 4 additions & 0 deletions testing/generate-custom-metadata/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# generate-custom-metadata

A small crate with a binary for creating a file containing scale encoded metadata with custom values and saving it under `artifacts/metadata_with_custom_values.scale`.
It also provides dispatch error types that are used in `../ui_tests`.
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
use codec::Encode;
use frame_metadata::v15::{CustomMetadata, ExtrinsicMetadata, OuterEnums, RuntimeMetadataV15};
use frame_metadata::RuntimeMetadataPrefixed;

use scale_info::form::PortableForm;
use scale_info::TypeInfo;
use scale_info::{meta_type, IntoPortable};
use std::collections::BTreeMap;

pub mod dispatch_error;

/// Generate metadata which contains a `Foo { a: u8, b: &str }` custom value.
pub fn metadata_custom_values_foo() -> RuntimeMetadataPrefixed {
let mut registry = scale_info::Registry::new();
Expand All @@ -23,7 +26,10 @@ pub fn metadata_custom_values_foo() -> RuntimeMetadataPrefixed {
}

let foo_value_metadata: frame_metadata::v15::CustomValueMetadata<PortableForm> = {
let value = Foo { a: 0, b: "Hello" };
let value = Foo {
a: 42,
b: "Have a great day!",
};
let foo_ty = scale_info::MetaType::new::<Foo>();
let foo_ty_id = registry.register_type(&foo_ty);
frame_metadata::v15::CustomValueMetadata {
Expand All @@ -48,7 +54,7 @@ pub fn metadata_custom_values_foo() -> RuntimeMetadataPrefixed {
let unit_ty = registry.register_type(&meta_type::<()>());

// Metadata needs to contain this DispatchError, since codegen looks for it.
registry.register_type(&meta_type::<crate::utils::dispatch_error::ArrayDispatchError>());
registry.register_type(&meta_type::<dispatch_error::ArrayDispatchError>());

let metadata = RuntimeMetadataV15 {
Copy link
Collaborator

@jsdw jsdw Aug 30, 2023

Choose a reason for hiding this comment

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

Below, we add some CustomMetadata with a valid Foo value inside it (sorry; I can't comment on the exact line).

Could we also add a custom value with a random type ID that isn't in use (eg u32::MAX); people might opt to do this too and we want to be sure that we can still access the bytes and that nothing will panic (just Err) if we try decoding them.

Copy link
Contributor Author

@tadeohepperle tadeohepperle Aug 30, 2023

Choose a reason for hiding this comment

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

In that case we just test if the bytes are returned from the dynamic interface right? E.g. we do not generate any static custom value address. Or should we generate a static address that has a return type of Vec<u8>?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Since we've decided that you can't call .at() on a value with an invalid type ID, perhaps we can test that such an attempt fails with a compile error (via trybuild), but that .at_bytes() works, sicne that's what we've decided to allow.

types: registry.into(),
Expand Down
14 changes: 14 additions & 0 deletions testing/generate-custom-metadata/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

use codec::Encode;

/// can be called from the root of the project with: `cargo run --bin generate-custom-metadata`.
/// Generates a "./artifacts/metadata_with_custom_values.scale" file.
fn main() {
let metadata_prefixed = generate_custom_metadata::metadata_custom_values_foo();
let bytes = metadata_prefixed.encode();
std::fs::write("./artifacts/metadata_with_custom_values.scale", bytes)
Copy link
Collaborator

@jsdw jsdw Aug 30, 2023

Choose a reason for hiding this comment

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

Can we add a call to this to /scripts/artifacts.sh (ie cargo run --bin generate-custom-metadata > artifacts/metadata_with_custom_values.scale), so that running that script updates all of our artifacts :)

(also, let's either allow the caller to decide where to write the file to, or just write the file to stdout, so that the file location can be specified in artifacts.sh so it's clear from that script what is being generated)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh yes, that is a great idea!

.expect("should be able to write custom metadata");
}
4 changes: 3 additions & 1 deletion testing/ui-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ publish = false

[dev-dependencies]
trybuild = { workspace = true }
hex = { workspace = true }
scale-info = { workspace = true, features = ["bit-vec"] }
frame-metadata ={ workspace = true }
codec = { package = "parity-scale-codec", workspace = true, features = ["derive", "bit-vec"] }
subxt = { workspace = true, features = ["native", "jsonrpsee"] }
subxt = { workspace = true, features = ["native", "jsonrpsee", "substrate-compat"] }
Copy link
Collaborator

Choose a reason for hiding this comment

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

Where is substrate-compat needed? Offhand I can't see any use for it but I'm likely to be missing something :)

subxt-metadata = { workspace = true }
generate-custom-metadata = { path = "../generate-custom-metadata" }
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: extra line

44 changes: 44 additions & 0 deletions testing/ui-tests/src/correct/custom_values.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use codec::{Decode};
use subxt::{ext::sp_core::H256, OfflineClient, PolkadotConfig};
use subxt_metadata::Metadata;

#[subxt::subxt(runtime_metadata_path = "../../../../artifacts/metadata_with_custom_values.scale", derive_for_all_types = "Eq, PartialEq")]
pub mod node {}

use node::runtime_types::generate_custom_metadata::Foo;

fn main() {
let api = construct_offline_client();

let expected_foo = Foo {
a: 42,
b: "Have a great day!".into(),
};

// static query:
let foo_address = node::custom().foo();
let foo = api.custom_values().at(&foo_address).unwrap();
assert_eq!(foo, expected_foo);

// dynamic query:
let foo_value = api.custom_values().at("Foo").unwrap();
let foo: Foo = foo_value.as_type().unwrap();
assert_eq!(foo, expected_foo);
}

fn construct_offline_client() -> OfflineClient<PolkadotConfig> {
let genesis_hash = {
let h = "91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3";
let bytes = hex::decode(h).unwrap();
H256::from_slice(&bytes)
};
let runtime_version = subxt::backend::RuntimeVersion {
spec_version: 9370,
transaction_version: 20,
};
let metadata = {
let bytes = std::fs::read("../../../../artifacts/metadata_with_custom_values.scale").unwrap();
Metadata::decode(&mut &*bytes).unwrap()
};
OfflineClient::<PolkadotConfig>::new(genesis_hash, runtime_version, metadata)
}
8 changes: 5 additions & 3 deletions testing/ui-tests/src/dispatch_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

use crate::utils::{
dispatch_error::{ArrayDispatchError, LegacyDispatchError, NamedFieldDispatchError},
generate_metadata_from_pallets_custom_dispatch_error,
use crate::utils::generate_metadata_from_pallets_custom_dispatch_error;

use generate_custom_metadata::dispatch_error::{
ArrayDispatchError, LegacyDispatchError, NamedFieldDispatchError,
};

use frame_metadata::RuntimeMetadataPrefixed;

pub fn metadata_array_dispatch_error() -> RuntimeMetadataPrefixed {
Expand Down
8 changes: 1 addition & 7 deletions testing/ui-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
//! Use with `TRYBUILD=overwrite` after updating codebase (see `trybuild` docs for more details on that)
//! to automatically regenerate `stderr` files, but don't forget to check that new files make sense.

mod custom_values;
mod dispatch_errors;
mod storage;
mod utils;
Expand All @@ -21,6 +20,7 @@ use crate::utils::MetadataTestRunner;
// Each of these tests leads to some rust code being compiled and
// executed to test that compilation is successful (or errors in the
// way that we'd expect).

#[test]
fn ui_tests() {
let mut m = MetadataTestRunner::default();
Expand Down Expand Up @@ -52,12 +52,6 @@ fn ui_tests() {
.build(dispatch_errors::metadata_array_dispatch_error()),
);

t.pass(
m.new_test_case()
.name("custom_values_foo")
.build(custom_values::metadata_custom_values_foo()),
);

// Test retaining only specific pallets and ensure that works.
for pallet in ["Babe", "Claims", "Grandpa", "Balances"] {
let mut metadata = MetadataTestRunner::load_metadata();
Expand Down
6 changes: 2 additions & 4 deletions testing/ui-tests/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

pub mod dispatch_error;
mod metadata_test_runner;

use frame_metadata::{
Expand All @@ -12,6 +11,7 @@ use frame_metadata::{
},
RuntimeMetadataPrefixed,
};
use generate_custom_metadata::dispatch_error::ArrayDispatchError;
use scale_info::{meta_type, IntoPortable, TypeInfo};

pub use metadata_test_runner::MetadataTestRunner;
Expand Down Expand Up @@ -78,9 +78,7 @@ pub fn generate_metadata_from_pallets_custom_dispatch_error<DispatchError: TypeI
/// We default to a useless extrinsic type, and register a fake `DispatchError`
/// type so that codegen is happy with the metadata generated.
pub fn generate_metadata_from_pallets(pallets: Vec<PalletMetadata>) -> RuntimeMetadataPrefixed {
generate_metadata_from_pallets_custom_dispatch_error::<dispatch_error::ArrayDispatchError>(
pallets,
)
generate_metadata_from_pallets_custom_dispatch_error::<ArrayDispatchError>(pallets)
}

/// Given some storage entries, generate a [`RuntimeMetadataPrefixed`] struct.
Expand Down