1 /* 2 * Copyright (C) 2014 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 17 package com.android.compatibility.common.util; 18 19 import org.xmlpull.v1.XmlPullParser; 20 import org.xmlpull.v1.XmlPullParserException; 21 import org.xmlpull.v1.XmlPullParserFactory; 22 import org.xmlpull.v1.XmlSerializer; 23 24 import java.io.ByteArrayInputStream; 25 import java.io.ByteArrayOutputStream; 26 import java.io.IOException; 27 import java.io.Serializable; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.List; 31 32 /** 33 * Utility class to add results to the report. 34 */ 35 public class ReportLog implements Serializable { 36 37 private static final String ENCODING = "UTF-8"; 38 private static final String TYPE = "org.kxml2.io.KXmlParser,org.kxml2.io.KXmlSerializer"; 39 40 // XML constants 41 private static final String METRIC_TAG = "Metric"; 42 private static final String MESSAGE_ATTR = "message"; 43 private static final String SCORETYPE_ATTR = "score_type"; 44 private static final String SCOREUNIT_ATTR = "score_unit"; 45 private static final String SOURCE_ATTR = "source"; 46 private static final String SUMMARY_TAG = "Summary"; 47 private static final String VALUE_TAG = "Value"; 48 private static final String DEFAULT_NAME = "default"; 49 50 protected Metric mSummary; 51 protected String mReportLogName; 52 protected String mStreamName; 53 54 public static class Metric implements Serializable { 55 private static final int MAX_SOURCE_LENGTH = 200; 56 private static final int MAX_MESSAGE_LENGTH = 200; 57 private static final int MAX_NUM_VALUES = 1000; 58 String mSource; 59 String mMessage; 60 double[] mValues; 61 ResultType mType; 62 ResultUnit mUnit; 63 Metric(String source, String message, double value, ResultType type, ResultUnit unit)64 Metric(String source, String message, double value, ResultType type, ResultUnit unit) { 65 this(source, message, new double[] { value }, type, unit); 66 } 67 68 /** 69 * Creates a metric array to be included in the report. Each object has a message 70 * describing its values and enums to interpret them. In addition, each result also includes 71 * class, method and line number information about the test which added this result which is 72 * collected by looking at the stack trace. 73 * 74 * @param message A string describing the values 75 * @param values An array of the values 76 * @param type Represents how to interpret the values (eg. A lower score is better) 77 * @param unit Represents the unit in which the values are (eg. Milliseconds) 78 */ Metric(String source, String message, double[] values, ResultType type, ResultUnit unit)79 Metric(String source, String message, double[] values, ResultType type, ResultUnit unit) { 80 int sourceLength = source.length(); 81 if (sourceLength > MAX_SOURCE_LENGTH) { 82 // Substring to the end 83 mSource = source.substring(sourceLength - MAX_SOURCE_LENGTH); 84 } else { 85 mSource = source; 86 } 87 int messageLength = message.length(); 88 if (messageLength > MAX_MESSAGE_LENGTH) { 89 // Substring from the start 90 mMessage = message.substring(0, MAX_MESSAGE_LENGTH); 91 } else { 92 mMessage = message; 93 } 94 int valuesLength = values.length; 95 if (valuesLength > MAX_NUM_VALUES) { 96 // Subarray from the start 97 mValues = Arrays.copyOf(values, MAX_NUM_VALUES); 98 } else { 99 mValues = values; 100 } 101 mType = type; 102 mUnit = unit; 103 } 104 getSource()105 public String getSource() { 106 return mSource; 107 } 108 getMessage()109 public String getMessage() { 110 return mMessage; 111 } 112 getValues()113 public double[] getValues() { 114 return mValues; 115 } 116 getType()117 public ResultType getType() { 118 return mType; 119 } 120 getUnit()121 public ResultUnit getUnit() { 122 return mUnit; 123 } 124 serialize(XmlSerializer serializer)125 void serialize(XmlSerializer serializer) 126 throws IllegalArgumentException, IllegalStateException, IOException { 127 serializer.startTag(null, METRIC_TAG); 128 serializer.attribute(null, SOURCE_ATTR, getSource()); 129 serializer.attribute(null, MESSAGE_ATTR, getMessage()); 130 serializer.attribute(null, SCORETYPE_ATTR, getType().toReportString()); 131 serializer.attribute(null, SCOREUNIT_ATTR, getUnit().toReportString()); 132 for (double d : getValues()) { 133 serializer.startTag(null, VALUE_TAG); 134 serializer.text(Double.toString(d)); 135 serializer.endTag(null, VALUE_TAG); 136 } 137 serializer.endTag(null, METRIC_TAG); 138 } 139 parse(XmlPullParser parser)140 static Metric parse(XmlPullParser parser) 141 throws XmlPullParserException, IOException { 142 parser.require(XmlPullParser.START_TAG, null, METRIC_TAG); 143 String source = parser.getAttributeValue(null, SOURCE_ATTR); 144 String message = parser.getAttributeValue(null, MESSAGE_ATTR); 145 ResultType type = ResultType.parseReportString( 146 parser.getAttributeValue(null, SCORETYPE_ATTR)); 147 ResultUnit unit = ResultUnit.parseReportString( 148 parser.getAttributeValue(null, SCOREUNIT_ATTR)); 149 List<String> valuesList = new ArrayList<>(); 150 while (parser.nextTag() == XmlPullParser.START_TAG) { 151 parser.require(XmlPullParser.START_TAG, null, VALUE_TAG); 152 valuesList.add(parser.nextText()); 153 parser.require(XmlPullParser.END_TAG, null, VALUE_TAG); 154 } 155 int length = valuesList.size(); 156 double[] values = new double[length]; 157 for (int i = 0; i < length; i++) { 158 values[i] = Double.parseDouble(valuesList.get(i)); 159 } 160 parser.require(XmlPullParser.END_TAG, null, METRIC_TAG); 161 return new Metric(source, message, values, type, unit); 162 } 163 } 164 ReportLog()165 public ReportLog() { 166 mReportLogName = DEFAULT_NAME; 167 } 168 ReportLog(String reportLogName, String streamName)169 public ReportLog(String reportLogName, String streamName) { 170 mReportLogName = reportLogName; 171 mStreamName = streamName; 172 } 173 174 /** 175 * Adds a double array of metrics to the report. 176 */ addValues(String message, double[] values, ResultType type, ResultUnit unit)177 public void addValues(String message, double[] values, ResultType type, ResultUnit unit) { 178 // Do nothing. Subclasses may implement using InfoStore to write metrics to files. 179 } 180 181 /** 182 * Adds a double array of metrics to the report. 183 */ addValues(String source, String message, double[] values, ResultType type, ResultUnit unit)184 public void addValues(String source, String message, double[] values, ResultType type, 185 ResultUnit unit) { 186 // Do nothing. Subclasses may implement using InfoStore to write metrics to files. 187 } 188 189 /** 190 * Adds a double metric to the report. 191 */ addValue(String message, double value, ResultType type, ResultUnit unit)192 public void addValue(String message, double value, ResultType type, ResultUnit unit) { 193 // Do nothing. Subclasses may implement using InfoStore to write metrics to files. 194 } 195 196 /** 197 * Adds a double metric to the report. 198 */ addValue(String source, String message, double value, ResultType type, ResultUnit unit)199 public void addValue(String source, String message, double value, ResultType type, 200 ResultUnit unit) { 201 // Do nothing. Subclasses may implement using InfoStore to write metrics to files. 202 } 203 204 /** 205 * Adds an int metric to the report. 206 */ addValue(String message, int value, ResultType type, ResultUnit unit)207 public void addValue(String message, int value, ResultType type, ResultUnit unit) { 208 // Do nothing. Subclasses may implement using InfoStore to write metrics to files. 209 } 210 211 /** 212 * Adds a long metric to the report. 213 */ addValue(String message, long value, ResultType type, ResultUnit unit)214 public void addValue(String message, long value, ResultType type, ResultUnit unit) { 215 // Do nothing. Subclasses may implement using InfoStore to write metrics to files. 216 } 217 218 /** 219 * Adds a float metric to the report. 220 */ addValue(String message, float value, ResultType type, ResultUnit unit)221 public void addValue(String message, float value, ResultType type, ResultUnit unit) { 222 // Do nothing. Subclasses may implement using InfoStore to write metrics to files. 223 } 224 225 /** 226 * Adds a boolean metric to the report. 227 */ addValue(String message, boolean value, ResultType type, ResultUnit unit)228 public void addValue(String message, boolean value, ResultType type, ResultUnit unit) { 229 // Do nothing. Subclasses may implement using InfoStore to write metrics to files. 230 } 231 232 /** 233 * Adds a String metric to the report. 234 */ addValue(String message, String value, ResultType type, ResultUnit unit)235 public void addValue(String message, String value, ResultType type, ResultUnit unit) { 236 // Do nothing. Subclasses may implement using InfoStore to write metrics to files. 237 } 238 239 /** 240 * Adds an int array of metrics to the report. 241 */ addValues(String message, int[] values, ResultType type, ResultUnit unit)242 public void addValues(String message, int[] values, ResultType type, ResultUnit unit) { 243 // Do nothing. Subclasses may implement using InfoStore to write metrics to files. 244 } 245 246 /** 247 * Adds a long array of metrics to the report. 248 */ addValues(String message, long[] values, ResultType type, ResultUnit unit)249 public void addValues(String message, long[] values, ResultType type, ResultUnit unit) { 250 // Do nothing. Subclasses may implement using InfoStore to write metrics to files. 251 } 252 253 /** 254 * Adds a float array of metrics to the report. 255 */ addValues(String message, float[] values, ResultType type, ResultUnit unit)256 public void addValues(String message, float[] values, ResultType type, ResultUnit unit) { 257 // Do nothing. Subclasses may implement using InfoStore to write metrics to files. 258 } 259 260 /** 261 * Adds a boolean array of metrics to the report. 262 */ addValues(String message, boolean[] values, ResultType type, ResultUnit unit)263 public void addValues(String message, boolean[] values, ResultType type, ResultUnit unit) { 264 // Do nothing. Subclasses may implement using InfoStore to write metrics to files. 265 } 266 267 /** 268 * Adds a String List of metrics to the report. 269 */ addValues(String message, List<String> values, ResultType type, ResultUnit unit)270 public void addValues(String message, List<String> values, ResultType type, ResultUnit unit) { 271 // Do nothing. Subclasses may implement using InfoStore to write metrics to files. 272 } 273 274 /** 275 * @param elem 276 */ setSummary(Metric elem)277 /* package */ void setSummary(Metric elem) { 278 mSummary = elem; 279 } 280 281 /** 282 * Sets the double metric summary of the report. 283 * 284 * NOTE: messages over {@value Metric#MAX_MESSAGE_LENGTH} chars will be trimmed. 285 */ setSummary(String message, double value, ResultType type, ResultUnit unit)286 public void setSummary(String message, double value, ResultType type, ResultUnit unit) { 287 setSummary(new Metric(Stacktrace.getTestCallerClassMethodNameLineNumber(), message, value, 288 type, unit)); 289 } 290 getSummary()291 public Metric getSummary() { 292 return mSummary; 293 } 294 295 /** 296 * Serializes a given {@link ReportLog} to a String. 297 * @throws XmlPullParserException 298 * @throws IOException 299 * @throws IllegalStateException 300 * @throws IllegalArgumentException 301 */ serialize(ReportLog reportlog)302 public static String serialize(ReportLog reportlog) throws XmlPullParserException, 303 IllegalArgumentException, IllegalStateException, IOException { 304 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 305 XmlSerializer serializer = XmlPullParserFactory.newInstance(TYPE, null).newSerializer(); 306 serializer.setOutput(byteArrayOutputStream, ENCODING); 307 serializer.startDocument(ENCODING, true); 308 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 309 serialize(serializer, reportlog); 310 serializer.endDocument(); 311 return byteArrayOutputStream.toString(ENCODING); 312 } 313 314 /** 315 * Serializes a given {@link ReportLog} to XML. 316 * @param serializer 317 * @param reportLog 318 * @throws IOException 319 */ serialize(XmlSerializer serializer, ReportLog reportLog)320 public static void serialize(XmlSerializer serializer, ReportLog reportLog) 321 throws IOException { 322 if (reportLog == null) { 323 throw new IllegalArgumentException("Metrics reports was null"); 324 } 325 Metric summary = reportLog.getSummary(); 326 // Summary is optional. Details are not included in result report. 327 if (summary != null) { 328 serializer.startTag(null, SUMMARY_TAG); 329 summary.serialize(serializer); 330 serializer.endTag(null, SUMMARY_TAG); 331 } 332 } 333 334 /** 335 * Parses a {@link ReportLog} from the given string. 336 * @throws XmlPullParserException 337 * @throws IOException 338 */ parse(String result)339 public static ReportLog parse(String result) throws XmlPullParserException, IOException { 340 if (result == null){ 341 throw new IllegalArgumentException("Metrics string was null"); 342 } 343 if (result.trim().isEmpty()) { 344 // Empty report. 345 return new ReportLog(); 346 } 347 XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 348 XmlPullParser parser = factory.newPullParser(); 349 parser.setInput(new ByteArrayInputStream(result.getBytes(ENCODING)), ENCODING); 350 try { 351 parser.nextTag(); 352 } catch (XmlPullParserException e) { 353 // Empty Report. 354 return new ReportLog(); 355 } 356 return parse(parser); 357 } 358 359 /** 360 * Parses a {@link ReportLog} from the given XML parser. 361 * @param parser 362 * @throws IOException 363 * @throws XmlPullParserException 364 */ parse(XmlPullParser parser)365 public static ReportLog parse(XmlPullParser parser) throws XmlPullParserException, IOException { 366 parser.require(XmlPullParser.START_TAG, null, SUMMARY_TAG); 367 parser.nextTag(); 368 ReportLog report = new ReportLog(); 369 report.setSummary(Metric.parse(parser)); 370 parser.nextTag(); 371 parser.require(XmlPullParser.END_TAG, null, SUMMARY_TAG); 372 return report; 373 } 374 } 375