Skip to content

Commit

Permalink
Simultaenously build Docker images used in tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kayabaNerve committed Nov 27, 2023
1 parent b79cf8a commit eee3918
Show file tree
Hide file tree
Showing 23 changed files with 640 additions and 528 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions processor/src/tests/literal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ mod bitcoin {
check::<IsTrue<{ Bitcoin::DUST >= bitcoin_serai::wallet::DUST }>>();
}

fn spawn_bitcoin() -> DockerTest {
serai_docker_tests::build("bitcoin".to_string());
async fn spawn_bitcoin() -> DockerTest {
serai_docker_tests::build("bitcoin".to_string()).await;

let composition = TestBodySpecification::with_image(
Image::with_repository("serai-dev-bitcoin").pull_policy(PullPolicy::Never),
Expand Down Expand Up @@ -73,8 +73,8 @@ mod monero {
use super::*;
use crate::networks::{Network, Monero};

fn spawn_monero() -> DockerTest {
serai_docker_tests::build("monero".to_string());
async fn spawn_monero() -> DockerTest {
serai_docker_tests::build("monero".to_string()).await;

let composition = TestBodySpecification::with_image(
Image::with_repository("serai-dev-monero").pull_policy(PullPolicy::Never),
Expand Down
70 changes: 40 additions & 30 deletions processor/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,49 +44,59 @@ macro_rules! test_network {
test_key_gen::<$N>().await;
}

#[test]
fn $scanner() {
#[tokio::test]
async fn $scanner() {
*INIT_LOGGER;
let docker = $docker();
docker.run(|ops| async move {
test_scanner($network(&ops).await).await;
});
let docker = $docker().await;
docker
.run_async(|ops| async move {
test_scanner($network(&ops).await).await;
})
.await;
}

#[test]
fn $signer() {
#[tokio::test]
async fn $signer() {
*INIT_LOGGER;
let docker = $docker();
docker.run(|ops| async move {
test_signer($network(&ops).await).await;
});
let docker = $docker().await;
docker
.run_async(|ops| async move {
test_signer($network(&ops).await).await;
})
.await;
}

#[test]
fn $wallet() {
#[tokio::test]
async fn $wallet() {
*INIT_LOGGER;
let docker = $docker();
docker.run(|ops| async move {
test_wallet($network(&ops).await).await;
});
let docker = $docker().await;
docker
.run_async(|ops| async move {
test_wallet($network(&ops).await).await;
})
.await;
}

#[test]
fn $addresses() {
#[tokio::test]
async fn $addresses() {
*INIT_LOGGER;
let docker = $docker();
docker.run(|ops| async move {
test_addresses($network(&ops).await).await;
});
let docker = $docker().await;
docker
.run_async(|ops| async move {
test_addresses($network(&ops).await).await;
})
.await;
}

#[test]
fn $no_deadlock_in_multisig_completed() {
#[tokio::test]
async fn $no_deadlock_in_multisig_completed() {
*INIT_LOGGER;
let docker = $docker();
docker.run(|ops| async move {
test_no_deadlock_in_multisig_completed($network(&ops).await).await;
});
let docker = $docker().await;
docker
.run_async(|ops| async move {
test_no_deadlock_in_multisig_completed($network(&ops).await).await;
})
.await;
}
};
}
Expand Down
2 changes: 1 addition & 1 deletion substrate/client/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ macro_rules! serai_test {
TestBodySpecification, DockerTest,
};

serai_docker_tests::build("serai".to_string());
serai_docker_tests::build("serai".to_string()).await;

let handle = concat!("serai_client-serai_node-", stringify!($name));

Expand Down
35 changes: 25 additions & 10 deletions tests/coordinator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,18 @@ mod tests;

static UNIQUE_ID: OnceLock<Mutex<u16>> = OnceLock::new();

pub fn coordinator_instance(
pub fn coordinator_docker_name() -> String {
"serai-dev-coordinator".to_string()
}

pub async fn coordinator_instance(
name: &str,
message_queue_key: <Ristretto as Ciphersuite>::F,
) -> TestBodySpecification {
serai_docker_tests::build("coordinator".to_string());
serai_docker_tests::build("coordinator".to_string()).await;

TestBodySpecification::with_image(
Image::with_repository("serai-dev-coordinator").pull_policy(PullPolicy::Never),
Image::with_repository(coordinator_docker_name()).pull_policy(PullPolicy::Never),
)
.replace_env(
[
Expand All @@ -63,11 +67,15 @@ pub fn coordinator_instance(
)
}

pub fn serai_composition(name: &str) -> TestBodySpecification {
serai_docker_tests::build("serai".to_string());
pub fn serai_docker_name() -> String {
"serai-dev-serai".to_string()
}

pub async fn serai_composition(name: &str) -> TestBodySpecification {
serai_docker_tests::build("serai".to_string()).await;

TestBodySpecification::with_image(
Image::with_repository("serai-dev-serai").pull_policy(PullPolicy::Never),
Image::with_repository(serai_docker_name()).pull_policy(PullPolicy::Never),
)
.replace_cmd(vec![
"serai-node".to_string(),
Expand All @@ -82,15 +90,22 @@ pub fn serai_composition(name: &str) -> TestBodySpecification {
}

pub type Handles = (String, String, String);
pub fn coordinator_stack(
pub async fn coordinator_stack(
name: &str,
) -> (Handles, <Ristretto as Ciphersuite>::F, Vec<TestBodySpecification>) {
let serai_composition = serai_composition(name);
serai_docker_tests::build_batch(vec![
serai_docker_name(),
serai_message_queue_tests::docker_name(),
coordinator_docker_name(),
])
.await;

let serai_composition = serai_composition(name).await;

let (coord_key, message_queue_keys, message_queue_composition) =
serai_message_queue_tests::instance();
serai_message_queue_tests::instance().await;

let coordinator_composition = coordinator_instance(name, coord_key);
let coordinator_composition = coordinator_instance(name, coord_key).await;

// Give every item in this stack a unique ID
// Uses a Mutex as we can't generate a 8-byte random ID without hitting hostname length limits
Expand Down
2 changes: 1 addition & 1 deletion tests/coordinator/src/tests/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ pub async fn batch(
#[tokio::test]
async fn batch_test() {
let _one_at_a_time = ONE_AT_A_TIME.get_or_init(|| Mutex::new(())).lock();
let (processors, test) = new_test();
let (processors, test) = new_test().await;

test
.run_async(|ops| async move {
Expand Down
2 changes: 1 addition & 1 deletion tests/coordinator/src/tests/key_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ pub async fn key_gen<C: Ciphersuite>(
#[tokio::test]
async fn key_gen_test() {
let _one_at_a_time = ONE_AT_A_TIME.get_or_init(|| Mutex::new(())).lock();
let (processors, test) = new_test();
let (processors, test) = new_test().await;

test
.run_async(|ops| async move {
Expand Down
5 changes: 3 additions & 2 deletions tests/coordinator/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub(crate) const THRESHOLD: usize = ((COORDINATORS * 2) / 3) + 1;

pub(crate) static ONE_AT_A_TIME: OnceLock<Mutex<()>> = OnceLock::new();

pub(crate) fn new_test() -> (Vec<(Handles, <Ristretto as Ciphersuite>::F)>, DockerTest) {
pub(crate) async fn new_test() -> (Vec<(Handles, <Ristretto as Ciphersuite>::F)>, DockerTest) {
let mut coordinators = vec![];
let mut test = DockerTest::new().with_network(dockertest::Network::Isolated);
for i in 0 .. COORDINATORS {
Expand All @@ -33,7 +33,8 @@ pub(crate) fn new_test() -> (Vec<(Handles, <Ristretto as Ciphersuite>::F)>, Dock
4 => "Eve",
5 => "Ferdie",
_ => panic!("needed a 7th name for a serai node"),
});
})
.await;
coordinators.push((handles, coord_key));
for composition in compositions {
test.provide_container(composition);
Expand Down
2 changes: 1 addition & 1 deletion tests/coordinator/src/tests/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ pub async fn sign<C: Ciphersuite>(
#[tokio::test]
async fn sign_test() {
let _one_at_a_time = ONE_AT_A_TIME.get_or_init(|| Mutex::new(())).lock();
let (processors, test) = new_test();
let (processors, test) = new_test().await;

test
.run_async(|ops| async move {
Expand Down
2 changes: 2 additions & 0 deletions tests/docker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
chrono = "0.4"

tokio = { version = "1", default-features = false, features = ["sync", "rt"] }
39 changes: 30 additions & 9 deletions tests/docker/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
use std::{
sync::{Mutex, OnceLock},
sync::{OnceLock, Arc},
collections::{HashSet, HashMap},
time::SystemTime,
path::PathBuf,
fs, env,
process::Command,
};

static BUILT: OnceLock<Mutex<HashMap<String, bool>>> = OnceLock::new();
pub fn build(name: String) {
use tokio::{sync::Mutex, process::Command};

static BUILT: OnceLock<Mutex<HashMap<String, Arc<Mutex<bool>>>>> = OnceLock::new();
pub async fn build(name: String) {
let built = BUILT.get_or_init(|| Mutex::new(HashMap::new()));
// Only one call to build will acquire this lock
let mut built_lock = built.lock().unwrap();
if built_lock.contains_key(&name) {
// If it was built, return
let mut built_lock = built.lock().await;
if !built_lock.contains_key(&name) {
built_lock.insert(name.clone(), Arc::new(Mutex::new(false)));
}
let this_lock = built_lock[&name].clone();
drop(built_lock);

let mut built_lock = this_lock.lock().await;
// Already built
if *built_lock {
return;
}

Expand All @@ -38,6 +46,7 @@ pub fn build(name: String) {
.arg("{{ .Metadata.LastTagTime }}")
.arg(format!("serai-dev-{name}"))
.output()
.await
{
let last_tag_time_buf = String::from_utf8(res.stdout).expect("docker had non-utf8 output");
let last_tag_time = last_tag_time_buf.trim();
Expand Down Expand Up @@ -133,7 +142,7 @@ pub fn build(name: String) {
if let Some(last_modified) = last_modified {
if last_modified < created_time {
println!("{} was built after the most recent source code edits, assuming built.", name);
built_lock.insert(name, true);
*built_lock = true;
return;
}
}
Expand All @@ -151,6 +160,7 @@ pub fn build(name: String) {
.spawn()
.unwrap()
.wait()
.await
.unwrap()
.success()
{
Expand Down Expand Up @@ -194,6 +204,7 @@ pub fn build(name: String) {
.arg("--all")
.arg("--force")
.output()
.await
.unwrap()
.status
.success()
Expand All @@ -203,5 +214,15 @@ pub fn build(name: String) {
}

// Set built
built_lock.insert(name, true);
*built_lock = true;
}

pub async fn build_batch(names: Vec<String>) {
let mut handles = vec![];
for name in names.into_iter().collect::<HashSet<_>>() {
handles.push(tokio::spawn(build(name)));
}
for handle in handles {
handle.await.unwrap();
}
}
1 change: 1 addition & 0 deletions tests/full-stack/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ serai-client = { path = "../../substrate/client", features = ["serai"] }
tokio = { version = "1", features = ["time"] }

dockertest = "0.4"
serai-docker-tests = { path = "../docker" }
serai-message-queue-tests = { path = "../message-queue" }
serai-processor-tests = { path = "../processor" }
serai-coordinator-tests = { path = "../coordinator" }
29 changes: 20 additions & 9 deletions tests/full-stack/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,30 @@ pub struct Handles {
serai: String,
}

pub fn full_stack(name: &str) -> (Handles, Vec<TestBodySpecification>) {
let (coord_key, message_queue_keys, message_queue_composition) = message_queue_instance();

let (bitcoin_composition, bitcoin_port) = network_instance(NetworkId::Bitcoin);
pub async fn full_stack(name: &str) -> (Handles, Vec<TestBodySpecification>) {
let mut docker_names = serai_processor_tests::docker_names(NetworkId::Bitcoin);
docker_names.append(&mut serai_processor_tests::docker_names(NetworkId::Monero));
docker_names.extend([
serai_message_queue_tests::docker_name(),
serai_coordinator_tests::serai_docker_name(),
serai_coordinator_tests::coordinator_docker_name(),
]);
serai_docker_tests::build_batch(docker_names).await;

let (coord_key, message_queue_keys, message_queue_composition) = message_queue_instance().await;

let (bitcoin_composition, bitcoin_port) = network_instance(NetworkId::Bitcoin).await;
let bitcoin_processor_composition =
processor_instance(NetworkId::Bitcoin, bitcoin_port, message_queue_keys[&NetworkId::Bitcoin]);
processor_instance(NetworkId::Bitcoin, bitcoin_port, message_queue_keys[&NetworkId::Bitcoin])
.await;

let (monero_composition, monero_port) = network_instance(NetworkId::Monero);
let (monero_composition, monero_port) = network_instance(NetworkId::Monero).await;
let monero_processor_composition =
processor_instance(NetworkId::Monero, monero_port, message_queue_keys[&NetworkId::Monero]);
processor_instance(NetworkId::Monero, monero_port, message_queue_keys[&NetworkId::Monero])
.await;

let coordinator_composition = coordinator_instance(name, coord_key);
let serai_composition = serai_composition(name);
let coordinator_composition = coordinator_instance(name, coord_key).await;
let serai_composition = serai_composition(name).await;

// Give every item in this stack a unique ID
// Uses a Mutex as we can't generate a 8-byte random ID without hitting hostname length limits
Expand Down
2 changes: 1 addition & 1 deletion tests/full-stack/src/tests/mint_and_burn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::tests::*;
#[tokio::test]
async fn mint_and_burn_test() {
let _one_at_a_time = ONE_AT_A_TIME.get_or_init(|| Mutex::new(())).lock();
let (handles, test) = new_test();
let (handles, test) = new_test().await;

test
.run_async(|ops| async move {
Expand Down
Loading

0 comments on commit eee3918

Please sign in to comment.