Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception thrown when snapshotting GoogleMap composable #709

Closed
BrianGardnerAtl opened this issue Jan 30, 2023 · 2 comments
Closed

Exception thrown when snapshotting GoogleMap composable #709

BrianGardnerAtl opened this issue Jan 30, 2023 · 2 comments
Labels
bug Something isn't working
Milestone

Comments

@BrianGardnerAtl
Copy link
Collaborator

Description
The GoogleMap composable is able to render in Android Studio Flamingo | 2022.2.1 Canary 10 (haven't tested on other versions yet) but attempting to snapshot test it using paparazzi throws an exception.

Steps to Reproduce
Repro project here: https://github.com/BrianGardnerAtl/PaparazziMapRepro
View the PaparazziMap composable and see how it renders (a blank box) in the Preview.
Attempt to record paparazzi screenshot from PaparazziMapText and see the exception

Expected behavior
Suuccessful snapshot taken of the composable. Since the GoogleMap composable short circuits and displays a plain box I don't expect to see the actual map, but not crashing would be nice.

Additional information:

  • Paparazzi Version: 1.2.0
  • OS: mac Ventura 13.2
  • Compile SDK: 33
  • Gradle Version:
  • Android Gradle Plugin Version:

Exception log

java.lang.NullPointerException: Cannot read field "metaData" because the return value of "com.google.android.gms.common.wrappers.PackageManagerWrapper.getApplicationInfo(String, int)" is null
	at com.google.android.gms.common.internal.zzag.zzc(com.google.android.gms:play-services-basement@@18.0.0:5)
	at com.google.android.gms.common.internal.zzag.zza(com.google.android.gms:play-services-basement@@18.0.0:1)
	at com.google.android.gms.common.GooglePlayServicesUtilLight.isGooglePlayServicesAvailable(com.google.android.gms:play-services-basement@@18.0.0:7)
	at com.google.android.gms.common.GooglePlayServicesUtil.isGooglePlayServicesAvailable(com.google.android.gms:play-services-base@@18.0.1:2)
	at com.google.android.gms.maps.internal.zzcb.zza(com.google.android.gms:play-services-maps@@18.1.0:3)
	at com.google.android.gms.maps.MapsInitializer.initialize(com.google.android.gms:play-services-maps@@18.1.0:4)
	at com.google.android.gms.maps.MapsInitializer.initialize(com.google.android.gms:play-services-maps@@18.1.0:1)
	at com.google.android.gms.maps.zzah.zzb(com.google.android.gms:play-services-maps@@18.1.0:2)
	at com.google.android.gms.maps.zzah.createDelegate(com.google.android.gms:play-services-maps@@18.1.0:1)
	at com.google.android.gms.dynamic.DeferredLifecycleHelper.zaf(com.google.android.gms:play-services-base@@18.0.1:6)
	at com.google.android.gms.dynamic.DeferredLifecycleHelper.onCreate(com.google.android.gms:play-services-base@@18.0.1:1)
	at com.google.android.gms.maps.MapView.onCreate(com.google.android.gms:play-services-maps@@18.1.0:4)
	at com.google.maps.android.compose.GoogleMapKt.lifecycleObserver$lambda$11(GoogleMap.kt:199)
	at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:360)
	at androidx.lifecycle.LifecycleRegistry.forwardPass(LifecycleRegistry.java:271)
	at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:313)
	at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:210)
	at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:138)
	at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:131)
	at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(AndroidComposeView.android.kt:1147)
	at android.view.View.dispatchAttachedToWindow(View.java:21291)
	at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3491)
	at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498)
	at android.view.ViewGroup.addViewInner(ViewGroup.java:5291)
	at android.view.ViewGroup.addView(ViewGroup.java:5077)
	at android.view.ViewGroup.addView(ViewGroup.java:5017)
	at android.view.ViewGroup.addView(ViewGroup.java:4989)
	at app.cash.paparazzi.Paparazzi.takeSnapshots(Paparazzi.kt:303)
	at app.cash.paparazzi.Paparazzi.snapshot(Paparazzi.kt:200)
	at app.cash.paparazzi.Paparazzi.snapshot$default(Paparazzi.kt:199)
	at app.cash.paparazzi.Paparazzi.snapshot(Paparazzi.kt:195)
	at app.cash.paparazzi.Paparazzi.snapshot$default(Paparazzi.kt:191)
	at com.example.views.PaparazziMapTest.default(PaparazziMapTest.kt:13)
	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:568)
	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:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	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: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)
@BrianGardnerAtl BrianGardnerAtl added the bug Something isn't working label Jan 30, 2023
@jrodbx jrodbx mentioned this issue Mar 25, 2023
@TWiStErRob
Copy link
Contributor

It's quite the hackery, but I was able to get a non-null info propagated to my components (non-Compose) like this:
https://github.com/TWiStErRob/net.twisterrob.sun/blob/1cac2ce24719368edce3a48e9b0fa3cb51a0836c/component/paparazzi/src/main/java/net/twisterrob/sun/test/screenshots/UsableActivityHack.kt#L139

@adamalyyan
Copy link
Collaborator

@TWiStErRob we tried a similar solution using ByteBuddy interceptors to workaround the Play services issue however we end up getting crashes deeper in the maps library that cannot be solve with intercepting calls.

We looked into how this is handled in Preview and the GoogleMap's composable checks to see if LocalInspectionMode is set and if it is, it will replace the maps composable with an empty box. Preview sets LocalInspectionMode for all composables it renders so it doesn't crash.

We do not want to set LocalInspectionMode globally in Paparazzi as the goal is to take a snapshot of the true production output and LocalInspectionMode could be used to show debug options. The solution we're recommending will be for users to wrap their composables with CompositionLocalProvider and set LocalInspectionMode for their specific views.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants