extern crate byztimed;
extern crate nix;
extern crate tempfile;
#[macro_use]
extern crate lazy_static;
use byztime::Context;
use std::env;
use std::fs;
use std::io;
use std::io::Write;
use std::path;
use std::process;
use std::thread;
use std::time;
mod common;
use common::*;
const ALICE_CONFIG: &'static str = r#"{
"timedata": "@tempdir@/alice.timedata",
"secret_store": "@tempdir@/alice.store",
"bind_port": @alice_port@,
"key": "@certdir@/alice.key",
"cert": "@certdir@/alice.crt",
"authorities": "@certdir@/trent.crt",
"logging": { "@tempdir@/alice.log": "debug" },
"poll_interval": 0.5,
"peers": {
"bob": {
"host": "127.0.0.1",
"port": @bob_port@,
"cert_name": "bob.test"
},
"charlie": {
"host": "127.0.0.1",
"port": @charlie_port@,
"cert_name": "charlie.test"
},
"dave": {
"host": "127.0.0.1",
"port": @dave_port@,
"cert_name": "dave.test"
}
}
}"#;
const BOB_CONFIG: &'static str = r#"{
"timedata": "@tempdir@/bob.timedata",
"secret_store": "@tempdir@/bob.store",
"bind_port": @bob_port@,
"key": "@certdir@/bob.key",
"cert": "@certdir@/bob.crt",
"authorities": "@certdir@/trent.crt",
"logging": { "@tempdir@/bob.log": "debug" },
"poll_interval": 0.5,
"peers": {
"alice": {
"host": "127.0.0.1",
"port": @alice_port@,
"cert_name": "alice.test"
},
"charlie": {
"host": "127.0.0.1",
"port": @charlie_port@,
"cert_name": "charlie.test"
},
"dave": {
"host": "127.0.0.1",
"port": @dave_port@,
"cert_name": "dave.test"
}
}
}"#;
const CHARLIE_CONFIG: &'static str = r#"{
"timedata": "@tempdir@/charlie.timedata",
"secret_store": "@tempdir@/charlie.store",
"bind_port": @charlie_port@,
"key": "@certdir@/charlie.key",
"cert": "@certdir@/charlie.crt",
"authorities": "@certdir@/trent.crt",
"logging": { "@tempdir@/charlie.log": "debug" },
"poll_interval": 0.5,
"peers": {
"alice": {
"host": "127.0.0.1",
"port": @alice_port@,
"cert_name": "alice.test"
},
"bob": {
"host": "127.0.0.1",
"port": @bob_port@,
"cert_name": "bob.test"
},
"dave": {
"host": "127.0.0.1",
"port": @dave_port@,
"cert_name": "dave.test"
}
}
}"#;
const DAVE_CONFIG: &'static str = r#"{
"timedata": "@tempdir@/dave.timedata",
"secret_store": "@tempdir@/dave.store",
"bind_port": @dave_port@,
"key": "@certdir@/dave.key",
"cert": "@certdir@/dave.crt",
"authorities": "@certdir@/trent.crt",
"logging": { "@tempdir@/dave.log": "debug" },
"poll_interval": 0.25,
"peers": {
"alice": {
"host": "127.0.0.1",
"port": @alice_port@,
"cert_name": "alice.test"
},
"bob": {
"host": "127.0.0.1",
"port": @bob_port@,
"cert_name": "bob.test"
},
"charlie": {
"host": "127.0.0.1",
"port": @charlie_port@,
"cert_name": "charlie.test"
}
}
}"#;
const SPAN_LIMIT: i64 = 25_000_000;
const DISPERSION_LIMIT: i64 = 1_000_000;
#[test]
fn four_node_local() {
match std::env::var_os("BYZTIMED_SAVE_INTEGRATION_TEST_OUTPUT") {
None => {
let temp_dir = tempfile::tempdir().unwrap();
run_four_node_local(temp_dir.path())
}
Some(dir) => run_four_node_local(dir.as_ref()),
}
}
fn run_four_node_local(temp_dir: &path::Path) {
let testbin_path = env::current_exe().unwrap();
let testbin_dir = testbin_path.parent().unwrap();
let bin_dir = testbin_dir.parent().unwrap();
let mut byztime_bin_path = bin_dir.to_owned();
byztime_bin_path.push("byztimed");
byztime_bin_path.set_extension(env::consts::EXE_EXTENSION);
assert!(byztime_bin_path.exists());
let mut cert_dir = path::PathBuf::new();
cert_dir.push(env!("CARGO_MANIFEST_DIR"));
cert_dir.push("tests");
cert_dir.push("test_certs");
assert!(cert_dir.exists());
let mut children = Vec::<ChildWrapper>::new();
let our_ports = find_ports(4);
for (config_filename, timedata_filename, store_dirname, stderr_filename, config_template) in &[
(
"alice.json",
"alice.timedata",
"alice.store",
"alice.stderr",
ALICE_CONFIG,
),
(
"bob.json",
"bob.timedata",
"bob.store",
"bob.stderr",
BOB_CONFIG,
),
(
"charlie.json",
"charlie.timedata",
"charlie.store",
"charlie.stderr",
CHARLIE_CONFIG,
),
(
"dave.json",
"dave.timedata",
"dave.store",
"dave.stderr",
DAVE_CONFIG,
),
] {
let config_contents = config_template
.replace("/", &path::MAIN_SEPARATOR.to_string())
.replace("@alice_port@", &our_ports[0].to_string())
.replace("@bob_port@", &our_ports[1].to_string())
.replace("@charlie_port@", &our_ports[2].to_string())
.replace("@dave_port@", &our_ports[3].to_string())
.replace("@tempdir@", temp_dir.to_str().unwrap())
.replace("@certdir@", cert_dir.to_str().unwrap());
let mut config_path = path::PathBuf::new();
config_path.push(temp_dir);
config_path.push(config_filename);
fs::write(&config_path, &config_contents).unwrap();
let mut timedata_path = path::PathBuf::new();
timedata_path.push(temp_dir);
timedata_path.push(timedata_filename);
if let Err(e) = fs::remove_file(&timedata_path) {
assert!(e.kind() == io::ErrorKind::NotFound);
}
let mut store_path = path::PathBuf::new();
store_path.push(temp_dir);
store_path.push(store_dirname);
fs::create_dir(&store_path).unwrap();
let mut stderr_path = path::PathBuf::new();
stderr_path.push(temp_dir);
stderr_path.push(stderr_filename);
let stderr = fs::File::create(stderr_path).unwrap();
children.push(
process::Command::new(&byztime_bin_path)
.arg(&config_path)
.stderr(stderr)
.spawn()
.unwrap()
.into(),
);
thread::sleep(time::Duration::from_nanos(200_000_000));
}
thread::sleep(time::Duration::from_secs(3));
for child in &children {
nix::sys::signal::kill(
nix::unistd::Pid::from_raw(child.id() as i32),
nix::sys::signal::Signal::SIGTERM,
)
.unwrap();
}
thread::sleep(time::Duration::from_secs(1));
for child in &mut children {
let _ = child.kill();
}
for child in children {
let output = child.wait_with_output().unwrap();
assert!(output.stderr.is_empty());
if !output.status.success() {
panic!("Output status: {}", output.status)
}
}
for log_file in &["alice.log", "bob.log", "charlie.log", "dave.log"] {
let mut log_path = path::PathBuf::new();
log_path.push(temp_dir);
log_path.push(log_file);
let log_contents = String::from_utf8(fs::read(&log_path).unwrap()).unwrap();
assert!(log_contents.find("ERROR").is_none());
}
let mut mins = Vec::new();
let mut ests = Vec::new();
let mut maxs = Vec::new();
let mut spans = Vec::new();
let mut summary_path = path::PathBuf::new();
summary_path.push(temp_dir);
summary_path.push("summary");
let mut summary_file = fs::File::create(&summary_path).unwrap();
for timedata_file in &[
"alice.timedata",
"bob.timedata",
"charlie.timedata",
"dave.timedata",
] {
let mut timedata_path = path::PathBuf::new();
timedata_path.push(temp_dir);
timedata_path.push(timedata_file);
let ctx = byztime::ConsumerContext::open(timedata_path.as_ref()).unwrap();
let (min, est, max) = ctx.offset().unwrap();
mins.push(min);
ests.push(est);
maxs.push(max);
spans.push(max - min);
writeln!(
summary_file,
"{}: min = {}; est = {}; max = {}; span = {}",
timedata_file,
min,
est,
max,
max - min
)
.unwrap();
}
mins.sort();
ests.sort();
maxs.sort();
spans.sort();
writeln!(summary_file, "").unwrap();
writeln!(summary_file, "Worst span: {}", spans[3]).unwrap();
writeln!(summary_file, "Dispersion: {}", ests[3] - ests[0]).unwrap();
for span in spans {
assert!(span > byztime::Timestamp::new(0, 0));
assert!(span < byztime::Timestamp::new(0, SPAN_LIMIT));
}
assert!(maxs[0] > mins[3]);
assert!(ests[3] - ests[0] < byztime::Timestamp::new(0, DISPERSION_LIMIT));
}