From bcb1afba40d31592831d2fbf5bfab40fd678fcf0 Mon Sep 17 00:00:00 2001 From: "Evgeny @ SimpleX Chat" <259188159+evgeny-simplex@users.noreply.github.com> Date: Thu, 29 Oct 2026 09:55:40 +0000 Subject: [PATCH] ci: desktop snapshots render animations at their end (#7916) * ci: desktop snapshots render animations at their end * ci: re-record the desktop snapshots of the loading link preview --- .../kotlin/chat/simplex/app/LayoutRecorder.kt | 8 +++++++- ...iewComposeLinkViewLoading-dark-font-100.png | Bin 1027 -> 679 bytes ...ewComposeLinkViewLoading-light-font-100.png | Bin 977 -> 617 bytes scripts/ci/desktop-linux/README.md | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/multiplatform/common/src/desktopTest/kotlin/chat/simplex/app/LayoutRecorder.kt b/apps/multiplatform/common/src/desktopTest/kotlin/chat/simplex/app/LayoutRecorder.kt index a8e2c7a999..c2a85dbb38 100644 --- a/apps/multiplatform/common/src/desktopTest/kotlin/chat/simplex/app/LayoutRecorder.kt +++ b/apps/multiplatform/common/src/desktopTest/kotlin/chat/simplex/app/LayoutRecorder.kt @@ -3,6 +3,7 @@ package chat.simplex.app import androidx.compose.runtime.Composable import androidx.compose.ui.ImageComposeScene import androidx.compose.ui.Modifier +import androidx.compose.ui.MotionDurationScale import androidx.compose.ui.layout.FirstBaseline import androidx.compose.ui.layout.LastBaseline import androidx.compose.ui.layout.layout @@ -12,6 +13,7 @@ import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.use +import kotlinx.coroutines.Dispatchers import org.jetbrains.skia.Image import kotlin.math.roundToInt @@ -27,8 +29,12 @@ fun Modifier.fixed(width: Int, height: Int, baseline: Int?): Modifier = layout { layout(width, height, if (baseline == null) emptyMap() else mapOf(FirstBaseline to baseline, LastBaseline to baseline)) { placeable.place(0, 0) } } +private object InstantMotion : MotionDurationScale { + override val scaleFactor = 0f +} + fun renderScene(direction: LayoutDirection = LayoutDirection.Ltr, width: Int = 400, height: Int = 400, frames: Int = 1, content: @Composable () -> Unit): Image = - ImageComposeScene(width = width, height = height, density = Density(1f), layoutDirection = direction, content = content).use { scene -> + ImageComposeScene(width = width, height = height, density = Density(1f), layoutDirection = direction, coroutineContext = Dispatchers.Unconfined + InstantMotion, content = content).use { scene -> (0 until frames).map { scene.render(it * 1_000_000_000L) }.last() } diff --git a/apps/multiplatform/common/src/desktopTest/snapshots/LinkPreviews.PreviewComposeLinkViewLoading-dark-font-100.png b/apps/multiplatform/common/src/desktopTest/snapshots/LinkPreviews.PreviewComposeLinkViewLoading-dark-font-100.png index f4850353df57763d978754a1d0c85c3806dc0880..483a2b9f7946689f1d8b2dee5ff22e501285de6d 100644 GIT binary patch delta 534 zcmV+x0_pvO2&V;*Gk*eJNkl;?TiCn_|JnVs&z` z;^OL{6(={LDs(F17X>VX-sY7;8$BLO1Z7rKF=07 z+ymzfKTgiQsY0R93;HLIMCn0)UVJ@Cv`kwC&n!6##G|k=-DZ z$paUW*$aO**oV`1qtk<(m0ocSbNi8&DdT@U%A|iG?xe?o5KU|l&T($$?L;}Ea zwF76PIxrfY&u&LV#Pr};G}CSV+5+J3Ba$zV|GaEZEqwlU0q?RMfK?;%>j6(%X0LzMjN`vK{Sl=EU)M_kJoG^YeBSV1vyTFT_m$NOYEOMQ+76BoPsLkDBuo~}4~tYkL<{(*Y;P~^+wk*iFsIwn_{hW?9zqgshgEHxK9PR6g^7B_QS8v!+fGr}ikN~hX`HlpU+ai<60~Z=00f4pm Y3OV_L39uVac>n+a07*qoM6N<$f*m~mfdBvi delta 901 zcmV;01A6?Y1%n8XGk*iRNkl z-eRi&fGd-10wIyc7Jm>D0QjfsDm`J_oaSumTAc;$ZE2p>knxF9t`1zw@w4Z$_s1hv z96(6^-cvmrvu^eBzx=IOOd9HE=Hlh6RvSP_rdB8v^6V2mS+#6QN~KZ`pE#X^$4;g9 z%9YG07SlerCBq{lRvTa%U4HKU)SvkxjR$w#aoo__7jnJh@qf(S{c`n$Wc`|zS+#6Q zMn*^T>7Flh;o_y6?~RR(XK-lP4g*ZH`qQ7MfAi}}p(c$7KA3XMVE0QI*!XIizk9oS zu1IQYYVv5;>WoiJgeDVNgUvt#o0Z7~_@+BO-HZ{JGe;g72) zBe`n6P1`FTk&1+5b?4F~$$?`h?I1uE#-vo& zm}~1^nU;uDBqVjk$tn5=2JId|6~&}5F`B-uJCh`HzJ4} z>>R*-=&r9we!JSA=B8O`Z*9rh3%^=XfGRMw=K1vZ?0?AY@83(3^ljala_x-j{AugR ziPPy^yfED>JB0+GiXzgy@12`B+Y1xd|GWWTZ8zJGpE;M&(XlLF(vd|E9LxFMOLh_9 zUPa`_>8x8Kl3Px1O-D>>o7(2TaW@l`%H>RyO6lxalod-ma`N2y43Au&a%@X;Q)U*6 zxjs5(w|@aDY_RLa3~bt-+56tjtSKHyEcWhCVSFS5n_o-K*l>!y`>P`)Np3#LzHITr ztXTG diff --git a/apps/multiplatform/common/src/desktopTest/snapshots/LinkPreviews.PreviewComposeLinkViewLoading-light-font-100.png b/apps/multiplatform/common/src/desktopTest/snapshots/LinkPreviews.PreviewComposeLinkViewLoading-light-font-100.png index f2445d2f18e7150987a641deca7f8469f7e0c112..66e375c1bbdb8a20daa297640f457b97aa6a5b84 100644 GIT binary patch literal 617 zcmeAS@N?(olHy`uVBq!ia0y~yV4MVGyKt}p$<>oK*8nM&VkgfK4j`!ENao$-;1FB7=eo0|zIA6B7_MOn~yh0uUZd8pwl7gH^(L z(QhAJe)93p{O7i_{Pk>wg&52%>J?_r@RVA6uUKx6zgqgF;@_*Ix97cP+}6I@eNvR} zPRrQ7_5Ex9@4e{t?u=@=zj`c?Wg<&#^@H(OSGi70z_>unbkPsb@IrUMC|WI;g-4=RG;imVtI dC9^*Y7rjVxdskP!0+@CfJYD@<);T3K0RR%->0oK*8nM&VkgfK4j`!ENaXQ}1P7e_?6)+|||GGWo0Zk{GRC8qqnoSjM& z*1gi&l(&noYr!tBOB+mr(mwm&EVwj9sJQr*&sqE5#k(wKsA)|}uQ+x6^*hV+hl?M4 z-&^t7=6t1v@TsRomJA>Cr|K{=I59O$U{FwD;NS!yC#D1Su7(B2lo%d#u`nnyI(RS$ z2!T)wlm`(2^Wf4z9!wgd^54cqx71~&po~IV57-BlZ#o*J&V=t{ibbI*Vo_ktMGlk|Gqcgc9qN8oGxu%to^&X zI_Un58Se9|%yidY{(md{{)Gv%XV3DHNSI@LZ{D6?zD7}gVS2m%7{1T(zE(4PP34NV zbslbRZku*zdwVSnnzpt=@$7bgL*Iti50?BzTl@BEGnI7r>@U}@-7U5%E~t-L(;8SZ?RuHqV)a`2I=q_p(uU;g%|HXb}2Zu9N?!o`-AH7AxZ9y_@Eez?-7 zTg}t!6$_s(I<@br^yKfmR)60s{L6p(>8lnO``(+SUcYu$bE?-&yUn)7*3XtMEv=H0 zk}4`GP}sYB*XI25{+YS6Coc83Pk+P^y6wy1l*;2%?`Q4*vFhpOdntFjq_{mNmDKLF zv%S50UDcV`&Cj1S9-rqCD6)+2gMGl&2`VQqHvao_Yr8r7x3^~}{&UuR^}5*ma_&r{ zwWf2w>z-S3GI*^OFey8<)_brJPNmqB^nM0fX!h)IpTLAMYgQu&X%Q~loCIIhy BvsVBB diff --git a/scripts/ci/desktop-linux/README.md b/scripts/ci/desktop-linux/README.md index bd6176f0c7..06211e6396 100644 --- a/scripts/ci/desktop-linux/README.md +++ b/scripts/ci/desktop-linux/README.md @@ -31,7 +31,7 @@ Queueing for a runner adds from under a minute to 20 minutes. Results are artifa ## Snapshots -`:common:desktopTest` runs `SnapshotTest` (`apps/multiplatform/common/src/desktopTest/kotlin/chat/simplex/app/SnapshotTest.kt`). It renders each preview listed in `previewScenes` with Compose's `ImageComposeScene`, 402 dp wide at density 1, one pixel per dp, as tall as the preview's content up to 874 dp, so a preview that fills any height is 874 pixels tall. It renders the light and dark themes without a wallpaper, over the theme's background, and keeps the fourth frame, 3 s of frame time after the first. As the app does for its screens, the test provides an `AppBarHandler` (`LocalAppBarHandler`) and the window state (`simplexWindowState`, sized as the scene). Each preview is rendered at the default font size; the chat item previews (package `views.chat.item`) also at 1.4 and 1.5, the two largest values of the desktop app's font size setting. +`:common:desktopTest` runs `SnapshotTest` (`apps/multiplatform/common/src/desktopTest/kotlin/chat/simplex/app/SnapshotTest.kt`). It renders each preview listed in `previewScenes` with Compose's `ImageComposeScene`, 402 dp wide at density 1, one pixel per dp, as tall as the preview's content up to 874 dp, so a preview that fills any height is 874 pixels tall. It renders the light and dark themes without a wallpaper, over the theme's background, and keeps the fourth frame, 3 s of frame time after the first. The scene's coroutine context sets the animation duration scale (`MotionDurationScale`) to 0, so each animation ends at its first frame and a spinner is drawn the same in every run. As the app does for its screens, the test provides an `AppBarHandler` (`LocalAppBarHandler`) and the window state (`simplexWindowState`, sized as the scene). Each preview is rendered at the default font size; the chat item previews (package `views.chat.item`) also at 1.4 and 1.5, the two largest values of the desktop app's font size setting. `everyPreviewIsRendered` reads the Kotlin sources of `commonMain` and `desktopMain` without comments, and fails when a `@Preview` function is missing from both `previewScenes` and `previewsCallingCore`, or when either names a function absent from the sources; a scene is named `.`. `previewsCallingCore` lists the previews whose layouts validate names or descriptions with the core library (`chatValidName`, `chatJsonLength`), which the test JVM does not load, so they are not rendered. The previews of `androidMain` and of the Android app module are left out too, since they use Android APIs.