OrchestrationListenerManager.java
/*
* Copyright (C) 2017 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.orchestrator.listeners;
import static androidx.test.orchestrator.junit.BundleJUnitUtils.getDescription;
import static androidx.test.orchestrator.junit.BundleJUnitUtils.getFailure;
import static androidx.test.orchestrator.junit.BundleJUnitUtils.getResult;
import android.app.Instrumentation;
import android.os.Bundle;
import android.util.Log;
import androidx.test.orchestrator.junit.ParcelableDescription;
import androidx.test.orchestrator.junit.ParcelableFailure;
import java.util.ArrayList;
import java.util.List;
/** Container class for all orchestration listeners */
public final class OrchestrationListenerManager {
private static final String TAG = "ListenerManager";
/** Message types sent from the remote instrumentation */
public enum TestEvent {
TEST_RUN_STARTED,
TEST_RUN_FINISHED,
TEST_STARTED,
TEST_FINISHED,
TEST_FAILURE,
TEST_ASSUMPTION_FAILURE,
TEST_IGNORED
}
public static final String KEY_TEST_EVENT = "TestEvent";
private final List<OrchestrationRunListener> listeners = new ArrayList<>();
private final Instrumentation instrumentation;
private boolean markTerminationAsFailure = false;
private ParcelableDescription lastDescription;
public OrchestrationListenerManager(Instrumentation instrumentation) {
if (null == instrumentation) {
throw new IllegalArgumentException("Instrumentation must not be null");
}
this.instrumentation = instrumentation;
}
public void addListener(OrchestrationRunListener listener) {
listener.setInstrumentation(instrumentation);
listeners.add(listener);
}
/** To be called after test collection, before the first test begins. */
public void orchestrationRunStarted(int testCount) {
for (OrchestrationRunListener listener : listeners) {
listener.orchestrationRunStarted(testCount);
}
}
/** To be called when the test process begins */
public void testProcessStarted(ParcelableDescription description) {
lastDescription = description;
markTerminationAsFailure = true;
}
/** To be called when the test process terminates, with the result from standard out. */
public void testProcessFinished(String outputFile) {
if (markTerminationAsFailure) {
for (OrchestrationRunListener listener : listeners) {
listener.testFailure(
new ParcelableFailure(
lastDescription,
new Throwable(
"Test instrumentation process crashed. Check " + outputFile + " for details")));
listener.testFinished(lastDescription);
}
}
}
/**
* Takes a test message and parses it out for all the listeners.
*
* @param bundle A bundle containing a key describing the type of message, and a bundle with the
* appropriate parcelable imitation of aJ Unit object.
*/
public void handleNotification(Bundle bundle) {
bundle.setClassLoader(getClass().getClassLoader());
cacheStatus(bundle);
for (OrchestrationRunListener listener : listeners) {
handleNotificationForListener(listener, bundle);
}
}
private void cacheStatus(Bundle bundle) {
if (getDescription(bundle) != null) {
lastDescription = getDescription(bundle);
}
TestEvent status = TestEvent.valueOf(bundle.getString(KEY_TEST_EVENT));
switch (status) {
case TEST_RUN_STARTED:
// Likely already set true in testProcessStarted(), but no reason to not set again.
markTerminationAsFailure = true;
break;
case TEST_FAILURE:
// After failure, no need to report further failures if process crashes
markTerminationAsFailure = false;
break;
case TEST_RUN_FINISHED:
// It's now ok to terminate safely.
markTerminationAsFailure = false;
break;
default:
// We only care about three cases.
}
}
private void handleNotificationForListener(OrchestrationRunListener listener, Bundle bundle) {
TestEvent status = TestEvent.valueOf(bundle.getString(KEY_TEST_EVENT));
switch (status) {
case TEST_RUN_STARTED:
listener.testRunStarted(getDescription(bundle));
break;
case TEST_STARTED:
listener.testStarted(getDescription(bundle));
break;
case TEST_FINISHED:
listener.testFinished(getDescription(bundle));
break;
case TEST_FAILURE:
listener.testFailure(getFailure(bundle));
break;
case TEST_ASSUMPTION_FAILURE:
listener.testAssumptionFailure(getFailure(bundle));
break;
case TEST_IGNORED:
listener.testIgnored(getDescription(bundle));
break;
case TEST_RUN_FINISHED:
listener.testRunFinished(getResult(bundle));
break;
default:
Log.e(TAG, "Unknown notification type");
}
}
}