Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
290 changes: 152 additions & 138 deletions crates/pop-cli/tests/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ impl Drop for TestChildProcess {

const RPC_CONNECT_TIMEOUT_SECS: u64 = 5;
const RPC_REQUEST_TIMEOUT_SECS: u64 = 5;
const COMMAND_TIMEOUT_SECS: u64 = 120;
const BLOCK_PRODUCTION_TIMEOUT_SECS: u64 = 60;
const COMMAND_WAIT_TIMEOUT_SECS: u64 = 600;
const CALL_COMMAND_TIMEOUT_SECS: u64 = 120;

async fn wait_for_command_success(
command: &mut tokio::process::Command,
Expand All @@ -64,6 +64,24 @@ async fn wait_for_command_success(
Ok(())
}

async fn terminate_child_process(child: &mut Child) -> Result<()> {
if child.try_wait()?.is_some() {
return Ok(());
}

#[cfg(unix)]
if let Some(pid) = child.id() {
let _ = std::process::Command::new("kill").args(["-INT", &pid.to_string()]).status();
if let Ok(Ok(_)) = tokio::time::timeout(Duration::from_secs(20), child.wait()).await {
return Ok(());
}
}

let _ = child.kill().await;
let _ = child.wait().await;
Ok(())
}

async fn rpc_request_string(url: &str, method: &str) -> Option<String> {
let client = tokio::time::timeout(
Duration::from_secs(RPC_CONNECT_TIMEOUT_SECS),
Expand Down Expand Up @@ -122,46 +140,6 @@ async fn wait_for_rpc_ready(
}

/// Wait until finalized head changes, proving blocks/finality are progressing.
async fn wait_for_block_production(
url: &str,
timeout_secs: u64,
process: &mut TestChildProcess,
) -> Result<()> {
let timeout = Duration::from_secs(timeout_secs);
let start = std::time::Instant::now();
let mut last_head = None;

println!("Waiting for block production at {} (timeout: {}s)...", url, timeout_secs);

loop {
if start.elapsed() > timeout {
return Err(anyhow::anyhow!(
"Finalized head did not advance at {} within {:?}",
url,
timeout
));
}

if let Some(status) = process.0.try_wait()? {
return Err(anyhow::anyhow!(
"Network process exited before block production was observed: {status}"
));
}

if let Some(head) = rpc_request_string(url, "chain_getFinalizedHead").await {
if let Some(previous) = &last_head &&
previous != &head
{
println!("✓ Finalized head advanced (took {:?})", start.elapsed());
return Ok(());
}
last_head = Some(head);
}

tokio::time::sleep(Duration::from_secs(2)).await;
}
}

fn write_network_config(
network_toml_path: &Path,
chain_spec_path: &Path,
Expand Down Expand Up @@ -224,19 +202,12 @@ async fn spawn_network_with_retry(

match wait_for_rpc_ready(&localhost_url, timeout, &mut process).await {
Ok(_) => {
wait_for_block_production(
&localhost_url,
BLOCK_PRODUCTION_TIMEOUT_SECS,
&mut process,
)
.await?;
println!("✓ Network started successfully on attempt {}", attempt);
return Ok((process, localhost_url));
},
Err(e) => {
println!("✗ Attempt {} failed: {}", attempt, e);
let _ = process.0.kill().await;
let _ = process.0.wait().await;
let _ = terminate_child_process(&mut process.0).await;

if attempt < max_retries {
println!("Waiting 5s before retry...");
Expand Down Expand Up @@ -277,7 +248,12 @@ async fn generate_all_the_templates() -> Result<()> {
"--verify",
],
);
assert!(command.spawn()?.wait().await?.success());
wait_for_command_success(
&mut command,
COMMAND_WAIT_TIMEOUT_SECS,
"pop new chain <template>",
)
.await?;
assert!(temp_dir.join(parachain_name).exists());
}
Ok(())
Expand Down Expand Up @@ -310,7 +286,12 @@ async fn parachain_lifecycle() -> Result<()> {
"npm",
],
);
assert!(command.spawn()?.wait().await?.success());
wait_for_command_success(
&mut command,
COMMAND_WAIT_TIMEOUT_SECS,
"pop new chain test_parachain",
)
.await?;
assert!(working_dir.exists());
assert!(working_dir.join("frontend").exists());
}
Expand Down Expand Up @@ -354,7 +335,12 @@ async fn parachain_lifecycle() -> Result<()> {
"--skip-build",
],
);
assert!(command.spawn()?.wait().await?.success());
wait_for_command_success(
&mut command,
COMMAND_WAIT_TIMEOUT_SECS,
"pop build spec --skip-build",
)
.await?;

// Assert build files have been generated
assert!(working_dir.join("target").exists());
Expand Down Expand Up @@ -391,103 +377,126 @@ async fn parachain_lifecycle() -> Result<()> {
.await?;

println!("Network ready, running chain calls...");
let call_result: Result<()> = async {
// `pop call chain --pallet System --function remark --args "0x11" --url
// ws://127.0.0.1:random_port --suri //Alice --skip-confirm`
let mut command = pop(
&working_dir,
[
"call",
"chain",
"--pallet",
"System",
"--function",
"remark",
"--args",
"0x11",
"--url",
&localhost_url,
"--suri",
"//Alice",
"--skip-confirm",
],
);
wait_for_command_success(&mut command, CALL_COMMAND_TIMEOUT_SECS, "pop call chain remark")
.await?;

// `pop call chain --pallet System --function remark --args "0x11" --url
// ws://127.0.0.1:random_port --suri //Alice --skip-confirm`
let mut command = pop(
&working_dir,
[
"call",
"chain",
"--pallet",
"System",
"--function",
"remark",
"--args",
"0x11",
"--url",
&localhost_url,
"--suri",
"//Alice",
"--skip-confirm",
],
);
wait_for_command_success(&mut command, COMMAND_TIMEOUT_SECS, "pop call chain remark").await?;

// `pop call chain --pallet System --function Account --args
// "15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5" --url ws://127.0.0.1:random_port
// --skip-confirm`
let mut command = pop(
&working_dir,
[
"call",
"chain",
"--pallet",
"System",
"--function",
"Account",
"--args",
"15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5",
"--url",
&localhost_url,
"--skip-confirm",
],
);
wait_for_command_success(&mut command, COMMAND_TIMEOUT_SECS, "pop call chain account").await?;
// `pop call chain --pallet System --function Account --args
// "15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5" --url ws://127.0.0.1:random_port
// --skip-confirm`
let mut command = pop(
&working_dir,
[
"call",
"chain",
"--pallet",
"System",
"--function",
"Account",
"--args",
"15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5",
"--url",
&localhost_url,
"--skip-confirm",
],
);
wait_for_command_success(&mut command, CALL_COMMAND_TIMEOUT_SECS, "pop call chain account")
.await?;

// `pop call chain --pallet System --function Account --args
// "15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5" --url ws://127.0.0.1:random_port`
let mut command = pop(
&working_dir,
[
"call",
"chain",
"--pallet",
"System",
"--function",
"Ss58Prefix",
"--url",
&localhost_url,
"--skip-confirm",
],
);
wait_for_command_success(&mut command, COMMAND_TIMEOUT_SECS, "pop call chain ss58-prefix")
// `pop call chain --pallet System --function Account --args
// "15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5" --url ws://127.0.0.1:random_port`
let mut command = pop(
&working_dir,
[
"call",
"chain",
"--pallet",
"System",
"--function",
"Ss58Prefix",
"--url",
&localhost_url,
"--skip-confirm",
],
);
wait_for_command_success(
&mut command,
CALL_COMMAND_TIMEOUT_SECS,
"pop call chain ss58-prefix",
)
.await?;

// pop call chain --call 0x00000411 --url ws://127.0.0.1:random_port --suri //Alice
// --skip-confirm
let mut command = pop(
&working_dir,
[
"call",
"chain",
"--call",
"0x00000411",
"--url",
&localhost_url,
"--suri",
"//Alice",
"--skip-confirm",
],
);
wait_for_command_success(&mut command, COMMAND_TIMEOUT_SECS, "pop call chain call-data")
// pop call chain --call 0x00000411 --url ws://127.0.0.1:random_port --suri //Alice
// --skip-confirm
let mut command = pop(
&working_dir,
[
"call",
"chain",
"--call",
"0x00000411",
"--url",
&localhost_url,
"--suri",
"//Alice",
"--skip-confirm",
],
);
wait_for_command_success(
&mut command,
CALL_COMMAND_TIMEOUT_SECS,
"pop call chain call-data",
)
.await?;

assert!(up.0.try_wait()?.is_none(), "the process should still be running");
// Stop the process
up.0.kill().await?;
up.0.wait().await?;
assert!(up.0.try_wait()?.is_none(), "the process should still be running");
Ok(())
}
.await;

terminate_child_process(&mut up.0).await?;
call_result?;

Ok(())
}

async fn test_benchmarking(working_dir: &Path) -> Result<()> {
// pop bench block --from 0 --to 1 --profile=release
let mut command = pop(working_dir, ["bench", "block", "-y", "--from", "0", "--to", "1"]);
assert!(command.spawn()?.wait().await?.success());
wait_for_command_success(
&mut command,
COMMAND_WAIT_TIMEOUT_SECS,
"pop bench block --from 0 --to 1",
)
.await?;
// pop bench machine --allow-fail --profile=release
command = pop(working_dir, ["bench", "machine", "-y", "--allow-fail"]);
assert!(command.spawn()?.wait().await?.success());
wait_for_command_success(
&mut command,
COMMAND_WAIT_TIMEOUT_SECS,
"pop bench machine --allow-fail",
)
.await?;
// pop bench overhead --runtime={runtime_path} --genesis-builder=runtime
// --genesis-builder-preset=development --weight-path={output_path} --profile=release --warmup=1
// --repeat=1 -y
Expand All @@ -510,7 +519,7 @@ async fn test_benchmarking(working_dir: &Path) -> Result<()> {
"-y",
],
);
assert!(command.spawn()?.wait().await?.success());
wait_for_command_success(&mut command, COMMAND_WAIT_TIMEOUT_SECS, "pop bench overhead").await?;

// pop bench pallet --runtime={runtime_path} --genesis-builder=runtime
// --pallets pallet_timestamp,pallet_system --extrinsic set,remark --output={output_path} -y
Expand All @@ -533,7 +542,7 @@ async fn test_benchmarking(working_dir: &Path) -> Result<()> {
"-y",
],
);
assert!(command.spawn()?.wait().await?.success());
wait_for_command_success(&mut command, COMMAND_WAIT_TIMEOUT_SECS, "pop bench pallet").await?;
// Parse weights file.
assert!(output_path.join("weights.rs").exists());
let content = fs::read_to_string(output_path.join("weights.rs"))?;
Expand Down Expand Up @@ -572,7 +581,12 @@ async fn test_benchmarking(working_dir: &Path) -> Result<()> {
"-y",
],
);
assert!(command.spawn()?.wait().await?.success());
wait_for_command_success(
&mut command,
COMMAND_WAIT_TIMEOUT_SECS,
"pop bench pallet --bench-file",
)
.await?;
Ok(())
}

Expand Down
Loading