diff --git a/spoon-runner/src/main/java/com/squareup/spoon/CliArgs.kt b/spoon-runner/src/main/java/com/squareup/spoon/CliArgs.kt index e7ca600e..f4d2e23b 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/CliArgs.kt +++ b/spoon-runner/src/main/java/com/squareup/spoon/CliArgs.kt @@ -77,13 +77,18 @@ internal class CliArgs(parser: ArgParser) { val singleInstrumentationCall by parser.flagging("--single-instrumentation-call", help = "Run all tests in a single instrumentation call") + val classLevelInstrumentation by parser.flagging("--class-level-instrumentation", + help = "Run each test class in a different instrumentation instance") + private fun validateInstrumentationArgs() { val isTestRunPackageLimited = instrumentationArgs?.contains("package") ?: false - val isTestRunClassLimited = instrumentationArgs?.contains("class") ?: false || className != null - || methodName != null + val isTestRunClassLimited = instrumentationArgs?.contains("class") ?: false || (className != null || methodName != null) if (isTestRunPackageLimited && isTestRunClassLimited) { throw SystemExitException("Ambiguous arguments: cannot provide both test package and test class(es)", 2) } + if (singleInstrumentationCall && classLevelInstrumentation) { + throw SystemExitException("Conflicting arguments: cannot set both single-instrumentation-call and class-level-instrumentation", 2) + } } init { diff --git a/spoon-runner/src/main/java/com/squareup/spoon/DeviceResult.java b/spoon-runner/src/main/java/com/squareup/spoon/DeviceResult.java index 966a79d3..012f1980 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/DeviceResult.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/DeviceResult.java @@ -1,6 +1,7 @@ package com.squareup.spoon; import com.squareup.spoon.misc.StackTrace; + import java.util.ArrayList; import java.util.Date; import java.util.HashMap; diff --git a/spoon-runner/src/main/java/com/squareup/spoon/DeviceTestResult.java b/spoon-runner/src/main/java/com/squareup/spoon/DeviceTestResult.java index bce465a3..f606a077 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/DeviceTestResult.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/DeviceTestResult.java @@ -2,6 +2,7 @@ import com.android.ddmlib.logcat.LogCatMessage; import com.squareup.spoon.misc.StackTrace; + import java.io.File; import java.util.ArrayList; import java.util.Collections; diff --git a/spoon-runner/src/main/java/com/squareup/spoon/SpoonDeviceLogger.java b/spoon-runner/src/main/java/com/squareup/spoon/SpoonDeviceLogger.java index f322661a..be066d38 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/SpoonDeviceLogger.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/SpoonDeviceLogger.java @@ -4,6 +4,7 @@ import com.android.ddmlib.logcat.LogCatListener; import com.android.ddmlib.logcat.LogCatMessage; import com.android.ddmlib.logcat.LogCatReceiverTask; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; diff --git a/spoon-runner/src/main/java/com/squareup/spoon/SpoonDeviceRunner.java b/spoon-runner/src/main/java/com/squareup/spoon/SpoonDeviceRunner.java index 3ac0fec4..ad9672f7 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/SpoonDeviceRunner.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/SpoonDeviceRunner.java @@ -13,7 +13,6 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Multimap; - import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.TrueFileFilter; @@ -25,10 +24,13 @@ import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import static com.android.ddmlib.FileListingService.FileEntry; import static com.android.ddmlib.SyncService.getNullProgressMonitor; @@ -75,6 +77,7 @@ public final class SpoonDeviceRunner { private final SpoonInstrumentationInfo instrumentationInfo; private final boolean codeCoverage; private final boolean singleInstrumentationCall; + private final boolean classLevelInstrumentation; private final List testRunListeners; private final boolean grantAll; @@ -86,11 +89,11 @@ public final class SpoonDeviceRunner { * @param output Path to output directory. * @param serial Device to run the test on. * @param debug Whether or not debug logging is enabled. - * @param adbTimeout time in ms for longest test execution + * @param adbTimeout Time in ms for longest test execution. * @param instrumentationInfo Test apk manifest information. * @param className Test class name to run or {@code null} to run all tests. - * @param methodName Test method name to run or {@code null} to run all tests. Must also pass - * {@code className}. + * @param methodName Test method name to run or {@code null} to run all tests. + * Must also pass {@code className}. * @param testRunListeners Additional TestRunListener or empty list. */ SpoonDeviceRunner(File testApk, List otherApks, File output, String serial, int shardIndex, @@ -98,7 +101,8 @@ public final class SpoonDeviceRunner { SpoonInstrumentationInfo instrumentationInfo, Map instrumentationArgs, String className, String methodName, IRemoteAndroidTestRunner.TestSize testSize, List testRunListeners, boolean codeCoverage, boolean grantAll, - boolean singleInstrumentationCall) { + boolean singleInstrumentationCall, boolean classLevelInstrumentation) { + this.testApk = testApk; this.otherApks = otherApks; this.serial = serial; @@ -108,13 +112,15 @@ public final class SpoonDeviceRunner { this.noAnimations = noAnimations; this.adbTimeout = adbTimeout; this.instrumentationArgs = ImmutableMap.copyOf(instrumentationArgs != null - ? instrumentationArgs : Collections.emptyMap()); + ? instrumentationArgs : Collections.emptyMap()); this.className = className; this.methodName = methodName; this.testSize = testSize; this.instrumentationInfo = instrumentationInfo; this.codeCoverage = codeCoverage; this.singleInstrumentationCall = singleInstrumentationCall; + this.classLevelInstrumentation = classLevelInstrumentation; + serial = SpoonUtils.sanitizeSerial(serial); this.work = FileUtils.getFile(output, TEMP_DIR, serial); this.junitReport = FileUtils.getFile(output, JUNIT_DIR, serial + ".xml"); @@ -136,8 +142,6 @@ private void printStream(InputStream stream, String tag) throws IOException { /** Execute instrumentation on the target device and return a result summary. */ public DeviceResult run(AndroidDebugBridge adb) { - String testPackage = instrumentationInfo.getInstrumentationPackage(); - String testRunner = instrumentationInfo.getTestRunnerClass(); logDebug(debug, "InstrumentationInfo: [%s]", instrumentationInfo); if (debug) { @@ -203,57 +207,93 @@ public DeviceResult run(AndroidDebugBridge adb) { // Create the output directory, if it does not already exist. work.mkdirs(); - // Determine the test set that is applicable for this device. - LogRecordingTestRunListener recorder; - List activeTests; - List ignoredTests; - try { - recorder = queryTestSet(testPackage, testRunner, device); - activeTests = recorder.activeTests(); - ignoredTests = recorder.ignoredTests(); - logDebug(debug, "Active tests: %s", activeTests); - logDebug(debug, "Ignored tests: %s", ignoredTests); - } catch (Exception e) { - return result - .addException(e) - .build(); - } + return runTests(device, result); + } + + private DeviceResult runTests(IDevice device, DeviceResult.Builder resultBuilder) { + + String testPackage = instrumentationInfo.getInstrumentationPackage(); + String testRunner = instrumentationInfo.getTestRunnerClass(); // Initiate device logging. SpoonDeviceLogger deviceLogger = new SpoonDeviceLogger(device); List listeners = new ArrayList<>(); - listeners.add(new SpoonTestRunListener(result, debug)); + listeners.add(new SpoonTestRunListener(resultBuilder, debug)); listeners.add(new XmlTestRunListener(junitReport)); if (testRunListeners != null) { listeners.addAll(testRunListeners); } - result.startTests(); + resultBuilder.startTests(); if (singleInstrumentationCall) { try { logDebug(debug, "Running all tests in a single instrumentation call on [%s]", serial); RemoteAndroidTestRunner runner = createConfiguredRunner(testPackage, testRunner, device); runner.run(listeners); } catch (Exception e) { - result.addException(e); + resultBuilder.addException(e); } } else { + // Determine the test set that is applicable for this device. + LogRecordingTestRunListener recorder; + List activeTests; + List ignoredTests; + try { + recorder = queryTestSet(testPackage, testRunner, device); + activeTests = recorder.activeTests(); + ignoredTests = recorder.ignoredTests(); + logDebug(debug, "Active tests: %s", activeTests); + logDebug(debug, "Ignored tests: %s", ignoredTests); + } catch (Exception e) { + return resultBuilder + .addException(e) + .build(); + } + MultiRunITestListener multiRunListener = new MultiRunITestListener(listeners); multiRunListener.multiRunStarted(recorder.runName(), recorder.testCount()); - for (TestIdentifier test : activeTests) { - try { - logDebug(debug, "Running %s on [%s]", test, serial); - RemoteAndroidTestRunner runner = createConfiguredRunner(testPackage, testRunner, device); - runner.removeInstrumentationArg("package"); - runner.removeInstrumentationArg("class"); - runner.setMethodName(test.getClassName(), test.getTestName()); - runner.run(listeners); - } catch (Exception e) { - result.addException(e); + if (classLevelInstrumentation) { + // Run tests from each test class in a separate instrumentation instance. + Collection groupedTests = activeTests + .stream() + .collect(Collectors.groupingBy( + TestIdentifier::getClassName, + LinkedHashMap::new, + Collectors.mapping( + testIdentifier -> testIdentifier.getClassName() + "#" + testIdentifier.getTestName(), + Collectors.joining(",")))) + .values(); + + for (String testGroup : groupedTests) { + try { + logDebug(debug, "Running %s on [%s]", testGroup, serial); + RemoteAndroidTestRunner runner + = createConfiguredRunner(testPackage, testRunner, device); + runner.removeInstrumentationArg("package"); + runner.setClassName(testGroup); + runner.run(listeners); + } catch (Exception e) { + resultBuilder.addException(e); + } + } + } else { + // Run every test in a separate instrumentation instance. + for (TestIdentifier test : activeTests) { + try { + logDebug(debug, "Running %s on [%s]", test, serial); + RemoteAndroidTestRunner runner + = createConfiguredRunner(testPackage, testRunner, device); + runner.removeInstrumentationArg("package"); + runner.setMethodName(test.getClassName(), test.getTestName()); + runner.run(listeners); + } catch (Exception e) { + resultBuilder.addException(e); + } } } + for (TestIdentifier ignoredTest : ignoredTests) { multiRunListener.testStarted(ignoredTest); multiRunListener.testIgnored(ignoredTest); @@ -262,9 +302,9 @@ public DeviceResult run(AndroidDebugBridge adb) { multiRunListener.multiRunEnded(); } - result.endTests(); + resultBuilder.endTests(); - mapLogsToTests(deviceLogger, result); + mapLogsToTests(deviceLogger, resultBuilder); try { logDebug(debug, "About to grab screenshots and prepare output for [%s]", serial); @@ -273,14 +313,14 @@ public DeviceResult run(AndroidDebugBridge adb) { pullCoverageFile(device); } - cleanScreenshotsDirectory(result); - cleanFilesDirectory(result); + cleanScreenshotsDirectory(resultBuilder); + cleanFilesDirectory(resultBuilder); } catch (Exception e) { - result.addException(e); + resultBuilder.addException(e); } logDebug(debug, "Done running for [%s]", serial); - return result.build(); + return resultBuilder.build(); } private void grantReadWriteExternalStorage(DeviceDetails deviceDetails, IDevice device) diff --git a/spoon-runner/src/main/java/com/squareup/spoon/SpoonInstrumentationInfo.java b/spoon-runner/src/main/java/com/squareup/spoon/SpoonInstrumentationInfo.java index d5dcf45e..51aa6d44 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/SpoonInstrumentationInfo.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/SpoonInstrumentationInfo.java @@ -1,12 +1,13 @@ package com.squareup.spoon; import com.squareup.spoon.internal.thirdparty.axmlparser.AXMLParser; +import org.apache.commons.lang3.builder.ToStringBuilder; + import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -import org.apache.commons.lang3.builder.ToStringBuilder; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/spoon-runner/src/main/java/com/squareup/spoon/SpoonLogger.java b/spoon-runner/src/main/java/com/squareup/spoon/SpoonLogger.java index de9c7414..f88c77ab 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/SpoonLogger.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/SpoonLogger.java @@ -24,10 +24,10 @@ static void logDebug(boolean debug, String message, Object... args) { private static String getPrefix() { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); - if (stackTrace == null || stackTrace.length < 4) return "[BOGUS]"; + if (stackTrace.length < 4) return "[BOGUS]"; String className = stackTrace[3].getClassName(); String methodName = stackTrace[3].getMethodName(); - className = className.replaceAll("[a-z\\.]", ""); + className = className.replaceAll("[a-z.]", ""); String timestamp = DATE_FORMAT.get().format(new Date()); return String.format("%s [%s.%s] ", timestamp, className, methodName); } diff --git a/spoon-runner/src/main/java/com/squareup/spoon/SpoonRunner.java b/spoon-runner/src/main/java/com/squareup/spoon/SpoonRunner.java index ecfb6b7f..ae59660f 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/SpoonRunner.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/SpoonRunner.java @@ -7,7 +7,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.squareup.spoon.html.HtmlRenderer; - import org.apache.commons.io.FileUtils; import java.io.BufferedReader; @@ -64,6 +63,7 @@ public final class SpoonRunner { private File initScript; private final boolean grantAll; private final boolean singleInstrumentationCall; + private final boolean classLevelInstrumentation; private SpoonRunner(String title, File androidSdk, File testApk, List otherApks, File output, boolean debug, boolean noAnimations, Duration adbTimeout, Set serials, @@ -71,7 +71,7 @@ private SpoonRunner(String title, File androidSdk, File testApk, List othe String className, String methodName, IRemoteAndroidTestRunner.TestSize testSize, boolean allowNoDevices, List testRunListeners, boolean sequential, File initScript, boolean grantAll, boolean terminateAdb, boolean codeCoverage, - boolean singleInstrumentationCall) { + boolean singleInstrumentationCall, boolean classLevelInstrumentation) { this.title = title; this.androidSdk = androidSdk; this.otherApks = otherApks; @@ -94,6 +94,7 @@ private SpoonRunner(String title, File androidSdk, File testApk, List othe this.initScript = initScript; this.grantAll = grantAll; this.singleInstrumentationCall = singleInstrumentationCall; + this.classLevelInstrumentation = classLevelInstrumentation; if (sequential) { this.threadExecutor = Executors.newSingleThreadExecutor(); @@ -109,9 +110,9 @@ private SpoonRunner(String title, File androidSdk, File testApk, List othe * @return {@code true} if there were no test failures or exceptions thrown. */ public boolean run() { - otherApks.forEach(otherApk -> { - checkArgument(otherApk.exists(), "Could not find other APK: " + otherApk); - }); + otherApks.forEach(otherApk -> checkArgument( + otherApk.exists(), + "Could not find other APK: " + otherApk)); checkArgument(testApk.exists(), "Could not find test APK: " + testApk); AndroidDebugBridge adb = SpoonUtils.initAdb(androidSdk, adbTimeout); @@ -165,11 +166,9 @@ private SpoonSummary runTests(AndroidDebugBridge adb, Set serials, throw new RuntimeException("Unable to clean output directory: " + output, e); } - logDebug(debug, "Instrumentation: %s from %s", testInfo.getInstrumentationPackage(), - testApk.getAbsolutePath()); - otherApks.forEach(otherApk -> { - logDebug(debug, "Other: %s", otherApk.getAbsolutePath()); - }); + logDebug(debug, "Instrumentation: %s from %s", + testInfo.getInstrumentationPackage(), testApk.getAbsolutePath()); + otherApks.forEach(otherApk -> logDebug(debug, "Other: %s", otherApk.getAbsolutePath())); final SpoonSummary.Builder summary = new SpoonSummary.Builder().setTitle(title).start(); @@ -204,20 +203,18 @@ private SpoonSummary runTests(AndroidDebugBridge adb, Set serials, final String safeSerial = SpoonUtils.sanitizeSerial(serial); logDebug(debug, "[%s] Starting execution.", serial); final int safeShardIndex = shardIndex; - Runnable runnable = new Runnable() { - @Override public void run() { - try { - summary.addResult(safeSerial, - getTestRunner(serial, safeShardIndex, numShards, testInfo).run(adb)); - } catch (Exception e) { - e.printStackTrace(System.out); - summary.addResult(safeSerial, new DeviceResult.Builder().addException(e).build()); - } finally { - done.countDown(); - remaining.remove(serial); - logDebug(debug, "[%s] Execution done. (%s remaining %s)", serial, done.getCount(), - remaining); - } + Runnable runnable = () -> { + try { + summary.addResult(safeSerial, + getTestRunner(serial, safeShardIndex, numShards, testInfo).run(adb)); + } catch (Exception e) { + e.printStackTrace(System.out); + summary.addResult(safeSerial, new DeviceResult.Builder().addException(e).build()); + } finally { + done.countDown(); + remaining.remove(serial); + logDebug(debug, "[%s] Execution done. (%s remaining %s)", serial, done.getCount(), + remaining); } }; if (shard) { @@ -295,7 +292,8 @@ private SpoonDeviceRunner getTestRunner(String serial, int shardIndex, int numSh SpoonInstrumentationInfo testInfo) { return new SpoonDeviceRunner(testApk, otherApks, output, serial, shardIndex, numShards, debug, noAnimations, adbTimeout, testInfo, instrumentationArgs, className, methodName, testSize, - testRunListeners, codeCoverage, grantAll, singleInstrumentationCall); + testRunListeners, codeCoverage, grantAll, singleInstrumentationCall, + classLevelInstrumentation); } /** Build a test suite for the specified devices and configuration. */ @@ -323,6 +321,7 @@ public static class Builder { private boolean codeCoverage; private boolean shard = false; private boolean singleInstrumentationCall = false; + private boolean classLevelInstrumentation = false; /** Identifying title for this execution. */ public Builder setTitle(String title) { @@ -464,6 +463,11 @@ public Builder setSingleInstrumentationCall(boolean singleInstrumentationCall) { return this; } + public Builder setClassLevelInstrumentation(Boolean classLevelInstrumentation) { + this.classLevelInstrumentation = classLevelInstrumentation; + return this; + } + public SpoonRunner build() { checkNotNull(androidSdk, "SDK is required."); checkArgument(androidSdk.exists(), "SDK path does not exist."); @@ -477,7 +481,7 @@ public SpoonRunner build() { return new SpoonRunner(title, androidSdk, testApk, otherApks, output, debug, noAnimations, adbTimeout, serials, skipDevices, shard, instrumentationArgs, className, methodName, testSize, allowNoDevices, testRunListeners, sequential, initScript, grantAll, - terminateAdb, codeCoverage, singleInstrumentationCall); + terminateAdb, codeCoverage, singleInstrumentationCall, classLevelInstrumentation); } } diff --git a/spoon-runner/src/main/java/com/squareup/spoon/SpoonTestRunListener.java b/spoon-runner/src/main/java/com/squareup/spoon/SpoonTestRunListener.java index da582b10..2d2c2323 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/SpoonTestRunListener.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/SpoonTestRunListener.java @@ -2,6 +2,7 @@ import com.android.ddmlib.testrunner.ITestRunListener; import com.android.ddmlib.testrunner.TestIdentifier; + import java.util.HashMap; import java.util.Map; diff --git a/spoon-runner/src/main/java/com/squareup/spoon/SpoonUtils.java b/spoon-runner/src/main/java/com/squareup/spoon/SpoonUtils.java index 93393de0..480db441 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/SpoonUtils.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/SpoonUtils.java @@ -9,6 +9,9 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import com.madgag.gif.fmsware.AnimatedGifEncoder; +import org.apache.commons.io.FileUtils; + +import javax.imageio.ImageIO; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.File; @@ -21,8 +24,6 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; -import javax.imageio.ImageIO; -import org.apache.commons.io.FileUtils; import static com.android.ddmlib.FileListingService.FileEntry; import static com.android.ddmlib.FileListingService.TYPE_DIRECTORY; @@ -74,10 +75,8 @@ static FileEntry obtainDirectoryFileEntry(String path) { lastEntry = c.newInstance(lastEntry, part, TYPE_DIRECTORY, lastEntry == null); } return lastEntry; - } catch (NoSuchMethodException ignored) { - } catch (InvocationTargetException ignored) { - } catch (InstantiationException ignored) { - } catch (IllegalAccessException ignored) { + } catch (NoSuchMethodException | InvocationTargetException + | InstantiationException | IllegalAccessException ignored) { } return null; } diff --git a/spoon-runner/src/main/java/com/squareup/spoon/XmlTestRunListener.java b/spoon-runner/src/main/java/com/squareup/spoon/XmlTestRunListener.java index 94761baf..f6862b14 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/XmlTestRunListener.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/XmlTestRunListener.java @@ -1,7 +1,6 @@ package com.squareup.spoon; import java.io.File; -import java.io.IOException; /** * An {@link com.android.ddmlib.testrunner.XmlTestRunListener XmlTestRunListener} that points @@ -22,7 +21,7 @@ public void testRunStarted(String runName, int numTests) { getRunResult().testRunStarted(runName, numTests); } - @Override protected File getResultFile(File reportDir) throws IOException { + @Override protected File getResultFile(File reportDir) { file.getParentFile().mkdirs(); return file; } diff --git a/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlDevice.java b/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlDevice.java index ca691272..86cdcfcf 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlDevice.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlDevice.java @@ -4,6 +4,7 @@ import com.squareup.spoon.DeviceResult; import com.squareup.spoon.DeviceTest; import com.squareup.spoon.DeviceTestResult; + import java.io.File; import java.util.ArrayList; import java.util.List; diff --git a/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlIndex.java b/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlIndex.java index cee8f82e..6e46d255 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlIndex.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlIndex.java @@ -6,6 +6,8 @@ import com.squareup.spoon.DeviceTest; import com.squareup.spoon.DeviceTestResult; import com.squareup.spoon.SpoonSummary; +import org.jetbrains.annotations.NotNull; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -94,7 +96,7 @@ static Device from(String serial, DeviceResult result) { this.executionFailed = executionFailed; } - @Override public int compareTo(Device other) { + @Override public int compareTo(@NotNull Device other) { if (name == null && other.name == null) { return serial.compareTo(other.serial); } @@ -137,7 +139,7 @@ static TestResult from(String serial, DeviceTest test, DeviceTestResult testResu this.status = status; } - @Override public int compareTo(TestResult other) { + @Override public int compareTo(@NotNull TestResult other) { return 0; } } diff --git a/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlLog.java b/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlLog.java index 2277df6f..ed8823b4 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlLog.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlLog.java @@ -3,6 +3,7 @@ import com.android.ddmlib.logcat.LogCatMessage; import com.squareup.spoon.DeviceTest; import com.squareup.spoon.DeviceTestResult; + import java.util.List; import static java.util.stream.Collectors.toList; diff --git a/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlRenderer.java b/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlRenderer.java index 2ba67ce9..0cde2dfd 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlRenderer.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlRenderer.java @@ -11,6 +11,10 @@ import com.squareup.spoon.DeviceTest; import com.squareup.spoon.DeviceTestResult; import com.squareup.spoon.SpoonSummary; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.lesscss.LessCompiler; + import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; @@ -22,9 +26,6 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import org.lesscss.LessCompiler; import static java.nio.charset.StandardCharsets.UTF_8; diff --git a/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlTest.java b/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlTest.java index b4f7dc67..5869533e 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlTest.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlTest.java @@ -5,6 +5,7 @@ import com.squareup.spoon.DeviceTest; import com.squareup.spoon.DeviceTestResult; import com.squareup.spoon.SpoonSummary; + import java.io.File; import java.util.ArrayList; import java.util.List; diff --git a/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlTv.java b/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlTv.java index dc402ce2..7a1a298c 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlTv.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlTv.java @@ -5,6 +5,8 @@ import com.squareup.spoon.DeviceResult; import com.squareup.spoon.DeviceTestResult; import com.squareup.spoon.SpoonSummary; +import org.jetbrains.annotations.NotNull; + import java.io.File; import java.util.List; import java.util.stream.Collectors; @@ -73,7 +75,7 @@ static Device from(String serial, DeviceResult result, File outputPath) { this.testResults = testResults; } - @Override public int compareTo(Device other) { + @Override public int compareTo(@NotNull Device other) { if (name == null && other.name == null) { return serial.compareTo(other.serial); } diff --git a/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlUtils.java b/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlUtils.java index edada80a..d58f38ac 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlUtils.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/html/HtmlUtils.java @@ -3,6 +3,8 @@ import com.squareup.spoon.DeviceDetails; import com.squareup.spoon.DeviceTestResult; import com.squareup.spoon.misc.StackTrace; +import org.apache.commons.lang3.StringEscapeUtils; + import java.io.File; import java.io.IOException; import java.text.Format; @@ -11,7 +13,6 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.atomic.AtomicLong; -import org.apache.commons.lang3.StringEscapeUtils; import static java.util.stream.Collectors.toList; diff --git a/spoon-runner/src/main/java/com/squareup/spoon/main.kt b/spoon-runner/src/main/java/com/squareup/spoon/main.kt index e3b5c108..aef35d46 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/main.kt +++ b/spoon-runner/src/main/java/com/squareup/spoon/main.kt @@ -14,7 +14,7 @@ fun main(vararg args: String) { cli.otherApks.forEach { addOtherApk(it) } cli.sdk?.let(this::setAndroidSdk) cli.title?.let(this::setTitle) - setInstrumentationArgs(cli.instrumentationArgs); + setInstrumentationArgs(cli.instrumentationArgs) cli.className?.let(this::setClassName) cli.methodName?.let(this::setMethodName) cli.size?.let(this::setTestSize) @@ -31,6 +31,7 @@ fun main(vararg args: String) { setDebug(cli.debug) setCodeCoverage(cli.coverage) setSingleInstrumentationCall(cli.singleInstrumentationCall) + setClassLevelInstrumentation(cli.classLevelInstrumentation) }.build() if (!runner.run() && !cli.alwaysZero) { diff --git a/spoon-runner/src/main/java/com/squareup/spoon/misc/StackTrace.java b/spoon-runner/src/main/java/com/squareup/spoon/misc/StackTrace.java index 431fc3b8..e2acf5af 100644 --- a/spoon-runner/src/main/java/com/squareup/spoon/misc/StackTrace.java +++ b/spoon-runner/src/main/java/com/squareup/spoon/misc/StackTrace.java @@ -1,12 +1,13 @@ package com.squareup.spoon.misc; import com.google.common.collect.ImmutableList; +import org.apache.commons.lang3.StringUtils; + import java.util.ArrayDeque; import java.util.Deque; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.lang3.StringUtils; import static com.google.common.base.Preconditions.checkNotNull; diff --git a/spoon-runner/src/test/java/com/squareup/spoon/CliArgsTest.kt b/spoon-runner/src/test/java/com/squareup/spoon/CliArgsTest.kt index 79719eb5..a7c26fd3 100644 --- a/spoon-runner/src/test/java/com/squareup/spoon/CliArgsTest.kt +++ b/spoon-runner/src/test/java/com/squareup/spoon/CliArgsTest.kt @@ -3,8 +3,8 @@ package com.squareup.spoon import com.google.common.truth.Truth.assertThat import com.xenomachina.argparser.ArgParser import com.xenomachina.argparser.SystemExitException -import java.io.File; import org.junit.Test +import java.io.File class CliArgsTest { diff --git a/spoon-runner/src/test/java/com/squareup/spoon/SpoonHtmlRendererTest.java b/spoon-runner/src/test/java/com/squareup/spoon/SpoonHtmlRendererTest.java index cc674ecc..9f007a7b 100644 --- a/spoon-runner/src/test/java/com/squareup/spoon/SpoonHtmlRendererTest.java +++ b/spoon-runner/src/test/java/com/squareup/spoon/SpoonHtmlRendererTest.java @@ -1,6 +1,11 @@ package com.squareup.spoon; import com.squareup.spoon.html.HtmlRenderer; +import org.apache.commons.io.FileUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -12,10 +17,6 @@ import java.nio.charset.MalformedInputException; import java.nio.charset.StandardCharsets; import java.util.Iterator; -import org.apache.commons.io.FileUtils; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; public final class SpoonHtmlRendererTest { diff --git a/spoon-runner/src/test/java/com/squareup/spoon/html/HtmlIndexTest.java b/spoon-runner/src/test/java/com/squareup/spoon/html/HtmlIndexTest.java index 84a4ddb5..e77832ed 100644 --- a/spoon-runner/src/test/java/com/squareup/spoon/html/HtmlIndexTest.java +++ b/spoon-runner/src/test/java/com/squareup/spoon/html/HtmlIndexTest.java @@ -1,9 +1,10 @@ package com.squareup.spoon.html; +import org.junit.Test; + import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.junit.Test; import static com.google.common.truth.Truth.assertThat; import static com.squareup.spoon.html.HtmlIndex.Device; diff --git a/spoon-runner/src/test/java/com/squareup/spoon/html/HtmlUtilsTest.java b/spoon-runner/src/test/java/com/squareup/spoon/html/HtmlUtilsTest.java index a5a43860..3b9573f5 100644 --- a/spoon-runner/src/test/java/com/squareup/spoon/html/HtmlUtilsTest.java +++ b/spoon-runner/src/test/java/com/squareup/spoon/html/HtmlUtilsTest.java @@ -1,15 +1,16 @@ package com.squareup.spoon.html; -import java.io.File; -import java.util.List; -import org.junit.Test; import com.squareup.spoon.html.HtmlUtils.ExceptionInfo; import com.squareup.spoon.misc.StackTrace; +import org.junit.Test; + +import java.io.File; +import java.util.List; import static com.google.common.truth.Truth.assertThat; import static com.squareup.spoon.html.HtmlUtils.createRelativeUri; -import static com.squareup.spoon.html.HtmlUtils.processStackTrace; import static com.squareup.spoon.html.HtmlUtils.humanReadableDuration; +import static com.squareup.spoon.html.HtmlUtils.processStackTrace; public final class HtmlUtilsTest { @Test public void relativeUriCreation() { @@ -37,7 +38,7 @@ public void relativeUriCreationFailsIfSame() { } /** - * This test is similar to {@link StackTraceTest#nestedCustomExceptionUnexpectedFormat}. + * This test is similar to {@code StackTraceTest#nestedCustomExceptionUnexpectedFormat}. * * The intent of this test is to check that unexpected format exceptions still print something * useful to the user in the test results. diff --git a/spoon-runner/src/test/java/com/squareup/spoon/misc/StackTraceTest.java b/spoon-runner/src/test/java/com/squareup/spoon/misc/StackTraceTest.java index b41ed830..98858ad1 100644 --- a/spoon-runner/src/test/java/com/squareup/spoon/misc/StackTraceTest.java +++ b/spoon-runner/src/test/java/com/squareup/spoon/misc/StackTraceTest.java @@ -1,8 +1,9 @@ package com.squareup.spoon.misc; +import org.junit.Test; + import java.util.ArrayDeque; import java.util.Deque; -import org.junit.Test; import static com.google.common.truth.Truth.assertThat;