From fc4f7a03fc75443a5efe365f89ac7fbb42f6f12b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 9 Jun 2026 19:26:04 +0000 Subject: [PATCH 1/2] fix(commons): make commonTest compile for iOS targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commons commonTest source set is shared across all KMP targets, including the iosArm64/iosSimulatorArm64 spike, but several tests still reached for JVM-only APIs that don't resolve on Kotlin/Native: - JUnit (`org.junit.*`, `junit.framework.TestCase`) → kotlin.test, with message arguments moved from first (JUnit) to last (kotlin.test). - `assertArrayEquals` → `assertContentEquals`. - `@JvmStatic` on the `android.util.Log` test stub → removed (it only affects JVM bytecode; companion calls work without it). - `seg.javaClass.simpleName` → `seg::class.simpleName!!`. - A test function name containing `()` (illegal on Native) → renamed. - `String(CharArray, offset, count)` → `CharArray.concatToString`. CliffDetectorTest exercises `computeStalledSpeakers`/`defaultCliffBackoffMs`, which live in the jvmAndroid-only NestViewModel and are invisible to iOS, so it moves to jvmTest alongside NestViewModelTest. --- .../src/commonTest/kotlin/android/util/Log.kt | 7 --- .../amethyst/commons/Base83Test.kt | 10 ++-- .../amethyst/commons/SRGBTest.kt | 8 ++-- .../amethyst/commons/ThumbHashTest.kt | 48 +++++++++---------- .../commons/emojicoder/EmojiCoderTest.kt | 8 ++-- .../commons/model/ChannelRelaysTest.kt | 4 +- .../namecoin/NamecoinSettingsTest.kt | 12 ++--- .../nip30CustomEmojis/OwnedEmojiPackTest.kt | 8 ++-- .../richtext/RichTextParserMultibyteTest.kt | 38 +++++++-------- .../commons/richtext/RichTextParserTest.kt | 25 +++++----- .../commons/ui/note/ReplyContextTest.kt | 8 ++-- .../amethyst/commons/util/CodePointsTest.kt | 20 ++++---- .../commons/viewmodels/CliffDetectorTest.kt | 0 13 files changed, 94 insertions(+), 102 deletions(-) rename commons/src/{commonTest => jvmTest}/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/CliffDetectorTest.kt (100%) diff --git a/commons/src/commonTest/kotlin/android/util/Log.kt b/commons/src/commonTest/kotlin/android/util/Log.kt index cc9ab36145..d0c09ca3af 100644 --- a/commons/src/commonTest/kotlin/android/util/Log.kt +++ b/commons/src/commonTest/kotlin/android/util/Log.kt @@ -22,13 +22,11 @@ package android.util class Log { companion object { - @JvmStatic fun isLoggable( tag: String?, msg: Int?, ): Boolean = true - @JvmStatic fun d( tag: String?, msg: String?, @@ -37,7 +35,6 @@ class Log { return 0 } - @JvmStatic fun i( tag: String?, msg: String?, @@ -46,7 +43,6 @@ class Log { return 0 } - @JvmStatic fun w( tag: String?, msg: String?, @@ -55,7 +51,6 @@ class Log { return 0 } - @JvmStatic fun w( tag: String?, msg: String?, @@ -66,7 +61,6 @@ class Log { return 0 } - @JvmStatic fun e( tag: String?, msg: String?, @@ -75,7 +69,6 @@ class Log { return 0 } - @JvmStatic fun e( tag: String?, msg: String?, diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/Base83Test.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/Base83Test.kt index b0d40b2303..77edcd8415 100644 --- a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/Base83Test.kt +++ b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/Base83Test.kt @@ -21,22 +21,22 @@ package com.vitorpamplona.amethyst.commons import com.vitorpamplona.amethyst.commons.blurhash.Base83 -import org.junit.Assert.assertEquals -import org.junit.Test +import kotlin.test.Test +import kotlin.test.assertEquals class Base83Test { @Test fun testEncodeDecode() { for (i in 0..820000) { - assertEquals("$i encode decode", i, Base83.decode(Base83.encode(i.toLong()))) + assertEquals(i, Base83.decode(Base83.encode(i.toLong())), "$i encode decode") } } @Test fun testSingleDigits() { for (i in 0..82) { - val expected: String = String(Base83.ALPHABET, i, 1) - assertEquals("$i encodes", expected, Base83.encode(i.toLong(), 1)) + val expected: String = Base83.ALPHABET.concatToString(i, i + 1) + assertEquals(expected, Base83.encode(i.toLong(), 1), "$i encodes") } } diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/SRGBTest.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/SRGBTest.kt index 569e45e563..c3a9d38daf 100644 --- a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/SRGBTest.kt +++ b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/SRGBTest.kt @@ -21,22 +21,22 @@ package com.vitorpamplona.amethyst.commons import com.vitorpamplona.amethyst.commons.blurhash.SRGB -import org.junit.Assert.assertEquals -import org.junit.Test import kotlin.math.round +import kotlin.test.Test +import kotlin.test.assertEquals class SRGBTest { @Test fun testEncodeDecode() { for (i in 0..255) { - assertEquals("$i encode decode", i, SRGB.linearToSrgb(SRGB.srgbToLinear(i))) + assertEquals(i, SRGB.linearToSrgb(SRGB.srgbToLinear(i)), "$i encode decode") } for (i in 0..100) { val srgb = SRGB.linearToSrgb(i / 100.0f) val linear = round(SRGB.srgbToLinear(srgb) * 100).toInt() - assertEquals("$i decode encode", i, linear) + assertEquals(i, linear, "$i decode encode") } } } diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/ThumbHashTest.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/ThumbHashTest.kt index a152963ec9..cddc9872db 100644 --- a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/ThumbHashTest.kt +++ b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/ThumbHashTest.kt @@ -22,12 +22,12 @@ package com.vitorpamplona.amethyst.commons import com.vitorpamplona.amethyst.commons.thumbhash.ThumbHashDecoder import com.vitorpamplona.amethyst.commons.thumbhash.ThumbHashEncoder -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull -import org.junit.Assert.assertNull -import org.junit.Assert.assertTrue -import org.junit.Test import kotlin.math.abs +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue class ThumbHashTest { @Test @@ -37,20 +37,20 @@ class ThumbHashTest { val pixels = IntArray(w * h) { 0xFFFF8040.toInt() } // opaque warm orange val hashBytes = ThumbHashEncoder.encode(pixels, w, h) - assertTrue("hash should have at least header bytes", hashBytes.size >= 5) + assertTrue(hashBytes.size >= 5, "hash should have at least header bytes") val decoded = ThumbHashDecoder.decode(hashBytes) assertNotNull(decoded) decoded!! - assertTrue("decoded width should be positive", decoded.width > 0) - assertTrue("decoded height should be positive", decoded.height > 0) + assertTrue(decoded.width > 0, "decoded width should be positive") + assertTrue(decoded.height > 0, "decoded height should be positive") val originalRatio = w.toFloat() / h.toFloat() val decodedRatio = decoded.width.toFloat() / decoded.height.toFloat() // ThumbHash loses some precision, but landscape vs portrait should be preserved. assertTrue( - "decoded ratio ($decodedRatio) should be on the same side of 1 as original ($originalRatio)", (originalRatio > 1f) == (decodedRatio > 1f) || originalRatio == decodedRatio, + "decoded ratio ($decodedRatio) should be on the same side of 1 as original ($originalRatio)", ) } @@ -69,8 +69,8 @@ class ThumbHashTest { } val encoded = ThumbHashEncoder.encodeToBase64(pixels, w, h) - assertTrue("base64 string should be non-empty", encoded.isNotEmpty()) - assertTrue("base64 string should not contain padding", !encoded.contains('=')) + assertTrue(encoded.isNotEmpty(), "base64 string should be non-empty") + assertTrue(!encoded.contains('='), "base64 string should not contain padding") val viaBase64 = ThumbHashDecoder.decode(encoded) assertNotNull(viaBase64) @@ -80,8 +80,8 @@ class ThumbHashTest { assertNotNull(viaBytes) viaBytes!! - assertEquals("base64 path and raw path should agree on width", viaBytes.width, viaBase64.width) - assertEquals("base64 path and raw path should agree on height", viaBytes.height, viaBase64.height) + assertEquals(viaBytes.width, viaBase64.width, "base64 path and raw path should agree on width") + assertEquals(viaBytes.height, viaBase64.height, "base64 path and raw path should agree on height") } @Test @@ -101,7 +101,7 @@ class ThumbHashTest { decoded!! for (p in decoded.pixels) { val a = (p ushr 24) and 0xff - assertEquals("alpha should be 255 for opaque encode", 255, a) + assertEquals(255, a, "alpha should be 255 for opaque encode") } } @@ -120,7 +120,7 @@ class ThumbHashTest { val a = (p ushr 24) and 0xff if (a > maxAlpha) maxAlpha = a } - assertTrue("max alpha of all-transparent decode should be low; got $maxAlpha", maxAlpha <= 16) + assertTrue(maxAlpha <= 16, "max alpha of all-transparent decode should be low; got $maxAlpha") } @Test @@ -150,9 +150,9 @@ class ThumbHashTest { val avgB = sumB / count // ThumbHash quantisation allows a handful of codepoints of drift. - assertTrue("avg R drift: expected ${target[0]}, got $avgR", abs(avgR - target[0]) < 8) - assertTrue("avg G drift: expected ${target[1]}, got $avgG", abs(avgG - target[1]) < 8) - assertTrue("avg B drift: expected ${target[2]}, got $avgB", abs(avgB - target[2]) < 8) + assertTrue(abs(avgR - target[0]) < 8, "avg R drift: expected ${target[0]}, got $avgR") + assertTrue(abs(avgG - target[1]) < 8, "avg G drift: expected ${target[1]}, got $avgG") + assertTrue(abs(avgB - target[2]) < 8, "avg B drift: expected ${target[2]}, got $avgB") } @Test @@ -163,7 +163,7 @@ class ThumbHashTest { val hash = ThumbHashEncoder.encode(pixels, w, h) val ratio = ThumbHashDecoder.aspectRatio(hash) assertNotNull(ratio) - assertTrue("landscape ratio should be > 1, got $ratio", ratio!! > 1f) + assertTrue(ratio!! > 1f, "landscape ratio should be > 1, got $ratio") } @Test @@ -174,11 +174,11 @@ class ThumbHashTest { val hash = ThumbHashEncoder.encode(pixels, w, h) val ratio = ThumbHashDecoder.aspectRatio(hash) assertNotNull(ratio) - assertTrue("portrait ratio should be < 1, got $ratio", ratio!! < 1f) + assertTrue(ratio!! < 1f, "portrait ratio should be < 1, got $ratio") } @Test - fun `repeated decodes produce identical output (cosine cache determinism)`() { + fun `repeated decodes produce identical output - cosine cache determinism`() { val w = 40 val h = 30 val pixels = @@ -222,8 +222,8 @@ class ThumbHashTest { // Chop off half the AC payload. val truncated = fullHash.copyOfRange(0, 5 + (fullHash.size - 5) / 4) assertNull( - "hash with insufficient AC bytes should be rejected", ThumbHashDecoder.decode(truncated), + "hash with insufficient AC bytes should be rejected", ) } @@ -239,13 +239,13 @@ class ThumbHashTest { assertNotNull(decoded) decoded!! assertTrue( - "expected output to fit in 32x32, got ${decoded.width}x${decoded.height}", decoded.width in 1..32 && decoded.height in 1..32, + "expected output to fit in 32x32, got ${decoded.width}x${decoded.height}", ) assertEquals( - "pixel buffer size must match dimensions", decoded.width * decoded.height, decoded.pixels.size, + "pixel buffer size must match dimensions", ) } } diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/emojicoder/EmojiCoderTest.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/emojicoder/EmojiCoderTest.kt index 578df11b05..5c174845fb 100644 --- a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/emojicoder/EmojiCoderTest.kt +++ b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/emojicoder/EmojiCoderTest.kt @@ -20,9 +20,9 @@ */ package com.vitorpamplona.amethyst.commons.emojicoder -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Test +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue class EmojiCoderTest { companion object { @@ -68,7 +68,7 @@ class EmojiCoderTest { val encoded = EmojiCoder.encode(emoji, sentence) val decoded = EmojiCoder.decode(encoded) assertEquals(sentence, decoded) - assertTrue("Failed sentence for emoji $emoji with sentence `$sentence`: `$encoded`", EmojiCoder.isCoded(encoded)) + assertTrue(EmojiCoder.isCoded(encoded), "Failed sentence for emoji $emoji with sentence `$sentence`: `$encoded`") } } } diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/model/ChannelRelaysTest.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/model/ChannelRelaysTest.kt index 316c8b3743..f1d9d36ac0 100644 --- a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/model/ChannelRelaysTest.kt +++ b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/model/ChannelRelaysTest.kt @@ -21,8 +21,8 @@ package com.vitorpamplona.amethyst.commons.model import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl -import org.junit.Assert.assertEquals -import org.junit.Test +import kotlin.test.Test +import kotlin.test.assertEquals /** * Lock in the descending-by-usage ordering and equal-count preservation of diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip05DnsIdentifiers/namecoin/NamecoinSettingsTest.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip05DnsIdentifiers/namecoin/NamecoinSettingsTest.kt index 3bcd5f52e7..77a7b54748 100644 --- a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip05DnsIdentifiers/namecoin/NamecoinSettingsTest.kt +++ b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip05DnsIdentifiers/namecoin/NamecoinSettingsTest.kt @@ -23,12 +23,12 @@ package com.vitorpamplona.amethyst.commons.model.nip05DnsIdentifiers.namecoin import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ElectrumxServer import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinBackend import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinCoreRpcConfig -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertNotNull -import org.junit.Assert.assertNull -import org.junit.Assert.assertTrue -import org.junit.Test +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue class NamecoinSettingsTest { // ── Server string parsing ────────────────────────────────────────── diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip30CustomEmojis/OwnedEmojiPackTest.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip30CustomEmojis/OwnedEmojiPackTest.kt index 037fb9f70c..90a73f8ae6 100644 --- a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip30CustomEmojis/OwnedEmojiPackTest.kt +++ b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/model/nip30CustomEmojis/OwnedEmojiPackTest.kt @@ -21,10 +21,10 @@ package com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Test +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue class OwnedEmojiPackTest { private val publicEmoji = EmojiUrlTag("public_one", "https://example.com/public.png") diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParserMultibyteTest.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParserMultibyteTest.kt index a1b896e453..397e7b79a7 100644 --- a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParserMultibyteTest.kt +++ b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParserMultibyteTest.kt @@ -21,9 +21,9 @@ package com.vitorpamplona.amethyst.commons.richtext import com.vitorpamplona.amethyst.commons.model.EmptyTagList -import org.junit.Assert.assertTrue -import org.junit.Test +import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertTrue class RichTextParserMultibyteTest { @Test @@ -42,20 +42,20 @@ class RichTextParserMultibyteTest { // user@example.com should be EmailSegment assertTrue( - "user@example.com should be EmailSegment", allSegments.any { it is EmailSegment && it.segmentText == "user@example.com" }, + "user@example.com should be EmailSegment", ) // user@example.com should NOT be a LinkSegment assertTrue( - "user@example.com should not be a LinkSegment", allSegments.none { it is LinkSegment && it.segmentText == "user@example.com" }, + "user@example.com should not be a LinkSegment", ) // user@example.com should not be in urlSet assertTrue( - "user@example.com should not be in urlSet", !state.urlSet.withScheme.contains("user@example.com") && !state.urlSet.withoutScheme.contains("user@example.com"), + "user@example.com should not be in urlSet", ) } @@ -145,8 +145,8 @@ class RichTextParserMultibyteTest { val state = RichTextParser().parseText(text, EmptyTagList, null) val allSegments = state.paragraphs.flatMap { it.words } assertTrue( - "user@example.com should be EmailSegment", allSegments.any { it is EmailSegment && it.segmentText == "user@example.com" }, + "user@example.com should be EmailSegment", ) } @@ -158,11 +158,11 @@ class RichTextParserMultibyteTest { val allSegments = state.paragraphs.flatMap { it.words } val urlSegments = allSegments.filterIsInstance() - assertTrue("Should have SchemelessUrlSegment", urlSegments.isNotEmpty()) - assertTrue("URL should be example.com", urlSegments.any { it.segmentText == "example.com" }) + assertTrue(urlSegments.isNotEmpty(), "Should have SchemelessUrlSegment") + assertTrue(urlSegments.any { it.segmentText == "example.com" }, "URL should be example.com") val textSegments = allSegments.filterIsInstance() - assertTrue("Should have prefix ああ", textSegments.any { it.segmentText == "ああ" }) + assertTrue(textSegments.any { it.segmentText == "ああ" }, "Should have prefix ああ") } @Test @@ -173,11 +173,11 @@ class RichTextParserMultibyteTest { val allSegments = state.paragraphs.flatMap { it.words } val urlSegments = allSegments.filterIsInstance() - assertTrue("Should have SchemelessUrlSegment", urlSegments.isNotEmpty()) - assertTrue("URL should be example.com", urlSegments.any { it.segmentText == "example.com" }) + assertTrue(urlSegments.isNotEmpty(), "Should have SchemelessUrlSegment") + assertTrue(urlSegments.any { it.segmentText == "example.com" }, "URL should be example.com") val textSegments = allSegments.filterIsInstance() - assertTrue("Should have suffix ああ", textSegments.any { it.segmentText == "ああ" }) + assertTrue(textSegments.any { it.segmentText == "ああ" }, "Should have suffix ああ") } @Test @@ -188,12 +188,12 @@ class RichTextParserMultibyteTest { val allSegments = state.paragraphs.flatMap { it.words } val emailSegment = allSegments.filterIsInstance() - assertTrue("Should have EmailSegment", emailSegment.isNotEmpty()) - assertTrue("Email should be user@example.com", emailSegment.any { it.segmentText == "user@example.com" }) + assertTrue(emailSegment.isNotEmpty(), "Should have EmailSegment") + assertTrue(emailSegment.any { it.segmentText == "user@example.com" }, "Email should be user@example.com") val textSegments = allSegments.filterIsInstance() - assertTrue("Should have prefix ほむほむ", textSegments.any { it.segmentText == "ほむほむ" }) - assertTrue("Should have suffix ほげほげ", textSegments.any { it.segmentText == "ほげほげ" }) + assertTrue(textSegments.any { it.segmentText == "ほむほむ" }, "Should have prefix ほむほむ") + assertTrue(textSegments.any { it.segmentText == "ほげほげ" }, "Should have suffix ほげほげ") } @Test @@ -204,10 +204,10 @@ class RichTextParserMultibyteTest { val allSegments = state.paragraphs.flatMap { it.words } val emailSegment = allSegments.filterIsInstance() - assertTrue("Should have EmailSegment", emailSegment.isNotEmpty()) - assertTrue("Email should be user@example.com", emailSegment.any { it.segmentText == "user@example.com" }) + assertTrue(emailSegment.isNotEmpty(), "Should have EmailSegment") + assertTrue(emailSegment.any { it.segmentText == "user@example.com" }, "Email should be user@example.com") val textSegments = allSegments.filterIsInstance() - assertTrue("Should have suffix ふがふが", textSegments.any { it.segmentText == "ふがふが" }) + assertTrue(textSegments.any { it.segmentText == "ふがふが" }, "Should have suffix ふがふが") } } diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParserTest.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParserTest.kt index f0f8ff5a94..68361d41c3 100644 --- a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParserTest.kt +++ b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/richtext/RichTextParserTest.kt @@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.commons.richtext import com.vitorpamplona.amethyst.commons.model.EmptyTagList import com.vitorpamplona.amethyst.commons.model.ImmutableListOfLists -import junit.framework.TestCase import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -688,17 +687,17 @@ class RichTextParserTest { val state = RichTextParser() .parseText(textToParse, EmptyTagList, null) - TestCase.assertEquals( + assertEquals( "relay.shitforce.one, relayable.org, universe.nostrich.land, nos.lol, universe.nostrich.land?lang=zh, universe.nostrich.land?lang=en, relay.damus.io, relay.nostr.wirednet.jp, offchain.pub, nostr.rocks, relay.wellorder.net, nostr.oxtr.dev, universe.nostrich.land?lang=ja, relay.mostr.pub, nostr.bitcoiner.social, Nostr-Check.com, MR.Rabbit, Ancap.su, miceliomad@miceliomad.github.io/nostr/, zapper.lol, smies.me, baller.hodl", state.urlSet.withoutScheme.joinToString(", "), ) - TestCase.assertEquals( + assertEquals( "jb55@jb55.com, Snowden@Nostr-Check.com, cameri@elder.nostr.land, natalie@NostrVerified.com, alanbwt@nostrplebs.com, rick@no.str.cr, shawn@shawnyeager.com, 0xtr@oxtr.dev, pavol@rusnak.io, caitlin@nostrverified.com, ralf@snort.social, stacksats@nostrplebs.com, MrHodl@nostrpurple.com, _@mikedilger.com, jascha@relayable.org, Nakadaimon@nostrplebs.com, KeithMukai@nostr.seedsigner.com, theguyswann@NostrVerified.com, dk@stacker.news, npub1z7eqn5603ltuxr77w70t3sasep8hyngzr6lxqpa9hfcqjwe9wmdqhw0qhv@nost.vip, miljan@primal.net, jared@nostrplebs.com, radii@orangepill.dev, _@katieannbaker.com, giacomozucco@BitcoinNostr.com, kr@stacker.news, phil@nostrpurple.com, angela@nostr.world, mason@lacosanostr.com, lau@nostr.report, damascusrex@iris.to, nym@nostr.fan, nico@nostrplebs.com, seekerdreamer1@stacker.news, thesamecat@iris.to, nitesh@noderunner.wtf, gpt3@jb55.com, byzantine@stacker.news, wealththeory@nostrplebs.com, gug@nostrplebs.com, lana@b.tc, shevacai@nostrplebs.com, joe@nostrpurple.com, simplestbitcoinbook@nostrplebs.com, knutsvanholm@iris.to, rs@zbd.ai, GRANTGILLIAM@grantgilliam.com, lifeloveliberty@iris.to, npub1s9c53smfq925qx6fgkqgw8as2e99l2hmj32gz0hjjhe8q67fxdvs3ga9je@nost.vip, arbedout@granddecentral.com, glowleaf@nostrplebs.com, modus@lacosanostr.com, anil@bitcoinnostr.com, documentingbtc@uselessshit.co, wolfbearclaw@nostr.messagepush.io, _@amboss.space, k3tan@k3tan.com, wolzie@BitcoinNostr.com, trey@nostrplebs.com, woody@fountain.fm, cosmicdimension@nostrplebs.com, mirbtc@getalby.com, marks@nostrplebs.com, barrensatin40@walletofsatoshi.com, alexemidio@alexemidio.github.io, Jenn@mintgreen.co, spacemonkey@nostrich.love, ishak@nostrplebs.com, GrassFedBitcoin@start9.com, ninoholds@nostrplebs.com, satcap@nostr.satcap.io, tpmoreira@nostrplebs.com, force2b@nostrplebs.com, hendrix@nostrplebs.com, TXMC@alphabetasoup.tv, pipleb@iris.to, reallhex@terranostr.com, nicb@nicb.me, NabismoPrime@BostonBTC.com, paco@iris.to, globalstatesmen@nostrplebs.com, _@NostrNet.work, crayonsmell@habel.net, ToxiKat27@Bitcoiner.social, jtrag@BitcoinNostr.com, joemartinmusic@nostrplebs.com, ph@nostrplebs.com, horse@iris.to, kp@no.str.cr, rebornbitcoiner@getalby.com, toshi@nostr-check.com, freeborn@nostrplebs.com, blee@bitcoiner.social, SatsTonight@BitcoinNostr.com, freeverification@Nostr-Check.com, cowmaster@getalby.com, hacker818@iris.to, amandabitcasa@nostrplebs.com, tiago@nostrplebs.com, sepehr@nostribe.com, gfy@stacker.news, cryptojournaal@iris.to, bon@nostrplebs.com, bot@binarywatch.org, moritz@getalby.com, hodlish@Nostr-Check.com, HolgerHatGarKeineNode@nip05.easify.de, joe@jaxo.github.io, hahattpro@iris.to, bensima@simatime.com, satan@nostrcheck.me, radvladdy@nostrplebs.com, yidneth@getalby.com, bellatrix@iris.to, securecoop@iris.to, charliesurf@ln.tips, bitcoinatm@Nostr-Check.com, lnstallone@allmysats.com, L0laL33tz@cashu.me, Lommy@nostrplebs.com, jgmontoya@nostrplebs.com, bavarianledger@iris.to, operator@brb.io, tiotito@nostriches.net, javi@www.javiergonzalez.io, geekigai@nostrplebs.com, Mr.Rabbit@BitcoinNostr.com, kilicl@nostr-check.com, alexbit@nostrbr.online, william@nostrplebs.com, oneezra@nostrplebs.com, iceandfirebtc@nostrplebs.com, nostrgang@nostrplebs.com, npub1vez5zekuzc3qk989q5gtly2zg9k2gz4l3wuplv5xs8y3se09yussg4vp7p@carteclip.com, Sqvaznyak@uselessshit.co, braj@nostrplebs.com, libertus@getalby.com, ZoeBoudreault@id.nostrfy.me, dnilso@iris.to, shroom@nostrplebs.com, olegaba@olegaba.com, wasabi@nostrplebs.com, artur@getalby.com, ihsanmd@getalby.com, satoshee@vida.page, ancapsu@getalby.com, niceaction@www.niceaction.com, seak@nostrplebs.com, andy@nodeless.io, pinkyjay@nostrplebs.com, criptobastardo@nostrplebs.com, lacosanostr@lacosanostr.com, teejem@nostrplebs.com, mewj@elder.nostr.land, thetonewrecker@nostrplebs.com, yodatravels@iris.to, bitcoin69@iris.to, Zzar@nostrplebs.com, vidalbidi@getalby.com, juangalt@current.ninja, dean@nostrplebs.com, alex2@nostrverified.com, trooper@iris.to, satscoinsv@getalby.com, aarbtc@iris.to, _@gue.yogsite.com, nostrmemes@iris.to, btcpavao@iris.to, Anonymous@BitcoinNostr.com, zoltanab@iris.to, katsu@onsats.org, bryan@nonni.io, pedromvpg@pedromvpg.com, sonicstudio@getalby.com, kounsellor@nostrplebs.com, katieross@nostrplebs.com, iamlj@iris.to, Zach@BitcoinNostr.com, amouranth@nostrcheck.me, hss5qy@getalby.com, dpcpw@iris.to, bitcoinfinity@nostrplebs.com, TonySeries@BitcoinNostr.com, kuobano@nostrplebs.com, kitakripto@BitcoinNostr.com, _@localhost.re, alxc@uselessshit.co, kukryr@orangepill.dev, saidah@nostrplebs.com, sharon21m@nostr.fan, dmn@noderunners.org, nela_at_nostrica2023@Nostr-Check.com, xbologg@nanostr.deno.dev, btcurenas@nostr.fan, amaluenda@getalby.com, queenb@nostrplebs.com, ezekiel@Nostr-Check.com, marakesh@getalby.com, storm@reddirtmining.io, _b_o_n_e_s_@stacker.news, bdichdbd@stacker.news, j9@nostrplebs.com, nokyctranslate@iris.to, Neomobius_at_mstdn.jp@mostr.pub, paddepadde@getcurrent.io, val@nostrplebs.com, dishwasher_iot@wlvs.space, 1@justinrezvani.com, sshh@nostrplebs.com, mk05@iris.to, herald@bitcoin-herald.org, nostr@pos.btcpayserver.it, thumpgofast@NostrVerified.com, neo@elder.nostr.land, alchemist@electronalchemy.com, timp@iris.to, ken@BitcoinNostr.com, irebus@nostr.red, stim4444@no.str.cr, layerlnw@nostr.fan, enhickman@enhickman.net, LiveFreeBTC@livefreebtc.org, aptx4869@aptx4869.app, khalil@klouche.com, nsec@ittybitty.tips, bigfish@iris.to, oldschool@iris.to, danton@nostrplebs.com, bitcoinzavior@nostrplebs.com, BitcoinSermons@BitcoinNostr.com, uknwlinux@plebs.place, satoshism@nostrplebs.com, jon@nostrplebs.com, hobozakki@nostrplebs.com, sirgalahodl@satstream.me, victor@lnmarkets.com, jacksmies@iris.to, chemaclass@snort.social, jayson@tautic.com, jesterhodl@jesterhodl.com, Bitcoin_Gamer_21@bitcoin-21.org, water-bot@gourcetools.github.io, w3crypto@iris.to, makaveli@nostrplebs.com, jamieanders@ln.tips, cball@nostrplebs.com, laura@nostrich.zone, kaz@reddirtmining.io, verismus@nostrplebs.com, bitpetro@nostrplebs.com, nossence@nossence.xyz, manbearpig@nostrplebs.com, johnsmith@nostrplebs.com, bitcoin@bitcoinedu.com, knggolf@nostrplebs.com, nighthaven@iris.to, xbt_fi@iris.to, gencarlosq17@iris.to, rotciv@plebs.place, Merlin@bitcoinnostr.com, yeg0rpetrov@iris.to, baloo@nostrpurple.com, jamesgospodyn@nostr.theorangepillapp.com, carl@armadalabs.studio, btcportal@nostrplebs.com, mjb@nostrplebs.com, esbewolkt@nostr.fan, koukos@iris.to, davekrock@NostrVerified.com, Bitcoinlovelife@BitcoinNostr.com, xolagl2@getalby.com, remoney@nostrplebs.com, roberto@bitcoiner.chat, cryptolazyninja@stacker.news, kimymt@getalby.com, benderlogic@rogue.earth, MAESTRO@BitcoinNostr.com, travis@west.report, coffeelover@nostrplebs.com, shadowysuperstore@shadowysuperstore.com, npub1wg2dsjnh0g7phheq23v288k0mj8x75fffmq7rghtkhv53027hnassf4w8t@nost.vip, eynhaender@nostrplebs.com, b1ackswan@nostrplebs.com, mrbitc0in@nostrplebs.com, jedi@nostrplebs.com, cloudnull@nostrplebs.com, Mrwh0@Mrwh0.github.io, shinohai@iris.to, awoi@iris.to, jcope101@nostrplebs.com, murmur@nostrplebs.com, dario@nostrplebs.com, leonawankum@BitcoinNostr.com, phil@iris.to, thatirdude@nostrplebs.com, K_hole@ketamine.com, 2@lokuyow.github.io, hazey@iris.to, HeineNon@tomottodx.github.io, titan@nostrplebs.com", state.urlSet.emails.joinToString(", "), ) - TestCase.assertEquals( + assertEquals( "", state.urlSet.withScheme.joinToString(", "), ) @@ -4046,7 +4045,7 @@ class RichTextParserTest { .forEachIndexed { index, seg -> assertEquals( expectedResult[index], - "${seg.javaClass.simpleName.replace("Segment", "")}(${seg.segmentText})", + "${seg::class.simpleName!!.replace("Segment", "")}(${seg.segmentText})", ) } @@ -4183,7 +4182,7 @@ class RichTextParserTest { .forEachIndexed { index, seg -> assertEquals( expectedResult[index], - "${seg.javaClass.simpleName.replace("Segment", "")}(${seg.segmentText})", + "${seg::class.simpleName!!.replace("Segment", "")}(${seg.segmentText})", ) } } @@ -4216,7 +4215,7 @@ class RichTextParserTest { .forEachIndexed { index, seg -> assertEquals( expectedResult[index], - "${seg.javaClass.simpleName.replace("Segment", "")}(${seg.segmentText})", + "${seg::class.simpleName!!.replace("Segment", "")}(${seg.segmentText})", ) } } @@ -4248,7 +4247,7 @@ class RichTextParserTest { .forEachIndexed { index, seg -> assertEquals( expectedResult[index], - "${seg.javaClass.simpleName.replace("Segment", "")}(${seg.segmentText})", + "${seg::class.simpleName!!.replace("Segment", "")}(${seg.segmentText})", ) } } @@ -4280,7 +4279,7 @@ class RichTextParserTest { .forEachIndexed { index, seg -> assertEquals( expectedResult[index], - "${seg.javaClass.simpleName.replace("Segment", "")}(${seg.segmentText})", + "${seg::class.simpleName!!.replace("Segment", "")}(${seg.segmentText})", ) } } @@ -4312,7 +4311,7 @@ class RichTextParserTest { .forEachIndexed { index, seg -> assertEquals( expectedResult[index], - "${seg.javaClass.simpleName.replace("Segment", "")}(${seg.segmentText})", + "${seg::class.simpleName!!.replace("Segment", "")}(${seg.segmentText})", ) } } @@ -4357,7 +4356,7 @@ class RichTextParserTest { .forEachIndexed { index, seg -> assertEquals( expectedResult[index], - "${seg.javaClass.simpleName.replace("Segment", "")}(${seg.segmentText})", + "${seg::class.simpleName!!.replace("Segment", "")}(${seg.segmentText})", ) } } @@ -4395,7 +4394,7 @@ class RichTextParserTest { .forEachIndexed { index, seg -> assertEquals( expectedResult[index], - "${seg.javaClass.simpleName.replace("Segment", "")}(${seg.segmentText})", + "${seg::class.simpleName!!.replace("Segment", "")}(${seg.segmentText})", ) } } @@ -4451,7 +4450,7 @@ class RichTextParserTest { paragraph.words.forEach { seg -> println( "\"${ - seg.javaClass.simpleName.replace( + seg::class.simpleName!!.replace( "Segment", "", ) diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/ui/note/ReplyContextTest.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/ui/note/ReplyContextTest.kt index 6226acc97c..e0d5791888 100644 --- a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/ui/note/ReplyContextTest.kt +++ b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/ui/note/ReplyContextTest.kt @@ -30,10 +30,10 @@ import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNull -import org.junit.Assert.assertTrue -import org.junit.Test +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull +import kotlin.test.assertTrue class ReplyContextTest { private val parentEventId = "b857504288c18a15950dd05b9e8772c62ca6289d5aac373c0a8ee5b132e94e7c" diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/util/CodePointsTest.kt b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/util/CodePointsTest.kt index ee7ea2c932..23b5970edc 100644 --- a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/util/CodePointsTest.kt +++ b/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/util/CodePointsTest.kt @@ -20,9 +20,9 @@ */ package com.vitorpamplona.amethyst.commons.util -import org.junit.Assert.assertArrayEquals -import org.junit.Assert.assertEquals -import org.junit.Test +import kotlin.test.Test +import kotlin.test.assertContentEquals +import kotlin.test.assertEquals class CodePointsTest { // ---- codePointCharCount ---- @@ -44,30 +44,30 @@ class CodePointsTest { @Test fun toCharsRoundTripsAscii() { - assertArrayEquals(charArrayOf('A'), codePointToChars(0x0041)) + assertContentEquals(charArrayOf('A'), codePointToChars(0x0041)) } @Test fun toCharsRoundTripsLastBmp() { - assertArrayEquals(charArrayOf('￿'), codePointToChars(0xFFFF)) + assertContentEquals(charArrayOf('￿'), codePointToChars(0xFFFF)) } @Test fun toCharsProducesSurrogatePairForGrinningFace() { // U+1F600 (😀) is encoded as the surrogate pair (0xD83D, 0xDE00). - assertArrayEquals(charArrayOf('\uD83D', '\uDE00'), codePointToChars(0x1F600)) + assertContentEquals(charArrayOf('\uD83D', '\uDE00'), codePointToChars(0x1F600)) } @Test fun toCharsProducesSurrogatePairForFirstSupplementary() { // U+10000 -> (0xD800, 0xDC00). - assertArrayEquals(charArrayOf('\uD800', '\uDC00'), codePointToChars(0x10000)) + assertContentEquals(charArrayOf('\uD800', '\uDC00'), codePointToChars(0x10000)) } @Test fun toCharsProducesSurrogatePairForLastCodePoint() { // U+10FFFF -> (0xDBFF, 0xDFFF). - assertArrayEquals(charArrayOf('\uDBFF', '\uDFFF'), codePointToChars(0x10FFFF)) + assertContentEquals(charArrayOf('\uDBFF', '\uDFFF'), codePointToChars(0x10FFFF)) } // ---- String.codePointAtKmp ---- @@ -141,11 +141,11 @@ class CodePointsTest { for (cp in samples) { val chars = codePointToChars(cp) val asString = chars.concatToString() - assertEquals("round-trip code point U+${cp.toString(16).uppercase()}", cp, asString.codePointAtKmp(0)) + assertEquals(cp, asString.codePointAtKmp(0), "round-trip code point U+${cp.toString(16).uppercase()}") assertEquals( - "char count for U+${cp.toString(16).uppercase()}", chars.size, codePointCharCount(cp), + "char count for U+${cp.toString(16).uppercase()}", ) } } diff --git a/commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/CliffDetectorTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/CliffDetectorTest.kt similarity index 100% rename from commons/src/commonTest/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/CliffDetectorTest.kt rename to commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/CliffDetectorTest.kt From ed37020fbf75872b37806a7694aa9d0e547088b1 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 9 Jun 2026 20:20:29 +0000 Subject: [PATCH 2/2] ci: run shared commonTest on iOS for :commons The test-quartz-ios job only compiled :commons production code for iOS (compileKotlinIos*), never the commonTest source set, so a test using a JVM-only API compiled fine on JVM/Android and shipped green while silently breaking compileTestKotlinIosSimulatorArm64. Mirror the quartz step: run :commons:iosSimulatorArm64Test (compiles + runs the shared commonTest suite on the simulator) and :commons:compileTestKotlinIosArm64 (device-only compile drift). This keeps commonTest KMP-clean going forward. --- .github/workflows/build.yml | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 092ea9e37d..7abc8593ca 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -227,16 +227,22 @@ jobs: :quartz:compileTestKotlinIosArm64 # :commons gained iosArm64 + iosSimulatorArm64 targets in Phase 2 of the - # iOS plan. Compile-only for now — actual UI / lifecycle wiring will - # land with the iosApp module in Phase 3. The container that runs Claude - # Code can't extract the Kotlin/Native LLVM toolchain (sandbox limit), - # so this is the first place commonMain Compose code is actually - # type-checked against an Apple Native frontend. - - name: Compile Commons for iOS + # iOS plan. Actual UI / lifecycle wiring will land with the iosApp module + # in Phase 3, but the shared commonMain + commonTest sources are already + # built here against an Apple Native frontend. Same two-task shape as + # quartz above: + # - iosSimulatorArm64Test compiles AND runs the shared commonTest suite + # on the simulator. commonTest is built for every target, so a test + # reaching for a JVM-only API (JUnit, javaClass, @JvmStatic, or a + # jvmAndroid-only symbol) breaks the Apple build even though + # :commons:jvmTest stays green — this is the job that catches it. + # - compileTestKotlinIosArm64 catches device-only compile drift + # (iosArm64 = aarch64-apple-ios) without needing a physical device. + - name: Test Commons on iOS run: | ./gradlew \ - :commons:compileKotlinIosSimulatorArm64 \ - :commons:compileKotlinIosArm64 + :commons:iosSimulatorArm64Test \ + :commons:compileTestKotlinIosArm64 - name: Upload iOS Test Reports uses: actions/upload-artifact@v7