Home | History | Annotate | Download | only in util
      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 
     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          */
     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 
    105         public String getSource() {
    106             return mSource;
    107         }
    108 
    109         public String getMessage() {
    110             return mMessage;
    111         }
    112 
    113         public double[] getValues() {
    114             return mValues;
    115         }
    116 
    117         public ResultType getType() {
    118             return mType;
    119         }
    120 
    121         public ResultUnit getUnit() {
    122             return mUnit;
    123         }
    124 
    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 
    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 
    165     public ReportLog() {
    166         mReportLogName = DEFAULT_NAME;
    167     }
    168 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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      */
    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      */
    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      */
    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      */
    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