Home | History | Annotate | Download | only in analysis
      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  *******************************************************************************/
     12 package org.jacoco.core.analysis;
     13 
     14 import static org.junit.Assert.assertEquals;
     15 import static org.junit.Assert.assertFalse;
     16 import static org.junit.Assert.assertNull;
     17 import static org.junit.Assert.assertTrue;
     18 import static org.junit.Assert.fail;
     19 
     20 import java.io.ByteArrayInputStream;
     21 import java.io.ByteArrayOutputStream;
     22 import java.io.File;
     23 import java.io.FileOutputStream;
     24 import java.io.InputStream;
     25 import java.io.IOException;
     26 import java.io.OutputStream;
     27 import java.util.Arrays;
     28 import java.util.Collections;
     29 import java.util.HashMap;
     30 import java.util.HashSet;
     31 import java.util.Map;
     32 import java.util.jar.JarInputStream;
     33 import java.util.jar.Pack200;
     34 import java.util.zip.GZIPOutputStream;
     35 import java.util.zip.ZipEntry;
     36 import java.util.zip.ZipOutputStream;
     37 
     38 import org.jacoco.core.data.ExecutionDataStore;
     39 import org.jacoco.core.internal.data.CRC64;
     40 import org.jacoco.core.test.TargetLoader;
     41 import org.junit.Before;
     42 import org.junit.Rule;
     43 import org.junit.Test;
     44 import org.junit.rules.TemporaryFolder;
     45 
     46 /**
     47  * Unit tests for {@link Analyzer}.
     48  */
     49 public class AnalyzerTest {
     50 
     51 	@Rule
     52 	public TemporaryFolder folder = new TemporaryFolder();
     53 
     54 	private Analyzer analyzer;
     55 
     56 	private Map<String, IClassCoverage> classes;
     57 
     58 	private ExecutionDataStore executionData;
     59 
     60 	private class EmptyStructureVisitor implements ICoverageVisitor {
     61 
     62 		public void visitCoverage(IClassCoverage coverage) {
     63 			final String name = coverage.getName();
     64 			assertNull("Class already processed: " + name,
     65 					classes.put(name, coverage));
     66 		}
     67 	}
     68 
     69 	@Before
     70 	public void setup() {
     71 		classes = new HashMap<String, IClassCoverage>();
     72 		executionData = new ExecutionDataStore();
     73 		analyzer = new Analyzer(executionData, new EmptyStructureVisitor());
     74 	}
     75 
     76 	@Test
     77 	public void testAnalyzeClassFromStream() throws IOException {
     78 		analyzer.analyzeClass(TargetLoader.getClassData(AnalyzerTest.class),
     79 				"Test");
     80 		assertClasses("org/jacoco/core/analysis/AnalyzerTest");
     81 	}
     82 
     83 	@Test
     84 	public void testAnalyzeClassFromByteArray() throws IOException {
     85 		analyzer.analyzeClass(
     86 				TargetLoader.getClassDataAsBytes(AnalyzerTest.class), "Test");
     87 		assertClasses("org/jacoco/core/analysis/AnalyzerTest");
     88 		assertFalse(classes.get("org/jacoco/core/analysis/AnalyzerTest")
     89 				.isNoMatch());
     90 	}
     91 
     92 	@Test
     93 	public void testAnalyzeClassIdMatch() throws IOException {
     94 		// class IDs are always calculated after downgrade of the version
     95 		final byte[] bytes = TargetLoader
     96 				.getClassDataAsBytes(AnalyzerTest.class);
     97 		executionData.get(Long.valueOf(CRC64.classId(bytes)),
     98 				"org/jacoco/core/analysis/AnalyzerTest", 200);
     99 		analyzer.analyzeClass(bytes, "Test");
    100 		assertFalse(classes.get("org/jacoco/core/analysis/AnalyzerTest")
    101 				.isNoMatch());
    102 	}
    103 
    104 	@Test
    105 	public void testAnalyzeClassNoIdMatch() throws IOException {
    106 		executionData.get(Long.valueOf(0),
    107 				"org/jacoco/core/analysis/AnalyzerTest", 200);
    108 		analyzer.analyzeClass(
    109 				TargetLoader.getClassDataAsBytes(AnalyzerTest.class), "Test");
    110 		assertTrue(classes.get("org/jacoco/core/analysis/AnalyzerTest")
    111 				.isNoMatch());
    112 	}
    113 
    114 	@Test
    115 	public void testAnalyzeClass_Broken() throws IOException {
    116 		final byte[] brokenclass = TargetLoader
    117 				.getClassDataAsBytes(AnalyzerTest.class);
    118 		brokenclass[10] = 0x23;
    119 		try {
    120 			analyzer.analyzeClass(brokenclass, "Broken.class");
    121 			fail("expected exception");
    122 		} catch (IOException e) {
    123 			assertEquals("Error while analyzing Broken.class.", e.getMessage());
    124 		}
    125 	}
    126 
    127 	private static class BrokenInputStream extends InputStream {
    128 		@Override
    129 		public int read() throws IOException {
    130 			throw new IOException();
    131 		}
    132 	}
    133 
    134 	/**
    135 	 * Triggers exception in
    136 	 * {@link Analyzer#analyzeClass(InputStream, String)}.
    137 	 */
    138 	@Test
    139 	public void testAnalyzeClass_BrokenStream() throws IOException {
    140 		try {
    141 			analyzer.analyzeClass(new BrokenInputStream(), "BrokenStream");
    142 			fail("exception expected");
    143 		} catch (IOException e) {
    144 			assertEquals("Error while analyzing BrokenStream.", e.getMessage());
    145 		}
    146 	}
    147 
    148 	@Test
    149 	public void testAnalyzeAll_Class() throws IOException {
    150 		final int count = analyzer.analyzeAll(
    151 				TargetLoader.getClassData(AnalyzerTest.class), "Test");
    152 		assertEquals(1, count);
    153 		assertClasses("org/jacoco/core/analysis/AnalyzerTest");
    154 	}
    155 
    156 	@Test
    157 	public void testAnalyzeAll_Zip() throws IOException {
    158 		final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    159 		final ZipOutputStream zip = new ZipOutputStream(buffer);
    160 		zip.putNextEntry(new ZipEntry(
    161 				"org/jacoco/core/analysis/AnalyzerTest.class"));
    162 		zip.write(TargetLoader.getClassDataAsBytes(AnalyzerTest.class));
    163 		zip.finish();
    164 		final int count = analyzer.analyzeAll(
    165 				new ByteArrayInputStream(buffer.toByteArray()), "Test");
    166 		assertEquals(1, count);
    167 		assertClasses("org/jacoco/core/analysis/AnalyzerTest");
    168 	}
    169 
    170 	@Test
    171 	public void testAnalyzeAll_EmptyZipEntry() throws IOException {
    172 		final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    173 		final ZipOutputStream zip = new ZipOutputStream(buffer);
    174 		zip.putNextEntry(new ZipEntry("empty.txt"));
    175 		zip.finish();
    176 		final int count = analyzer.analyzeAll(
    177 				new ByteArrayInputStream(buffer.toByteArray()), "Test");
    178 		assertEquals(0, count);
    179 	}
    180 
    181 	/**
    182 	 * Triggers exception in
    183 	 * {@link Analyzer#analyzeAll(java.io.InputStream, String)}.
    184 	 */
    185 	@Test
    186 	public void testAnalyzeAll_Broken() throws IOException {
    187 		try {
    188 			analyzer.analyzeAll(new BrokenInputStream(), "Test");
    189 			fail("expected exception");
    190 		} catch (IOException e) {
    191 			assertEquals("Error while analyzing Test.", e.getMessage());
    192 		}
    193 	}
    194 
    195 	/**
    196 	 * Triggers exception in
    197 	 * {@link Analyzer#analyzeGzip(java.io.InputStream, String)}.
    198 	 */
    199 	@Test
    200 	public void testAnalyzeAll_BrokenGZ() {
    201 		final byte[] buffer = new byte[] { 0x1f, (byte) 0x8b, 0x00, 0x00 };
    202 		try {
    203 			analyzer.analyzeAll(new ByteArrayInputStream(buffer), "Test.gz");
    204 			fail("expected exception");
    205 		} catch (IOException e) {
    206 			assertEquals("Error while analyzing Test.gz.", e.getMessage());
    207 		}
    208 	}
    209 
    210 	@Test
    211 	public void testAnalyzeAll_Pack200() throws IOException {
    212 		final ByteArrayOutputStream zipbuffer = new ByteArrayOutputStream();
    213 		final ZipOutputStream zip = new ZipOutputStream(zipbuffer);
    214 		zip.putNextEntry(new ZipEntry(
    215 				"org/jacoco/core/analysis/AnalyzerTest.class"));
    216 		zip.write(TargetLoader.getClassDataAsBytes(AnalyzerTest.class));
    217 		zip.finish();
    218 
    219 		final ByteArrayOutputStream pack200buffer = new ByteArrayOutputStream();
    220 		GZIPOutputStream gzipOutput = new GZIPOutputStream(pack200buffer);
    221 		Pack200.newPacker().pack(
    222 				new JarInputStream(new ByteArrayInputStream(
    223 						zipbuffer.toByteArray())), gzipOutput);
    224 		gzipOutput.finish();
    225 
    226 		final int count = analyzer.analyzeAll(new ByteArrayInputStream(
    227 				pack200buffer.toByteArray()), "Test");
    228 		assertEquals(1, count);
    229 		assertClasses("org/jacoco/core/analysis/AnalyzerTest");
    230 	}
    231 
    232 	/**
    233 	 * Triggers exception in
    234 	 * {@link Analyzer#analyzePack200(java.io.InputStream, String)}.
    235 	 */
    236 	@Test
    237 	public void testAnalyzeAll_BrokenPack200() {
    238 		final byte[] buffer = new byte[] { (byte) 0xca, (byte) 0xfe,
    239 				(byte) 0xd0, 0x0d };
    240 		try {
    241 			analyzer.analyzeAll(new ByteArrayInputStream(buffer),
    242 					"Test.pack200");
    243 			fail("expected exception");
    244 		} catch (IOException e) {
    245 			assertEquals("Error while analyzing Test.pack200.", e.getMessage());
    246 		}
    247 	}
    248 
    249 	@Test
    250 	public void testAnalyzeAll_Empty() throws IOException {
    251 		final int count = analyzer.analyzeAll(new ByteArrayInputStream(
    252 				new byte[0]), "Test");
    253 		assertEquals(0, count);
    254 		assertEquals(Collections.emptyMap(), classes);
    255 	}
    256 
    257 	@Test
    258 	public void testAnalyzeAll_Folder() throws IOException {
    259 		createClassfile("bin1", AnalyzerTest.class);
    260 		final int count = analyzer.analyzeAll(folder.getRoot());
    261 		assertEquals(1, count);
    262 		assertClasses("org/jacoco/core/analysis/AnalyzerTest");
    263 	}
    264 
    265 	@Test
    266 	public void testAnalyzeAll_Path() throws IOException {
    267 		createClassfile("bin1", Analyzer.class);
    268 		createClassfile("bin2", AnalyzerTest.class);
    269 		String path = "bin1" + File.pathSeparator + "bin2";
    270 		final int count = analyzer.analyzeAll(path, folder.getRoot());
    271 		assertEquals(2, count);
    272 		assertClasses("org/jacoco/core/analysis/Analyzer",
    273 				"org/jacoco/core/analysis/AnalyzerTest");
    274 	}
    275 
    276 	/**
    277 	 * Triggers exception in
    278 	 * {@link Analyzer#nextEntry(java.util.zip.ZipInputStream, String)}.
    279 	 */
    280 	@Test
    281 	public void testAnalyzeAll_BrokenZip() {
    282 		final byte[] buffer = new byte[30];
    283 		buffer[0] = 0x50;
    284 		buffer[1] = 0x4b;
    285 		buffer[2] = 0x03;
    286 		buffer[3] = 0x04;
    287 		Arrays.fill(buffer, 4, buffer.length, (byte) 0x42);
    288 		try {
    289 			analyzer.analyzeAll(new ByteArrayInputStream(buffer), "Test.zip");
    290 			fail("expected exception");
    291 		} catch (IOException e) {
    292 			assertEquals("Error while analyzing Test.zip.", e.getMessage());
    293 		}
    294 	}
    295 
    296 	/**
    297 	 * With JDK 5 triggers exception in
    298 	 * {@link Analyzer#nextEntry(ZipInputStream, String)},
    299 	 * i.e. message will contain only "broken.zip".
    300 	 *
    301 	 * With JDK > 5 triggers exception in
    302 	 * {@link Analyzer#analyzeAll(java.io.InputStream, String)},
    303 	 * i.e. message will contain only "broken.zip (at) brokenentry.txt".
    304 	 */
    305 	@Test
    306 	public void testAnalyzeAll_BrokenZipEntry() throws IOException {
    307 		File file = new File(folder.getRoot(), "broken.zip");
    308 		OutputStream out = new FileOutputStream(file);
    309 		ZipOutputStream zip = new ZipOutputStream(out);
    310 		zip.putNextEntry(new ZipEntry("brokenentry.txt"));
    311 		out.write(0x23); // Unexpected data here
    312 		zip.close();
    313 		try {
    314 			analyzer.analyzeAll(file);
    315 			fail("expected exception");
    316 		} catch (IOException e) {
    317 			assertTrue(e.getMessage().startsWith("Error while analyzing"));
    318 			assertTrue(e.getMessage().contains("broken.zip"));
    319 		}
    320 	}
    321 
    322 	/**
    323 	 * Triggers exception in
    324 	 * {@link Analyzer#analyzeClass(java.io.InputStream, String)}.
    325 	 */
    326 	@Test
    327 	public void testAnalyzeAll_BrokenClassFileInZip() throws IOException {
    328 		final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    329 		final ZipOutputStream zip = new ZipOutputStream(buffer);
    330 		zip.putNextEntry(new ZipEntry(
    331 				"org/jacoco/core/analysis/AnalyzerTest.class"));
    332 		final byte[] brokenclass = TargetLoader
    333 				.getClassDataAsBytes(AnalyzerTest.class);
    334 		brokenclass[10] = 0x23;
    335 		zip.write(brokenclass);
    336 		zip.finish();
    337 
    338 		try {
    339 			analyzer.analyzeAll(new ByteArrayInputStream(buffer.toByteArray()),
    340 					"test.zip");
    341 			fail("expected exception");
    342 		} catch (IOException e) {
    343 			assertEquals(
    344 					"Error while analyzing test.zip@org/jacoco/core/analysis/AnalyzerTest.class.",
    345 					e.getMessage());
    346 		}
    347 	}
    348 
    349 	private void createClassfile(final String dir, final Class<?> source)
    350 			throws IOException {
    351 		File file = new File(folder.getRoot(), dir);
    352 		file.mkdirs();
    353 		file = new File(file, "some.class");
    354 		OutputStream out = new FileOutputStream(file);
    355 		out.write(TargetLoader.getClassDataAsBytes(source));
    356 		out.close();
    357 	}
    358 
    359 	private void assertClasses(String... classNames) {
    360 		assertEquals(new HashSet<String>(Arrays.asList(classNames)),
    361 				classes.keySet());
    362 	}
    363 
    364 }
    365