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