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