diff --git a/src/main/scala/AndroidDefault.scala b/src/main/scala/AndroidDefault.scala index 5f2169e..4467644 100644 --- a/src/main/scala/AndroidDefault.scala +++ b/src/main/scala/AndroidDefault.scala @@ -34,6 +34,7 @@ object AndroidDefaults { dxJavaOpts := DefaultDxJavaOpts, manifestSchema := DefaultManifestSchema, envs := DefaultEnvs, + // a list of modules which are already included in Android preinstalledModules := Seq[ModuleID]( ModuleID("org.apache.httpcomponents", "httpcore", null), diff --git a/src/main/scala/AndroidHelpers.scala b/src/main/scala/AndroidHelpers.scala index c2f1cc9..8e11ea7 100644 --- a/src/main/scala/AndroidHelpers.scala +++ b/src/main/scala/AndroidHelpers.scala @@ -1,3 +1,4 @@ +import java.io.InputStream import sbt._ import Keys._ @@ -25,7 +26,8 @@ object AndroidHelpers { (manifest(mpath) \ "uses-sdk").head.attribute(schema, key).map(_.text.toInt) def adbTask(dPath: String, emulator: Boolean, s: TaskStreams, action: String*) { - val (exit, out) = adbTaskWithOutput(dPath, emulator, s, action:_*) + val out = new StringBuffer + val exit = adbTaskWithOutput(dPath, emulator, s, action:_*) { i => out.append(IO.readStream(i)) } if (exit != 0 || // adb doesn't bother returning a non-zero exit code on failure out.toString.contains("Failure")) { @@ -34,15 +36,14 @@ object AndroidHelpers { } else s.log.info(out.toString) } - def adbTaskWithOutput(dPath: String, emulator: Boolean, s: TaskStreams, action: String*) = { + def adbTaskWithOutput(dPath: String, emulator: Boolean, s: TaskStreams, action: String*) (parser:InputStream => Unit) = { val adb = Seq(dPath, if (emulator) "-e" else "-d") ++ action s.log.debug(adb.mkString(" ")) val out = new StringBuffer - val exit = adb.run(new ProcessIO(input => (), - output => out.append(IO.readStream(output)), + adb.run(new ProcessIO(input => (), + output => parser(output), error => out.append(IO.readStream(error))) ).exitValue() - (exit, out.toString) } def startTask(emulator: Boolean) = diff --git a/src/main/scala/AndroidInstall.scala b/src/main/scala/AndroidInstall.scala index 0517adb..8ec9635 100644 --- a/src/main/scala/AndroidInstall.scala +++ b/src/main/scala/AndroidInstall.scala @@ -92,6 +92,8 @@ object AndroidInstall { "-keep public class * extends android.content.ContentProvider" :: "-keep public class * extends android.view.View" :: "-keep public class * extends android.app.Application" :: + "-keep public class * extends android.app.Instrumentation" :: + "-keep public class * extends android.test.InstrumentationTestRunner" :: "-keep public class "+manifestPackage+".** { public protected *; }" :: "-keep public class * implements junit.framework.Test { public void test*(); }" :: proguardOption :: Nil ) diff --git a/src/main/scala/AndroidKeys.scala b/src/main/scala/AndroidKeys.scala index 1c386cb..91141f3 100644 --- a/src/main/scala/AndroidKeys.scala +++ b/src/main/scala/AndroidKeys.scala @@ -178,6 +178,8 @@ object AndroidKeys { val testDevice = TaskKey[Unit]("test-device", "runs tests on device") val testOnlyEmulator = InputKey[Unit]("test-only-emulator", "run a single test on emulator") val testOnlyDevice = InputKey[Unit]("test-only-device", "run a single test on device") + val instrumentationRunner = SettingKey[String] ("instrumentation-runner", "instrumentation test runner e.g. android.test.InstrumentationTestRunner") + val testOutputParser = TaskKey[Option[AndroidTest.TestParser]] ("test-output-parser", "Optional test parser") /** Github tasks & keys */ val uploadGithub = TaskKey[Option[String]]("github-upload", "Upload file to github") diff --git a/src/main/scala/AndroidTest.scala b/src/main/scala/AndroidTest.scala index a437a91..e8b8761 100644 --- a/src/main/scala/AndroidTest.scala +++ b/src/main/scala/AndroidTest.scala @@ -1,3 +1,4 @@ +import java.io.InputStream import sbt._ import Keys._ @@ -10,20 +11,27 @@ import Cache.seqFormat import com.android.ddmlib.testrunner.{InstrumentationResultParser,ITestRunListener} object AndroidTest { - def instrumentationTestAction(emulator: Boolean) = (dbPath, manifestPackage, streams) map { - (dbPath, manifestPackage, s) => - val action = Seq("shell", "am", "instrument", "-r", "-w", - manifestPackage+"/android.test.InstrumentationTestRunner") - val (exit, out) = adbTaskWithOutput(dbPath.absolutePath, emulator, s, action:_*) - if (exit == 0) parseTests(out, manifestPackage, s.log) + + type TestParser = (InputStream => Unit) + + val DefaultInstrumentationRunner = "android.test.InstrumentationTestRunner" + + def instrumentationTestAction(emulator: Boolean) = (dbPath, manifestPackage, instrumentationRunner, testOutputParser, streams) map { + (dbPath, manifestPackage, inst, parser, s) => + val action = Seq("shell", "am", "instrument", "-r", "-w", manifestPackage+"/" + inst) + val out = new StringBuffer + val exit = adbTaskWithOutput(dbPath.absolutePath, emulator, s, action:_*) {i => parser.map(_.apply(i)).getOrElse(out.append(IO.readStream(i))) } + if (exit == 0) parseTests(out.toString, manifestPackage, s.log) else sys.error("am instrument returned error %d\n\n%s".format(exit, out)) () } - def runSingleTest(emulator: Boolean) = (test: TaskKey[String]) => (test, dbPath, manifestPackage, streams) map { (test, dbPath, manifestPackage, s) => - val action = Seq("shell", "am", "instrument", "-r", "-w", "-e", "class", test, manifestPackage+"/android.test.InstrumentationTestRunner") - val (exit, out) = adbTaskWithOutput(dbPath.absolutePath, emulator, s, action:_*) - if (exit == 0) parseTests(out, manifestPackage, s.log) + def runSingleTest(emulator: Boolean) = (test: TaskKey[String]) => (test, dbPath, manifestPackage, instrumentationRunner, testOutputParser,streams) map { + (test, dbPath, manifestPackage, inst,parser, s) => + val action = Seq("shell", "am", "instrument", "-r", "-w", "-e", "class", test, manifestPackage+"/" + inst) + val out = new StringBuffer + val exit = adbTaskWithOutput(dbPath.absolutePath, emulator, s, action:_*) {i => parser.map(_.apply(i)).getOrElse(out.append(IO.readStream(i))) } + if (exit == 0) parseTests(out.toString, manifestPackage, s.log) else sys.error("am instrument returned error %d\n\n%s".format(exit, out)) () } @@ -58,6 +66,8 @@ object AndroidTest { AndroidBase.settings ++ AndroidInstall.settings ++ inConfig(Android) (Seq ( + instrumentationRunner := DefaultInstrumentationRunner, + testOutputParser := None, testEmulator <<= instrumentationTestAction(true), testDevice <<= instrumentationTestAction(false), testOnlyEmulator <<= InputTask(loadForParser(definedTestNames in Test)( (s, i) => testParser(s, i getOrElse Nil))) { test =>