Skip to content

Commit d90cc7d

Browse files
dfa1claude
andcommitted
refactor(integration): merge file-size tests into FileSizeComparisonIntegrationTest
Three-tier structure: CSV (baseline) → JNI/Rust (oracle) → Java (SUT). Drops separate WriteFileSizeIntegrationTest and VortexVsCsvFileSizeTest. Both vortex writers now use cascading(3) for fair comparison. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 185bb37 commit d90cc7d

2 files changed

Lines changed: 79 additions & 174 deletions

File tree

integration/src/test/java/io/github/dfa1/vortex/integration/WriteFileSizeIntegrationTest.java renamed to integration/src/test/java/io/github/dfa1/vortex/integration/FileSizeComparisonIntegrationTest.java

Lines changed: 79 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import dev.vortex.jni.NativeLoader;
66
import io.github.dfa1.vortex.core.DType;
77
import io.github.dfa1.vortex.core.PType;
8-
import io.github.dfa1.vortex.core.array.DoubleArray;
98
import io.github.dfa1.vortex.core.array.LongArray;
109
import io.github.dfa1.vortex.encoding.EncodingRegistry;
1110
import io.github.dfa1.vortex.io.VortexReader;
@@ -18,6 +17,7 @@
1817
import org.apache.arrow.vector.BigIntVector;
1918
import org.apache.arrow.vector.DateDayVector;
2019
import org.apache.arrow.vector.Float8Vector;
20+
import org.apache.arrow.vector.VarCharVector;
2121
import org.apache.arrow.vector.VectorSchemaRoot;
2222
import org.apache.arrow.vector.types.DateUnit;
2323
import org.apache.arrow.vector.types.FloatingPointPrecision;
@@ -27,39 +27,33 @@
2727
import org.junit.jupiter.api.Test;
2828
import org.junit.jupiter.api.io.TempDir;
2929

30+
import java.io.BufferedWriter;
3031
import java.io.IOException;
3132
import java.nio.channels.FileChannel;
33+
import java.nio.charset.StandardCharsets;
3234
import java.nio.file.Files;
3335
import java.nio.file.Path;
3436
import java.nio.file.StandardOpenOption;
37+
import java.time.LocalDate;
3538
import java.util.HashMap;
3639
import java.util.List;
3740
import java.util.Map;
3841

3942
import static org.assertj.core.api.Assertions.assertThat;
4043

41-
/// Verifies that Java and JNI writers produce valid, readable files for the same numeric OHLC dataset,
42-
/// and captures file sizes for regression visibility.
44+
/// File-size comparison: CSV (baseline) vs JNI/Rust writer (oracle) vs Java writer (SUT).
4345
///
44-
/// Schema: date(I32), open/high/low/close(F64), volume(I64) — 6 columns, no strings.
45-
class WriteFileSizeIntegrationTest {
46+
/// Schema: symbol(Utf8), date(I32), open/high/low/close(F64), volume(I64) — 7 columns.
47+
/// Both Vortex writers use cascading(3) to match the Rust default compression pipeline.
48+
class FileSizeComparisonIntegrationTest {
4649

4750
private static final int TOTAL_ROWS = 100_000;
4851
private static final int BATCH_SIZE = 50_000;
4952

50-
private static final ArrowType F64_TYPE = new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE);
51-
private static final Schema JNI_SCHEMA = new Schema(List.of(
52-
Field.notNullable("date", new ArrowType.Date(DateUnit.DAY)),
53-
Field.notNullable("open", F64_TYPE),
54-
Field.notNullable("high", F64_TYPE),
55-
Field.notNullable("low", F64_TYPE),
56-
Field.notNullable("close", F64_TYPE),
57-
Field.notNullable("volume", new ArrowType.Int(64, true))
58-
));
59-
6053
private static final DType.Struct JAVA_SCHEMA = new DType.Struct(
61-
List.of("date", "open", "high", "low", "close", "volume"),
54+
List.of("symbol", "date", "open", "high", "low", "close", "volume"),
6255
List.of(
56+
new DType.Utf8(false),
6357
new DType.Primitive(PType.I32, false),
6458
new DType.Primitive(PType.F64, false),
6559
new DType.Primitive(PType.F64, false),
@@ -70,42 +64,71 @@ class WriteFileSizeIntegrationTest {
7064
false
7165
);
7266

67+
private static final Schema JNI_SCHEMA = new Schema(List.of(
68+
Field.notNullable("symbol", new ArrowType.Utf8()),
69+
Field.notNullable("date", new ArrowType.Date(DateUnit.DAY)),
70+
Field.notNullable("open", new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)),
71+
Field.notNullable("high", new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)),
72+
Field.notNullable("low", new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)),
73+
Field.notNullable("close", new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)),
74+
Field.notNullable("volume", new ArrowType.Int(64, true))
75+
));
76+
7377
private static final Session SESSION = Session.create();
7478
private static final BufferAllocator ALLOCATOR = ArrowAllocation.rootAllocator();
7579

7680
static {
7781
NativeLoader.loadJni();
7882
}
7983

80-
8184
// ── Writers ───────────────────────────────────────────────────────────────
8285

83-
private static void writeJava(Path file, List<OhlcGenerator.OhlcBatch> batches) throws IOException {
86+
private static Path writeCsv(Path dir, List<OhlcGenerator.OhlcBatch> batches) throws IOException {
87+
Path file = dir.resolve("ohlc.csv");
88+
try (BufferedWriter csv = Files.newBufferedWriter(file)) {
89+
csv.write("symbol,date,open,high,low,close,volume\n");
90+
for (OhlcGenerator.OhlcBatch b : batches) {
91+
for (int i = 0; i < b.dates().length; i++) {
92+
csv.write(b.symbols()[i] + "," + LocalDate.ofEpochDay(b.dates()[i]) + ","
93+
+ b.open()[i] + "," + b.high()[i] + ","
94+
+ b.low()[i] + "," + b.close()[i] + "," + b.volume()[i] + "\n");
95+
}
96+
}
97+
}
98+
return file;
99+
}
100+
101+
private static Path writeJava(Path dir, List<OhlcGenerator.OhlcBatch> batches) throws IOException {
102+
Path file = dir.resolve("ohlc-java.vtx");
84103
try (FileChannel ch = FileChannel.open(file, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
85104
VortexWriter writer = VortexWriter.create(ch, JAVA_SCHEMA, WriteOptions.cascading(3))) {
86105
for (OhlcGenerator.OhlcBatch b : batches) {
87106
writer.writeChunk(Map.of(
88-
"date", b.dates(), "open", b.open(), "high", b.high(),
89-
"low", b.low(), "close", b.close(), "volume", b.volume()
90-
));
107+
"symbol", b.symbols(), "date", b.dates(),
108+
"open", b.open(), "high", b.high(),
109+
"low", b.low(), "close", b.close(), "volume", b.volume()));
91110
}
92111
}
112+
return file;
93113
}
94114

95-
private static void writeJni(Path file, List<OhlcGenerator.OhlcBatch> batches) throws IOException {
115+
private static Path writeJni(Path dir, List<OhlcGenerator.OhlcBatch> batches) throws IOException {
116+
Path file = dir.resolve("ohlc-jni.vtx");
96117
String uri = file.toAbsolutePath().toUri().toString();
97118
try (dev.vortex.api.VortexWriter writer = dev.vortex.api.VortexWriter.create(
98119
SESSION, uri, JNI_SCHEMA, new HashMap<>(), ALLOCATOR)) {
99120
for (OhlcGenerator.OhlcBatch b : batches) {
100121
try (VectorSchemaRoot root = VectorSchemaRoot.create(JNI_SCHEMA, ALLOCATOR)) {
101-
DateDayVector dateVec = (DateDayVector) root.getVector("date");
102-
Float8Vector openVec = (Float8Vector) root.getVector("open");
103-
Float8Vector highVec = (Float8Vector) root.getVector("high");
104-
Float8Vector lowVec = (Float8Vector) root.getVector("low");
105-
Float8Vector closeVec = (Float8Vector) root.getVector("close");
106-
BigIntVector volVec = (BigIntVector) root.getVector("volume");
122+
VarCharVector symbolVec = (VarCharVector) root.getVector("symbol");
123+
DateDayVector dateVec = (DateDayVector) root.getVector("date");
124+
Float8Vector openVec = (Float8Vector) root.getVector("open");
125+
Float8Vector highVec = (Float8Vector) root.getVector("high");
126+
Float8Vector lowVec = (Float8Vector) root.getVector("low");
127+
Float8Vector closeVec = (Float8Vector) root.getVector("close");
128+
BigIntVector volVec = (BigIntVector) root.getVector("volume");
107129

108130
int n = b.dates().length;
131+
symbolVec.allocateNew();
109132
dateVec.allocateNew(n);
110133
openVec.allocateNew(n);
111134
highVec.allocateNew(n);
@@ -114,6 +137,7 @@ private static void writeJni(Path file, List<OhlcGenerator.OhlcBatch> batches) t
114137
volVec.allocateNew(n);
115138

116139
for (int i = 0; i < n; i++) {
140+
symbolVec.setSafe(i, b.symbols()[i].getBytes(StandardCharsets.UTF_8));
117141
dateVec.setSafe(i, b.dates()[i]);
118142
openVec.setSafe(i, b.open()[i]);
119143
highVec.setSafe(i, b.high()[i]);
@@ -131,77 +155,49 @@ private static void writeJni(Path file, List<OhlcGenerator.OhlcBatch> batches) t
131155
}
132156
}
133157
}
158+
return file;
134159
}
135160

136-
// ── Tests ─────────────────────────────────────────────────────────────────
161+
// ── Test ──────────────────────────────────────────────────────────────────
137162

138163
@Test
139-
void javaFile_isReadable_withCorrectRowCount(@TempDir Path tmp) throws IOException {
164+
void fileSizeComparison(@TempDir Path tmp) throws IOException {
140165
// Given
141-
Path file = tmp.resolve("ohlc-java.vtx");
142166
List<OhlcGenerator.OhlcBatch> batches = OhlcGenerator.generate(TOTAL_ROWS, BATCH_SIZE);
143167

144168
// When
145-
writeJava(file, batches);
169+
Path csvFile = writeCsv(tmp, batches);
170+
Path jniFile = writeJni(tmp, batches);
171+
Path javaFile = writeJava(tmp, batches);
146172

147-
// Then — readable and correct row count
148-
long totalRows = 0L;
149-
try (VortexReader reader = VortexReader.open(file, EncodingRegistry.loadAll())) {
150-
var iter = reader.scan(io.github.dfa1.vortex.scan.ScanOptions.columns("volume"));
151-
while (iter.hasNext()) {
152-
totalRows += iter.next().<LongArray>column("volume").length();
153-
}
154-
}
155-
assertThat(totalRows).isEqualTo(TOTAL_ROWS);
156-
System.out.printf("[WriteFileSizeIntegrationTest] Java file: %,d bytes (%.2f MB)%n",
157-
Files.size(file), Files.size(file) / 1_048_576.0);
158-
}
173+
long csvSize = Files.size(csvFile);
174+
long jniSize = Files.size(jniFile);
175+
long javaSize = Files.size(javaFile);
159176

160-
@Test
161-
void jniFile_isReadable_withCorrectRowCount(@TempDir Path tmp) throws IOException {
162-
// Given
163-
Path file = tmp.resolve("ohlc-jni.vtx");
164-
List<OhlcGenerator.OhlcBatch> batches = OhlcGenerator.generate(TOTAL_ROWS, BATCH_SIZE);
177+
// Then — report
178+
System.out.printf(
179+
"[FileSizeComparison] %,d rows CSV=%,d bytes (%.1f MB) JNI=%,d bytes (%.1f MB) Java=%,d bytes (%.1f MB) Java/JNI=%.2fx CSV/Java=%.1fx%n",
180+
TOTAL_ROWS,
181+
csvSize, csvSize / 1_048_576.0,
182+
jniSize, jniSize / 1_048_576.0,
183+
javaSize, javaSize / 1_048_576.0,
184+
(double) javaSize / jniSize,
185+
(double) csvSize / javaSize);
165186

166-
// When
167-
writeJni(file, batches);
187+
// Then — Java beats CSV
188+
assertThat(javaSize).isLessThan(csvSize);
168189

169-
// Then — readable and correct row count
190+
// Then — Java within 2x of JNI (both use cascading(3))
191+
assertThat(javaSize).isLessThan(jniSize * 2);
192+
193+
// Then — Java file is readable with correct row count
170194
long totalRows = 0L;
171-
try (VortexReader reader = VortexReader.open(file, EncodingRegistry.loadAll())) {
172-
var iter = reader.scan(io.github.dfa1.vortex.scan.ScanOptions.columns("close"));
195+
try (VortexReader reader = VortexReader.open(javaFile, EncodingRegistry.loadAll())) {
196+
var iter = reader.scan(io.github.dfa1.vortex.scan.ScanOptions.columns("volume"));
173197
while (iter.hasNext()) {
174-
totalRows += iter.next().<DoubleArray>column("close").length();
198+
totalRows += iter.next().<LongArray>column("volume").length();
175199
}
176200
}
177201
assertThat(totalRows).isEqualTo(TOTAL_ROWS);
178202
}
179-
180-
@Test
181-
void fileSizeComparison(@TempDir Path tmp) throws IOException {
182-
// Given
183-
Path javaFile = tmp.resolve("ohlc-java.vtx");
184-
Path jniFile = tmp.resolve("ohlc-jni.vtx");
185-
List<OhlcGenerator.OhlcBatch> batches = OhlcGenerator.generate(TOTAL_ROWS, BATCH_SIZE);
186-
187-
// When
188-
writeJava(javaFile, batches);
189-
writeJni(jniFile, batches);
190-
191-
long javaSize = Files.size(javaFile);
192-
long jniSize = Files.size(jniFile);
193-
194-
// Then
195-
System.out.printf("[WriteFileSizeIntegrationTest] %d rows: Java=%,d bytes (%.1f MB) JNI=%,d bytes (%.1f MB) ratio=%.2fx%n",
196-
TOTAL_ROWS,
197-
javaSize, javaSize / 1_048_576.0,
198-
jniSize, jniSize / 1_048_576.0,
199-
(double) javaSize / jniSize);
200-
201-
assertThat(javaSize).isPositive();
202-
assertThat(jniSize).isPositive();
203-
// Both writers use cascading(3): ALP+bitpacking for F64, delta+bitpacking for I64.
204-
// Bound < 2× catches regressions while tolerating minor encoding strategy differences.
205-
assertThat(javaSize).isLessThan(jniSize * 2);
206-
}
207203
}

integration/src/test/java/io/github/dfa1/vortex/integration/VortexVsCsvFileSizeTest.java

Lines changed: 0 additions & 91 deletions
This file was deleted.

0 commit comments

Comments
 (0)