blob: edecf725805fce347f8c27dc23035a2dd040bf81 [file] [log] [blame]
/*
* Copyright (C) 2010 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 com.android.tradefed.result;
import com.android.ddmlib.testrunner.TestIdentifier;
import com.android.ddmlib.testrunner.TestResult.TestStatus;
import com.android.ddmlib.testrunner.TestRunResult;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.config.Option;
import com.android.tradefed.invoker.IInvocationContext;
import com.google.common.annotations.VisibleForTesting;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* A {@link ITestInvocationListener} that will collect all test results.
* <p/>
* Although the data structures used in this object are thread-safe, the
* {@link ITestInvocationListener} callbacks must be called in the correct order.
*/
public class CollectingTestListener implements ITestInvocationListener {
// Stores the test results
// Uses a synchronized map to make thread safe.
// Uses a LinkedHashmap to have predictable iteration order
private Map<String, TestRunResult> mRunResultsMap =
Collections.synchronizedMap(new LinkedHashMap<String, TestRunResult>());
private Map<TestRunResult, IInvocationContext> mModuleContextMap =
Collections.synchronizedMap(new LinkedHashMap<TestRunResult, IInvocationContext>());
private TestRunResult mCurrentResults = new TestRunResult();
private IInvocationContext mCurrentModuleContext = null;
/** represents sums of tests in each TestStatus state for all runs.
* Indexed by TestStatus.ordinal() */
private int[] mStatusCounts = new int[TestStatus.values().length];
/** tracks if mStatusCounts is accurate, or if it needs to be recalculated */
private boolean mIsCountDirty = true;
@Option(name = "aggregate-metrics", description =
"attempt to add test metrics values for test runs with the same name." )
private boolean mIsAggregateMetrics = false;
private IBuildInfo mBuildInfo;
private IInvocationContext mContext;
/** Toggle the 'aggregate metrics' option */
protected void setIsAggregrateMetrics(boolean aggregate) {
mIsAggregateMetrics = aggregate;
}
/**
* {@inheritDoc}
*/
@Override
public void invocationStarted(IInvocationContext context) {
mContext = context;
if (context != null) {
mBuildInfo = context.getBuildInfos().get(0);
}
}
/**
* Return the primary build info that was reported via {@link
* #invocationStarted(IInvocationContext)}. Primary build is the build returned by the first
* build provider of the running configuration. Returns null if there is no context (no build to
* test case).
*/
public IBuildInfo getPrimaryBuildInfo() {
if (mContext == null) {
return null;
} else {
return mContext.getBuildInfos().get(0);
}
}
/**
* Return the invocation context that was reported via
* {@link #invocationStarted(IInvocationContext)}
*/
public IInvocationContext getInvocationContext() {
return mContext;
}
/**
* Returns the build info.
*
* @deprecated rely on the {@link IBuildInfo} from {@link #getInvocationContext()}.
*/
@Deprecated
public IBuildInfo getBuildInfo() {
return mBuildInfo;
}
/**
* Set the build info. Should only be used for testing.
*
* @deprecated Not necessary for testing anymore.
*/
@VisibleForTesting
@Deprecated
public void setBuildInfo(IBuildInfo buildInfo) {
mBuildInfo = buildInfo;
}
@Override
public void testModuleStarted(IInvocationContext moduleContext) {
mCurrentModuleContext = moduleContext;
}
@Override
public void testModuleEnded() {
mCurrentModuleContext = null;
}
/**
* {@inheritDoc}
*/
@Override
public void testRunStarted(String name, int numTests) {
if (mRunResultsMap.containsKey(name)) {
// rerun of previous run. Add test results to it
mCurrentResults = mRunResultsMap.get(name);
} else {
// new run
mCurrentResults = new TestRunResult();
mCurrentResults.setAggregateMetrics(mIsAggregateMetrics);
mRunResultsMap.put(name, mCurrentResults);
// track the module context associated with the results.
if (mCurrentModuleContext != null) {
mModuleContextMap.put(mCurrentResults, mCurrentModuleContext);
}
}
mCurrentResults.testRunStarted(name, numTests);
mIsCountDirty = true;
}
/**
* {@inheritDoc}
*/
@Override
public void testStarted(TestIdentifier test) {
testStarted(test, System.currentTimeMillis());
}
/** {@inheritDoc} */
@Override
public void testStarted(TestIdentifier test, long startTime) {
mIsCountDirty = true;
mCurrentResults.testStarted(test, startTime);
}
/**
* {@inheritDoc}
*/
@Override
public void testEnded(TestIdentifier test, Map<String, String> testMetrics) {
testEnded(test, System.currentTimeMillis(), testMetrics);
}
/** {@inheritDoc} */
@Override
public void testEnded(TestIdentifier test, long endTime, Map<String, String> testMetrics) {
mIsCountDirty = true;
mCurrentResults.testEnded(test, endTime, testMetrics);
}
/** {@inheritDoc} */
@Override
public void testFailed(TestIdentifier test, String trace) {
mIsCountDirty = true;
mCurrentResults.testFailed(test, trace);
}
@Override
public void testAssumptionFailure(TestIdentifier test, String trace) {
mIsCountDirty = true;
mCurrentResults.testAssumptionFailure(test, trace);
}
@Override
public void testIgnored(TestIdentifier test) {
mIsCountDirty = true;
mCurrentResults.testIgnored(test);
}
/**
* {@inheritDoc}
*/
@Override
public void testRunEnded(long elapsedTime, Map<String, String> runMetrics) {
mIsCountDirty = true;
mCurrentResults.testRunEnded(elapsedTime, runMetrics);
}
/**
* {@inheritDoc}
*/
@Override
public void testRunFailed(String errorMessage) {
mIsCountDirty = true;
mCurrentResults.testRunFailed(errorMessage);
}
/**
* {@inheritDoc}
*/
@Override
public void testRunStopped(long elapsedTime) {
mIsCountDirty = true;
mCurrentResults.testRunStopped(elapsedTime);
}
/**
* Gets the results for the current test run.
* <p/>
* Note the results may not be complete. It is recommended to test the value of {@link
* TestRunResult#isRunComplete()} and/or (@link TestRunResult#isRunFailure()} as appropriate
* before processing the results.
*
* @return the {@link TestRunResult} representing data collected during last test run
*/
public TestRunResult getCurrentRunResults() {
return mCurrentResults;
}
/**
* Gets the results for all test runs.
*/
public Collection<TestRunResult> getRunResults() {
return mRunResultsMap.values();
}
/**
* Returns the {@link IInvocationContext} of the module associated with the results or null if
* it was not associated with any module.
*/
public IInvocationContext getModuleContextForRunResult(TestRunResult res) {
return mModuleContextMap.get(res);
}
/** Returns True if the result map already has an entry for the run name. */
public boolean hasResultFor(String runName) {
return mRunResultsMap.containsKey(runName);
}
/** Gets the total number of complete tests for all runs. */
public int getNumTotalTests() {
int total = 0;
// force test count
getNumTestsInState(TestStatus.PASSED);
for (TestStatus s : TestStatus.values()) {
total += mStatusCounts[s.ordinal()];
}
return total;
}
/**
* Gets the number of tests in given state for this run.
*/
public int getNumTestsInState(TestStatus status) {
if (mIsCountDirty) {
for (TestStatus s : TestStatus.values()) {
mStatusCounts[s.ordinal()] = 0;
for (TestRunResult result : mRunResultsMap.values()) {
mStatusCounts[s.ordinal()] += result.getNumTestsInState(s);
}
}
mIsCountDirty = false;
}
return mStatusCounts[status.ordinal()];
}
/**
* @return true if invocation had any failed or assumption failed tests.
*/
public boolean hasFailedTests() {
return getNumAllFailedTests() > 0;
}
/**
* {@inheritDoc}
*/
@Override
public void invocationEnded(long elapsedTime) {
// ignore
}
/**
* {@inheritDoc}
*/
@Override
public void invocationFailed(Throwable cause) {
// ignore
}
/**
* {@inheritDoc}
*/
@Override
public TestSummary getSummary() {
// ignore
return null;
}
/**
* {@inheritDoc}
*/
@Override
public void testLog(String dataName, LogDataType dataType, InputStreamSource dataStream) {
// ignore
}
/**
* Return total number of tests in a failure state (only failed, assumption failures do not
* count toward it).
*/
public int getNumAllFailedTests() {
return getNumTestsInState(TestStatus.FAILURE);
}
}