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 com.android.tradefed.testtype.suite; 17 18 import com.android.tradefed.error.HarnessRuntimeException; 19 import com.android.tradefed.result.error.InfraErrorIdentifier; 20 import com.android.tradefed.util.AbiUtils; 21 22 import java.util.regex.Matcher; 23 import java.util.regex.Pattern; 24 25 /** Represents a filter for including and excluding tests. */ 26 public class SuiteTestFilter { 27 28 private final Integer mShardIndex; 29 private final String mAbi; 30 private final String mName; 31 private final String mTest; 32 33 private static final Pattern PARAMETERIZED_TEST_REGEX = Pattern.compile("(.*)?\\[(.*)\\]$"); 34 35 /** 36 * Builds a new {@link SuiteTestFilter} from the given string. Filters can be in one of four 37 * forms, the instance will be initialized as; -"name" -> abi = null, name = "name", test = null 38 * -"name" "test..." -> abi = null, name = "name", test = "test..." -"abi" "name" -> abi = 39 * "abi", name = "name", test = null -"abi" "name" "test..." -> abi = "abi", name = "name", test 40 * = "test..." 41 * 42 * <p>Test identifier can contain multiple parts, eg parameterized tests. 43 * 44 * @param filter the filter to parse 45 * @return the {@link SuiteTestFilter} 46 */ createFrom(String filter)47 public static SuiteTestFilter createFrom(String filter) { 48 if (filter.isEmpty()) { 49 throw new HarnessRuntimeException( 50 "Filter was empty", InfraErrorIdentifier.OPTION_CONFIGURATION_ERROR); 51 } 52 String[] parts = filter.split(" +"); 53 Integer shardIndex = null; 54 String abi = null; 55 String name = null; 56 String test = null; 57 // Either: 58 // <name> 59 // <name> <test> 60 // <abi> <name> 61 // <abi> <name> <test> 62 if (parts.length == 1) { 63 name = parts[0]; 64 } else { 65 int index = 0; 66 if (parts[index].startsWith("shard_")) { 67 shardIndex = Integer.parseInt(parts[index].substring("shard_".length())); 68 index++; 69 } else { 70 try { 71 shardIndex = Integer.parseInt(parts[index]); 72 index++; 73 } catch (NumberFormatException e) { 74 // Ignore 75 } 76 } 77 if (AbiUtils.isAbiSupportedByCompatibility(parts[index])) { 78 abi = parts[index]; 79 index++; 80 } 81 name = parts[index]; 82 index++; 83 parts = filter.split(" +", index + 1); 84 if (parts.length > index) { 85 test = parts[index]; 86 } 87 } 88 return new SuiteTestFilter(shardIndex, abi, name, test); 89 } 90 91 /** 92 * Creates a new {@link SuiteTestFilter} from the given parts. 93 * 94 * @param abi The ABI must be supported {@link AbiUtils#isAbiSupportedByCompatibility(String)} 95 * @param name The module's name 96 * @param test The test's identifier eg <package>.<class>#<method> 97 */ SuiteTestFilter(String abi, String name, String test)98 public SuiteTestFilter(String abi, String name, String test) { 99 this(null, abi, name, test); 100 } 101 102 /** 103 * Creates a new {@link SuiteTestFilter} from the given parts. 104 * 105 * @param abi The ABI must be supported {@link AbiUtils#isAbiSupportedByCompatibility(String)} 106 * @param name The module's name 107 * @param test The test's identifier eg <package>.<class>#<method> 108 */ SuiteTestFilter(Integer shardIndex, String abi, String name, String test)109 public SuiteTestFilter(Integer shardIndex, String abi, String name, String test) { 110 mShardIndex = shardIndex; 111 mAbi = abi; 112 mName = name; 113 mTest = test; 114 } 115 116 /** 117 * Returns a String representation of this filter. This function is the inverse of {@link 118 * SuiteTestFilter#createFrom(String)}. 119 * 120 * <p>For a valid filter f; 121 * 122 * <pre>{@code 123 * new TestFilter(f).toString().equals(f) 124 * }</pre> 125 */ 126 @Override toString()127 public String toString() { 128 StringBuilder sb = new StringBuilder(); 129 if (mShardIndex != null) { 130 sb.append(mShardIndex.toString()); 131 sb.append(" "); 132 } 133 if (mAbi != null) { 134 sb.append(mAbi.trim()); 135 sb.append(" "); 136 } 137 if (mName != null) { 138 sb.append(mName.trim()); 139 } 140 if (mTest != null) { 141 sb.append(" "); 142 sb.append(mTest.trim()); 143 } 144 return sb.toString(); 145 } 146 147 /** Returns the shard index of the test, or null if not specified. */ getShardIndex()148 public Integer getShardIndex() { 149 return mShardIndex; 150 } 151 152 /** @return the abi of this filter, or null if not specified. */ getAbi()153 public String getAbi() { 154 return mAbi; 155 } 156 157 /** @return the module name of this filter, or null if not specified. */ getName()158 public String getName() { 159 return mName; 160 } 161 getModuleId()162 public String getModuleId() { 163 StringBuilder sb = new StringBuilder(); 164 if (mAbi != null) { 165 sb.append(mAbi.trim()); 166 sb.append(" "); 167 } 168 if (mName != null) { 169 sb.append(mName.trim()); 170 } 171 return sb.toString(); 172 } 173 174 /** 175 * Returns the base name of the module without any parameterization. If not parameterized, it 176 * will return {@link #getName()}; 177 */ getBaseName()178 public String getBaseName() { 179 // If the module looks parameterized, return the base non-parameterized name. 180 Matcher m = PARAMETERIZED_TEST_REGEX.matcher(mName); 181 if (m.find()) { 182 return m.group(1); 183 } 184 return mName; 185 } 186 187 /** 188 * If the module is parameterized, returns the parameter value. Null if not parameterized. 189 */ getParameterName()190 public String getParameterName() { 191 // If the module looks parameterized, return the parameter name. 192 Matcher m = PARAMETERIZED_TEST_REGEX.matcher(mName); 193 if (m.find()) { 194 return m.group(2); 195 } 196 return null; 197 } 198 199 /** @return the test identifier of this filter, or null if not specified. */ getTest()200 public String getTest() { 201 return mTest; 202 } 203 204 @Override hashCode()205 public int hashCode() { 206 final int prime = 31; 207 int result = 1; 208 result = prime * result + ((mAbi == null) ? 0 : mAbi.hashCode()); 209 result = prime * result + ((mName == null) ? 0 : mName.hashCode()); 210 result = prime * result + ((mShardIndex == null) ? 0 : mShardIndex.hashCode()); 211 result = prime * result + ((mTest == null) ? 0 : mTest.hashCode()); 212 return result; 213 } 214 215 @Override equals(Object obj)216 public boolean equals(Object obj) { 217 if (this == obj) { 218 return true; 219 } 220 if (obj == null) { 221 return false; 222 } 223 if (getClass() != obj.getClass()) { 224 return false; 225 } 226 SuiteTestFilter other = (SuiteTestFilter) obj; 227 if (mAbi == null) { 228 if (other.mAbi != null) { 229 return false; 230 } 231 } else if (!mAbi.equals(other.mAbi)) { 232 return false; 233 } 234 if (mName == null) { 235 if (other.mName != null) { 236 return false; 237 } 238 } else if (!mName.equals(other.mName)) { 239 return false; 240 } 241 if (mShardIndex == null) { 242 if (other.mShardIndex != null) { 243 return false; 244 } 245 } else if (!mShardIndex.equals(other.mShardIndex)) { 246 return false; 247 } 248 if (mTest == null) { 249 if (other.mTest != null) { 250 return false; 251 } 252 } else if (!mTest.equals(other.mTest)) { 253 return false; 254 } 255 return true; 256 } 257 } 258