Move test crates into a "testing" folder and add a ui (trybuild) test and ui-test helpers (#567)

* move test crates into a testing folder and add a ui test and helpers

* undo wee mixup with another PR

* cargo fmt

* clippy

* tidy ui-tests a little

* test different DispatchError types

* refactor dispatch error stuff

* name ui tests

* duff => useless

* align versions and cargo fmt
This commit is contained in:
James Wilson
2022-06-17 14:33:58 +01:00
committed by GitHub
parent 7621d71892
commit 9cf63bafac
31 changed files with 1005 additions and 4 deletions
@@ -0,0 +1,120 @@
// 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 scale_info::{
build::{
FieldsBuilder,
NamedFields,
UnnamedFields,
Variants,
},
Path,
Type,
TypeInfo,
};
/// See the `ModuleErrorType` in `subxt_codegen` for more info on the different DispatchError
/// types that we've encountered. We need the path to match `sp_runtime::DispatchError`, otherwise
/// we could just implement roughly the correct types and derive TypeInfo on them.
/// This type has TypeInfo compatible with the `NamedField` version of the DispatchError.
/// This is the oldest version that subxt supports:
/// `DispatchError::Module { index: u8, error: u8 }`
pub enum NamedFieldDispatchError {}
impl TypeInfo for NamedFieldDispatchError {
type Identity = Self;
fn type_info() -> Type {
Type::builder()
.path(Path::new("DispatchError", "sp_runtime"))
.variant(Variants::new().variant("Module", |builder| {
builder
.fields(
FieldsBuilder::<NamedFields>::default()
.field(|b| b.name("error").ty::<u8>())
.field(|b| b.name("index").ty::<u8>()),
)
.index(0)
}))
}
}
/// This type has TypeInfo compatible with the `LegacyError` version of the DispatchError.
/// This is the version wasn't around for long:
/// `DispatchError::Module ( sp_runtime::ModuleError { index: u8, error: u8 } )`
pub enum LegacyDispatchError {}
impl TypeInfo for LegacyDispatchError {
type Identity = Self;
fn type_info() -> Type {
struct ModuleError;
impl TypeInfo for ModuleError {
type Identity = Self;
fn type_info() -> Type {
Type::builder()
.path(Path::new("ModuleError", "sp_runtime"))
.composite(
FieldsBuilder::<NamedFields>::default()
.field(|b| b.name("index").ty::<u8>())
.field(|b| b.name("error").ty::<u8>()),
)
}
}
Type::builder()
.path(Path::new("DispatchError", "sp_runtime"))
.variant(Variants::new().variant("Module", |builder| {
builder
.fields(
FieldsBuilder::<UnnamedFields>::default()
.field(|b| b.ty::<ModuleError>()),
)
.index(0)
}))
}
}
/// This type has TypeInfo compatible with the `ArrayError` version of the DispatchError.
/// This is the current version:
/// `DispatchError::Module ( sp_runtime::ModuleError { index: u8, error: [u8; 4] } )`
pub enum ArrayDispatchError {}
impl TypeInfo for ArrayDispatchError {
type Identity = Self;
fn type_info() -> Type {
struct ModuleError;
impl TypeInfo for ModuleError {
type Identity = Self;
fn type_info() -> Type {
Type::builder()
.path(Path::new("ModuleError", "sp_runtime"))
.composite(
FieldsBuilder::<NamedFields>::default()
.field(|b| b.name("index").ty::<u8>())
.field(|b| b.name("error").ty::<[u8; 4]>()),
)
}
}
Type::builder()
.path(Path::new("DispatchError", "sp_runtime"))
.variant(Variants::new().variant("Module", |builder| {
builder
.fields(
FieldsBuilder::<UnnamedFields>::default()
.field(|b| b.ty::<ModuleError>()),
)
.index(0)
}))
}
}
@@ -0,0 +1,85 @@
// 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 codec::Encode;
use frame_metadata::RuntimeMetadataPrefixed;
static TEST_DIR_PREFIX: &str = "subxt_generated_ui_tests_";
#[derive(Default)]
pub struct MetadataTestRunner {
index: usize,
}
impl MetadataTestRunner {
pub fn path_to_ui_test_for_metadata(
&mut self,
name: impl AsRef<str>,
metadata: RuntimeMetadataPrefixed,
) -> String {
let test_name = name.as_ref();
// increment test index to avoid overlaps.
let index = self.index;
self.index += 1;
let mut tmp_dir = std::env::temp_dir();
tmp_dir.push(format!("{TEST_DIR_PREFIX}{index}"));
let tmp_metadata_path = {
let mut t = tmp_dir.clone();
t.push("metadata.scale");
t.to_string_lossy().into_owned()
};
let tmp_rust_path = {
let mut t = tmp_dir.clone();
t.push(format!("{test_name}.rs"));
t.to_string_lossy().into_owned()
};
let encoded_metadata = metadata.encode();
let rust_file = format!(
r#"
use subxt;
#[subxt::subxt(runtime_metadata_path = "{tmp_metadata_path}")]
pub mod polkadot {{}}
fn main() {{}}
"#
);
std::fs::create_dir_all(&tmp_dir).expect("could not create tmp ui test dir");
// Write metadata to tmp folder:
std::fs::write(&tmp_metadata_path, &encoded_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
}
}
// `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");
}
}
}
+104
View File
@@ -0,0 +1,104 @@
// 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/>.
pub mod dispatch_error;
mod metadata_test_runner;
use frame_metadata::{
v14::RuntimeMetadataV14,
ExtrinsicMetadata,
PalletMetadata,
PalletStorageMetadata,
RuntimeMetadataPrefixed,
StorageEntryMetadata,
};
use scale_info::{
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>,
) -> RuntimeMetadataPrefixed {
// We don't care about the extrinsic type.
let extrinsic = ExtrinsicMetadata {
ty: meta_type::<()>(),
version: 0,
signed_extensions: vec![],
};
// Construct metadata manually from our types (See `RuntimeMetadataV14::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);
let ty = registry.register_type(&meta_type::<()>());
// Metadata needs to contain this DispatchError, since codegen looks for it.
registry.register_type(&meta_type::<DispatchError>());
let metadata = RuntimeMetadataV14 {
types: registry.into(),
pallets,
extrinsic,
ty,
};
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::<
dispatch_error::ArrayDispatchError,
>(pallets)
}
/// 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,
};
generate_metadata_from_pallets(vec![pallet])
}