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.result.error.ErrorIdentifier; 19 import com.android.tradefed.result.proto.TestRecordProto.FailureStatus; 20 21 import java.util.ArrayList; 22 import java.util.Arrays; 23 import java.util.List; 24 25 import javax.annotation.Nullable; 26 27 /** 28 * Collect multiple {@link FailureDescription} in one holder. This can be used to carry all the 29 * failures description when several attempts on the same test case or run are made, each resulting 30 * in a failure. 31 */ 32 public final class MultiFailureDescription extends FailureDescription { 33 34 private List<FailureDescription> mFailures = new ArrayList<>(); 35 MultiFailureDescription(List<FailureDescription> failures)36 public MultiFailureDescription(List<FailureDescription> failures) { 37 super(); 38 addMultiFailures(failures); 39 } 40 MultiFailureDescription(FailureDescription... failures)41 public MultiFailureDescription(FailureDescription... failures) { 42 this(Arrays.asList(failures)); 43 } 44 45 /** 46 * Add another failure to an existing {@link MultiFailureDescription}. 47 * 48 * @param failure The additional failure 49 * @return The current {@link MultiFailureDescription}. 50 */ addFailure(FailureDescription failure)51 public MultiFailureDescription addFailure(FailureDescription failure) { 52 if (failure instanceof MultiFailureDescription) { 53 addMultiFailures(((MultiFailureDescription) failure).getFailures()); 54 } else { 55 mFailures.add(failure); 56 } 57 return this; 58 } 59 60 /** 61 * Returns the list of {@link FailureDescription} tracked by the {@link 62 * MultiFailureDescription}. 63 */ getFailures()64 public List<FailureDescription> getFailures() { 65 return mFailures; 66 } 67 68 @Override getFailureStatus()69 public @Nullable FailureStatus getFailureStatus() { 70 if (mFailures.isEmpty()) { 71 return null; 72 } 73 // Default to the first reported failure 74 return mFailures.get(0).getFailureStatus(); 75 } 76 77 @Override getErrorMessage()78 public String getErrorMessage() { 79 if (mFailures.isEmpty()) { 80 return null; 81 } 82 // Default to the first reported failure 83 return toString(); 84 } 85 86 @Override getErrorIdentifier()87 public ErrorIdentifier getErrorIdentifier() { 88 if (mFailures.isEmpty()) { 89 return null; 90 } 91 // Default to the first reported failure 92 return mFailures.get(0).getErrorIdentifier(); 93 } 94 95 @Override getOrigin()96 public String getOrigin() { 97 if (mFailures.isEmpty()) { 98 return null; 99 } 100 // Default to the first reported failure 101 return mFailures.get(0).getOrigin(); 102 } 103 104 @Override isRetriable()105 public boolean isRetriable() { 106 for (FailureDescription desc : mFailures) { 107 if (desc.isRetriable()) { 108 return true; 109 } 110 } 111 // If none of the sub-failures are retriable, don't retry. 112 return false; 113 } 114 115 @Override toString()116 public String toString() { 117 // Fallback to Single failure type 118 if (mFailures.size() == 1) { 119 return mFailures.get(0).toString(); 120 } 121 StringBuilder sb = 122 new StringBuilder(String.format("There were %d failures:", mFailures.size())); 123 for (FailureDescription f : mFailures) { 124 sb.append(String.format("\n %s", f.toString())); 125 } 126 return sb.toString(); 127 } 128 129 @Override equals(Object obj)130 public boolean equals(Object obj) { 131 if (this == obj) return true; 132 if (obj == null) return false; 133 if (getClass() != obj.getClass()) return false; 134 MultiFailureDescription other = (MultiFailureDescription) obj; 135 if (other.mFailures.size() != this.mFailures.size()) { 136 return false; 137 } 138 for (int i = 0; i < this.mFailures.size(); i++) { 139 if (!this.mFailures.get(i).equals(other.mFailures.get(i))) { 140 return false; 141 } 142 } 143 return true; 144 } 145 146 /** Un-nest all the sub-MultiFailureDescription for ease of tracking. */ addMultiFailures(List<FailureDescription> failures)147 private void addMultiFailures(List<FailureDescription> failures) { 148 for (FailureDescription failure : failures) { 149 if (failure instanceof MultiFailureDescription) { 150 addMultiFailures(((MultiFailureDescription) failure).getFailures()); 151 } else { 152 mFailures.add(failure); 153 } 154 } 155 } 156 } 157