View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
4    */
5   package mockit.coverage.reporting;
6   
7   import edu.umd.cs.findbugs.annotations.NonNull;
8   import edu.umd.cs.findbugs.annotations.Nullable;
9   
10  import java.io.File;
11  import java.io.IOException;
12  import java.nio.file.Path;
13  import java.util.ArrayList;
14  import java.util.Collection;
15  import java.util.HashMap;
16  import java.util.List;
17  import java.util.Map;
18  import java.util.Map.Entry;
19  import java.util.Set;
20  
21  import mockit.coverage.Configuration;
22  import mockit.coverage.data.CoverageData;
23  import mockit.coverage.data.FileCoverageData;
24  import mockit.coverage.reporting.packages.IndexPage;
25  import mockit.coverage.reporting.sourceFiles.FileCoverageReport;
26  import mockit.coverage.reporting.sourceFiles.InputFile;
27  
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  
31  public final class CoverageReport {
32  
33      /** The logger. */
34      private static final Logger logger = LoggerFactory.getLogger(CoverageReport.class);
35  
36      @NonNull
37      private final String outputDir;
38      private boolean outputDirCreated;
39      @Nullable
40      private final List<File> sourceDirs;
41      @NonNull
42      private final Map<String, FileCoverageData> fileToFileData;
43      @NonNull
44      private final Map<String, List<String>> packageToFiles;
45      private final boolean withCallPoints;
46      @Nullable
47      private final Collection<String> sourceFilesNotFound;
48  
49      public CoverageReport(@NonNull String outputDir, boolean outputDirCreated, @Nullable String[] srcDirs,
50              @NonNull CoverageData coverageData, boolean withCallPoints) {
51          this.outputDir = Configuration.getOrChooseOutputDirectory(outputDir, "coverage-report");
52          this.outputDirCreated = outputDirCreated;
53          sourceDirs = srcDirs == null ? null : new SourceFiles().buildListOfSourceDirectories(srcDirs);
54          fileToFileData = coverageData.getFileToFileData();
55          packageToFiles = new HashMap<>();
56          this.withCallPoints = withCallPoints;
57          sourceFilesNotFound = srcDirs == null ? null : new ArrayList<>();
58      }
59  
60      public void generate() throws IOException {
61          createReportOutputDirIfNotExists();
62  
63          File outputFile = createOutputFileForIndexPage();
64  
65          if (outputFile == null) {
66              return;
67          }
68  
69          boolean withSourceFilePages = sourceDirs != null;
70  
71          if (withSourceFilePages && sourceDirs.size() > 1) {
72              logger.info("JMockit: Coverage source dirs: {}", sourceDirs);
73          }
74  
75          generateFileCoverageReportsWhileBuildingPackageLists();
76  
77          new StaticFiles(outputDir).copyToOutputDir(withSourceFilePages);
78          new IndexPage(outputFile, sourceDirs, sourceFilesNotFound, packageToFiles, fileToFileData).generate();
79  
80          logger.info("JMockit: Coverage report written to {}", outputFile.getParentFile().getCanonicalPath());
81      }
82  
83      private void createReportOutputDirIfNotExists() {
84          if (!outputDirCreated) {
85              File outDir = Path.of(outputDir).toFile();
86              outputDirCreated = outDir.mkdirs();
87          }
88      }
89  
90      @Nullable
91      private File createOutputFileForIndexPage() throws IOException {
92          File outputFile = Path.of(outputDir, "index.html").toFile();
93  
94          if (outputFile.exists() && !outputFile.canWrite()) {
95              logger.info("JMockit: {} is read-only; report generation canceled", outputFile.getCanonicalPath());
96              return null;
97          }
98  
99          return outputFile;
100     }
101 
102     private void generateFileCoverageReportsWhileBuildingPackageLists() throws IOException {
103         Set<Entry<String, FileCoverageData>> files = fileToFileData.entrySet();
104 
105         for (Entry<String, FileCoverageData> fileAndFileData : files) {
106             generateFileCoverageReport(fileAndFileData.getKey(), fileAndFileData.getValue());
107         }
108     }
109 
110     private void generateFileCoverageReport(@NonNull String sourceFile, @NonNull FileCoverageData fileData)
111             throws IOException {
112         if (sourceDirs != null) {
113             InputFile inputFile = InputFile.createIfFileExists(sourceDirs, sourceFile);
114 
115             if (inputFile != null) {
116                 new FileCoverageReport(outputDir, inputFile, fileData, withCallPoints).generate();
117             } else {
118                 deleteOutdatedHTMLFileIfExists(sourceFile);
119 
120                 if (sourceFilesNotFound != null) {
121                     sourceFilesNotFound.add(sourceFile);
122                 }
123             }
124         }
125 
126         addFileToPackageFileList(sourceFile);
127     }
128 
129     private void addFileToPackageFileList(@NonNull String file) {
130         int p = file.lastIndexOf('/');
131         String filePackage = p < 0 ? "" : file.substring(0, p);
132         List<String> filesInPackage = packageToFiles.get(filePackage);
133 
134         if (filesInPackage == null) {
135             filesInPackage = new ArrayList<>();
136             packageToFiles.put(filePackage, filesInPackage);
137         }
138 
139         filesInPackage.add(file.substring(p + 1));
140     }
141 
142     private void deleteOutdatedHTMLFileIfExists(@NonNull String filePath) {
143         if (!outputDirCreated) {
144             File outputFile = OutputFile.getOutputFile(outputDir, filePath);
145             // noinspection ResultOfMethodCallIgnored
146             outputFile.delete();
147         }
148     }
149 }