##### Coverage Report - org.jbehave.core.reporters.TemplateableViewGenerator

Classes in this File | Line Coverage | Branch Coverage | Complexity
TemplateableViewGenerator |
94% | 112/119
88% | 23/26
| 1.894736842105263;1.895
TemplateableViewGenerator$1 |
100% | 6/6
100% | 8/8
| 1.894736842105263;1.895
TemplateableViewGenerator$Report |
91% | 31/34
83% | 5/6
| 1.894736842105263;1.895
TemplateableViewGenerator$ReportCreationFailed |
100% | 2/2
N/A | N/A
| 1.894736842105263;1.895
TemplateableViewGenerator$ReportsTable |
90% | 30/33
87% | 7/8
| 1.894736842105263;1.895
TemplateableViewGenerator$TimeFormatter |
100% | 12/12
N/A | N/A
| 1.894736842105263;1.895
TemplateableViewGenerator$ViewGenerationFailedForTemplate |
100% | 2/2
N/A | N/A
| 1.894736842105263;1.895

1 |  |
     package org.jbehave.core.reporters;
2 |  |
3 |  |
     import static java.util.Arrays.asList;
4 |  |
5 |  |
     import java.io.File;
6 |  |
     import java.io.FileInputStream;
7 |  |
     import java.io.FileReader;
8 |  |
     import java.io.FileWriter;
9 |  |
     import java.io.FilenameFilter;
10 |  |
     import java.io.InputStream;
11 |  |
     import java.io.Writer;
12 |  |
     import java.util.ArrayList;
13 |  |
     import java.util.Collection;
14 |  |
     import java.util.Collections;
15 |  |
     import java.util.Date;
16 |  |
     import java.util.Enumeration;
17 |  |
     import java.util.Formatter;
18 |  |
     import java.util.HashMap;
19 |  |
     import java.util.List;
20 |  |
     import java.util.Map;
21 |  |
     import java.util.Properties;
22 |  |
     import java.util.SortedMap;
23 |  |
     import java.util.TreeMap;
24 |  |
25 |  |
     import org.apache.commons.io.FilenameUtils;
26 |  |
     import org.apache.commons.lang.builder.CompareToBuilder;
27 |  |
     import org.apache.commons.lang.builder.ToStringBuilder;
28 |  |
     import org.apache.commons.lang.builder.ToStringStyle;
29 |  |
     import org.jbehave.core.io.IOUtils;
30 |  |
     import org.jbehave.core.io.StoryNameResolver;
31 |  |
     import org.jbehave.core.model.StoryLanes;
32 |  |
     import org.jbehave.core.model.StoryMaps;
33 |  |
34 |  |
     /**
35 |  |
      * <p>
36 |  |
      * {@link ViewGenerator}, which uses the configured {@link TemplateProcessor} to
37 |  |
      * generate the views from templates. The default view properties are
38 |  |
      * overridable via the method {@link Properties} parameter. To override, specify
39 |  |
      * the path to the new template under the appropriate key:
40 |  |
      *
41 |  |
      * <pre>
42 |  |
      * &quot;views&quot;: the path to global view template, including reports and maps views
43 |  |
      * &quot;maps&quot;: the path to the maps view template
44 |  |
      * &quot;reports&quot;: the path to the reports view template
45 |  |
      * &quot;decorated&quot;: the path to the template to generate a decorated (i.e. styled) single report
46 |  |
      * &quot;nonDecorated&quot;: the path to the template to generated a non decorated single report
47 |  |
      * </pre>
48 |  |
      * <p>
49 |  |
      * The view generator provides the following resources:
50 |  |
      *
51 |  |
      * <pre>
52 |  |
      * &quot;decorateNonHtml&quot; = &quot;true&quot;
53 |  |
      * &quot;defaultFormats&quot; = &quot;stats&quot;
54 |  |
      * &quot;viewDirectory&quot; = &quot;view&quot;
55 |  |
      * </pre>
56 |  |
      *
57 |  |
      * </p>
58 |  |
      *
59 |  |
      * @author Mauro Talevi
60 |  |
      */
61 |  |
     public class TemplateableViewGenerator implements ViewGenerator {
62 |  |
63 |  |
         private final StoryNameResolver nameResolver;
64 |  |
         private final TemplateProcessor processor;
65 |  |
         private Properties viewProperties;
66 | 1005 |
         private List<Report> reports = new ArrayList<Report>();
67 |  |
68 | 1005 |
         public TemplateableViewGenerator(StoryNameResolver nameResolver, TemplateProcessor processor) {
69 | 1005 |
             this.nameResolver = nameResolver;
70 | 1005 |
             this.processor = processor;
71 | 1005 |
         }
72 |  |
73 |  |
         public Properties defaultViewProperties() {
74 | 0 |
             Properties properties = new Properties();
75 | 0 |
             properties.setProperty("decorateNonHtml", "true");
76 | 0 |
             properties.setProperty("defaultFormats", "stats");
77 | 0 |
             properties.setProperty("viewDirectory", "view");
78 | 0 |
             return properties;
79 |  |
         }
80 |  |
81 |  |
         private Properties mergeWithDefault(Properties properties) {
82 | 14 |
             Properties merged = defaultViewProperties();
83 | 14 |
             merged.putAll(properties);
84 | 14 |
             return merged;
85 |  |
         }
86 |  |
87 |  |
         private void generateViewsIndex(File outputDirectory) {
88 | 13 |
             String outputName = templateResource("viewDirectory") + "/index.html";
89 | 13 |
             String viewsTemplate = templateResource("views");
90 | 13 |
             Map<String, Object> dataModel = newDataModel();
91 | 13 |
             dataModel.put("date", new Date());
92 | 13 |
             write(outputDirectory, outputName, viewsTemplate, dataModel);
93 | 13 |
         }
94 |  |
95 |  |
         public void generateMapsView(File outputDirectory, StoryMaps storyMaps, Properties viewProperties) {
96 | 1 |
             this.viewProperties = mergeWithDefault(viewProperties);
97 | 1 |
             String outputName = templateResource("viewDirectory") + "/maps.html";
98 | 1 |
             String mapsTemplate = templateResource("maps");
99 | 1 |
             Map<String, Object> dataModel = newDataModel();
100 | 1 |
             dataModel.put("storyLanes", new StoryLanes(storyMaps, nameResolver));
101 | 1 |
             dataModel.put("date", new Date());
102 | 1 |
             write(outputDirectory, outputName, mapsTemplate, dataModel);
103 | 1 |
             generateViewsIndex(outputDirectory);
104 | 1 |
         }
105 |  |
106 |  |
         public void generateReportsView(File outputDirectory, List<String> formats, Properties viewProperties) {
107 | 13 |
             this.viewProperties = mergeWithDefault(viewProperties);
108 | 13 |
             String outputName = templateResource("viewDirectory") + "/reports.html";
109 | 13 |
             String reportsTemplate = templateResource("reports");
110 | 13 |
             List<String> mergedFormats = mergeFormatsWithDefaults(formats);
111 | 13 |
             reports = createReports(readReportFiles(outputDirectory, outputName, mergedFormats));
112 | 13 |
             Map<String, Object> dataModel = newDataModel();
113 | 13 |
             dataModel.put("reportsTable", new ReportsTable(reports, nameResolver));
114 | 13 |
             dataModel.put("date", new Date());
115 | 13 |
             dataModel.put("timeFormatter", new TimeFormatter());
116 | 13 |
             write(outputDirectory, outputName, reportsTemplate, dataModel);
117 | 12 |
             generateViewsIndex(outputDirectory);
118 | 12 |
         }
119 |  |
120 |  |
         public ReportsCount getReportsCount() {
121 | 10 |
             int stories = countStoriesWithScenarios();
122 | 10 |
             int storiesNotAllowed = count("notAllowed", reports);
123 | 10 |
             int storiesPending = count("pending", reports);
124 | 10 |
             int scenarios = count("scenarios", reports);
125 | 10 |
             int scenariosFailed = count("scenariosFailed", reports);
126 | 10 |
             int scenariosNotAllowed = count("scenariosNotAllowed", reports);
127 | 10 |
             int scenariosPending = count("scenariosPending", reports);
128 | 10 |
             int stepsFailed = count("stepsFailed", reports);
129 | 10 |
             return new ReportsCount(stories, storiesNotAllowed, storiesPending, scenarios, scenariosFailed,
130 |  |
                     scenariosNotAllowed, scenariosPending, stepsFailed);
131 |  |
         }
132 |  |
133 |  |
         private int countStoriesWithScenarios(){
134 | 10 |
             int storyCount = 0;
135 | 10 |
             for (Report report : reports){
136 | 14 |
                 Map<String, Integer> stats = report.getStats();
137 | 14 |
                 if (stats.containsKey("scenarios")){
138 | 0 |
                     if (stats.get("scenarios") > 0)
139 | 0 |
                     storyCount++;
140 |  |
                 }
141 | 14 |
             }
142 | 10 |
             return storyCount;
143 |  |
         }
144 |  |
145 |  |
         int count(String event, Collection<Report> reports) {
146 | 72 |
             int count = 0;
147 | 72 |
             for (Report report : reports) {
148 | 100 |
                 Properties stats = report.asProperties("stats");
149 | 100 |
                 if (stats.containsKey(event)) {
150 | 1 |
                     count = count + Integer.parseInt((String) stats.get(event));
151 |  |
                 }
152 | 100 |
             }
153 | 72 |
             return count;
154 |  |
         }
155 |  |
156 |  |
         private List<String> mergeFormatsWithDefaults(List<String> formats) {
157 | 13 |
             List<String> merged = new ArrayList<String>();
158 | 13 |
             merged.addAll(asList(templateResource("defaultFormats").split(",")));
159 | 13 |
             merged.addAll(formats);
160 | 13 |
             return merged;
161 |  |
         }
162 |  |
163 |  |
         List<Report> createReports(Map<String, List<File>> reportFiles) {
164 |  |
             try {
165 | 14 |
                 String decoratedTemplate = templateResource("decorated");
166 | 13 |
                 String nonDecoratedTemplate = templateResource("nonDecorated");
167 | 13 |
                 String viewDirectory = templateResource("viewDirectory");
168 | 13 |
                 boolean decorateNonHtml = Boolean.valueOf(templateResource("decorateNonHtml"));
169 | 13 |
                 List<Report> reports = new ArrayList<Report>();
170 | 13 |
                 for (String name : reportFiles.keySet()) {
171 | 17 |
                     Map<String, File> filesByFormat = new HashMap<String, File>();
172 | 17 |
                     for (File file : reportFiles.get(name)) {
173 | 25 |
                         String fileName = file.getName();
174 | 25 |
                         String format = FilenameUtils.getExtension(fileName);
175 | 25 |
                         Map<String, Object> dataModel = newDataModel();
176 | 25 |
                         dataModel.put("name", name);
177 | 25 |
                         dataModel.put("body", IOUtils.toString(new FileReader(file), true));
178 | 25 |
                         dataModel.put("format", format);
179 | 25 |
                         File outputDirectory = file.getParentFile();
180 | 25 |
                         String outputName = viewDirectory + "/" + fileName;
181 | 25 |
                         String template = decoratedTemplate;
182 | 25 |
                         if (!format.equals("html")) {
183 | 17 |
                             if (decorateNonHtml) {
184 | 16 |
                                 outputName = outputName + ".html";
185 |  |
                             } else {
186 | 1 |
                                 template = nonDecoratedTemplate;
187 |  |
                             }
188 |  |
                         }
189 | 25 |
                         File written = write(outputDirectory, outputName, template, dataModel);
190 | 25 |
                         filesByFormat.put(format, written);
191 | 25 |
                     }
192 | 17 |
                     reports.add(new Report(name, filesByFormat));
193 | 17 |
                 }
194 | 13 |
                 return reports;
195 | 1 |
             } catch (Exception e) {
196 | 1 |
                 throw new ReportCreationFailed(reportFiles, e);
197 |  |
             }
198 |  |
         }
199 |  |
200 |  |
         SortedMap<String, List<File>> readReportFiles(File outputDirectory, final String outputName,
201 |  |
                 final List<String> formats) {
202 | 16 |
             SortedMap<String, List<File>> reportFiles = new TreeMap<String, List<File>>();
203 | 16 |
             if (outputDirectory == null || !outputDirectory.exists()) {
204 | 2 |
                 return reportFiles;
205 |  |
             }
206 | 14 |
             String[] fileNames = outputDirectory.list(new FilenameFilter() {
207 |  |
                 public boolean accept(File dir, String name) {
208 | 189 |
                     return !name.equals(outputName) && hasFormats(name, formats);
209 |  |
                 }
210 |  |
211 |  |
                 private boolean hasFormats(String name, List<String> formats) {
212 | 188 |
                     for (String format : formats) {
213 | 265 |
                         if (name.endsWith(format)) {
214 | 29 |
                             return true;
215 |  |
                         }
216 |  |
                     }
217 | 159 |
                     return false;
218 |  |
                 }
219 |  |
             });
220 | 43 |
             for (String fileName : fileNames) {
221 | 29 |
                 String name = FilenameUtils.getBaseName(fileName);
222 | 29 |
                 List<File> filesByName = reportFiles.get(name);
223 | 29 |
                 if (filesByName == null) {
224 | 19 |
                     filesByName = new ArrayList<File>();
225 | 19 |
                     reportFiles.put(name, filesByName);
226 |  |
                 }
227 | 29 |
                 filesByName.add(new File(outputDirectory, fileName));
228 |  |
             }
229 | 14 |
             return reportFiles;
230 |  |
         }
231 |  |
232 |  |
         private File write(File outputDirectory, String outputName, String resource, Map<String, Object> dataModel) {
233 |  |
             try {
234 | 52 |
                 File file = new File(outputDirectory, outputName);
235 | 52 |
                 file.getParentFile().mkdirs();
236 | 52 |
                 Writer writer = new FileWriter(file);
237 | 52 |
                 processor.process(resource, dataModel, writer);
238 | 51 |
                 writer.close();
239 | 51 |
                 return file;
240 | 1 |
             } catch (Exception e) {
241 | 1 |
                 throw new ViewGenerationFailedForTemplate(resource, e);
242 |  |
             }
243 |  |
         }
244 |  |
245 |  |
         private String templateResource(String format) {
246 | 120 |
             return viewProperties.getProperty(format);
247 |  |
         }
248 |  |
249 |  |
         private Map<String, Object> newDataModel() {
250 | 52 |
             return new HashMap<String, Object>();
251 |  |
         }
252 |  |
253 |  |
         @SuppressWarnings("serial")
254 |  |
         public static class ReportCreationFailed extends RuntimeException {
255 |  |
256 |  |
             public ReportCreationFailed(Map<String, List<File>> reportFiles, Exception cause) {
257 | 1 |
                 super("Report creation failed from file " + reportFiles, cause);
258 | 1 |
             }
259 |  |
         }
260 |  |
261 |  |
         @SuppressWarnings("serial")
262 |  |
         public static class ViewGenerationFailedForTemplate extends RuntimeException {
263 |  |
264 |  |
             public ViewGenerationFailedForTemplate(String resource, Exception cause) {
265 | 1 |
                 super(resource, cause);
266 | 1 |
             }
267 |  |
268 |  |
         }
269 |  |
270 |  |
         public static class ReportsTable {
271 |  |
272 | 13 |
             private final Map<String, Report> reports = new HashMap<String, Report>();
273 |  |
             private final StoryNameResolver nameResolver;
274 |  |
275 | 13 |
             public ReportsTable(List<Report> reports, StoryNameResolver nameResolver) {
276 | 13 |
                 this.nameResolver = nameResolver;
277 | 13 |
                 index(reports);
278 | 13 |
                 addTotalsReport();
279 | 13 |
             }
280 |  |
281 |  |
             private void index(List<Report> reports) {
282 | 13 |
                 for (Report report : reports) {
283 | 17 |
                     report.nameAs(nameResolver.resolveName(report.getPath()));
284 | 17 |
                     this.reports.put(report.getName(), report);
285 |  |
                 }
286 | 13 |
             }
287 |  |
288 |  |
             private void addTotalsReport() {
289 | 13 |
                 Report report = totals(reports.values());
290 | 13 |
                 report.nameAs(nameResolver.resolveName(report.getPath()));
291 | 13 |
                 reports.put(report.getName(), report);
292 | 13 |
             }
293 |  |
294 |  |
             private Report totals(Collection<Report> values) {
295 | 13 |
                 Map<String, Integer> totals = new HashMap<String, Integer>();
296 | 13 |
                 for (Report report : values) {
297 | 17 |
                     Map<String, Integer> stats = report.getStats();
298 | 17 |
                     for (String key : stats.keySet()) {
299 | 180 |
                         Integer total = totals.get(key);
300 | 180 |
                         if (total == null) {
301 | 180 |
                             total = 0;
302 |  |
                         }
303 | 180 |
                         total = total + stats.get(key);
304 | 180 |
                         totals.put(key, total);
305 | 180 |
                     }
306 | 17 |
                 }
307 | 13 |
                 return new Report("Totals", new HashMap<String, File>(), totals);
308 |  |
             }
309 |  |
310 |  |
             public List<Report> getReports() {
311 | 0 |
                 List<Report> list = new ArrayList<Report>(reports.values());
312 | 0 |
                 Collections.sort(list);
313 | 0 |
                 return list;
314 |  |
             }
315 |  |
316 |  |
             public List<String> getReportNames() {
317 | 12 |
                 List<String> list = new ArrayList<String>(reports.keySet());
318 | 12 |
                 Collections.sort(list);
319 | 12 |
                 return list;
320 |  |
             }
321 |  |
322 |  |
             public Report getReport(String name) {
323 | 40 |
                 return reports.get(name);
324 |  |
             }
325 |  |
         }
326 |  |
327 | 0 |
         public static class Report implements Comparable<Report> {
328 |  |
329 |  |
             private final String path;
330 |  |
             private final Map<String, File> filesByFormat;
331 |  |
             private Map<String, Integer> stats;
332 |  |
             private String name;
333 |  |
334 |  |
             public Report(String path, Map<String, File> filesByFormat) {
335 | 18 |
                 this(path, filesByFormat, null);
336 | 18 |
             }
337 |  |
338 | 31 |
             public Report(String path, Map<String, File> filesByFormat, Map<String, Integer> stats) {
339 | 31 |
                 this.path = path;
340 | 31 |
                 this.filesByFormat = filesByFormat;
341 | 31 |
                 this.stats = stats;
342 | 31 |
             }
343 |  |
344 |  |
             public String getPath() {
345 | 30 |
                 return path;
346 |  |
             }
347 |  |
348 |  |
             public String getName() {
349 | 46 |
                 return name != null ? name : path;
350 |  |
             }
351 |  |
352 |  |
             public void nameAs(String name) {
353 | 30 |
                 this.name = name;
354 | 30 |
             }
355 |  |
356 |  |
             public Map<String, File> getFilesByFormat() {
357 | 16 |
                 return filesByFormat;
358 |  |
             }
359 |  |
360 |  |
             public Properties asProperties(String format) {
361 | 116 |
                 Properties p = new Properties();
362 | 116 |
                 File stats = filesByFormat.get(format);
363 |  |
                 try {
364 | 116 |
                     InputStream inputStream = new FileInputStream(stats);
365 | 80 |
                     p.load(inputStream);
366 | 80 |
                     inputStream.close();
367 | 36 |
                 } catch (Exception e) {
368 |  |
                     // return empty map
369 | 80 |
                 }
370 | 116 |
                 return p;
371 |  |
             }
372 |  |
373 |  |
             public Map<String, Integer> getStats() {
374 | 59 |
                 if (stats == null) {
375 | 17 |
                     Properties p = asProperties("stats");
376 | 17 |
                     stats = new HashMap<String, Integer>();
377 | 17 |
                     for (Enumeration<?> e = p.propertyNames(); e.hasMoreElements();) {
378 | 180 |
                         String key = (String) e.nextElement();
379 | 180 |
                         stats.put(key, valueOf(key, p));
380 | 180 |
                     }
381 |  |
                 }
382 | 59 |
                 return stats;
383 |  |
             }
384 |  |
385 |  |
             private Integer valueOf(String key, Properties p) {
386 |  |
                 try {
387 | 180 |
                     return Integer.valueOf(p.getProperty(key));
388 | 180 |
                 } catch (NumberFormatException e) {
389 | 180 |
                     return 0;
390 |  |
                 }
391 |  |
             }
392 |  |
393 |  |
             public int compareTo(Report that) {
394 | 0 |
                 return CompareToBuilder.reflectionCompare(this.getName(), that.getName());
395 |  |
             }
396 |  |
397 |  |
             @Override
398 |  |
             public String toString() {
399 | 0 |
                 return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append(path).toString();
400 |  |
             }
401 |  |
         }
402 |  |
403 | 14 |
         public static class TimeFormatter {
404 |  |
405 |  |
             public String formatMillis(long millis) {
406 | 35 |
                 int second = 1000;
407 | 35 |
                 int minute = 60 * second;
408 | 35 |
                 int hour = 60 * minute;
409 | 35 |
                 long hours = millis / hour;
410 | 35 |
                 long minutes = (millis % hour) / minute;
411 | 35 |
                 long seconds = ((millis % hour) % minute) / second;
412 | 35 |
                 long milliseconds = ((millis % hour) % minute % second);
413 | 35 |
                 Formatter formatter = new Formatter();
414 | 35 |
                 String result = formatter.format("%02d:%02d:%02d.%03d", hours, minutes, seconds, milliseconds).toString();
415 | 35 |
                 formatter.close();
416 | 35 |
                 return result;
417 |  |
             }
418 |  |
419 |  |
         }
420 |  |
     }