diff options
author | Julien Desprez <jdesprez@google.com> | 2017-09-29 19:08:40 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2017-09-29 19:08:40 +0000 |
commit | 6392958350f7b886cba3d5d7fa394a00aa892e79 (patch) | |
tree | 091c18705e9f23ad246e96c880365712483be93b | |
parent | 6d7988df6512678b17cd5bfbde01a47ae4afd47c (diff) | |
parent | 71fa109b10b558354063fa7c38762d415cc15529 (diff) | |
download | tradefederation-6392958350f7b886cba3d5d7fa394a00aa892e79.tar.gz |
Merge "Introduce testModuleStart/End callbacks" into oc-dev am: 82e24a3a7c
am: 71fa109b10
Change-Id: Ice8bff46082f8781c4477db28f61f85b2755133f
10 files changed, 204 insertions, 4 deletions
diff --git a/src/com/android/tradefed/invoker/ShardListener.java b/src/com/android/tradefed/invoker/ShardListener.java index ea0f1fb31..8aa56251c 100644 --- a/src/com/android/tradefed/invoker/ShardListener.java +++ b/src/com/android/tradefed/invoker/ShardListener.java @@ -113,7 +113,19 @@ public class ShardListener extends CollectingTestListener { super.invocationEnded(elapsedTime); synchronized (mMasterListener) { logShardContent(getRunResults()); + IInvocationContext moduleContext = null; for (TestRunResult runResult : getRunResults()) { + // Stop or start the module + if (moduleContext != null + && !getModuleContextForRunResult(runResult).equals(moduleContext)) { + mMasterListener.testModuleEnded(); + moduleContext = null; + } + if (moduleContext == null && getModuleContextForRunResult(runResult) != null) { + moduleContext = getModuleContextForRunResult(runResult); + mMasterListener.testModuleStarted(moduleContext); + } + mMasterListener.testRunStarted(runResult.getName(), runResult.getNumTests()); forwardTestResults(runResult.getTestResults()); if (runResult.isRunFailure()) { @@ -121,6 +133,11 @@ public class ShardListener extends CollectingTestListener { } mMasterListener.testRunEnded(runResult.getElapsedTime(), runResult.getRunMetrics()); } + // Close the last module + if (moduleContext != null) { + mMasterListener.testModuleEnded(); + moduleContext = null; + } mMasterListener.invocationEnded(elapsedTime); } } diff --git a/src/com/android/tradefed/result/CollectingTestListener.java b/src/com/android/tradefed/result/CollectingTestListener.java index 80d5987eb..edecf7258 100644 --- a/src/com/android/tradefed/result/CollectingTestListener.java +++ b/src/com/android/tradefed/result/CollectingTestListener.java @@ -42,7 +42,10 @@ public class CollectingTestListener implements ITestInvocationListener { // 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() */ @@ -116,6 +119,16 @@ public class CollectingTestListener implements ITestInvocationListener { mBuildInfo = buildInfo; } + @Override + public void testModuleStarted(IInvocationContext moduleContext) { + mCurrentModuleContext = moduleContext; + } + + @Override + public void testModuleEnded() { + mCurrentModuleContext = null; + } + /** * {@inheritDoc} */ @@ -130,6 +143,10 @@ public class CollectingTestListener implements ITestInvocationListener { 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; @@ -232,6 +249,14 @@ public class CollectingTestListener implements ITestInvocationListener { 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); diff --git a/src/com/android/tradefed/result/ITestInvocationListener.java b/src/com/android/tradefed/result/ITestInvocationListener.java index c8fbd7c2d..5e951d8db 100644 --- a/src/com/android/tradefed/result/ITestInvocationListener.java +++ b/src/com/android/tradefed/result/ITestInvocationListener.java @@ -20,6 +20,7 @@ import com.android.ddmlib.testrunner.TestIdentifier; import com.android.tradefed.command.ICommandScheduler; import com.android.tradefed.invoker.IInvocationContext; import com.android.tradefed.log.ITestLogger; +import com.android.tradefed.testtype.suite.ITestSuite; import java.util.Map; @@ -95,6 +96,18 @@ public interface ITestInvocationListener extends ITestRunListener, ITestLogger { } /** + * Reports the beginning of a module running. This callback is associated with {@link + * #testModuleEnded()} and is optional in the sequence. It is only used during a run that uses + * modules: {@link ITestSuite} based runners. + * + * @param moduleContext the {@link IInvocationContext} of the module. + */ + public default void testModuleStarted(IInvocationContext moduleContext) {} + + /** Reports the end of a module run. */ + public default void testModuleEnded() {} + + /** * {@inheritDoc} */ @Override diff --git a/src/com/android/tradefed/result/ResultForwarder.java b/src/com/android/tradefed/result/ResultForwarder.java index 494fc7aef..41a3e8809 100644 --- a/src/com/android/tradefed/result/ResultForwarder.java +++ b/src/com/android/tradefed/result/ResultForwarder.java @@ -297,4 +297,18 @@ public class ResultForwarder implements ITestInvocationListener { } } } + + @Override + public void testModuleStarted(IInvocationContext moduleContext) { + for (ITestInvocationListener listener : mListeners) { + listener.testModuleStarted(moduleContext); + } + } + + @Override + public void testModuleEnded() { + for (ITestInvocationListener listener : mListeners) { + listener.testModuleEnded(); + } + } } diff --git a/src/com/android/tradefed/testtype/suite/ITestSuite.java b/src/com/android/tradefed/testtype/suite/ITestSuite.java index cdef58310..90ed34c7c 100644 --- a/src/com/android/tradefed/testtype/suite/ITestSuite.java +++ b/src/com/android/tradefed/testtype/suite/ITestSuite.java @@ -233,7 +233,7 @@ public abstract class ITestSuite } try { - mContext.setModuleInvocationContext(module.getModuleInvocationContext()); + listener.testModuleStarted(module.getModuleInvocationContext()); // Populate the module context with devices and builds for (String deviceName : mContext.getDeviceConfigNames()) { module.getModuleInvocationContext() @@ -245,7 +245,7 @@ public abstract class ITestSuite } finally { // clear out module invocation context since we are now done with module // execution - mContext.setModuleInvocationContext(null); + listener.testModuleEnded(); } } } catch (DeviceNotAvailableException e) { diff --git a/tests/src/com/android/tradefed/UnitTests.java b/tests/src/com/android/tradefed/UnitTests.java index 5b9e12e1f..8e599eee5 100644 --- a/tests/src/com/android/tradefed/UnitTests.java +++ b/tests/src/com/android/tradefed/UnitTests.java @@ -64,6 +64,7 @@ import com.android.tradefed.device.TopHelperTest; import com.android.tradefed.device.WaitDeviceRecoveryTest; import com.android.tradefed.device.WifiHelperTest; import com.android.tradefed.invoker.InvocationContextTest; +import com.android.tradefed.invoker.ShardListenerTest; import com.android.tradefed.invoker.TestInvocationMultiTest; import com.android.tradefed.invoker.TestInvocationTest; import com.android.tradefed.invoker.shard.ShardHelperTest; @@ -299,6 +300,7 @@ import org.junit.runners.Suite.SuiteClasses; // invoker InvocationContextTest.class, + ShardListenerTest.class, TestInvocationMultiTest.class, TestInvocationTest.class, diff --git a/tests/src/com/android/tradefed/invoker/ShardListenerTest.java b/tests/src/com/android/tradefed/invoker/ShardListenerTest.java new file mode 100644 index 000000000..705b55c49 --- /dev/null +++ b/tests/src/com/android/tradefed/invoker/ShardListenerTest.java @@ -0,0 +1,121 @@ +/* + * 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 com.android.tradefed.invoker; + +import com.android.ddmlib.testrunner.TestIdentifier; +import com.android.tradefed.build.BuildInfo; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.result.ITestInvocationListener; + +import org.easymock.EasyMock; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Collections; + +/** Unit tests for {@link ShardListener}. */ +@RunWith(JUnit4.class) +public class ShardListenerTest { + private ShardListener mShardListener; + private ITestInvocationListener mMockListener; + private IInvocationContext mContext; + private ITestDevice mMockDevice; + + @Before + public void setUp() { + mMockListener = EasyMock.createMock(ITestInvocationListener.class); + mShardListener = new ShardListener(mMockListener); + mMockDevice = EasyMock.createMock(ITestDevice.class); + EasyMock.expect(mMockDevice.getSerialNumber()).andStubReturn("serial"); + mContext = new InvocationContext(); + mContext.addDeviceBuildInfo("default", new BuildInfo()); + mContext.addAllocatedDevice("default", mMockDevice); + } + + /** Ensure that all the events given to the shardlistener are replayed on invocationEnded. */ + @Test + public void testBufferAndReplay() { + mMockListener.invocationStarted(mContext); + mMockListener.testRunStarted("run1", 1); + TestIdentifier tid = new TestIdentifier("class1", "name1"); + mMockListener.testStarted(tid, 0l); + mMockListener.testEnded(tid, 0l, Collections.emptyMap()); + mMockListener.testRunEnded(0l, Collections.emptyMap()); + mMockListener.invocationEnded(0l); + + EasyMock.replay(mMockListener, mMockDevice); + mShardListener.invocationStarted(mContext); + mShardListener.testRunStarted("run1", 1); + mShardListener.testStarted(tid, 0l); + mShardListener.testEnded(tid, 0l, Collections.emptyMap()); + mShardListener.testRunEnded(0l, Collections.emptyMap()); + mShardListener.invocationEnded(0l); + EasyMock.verify(mMockListener, mMockDevice); + } + + /** Test that the buffering of events is properly done in respect to the modules too. */ + @Test + public void testBufferAndReplay_withModule() { + IInvocationContext module1 = new InvocationContext(); + IInvocationContext module2 = new InvocationContext(); + mMockListener.invocationStarted(mContext); + mMockListener.testModuleStarted(module1); + mMockListener.testRunStarted("run1", 1); + TestIdentifier tid = new TestIdentifier("class1", "name1"); + mMockListener.testStarted(tid, 0l); + mMockListener.testEnded(tid, 0l, Collections.emptyMap()); + mMockListener.testRunEnded(0l, Collections.emptyMap()); + mMockListener.testRunStarted("run2", 1); + mMockListener.testStarted(tid, 0l); + mMockListener.testEnded(tid, 0l, Collections.emptyMap()); + mMockListener.testRunEnded(0l, Collections.emptyMap()); + mMockListener.testModuleEnded(); + // expectation on second module + mMockListener.testModuleStarted(module2); + mMockListener.testRunStarted("run3", 1); + mMockListener.testStarted(tid, 0l); + mMockListener.testEnded(tid, 0l, Collections.emptyMap()); + mMockListener.testRunEnded(0l, Collections.emptyMap()); + mMockListener.testModuleEnded(); + mMockListener.invocationEnded(0l); + + EasyMock.replay(mMockListener, mMockDevice); + mShardListener.invocationStarted(mContext); + // 1st module + mShardListener.testModuleStarted(module1); + mShardListener.testRunStarted("run1", 1); + mShardListener.testStarted(tid, 0l); + mShardListener.testEnded(tid, 0l, Collections.emptyMap()); + mShardListener.testRunEnded(0l, Collections.emptyMap()); + mShardListener.testRunStarted("run2", 1); + mShardListener.testStarted(tid, 0l); + mShardListener.testEnded(tid, 0l, Collections.emptyMap()); + mShardListener.testRunEnded(0l, Collections.emptyMap()); + mShardListener.testModuleEnded(); + // 2nd module + mShardListener.testModuleStarted(module2); + mShardListener.testRunStarted("run3", 1); + mShardListener.testStarted(tid, 0l); + mShardListener.testEnded(tid, 0l, Collections.emptyMap()); + mShardListener.testRunEnded(0l, Collections.emptyMap()); + mShardListener.testModuleEnded(); + + mShardListener.invocationEnded(0l); + EasyMock.verify(mMockListener, mMockDevice); + } +} diff --git a/tests/src/com/android/tradefed/testtype/suite/ITestSuiteMultiTest.java b/tests/src/com/android/tradefed/testtype/suite/ITestSuiteMultiTest.java index d2bafe11d..0dc0fda15 100644 --- a/tests/src/com/android/tradefed/testtype/suite/ITestSuiteMultiTest.java +++ b/tests/src/com/android/tradefed/testtype/suite/ITestSuiteMultiTest.java @@ -111,7 +111,7 @@ public class ITestSuiteMultiTest { mTestSuite.setInvocationContext(mContext); mTestSuite.setSystemStatusChecker(new ArrayList<>()); - + mMockListener.testModuleStarted(EasyMock.anyObject()); mMockListener.testRunStarted("test1", 2); TestIdentifier test1 = new TestIdentifier(MultiDeviceStubTest.class.getSimpleName(), "test0"); @@ -122,7 +122,7 @@ public class ITestSuiteMultiTest { mMockListener.testStarted(test2, 0l); mMockListener.testEnded(test2, 5l, Collections.emptyMap()); mMockListener.testRunEnded(EasyMock.anyLong(), EasyMock.anyObject()); - + mMockListener.testModuleEnded(); EasyMock.replay( mMockListener, mMockBuildInfo1, mMockBuildInfo2, mMockDevice1, mMockDevice2); mTestSuite.run(mMockListener); diff --git a/tests/src/com/android/tradefed/testtype/suite/ITestSuiteTest.java b/tests/src/com/android/tradefed/testtype/suite/ITestSuiteTest.java index 3750c4d02..f691558e4 100644 --- a/tests/src/com/android/tradefed/testtype/suite/ITestSuiteTest.java +++ b/tests/src/com/android/tradefed/testtype/suite/ITestSuiteTest.java @@ -166,11 +166,13 @@ public class ITestSuiteTest { /** Helper to expect the test run callback. */ private void expectTestRun(ITestInvocationListener listener) { + listener.testModuleStarted(EasyMock.anyObject()); listener.testRunStarted(TEST_CONFIG_NAME, 1); TestIdentifier test = new TestIdentifier(EMPTY_CONFIG, EMPTY_CONFIG); listener.testStarted(test, 0); listener.testEnded(test, 5, Collections.emptyMap()); listener.testRunEnded(EasyMock.anyLong(), EasyMock.anyObject()); + listener.testModuleEnded(); } /** Test for {@link ITestSuite#run(ITestInvocationListener)}. */ @@ -307,11 +309,13 @@ public class ITestSuiteTest { setter.setOptionValue("skip-all-system-status-check", "true"); setter.setOptionValue("reboot-per-module", "true"); EasyMock.expect(mMockDevice.getProperty("ro.build.type")).andReturn("user"); + mMockListener.testModuleStarted(EasyMock.anyObject()); mMockListener.testRunStarted(TEST_CONFIG_NAME, 1); EasyMock.expectLastCall().times(1); mMockListener.testRunFailed("Module test only ran 0 out of 1 expected tests."); mMockListener.testRunEnded(EasyMock.anyLong(), EasyMock.anyObject()); EasyMock.expectLastCall().times(1); + mMockListener.testModuleEnded(); replayMocks(); mTestSuite.run(mMockListener); verifyMocks(); @@ -352,11 +356,13 @@ public class ITestSuiteTest { setter.setOptionValue("skip-all-system-status-check", "true"); setter.setOptionValue("reboot-per-module", "true"); EasyMock.expect(mMockDevice.getProperty("ro.build.type")).andReturn("user"); + mMockListener.testModuleStarted(EasyMock.anyObject()); mMockListener.testRunStarted(TEST_CONFIG_NAME, 1); EasyMock.expectLastCall().times(1); mMockListener.testRunFailed("Module test only ran 0 out of 1 expected tests."); mMockListener.testRunEnded(EasyMock.anyLong(), EasyMock.anyObject()); EasyMock.expectLastCall().times(1); + mMockListener.testModuleEnded(); replayMocks(); mTestSuite.run(mMockListener); verifyMocks(); diff --git a/tests/src/com/android/tradefed/testtype/suite/TfSuiteRunnerTest.java b/tests/src/com/android/tradefed/testtype/suite/TfSuiteRunnerTest.java index b67d8789f..240e50885 100644 --- a/tests/src/com/android/tradefed/testtype/suite/TfSuiteRunnerTest.java +++ b/tests/src/com/android/tradefed/testtype/suite/TfSuiteRunnerTest.java @@ -182,8 +182,10 @@ public class TfSuiteRunnerTest { mRunner.setSystemStatusChecker(new ArrayList<>()); mRunner.setInvocationContext(new InvocationContext()); // runs the expanded suite + listener.testModuleStarted(EasyMock.anyObject()); listener.testRunStarted("suite/stub1", 0); listener.testRunEnded(EasyMock.anyLong(), EasyMock.anyObject()); + listener.testModuleEnded(); EasyMock.replay(listener); mRunner.run(listener); EasyMock.verify(listener); |