SmokeFixture.kt (4450B)
1 package tera.smoke 2 3 import java.nio.file.Files 4 import java.nio.file.Path 5 import java.security.MessageDigest 6 import java.util.HexFormat 7 import java.util.concurrent.ArrayBlockingQueue 8 import java.util.concurrent.CountDownLatch 9 import java.util.concurrent.TimeUnit 10 import kotlin.test.assertNotNull 11 import uniffi.tera_core.FfiAddCommandType 12 import uniffi.tera_core.FfiAddDraftInput 13 import uniffi.tera_core.FfiBlossomEndpointAuthority 14 import uniffi.tera_core.FfiBlossomHostKind 15 import uniffi.tera_core.FfiMediaFile 16 import uniffi.tera_core.FfiPreparedMediaInput 17 import uniffi.tera_core.FfiRuntimeChangeDelivery 18 import uniffi.tera_core.FfiRuntimeChangeKind 19 import uniffi.tera_core.FfiRuntimeChangeRecord 20 import uniffi.tera_core.HostSigningRequest 21 import uniffi.tera_core.HostSigningResult 22 import uniffi.tera_core.ProtectedDataAvailability 23 import uniffi.tera_core.SignerAvailabilityRecord 24 import uniffi.tera_core.SignerStatusRecord 25 import uniffi.tera_core.TeraHostSigner 26 import uniffi.tera_core.TeraRuntime 27 import uniffi.tera_core.TeraRuntimeObserver 28 29 internal const val PUBLIC_KEY = "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" 30 internal val GENERATION = "04".repeat(32) 31 internal const val UNIX_MS = 1_800_000_000_000uL 32 internal const val UNIX_S = 1_800_000_000uL 33 34 internal object UnavailableSigner : TeraHostSigner { 35 override suspend fun signerStatus() = SignerStatusRecord(1u, SignerAvailabilityRecord.UNAVAILABLE) 36 override suspend fun sign(request: HostSigningRequest): HostSigningResult = 37 error("Binding smoke must never request a signature") 38 } 39 40 internal class SmokeFixture : AutoCloseable { 41 val root: Path = Files.createTempDirectory( 42 Files.createDirectories(Path.of(System.getProperty("tera.smoke.data"))), "binding-", 43 ) 44 val owner: Path = Files.createDirectories(root.resolve("radroots/users").resolve(PUBLIC_KEY)) 45 val bytes = byteArrayOf(137.toByte(), 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 2, 0, 0, 0, 2) 46 val original: Path = root.resolve("original.png").also { Files.write(it, bytes) } 47 val sha256: String = HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(bytes)) 48 49 suspend fun runtime(protectedData: ProtectedDataAvailability = ProtectedDataAvailability.AVAILABLE): TeraRuntime = 50 TeraRuntime.withHostSigner(root.toString(), PUBLIC_KEY, GENERATION, UNIX_MS, protectedData, UnavailableSigner) 51 .also { runtime -> 52 runtime.configureBlossom( 53 FfiBlossomHostKind.SIMULATOR, FfiBlossomEndpointAuthority.LOOPBACK_DEVELOPMENT, 54 "http://127.0.0.1:3000", emptyList(), 55 ) 56 } 57 58 fun media(file: FfiMediaFile) = FfiPreparedMediaInput( 59 schemaVersion = 2u, opaqueReference = "media:$sha256", file = file, 60 sha256 = sha256, mediaType = "image/png", byteSize = bytes.size.toULong(), 61 width = 2u, height = 2u, alt = "Binding fixture", preparedAtUnixS = UNIX_S, 62 ) 63 64 fun draft(media: List<FfiPreparedMediaInput>) = FfiAddDraftInput( 65 schemaVersion = 1u, commandType = FfiAddCommandType.CREATE_PHOTO_UPDATE, 66 content = "Binding smoke", identifier = null, title = null, summary = null, 67 location = null, eventTiming = null, eventStartDate = null, eventEndDate = null, 68 eventStartUnixS = null, eventEndUnixS = null, eventTimezone = null, 69 priceAmount = null, currency = null, unit = null, quantity = null, 70 foodPublishedAtUnixS = null, foodStatus = null, media = media, 71 ) 72 73 override fun close() { 74 Files.walk(root).use { paths -> 75 paths.sorted(Comparator.reverseOrder()).forEach { Files.delete(it) } 76 } 77 } 78 } 79 80 internal class SmokeObserver(private val paused: Boolean = false) : TeraRuntimeObserver { 81 private val events = ArrayBlockingQueue<FfiRuntimeChangeRecord>(64) 82 val released = CountDownLatch(1) 83 84 override fun onChange(change: FfiRuntimeChangeRecord) { 85 check(events.offer(change)) { "The bounded smoke observer overflowed" } 86 if (paused && change.kind == FfiRuntimeChangeKind.INITIAL && change.delivery == FfiRuntimeChangeDelivery.CHANGE) { 87 check(released.await(10, TimeUnit.SECONDS)) { "The test did not release its callback" } 88 } 89 } 90 91 fun next(): FfiRuntimeChangeRecord = assertNotNull(events.poll(5, TimeUnit.SECONDS), "Native callback deadline") 92 fun pending(): Int = events.size 93 }