mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 02:21:14 +00:00
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:
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "test-runtime"
|
||||
version = "0.21.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
subxt = { path = "../../subxt" }
|
||||
sp-runtime = "6.0.0"
|
||||
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full", "bit-vec"] }
|
||||
|
||||
[build-dependencies]
|
||||
subxt = { path = "../../subxt" }
|
||||
sp-core = "6.0.0"
|
||||
tokio = { version = "1.8", features = ["macros", "rt-multi-thread"] }
|
||||
which = "4.2.2"
|
||||
@@ -0,0 +1,10 @@
|
||||
# test-runtime
|
||||
|
||||
The logic for this crate exists mainly in the `build.rs` file.
|
||||
|
||||
At compile time, this crate will:
|
||||
- Spin up a local `substrate` binary (set the `SUBSTRATE_NODE_PATH` env var to point to a custom binary, otherwise it'll look for `substrate` on your PATH).
|
||||
- Obtain metadata from this node.
|
||||
- Export the metadata and a `node_runtime` module which has been annotated using the `subxt` proc macro and is based off the above metadata.
|
||||
|
||||
The reason for doing this is that our integration tests (which also spin up a Substrate node) can then use the generated `subxt` types from the exact node being tested against, so that we don't have to worry about metadata getting out of sync with the binary under test.
|
||||
@@ -0,0 +1,171 @@
|
||||
// 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 std::{
|
||||
env,
|
||||
fs,
|
||||
net::TcpListener,
|
||||
ops::{
|
||||
Deref,
|
||||
DerefMut,
|
||||
},
|
||||
path::Path,
|
||||
process::Command,
|
||||
thread,
|
||||
time,
|
||||
};
|
||||
use subxt::rpc::{
|
||||
self,
|
||||
ClientT,
|
||||
};
|
||||
|
||||
static SUBSTRATE_BIN_ENV_VAR: &str = "SUBSTRATE_NODE_PATH";
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
run().await;
|
||||
}
|
||||
|
||||
async fn run() {
|
||||
// Select substrate binary to run based on env var.
|
||||
let substrate_bin =
|
||||
env::var(SUBSTRATE_BIN_ENV_VAR).unwrap_or_else(|_| "substrate".to_owned());
|
||||
|
||||
// Run binary.
|
||||
let port = next_open_port().expect("Cannot spawn substrate: no available ports");
|
||||
let cmd = Command::new(&substrate_bin)
|
||||
.arg("--dev")
|
||||
.arg("--tmp")
|
||||
.arg(format!("--ws-port={}", port))
|
||||
.spawn();
|
||||
let mut cmd = match cmd {
|
||||
Ok(cmd) => KillOnDrop(cmd),
|
||||
Err(ref e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||
panic!("A substrate binary should be installed on your path for testing purposes. \
|
||||
See https://github.com/paritytech/subxt/tree/master#integration-testing")
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("Cannot spawn substrate command '{}': {}", substrate_bin, e)
|
||||
}
|
||||
};
|
||||
|
||||
// Download metadata from binary; retry until successful, or a limit is hit.
|
||||
let metadata_bytes: sp_core::Bytes = {
|
||||
const MAX_RETRIES: usize = 6;
|
||||
let mut retries = 0;
|
||||
|
||||
loop {
|
||||
if retries >= MAX_RETRIES {
|
||||
panic!("Cannot connect to substrate node after {} retries", retries);
|
||||
}
|
||||
|
||||
// It might take a while for substrate node that spin up the RPC server.
|
||||
// Thus, the connection might get rejected a few times.
|
||||
let res = match rpc::ws_client(&format!("ws://localhost:{}", port)).await {
|
||||
Ok(c) => c.request("state_getMetadata", None).await,
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
|
||||
match res {
|
||||
Ok(res) => {
|
||||
let _ = cmd.kill();
|
||||
break res
|
||||
}
|
||||
_ => {
|
||||
thread::sleep(time::Duration::from_secs(1 << retries));
|
||||
retries += 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Save metadata to a file:
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let metadata_path = Path::new(&out_dir).join("metadata.scale");
|
||||
fs::write(&metadata_path, &metadata_bytes.0).expect("Couldn't write metadata output");
|
||||
|
||||
// Write out our expression to generate the runtime API to a file. Ideally, we'd just write this code
|
||||
// in lib.rs, but we must pass a string literal (and not `concat!(..)`) as an arg to `runtime_metadata_path`,
|
||||
// and so we need to spit it out here and include it verbatim instead.
|
||||
let runtime_api_contents = format!(
|
||||
r#"
|
||||
#[subxt::subxt(
|
||||
runtime_metadata_path = "{}",
|
||||
derive_for_all_types = "Eq, PartialEq"
|
||||
)]
|
||||
pub mod node_runtime {{
|
||||
#[subxt(substitute_type = "sp_arithmetic::per_things::Perbill")]
|
||||
use sp_runtime::Perbill;
|
||||
}}
|
||||
"#,
|
||||
metadata_path
|
||||
.to_str()
|
||||
.expect("Path to metadata should be stringifiable")
|
||||
);
|
||||
let runtime_path = Path::new(&out_dir).join("runtime.rs");
|
||||
fs::write(&runtime_path, runtime_api_contents)
|
||||
.expect("Couldn't write runtime rust output");
|
||||
|
||||
let substrate_path =
|
||||
which::which(substrate_bin).expect("Cannot resolve path to substrate binary");
|
||||
|
||||
// Re-build if the substrate binary we're pointed to changes (mtime):
|
||||
println!(
|
||||
"cargo:rerun-if-changed={}",
|
||||
substrate_path.to_string_lossy()
|
||||
);
|
||||
// Re-build if we point to a different substrate binary:
|
||||
println!("cargo:rerun-if-env-changed={}", SUBSTRATE_BIN_ENV_VAR);
|
||||
// Re-build if this file changes:
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
}
|
||||
|
||||
/// Returns the next open port, or None if no port found.
|
||||
fn next_open_port() -> Option<u16> {
|
||||
match TcpListener::bind(("127.0.0.1", 0)) {
|
||||
Ok(listener) => {
|
||||
if let Ok(address) = listener.local_addr() {
|
||||
Some(address.port())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// If the substrate process isn't explicitly killed on drop,
|
||||
/// it seems that panics that occur while the command is running
|
||||
/// will leave it running and block the build step from ever finishing.
|
||||
/// Wrapping it in this prevents this from happening.
|
||||
struct KillOnDrop(std::process::Child);
|
||||
|
||||
impl Deref for KillOnDrop {
|
||||
type Target = std::process::Child;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl DerefMut for KillOnDrop {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
impl Drop for KillOnDrop {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.0.kill();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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/>.
|
||||
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
/// The SCALE encoded metadata obtained from a local run of a substrate node.
|
||||
pub static METADATA: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/metadata.scale"));
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/runtime.rs"));
|
||||
Reference in New Issue
Block a user