How does gradle run main in test module? - kotlin

I have a luncher for test only so it is defined in test moudle. I run it with IDEA run configuration and it throws the error that Cannot find the main class. I'm guess this test class is not compiled.
// in test moudule
object Launch {
#JvmStatic
fun main(args: Array<String>) {
println("launch")
}
}
I also found that once I run other test case with #Test, it will compile Launch and make it work as I expected.
EDITED:
I found that the Launch.class had been output to build/classes/kotlin/test, it means that Launch had be compiled. But it still doesn't work. I'm confused.

Related

Why launching my coroutine fails to find a class kotlin.Random?

This worked for me in the past:
CoroutineScope(Dispatchers.IO).launch { ...
as well as this:
GlobalScope.launch { ...
I am trying to build a SpringBoot app with Kotlin 1.6.10. and these commands blow up as follows:
Could not initialize class kotlin.random.Random
kotlin/internal/jdk7/JDK7PlatformImplementations
this app definitely uses Java 11, so the reference to jdk7 is a surprise as well. Can someone explain what is going on?
Here are the dependencies
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core") {
version {
strictly("1.6.1")
}
}
TIA
Edit: we do not use Random at all, but we added code to invoke it, and it works.

How can I unit test a function using the same obfuscated files as in my release build?

In my app I’ve recently updated Kotlin from 1.5.31 to 1.6.10. Since then a specific function that uses reflection (kotlin-reflect) stopped working as expected. It works perfectly fine in 1.5.31.
Now, when I run the debug build, the function works correctly, but when I run the release build (the one that is shrunk and obfuscated) it doesn’t.
This was something that I detected by pure chance (unfortunately I’m not using TDD… yes, I know I should, but sometimes we learn the hard way), so I decided it’s time to start testing to avoid this kind of unexpected issues, especially this one.
I’ve been doing some research, but I’ve found nothing on how to do this specific thing.
The idea is to be able to run a test that would catch this issue that only appears when the code is obfuscated.
I’ve implemented a simple test and the test doesn’t fail as it should, because it is using the code that it is not obfuscated.
The test is the following:
class UtilsTest {
#Test
fun dataClassAsStringToDataClass_ValidInput_OutputShouldBeEqualInput(){
//GIVEN
val defaultTimerData = TimerViewModelData(
hours = DEFAULT_HOURS,
minutes = DEFAULT_MINUTES,
seconds = DEFAULT_SECONDS,
showSmallTimer = DEFAULT_SHOW_SMALL_TIMER,
elapsedTimeMode = DEFAULT_ELAPSED_TIME_MODE
)
val defaultTimerDataAsString = defaultTimerData.toString()
//WHEN
val dataClassRecovered = dataClassAsStringToDataClass<TimerViewModelData>(defaultTimerDataAsString)
//THEN
assertEquals(dataClassRecovered, defaultTimerData)
}
}
As you may have guessed, I have some -keep rules implemented in my proguard-rules.pro file, otherwise the code wouldn’t run even on Kotlin 1.5.31.
I’ve been reading about testProguardFiles in gradle, but I don’t know if this is useful at all, or at least I couldn’t make it work as I expected.
This is part of my build.gradle file:
buildTypes {
debug {
versionNameSuffix "-debug"
}
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
testProguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
signingConfig signingConfigs.release
ndk {
debugSymbolLevel "FULL"
}
}
}
Let me know in the comments if you need any other information, and I’ll be glad to share.
Just in case, let’s clarify that the purpose of this question is to know how to do the test that should fail when the code changes its behavior as a consequence of being obfuscated, not what has changed between Kotlin versions that is breaking my code… that is another story.

Dealing with R8 + JvmStatic Annotation + Lambda in public API for Android Library written in Kotlin

First of all, please note that I'm not expecting why do you want to obfuscate library comments. This is a genuine problem I'm asking about.
I have been having an issue dealing with R8/obfuscation with an Android library written in Kotlin.
I've a public API method which is annotated with #JvmStatic and that method takes a Lambda as parameter.
For example, take a look at code below,
typealias MyLambdaCallback = (String, Map<String, Any>) -> Unit
#Keep
object MyApi {
private var callback: MyLambdaCallback? = null
#JvmStatic
fun setCallback(callback: MyLambdaCallback) {
this.callback = callback
}
}
I have added #Jvmstatic so that Java calling code can call the method statically rather than doing MyApi.INSTANCE.setCallback()
When I release the library without minification, everything is fine and calling code from both Java and Kotlin is written as expected.
But now I want to release the library while turning on minification.
That creates an issue.
Here is the error
java.lang.IncompatibleClassChangeError: The method 'void setCallback(kotlin.jvm.functions.Function2)' was expected to be of type virtual but instead was found to be of type static (declaration of 'com.demo.basic.Application' appears in /data/app/com.demo.basic-_0uJXPbtfs3UZ2Rp2h-RdQ==/base.apk!classes2.dex)
Am I making a mistake somewhere or this is expected as some kind of limitation ?
What did I Try ?
Removing #Jvmstatic resolves the issue but it created ugly Java calling code
Kept #Jvmstatic but removed Lambda converting Lambda into an interface with one method and everything is working fine. Unfortunately SAM for Kotlin classes is not there yet, so calling Kotlin code looks ugly.
This is tracked on the R8 issue tracker http://issuetracker.google.com/158393309 which has more details.
The short story is that this has been fixed in R8 version 2.1.35, which can be used by making the following changes to the top level build.gradle file:
repositories {
maven {
url 'https://storage.googleapis.com/r8-releases/raw'
}
}
dependencies {
classpath 'com.android.tools:r8:2.1.35' // Must be before the Gradle Plugin for Android.
classpath 'com.android.tools.build:gradle:X.Y.Z' // Your current AGP version.
}
R8 team has fixed this issue along with related issue b/158400283 in R8 version 2.1.42
Fix should already be available in Android Studio 4.1 beta or higher, but if you're using stable Android Studio 4.0 then add following to your top-level build.gradle file:
buildscript {
repositories {
maven {
url 'https://storage.googleapis.com/r8-releases/raw'
}
}
dependencies {
classpath 'com.android.tools:r8:2.1.42' // Must be before the Gradle Plugin for Android.
classpath 'com.android.tools.build:gradle:X.Y.Z' // Your current AGP version.
}
}

Check for dependencies update with kotlin-dsl

This question has already been asked before, however the solution is still unknown... Kotlin DSL build scripts dependency updates
With the new implementation of kotlin-dsl. Now the imports looks like this.
implementation Koin.core
implementation Koin.android
and the buildSrc.
object Versions{
const val koin = "2.0.1"
}
object Koin {
val core = "org.koin:koin-core:${Versions.koin}"
val android = "org.koin:koin-android:${Versions.koin}"
val scope = "org.koin:koin-androidx-scope:${Versions.koin}"
val viewModel = "org.koin:koin-androidx-viewmodel:${Versions.koin}"
val extension = "org.koin:koin-androidx-ext:${Versions.koin}"
val test = "org.koin:koin-test:${Versions.koin}"
}
in this case Koin is using a previous version, but i know that there's a new version https://github.com/InsertKoinIO/koin
anyone knows how to check if the dependencies has a newer version with kotlin-dsl?
I've tested this Gradle Dependencies Update Check Plugin on my Android/Kotlin DSL build (with a separate Versions class with versions definitions) and it works fine for me:
CheckDependencyUpdates Gradle Plugin
(I've also tested that it works with a traditional Groovy-DSL project)
To install the plugin (copied from linked page) add the following to your build.gradle.kts. Note that I've removed the version number from this as it will, unlike the page I've linked to, get out of date:
plugins {
id("name.remal.check-dependency-updates")
}
To run the update check (copied from gradle tasks) run the following:
gradle checkDependencyUpdates
You will see an output section similar to the following:
New dependency version: com.android.tools.build:aapt2: 3.6.1-6040484 -> 3.6.3-6040484
New dependency version: com.android.tools.lint:lint-gradle: 26.6.1 -> 26.6.3
I made this plugin. Dependency Updates Commenter.
Just apply plugin and add annotation to the dependency properties and execute commentDependencyUpdates task. This is the example:
object Junit {
const val junit = "junit:junit:4.12"
}
import io.github.zeroarst.dependencyupdatescommenter.CommentUpdates
object Junit {
// Available versions:
// 4.13-rc-2
// 4.13-rc-1
// 4.13-beta-3
// 4.13-beta-2
// 4.13-beta-1
#CommentUpdates
const val junit = "junit:junit:4.12"
}

How can I execute a terminal command when opening IntelliJ Project?

Not even sure if this possible with IntelliJ but I'd like to tie the vagrant up command to run automatically when I open an IntelliJ project. I've scoured the settings but haven't been able to find anything that gives me this functionality.
You can write a plugin.
https://www.jetbrains.com/help/idea/plugin-development-guidelines.html
Define an application component in the plugin.xml
<application-components>
<component>
<implementation-class>com.steve.plugins.recentprojects.RecentProjects</implementation-class>
</component>
</application-components>
And then you implement ApplicationComponent, which defines these methods in a parent interface:
public interface BaseComponent extends com.intellij.openapi.components.NamedComponent {
default void initComponent() { /* compiled code */ }
default void disposeComponent() { /* compiled code */ }
}
It seems like initComponent() can be a nice place to insert a function to start vagrant.
Alternatively... externalise the startup, write a script that starts vagrant and then starts intellij...