TestExecutor.java

/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * 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 androidx.test.internal.runner;

import android.app.Instrumentation;
import android.os.Bundle;
import android.util.Log;
import androidx.test.internal.runner.listener.InstrumentationRunListener;
import androidx.test.internal.util.Checks;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import org.junit.runner.Description;
import org.junit.runner.JUnitCore;
import org.junit.runner.Request;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;

/**
 * Class that given a Request containing tests to run, wires up the test listeners and actually
 * executes the test using upstream JUnit
 */
public final class TestExecutor {
  private static final String LOG_TAG = "TestExecutor";

  private final List<RunListener> listeners;
  private final Instrumentation instr;

  private TestExecutor(Builder builder) {
    listeners = Checks.checkNotNull(builder.listeners);
    instr = builder.instr;
  }

  /** Execute the tests */
  public Bundle execute(Request request) {
    Bundle resultBundle = new Bundle();
    Result junitResults = new Result();
    try {
      JUnitCore testRunner = new JUnitCore();
      setUpListeners(testRunner);
      junitResults = testRunner.run(request);
    } catch (Throwable t) {
      final String msg = "Fatal exception when running tests";
      Log.e(LOG_TAG, msg, t);
      junitResults.getFailures().add(new Failure(Description.createSuiteDescription(msg), t));
    } finally {
      ByteArrayOutputStream summaryStream = new ByteArrayOutputStream();
      // create the stream used to output summary data to the user
      PrintStream summaryWriter = new PrintStream(summaryStream);
      reportRunEnded(listeners, summaryWriter, resultBundle, junitResults);
      summaryWriter.close();
      resultBundle.putString(
          Instrumentation.REPORT_KEY_STREAMRESULT, String.format("\n%s", summaryStream.toString()));
    }
    return resultBundle;
  }

  /** Initialize listeners and add them to the JUnitCore runner */
  private void setUpListeners(JUnitCore testRunner) {
    for (RunListener listener : listeners) {
      Log.d(LOG_TAG, "Adding listener " + listener.getClass().getName());
      testRunner.addListener(listener);
      if (listener instanceof InstrumentationRunListener) {
        ((InstrumentationRunListener) listener).setInstrumentation(instr);
      }
    }
  }

  private void reportRunEnded(
      List<RunListener> listeners,
      PrintStream summaryWriter,
      Bundle resultBundle,
      Result jUnitResults) {
    for (RunListener listener : listeners) {
      if (listener instanceof InstrumentationRunListener) {
        ((InstrumentationRunListener) listener)
            .instrumentationRunFinished(summaryWriter, resultBundle, jUnitResults);
      }
    }
  }

  public static class Builder {
    private final List<RunListener> listeners = new ArrayList<RunListener>();
    private final Instrumentation instr;

    public Builder(Instrumentation instr) {
      this.instr = instr;
    }

    /**
     * Adds a custom RunListener
     *
     * @param listener the listener to add
     * @return the {@link androidx.test.internal.runner.TestExecutor.Builder}
     */
    public Builder addRunListener(RunListener listener) {
      listeners.add(listener);
      return this;
    }

    public TestExecutor build() {
      return new TestExecutor(this);
    }
  }
}