From 748ceeae133b38efb9d1f8435b1a0b1b8435a544 Mon Sep 17 00:00:00 2001 From: Niklas Adolfsson Date: Mon, 9 May 2022 15:56:48 +0200 Subject: [PATCH] fix: read WS address from substrate output --- bin/node/cli/tests/common.rs | 52 ++++++++++++++++--- .../tests/running_the_node_and_interrupt.rs | 17 ++++-- bin/node/cli/tests/temp_base_path_works.rs | 10 ++-- 3 files changed, 65 insertions(+), 14 deletions(-) diff --git a/bin/node/cli/tests/common.rs b/bin/node/cli/tests/common.rs index c17cabfa1d38a..9c739c2cf2d28 100644 --- a/bin/node/cli/tests/common.rs +++ b/bin/node/cli/tests/common.rs @@ -26,15 +26,14 @@ use nix::{ use node_primitives::Block; use remote_externalities::rpc_api; use std::{ + io::{BufRead, BufReader, Read}, ops::{Deref, DerefMut}, path::Path, - process::{Child, Command, ExitStatus}, + process::{self, Child, Command, ExitStatus}, time::Duration, }; use tokio::time::timeout; -static LOCALHOST_WS: &str = "ws://127.0.0.1:9944/"; - /// Wait for the given `child` the given number of `secs`. /// /// Returns the `Some(exit status)` or `None` if the process did not finish in the given time. @@ -63,8 +62,9 @@ pub fn wait_for(child: &mut Child, secs: u64) -> Result { pub async fn wait_n_finalized_blocks( n: usize, timeout_secs: u64, + url: &str, ) -> Result<(), tokio::time::error::Elapsed> { - timeout(Duration::from_secs(timeout_secs), wait_n_finalized_blocks_from(n, LOCALHOST_WS)).await + timeout(Duration::from_secs(timeout_secs), wait_n_finalized_blocks_from(n, url)).await } /// Wait for at least n blocks to be finalized from a specified node @@ -85,12 +85,23 @@ pub async fn wait_n_finalized_blocks_from(n: usize, url: &str) { /// Run the node for a while (3 blocks) pub async fn run_node_for_a_while(base_path: &Path, args: &[&str]) { - let mut cmd = Command::new(cargo_bin("substrate")); + let mut cmd = Command::new(cargo_bin("substrate")) + .stdout(process::Stdio::piped()) + .stderr(process::Stdio::piped()) + .args(args) + .arg("-d") + .arg(base_path) + .spawn() + .unwrap(); - let mut child = KillChildOnDrop(cmd.args(args).arg("-d").arg(base_path).spawn().unwrap()); + let stderr = cmd.stderr.take().unwrap(); + + let mut child = KillChildOnDrop(cmd); + + let (ws_url, _) = find_ws_url_from_output(stderr); // Let it produce some blocks. - let _ = wait_n_finalized_blocks(3, 30).await; + let _ = wait_n_finalized_blocks(3, 30, &ws_url).await; assert!(child.try_wait().unwrap().is_none(), "the process should still be running"); @@ -134,3 +145,30 @@ impl DerefMut for KillChildOnDrop { &mut self.0 } } + +/// Read the WS address from the output. +/// +/// This is hack to get the actual binded sockaddr because +/// substrate assigns a random port if the specified port was already binded. +pub fn find_ws_url_from_output(read: impl Read + Send) -> (String, String) { + let mut data = String::new(); + + let ws_url = BufReader::new(read) + .lines() + .find_map(|line| { + let line = + line.expect("failed to obtain next line from stdout for WS address discovery"); + data.push_str(&line); + + // does the line contain our port (we expect this specific output from substrate). + let sock_addr = match line.split_once("Running JSON-RPC WS server: addr=") { + None => return None, + Some((_, after)) => after.split_once(",").unwrap().0, + }; + + Some(format!("ws://{}", sock_addr)) + }) + .expect("We should get a WebSocket address"); + + (ws_url, data) +} diff --git a/bin/node/cli/tests/running_the_node_and_interrupt.rs b/bin/node/cli/tests/running_the_node_and_interrupt.rs index 4d6912e26a13a..ddbb9c3a44868 100644 --- a/bin/node/cli/tests/running_the_node_and_interrupt.rs +++ b/bin/node/cli/tests/running_the_node_and_interrupt.rs @@ -25,7 +25,7 @@ use nix::{ }, unistd::Pid, }; -use std::process::{Child, Command}; +use std::process::{self, Child, Command}; use tempfile::tempdir; pub mod common; @@ -36,6 +36,8 @@ async fn running_the_node_works_and_can_be_interrupted() { let base_path = tempdir().expect("could not create a temp dir"); let mut cmd = common::KillChildOnDrop( Command::new(cargo_bin("substrate")) + .stdout(process::Stdio::piped()) + .stderr(process::Stdio::piped()) .args(&["--dev", "-d"]) .arg(base_path.path()) .arg("--no-hardware-benchmarks") @@ -43,7 +45,11 @@ async fn running_the_node_works_and_can_be_interrupted() { .unwrap(), ); - common::wait_n_finalized_blocks(3, 60) + let stderr = cmd.stderr.take().unwrap(); + + let (ws_url, _) = common::find_ws_url_from_output(stderr); + + common::wait_n_finalized_blocks(3, 30, &ws_url) .await .expect("Blocks are produced in time"); assert!(cmd.try_wait().unwrap().is_none(), "the process should still be running"); @@ -64,6 +70,8 @@ async fn running_the_node_works_and_can_be_interrupted() { async fn running_two_nodes_with_the_same_ws_port_should_work() { fn start_node() -> Child { Command::new(cargo_bin("substrate")) + .stdout(process::Stdio::piped()) + .stderr(process::Stdio::piped()) .args(&["--dev", "--tmp", "--ws-port=45789", "--no-hardware-benchmarks"]) .spawn() .unwrap() @@ -72,7 +80,10 @@ async fn running_two_nodes_with_the_same_ws_port_should_work() { let mut first_node = common::KillChildOnDrop(start_node()); let mut second_node = common::KillChildOnDrop(start_node()); - let _ = common::wait_n_finalized_blocks(3, 30).await; + let stderr = first_node.stderr.take().unwrap(); + let (ws_url, _) = common::find_ws_url_from_output(stderr); + + common::wait_n_finalized_blocks(3, 30, &ws_url).await.unwrap(); assert!(first_node.try_wait().unwrap().is_none(), "The first node should still be running"); assert!(second_node.try_wait().unwrap().is_none(), "The second node should still be running"); diff --git a/bin/node/cli/tests/temp_base_path_works.rs b/bin/node/cli/tests/temp_base_path_works.rs index df293161e3234..98422a21f5308 100644 --- a/bin/node/cli/tests/temp_base_path_works.rs +++ b/bin/node/cli/tests/temp_base_path_works.rs @@ -43,8 +43,11 @@ async fn temp_base_path_works() { .unwrap(), ); + let mut stderr = child.stderr.take().unwrap(); + let (ws_url, mut data) = common::find_ws_url_from_output(&mut stderr); + // Let it produce some blocks. - common::wait_n_finalized_blocks(3, 30).await.unwrap(); + common::wait_n_finalized_blocks(3, 30, &ws_url).await.unwrap(); assert!(child.try_wait().unwrap().is_none(), "the process should still be running"); // Stop the process @@ -52,10 +55,9 @@ async fn temp_base_path_works() { assert!(common::wait_for(&mut child, 40).map(|x| x.success()).unwrap_or_default()); // Ensure the database has been deleted - let mut stderr = String::new(); - child.stderr.as_mut().unwrap().read_to_string(&mut stderr).unwrap(); + stderr.read_to_string(&mut data).unwrap(); let re = Regex::new(r"Database: .+ at (\S+)").unwrap(); - let db_path = PathBuf::from(re.captures(stderr.as_str()).unwrap().get(1).unwrap().as_str()); + let db_path = PathBuf::from(re.captures(data.as_str()).unwrap().get(1).unwrap().as_str()); assert!(!db_path.exists()); }