Re-interpret the exception from sandbox
Test: unit tests
Bug: 153374987
Change-Id: I11b1b1b118ea2471f346e312d8ea96bd8fa8dae3
diff --git a/src/com/android/tradefed/result/proto/ProtoResultParser.java b/src/com/android/tradefed/result/proto/ProtoResultParser.java
index bb07623..5211aad 100644
--- a/src/com/android/tradefed/result/proto/ProtoResultParser.java
+++ b/src/com/android/tradefed/result/proto/ProtoResultParser.java
@@ -36,9 +36,11 @@
import com.android.tradefed.result.proto.LogFileProto.LogFileInfo;
import com.android.tradefed.result.proto.TestRecordProto.ChildReference;
import com.android.tradefed.result.proto.TestRecordProto.DebugInfo;
+import com.android.tradefed.result.proto.TestRecordProto.DebugInfoContext;
import com.android.tradefed.result.proto.TestRecordProto.TestRecord;
import com.android.tradefed.testtype.suite.ModuleDefinition;
import com.android.tradefed.util.MultiMap;
+import com.android.tradefed.util.SerializationUtil;
import com.android.tradefed.util.proto.TestRecordProtoUtil;
import com.google.common.base.Splitter;
@@ -274,9 +276,24 @@
}
if (endInvocationProto.hasDebugInfo()) {
- // TODO: Re-interpret the exception with proper type.
String trace = endInvocationProto.getDebugInfo().getTrace();
- mListener.invocationFailed(new Throwable(trace));
+ Throwable invocationError = new Throwable(trace);
+ if (endInvocationProto.getDebugInfo().hasDebugInfoContext()) {
+ DebugInfoContext failureContext =
+ endInvocationProto.getDebugInfo().getDebugInfoContext();
+ if (!Strings.isNullOrEmpty(failureContext.getErrorType())) {
+ try {
+ invocationError =
+ (Throwable)
+ SerializationUtil.deserialize(
+ failureContext.getErrorType());
+ } catch (IOException e) {
+ CLog.e("Failed to deserialize the invocation exception:");
+ CLog.e(e);
+ }
+ }
+ }
+ mListener.invocationFailed(invocationError);
}
log("Invocation ended proto");
diff --git a/src/com/android/tradefed/result/proto/ProtoResultReporter.java b/src/com/android/tradefed/result/proto/ProtoResultReporter.java
index 6ba026f..4e77622 100644
--- a/src/com/android/tradefed/result/proto/ProtoResultReporter.java
+++ b/src/com/android/tradefed/result/proto/ProtoResultReporter.java
@@ -33,12 +33,14 @@
import com.android.tradefed.result.proto.TestRecordProto.TestStatus;
import com.android.tradefed.result.retry.ISupportGranularResults;
import com.android.tradefed.testtype.suite.ModuleDefinition;
+import com.android.tradefed.util.SerializationUtil;
import com.android.tradefed.util.StreamUtil;
import com.google.common.base.Strings;
import com.google.protobuf.Any;
import com.google.protobuf.Timestamp;
+import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
@@ -192,6 +194,14 @@
debugBuilder.setErrorMessage(mInvocationFailure.getMessage());
}
debugBuilder.setTrace(StreamUtil.getStackTrace(mInvocationFailure));
+ DebugInfoContext.Builder debugContext = DebugInfoContext.newBuilder();
+ try {
+ debugContext.setErrorType(SerializationUtil.serializeToString(mInvocationFailure));
+ } catch (IOException e) {
+ CLog.e("Failed to serialize the invocation failure:");
+ CLog.e(e);
+ }
+ debugBuilder.setDebugInfoContext(debugContext);
mInvocationRecordBuilder.setDebugInfo(debugBuilder);
}
diff --git a/src/com/android/tradefed/util/SerializationUtil.java b/src/com/android/tradefed/util/SerializationUtil.java
index 0c991ff..0bd3fed 100644
--- a/src/com/android/tradefed/util/SerializationUtil.java
+++ b/src/com/android/tradefed/util/SerializationUtil.java
@@ -15,6 +15,8 @@
*/
package com.android.tradefed.util;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -22,6 +24,7 @@
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
+import java.util.Base64;
/** Utility to serialize/deserialize an object that implements {@link Serializable}. */
public class SerializationUtil {
@@ -53,6 +56,49 @@
}
/**
+ * Serialize and object into a base64 encoded string.
+ *
+ * @param o the object to serialize.
+ * @return the {@link String} where the object was serialized.
+ * @throws IOException if serialization fails.
+ */
+ public static String serializeToString(Serializable o) throws IOException {
+ ByteArrayOutputStream byteOut = null;
+ ObjectOutputStream out = null;
+ try {
+ byteOut = new ByteArrayOutputStream();
+ out = new ObjectOutputStream(byteOut);
+ out.writeObject(o);
+ return Base64.getEncoder().encodeToString(byteOut.toByteArray());
+ } finally {
+ StreamUtil.close(out);
+ StreamUtil.close(byteOut);
+ }
+ }
+
+ /**
+ * Deserialize an object that was serialized using {@link #serializeToString(Serializable)}.
+ *
+ * @param serialized the base64 string where the object was serialized.
+ * @return the Object deserialized.
+ * @throws IOException if the deserialization fails.
+ */
+ public static Object deserialize(String serialized) throws IOException {
+ ByteArrayInputStream bais = null;
+ ObjectInputStream in = null;
+ try {
+ bais = new ByteArrayInputStream(Base64.getDecoder().decode(serialized));
+ in = new ObjectInputStream(bais);
+ return in.readObject();
+ } catch (ClassNotFoundException cnfe) {
+ throw new RuntimeException(cnfe);
+ } finally {
+ StreamUtil.close(in);
+ StreamUtil.close(bais);
+ }
+ }
+
+ /**
* Deserialize an object that was serialized using {@link #serialize(Serializable)}.
*
* @param serializedFile the file where the object was serialized.
diff --git a/tests/src/com/android/tradefed/result/proto/ProtoResultParserTest.java b/tests/src/com/android/tradefed/result/proto/ProtoResultParserTest.java
index 517c0a0..eabcab2 100644
--- a/tests/src/com/android/tradefed/result/proto/ProtoResultParserTest.java
+++ b/tests/src/com/android/tradefed/result/proto/ProtoResultParserTest.java
@@ -17,6 +17,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import com.android.tradefed.build.BuildInfo;
import com.android.tradefed.config.ConfigurationDef;
@@ -166,7 +167,8 @@
mMockListener.logAssociation(
EasyMock.eq("subprocess-invocation_log1"), EasyMock.anyObject());
// Invocation failure is replayed
- mMockListener.invocationFailed(EasyMock.anyObject());
+ Capture<Throwable> captureInvocFailure = new Capture<>();
+ mMockListener.invocationFailed(EasyMock.capture(captureInvocFailure));
mMockListener.invocationEnded(500L);
EasyMock.replay(mMockListener);
@@ -225,6 +227,9 @@
assertEquals(logFile.getType(), capturedFile.getType());
assertEquals(logFile.getSize(), capturedFile.getSize());
+ Throwable invocFailureCaptured = captureInvocFailure.getValue();
+ assertTrue(invocFailureCaptured instanceof RuntimeException);
+
// Check Context at the end
assertEquals(
"build_value", context.getBuildInfos().get(0).getBuildAttributes().get(TEST_KEY));
diff --git a/tests/src/com/android/tradefed/util/SerializationUtilTest.java b/tests/src/com/android/tradefed/util/SerializationUtilTest.java
index aab84cb..08a2f4f 100644
--- a/tests/src/com/android/tradefed/util/SerializationUtilTest.java
+++ b/tests/src/com/android/tradefed/util/SerializationUtilTest.java
@@ -15,7 +15,9 @@
*/
package com.android.tradefed.util;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.android.tradefed.build.BuildInfo;
import com.android.tradefed.build.BuildSerializedVersion;
@@ -48,6 +50,15 @@
}
}
+ @Test
+ public void testSerialize_DeserializeString() throws Exception {
+ RuntimeException e = new RuntimeException("test");
+ String serializedException = SerializationUtil.serializeToString(e);
+ Object o = SerializationUtil.deserialize(serializedException);
+ assertTrue(o instanceof RuntimeException);
+ assertEquals("test", ((RuntimeException) o).getMessage());
+ }
+
/** Tests that serialization and deserialization creates a similar object from the original. */
@Test
public void testSerialize_Deserialize() throws Exception {