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