use std::{
path::{Path, PathBuf},
process::Command,
sync::mpsc,
thread,
};
use crate::catalog::TetFile;
use crate::query::{
ExecuteQueryOptions, QueryOutputFormat, execute_query_document, format_query_response,
parse_query_json, validate_query,
};
use super::fixture::write_multichunk_2x3_tiles;
const WORKERS: usize = 8;
const EXPECTED_SUM: f64 = 21.0;
fn sum_query_doc(dataset: &str) -> crate::query::QueryDocument {
let doc = parse_query_json(&format!(r#"{{"dataset":"{dataset}","sum":[]}}"#)).unwrap();
validate_query(&doc).unwrap();
doc
}
fn assert_sum_response(response: &crate::query::QueryResponse) {
let line = format_query_response(response, QueryOutputFormat::Quiet).unwrap();
assert!(
line.contains("sum=21") || line.contains("sum=21.0"),
"expected sum=21 in quiet line, got: {line}"
);
let exec = response.execution.as_ref().expect("execution block");
let sum = exec.operation_sum.expect("operation_sum");
assert!((sum - EXPECTED_SUM).abs() < 1e-9, "sum={sum}");
}
#[test]
fn concurrent_library_queries_on_sealed_file() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("sealed.tet");
write_multichunk_2x3_tiles(&path, "a");
let path = path.to_path_buf();
let (tx, rx) = mpsc::channel();
let handles: Vec<_> = (0..WORKERS)
.map(|_| {
let path = path.clone();
let tx = tx.clone();
thread::spawn(move || {
let file = TetFile::open(&path).unwrap();
let doc = sum_query_doc("a");
let response = execute_query_document(
&doc,
file.path(),
file.mmap(),
ExecuteQueryOptions::execute_no_preview(),
None,
)
.unwrap();
tx.send(response).unwrap();
})
})
.collect();
for h in handles {
h.join().unwrap();
}
drop(tx);
for response in rx.iter().take(WORKERS) {
assert_sum_response(&response);
}
}
fn tet_query_command(tet_path: &Path, query_json: &str) -> Command {
let path_arg = tet_path.to_str().expect("fixture path must be UTF-8");
let base_args = [
"query",
query_json,
"-t",
path_arg,
"-x",
"-q",
"--preview",
"0",
];
if let Ok(exe) = std::env::var("CARGO_BIN_EXE_tet") {
let mut cmd = Command::new(exe);
cmd.args(base_args);
return cmd;
}
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
for rel in ["target/debug/tet", "target/release/tet"] {
let bin = root.join(rel);
if bin.is_file() {
let mut cmd = Command::new(bin);
cmd.args(base_args);
return cmd;
}
}
let mut cmd = Command::new(env!("CARGO"));
let mut args = vec![
"run".to_owned(),
"--quiet".to_owned(),
"--bin".to_owned(),
"tet".to_owned(),
"--".to_owned(),
];
args.extend(base_args.iter().map(|s| (*s).to_owned()));
cmd.args(args);
cmd
}
#[test]
fn concurrent_process_tet_query_smoke() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("sealed.tet");
write_multichunk_2x3_tiles(&path, "a");
let query = r#"{"dataset":"a","sum":[]}"#;
let path = path.to_path_buf();
let (tx, rx) = mpsc::channel();
let workers = WORKERS.min(4);
let handles: Vec<_> = (0..workers)
.map(|_| {
let path = path.clone();
let query = query.to_owned();
let tx = tx.clone();
thread::spawn(move || {
let output = tet_query_command(&path, &query)
.output()
.expect("spawn tet query");
tx.send(output).unwrap();
})
})
.collect();
for h in handles {
h.join().unwrap();
}
drop(tx);
for output in rx.iter().take(workers) {
assert!(
output.status.success(),
"tet query failed: status={:?} stderr={}",
output.status,
String::from_utf8_lossy(&output.stderr)
);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("sum=21"),
"expected sum=21 on stdout, got: {stdout}"
);
}
}