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