1 /* 2 * Copyright (C) 2013 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package com.google.caliper.runner; 16 17 import static org.junit.Assert.assertTrue; 18 import static org.junit.Assert.fail; 19 20 import com.google.caliper.BeforeExperiment; 21 import com.google.caliper.Benchmark; 22 import com.google.common.collect.Lists; 23 24 import org.junit.Rule; 25 import org.junit.Test; 26 import org.junit.runner.RunWith; 27 import org.junit.runners.JUnit4; 28 29 import java.util.List; 30 31 /** 32 * Integration tests for misbehaving benchmarks. 33 */ 34 @RunWith(JUnit4.class) 35 public class BadUserCodeTest { 36 @Rule public CaliperTestWatcher runner = new CaliperTestWatcher(); 37 38 @Test 39 testExceptionInInit()40 public void testExceptionInInit() throws Exception { 41 try { 42 runner.forBenchmark(ExceptionInInitBenchmark.class).run(); 43 fail(); 44 } catch (UserCodeException expected) {} 45 } 46 throwSomeUserException()47 private static void throwSomeUserException() { 48 throw new RuntimeException(); 49 } 50 51 static class ExceptionInInitBenchmark { 52 static { throwSomeUserException()53 throwSomeUserException(); 54 } 55 timeSomething(int reps)56 @Benchmark void timeSomething(int reps) { 57 fail("" + reps); 58 } 59 } 60 61 @Test 62 testExceptionInConstructor()63 public void testExceptionInConstructor() throws Exception { 64 try { 65 runner.forBenchmark(ExceptionInConstructorBenchmark.class).run(); 66 fail(); 67 } catch (UserCodeException expected) {} 68 } 69 70 static class ExceptionInConstructorBenchmark { ExceptionInConstructorBenchmark()71 ExceptionInConstructorBenchmark() { 72 throw new RuntimeException(); 73 } 74 timeSomething(int reps)75 @Benchmark void timeSomething(int reps) { 76 fail("" + reps); 77 } 78 } 79 80 @Test 81 testExceptionInMethod()82 public void testExceptionInMethod() throws Exception { 83 try { 84 runner.forBenchmark(ExceptionInMethodBenchmark.class).run(); 85 fail(); 86 } catch (UserCodeException expected) {} 87 } 88 89 static class ExceptionInMethodBenchmark { timeSomething(@uppressWarnings"unused") int reps)90 @Benchmark void timeSomething(@SuppressWarnings("unused") int reps) { 91 throw new RuntimeException(); 92 } 93 } 94 95 @Test 96 testExceptionInMethod_notInDryRun()97 public void testExceptionInMethod_notInDryRun() throws Exception { 98 try { 99 runner.forBenchmark(ExceptionLateInMethodBenchmark.class).run(); 100 fail(); 101 } catch (ProxyWorkerException expected) { 102 assertTrue(expected.getMessage().contains(ExceptionLateInMethodBenchmark.class.getName())); 103 } 104 } 105 106 static class ExceptionLateInMethodBenchmark { timeSomething(int reps)107 @Benchmark void timeSomething(int reps) { 108 if (reps > 1) { 109 throw new RuntimeException(); 110 } 111 } 112 } 113 114 @Test 115 testExceptionInSetUp()116 public void testExceptionInSetUp() throws Exception { 117 try { 118 runner.forBenchmark(ExceptionInSetUpBenchmark.class).run(); 119 fail(); 120 } catch (UserCodeException expected) {} 121 } 122 123 static class ExceptionInSetUpBenchmark { setUp()124 @BeforeExperiment void setUp() { 125 throw new RuntimeException(); 126 } 127 timeSomething(int reps)128 @Benchmark void timeSomething(int reps) { 129 fail("" + reps); 130 } 131 } 132 133 @Test 134 testNonDeterministicAllocation_noTrackAllocations()135 public void testNonDeterministicAllocation_noTrackAllocations() throws Exception { 136 try { 137 runner.forBenchmark(NonDeterministicAllocationBenchmark.class) 138 .instrument("allocation") 139 .options("-Cinstrument.allocation.options.trackAllocations=" + false) 140 .run(); 141 fail(); 142 } catch (ProxyWorkerException expected) { 143 String message = "Your benchmark appears to have non-deterministic allocation behavior"; 144 assertTrue("Expected " + expected.getMessage() + " to contain " + message, 145 expected.getMessage().contains(message)); 146 } 147 } 148 149 @Test 150 testNonDeterministicAllocation_trackAllocations()151 public void testNonDeterministicAllocation_trackAllocations() throws Exception { 152 try { 153 runner.forBenchmark(NonDeterministicAllocationBenchmark.class) 154 .instrument("allocation") 155 .options("-Cinstrument.allocation.options.trackAllocations=" + true) 156 .run(); 157 fail(); 158 } catch (ProxyWorkerException expected) { 159 String message = "Your benchmark appears to have non-deterministic allocation behavior"; 160 assertTrue("Expected " + expected.getMessage() + " to contain " + message, 161 expected.getMessage().contains(message)); 162 } 163 } 164 165 /** The number of allocations is non deterministic because it depends on static state. */ 166 static class NonDeterministicAllocationBenchmark { 167 static int timeCount = 0; 168 // We dump items into this list so the jit cannot remove the allocations 169 static List<Object> list = Lists.newArrayList(); timeSomethingFBZ(@uppressWarnings"unused") int reps)170 @Benchmark int timeSomethingFBZ(@SuppressWarnings("unused") int reps) { 171 timeCount++; 172 if (timeCount % 2 == 0) { 173 list.add(new Object()); 174 return list.hashCode(); 175 } 176 return this.hashCode(); 177 } 178 } 179 180 @Test 181 testComplexNonDeterministicAllocation_noTrackAllocations()182 public void testComplexNonDeterministicAllocation_noTrackAllocations() throws Exception { 183 // Without trackAllocations enabled this kind of non-determinism cannot be detected. 184 runner.forBenchmark(ComplexNonDeterministicAllocationBenchmark.class) 185 .instrument("allocation") 186 .options("-Cinstrument.allocation.options.trackAllocations=" + false) 187 .run(); 188 } 189 190 @Test 191 testComplexNonDeterministicAllocation_trackAllocations()192 public void testComplexNonDeterministicAllocation_trackAllocations() throws Exception { 193 try { 194 runner.forBenchmark(ComplexNonDeterministicAllocationBenchmark.class) 195 .instrument("allocation") 196 .options("-Cinstrument.allocation.options.trackAllocations=" + true) 197 .run(); 198 } catch (ProxyWorkerException expected) { 199 String message = "Your benchmark appears to have non-deterministic allocation behavior"; 200 assertTrue("Expected " + expected.getMessage() + " to contain " + message, 201 expected.getMessage().contains(message)); 202 } 203 } 204 205 /** Benchmark allocates the same number of things each time but in a different way. */ 206 static class ComplexNonDeterministicAllocationBenchmark { 207 static int timeCount = 0; timeSomethingFBZ(@uppressWarnings"unused") int reps)208 @Benchmark int timeSomethingFBZ(@SuppressWarnings("unused") int reps) { 209 // We dump items into this list so the jit cannot remove the allocations 210 List<Object> list = Lists.newArrayList(); 211 timeCount++; 212 if (timeCount % 2 == 0) { 213 list.add(new Object()); 214 } else { 215 list.add(new Object()); 216 } 217 return list.hashCode(); 218 } 219 } 220 } 221