influxdb/test_helpers/src/lib.rs

161 lines
5.2 KiB
Rust

#![deny(broken_intra_doc_links, rust_2018_idioms)]
#![warn(
missing_copy_implementations,
missing_debug_implementations,
clippy::explicit_iter_loop,
clippy::use_self,
clippy::clone_on_ref_ptr
)]
use std::{
env, f64,
sync::{Arc, Once},
};
pub use tempfile;
pub mod tracing;
pub type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
pub type Result<T = (), E = Error> = std::result::Result<T, E>;
/// A test helper function for asserting floating point numbers are within the
/// machine epsilon because strict comparison of floating point numbers is
/// incorrect
pub fn approximately_equal(f1: f64, f2: f64) -> bool {
(f1 - f2).abs() < f64::EPSILON
}
pub fn all_approximately_equal(f1: &[f64], f2: &[f64]) -> bool {
f1.len() == f2.len() && f1.iter().zip(f2).all(|(&a, &b)| approximately_equal(a, b))
}
/// Return a temporary directory that is deleted when the object is dropped
pub fn tmp_dir() -> Result<tempfile::TempDir> {
let _ = dotenv::dotenv();
let root = env::var_os("TEST_INFLUXDB_IOX_DB_DIR").unwrap_or_else(|| env::temp_dir().into());
Ok(tempfile::Builder::new()
.prefix("influxdb_iox")
.tempdir_in(root)?)
}
pub fn tmp_file() -> Result<tempfile::NamedTempFile> {
let _ = dotenv::dotenv();
let root = env::var_os("TEST_INFLUXDB_IOX_DB_DIR").unwrap_or_else(|| env::temp_dir().into());
Ok(tempfile::Builder::new()
.prefix("influxdb_iox")
.tempfile_in(root)?)
}
/// Writes the specified string to a new temporary file, returning the Path to
/// the file
pub fn make_temp_file<C: AsRef<[u8]>>(contents: C) -> tempfile::NamedTempFile {
let file = tmp_file().expect("creating temp file");
std::fs::write(&file, contents).expect("writing data to temp file");
file
}
/// convert form that is easier to type in tests to what some code needs
pub fn str_vec_to_arc_vec(str_vec: &[&str]) -> Arc<Vec<Arc<str>>> {
Arc::new(str_vec.iter().map(|s| Arc::from(*s)).collect())
}
/// convert form that is easier to type in tests to what some code needs
pub fn str_pair_vec_to_vec(str_vec: &[(&str, &str)]) -> Vec<(Arc<str>, Arc<str>)> {
str_vec
.iter()
.map(|(s1, s2)| (Arc::from(*s1), Arc::from(*s2)))
.collect()
}
/// Converts bytes representing tag_keys values to Rust strings,
/// handling the special case `_m(0x00)` and `_f(0xff)` values. Other
/// than `0xff` panics on any non-utf8 string.
pub fn tag_key_bytes_to_strings(bytes: Vec<u8>) -> String {
match bytes.as_slice() {
[0] => "_m(0x00)".into(),
// note this isn't valid UTF8 and thus would assert below
[255] => "_f(0xff)".into(),
_ => String::from_utf8(bytes).expect("string value response was not utf8"),
}
}
static LOG_SETUP: Once = Once::new();
/// Enables debug logging regardless of the value of RUST_LOG
/// environment variable. If RUST_LOG isn't specifies, defaults to
/// "debug"
pub fn start_logging() {
// ensure the global has been initialized
LOG_SETUP.call_once(|| {
// honor any existing RUST_LOG level
if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "debug");
}
// Configure the logger to write to stderr and install it
let output_stream = std::io::stderr;
use observability_deps::tracing_subscriber::{self, prelude::*, EnvFilter};
tracing_subscriber::registry()
.with(EnvFilter::from_default_env())
.with(tracing_subscriber::fmt::layer().with_writer(output_stream))
.init();
})
}
/// Enables debug logging if the RUST_LOG environment variable is
/// set. Does nothing if RUST_LOG is not set. If enable_logging has
/// been set previously, does nothing
pub fn maybe_start_logging() {
if std::env::var("RUST_LOG").is_ok() {
start_logging()
}
}
#[macro_export]
/// A macro to assert that one string is contained within another with
/// a nice error message if they are not.
///
/// Usage: `assert_contains!(actual, expected)`
///
/// Is a macro so test error
/// messages are on the same line as the failure;
///
/// Both arguments must be convertable into Strings (Into<String>)
macro_rules! assert_contains {
($ACTUAL: expr, $EXPECTED: expr) => {
let actual_value: String = $ACTUAL.into();
let expected_value: String = $EXPECTED.into();
assert!(
actual_value.contains(&expected_value),
"Can not find expected in actual.\n\nExpected:\n{}\n\nActual:\n{}",
expected_value,
actual_value
);
};
}
#[macro_export]
/// A macro to assert that one string is NOT contained within another with
/// a nice error message if that check fails. Is a macro so test error
/// messages are on the same line as the failure;
///
/// Both arguments must be convertable into Strings (Into<String>)
macro_rules! assert_not_contains {
($ACTUAL: expr, $UNEXPECTED: expr) => {
let actual_value: String = $ACTUAL.into();
let unexpected_value: String = $UNEXPECTED.into();
assert!(
!actual_value.contains(&unexpected_value),
"Found unexpected value in actual.\n\nUnexpected:\n{}\n\nActual:\n{}",
unexpected_value,
actual_value
);
};
}