-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(test): implementing a new test framework for core lightning
Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
- Loading branch information
1 parent
5460467
commit d7f5048
Showing
5 changed files
with
286 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ members = [ | |
"plugin", | ||
"plugin_macros", | ||
"conf", | ||
"testing", | ||
] | ||
resolver = "2" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
name = "testing" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
clightningrpc = "0.3.0-beta.6" | ||
bitcoincore-rpc = "0.17.0" | ||
log = "0.4.19" | ||
tempfile = "3.6.0" | ||
port-selector = "0.1.6" | ||
anyhow = "1.0.71" | ||
tokio = { version = "1.22.0", features = ["process", "time", "fs"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
//! Bitcoin Testing framework. | ||
use bitcoincore_rpc::{Auth, Client, RpcApi}; | ||
use port::Port; | ||
use port_selector as port; | ||
use tempfile::TempDir; | ||
|
||
pub mod macros { | ||
#[macro_export] | ||
macro_rules! bitcoind { | ||
($dir:expr, $port:expr, $opt_args:expr) => { | ||
async { | ||
use std::process::Stdio; | ||
|
||
use log; | ||
use tokio::process::Command; | ||
|
||
let opt_args = format!($opt_args); | ||
let args = opt_args.trim(); | ||
let args_tok: Vec<&str> = args.split(" ").collect(); | ||
log::debug!("additional args: {:?}", args_tok); | ||
let mut command = Command::new("bitcoind"); | ||
command | ||
.args(&args_tok) | ||
.arg(format!("-port={}", $port + 1)) | ||
.arg(format!("-rpcport={}", $port)) | ||
.arg(format!("-datadir={}", $dir.path().to_str().unwrap())) | ||
.stdout(Stdio::null()) | ||
.spawn() | ||
} | ||
.await | ||
}; | ||
($dir:expr, $port:expr) => { | ||
$crate::bitcoind!($dir, $port, "") | ||
}; | ||
} | ||
|
||
pub use bitcoind; | ||
} | ||
|
||
pub struct BtcNode { | ||
inner: Client, | ||
pub user: String, | ||
pub pass: String, | ||
pub port: Port, | ||
root_path: TempDir, | ||
process: Vec<tokio::process::Child>, | ||
} | ||
|
||
impl Drop for BtcNode { | ||
fn drop(&mut self) { | ||
for process in self.process.iter() { | ||
let Some(child) = process.id() else { | ||
continue; | ||
}; | ||
let Ok(mut kill) = std::process::Command::new("kill") | ||
.args(["-s", "SIGKILL", &child.to_string()]) | ||
.spawn() else { | ||
continue; | ||
}; | ||
let _ = kill.wait(); | ||
} | ||
|
||
let result = std::fs::remove_dir_all(self.root_path.path()); | ||
log::debug!(target: "btc", "clean up function {:?}", result); | ||
} | ||
} | ||
|
||
impl BtcNode { | ||
pub async fn tmp() -> anyhow::Result<Self> { | ||
let dir = tempfile::tempdir()?; | ||
let user = "crab".to_owned(); | ||
let pass = "crab".to_owned(); | ||
let port = port::random_free_port().unwrap(); | ||
let process = macros::bitcoind!( | ||
dir, | ||
port, | ||
"-server -regtest -rpcuser={user} -rpcpassword={pass}" | ||
)?; | ||
let rpc = Client::new( | ||
&format!("http://localhost:{port}"), | ||
Auth::UserPass(user.clone(), pass.clone()), | ||
)?; | ||
let bg_process = vec![process]; | ||
Ok(Self { | ||
inner: rpc, | ||
root_path: dir, | ||
user, | ||
pass, | ||
port, | ||
process: bg_process, | ||
}) | ||
} | ||
|
||
pub fn rpc(&self) -> &Client { | ||
&self.inner | ||
} | ||
|
||
pub async fn stop(&mut self) -> anyhow::Result<()> { | ||
log::info!("stop bitcoin node"); | ||
self.inner.stop()?; | ||
for process in self.process.iter_mut() { | ||
process.kill().await?; | ||
let _ = process.wait().await?; | ||
log::debug!("process killed"); | ||
} | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
//! Integration testing library for core lightning | ||
use port_selector as port; | ||
use tempfile::TempDir; | ||
|
||
use clightningrpc::LightningRPC; | ||
|
||
use crate::btc::BtcNode; | ||
use crate::prelude::*; | ||
|
||
pub mod macros { | ||
#[macro_export] | ||
macro_rules! lightningd { | ||
($dir:expr, $port:expr, $($opt_args:tt)*) => { | ||
async { | ||
use std::process::Stdio; | ||
|
||
use tokio::process::Command; | ||
|
||
let opt_args = format!($($opt_args)*); | ||
let args = opt_args.trim(); | ||
let args_tok: Vec<&str> = args.split(" ").collect(); | ||
|
||
let path = format!("{}/.lightning", $dir.path().to_str().unwrap()); | ||
log::info!("core lightning home {path}"); | ||
check_dir_or_make_if_missing(path.clone()).await.unwrap(); | ||
let mut command = Command::new("lightningd"); | ||
command | ||
.args(&args_tok) | ||
.arg(format!("--addr=127.0.0.1:{}", $port)) | ||
.arg(format!("--bind-addr=127.0.0.1:{}", $port)) | ||
.arg(format!("--lightning-dir={path}")) | ||
.arg("--dev-fast-gossip") | ||
.arg("--funding-confirms=1") | ||
.stdout(Stdio::null()) | ||
.spawn() | ||
}.await | ||
}; | ||
($dir:expr, $port:expr) => { | ||
$crate::lightningd!($dir, $port, "") | ||
}; | ||
} | ||
|
||
pub use lightningd; | ||
} | ||
|
||
pub struct Node { | ||
inner: LightningRPC, | ||
root_path: TempDir, | ||
bitcoin: BtcNode, | ||
process: Vec<tokio::process::Child>, | ||
} | ||
|
||
impl Drop for Node { | ||
fn drop(&mut self) { | ||
for process in self.process.iter() { | ||
let Some(child) = process.id() else { | ||
continue; | ||
}; | ||
let Ok(mut kill) = std::process::Command::new("kill") | ||
.args(["-s", "SIGKILL", &child.to_string()]) | ||
.spawn() else { | ||
continue; | ||
}; | ||
let _ = kill.wait(); | ||
} | ||
|
||
let result = std::fs::remove_dir_all(self.root_path.path()); | ||
log::debug!(target: "cln", "clean up function {:?}", result); | ||
} | ||
} | ||
|
||
impl Node { | ||
pub async fn tmp() -> anyhow::Result<Self> { | ||
let btc = BtcNode::tmp().await?; | ||
|
||
let dir = tempfile::tempdir()?; | ||
|
||
let process = macros::lightningd!( | ||
dir, | ||
port::random_free_port().unwrap(), | ||
"--network=regtest --bitcoin-rpcuser={} --bitcoin-rpcpassword={} --bitcoin-rpcport={}", | ||
btc.user, | ||
btc.pass, | ||
btc.port, | ||
)?; | ||
|
||
let rpc = LightningRPC::new(dir.path().join(".lightning/regtest").join("lightning-rpc")); | ||
|
||
wait_for!(async { rpc.getinfo() }); | ||
|
||
Ok(Self { | ||
inner: rpc, | ||
root_path: dir, | ||
bitcoin: btc, | ||
process: vec![process], | ||
}) | ||
} | ||
|
||
pub fn rpc(&self) -> &LightningRPC { | ||
&self.inner | ||
} | ||
|
||
pub async fn stop(&mut self) -> anyhow::Result<()> { | ||
log::info!("stop lightning node"); | ||
self.inner.stop()?; | ||
for process in self.process.iter_mut() { | ||
process.kill().await?; | ||
let _ = process.wait().await?; | ||
log::debug!("killing process"); | ||
} | ||
self.bitcoin.stop().await?; | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
pub mod btc; | ||
pub mod cln; | ||
|
||
pub mod prelude { | ||
pub use crate::macros::*; | ||
pub use port_selector as port; | ||
pub use tempfile; | ||
|
||
pub use crate::check_dir_or_make_if_missing; | ||
} | ||
|
||
pub mod macros { | ||
#[macro_export] | ||
macro_rules! wait_for { | ||
($callback:expr, $timeout:expr) => { | ||
use log; | ||
use tokio::time::{sleep, Duration}; | ||
|
||
for wait in 0..$timeout { | ||
if let Err(err) = $callback.await { | ||
log::debug!("callback return {:?}", err); | ||
sleep(Duration::from_millis(wait)).await; | ||
continue; | ||
} | ||
log::info!("callback completed in {wait} milliseconds"); | ||
break; | ||
} | ||
}; | ||
($callback:expr) => { | ||
use crate::DEFAULT_TIMEOUT; | ||
Check warning on line 30 in testing/src/lib.rs GitHub Actions / clippy`crate` references the macro call's crate
|
||
|
||
$crate::wait_for!($callback, DEFAULT_TIMEOUT); | ||
}; | ||
} | ||
|
||
pub use wait_for; | ||
} | ||
|
||
static DEFAULT_TIMEOUT: u64 = 100; | ||
|
||
pub async fn check_dir_or_make_if_missing(path: String) -> anyhow::Result<()> { | ||
use std::path::Path; | ||
use tokio::fs::create_dir; | ||
|
||
if !Path::exists(Path::new(&path.to_owned())) { | ||
create_dir(path.clone()).await?; | ||
log::debug!("created dir {path}"); | ||
} | ||
Ok(()) | ||
} |