All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mockit.coverage.data.CoverageData Maven / Gradle / Ivy

Go to download

JMockit is a Java toolkit for automated developer testing. It contains mocking/faking APIs and a code coverage tool, supporting both JUnit and TestNG. The mocking APIs allow all kinds of Java code, without testability restrictions, to be tested in isolation from selected dependencies.

There is a newer version: 1.49
Show newest version
/*
 * Copyright (c) 2006 JMockit developers
 * This file is subject to the terms of the MIT license (see LICENSE.txt).
 */
package mockit.coverage.data;

import java.io.*;
import java.util.*;
import java.util.Map.*;
import java.util.jar.*;
import javax.annotation.*;

import mockit.coverage.*;
import mockit.internal.util.*;

/**
 * Coverage data captured for all source files exercised during a test run.
 */
public final class CoverageData implements Serializable
{
   private static final long serialVersionUID = -4860004226098360259L;
   @Nonnull private static final CoverageData instance = new CoverageData();

   @Nonnull public static CoverageData instance() { return instance; }

   private boolean withCallPoints;

   @Nonnull private final Map fileToFileData = new LinkedHashMap<>();
   @Nonnull private final List indexedFileData = new ArrayList<>(100);

   public boolean isWithCallPoints() { return withCallPoints; }
   public void setWithCallPoints(boolean withCallPoints) { this.withCallPoints = withCallPoints; }

   @Nonnull public Map getFileToFileData() { return fileToFileData; }

   @Nonnull
   public FileCoverageData getOrAddFile(@Nonnull String file, @Nullable String kindOfTopLevelType) {
      FileCoverageData fileData = fileToFileData.get(file);

      // For a class with nested/inner classes, a previous class in the same source file may already have been added.
      if (fileData == null) {
         int fileIndex = indexedFileData.size();
         fileData = new FileCoverageData(fileIndex, kindOfTopLevelType);
         indexedFileData.add(fileData);
         fileToFileData.put(file, fileData);
      }
      else if (kindOfTopLevelType != null) {
         fileData.kindOfTopLevelType = kindOfTopLevelType;
      }

      return fileData;
   }

   @Nonnull public FileCoverageData getFileData(@Nonnull String file) { return fileToFileData.get(file); }
   @Nonnull public FileCoverageData getFileData(@Nonnegative int fileIndex) { return indexedFileData.get(fileIndex); }

   public boolean isEmpty() { return fileToFileData.isEmpty(); }
   public void clear() { fileToFileData.clear(); }

   /**
    * Computes the coverage percentage over a subset of the available source files.
    *
    * @param fileNamePrefix a regular expression for matching the names of the source files to be considered, or null to consider
    *                       all files
    *
    * @return the computed percentage from 0 to 100 (inclusive), or -1 if no meaningful value could be computed
    */
   public int getPercentage(@Nullable String fileNamePrefix) {
      int coveredItems = 0;
      int totalItems = 0;

      for (Entry fileAndFileData : fileToFileData.entrySet()) {
         String sourceFile = fileAndFileData.getKey();

         if (fileNamePrefix == null || sourceFile.startsWith(fileNamePrefix)) {
            FileCoverageData fileData = fileAndFileData.getValue();
            coveredItems += fileData.getCoveredItems();
            totalItems += fileData.getTotalItems();
         }
      }

      return CoveragePercentage.calculate(coveredItems, totalItems);
   }

   /**
    * Finds the source file with the smallest coverage percentage.
    *
    * @return the percentage value for the file found, or Integer.MAX_VALUE if no file is found with a meaningful percentage
    */
   @Nonnegative
   public int getSmallestPerFilePercentage() {
      int minPercentage = Integer.MAX_VALUE;

      for (FileCoverageData fileData : fileToFileData.values()) {
         if (!fileData.wasLoadedAfterTestCompletion()) {
            int percentage = fileData.getCoveragePercentage();

            if (percentage >= 0 && percentage < minPercentage) {
               minPercentage = percentage;
            }
         }
      }

      return minPercentage;
   }

   public void fillLastModifiedTimesForAllClassFiles() {
      for (Iterator> itr = fileToFileData.entrySet().iterator(); itr.hasNext(); ) {
         Entry fileAndFileData = itr.next();
         long lastModified = getLastModifiedTimeForClassFile(fileAndFileData.getKey());

         if (lastModified > 0L) {
            FileCoverageData fileCoverageData = fileAndFileData.getValue();
            fileCoverageData.lastModified = lastModified;
            continue;
         }

         itr.remove();
      }
   }

   private long getLastModifiedTimeForClassFile(@Nonnull String sourceFilePath) {
      String sourceFilePathNoExt = sourceFilePath.substring(0, sourceFilePath.lastIndexOf('.'));
      String className = sourceFilePathNoExt.replace('/', '.');

      Class coveredClass = findCoveredClass(className);

      if (coveredClass == null) {
         return 0L;
      }

      String locationPath = Utilities.getClassFileLocationPath(coveredClass);

      if (locationPath.endsWith(".jar")) {
         try { return getLastModifiedTimeFromJarEntry(sourceFilePathNoExt, locationPath); }
         catch (IOException ignore) { return 0L; }
      }

      String pathToClassFile = locationPath + sourceFilePathNoExt + ".class";

      return new File(pathToClassFile).lastModified();
   }

   private static long getLastModifiedTimeFromJarEntry(
      @Nonnull String sourceFilePathNoExt, @Nonnull String locationPath
   ) throws IOException {

      try (JarFile jarFile = new JarFile(locationPath)) {
         JarEntry classEntry = jarFile.getJarEntry(sourceFilePathNoExt + ".class");
         return classEntry.getTime();
      }
   }

   @Nullable
   private Class findCoveredClass(@Nonnull String className) {
      ClassLoader currentCL = getClass().getClassLoader();
      Class coveredClass = loadClass(className, currentCL);

      if (coveredClass == null) {
         ClassLoader systemCL = ClassLoader.getSystemClassLoader();

         if (systemCL != currentCL) {
            coveredClass = loadClass(className, systemCL);
         }

         if (coveredClass == null) {
            ClassLoader contextCL = Thread.currentThread().getContextClassLoader();

            if (contextCL != null && contextCL != systemCL) {
               coveredClass = loadClass(className, contextCL);
            }
         }
      }

      return coveredClass;
   }

   @Nullable
   private static Class loadClass(@Nonnull String className, @Nullable ClassLoader loader) {
      try {
         return Class.forName(className, false, loader);
      }
      catch (ClassNotFoundException | NoClassDefFoundError ignore) { return null; }
   }

   /**
    * Reads a serialized CoverageData object from the given file (normally, a "coverage.ser" file generated at the end of
    * a previous test run).
    *
    * @param dataFile the ".ser" file containing a serialized CoverageData instance
    *
    * @return a new object containing all coverage data resulting from a previous test run
    */
   @Nonnull
   public static CoverageData readDataFromFile(@Nonnull File dataFile) throws IOException {
      try (ObjectInputStream input = new ObjectInputStream(new BufferedInputStream(new FileInputStream(dataFile)))) {
         return (CoverageData) input.readObject();
      }
      catch (ClassNotFoundException e) {
         throw new RuntimeException("Serialized class in coverage data file \"" + dataFile + "\" not found in classpath", e);
      }
   }

   public void writeDataToFile(@Nonnull File dataFile) throws IOException {
      try (ObjectOutputStream output = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile)))) {
         output.writeObject(this);
      }
   }

   public void merge(@Nonnull CoverageData previousData) {
      withCallPoints |= previousData.withCallPoints;

      for (Entry previousFileAndFileData : previousData.fileToFileData.entrySet()) {
         String previousFile = previousFileAndFileData.getKey();
         FileCoverageData previousFileData = previousFileAndFileData.getValue();
         FileCoverageData fileData = fileToFileData.get(previousFile);

         if (fileData == null) {
            fileToFileData.put(previousFile, previousFileData);
         }
         else if (fileData.lastModified > 0 && previousFileData.lastModified == fileData.lastModified) {
            fileData.mergeWithDataFromPreviousTestRun(previousFileData);
         }
      }
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy