mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-25 18:47:56 +00:00
Factor substrate node runner into separate crate (#913)
* factor out node starting thing to separate crate to use in test-runtime and integration-tests * remove subxt dep on test-runtime build, and clippy tidyup * remove now-unneeded dep * Update testing/substrate-runner/Cargo.toml Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> * Update testing/integration-tests/Cargo.toml Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com> * remove unneeded port things for backward compat --------- Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
This commit is contained in:
@@ -3,28 +3,17 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use sp_keyring::AccountKeyring;
|
||||
use std::{
|
||||
ffi::{OsStr, OsString},
|
||||
io::{BufRead, BufReader, Read},
|
||||
process,
|
||||
};
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use substrate_runner::SubstrateNode;
|
||||
use subxt::{Config, OnlineClient};
|
||||
|
||||
/// Spawn a local substrate node for testing subxt.
|
||||
pub struct TestNodeProcess<R: Config> {
|
||||
proc: process::Child,
|
||||
// Keep a handle to the node; once it's dropped the node is killed.
|
||||
_proc: SubstrateNode,
|
||||
client: OnlineClient<R>,
|
||||
}
|
||||
|
||||
impl<R> Drop for TestNodeProcess<R>
|
||||
where
|
||||
R: Config,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
let _ = self.kill();
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> TestNodeProcess<R>
|
||||
where
|
||||
R: Config,
|
||||
@@ -37,17 +26,6 @@ where
|
||||
TestNodeProcessBuilder::new(program)
|
||||
}
|
||||
|
||||
/// Attempt to kill the running substrate process.
|
||||
pub fn kill(&mut self) -> Result<(), String> {
|
||||
tracing::info!("Killing node process {}", self.proc.id());
|
||||
if let Err(err) = self.proc.kill() {
|
||||
let err = format!("Error killing node process {}: {}", self.proc.id(), err);
|
||||
tracing::error!("{}", err);
|
||||
return Err(err);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the subxt client connected to the running node.
|
||||
pub fn client(&self) -> OnlineClient<R> {
|
||||
self.client.clone()
|
||||
@@ -78,78 +56,31 @@ impl TestNodeProcessBuilder {
|
||||
}
|
||||
|
||||
/// Spawn the substrate node at the given path, and wait for rpc to be initialized.
|
||||
pub async fn spawn<R>(&self) -> Result<TestNodeProcess<R>, String>
|
||||
pub async fn spawn<R>(self) -> Result<TestNodeProcess<R>, String>
|
||||
where
|
||||
R: Config,
|
||||
{
|
||||
let mut cmd = process::Command::new(&self.node_path);
|
||||
cmd.env("RUST_LOG", "info")
|
||||
.arg("--dev")
|
||||
.arg("--tmp")
|
||||
.stdout(process::Stdio::piped())
|
||||
.stderr(process::Stdio::piped())
|
||||
.arg("--port=0")
|
||||
.arg("--rpc-port=0")
|
||||
.arg("--ws-port=0");
|
||||
let mut node_builder = SubstrateNode::builder();
|
||||
|
||||
node_builder.binary_path(self.node_path);
|
||||
|
||||
if let Some(authority) = self.authority {
|
||||
let authority = format!("{authority:?}");
|
||||
let arg = format!("--{}", authority.as_str().to_lowercase());
|
||||
cmd.arg(arg);
|
||||
node_builder.arg(authority.as_str().to_lowercase());
|
||||
}
|
||||
|
||||
let mut proc = cmd.spawn().map_err(|e| {
|
||||
format!(
|
||||
"Error spawning substrate node '{}': {}",
|
||||
self.node_path.to_string_lossy(),
|
||||
e
|
||||
)
|
||||
})?;
|
||||
|
||||
// Wait for RPC port to be logged (it's logged to stderr):
|
||||
let stderr = proc.stderr.take().unwrap();
|
||||
let ws_port = find_substrate_port_from_output(stderr);
|
||||
let ws_url = format!("ws://127.0.0.1:{ws_port}");
|
||||
// Spawn the node and retrieve a URL to it:
|
||||
let proc = node_builder.spawn().map_err(|e| e.to_string())?;
|
||||
let ws_url = format!("ws://127.0.0.1:{}", proc.ws_port());
|
||||
|
||||
// Connect to the node with a subxt client:
|
||||
let client = OnlineClient::from_url(ws_url.clone()).await;
|
||||
match client {
|
||||
Ok(client) => Ok(TestNodeProcess { proc, client }),
|
||||
Err(err) => {
|
||||
let err = format!("Failed to connect to node rpc at {ws_url}: {err}");
|
||||
tracing::error!("{}", err);
|
||||
proc.kill().map_err(|e| {
|
||||
format!("Error killing substrate process '{}': {}", proc.id(), e)
|
||||
})?;
|
||||
Err(err)
|
||||
}
|
||||
Ok(client) => Ok(TestNodeProcess {
|
||||
_proc: proc,
|
||||
client,
|
||||
}),
|
||||
Err(err) => Err(format!("Failed to connect to node rpc at {ws_url}: {err}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Consume a stderr reader from a spawned substrate command and
|
||||
// locate the port number that is logged out to it.
|
||||
fn find_substrate_port_from_output(r: impl Read + Send + 'static) -> u16 {
|
||||
BufReader::new(r)
|
||||
.lines()
|
||||
.find_map(|line| {
|
||||
let line = line.expect("failed to obtain next line from stdout for port discovery");
|
||||
|
||||
// does the line contain our port (we expect this specific output from substrate).
|
||||
let line_end = line
|
||||
.rsplit_once("Listening for new connections on 127.0.0.1:")
|
||||
.or_else(|| line.rsplit_once("Running JSON-RPC WS server: addr=127.0.0.1:"))
|
||||
.map(|(_, port_str)| port_str)?;
|
||||
|
||||
// trim non-numeric chars from the end of the port part of the line.
|
||||
let port_str = line_end.trim_end_matches(|b: char| !b.is_ascii_digit());
|
||||
|
||||
// expect to have a number here (the chars after '127.0.0.1:') and parse them into a u16.
|
||||
let port_num = port_str
|
||||
.parse()
|
||||
.unwrap_or_else(|_| panic!("valid port expected for log line, got '{port_str}'"));
|
||||
|
||||
Some(port_num)
|
||||
})
|
||||
.expect("We should find a port before the reader ends")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user