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