|
20 | 20 | import org.junit.jupiter.api.Test; |
21 | 21 | import tools.jackson.databind.ObjectMapper; |
22 | 22 |
|
| 23 | +import java.util.HashMap; |
23 | 24 | import java.util.List; |
24 | 25 | import java.util.Map; |
25 | 26 |
|
@@ -177,10 +178,132 @@ void testSerializeX509ValidityMixedDates() throws Exception { |
177 | 178 | assertThat(seq.getObjectAt(1)).isInstanceOf(ASN1GeneralizedTime.class); |
178 | 179 | } |
179 | 180 |
|
| 181 | + // ----------------------------------------------------------------------- |
| 182 | + // bit_map — 4 combinations of bit_order × byte_order |
| 183 | + // |
| 184 | + // byte_order and bit_order describe the INPUT data layout. |
| 185 | + // Output is always big-endian MSB-first (standard Java / network byte order). |
| 186 | + // |
| 187 | + // Logical value: [0xFE, 0xCA] (big-endian output) |
| 188 | + // 0xFE = 1111_1110, 0xCA = 1100_1010 |
| 189 | + // |
| 190 | + // ----------------------------------------------------------------------- |
| 191 | + |
| 192 | + @Test |
| 193 | + void testBitMapMsbFirstBigEndian() throws Exception { |
| 194 | + // Input already in big-endian MSB-first order — no reordering needed. |
| 195 | + // group[0]=0xFE=[1,1,1,1,1,1,1,0] → bytes[0] |
| 196 | + // group[1]=0xCA=[1,1,0,0,1,0,1,0] → bytes[1] |
| 197 | + List<Boolean> bits = List.of( |
| 198 | + true, true, true, true, true, true, true, false, |
| 199 | + true, true, false, false, true, false, true, false); |
| 200 | + |
| 201 | + byte[] result = serializeBitMap("big_endian", "msb_first", bits); |
| 202 | + assertThat(result).isEqualTo(new byte[]{(byte) 0xFE, (byte) 0xCA}); |
| 203 | + } |
| 204 | + |
| 205 | + @Test |
| 206 | + void testBitMapMsbFirstLittleEndian() throws Exception { |
| 207 | + // Input is little-endian (LSB group first) MSB-first within each byte. |
| 208 | + // Serializer reverses byte groups to produce big-endian output. |
| 209 | + // group[0]=0xCA=[1,1,0,0,1,0,1,0] → reversed → bytes[1] |
| 210 | + // group[1]=0xFE=[1,1,1,1,1,1,1,0] → reversed → bytes[0] |
| 211 | + List<Boolean> bits = List.of( |
| 212 | + true, true, false, false, true, false, true, false, |
| 213 | + true, true, true, true, true, true, true, false); |
| 214 | + |
| 215 | + byte[] result = serializeBitMap("little_endian", "msb_first", bits); |
| 216 | + assertThat(result).isEqualTo(new byte[]{(byte) 0xFE, (byte) 0xCA}); |
| 217 | + } |
| 218 | + |
| 219 | + @Test |
| 220 | + void testBitMapLsbFirstBigEndian() throws Exception { |
| 221 | + // Input is big-endian, LSB-first within each byte. |
| 222 | + // 0xFE=1111_1110: bit0=0,bit1=1..bit7=1 → [F,T,T,T,T,T,T,T] |
| 223 | + // 0xCA=1100_1010: bit0=0,bit1=1,bit2=0,bit3=1,bit4=0,bit5=0,bit6=1,bit7=1 → [F,T,F,T,F,F,T,T] |
| 224 | + List<Boolean> bits = List.of( |
| 225 | + false, true, true, true, true, true, true, true, |
| 226 | + false, true, false, true, false, false, true, true); |
| 227 | + |
| 228 | + byte[] result = serializeBitMap("big_endian", "lsb_first", bits); |
| 229 | + assertThat(result).isEqualTo(new byte[]{(byte) 0xFE, (byte) 0xCA}); |
| 230 | + } |
| 231 | + |
| 232 | + @Test |
| 233 | + void testBitMapLsbFirstLittleEndian() throws Exception { |
| 234 | + // Input is little-endian LSB-first — both orderings reversed vs output. |
| 235 | + // group[0]=0xCA(LSB first)=[F,T,F,T,F,F,T,T] → reversed → bytes[1] |
| 236 | + // group[1]=0xFE(LSB first)=[F,T,T,T,T,T,T,T] → reversed → bytes[0] |
| 237 | + List<Boolean> bits = List.of( |
| 238 | + false, true, false, true, false, false, true, true, |
| 239 | + false, true, true, true, true, true, true, true); |
| 240 | + |
| 241 | + byte[] result = serializeBitMap("little_endian", "lsb_first", bits); |
| 242 | + assertThat(result).isEqualTo(new byte[]{(byte) 0xFE, (byte) 0xCA}); |
| 243 | + } |
| 244 | + |
| 245 | + @Test |
| 246 | + void testBitMapPartialByteSingleGroup() throws Exception { |
| 247 | + // 5 bits, big_endian msb_first: [1,0,1,1,0] packed from bit7 downward |
| 248 | + // = 1011_0000 = 0xB0, remaining 3 bits are zero |
| 249 | + List<Boolean> bits = List.of(true, false, true, true, false); |
| 250 | + |
| 251 | + byte[] result = serializeBitMap("big_endian", "msb_first", bits); |
| 252 | + assertThat(result).hasSize(1); |
| 253 | + assertThat(result).isEqualTo(new byte[]{(byte) 0xB0}); |
| 254 | + } |
| 255 | + |
| 256 | + @Test |
| 257 | + void testBitMapPartialByteAfterCompleteGroup() throws Exception { |
| 258 | + // 10 bits, big_endian msb_first: |
| 259 | + // group[0] = [1,1,1,1,1,1,1,0] = 0xFE (complete) → bytes[0] |
| 260 | + // partial = [1,1] = 1100_0000 = 0xC0 → bytes[1] |
| 261 | + List<Boolean> bits = List.of( |
| 262 | + true, true, true, true, true, true, true, false, |
| 263 | + true, true); |
| 264 | + |
| 265 | + byte[] result = serializeBitMap("big_endian", "msb_first", bits); |
| 266 | + assertThat(result).hasSize(2); |
| 267 | + assertThat(result).isEqualTo(new byte[]{(byte) 0xFE, (byte) 0xC0}); |
| 268 | + } |
| 269 | + |
180 | 270 | // ----------------------------------------------------------------------- |
181 | 271 | // helpers |
182 | 272 | // ----------------------------------------------------------------------- |
183 | 273 |
|
| 274 | + private byte[] serializeBitMap(String byteOrder, String bitOrder, List<Boolean> bits) throws Exception { |
| 275 | + // Build body as JSON array of individually named placeholders: ["${b0}", "${b1}", ...] |
| 276 | + // This avoids the ContextNode wrapping issue that occurs when a single placeholder |
| 277 | + // resolves to a List — individual primitive params unwrap cleanly. |
| 278 | + StringBuilder bodyArray = new StringBuilder("["); |
| 279 | + for (int i = 0; i < bits.size(); i++) { |
| 280 | + if (i > 0) bodyArray.append(","); |
| 281 | + bodyArray.append("\"${b").append(i).append("}\""); |
| 282 | + } |
| 283 | + bodyArray.append("]"); |
| 284 | + |
| 285 | + String json = """ |
| 286 | + { |
| 287 | + "type": "system", |
| 288 | + "attributes": { |
| 289 | + "kind": "bit_map", |
| 290 | + "byte_order": "%s", |
| 291 | + "bit_order": "%s" |
| 292 | + }, |
| 293 | + "body": %s |
| 294 | + } |
| 295 | + """.formatted(byteOrder, bitOrder, bodyArray); |
| 296 | + |
| 297 | + Map<String, Object> params = new HashMap<>(); |
| 298 | + for (int i = 0; i < bits.size(); i++) { |
| 299 | + params.put("b" + i, bits.get(i)); |
| 300 | + } |
| 301 | + |
| 302 | + List<byte[]> result = enotSerializer.serialize(json, ctx(params), enotContext); |
| 303 | + assertThat(result).hasSize(1); |
| 304 | + return result.get(0); |
| 305 | + } |
| 306 | + |
184 | 307 | /** Parses one DER blob and asserts SET { SEQUENCE { OID(2.5.4.11), UTF8String(expectedUnit) } } */ |
185 | 308 | private void assertOrgUnitDer(byte[] der, String expectedUnit) throws Exception { |
186 | 309 | ASN1Set set = (ASN1Set) ASN1Primitive.fromByteArray(der); |
|
0 commit comments