use std::path::{Path, PathBuf};
use relative_path::RelativePath;
#[track_caller]
pub(crate) fn get_or_save_test_results<R>(test_name: &str, results: &R) -> R
where
R: serde::Serialize + for<'a> serde::Deserialize<'a> + Clone,
{
match _get_or_save_test_results(
test_name,
serde_json::to_value(results).expect("while serializing results"),
) {
Some(output) => match R::deserialize(output) {
Ok(deserialized) => deserialized,
Err(err) => {
panic!(
"Error encountered while deserializing cached test result: {}. \
If the data structure representation changed {}",
err,
env_hint()
);
}
},
None => results.clone(),
}
}
#[derive(Debug)]
pub(crate) struct TestRoot {
path: String,
}
impl TestRoot {
pub(crate) fn new<P: Into<String>>(path: P) -> Self {
Self { path: path.into() }
}
pub(crate) fn path(&mut self) -> TestPath<'_> {
TestPath::new(&mut self.path)
}
}
#[derive(Debug)]
pub(crate) struct TestPath<'a> {
path: &'a mut String,
len: usize,
}
impl<'a> TestPath<'a> {
pub(crate) fn new(path: &'a mut String) -> Self {
let len = path.len();
Self { path, len }
}
}
impl TestPath<'_> {
pub(crate) fn push<P: AsRef<str>>(&mut self, name: P) -> TestPath<'_> {
self._push(name.as_ref())
}
pub(crate) fn _push(&mut self, name: &str) -> TestPath<'_> {
let len = self.path.len();
self.path.push('/');
self.path.push_str(name.as_ref());
TestPath {
path: self.path,
len,
}
}
}
impl Drop for TestPath<'_> {
fn drop(&mut self) {
self.path.truncate(self.len)
}
}
impl std::ops::Deref for TestPath<'_> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.path
}
}
impl AsRef<str> for TestPath<'_> {
fn as_ref(&self) -> &str {
self.path
}
}
impl AsRef<std::path::Path> for TestPath<'_> {
fn as_ref(&self) -> &std::path::Path {
self.path.as_ref()
}
}
const BUILD_DIR: &str = env!("CARGO_MANIFEST_DIR");
const CONFIGURATION: &str = "DISKANN_TEST";
fn env_hint() -> &'static str {
"try running with the environment variable \"DISKANN_TEST=overwrite\""
}
#[derive(Debug)]
enum Mode {
Test,
Overwrite,
}
impl Mode {
fn current() -> Self {
match std::env::var(CONFIGURATION) {
Ok(v) => {
if v == "overwrite" {
Self::Overwrite
} else {
panic!(
"Unknown value for {}: \"{}\". Expected \"overwrite\"",
CONFIGURATION, v
);
}
}
Err(std::env::VarError::NotPresent) => Self::Test,
Err(std::env::VarError::NotUnicode(_)) => {
panic!("Value for {} is not valid unicode", CONFIGURATION)
}
}
}
}
fn result_path(relative: &RelativePath) -> PathBuf {
let build_dir: &Path = BUILD_DIR.as_ref();
relative.to_path(build_dir.join("test").join("generated"))
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct TestResult {
file: String,
test: String,
payload: serde_json::Value,
}
#[track_caller]
fn _get_or_save_test_results(test: &str, value: serde_json::Value) -> Option<serde_json::Value> {
let caller = std::panic::Location::caller();
let result_path = {
let mut result_path = result_path(RelativePath::from_path(test).unwrap());
result_path.set_extension("json");
result_path
};
match Mode::current() {
Mode::Test => {
let file = match std::fs::File::open(&result_path) {
Ok(file) => file,
Err(err) => panic!(
"Could not open {}: {}. If you added a new test {}",
result_path.display(),
err,
env_hint(),
),
};
let reader = std::io::BufReader::new(file);
let result: TestResult =
serde_json::from_reader(reader).expect("deserialization should succeed");
Some(result.payload)
}
Mode::Overwrite => {
let result_dir = result_path
.parent()
.expect("Result path should have a parent directory");
if let Err(err) = std::fs::create_dir_all(result_dir) {
panic!(
"Problem creating paths \"{}\": {}",
result_path.display(),
err
);
}
let buffer = match std::fs::File::create(&result_path) {
Ok(buffer) => buffer,
Err(err) => {
panic!(
"Error opening path {} for writing: {}",
result_path.display(),
err
);
}
};
let result = TestResult {
file: normalize_file(caller.file()),
test: normalize_file(test),
payload: value,
};
serde_json::to_writer_pretty(buffer, &result).expect("serialization should succeed");
None
}
}
}
fn normalize_file(s: &str) -> String {
s.replace("\\", "/")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_path() {
let mut x = TestRoot::new("some/root");
let mut y = x.path();
assert_eq!(&*y, "some/root");
{
let mut z = y.push("another");
assert_eq!(&*z, "some/root/another");
{
let w = z.push("one-more");
assert_eq!(&*w, "some/root/another/one-more");
std::mem::drop(w);
let w = z.push("again");
assert_eq!(&*w, "some/root/another/again");
}
assert_eq!(&*z, "some/root/another");
}
assert_eq!(&*y, "some/root");
}
}