How to force Jacoco to use a specific version in Gradle - android

Based on the release notes in version 0.8.3 the not-null assertion operator is filtered out, I'm using Jacoco version 0.8.5 like this:
jacoco {
toolVersion = "0.8.5"
}
But it's telling me that Not covered by tests (8 conditions)
I'm using com.dicedmelon.gradle:jacoco-android Github link
I think toolVersion = "0.8.5" not working or something like that, so for that I need a way to force Jacoco version.
Is there any way to fix this issue?

Without seeing your code and your tests I can't say with 100% confidence but it looks like Jacoco is working fine and there are cases not covered there.
You're using the !! 3 times. When you use this operator, in reality, you're creating 2 flows, for when the variable is null and another for not-null. If you add tests for the cases where the variables are null, you should reach 100% coverage.
Just to make explicit, if you would handle your nullable with safe calls, you would have something like this:
val token = authResult.user?.let {
authenticationDataSource.getIdToken(true, it)
?.let { it.token }
?: throw GetIdTokenResultIsNullException()
} ?: throw UserIsNullException()
token?.let {
authenticationDataSource.loginBy(AuthenticationPayload(it))
} ?: throw TokenIsNullException()
Wherever, I'm throwing exceptions you should handle that case as desired, and this is the alternate branch that is created by the nullability.
If you're sure that your values won't be null then you should change your types to make it clear and avoid extra checks.
On a side note, jacoco-android doesn't seem to be maintained anymore here and it's not compatible with newer gradle versions, so I would recommend using Jacoco directly. Migrating from jacoco-android to jacoco shouldn't be that hard.

Related

How to conditionally skip a unit test in Android and kotlin?

I need to run a unit test based on whether this asset exists at runtime. The reason is, I am downloading the file in react native and in android, I am running some unit tests that requires this file.
I would like to run the unit test only if this file exists. Does anyone have any suggestions or code samples on how this can be achieved? Or is there another way these unit tests can be accomplished?
You shouldn't do that in a unit test because you want to have the file locally in your testing environment or have a mock that provides it. Otherwise you're not Eradicating Non-Determinism in Tests.
Let's assume you need to do something like that anyway. So, I would add a condition in the assert expression:
#Test
fun `Given a variable When has a value Then assert it has a value`(){
var myVar = null
myVar = getAValue()
myVar?.let {
assertNotEquals(null, myVar)
}
}
To me, eradicating non determinism in this particular context means that test scope has always specific expected values, the conditional expression in the let or an if enclosing an assert expression violates that. Hence the code above shouldn't be part of your tests. Instead, you have to write a test for the case myVar is null, you write another test for the case myVar is non null.
That's why I use Given, When, Then the conditional state would make the Given/When very messy.

Why is rememberSaveable variable flagged as unused by Android Studio Arctic Fox?

Android Studio Arctic Fox (Patch 3) flags, "The value true assigned to var isVisited: Boolean in the following composable is never used":
#Composable
fun MainView(navController: NavController) {
var isVisited by rememberSaveable { mutableStateOf(false) }
if (!isVisited) {
isVisited = true // never used?
navController.navigate("NextView")
}
Button(onClick = { navController.navigate("NextView") }) {
Text(text = "MainView")
}
}
while it is clearly working as intended: to prevent MainView from navigating to NextView on subsequent visits to MainView. Here's the definition of NextView:
#Composable
fun NextView(navController: NavController) {
Button(onClick = { navController.popBackStack() }) {
Text(text = "NextView")
}
}
Is Android Studio simply not recognizing variable usage across recompositions? Or is there a more idiomatic way to execute code conditionally upon recompositions? Thanks for any answer or pointer.
I'm building for API Level 31 with Kotlin 1.5.31, Compose 1.1.0-alpha06, navigation-compose 2.4.0-alpha04, lifecycle-runtime-ktx 2.4.0-rc01, though I've seen the same behavior on API Level 30, Kotlin 1.5.21, Compose 1.0.1, navigation-compose 2.4.0-alpha04, lifecycle-runtime-ktx 2.3.1. (I'd be happy to share my MainActivity where I set up NavHost with these two views or other dependency and system info if helpful.)
2022 ANSWER:
The studio code highlighter was designed with the traditional programming model in mind, i.e., the non-declarative View System. In that, a modern Composable would be treated as just a regular function. Now inside that function, you are creating a variable, reading it in a conditional, then updating its value. Then that value is never accessed in the scope of that function, and hence the warning "The updated value is never used".
However, we being Compose-Oriented now, know that it is a declarative paradigm and that recompositions are a thing, by the virtue of which, the function would be called over and over again and hence the updated value will, in fact, be used within that same conditional, which is why the rememberSaveable block is there to begin with. The same goes for the compiler, since you never "type the name of the variable" in the function scope after updating it, looking from the traditional way, the value is never used again, so the update is pointless to the compiler (as well as the highlighter), and hence the warning. It only makes sense to ignore it, but if you are really bugged by it, just type the name of the variable right after the whole loop, it should go away. God I was so dumb a year ago...
WRITTEN BY 2021 ME; NOT AS RELIABLE. FEEL FREE TO IGNORE
TOP OF THE LINE: You can safely ignore that warning, because the purpose of warnings is to prevent developers from using unnecessary system resources. Because of some internal highlighter logic error (perhaps?), it identifies a useful variable as an unused one, but since you know you are actively using it in code flow, you can just ditch that warning. Just suppress it with an annotation if it bugs you.
BODY CONTENT:-
Don't bother, it happens at times. There seems no error to me, just try deleting the line, and then re-adding it while using as much code completion as you can. If that does not help, just compile the project, and see the build log. If it does not show the warning: var 'isVisited' is never used, then you can relax about it, since it would then be a bug in the studio's code highlighter. Long as you don't receive any build time warnings, be sure that it does not pose any threat pertaining to performance or whatsoever.

In appcenter failed unit test specifies only function name and line number

Edit, probably the better place to have posted this is on the appcenter forums (which I have now done):
https://github.com/microsoft/appcenter-cli/issues/1137
In short, an app I'm working on is built in appcenter and set to run unit tests, the problem I have though is that I'm unable to figure out what is making them fail (they won't fail locally).
I can't share the code that I'm having trouble with here, but to illustrate the problem I'm having, Suppose I defined the following kotlin function:
fun returnFooString() {
return "Foo "
}
and wrote the following test:
#Test
fun test_returnFooString_returns_foo() {
val foo = returnFooString()
assertThat("${foo} is equal to "Foo", "Foo", foo)
}
What I'd like to see is something like:
java.lang.AssertionError: Foo is equal to Foo
Expected: "Foo"
but: was "Foo "
Expected :Foo
Actual :Foo
However the only thing I would see in the appcenter logs for this failing test is:
com.mypackage.name.MyTest > test_returnFooString_returns_foo FAILED
java.lang.AssertionError at MyTest.kt:4
and so I have no clue what just happened. I'm still somewhat of a beginner to android development, and though I've searched google, I haven't been successful in finding something that looks relevant. Is there some setting that can be placed in the build.gradle to not suppress the assertion message, or some environment variable(s) I need to specify in appcenter, or something else to see what was expected and received?
Right now I'm seeing if I can throw exceptions in the test / code to shed some light on the issue but there must be a nicer way (especially since it takes about 10 minutes for a build)
Edit: throwing exceptions doesn't work, if I throw an exception logging out every variable I'm interested in, all I get back is something like:
com.mypackage.name.MyTest > test_returnFooString_returns_foo FAILED
java.lang.Exception at MyTest.kt:4
At this point it seems I either have to abandon the tests or do something crazy like writing 256 tests to figure out the first character my string has at position 0, then 10 minutes later another 256 to figure out the second ...
I turns out that logging can be enabled with the following:
testOptions {
unitTests.all {
testLogging {
showStandardStreams true
}
}
}

JVM error when compiling Kotlin function with coroutines scopes

I'm trying to migrate my MVP application from Rx to Kotlin's coroutines (which I'm new to).
As I was running some trials, I found that any code with the following structure fails to compile with org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: wrong bytecode generated
val scope = CoroutineScope(Dispatchers.IO)
fun a(i: Int) {
scope.launch {
withContext(Dispatchers.Main) {
val b = i + 1
}
}
}
It appears that trying to access the parameter i inside the withContext is the problem. If I assign the value of i to something else inside the function block and use that instead, it works alright. But I have the feeling I might be doing something extra wrong here hehe
This lives in a Presenter. My idea is to use "launch" with the IO dispatcher to call the repository and get some data from the database, then use it to update the UI in the Main dispatcher. It looks solid to me, but I'm a bit worried because apparently no one else is running into that same issue, which might mean this pattern I'm trying to implement should be avoided for some reason.
Any clues?
After some fiddling I found that the problem was with my kotlin plugin version.
I changed it from 1.3.50 to 1.3.72 and... magic :D

TaskGraph.useFilter alternates for kotlin dsl

Im looking for an alternative to this line of code from gradle and im looking for its counter part in kotlin dsl can anyone shed some light on this as it is blocking builds for me and am at a loss as the usefilter is not available in kotlin dsl
gradle.taskGraph.useFilter {
if (task.name.contains("GoogleServices") && (task.name.contains("Perf") || task.name.contains("Demo") || task.name.contains("Dev") || task.name.contains("Debug"))) {
return false
}
return true
}
useFilter is a method declared in a non-public interface TaskExecutionGraphInternal which is subtype of the public TaskExecutionGraph and is implemented by the actual task graph implementation class.
The call to useFilter works in Groovy because of dynamic dispatch: it searches among the all methods of the actual class in run time, thus allowing to call even those methods that are not available through the public API.
In Kotlin, you have to cast the task graph to that interface explicitly to be able to call this method statically:
(gradle.taskGraph as org.gradle.execution.taskgraph.TaskExecutionGraphInternal)
.useFilter { it.name.contains("GoogleServices") && ... }
Note that using non public Gradle API makes your build fragile and sensitive to upgrading the version of Gradle.

Categories

Resources