Skip to content

Instantly share code, notes, and snippets.

@dsvoronin
Last active June 18, 2020 21:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dsvoronin/ed6465f2da6c9432412664ed6e85429a to your computer and use it in GitHub Desktop.
Save dsvoronin/ed6465f2da6c9432412664ed6e85429a to your computer and use it in GitHub Desktop.
Robolectric offline

Robolectric runtime jar fetch problem

Robolectric fetches and caches it's own android jar package by itself during initialization, this mechanism is kinda unreliable, for example it can fail your CI builds from time to time.

If you have same problems, take a look at our approach.

Custom robolectric gradle module

We created gradle module, containing fetch logic as a custom task; see attached robolectric.build.gradle;
All other modules, containing robolectric tests should dependens on it.

Custom task eagerly resolves jar's and store this as a typical dependency in gradle cache.

Custom robolectric SDK provider

Now we need to tell robolectric to use our, already fetched jar's, instead of downloading them again;
To do so we have to register custom SdkProvider, via robolectric plugin api
Just place file, named: org.robolectric.pluginapi.SdkProvider(attached to gist) to <our-custom-robolectric-module>/src/main/resources/META-INF.services/
File just points to class in out module.

AvitoSdkProvider(attached in gist) overrides getSdks() function and returns only relevant (downloaded in gradle) jar paths.

RobolectricSdk(attached in gist) overrides getJarPath() to point on gradle caches path, that we uses to cache jar's

Related issues:

package com.avito.android.util
import org.robolectric.pluginapi.Sdk
import org.robolectric.pluginapi.SdkProvider
import javax.annotation.Priority
@Priority(Integer.MAX_VALUE)
class AvitoSdkProvider : SdkProvider {
private val availableSdks = setOf(
RobolectricSdk(apiLevel = 23, androidVersion = "6.0.1", androidRevision = "r3", robolectricRevision = "r1"),
RobolectricSdk(apiLevel = 26, androidVersion = "8.0.0", androidRevision = "r4", robolectricRevision = "r1")
)
override fun getSdks(): Collection<Sdk> = availableSdks
}
com.avito.android.util.AvitoSdkProvider
import org.gradle.api.artifacts.Configuration.State
plugins {
id 'kotlin'
}
// creating custom configuration just to fetch jar's
configurations {
robolectric6
robolectric8
}
dependencies {
// single place for robolectric dependency
api("org.robolectric:robolectric:4.3") {
// https://github.com/robolectric/robolectric/issues/5245
exclude group: "com.google.auto.service", module: "auto-service"
}
implementation(Dependencies.kotlinStdlib)
implementation(Dependencies.test.junit)
// point on relevant for your project robolectric jar's
// look for them here: https://mvnrepository.com/artifact/org.robolectric/android-all
robolectric6("org.robolectric:android-all:6.0.1_r3-robolectric-r1")
robolectric8("org.robolectric:android-all:8.0.0_r4-robolectric-r1")
}
tasks.register("robolectricFetch", RobolectricFetchTask)
jar.dependsOn("robolectricFetch")
abstract class RobolectricFetchTask extends DefaultTask {
@TaskAction
void execute() {
def roboConfigurations = project.configurations.findAll { it.name.startsWith("robolectric") }
roboConfigurations.each {
if (it.state != State.RESOLVED) {
it.resolve()
}
}
}
}
package com.avito.android.util
import org.robolectric.pluginapi.Sdk
import java.io.File
import java.nio.file.Path
@SuppressWarnings("NewApi")
class RobolectricSdk(
apiLevel: Int,
private val androidVersion: String,
private val androidRevision: String,
private val robolectricRevision: String
) : Sdk(apiLevel) {
val path: Path by lazy {
val version = "${androidVersion}_$androidRevision-robolectric-$robolectricRevision"
val gradleHome: String? = System.getenv("GRADLE_USER_HOME") ?: "${System.getProperty("user.home")}/.gradle"
val cacheDir = "$gradleHome/caches/modules-2/files-2.1/org.robolectric/android-all/$version"
val fileName = "android-all-$version.jar"
File(cacheDir).walk().find { it.name == fileName }?.toPath()
?: error("Can't find file cached jar for api $apiLevel in $cacheDir with name $fileName")
}
override fun getJarPath(): Path = path
override fun getUnsupportedMessage(): String = ""
override fun getAndroidVersion(): String = androidVersion
/**
* don't know what is it, copypaste from robolectric
*/
override fun getAndroidCodeName(): String = "REL"
override fun isSupported(): Boolean = true
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment