Home | History | Annotate | Download | only in testtype
      1 /*
      2  * Copyright (C) 2016 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.tradefed.testtype;
     17 
     18 import com.android.tradefed.device.CollectingOutputReceiver;
     19 import com.android.tradefed.log.LogUtil.CLog;
     20 import com.android.tradefed.result.ITestInvocationListener;
     21 import com.android.tradefed.result.TestDescription;
     22 import com.android.tradefed.util.proto.TfMetricProtoUtil;
     23 
     24 import org.json.JSONArray;
     25 import org.json.JSONException;
     26 import org.json.JSONObject;
     27 
     28 import java.util.HashMap;
     29 import java.util.Iterator;
     30 import java.util.Map;
     31 
     32 /**
     33  * Parses the results of Google Benchmark that run from shell,
     34  * and return a map with all the results.
     35  */
     36 public class GoogleBenchmarkResultParser {
     37 
     38     private String mTestClassName;
     39     private final ITestInvocationListener mTestListener;
     40 
     41     public GoogleBenchmarkResultParser(String testClassName, ITestInvocationListener listener) {
     42         mTestClassName = testClassName;
     43         mTestListener = listener;
     44     }
     45 
     46     /**
     47      * Parse an individual output line.
     48      * name,iterations,real_time,cpu_time,bytes_per_second,items_per_second,label
     49      *
     50      * @param output  contains the test output
     51      * @return a map containing the number of tests that ran.
     52      */
     53     public Map<String, String> parse(CollectingOutputReceiver output) {
     54         String outputLogs = output.getOutput();
     55         Map<String, String> results = new HashMap<String, String>();
     56         JSONObject res = null;
     57         outputLogs = sanitizeOutput(outputLogs);
     58         try {
     59             res = new JSONObject(outputLogs);
     60             // Parse context first
     61             JSONObject context = res.getJSONObject("context");
     62             results = parseJsonToMap(context);
     63         } catch (JSONException e) {
     64             CLog.e("Failed to Parse context:");
     65             CLog.e(e);
     66             CLog.d("output was:\n%s\n", outputLogs);
     67             mTestListener.testRunFailed(String.format("Failed to Parse context: %s", e));
     68             return results;
     69         }
     70         try {
     71             // Benchmark results next
     72             JSONArray benchmarks = res.getJSONArray("benchmarks");
     73             for (int i = 0; i < benchmarks.length(); i++) {
     74                 Map<String, String> testResults = new HashMap<String, String>();
     75                 JSONObject testRes = (JSONObject) benchmarks.get(i);
     76                 String name = testRes.getString("name");
     77                 TestDescription testId = new TestDescription(mTestClassName, name);
     78                 mTestListener.testStarted(testId);
     79                 try {
     80                     testResults = parseJsonToMap(testRes);
     81                 } catch (JSONException e) {
     82                     CLog.e(e);
     83                     mTestListener.testFailed(testId,String.format("Test failed to generate "
     84                                 + "proper results: %s", e.getMessage()));
     85                 }
     86                 mTestListener.testEnded(testId, TfMetricProtoUtil.upgradeConvert(testResults));
     87             }
     88             results.put("Pass", Integer.toString(benchmarks.length()));
     89         } catch (JSONException e) {
     90             CLog.e(e);
     91             results.put("ERROR", e.getMessage());
     92             mTestListener.testRunFailed(String.format("Failed to parse benchmarks results: %s", e));
     93         }
     94         return results;
     95     }
     96 
     97     /**
     98      * Helper that go over all json keys and put them in a map with their matching value.
     99      */
    100     protected Map<String, String> parseJsonToMap(JSONObject j) throws JSONException {
    101         Map<String, String> testResults = new HashMap<String, String>();
    102         Iterator<?> i = j.keys();
    103         while(i.hasNext()) {
    104             String key = (String) i.next();
    105             testResults.put(key, j.get(key).toString());
    106         }
    107         return testResults;
    108     }
    109 
    110     /**
    111      * In some cases a warning is printed before the JSON output. We remove it to avoid parsing
    112      * failures.
    113      */
    114     private String sanitizeOutput(String output) {
    115         // If it already looks like a proper JSON.
    116         // TODO: Maybe parse first and if it fails sanitize. Could avoid some failures?
    117         if (output.startsWith("{")) {
    118             return output;
    119         }
    120         int indexStart = output.indexOf('{');
    121         if (indexStart == -1) {
    122             // Nothing we can do here, the parsing will most likely fail afterward.
    123             CLog.w("Output does not look like a proper JSON.");
    124             return output;
    125         }
    126         String newOuput = output.substring(indexStart);
    127         CLog.d("We removed the following from the output: '%s'", output.subSequence(0, indexStart));
    128         return newOuput;
    129     }
    130 }
    131