1 /******************************************************************************* 2 * Copyright (c) 2009, 2021 Mountainminds GmbH & Co. KG and Contributors 3 * This program and the accompanying materials are made available under 4 * the terms of the Eclipse Public License 2.0 which is available at 5 * http://www.eclipse.org/legal/epl-2.0 6 * 7 * SPDX-License-Identifier: EPL-2.0 8 * 9 * Contributors: 10 * Marc R. Hoffmann - initial API and implementation 11 * 12 *******************************************************************************/ 13 package org.jacoco.core.test.validation; 14 15 import static org.junit.Assert.assertEquals; 16 import static org.junit.Assert.assertTrue; 17 18 import java.io.IOException; 19 import java.lang.reflect.Method; 20 import java.util.Arrays; 21 22 import org.jacoco.core.analysis.Analyzer; 23 import org.jacoco.core.analysis.CoverageBuilder; 24 import org.jacoco.core.analysis.ICounter; 25 import org.jacoco.core.analysis.ILine; 26 import org.jacoco.core.data.ExecutionData; 27 import org.jacoco.core.data.ExecutionDataStore; 28 import org.jacoco.core.internal.analysis.CounterImpl; 29 import org.jacoco.core.test.InstrumentingLoader; 30 import org.jacoco.core.test.TargetLoader; 31 import org.jacoco.core.test.validation.Source.Line; 32 import org.jacoco.core.test.validation.targets.Stubs; 33 import org.junit.Before; 34 import org.junit.Test; 35 36 /** 37 * Base class for validation tests. It executes the given class under code 38 * coverage and provides the coverage results for validation. 39 */ 40 public abstract class ValidationTestBase { 41 42 protected static final boolean isJDKCompiler = Compiler.DETECT.isJDK(); 43 44 protected static final JavaVersion JAVA_VERSION = new JavaVersion( 45 System.getProperty("java.version")); 46 47 private static final String[] STATUS_NAME = new String[4]; 48 49 { 50 STATUS_NAME[ICounter.EMPTY] = "EMPTY"; 51 STATUS_NAME[ICounter.NOT_COVERED] = "NOT_COVERED"; 52 STATUS_NAME[ICounter.FULLY_COVERED] = "FULLY_COVERED"; 53 STATUS_NAME[ICounter.PARTLY_COVERED] = "PARTLY_COVERED"; 54 } 55 56 private final Class<?> target; 57 58 private Source source; 59 60 private InstrumentingLoader loader; 61 ValidationTestBase(final Class<?> target)62 protected ValidationTestBase(final Class<?> target) { 63 this.target = target; 64 } 65 66 @Before setup()67 public void setup() throws Exception { 68 final ExecutionDataStore store = execute(); 69 analyze(store); 70 } 71 execute()72 private ExecutionDataStore execute() throws Exception { 73 loader = new InstrumentingLoader(target); 74 run(loader.loadClass(target.getName())); 75 return loader.collect(); 76 } 77 run(final Class<?> targetClass)78 protected void run(final Class<?> targetClass) throws Exception { 79 targetClass.getMethod("main", String[].class).invoke(null, 80 (Object) new String[0]); 81 } 82 analyze(final ExecutionDataStore store)83 private void analyze(final ExecutionDataStore store) throws IOException { 84 final CoverageBuilder builder = new CoverageBuilder(); 85 final Analyzer analyzer = new Analyzer(store, builder); 86 for (ExecutionData data : store.getContents()) { 87 analyze(analyzer, data); 88 } 89 source = Source.load(target, builder.getBundle("Test")); 90 } 91 analyze(final Analyzer analyzer, final ExecutionData data)92 private void analyze(final Analyzer analyzer, final ExecutionData data) 93 throws IOException { 94 final byte[] bytes = TargetLoader 95 .getClassDataAsBytes(target.getClassLoader(), data.getName()); 96 analyzer.analyzeClass(bytes, data.getName()); 97 } 98 99 /** 100 * All single line comments are interpreted as statements in the following 101 * format: 102 * 103 * <pre> 104 * // statement1() statement2() 105 * </pre> 106 */ 107 @Test execute_assertions_in_comments()108 public void execute_assertions_in_comments() throws IOException { 109 for (Line line : source.getLines()) { 110 String exec = line.getComment(); 111 if (exec != null) { 112 StatementParser.parse(exec, new StatementExecutor(this, line), 113 line.toString()); 114 } 115 } 116 } 117 118 @Test last_line_in_coverage_data_should_be_less_or_equal_to_number_of_lines_in_source_file()119 public void last_line_in_coverage_data_should_be_less_or_equal_to_number_of_lines_in_source_file() { 120 assertTrue(String.format( 121 "Last line in coverage data (%d) should be less or equal to number of lines in source file (%d)", 122 Integer.valueOf(source.getCoverage().getLastLine()), 123 Integer.valueOf(source.getLines().size())), 124 source.getCoverage().getLastLine() <= source.getLines().size()); 125 } 126 127 @Test all_missed_instructions_should_have_line_number()128 public void all_missed_instructions_should_have_line_number() { 129 CounterImpl c = CounterImpl.COUNTER_0_0; 130 for (Line line : source.getLines()) { 131 c = c.increment(line.getCoverage().getInstructionCounter()); 132 } 133 assertEquals( 134 "sum of missed instructions of all lines should be equal to missed instructions of file", 135 source.getCoverage().getInstructionCounter().getMissedCount(), 136 c.getMissedCount()); 137 } 138 139 @Test all_branches_should_have_line_number()140 public void all_branches_should_have_line_number() { 141 CounterImpl c = CounterImpl.COUNTER_0_0; 142 for (Line line : source.getLines()) { 143 c = c.increment(line.getCoverage().getBranchCounter()); 144 } 145 assertEquals( 146 "sum of branch counters of all lines should be equal to branch counter of file", 147 source.getCoverage().getBranchCounter(), c); 148 } 149 150 /* 151 * Predefined assertion methods: 152 */ 153 assertCoverage(final Line line, final int insnStatus, final int missedBranches, final int coveredBranches)154 private void assertCoverage(final Line line, final int insnStatus, 155 final int missedBranches, final int coveredBranches) { 156 final ILine coverage = line.getCoverage(); 157 158 String msg = String.format("Instructions (%s)", line); 159 final int actualStatus = coverage.getInstructionCounter().getStatus(); 160 assertEquals(msg, STATUS_NAME[insnStatus], STATUS_NAME[actualStatus]); 161 162 msg = String.format("Branches (%s)", line); 163 assertEquals(msg, 164 CounterImpl.getInstance(missedBranches, coveredBranches), 165 coverage.getBranchCounter()); 166 } 167 assertFullyCovered(final Line line, final int missedBranches, final int coveredBranches)168 public void assertFullyCovered(final Line line, final int missedBranches, 169 final int coveredBranches) { 170 assertCoverage(line, ICounter.FULLY_COVERED, missedBranches, 171 coveredBranches); 172 } 173 assertFullyCovered(final Line line)174 public void assertFullyCovered(final Line line) { 175 assertFullyCovered(line, 0, 0); 176 } 177 assertPartlyCovered(final Line line, final int missedBranches, final int coveredBranches)178 public void assertPartlyCovered(final Line line, final int missedBranches, 179 final int coveredBranches) { 180 assertCoverage(line, ICounter.PARTLY_COVERED, missedBranches, 181 coveredBranches); 182 } 183 assertPartlyCovered(final Line line)184 public void assertPartlyCovered(final Line line) { 185 assertPartlyCovered(line, 0, 0); 186 } 187 assertNotCovered(final Line line, final int missedBranches, final int coveredBranches)188 public void assertNotCovered(final Line line, final int missedBranches, 189 final int coveredBranches) { 190 assertCoverage(line, ICounter.NOT_COVERED, missedBranches, 191 coveredBranches); 192 } 193 assertNotCovered(final Line line)194 public void assertNotCovered(final Line line) { 195 assertNotCovered(line, 0, 0); 196 } 197 assertEmpty(final Line line)198 public void assertEmpty(final Line line) { 199 assertCoverage(line, ICounter.EMPTY, 0, 0); 200 } 201 assertLogEvents(String... events)202 protected void assertLogEvents(String... events) throws Exception { 203 final Method getter = Class 204 .forName(Stubs.class.getName(), false, loader) 205 .getMethod("getLogEvents"); 206 assertEquals("Log events", Arrays.asList(events), getter.invoke(null)); 207 } 208 assertMethodCount(final int expectedTotal)209 protected void assertMethodCount(final int expectedTotal) { 210 assertEquals(expectedTotal, 211 source.getCoverage().getMethodCounter().getTotalCount()); 212 } 213 214 } 215