Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2015 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.compatibility.common.util;
     17 
     18 import org.xmlpull.v1.XmlPullParser;
     19 import org.xmlpull.v1.XmlPullParserException;
     20 import org.xmlpull.v1.XmlPullParserFactory;
     21 import org.xmlpull.v1.XmlSerializer;
     22 
     23 import java.io.File;
     24 import java.io.FileNotFoundException;
     25 import java.io.FileOutputStream;
     26 import java.io.FileReader;
     27 import java.io.IOException;
     28 import java.io.OutputStream;
     29 import java.net.InetAddress;
     30 import java.net.UnknownHostException;
     31 import java.text.SimpleDateFormat;
     32 import java.util.ArrayList;
     33 import java.util.Comparator;
     34 import java.util.Collections;
     35 import java.util.Date;
     36 import java.util.List;
     37 import java.util.Map.Entry;
     38 import java.util.Set;
     39 
     40 /**
     41  * Handles conversion of results to/from files.
     42  */
     43 public class ResultHandler {
     44 
     45     private static final String ENCODING = "UTF-8";
     46     private static final String TYPE = "org.kxml2.io.KXmlParser,org.kxml2.io.KXmlSerializer";
     47     private static final String NS = null;
     48     private static final String RESULT_FILE_VERSION = "5.0";
     49     /* package */ static final String TEST_RESULT_FILE_NAME = "test_result.xml";
     50 
     51     // XML constants
     52     private static final String ABI_ATTR = "abi";
     53     private static final String BUGREPORT_TAG = "BugReport";
     54     private static final String BUILD_FINGERPRINT = "build_fingerprint";
     55     private static final String BUILD_ID = "build_id";
     56     private static final String BUILD_PRODUCT = "build_product";
     57     private static final String BUILD_TAG = "Build";
     58     private static final String CASE_TAG = "TestCase";
     59     private static final String COMMAND_LINE_ARGS = "command_line_args";
     60     private static final String DEVICES_ATTR = "devices";
     61     private static final String DONE_ATTR = "done";
     62     private static final String END_DISPLAY_TIME_ATTR = "end_display";
     63     private static final String END_TIME_ATTR = "end";
     64     private static final String FAILED_ATTR = "failed";
     65     private static final String FAILURE_TAG = "Failure";
     66     private static final String HOST_NAME_ATTR = "host_name";
     67     private static final String JAVA_VENDOR_ATTR = "java_vendor";
     68     private static final String JAVA_VERSION_ATTR = "java_version";
     69     private static final String LOGCAT_TAG = "Logcat";
     70     private static final String LOG_URL_ATTR = "log_url";
     71     private static final String MESSAGE_ATTR = "message";
     72     private static final String MODULE_TAG = "Module";
     73     private static final String MODULES_EXECUTED_ATTR = "modules_done";
     74     private static final String MODULES_TOTAL_ATTR = "modules_total";
     75     private static final String NAME_ATTR = "name";
     76     private static final String NOT_EXECUTED_ATTR = "not_executed";
     77     private static final String OS_ARCH_ATTR = "os_arch";
     78     private static final String OS_NAME_ATTR = "os_name";
     79     private static final String OS_VERSION_ATTR = "os_version";
     80     private static final String PASS_ATTR = "pass";
     81     private static final String REPORT_VERSION_ATTR = "report_version";
     82     private static final String REFERENCE_URL_ATTR = "reference_url";
     83     private static final String RESULT_ATTR = "result";
     84     private static final String RESULT_TAG = "Result";
     85     private static final String RUNTIME_ATTR = "runtime";
     86     private static final String SCREENSHOT_TAG = "Screenshot";
     87     private static final String STACK_TAG = "StackTrace";
     88     private static final String START_DISPLAY_TIME_ATTR = "start_display";
     89     private static final String START_TIME_ATTR = "start";
     90     private static final String SUITE_NAME_ATTR = "suite_name";
     91     private static final String SUITE_PLAN_ATTR = "suite_plan";
     92     private static final String SUITE_VERSION_ATTR = "suite_version";
     93     private static final String SUITE_BUILD_ATTR = "suite_build_number";
     94     private static final String SUMMARY_TAG = "Summary";
     95     private static final String TEST_TAG = "Test";
     96 
     97     /**
     98      * @param resultsDir
     99      */
    100     public static List<IInvocationResult> getResults(File resultsDir) {
    101         List<IInvocationResult> results = new ArrayList<>();
    102         File[] files = resultsDir.listFiles();
    103         if (files == null || files.length == 0) {
    104             // No results, just return the empty list
    105             return results;
    106         }
    107         for (File resultDir : files) {
    108             if (!resultDir.isDirectory()) {
    109                 continue;
    110             }
    111             try {
    112                 File resultFile = new File(resultDir, TEST_RESULT_FILE_NAME);
    113                 if (!resultFile.exists()) {
    114                     continue;
    115                 }
    116                 IInvocationResult invocation = new InvocationResult();
    117                 XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
    118                 XmlPullParser parser = factory.newPullParser();
    119                 parser.setInput(new FileReader(resultFile));
    120 
    121                 parser.nextTag();
    122                 parser.require(XmlPullParser.START_TAG, NS, RESULT_TAG);
    123                 invocation.setStartTime(Long.valueOf(
    124                         parser.getAttributeValue(NS, START_TIME_ATTR)));
    125                 invocation.setTestPlan(parser.getAttributeValue(NS, SUITE_PLAN_ATTR));
    126                 invocation.setCommandLineArgs(parser.getAttributeValue(NS, COMMAND_LINE_ARGS));
    127                 String deviceList = parser.getAttributeValue(NS, DEVICES_ATTR);
    128                 for (String device : deviceList.split(",")) {
    129                     invocation.addDeviceSerial(device);
    130                 }
    131 
    132                 parser.nextTag();
    133                 parser.require(XmlPullParser.START_TAG, NS, BUILD_TAG);
    134                 invocation.addInvocationInfo(BUILD_ID, parser.getAttributeValue(NS, BUILD_ID));
    135                 invocation.addInvocationInfo(BUILD_PRODUCT, parser.getAttributeValue(NS,
    136                         BUILD_PRODUCT));
    137                 invocation.setBuildFingerprint(parser.getAttributeValue(NS, BUILD_FINGERPRINT));
    138 
    139                 // TODO(stuartscott): may want to reload these incase the retry was done with
    140                 // --skip-device-info flag
    141                 parser.nextTag();
    142                 parser.require(XmlPullParser.END_TAG, NS, BUILD_TAG);
    143                 parser.nextTag();
    144                 parser.require(XmlPullParser.START_TAG, NS, SUMMARY_TAG);
    145                 parser.nextTag();
    146                 parser.require(XmlPullParser.END_TAG, NS, SUMMARY_TAG);
    147                 while (parser.nextTag() == XmlPullParser.START_TAG) {
    148                     parser.require(XmlPullParser.START_TAG, NS, MODULE_TAG);
    149                     String name = parser.getAttributeValue(NS, NAME_ATTR);
    150                     String abi = parser.getAttributeValue(NS, ABI_ATTR);
    151                     String moduleId = AbiUtils.createId(abi, name);
    152                     boolean done = Boolean.parseBoolean(parser.getAttributeValue(NS, DONE_ATTR));
    153                     IModuleResult module = invocation.getOrCreateModule(moduleId);
    154                     module.setDone(done);
    155                     int notExecuted =
    156                             Integer.parseInt(parser.getAttributeValue(NS, NOT_EXECUTED_ATTR));
    157                     module.setNotExecuted(notExecuted);
    158                     while (parser.nextTag() == XmlPullParser.START_TAG) {
    159                         parser.require(XmlPullParser.START_TAG, NS, CASE_TAG);
    160                         String caseName = parser.getAttributeValue(NS, NAME_ATTR);
    161                         ICaseResult testCase = module.getOrCreateResult(caseName);
    162                         while (parser.nextTag() == XmlPullParser.START_TAG) {
    163                             parser.require(XmlPullParser.START_TAG, NS, TEST_TAG);
    164                             String testName = parser.getAttributeValue(NS, NAME_ATTR);
    165                             ITestResult test = testCase.getOrCreateResult(testName);
    166                             String result = parser.getAttributeValue(NS, RESULT_ATTR);
    167                             test.setResultStatus(TestStatus.getStatus(result));
    168                             test.setRetry(true);
    169                             while (parser.nextTag() == XmlPullParser.START_TAG) {
    170                                 if (parser.getName().equals(FAILURE_TAG)) {
    171                                     test.setMessage(parser.getAttributeValue(NS, MESSAGE_ATTR));
    172                                     if (parser.nextTag() == XmlPullParser.START_TAG) {
    173                                         parser.require(XmlPullParser.START_TAG, NS, STACK_TAG);
    174                                         test.setStackTrace(parser.nextText());
    175                                         parser.require(XmlPullParser.END_TAG, NS, STACK_TAG);
    176                                         parser.nextTag();
    177                                     }
    178                                     parser.require(XmlPullParser.END_TAG, NS, FAILURE_TAG);
    179                                 } else if (parser.getName().equals(BUGREPORT_TAG)) {
    180                                     test.setBugReport(parser.nextText());
    181                                     parser.require(XmlPullParser.END_TAG, NS, BUGREPORT_TAG);
    182                                 } else if (parser.getName().equals(LOGCAT_TAG)) {
    183                                     test.setLog(parser.nextText());
    184                                     parser.require(XmlPullParser.END_TAG, NS, LOGCAT_TAG);
    185                                 } else if (parser.getName().equals(SCREENSHOT_TAG)) {
    186                                     test.setScreenshot(parser.nextText());
    187                                     parser.require(XmlPullParser.END_TAG, NS, SCREENSHOT_TAG);
    188                                 } else {
    189                                     test.setReportLog(ReportLog.parse(parser));
    190                                 }
    191                             }
    192                             parser.require(XmlPullParser.END_TAG, NS, TEST_TAG);
    193                         }
    194                         parser.require(XmlPullParser.END_TAG, NS, CASE_TAG);
    195                     }
    196                     parser.require(XmlPullParser.END_TAG, NS, MODULE_TAG);
    197                 }
    198                 parser.require(XmlPullParser.END_TAG, NS, RESULT_TAG);
    199                 results.add(invocation);
    200             } catch (XmlPullParserException e) {
    201                 e.printStackTrace();
    202             } catch (FileNotFoundException e) {
    203                 e.printStackTrace();
    204             } catch (IOException e) {
    205                 e.printStackTrace();
    206             }
    207         }
    208         // Sort the table entries on each entry's timestamp.
    209         Collections.sort(results, new Comparator<IInvocationResult>() {
    210             public int compare(IInvocationResult result1, IInvocationResult result2) {
    211                 return Long.compare(result1.getStartTime(), result2.getStartTime());
    212             }
    213         });
    214         return results;
    215     }
    216 
    217     /**
    218      * @param result
    219      * @param resultDir
    220      * @param startTime
    221      * @param referenceUrl A nullable string that can contain a URL to a related data
    222      * @param logUrl A nullable string that can contain a URL to related log files
    223      * @param commandLineArgs A string containing the arguments to the run command
    224      * @return The result file created.
    225      * @throws IOException
    226      * @throws XmlPullParserException
    227      */
    228     public static File writeResults(String suiteName, String suiteVersion, String suitePlan,
    229             String suiteBuild, IInvocationResult result, File resultDir,
    230             long startTime, long endTime, String referenceUrl, String logUrl,
    231             String commandLineArgs)
    232                     throws IOException, XmlPullParserException {
    233         int passed = result.countResults(TestStatus.PASS);
    234         int failed = result.countResults(TestStatus.FAIL);
    235         int notExecuted = result.getNotExecuted();
    236         File resultFile = new File(resultDir, TEST_RESULT_FILE_NAME);
    237         OutputStream stream = new FileOutputStream(resultFile);
    238         XmlSerializer serializer = XmlPullParserFactory.newInstance(TYPE, null).newSerializer();
    239         serializer.setOutput(stream, ENCODING);
    240         serializer.startDocument(ENCODING, false);
    241         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
    242         serializer.processingInstruction(
    243                 "xml-stylesheet type=\"text/xsl\" href=\"compatibility_result.xsl\"");
    244         serializer.startTag(NS, RESULT_TAG);
    245         serializer.attribute(NS, START_TIME_ATTR, String.valueOf(startTime));
    246         serializer.attribute(NS, END_TIME_ATTR, String.valueOf(endTime));
    247         serializer.attribute(NS, START_DISPLAY_TIME_ATTR, toReadableDateString(startTime));
    248         serializer.attribute(NS, END_DISPLAY_TIME_ATTR, toReadableDateString(endTime));
    249 
    250         serializer.attribute(NS, SUITE_NAME_ATTR, suiteName);
    251         serializer.attribute(NS, SUITE_VERSION_ATTR, suiteVersion);
    252         serializer.attribute(NS, SUITE_PLAN_ATTR, suitePlan);
    253         serializer.attribute(NS, SUITE_BUILD_ATTR, suiteBuild);
    254         serializer.attribute(NS, REPORT_VERSION_ATTR, RESULT_FILE_VERSION);
    255         serializer.attribute(NS, COMMAND_LINE_ARGS, nullToEmpty(commandLineArgs));
    256 
    257         if (referenceUrl != null) {
    258             serializer.attribute(NS, REFERENCE_URL_ATTR, referenceUrl);
    259         }
    260 
    261         if (logUrl != null) {
    262             serializer.attribute(NS, LOG_URL_ATTR, logUrl);
    263         }
    264 
    265         // Device Info
    266         Set<String> devices = result.getDeviceSerials();
    267         StringBuilder deviceList = new StringBuilder();
    268         boolean first = true;
    269         for (String device : devices) {
    270             if (first) {
    271                 first = false;
    272             } else {
    273                 deviceList.append(",");
    274             }
    275             deviceList.append(device);
    276         }
    277         serializer.attribute(NS, DEVICES_ATTR, deviceList.toString());
    278 
    279         // Host Info
    280         String hostName = "";
    281         try {
    282             hostName = InetAddress.getLocalHost().getHostName();
    283         } catch (UnknownHostException ignored) {}
    284         serializer.attribute(NS, HOST_NAME_ATTR, hostName);
    285         serializer.attribute(NS, OS_NAME_ATTR, System.getProperty("os.name"));
    286         serializer.attribute(NS, OS_VERSION_ATTR, System.getProperty("os.version"));
    287         serializer.attribute(NS, OS_ARCH_ATTR, System.getProperty("os.arch"));
    288         serializer.attribute(NS, JAVA_VENDOR_ATTR, System.getProperty("java.vendor"));
    289         serializer.attribute(NS, JAVA_VERSION_ATTR, System.getProperty("java.version"));
    290 
    291         // Build Info
    292         serializer.startTag(NS, BUILD_TAG);
    293         for (Entry<String, String> entry : result.getInvocationInfo().entrySet()) {
    294             serializer.attribute(NS, entry.getKey(), entry.getValue());
    295         }
    296         serializer.endTag(NS, BUILD_TAG);
    297 
    298         // Summary
    299         serializer.startTag(NS, SUMMARY_TAG);
    300         serializer.attribute(NS, PASS_ATTR, Integer.toString(passed));
    301         serializer.attribute(NS, FAILED_ATTR, Integer.toString(failed));
    302         serializer.attribute(NS, NOT_EXECUTED_ATTR, Integer.toString(notExecuted));
    303         serializer.attribute(NS, MODULES_EXECUTED_ATTR,
    304                 Integer.toString(result.getModuleCompleteCount()));
    305         serializer.attribute(NS, MODULES_TOTAL_ATTR,
    306                 Integer.toString(result.getModules().size()));
    307         serializer.endTag(NS, SUMMARY_TAG);
    308 
    309         // Results
    310         for (IModuleResult module : result.getModules()) {
    311             serializer.startTag(NS, MODULE_TAG);
    312             serializer.attribute(NS, NAME_ATTR, module.getName());
    313             serializer.attribute(NS, ABI_ATTR, module.getAbi());
    314             serializer.attribute(NS, RUNTIME_ATTR, String.valueOf(module.getRuntime()));
    315             serializer.attribute(NS, DONE_ATTR, Boolean.toString(module.isDone()));
    316             serializer.attribute(NS, NOT_EXECUTED_ATTR, Integer.toString(module.getNotExecuted()));
    317             for (ICaseResult cr : module.getResults()) {
    318                 serializer.startTag(NS, CASE_TAG);
    319                 serializer.attribute(NS, NAME_ATTR, cr.getName());
    320                 for (ITestResult r : cr.getResults()) {
    321                     TestStatus status = r.getResultStatus();
    322                     if (status == null) {
    323                         continue; // test was not executed, don't report
    324                     }
    325                     serializer.startTag(NS, TEST_TAG);
    326                     serializer.attribute(NS, RESULT_ATTR, status.getValue());
    327                     serializer.attribute(NS, NAME_ATTR, r.getName());
    328                     String message = r.getMessage();
    329                     if (message != null) {
    330                         serializer.startTag(NS, FAILURE_TAG);
    331                         serializer.attribute(NS, MESSAGE_ATTR, message);
    332                         String stackTrace = r.getStackTrace();
    333                         if (stackTrace != null) {
    334                             serializer.startTag(NS, STACK_TAG);
    335                             serializer.text(stackTrace);
    336                             serializer.endTag(NS, STACK_TAG);
    337                         }
    338                         serializer.endTag(NS, FAILURE_TAG);
    339                     }
    340                     String bugreport = r.getBugReport();
    341                     if (bugreport != null) {
    342                         serializer.startTag(NS, BUGREPORT_TAG);
    343                         serializer.text(bugreport);
    344                         serializer.endTag(NS, BUGREPORT_TAG);
    345                     }
    346                     String logcat = r.getLog();
    347                     if (logcat != null) {
    348                         serializer.startTag(NS, LOGCAT_TAG);
    349                         serializer.text(logcat);
    350                         serializer.endTag(NS, LOGCAT_TAG);
    351                     }
    352                     String screenshot = r.getScreenshot();
    353                     if (screenshot != null) {
    354                         serializer.startTag(NS, SCREENSHOT_TAG);
    355                         serializer.text(screenshot);
    356                         serializer.endTag(NS, SCREENSHOT_TAG);
    357                     }
    358                     ReportLog report = r.getReportLog();
    359                     if (report != null) {
    360                         ReportLog.serialize(serializer, report);
    361                     }
    362                     serializer.endTag(NS, TEST_TAG);
    363                 }
    364                 serializer.endTag(NS, CASE_TAG);
    365             }
    366             serializer.endTag(NS, MODULE_TAG);
    367         }
    368         serializer.endDocument();
    369         return resultFile;
    370     }
    371 
    372     /**
    373      * Find the IInvocationResult for the given sessionId.
    374      */
    375     public static IInvocationResult findResult(File resultsDir, Integer sessionId)
    376             throws FileNotFoundException {
    377         if (sessionId < 0) {
    378             throw new IllegalArgumentException(
    379                 String.format("Invalid session id [%d] ", sessionId));
    380         }
    381 
    382         List<IInvocationResult> results = getResults(resultsDir);
    383         if (results == null || sessionId >= results.size()) {
    384             throw new RuntimeException(String.format("Could not find session [%d]", sessionId));
    385         }
    386         return results.get(sessionId);
    387     }
    388 
    389     /**
    390      * Return the given time as a {@link String} suitable for displaying.
    391      * <p/>
    392      * Example: Fri Aug 20 15:13:03 PDT 2010
    393      *
    394      * @param time the epoch time in ms since midnight Jan 1, 1970
    395      */
    396     static String toReadableDateString(long time) {
    397         SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
    398         return dateFormat.format(new Date(time));
    399     }
    400 
    401     /**
    402      * When nullable is null, return an empty string. Otherwise, return the value in nullable.
    403      */
    404     private static String nullToEmpty(String nullable) {
    405         return nullable == null ? "" : nullable;
    406     }
    407 }
    408