aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/junitparams/naming/MacroSubstitutionNamingStrategy.java
blob: 3386e5125ea184aa4f70b84151da6f85d2c1557f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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;
    }

    // Android-added: allowable test names
    private static final Pattern ALLOWABLE_TEST_NAMES = Pattern.compile("\\w+(\\[\\d+])?");

    @Override
    public String getTestCaseName(int parametersIndex, Object parameters) {
        TestCaseName testCaseName = method.getAnnotation(TestCaseName.class);

        String template = getTemplate(testCaseName);
        String builtName = buildNameByTemplate(template, parametersIndex, parameters);

        // 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
        if (!ALLOWABLE_TEST_NAMES.matcher(builtName).matches()) {
            throw new IllegalStateException(String.format(
                    "@TestCaseName(\"%s\") not currently supported as it generated a test name of"
                            + " \"%s\" which will not work properly in CTS, must match \"%s\"",
                            template, builtName, ALLOWABLE_TEST_NAMES));
        }

        if (builtName.trim().isEmpty()) {
            return buildNameByTemplate(DEFAULT_TEMPLATE, parametersIndex, parameters);
        } else {
            return builtName;
        }
    }

    private String getTemplate(TestCaseName testCaseName) {
        if (testCaseName != null) {
            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())
        );
    }
}