From 384160286b9c274aedda087af5ff1ca4761c7b27 Mon Sep 17 00:00:00 2001 From: Davide Angelocola Date: Fri, 19 Jun 2026 22:01:03 +0200 Subject: [PATCH] test(reader): direct coverage for Offset* views + RleArrays OffsetBool/Int/Long/Double were only covered incidentally through the RLE decode path; add direct slice tests alongside the existing Byte/Short/Float ones. Add RleArraysTest for chunkValueCount (middle-chunk, last-chunk, and firstOffset-subtraction branches). Co-Authored-By: Claude Opus 4.8 --- .../vortex/reader/array/OffsetArrayTest.java | 101 ++++++++++++++++++ .../vortex/reader/array/RleArraysTest.java | 42 ++++++++ 2 files changed, 143 insertions(+) create mode 100644 reader/src/test/java/io/github/dfa1/vortex/reader/array/RleArraysTest.java diff --git a/reader/src/test/java/io/github/dfa1/vortex/reader/array/OffsetArrayTest.java b/reader/src/test/java/io/github/dfa1/vortex/reader/array/OffsetArrayTest.java index 15ca44f6..197d33df 100644 --- a/reader/src/test/java/io/github/dfa1/vortex/reader/array/OffsetArrayTest.java +++ b/reader/src/test/java/io/github/dfa1/vortex/reader/array/OffsetArrayTest.java @@ -140,6 +140,107 @@ void foldReducesSlicedRange() { } } + @Nested + class Bool { + @Test + void getBooleanShiftsByOffset() { + try (Arena arena = Arena.ofConfined()) { + // Given — packed bits [F, T, T, F] + BoolArray inner = boolArray(arena, false, true, true, false); + var sut = new OffsetBoolArray(new DType.Bool(false), 2, inner, 1L); + + // When / Then + assertThat(sut.getBoolean(0)).isTrue(); // inner[1] + assertThat(sut.getBoolean(1)).isTrue(); // inner[2] + assertThat(sut.length()).isEqualTo(2); + } + } + } + + @Nested + class Int { + @Test + void getIntShiftsByOffset() { + try (Arena arena = Arena.ofConfined()) { + // Given + IntArray inner = intArray(arena, 10, 20, 30, 40); + var sut = new OffsetIntArray(new DType.Primitive(PType.I32, false), 2, inner, 2L); + + // When / Then + assertThat(sut.getInt(0)).isEqualTo(30); + assertThat(sut.getInt(1)).isEqualTo(40); + } + } + } + + @Nested + class Long { + @Test + void getLongShiftsByOffset() { + try (Arena arena = Arena.ofConfined()) { + // Given + LongArray inner = longArray(arena, 5L, 6L, 7L, 8L); + var sut = new OffsetLongArray(new DType.Primitive(PType.I64, false), 2, inner, 1L); + + // When / Then + assertThat(sut.getLong(0)).isEqualTo(6L); + assertThat(sut.getLong(1)).isEqualTo(7L); + } + } + } + + @Nested + class Double { + @Test + void getDoubleShiftsByOffset() { + try (Arena arena = Arena.ofConfined()) { + // Given + DoubleArray inner = doubleArray(arena, 1.5, 2.5, 3.5); + var sut = new OffsetDoubleArray(new DType.Primitive(PType.F64, false), 2, inner, 1L); + + // When / Then + assertThat(sut.getDouble(0)).isEqualTo(2.5); + assertThat(sut.getDouble(1)).isEqualTo(3.5); + } + } + } + + private static BoolArray boolArray(Arena arena, boolean... vs) { + MemorySegment seg = arena.allocate((vs.length + 7) / 8); + for (int i = 0; i < vs.length; i++) { + if (vs[i]) { + long bi = i >>> 3; + byte b = seg.get(ValueLayout.JAVA_BYTE, bi); + seg.set(ValueLayout.JAVA_BYTE, bi, (byte) ((b & 0xff) | (1 << (i & 7)))); + } + } + return new MaterializedBoolArray(new DType.Bool(false), vs.length, seg.asReadOnly()); + } + + private static IntArray intArray(Arena arena, int... vs) { + MemorySegment seg = arena.allocate(vs.length * 4L, 4); + for (int i = 0; i < vs.length; i++) { + seg.setAtIndex(ValueLayout.JAVA_INT, i, vs[i]); + } + return new MaterializedIntArray(new DType.Primitive(PType.I32, false), vs.length, seg.asReadOnly()); + } + + private static LongArray longArray(Arena arena, long... vs) { + MemorySegment seg = arena.allocate(vs.length * 8L, 8); + for (int i = 0; i < vs.length; i++) { + seg.setAtIndex(ValueLayout.JAVA_LONG, i, vs[i]); + } + return new MaterializedLongArray(new DType.Primitive(PType.I64, false), vs.length, seg.asReadOnly()); + } + + private static DoubleArray doubleArray(Arena arena, double... vs) { + MemorySegment seg = arena.allocate(vs.length * 8L, 8); + for (int i = 0; i < vs.length; i++) { + seg.setAtIndex(ValueLayout.JAVA_DOUBLE, i, vs[i]); + } + return new MaterializedDoubleArray(new DType.Primitive(PType.F64, false), vs.length, seg.asReadOnly()); + } + private static ByteArray byteArray(Arena arena, byte... vs) { MemorySegment seg = arena.allocate(vs.length, 1); for (int i = 0; i < vs.length; i++) { diff --git a/reader/src/test/java/io/github/dfa1/vortex/reader/array/RleArraysTest.java b/reader/src/test/java/io/github/dfa1/vortex/reader/array/RleArraysTest.java new file mode 100644 index 00000000..516aaea4 --- /dev/null +++ b/reader/src/test/java/io/github/dfa1/vortex/reader/array/RleArraysTest.java @@ -0,0 +1,42 @@ +package io.github.dfa1.vortex.reader.array; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class RleArraysTest { + + @Test + void chunkValueCount_middleChunk_usesNextOffset() { + // Given — two chunks, pool [0,3,5); firstOffset 0 + long[] offsets = {0, 3}; + + // When + int count = RleArrays.chunkValueCount(0, 2, offsets, 0L, 5L); + + // Then — offsets[1] - offsets[0] = 3 + assertThat(count).isEqualTo(3); + } + + @Test + void chunkValueCount_lastChunk_usesValuesLen() { + // Given — last chunk falls back to the total pool length + long[] offsets = {0, 3}; + + // When + int count = RleArrays.chunkValueCount(1, 2, offsets, 0L, 5L); + + // Then — valuesLen(5) - offsets[1](3) = 2 + assertThat(count).isEqualTo(2); + } + + @Test + void chunkValueCount_subtractsFirstOffset() { + // Given — pool origin shifted by firstOffset 10 + long[] offsets = {10, 13}; + + // When / Then — both branches still measure the local span + assertThat(RleArrays.chunkValueCount(0, 2, offsets, 10L, 5L)).isEqualTo(3); + assertThat(RleArrays.chunkValueCount(1, 2, offsets, 10L, 5L)).isEqualTo(2); + } +}