View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
5    */
6   package mockit.coverage;
7   
8   import edu.umd.cs.findbugs.annotations.NonNull;
9   import edu.umd.cs.findbugs.annotations.Nullable;
10  
11  import java.io.File;
12  import java.io.IOException;
13  import java.nio.file.Path;
14  
15  import mockit.coverage.data.CoverageData;
16  import mockit.coverage.reporting.CoverageReport;
17  
18  import org.slf4j.Logger;
19  import org.slf4j.LoggerFactory;
20  
21  @SuppressWarnings("DynamicRegexReplaceableByCompiledPattern")
22  final class OutputFileGenerator {
23  
24      /** The logger. */
25      private static final Logger logger = LoggerFactory.getLogger(CoverageReport.class);
26  
27      private static final String[] ALL_SOURCE_DIRS = {};
28  
29      @NonNull
30      private final String[] outputFormats;
31      @NonNull
32      private final String outputDir;
33      @Nullable
34      private final String[] sourceDirs;
35  
36      OutputFileGenerator() {
37          outputFormats = getOutputFormat();
38          outputDir = Configuration.getProperty("outputDir", "");
39  
40          String commaSeparatedDirs = Configuration.getProperty("srcDirs");
41  
42          if (commaSeparatedDirs == null) {
43              sourceDirs = ALL_SOURCE_DIRS;
44          } else if (commaSeparatedDirs.isEmpty()) {
45              sourceDirs = null;
46          } else {
47              sourceDirs = commaSeparatedDirs.split("\\s*,\\s*");
48          }
49      }
50  
51      @NonNull
52      private static String[] getOutputFormat() {
53          String format = Configuration.getProperty("output", "");
54          return format.isEmpty() ? new String[] { "html" } : format.trim().split("\\s*,\\s*|\\s+");
55      }
56  
57      boolean isOutputToBeGenerated() {
58          return isHTMLWithNoCallPoints() || isWithCallPoints() || hasOutputFormat("serial")
59                  || hasOutputFormat("serial-append") || hasOutputFormat("xml");
60      }
61  
62      private boolean isHTMLWithNoCallPoints() {
63          return hasOutputFormat("html") || hasOutputFormat("html-nocp");
64      }
65  
66      boolean isWithCallPoints() {
67          return hasOutputFormat("html-cp");
68      }
69  
70      private boolean hasOutputFormat(@NonNull String format) {
71          for (String outputFormat : outputFormats) {
72              if (format.equals(outputFormat)) {
73                  return true;
74              }
75          }
76  
77          return false;
78      }
79  
80      void generate() {
81          CoverageData coverageData = CoverageData.instance();
82  
83          if (coverageData.isEmpty()) {
84              logger.info("JMockit: No classes were instrumented for coverage; please make sure that ");
85  
86              String classesRegexp = Configuration.getProperty("classes");
87  
88              if (classesRegexp == null) {
89                  logger.info("classes exercised by tests are in a directory included in the runtime classpath");
90              } else {
91                  logger.info(
92                          "classes selected for coverage through the regular expression '{}' are available from the runtime classpath",
93                          classesRegexp);
94              }
95  
96              logger.info(", and that they have been compiled with debug information.");
97              return;
98          }
99  
100         boolean outputDirCreated = createOutputDirIfSpecifiedButNotExists();
101 
102         try {
103             generateAccretionDataFileIfRequested(coverageData);
104             generateXmlDataFileIfRequested(coverageData);
105             generateHTMLReportIfRequested(coverageData, outputDirCreated);
106         } catch (IOException e) {
107             throw new RuntimeException(e);
108         }
109     }
110 
111     void generateAggregateReportFromInputFiles(@NonNull String[] inputPaths) {
112         boolean outputDirCreated = createOutputDirIfSpecifiedButNotExists();
113 
114         try {
115             CoverageData coverageData = new DataFileMerging(inputPaths).merge();
116             generateHTMLReportIfRequested(coverageData, outputDirCreated);
117         } catch (IOException e) {
118             throw new RuntimeException(e);
119         }
120     }
121 
122     private boolean createOutputDirIfSpecifiedButNotExists() {
123         if (outputDir.isEmpty()) {
124             return false;
125         }
126 
127         File outDir = Path.of(outputDir).toFile();
128         return outDir.mkdirs();
129     }
130 
131     private void generateAccretionDataFileIfRequested(@NonNull CoverageData newData) throws IOException {
132         if (hasOutputFormat("serial")) {
133             new AccretionFile(outputDir, newData).generate();
134         } else if (hasOutputFormat("serial-append")) {
135             AccretionFile accretionFile = new AccretionFile(outputDir, newData);
136             accretionFile.mergeDataFromExistingFileIfAny();
137             accretionFile.generate();
138         }
139     }
140 
141     private void generateXmlDataFileIfRequested(@NonNull CoverageData newData) throws IOException {
142         if (hasOutputFormat("xml")) {
143             new XmlFile(outputDir, newData).generate();
144         }
145     }
146 
147     private void generateHTMLReportIfRequested(@NonNull CoverageData coverageData, boolean outputDirCreated)
148             throws IOException {
149         if (isHTMLWithNoCallPoints()) {
150             new CoverageReport(outputDir, outputDirCreated, sourceDirs, coverageData, false).generate();
151         } else if (isWithCallPoints()) {
152             new CoverageReport(outputDir, outputDirCreated, sourceDirs, coverageData, true).generate();
153         }
154     }
155 }