CodexPdfRoundTripTest.kt

3.5 KB · raw

1package com.peachpasswords.codex23import androidx.test.ext.junit.runners.AndroidJUnit44import androidx.test.platform.app.InstrumentationRegistry5import org.junit.Assert.assertEquals6import org.junit.Test7import org.junit.runner.RunWith8import java.io.File9import java.io.FileOutputStream1011/**12 * End-to-end regression test for the Codex PDF round trip: render a page of13 * Aztec codes to PDF bytes, write them to disk, then decode the same file14 * back via [CodexRestore] exactly as the real restore flow does (SAF-style15 * file URI, [android.graphics.pdf.PdfRenderer], per-cell crop). This is the16 * real bug surface: the data-layer (compression, Reed-Solomon, envelope17 * integrity) already has coverage in [CodexSuiteTest], but nothing previously18 * exercised the actual PDF render → rasterize → decode pipeline, which is19 * exactly where the bug lived (PdfDocument's raster bitmap embedding20 * corrupted crisp module edges, and 640px crop resolution was independently21 * too low once codes needed more than a couple of Aztec layers).22 */23@RunWith(AndroidJUnit4::class)24class CodexPdfRoundTripTest {2526    @Test27    fun pdfRoundTripDecodesAllCodesAtRealisticScale() {28        val context = InstrumentationRegistry.getInstrumentation().targetContext2930        // ~75 envelopes: matches the code count a real multi-hundred-entry31        // vault's Codex produces. Pseudo-random filler (not a single repeated32        // character) so deflate can't collapse it to a trivial size.33        val filler = StringBuilder()34        val rnd = java.util.Random(42)35        repeat(36000) { filler.append(('a' + rnd.nextInt(26))) }36        val fakePayload = CodexPayload(37            verifyCode = "PX-TEST-0000",38            vaultBlob = filler.toString(),39            timestamp = 0L,40            entryCount = 1,41            vaultFingerprint = CodexCodec.sha256Hex(filler.toString().toByteArray(Charsets.UTF_8)).take(32),42            generatedAt = 0L,43        )44        val payloadJson = CodexCodec.json.encodeToString(CodexPayload.serializer(), fakePayload)45        val compressed = CodexCodec.deflate(payloadJson.toByteArray(Charsets.UTF_8))46        val (envelopes, layout) = CodexCodec.encodeToEnvelopes(compressed, "PX-TEST-0000")4748        val aztecs = envelopes.map { env ->49            val jsonStr = CodexCodec.json.encodeToString(ChunkEnvelope.serializer(), env)50            CodexAztec.encodeMatrix(jsonStr)51        }5253        val manifest = CodexManifest(54            verifyCode = "PX-TEST-0000",55            totalChunks = layout.totalChunks,56            dataChunks = layout.dataChunks,57            parityChunks = layout.parityChunks,58            stripeCount = layout.stripeCount,59            pageCount = Math.ceil(layout.totalChunks.toDouble() / CODES_PER_PAGE).toInt(),60            entryCount = 1,61            vaultFingerprint = "test",62            generatedAt = 0L,63        )64        val pdfBytes = CodexPdf.render(aztecs, manifest)6566        val pdfFile = File(context.cacheDir, "roundtrip_test.pdf")67        FileOutputStream(pdfFile).use { it.write(pdfBytes) }68        val uri = android.net.Uri.fromFile(pdfFile)6970        val result = CodexRestore.decodeFromUri(context, uri)71        assertEquals(72            "Every generated code should decode back out of the rendered PDF",73            envelopes.size,74            result.envelopes.size,75        )7677        val payload = CodexRestore.reconstructPayload(result.envelopes)78        assertEquals("PX-TEST-0000", payload.verifyCode)79        assertEquals(fakePayload.vaultBlob, payload.vaultBlob)80        assertEquals(fakePayload.vaultFingerprint, payload.vaultFingerprint)81    }82}