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