/* * Copyright (c) 2006 Rogério Liesenfeld * This file is subject to the terms of the MIT license (see LICENSE.txt). */ package mockit.coverage; import java.lang.instrument.*; import java.security.*; import javax.annotation.*; import mockit.coverage.data.*; import mockit.coverage.modification.*; import mockit.internal.startup.*; public final class CodeCoverage implements ClassFileTransformer { private static CodeCoverage instance; private static boolean inATestRun = true; @Nonnull private final ClassModification classModification; @Nonnull private final OutputFileGenerator outputGenerator; private boolean outputPendingForShutdown; private boolean inactive; public static void main(@Nonnull String[] args) { if (args.length == 1) { String pid = args[0]; try { //noinspection ResultOfMethodCallIgnored Integer.parseInt(pid); new AgentLoader(pid).loadAgent("coverage"); return; } catch (NumberFormatException ignore) {} } OutputFileGenerator generator = createOutputFileGenerator(null); generator.generateAggregateReportFromInputFiles(args); } @Nonnull private static OutputFileGenerator createOutputFileGenerator(@Nullable ClassModification classModification) { OutputFileGenerator generator = new OutputFileGenerator(classModification); CoverageData.instance().setWithCallPoints(generator.isWithCallPoints()); return generator; } public static boolean isTestRun() { return inATestRun; } public CodeCoverage() { classModification = new ClassModification(); outputGenerator = createOutputFileGenerator(classModification); outputPendingForShutdown = true; instance = this; Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { TestRun.terminate(); if (outputPendingForShutdown) { if (outputGenerator.isOutputToBeGenerated()) { outputGenerator.generate(CodeCoverage.this); } new CoverageCheck().verifyThresholds(); } Startup.instrumentation().removeTransformer(CodeCoverage.this); } }); } public static boolean active() { String coverageOutput = Configuration.getProperty("output"); String coverageClasses = Configuration.getProperty("classes"); String coverageMetrics = Configuration.getProperty("metrics"); return (coverageOutput != null || coverageClasses != null || coverageMetrics != null) && !("none".equals(coverageOutput) || "none".equals(coverageClasses) || "none".equals(coverageMetrics)); } @Nonnull public static CodeCoverage create(boolean standalone, boolean generateOutputOnShutdown) { inATestRun = !standalone; instance = new CodeCoverage(); instance.outputPendingForShutdown = generateOutputOnShutdown; return instance; } public static void resetConfiguration() { Startup.instrumentation().removeTransformer(instance); CoverageData.instance().clear(); CodeCoverage coverage = create(true, false); Startup.instrumentation().addTransformer(coverage); instance.outputPendingForShutdown = false; } public static void generateOutput(boolean resetState) { instance.outputGenerator.generate(null); instance.outputPendingForShutdown = false; if (resetState) { CoverageData.instance().reset(); } } @Nullable @Override public byte[] transform( @Nullable ClassLoader loader, @Nonnull String internalClassName, @Nullable Class<?> classBeingRedefined, @Nullable ProtectionDomain protectionDomain, @Nonnull byte[] originalClassfile) { if (loader == null || classBeingRedefined != null || protectionDomain == null || inactive) { return null; } String className = internalClassName.replace('/', '.'); byte[] modifiedClassfile = classModification.modifyClass(className, protectionDomain, originalClassfile); return modifiedClassfile; } void deactivate() { inactive = true; } }