diff --git a/packages/cultcache-ts/test/union-parity.test.ts b/packages/cultcache-ts/test/union-parity.test.ts index da6d1e9e..f3e79b37 100644 --- a/packages/cultcache-ts/test/union-parity.test.ts +++ b/packages/cultcache-ts/test/union-parity.test.ts @@ -772,7 +772,7 @@ test("slots read and rewrite exactly what the C# reference does for nil, a str i 4: ["l", list(string), () => []], }); const rows = readWire(new Uint8Array(await readFile(join(vectors, "..", "slot-nil", "reads.msgpack")))) as [string, Uint8Array, boolean, Uint8Array][]; - assert.equal(rows.length, 15); + assert.equal(rows.length, 17); let accepted = 0; for (const [name, payload, wasAccepted, rewritten] of rows) { if (wasAccepted) { @@ -782,7 +782,14 @@ test("slots read and rewrite exactly what the C# reference does for nil, a str i assert.throws(() => probe.decode(readWire(payload)), (error: Error) => !error.message.includes(hex(payload).slice(2)), name); } } - assert.equal(accepted, 13); + assert.equal(accepted, 15); + // A character outside the BMP is four UTF-8 bytes (U+1F600 is f0 9f 98 80); a CESU-8 reader, or one with no four-byte + // branch, fails here against the bytes the C# reference wrote. + const astral = rows.find(([name]) => name === "string-astral")!; + assert.equal(probe.decode(readWire(astral[1])).s, "\u{1F600}"); + assert.equal(hex(writeWire(probe.encode(probe.decode(readWire(astral[1]))))), hex(astral[3])); + const astralInBytes = rows.find(([name]) => name === "bytes-str-astral")!; + assert.equal(hex(probe.decode(readWire(astralInBytes[1])).b), "f09f9880"); }); test("a nil reads as null and writes as nil in every reference slot, and a value slot refuses it", () => { diff --git a/tests/GameCult.Caching.Tests/SlotNilVectorTests.cs b/tests/GameCult.Caching.Tests/SlotNilVectorTests.cs index 5b1a637b..e3a24f76 100644 --- a/tests/GameCult.Caching.Tests/SlotNilVectorTests.cs +++ b/tests/GameCult.Caching.Tests/SlotNilVectorTests.cs @@ -44,6 +44,8 @@ namespace GameCult.Caching.Tests ("nil-int", "95a161c0c4026162c090"), ("bytes-str", "95a16107a3616263c090"), ("bytes-str-euro", "95a16107a3e282acc090"), + ("string-astral", "95a4f09f988007c4026162c090"), + ("bytes-str-astral", "95a16107a4f09f9880c090"), ("bytes-bin", "95a16107c403616263c090"), ("short-0", "90"), ("short-1", "91a161"), diff --git a/tests/vectors/slot-nil/README.md b/tests/vectors/slot-nil/README.md index f7ac1a2b..a74f6fdb 100644 --- a/tests/vectors/slot-nil/README.md +++ b/tests/vectors/slot-nil/README.md @@ -8,5 +8,5 @@ A record of five slots: `S` string, `I` int, `B` byte[], `R` a record of one str - `reads.msgpack`: an array of `[name, payload, accepted, rewritten]`. `payload` (bin) is the record fed to the reference; `accepted` is whether it read; `rewritten` (bin) is what the reference writes after reading it, and empty - when refused. It covers a nil in each kind of slot, a MessagePack str in the byte[] slot, and records short of their - last slots. + when refused. It covers a nil in each kind of slot, a MessagePack str in the byte[] slot, a four-byte UTF-8 character + (U+1F600) as the string slot and as a str in the byte[] slot, and records short of their last slots.