Skip to content

Round trips and cipher vectors for core, which had one test - #48

Open
lgnap wants to merge 1 commit into
antoinevalentinHA:masterfrom
lgnap:test/core-round-trip
Open

Round trips and cipher vectors for core, which had one test#48
lgnap wants to merge 1 commit into
antoinevalentinHA:masterfrom
lgnap:test/core-round-trip

Conversation

@lgnap

@lgnap lgnap commented Sep 5, 2026

Copy link
Copy Markdown

First slice of the core coverage we discussed — the module that produces the bytes written to the card, and which had exactly one test, on the endianness helpers.

core goes from 1 test to 24; the suite from 284 to 304, 0 failures, 39 skipped on Linux.

What is here

Round trips — a pack written and read back must describe the same story — for the raw binary and archive formats, plus the XXTEA cipher every device transfer goes through. PackFixtures builds packs in code: generated byte patterns carrying the mime types the writers require, no device data and no third-party content. Asset sizes default to whole sectors so that a failed comparison means something broke rather than that padding exists.

Four things they found

Written before touching anything, exactly as TESTING.md prescribes. None is a regression — all four are behaviour nobody had looked at. All four are KNOWN GAP, recorded and not fixed.

1. No pack-level uuid survives archive or raw. Both readers build the pack from their first stage node — ArchiveStoryPackReader from nodes.get(0).getUuid(), the binary one from the node at sector 0 — and ArchiveStoryPackWriter never writes a pack uuid at all. Only the FS format carries one of its own.

This is not cosmetic: LibraryService names a converted artifact <uuid>.converted_<millis> from exactly this value, so the file a conversion produces is named after the pack's first node rather than the pack.

2. An option pointing at the first stage node loses the entire option list. Options are stored as sector addresses and the list ends at the first zero. Stage nodes are laid out from sector 0, so the first one has address 0 and is indistinguishable from the terminator.

options include the first node = true   ->  options read back = 0
options include the first node = false  ->  options read back = 1

Not just the offending entry — everything after it goes too. "Back to the beginning" is an ordinary thing for a menu to offer, so this is reachable from a pack someone would actually build. The archive format keeps the same story intact, which is what makes it visible rather than a matter of opinion.

I did not fix it. Fixing it means changing either the terminator or the layout, and that is a format decision rather than a reader fix — one for you, not for a commit whose job is to establish what the code does today.

3. The raw format pads assets to whole sectors and the reader returns the whole allocation, so a 513-byte asset comes back as 1024. The written bytes are intact at the front, the rest is zero, and nothing in the model records the real length.

4. XXTEA returns a one-word block unchanged rather than refusing it. A caller handing it a short tail gets plaintext back with no indication that nothing happened.

The cipher tests

XXTEACipherTest holds the round trip, the two-word boundary, a check that ciphering actually changes the data — so the round trip is not trivially true — and a fixed vector.

The vector is pinned from the current implementation, not from an external specification, and the test says so. Its purpose is that a change to the rounds, the delta constant or the key endianness fails here rather than being discovered as packs that no longer play.

What is deliberately not here

  • No FS round trip. It writes a folder tree and wants a different harness; it belongs in the next slice.
  • PackAssetsCompression is untouched. It does real image and audio conversion, so exercising it needs real media rather than byte patterns, and that is a fixture question worth deciding separately.
  • No failure paths yet — truncated files, bad magic numbers, an asset referenced but missing. Those characterize error handling rather than the happy path and I would rather land the round trips first.

This is a first slice, not the whole module.

Checks

304 Java tests, 0 failures, 39 skipped on Linux, on Temurin 11. Nothing outside core/src/test and TESTING.md is touched, so no production behaviour changes. Windows unverified locally; your CI covers it.

core produces the bytes written to the card and had a single test, on the
endianness helpers. These are round trips -- a pack written and read back must
describe the same story -- plus the cipher every device transfer goes through.

Written before touching anything, and they found four things. None is a
regression; all four are behaviour nobody had looked at.

1. No pack-level uuid survives archive or raw. Both readers build the pack from
   their first stage node -- ArchiveStoryPackReader from nodes.get(0), the
   binary one from the node at sector 0 -- and the archive writer never writes a
   pack uuid at all. Only the FS format carries one. LibraryService names a
   converted artifact from this value, so the name follows the first node.

2. An action node option pointing at the first stage node loses the whole
   option list. Options are sector addresses, the list ends at the first zero,
   and stage nodes are laid out from sector 0, so the first one is
   indistinguishable from the terminator. Not just that entry: everything after
   it goes too. "Back to the beginning" is an ordinary menu option, so this is
   reachable from a pack someone would build. The archive format keeps the same
   story intact, which is what makes it visible.

3. The raw format pads assets to whole sectors and the reader returns the whole
   allocation, so a 513-byte asset comes back as 1024. The bytes written are
   intact at the front and the rest is zero.

4. XXTEA returns a one-word block unchanged rather than refusing it. A caller
   handing it a short tail gets plaintext back and no indication.

All four are KNOWN GAP cases: recorded, not fixed. Fixing 2 means changing
either the terminator or the layout, which is a format decision rather than a
reader fix, and none of them should be corrected inside a commit whose job is
to establish what the code does today.

PackFixtures builds packs in code. The assets are generated byte patterns
carrying the mime types the writers require -- no device data and no third-party
content, per the existing rule. Sizes default to whole sectors so a failed
comparison means something broke rather than that padding exists.

Not covered, and worth saying: the FS format has no round trip here, and
PackAssetsCompression is untouched because it does real image and audio
conversion and needs real media to exercise. This is the first slice of the
module, not the whole of it.

304 Java tests, 0 failures, 39 skipped on Linux -- 284 before, and core goes
from 1 to 24.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant