From b9c768d08667f228e9bf0cefa57607c8f4dd6def Mon Sep 17 00:00:00 2001 From: Michael Hoisie Date: Tue, 2 Apr 2024 08:41:27 -0700 Subject: [PATCH] Reduce references to `android.util.Log$TerribleFailure` in ShadowLog `android.util.Log$TerribleFailure` is a `@hide` class in `android.util.Log`. If tests reference ShadowLog in static blocks or outside of a Robolectric ClassLoader, a NoClassDefFoundError would result. Update TerribleFailureReflector to return a generic Throwable in the `@Constructor` method. Fixes #8957 PiperOrigin-RevId: 621187529 --- .../robolectric/ShadowLogResolutionTest.java | 22 +++++++++++++++++++ .../org/robolectric/shadows/ShadowLog.java | 9 ++++---- 2 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 integration_tests/dependency-on-stubs/src/test/java/org/robolectric/ShadowLogResolutionTest.java diff --git a/integration_tests/dependency-on-stubs/src/test/java/org/robolectric/ShadowLogResolutionTest.java b/integration_tests/dependency-on-stubs/src/test/java/org/robolectric/ShadowLogResolutionTest.java new file mode 100644 index 00000000000..0324e81c1e4 --- /dev/null +++ b/integration_tests/dependency-on-stubs/src/test/java/org/robolectric/ShadowLogResolutionTest.java @@ -0,0 +1,22 @@ +package org.robolectric; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.robolectric.shadows.ShadowLog; + +/** + * This test attempts to reference ShadowLow from outside of the context of a Robolectric + * environment. ShadowLog contained reflector interfaces that referenced `@hide` classes (e.g. + * TerribleFailure), which cannot be referenced outside of a Robolectric ClassLoader. + * + * @see Issue 8957 for more + * details. + */ +@RunWith(JUnit4.class) +public class ShadowLogResolutionTest { + @Test + public void reference_shadowLog_outsideRobolectric() { + ShadowLog.stream = System.out; + } +} diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLog.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLog.java index 82e4e43d211..b71d462b388 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLog.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLog.java @@ -115,17 +115,17 @@ protected static int wtf(String tag, String msg) { protected static int wtf(String tag, String msg, Throwable throwable) { addLog(Log.ASSERT, tag, msg, throwable); // invoking the wtfHandler - TerribleFailure terribleFailure = + Throwable terribleFailure = reflector(TerribleFailureReflector.class).newTerribleFailure(msg, throwable); if (wtfIsFatal) { Util.sneakyThrow(terribleFailure); } TerribleFailureHandler terribleFailureHandler = reflector(LogReflector.class).getWtfHandler(); if (RuntimeEnvironment.getApiLevel() >= L.SDK_INT) { - terribleFailureHandler.onTerribleFailure(tag, terribleFailure, false); + terribleFailureHandler.onTerribleFailure(tag, (TerribleFailure) terribleFailure, false); } else { reflector(TerribleFailureHandlerReflector.class, terribleFailureHandler) - .onTerribleFailure(tag, terribleFailure); + .onTerribleFailure(tag, (TerribleFailure) terribleFailure); } return 0; } @@ -374,7 +374,8 @@ interface TerribleFailureHandlerReflector { @ForType(TerribleFailure.class) interface TerribleFailureReflector { + // The return value should be generic because TerribleFailure is a hidden class. @Constructor - TerribleFailure newTerribleFailure(String msg, Throwable cause); + Throwable newTerribleFailure(String msg, Throwable cause); } }