paparazzi icon indicating copy to clipboard operation
paparazzi copied to clipboard

paparazzi.gif fails with IllegalArgumentException

Open martirius opened this issue 2 years ago • 4 comments

Description Trying to run paparazzi.gif fails with this stacktrace:

java.lang.IllegalArgumentException: Component 1 width should be a multiple of 2 for colorspace: YUV420J
	at org.jcodec.common.model.Picture.<init>(Picture.java:54)
	at org.jcodec.common.model.Picture.createCropped(Picture.java:94)
	at org.jcodec.common.model.Picture.create(Picture.java:75)
	at org.jcodec.api.transcode.PixelStoreImpl.getPicture(PixelStoreImpl.java:25)
	at org.jcodec.api.SequenceEncoder.encodeNativeFrame(SequenceEncoder.java:94)
	at org.jcodec.api.awt.AWTSequenceEncoder.encodeImage(AWTSequenceEncoder.java:49)
	at app.cash.paparazzi.HtmlReportWriter.writeVideo(HtmlReportWriter.kt:170)
	at app.cash.paparazzi.HtmlReportWriter.access$writeVideo(HtmlReportWriter.kt:60)
	at app.cash.paparazzi.HtmlReportWriter$newFrameHandler$1.close(HtmlReportWriter.kt:109)
	at kotlin.io.CloseableKt.closeFinally(Closeable.kt:56)
	at app.cash.paparazzi.Paparazzi.takeSnapshots(Paparazzi.kt:270)
	at app.cash.paparazzi.Paparazzi.gif(Paparazzi.kt:217)
	at app.cash.paparazzi.Paparazzi.gif$default(Paparazzi.kt:204)
	at com.app.testapp.ui.ScreenshotRule.takeGifLocalized(ScreenshotRule.kt:52)
	at com.app.testapp.ui.ScreenshotRule.takeGifLocalized$default(ScreenshotRule.kt:42)
	at com.app.testapp.ui.login.LoginManualScreenshotTest.login_manual(LoginManualScreenshotTest.kt:17)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at app.cash.paparazzi.Paparazzi$apply$statement$1.evaluate(Paparazzi.kt:118)
	at app.cash.paparazzi.agent.AgentTestRule$apply$1.evaluate(AgentTestRule.kt:17)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
	at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)

Steps to Reproduce Steps to reproduce are simple, just run a test and try to record a gif, even with very simple layout.

The test:

class SimpleGifTest {

    @get:Rule
    val screenshotRule = ScreenshotRule()

    @Test
    fun gifTest() {
        screenshotRule.takeGifLocalized("simple_gif", R.layout.layout_settings_row, locale = "en")
    }
}

The custom test rule:

class ScreenshotRule : TestRule {

    private val paparazzi = Paparazzi(
        deviceConfig = DeviceConfig.PIXEL_4,
        theme = "MaterialTheme.Custom",
        environment = detectEnvironment(),
        supportsRtl = true
    )

    fun takeScreenshotLocalized(
        screenshotName: String,
        @LayoutRes viewResourceId: Int,
        locale: String,
        viewModeller: (view: View) -> View = { v -> v }
    ) {
        paparazzi.unsafeUpdateConfig(deviceConfig = DeviceConfig.PIXEL_4.copy(locale = locale))
        val view = paparazzi.inflate<View>(viewResourceId)

        val modelledView = viewModeller(view)
        paparazzi.snapshot(
            modelledView,
            name = "${screenshotName}_${locale}",
        )
    }

    fun takeGifLocalized(
        screenshotName: String,
        @LayoutRes viewResourceId: Int,
        locale: String,
        viewModeller: (view: View) -> View = { v -> v }
    ) {
        paparazzi.unsafeUpdateConfig(deviceConfig = DeviceConfig.PIXEL_4.copy(locale = locale))
        val view = paparazzi.inflate<View>(viewResourceId)

        val modelledView = viewModeller(view)
        paparazzi.gif(
            modelledView,
            name = "${screenshotName}_${locale}",
        )
    }


    override fun apply(base: Statement, description: Description): Statement {
        return paparazzi.apply(base, description)
    }
}

The layout:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingVertical="@dimen/default_margin">

    <TextView
        android:id="@+id/title_text_view"
        style="@style/TextAppearance.Custom.Headline4"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="@dimen/default_margin"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/disclosure_image_view"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="This is a long title that may go on two lines and it is supposed to, isn't it?" />

    <ImageView
        android:id="@+id/disclosure_image_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="@dimen/default_margin"
        android:scaleType="fitEnd"
        android:src="@drawable/ic_chevron_right_black"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Expected behavior Gif recorded

Additional information:

  • Paparazzi Version: 1.2.0
  • OS: MacOS Ventura 13.2.1
  • Compile SDK: 33
  • Gradle Version: 7.5.1
  • Android Gradle Plugin Version: 7.3.1

martirius avatar Feb 24 '23 15:02 martirius