LeoColman
LeoColman

Reputation: 7153

Intellij IDEA won't stop at Kotlin breakpoints when running with JUnit

When trying to execute some Kotlin code, while also using JUnit, the Intellij IDEA will execute the code until the end, instead of stopping at the breakpoint.

Demo:

class Tester {

    @Test
    fun shouldBreakpoint() {
        //Line where threads should suspend:
        println("Should Suspend Here") //Breakpoint added to this line

        println("Shouldn't run this code unless I release above breakpoint")

    }
}

When clicking "Debug Tester" or "Debug shouldBreakpoint", no breakpoints will work. enter image description here

The console outputs both print lines, without ever stopping at the breakpoint. If the same code is written in Java, the debugger works:

public class Testerino {

    @Test
    public void shouldBreakpoint() {

        System.out.println("Should Suspend Here"); //Breakpoint added to this line

        System.out.println("Shouldn't run this code unless I release above breakpoint");
    }
}

When running on Kotlin main function, it also works correctly:

fun main(args: Array<String>) {
println("Should Suspend Here") //Breakpoint added to this line

println("Shouldn't run this code unless I release above breakpoint")

}

This is running on an Android Project, and the build.gradle (app) file is:

   apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

android {
    compileSdkVersion 27
    buildToolsVersion "27.0.3"
    defaultConfig {
        applicationId "me.kerooker.visualhonk"
        minSdkVersion 19
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
         vectorDrawables.useSupportLibrary = true

    }
    buildTypes {
        release {
            minifyEnabled false
            shrinkResources false
            proguardFiles getDefaultProguardFile('proguard-android.txt')
        }
        debug {
            debuggable true
            minifyEnabled false // set this to false
            proguardFiles getDefaultProguardFile('proguard-android.txt')
        }
    }
}

configurations.all {
    resolutionStrategy {
        forcedModules = [
                "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version",
                "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
        ]
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:27.+'
    compile 'com.android.support.constraint:constraint-layout:+'
    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

    testCompile 'io.kotlintest:kotlintest:2.0.7'
    testCompile 'junit:junit:4.12'


}
repositories {
    mavenCentral()
}

What can be done for Intellij to correctly recognize and stop at breakpoints?

Upvotes: 2

Views: 5492

Answers (1)

LeoColman
LeoColman

Reputation: 7153

This bug seems to be fixed as of this commit. Update Intellij to get the new version.

If you can't update, a workaround to this is removing gradle aware make from the run process, and only build your code when you make a change (and not every time you start the debug)

Explanation to fix:

Hitting breakpoints in Kotlin JUnit test with an Android Gradle
project sometimes(*) does not work in Android Studio 3.1 beta.

(*) The issue is related to various threads running concurrently
with "dumb" mode background code, so the issue reproduces only
on certain configurations.

In a nutshell, the issue is as follows

* On one hand, gradle build finishes, fires an event ("buildFinished")
  that is processed "later" and that causes the IDE to enter "dumb" mode
  for a shot amount of time (about 300-500 msec on a somewhat up to date
  multi-core computer).

* On the other hand, once the JVM of the debuggee is started, breakpoints
  need to be resolved (on the debugger thread, through a call to
  com.intellij.debugger.engine.CompoundPositionManager.createPrepareRequests.
  This code calls into the "KotlinPositionManager.createPrepareRequests",
  which in turns calls into "PerFileAnalysisCache.analyze". That method
  returns "AnalysisResult.EMPTY" is the project is in dumb mode.
  This return value prevents callers from successfully resolving
  the source location into a breakpoint.

Given that the 2 code paths above execute on separate threads without
explicit synchronization, the "failed to resolve breakpoint" issue
occurs sporadically, to the point it happens 100% of the time on
certain configuration.

The fix is so wrap the kotlin breakpoint resolution code inside
a "runReadActionInSmartMode" so that the debugger thread "waits"
for "dumb" mode to terminates before trying to resolve breakpoint
locations.

Upvotes: 2

Related Questions