Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 9 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,15 @@ These CI checks are the ones most often missed when adding files rather than edi
- Strongly consider `rstest` cases when parameterizing repetitive test logic.
- Prefer test functions that return `VortexResult<()>` and use `?` instead of `unwrap`.
- Prefer test module names `tests`, not `test`.
- Use `assert_arrays_eq!` for array comparisons instead of element-by-element assertions.
- Use `assert_arrays_eq!` for array comparisons instead of element-by-element assertions. Put the
result under test on the left: the macro already executes it recursively. Do not explicitly
canonicalize solely for this assertion; retain execution needed for physical representation,
resource, or error assertions.
- Preserve conformance suite entry points and input cases when auditing tests. For batch kernel
coverage, execute lazy results before comparing logical values, nulls, lengths, and expected
dtypes. Scalar probing alone may bypass the batch kernel.
- Derive expected errors from the input contract or a reference result; do not silently skip
failed operations. Invalid-input fixtures should reach the specific guard they claim to test.
- Keep tests concise and focused on behavior, edge cases, and regressions.
- If a bug fix is requested, add or identify a regression test when practical. Leave execution
to the user; when tests are run, a test that passes before and after the fix does not prove it.
Expand Down
15 changes: 7 additions & 8 deletions bench-orchestrator/tests/test_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ def test_build_command_adds_duckdb_cleanup_flag() -> None:
options={"scale-factor": "1.0"},
)

assert cmd[:5] == [
assert cmd[:7] == [
"/tmp/duckdb-bench",
"tpch",
"--display-format",
"gh-json",
"--iterations",
"7",
"--hide-progress-bar",
]
assert "--formats" in cmd
assert "parquet,vortex" in cmd
assert cmd[cmd.index("--formats") + 1] == "parquet,vortex"
assert "--delete-duckdb-database" in cmd
assert "--opt" in cmd
assert "scale-factor=1.0" in cmd
assert cmd[cmd.index("--opt") + 1] == "scale-factor=1.0"


def test_build_command_serializes_vortex_spatial_native_format() -> None:
Expand All @@ -43,7 +43,7 @@ def test_build_command_serializes_vortex_spatial_native_format() -> None:
options={"scale-factor": "1.0"},
)

assert "parquet,vortex,vortex-spatial-native" in cmd
assert cmd[cmd.index("--formats") + 1] == "parquet,vortex,vortex-spatial-native"


def test_build_command_omits_formats_for_lance_backend() -> None:
Expand All @@ -57,8 +57,7 @@ def test_build_command_omits_formats_for_lance_backend() -> None:

assert cmd[0] == "/tmp/lance-bench"
assert "--formats" not in cmd
assert "--queries" in cmd
assert "3" in cmd
assert cmd[cmd.index("--queries") + 1] == "3"


def test_build_command_includes_ingest_output_when_set() -> None:
Expand Down
54 changes: 35 additions & 19 deletions benchmarks/random-access-bench/src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,13 +212,6 @@ mod tests {
}
}

#[test]
fn column_label_uses_format_ext_and_mode() {
assert_eq!(column_label(Format::Parquet, false), "parquet-cached");
assert_eq!(column_label(Format::Parquet, true), "parquet-reopen");
assert_eq!(column_label(Format::OnDiskVortex, false), "vortex-cached");
}

#[test]
fn render_emits_single_header_row_with_format_mode_columns() -> Result<()> {
let runs = vec![
Expand Down Expand Up @@ -265,18 +258,41 @@ mod tests {
)?;
let rendered = strip_ansi(&String::from_utf8(buf)?);

assert!(
rendered.contains("parquet-cached") && rendered.contains("parquet-reopen"),
"expected format-mode column headers, got:\n{rendered}"
);
assert!(
rendered.contains("vortex-cached") && rendered.contains("vortex-reopen"),
"expected ext-based column headers, got:\n{rendered}"
);
assert!(
rendered.contains("random-access/taxi")
&& rendered.contains("random-access/taxi/uniform"),
"expected display-name row labels, got:\n{rendered}"
let rows = rendered
.lines()
.filter(|line| line.starts_with('│'))
.map(|line| {
line.trim_matches('│')
.split('│')
.map(str::trim)
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
assert_eq!(
rows,
vec![
vec![
"Benchmark",
"parquet-cached",
"parquet-reopen",
"vortex-cached",
"vortex-reopen"
],
vec![
"random-access/taxi",
"100 μs (1.00)",
"200 μs (2.00)",
"50 μs (0.50)",
"110 μs (1.10)"
],
vec![
"random-access/taxi/uniform",
"300 μs (1.00)",
"600 μs (2.00)",
"150 μs (0.50)",
"330 μs (1.10)"
],
]
);
Ok(())
}
Expand Down
38 changes: 27 additions & 11 deletions benchmarks/string-bench/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,10 +436,17 @@ mod tests {
let mut ctx = SESSION.create_execution_ctx();

for (kind, expected, actual) in cases {
assert!(
verify_canonicalized("fixture", &expected, &actual, &mut ctx).is_err(),
"{kind} mismatch must be rejected"
);
let err = verify_canonicalized("fixture", &expected, &actual, &mut ctx)
.unwrap_err()
.to_string();
let expected_error = match kind {
"dtype" => "canonicalized dtype",
"length" => "canonicalized row count",
"validity" => "validity mismatch at row 1",
"value" => "canonicalized value differs from input at row 1",
_ => unreachable!(),
};
assert!(err.contains(expected_error), "{kind}: {err}");
}
}

Expand All @@ -459,16 +466,21 @@ mod tests {

#[test]
fn iterations_must_be_positive() -> Result<()> {
assert!(validate_iterations(0).is_err());
assert_eq!(
validate_iterations(0).unwrap_err().to_string(),
"iterations must be greater than zero"
);
validate_iterations(1)
}

#[tokio::test]
async fn clickbench_shard_rejects_out_of_range() {
assert!(
assert_eq!(
download_clickbench_shard(CLICKBENCH_SHARD_COUNT)
.await
.is_err()
.unwrap_err()
.to_string(),
"invalid ClickBench shard 100 (want 0..99)",
);
}

Expand All @@ -495,10 +507,14 @@ mod tests {
name: name.to_string(),
array,
};
assert!(
prepare_column(&column, &mut ctx).is_err(),
"{name} input must be rejected"
);
let err = prepare_column(&column, &mut ctx).unwrap_err().to_string();
let expected_error = match name {
"null" => "contains 1 null strings",
"empty" => "zero uncompressed bytes",
"binary" => "requires Utf8 input",
_ => unreachable!(),
};
assert!(err.contains(expected_error), "{name}: {err}");
}
}
}
114 changes: 26 additions & 88 deletions encodings/alp/src/alp/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,12 +474,9 @@ mod tests {
use std::sync::LazyLock;

use rstest::rstest;
use vortex_array::Canonical;
use vortex_array::IntoArray;
use vortex_array::VortexSessionExecute;
use vortex_array::arrays::PrimitiveArray;
use vortex_array::assert_arrays_eq;
use vortex_error::VortexExpect;
use vortex_session::VortexSession;

use super::*;
Expand Down Expand Up @@ -507,17 +504,12 @@ mod tests {
let values = PrimitiveArray::from_iter((0..size).map(|i| i as f32));
let encoded = alp_encode(values.as_view(), None, &mut ctx).unwrap();

let result_canonical = {
encoded
.clone()
.into_array()
.execute::<Canonical>(&mut ctx)
.unwrap()
};
// Compare against the traditional array-based decompress path
let expected = decompress_into_array(encoded, &mut SESSION.create_execution_ctx()).unwrap();
let expected =
decompress_into_array(encoded.clone(), &mut SESSION.create_execution_ctx()).unwrap();

assert_arrays_eq!(result_canonical.into_array(), expected, &mut ctx);
assert_arrays_eq!(expected, values, &mut ctx);
assert_arrays_eq!(encoded, values, &mut ctx);
}

#[rstest]
Expand All @@ -536,15 +528,12 @@ mod tests {
alp_encode(values.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();

let mut ctx = SESSION.create_execution_ctx();
let result_canonical = encoded
.clone()
.into_array()
.execute::<Canonical>(&mut ctx)
.unwrap();
// Compare against the traditional array-based decompress path
let expected = decompress_into_array(encoded, &mut SESSION.create_execution_ctx()).unwrap();
let expected =
decompress_into_array(encoded.clone(), &mut SESSION.create_execution_ctx()).unwrap();

assert_arrays_eq!(result_canonical.into_array(), expected, &mut ctx);
assert_arrays_eq!(expected, values, &mut ctx);
assert_arrays_eq!(encoded, values, &mut ctx);
}

#[rstest]
Expand All @@ -569,15 +558,12 @@ mod tests {
assert!(encoded.patches().unwrap().array_len() > 0);

let mut ctx = SESSION.create_execution_ctx();
let result_canonical = encoded
.clone()
.into_array()
.execute::<Canonical>(&mut ctx)
.unwrap();
// Compare against the traditional array-based decompress path
let expected = decompress_into_array(encoded, &mut SESSION.create_execution_ctx()).unwrap();
let expected =
decompress_into_array(encoded.clone(), &mut SESSION.create_execution_ctx()).unwrap();

assert_arrays_eq!(result_canonical.into_array(), expected, &mut ctx);
assert_arrays_eq!(expected, array, &mut ctx);
assert_arrays_eq!(encoded, array, &mut ctx);
}

#[rstest]
Expand All @@ -600,15 +586,12 @@ mod tests {
alp_encode(array.as_view(), None, &mut SESSION.create_execution_ctx()).unwrap();

let mut ctx = SESSION.create_execution_ctx();
let result_canonical = encoded
.clone()
.into_array()
.execute::<Canonical>(&mut ctx)
.unwrap();
// Compare against the traditional array-based decompress path
let expected = decompress_into_array(encoded, &mut SESSION.create_execution_ctx()).unwrap();
let expected =
decompress_into_array(encoded.clone(), &mut SESSION.create_execution_ctx()).unwrap();

assert_arrays_eq!(result_canonical.into_array(), expected, &mut ctx);
assert_arrays_eq!(expected, array, &mut ctx);
assert_arrays_eq!(encoded, array, &mut ctx);
}

#[rstest]
Expand All @@ -634,15 +617,12 @@ mod tests {
assert!(encoded.patches().unwrap().array_len() > 0);

let mut ctx = SESSION.create_execution_ctx();
let result_canonical = encoded
.clone()
.into_array()
.execute::<Canonical>(&mut ctx)
.unwrap();
// Compare against the traditional array-based decompress path
let expected = decompress_into_array(encoded, &mut SESSION.create_execution_ctx()).unwrap();
let expected =
decompress_into_array(encoded.clone(), &mut SESSION.create_execution_ctx()).unwrap();

assert_arrays_eq!(result_canonical.into_array(), expected, &mut ctx);
assert_arrays_eq!(expected, array, &mut ctx);
assert_arrays_eq!(encoded, array, &mut ctx);
}

#[rstest]
Expand All @@ -667,31 +647,11 @@ mod tests {
let encoded = alp_encode(array.as_view(), None, &mut ctx).unwrap();

let slice_end = size - slice_start;
let slice_len = slice_end - slice_start;
let sliced_encoded = encoded.slice(slice_start..slice_end).unwrap();

let result_canonical = sliced_encoded.execute::<Canonical>(&mut ctx).unwrap();
let result_primitive = result_canonical.into_primitive();

for idx in 0..slice_len {
let expected_value = values[slice_start + idx];

let result_valid = result_primitive
.validity()
.vortex_expect("result validity should be derivable")
.execute_is_valid(idx, &mut ctx)
.unwrap();
assert_eq!(
result_valid,
expected_value.is_some(),
"Validity mismatch at idx={idx}",
);

if let Some(expected_val) = expected_value {
let result_val = result_primitive.as_slice::<f64>()[idx];
assert_eq!(result_val, expected_val, "Value mismatch at idx={idx}",);
}
}
let expected =
PrimitiveArray::from_option_iter(values[slice_start..slice_end].iter().copied());
assert_arrays_eq!(sliced_encoded, expected, &mut ctx);
}

#[rstest]
Expand All @@ -716,33 +676,11 @@ mod tests {
let encoded = alp_encode(array.as_view(), None, &mut ctx).unwrap();

let slice_end = size - slice_start;
let slice_len = slice_end - slice_start;
let sliced_encoded = encoded.slice(slice_start..slice_end).unwrap();

let result_primitive = sliced_encoded.execute::<PrimitiveArray>(&mut ctx).unwrap();

for idx in 0..slice_len {
let expected_value = values[slice_start + idx];

let result_valid = result_primitive
.as_ref()
.validity()
.unwrap()
.execute_mask(result_primitive.as_ref().len(), &mut ctx)
.unwrap()
.value(idx);
assert_eq!(
result_valid,
expected_value.is_some(),
"Validity mismatch at idx={idx}",
);

if let Some(expected_val) = expected_value {
let buf = result_primitive.to_buffer::<f64>();
let result_val = buf.as_slice()[idx];
assert_eq!(result_val, expected_val, "Value mismatch at idx={idx}",);
}
}
let expected =
PrimitiveArray::from_option_iter(values[slice_start..slice_end].iter().copied());
assert_arrays_eq!(sliced_encoded, expected, &mut ctx);
}

/// Regression test for issue #5948: execute_decompress drops patches when chunk_offsets is
Expand Down
Loading
Loading