cashapp / paparazzi

Render your Android screens without a physical device or emulator
https://cashapp.github.io/paparazzi/
Apache License 2.0
2.32k stars 216 forks source link

Github Actions: Fontconfig head is null, check your fonts or fonts configuration #1555

Closed sdoward closed 2 months ago

sdoward commented 3 months ago

Description When running paparazzi all tests fail with the following...

Caused by: java.lang.RuntimeException: Fontconfig head is null, check your fonts or fonts configuration

This is working locally on masOS but failing on CI using Github actions and linux

Full Stacktrace
``` java.lang.InternalError: java.lang.reflect.InvocationTargetException at java.desktop/sun.font.FontManagerFactory$1.run(FontManagerFactory.java:87) at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) at java.desktop/sun.font.FontManagerFactory.getInstance(FontManagerFactory.java:75) at java.desktop/java.awt.Font.getFont2D(Font.java:526) at java.desktop/java.awt.Font$FontAccessImpl.getFont2D(Font.java:265) at java.desktop/sun.font.FontUtilities.getFont2D(FontUtilities.java:151) at java.desktop/sun.java2d.SunGraphics2D.checkFontInfo(SunGraphics2D.java:672) at java.desktop/sun.java2d.SunGraphics2D.getFontInfo(SunGraphics2D.java:838) at java.desktop/sun.java2d.pipe.GlyphListPipe.drawString(GlyphListPipe.java:46) at java.desktop/sun.java2d.SunGraphics2D.drawString(SunGraphics2D.java:2938) at app.cash.paparazzi.internal.ImageUtils.compareImages(ImageUtils.kt:222) at app.cash.paparazzi.internal.ImageUtils.compareImages$default(ImageUtils.kt:129) at app.cash.paparazzi.internal.ImageUtils.assertImageSimilar(ImageUtils.kt:90) at app.cash.paparazzi.SnapshotVerifier$newFrameHandler$1.handle(SnapshotVerifier.kt:73) at app.cash.paparazzi.Paparazzi$apply$1$evaluate$1.invoke(Paparazzi.kt:73) at app.cash.paparazzi.Paparazzi$apply$1$evaluate$1.invoke(Paparazzi.kt:63) at app.cash.paparazzi.PaparazziSdk$takeSnapshots$3.invoke(PaparazziSdk.kt:301) at app.cash.paparazzi.PaparazziSdk$takeSnapshots$3.invoke(PaparazziSdk.kt:288) at app.cash.paparazzi.PaparazziSdk.withTime(PaparazziSdk.kt:340) at app.cash.paparazzi.PaparazziSdk.takeSnapshots(PaparazziSdk.kt:288) at app.cash.paparazzi.PaparazziSdk.snapshot(PaparazziSdk.kt:179) at app.cash.paparazzi.Paparazzi.snapshot(Paparazzi.kt:111) at app.cash.paparazzi.Paparazzi.snapshot$default(Paparazzi.kt:108) at org.sharethemeal.android.view.ComponentShotTest.progressComponent(ComponentShotTest.kt:37) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:569) 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 com.google.testing.junit.testparameterinjector.PluggableTestRunner$2.evaluate(PluggableTestRunner.java:249) at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61) at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61) at app.cash.paparazzi.Paparazzi$apply$1.evaluate(Paparazzi.kt:79) at org.junit.rules.RunRules.evaluate(RunRules.java:20) at com.google.testing.junit.testparameterinjector.PluggableTestRunner$ContextMethodRule$1.evaluate(PluggableTestRunner.java:433) 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:112) 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:40) at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:60) at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:52) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:569) 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 jdk.proxy1/jdk.proxy1.$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:119) at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:66) at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74) Caused by: java.lang.reflect.InvocationTargetException at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500) at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481) at java.desktop/sun.font.FontManagerFactory$1.run(FontManagerFactory.java:85) ... 71 more Caused by: java.lang.RuntimeException: Fontconfig head is null, check your fonts or fonts configuration at java.desktop/sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1271) at java.desktop/sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:224) at java.desktop/sun.awt.FontConfiguration.init(FontConfiguration.java:106) at java.desktop/sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:706) at java.desktop/sun.font.SunFontManager$2.run(SunFontManager.java:358) at java.desktop/sun.font.SunFontManager$2.run(SunFontManager.java:315) at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) at java.desktop/sun.font.SunFontManager.(SunFontManager.java:315) at java.desktop/sun.awt.FcFontManager.(FcFontManager.java:35) at java.desktop/sun.awt.X11FontManager.(X11FontManager.java:56) ... 77 more progressComponent[SAMSUNG_GALAXY_S10,en_GB,Monetary] java.lang.InternalError: java.lang.reflect.InvocationTargetException at java.desktop/sun.font.FontManagerFactory$1.run(FontManagerFactory.java:87) at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) at java.desktop/sun.font.FontManagerFactory.getInstance(FontManagerFactory.java:75) at java.desktop/java.awt.Font.getFont2D(Font.java:526) at java.desktop/java.awt.Font$FontAccessImpl.getFont2D(Font.java:265) at java.desktop/sun.font.FontUtilities.getFont2D(FontUtilities.java:151) at java.desktop/sun.java2d.SunGraphics2D.checkFontInfo(SunGraphics2D.java:672) at java.desktop/sun.java2d.SunGraphics2D.getFontInfo(SunGraphics2D.java:838) at java.desktop/sun.java2d.pipe.GlyphListPipe.drawString(GlyphListPipe.java:46) at java.desktop/sun.java2d.SunGraphics2D.drawString(SunGraphics2D.java:2938) at app.cash.paparazzi.internal.ImageUtils.compareImages(ImageUtils.kt:222) at app.cash.paparazzi.internal.ImageUtils.compareImages$default(ImageUtils.kt:129) at app.cash.paparazzi.internal.ImageUtils.assertImageSimilar(ImageUtils.kt:90) at app.cash.paparazzi.SnapshotVerifier$newFrameHandler$1.handle(SnapshotVerifier.kt:73) at app.cash.paparazzi.Paparazzi$apply$1$evaluate$1.invoke(Paparazzi.kt:73) at app.cash.paparazzi.Paparazzi$apply$1$evaluate$1.invoke(Paparazzi.kt:63) at app.cash.paparazzi.PaparazziSdk$takeSnapshots$3.invoke(PaparazziSdk.kt:301) at app.cash.paparazzi.PaparazziSdk$takeSnapshots$3.invoke(PaparazziSdk.kt:288) at app.cash.paparazzi.PaparazziSdk.withTime(PaparazziSdk.kt:340) at app.cash.paparazzi.PaparazziSdk.takeSnapshots(PaparazziSdk.kt:288) at app.cash.paparazzi.PaparazziSdk.snapshot(PaparazziSdk.kt:179) at app.cash.paparazzi.Paparazzi.snapshot(Paparazzi.kt:111) at app.cash.paparazzi.Paparazzi.snapshot$default(Paparazzi.kt:108) at org.sharethemeal.android.view.ComponentShotTest.progressComponent(ComponentShotTest.kt:37) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:569) 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 com.google.testing.junit.testparameterinjector.PluggableTestRunner$2.evaluate(PluggableTestRunner.java:249) at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61) at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61) at app.cash.paparazzi.Paparazzi$apply$1.evaluate(Paparazzi.kt:79) at org.junit.rules.RunRules.evaluate(RunRules.java:20) at com.google.testing.junit.testparameterinjector.PluggableTestRunner$ContextMethodRule$1.evaluate(PluggableTestRunner.java:433) 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:112) 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:40) at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:60) at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:52) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:569) 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 jdk.proxy1/jdk.proxy1.$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:119) at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:66) at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74) Caused by: java.lang.reflect.InvocationTargetException at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500) at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481) at java.desktop/sun.font.FontManagerFactory$1.run(FontManagerFactory.java:85) ... 71 more Caused by: java.lang.RuntimeException: Fontconfig head is null, check your fonts or fonts configuration at java.desktop/sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1271) at java.desktop/sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:224) at java.desktop/sun.awt.FontConfiguration.init(FontConfiguration.java:106) at java.desktop/sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:706) at java.desktop/sun.font.SunFontManager$2.run(SunFontManager.java:358) at java.desktop/sun.font.SunFontManager$2.run(SunFontManager.java:315) at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) at java.desktop/sun.font.SunFontManager.(SunFontManager.java:315) at java.desktop/sun.awt.FcFontManager.(FcFontManager.java:35) at java.desktop/sun.awt.X11FontManager.(X11FontManager.java:56) ... 77 more ```

Additional information:

Screenshots If applicable, add screenshots to help explain your problem.

geoff-powell commented 3 months ago

hmm, looks like this is most likely due to your linux configuration for github actions. Do you mind providing the github actions yaml config for the os provided to runs-on:

sdoward commented 3 months ago

@geoff-powell We have self hosted runners which run on Amazon Linux 2023

geoff-powell commented 3 months ago

hmm, you might need to install the fontconfig package as part of your initialization script in your github actions yaml or install font-config on the self hosted runner directly. https://youtrack.jetbrains.com/issue/FL-23394/Smart-Mode-on-a-remote-linux-host-doesnt-start-with-java.lang.RuntimeException-Fontconfig-head-is-null-check-your-fonts-or-fonts#focus=Comments-27-8539455.0-0

pdeveltere commented 2 months ago

The library should work without having to install other packages on the OS. Or at least in that case dependencies should be documented..

It would be interestign to find out why the library needs sun.awt.FontConfiguration

geoff-powell commented 2 months ago

Yep agreed, @jrodbx had the same thought and we now use pre-rendered text instead of relying on java awt. https://github.com/cashapp/paparazzi/commit/5de870aac42a0f5bcb5bbfb3f09a260bd204bcd9

This shouldn't be an issue when the next version is release. Feel free to try out the SNAPSHOT version