<commit_before>package com.squareup.spoon;

import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner;
import com.android.ddmlib.testrunner.ITestRunListener;
import com.beust.jcommander.IStringConverter;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.beust.jcommander.converters.IParameterSplitter;
import com.google.common.collect.ImmutableSet;
import com.squareup.spoon.html.HtmlRenderer;
import org.apache.commons.io.FileUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.squareup.spoon.DeviceTestResult.Status;
import static com.squareup.spoon.SpoonInstrumentationInfo.parseFromFile;
import static com.squareup.spoon.SpoonLogger.logDebug;
import static com.squareup.spoon.SpoonLogger.logInfo;
import static java.util.Collections.synchronizedSet;

/** Represents a collection of devices and the test configuration to be executed. */
public final class SpoonRunner {
  private static final String DEFAULT_TITLE = "Spoon Execution";
  public static final String DEFAULT_OUTPUT_DIRECTORY = "spoon-output";
  private static final int DEFAULT_ADB_TIMEOUT_SEC = 10 * 60; //10 minutes
  private final ExecutorService threadExecutor;

  private final String title;
  private final File androidSdk;
  private final File applicationApk;
  private final File instrumentationApk;
  private final File output;
  private final boolean debug;
  private final boolean noAnimations;
  private final int adbTimeoutMillis;
  private final List<String> instrumentationArgs;
  private final String className;
  private final String methodName;
  private final Set<String> serials;
  private final Set<String> skipDevices;
  private final boolean shard;
  private final String classpath;
  private final IRemoteAndroidTestRunner.TestSize testSize;
  private boolean codeCoverage;
  private final boolean failIfNoDeviceConnected;
  private final List<ITestRunListener> testRunListeners;
  private final boolean terminateAdb;
  private File initScript;
  private final boolean grantAll;

  private SpoonRunner(String title, File androidSdk, File applicationApk, File instrumentationApk,
      File output, boolean debug, boolean noAnimations, int adbTimeoutMillis, Set<String> serials,
      Set<String> skipDevices,
      boolean shard, String classpath, List<String> instrumentationArgs, String className,
      String methodName, IRemoteAndroidTestRunner.TestSize testSize,
      boolean failIfNoDeviceConnected, List<ITestRunListener> testRunListeners, boolean sequential,
      File initScript, boolean grantAll, boolean terminateAdb, boolean codeCoverage) {
    this.title = title;
    this.androidSdk = androidSdk;
    this.applicationApk = applicationApk;
    this.instrumentationApk = instrumentationApk;
    this.output = output;
    this.debug = debug;
    this.noAnimations = noAnimations;
    this.adbTimeoutMillis = adbTimeoutMillis;
    this.instrumentationArgs = instrumentationArgs;
    this.className = className;
    this.methodName = methodName;
    this.classpath = classpath;
    this.testSize = testSize;
    this.skipDevices = skipDevices;
    this.codeCoverage = codeCoverage;
    this.serials = ImmutableSet.copyOf(serials);
    this.shard = shard;
    this.failIfNoDeviceConnected = failIfNoDeviceConnected;
    this.testRunListeners = testRunListeners;
    this.terminateAdb = terminateAdb;
    this.initScript = initScript;
    this.grantAll = grantAll;

    if (sequential) {
      this.threadExecutor = Executors.newSingleThreadExecutor();
    } else {
      this.threadExecutor = Executors.newCachedThreadPool();
    }
  }

  /**
   * Install and execute the tests on all specified devices.
   *
   * @return {@code true} if there were no test failures or exceptions thrown.
   */
  public boolean run() {
    checkArgument(applicationApk.exists(), "Could not find application APK.");
    checkArgument(instrumentationApk.exists(), "Could not find instrumentation APK.");

    AndroidDebugBridge adb = SpoonUtils.initAdb(androidSdk, adbTimeoutMillis);

    try {
      final SpoonInstrumentationInfo testInfo = parseFromFile(instrumentationApk);

      // If we were given an empty serial set, load all available devices.
      Set<String> serials = this.serials;
      if (serials.isEmpty()) {
        serials = SpoonUtils.findAllDevices(adb, testInfo.getMinSdkVersion());
      }
      if (this.skipDevices != null && !this.skipDevices.isEmpty()) {
        serials.removeAll(this.skipDevices);
      }
      if (failIfNoDeviceConnected && serials.isEmpty()) {
        throw new RuntimeException("No device(s) found.");
      }

      // Execute all the things...
      SpoonSummary summary = runTests(adb, serials);
      // ...and render to HTML
      new HtmlRenderer(summary, SpoonUtils.GSON, output).render();

      return parseOverallSuccess(summary);
    } finally {
      if (terminateAdb) {
        AndroidDebugBridge.terminate();
      }
    }
  }

  private SpoonSummary runTests(AndroidDebugBridge adb, Set<String> serials) {
    int targetCount = serials.size();
    logInfo("Executing instrumentation suite on %d device(s).", targetCount);

    try {
      FileUtils.deleteDirectory(output);
    } catch (IOException e) {
      throw new RuntimeException("Unable to clean output directory: " + output, e);
    }

    final SpoonInstrumentationInfo testInfo = parseFromFile(instrumentationApk);
    logDebug(debug, "Application: %s from %s", testInfo.getApplicationPackage(),
        applicationApk.getAbsolutePath());
    logDebug(debug, "Instrumentation: %s from %s", testInfo.getInstrumentationPackage(),
        instrumentationApk.getAbsolutePath());

    final SpoonSummary.Builder summary = new SpoonSummary.Builder().setTitle(title).start();

    if (testSize != null) {
      summary.setTestSize(testSize);
    }

    if (targetCount == 1) {
      // Since there is only one device just execute it synchronously in this process.
      executeInitScript();
      String serial = serials.iterator().next();
      String safeSerial = SpoonUtils.sanitizeSerial(serial);
      try {
        logDebug(debug, "[%s] Starting execution.", serial);
        summary.addResult(safeSerial, getTestRunner(serial, 0, 0, testInfo).run(adb));
      } catch (Exception e) {
        logDebug(debug, "[%s] Execution exception!", serial);
        e.printStackTrace(System.out);
        summary.addResult(safeSerial, new DeviceResult.Builder().addException(e).build());
      } finally {
        logDebug(debug, "[%s] Execution done.", serial);<commit_msg>Parse instrumentation apk once. (#408)<commit_after>package com.squareup.spoon;

import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner;
import com.android.ddmlib.testrunner.ITestRunListener;
import com.beust.jcommander.IStringConverter;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.beust.jcommander.converters.IParameterSplitter;
import com.google.common.collect.ImmutableSet;
import com.squareup.spoon.html.HtmlRenderer;
import org.apache.commons.io.FileUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.squareup.spoon.DeviceTestResult.Status;
import static com.squareup.spoon.SpoonInstrumentationInfo.parseFromFile;
import static com.squareup.spoon.SpoonLogger.logDebug;
import static com.squareup.spoon.SpoonLogger.logInfo;
import static java.util.Collections.synchronizedSet;

/** Represents a collection of devices and the test configuration to be executed. */
public final class SpoonRunner {
  private static final String DEFAULT_TITLE = "Spoon Execution";
  public static final String DEFAULT_OUTPUT_DIRECTORY = "spoon-output";
  private static final int DEFAULT_ADB_TIMEOUT_SEC = 10 * 60; //10 minutes
  private final ExecutorService threadExecutor;

  private final String title;
  private final File androidSdk;
  private final File applicationApk;
  private final File instrumentationApk;
  private final File output;
  private final boolean debug;
  private final boolean noAnimations;
  private final int adbTimeoutMillis;
  private final List<String> instrumentationArgs;
  private final String className;
  private final String methodName;
  private final Set<String> serials;
  private final Set<String> skipDevices;
  private final boolean shard;
  private final String classpath;
  private final IRemoteAndroidTestRunner.TestSize testSize;
  private boolean codeCoverage;
  private final boolean failIfNoDeviceConnected;
  private final List<ITestRunListener> testRunListeners;
  private final boolean terminateAdb;
  private File initScript;
  private final boolean grantAll;

  private SpoonRunner(String title, File androidSdk, File applicationApk, File instrumentationApk,
      File output, boolean debug, boolean noAnimations, int adbTimeoutMillis, Set<String> serials,
      Set<String> skipDevices,
      boolean shard, String classpath, List<String> instrumentationArgs, String className,
      String methodName, IRemoteAndroidTestRunner.TestSize testSize,
      boolean failIfNoDeviceConnected, List<ITestRunListener> testRunListeners, boolean sequential,
      File initScript, boolean grantAll, boolean terminateAdb, boolean codeCoverage) {
    this.title = title;
    this.androidSdk = androidSdk;
    this.applicationApk = applicationApk;
    this.instrumentationApk = instrumentationApk;
    this.output = output;
    this.debug = debug;
    this.noAnimations = noAnimations;
    this.adbTimeoutMillis = adbTimeoutMillis;
    this.instrumentationArgs = instrumentationArgs;
    this.className = className;
    this.methodName = methodName;
    this.classpath = classpath;
    this.testSize = testSize;
    this.skipDevices = skipDevices;
    this.codeCoverage = codeCoverage;
    this.serials = ImmutableSet.copyOf(serials);
    this.shard = shard;
    this.failIfNoDeviceConnected = failIfNoDeviceConnected;
    this.testRunListeners = testRunListeners;
    this.terminateAdb = terminateAdb;
    this.initScript = initScript;
    this.grantAll = grantAll;

    if (sequential) {
      this.threadExecutor = Executors.newSingleThreadExecutor();
    } else {
      this.threadExecutor = Executors.newCachedThreadPool();
    }
  }

  /**
   * Install and execute the tests on all specified devices.
   *
   * @return {@code true} if there were no test failures or exceptions thrown.
   */
  public boolean run() {
    checkArgument(applicationApk.exists(), "Could not find application APK.");
    checkArgument(instrumentationApk.exists(), "Could not find instrumentation APK.");

    AndroidDebugBridge adb = SpoonUtils.initAdb(androidSdk, adbTimeoutMillis);

    try {
      final SpoonInstrumentationInfo testInfo = parseFromFile(instrumentationApk);

      // If we were given an empty serial set, load all available devices.
      Set<String> serials = this.serials;
      if (serials.isEmpty()) {
        serials = SpoonUtils.findAllDevices(adb, testInfo.getMinSdkVersion());
      }
      if (this.skipDevices != null && !this.skipDevices.isEmpty()) {
        serials.removeAll(this.skipDevices);
      }
      if (failIfNoDeviceConnected && serials.isEmpty()) {
        throw new RuntimeException("No device(s) found.");
      }

      // Execute all the things...
      SpoonSummary summary = runTests(adb, serials, testInfo);
      // ...and render to HTML
      new HtmlRenderer(summary, SpoonUtils.GSON, output).render();

      return parseOverallSuccess(summary);
    } finally {
      if (terminateAdb) {
        AndroidDebugBridge.terminate();
      }
    }
  }

  private SpoonSummary runTests(AndroidDebugBridge adb, Set<String> serials,
      final SpoonInstrumentationInfo testInfo) {
    int targetCount = serials.size();
    logInfo("Executing instrumentation suite on %d device(s).", targetCount);

    try {
      FileUtils.deleteDirectory(output);
    } catch (IOException e) {
      throw new RuntimeException("Unable to clean output directory: " + output, e);
    }

    logDebug(debug, "Application: %s from %s", testInfo.getApplicationPackage(),
        applicationApk.getAbsolutePath());
    logDebug(debug, "Instrumentation: %s from %s", testInfo.getInstrumentationPackage(),
        instrumentationApk.getAbsolutePath());

    final SpoonSummary.Builder summary = new SpoonSummary.Builder().setTitle(title).start();

    if (testSize != null) {
      summary.setTestSize(testSize);
    }

    if (targetCount == 1) {
      // Since there is only one device just execute it synchronously in this process.
      executeInitScript();
      String serial = serials.iterator().next();
      String safeSerial = SpoonUtils.sanitizeSerial(serial);
      try {
        logDebug(debug, "[%s] Starting execution.", serial);
        summary.addResult(safeSerial, getTestRunner(serial, 0, 0, testInfo).run(adb));
      } catch (Exception e) {
        logDebug(debug, "[%s] Execution exception!", serial);
        e.printStackTrace(System.out);
        summary.addResult(safeSerial, new DeviceResult.Builder().addException(e).build());
      } finally {
        logDebug(debug, "[%s] Execution done.", serial);
{'fraction_non_alphanumeric': 0.09178912180513964, 'fraction_numerical': 0.0014624973883975206, 'mean_word_length': 4.235144002916515, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6078318', 'n_tokens_mistral': 4112, 'n_tokens_neox': 4040, 'n_words': 1021}