1 /*
2  * Copyright (C) 2018 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 android.device.collectors;
17 
18 import android.os.Bundle;
19 import android.util.Log;
20 
21 import androidx.annotation.VisibleForTesting;
22 
23 import com.android.helpers.ICollectorHelper;
24 
25 import org.junit.runner.Description;
26 import org.junit.runner.Result;
27 import org.junit.runner.notification.Failure;
28 
29 import java.util.Map;
30 import java.util.function.Function;
31 
32 /**
33  * A {@link BaseCollectionListener} that captures metrics collected during the testing.
34  *
35  * Metrics can be collected at the test run level or per test method using per_run option.
36  *
37  * If there are any failure in the metric collection, tests will still proceed to run and
38  * not posting the metrics at the end of the test.
39  *
40  * Do NOT throw exception anywhere in this class. We don't want to halt the test when metrics
41  * collection fails.
42  */
43 public class BaseCollectionListener<T> extends BaseMetricListener {
44 
45     protected ICollectorHelper mHelper;
46     // Collect per run if it is set to true otherwise collect per test.
47     public static final String COLLECT_PER_RUN = "per_run";
48     // Skip failure metrics collection if this flag is set to true.
49     public static final String SKIP_TEST_FAILURE_METRICS = "skip_test_failure_metrics";
50     protected boolean mIsCollectPerRun;
51     protected boolean mSkipTestFailureMetrics;
52     private boolean mIsTestFailed = false;
53 
BaseCollectionListener()54     public BaseCollectionListener() {
55         super();
56     }
57 
58     @VisibleForTesting
BaseCollectionListener(Bundle args, ICollectorHelper helper)59     public BaseCollectionListener(Bundle args, ICollectorHelper helper) {
60         super(args);
61         mHelper = helper;
62     }
63 
64     @Override
onTestRunStart(DataRecord runData, Description description)65     public void onTestRunStart(DataRecord runData, Description description) {
66 
67         if (mIsCollectPerRun) {
68             Function<String, Boolean> filter = getFilter(description);
69             testStart(filter, description);
70         }
71     }
72 
73     @Override
parseArguments()74     protected void parseArguments() {
75         super.parseArguments();
76         Bundle args = getArgsBundle();
77         mIsCollectPerRun = "true".equals(args.getString(COLLECT_PER_RUN));
78         // By default this flag is set to false to collect the metrics on test failure.
79         mSkipTestFailureMetrics = "true".equals(args.getString(SKIP_TEST_FAILURE_METRICS));
80     }
81 
getFilter(Description description)82     protected Function<String, Boolean> getFilter(Description description) {
83         return null;
84     }
85 
86     @Override
onTestStart(DataRecord testData, Description description)87     public final void onTestStart(DataRecord testData, Description description) {
88         mIsTestFailed = false;
89         if (!mIsCollectPerRun) {
90             Function<String, Boolean> filter = getFilter(description);
91             testStart(filter, description);
92         }
93     }
94 
95     @Override
onTestFail(DataRecord testData, Description description, Failure failure)96     public void onTestFail(DataRecord testData, Description description, Failure failure) {
97         mIsTestFailed = true;
98     }
99 
100     @Override
onTestEnd(DataRecord testData, Description description)101     public void onTestEnd(DataRecord testData, Description description) {
102         if (!mIsCollectPerRun) {
103             try {
104                 // Skip adding the metrics collected during the test failure
105                 // if the skip metrics on test failure flag is enabled and the
106                 // current test is failed.
107                 if (shouldSkipFailureTestMetrics()) {
108                     Log.i(getTag(), "Skipping the metric collection.");
109                 } else {
110                     // Collect the metrics.
111                     collectMetrics(testData);
112                 }
113             } finally {
114                 mHelper.stopCollecting();
115             }
116         }
117     }
118 
119     @Override
onTestRunEnd(DataRecord runData, Result result)120     public void onTestRunEnd(DataRecord runData, Result result) {
121         if (mIsCollectPerRun) {
122             try {
123                 collectMetrics(runData);
124             } finally {
125                 mHelper.stopCollecting();
126             }
127         }
128     }
129 
testStart(Function<String, Boolean> filter, Description description)130     public void testStart(Function<String, Boolean> filter, Description description) {
131         if (filter == null) {
132             mHelper.startCollecting();
133         } else {
134             mHelper.startCollecting(filter);
135         }
136     }
137 
createHelperInstance(ICollectorHelper helper)138     protected void createHelperInstance(ICollectorHelper helper) {
139         mHelper = helper;
140     }
141 
collectMetrics(DataRecord data)142     protected void collectMetrics(DataRecord data) {
143         Map<String, T> metrics = mHelper.getMetrics();
144         for (Map.Entry<String, T> entry : metrics.entrySet()) {
145             data.addStringMetric(entry.getKey(), entry.getValue().toString());
146         }
147     }
148 
shouldSkipFailureTestMetrics()149     protected boolean shouldSkipFailureTestMetrics() {
150         return mSkipTestFailureMetrics && mIsTestFailed;
151     }
152 }
153