/* * Copyright 2011 Henry Coles * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http:www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the License. */ package sun.pitest; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.mockito.Mockito.verify; import java.util.Collection; import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.pitest.functional.SideEffect2; public class CodeCoverageStoreTest { @Mock private InvokeReceiver receiver; @Before public void setUp() { MockitoAnnotations.initMocks(this); CodeCoverageStore.init(this.receiver); } @After public void cleanUp() { CodeCoverageStore.resetAllStaticState(); } @Test public void shouldRegisterNewClassesWithReceiver() { final int id = CodeCoverageStore.registerClass("Foo"); verify(this.receiver).registerClass(id, "Foo"); } @Test public void shouldGenerateNewClassIdForEachClass() { final int id = CodeCoverageStore.registerClass("Foo"); final int id2 = CodeCoverageStore.registerClass("Bar"); assertFalse(id == id2); } @Test public void shouldCodeAndEncodeWhenClassIdAndLineNumberAreAtMaximum() { final long value = CodeCoverageStore.encode(Integer.MAX_VALUE, Integer.MAX_VALUE); assertEquals(Integer.MAX_VALUE, CodeCoverageStore.decodeClassId(value)); assertEquals(Integer.MAX_VALUE, CodeCoverageStore.decodeLineId(value)); } @Test public void shouldCodeAndEncodeWhenClassIdAndLineNumberAreAtMinimum() { final long value = CodeCoverageStore.encode(Integer.MIN_VALUE, 0); assertEquals(Integer.MIN_VALUE, CodeCoverageStore.decodeClassId(value)); assertEquals(0, CodeCoverageStore.decodeLineId(value)); } @Test public void shouldCodeAndEncodeWhenClassIdAndLineNumberAreZero() { final long value = CodeCoverageStore.encode(0, 0); assertEquals(0, CodeCoverageStore.decodeClassId(value)); assertEquals(0, CodeCoverageStore.decodeLineId(value)); } @Test public void shouldClearHitCountersWhenReset() { final int classId = CodeCoverageStore.registerClass("foo"); CodeCoverageStore.registerClassProbes(classId, 1); CodeCoverageStore.visitProbes(classId, 0, new boolean[] { true }); CodeCoverageStore.reset(); final Collection<Long> actual = CodeCoverageStore.getHits(); assertEquals(Collections.emptyList(), actual); } @Test public void shouldBeSafeToAccessAcrossMultipleThreads() throws InterruptedException, ExecutionException { CodeCoverageStore.registerClass("foo"); CodeCoverageStore.registerClassProbes(0, 1); final Callable<ConcurrentModificationException> read = makeReader(); final ExecutorService pool = Executors.newFixedThreadPool(13); for (int i = 1; i != 13; i++) { pool.submit(makeWriter(i)); } final Future<ConcurrentModificationException> future = pool.submit(read); pool.shutdown(); assertNull(future.get()); } @Test public void shouldReportCorrectCoverageForSpecialisation1() { final SideEffect2<Integer, boolean[]> se = new SideEffect2<Integer, boolean[]>() { @Override public void apply(final Integer classId, final boolean[] probes) { CodeCoverageStore.visitProbes(classId, 0, probes[0]); } }; assertLineCombinations(1, se); } @Test public void shouldReportCorrectCoverageForSpecialisation2() { final SideEffect2<Integer, boolean[]> se = new SideEffect2<Integer, boolean[]>() { @Override public void apply(final Integer classId, final boolean[] probes) { CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1]); } }; assertLineCombinations(2, se); } @Test public void shouldReportCorrectCoverageForSpecialisation3() { final SideEffect2<Integer, boolean[]> se = new SideEffect2<Integer, boolean[]>() { @Override public void apply(final Integer classId, final boolean[] probes) { CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], probes[2]); } }; assertLineCombinations(3, se); } @Test public void shouldReportCorrectCoverageForSpecialisation4() { final SideEffect2<Integer, boolean[]> se = new SideEffect2<Integer, boolean[]>() { @Override public void apply(final Integer classId, final boolean[] probes) { CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], probes[2], probes[3]); } }; assertLineCombinations(4, se); } @Test public void shouldReportCorrectCoverageForSpecialisation5() { final SideEffect2<Integer, boolean[]> se = new SideEffect2<Integer, boolean[]>() { @Override public void apply(final Integer classId, final boolean[] probes) { CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], probes[2], probes[3], probes[4]); } }; assertLineCombinations(5, se); } @Test public void shouldReportCorrectCoverageForSpecialisation6() { final SideEffect2<Integer, boolean[]> se = new SideEffect2<Integer, boolean[]>() { @Override public void apply(final Integer classId, final boolean[] probes) { CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], probes[2], probes[3], probes[4], probes[5]); } }; assertLineCombinations(6, se); } @Test public void shouldReportCorrectCoverageForSpecialisation7() { final SideEffect2<Integer, boolean[]> se = new SideEffect2<Integer, boolean[]>() { @Override public void apply(final Integer classId, final boolean[] probes) { CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], probes[2], probes[3], probes[4], probes[5], probes[6]); } }; assertLineCombinations(7, se); } @Test public void shouldReportCorrectCoverageForSpecialisation8() { final SideEffect2<Integer, boolean[]> se = new SideEffect2<Integer, boolean[]>() { @Override public void apply(final Integer classId, final boolean[] probes) { CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], probes[2], probes[3], probes[4], probes[5], probes[6], probes[7]); } }; assertLineCombinations(8, se); } @Test public void shouldReportCorrectCoverageForSpecialisation9() { final SideEffect2<Integer, boolean[]> se = new SideEffect2<Integer, boolean[]>() { @Override public void apply(final Integer classId, final boolean[] probes) { CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], probes[2], probes[3], probes[4], probes[5], probes[6], probes[7], probes[8]); } }; assertLineCombinations(9, se); } @Test public void shouldReportCorrectCoverageForSpecialisation10() { final SideEffect2<Integer, boolean[]> se = new SideEffect2<Integer, boolean[]>() { @Override public void apply(final Integer classId, final boolean[] probes) { CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], probes[2], probes[3], probes[4], probes[5], probes[6], probes[7], probes[8], probes[9]); } }; assertLineCombinations(10, se); } @Test public void shouldReportCorrectCoverageForSpecialisation11() { final SideEffect2<Integer, boolean[]> se = new SideEffect2<Integer, boolean[]>() { @Override public void apply(final Integer classId, final boolean[] probes) { CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], probes[2], probes[3], probes[4], probes[5], probes[6], probes[7], probes[8], probes[9], probes[10]); } }; assertLineCombinations(11, se); } @Test public void shouldReportCorrectCoverageForSpecialisation12() { final SideEffect2<Integer, boolean[]> se = new SideEffect2<Integer, boolean[]>() { @Override public void apply(final Integer classId, final boolean[] probes) { CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], probes[2], probes[3], probes[4], probes[5], probes[6], probes[7], probes[8], probes[9], probes[10], probes[11]); } }; assertLineCombinations(12, se); } @Test public void shouldReportCorrectCoverageForSpecialisation13() { final SideEffect2<Integer, boolean[]> se = new SideEffect2<Integer, boolean[]>() { @Override public void apply(final Integer classId, final boolean[] probes) { CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], probes[2], probes[3], probes[4], probes[5], probes[6], probes[7], probes[8], probes[9], probes[10], probes[11], probes[12]); } }; assertLineCombinations(13, se); } @Test public void shouldReportCorrectCoverageForSpecialisation14() { final SideEffect2<Integer, boolean[]> se = new SideEffect2<Integer, boolean[]>() { @Override public void apply(final Integer classId, final boolean[] probes) { CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], probes[2], probes[3], probes[4], probes[5], probes[6], probes[7], probes[8], probes[9], probes[10], probes[11], probes[12], probes[13]); } }; assertLineCombinations(14, se); } @Test public void shouldReportCorrectCoverageForSpecialisation15() { final SideEffect2<Integer, boolean[]> se = new SideEffect2<Integer, boolean[]>() { @Override public void apply(final Integer classId, final boolean[] probes) { CodeCoverageStore.visitProbes(classId, 0, probes[0], probes[1], probes[2], probes[3], probes[4], probes[5], probes[6], probes[7], probes[8], probes[9], probes[10], probes[11], probes[12], probes[13], probes[14]); } }; assertLineCombinations(15, se); } private void assertLineCombinations(final int size, final SideEffect2<Integer, boolean[]> function) { ascendingPermuation(size, function); CodeCoverageStore.resetAllStaticState(); descendingPermutation(size, function); } private void ascendingPermuation(final int size, final SideEffect2<Integer, boolean[]> function) { final int classId = CodeCoverageStore.registerClass("foo"); CodeCoverageStore.registerClassProbes(classId, 15); final boolean[] probes = new boolean[size]; function.apply(classId, probes); assertDoesNotHitLine(classId, 1, 2, 3); for (int i = 0; i != probes.length; i++) { probes[i] = true; function.apply(classId, probes); for (int j = 0; j <= i; j++) { assertHitsLine(classId, j); } for (int j = i + 1; j != probes.length; j++) { assertDoesNotHitLine(classId, j); } } } private void descendingPermutation(final int size, final SideEffect2<Integer, boolean[]> function) { final int classId = CodeCoverageStore.registerClass("foo"); CodeCoverageStore.registerClassProbes(classId, 15); final boolean[] probes = new boolean[size]; for (int i = probes.length - 1; i != 0; i--) { probes[i] = true; function.apply(classId, probes); for (int j = 0; j != i; j++) { assertDoesNotHitLine(classId, j); } for (int j = probes.length; j != i; j--) { assertHitsLine(classId, j - 1); } } } private void assertHitsLine(final int classId, final int... i) { final Collection<Long> actual = CodeCoverageStore.getHits(); for (final int probe : i) { assertThat(actual).contains(CodeCoverageStore.encode(classId, probe)); } } private void assertDoesNotHitLine(final int classId, final int... i) { final Collection<Long> actual = CodeCoverageStore.getHits(); for (final int probe : i) { assertThat(actual).doesNotContain( CodeCoverageStore.encode(classId, probe)); } } private Callable<ConcurrentModificationException> makeReader() { final Callable<ConcurrentModificationException> read = new Callable<ConcurrentModificationException>() { @Override public ConcurrentModificationException call() throws Exception { ConcurrentModificationException error = null; try { pointlesslyIterateCollection(); pointlesslyIterateCollection(); pointlesslyIterateCollection(); } catch (final ConcurrentModificationException ex) { error = ex; } return error; } private long pointlesslyIterateCollection() { long total = 0; for (final Long i : CodeCoverageStore.getHits()) { total += i; try { Thread.sleep(5); } catch (final InterruptedException e) { } } return total; } }; return read; } private static Runnable makeWriter(final int sleepPeriod) { final Runnable write = new Runnable() { @Override public void run() { for (int i = 0; i != 1000; i++) { try { Thread.sleep(sleepPeriod); } catch (final InterruptedException e) { } final boolean b[] = new boolean[1000]; CodeCoverageStore.visitProbes(0, 0, b); } } }; return write; } }