forked from jenkinsci/analysis-model
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRfLintParser.java
195 lines (170 loc) · 6.66 KB
/
RfLintParser.java
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
package edu.hm.hafner.analysis.parser;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.CaseUtils;
import edu.hm.hafner.analysis.Issue;
import edu.hm.hafner.analysis.IssueBuilder;
import edu.hm.hafner.analysis.IssueParser;
import edu.hm.hafner.analysis.ParsingCanceledException;
import edu.hm.hafner.analysis.ReaderFactory;
import edu.hm.hafner.analysis.Report;
import edu.hm.hafner.analysis.Severity;
import static edu.hm.hafner.analysis.Categories.*;
/**
* A parser for <a href="http://robotframework.org/">Robot Framework</a>. Parses output from <a
* href="https://github.com/boakley/robotframework-lint">robotframework-lint</a>.
* To generate rflint file use: <pre>
* cmd$ pip install robotframework-lint
* cmd$ rflint path/to/test.robot
* </pre>
*
* @author traitanit
* @author Bassam Khouri
*/
public class RfLintParser extends IssueParser {
/**
* Map of Robot Framework severity to the analysis model severity.
*/
private enum RfLintSeverity {
ERROR(Severity.WARNING_HIGH),
E(ERROR),
WARNING(Severity.WARNING_NORMAL),
W(WARNING),
IGNORE(Severity.WARNING_LOW),
I(IGNORE);
private final Severity severityLevel;
RfLintSeverity(final Severity level) {
this.severityLevel = level;
}
RfLintSeverity(final RfLintSeverity e) {
this(e.severityLevel);
}
public Severity getSeverityLevel() {
return this.severityLevel;
}
/**
* Determines the RfLintSeverity based on the provided character.
*
* @param violationSeverity
* The character presentiting the violation severity
*
* @return An instance of RfLintSeverity matching the character. `WARNING` as the default if the severity
* character is not valid.
*/
public static RfLintSeverity fromCharacter(final char violationSeverity) {
if (EnumUtils.isValidEnum(RfLintSeverity.class, String.valueOf(violationSeverity))) {
return RfLintSeverity.valueOf(String.valueOf(violationSeverity));
}
return WARNING;
}
}
/**
* The possible categories.
*/
private enum RfLintCategory {
SUITE("Suite"),
KEYWORD("Keyword"),
TEST_CASE("Test Case"),
OTHER("Other"),
CUSTOM("Custom");
private final String name;
RfLintCategory(final String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
/**
* The list of the rule names built into rflint.
*/
private enum RfLintRuleName {
DUPLICATE_KEYWORD_NAMES(RfLintCategory.SUITE),
DUPLICATE_TEST_NAMES(RfLintCategory.SUITE),
FILE_TOO_LONG(RfLintCategory.OTHER),
INVALID_TABLE(RfLintCategory.SUITE),
LINE_TOO_LONG(RfLintCategory.OTHER),
PERIOD_IN_SUITE_NAME(RfLintCategory.SUITE),
PERIOD_IN_TEST_NAME(RfLintCategory.TEST_CASE),
REQUIRE_KEYWORD_DOCUMENTATION(RfLintCategory.KEYWORD),
REQUIRE_SUITE_DOCUMENTATION(RfLintCategory.SUITE),
REQUIRE_TEST_DOCUMENTATION(RfLintCategory.SUITE),
TAG_WITH_SPACES(RfLintCategory.TEST_CASE),
TOO_FEW_KEYWORD_STEPS(RfLintCategory.KEYWORD),
TOO_MANY_TEST_CASES(RfLintCategory.SUITE),
TOO_FEW_TEST_STEPS(RfLintCategory.TEST_CASE),
TOO_MANY_TEST_STEPS(RfLintCategory.TEST_CASE),
TRAILING_BLANK_LINES(RfLintCategory.OTHER),
TRAILING_WHITESPACE(RfLintCategory.OTHER),
UNKNOWN(RfLintCategory.CUSTOM);
private final RfLintCategory category;
RfLintRuleName(final RfLintCategory category) {
this.category = category;
}
public RfLintCategory getCategory() {
return this.category;
}
/**
* Determines the RfLintRuleName based on the provided name.
*
* @param name
* the name of the rule
*
* @return An instance of RfLintRuleName matching the name. `UNKNOWN` as the default if the name is not valid.
*/
public static RfLintRuleName fromName(final String name) {
for (RfLintRuleName rule : values()) {
if (CaseUtils.toCamelCase(rule.name(), true, '_').equals(name)) {
return rule;
}
}
return UNKNOWN;
}
}
private static final long serialVersionUID = -7903991158616386226L;
private String fileName = StringUtils.EMPTY;
private static final Pattern WARNING_PATTERN = Pattern.compile(
"(?<severity>[WEI]): (?<lineNumber>\\d+), (?<columnNumber>\\d+): (?<message>.*) \\((?<ruleName>.*)\\)");
private static final Pattern FILE_PATTERN = Pattern.compile("\\+\\s(?<filename>.*)");
@Override
public Report parse(final ReaderFactory readerFactory) {
try (Stream<String> lines = readerFactory.readStream()) {
Report warnings = new Report();
lines.forEach(line -> {
Matcher fileMatcher = FILE_PATTERN.matcher(line);
if (fileMatcher.find()) {
fileName = fileMatcher.group(1);
}
Matcher matcher = WARNING_PATTERN.matcher(line);
if (matcher.find()) {
warnings.add(createIssue(matcher, new IssueBuilder()));
}
if (Thread.interrupted()) {
throw new ParsingCanceledException();
}
});
return warnings;
}
}
private Issue createIssue(final Matcher matcher, final IssueBuilder builder) {
String message = matcher.group("message");
String severityStr = guessCategoryIfEmpty(matcher.group("severity"), message);
Severity priority = RfLintSeverity.fromCharacter(severityStr.charAt(0)).getSeverityLevel();
String ruleName = matcher.group("ruleName");
RfLintCategory category = RfLintRuleName.fromName(ruleName).getCategory();
return builder.setFileName(fileName)
.setPackageName(Objects.toString(Paths.get(fileName).getParent()))
.setLineStart(matcher.group("lineNumber"))
.setColumnStart(matcher.group("columnNumber"))
.setCategory(category.getName())
.setType(ruleName)
.setMessage(message)
.setSeverity(priority)
.build();
}
}