1 /*
2  * Copyright (C) 2016 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.testtype.suite;
17 
18 import com.android.tradefed.device.DeviceNotAvailableException;
19 import com.android.tradefed.device.DeviceProperties;
20 import com.android.tradefed.device.ITestDevice;
21 import com.android.tradefed.log.ITestLogger;
22 import com.android.tradefed.log.LogUtil.CLog;
23 import com.android.tradefed.result.ITestInvocationListener;
24 import com.android.tradefed.result.InputStreamSource;
25 import com.android.tradefed.result.LogDataType;
26 import com.android.tradefed.result.TestDescription;
27 import com.android.tradefed.util.IRunUtil;
28 import com.android.tradefed.util.RunUtil;
29 
30 import com.google.common.annotations.VisibleForTesting;
31 
32 import java.util.List;
33 
34 /**
35  * Listener used to take action such as screenshot, bugreport, logcat collection upon a test failure
36  * when requested.
37  */
38 public class TestFailureListener implements ITestInvocationListener {
39 
40     private List<ITestDevice> mListDevice;
41     private ITestLogger mLogger;
42     // Settings for the whole invocation
43     private boolean mBugReportOnFailure;
44     private boolean mRebootOnFailure;
45 
46     // module specific values
47     private boolean mModuleBugReportOnFailure = true;
48 
TestFailureListener( List<ITestDevice> devices, boolean bugReportOnFailure, boolean rebootOnFailure)49     public TestFailureListener(
50             List<ITestDevice> devices, boolean bugReportOnFailure, boolean rebootOnFailure) {
51         mListDevice = devices;
52         mBugReportOnFailure = bugReportOnFailure;
53         mRebootOnFailure = rebootOnFailure;
54     }
55 
56     /** {@inheritDoc} */
57     @Override
testFailed(TestDescription test, String trace)58     public void testFailed(TestDescription test, String trace) {
59         CLog.i("FailureListener.testFailed %s %b", test.toString(), mBugReportOnFailure);
60         for (ITestDevice device : mListDevice) {
61             captureFailure(device, test);
62         }
63     }
64 
65     /** Capture the appropriate logs for one device for one test failure. */
captureFailure(ITestDevice device, TestDescription test)66     private void captureFailure(ITestDevice device, TestDescription test) {
67         String serial = device.getSerialNumber();
68         if (mBugReportOnFailure && mModuleBugReportOnFailure) {
69             if (!device.logBugreport(
70                     String.format("%s-%s-bugreport", test.toString(), serial), mLogger)) {
71                 CLog.e("Failed to capture bugreport for %s failure on %s.", test, serial);
72             }
73         }
74         if (mRebootOnFailure) {
75             try {
76                 // Rebooting on all failures can hide legitimate issues and platform instabilities,
77                 // therefore only allowed on "user-debug" and "eng" builds.
78                 if ("user".equals(device.getProperty(DeviceProperties.BUILD_TYPE))) {
79                     CLog.e("Reboot-on-failure should only be used during development," +
80                             " this is a\" user\" build device");
81                 } else {
82                     device.reboot();
83                 }
84             } catch (DeviceNotAvailableException e) {
85                 CLog.e(e);
86                 CLog.e("Device %s became unavailable while rebooting", serial);
87             }
88         }
89     }
90 
91     /** Join on all the logcat capturing threads to ensure they terminate. */
join()92     public void join() {
93         // Reset the module config to use the invocation settings by default.
94         mModuleBugReportOnFailure = true;
95     }
96 
97     /**
98      * Forward the log to the logger, do not do it from whitin the #testLog callback as if
99      * TestFailureListener is part of the chain, it will results in an infinite loop.
100      */
testLogForward( String dataName, LogDataType dataType, InputStreamSource dataStream)101     public void testLogForward(
102             String dataName, LogDataType dataType, InputStreamSource dataStream) {
103         mLogger.testLog(dataName, dataType, dataStream);
104     }
105 
106     @Override
testLog(String dataName, LogDataType dataType, InputStreamSource dataStream)107     public void testLog(String dataName, LogDataType dataType, InputStreamSource dataStream) {
108         // Explicitly do nothing on testLog
109     }
110 
111     /**
112      * Get the default {@link IRunUtil} instance
113      */
114     @VisibleForTesting
getRunUtil()115     IRunUtil getRunUtil() {
116         return RunUtil.getDefault();
117     }
118 
119     /**
120      * Allows to override the invocation settings of capture on failure by the module specific
121      * configurations.
122      *
123      * @param bugreportOnFailure true to capture a bugreport on test failure. False otherwise.
124      */
applyModuleConfiguration(boolean bugreportOnFailure)125     public void applyModuleConfiguration(boolean bugreportOnFailure) {
126         mModuleBugReportOnFailure = bugreportOnFailure;
127     }
128 
129     /** Sets where the logs should be saved. */
setLogger(ITestLogger logger)130     public void setLogger(ITestLogger logger) {
131         mLogger = logger;
132     }
133 }
134