-
Notifications
You must be signed in to change notification settings - Fork 405
Expand file tree
/
Copy pathReportParser.java
More file actions
executable file
·183 lines (163 loc) · 7.29 KB
/
ReportParser.java
File metadata and controls
executable file
·183 lines (163 loc) · 7.29 KB
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
package net.masterthought.cucumber;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.collect.Lists;
import net.masterthought.cucumber.json.Element;
import net.masterthought.cucumber.json.Feature;
import net.masterthought.cucumber.reducers.ReducingMethod;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
/**
* @author Damian Szczepanik (damianszczepanik@github)
*/
public class ReportParser {
private static final Logger LOG = Logger.getLogger(ReportParser.class.getName());
private final ObjectMapper mapper = new ObjectMapper();
private final Configuration configuration;
public ReportParser(Configuration configuration) {
this.configuration = configuration;
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// this prevents printing eg. 2.20 as 2.2
mapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
mapper.registerModule(new JavaTimeModule());
// pass configuration to deserializers
InjectableValues values = new InjectableValues.Std().addValue(Configuration.class, configuration);
mapper.setInjectableValues(values);
}
/**
* Parsed passed files and extracts features files.
*
* @param jsonFiles
* JSON files to read
* @return array of parsed features
*/
public List<Feature> parseJsonFiles(List<String> jsonFiles) {
if (jsonFiles.isEmpty()) {
throw new ValidationException("None report file was added!");
}
List<Feature> featureResults = new ArrayList<>();
for (int i = 0; i < jsonFiles.size(); i++) {
String jsonFile = jsonFiles.get(i);
// if file is empty (is not valid JSON report), check if should be skipped or not
if (new File(jsonFile).length() == 0
&& configuration.containsReducingMethod(ReducingMethod.SKIP_EMPTY_JSON_FILES)) {
continue;
}
Feature[] features = parseForFeature(jsonFile);
if (configuration.containsReducingMethod(ReducingMethod.KEEP_ONLY_LATEST_SCENARIO_RUNS)) {
keepOnlyLatestScenarioRuns(features);
}
LOG.log(Level.INFO, String.format("File '%s' contains %d features", jsonFile, features.length));
featureResults.addAll(Arrays.asList(features));
}
// report that has no features seems to be not valid
if (featureResults.isEmpty()) {
throw new ValidationException("Passed files have no features!");
}
return featureResults;
}
/**
* If the JSON file has the same scenarios run multiple times, keep only the
* latest scenario's run.
*/
private void keepOnlyLatestScenarioRuns(Feature[] features) {
for (Feature feature : features) {
Element[] elements = feature.getElements();
List<Element> elementList = Lists.newArrayList(elements);
ListIterator<Element> li = elementList.listIterator(elementList.size());
Optional<Element> lastElement = Optional.empty();
int numRemoved = 0;
while (li.hasPrevious()) {
Element element = li.previous();
if (lastElement.isPresent() && element.getId().equals(lastElement.get().getId())) {
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, String.format("Reducing method KEEP_ONLY_EARLIEST_SCENARIO_RUNS is removing an earlier test result of scenario %s", feature.getName()));
}
li.remove();
++numRemoved;
} else {
addRetryNumberToElmIfNeeded(lastElement, numRemoved);
lastElement = Optional.of(element);
numRemoved = 0;
}
}
addRetryNumberToElmIfNeeded(lastElement, numRemoved);
feature.setElements(elementList.toArray(new Element[0]));
}
}
private void addRetryNumberToElmIfNeeded(Optional<Element> lastElement, int numRemoved) {
if (lastElement.isPresent() && numRemoved > 0) {
lastElement.get().appendToName(" [Retry count " + (numRemoved + 1) + "]");
}
}
/**
* Reads passed file and returns parsed features.
*
* @param jsonFile
* JSON file that should be read
* @return array of parsed features
*/
private Feature[] parseForFeature(String jsonFile) {
try (Reader reader = new InputStreamReader(new FileInputStream(jsonFile), StandardCharsets.UTF_8)) {
Feature[] features = mapper.readValue(reader, Feature[].class);
if (ArrayUtils.isEmpty(features)) {
LOG.log(Level.INFO, "File '{0}' does not contain features", jsonFile);
}
return features;
} catch (JsonMappingException e) {
throw new ValidationException(String.format("File '%s' is not proper Cucumber report!", jsonFile), e);
} catch (IOException e) {
// IO problem - stop generating and re-throw the problem
throw new ValidationException(e);
}
}
/**
* Parses passed properties files for classifications. These classifications within each file get added to the overview-features page as metadata.
* File and metadata order within the individual files are preserved when classifications are added.
*
* @param propertiesFiles
* property files to read
*/
public void parseClassificationsFiles(List<String> propertiesFiles) {
if (isNotEmpty(propertiesFiles)) {
for (String propertyFile : propertiesFiles) {
if (StringUtils.isNotEmpty(propertyFile)) {
processClassificationFile(propertyFile);
}
}
}
}
private void processClassificationFile(String file) {
try {
PropertiesConfiguration config = new PropertiesConfiguration(file);
Iterator<String> keys = config.getKeys();
while (keys.hasNext()) {
String key = keys.next();
String value = config.getProperty(key).toString();
this.configuration.addClassifications(key, value);
}
} catch (ConfigurationException e) {
throw new ValidationException(String.format("File '%s' doesn't exist or the properties file is invalid!", file), e);
}
}
}