Add test module start/end to subprocess reporter
- Ensure Abi/IAbi is serializable so context can be passed.
- Add callbacks to properly carry the full state.
Test: unit tests
local google-cts-launcher run that reports module start/end
https://sponge.corp.google.com/invocation?id=99ece292-e5af-41e4-aa7b-ac6b50404d6a
Bug: None
Change-Id: I42eafba8c3d3ee6065d31d1ec9bb776a02e1392e
diff --git a/src/com/android/tradefed/result/SubprocessResultsReporter.java b/src/com/android/tradefed/result/SubprocessResultsReporter.java
index a73cbb8..118d58b 100644
--- a/src/com/android/tradefed/result/SubprocessResultsReporter.java
+++ b/src/com/android/tradefed/result/SubprocessResultsReporter.java
@@ -27,12 +27,15 @@
import com.android.tradefed.util.SubprocessEventHelper.InvocationStartedEventInfo;
import com.android.tradefed.util.SubprocessEventHelper.TestEndedEventInfo;
import com.android.tradefed.util.SubprocessEventHelper.TestLogEventInfo;
+import com.android.tradefed.util.SubprocessEventHelper.TestModuleStartedEventInfo;
import com.android.tradefed.util.SubprocessEventHelper.TestRunEndedEventInfo;
import com.android.tradefed.util.SubprocessEventHelper.TestRunFailedEventInfo;
import com.android.tradefed.util.SubprocessEventHelper.TestRunStartedEventInfo;
import com.android.tradefed.util.SubprocessEventHelper.TestStartedEventInfo;
import com.android.tradefed.util.SubprocessTestResultsParser;
+import org.json.JSONObject;
+
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
@@ -203,6 +206,19 @@
printEvent(SubprocessTestResultsParser.StatusKeys.INVOCATION_FAILED, info);
}
+ /** {@inheritDoc} */
+ @Override
+ public void testModuleStarted(IInvocationContext moduleContext) {
+ TestModuleStartedEventInfo info = new TestModuleStartedEventInfo(moduleContext);
+ printEvent(SubprocessTestResultsParser.StatusKeys.TEST_MODULE_STARTED, info);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void testModuleEnded() {
+ printEvent(SubprocessTestResultsParser.StatusKeys.TEST_MODULE_ENDED, new JSONObject());
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/src/com/android/tradefed/testtype/Abi.java b/src/com/android/tradefed/testtype/Abi.java
index daa28ac..343c585 100644
--- a/src/com/android/tradefed/testtype/Abi.java
+++ b/src/com/android/tradefed/testtype/Abi.java
@@ -15,10 +15,13 @@
*/
package com.android.tradefed.testtype;
+import com.android.tradefed.build.BuildSerializedVersion;
+
/**
* A class representing an ABI.
*/
public class Abi implements IAbi {
+ private static final long serialVersionUID = BuildSerializedVersion.VERSION;
private final String mName;
private final String mBitness;
diff --git a/src/com/android/tradefed/testtype/IAbi.java b/src/com/android/tradefed/testtype/IAbi.java
index 5c5aec0..2a349e1 100644
--- a/src/com/android/tradefed/testtype/IAbi.java
+++ b/src/com/android/tradefed/testtype/IAbi.java
@@ -15,10 +15,10 @@
*/
package com.android.tradefed.testtype;
-/**
- * Interface representing the ABI under test.
- */
-public interface IAbi {
+import java.io.Serializable;
+
+/** Interface representing the ABI under test. */
+public interface IAbi extends Serializable {
/**
* @return The name of the ABI.
diff --git a/src/com/android/tradefed/util/SubprocessEventHelper.java b/src/com/android/tradefed/util/SubprocessEventHelper.java
index 577477b..ffc0216 100644
--- a/src/com/android/tradefed/util/SubprocessEventHelper.java
+++ b/src/com/android/tradefed/util/SubprocessEventHelper.java
@@ -15,6 +15,7 @@
*/
package com.android.tradefed.util;
+import com.android.tradefed.invoker.IInvocationContext;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.LogDataType;
@@ -22,6 +23,7 @@
import org.json.JSONObject;
import java.io.File;
+import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
@@ -49,6 +51,8 @@
private static final String TEST_TAG_KEY = "testTag";
+ private static final String MODULE_CONTEXT_KEY = "moduleContextFileName";
+
/**
* Helper for testRunStarted information
*/
@@ -419,4 +423,37 @@
return tags.toString();
}
}
+
+ /** Helper for test module started information. */
+ public static class TestModuleStartedEventInfo {
+ public IInvocationContext mModuleContext;
+
+ public TestModuleStartedEventInfo(IInvocationContext moduleContext) {
+ mModuleContext = moduleContext;
+ }
+
+ public TestModuleStartedEventInfo(JSONObject jsonObject) throws JSONException {
+ String file = jsonObject.getString(MODULE_CONTEXT_KEY);
+ try {
+ mModuleContext =
+ (IInvocationContext) SerializationUtil.deserialize(new File(file), true);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ JSONObject tags = null;
+ try {
+ tags = new JSONObject();
+ File serializedContext = SerializationUtil.serialize(mModuleContext);
+ tags.put(MODULE_CONTEXT_KEY, serializedContext.getAbsolutePath());
+ } catch (IOException | JSONException e) {
+ CLog.e(e);
+ throw new RuntimeException(e);
+ }
+ return tags.toString();
+ }
+ }
}
diff --git a/src/com/android/tradefed/util/SubprocessTestResultsParser.java b/src/com/android/tradefed/util/SubprocessTestResultsParser.java
index d8398b3..ade8651 100644
--- a/src/com/android/tradefed/util/SubprocessTestResultsParser.java
+++ b/src/com/android/tradefed/util/SubprocessTestResultsParser.java
@@ -27,6 +27,7 @@
import com.android.tradefed.util.SubprocessEventHelper.InvocationStartedEventInfo;
import com.android.tradefed.util.SubprocessEventHelper.TestEndedEventInfo;
import com.android.tradefed.util.SubprocessEventHelper.TestLogEventInfo;
+import com.android.tradefed.util.SubprocessEventHelper.TestModuleStartedEventInfo;
import com.android.tradefed.util.SubprocessEventHelper.TestRunEndedEventInfo;
import com.android.tradefed.util.SubprocessEventHelper.TestRunFailedEventInfo;
import com.android.tradefed.util.SubprocessEventHelper.TestRunStartedEventInfo;
@@ -61,6 +62,7 @@
private ITestInvocationListener mListener;
private TestIdentifier mCurrentTest = null;
+ private IInvocationContext mCurrentModuleContext = null;
private Pattern mPattern = null;
private Map<String, EventHandler> mHandlerMap = null;
private EventReceiverThread mEventReceiver = null;
@@ -78,6 +80,8 @@
public static final String TEST_RUN_ENDED = "TEST_RUN_ENDED";
public static final String TEST_RUN_FAILED = "TEST_RUN_FAILED";
public static final String TEST_RUN_STARTED = "TEST_RUN_STARTED";
+ public static final String TEST_MODULE_STARTED = "TEST_MODULE_STARTED";
+ public static final String TEST_MODULE_ENDED = "TEST_MODULE_ENDED";
public static final String TEST_LOG = "TEST_LOG";
public static final String INVOCATION_STARTED = "INVOCATION_STARTED";
}
@@ -210,6 +214,8 @@
sb.append(StatusKeys.TEST_RUN_ENDED).append("|");
sb.append(StatusKeys.TEST_RUN_FAILED).append("|");
sb.append(StatusKeys.TEST_RUN_STARTED).append("|");
+ sb.append(StatusKeys.TEST_MODULE_STARTED).append("|");
+ sb.append(StatusKeys.TEST_MODULE_ENDED).append("|");
sb.append(StatusKeys.TEST_LOG).append("|");
sb.append(StatusKeys.INVOCATION_STARTED);
String patt = String.format("(.*)(%s)( )(.*)", sb.toString());
@@ -227,6 +233,8 @@
mHandlerMap.put(StatusKeys.TEST_RUN_ENDED, new TestRunEndedEventHandler());
mHandlerMap.put(StatusKeys.TEST_RUN_FAILED, new TestRunFailedEventHandler());
mHandlerMap.put(StatusKeys.TEST_RUN_STARTED, new TestRunStartedEventHandler());
+ mHandlerMap.put(StatusKeys.TEST_MODULE_STARTED, new TestModuleStartedEventHandler());
+ mHandlerMap.put(StatusKeys.TEST_MODULE_ENDED, new TestModuleEndedEventHandler());
mHandlerMap.put(StatusKeys.TEST_LOG, new TestLogEventHandler());
mHandlerMap.put(StatusKeys.INVOCATION_STARTED, new InvocationStartedEventHandler());
}
@@ -392,6 +400,27 @@
}
}
+ private class TestModuleStartedEventHandler implements EventHandler {
+ @Override
+ public void handleEvent(String eventJson) throws JSONException {
+ TestModuleStartedEventInfo module =
+ new TestModuleStartedEventInfo(new JSONObject(eventJson));
+ mCurrentModuleContext = module.mModuleContext;
+ mListener.testModuleStarted(module.mModuleContext);
+ }
+ }
+
+ private class TestModuleEndedEventHandler implements EventHandler {
+ @Override
+ public void handleEvent(String eventJson) throws JSONException {
+ if (mCurrentModuleContext == null) {
+ CLog.w("Calling testModuleEnded when testModuleStarted was not called.");
+ }
+ mListener.testModuleEnded();
+ mCurrentModuleContext = null;
+ }
+ }
+
private class TestLogEventHandler implements EventHandler {
@Override
public void handleEvent(String eventJson) throws JSONException {
diff --git a/tests/src/com/android/tradefed/util/SubprocessTestResultsParserTest.java b/tests/src/com/android/tradefed/util/SubprocessTestResultsParserTest.java
index 036ce2f..4d84ada 100644
--- a/tests/src/com/android/tradefed/util/SubprocessTestResultsParserTest.java
+++ b/tests/src/com/android/tradefed/util/SubprocessTestResultsParserTest.java
@@ -15,15 +15,22 @@
*/
package com.android.tradefed.util;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.invoker.IInvocationContext;
import com.android.tradefed.invoker.InvocationContext;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.ITestInvocationListener;
-import junit.framework.TestCase;
-
import org.easymock.Capture;
import org.easymock.EasyMock;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
import java.io.BufferedReader;
import java.io.File;
@@ -32,13 +39,11 @@
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
-import java.util.Map;
import java.util.Vector;
-/**
- * Unit Tests for {@link SubprocessTestResultsParser}
- */
-public class SubprocessTestResultsParserTest extends TestCase {
+/** Unit Tests for {@link SubprocessTestResultsParser} */
+@RunWith(JUnit4.class)
+public class SubprocessTestResultsParserTest {
private static final String TEST_TYPE_DIR = "testdata";
private static final String SUBPROC_OUTPUT_FILE_1 = "subprocess1.txt";
@@ -70,10 +75,8 @@
return fileContents.toArray(new String[fileContents.size()]);
}
- /**
- * Tests the parser for cases of test failed, ignored, assumption failure
- */
- @SuppressWarnings("unchecked")
+ /** Tests the parser for cases of test failed, ignored, assumption failure */
+ @Test
public void testParse_randomEvents() throws Exception {
String[] contents = readInFile(SUBPROC_OUTPUT_FILE_1);
ITestInvocationListener mockRunListener =
@@ -82,12 +85,9 @@
mockRunListener.testStarted((TestIdentifier) EasyMock.anyObject(), EasyMock.anyLong());
EasyMock.expectLastCall().times(4);
mockRunListener.testEnded(
- (TestIdentifier) EasyMock.anyObject(),
- EasyMock.anyLong(),
- (Map<String, String>) EasyMock.anyObject());
+ (TestIdentifier) EasyMock.anyObject(), EasyMock.anyLong(), EasyMock.anyObject());
EasyMock.expectLastCall().times(4);
- mockRunListener.testRunEnded(EasyMock.anyLong(),
- (Map<String, String>) EasyMock.anyObject());
+ mockRunListener.testRunEnded(EasyMock.anyLong(), EasyMock.anyObject());
EasyMock.expectLastCall().times(1);
mockRunListener.testIgnored((TestIdentifier)EasyMock.anyObject());
EasyMock.expectLastCall();
@@ -111,10 +111,8 @@
}
}
- /**
- * Tests the parser for cases of test starting without closing.
- */
- @SuppressWarnings("unchecked")
+ /** Tests the parser for cases of test starting without closing. */
+ @Test
public void testParse_invalidEventOrder() throws Exception {
String[] contents = readInFile(SUBPROC_OUTPUT_FILE_2);
ITestInvocationListener mockRunListener =
@@ -123,14 +121,11 @@
mockRunListener.testStarted((TestIdentifier) EasyMock.anyObject(), EasyMock.anyLong());
EasyMock.expectLastCall().times(4);
mockRunListener.testEnded(
- (TestIdentifier) EasyMock.anyObject(),
- EasyMock.anyLong(),
- (Map<String, String>) EasyMock.anyObject());
+ (TestIdentifier) EasyMock.anyObject(), EasyMock.anyLong(), EasyMock.anyObject());
EasyMock.expectLastCall().times(3);
mockRunListener.testRunFailed((String)EasyMock.anyObject());
EasyMock.expectLastCall().times(1);
- mockRunListener.testRunEnded(EasyMock.anyLong(),
- (Map<String, String>) EasyMock.anyObject());
+ mockRunListener.testRunEnded(EasyMock.anyLong(), EasyMock.anyObject());
EasyMock.expectLastCall().times(1);
mockRunListener.testIgnored((TestIdentifier)EasyMock.anyObject());
EasyMock.expectLastCall();
@@ -151,18 +146,14 @@
}
}
- /**
- * Tests the parser for cases of test starting without closing.
- */
- @SuppressWarnings("unchecked")
+ /** Tests the parser for cases of test starting without closing. */
+ @Test
public void testParse_testNotStarted() throws Exception {
ITestInvocationListener mockRunListener =
EasyMock.createMock(ITestInvocationListener.class);
mockRunListener.testRunStarted("arm64-v8a CtsGestureTestCases", 4);
mockRunListener.testEnded(
- (TestIdentifier) EasyMock.anyObject(),
- EasyMock.anyLong(),
- (Map<String, String>) EasyMock.anyObject());
+ (TestIdentifier) EasyMock.anyObject(), EasyMock.anyLong(), EasyMock.anyObject());
EasyMock.expectLastCall().times(1);
EasyMock.replay(mockRunListener);
File tmp = FileUtil.createTempFile("sub", "unit");
@@ -189,6 +180,7 @@
}
/** Tests the parser for a cases when there is no start/end time stamp. */
+ @Test
public void testParse_noTimeStamp() throws Exception {
ITestInvocationListener mockRunListener =
EasyMock.createMock(ITestInvocationListener.class);
@@ -224,10 +216,8 @@
}
}
- /**
- * Test injecting an invocation failure and verify the callback is called.
- */
- @SuppressWarnings("unchecked")
+ /** Test injecting an invocation failure and verify the callback is called. */
+ @Test
public void testParse_invocationFailed() throws Exception {
ITestInvocationListener mockRunListener =
EasyMock.createMock(ITestInvocationListener.class);
@@ -259,18 +249,14 @@
}
}
- /**
- * Report results when received from socket.
- */
- @SuppressWarnings("unchecked")
+ /** Report results when received from socket. */
+ @Test
public void testParser_receiveFromSocket() throws Exception {
ITestInvocationListener mockRunListener =
EasyMock.createMock(ITestInvocationListener.class);
mockRunListener.testRunStarted("arm64-v8a CtsGestureTestCases", 4);
mockRunListener.testEnded(
- (TestIdentifier) EasyMock.anyObject(),
- EasyMock.anyLong(),
- (Map<String, String>) EasyMock.anyObject());
+ (TestIdentifier) EasyMock.anyObject(), EasyMock.anyLong(), EasyMock.anyObject());
EasyMock.expectLastCall().times(1);
EasyMock.replay(mockRunListener);
SubprocessTestResultsParser resultParser = null;
@@ -303,10 +289,8 @@
}
}
- /**
- * When the receiver thread fails to join then an exception is thrown.
- */
- @SuppressWarnings("unchecked")
+ /** When the receiver thread fails to join then an exception is thrown. */
+ @Test
public void testParser_failToJoin() throws Exception {
ITestInvocationListener mockRunListener =
EasyMock.createMock(ITestInvocationListener.class);
@@ -323,6 +307,7 @@
}
/** Tests the parser receiving event on updating test tag. */
+ @Test
public void testParse_testTag() throws Exception {
final String subTestTag = "test_tag_in_subprocess";
InvocationContext context = new InvocationContext();
@@ -351,6 +336,7 @@
}
/** Tests the parser should not overwrite the test tag in parent process if it's already set. */
+ @Test
public void testParse_testTagNotOverwrite() throws Exception {
final String subTestTag = "test_tag_in_subprocess";
final String parentTestTag = "test_tag_in_parent_process";
@@ -374,4 +360,37 @@
FileUtil.deleteFile(tmp);
}
}
+
+ /** Test that module start and end is properly parsed when reported. */
+ @Test
+ public void testParse_moduleStarted_end() throws Exception {
+ ITestInvocationListener mockRunListener =
+ EasyMock.createMock(ITestInvocationListener.class);
+ mockRunListener.testModuleStarted(EasyMock.anyObject());
+ mockRunListener.testModuleEnded();
+ EasyMock.replay(mockRunListener);
+ IInvocationContext fakeModuleContext = new InvocationContext();
+ File tmp = FileUtil.createTempFile("sub", "unit");
+ SubprocessTestResultsParser resultParser = null;
+ File serializedModule = null;
+ try {
+ serializedModule = SerializationUtil.serialize(fakeModuleContext);
+ resultParser =
+ new SubprocessTestResultsParser(mockRunListener, new InvocationContext());
+ String moduleStart =
+ String.format(
+ "TEST_MODULE_STARTED {\"moduleContextFileName\":\"%s\"}\n",
+ serializedModule.getAbsolutePath());
+ FileUtil.writeToFile(moduleStart, tmp, true);
+ String moduleEnd = "TEST_MODULE_ENDED {}\n";
+ FileUtil.writeToFile(moduleEnd, tmp, true);
+
+ resultParser.parseFile(tmp);
+ EasyMock.verify(mockRunListener);
+ } finally {
+ StreamUtil.close(resultParser);
+ FileUtil.deleteFile(tmp);
+ FileUtil.deleteFile(serializedModule);
+ }
+ }
}