Skip to content

Commit 44f62ff

Browse files
dfa1claude
andcommitted
test: reorganize encoding tests with @nested Encode/Decode groups
All 19 encoding test classes now use @nested to group tests by feature: - Encode (round-trips, encode-side assertions) and Decode (decode-only tests) nested classes per CLAUDE.md guidelines - Private helpers moved to end of the class they serve - Static method-source providers moved inside the nested class that uses them Also discovered SequenceEncoding.encode() is now implemented; updated its test class to cover the encode path with round-trip tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent aad004f commit 44f62ff

20 files changed

Lines changed: 2045 additions & 1900 deletions

core/src/test/java/io/github/dfa1/vortex/encoding/AlpEncodingTest.java

Lines changed: 240 additions & 234 deletions
Large diffs are not rendered by default.

core/src/test/java/io/github/dfa1/vortex/encoding/BitpackedEncodingPatchesTest.java

Lines changed: 67 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.github.dfa1.vortex.core.ArrayStats;
77
import io.github.dfa1.vortex.core.DType;
88
import io.github.dfa1.vortex.core.PType;
9+
import org.junit.jupiter.api.Nested;
910
import org.junit.jupiter.api.Test;
1011

1112
import java.lang.foreign.Arena;
@@ -21,67 +22,71 @@ class BitpackedEncodingPatchesTest {
2122
private static final DType I32_DTYPE = new DType.Primitive(PType.I32, false);
2223
private static final ValueLayout.OfInt LE_INT = ValueLayout.JAVA_INT_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN);
2324

24-
@Test
25-
void decode_appliesPatches_overridingBitPackedValues() {
26-
// Given — bit-pack [10, 20, 30, 40, 50] via the production encoder (bitWidth = 6),
27-
// then attach synthetic patches metadata that rewrites indices [1, 3] with [777, 999].
28-
int[] base = {10, 20, 30, 40, 50};
29-
BitpackedEncoding sut = new BitpackedEncoding();
30-
EncodeResult packed = sut.encode(I32_DTYPE, base);
31-
32-
MemorySegment packedSeg = packed.buffers().getFirst();
33-
byte[] packedBytes = packedSeg.toArray(java.lang.foreign.ValueLayout.JAVA_BYTE);
34-
35-
// Build new BitPackedMetadata that re-uses the packed bytes but advertises patches.
36-
EncodingProtos.PatchesMetadata patches = EncodingProtos.PatchesMetadata.newBuilder()
37-
.setLen(2)
38-
.setOffset(0)
39-
.setIndicesPtype(DTypeProtos.PType.U32)
40-
.build();
41-
byte[] metaBytes = EncodingProtos.BitPackedMetadata.newBuilder()
42-
.setBitWidth(6) // matches encoder's choice for max=50
43-
.setOffset(0)
44-
.setPatches(patches)
45-
.build()
46-
.toByteArray();
47-
48-
byte[] idxBuf = new byte[2 * 4];
49-
ByteBuffer.wrap(idxBuf).order(ByteOrder.LITTLE_ENDIAN).putInt(1).putInt(3);
50-
byte[] valBuf = new byte[2 * 4];
51-
ByteBuffer.wrap(valBuf).order(ByteOrder.LITTLE_ENDIAN).putInt(777).putInt(999);
52-
53-
ArrayNode idxNode = new ArrayNode(EncodingId.VORTEX_PRIMITIVE, null,
54-
new ArrayNode[0], new int[]{1}, ArrayStats.empty());
55-
ArrayNode valNode = new ArrayNode(EncodingId.VORTEX_PRIMITIVE, null,
56-
new ArrayNode[0], new int[]{2}, ArrayStats.empty());
57-
ArrayNode bpNode = new ArrayNode(EncodingId.FASTLANES_BITPACKED,
58-
ByteBuffer.wrap(metaBytes),
59-
new ArrayNode[]{idxNode, valNode},
60-
new int[]{0},
61-
ArrayStats.empty());
62-
63-
MemorySegment[] segments = {
64-
MemorySegment.ofArray(packedBytes),
65-
MemorySegment.ofArray(idxBuf),
66-
MemorySegment.ofArray(valBuf)
67-
};
68-
69-
EncodingRegistry registry = EncodingRegistry.empty();
70-
registry.register(new BitpackedEncoding());
71-
registry.register(new PrimitiveEncoding());
72-
73-
DecodeContext ctx = new DecodeContext(
74-
bpNode, I32_DTYPE, base.length, segments, registry, Arena.global());
75-
76-
// When
77-
Array result = sut.decode(ctx);
78-
79-
// Then
80-
assertThat(result.length()).isEqualTo(base.length);
81-
assertThat(result.buffer(0).get(LE_INT, 0L)).isEqualTo(10);
82-
assertThat(result.buffer(0).get(LE_INT, 4L)).isEqualTo(777);
83-
assertThat(result.buffer(0).get(LE_INT, 8L)).isEqualTo(30);
84-
assertThat(result.buffer(0).get(LE_INT, 12L)).isEqualTo(999);
85-
assertThat(result.buffer(0).get(LE_INT, 16L)).isEqualTo(50);
25+
@Nested
26+
class Decode {
27+
28+
@Test
29+
void decode_appliesPatches_overridingBitPackedValues() {
30+
// Given — bit-pack [10, 20, 30, 40, 50] via the production encoder (bitWidth = 6),
31+
// then attach synthetic patches metadata that rewrites indices [1, 3] with [777, 999].
32+
int[] base = {10, 20, 30, 40, 50};
33+
BitpackedEncoding sut = new BitpackedEncoding();
34+
EncodeResult packed = sut.encode(I32_DTYPE, base);
35+
36+
MemorySegment packedSeg = packed.buffers().getFirst();
37+
byte[] packedBytes = packedSeg.toArray(java.lang.foreign.ValueLayout.JAVA_BYTE);
38+
39+
// Build new BitPackedMetadata that re-uses the packed bytes but advertises patches.
40+
EncodingProtos.PatchesMetadata patches = EncodingProtos.PatchesMetadata.newBuilder()
41+
.setLen(2)
42+
.setOffset(0)
43+
.setIndicesPtype(DTypeProtos.PType.U32)
44+
.build();
45+
byte[] metaBytes = EncodingProtos.BitPackedMetadata.newBuilder()
46+
.setBitWidth(6) // matches encoder's choice for max=50
47+
.setOffset(0)
48+
.setPatches(patches)
49+
.build()
50+
.toByteArray();
51+
52+
byte[] idxBuf = new byte[2 * 4];
53+
ByteBuffer.wrap(idxBuf).order(ByteOrder.LITTLE_ENDIAN).putInt(1).putInt(3);
54+
byte[] valBuf = new byte[2 * 4];
55+
ByteBuffer.wrap(valBuf).order(ByteOrder.LITTLE_ENDIAN).putInt(777).putInt(999);
56+
57+
ArrayNode idxNode = new ArrayNode(EncodingId.VORTEX_PRIMITIVE, null,
58+
new ArrayNode[0], new int[]{1}, ArrayStats.empty());
59+
ArrayNode valNode = new ArrayNode(EncodingId.VORTEX_PRIMITIVE, null,
60+
new ArrayNode[0], new int[]{2}, ArrayStats.empty());
61+
ArrayNode bpNode = new ArrayNode(EncodingId.FASTLANES_BITPACKED,
62+
ByteBuffer.wrap(metaBytes),
63+
new ArrayNode[]{idxNode, valNode},
64+
new int[]{0},
65+
ArrayStats.empty());
66+
67+
MemorySegment[] segments = {
68+
MemorySegment.ofArray(packedBytes),
69+
MemorySegment.ofArray(idxBuf),
70+
MemorySegment.ofArray(valBuf)
71+
};
72+
73+
EncodingRegistry registry = EncodingRegistry.empty();
74+
registry.register(new BitpackedEncoding());
75+
registry.register(new PrimitiveEncoding());
76+
77+
DecodeContext ctx = new DecodeContext(
78+
bpNode, I32_DTYPE, base.length, segments, registry, Arena.global());
79+
80+
// When
81+
Array result = sut.decode(ctx);
82+
83+
// Then
84+
assertThat(result.length()).isEqualTo(base.length);
85+
assertThat(result.buffer(0).get(LE_INT, 0L)).isEqualTo(10);
86+
assertThat(result.buffer(0).get(LE_INT, 4L)).isEqualTo(777);
87+
assertThat(result.buffer(0).get(LE_INT, 8L)).isEqualTo(30);
88+
assertThat(result.buffer(0).get(LE_INT, 12L)).isEqualTo(999);
89+
assertThat(result.buffer(0).get(LE_INT, 16L)).isEqualTo(50);
90+
}
8691
}
8792
}

core/src/test/java/io/github/dfa1/vortex/encoding/BitpackedEncodingTest.java

Lines changed: 57 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.github.dfa1.vortex.core.DType;
44
import io.github.dfa1.vortex.core.PType;
55
import io.github.dfa1.vortex.core.array.Array;
6+
import org.junit.jupiter.api.Nested;
67
import org.junit.jupiter.params.ParameterizedTest;
78
import org.junit.jupiter.params.provider.Arguments;
89
import org.junit.jupiter.params.provider.MethodSource;
@@ -19,65 +20,69 @@ class BitpackedEncodingTest {
1920
private static final DType U32_DTYPE = new DType.Primitive(PType.U32, false);
2021
private static final DType U64_DTYPE = new DType.Primitive(PType.U64, false);
2122

22-
static Stream<Arguments> u32Arrays() {
23-
return Stream.of(
24-
Arguments.of("empty", new int[]{}),
25-
Arguments.of("single", new int[]{0}),
26-
Arguments.of("all-zeros", new int[]{0, 0, 0, 0, 0}),
27-
Arguments.of("small-values", new int[]{1, 2, 3, 4, 5, 6, 7}),
28-
Arguments.of("mixed", new int[]{0, 7, 63, 255, 1023, 65535}),
29-
Arguments.of("max-unsigned", new int[]{-1, -1, -1}) // 0xFFFFFFFF
30-
);
31-
}
32-
33-
static Stream<Arguments> u64Arrays() {
34-
return Stream.of(
35-
Arguments.of("empty", new long[]{}),
36-
Arguments.of("single", new long[]{0L}),
37-
Arguments.of("small-values", new long[]{1L, 2L, 3L, 4L, 5L}),
38-
Arguments.of("large-values", new long[]{0L, 0xFFFFL, 0xFFFFFFL, 0xFFFFFFFFL})
39-
);
40-
}
23+
@Nested
24+
class Encode {
4125

42-
@ParameterizedTest(name = "{0}")
43-
@MethodSource("u32Arrays")
44-
void encodeDecode_u32_isLossless(String name, int[] data) {
45-
// Given
46-
var sut = new BitpackedEncoding();
47-
EncodingRegistry registry = EncodingRegistry.empty();
48-
registry.register(sut);
26+
@ParameterizedTest(name = "{0}")
27+
@MethodSource("u32Arrays")
28+
void encodeDecode_u32_isLossless(String name, int[] data) {
29+
// Given
30+
var sut = new BitpackedEncoding();
31+
EncodingRegistry registry = EncodingRegistry.empty();
32+
registry.register(sut);
4933

50-
// When
51-
EncodeResult encoded = sut.encode(U32_DTYPE, data);
52-
DecodeContext ctx = EncodeTestHelper.toDecodeContext(encoded, data.length, U32_DTYPE, registry);
53-
Array result = sut.decode(ctx);
34+
// When
35+
EncodeResult encoded = sut.encode(U32_DTYPE, data);
36+
DecodeContext ctx = EncodeTestHelper.toDecodeContext(encoded, data.length, U32_DTYPE, registry);
37+
Array result = sut.decode(ctx);
5438

55-
// Then
56-
assertThat(result.length()).isEqualTo(data.length);
57-
var le = ValueLayout.JAVA_INT_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN);
58-
for (int i = 0; i < data.length; i++) {
59-
assertThat(result.buffer(0).get(le, (long) i * 4)).as("index %d", i).isEqualTo(data[i]);
39+
// Then
40+
assertThat(result.length()).isEqualTo(data.length);
41+
var le = ValueLayout.JAVA_INT_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN);
42+
for (int i = 0; i < data.length; i++) {
43+
assertThat(result.buffer(0).get(le, (long) i * 4)).as("index %d", i).isEqualTo(data[i]);
44+
}
6045
}
61-
}
6246

63-
@ParameterizedTest(name = "{0}")
64-
@MethodSource("u64Arrays")
65-
void encodeDecode_u64_isLossless(String name, long[] data) {
66-
// Given
67-
var sut = new BitpackedEncoding();
68-
EncodingRegistry registry = EncodingRegistry.empty();
69-
registry.register(sut);
47+
@ParameterizedTest(name = "{0}")
48+
@MethodSource("u64Arrays")
49+
void encodeDecode_u64_isLossless(String name, long[] data) {
50+
// Given
51+
var sut = new BitpackedEncoding();
52+
EncodingRegistry registry = EncodingRegistry.empty();
53+
registry.register(sut);
7054

71-
// When
72-
EncodeResult encoded = sut.encode(U64_DTYPE, data);
73-
DecodeContext ctx = EncodeTestHelper.toDecodeContext(encoded, data.length, U64_DTYPE, registry);
74-
Array result = sut.decode(ctx);
55+
// When
56+
EncodeResult encoded = sut.encode(U64_DTYPE, data);
57+
DecodeContext ctx = EncodeTestHelper.toDecodeContext(encoded, data.length, U64_DTYPE, registry);
58+
Array result = sut.decode(ctx);
59+
60+
// Then
61+
assertThat(result.length()).isEqualTo(data.length);
62+
var le = ValueLayout.JAVA_LONG_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN);
63+
for (int i = 0; i < data.length; i++) {
64+
assertThat(result.buffer(0).get(le, (long) i * 8)).as("index %d", i).isEqualTo(data[i]);
65+
}
66+
}
67+
68+
static Stream<Arguments> u32Arrays() {
69+
return Stream.of(
70+
Arguments.of("empty", new int[]{}),
71+
Arguments.of("single", new int[]{0}),
72+
Arguments.of("all-zeros", new int[]{0, 0, 0, 0, 0}),
73+
Arguments.of("small-values", new int[]{1, 2, 3, 4, 5, 6, 7}),
74+
Arguments.of("mixed", new int[]{0, 7, 63, 255, 1023, 65535}),
75+
Arguments.of("max-unsigned", new int[]{-1, -1, -1}) // 0xFFFFFFFF
76+
);
77+
}
7578

76-
// Then
77-
assertThat(result.length()).isEqualTo(data.length);
78-
var le = ValueLayout.JAVA_LONG_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN);
79-
for (int i = 0; i < data.length; i++) {
80-
assertThat(result.buffer(0).get(le, (long) i * 8)).as("index %d", i).isEqualTo(data[i]);
79+
static Stream<Arguments> u64Arrays() {
80+
return Stream.of(
81+
Arguments.of("empty", new long[]{}),
82+
Arguments.of("single", new long[]{0L}),
83+
Arguments.of("small-values", new long[]{1L, 2L, 3L, 4L, 5L}),
84+
Arguments.of("large-values", new long[]{0L, 0xFFFFL, 0xFFFFFFL, 0xFFFFFFFFL})
85+
);
8186
}
8287
}
8388
}

core/src/test/java/io/github/dfa1/vortex/encoding/BoolEncodingTest.java

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.github.dfa1.vortex.core.DType;
44
import io.github.dfa1.vortex.core.array.Array;
55
import io.github.dfa1.vortex.core.array.BoolArray;
6+
import org.junit.jupiter.api.Nested;
67
import org.junit.jupiter.params.ParameterizedTest;
78
import org.junit.jupiter.params.provider.MethodSource;
89

@@ -16,52 +17,56 @@ class BoolEncodingTest {
1617

1718
private static final DType BOOL_DTYPE = new DType.Bool(false);
1819

19-
static Stream<boolean[]> boolArrays() {
20-
return Stream.of(
21-
new boolean[]{},
22-
new boolean[]{false},
23-
new boolean[]{true},
24-
new boolean[]{false, true, false, true, false, true, false, true},
25-
new boolean[]{true, true, true, false, false, false, true, false, true},
26-
new boolean[]{false, false, false, false, false, false, false},
27-
new boolean[]{true, true, true, true, true, true, true, true, true}
28-
);
29-
}
20+
@Nested
21+
class Encode {
3022

31-
@ParameterizedTest
32-
@MethodSource("boolArrays")
33-
void encodeDecode_isLossless(boolean[] data) {
34-
// Given
35-
var sut = new BoolEncoding();
36-
EncodingRegistry registry = EncodingRegistry.empty();
37-
registry.register(sut);
23+
@ParameterizedTest
24+
@MethodSource("boolArrays")
25+
void encodeDecode_isLossless(boolean[] data) {
26+
// Given
27+
var sut = new BoolEncoding();
28+
EncodingRegistry registry = EncodingRegistry.empty();
29+
registry.register(sut);
3830

39-
// When
40-
EncodeResult encoded = sut.encode(BOOL_DTYPE, data);
41-
DecodeContext ctx = EncodeTestHelper.toDecodeContext(encoded, data.length, BOOL_DTYPE, registry);
42-
Array result = sut.decode(ctx);
31+
// When
32+
EncodeResult encoded = sut.encode(BOOL_DTYPE, data);
33+
DecodeContext ctx = EncodeTestHelper.toDecodeContext(encoded, data.length, BOOL_DTYPE, registry);
34+
Array result = sut.decode(ctx);
4335

44-
// Then
45-
assertThat(result).isInstanceOf(BoolArray.class);
46-
assertThat(result.length()).isEqualTo(data.length);
47-
for (int i = 0; i < data.length; i++) {
48-
byte byteVal = result.buffer(0).get(ValueLayout.JAVA_BYTE, i / 8);
49-
boolean decoded = ((byteVal >>> (i % 8)) & 1) == 1;
50-
assertThat(decoded).as("index %d", i).isEqualTo(data[i]);
36+
// Then
37+
assertThat(result).isInstanceOf(BoolArray.class);
38+
assertThat(result.length()).isEqualTo(data.length);
39+
for (int i = 0; i < data.length; i++) {
40+
byte byteVal = result.buffer(0).get(ValueLayout.JAVA_BYTE, i / 8);
41+
boolean decoded = ((byteVal >>> (i % 8)) & 1) == 1;
42+
assertThat(decoded).as("index %d", i).isEqualTo(data[i]);
43+
}
5144
}
52-
}
5345

54-
@ParameterizedTest
55-
@MethodSource("boolArrays")
56-
void encodedSize_isPackedBits(boolean[] data) {
57-
// Given
58-
var sut = new BoolEncoding();
46+
@ParameterizedTest
47+
@MethodSource("boolArrays")
48+
void encodedSize_isPackedBits(boolean[] data) {
49+
// Given
50+
var sut = new BoolEncoding();
5951

60-
// When
61-
EncodeResult encoded = sut.encode(BOOL_DTYPE, data);
52+
// When
53+
EncodeResult encoded = sut.encode(BOOL_DTYPE, data);
6254

63-
// Then — bit-packed: ceiling(n/8) bytes, always ≤ n bytes raw
64-
long totalBytes = encoded.buffers().stream().mapToLong(java.lang.foreign.MemorySegment::byteSize).sum();
65-
assertThat(totalBytes).isEqualTo((long) (data.length + 7) / 8);
55+
// Then — bit-packed: ceiling(n/8) bytes, always ≤ n bytes raw
56+
long totalBytes = encoded.buffers().stream().mapToLong(java.lang.foreign.MemorySegment::byteSize).sum();
57+
assertThat(totalBytes).isEqualTo((long) (data.length + 7) / 8);
58+
}
59+
60+
static Stream<boolean[]> boolArrays() {
61+
return Stream.of(
62+
new boolean[]{},
63+
new boolean[]{false},
64+
new boolean[]{true},
65+
new boolean[]{false, true, false, true, false, true, false, true},
66+
new boolean[]{true, true, true, false, false, false, true, false, true},
67+
new boolean[]{false, false, false, false, false, false, false},
68+
new boolean[]{true, true, true, true, true, true, true, true, true}
69+
);
70+
}
6671
}
6772
}

0 commit comments

Comments
 (0)