1 /* 2 * Copyright (C) 2009 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.caliper.runner; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.fail; 21 22 import com.google.caliper.Benchmark; 23 import com.google.caliper.Param; 24 import com.google.caliper.config.InvalidConfigurationException; 25 import com.google.caliper.util.InvalidCommandException; 26 27 import junit.framework.AssertionFailedError; 28 29 import org.junit.Test; 30 import org.junit.runner.RunWith; 31 import org.junit.runners.JUnit4; 32 33 import java.io.PrintWriter; 34 import java.io.StringWriter; 35 36 /** 37 * Unit test covering common user mistakes in benchmark classes. 38 */ 39 @RunWith(JUnit4.class) 40 41 public class MalformedBenchmarksTest { 42 // Put the expected messages together here, which may promote some kind of 43 // consistency in their wording. :) 44 45 private static final String ABSTRACT = 46 "Class '%s' is abstract"; 47 private static final String NO_CONSTRUCTOR = 48 "Benchmark class %s does not have a publicly visible default constructor"; 49 private static final String NO_METHODS = 50 "There were no experiments to be performed for the class %s using the instruments " + 51 "[allocation, runtime]"; 52 private static final String STATIC_BENCHMARK = 53 "Benchmark methods must not be static: timeIt"; 54 private static final String WRONG_ARGUMENTS = 55 "Benchmark methods must have no arguments or accept a single int or long parameter: timeIt"; 56 private static final String STATIC_PARAM = 57 "Parameter field 'oops' must not be static"; 58 private static final String RESERVED_PARAM = 59 "Class '%s' uses reserved parameter name 'vm'"; 60 private static final String NO_CONVERSION = "Type 'Object' of parameter field 'oops' " 61 + "has no recognized String-converting method; see <TODO> for details"; 62 private static final String CONVERT_FAILED = // granted this one's a little weird (and brittle) 63 "Cannot convert value 'oops' to type 'int': For input string: \"oops\""; 64 abstractBenchmark()65 @Test public void abstractBenchmark() throws Exception { 66 expectException(ABSTRACT, AbstractBenchmark.class); 67 } 68 abstract static class AbstractBenchmark {} 69 noSuitableConstructor()70 @Test public void noSuitableConstructor() throws Exception { 71 expectException(String.format(NO_CONSTRUCTOR, BadConstructorBenchmark.class.getName()), 72 BadConstructorBenchmark.class); 73 } 74 75 @SuppressWarnings("unused") 76 static class BadConstructorBenchmark { BadConstructorBenchmark(String damnParam)77 BadConstructorBenchmark(String damnParam) {} timeIt(int reps)78 @Benchmark void timeIt(int reps) {} 79 } 80 noBenchmarkMethods()81 @Test public void noBenchmarkMethods() throws Exception { 82 expectException(NO_METHODS, NoMethodsBenchmark.class); 83 } 84 85 @SuppressWarnings("unused") 86 static class NoMethodsBenchmark { timeIt(int reps)87 void timeIt(int reps) {} // not annotated 88 } 89 staticBenchmarkMethod()90 @Test public void staticBenchmarkMethod() throws Exception { 91 expectException(STATIC_BENCHMARK, StaticBenchmarkMethodBenchmark.class); 92 } 93 94 @SuppressWarnings("unused") 95 static class StaticBenchmarkMethodBenchmark { timeIt(int reps)96 @Benchmark public static void timeIt(int reps) {} 97 } 98 wrongSignature()99 @Test public void wrongSignature() throws Exception { 100 expectException(WRONG_ARGUMENTS, BoxedParamBenchmark.class); 101 expectException(WRONG_ARGUMENTS, ExtraParamBenchmark.class); 102 } 103 104 @SuppressWarnings("unused") 105 static class BoxedParamBenchmark { timeIt(Integer reps)106 @Benchmark void timeIt(Integer reps) {} 107 } 108 109 @SuppressWarnings("unused") 110 static class ExtraParamBenchmark { timeIt(int reps, int what)111 @Benchmark void timeIt(int reps, int what) {} 112 } 113 hasBenchmarkOverloads()114 @Test public void hasBenchmarkOverloads() throws Exception { 115 // N.B. baz is fine since although it has an overload, its overload is not a benchmark method. 116 expectException( 117 "Overloads are disallowed for benchmark methods, found overloads of [bar, foo] in " 118 + "benchmark OverloadsAnnotatedBenchmark", 119 OverloadsAnnotatedBenchmark.class); 120 } 121 122 @SuppressWarnings("unused") 123 static class OverloadsAnnotatedBenchmark { foo(long reps)124 @Benchmark public void foo(long reps) {} foo(int reps)125 @Benchmark public void foo(int reps) {} bar(long reps)126 @Benchmark public void bar(long reps) {} bar(int reps)127 @Benchmark public void bar(int reps) {} baz(int reps)128 @Benchmark public void baz(int reps) {} baz(long reps, boolean thing)129 public void baz(long reps, boolean thing) {} baz(long reps)130 public void baz(long reps) {} 131 } 132 staticParam()133 @Test public void staticParam() throws Exception { 134 expectException(STATIC_PARAM, StaticParamBenchmark.class); 135 } 136 static class StaticParamBenchmark { 137 @Param static String oops; 138 } 139 reservedParameterName()140 @Test public void reservedParameterName() throws Exception { 141 expectException(RESERVED_PARAM, ReservedParamBenchmark.class); 142 } 143 static class ReservedParamBenchmark { 144 @Param String vm; 145 } 146 unparsableParamType()147 @Test public void unparsableParamType() throws Exception { 148 expectException(NO_CONVERSION, UnparsableParamTypeBenchmark.class); 149 } 150 static class UnparsableParamTypeBenchmark { 151 @Param Object oops; 152 } 153 unparsableParamDefault()154 @Test public void unparsableParamDefault() throws Exception { 155 expectException(CONVERT_FAILED, UnparsableParamDefaultBenchmark.class); 156 } 157 static class UnparsableParamDefaultBenchmark { 158 @Param({"1", "2", "oops"}) int number; 159 } 160 161 // end of tests 162 expectException(String expectedMessageFmt, Class<?> benchmarkClass)163 private void expectException(String expectedMessageFmt, Class<?> benchmarkClass) 164 throws InvalidCommandException, InvalidConfigurationException { 165 try { 166 CaliperMain.exitlessMain( 167 new String[] {"--instrument=allocation,runtime", "--dry-run", benchmarkClass.getName()}, 168 new PrintWriter(new StringWriter()), new PrintWriter(new StringWriter())); 169 fail("no exception thrown"); 170 } catch (InvalidBenchmarkException e) { 171 try { 172 String expectedMessageText = 173 String.format(expectedMessageFmt, benchmarkClass.getSimpleName()); 174 assertEquals(expectedMessageText, e.getMessage()); 175 176 // don't swallow our real stack trace 177 } catch (AssertionFailedError afe) { 178 afe.initCause(e); 179 throw afe; 180 } 181 } 182 } 183 } 184