1 /******************************************************************************* 2 * Copyright (c) 2009, 2021 Mountainminds GmbH & Co. KG and Contributors 3 * This program and the accompanying materials are made available under 4 * the terms of the Eclipse Public License 2.0 which is available at 5 * http://www.eclipse.org/legal/epl-2.0 6 * 7 * SPDX-License-Identifier: EPL-2.0 8 * 9 * Contributors: 10 * Marc R. Hoffmann - initial API and implementation 11 * 12 *******************************************************************************/ 13 package org.jacoco.examples; 14 15 import java.io.InputStream; 16 import java.io.PrintStream; 17 import java.util.HashMap; 18 import java.util.Map; 19 20 import org.jacoco.core.analysis.Analyzer; 21 import org.jacoco.core.analysis.CoverageBuilder; 22 import org.jacoco.core.analysis.IClassCoverage; 23 import org.jacoco.core.analysis.ICounter; 24 import org.jacoco.core.data.ExecutionDataStore; 25 import org.jacoco.core.data.SessionInfoStore; 26 import org.jacoco.core.instr.Instrumenter; 27 import org.jacoco.core.runtime.IRuntime; 28 import org.jacoco.core.runtime.LoggerRuntime; 29 import org.jacoco.core.runtime.RuntimeData; 30 31 /** 32 * Example usage of the JaCoCo core API. In this tutorial a single target class 33 * will be instrumented and executed. Finally the coverage information will be 34 * dumped. 35 */ 36 public final class CoreTutorial { 37 38 /** 39 * The test target we want to see code coverage for. 40 */ 41 public static class TestTarget implements Runnable { 42 run()43 public void run() { 44 isPrime(7); 45 } 46 isPrime(final int n)47 private boolean isPrime(final int n) { 48 for (int i = 2; i * i <= n; i++) { 49 if ((n ^ i) == 0) { 50 return false; 51 } 52 } 53 return true; 54 } 55 56 } 57 58 /** 59 * A class loader that loads classes from in-memory data. 60 */ 61 public static class MemoryClassLoader extends ClassLoader { 62 63 private final Map<String, byte[]> definitions = new HashMap<String, byte[]>(); 64 65 /** 66 * Add a in-memory representation of a class. 67 * 68 * @param name 69 * name of the class 70 * @param bytes 71 * class definition 72 */ addDefinition(final String name, final byte[] bytes)73 public void addDefinition(final String name, final byte[] bytes) { 74 definitions.put(name, bytes); 75 } 76 77 @Override loadClass(final String name, final boolean resolve)78 protected Class<?> loadClass(final String name, final boolean resolve) 79 throws ClassNotFoundException { 80 final byte[] bytes = definitions.get(name); 81 if (bytes != null) { 82 return defineClass(name, bytes, 0, bytes.length); 83 } 84 return super.loadClass(name, resolve); 85 } 86 87 } 88 89 private final PrintStream out; 90 91 /** 92 * Creates a new example instance printing to the given stream. 93 * 94 * @param out 95 * stream for outputs 96 */ CoreTutorial(final PrintStream out)97 public CoreTutorial(final PrintStream out) { 98 this.out = out; 99 } 100 101 /** 102 * Run this example. 103 * 104 * @throws Exception 105 * in case of errors 106 */ execute()107 public void execute() throws Exception { 108 final String targetName = TestTarget.class.getName(); 109 110 // For instrumentation and runtime we need a IRuntime instance 111 // to collect execution data: 112 final IRuntime runtime = new LoggerRuntime(); 113 114 // The Instrumenter creates a modified version of our test target class 115 // that contains additional probes for execution data recording: 116 final Instrumenter instr = new Instrumenter(runtime); 117 InputStream original = getTargetClass(targetName); 118 final byte[] instrumented = instr.instrument(original, targetName); 119 original.close(); 120 121 // Now we're ready to run our instrumented class and need to startup the 122 // runtime first: 123 final RuntimeData data = new RuntimeData(); 124 runtime.startup(data); 125 126 // In this tutorial we use a special class loader to directly load the 127 // instrumented class definition from a byte[] instances. 128 final MemoryClassLoader memoryClassLoader = new MemoryClassLoader(); 129 memoryClassLoader.addDefinition(targetName, instrumented); 130 final Class<?> targetClass = memoryClassLoader.loadClass(targetName); 131 132 // Here we execute our test target class through its Runnable interface: 133 final Runnable targetInstance = (Runnable) targetClass.newInstance(); 134 targetInstance.run(); 135 136 // At the end of test execution we collect execution data and shutdown 137 // the runtime: 138 final ExecutionDataStore executionData = new ExecutionDataStore(); 139 final SessionInfoStore sessionInfos = new SessionInfoStore(); 140 data.collect(executionData, sessionInfos, false); 141 runtime.shutdown(); 142 143 // Together with the original class definition we can calculate coverage 144 // information: 145 final CoverageBuilder coverageBuilder = new CoverageBuilder(); 146 final Analyzer analyzer = new Analyzer(executionData, coverageBuilder); 147 original = getTargetClass(targetName); 148 analyzer.analyzeClass(original, targetName); 149 original.close(); 150 151 // Let's dump some metrics and line coverage information: 152 for (final IClassCoverage cc : coverageBuilder.getClasses()) { 153 out.printf("Coverage of class %s%n", cc.getName()); 154 155 printCounter("instructions", cc.getInstructionCounter()); 156 printCounter("branches", cc.getBranchCounter()); 157 printCounter("lines", cc.getLineCounter()); 158 printCounter("methods", cc.getMethodCounter()); 159 printCounter("complexity", cc.getComplexityCounter()); 160 161 for (int i = cc.getFirstLine(); i <= cc.getLastLine(); i++) { 162 out.printf("Line %s: %s%n", Integer.valueOf(i), 163 getColor(cc.getLine(i).getStatus())); 164 } 165 } 166 } 167 getTargetClass(final String name)168 private InputStream getTargetClass(final String name) { 169 final String resource = '/' + name.replace('.', '/') + ".class"; 170 return getClass().getResourceAsStream(resource); 171 } 172 printCounter(final String unit, final ICounter counter)173 private void printCounter(final String unit, final ICounter counter) { 174 final Integer missed = Integer.valueOf(counter.getMissedCount()); 175 final Integer total = Integer.valueOf(counter.getTotalCount()); 176 out.printf("%s of %s %s missed%n", missed, total, unit); 177 } 178 getColor(final int status)179 private String getColor(final int status) { 180 switch (status) { 181 case ICounter.NOT_COVERED: 182 return "red"; 183 case ICounter.PARTLY_COVERED: 184 return "yellow"; 185 case ICounter.FULLY_COVERED: 186 return "green"; 187 } 188 return ""; 189 } 190 191 /** 192 * Entry point to run this examples as a Java application. 193 * 194 * @param args 195 * list of program arguments 196 * @throws Exception 197 * in case of errors 198 */ main(final String[] args)199 public static void main(final String[] args) throws Exception { 200 new CoreTutorial(System.out).execute(); 201 } 202 203 } 204