diff options
author | Marc R. Hoffmann <hoffmann@mountainminds.com> | 2013-05-20 12:56:11 +0200 |
---|---|---|
committer | Marc R. Hoffmann <hoffmann@mountainminds.com> | 2013-05-27 06:55:19 +0200 |
commit | ac07e252571819685d3f74cb69c90c23abd340a0 (patch) | |
tree | 1bf566cccd3830251dc96bd08c5d2f019a80aa32 | |
parent | 65d0700ffc0988767ca4c003b22063e56d2e837f (diff) | |
download | jacoco-ac07e252571819685d3f74cb69c90c23abd340a0.tar.gz |
Context information for error messages.
Analyzer and Instrumenter now expect a resource name parameter to
provide better messages in case of internal errors.
20 files changed, 312 insertions, 148 deletions
diff --git a/jacoco-maven-plugin/src/org/jacoco/maven/InstrumentMojo.java b/jacoco-maven-plugin/src/org/jacoco/maven/InstrumentMojo.java index 0888b796..d5af656a 100644 --- a/jacoco-maven-plugin/src/org/jacoco/maven/InstrumentMojo.java +++ b/jacoco-maven-plugin/src/org/jacoco/maven/InstrumentMojo.java @@ -11,13 +11,6 @@ *******************************************************************************/ package org.jacoco.maven; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.codehaus.plexus.util.FileUtils; -import org.codehaus.plexus.util.IOUtil; -import org.jacoco.core.instr.Instrumenter; -import org.jacoco.core.runtime.OfflineInstrumentationAccessGenerator; - import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -26,6 +19,13 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.List; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.jacoco.core.instr.Instrumenter; +import org.jacoco.core.runtime.OfflineInstrumentationAccessGenerator; + /** * Performs offline instrumentation. Note that after execution of test you must * restore original classes with help of "restore-instrumented-classes" goal. @@ -59,8 +59,8 @@ public class InstrumentMojo extends AbstractJacocoMojo { final List<String> fileNames; try { - fileNames = new FileFilter(this.getIncludes(), - this.getExcludes()).getFileNames(classesDir); + fileNames = new FileFilter(this.getIncludes(), this.getExcludes()) + .getFileNames(classesDir); } catch (final IOException e1) { throw new MojoExecutionException( "Unable to get list of files to instrument.", e1); @@ -78,7 +78,7 @@ public class InstrumentMojo extends AbstractJacocoMojo { FileUtils.copyFile(source, backup); input = new FileInputStream(backup); output = new FileOutputStream(source); - instrumenter.instrument(input, output); + instrumenter.instrument(input, output, source.getPath()); } catch (final IOException e2) { throw new MojoExecutionException( "Unable to instrument file.", e2); diff --git a/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/CoverageTransformerTest.java b/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/CoverageTransformerTest.java index 563f531f..98cca18c 100644 --- a/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/CoverageTransformerTest.java +++ b/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/CoverageTransformerTest.java @@ -139,7 +139,7 @@ public class CoverageTransformerTest { } recorder.assertException(IllegalClassFormatException.class, "Error while instrumenting class org.jacoco.Sample.", - NullPointerException.class); + IOException.class); recorder.clear(); } diff --git a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/CoverageTransformer.java b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/CoverageTransformer.java index c53e7d42..a2b35f24 100644 --- a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/CoverageTransformer.java +++ b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/CoverageTransformer.java @@ -11,8 +11,6 @@ *******************************************************************************/ package org.jacoco.agent.rt.internal; -import static java.lang.String.format; - import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; @@ -86,10 +84,10 @@ public class CoverageTransformer implements ClassFileTransformer { // reference as probes might have changed. runtime.disconnect(classBeingRedefined); } - return instrumenter.instrument(classfileBuffer); + return instrumenter.instrument(classfileBuffer, classname); } catch (final Exception ex) { final IllegalClassFormatException wrapper = new IllegalClassFormatException( - format("Error while instrumenting class %s.", classname)); + ex.getMessage()); wrapper.initCause(ex); // Report this, as the exception is ignored by the JVM: logger.logExeption(wrapper); diff --git a/org.jacoco.ant/src/org/jacoco/ant/InstrumentTask.java b/org.jacoco.ant/src/org/jacoco/ant/InstrumentTask.java index 09b8aec2..345197a8 100644 --- a/org.jacoco.ant/src/org/jacoco/ant/InstrumentTask.java +++ b/org.jacoco.ant/src/org/jacoco/ant/InstrumentTask.java @@ -88,7 +88,8 @@ public class InstrumentTask extends Task { try { input = resource.getInputStream(); output = new FileOutputStream(file); - return instrumenter.instrumentAll(input, output); + return instrumenter.instrumentAll(input, output, + resource.getName()); } finally { FileUtils.close(input); FileUtils.close(output); diff --git a/org.jacoco.ant/src/org/jacoco/ant/ReportTask.java b/org.jacoco.ant/src/org/jacoco/ant/ReportTask.java index 329914ff..21dc34c2 100644 --- a/org.jacoco.ant/src/org/jacoco/ant/ReportTask.java +++ b/org.jacoco.ant/src/org/jacoco/ant/ReportTask.java @@ -568,7 +568,7 @@ public class ReportTask extends Task { analyzer.analyzeAll(((FileResource) resource).getFile()); } else { final InputStream in = resource.getInputStream(); - analyzer.analyzeAll(in); + analyzer.analyzeAll(in, resource.getName()); in.close(); } } diff --git a/org.jacoco.core.test/src/org/jacoco/core/analysis/AnalyzerTest.java b/org.jacoco.core.test/src/org/jacoco/core/analysis/AnalyzerTest.java index 741f63e1..69a601c4 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/analysis/AnalyzerTest.java +++ b/org.jacoco.core.test/src/org/jacoco/core/analysis/AnalyzerTest.java @@ -13,6 +13,7 @@ package org.jacoco.core.analysis; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -65,7 +66,8 @@ public class AnalyzerTest { @Test public void testAnalyzeClass1() throws IOException { - analyzer.analyzeClass(TargetLoader.getClassData(AnalyzerTest.class)); + analyzer.analyzeClass(TargetLoader.getClassData(AnalyzerTest.class), + "Test"); assertEquals( Collections.singleton("org/jacoco/core/analysis/AnalyzerTest"), classes); @@ -73,17 +75,30 @@ public class AnalyzerTest { @Test public void testAnalyzeClass2() throws IOException { - analyzer.analyzeClass(TargetLoader - .getClassDataAsBytes(AnalyzerTest.class)); + analyzer.analyzeClass( + TargetLoader.getClassDataAsBytes(AnalyzerTest.class), "Test"); assertEquals( Collections.singleton("org/jacoco/core/analysis/AnalyzerTest"), classes); } @Test + public void testAnalyzeClass_Broken() throws IOException { + final byte[] brokenclass = TargetLoader + .getClassDataAsBytes(AnalyzerTest.class); + brokenclass[10] = 0x23; + try { + analyzer.analyzeClass(brokenclass, "Broken"); + fail(); + } catch (IOException e) { + assertEquals("Error while analyzing class Broken.", e.getMessage()); + } + } + + @Test public void testAnalyzeAll_Class() throws IOException { - final int count = analyzer.analyzeAll(TargetLoader - .getClassData(AnalyzerTest.class)); + final int count = analyzer.analyzeAll( + TargetLoader.getClassData(AnalyzerTest.class), "Test"); assertEquals(1, count); assertEquals( Collections.singleton("org/jacoco/core/analysis/AnalyzerTest"), @@ -98,8 +113,8 @@ public class AnalyzerTest { "org/jacoco/core/analysis/AnalyzerTest.class")); zip.write(TargetLoader.getClassDataAsBytes(AnalyzerTest.class)); zip.finish(); - final int count = analyzer.analyzeAll(new ByteArrayInputStream(buffer - .toByteArray())); + final int count = analyzer.analyzeAll( + new ByteArrayInputStream(buffer.toByteArray()), "Test"); assertEquals(1, count); assertEquals( Collections.singleton("org/jacoco/core/analysis/AnalyzerTest"), @@ -123,7 +138,7 @@ public class AnalyzerTest { gzipOutput.finish(); final int count = analyzer.analyzeAll(new ByteArrayInputStream( - pack200buffer.toByteArray())); + pack200buffer.toByteArray()), "Test"); assertEquals(1, count); assertEquals( Collections.singleton("org/jacoco/core/analysis/AnalyzerTest"), @@ -133,7 +148,7 @@ public class AnalyzerTest { @Test public void testAnalyzeAll_Empty() throws IOException { final int count = analyzer.analyzeAll(new ByteArrayInputStream( - new byte[0])); + new byte[0]), "Test"); assertEquals(0, count); assertEquals(Collections.emptySet(), classes); } @@ -172,6 +187,29 @@ public class AnalyzerTest { analyzer.analyzeAll(file); } + @Test + public void testAnalyzeAll_BrokenClassFileInZip() throws IOException { + final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + final ZipOutputStream zip = new ZipOutputStream(buffer); + zip.putNextEntry(new ZipEntry( + "org/jacoco/core/analysis/AnalyzerTest.class")); + final byte[] brokenclass = TargetLoader + .getClassDataAsBytes(AnalyzerTest.class); + brokenclass[10] = 0x23; + zip.write(brokenclass); + zip.finish(); + + try { + analyzer.analyzeAll(new ByteArrayInputStream(buffer.toByteArray()), + "test.zip"); + fail(); + } catch (IOException e) { + assertEquals( + "Error while analyzing class test.zip@org/jacoco/core/analysis/AnalyzerTest.class.", + e.getMessage()); + } + } + private void createClassfile(final String dir, final Class<?> source) throws IOException { File file = new File(folder.getRoot(), dir); diff --git a/org.jacoco.core.test/src/org/jacoco/core/instr/InstrumenterTest.java b/org.jacoco.core.test/src/org/jacoco/core/instr/InstrumenterTest.java index 26b8a8f2..50bc200c 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/instr/InstrumenterTest.java +++ b/org.jacoco.core.test/src/org/jacoco/core/instr/InstrumenterTest.java @@ -13,6 +13,7 @@ package org.jacoco.core.instr; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -31,6 +32,7 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; +import org.jacoco.core.analysis.AnalyzerTest; import org.jacoco.core.runtime.RuntimeData; import org.jacoco.core.runtime.SystemPropertiesRuntime; import org.jacoco.core.test.TargetLoader; @@ -80,10 +82,49 @@ public class InstrumenterTest { } @Test + public void testInstrumentClass() throws Exception { + byte[] bytes = instrumenter.instrument( + TargetLoader.getClassDataAsBytes(InstrumenterTest.class), + "Test"); + TargetLoader loader = new TargetLoader(InstrumenterTest.class, bytes); + Class<?> clazz = loader.getTargetClass(); + assertEquals("org.jacoco.core.instr.InstrumenterTest", clazz.getName()); + } + + @Test + public void testInstrumentBrokenClass1() throws IOException { + final byte[] brokenclass = TargetLoader + .getClassDataAsBytes(AnalyzerTest.class); + brokenclass[10] = 0x23; + try { + instrumenter.instrument(brokenclass, "Broken"); + fail(); + } catch (IOException e) { + assertEquals("Error while instrumenting class Broken.", + e.getMessage()); + } + } + + @Test + public void testInstrumentBrokenClass2() throws IOException { + final byte[] brokenclass = TargetLoader + .getClassDataAsBytes(AnalyzerTest.class); + brokenclass[10] = 0x23; + try { + instrumenter.instrument(new ByteArrayInputStream(brokenclass), + "Broken"); + fail(); + } catch (IOException e) { + assertEquals("Error while instrumenting class Broken.", + e.getMessage()); + } + } + + @Test public void testSerialization() throws Exception { // Create instrumented instance: - byte[] bytes = instrumenter.instrument(TargetLoader - .getClassData(SerializationTarget.class)); + byte[] bytes = instrumenter.instrument( + TargetLoader.getClassData(SerializationTarget.class), "Test"); TargetLoader loader = new TargetLoader(SerializationTarget.class, bytes); Object obj1 = loader.getTargetClass() .getConstructor(String.class, Integer.TYPE) @@ -104,7 +145,7 @@ public class InstrumenterTest { InputStream in = TargetLoader.getClassData(getClass()); OutputStream out = new ByteArrayOutputStream(); - int count = instrumenter.instrumentAll(in, out); + int count = instrumenter.instrumentAll(in, out, "Test"); assertEquals(1, count); } @@ -119,7 +160,7 @@ public class InstrumenterTest { ByteArrayOutputStream out = new ByteArrayOutputStream(); int count = instrumenter.instrumentAll( - new ByteArrayInputStream(buffer.toByteArray()), out); + new ByteArrayInputStream(buffer.toByteArray()), out, "Test"); assertEquals(1, count); ZipInputStream zipin = new ZipInputStream(new ByteArrayInputStream( @@ -129,6 +170,29 @@ public class InstrumenterTest { } @Test + public void testInstrumentAll_BrokenClassFileInZip() throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + ZipOutputStream zipout = new ZipOutputStream(buffer); + zipout.putNextEntry(new ZipEntry("Test.class")); + final byte[] brokenclass = TargetLoader.getClassDataAsBytes(getClass()); + brokenclass[10] = 0x23; + zipout.write(brokenclass); + zipout.finish(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + instrumenter.instrumentAll( + new ByteArrayInputStream(buffer.toByteArray()), out, + "test.zip"); + fail(); + } catch (IOException e) { + assertEquals( + "Error while instrumenting class test.zip@Test.class.", + e.getMessage()); + } + } + + @Test public void testInstrumentAll_Pack200() throws IOException { ByteArrayOutputStream jarbuffer = new ByteArrayOutputStream(); ZipOutputStream zipout = new ZipOutputStream(jarbuffer); @@ -145,7 +209,7 @@ public class InstrumenterTest { ByteArrayOutputStream out = new ByteArrayOutputStream(); int count = instrumenter.instrumentAll(new ByteArrayInputStream( - pack200buffer.toByteArray()), out); + pack200buffer.toByteArray()), out, "Test"); jarbuffer.reset(); Pack200.newUnpacker() @@ -164,7 +228,7 @@ public class InstrumenterTest { InputStream in = new ByteArrayInputStream("text".getBytes()); ByteArrayOutputStream out = new ByteArrayOutputStream(); - int count = instrumenter.instrumentAll(in, out); + int count = instrumenter.instrumentAll(in, out, "Test"); assertEquals(0, count); assertEquals("text", new String(out.toByteArray())); diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/perf/AnalysisTimeScenario.java b/org.jacoco.core.test/src/org/jacoco/core/test/perf/AnalysisTimeScenario.java index bcc57e72..846eb821 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/test/perf/AnalysisTimeScenario.java +++ b/org.jacoco.core.test/src/org/jacoco/core/test/perf/AnalysisTimeScenario.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.jacoco.core.test.perf; +import java.util.concurrent.Callable; + import org.jacoco.core.analysis.Analyzer; import org.jacoco.core.analysis.IClassCoverage; import org.jacoco.core.analysis.ICoverageVisitor; @@ -33,7 +35,7 @@ public class AnalysisTimeScenario extends TimedScenario { } @Override - protected Runnable getInstrumentedRunnable() throws Exception { + protected Callable<Void> getInstrumentedCallable() throws Exception { final byte[] bytes = TargetLoader.getClassDataAsBytes(target); final ExecutionDataStore executionData = new ExecutionDataStore(); ICoverageVisitor visitor = new ICoverageVisitor() { @@ -41,11 +43,12 @@ public class AnalysisTimeScenario extends TimedScenario { } }; final Analyzer analyzer = new Analyzer(executionData, visitor); - return new Runnable() { - public void run() { + return new Callable<Void>() { + public Void call() throws Exception { for (int i = 0; i < count; i++) { - analyzer.analyzeClass(bytes); + analyzer.analyzeClass(bytes, target.getName()); } + return null; } }; } diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/perf/ExecuteInstrumentedCodeScenario.java b/org.jacoco.core.test/src/org/jacoco/core/test/perf/ExecuteInstrumentedCodeScenario.java index 98afdde6..d78156e1 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/test/perf/ExecuteInstrumentedCodeScenario.java +++ b/org.jacoco.core.test/src/org/jacoco/core/test/perf/ExecuteInstrumentedCodeScenario.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.jacoco.core.test.perf; +import java.util.concurrent.Callable; + import org.jacoco.core.instr.Instrumenter; import org.jacoco.core.runtime.IRuntime; import org.jacoco.core.runtime.LoggerRuntime; @@ -24,16 +26,17 @@ import org.objectweb.asm.ClassReader; */ public class ExecuteInstrumentedCodeScenario extends TimedScenario { - private final Class<? extends Runnable> target; + private final Class<? extends Callable<Void>> target; protected ExecuteInstrumentedCodeScenario(String description, - Class<? extends Runnable> target) { + Class<? extends Callable<Void>> target) { super(description); this.target = target; } @Override - protected Runnable getInstrumentedRunnable() throws Exception { + @SuppressWarnings("unchecked") + protected Callable<Void> getInstrumentedCallable() throws Exception { ClassReader reader = new ClassReader(TargetLoader.getClassData(target)); IRuntime runtime = new LoggerRuntime(); runtime.startup(new RuntimeData()); @@ -41,11 +44,11 @@ public class ExecuteInstrumentedCodeScenario extends TimedScenario { final byte[] instrumentedBuffer = instr.instrument(reader); final TargetLoader loader = new TargetLoader(target, instrumentedBuffer); - return (Runnable) loader.newTargetInstance(); + return (Callable<Void>) loader.newTargetInstance(); } @Override - protected Runnable getReferenceRunnable() throws Exception { + protected Callable<Void> getReferenceCallable() throws Exception { return target.newInstance(); } diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/perf/InstrumentationTimeScenario.java b/org.jacoco.core.test/src/org/jacoco/core/test/perf/InstrumentationTimeScenario.java index a5138504..2c171421 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/test/perf/InstrumentationTimeScenario.java +++ b/org.jacoco.core.test/src/org/jacoco/core/test/perf/InstrumentationTimeScenario.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.jacoco.core.test.perf; +import java.util.concurrent.Callable; + import org.jacoco.core.instr.Instrumenter; import org.jacoco.core.runtime.LoggerRuntime; import org.jacoco.core.test.TargetLoader; @@ -31,14 +33,15 @@ public class InstrumentationTimeScenario extends TimedScenario { } @Override - protected Runnable getInstrumentedRunnable() throws Exception { + protected Callable<Void> getInstrumentedCallable() throws Exception { final byte[] bytes = TargetLoader.getClassDataAsBytes(target); final Instrumenter instr = new Instrumenter(new LoggerRuntime()); - return new Runnable() { - public void run() { + return new Callable<Void>() { + public Void call() throws Exception { for (int i = 0; i < count; i++) { - instr.instrument(bytes); + instr.instrument(bytes, "TestTarget"); } + return null; } }; } diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/perf/TimedScenario.java b/org.jacoco.core.test/src/org/jacoco/core/test/perf/TimedScenario.java index 20f5f179..56693ac9 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/test/perf/TimedScenario.java +++ b/org.jacoco.core.test/src/org/jacoco/core/test/perf/TimedScenario.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.jacoco.core.test.perf; +import java.util.concurrent.Callable; + /** * Base class for execution time test scenarios. */ @@ -25,8 +27,8 @@ public abstract class TimedScenario implements IPerfScenario { } public void run(final IPerfOutput output) throws Exception { - final long time = getMinimumTime(getInstrumentedRunnable()); - final Runnable refRunnable = getReferenceRunnable(); + final long time = getMinimumTime(getInstrumentedCallable()); + final Callable<Void> refRunnable = getReferenceCallable(); final long reftime; if (refRunnable == null) { reftime = IPerfOutput.NO_REFERENCE; @@ -42,8 +44,9 @@ public abstract class TimedScenario implements IPerfScenario { * * @param subject * @return minimum execution time in nano seconds + * @throws Exception */ - private long getMinimumTime(final Runnable subject) { + private long getMinimumTime(final Callable<Void> subject) throws Exception { long min = Long.MAX_VALUE; for (int i = 0; i < RUNS; i++) { final long t = getTime(subject); @@ -52,15 +55,16 @@ public abstract class TimedScenario implements IPerfScenario { return min; } - private long getTime(final Runnable subject) { + private long getTime(final Callable<Void> subject) throws Exception { long start = System.nanoTime(); - subject.run(); + subject.call(); return System.nanoTime() - start; } - protected abstract Runnable getInstrumentedRunnable() throws Exception; + protected abstract Callable<Void> getInstrumentedCallable() + throws Exception; - protected Runnable getReferenceRunnable() throws Exception { + protected Callable<Void> getReferenceCallable() throws Exception { return null; } diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/perf/targets/Target01.java b/org.jacoco.core.test/src/org/jacoco/core/test/perf/targets/Target01.java index 521ca831..06de916e 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/test/perf/targets/Target01.java +++ b/org.jacoco.core.test/src/org/jacoco/core/test/perf/targets/Target01.java @@ -11,21 +11,24 @@ *******************************************************************************/ package org.jacoco.core.test.perf.targets; +import java.util.concurrent.Callable; + /** * Plain method calls. */ -public class Target01 implements Runnable { +public class Target01 implements Callable<Void> { @SuppressWarnings("unused") private int c; // 4 ^ 0 = 1 times - public void run() { + public Void call() throws Exception { m1(); m1(); m1(); m1(); c++; // some side effect, otherwise the JIT will remove the method + return null; } // 4 ^ 1 = 4 times diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/perf/targets/Target02.java b/org.jacoco.core.test/src/org/jacoco/core/test/perf/targets/Target02.java index c36946a1..b32d8191 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/test/perf/targets/Target02.java +++ b/org.jacoco.core.test/src/org/jacoco/core/test/perf/targets/Target02.java @@ -11,17 +11,20 @@ *******************************************************************************/ package org.jacoco.core.test.perf.targets; +import java.util.concurrent.Callable; + /** * Simple Loop. */ -public class Target02 implements Runnable { +public class Target02 implements Callable<Void> { - public void run() { + public Void call() throws Exception { @SuppressWarnings("unused") int count = 0; for (int i = 0; i < 10000000; i++) { count++; } + return null; } } diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/perf/targets/Target03.java b/org.jacoco.core.test/src/org/jacoco/core/test/perf/targets/Target03.java index 25eac46d..c104e7da 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/test/perf/targets/Target03.java +++ b/org.jacoco.core.test/src/org/jacoco/core/test/perf/targets/Target03.java @@ -12,6 +12,7 @@ package org.jacoco.core.test.perf.targets; import java.util.Random; +import java.util.concurrent.Callable; /** * "Game of Life" implementation as a more reference scenario. Obviously the @@ -19,7 +20,7 @@ import java.util.Random; * runner targets one class only. Also one could think about more efficient * implementations which again is not the focus here. */ -public class Target03 implements Runnable { +public class Target03 implements Callable<Void> { private final int width; @@ -130,12 +131,13 @@ public class Target03 implements Runnable { return sb.toString(); } - public void run() { + public Void call() throws Exception { clear(); randomFill(123, width * height / 2); for (int i = 0; i < 20; i++) { tick(); } + return null; } // Demo diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/validation/ClassFileVersionsTest.java b/org.jacoco.core.test/src/org/jacoco/core/test/validation/ClassFileVersionsTest.java index 6b6e5c10..6b5d2bfc 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/test/validation/ClassFileVersionsTest.java +++ b/org.jacoco.core.test/src/org/jacoco/core/test/validation/ClassFileVersionsTest.java @@ -25,6 +25,8 @@ import static org.objectweb.asm.Opcodes.V1_5; import static org.objectweb.asm.Opcodes.V1_6; import static org.objectweb.asm.Opcodes.V1_7; +import java.io.IOException; + import org.jacoco.core.instr.Instrumenter; import org.jacoco.core.runtime.IRuntime; import org.jacoco.core.runtime.SystemPropertiesRuntime; @@ -41,46 +43,46 @@ import org.objectweb.asm.Opcodes; public class ClassFileVersionsTest { @Test - public void test_1_1() { + public void test_1_1() throws IOException { testVersion(V1_1, false); } @Test - public void test_1_2() { + public void test_1_2() throws IOException { testVersion(V1_2, false); } @Test - public void test_1_3() { + public void test_1_3() throws IOException { testVersion(V1_3, false); } @Test - public void test_1_4() { + public void test_1_4() throws IOException { testVersion(V1_4, false); } @Test - public void test_1_5() { + public void test_1_5() throws IOException { testVersion(V1_5, false); } @Test - public void test_1_6() { + public void test_1_6() throws IOException { testVersion(V1_6, true); } @Test - public void test_1_7() { + public void test_1_7() throws IOException { testVersion(V1_7, true); } - private void testVersion(int version, boolean frames) { + private void testVersion(int version, boolean frames) throws IOException { final byte[] original = createClass(version); IRuntime runtime = new SystemPropertiesRuntime(); Instrumenter instrumenter = new Instrumenter(runtime); - byte[] instrumented = instrumenter.instrument(original); + byte[] instrumented = instrumenter.instrument(original, "TestTarget"); assertFrames(instrumented, frames); } diff --git a/org.jacoco.core.test/src/org/jacoco/core/test/validation/FramesTest.java b/org.jacoco.core.test/src/org/jacoco/core/test/validation/FramesTest.java index 162ccc52..f5ac4f5e 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/test/validation/FramesTest.java +++ b/org.jacoco.core.test/src/org/jacoco/core/test/validation/FramesTest.java @@ -76,7 +76,7 @@ public class FramesTest { IRuntime runtime = new SystemPropertiesRuntime(); Instrumenter instrumenter = new Instrumenter(runtime); source = calculateFrames(source); - byte[] actual = instrumenter.instrument(source); + byte[] actual = instrumenter.instrument(source, "TestTarget"); byte[] expected = calculateFrames(actual); assertEquals(dump(expected), dump(actual)); diff --git a/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java b/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java index f25da1e1..0a55213d 100644 --- a/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java +++ b/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java @@ -17,6 +17,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.StringTokenizer; import java.util.zip.GZIPInputStream; +import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.jacoco.core.data.ExecutionData; @@ -100,9 +101,18 @@ public class Analyzer { * * @param buffer * class definitions + * @param name + * a name used for exception messages + * @throws IOException + * if the class can't be analyzed */ - public void analyzeClass(final byte[] buffer) { - analyzeClass(new ClassReader(buffer)); + public void analyzeClass(final byte[] buffer, final String name) + throws IOException { + try { + analyzeClass(new ClassReader(buffer)); + } catch (final RuntimeException cause) { + throw analyzerError(name, cause); + } } /** @@ -110,27 +120,26 @@ public class Analyzer { * * @param input * stream to read class definition from + * @param name + * a name used for exception messages * @throws IOException - * if the stream can't be read + * if the stream can't be read or the class can't be analyzed */ - public void analyzeClass(final InputStream input) throws IOException { - analyzeClass(new ClassReader(input)); + public void analyzeClass(final InputStream input, final String name) + throws IOException { + try { + analyzeClass(new ClassReader(input)); + } catch (final RuntimeException e) { + throw analyzerError(name, e); + } } - /** - * Analyzes all classes contained in the ZIP archive (jar, war, ear, etc.) - * given as an input stream. Contained archives are read recursively. - * - * @param input - * ZIP archive data - * @return number of class files found - * @throws IOException - * if the stream can't be read - * @deprecated Use {@link #analyzeAll(InputStream)} instead - */ - @Deprecated - public int analyzeArchive(final InputStream input) throws IOException { - return analyzeZip(input); + private IOException analyzerError(final String name, + final RuntimeException cause) { + final IOException ex = new IOException(String.format( + "Error while analyzing class %s.", name)); + ex.initCause(cause); + return ex; } /** @@ -141,22 +150,25 @@ public class Analyzer { * * @param input * input data + * @param name + * a name used for exception messages * @return number of class files found * @throws IOException - * if the stream can't be read + * if the stream can't be read or a class can't be analyzed */ - public int analyzeAll(final InputStream input) throws IOException { + public int analyzeAll(final InputStream input, final String name) + throws IOException { final ContentTypeDetector detector = new ContentTypeDetector(input); switch (detector.getType()) { case ContentTypeDetector.CLASSFILE: - analyzeClass(detector.getInputStream()); + analyzeClass(detector.getInputStream(), name); return 1; case ContentTypeDetector.ZIPFILE: - return analyzeZip(detector.getInputStream()); + return analyzeZip(detector.getInputStream(), name); case ContentTypeDetector.GZFILE: - return analyzeGzip(detector.getInputStream()); + return analyzeGzip(detector.getInputStream(), name); case ContentTypeDetector.PACK200FILE: - return analyzePack200(detector.getInputStream()); + return analyzePack200(detector.getInputStream(), name); default: return 0; } @@ -171,7 +183,7 @@ public class Analyzer { * file or folder to look for class files * @return number of class files found * @throws IOException - * if the file can't be read + * if the file can't be read or a class can't be analyzed */ public int analyzeAll(final File file) throws IOException { int count = 0; @@ -182,7 +194,7 @@ public class Analyzer { } else { final InputStream in = new FileInputStream(file); try { - count += analyzeAll(in); + count += analyzeAll(in, file.getPath()); } finally { in.close(); } @@ -202,7 +214,7 @@ public class Analyzer { * entries * @return number of class files found * @throws IOException - * if a file can't be read + * if a file can't be read or a class can't be analyzed */ public int analyzeAll(final String path, final File basedir) throws IOException { @@ -214,21 +226,25 @@ public class Analyzer { return count; } - private int analyzeZip(final InputStream input) throws IOException { + private int analyzeZip(final InputStream input, final String name) + throws IOException { final ZipInputStream zip = new ZipInputStream(input); + ZipEntry entry; int count = 0; - while (zip.getNextEntry() != null) { - count += analyzeAll(zip); + while ((entry = zip.getNextEntry()) != null) { + count += analyzeAll(zip, name + "@" + entry.getName()); } return count; } - private int analyzeGzip(final InputStream input) throws IOException { - return analyzeAll(new GZIPInputStream(input)); + private int analyzeGzip(final InputStream input, final String name) + throws IOException { + return analyzeAll(new GZIPInputStream(input), name); } - private int analyzePack200(final InputStream input) throws IOException { - return analyzeAll(Pack200Streams.unpack(input)); + private int analyzePack200(final InputStream input, final String name) + throws IOException { + return analyzeAll(Pack200Streams.unpack(input), name); } } diff --git a/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java b/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java index 9f7c5158..c4d52669 100644 --- a/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java +++ b/org.jacoco.core/src/org/jacoco/core/instr/Instrumenter.java @@ -84,11 +84,19 @@ public class Instrumenter { * * @param buffer * definition of the class + * @param name + * a name used for exception messages * @return instrumented definition - * + * @throws IOException + * if the class can't be analyzed */ - public byte[] instrument(final byte[] buffer) { - return instrument(new ClassReader(buffer)); + public byte[] instrument(final byte[] buffer, final String name) + throws IOException { + try { + return instrument(new ClassReader(buffer)); + } catch (final RuntimeException e) { + throw instrumentError(name, e); + } } /** @@ -96,12 +104,20 @@ public class Instrumenter { * * @param input * stream to read class definition from + * @param name + * a name used for exception messages * @return instrumented definition * @throws IOException - * if reading data from the stream fails + * if reading data from the stream fails or the class can't be + * instrumented */ - public byte[] instrument(final InputStream input) throws IOException { - return instrument(new ClassReader(input)); + public byte[] instrument(final InputStream input, final String name) + throws IOException { + try { + return instrument(new ClassReader(input)); + } catch (final RuntimeException e) { + throw instrumentError(name, e); + } } /** @@ -111,32 +127,27 @@ public class Instrumenter { * stream to read class definition from * @param output * stream to write the instrumented version of the class to + * @param name + * a name used for exception messages * @throws IOException - * if reading data from the stream fails + * if reading data from the stream fails or the class can't be + * instrumented */ - public void instrument(final InputStream input, final OutputStream output) - throws IOException { - output.write(instrument(new ClassReader(input))); + public void instrument(final InputStream input, final OutputStream output, + final String name) throws IOException { + try { + output.write(instrument(new ClassReader(input))); + } catch (final RuntimeException e) { + throw instrumentError(name, e); + } } - /** - * Creates a instrumented version of the given archive, i.e. with all class - * files contained in this archive instrumented. Contained resources which - * are no class files or archive files are copied as is. - * - * @param input - * stream to read archive from - * @param output - * stream to write the instrumented version of the class to - * @return number of instrumented classes - * @throws IOException - * if reading data from the stream fails - * @deprecated Use {@link #instrumentAll(InputStream, OutputStream)} instead - */ - @Deprecated - public int instrumentArchive(final InputStream input, - final OutputStream output) throws IOException { - return instrumentZip(input, output); + private IOException instrumentError(final String name, + final RuntimeException cause) { + final IOException ex = new IOException(String.format( + "Error while instrumenting class %s.", name)); + ex.initCause(cause); + return ex; } /** @@ -148,38 +159,41 @@ public class Instrumenter { * stream to contents from * @param output * stream to write the instrumented version of the contents + * @param name + * a name used for exception messages * @return number of instrumented classes * @throws IOException - * if reading data from the stream fails + * if reading data from the stream fails or a class can't be + * instrumented */ - public int instrumentAll(final InputStream input, final OutputStream output) - throws IOException { + public int instrumentAll(final InputStream input, + final OutputStream output, final String name) throws IOException { final ContentTypeDetector detector = new ContentTypeDetector(input); switch (detector.getType()) { case ContentTypeDetector.CLASSFILE: - instrument(detector.getInputStream(), output); + instrument(detector.getInputStream(), output, name); return 1; case ContentTypeDetector.ZIPFILE: - return instrumentZip(detector.getInputStream(), output); + return instrumentZip(detector.getInputStream(), output, name); case ContentTypeDetector.GZFILE: - return instrumentGzip(detector.getInputStream(), output); + return instrumentGzip(detector.getInputStream(), output, name); case ContentTypeDetector.PACK200FILE: - return instrumentPack200(detector.getInputStream(), output); + return instrumentPack200(detector.getInputStream(), output, name); default: copy(detector.getInputStream(), output); return 0; } } - private int instrumentZip(final InputStream input, final OutputStream output) - throws IOException { + private int instrumentZip(final InputStream input, + final OutputStream output, final String name) throws IOException { final ZipInputStream zipin = new ZipInputStream(input); final ZipOutputStream zipout = new ZipOutputStream(output); ZipEntry entry; int count = 0; while ((entry = zipin.getNextEntry()) != null) { zipout.putNextEntry(new ZipEntry(entry.getName())); - count += instrumentAll(zipin, zipout); + count += instrumentAll(zipin, zipout, name + "@" + entry.getName()); zipout.closeEntry(); } zipout.finish(); @@ -187,17 +201,18 @@ public class Instrumenter { } private int instrumentGzip(final InputStream input, - final OutputStream output) throws IOException { + final OutputStream output, final String name) throws IOException { final GZIPOutputStream gzout = new GZIPOutputStream(output); - final int count = instrumentAll(new GZIPInputStream(input), gzout); + final int count = instrumentAll(new GZIPInputStream(input), gzout, name); gzout.finish(); return count; } private int instrumentPack200(final InputStream input, - final OutputStream output) throws IOException { + final OutputStream output, final String name) throws IOException { final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - final int count = instrumentAll(Pack200Streams.unpack(input), buffer); + final int count = instrumentAll(Pack200Streams.unpack(input), buffer, + name); Pack200Streams.pack(buffer.toByteArray(), output); return count; } diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html index 661f6fc9..9415660c 100644 --- a/org.jacoco.doc/docroot/doc/changes.html +++ b/org.jacoco.doc/docroot/doc/changes.html @@ -34,10 +34,19 @@ by certain tools like ProGuard (GitHub #85).</li> </ul> +<h3>Non-functional Changes</h3> +<ul> + <li>More context information when exceptions occur during analysis or + instrumentation (GitHub #104).</li> +</ul> + + <h3>API Changes</h3> <ul> <li>The configuration of the Maven check goal has been reworked to support checks on any element type (GitHub #106).</li> + <li><code>Analyzer</code> and <code>Instrumenter</code> expect resource name + as additional parameter for better error messages (GitHub #104).</li> </ul> diff --git a/org.jacoco.examples/src/org/jacoco/examples/CoreTutorial.java b/org.jacoco.examples/src/org/jacoco/examples/CoreTutorial.java index c02f76d1..55fd6cf2 100644 --- a/org.jacoco.examples/src/org/jacoco/examples/CoreTutorial.java +++ b/org.jacoco.examples/src/org/jacoco/examples/CoreTutorial.java @@ -119,8 +119,8 @@ public final class CoreTutorial { // The Instrumenter creates a modified version of our test target class // that contains additional probes for execution data recording: final Instrumenter instr = new Instrumenter(runtime); - final byte[] instrumented = instr - .instrument(getTargetClass(targetName)); + final byte[] instrumented = instr.instrument( + getTargetClass(targetName), targetName); // Now we're ready to run our instrumented class and need to startup the // runtime first: @@ -148,7 +148,7 @@ public final class CoreTutorial { // information: final CoverageBuilder coverageBuilder = new CoverageBuilder(); final Analyzer analyzer = new Analyzer(executionData, coverageBuilder); - analyzer.analyzeClass(getTargetClass(targetName)); + analyzer.analyzeClass(getTargetClass(targetName), targetName); // Let's dump some metrics and line coverage information: for (final IClassCoverage cc : coverageBuilder.getClasses()) { |