package junitparams.naming; import junitparams.internal.TestMethod; import junitparams.internal.Utils; import java.util.Arrays; import java.util.HashSet; import java.util.Locale; import java.util.regex.Pattern; public class MacroSubstitutionNamingStrategy implements TestCaseNamingStrategy { private static final String MACRO_PATTERN = "\\{[^\\}]{0,50}\\}"; // Pattern that keeps delimiters in split result private static final Pattern MACRO_SPLIT_PATTERN = Pattern.compile(String.format("(?=%s)|(?<=%s)", MACRO_PATTERN, MACRO_PATTERN)); private static final String MACRO_START = "{"; private static final String MACRO_END = "}"; // Android-changed: CTS and AndroidJUnitRunner rely on specific format to test names, changing // them will prevent CTS and AndroidJUnitRunner from working properly; see b/36541809 static final String DEFAULT_TEMPLATE = "{method}[{index}]"; private TestMethod method; public MacroSubstitutionNamingStrategy(TestMethod testMethod) { this.method = testMethod; } @Override public String getTestCaseName(int parametersIndex, Object parameters) { TestCaseName testCaseName = method.getAnnotation(TestCaseName.class); String template = getTemplate(testCaseName); String builtName = buildNameByTemplate(template, parametersIndex, parameters); if (builtName.trim().isEmpty()) { return buildNameByTemplate(DEFAULT_TEMPLATE, parametersIndex, parameters); } else { return builtName; } } private String getTemplate(TestCaseName testCaseName) { if (testCaseName != null) { // Android-changed: CTS and AndroidJUnitRunner rely on specific format to test names, // changing them will prevent CTS and AndroidJUnitRunner from working properly; // see b/36541809 throw new IllegalStateException( "@TestCaseName not currently supported as it breaks running tests in CTS"); // return testCaseName.value(); } return DEFAULT_TEMPLATE; } private String buildNameByTemplate(String template, int parametersIndex, Object parameters) { StringBuilder nameBuilder = new StringBuilder(); String[] parts = MACRO_SPLIT_PATTERN.split(template); for (String part : parts) { String transformedPart = transformPart(part, parametersIndex, parameters); nameBuilder.append(transformedPart); } return nameBuilder.toString(); } private String transformPart(String part, int parametersIndex, Object parameters) { if (isMacro(part)) { return lookupMacroValue(part, parametersIndex, parameters); } return part; } private String lookupMacroValue(String macro, int parametersIndex, Object parameters) { String macroKey = getMacroKey(macro); switch (Macro.parse(macroKey)) { case INDEX: return String.valueOf(parametersIndex); case PARAMS: return Utils.stringify(parameters); case METHOD: return method.name(); default: return substituteDynamicMacro(macro, macroKey, parameters); } } private String substituteDynamicMacro(String macro, String macroKey, Object parameters) { if (isMethodParameterIndex(macroKey)) { int index = parseIndex(macroKey); return Utils.getParameterStringByIndexOrEmpty(parameters, index); } return macro; } private boolean isMethodParameterIndex(String macroKey) { return macroKey.matches("\\d+"); } private int parseIndex(String macroKey) { return Integer.parseInt(macroKey); } private String getMacroKey(String macro) { return macro .substring(MACRO_START.length(), macro.length() - MACRO_END.length()) .toUpperCase(Locale.ENGLISH); } private boolean isMacro(String part) { return part.startsWith(MACRO_START) && part.endsWith(MACRO_END); } private enum Macro { INDEX, PARAMS, METHOD, NONE; public static Macro parse(String value) { if (macros.contains(value)) { return Macro.valueOf(value); } else { return Macro.NONE; } } private static final HashSet<String> macros = new HashSet<String>(Arrays.asList( Macro.INDEX.toString(), Macro.PARAMS.toString(), Macro.METHOD.toString()) ); } }