Skip to content

Commit 5314209

Browse files
authored
Test IO file sizes in Rust and array sizes in Python doctests (#9792)
Python io doc tests check compressed size in bytes since that's what they're talking about. Keep the file size test as a rust test --------- Signed-off-by: Robert Kruszewski <github@robertk.io>
1 parent 58a3c2f commit 5314209

4 files changed

Lines changed: 65 additions & 9 deletions

File tree

‎Cargo.lock‎

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎vortex-file/Cargo.toml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ vortex-zstd = { workspace = true, optional = true }
6060
allocator-api2 = { workspace = true }
6161
divan = { workspace = true }
6262
mimalloc = { workspace = true }
63+
rand = { workspace = true }
6364
rstest = { workspace = true }
65+
tempfile = { workspace = true }
6466
tokio = { workspace = true, features = ["full"] }
6567
vortex-array = { workspace = true, features = ["_test-harness"] }
6668
vortex-io = { workspace = true, features = ["tokio"] }

‎vortex-file/src/tests.rs‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// SPDX-FileCopyrightText: Copyright the Vortex contributors
33

44
#![expect(clippy::cast_possible_truncation)]
5+
use std::fs;
56
use std::iter;
67
use std::sync::Arc;
78
use std::sync::LazyLock;
@@ -11,7 +12,11 @@ use flatbuffers::FlatBufferBuilder;
1112
use futures::StreamExt;
1213
use futures::TryStreamExt;
1314
use futures::pin_mut;
15+
use rand::RngExt;
16+
use rand::SeedableRng;
17+
use rand::rngs::StdRng;
1418
use rstest::rstest;
19+
use tempfile::tempdir;
1520
use vortex_array::ArrayRef;
1621
use vortex_array::IntoArray;
1722
use vortex_array::VortexSessionExecute;
@@ -82,7 +87,10 @@ use vortex_edition::EditionSessionExt;
8287
use vortex_edition::declarations::core::CORE_2026_08_3;
8388
use vortex_error::VortexExpect;
8489
use vortex_error::VortexResult;
90+
use vortex_io::VortexWrite;
91+
use vortex_io::runtime::tokio::TokioRuntime;
8592
use vortex_io::session::RuntimeSession;
93+
use vortex_io::std_file::FileWrite;
8694
use vortex_layout::DynLayout;
8795
use vortex_layout::LayoutStrategy;
8896
use vortex_layout::layouts::buffered::BufferedStrategy;
@@ -137,6 +145,52 @@ async fn test_eof_values() {
137145
assert_eq!(V1_FOOTER_FBS_SIZE, 32);
138146
}
139147

148+
// Optional encodings affect both compression choices and the registry stored in the footer.
149+
#[rstest]
150+
#[case::default(
151+
BtrBlocksCompressorBuilder::from_session(&SESSION),
152+
if cfg!(feature = "zstd") { 70_036 } else { 69_972 }
153+
)]
154+
#[cfg_attr(
155+
feature = "zstd",
156+
case::compact(
157+
BtrBlocksCompressorBuilder::from_session(&SESSION).with_compact(),
158+
55_112
159+
)
160+
)]
161+
#[tokio::test]
162+
#[cfg_attr(miri, ignore)]
163+
async fn test_stock_ticker_file_size(
164+
#[case] compressor: BtrBlocksCompressorBuilder,
165+
#[case] expected_size: u64,
166+
) -> VortexResult<()> {
167+
// Same stock-ticker distribution as the Python IO doctest, with a fixed Rust RNG seed.
168+
let mut rng = StdRng::seed_from_u64(0);
169+
let array = PrimitiveArray::from_iter((0..100_000i64).map(|i| rng.random_range(i..=i + 10)))
170+
.into_array();
171+
let directory = tempdir()?;
172+
let path = directory.path().join("stock_ticker.vortex");
173+
let mut writer = FileWrite::create(&path, TokioRuntime::current()).await?;
174+
SESSION
175+
.write_options()
176+
.with_strategy(
177+
crate::strategy::WriteStrategyBuilder::from_session(&SESSION)
178+
.with_btrblocks_builder(compressor)
179+
.build(),
180+
)
181+
.write(&mut writer, array.clone().to_array_stream())
182+
.await?;
183+
writer.shutdown().await?;
184+
185+
let file = SESSION
186+
.open_options()
187+
.open_buffer(ByteBuffer::from(fs::read(&path)?))?;
188+
let actual = file.scan()?.into_array_stream()?.read_all().await?;
189+
assert_arrays_eq!(actual, array, &mut SESSION.create_execution_ctx());
190+
assert_eq!(fs::metadata(path)?.len(), expected_size);
191+
Ok(())
192+
}
193+
140194
#[tokio::test]
141195
#[cfg_attr(miri, ignore)]
142196
async fn test_read_simple() {

‎vortex-python/src/io.rs‎

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -303,18 +303,16 @@ impl PyVortexWriteOptions {
303303
/// Let's model some stock ticker data. As you may know, the stock market always (noisly) goes
304304
/// up:
305305
///
306-
/// >>> import os
307306
/// >>> import random
308307
/// >>> sprl = vx.array([random.randint(i, i + 10) for i in range(100_000)])
309308
///
310-
/// If we naively wrote 4-bytes for each of these integers to a file we'd have 400,000 bytes!
311-
/// Let's see how small this is when we write with the default Vortex write options (which are
312-
/// also used by :func:`vortex.io.write`):
309+
/// If we naively wrote 8-bytes for each of these integers to a file we'd have 800,000 bytes!
310+
/// Let's see how small the array buffers are when we write with the default Vortex write
311+
/// options (which are also used by :func:`vortex.io.write`):
313312
///
314313
/// >>> vx.io.VortexWriteOptions.default().write(sprl, "chonky.vortex")
315-
/// >>> import os
316-
/// >>> os.path.getsize('chonky.vortex')
317-
/// 215788
314+
/// >>> vx.open("chonky.vortex").scan().read_all().nbytes
315+
/// 213248
318316
///
319317
/// Wow, Vortex manages to use about two bytes per integer! So advanced. So tiny.
320318
///
@@ -323,8 +321,8 @@ impl PyVortexWriteOptions {
323321
/// We sure can.
324322
///
325323
/// >>> vx.io.VortexWriteOptions.compact().write(sprl, "tiny.vortex")
326-
/// >>> os.path.getsize('tiny.vortex')
327-
/// 54992
324+
/// >>> vx.open("tiny.vortex").scan().read_all().nbytes
325+
/// 52564
328326
///
329327
/// Random numbers are not (usually) composed of random bytes!
330328
#[staticmethod]

0 commit comments

Comments
 (0)