1 /*
2  * Copyright (C) 2012 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.cts.util;
18 
19 import java.util.LinkedList;
20 import java.util.List;
21 
22 import junit.framework.Assert;
23 
24 
25 /**
26  * Utility class to print performance measurement result back to host.
27  * For now, throws know exception with message.
28  *
29  * Format:
30  * Message = summary log SUMMARY_SEPARATOR [LOG_SEPARATOR log]*
31  * summary = message|target|unit|type|value, target can be " " if there is no target set.
32  * log for array = classMethodName:line_number|message|unit|type|space seSummaryparated values
33  */
34 public class ReportLog {
35     private static final String LOG_SEPARATOR = "+++";
36     private static final String SUMMARY_SEPARATOR = "++++";
37     private static final String LOG_ELEM_SEPARATOR = "|";
38 
39     private List<String> mMessages = new LinkedList<String> ();
40     private String mSummary = null;
41     protected static int mDepth = 3;
42 
43     /**
44      * print array of values to output log
45      * <p>Note: test identifier is inferred from call stack trace based on class and method name
46      */
printArray(String message, double[] values, ResultType type, ResultUnit unit)47     public void printArray(String message, double[] values, ResultType type, ResultUnit unit) {
48         doPrintArray(message, values, type, unit);
49     }
50 
51     /**
52      * print array of values to output log
53      */
printArray(String testId, String message, double[] values, ResultType type, ResultUnit unit)54     public void printArray(String testId, String message,
55             double[] values, ResultType type, ResultUnit unit) {
56         doPrintArray(testId, message, values, type, unit);
57     }
58 
59     /**
60      * Print a value to output log
61      * <p>Note: test identifier is inferred from call stack trace based on class and method name
62      */
printValue(String message, double value, ResultType type, ResultUnit unit)63     public void printValue(String message, double value, ResultType type, ResultUnit unit) {
64         double[] vals = { value };
65         doPrintArray(message, vals, type, unit);
66     }
67 
68     /**
69      * Print a value to output log
70      */
printValue(String testId, String message, double value, ResultType type, ResultUnit unit)71     public void printValue(String testId, String message,
72             double value, ResultType type, ResultUnit unit) {
73         double[] vals = { value };
74         doPrintArray(testId, message, vals, type, unit);
75     }
76 
doPrintArray(String message, double[] values, ResultType type, ResultUnit unit)77     private void doPrintArray(String message, double[] values, ResultType type, ResultUnit unit) {
78         doPrintArray(getClassMethodNames(mDepth + 1, true), message, values, type, unit);
79     }
80 
doPrintArray(String testId, String message, double[] values, ResultType type, ResultUnit unit)81     private void doPrintArray(String testId, String message,
82             double[] values, ResultType type, ResultUnit unit) {
83         StringBuilder builder = new StringBuilder();
84         // note mDepth + 1 as this function will be called by printVaue or printArray
85         // and we need caller of printValue / printArray
86         builder.append(testId);
87         builder.append(LOG_ELEM_SEPARATOR);
88         builder.append(message);
89         builder.append(LOG_ELEM_SEPARATOR);
90         builder.append(type.getXmlString());
91         builder.append(LOG_ELEM_SEPARATOR);
92         builder.append(unit.getXmlString());
93         builder.append(LOG_ELEM_SEPARATOR);
94         for (double v : values) {
95             builder.append(v);
96             builder.append(" ");
97         }
98         mMessages.add(builder.toString());
99         printLog(builder.toString());
100     }
101 
102     /**
103      * record the result of benchmarking with performance target.
104      * Depending on the ResultType, the function can fail if the result
105      * does not meet the target. For example, for the type of HIGHER_BETTER,
106      * value of 1.0 with target of 2.0 will fail.
107      *
108      * @param message message to be printed in the final report
109      * @param target target performance for the benchmarking
110      * @param value measured value
111      * @param type
112      * @param unit
113      */
printSummaryWithTarget(String message, double target, double value, ResultType type, ResultUnit unit)114     public void printSummaryWithTarget(String message, double target, double value,
115             ResultType type, ResultUnit unit) {
116         mSummary = message + LOG_ELEM_SEPARATOR + target + LOG_ELEM_SEPARATOR + type.getXmlString()
117                 + LOG_ELEM_SEPARATOR + unit.getXmlString() + LOG_ELEM_SEPARATOR + value;
118         boolean resultOk = true;
119         if (type == ResultType.HIGHER_BETTER) {
120             resultOk = value >= target;
121         } else if (type == ResultType.LOWER_BETTER) {
122             resultOk = value <= target;
123         }
124         if (!resultOk) {
125             Assert.fail("Measured result " + value + " does not meet perf target " + target +
126                     " with type " + type.getXmlString());
127         }
128     }
129 
130     /**
131      * For standard report summary without target value.
132      * Note that this function will not fail as there is no target.
133      * @param message
134      * @param value
135      * @param type type of the value
136      * @param unit unit of the data
137      */
printSummary(String message, double value, ResultType type, ResultUnit unit)138     public void printSummary(String message, double value, ResultType type, ResultUnit unit) {
139         mSummary = message + LOG_ELEM_SEPARATOR + " " + LOG_ELEM_SEPARATOR + type.getXmlString() +
140                 LOG_ELEM_SEPARATOR + unit.getXmlString() + LOG_ELEM_SEPARATOR + value;
141     }
142 
143     /**
144      * @return a string representation of this report.
145      */
generateReport()146     protected String generateReport() {
147         if ((mSummary == null) && mMessages.isEmpty()) {
148             // just return empty string
149             return "";
150         }
151         StringBuilder builder = new StringBuilder();
152         builder.append(mSummary);
153         builder.append(SUMMARY_SEPARATOR);
154         for (String entry : mMessages) {
155             builder.append(entry);
156             builder.append(LOG_SEPARATOR);
157         }
158         // delete the last separator
159         if (builder.length() >= LOG_SEPARATOR.length()) {
160             builder.delete(builder.length() - LOG_SEPARATOR.length(), builder.length());
161         }
162         mSummary = null;
163         mMessages.clear();
164         return builder.toString();
165     }
166 
167     /**
168      * calculate rate per sec for given change happened during given timeInMSec.
169      * timeInSec with 0 value will be changed to small value to prevent divide by zero.
170      * @param change total change of quality for the given duration timeInMSec.
171      * @param timeInMSec
172      * @return
173      */
calcRatePerSec(double change, double timeInMSec)174     public static double calcRatePerSec(double change, double timeInMSec) {
175         if (timeInMSec == 0) {
176             return change * 1000.0 / 0.001; // do not allow zero
177         } else {
178             return change * 1000.0 / timeInMSec;
179         }
180     }
181 
182     /**
183      * array version of calcRatePerSecArray
184      */
calcRatePerSecArray(double change, double[] timeInMSec)185     public static double[] calcRatePerSecArray(double change, double[] timeInMSec) {
186         double[] result = new double[timeInMSec.length];
187         change *= 1000.0;
188         for (int i = 0; i < timeInMSec.length; i++) {
189             if (timeInMSec[i] == 0) {
190                 result[i] = change / 0.001;
191             } else {
192                 result[i] = change / timeInMSec[i];
193             }
194         }
195         return result;
196     }
197 
198     /**
199      * copy array from src to dst with given offset in dst.
200      * dst should be big enough to hold src
201      */
copyArray(double[] src, double[] dst, int dstOffset)202     public static void copyArray(double[] src, double[] dst, int dstOffset) {
203         for (int i = 0; i < src.length; i++) {
204             dst[dstOffset + i] = src[i];
205         }
206     }
207 
208     /**
209      * get classname#methodname from call stack of the current thread
210      */
getClassMethodNames()211     public static String getClassMethodNames() {
212         return getClassMethodNames(mDepth, false);
213     }
214 
getClassMethodNames(int depth, boolean addLineNumber)215     private static String getClassMethodNames(int depth, boolean addLineNumber) {
216         StackTraceElement[] elements = Thread.currentThread().getStackTrace();
217         String names = elements[depth].getClassName() + "#" + elements[depth].getMethodName() +
218                 (addLineNumber ? ":" + elements[depth].getLineNumber() : "");
219         return names;
220     }
221 
222     /**
223      * to be overridden by child to print message to be passed
224      */
printLog(String msg)225     protected void printLog(String msg) {
226 
227     }
228 }
229