1 /*
2  * Copyright (C) 2020 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.result;
17 
18 import com.android.tradefed.log.LogUtil.CLog;
19 import com.android.tradefed.result.error.ErrorIdentifier;
20 import com.android.tradefed.result.proto.TestRecordProto;
21 
22 import javax.annotation.Nullable;
23 
24 /**
25  * The class describing a failure information in Trade Federation. This class contains the debugging
26  * information and context of the failure that helps understanding the issue.
27  */
28 public class FailureDescription {
29     // The error message generated from the failure
30     private String mErrorMessage;
31     // Optional: The category of the failure
32     private @Nullable TestRecordProto.FailureStatus mFailureStatus =
33             TestRecordProto.FailureStatus.UNSET;
34     // Optional: Context of the action in progress during the failure
35     private @Nullable ActionInProgress mActionInProgress = ActionInProgress.UNSET;
36     // Optional: A free-formed text that help debugging the failure
37     private @Nullable String mDebugHelpMessage = null;
38     // Optional: The exception that triggered the failure
39     private @Nullable Throwable mCause = null;
40     // Whether or not the error is retriable by Tradefed auto-retry. By Default we retry it all.
41     private boolean mRetriable = true;
42     // Test Feature: whether or not to rerun the full run in case of run failure.
43     private boolean mRunFailureReRunAll = true;
44 
45     // Error identifiers
46     // Optional: The error identifier and its code
47     private @Nullable ErrorIdentifier mErrorId = null;
48     // Optional: The class that raised the error
49     private @Nullable String mOrigin = null;
50 
FailureDescription()51     FailureDescription() {}
52 
53     /**
54      * Set the {@link com.android.tradefed.result.proto.TestRecordProto.FailureStatus} associated
55      * with the failure.
56      */
setFailureStatus(TestRecordProto.FailureStatus status)57     public FailureDescription setFailureStatus(TestRecordProto.FailureStatus status) {
58         mFailureStatus = status;
59         if (mErrorId != null && mFailureStatus != mErrorId.status()) {
60             CLog.w(
61                     "Failure: %s, status set to %s, not aligned with errorId: %s (%s)",
62                     this, mFailureStatus, mErrorId, mErrorId.status());
63         }
64         return this;
65     }
66 
67     /** Returns the FailureStatus associated with the failure. Can be null. */
getFailureStatus()68     public @Nullable TestRecordProto.FailureStatus getFailureStatus() {
69         if (TestRecordProto.FailureStatus.UNSET.equals(mFailureStatus)) {
70             return null;
71         }
72         return mFailureStatus;
73     }
74 
75     /** Sets the action in progress during the failure. */
setActionInProgress(ActionInProgress action)76     public FailureDescription setActionInProgress(ActionInProgress action) {
77         mActionInProgress = action;
78         return this;
79     }
80 
81     /** Returns the action in progress during the failure. Can be null. */
getActionInProgress()82     public @Nullable ActionInProgress getActionInProgress() {
83         return mActionInProgress;
84     }
85 
86     /** Sets the debug help message for the failure. */
setDebugHelpMessage(String message)87     public FailureDescription setDebugHelpMessage(String message) {
88         mDebugHelpMessage = message;
89         return this;
90     }
91 
92     /** Returns the debug help message. Can be null. */
getDebugHelpMessage()93     public @Nullable String getDebugHelpMessage() {
94         return mDebugHelpMessage;
95     }
96 
97     /** Sets the exception that caused the failure if any. */
setCause(Throwable cause)98     public FailureDescription setCause(Throwable cause) {
99         mCause = cause;
100         return this;
101     }
102 
103     /** Returns the exception that caused the failure. Can be null. */
getCause()104     public @Nullable Throwable getCause() {
105         return mCause;
106     }
107 
108     /** Sets whether or not the failure is retriable. */
setRetriable(boolean retriable)109     public FailureDescription setRetriable(boolean retriable) {
110         mRetriable = retriable;
111         return this;
112     }
113 
114     /** Returns whether or not the error is retriable or not. */
isRetriable()115     public boolean isRetriable() {
116         return mRetriable;
117     }
118 
119     /** Sets whether or not to rerun the full run when a run failure occurs. */
setFullRerun(boolean fullRerun)120     public FailureDescription setFullRerun(boolean fullRerun) {
121         mRunFailureReRunAll = fullRerun;
122         return this;
123     }
124 
125     /** Returns whether or not we need to retry the full run or not. */
rerunFull()126     public boolean rerunFull() {
127         return mRunFailureReRunAll;
128     }
129 
130     /** Sets the {@link ErrorIdentifier} representing the failure. */
setErrorIdentifier(ErrorIdentifier errorId)131     public FailureDescription setErrorIdentifier(ErrorIdentifier errorId) {
132         mErrorId = errorId;
133         if (getFailureStatus() == null && errorId != null) {
134             mFailureStatus = errorId.status();
135         }
136         return this;
137     }
138 
139     /** Returns the {@link ErrorIdentifier} representing the failure. Can be null. */
getErrorIdentifier()140     public ErrorIdentifier getErrorIdentifier() {
141         return mErrorId;
142     }
143 
144     /** Sets the origin of the error. */
setOrigin(String origin)145     public FailureDescription setOrigin(String origin) {
146         mOrigin = origin;
147         return this;
148     }
149 
150     /** Returns the origin of the error. Can be null. */
getOrigin()151     public String getOrigin() {
152         return mOrigin;
153     }
154 
155     /** Sets the error message. */
setErrorMessage(String errorMessage)156     public void setErrorMessage(String errorMessage) {
157         mErrorMessage = errorMessage;
158     }
159 
160     /** Returns the error message associated with the failure. */
getErrorMessage()161     public String getErrorMessage() {
162         return mErrorMessage;
163     }
164 
165     /**
166      * A formatted way of displaying the error and some details.
167      */
getFormattedErrorMessage()168     public String getFormattedErrorMessage() {
169         StringBuilder s = new StringBuilder();
170         if (mErrorId != null) {
171             s.append("[");
172             s.append(mErrorId.name());
173             s.append("|");
174             s.append(mErrorId.code());
175             s.append("|");
176             s.append(mErrorId.status());
177             s.append("] ");
178         }
179         s.append(this.toString());
180         return s.toString();
181     }
182 
183     @Override
toString()184     public String toString() {
185         // For backward compatibility of result interface, toString falls back to the simple message
186         return mErrorMessage;
187     }
188 
189     /**
190      * Create a {@link FailureDescription} based on the error message generated from the failure.
191      *
192      * @param errorMessage The error message from the failure.
193      * @return the created {@link FailureDescription}
194      */
create(String errorMessage)195     public static FailureDescription create(String errorMessage) {
196         return create(errorMessage, TestRecordProto.FailureStatus.UNSET);
197     }
198 
199     /**
200      * Create a {@link FailureDescription} based on the error message generated from the failure.
201      *
202      * @param errorMessage The error message from the failure.
203      * @param status The status associated with the failure.
204      * @return the created {@link FailureDescription}
205      */
create( String errorMessage, @Nullable TestRecordProto.FailureStatus status)206     public static FailureDescription create(
207             String errorMessage, @Nullable TestRecordProto.FailureStatus status) {
208         FailureDescription info = new FailureDescription();
209         info.mErrorMessage = errorMessage;
210         info.mFailureStatus = status;
211         return info;
212     }
213 
214     @Override
hashCode()215     public int hashCode() {
216         final int prime = 31;
217         int result = 1;
218         result = prime * result + ((mActionInProgress == null) ? 0 : mActionInProgress.hashCode());
219         result = prime * result + ((mDebugHelpMessage == null) ? 0 : mDebugHelpMessage.hashCode());
220         result = prime * result + ((mErrorMessage == null) ? 0 : mErrorMessage.hashCode());
221         result = prime * result + ((mFailureStatus == null) ? 0 : mFailureStatus.hashCode());
222         return result;
223     }
224 
225     @Override
equals(Object obj)226     public boolean equals(Object obj) {
227         if (this == obj) {
228             return true;
229         }
230         if (obj == null) {
231             return false;
232         }
233         if (getClass() != obj.getClass()) {
234             return false;
235         }
236         FailureDescription other = (FailureDescription) obj;
237         if (mActionInProgress != other.mActionInProgress) {
238             return false;
239         }
240         if (mDebugHelpMessage == null) {
241             if (other.mDebugHelpMessage != null) {
242                 return false;
243             }
244         } else if (!mDebugHelpMessage.equals(other.mDebugHelpMessage)) {
245             return false;
246         }
247         if (mErrorMessage == null) {
248             if (other.mErrorMessage != null) {
249                 return false;
250             }
251         } else if (!mErrorMessage.equals(other.mErrorMessage)) {
252             return false;
253         }
254         if (mFailureStatus != other.mFailureStatus) {
255             return false;
256         }
257         return true;
258     }
259 }
260