mod index;
mod store;
mod support;
use diskann_benchmark_runner::{App, Registry, output};
fn registry() -> anyhow::Result<Registry> {
let mut registry = Registry::new();
registry.register("store-stress", store::StoreStress)?;
index::register(&mut registry)?;
Ok(registry)
}
fn main() -> anyhow::Result<()> {
let app = App::parse();
app.run(®istry()?, &mut output::default())
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::Path;
use diskann_benchmark_runner::{
app::{Check, Commands},
output::Memory,
};
use diskann_utils::test_data_root;
use serde::{Deserialize, Serialize};
use serde_json::Value;
const DISKANN_TEST_ENV: &str = "DISKANN_TEST";
fn overwrite_baselines() -> bool {
match std::env::var(DISKANN_TEST_ENV) {
Ok(v) if v == "overwrite" => true,
Ok(v) => {
panic!("unknown value for {DISKANN_TEST_ENV}: \"{v}\". Expected \"overwrite\"")
}
Err(std::env::VarError::NotPresent) => false,
Err(std::env::VarError::NotUnicode(_)) => {
panic!("value for {DISKANN_TEST_ENV} is not unicode")
}
}
}
fn example_directory() -> std::path::PathBuf {
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("integration")
.join("jsons")
}
fn load_from_file<T>(path: &std::path::Path) -> T
where
T: for<'a> Deserialize<'a>,
{
let file = std::fs::File::open(path).unwrap();
let reader = std::io::BufReader::new(file);
serde_json::from_reader(reader).unwrap()
}
fn value_from_file(path: &std::path::Path) -> serde_json::Value {
load_from_file(path)
}
fn save_to_file<T>(path: &std::path::Path, value: &T, force: bool)
where
T: Serialize + ?Sized,
{
if path.exists() && !force {
panic!("path {} already exists!", path.display());
}
let buffer = std::fs::File::create(path).unwrap();
serde_json::to_writer_pretty(buffer, value).unwrap();
}
fn prefix_search_directories(raw: &mut serde_json::Value, root: &std::path::Path) {
let key = "search_directories";
if let serde_json::Value::Object(obj) = raw {
let value = obj
.get_mut(key)
.expect("key \"search_directories\" should exist");
if let serde_json::Value::Array(directories) = value {
for value in directories.iter_mut() {
if let serde_json::Value::String(dir) = value {
*dir = root.join(&dir).to_str().unwrap().into();
}
}
} else {
panic!("Expected an Array - got {}", raw);
}
} else {
panic!("Expected an Object - got {}", raw);
}
}
fn prepend(input: &Path, output: &Path, root: &Path) {
let mut v = value_from_file(input);
prefix_search_directories(&mut v, root);
save_to_file(output, &v, false);
}
fn run_example(name: &str) {
let input_file = example_directory().join(name);
assert!(input_file.exists(), "missing example file: {input_file:?}");
let tempdir = tempfile::tempdir().unwrap();
let modified_input_file = tempdir.path().join("input.json");
let output_file = tempdir.path().join("output.json");
prepend(&input_file, &modified_input_file, &test_data_root());
let command = Commands::Run {
input_file: modified_input_file,
output_file: output_file.clone(),
dry_run: false,
allow_debug: true,
};
let app = App::from_commands(command);
let mut output = Memory::new();
app.run(®istry().unwrap(), &mut output).unwrap();
assert!(output_file.exists(), "results file was not written");
}
fn run_regression_example(input_name: &str, tolerances_name: &str, baseline_name: &str) {
let input_file = example_directory().join(input_name);
let tolerances_file = example_directory().join(tolerances_name);
let baseline_file = example_directory().join(baseline_name);
assert!(input_file.exists(), "missing example file: {input_file:?}");
assert!(
tolerances_file.exists(),
"missing tolerances file: {tolerances_file:?}"
);
let tempdir = tempfile::tempdir().unwrap();
let modified_input_file = tempdir.path().join("input.json");
let output_file = tempdir.path().join("output.json");
prepend(&input_file, &modified_input_file, &test_data_root());
let command = Commands::Run {
input_file: modified_input_file.clone(),
output_file: output_file.clone(),
dry_run: false,
allow_debug: true,
};
let mut output = Memory::new();
App::from_commands(command)
.run(®istry().unwrap(), &mut output)
.unwrap();
assert!(output_file.exists(), "results file was not written");
if overwrite_baselines() {
let mut v = value_from_file(&output_file);
scrub(&mut v, &test_data_root());
save_to_file(&baseline_file, &v, true);
return;
}
assert!(
baseline_file.exists(),
"missing baseline {baseline_file:?}; regenerate it with {DISKANN_TEST_ENV}=overwrite"
);
let command = Commands::Check(Check::Run {
tolerances: tolerances_file,
input_file: modified_input_file,
before: baseline_file,
after: output_file,
output_file: None,
});
let mut output = Memory::new();
if let Err(err) = App::from_commands(command).run(®istry().unwrap(), &mut output) {
panic!(
"Regression check failed:\n\n{}\n\n{}",
err,
String::from_utf8(output.into_inner()).unwrap()
);
}
}
fn scrub(value: &mut Value, root: &Path) {
let mut values = vec![value];
while let Some(value) = values.pop() {
match value {
Value::Null | Value::Bool(_) | Value::Number(_) => {}
Value::String(s) => {
*s = diskann_benchmark_runner::ux::scrub_path(s.clone(), root, "");
}
Value::Array(v) => v.iter_mut().for_each(|v| values.push(v)),
Value::Object(m) => m.values_mut().for_each(|v| values.push(v)),
}
}
}
#[test]
fn store_stress_integration() {
run_example("store-stress-test.json");
}
#[test]
#[cfg(not(miri))]
fn graph_index() {
run_regression_example(
"integration.json",
"checks.json",
"integration-baseline.json",
);
}
}