Skip to content

Commit a61e321

Browse files
dfa1claude
andcommitted
test(reader): full coverage for LazyRle*Array families
Add ByteDispatch/ShortDispatch (were ~19%) and close the remaining Long/Int gaps to 100% line + branch: constant-run, empty-chunk, per-row indexed lookup with out-of-range clamp, signed/unsigned getInt widening, multi-chunk fold, and the fold/forEach loop exiting on length before chunk exhaustion. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 32a35e0 commit a61e321

1 file changed

Lines changed: 209 additions & 0 deletions

File tree

reader/src/test/java/io/github/dfa1/vortex/reader/array/LazyRleArrayTest.java

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ class LazyRleArrayTest {
1818

1919
private static final DType I64 = new DType.Primitive(PType.I64, false);
2020
private static final DType I32 = new DType.Primitive(PType.I32, false);
21+
private static final DType U8 = new DType.Primitive(PType.U8, false);
22+
private static final DType I8 = new DType.Primitive(PType.I8, false);
23+
private static final DType U16 = new DType.Primitive(PType.U16, false);
24+
private static final DType I16 = new DType.Primitive(PType.I16, false);
2125

2226
@Nested
2327
class LongDispatch {
@@ -99,6 +103,54 @@ void foldSumsCorrectly() {
99103
// 1024 * 7 + 2 * 11 = 7168 + 22 = 7190
100104
assertThat(sum).isEqualTo(7190L);
101105
}
106+
107+
@Test
108+
void getLongLookupIndexedClampAndEmpty() {
109+
// Given — indexed chunk; index[2] out of range clamps to last value
110+
int[] idx = new int[1024];
111+
idx[0] = 0;
112+
idx[1] = 1;
113+
idx[2] = 9;
114+
var x = new LazyRleLongArray(I64, 3, new long[]{10L, 20L, 30L}, idx,
115+
new long[]{0L}, 0L, 3L, 1, 0);
116+
assertThat(x.getLong(0)).isEqualTo(10L);
117+
assertThat(x.getLong(1)).isEqualTo(20L);
118+
assertThat(x.getLong(2)).isEqualTo(30L); // clamped
119+
120+
// empty chunk → zero
121+
var e = new LazyRleLongArray(I64, 2, new long[0], new int[1024],
122+
new long[]{0L}, 0L, 0L, 1, 0);
123+
assertThat(e.getLong(0)).isZero();
124+
}
125+
126+
@Test
127+
void foldStopsWhenLengthSatisfiedBeforeChunksExhausted() {
128+
// Given — 2 chunks declared but length only spans 5 rows of chunk 0;
129+
// the fold loop must exit on `emitted < n`, not on chunk exhaustion
130+
var sut = new LazyRleLongArray(I64, 5, new long[]{1L, 2L}, new int[2048],
131+
new long[]{0L, 1L}, 0L, 2L, 2, 0);
132+
assertThat(sut.fold(0L, java.lang.Long::sum)).isEqualTo(5L);
133+
}
134+
135+
@Test
136+
void forEachIndexedClampAndEmptyChunk() {
137+
// indexed forEach with out-of-range index → clamps to last value
138+
int[] idx = new int[1024];
139+
idx[0] = 0;
140+
idx[1] = 9;
141+
var x = new LazyRleLongArray(I64, 2, new long[]{10L, 20L}, idx,
142+
new long[]{0L}, 0L, 2L, 1, 0);
143+
var seen = new ArrayList<java.lang.Long>();
144+
x.forEachLong(seen::add);
145+
assertThat(seen).containsExactly(10L, 20L);
146+
147+
// empty chunk via forEach → zeros
148+
var e = new LazyRleLongArray(I64, 2, new long[0], new int[1024],
149+
new long[]{0L}, 0L, 0L, 1, 0);
150+
var zeros = new ArrayList<java.lang.Long>();
151+
e.forEachLong(zeros::add);
152+
assertThat(zeros).containsExactly(0L, 0L);
153+
}
102154
}
103155

104156
@Nested
@@ -119,5 +171,162 @@ void intLookupAndForEach() {
119171
assertThat(sut.getInt(1)).isEqualTo(200);
120172
assertThat(sut.getInt(2)).isEqualTo(100);
121173
}
174+
175+
@Test
176+
void forEachFoldClampAndEmptyChunk() {
177+
// Given — values [1,2,3]; index[3] out of range → clamps to last (3)
178+
int[] indices = new int[1024];
179+
indices[0] = 0;
180+
indices[1] = 1;
181+
indices[2] = 2;
182+
indices[3] = 99;
183+
var sut = new LazyRleIntArray(I32, 4, new int[]{1, 2, 3}, indices,
184+
new long[]{0L}, 0L, 3L, 1, 0);
185+
186+
assertThat(sut.getInt(0)).isEqualTo(1);
187+
assertThat(sut.getInt(3)).isEqualTo(3); // getInt lookup clamp path
188+
var seen = new ArrayList<java.lang.Integer>();
189+
sut.forEachInt(seen::add);
190+
assertThat(seen).containsExactly(1, 2, 3, 3);
191+
assertThat(sut.fold(0, Integer::sum)).isEqualTo(9);
192+
193+
// empty chunk (0 distinct values) → zero, via getInt, forEach and fold
194+
var empty = new LazyRleIntArray(I32, 2, new int[0], new int[1024],
195+
new long[]{0L}, 0L, 0L, 1, 0);
196+
assertThat(empty.getInt(0)).isZero();
197+
var zeros = new ArrayList<java.lang.Integer>();
198+
empty.forEachInt(zeros::add);
199+
assertThat(zeros).containsExactly(0, 0);
200+
assertThat(empty.fold(0, Integer::sum)).isZero();
201+
}
202+
203+
@Test
204+
void getIntConstantRunFastPath() {
205+
// single distinct value → getInt hits the numChunkValues == 1 lookup branch
206+
var sut = new LazyRleIntArray(I32, 3, new int[]{42}, new int[1024],
207+
new long[]{0L}, 0L, 1L, 1, 0);
208+
assertThat(sut.getInt(0)).isEqualTo(42);
209+
assertThat(sut.getInt(2)).isEqualTo(42);
210+
}
211+
212+
@Test
213+
void foldStopsWhenLengthSatisfiedBeforeChunksExhausted() {
214+
// 2 chunks declared, length spans only 5 rows of chunk 0 → loop exits on emitted < n
215+
var sut = new LazyRleIntArray(I32, 5, new int[]{1, 2}, new int[2048],
216+
new long[]{0L, 1L}, 0L, 2L, 2, 0);
217+
assertThat(sut.fold(0, Integer::sum)).isEqualTo(5);
218+
}
219+
}
220+
221+
@Nested
222+
class ByteDispatch {
223+
224+
@Test
225+
void lookupConstantEmptyIndexedClamp() {
226+
// constant run (1 distinct)
227+
var c = new LazyRleByteArray(U8, 3, new byte[]{7}, new int[1024],
228+
new long[]{0L}, 0L, 1L, 1, 0, true);
229+
assertThat(c.getByte(0)).isEqualTo((byte) 7);
230+
assertThat(c.fold(0L, Long::sum)).isEqualTo(21L);
231+
232+
// empty chunk (0 distinct) → zero
233+
var e = new LazyRleByteArray(U8, 2, new byte[0], new int[1024],
234+
new long[]{0L}, 0L, 0L, 1, 0, true);
235+
assertThat(e.getByte(0)).isZero();
236+
assertThat(e.fold(0L, Long::sum)).isZero();
237+
238+
// indexed with out-of-range index → clamps to last value
239+
int[] idx = new int[1024];
240+
idx[0] = 0;
241+
idx[1] = 1;
242+
idx[2] = 2;
243+
idx[3] = 99;
244+
var x = new LazyRleByteArray(U8, 4, new byte[]{1, 2, 3}, idx,
245+
new long[]{0L}, 0L, 3L, 1, 0, true);
246+
assertThat(x.getByte(0)).isEqualTo((byte) 1);
247+
assertThat(x.getByte(3)).isEqualTo((byte) 3); // clamped
248+
assertThat(x.fold(0L, Long::sum)).isEqualTo(9L);
249+
}
250+
251+
@Test
252+
void getIntWidensSignedAndUnsigned() {
253+
int[] idx = new int[1024];
254+
var u = new LazyRleByteArray(U8, 1, new byte[]{(byte) 0xF0}, idx,
255+
new long[]{0L}, 0L, 1L, 1, 0, true);
256+
assertThat(u.getInt(0)).isEqualTo(240);
257+
var s = new LazyRleByteArray(I8, 1, new byte[]{(byte) 0xF0}, idx,
258+
new long[]{0L}, 0L, 1L, 1, 0, false);
259+
assertThat(s.getInt(0)).isEqualTo(-16);
260+
}
261+
262+
@Test
263+
void multiChunkFoldSignedWiden() {
264+
// two constant chunks: 1024×1 + 2×2; signed widening path in foldChunk
265+
var sut = new LazyRleByteArray(I8, 1026, new byte[]{1, 2}, new int[2048],
266+
new long[]{0L, 1L}, 0L, 2L, 2, 0, false);
267+
assertThat(sut.getByte(1024)).isEqualTo((byte) 2);
268+
assertThat(sut.fold(0L, Long::sum)).isEqualTo(1024L + 4L);
269+
}
270+
271+
@Test
272+
void foldStopsWhenLengthSatisfiedBeforeChunksExhausted() {
273+
var sut = new LazyRleByteArray(U8, 5, new byte[]{1, 2}, new int[2048],
274+
new long[]{0L, 1L}, 0L, 2L, 2, 0, true);
275+
assertThat(sut.fold(0L, Long::sum)).isEqualTo(5L);
276+
}
277+
}
278+
279+
@Nested
280+
class ShortDispatch {
281+
282+
@Test
283+
void lookupConstantEmptyIndexedClamp() {
284+
var c = new LazyRleShortArray(U16, 3, new short[]{7}, new int[1024],
285+
new long[]{0L}, 0L, 1L, 1, 0, true);
286+
assertThat(c.getShort(0)).isEqualTo((short) 7);
287+
assertThat(c.fold(0L, Long::sum)).isEqualTo(21L);
288+
289+
var e = new LazyRleShortArray(U16, 2, new short[0], new int[1024],
290+
new long[]{0L}, 0L, 0L, 1, 0, true);
291+
assertThat(e.getShort(0)).isZero();
292+
assertThat(e.fold(0L, Long::sum)).isZero();
293+
294+
int[] idx = new int[1024];
295+
idx[0] = 0;
296+
idx[1] = 1;
297+
idx[2] = 2;
298+
idx[3] = 99;
299+
var x = new LazyRleShortArray(U16, 4, new short[]{1, 2, 3}, idx,
300+
new long[]{0L}, 0L, 3L, 1, 0, true);
301+
assertThat(x.getShort(0)).isEqualTo((short) 1);
302+
assertThat(x.getShort(3)).isEqualTo((short) 3); // clamped
303+
assertThat(x.fold(0L, Long::sum)).isEqualTo(9L);
304+
}
305+
306+
@Test
307+
void getIntWidensSignedAndUnsigned() {
308+
int[] idx = new int[1024];
309+
var u = new LazyRleShortArray(U16, 1, new short[]{(short) 0xFF00}, idx,
310+
new long[]{0L}, 0L, 1L, 1, 0, true);
311+
assertThat(u.getInt(0)).isEqualTo(0xFF00);
312+
var s = new LazyRleShortArray(I16, 1, new short[]{(short) 0xFF00}, idx,
313+
new long[]{0L}, 0L, 1L, 1, 0, false);
314+
assertThat(s.getInt(0)).isEqualTo((int) (short) 0xFF00);
315+
}
316+
317+
@Test
318+
void multiChunkFoldSignedWiden() {
319+
var sut = new LazyRleShortArray(I16, 1026, new short[]{1, 2}, new int[2048],
320+
new long[]{0L, 1L}, 0L, 2L, 2, 0, false);
321+
assertThat(sut.getShort(1024)).isEqualTo((short) 2);
322+
assertThat(sut.fold(0L, Long::sum)).isEqualTo(1024L + 4L);
323+
}
324+
325+
@Test
326+
void foldStopsWhenLengthSatisfiedBeforeChunksExhausted() {
327+
var sut = new LazyRleShortArray(U16, 5, new short[]{1, 2}, new int[2048],
328+
new long[]{0L, 1L}, 0L, 2L, 2, 0, true);
329+
assertThat(sut.fold(0L, Long::sum)).isEqualTo(5L);
330+
}
122331
}
123332
}

0 commit comments

Comments
 (0)