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

Improve error message for Robolectric.buildActivity() if provided an abstract class #8997

Closed
david-allison opened this issue Apr 16, 2024 · 4 comments · Fixed by #9085
Closed

Comments

@david-allison
Copy link

david-allison commented Apr 16, 2024

Is your feature request related to a problem? Please describe.
A developer wanted help with their code producing the following error message

Kotlin

        val controller = Robolectric.buildActivity(AbstractClass::class.java)

Stack Trace

    java.lang.RuntimeException: error instantiating com.ichi2.anki.AbstractFlashcardViewer
        at org.robolectric.util.ReflectionHelpers.callConstructor(ReflectionHelpers.java:440)
        at org.robolectric.Robolectric.buildActivity(Robolectric.java:113)
        at org.robolectric.Robolectric.buildActivity(Robolectric.java:78)
        at com.ichi2.anki.AbstractFlashcardViewerCommandTest.testRefreshIfRequired(AbstractFlashcardViewerCommandTest.kt:163)
        Caused by:
        java.lang.InstantiationException
            at java.base/jdk.internal.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48)
            at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
            at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
            at org.robolectric.util.ReflectionHelpers.callConstructor(ReflectionHelpers.java:438)
            ... 3 more

Describe the solution you'd like
The error message should state that the provided class is abstract (and that abstract classes are not supported)

Describe alternatives you've considered
N/A

Additional context

Sample isAbstract checking code

java.lang.reflect.Modifier.isAbstract(clazz.getModifiers())

https://stackoverflow.com/a/1072895

public static <R> R callConstructor(
Class<? extends R> clazz, ClassParameter<?>... classParameters) {
perfStatsCollector.incrementCount("ReflectionHelpers.callConstructor-" + clazz.getName());
try {
final Class<?>[] classes = ClassParameter.getClasses(classParameters);
final Object[] values = ClassParameter.getValues(classParameters);
Constructor<? extends R> constructor = clazz.getDeclaredConstructor(classes);
constructor.setAccessible(true);
return constructor.newInstance(values);
} catch (InstantiationException e) {
throw new RuntimeException("error instantiating " + clazz.getName(), e);
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof RuntimeException) {
throw (RuntimeException) e.getTargetException();
}
if (e.getTargetException() instanceof Error) {
throw (Error) e.getTargetException();
}
throw new RuntimeException(e.getTargetException());
} catch (Exception e) {
throw new RuntimeException(e);
}
}

@utzcoz
Copy link
Member

utzcoz commented May 12, 2024

@david-allison I have tried it with Java's abstract class, and compiler prohibit I pass the abstract class extends from Activity with buildActivity. Does Kotlin have differences?

@david-allison
Copy link
Author

david-allison commented May 12, 2024

seems reproducible in Java

package com.ichi2.anki.dialogs.tags;

import android.app.Activity;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;

import androidx.test.ext.junit.runners.AndroidJUnit4;

@RunWith(AndroidJUnit4.class)
public class ActivityJavaTest {

    @Test
    public void robolectricTest() {
        Robolectric.buildActivity(TestActivity.class);
    }
}

abstract class TestActivity extends Activity {

}
Executing tasks: [:AnkiDroid:testAmazonDebugUnitTest, --tests, com.ichi2.anki.dialogs.tags.ActivityJavaTest] in project /Users/davidallison/StudioProjects/Anki-Android

...

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/davidallison/.gradle/caches/transforms-4/a1e787275c3dbd4ad78f089fa3bcbd13/transformed/slf4j-timber-3.1-runtime.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/davidallison/.gradle/caches/transforms-4/27e100f54975c2f39632cf7042c5c127/transformed/slf4j-timber-3.1/jars/classes.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [com.arcao.slf4j.timber.TimberLoggerFactory]

java.lang.RuntimeException: error instantiating com.ichi2.anki.dialogs.tags.TestActivity
	at org.robolectric.util.ReflectionHelpers.callConstructor(ReflectionHelpers.java:457)
	at org.robolectric.Robolectric.instantiateActivity(Robolectric.java:448)
	at org.robolectric.Robolectric.buildActivity(Robolectric.java:120)
	at org.robolectric.Robolectric.buildActivity(Robolectric.java:85)
	at com.ichi2.anki.dialogs.tags.ActivityJavaTest.robolectricest(ActivityJavaTest.java:32)
	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 org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:489)
	at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$2(SandboxTestRunner.java:290)
	at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:104)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:840)
	Suppressed: org.robolectric.android.internal.AndroidTestEnvironment$UnExecutedRunnablesException: Main looper has queued unexecuted runnables. This might be the cause of the test failure. You might need a shadowOf(Looper.getMainLooper()).idle() call.
Caused by: java.lang.InstantiationException
	at java.base/jdk.internal.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
	at org.robolectric.util.ReflectionHelpers.callConstructor(ReflectionHelpers.java:455)
	... 20 more


ActivityJavaTest > robolectricest FAILED
    java.lang.RuntimeException: error instantiating com.ichi2.anki.dialogs.tags.TestActivity
        at org.robolectric.util.ReflectionHelpers.callConstructor(ReflectionHelpers.java:457)
        at org.robolectric.Robolectric.instantiateActivity(Robolectric.java:448)
        at org.robolectric.Robolectric.buildActivity(Robolectric.java:120)
        at org.robolectric.Robolectric.buildActivity(Robolectric.java:85)
        at com.ichi2.anki.dialogs.tags.ActivityJavaTest.robolectricest(ActivityJavaTest.java:32)
        Caused by:
        java.lang.InstantiationException
            at java.base/jdk.internal.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48)
            at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
            at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
            at org.robolectric.util.ReflectionHelpers.callConstructor(ReflectionHelpers.java:455)
            ... 4 more
1 test completed, 1 failed
> Task :AnkiDroid:testAmazonDebugUnitTest FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':AnkiDroid:testAmazonDebugUnitTest'.
> There were failing tests. See the report at: file:///Users/davidallison/StudioProjects/Anki-Android/AnkiDroid/build/reports/tests/testAmazonDebugUnitTest/index.html
* Try:
> Run with --scan to get full insights.
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
For more on this, please refer to https://docs.gradle.org/8.7/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
BUILD FAILED in 8s
62 actionable tasks: 12 executed, 50 up-to-date
16:42:35: Execution finished ':AnkiDroid:testAmazonDebugUnitTest --tests "com.ichi2.anki.dialogs.tags.ActivityJavaTest"'.

@utzcoz
Copy link
Member

utzcoz commented May 12, 2024

Thanks. I will try it again. Looks like Android Studio gives me wrong hints.

@utzcoz
Copy link
Member

utzcoz commented May 20, 2024

@david-allison You can try it with 4.13 snapshot tomorrow or wait 4.13 stable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants