• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.core.runtime;
14 
15 import static org.junit.Assert.assertEquals;
16 import static org.junit.Assert.assertSame;
17 
18 import org.jacoco.core.JaCoCo;
19 import org.jacoco.core.instr.MethodRecorder;
20 import org.jacoco.core.internal.instr.InstrSupport;
21 import org.jacoco.core.test.TargetLoader;
22 import org.junit.Before;
23 import org.junit.BeforeClass;
24 import org.junit.Test;
25 import org.objectweb.asm.ClassWriter;
26 import org.objectweb.asm.Opcodes;
27 import org.objectweb.asm.Type;
28 import org.objectweb.asm.commons.GeneratorAdapter;
29 import org.objectweb.asm.commons.Method;
30 
31 /**
32  * Unit tests for {@link OfflineInstrumentationAccessGenerator}.
33  */
34 public class OfflineInstrumentationAccessGeneratorTest {
35 
36 	private IExecutionDataAccessorGenerator generator;
37 
38 	private static boolean[] probes;
39 
40 	// runtime stub
getProbes(final long classid, final String classname, final int probecount)41 	public static boolean[] getProbes(final long classid,
42 			final String classname, final int probecount) {
43 		return probes;
44 	}
45 
46 	@BeforeClass
setupClass()47 	public static void setupClass() {
48 		probes = new boolean[3];
49 	}
50 
51 	@Before
setup()52 	public void setup() {
53 		String name = getClass().getName().replace('.', '/');
54 		generator = new OfflineInstrumentationAccessGenerator(name);
55 	}
56 
57 	@Test
testRuntimeAccess()58 	public void testRuntimeAccess() throws Exception {
59 		ITarget target = generateAndInstantiateClass(123);
60 		assertSame(probes, target.get());
61 	}
62 
63 	@Test
testRuntimeClassName()64 	public void testRuntimeClassName() throws Exception {
65 		generator = new OfflineInstrumentationAccessGenerator();
66 		MethodRecorder actual = new MethodRecorder();
67 		generator.generateDataAccessor(987654321, "foo/Bar", 17,
68 				actual.getVisitor());
69 
70 		MethodRecorder expected = new MethodRecorder();
71 		expected.getVisitor().visitLdcInsn(Long.valueOf(987654321));
72 		expected.getVisitor().visitLdcInsn("foo/Bar");
73 		expected.getVisitor().visitIntInsn(Opcodes.BIPUSH, 17);
74 		String rtname = JaCoCo.RUNTIMEPACKAGE.replace('.', '/') + "/Offline";
75 		expected.getVisitor().visitMethodInsn(Opcodes.INVOKESTATIC, rtname,
76 				"getProbes", "(JLjava/lang/String;I)[Z", false);
77 
78 		assertEquals(expected, actual);
79 	}
80 
81 	/**
82 	 * Creates a new class with the given id, loads this class and instantiates
83 	 * it. The constructor of the generated class will request the probe array
84 	 * from the access generator under test.
85 	 */
generateAndInstantiateClass(int classid)86 	private ITarget generateAndInstantiateClass(int classid)
87 			throws InstantiationException, IllegalAccessException {
88 
89 		final String className = "org/jacoco/test/targets/RuntimeTestTarget_"
90 				+ classid;
91 		Type classType = Type.getObjectType(className);
92 
93 		final ClassWriter writer = new ClassWriter(0);
94 		writer.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, className, null,
95 				"java/lang/Object",
96 				new String[] { Type.getInternalName(ITarget.class) });
97 
98 		writer.visitField(InstrSupport.DATAFIELD_ACC,
99 				InstrSupport.DATAFIELD_NAME, InstrSupport.DATAFIELD_DESC, null,
100 				null);
101 
102 		// Constructor
103 		GeneratorAdapter gen = new GeneratorAdapter(
104 				writer.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null,
105 						new String[0]),
106 				Opcodes.ACC_PUBLIC, "<init>", "()V");
107 		gen.visitCode();
108 		gen.loadThis();
109 		gen.invokeConstructor(Type.getType(Object.class),
110 				new Method("<init>", "()V"));
111 		gen.loadThis();
112 		final int size = generator.generateDataAccessor(classid, className, 2,
113 				gen);
114 		gen.putStatic(classType, InstrSupport.DATAFIELD_NAME,
115 				Type.getObjectType(InstrSupport.DATAFIELD_DESC));
116 		gen.returnValue();
117 		gen.visitMaxs(size + 1, 0);
118 		gen.visitEnd();
119 
120 		// get()
121 		gen = new GeneratorAdapter(writer.visitMethod(Opcodes.ACC_PUBLIC, "get",
122 				"()[Z", null, new String[0]), Opcodes.ACC_PUBLIC, "get",
123 				"()[Z");
124 		gen.visitCode();
125 		gen.getStatic(classType, InstrSupport.DATAFIELD_NAME,
126 				Type.getObjectType(InstrSupport.DATAFIELD_DESC));
127 		gen.returnValue();
128 		gen.visitMaxs(1, 0);
129 		gen.visitEnd();
130 
131 		writer.visitEnd();
132 
133 		final TargetLoader loader = new TargetLoader();
134 		return (ITarget) loader
135 				.add(className.replace('/', '.'), writer.toByteArray())
136 				.newInstance();
137 	}
138 
139 	/**
140 	 * With this interface access read coverage data of the generated class.
141 	 */
142 	public interface ITarget {
143 
144 		/**
145 		 * Returns a reference to the probe array.
146 		 *
147 		 * @return the probe array
148 		 */
get()149 		boolean[] get();
150 
151 	}
152 
153 }
154