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