fix: Convert vendor/pezkuwi-subxt from submodule to regular directory
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "ui-tests"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
[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"] }
|
||||
pezkuwi-subxt = { workspace = true, features = ["native", "jsonrpsee", "runtime-wasm-path"] }
|
||||
pezkuwi-subxt-metadata = { workspace = true }
|
||||
subxt-utils-stripmetadata = { workspace = true }
|
||||
generate-custom-metadata = { path = "../generate-custom-metadata" }
|
||||
@@ -0,0 +1,48 @@
|
||||
use codec::{Decode};
|
||||
use subxt::{config::substrate::H256, OfflineClient, PolkadotConfig};
|
||||
use pezkuwi_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_address = subxt::dynamic::custom_value::<Foo>("Foo");
|
||||
let foo = api.custom_values().at(&foo_address).unwrap();
|
||||
assert_eq!(foo, expected_foo);
|
||||
|
||||
// static query for some custom value that has an invalid type id: (we can still access the bytes)
|
||||
let custom_bytes = api.custom_values().bytes_at("InvalidTypeId").unwrap();
|
||||
assert_eq!(vec![0,1,2,3], custom_bytes);
|
||||
}
|
||||
|
||||
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::client::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)
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
use codec::{Decode, Encode};
|
||||
use subxt::utils::AccountId32;
|
||||
|
||||
#[derive(Encode, Decode, subxt::ext::scale_encode::EncodeAsType, subxt::ext::scale_decode::DecodeAsType, Debug)]
|
||||
#[encode_as_type(crate_path = "subxt::ext::scale_encode")]
|
||||
#[decode_as_type(crate_path = "subxt::ext::scale_decode")]
|
||||
pub struct CustomAddress(u16);
|
||||
|
||||
#[derive(Encode, Decode, subxt::ext::scale_encode::EncodeAsType, subxt::ext::scale_decode::DecodeAsType, Debug)]
|
||||
#[encode_as_type(crate_path = "subxt::ext::scale_encode")]
|
||||
#[decode_as_type(crate_path = "subxt::ext::scale_decode")]
|
||||
pub struct Generic<T>(T);
|
||||
|
||||
#[derive(Encode, Decode, subxt::ext::scale_encode::EncodeAsType, subxt::ext::scale_decode::DecodeAsType, Debug)]
|
||||
#[encode_as_type(crate_path = "subxt::ext::scale_encode")]
|
||||
#[decode_as_type(crate_path = "subxt::ext::scale_decode")]
|
||||
pub struct Second<T, U>(T, U);
|
||||
|
||||
#[derive(Encode, Decode, Debug)]
|
||||
pub struct DoesntImplEncodeDecodeAsType(u16);
|
||||
|
||||
#[subxt::subxt(
|
||||
runtime_metadata_path = "../../../../artifacts/polkadot_metadata_small.scale",
|
||||
substitute_type(
|
||||
path = "sp_runtime::multiaddress::MultiAddress<A, B>",
|
||||
// Discarding both params:
|
||||
with = "crate::CustomAddress"
|
||||
)
|
||||
)]
|
||||
pub mod node_runtime {}
|
||||
|
||||
#[subxt::subxt(
|
||||
runtime_metadata_path = "../../../../artifacts/polkadot_metadata_small.scale",
|
||||
substitute_type(
|
||||
path = "sp_runtime::multiaddress::MultiAddress<A, B>",
|
||||
// Discarding second param:
|
||||
with = "crate::Generic<A>"
|
||||
)
|
||||
)]
|
||||
pub mod node_runtime2 {}
|
||||
|
||||
#[subxt::subxt(
|
||||
runtime_metadata_path = "../../../../artifacts/polkadot_metadata_small.scale",
|
||||
substitute_type(
|
||||
path = "sp_runtime::multiaddress::MultiAddress<A, B>",
|
||||
// Discarding first param:
|
||||
with = "crate::Generic<B>"
|
||||
)
|
||||
)]
|
||||
pub mod node_runtime3 {}
|
||||
|
||||
#[subxt::subxt(
|
||||
runtime_metadata_path = "../../../../artifacts/polkadot_metadata_small.scale",
|
||||
substitute_type(
|
||||
path = "sp_runtime::multiaddress::MultiAddress<A, B>",
|
||||
// Swapping params:
|
||||
with = "crate::Second<B, A>"
|
||||
)
|
||||
)]
|
||||
pub mod node_runtime4 {}
|
||||
|
||||
#[subxt::subxt(
|
||||
runtime_metadata_path = "../../../../artifacts/polkadot_metadata_small.scale",
|
||||
substitute_type(
|
||||
path = "sp_runtime::multiaddress::MultiAddress",
|
||||
// Ignore input params and just use concrete types on output:
|
||||
with = "crate::Second<bool, ::std::vec::Vec<u8>>"
|
||||
)
|
||||
)]
|
||||
pub mod node_runtime5 {}
|
||||
|
||||
#[subxt::subxt(
|
||||
runtime_metadata_path = "../../../../artifacts/polkadot_metadata_small.scale",
|
||||
substitute_type(
|
||||
path = "sp_runtime::multiaddress::MultiAddress<A, B>",
|
||||
// We can put a static type in, too:
|
||||
with = "crate::Second<B, u16>"
|
||||
)
|
||||
)]
|
||||
pub mod node_runtime6 {}
|
||||
|
||||
#[subxt::subxt(
|
||||
runtime_metadata_path = "../../../../artifacts/polkadot_metadata_small.scale",
|
||||
substitute_type(
|
||||
path = "sp_runtime::multiaddress::MultiAddress<A, B>",
|
||||
// Check that things can be wrapped in our Static type:
|
||||
with = "::pezkuwi_subxt::utils::Static<crate::DoesntImplEncodeDecodeAsType>"
|
||||
)
|
||||
)]
|
||||
pub mod node_runtime7 {}
|
||||
|
||||
#[subxt::subxt(
|
||||
runtime_metadata_path = "../../../../artifacts/polkadot_metadata_small.scale",
|
||||
substitute_type(
|
||||
path = "sp_runtime::multiaddress::MultiAddress<A, B>",
|
||||
// Recursive type param substitution should work too (swapping out nested A and B):
|
||||
with = "::pezkuwi_subxt::utils::Static<crate::Second<A, B>>"
|
||||
)
|
||||
)]
|
||||
pub mod node_runtime8 {}
|
||||
|
||||
fn main() {
|
||||
// We assume Polkadot's config of MultiAddress<AccountId32, ()> here
|
||||
let _ = node_runtime::tx()
|
||||
.balances()
|
||||
.transfer_allow_death(CustomAddress(1337), 123);
|
||||
|
||||
let _ = node_runtime2::tx()
|
||||
.balances()
|
||||
.transfer_allow_death(Generic(AccountId32::from([0x01;32])), 123);
|
||||
|
||||
let _ = node_runtime3::tx()
|
||||
.balances()
|
||||
.transfer_allow_death(Generic(()), 123);
|
||||
|
||||
let _ = node_runtime4::tx()
|
||||
.balances()
|
||||
.transfer_allow_death(Second((), AccountId32::from([0x01;32])), 123);
|
||||
|
||||
let _ = node_runtime5::tx()
|
||||
.balances()
|
||||
.transfer_allow_death(Second(true, vec![1u8, 2u8]), 123);
|
||||
|
||||
let _ = node_runtime6::tx()
|
||||
.balances()
|
||||
.transfer_allow_death(Second((), 1234u16), 123);
|
||||
|
||||
let _ = node_runtime7::tx()
|
||||
.balances()
|
||||
.transfer_allow_death(subxt::utils::Static(DoesntImplEncodeDecodeAsType(1337)), 123);
|
||||
|
||||
let _ = node_runtime8::tx()
|
||||
.balances()
|
||||
.transfer_allow_death(subxt::utils::Static(Second(AccountId32::from([0x01;32]), ())), 123);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#[subxt::subxt(runtime_metadata_path = "../../../../artifacts/polkadot_metadata_tiny.scale")]
|
||||
pub mod node_runtime {
|
||||
pub struct SomeStruct;
|
||||
pub enum SomeEnum {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
pub trait SomeTrait {
|
||||
fn some_func(&self) -> u32;
|
||||
}
|
||||
impl SomeTrait for SomeStruct {
|
||||
fn some_func(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
}
|
||||
impl SomeTrait for SomeEnum {
|
||||
fn some_func(&self) -> u32 {
|
||||
2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use node_runtime::SomeTrait;
|
||||
|
||||
let unit = node_runtime::SomeStruct;
|
||||
assert_eq!(unit.some_func(), 1);
|
||||
let enumeration = node_runtime::SomeEnum::A;
|
||||
assert_eq!(enumeration.some_func(), 2);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#[subxt::subxt(runtime_path = "../../../../artifacts/westend_runtime.wasm")]
|
||||
mod runtime {}
|
||||
|
||||
#[subxt::subxt(runtime_path = "../../../../artifacts/westend_runtime.compact.compressed.wasm")]
|
||||
mod runtime_compressed {}
|
||||
|
||||
fn main() {
|
||||
use runtime;
|
||||
use runtime_compressed;
|
||||
|
||||
let _ = runtime::system::events::CodeUpdated;
|
||||
let _ = runtime_compressed::system::events::CodeUpdated;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
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 {
|
||||
generate_metadata_from_pallets_custom_dispatch_error::<ArrayDispatchError>(vec![], vec![])
|
||||
}
|
||||
|
||||
pub fn metadata_legacy_dispatch_error() -> RuntimeMetadataPrefixed {
|
||||
generate_metadata_from_pallets_custom_dispatch_error::<LegacyDispatchError>(vec![], vec![])
|
||||
}
|
||||
|
||||
pub fn metadata_named_field_dispatch_error() -> RuntimeMetadataPrefixed {
|
||||
generate_metadata_from_pallets_custom_dispatch_error::<NamedFieldDispatchError>(vec![], vec![])
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#[subxt::subxt()]
|
||||
pub mod node_runtime {}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
error: At least one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' or 'runtime_path` can be provided
|
||||
--> src/incorrect/need_url_or_path.rs:1:1
|
||||
|
|
||||
1 | #[subxt::subxt()]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the attribute macro `subxt::subxt` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#[subxt::subxt(
|
||||
runtime_metadata_path = "../../../../artifacts/polkadot_metadata_small.scale",
|
||||
substitute_type(
|
||||
path = "sp_runtime::multiaddress::Event",
|
||||
with = "crate::MyEvent"
|
||||
)
|
||||
)]
|
||||
pub mod node_runtime {}
|
||||
|
||||
fn main() {}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
error: Type `Event` does not exist at path `sp_runtime::multiaddress::Event`
|
||||
|
||||
A type with the same name is present at:
|
||||
frame_system::pallet::Event
|
||||
pallet_indices::pallet::Event
|
||||
pallet_balances::pallet::Event
|
||||
pallet_parameters::pallet::Event
|
||||
pallet_transaction_payment::pallet::Event
|
||||
pallet_offences::pallet::Event
|
||||
pallet_session::historical::pallet::Event
|
||||
pallet_session::pallet::Event
|
||||
pallet_grandpa::pallet::Event
|
||||
pallet_treasury::pallet::Event
|
||||
pallet_conviction_voting::pallet::Event
|
||||
pallet_ranked_collective::pallet::Event
|
||||
pallet_whitelist::pallet::Event
|
||||
polkadot_runtime_common::claims::pallet::Event
|
||||
pallet_utility::pallet::Event
|
||||
pallet_identity::pallet::Event
|
||||
pallet_society::pallet::Event
|
||||
pallet_recovery::pallet::Event
|
||||
pallet_vesting::pallet::Event
|
||||
pallet_scheduler::pallet::Event
|
||||
pallet_proxy::pallet::Event
|
||||
pallet_multisig::pallet::Event
|
||||
pallet_preimage::pallet::Event
|
||||
pallet_asset_rate::pallet::Event
|
||||
pallet_bounties::pallet::Event
|
||||
pallet_child_bounties::pallet::Event
|
||||
pallet_nis::pallet::Event
|
||||
pallet_balances::pallet::Event
|
||||
polkadot_runtime_parachains::inclusion::pallet::Event
|
||||
polkadot_runtime_parachains::paras::pallet::Event
|
||||
polkadot_runtime_parachains::hrmp::pallet::Event
|
||||
polkadot_runtime_parachains::disputes::pallet::Event
|
||||
pallet_message_queue::pallet::Event
|
||||
polkadot_runtime_parachains::on_demand::pallet::Event
|
||||
polkadot_runtime_common::paras_registrar::pallet::Event
|
||||
polkadot_runtime_common::slots::pallet::Event
|
||||
polkadot_runtime_common::auctions::pallet::Event
|
||||
polkadot_runtime_common::crowdloan::pallet::Event
|
||||
polkadot_runtime_parachains::coretime::pallet::Event
|
||||
pallet_migrations::pallet::Event
|
||||
pallet_xcm::pallet::Event
|
||||
polkadot_runtime_common::identity_migrator::pallet::Event
|
||||
polkadot_runtime_common::assigned_slots::pallet::Event
|
||||
rococo_runtime::validator_manager::pallet::Event
|
||||
pallet_state_trie_migration::pallet::Event
|
||||
pallet_root_testing::pallet::Event
|
||||
pallet_sudo::pallet::Event
|
||||
--> src/incorrect/substitute_at_wrong_path.rs:1:1
|
||||
|
|
||||
1 | / #[subxt::subxt(
|
||||
2 | | runtime_metadata_path = "../../../../artifacts/polkadot_metadata_small.scale",
|
||||
3 | | substitute_type(
|
||||
4 | | path = "sp_runtime::multiaddress::Event",
|
||||
... |
|
||||
7 | | )]
|
||||
| |__^
|
||||
|
|
||||
= note: this error originates in the attribute macro `subxt::subxt` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#[subxt::subxt(
|
||||
runtime_metadata_path = "../../../../artifacts/polkadot_metadata_small.scale",
|
||||
substitute_type(
|
||||
path = "frame_support::dispatch::DispatchInfo",
|
||||
with = "my_mod::DispatchInfo"
|
||||
)
|
||||
)]
|
||||
pub mod node_runtime {}
|
||||
|
||||
fn main() {}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
error: Type `DispatchInfo` does not exist at path `frame_support::dispatch::DispatchInfo`
|
||||
|
||||
There is no Type with name `DispatchInfo` in the provided metadata.
|
||||
--> src/incorrect/substitute_path_not_absolute.rs:1:1
|
||||
|
|
||||
1 | / #[subxt::subxt(
|
||||
2 | | runtime_metadata_path = "../../../../artifacts/polkadot_metadata_small.scale",
|
||||
3 | | substitute_type(
|
||||
4 | | path = "frame_support::dispatch::DispatchInfo",
|
||||
... |
|
||||
7 | | )]
|
||||
| |__^
|
||||
|
|
||||
= note: this error originates in the attribute macro `subxt::subxt` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
@@ -0,0 +1,14 @@
|
||||
#[subxt::subxt(
|
||||
runtime_metadata_path = "../../../../artifacts/polkadot_metadata_tiny.scale",
|
||||
runtime_metadata_insecure_url = "wss://rpc.polkadot.io:443"
|
||||
)]
|
||||
pub mod node_runtime {}
|
||||
|
||||
#[subxt::subxt(
|
||||
runtime_metadata_path = "../../../../artifacts/polkadot_metadata_tiny.scale",
|
||||
runtime_metadata_insecure_url = "wss://rpc.polkadot.io:443",
|
||||
runtime_path = "../../../../artifacts/westend_runtime.wasm"
|
||||
)]
|
||||
pub mod node_runtime2 {}
|
||||
|
||||
fn main() {}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
error: Only one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' or 'runtime_path` can be provided
|
||||
--> src/incorrect/url_and_path_provided.rs:1:1
|
||||
|
|
||||
1 | / #[subxt::subxt(
|
||||
2 | | runtime_metadata_path = "../../../../artifacts/polkadot_metadata_tiny.scale",
|
||||
3 | | runtime_metadata_insecure_url = "wss://rpc.polkadot.io:443"
|
||||
4 | | )]
|
||||
| |__^
|
||||
|
|
||||
= note: this error originates in the attribute macro `subxt::subxt` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: Only one of 'runtime_metadata_path', 'runtime_metadata_insecure_url' or `runtime_path` must be provided
|
||||
--> src/incorrect/url_and_path_provided.rs:7:1
|
||||
|
|
||||
7 | / #[subxt::subxt(
|
||||
8 | | runtime_metadata_path = "../../../../artifacts/polkadot_metadata_tiny.scale",
|
||||
9 | | runtime_metadata_insecure_url = "wss://rpc.polkadot.io:443",
|
||||
10 | | runtime_path = "../../../../artifacts/westend_runtime.wasm"
|
||||
11 | | )]
|
||||
| |__^
|
||||
|
|
||||
= note: this error originates in the attribute macro `subxt::subxt` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
#![cfg(test)]
|
||||
|
||||
//! UI test set uses [`trybuild`](https://docs.rs/trybuild/latest/trybuild/index.html) to
|
||||
//! check whether expected valid examples of code compile correctly, and for incorrect ones
|
||||
//! errors are helpful and valid (e.g. have correct spans).
|
||||
//!
|
||||
//!
|
||||
//! 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 dispatch_errors;
|
||||
mod runtime_apis;
|
||||
mod storage;
|
||||
mod utils;
|
||||
|
||||
use crate::utils::MetadataTestRunner;
|
||||
use frame_metadata::{RuntimeMetadata, RuntimeMetadataPrefixed};
|
||||
use pezkuwi_subxt_utils_stripmetadata::StripMetadata;
|
||||
|
||||
// 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).
|
||||
|
||||
fn strip_metadata<Pallets, Apis>(
|
||||
metadata: &mut RuntimeMetadataPrefixed,
|
||||
pallets: Pallets,
|
||||
apis: Apis,
|
||||
) where
|
||||
Pallets: Fn(&str) -> bool,
|
||||
Apis: Fn(&str) -> bool,
|
||||
{
|
||||
match &mut metadata.1 {
|
||||
RuntimeMetadata::V14(m) => m.strip_metadata(pallets, apis),
|
||||
RuntimeMetadata::V15(m) => m.strip_metadata(pallets, apis),
|
||||
RuntimeMetadata::V16(m) => m.strip_metadata(pallets, apis),
|
||||
m => panic!(
|
||||
"Metadata should be V14, V15 or V16, but is V{}",
|
||||
m.version()
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ui_tests() {
|
||||
let mut m = MetadataTestRunner::default();
|
||||
let t = trybuild::TestCases::new();
|
||||
|
||||
t.pass("src/correct/*.rs");
|
||||
// Check that storage maps with no keys are handled properly.
|
||||
t.pass(
|
||||
m.new_test_case()
|
||||
.name("storage_map_no_keys")
|
||||
.build(storage::metadata_storage_map_no_keys()),
|
||||
);
|
||||
|
||||
// Check runtime APIs with _ in method names work
|
||||
t.pass(
|
||||
m.new_test_case()
|
||||
.name("runtime_api_underscore_method_name")
|
||||
.build(runtime_apis::metadata_runtime_api_underscore_method_name()),
|
||||
);
|
||||
|
||||
// Test that the codegen can handle the different types of DispatchError.
|
||||
t.pass(
|
||||
m.new_test_case()
|
||||
.name("named_field_dispatch_error")
|
||||
.build(dispatch_errors::metadata_named_field_dispatch_error()),
|
||||
);
|
||||
t.pass(
|
||||
m.new_test_case()
|
||||
.name("legacy_dispatch_error")
|
||||
.build(dispatch_errors::metadata_legacy_dispatch_error()),
|
||||
);
|
||||
t.pass(
|
||||
m.new_test_case()
|
||||
.name("array_dispatch_error")
|
||||
.build(dispatch_errors::metadata_array_dispatch_error()),
|
||||
);
|
||||
|
||||
// Test retaining only specific pallets and ensure that works.
|
||||
for pallet in ["Babe", "Claims", "Grandpa", "Balances"] {
|
||||
let mut metadata = MetadataTestRunner::load_metadata();
|
||||
strip_metadata(&mut metadata, |p| p == pallet, |_| true);
|
||||
|
||||
t.pass(
|
||||
m.new_test_case()
|
||||
.name(format!("retain_pallet_{pallet}"))
|
||||
.build(metadata),
|
||||
);
|
||||
}
|
||||
|
||||
// Test retaining only specific runtime APIs to ensure that works.
|
||||
for runtime_api in ["Core", "Metadata"] {
|
||||
let mut metadata = MetadataTestRunner::load_metadata();
|
||||
strip_metadata(&mut metadata, |_| true, |r| r == runtime_api);
|
||||
|
||||
t.pass(
|
||||
m.new_test_case()
|
||||
.name(format!("retain_runtime_api_{runtime_api}"))
|
||||
.build(metadata),
|
||||
);
|
||||
}
|
||||
|
||||
// Validation should succeed when metadata we codegen from is stripped and
|
||||
// client state is full:
|
||||
{
|
||||
let mut metadata = MetadataTestRunner::load_metadata();
|
||||
strip_metadata(
|
||||
&mut metadata,
|
||||
|p| ["Babe", "Claims"].contains(&p),
|
||||
|r| ["Core", "Metadata"].contains(&r),
|
||||
);
|
||||
|
||||
t.pass(
|
||||
m.new_test_case()
|
||||
.name("stripped_metadata_validates_against_full")
|
||||
.validation_metadata(MetadataTestRunner::load_metadata())
|
||||
.build(metadata),
|
||||
);
|
||||
}
|
||||
|
||||
// Finally as a sanity check, codegen against stripped metadata should
|
||||
// _not_ compare valid against client with differently stripped metadata.
|
||||
{
|
||||
let mut codegen_metadata = MetadataTestRunner::load_metadata();
|
||||
strip_metadata(
|
||||
&mut codegen_metadata,
|
||||
|p| ["Babe", "Claims"].contains(&p),
|
||||
|r| ["Core", "Metadata"].contains(&r),
|
||||
);
|
||||
let mut validation_metadata = MetadataTestRunner::load_metadata();
|
||||
strip_metadata(
|
||||
&mut validation_metadata,
|
||||
|p| p != "Claims",
|
||||
|r| r != "Metadata",
|
||||
);
|
||||
|
||||
t.pass(
|
||||
m.new_test_case()
|
||||
.name("stripped_metadata_doesnt_validate_against_different")
|
||||
.validation_metadata(validation_metadata)
|
||||
.expects_invalid()
|
||||
.build(codegen_metadata),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ui_fail() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("src/incorrect/*.rs");
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use frame_metadata::{
|
||||
v15::{RuntimeApiMetadata, RuntimeApiMethodMetadata, RuntimeApiMethodParamMetadata},
|
||||
RuntimeMetadataPrefixed,
|
||||
};
|
||||
|
||||
use crate::utils::generate_metadata_from_runtime_apis;
|
||||
|
||||
/// Generate metadata which contains a `Map` storage entry with no hashers/values.
|
||||
/// This is a bit of an odd case, but it was raised in https://github.com/paritytech/subxt/issues/552,
|
||||
/// and this test will fail before the fix and should pass once the fix is applied.
|
||||
pub fn metadata_runtime_api_underscore_method_name() -> RuntimeMetadataPrefixed {
|
||||
generate_metadata_from_runtime_apis(vec![RuntimeApiMetadata {
|
||||
name: "MyApi".to_owned(),
|
||||
docs: vec![],
|
||||
methods: vec![RuntimeApiMethodMetadata {
|
||||
name: "my_method".to_owned(),
|
||||
inputs: vec![RuntimeApiMethodParamMetadata {
|
||||
name: "_".to_owned(), // The important bit we're testing.
|
||||
ty: 0.into(), // we don't care what type this is.
|
||||
}],
|
||||
output: 0.into(), // we don't care what type this is.
|
||||
docs: vec![],
|
||||
}],
|
||||
}])
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use frame_metadata::{
|
||||
v15::{StorageEntryMetadata, StorageEntryModifier, StorageEntryType},
|
||||
RuntimeMetadataPrefixed,
|
||||
};
|
||||
use scale_info::meta_type;
|
||||
|
||||
use crate::utils::generate_metadata_from_storage_entries;
|
||||
|
||||
/// Generate metadata which contains a `Map` storage entry with no hashers/values.
|
||||
/// This is a bit of an odd case, but it was raised in https://github.com/paritytech/subxt/issues/552,
|
||||
/// and this test will fail before the fix and should pass once the fix is applied.
|
||||
pub fn metadata_storage_map_no_keys() -> RuntimeMetadataPrefixed {
|
||||
generate_metadata_from_storage_entries(vec![StorageEntryMetadata {
|
||||
name: "MapWithNoKeys",
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hashers: vec![],
|
||||
key: meta_type::<()>(),
|
||||
value: meta_type::<u32>(),
|
||||
},
|
||||
default: vec![0],
|
||||
docs: vec![],
|
||||
}])
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use frame_metadata::RuntimeMetadataPrefixed;
|
||||
use std::io::Read;
|
||||
|
||||
static TEST_DIR_PREFIX: &str = "subxt_generated_ui_tests_";
|
||||
static METADATA_FILE: &str = "../../artifacts/polkadot_metadata_full.scale";
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MetadataTestRunner {
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl MetadataTestRunner {
|
||||
/// Loads metadata that we can use in our tests. Panics if
|
||||
/// there is some issue decoding the metadata.
|
||||
pub fn load_metadata() -> RuntimeMetadataPrefixed {
|
||||
let mut file =
|
||||
std::fs::File::open(METADATA_FILE).expect("Cannot open metadata.scale artifact");
|
||||
|
||||
let mut bytes = Vec::new();
|
||||
file.read_to_end(&mut bytes)
|
||||
.expect("Failed to read metadata.scale file");
|
||||
|
||||
RuntimeMetadataPrefixed::decode(&mut &*bytes).expect("Cannot decode metadata bytes")
|
||||
}
|
||||
|
||||
/// Create a new test case.
|
||||
pub fn new_test_case(&mut self) -> MetadataTestRunnerCaseBuilder {
|
||||
let index = self.index;
|
||||
// increment index so that each test case gets its own folder path.
|
||||
self.index += 1;
|
||||
|
||||
MetadataTestRunnerCaseBuilder::new(index)
|
||||
}
|
||||
}
|
||||
|
||||
// `trybuild` runs all tests once it's dropped. So, we defer all cleanup until we
|
||||
// are dropped too, to make sure that cleanup happens after tests are ran.
|
||||
impl Drop for MetadataTestRunner {
|
||||
fn drop(&mut self) {
|
||||
for i in 0..self.index {
|
||||
let mut tmp_dir = std::env::temp_dir();
|
||||
tmp_dir.push(format!("{TEST_DIR_PREFIX}{i}"));
|
||||
std::fs::remove_dir_all(tmp_dir).expect("cannot cleanup temp files");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a single test case.
|
||||
pub struct MetadataTestRunnerCaseBuilder {
|
||||
index: usize,
|
||||
name: String,
|
||||
validation_metadata: Option<RuntimeMetadataPrefixed>,
|
||||
should_be_valid: bool,
|
||||
}
|
||||
|
||||
impl MetadataTestRunnerCaseBuilder {
|
||||
fn new(index: usize) -> Self {
|
||||
MetadataTestRunnerCaseBuilder {
|
||||
index,
|
||||
name: format!("Test {index}"),
|
||||
validation_metadata: None,
|
||||
should_be_valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the test name.
|
||||
pub fn name(mut self, name: impl AsRef<str>) -> Self {
|
||||
name.as_ref().clone_into(&mut self.name);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set metadata to be validated against the generated code.
|
||||
/// By default, we'll validate the same metadata used to generate the code.
|
||||
pub fn validation_metadata(mut self, md: impl Into<RuntimeMetadataPrefixed>) -> Self {
|
||||
self.validation_metadata = Some(md.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Expect the validation metadata provided to _not_ be valid.
|
||||
pub fn expects_invalid(mut self) -> Self {
|
||||
self.should_be_valid = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// At the minimum, takes some metadata and a test name, generates the code
|
||||
/// and hands back a path to some generated code that `trybuild` can be pointed at.
|
||||
/// validation metadata and expected validity can also be provided.
|
||||
///
|
||||
/// The generated code:
|
||||
/// - checks that the subxt macro can perform codegen given the
|
||||
/// provided macro_metadata without running into any issues.
|
||||
/// - checks that the `runtime::is_codegen_valid_for` function returns
|
||||
/// true or false when compared to the `validation_metadata`, according
|
||||
/// to whether `expects_invalid()` is set or not.
|
||||
///
|
||||
/// The generated code will be tidied up when the `MetadataTestRunner` that
|
||||
/// this was handed out from is dropped.
|
||||
pub fn build(self, macro_metadata: frame_metadata::RuntimeMetadataPrefixed) -> String {
|
||||
let validation_metadata = self.validation_metadata.unwrap_or_else(|| {
|
||||
// RuntimeMetadataPrefixed doesn't implement Clone for some reason (we should prob fix that).
|
||||
// until then, this hack clones it by encoding and then decoding it again from bytes..
|
||||
clone_via_encode(¯o_metadata)
|
||||
});
|
||||
|
||||
let index = self.index;
|
||||
let mut tmp_dir = std::env::temp_dir();
|
||||
tmp_dir.push(format!("{TEST_DIR_PREFIX}{index}"));
|
||||
|
||||
let tmp_macro_metadata_path = {
|
||||
let mut t = tmp_dir.clone();
|
||||
t.push("macro_metadata.scale");
|
||||
t.to_string_lossy().into_owned()
|
||||
};
|
||||
let tmp_validation_metadata_path = {
|
||||
let mut t = tmp_dir.clone();
|
||||
t.push("validation_metadata.scale");
|
||||
t.to_string_lossy().into_owned()
|
||||
};
|
||||
let tmp_rust_path = {
|
||||
let mut t = tmp_dir.clone();
|
||||
let test_name = &self.name;
|
||||
t.push(format!("{test_name}.rs"));
|
||||
t.to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let encoded_macro_metadata = macro_metadata.encode();
|
||||
let encoded_validation_metadata = validation_metadata.encode();
|
||||
|
||||
let should_be_valid_str = if self.should_be_valid {
|
||||
"true"
|
||||
} else {
|
||||
"false"
|
||||
};
|
||||
|
||||
let rust_file = format!(
|
||||
r#"
|
||||
use subxt;
|
||||
use subxt::ext::codec::Decode;
|
||||
use std::io::Read;
|
||||
|
||||
#[subxt::subxt(runtime_metadata_path = "{tmp_macro_metadata_path}")]
|
||||
pub mod polkadot {{}}
|
||||
|
||||
fn main() {{
|
||||
// load validation metadata:
|
||||
let mut file = std::fs::File::open("{tmp_validation_metadata_path}")
|
||||
.expect("validation_metadata exists");
|
||||
|
||||
let mut bytes = Vec::new();
|
||||
file.read_to_end(&mut bytes)
|
||||
.expect("Failed to read metadata.scale file");
|
||||
|
||||
let metadata = subxt::Metadata::decode(&mut &*bytes)
|
||||
.expect("Cannot decode metadata bytes");
|
||||
|
||||
// validate it:
|
||||
let is_valid = polkadot::is_codegen_valid_for(&metadata);
|
||||
assert_eq!(is_valid, {should_be_valid_str}, "expected validity to line up");
|
||||
}}
|
||||
"#
|
||||
);
|
||||
|
||||
std::fs::create_dir_all(&tmp_dir).expect("could not create tmp ui test dir");
|
||||
// Write metadatas to tmp folder:
|
||||
std::fs::write(&tmp_macro_metadata_path, encoded_macro_metadata).unwrap();
|
||||
std::fs::write(&tmp_validation_metadata_path, encoded_validation_metadata).unwrap();
|
||||
// Write test file to tmp folder (it'll be moved by trybuild):
|
||||
std::fs::write(&tmp_rust_path, rust_file).unwrap();
|
||||
|
||||
tmp_rust_path
|
||||
}
|
||||
}
|
||||
|
||||
fn clone_via_encode<T: codec::Encode + codec::Decode>(item: &T) -> T {
|
||||
let bytes = item.encode();
|
||||
T::decode(&mut &*bytes).unwrap()
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
mod metadata_test_runner;
|
||||
|
||||
use frame_metadata::{
|
||||
v15::{
|
||||
CustomMetadata, ExtrinsicMetadata, OuterEnums, PalletMetadata, PalletStorageMetadata,
|
||||
RuntimeApiMetadata, RuntimeMetadataV15, StorageEntryMetadata,
|
||||
},
|
||||
RuntimeMetadataPrefixed,
|
||||
};
|
||||
use generate_custom_metadata::dispatch_error::ArrayDispatchError;
|
||||
use scale_info::{form::PortableForm, meta_type, IntoPortable, TypeInfo};
|
||||
|
||||
pub use metadata_test_runner::MetadataTestRunner;
|
||||
|
||||
/// Given some pallet metadata, generate a [`RuntimeMetadataPrefixed`] struct.
|
||||
/// We default to a useless extrinsic type, and register a fake `DispatchError`
|
||||
/// type matching the generic type param provided.
|
||||
pub fn generate_metadata_from_pallets_custom_dispatch_error<DispatchError: TypeInfo + 'static>(
|
||||
pallets: Vec<PalletMetadata>,
|
||||
runtime_apis: Vec<RuntimeApiMetadata<PortableForm>>,
|
||||
) -> RuntimeMetadataPrefixed {
|
||||
// We don't care about the extrinsic type.
|
||||
let extrinsic = ExtrinsicMetadata {
|
||||
version: 0,
|
||||
signed_extensions: vec![],
|
||||
address_ty: meta_type::<()>(),
|
||||
call_ty: meta_type::<()>(),
|
||||
signature_ty: meta_type::<()>(),
|
||||
extra_ty: meta_type::<()>(),
|
||||
};
|
||||
|
||||
// Construct metadata manually from our types (See `RuntimeMetadataV15::new()`).
|
||||
// Add any extra types we need to the registry.
|
||||
let mut registry = scale_info::Registry::new();
|
||||
let pallets = registry.map_into_portable(pallets);
|
||||
let extrinsic = extrinsic.into_portable(&mut registry);
|
||||
|
||||
#[derive(TypeInfo)]
|
||||
struct Runtime;
|
||||
#[derive(TypeInfo)]
|
||||
enum RuntimeCall {}
|
||||
#[derive(TypeInfo)]
|
||||
enum RuntimeEvent {}
|
||||
#[derive(TypeInfo)]
|
||||
enum RuntimeError {}
|
||||
|
||||
let ty = registry.register_type(&meta_type::<Runtime>());
|
||||
let runtime_call = registry.register_type(&meta_type::<RuntimeCall>());
|
||||
let runtime_event = registry.register_type(&meta_type::<RuntimeEvent>());
|
||||
let runtime_error = registry.register_type(&meta_type::<RuntimeError>());
|
||||
|
||||
// Metadata needs to contain this DispatchError, since codegen looks for it.
|
||||
registry.register_type(&meta_type::<DispatchError>());
|
||||
|
||||
let metadata = RuntimeMetadataV15 {
|
||||
types: registry.into(),
|
||||
pallets,
|
||||
extrinsic,
|
||||
ty,
|
||||
apis: runtime_apis,
|
||||
outer_enums: OuterEnums {
|
||||
call_enum_ty: runtime_call,
|
||||
event_enum_ty: runtime_event,
|
||||
error_enum_ty: runtime_error,
|
||||
},
|
||||
custom: CustomMetadata {
|
||||
map: Default::default(),
|
||||
},
|
||||
};
|
||||
|
||||
RuntimeMetadataPrefixed::from(metadata)
|
||||
}
|
||||
|
||||
/// Given some pallet metadata, generate a [`RuntimeMetadataPrefixed`] struct.
|
||||
/// 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::<ArrayDispatchError>(pallets, vec![])
|
||||
}
|
||||
|
||||
/// Given some runtime API metadata, generate a [`RuntimeMetadataPrefixed`] struct.
|
||||
/// 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_runtime_apis(
|
||||
runtime_apis: Vec<RuntimeApiMetadata<PortableForm>>,
|
||||
) -> RuntimeMetadataPrefixed {
|
||||
generate_metadata_from_pallets_custom_dispatch_error::<ArrayDispatchError>(vec![], runtime_apis)
|
||||
}
|
||||
|
||||
/// Given some storage entries, generate a [`RuntimeMetadataPrefixed`] struct.
|
||||
/// We default to a useless extrinsic type, mock a pallet out, and register a
|
||||
/// fake `DispatchError` type so that codegen is happy with the metadata generated.
|
||||
pub fn generate_metadata_from_storage_entries(
|
||||
storage_entries: Vec<StorageEntryMetadata>,
|
||||
) -> RuntimeMetadataPrefixed {
|
||||
let storage = PalletStorageMetadata {
|
||||
prefix: "System",
|
||||
entries: storage_entries,
|
||||
};
|
||||
|
||||
let pallet = PalletMetadata {
|
||||
index: 0,
|
||||
name: "System",
|
||||
storage: Some(storage),
|
||||
constants: vec![],
|
||||
calls: None,
|
||||
event: None,
|
||||
error: None,
|
||||
docs: vec![],
|
||||
};
|
||||
|
||||
generate_metadata_from_pallets(vec![pallet])
|
||||
}
|
||||
Reference in New Issue
Block a user