// 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 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( 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"); }