[sdks/android] Move main Mentat Android SDK tests from androidTest to test.

This leverages JNA to test the Android SDK on the host machine using
Robolectric, which is significantly faster and easier to debug than
the equivalent on-device instrumentation tests.

We'll still want instrumentation smoke tests, but they won't need to
cover the entire range of the Android SDK.
This commit is contained in:
Nick Alexander 2018-07-31 09:54:29 -07:00
commit 4325d6c0c3
6 changed files with 210 additions and 298 deletions

View file

@ -29,9 +29,13 @@ buildscript {
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
classpath 'gradle.plugin.org.mozilla.rust-android-gradle:plugin:0.0.4' classpath 'gradle.plugin.org.mozilla.rust-android-gradle:plugin:0.1.0'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
// Yes, this is unusual. We want to access some host-specific
// computation at build time.
classpath 'net.java.dev.jna:jna:4.5.2'
} }
} }

View file

@ -6,6 +6,8 @@ apply plugin: 'com.jfrog.bintray'
// Simply applying this plugin gets bintray to publish a pom file. // Simply applying this plugin gets bintray to publish a pom file.
apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'com.github.dcendents.android-maven'
import com.sun.jna.Platform
android { android {
compileSdkVersion rootProject.ext.build['compileSdkVersion'] compileSdkVersion rootProject.ext.build['compileSdkVersion']
defaultConfig { defaultConfig {
@ -26,6 +28,9 @@ android {
sourceSets { sourceSets {
androidTest.assets.srcDirs += '../../../../fixtures' androidTest.assets.srcDirs += '../../../../fixtures'
test.resources.srcDirs += '../../../../fixtures'
test.resources.srcDirs += "$buildDir/rustResources"
} }
// TODO silences: // TODO silences:
@ -40,27 +45,60 @@ android {
packagingOptions { packagingOptions {
doNotStrip "**/*.so" doNotStrip "**/*.so"
} }
testOptions.unitTests.all {
maxParallelForks = 4
}
} }
cargo { cargo {
module = '../../../../ffi' module = '../../../../ffi'
targetDirectory = '../../../../target' targetDirectory = '../../../../target'
targetInclude = 'libmentat_ffi.so' targetIncludes = ['libmentat_ffi.so', 'libmentat_ffi.dylib', 'libmentat_ffi.dll']
targets = [ targets = [
'x86', 'default', // For unit tests.
'arm', 'arm',
'arm64', 'arm64',
'x86',
] ]
// This puts the output of `cargo build` (the "default" toolchain) into the correct directory
// for JNA to find it.
defaultToolchainBuildPrefixDir = Platform.RESOURCE_PREFIX
}
configurations {
// There's an interaction between Gradle's resolution of dependencies with different types
// (@jar, @aar) for `implementation` and `testImplementation` and with Android Studio's built-in
// JUnit test runner. The runtime classpath in the built-in JUnit test runner gets the
// dependency from the `implementation`, which is type @aar, and therefore the JNA dependency
// doesn't provide the JNI dispatch libraries in the correct Java resource directories. I think
// what's happening is that @aar type in `implementation` resolves to the @jar type in
// `testImplementation`, and that it wins the dependency resolution battle.
//
// A workaround is to add a new configuration which depends on the @jar type and to reference
// the underlying JAR file directly in `testImplementation`. This JAR file doesn't resolve to
// the @aar type in `implementation`. This works when invoked via `gradle`, but also sets the
// correct runtime classpath when invoked with Android Studio's built-in JUnit test runner.
// Success!
jnaForTest
} }
dependencies { dependencies {
jnaForTest 'net.java.dev.jna:jna:4.5.2@jar'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'net.java.dev.jna:jna:4.5.2@aar'
testImplementation files(configurations.jnaForTest.files)
testImplementation 'junit:junit:4.12'
testImplementation 'org.robolectric:robolectric:3.8'
testImplementation 'org.mockito:mockito-core:2.20.0'
androidTestImplementation 'com.android.support:support-annotations:27.1.1' androidTestImplementation 'com.android.support:support-annotations:27.1.1'
androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2' androidTestImplementation 'com.android.support.test:rules:1.0.2'
testImplementation 'junit:junit:4.12'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'net.java.dev.jna:jna:4.5.2@aar'
} }
repositories { repositories {
@ -68,7 +106,7 @@ repositories {
} }
afterEvaluate { afterEvaluate {
// The `cargoBuild` tasks isn't available until after evaluation. // The `cargoBuild` task isn't available until after evaluation.
android.libraryVariants.all { variant -> android.libraryVariants.all { variant ->
def productFlavor = "" def productFlavor = ""
variant.productFlavors.each { variant.productFlavors.each {
@ -76,6 +114,8 @@ afterEvaluate {
} }
def buildType = "${variant.buildType.name.capitalize()}" def buildType = "${variant.buildType.name.capitalize()}"
tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["cargoBuild"]) tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["cargoBuild"])
tasks["process${productFlavor}${buildType}UnitTestJavaRes"].dependsOn(tasks["cargoBuild"])
} }
} }

View file

@ -1,27 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* Copyright 2018 Mozilla
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the
* License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. */
package org.mozilla.mentat;
import java.util.EventListener;
interface ExpectationEventListener extends EventListener {
public void fulfill();
}
public class Expectation implements ExpectationEventListener {
public boolean isFulfilled = false;
public void fulfill() {
this.isFulfilled = true;
synchronized (this) {
notifyAll( );
}
}
}

View file

@ -21,10 +21,6 @@ import com.sun.jna.Pointer;
* The raw pointer it holds is a pointer to a Store. * The raw pointer it holds is a pointer to a Store.
*/ */
public class Mentat extends RustObject<JNA.Store> { public class Mentat extends RustObject<JNA.Store> {
static {
System.loadLibrary("mentat_ffi");
}
/** /**
* Create a new Mentat with the provided pointer to a Mentat Store * Create a new Mentat with the provided pointer to a Mentat Store
* @param rawPointer A pointer to a Mentat Store. * @param rawPointer A pointer to a Mentat Store.

View file

@ -0,0 +1 @@
manifest=--none