1 package com.android.helpers;
2 
3 import android.app.Instrumentation;
4 import android.os.ParcelFileDescriptor;
5 import android.util.Log;
6 
7 import java.io.ByteArrayOutputStream;
8 import java.io.File;
9 import java.io.FileInputStream;
10 import java.io.FileNotFoundException;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.text.DecimalFormat;
14 import java.text.ParseException;
15 import java.util.ArrayList;
16 import java.util.List;
17 import java.util.Map;
18 
19 /**
20  * MetricUtility consist of basic utility methods to construct the metrics
21  * reported at the end of the test.
22  */
23 public class MetricUtility {
24 
25     private static final String TAG = MetricUtility.class.getSimpleName();
26     private static final String KEY_JOIN = "_";
27     public static final String METRIC_SEPARATOR = ",";
28 
29     public static final int BUFFER_SIZE = 1024;
30     private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("#0.000001");
31 
32     /**
33      * Append the given array of string to construct the final key used to track the metrics.
34      *
35      * @param keys to append using KEY_JOIN
36      */
constructKey(String... keys)37     public static String constructKey(String... keys) {
38         return String.join(KEY_JOIN, keys);
39     }
40 
41     /**
42      * Add metric to the result map. If metric key already exist append the new metric.
43      *
44      * @param metricKey Unique key to track the metric.
45      * @param metric metric to track.
46      * @param resultMap map of all the metrics.
47      */
addMetric(String metricKey, long metric, Map<String, StringBuilder> resultMap)48     public static void addMetric(String metricKey, long metric, Map<String,
49             StringBuilder> resultMap) {
50         resultMap.compute(metricKey, (key, value) -> (value == null) ?
51                 new StringBuilder().append(metric) : value.append(METRIC_SEPARATOR).append(metric));
52     }
53 
54     /**
55      * Add metric to the result map. If metric key already exist append the new metric.
56      *
57      * @param metricKey Unique key to track the metric.
58      * @param metric metric to track.
59      * @param resultMap map of all the metrics.
60      */
addMetric( String metricKey, double metric, Map<String, StringBuilder> resultMap)61     public static void addMetric(
62             String metricKey, double metric, Map<String, StringBuilder> resultMap) {
63         resultMap.compute(
64                 metricKey,
65                 (key, value) ->
66                         (value == null ? new StringBuilder() : value.append(METRIC_SEPARATOR))
67                                 .append(DOUBLE_FORMAT.format(metric)));
68     }
69 
70     /**
71      * Add metric to the result map. If metric key already exist increment the value by 1.
72      *
73      * @param metricKey Unique key to track the metric.
74      * @param resultMap map of all the metrics.
75      */
addMetric(String metricKey, Map<String, Integer> resultMap)76     public static void addMetric(String metricKey, Map<String,
77             Integer> resultMap) {
78         resultMap.compute(metricKey, (key, value) -> (value == null) ? 1 : value + 1);
79     }
80 
81     /**
82      * Get metric values from result map.
83      *
84      * @param metricKey Unique key to track the metric.
85      * @param resultMap Map of all the metrics.
86      * @return Double List of metric values for metric key
87      */
getMetricDoubles( String metricKey, Map<String, StringBuilder> resultMap)88     public static List<Double> getMetricDoubles(
89             String metricKey, Map<String, StringBuilder> resultMap) {
90         List<Double> result = new ArrayList<Double>();
91         if (!resultMap.containsKey(metricKey)) {
92             Log.e(TAG, String.format("No such metric key %s", metricKey));
93             return result;
94         } else {
95             String value = resultMap.get(metricKey).toString();
96             if (value.length() == 0) {
97                 Log.e(TAG, String.format("Missed value of metric key %s", metricKey));
98                 return result;
99             } else {
100                 String[] values = value.split(METRIC_SEPARATOR);
101                 for (int i = 0; i < values.length; i++) {
102                     try {
103                         result.add(DOUBLE_FORMAT.parse(values[i]).doubleValue());
104                     } catch (ParseException e) {
105                         Log.e(
106                                 TAG,
107                                 String.format(
108                                         "Error parsing value of metric key %s: #%d of value %s",
109                                         metricKey, i, value));
110                         return new ArrayList<Double>();
111                     }
112                 }
113             }
114         }
115         return result;
116     }
117 
118     /**
119      * Turn executeShellCommand into a blocking operation.
120      *
121      * @param command shell command to be executed.
122      * @param instr used to run the shell command.
123      * @return byte array of execution result
124      */
executeCommandBlocking(String command, Instrumentation instr)125     public static byte[] executeCommandBlocking(String command, Instrumentation instr) {
126         try (InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(instr.getUiAutomation()
127                 .executeShellCommand(command));
128                 ByteArrayOutputStream out = new ByteArrayOutputStream()) {
129             byte[] buf = new byte[BUFFER_SIZE];
130             int length;
131             Log.i(TAG, "Start reading the data");
132             while ((length = is.read(buf)) >= 0) {
133                 out.write(buf, 0, length);
134             }
135             Log.i(TAG, "Stop reading the data");
136             return out.toByteArray();
137         } catch (IOException e) {
138             Log.e(TAG, "Error executing: " + command, e);
139             return null;
140         }
141     }
142 
143     /**
144      * Read contents from the file into string.
145      *
146      * @param processIdFile to read the contents from.
147      * @return String output of the contents.
148      */
readStringFromFile(File processIdFile)149     public static String readStringFromFile(File processIdFile)
150             throws FileNotFoundException, IOException {
151         FileInputStream stream = new FileInputStream(processIdFile);
152         byte[] buffer = new byte[10];
153         StringBuilder builder = new StringBuilder();
154         while (stream.read(buffer) != -1) {
155             builder.append(new String(buffer));
156             buffer = new byte[10];
157         }
158         stream.close();
159 
160         return builder.toString();
161     }
162 }
163