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.cli.internal.commands;
13
14import static org.junit.Assert.assertArrayEquals;
15import static org.junit.Assert.assertFalse;
16import static org.junit.Assert.assertTrue;
17import static org.junit.Assert.fail;
18
19import java.io.File;
20import java.io.FileInputStream;
21import java.io.FileOutputStream;
22import java.io.IOException;
23import java.io.InputStream;
24import java.io.OutputStream;
25import java.util.HashSet;
26import java.util.Set;
27
28import org.jacoco.cli.internal.CommandTestBase;
29import org.junit.Rule;
30import org.junit.Test;
31import org.junit.rules.TemporaryFolder;
32import org.objectweb.asm.ClassReader;
33import org.objectweb.asm.ClassVisitor;
34import org.objectweb.asm.FieldVisitor;
35import org.objectweb.asm.Opcodes;
36
37/**
38 * Unit tests for {@link Instrument}.
39 */
40public class InstrumentTest extends CommandTestBase {
41
42	@Rule
43	public TemporaryFolder tmp = new TemporaryFolder();
44
45	@Test
46	public void should_print_usage_when_no_options_are_given()
47			throws Exception {
48		execute("instrument");
49		assertFailure();
50		assertContains("Option \"--dest\" is required", err);
51		assertContains(
52				"Usage: java -jar jacococli.jar instrument [<sourcefiles> ...]",
53				err);
54	}
55
56	@Test
57	public void should_instrument_class_files_and_copy_resources_when_folder_is_given()
58			throws Exception {
59		File destdir = tmp.getRoot();
60
61		execute("instrument", "--dest", destdir.getAbsolutePath(),
62				getClassPath());
63
64		assertOk();
65		assertContains("[INFO] 14 classes instrumented to "
66				+ destdir.getAbsolutePath(), out);
67
68		// non class-file resources are copied:
69		assertTrue(new File(destdir, "about.html").isFile());
70
71		assertInstrumented(new File(destdir,
72				"org/jacoco/cli/internal/commands/InstrumentTest.class"));
73	}
74
75	@Test
76	public void should_instrument_class_files_to_dest_folder_when_class_files_are_given()
77			throws Exception {
78		File destdir = tmp.getRoot();
79
80		File src = new File(getClassPath(),
81				"org/jacoco/cli/internal/commands/InstrumentTest.class");
82
83		execute("instrument", "--dest", destdir.getAbsolutePath(),
84				src.getAbsolutePath());
85
86		assertOk();
87		assertContains(
88				"[INFO] 1 classes instrumented to " + destdir.getAbsolutePath(),
89				out);
90
91		assertInstrumented(new File(destdir, "InstrumentTest.class"));
92	}
93
94	@Test
95	public void should_not_instrument_anything_when_no_source_is_given()
96			throws Exception {
97		File destdir = tmp.getRoot();
98
99		execute("instrument", "--dest", destdir.getAbsolutePath());
100
101		assertOk();
102		assertArrayEquals(new String[0], destdir.list());
103	}
104
105	@Test
106	public void should_not_create_dest_file_when_source_class_is_broken()
107			throws Exception {
108		File srcdir = new File(tmp.getRoot(), "src");
109		srcdir.mkdir();
110		File destdir = new File(tmp.getRoot(), "dest");
111		destdir.mkdir();
112
113		OutputStream out = new FileOutputStream(
114				new File(srcdir, "Broken.class"));
115		out.write((byte) 0xca);
116		out.write((byte) 0xfe);
117		out.write((byte) 0xba);
118		out.write((byte) 0xbe);
119		out.write((byte) 0x00);
120		out.write((byte) 0x00);
121		out.write((byte) 0x00);
122		out.write((byte) 50);
123		out.close();
124
125		try {
126			execute("instrument", "--dest", destdir.getAbsolutePath(),
127					srcdir.getAbsolutePath());
128			fail("exception expected");
129		} catch (IOException expected) {
130		}
131
132		assertFalse(new File(destdir, "Broken.class").exists());
133	}
134
135	private void assertInstrumented(File classfile) throws IOException {
136		InputStream in = new FileInputStream(classfile);
137		ClassReader reader = new ClassReader(in);
138		in.close();
139		final Set<String> fields = new HashSet<String>();
140		reader.accept(new ClassVisitor(Opcodes.ASM6) {
141			@Override
142			public FieldVisitor visitField(int access, String name, String desc,
143					String signature, Object value) {
144				fields.add(name);
145				return null;
146			}
147		}, 0);
148		assertTrue(fields.contains("$jacocoData"));
149	}
150
151}
152