Skip to content

Instantly share code, notes, and snippets.

@realdadfish
Last active February 5, 2021 21:55
Show Gist options
  • Save realdadfish/c5a8a8e944965055c13490764618eb44 to your computer and use it in GitHub Desktop.
Save realdadfish/c5a8a8e944965055c13490764618eb44 to your computer and use it in GitHub Desktop.
Custom Dependency Resolver for Robolectric 4.2+

Configures a custom private maven repository for Robolectric to fetch it's artifacts from.

This replaces previous configurations like this:

android.unitTests.all {
    systemProperty 'robolectric.dependency.repo.url', 'https://path/to/private/maven/repo'
    systemProperty 'robolectric.dependency.repo.id', 'private-repo'
}

It also fixes the issue that in Android Studio / IntelliJ this very configuration was not picked up, but effectively led to test failures because Robolectric tried to fetch its dependencies from its default sonatype repository.

Create a new plain Java project, add / adapt the above two files, build and finally publish the artifact to your local Maven repository. Then, include this dependency alongside of Robolectric itself:

dependencies {
    testImplementation "org.robolectric:robolectric:4.3.1"
    testImplementation "my.app.test:robolectric-configuration:1.0"
}
buildscript {
ext.kotlin_version = '1.3.60'
repositories {
maven {
url 'https://your/private/company/maven/repo'
}
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
plugins {
id 'maven-publish'
}
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'
repositories {
maven {
url 'https://your/private/company/maven/repo'
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
def autoService = "1.0-rc6"
implementation "com.google.auto.service:auto-service-annotations:$autoService"
kapt "com.google.auto.service:auto-service:$autoService"
implementation "org.robolectric:pluginapi:4.3.1"
}
publishing {
publications {
maven(MavenPublication) {
groupId = 'my.app.test'
artifactId = 'robolectric-configuration'
version = '1.0'
from components.java
}
}
repositories {
maven {
url "https://your/private/company/maven/repo"
credentials {
username = /* ... */
password = /* ... */
}
}
}
}
import com.google.auto.service.AutoService
import org.robolectric.internal.dependency.DependencyResolver
import org.robolectric.pluginapi.SdkProvider
import org.apache.maven.artifact.ant.DependenciesTask
import org.robolectric.internal.dependency.DependencyJar
import java.io.IOException
import java.nio.channels.FileLock
import java.nio.channels.FileChannel
import java.io.RandomAccessFile
import java.io.File
import java.net.MalformedURLException
import java.nio.file.Paths
import java.lang.System.getProperties
import java.util.Hashtable
import org.apache.maven.model.Dependency
import org.apache.tools.ant.Project
import org.apache.maven.artifact.ant.Authentication
import org.apache.maven.artifact.ant.RemoteRepository
import java.net.URL
@AutoService(DependencyResolver::class)
class MyDependencyResolver private constructor(
private val repositoryUrl: String,
private val repositoryId: String,
private val repositoryUserName: String? = null,
private val repositoryPassword: String? = null
) : DependencyResolver {
constructor() : this(REPO_URL, REPO_NAME /* put credentials here, if needed */)
companion object {
const val REPO_NAME = "my-repo-name"
const val REPO_URL = "https://host/path/to/internal/maven/repository/"
}
override fun getLocalArtifactUrl(dependencyJar: DependencyJar): URL? {
val dependenciesTask = DependenciesTask()
val remoteRepository = RemoteRepository()
remoteRepository.url = repositoryUrl
remoteRepository.id = repositoryId
if (repositoryUserName != null || repositoryPassword != null) {
val authentication = Authentication()
authentication.userName = repositoryUserName
authentication.password = repositoryPassword
remoteRepository.addAuthentication(authentication)
}
dependenciesTask.addConfiguredRemoteRepository(remoteRepository)
val project = Project()
dependenciesTask.project = project
val dependency = Dependency()
dependency.artifactId = dependencyJar.artifactId
dependency.groupId = dependencyJar.groupId
dependency.type = dependencyJar.type
dependency.version = dependencyJar.version
if (dependencyJar.classifier != null) {
dependency.classifier = dependencyJar.classifier
}
dependenciesTask.addDependency(dependency)
whileLocked(Runnable { dependenciesTask.execute() })
val artifacts = project.properties
try {
return Paths.get(artifacts[key(dependencyJar)] as String).toUri().toURL()
} catch (e: MalformedURLException) {
throw RuntimeException(e)
}
}
private fun whileLocked(runnable: Runnable) {
val lockFile = File(System.getProperty("user.home"), ".robolectric-download-lock")
try {
RandomAccessFile(lockFile, "rw").use { raf ->
raf.channel.use { channel ->
val fileLock = channel.lock()
try {
println("Downloading from maven ")
runnable.run()
} finally {
fileLock.release()
}
}
}
} catch (e: IOException) {
throw IllegalStateException("Couldn't create lock file $lockFile", e)
} finally {
lockFile.delete()
}
}
private fun key(dependency: DependencyJar): String {
var key = dependency.groupId + ":" + dependency.artifactId + ":" + dependency.type
if (dependency.classifier != null) {
key += ":" + dependency.classifier
}
return key
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment