• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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