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.lang.instrument.ClassFileTransformer;
12  import java.security.ProtectionDomain;
13  
14  import mockit.coverage.data.CoverageData;
15  import mockit.coverage.modification.ClassModification;
16  import mockit.coverage.modification.ClassesNotLoaded;
17  import mockit.internal.startup.Startup;
18  
19  public final class CodeCoverage implements ClassFileTransformer {
20      @NonNull
21      private final ClassModification classModification;
22  
23      public static void main(@NonNull String[] args) {
24          OutputFileGenerator generator = createOutputFileGenerator();
25          generator.generateAggregateReportFromInputFiles(args);
26      }
27  
28      @NonNull
29      private static OutputFileGenerator createOutputFileGenerator() {
30          OutputFileGenerator generator = new OutputFileGenerator();
31          CoverageData.instance().setWithCallPoints(generator.isWithCallPoints());
32          return generator;
33      }
34  
35      public static boolean active() {
36          String coverageOutput = Configuration.getProperty("output");
37          String coverageClasses = Configuration.getProperty("classes");
38          return (coverageOutput != null || coverageClasses != null) && !"none".equals(coverageOutput)
39                  && !"none".equals(coverageClasses);
40      }
41  
42      public CodeCoverage() {
43          classModification = new ClassModification();
44          final OutputFileGenerator outputGenerator = createOutputFileGenerator();
45          final CoverageCheck coverageCheck = CoverageCheck.createIfApplicable();
46  
47          Runtime.getRuntime().addShutdownHook(new Thread() {
48              @Override
49              public void run() {
50                  TestRun.terminate();
51  
52                  if (outputGenerator.isOutputToBeGenerated()) {
53                      if (classModification.shouldConsiderClassesNotLoaded()) {
54                          new ClassesNotLoaded(classModification).gatherCoverageData();
55                      }
56  
57                      Startup.instrumentation().removeTransformer(CodeCoverage.this);
58                      outputGenerator.generate();
59                  } else {
60                      Startup.instrumentation().removeTransformer(CodeCoverage.this);
61                  }
62  
63                  if (coverageCheck != null) {
64                      coverageCheck.verifyThresholds();
65                  }
66              }
67          });
68      }
69  
70      @Nullable
71      @Override
72      public byte[] transform(@Nullable ClassLoader loader, @NonNull String internalClassName,
73              @Nullable Class<?> classBeingRedefined, @Nullable ProtectionDomain protectionDomain,
74              @NonNull byte[] originalClassfile) {
75          if (loader == null || classBeingRedefined != null || protectionDomain == null) {
76              return null;
77          }
78  
79          String className = internalClassName.replace('/', '.');
80          return classModification.modifyClass(className, protectionDomain, originalClassfile);
81      }
82  }