• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.device.metric;
17 
18 import com.android.tradefed.build.IBuildInfo;
19 import com.android.tradefed.config.Option;
20 import com.android.tradefed.device.ITestDevice;
21 import com.android.tradefed.invoker.IInvocationContext;
22 import com.android.tradefed.log.LogUtil.CLog;
23 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
24 import com.android.tradefed.result.ITestInvocationListener;
25 import com.android.tradefed.result.InputStreamSource;
26 import com.android.tradefed.result.LogDataType;
27 import com.android.tradefed.result.TestDescription;
28 
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Set;
36 
37 /**
38  * Base implementation of {@link IMetricCollector} that allows to start and stop collection on
39  * {@link #onTestRunStart(DeviceMetricData)} and {@link #onTestRunEnd(DeviceMetricData, Map)}.
40  */
41 public class BaseDeviceMetricCollector implements IMetricCollector {
42 
43     public static final String TEST_CASE_INCLUDE_GROUP_OPTION = "test-case-include-group";
44     public static final String TEST_CASE_EXCLUDE_GROUP_OPTION = "test-case-exclude-group";
45 
46     @Option(name = "disable", description = "disables the metrics collector")
47     private boolean mDisable = false;
48 
49     @Option(
50         name = TEST_CASE_INCLUDE_GROUP_OPTION,
51         description =
52                 "Specify a group to include as part of the collection,"
53                         + "group can be specified via @MetricOption. Can be repeated."
54                         + "Usage: @MetricOption(group = \"groupname\") to your test methods, then"
55                         + "use --test-case-include-anotation groupename to only run your group."
56     )
57     private List<String> mTestCaseIncludeAnnotationGroup = new ArrayList<>();
58 
59     @Option(
60         name = TEST_CASE_EXCLUDE_GROUP_OPTION,
61         description =
62                 "Specify a group to exclude from the metric collection,"
63                         + "group can be specified via @MetricOption. Can be repeated."
64     )
65     private List<String> mTestCaseExcludeAnnotationGroup = new ArrayList<>();
66 
67     private IInvocationContext mContext;
68     private ITestInvocationListener mForwarder;
69     private DeviceMetricData mRunData;
70     private DeviceMetricData mTestData;
71     private String mTag;
72     private String mRunName;
73     /**
74      * Variable for whether or not to skip the collection of one test case because it was filtered.
75      */
76     private boolean mSkipTestCase = false;
77 
78     @Override
init( IInvocationContext context, ITestInvocationListener listener)79     public ITestInvocationListener init(
80             IInvocationContext context, ITestInvocationListener listener) {
81         mContext = context;
82         mForwarder = listener;
83         return this;
84     }
85 
86     @Override
getDevices()87     public final List<ITestDevice> getDevices() {
88         return mContext.getDevices();
89     }
90 
91     @Override
getBuildInfos()92     public final List<IBuildInfo> getBuildInfos() {
93         return mContext.getBuildInfos();
94     }
95 
96     @Override
getInvocationListener()97     public final ITestInvocationListener getInvocationListener() {
98         return mForwarder;
99     }
100 
101     @Override
onTestRunStart(DeviceMetricData runData)102     public void onTestRunStart(DeviceMetricData runData) {
103         // Does nothing
104     }
105 
106     @Override
onTestRunEnd( DeviceMetricData runData, final Map<String, Metric> currentRunMetrics)107     public void onTestRunEnd(
108             DeviceMetricData runData, final Map<String, Metric> currentRunMetrics) {
109         // Does nothing
110     }
111 
112     @Override
onTestStart(DeviceMetricData testData)113     public void onTestStart(DeviceMetricData testData) {
114         // Does nothing
115     }
116 
117     @Override
onTestFail(DeviceMetricData testData, TestDescription test)118     public void onTestFail(DeviceMetricData testData, TestDescription test) {
119         // Does nothing
120     }
121 
122     @Override
onTestAssumptionFailure(DeviceMetricData testData, TestDescription test)123     public void onTestAssumptionFailure(DeviceMetricData testData, TestDescription test) {
124         // Does nothing
125     }
126 
127     @Override
onTestEnd( DeviceMetricData testData, final Map<String, Metric> currentTestCaseMetrics)128     public void onTestEnd(
129             DeviceMetricData testData, final Map<String, Metric> currentTestCaseMetrics) {
130         // Does nothing
131     }
132 
133     /** =================================== */
134     /** Invocation Listeners for forwarding */
135     @Override
invocationStarted(IInvocationContext context)136     public final void invocationStarted(IInvocationContext context) {
137         mForwarder.invocationStarted(context);
138     }
139 
140     @Override
invocationFailed(Throwable cause)141     public final void invocationFailed(Throwable cause) {
142         mForwarder.invocationFailed(cause);
143     }
144 
145     @Override
invocationEnded(long elapsedTime)146     public final void invocationEnded(long elapsedTime) {
147         mForwarder.invocationEnded(elapsedTime);
148     }
149 
150     @Override
testLog(String dataName, LogDataType dataType, InputStreamSource dataStream)151     public final void testLog(String dataName, LogDataType dataType, InputStreamSource dataStream) {
152         mForwarder.testLog(dataName, dataType, dataStream);
153     }
154 
155     /** Test run callbacks */
156     @Override
testRunStarted(String runName, int testCount)157     public final void testRunStarted(String runName, int testCount) {
158         mRunData = new DeviceMetricData(mContext);
159         mRunName = runName;
160         try {
161             onTestRunStart(mRunData);
162         } catch (Throwable t) {
163             // Prevent exception from messing up the status reporting.
164             CLog.e(t);
165         }
166         mForwarder.testRunStarted(runName, testCount);
167     }
168 
169     @Override
testRunFailed(String errorMessage)170     public final void testRunFailed(String errorMessage) {
171         mForwarder.testRunFailed(errorMessage);
172     }
173 
174     @Override
testRunStopped(long elapsedTime)175     public final void testRunStopped(long elapsedTime) {
176         mForwarder.testRunStopped(elapsedTime);
177     }
178 
179     @Override
testRunEnded(long elapsedTime, HashMap<String, Metric> runMetrics)180     public final void testRunEnded(long elapsedTime, HashMap<String, Metric> runMetrics) {
181         try {
182             onTestRunEnd(mRunData, runMetrics);
183             mRunData.addToMetrics(runMetrics);
184         } catch (Throwable t) {
185             // Prevent exception from messing up the status reporting.
186             CLog.e(t);
187         }
188         mForwarder.testRunEnded(elapsedTime, runMetrics);
189     }
190 
191     /** Test cases callbacks */
192     @Override
testStarted(TestDescription test)193     public final void testStarted(TestDescription test) {
194         testStarted(test, System.currentTimeMillis());
195     }
196 
197     @Override
testStarted(TestDescription test, long startTime)198     public final void testStarted(TestDescription test, long startTime) {
199         mTestData = new DeviceMetricData(mContext);
200         mSkipTestCase = shouldSkip(test);
201         if (!mSkipTestCase) {
202             try {
203                 onTestStart(mTestData);
204             } catch (Throwable t) {
205                 // Prevent exception from messing up the status reporting.
206                 CLog.e(t);
207             }
208         }
209         mForwarder.testStarted(test, startTime);
210     }
211 
212     @Override
testFailed(TestDescription test, String trace)213     public final void testFailed(TestDescription test, String trace) {
214         mSkipTestCase = shouldSkip(test);
215         if (!mSkipTestCase) {
216             try {
217                 onTestFail(mTestData, test);
218             } catch (Throwable t) {
219                 // Prevent exception from messing up the status reporting.
220                 CLog.e(t);
221             }
222         }
223         mForwarder.testFailed(test, trace);
224     }
225 
226     @Override
testEnded(TestDescription test, HashMap<String, Metric> testMetrics)227     public final void testEnded(TestDescription test, HashMap<String, Metric> testMetrics) {
228         testEnded(test, System.currentTimeMillis(), testMetrics);
229     }
230 
231     @Override
testEnded( TestDescription test, long endTime, HashMap<String, Metric> testMetrics)232     public final void testEnded(
233             TestDescription test, long endTime, HashMap<String, Metric> testMetrics) {
234         if (!mSkipTestCase) {
235             try {
236                 onTestEnd(mTestData, testMetrics);
237                 mTestData.addToMetrics(testMetrics);
238             } catch (Throwable t) {
239                 // Prevent exception from messing up the status reporting.
240                 CLog.e(t);
241             }
242         } else {
243             CLog.d("Skipping %s collection for %s.", this.getClass().getName(), test.toString());
244         }
245         mForwarder.testEnded(test, endTime, testMetrics);
246     }
247 
248     @Override
testAssumptionFailure(TestDescription test, String trace)249     public final void testAssumptionFailure(TestDescription test, String trace) {
250         mSkipTestCase = shouldSkip(test);
251         if (!mSkipTestCase) {
252             try {
253                 onTestAssumptionFailure(mTestData, test);
254             } catch (Throwable t) {
255                 // Prevent exception from messing up the status reporting.
256                 CLog.e(t);
257             }
258         }
259         mForwarder.testAssumptionFailure(test, trace);
260     }
261 
262     @Override
testIgnored(TestDescription test)263     public final void testIgnored(TestDescription test) {
264         mForwarder.testIgnored(test);
265     }
266 
267     /** {@inheritDoc} */
268     @Override
isDisabled()269     public final boolean isDisabled() {
270         return mDisable;
271     }
272 
273     /** {@inheritDoc} */
274     @Override
setDisable(boolean isDisabled)275     public final void setDisable(boolean isDisabled) {
276         mDisable = isDisabled;
277     }
278 
279     /**
280      * Sets the {@code mTag} of the collector. It can be used to specify the interval of the
281      * collector.
282      *
283      * @param tag the unique identifier of the collector.
284      */
setTag(String tag)285     public void setTag(String tag) {
286         mTag = tag;
287     }
288 
289     /**
290      * Returns the identifier {@code mTag} of the collector.
291      *
292      * @return mTag, the unique identifier of the collector.
293      */
getTag()294     public String getTag() {
295         return mTag;
296     }
297 
298     /**
299      * Returns the name of test run {@code mRunName} that triggers the collector.
300      *
301      * @return mRunName, the current test run name.
302      */
getRunName()303     public String getRunName() {
304         return mRunName;
305     }
306 
307     /**
308      * Helper to decide if a test case should or not run the collector method associated.
309      *
310      * @param desc the identifier of the test case.
311      * @return True the collector should be skipped. False otherwise.
312      */
shouldSkip(TestDescription desc)313     private boolean shouldSkip(TestDescription desc) {
314         Set<String> testCaseGroups = new HashSet<>();
315         if (desc.getAnnotation(MetricOption.class) != null) {
316             String groupName = desc.getAnnotation(MetricOption.class).group();
317             testCaseGroups.addAll(Arrays.asList(groupName.split(",")));
318         } else {
319             // Add empty group name for default case.
320             testCaseGroups.add("");
321         }
322         // Exclusion has priority: if any of the groups is excluded, exclude the test case.
323         for (String groupName : testCaseGroups) {
324             if (mTestCaseExcludeAnnotationGroup.contains(groupName)) {
325                 return true;
326             }
327         }
328         // Inclusion filter: if any of the group is included, include the test case.
329         for (String includeGroupName : mTestCaseIncludeAnnotationGroup) {
330             if (testCaseGroups.contains(includeGroupName)) {
331                 return false;
332             }
333         }
334 
335         // If we had filters and did not match any groups
336         if (!mTestCaseIncludeAnnotationGroup.isEmpty()) {
337             return true;
338         }
339         return false;
340     }
341 }
342