1 package junitparams.naming; 2 3 import junitparams.internal.TestMethod; 4 import junitparams.internal.Utils; 5 6 import java.util.Arrays; 7 import java.util.HashSet; 8 import java.util.Locale; 9 import java.util.regex.Pattern; 10 11 public class MacroSubstitutionNamingStrategy implements TestCaseNamingStrategy { 12 private static final String MACRO_PATTERN = "\\{[^\\}]{0,50}\\}"; 13 // Pattern that keeps delimiters in split result 14 private static final Pattern MACRO_SPLIT_PATTERN = Pattern.compile(String.format("(?=%s)|(?<=%s)", MACRO_PATTERN, MACRO_PATTERN)); 15 private static final String MACRO_START = "{"; 16 private static final String MACRO_END = "}"; 17 // Android-changed: CTS and AndroidJUnitRunner rely on specific format to test names, changing 18 // them will prevent CTS and AndroidJUnitRunner from working properly; see b/36541809 19 static final String DEFAULT_TEMPLATE = "{method}[{index}]"; 20 private TestMethod method; 21 MacroSubstitutionNamingStrategy(TestMethod testMethod)22 public MacroSubstitutionNamingStrategy(TestMethod testMethod) { 23 this.method = testMethod; 24 } 25 26 // Android-added: allowable test names 27 private static final Pattern ALLOWABLE_TEST_NAMES = Pattern.compile("\\w+(\\[\\d+])?"); 28 29 @Override getTestCaseName(int parametersIndex, Object parameters)30 public String getTestCaseName(int parametersIndex, Object parameters) { 31 TestCaseName testCaseName = method.getAnnotation(TestCaseName.class); 32 33 String template = getTemplate(testCaseName); 34 String builtName = buildNameByTemplate(template, parametersIndex, parameters); 35 36 // Android-changed: CTS and AndroidJUnitRunner rely on specific format to test names, 37 // changing them will prevent CTS and AndroidJUnitRunner from working properly; 38 // see b/36541809 39 if (!ALLOWABLE_TEST_NAMES.matcher(builtName).matches()) { 40 throw new IllegalStateException(String.format( 41 "@TestCaseName(\"%s\") not currently supported as it generated a test name of" 42 + " \"%s\" which will not work properly in CTS, must match \"%s\"", 43 template, builtName, ALLOWABLE_TEST_NAMES)); 44 } 45 46 if (builtName.trim().isEmpty()) { 47 return buildNameByTemplate(DEFAULT_TEMPLATE, parametersIndex, parameters); 48 } else { 49 return builtName; 50 } 51 } 52 getTemplate(TestCaseName testCaseName)53 private String getTemplate(TestCaseName testCaseName) { 54 if (testCaseName != null) { 55 return testCaseName.value(); 56 } 57 58 return DEFAULT_TEMPLATE; 59 } 60 buildNameByTemplate(String template, int parametersIndex, Object parameters)61 private String buildNameByTemplate(String template, int parametersIndex, Object parameters) { 62 StringBuilder nameBuilder = new StringBuilder(); 63 64 String[] parts = MACRO_SPLIT_PATTERN.split(template); 65 66 for (String part : parts) { 67 String transformedPart = transformPart(part, parametersIndex, parameters); 68 nameBuilder.append(transformedPart); 69 } 70 71 return nameBuilder.toString(); 72 } 73 transformPart(String part, int parametersIndex, Object parameters)74 private String transformPart(String part, int parametersIndex, Object parameters) { 75 if (isMacro(part)) { 76 return lookupMacroValue(part, parametersIndex, parameters); 77 } 78 79 return part; 80 } 81 lookupMacroValue(String macro, int parametersIndex, Object parameters)82 private String lookupMacroValue(String macro, int parametersIndex, Object parameters) { 83 String macroKey = getMacroKey(macro); 84 85 switch (Macro.parse(macroKey)) { 86 case INDEX: return String.valueOf(parametersIndex); 87 case PARAMS: return Utils.stringify(parameters); 88 case METHOD: return method.name(); 89 default: return substituteDynamicMacro(macro, macroKey, parameters); 90 } 91 } 92 substituteDynamicMacro(String macro, String macroKey, Object parameters)93 private String substituteDynamicMacro(String macro, String macroKey, Object parameters) { 94 if (isMethodParameterIndex(macroKey)) { 95 int index = parseIndex(macroKey); 96 return Utils.getParameterStringByIndexOrEmpty(parameters, index); 97 } 98 99 return macro; 100 } 101 isMethodParameterIndex(String macroKey)102 private boolean isMethodParameterIndex(String macroKey) { 103 return macroKey.matches("\\d+"); 104 } 105 parseIndex(String macroKey)106 private int parseIndex(String macroKey) { 107 return Integer.parseInt(macroKey); 108 } 109 getMacroKey(String macro)110 private String getMacroKey(String macro) { 111 return macro 112 .substring(MACRO_START.length(), macro.length() - MACRO_END.length()) 113 .toUpperCase(Locale.ENGLISH); 114 } 115 isMacro(String part)116 private boolean isMacro(String part) { 117 return part.startsWith(MACRO_START) && part.endsWith(MACRO_END); 118 } 119 120 private enum Macro { 121 INDEX, 122 PARAMS, 123 METHOD, 124 NONE; 125 parse(String value)126 public static Macro parse(String value) { 127 if (macros.contains(value)) { 128 return Macro.valueOf(value); 129 } else { 130 return Macro.NONE; 131 } 132 } 133 134 private static final HashSet<String> macros = new HashSet<String>(Arrays.asList( 135 Macro.INDEX.toString(), Macro.PARAMS.toString(), Macro.METHOD.toString()) 136 ); 137 } 138 } 139