1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.cts.tradefed.result; 17 18 import com.android.compatibility.common.util.MetricsXmlSerializer; 19 import com.android.compatibility.common.util.ReportLog; 20 import com.android.cts.tradefed.result.TestLog.TestLogType; 21 22 import org.kxml2.io.KXmlSerializer; 23 import org.xmlpull.v1.XmlPullParser; 24 import org.xmlpull.v1.XmlPullParserException; 25 26 import java.io.IOException; 27 import java.util.ArrayList; 28 import java.util.List; 29 30 /** 31 * Data structure that represents a "Test" result XML element. 32 */ 33 class Test extends AbstractXmlPullParser { 34 static final String TAG = "Test"; 35 private static final String NAME_ATTR = "name"; 36 private static final String MESSAGE_ATTR = "message"; 37 private static final String ENDTIME_ATTR = "endtime"; 38 private static final String STARTTIME_ATTR = "starttime"; 39 private static final String RESULT_ATTR = "result"; 40 private static final String SCENE_TAG = "FailedScene"; 41 private static final String STACK_TAG = "StackTrace"; 42 private String mName; 43 private CtsTestStatus mResult; 44 private String mStartTime; 45 private String mEndTime; 46 private String mMessage; 47 private String mStackTrace; 48 private ReportLog mReport; 49 50 /** 51 * Log info for this test like a logcat dump or bugreport. 52 * Use *Locked methods instead of mutating this directly. 53 */ 54 private List<TestLog> mTestLogs; 55 56 /** 57 * Create an empty {@link Test} 58 */ Test()59 public Test() { 60 } 61 62 /** 63 * Create a {@link Test}. 64 * 65 * @param name 66 */ Test(String name)67 public Test(String name) { 68 mName = name; 69 mResult = CtsTestStatus.NOT_EXECUTED; 70 mStartTime = TimeUtil.getTimestamp(); 71 updateEndTime(); 72 } 73 74 /** 75 * Add a test log to this Test. 76 */ addTestLog(TestLog testLog)77 public void addTestLog(TestLog testLog) { 78 addTestLogLocked(testLog); 79 } 80 81 /** 82 * Set the name of this {@link Test} 83 */ setName(String name)84 public void setName(String name) { 85 mName = name; 86 } 87 88 /** 89 * Get the name of this {@link Test} 90 */ getName()91 public String getName() { 92 return mName; 93 } 94 getResult()95 public CtsTestStatus getResult() { 96 return mResult; 97 } 98 getMessage()99 public String getMessage() { 100 return mMessage; 101 } 102 setMessage(String message)103 public void setMessage(String message) { 104 mMessage = message; 105 } 106 getStartTime()107 public String getStartTime() { 108 return mStartTime; 109 } 110 getEndTime()111 public String getEndTime() { 112 return mEndTime; 113 } 114 getStackTrace()115 public String getStackTrace() { 116 return mStackTrace; 117 } 118 setStackTrace(String stackTrace)119 public void setStackTrace(String stackTrace) { 120 121 mStackTrace = sanitizeStackTrace(stackTrace); 122 mMessage = getFailureMessageFromStackTrace(mStackTrace); 123 } 124 getReportLog()125 public ReportLog getReportLog() { 126 return mReport; 127 } 128 setReportLog(ReportLog report)129 public void setReportLog(ReportLog report) { 130 mReport = report; 131 } 132 updateEndTime()133 public void updateEndTime() { 134 mEndTime = TimeUtil.getTimestamp(); 135 } 136 setResultStatus(CtsTestStatus status)137 public void setResultStatus(CtsTestStatus status) { 138 mResult = status; 139 } 140 141 /** 142 * Serialize this object and all its contents to XML. 143 * 144 * @param serializer 145 * @throws IOException 146 */ serialize(KXmlSerializer serializer)147 public void serialize(KXmlSerializer serializer) 148 throws IOException { 149 serializer.startTag(CtsXmlResultReporter.ns, TAG); 150 serializer.attribute(CtsXmlResultReporter.ns, NAME_ATTR, getName()); 151 serializer.attribute(CtsXmlResultReporter.ns, RESULT_ATTR, mResult.getValue()); 152 serializer.attribute(CtsXmlResultReporter.ns, STARTTIME_ATTR, mStartTime); 153 serializer.attribute(CtsXmlResultReporter.ns, ENDTIME_ATTR, mEndTime); 154 155 serializeTestLogsLocked(serializer); 156 157 if (mMessage != null) { 158 serializer.startTag(CtsXmlResultReporter.ns, SCENE_TAG); 159 serializer.attribute(CtsXmlResultReporter.ns, MESSAGE_ATTR, mMessage); 160 if (mStackTrace != null) { 161 serializer.startTag(CtsXmlResultReporter.ns, STACK_TAG); 162 serializer.text(mStackTrace); 163 serializer.endTag(CtsXmlResultReporter.ns, STACK_TAG); 164 } 165 serializer.endTag(CtsXmlResultReporter.ns, SCENE_TAG); 166 } 167 MetricsXmlSerializer metricsXmlSerializer = new MetricsXmlSerializer(serializer); 168 metricsXmlSerializer.serialize(mReport); 169 serializer.endTag(CtsXmlResultReporter.ns, TAG); 170 } 171 172 /** 173 * Strip out any invalid XML characters that might cause the report to be unviewable. 174 * http://www.w3.org/TR/REC-xml/#dt-character 175 */ sanitizeStackTrace(String trace)176 private static String sanitizeStackTrace(String trace) { 177 if (trace != null) { 178 return trace.replaceAll("[^\\u0009\\u000A\\u000D\\u0020-\\uD7FF\\uE000-\\uFFFD]", ""); 179 } else { 180 return null; 181 } 182 } 183 184 /** 185 * Gets the failure message to show from the stack trace. 186 * <p/> 187 * Exposed for unit testing 188 * 189 * @param stack the full stack trace 190 * @return the failure message 191 */ getFailureMessageFromStackTrace(String stack)192 static String getFailureMessageFromStackTrace(String stack) { 193 // return the first two lines of stack as failure message 194 int endPoint = stack.indexOf('\n'); 195 if (endPoint != -1) { 196 int nextLine = stack.indexOf('\n', endPoint + 1); 197 if (nextLine != -1) { 198 return stack.substring(0, nextLine); 199 } 200 } 201 return stack; 202 } 203 204 /** 205 * Populates this class with test result data parsed from XML. 206 * 207 * @param parser the {@link XmlPullParser}. Expected to be pointing at start 208 * of a Test tag 209 */ 210 @Override parse(XmlPullParser parser)211 void parse(XmlPullParser parser) throws XmlPullParserException, IOException { 212 if (!parser.getName().equals(TAG)) { 213 throw new XmlPullParserException(String.format( 214 "invalid XML: Expected %s tag but received %s", TAG, parser.getName())); 215 } 216 setName(getAttribute(parser, NAME_ATTR)); 217 mResult = CtsTestStatus.getStatus(getAttribute(parser, RESULT_ATTR)); 218 mStartTime = getAttribute(parser, STARTTIME_ATTR); 219 mEndTime = getAttribute(parser, ENDTIME_ATTR); 220 221 int eventType = parser.next(); 222 while (eventType != XmlPullParser.END_DOCUMENT) { 223 if (eventType == XmlPullParser.START_TAG && parser.getName().equals(SCENE_TAG)) { 224 mMessage = getAttribute(parser, MESSAGE_ATTR); 225 } else if (eventType == XmlPullParser.START_TAG && parser.getName().equals(STACK_TAG)) { 226 mStackTrace = parser.nextText(); 227 } else if (eventType == XmlPullParser.START_TAG && TestLog.isTag(parser.getName())) { 228 parseTestLog(parser); 229 } else if (eventType == XmlPullParser.END_TAG && parser.getName().equals(TAG)) { 230 return; 231 } 232 eventType = parser.next(); 233 } 234 } 235 236 /** Parse a TestLog entry from the parser positioned at a TestLog tag. */ parseTestLog(XmlPullParser parser)237 private void parseTestLog(XmlPullParser parser) throws XmlPullParserException{ 238 TestLog log = TestLog.fromXml(parser); 239 if (log == null) { 240 throw new XmlPullParserException("invalid XML: bad test log tag"); 241 } 242 addTestLog(log); 243 } 244 245 /** Add a TestLog to the test in a thread safe manner. */ addTestLogLocked(TestLog testLog)246 private synchronized void addTestLogLocked(TestLog testLog) { 247 if (mTestLogs == null) { 248 mTestLogs = new ArrayList<>(TestLogType.values().length); 249 } 250 mTestLogs.add(testLog); 251 } 252 253 /** Serialize the TestLogs of this test in a thread safe manner. */ serializeTestLogsLocked(KXmlSerializer serializer)254 private synchronized void serializeTestLogsLocked(KXmlSerializer serializer) throws IOException { 255 if (mTestLogs != null) { 256 for (TestLog log : mTestLogs) { 257 log.serialize(serializer); 258 } 259 } 260 } 261 } 262