Michael
Michael

Reputation: 54705

java.lang.VerifyError when writing Android tests with traits in Kotlin

I'm trying to create a trait for Android's InstrumentationTestCase that contains an abstract property and a method that uses this property. Unfortunately when I run this test it crashes with a java.lang.VerifyError. So here's a code sample that causes the crash:

trait ExtendedInstrumentationTestCase : InstrumentationTestCase {
    val string: String

    fun printString(): Unit {
        println(string)
    }
}

class MyApplicationTestCase :
        ApplicationTestCase<Application>(javaClass<Application>()),
        ExtendedInstrumentationTestCase {

    override val string: String = "test"

    override fun setUp() {
        super<ApplicationTestCase>.setUp()

        printString()
    }

    override fun tearDown() {
        super<ApplicationTestCase>.tearDown()
    }
}

And that's what I get from the logcat:

W/dalvikvm﹕ VFY: Lcom/kotlintest/MyApplicationTestCase; is not instance of Landroid/test/InstrumentationTestCase;
W/dalvikvm﹕ VFY: bad arg 0 (into Landroid/test/InstrumentationTestCase;)
W/dalvikvm﹕ VFY:  rejecting call to Lcom/kotlintest/ExtendedInstrumentationTestCase$$TImpl;.printString (Landroid/test/InstrumentationTestCase;)V
W/dalvikvm﹕ VFY:  rejecting opcode 0x71 at 0x0000
W/dalvikvm﹕ VFY:  rejected Lcom/kotlintest/MyApplicationTestCase;.printString ()V
W/dalvikvm﹕ Verifier rejected class Lcom/kotlintest/MyApplicationTestCase;
W/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0xb1e90648)
E/AndroidRuntime﹕ FATAL EXCEPTION: main
    java.lang.VerifyError: com/kotlintest/MyApplicationTestCase
            at java.lang.Class.getDeclaredConstructors(Native Method)
            at java.lang.Class.getConstructors(Class.java:459)
            at android.test.suitebuilder.TestGrouping$TestCasePredicate.hasValidConstructor(TestGrouping.java:228)
            at android.test.suitebuilder.TestGrouping$TestCasePredicate.apply(TestGrouping.java:217)
            at android.test.suitebuilder.TestGrouping$TestCasePredicate.apply(TestGrouping.java:213)
            at android.test.suitebuilder.TestGrouping.select(TestGrouping.java:172)
            at android.test.suitebuilder.TestGrouping.selectTestClasses(TestGrouping.java:162)
            at android.test.suitebuilder.TestGrouping.testCaseClassesInPackage(TestGrouping.java:156)
            at android.test.suitebuilder.TestGrouping.addPackagesRecursive(TestGrouping.java:117)
            at android.test.suitebuilder.TestSuiteBuilder.includePackages(TestSuiteBuilder.java:102)
            at android.test.InstrumentationTestRunner.onCreate(InstrumentationTestRunner.java:366)
            at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4435)
            at android.app.ActivityThread.access$1300(ActivityThread.java:141)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1316)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:5103)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:525)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
            at dalvik.system.NativeStart.main(Native Method)

And when I make the printString() method an extension method my tests start working:

trait ExtendedInstrumentationTestCase : InstrumentationTestCase {
    val string: String
}

fun ExtendedInstrumentationTestCase.printString(): Unit {
    println(string)
}

As far as I understand the bug is in Dalvik, but I'd like to known exactly where the bug is, why it happens and how I can change the first version of my trait to make this code work.

Upvotes: 1

Views: 955

Answers (1)

Alexander Udalov
Alexander Udalov

Reputation: 32776

This is a bug in Kotlin compiler: KT-3006.

The problem is you're inheriting from a trait which requires a class, without invoking the superclass' constructor. Compiler incorrectly allows this and it blows up later at runtime.

The correct fix would be to invoke InstrumentationTestCase's constructor explicitly:

class MyApplicationTestCase :
        ApplicationTestCase<Application>(javaClass<Application>()),
        ExtendedInstrumentationTestCase,
        InstrumentationTestCase() {
...

Upvotes: 4

Related Questions