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.tradefed.result.TestResult; 19 20 import org.kxml2.io.KXmlSerializer; 21 import org.xmlpull.v1.XmlPullParser; 22 import org.xmlpull.v1.XmlPullParserException; 23 24 import java.io.IOException; 25 26 /** 27 * Data structure that represents a "Test" result XML element. 28 */ 29 class Test extends AbstractXmlPullParser { 30 31 static final String TAG = "Test"; 32 private static final String NAME_ATTR = "name"; 33 private static final String MESSAGE_ATTR = "message"; 34 private static final String ENDTIME_ATTR = "endtime"; 35 private static final String STARTTIME_ATTR = "starttime"; 36 private static final String RESULT_ATTR = "result"; 37 private static final String SCENE_TAG = "FailedScene"; 38 private static final String STACK_TAG = "StackTrace"; 39 40 private String mName; 41 private CtsTestStatus mResult; 42 private String mStartTime; 43 private String mEndTime; 44 private String mMessage; 45 private String mStackTrace; 46 47 /** 48 * Create an empty {@link Test} 49 */ 50 public Test() { 51 } 52 53 /** 54 * Create a {@link Test} from a {@link TestResult}. 55 * 56 * @param name 57 */ 58 public Test(String name) { 59 mName = name; 60 mResult = CtsTestStatus.NOT_EXECUTED; 61 mStartTime = TimeUtil.getTimestamp(); 62 updateEndTime(); 63 } 64 65 /** 66 * Set the name of this {@link Test} 67 */ 68 public void setName(String name) { 69 mName = name; 70 } 71 72 /** 73 * Get the name of this {@link Test} 74 */ 75 public String getName() { 76 return mName; 77 } 78 79 public CtsTestStatus getResult() { 80 return mResult; 81 } 82 83 public String getMessage() { 84 return mMessage; 85 } 86 87 public String getStartTime() { 88 return mStartTime; 89 } 90 91 public String getEndTime() { 92 return mEndTime; 93 } 94 95 public String getStackTrace() { 96 return mStackTrace; 97 } 98 99 public void setStackTrace(String stackTrace) { 100 101 mStackTrace = sanitizeStackTrace(stackTrace); 102 mMessage = getFailureMessageFromStackTrace(mStackTrace); 103 } 104 105 public void updateEndTime() { 106 mEndTime = TimeUtil.getTimestamp(); 107 } 108 109 public void setResultStatus(CtsTestStatus status) { 110 mResult = status; 111 } 112 113 /** 114 * Serialize this object and all its contents to XML. 115 * 116 * @param serializer 117 * @throws IOException 118 */ 119 public void serialize(KXmlSerializer serializer) 120 throws IOException { 121 serializer.startTag(CtsXmlResultReporter.ns, TAG); 122 serializer.attribute(CtsXmlResultReporter.ns, NAME_ATTR, getName()); 123 serializer.attribute(CtsXmlResultReporter.ns, RESULT_ATTR, mResult.getValue()); 124 serializer.attribute(CtsXmlResultReporter.ns, STARTTIME_ATTR, mStartTime); 125 serializer.attribute(CtsXmlResultReporter.ns, ENDTIME_ATTR, mEndTime); 126 127 if (mMessage != null) { 128 serializer.startTag(CtsXmlResultReporter.ns, SCENE_TAG); 129 serializer.attribute(CtsXmlResultReporter.ns, MESSAGE_ATTR, mMessage); 130 if (mStackTrace != null) { 131 serializer.startTag(CtsXmlResultReporter.ns, STACK_TAG); 132 serializer.text(mStackTrace); 133 serializer.endTag(CtsXmlResultReporter.ns, STACK_TAG); 134 } 135 serializer.endTag(CtsXmlResultReporter.ns, SCENE_TAG); 136 } 137 serializer.endTag(CtsXmlResultReporter.ns, TAG); 138 } 139 140 /** 141 * Strip out any invalid XML characters that might cause the report to be unviewable. 142 * http://www.w3.org/TR/REC-xml/#dt-character 143 */ 144 private static String sanitizeStackTrace(String trace) { 145 if (trace != null) { 146 return trace.replaceAll("[^\\u0009\\u000A\\u000D\\u0020-\\uD7FF\\uE000-\\uFFFD]", ""); 147 } else { 148 return null; 149 } 150 } 151 152 /** 153 * Gets the failure message to show from the stack trace. 154 * <p/> 155 * Exposed for unit testing 156 * 157 * @param stack the full stack trace 158 * @return the failure message 159 */ 160 static String getFailureMessageFromStackTrace(String stack) { 161 // return the first two lines of stack as failure message 162 int endPoint = stack.indexOf('\n'); 163 if (endPoint != -1) { 164 int nextLine = stack.indexOf('\n', endPoint + 1); 165 if (nextLine != -1) { 166 return stack.substring(0, nextLine); 167 } 168 } 169 return stack; 170 } 171 172 /** 173 * Populates this class with test result data parsed from XML. 174 * 175 * @param parser the {@link XmlPullParser}. Expected to be pointing at start 176 * of a Test tag 177 */ 178 @Override 179 void parse(XmlPullParser parser) throws XmlPullParserException, IOException { 180 if (!parser.getName().equals(TAG)) { 181 throw new XmlPullParserException(String.format( 182 "invalid XML: Expected %s tag but received %s", TAG, parser.getName())); 183 } 184 setName(getAttribute(parser, NAME_ATTR)); 185 mResult = CtsTestStatus.getStatus(getAttribute(parser, RESULT_ATTR)); 186 mStartTime = getAttribute(parser, STARTTIME_ATTR); 187 mEndTime = getAttribute(parser, ENDTIME_ATTR); 188 189 int eventType = parser.next(); 190 while (eventType != XmlPullParser.END_DOCUMENT) { 191 if (eventType == XmlPullParser.START_TAG && parser.getName().equals(SCENE_TAG)) { 192 mMessage = getAttribute(parser, MESSAGE_ATTR); 193 } else if (eventType == XmlPullParser.START_TAG && parser.getName().equals(STACK_TAG)) { 194 mStackTrace = parser.nextText(); 195 } else if (eventType == XmlPullParser.END_TAG && parser.getName().equals(TAG)) { 196 return; 197 } 198 eventType = parser.next(); 199 } 200 } 201 } 202