Skip to content

Commit ec1ec64

Browse files
dfa1claude
andcommitted
test: verify random-order getLong() access is correct for key encodings
Catches bugs where value[N] accidentally depends on value[N-1] in encode or decode — e.g. an unapplied delta or a stateful cursor. Tests Primitive, BitpackedU64, and FrameOfReference. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 02e742f commit ec1ec64

1 file changed

Lines changed: 72 additions & 0 deletions

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package io.github.dfa1.vortex.encoding;
2+
3+
import io.github.dfa1.vortex.core.array.Array;
4+
import io.github.dfa1.vortex.core.array.LongArray;
5+
import org.junit.jupiter.params.ParameterizedTest;
6+
import org.junit.jupiter.params.provider.Arguments;
7+
import org.junit.jupiter.params.provider.MethodSource;
8+
9+
import java.util.Random;
10+
import java.util.stream.Stream;
11+
12+
import static org.assertj.core.api.Assertions.assertThat;
13+
14+
/// Verifies that decoded arrays support correct random-order element access.
15+
///
16+
/// The O(1) random-access claim in docs/explanation.md holds only if every
17+
/// position decodes independently. This test catches encoder bugs where
18+
/// value[N] accidentally depends on value[N-1] — e.g. an unapplied delta,
19+
/// an off-by-one in residual accumulation, or a stateful read cursor that
20+
/// advances forward and gives wrong values when accessed out of order.
21+
///
22+
/// Data: {@code value[i] = i * 1_000_003 + 7} — every position unique,
23+
/// prime multiplier prevents accidental aliasing.
24+
/// Access orders: reverse (N-1…0) and seeded random — the combination
25+
/// catches symmetric bugs that reverse alone might miss.
26+
class RandomAccessTest {
27+
28+
private static final int N = 1024;
29+
private static final long SEED = 0xDEADBEEFL;
30+
31+
static Stream<Arguments> encodings() {
32+
return Stream.of(
33+
Arguments.of("Primitive", new PrimitiveEncoding(), DTypes.I64),
34+
Arguments.of("BitpackedU64", new BitpackedEncoding(), DTypes.U64),
35+
Arguments.of("FrameOfReference", new FrameOfReferenceEncoding(), DTypes.I64)
36+
);
37+
}
38+
39+
@ParameterizedTest(name = "{0}")
40+
@MethodSource("encodings")
41+
void randomOrderAccess_matchesForwardOrder(String name, Encoding sut, DType dtype) {
42+
// Given — unique value per position, no two rows the same
43+
long[] original = new long[N];
44+
for (int i = 0; i < N; i++) {
45+
original[i] = (long) i * 1_000_003L + 7L;
46+
}
47+
48+
EncodingRegistry registry = TestRegistry.withPrimitive(sut);
49+
EncodeResult encoded = sut.encode(dtype, original);
50+
DecodeContext ctx = EncodeTestHelper.toDecodeContext(encoded, N, dtype, registry);
51+
52+
// When
53+
Array array = sut.decode(ctx);
54+
LongArray result = (LongArray) array;
55+
56+
// Then — reverse order
57+
for (int i = N - 1; i >= 0; i--) {
58+
assertThat(result.getLong(i))
59+
.as("reverse access at index %d", i)
60+
.isEqualTo(original[i]);
61+
}
62+
63+
// Then — random order (seeded for reproducibility)
64+
Random rng = new Random(SEED);
65+
for (int check = 0; check < N; check++) {
66+
int i = rng.nextInt(N);
67+
assertThat(result.getLong(i))
68+
.as("random access at index %d", i)
69+
.isEqualTo(original[i]);
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)