blob: 80a133034e457217d5cf56a158a2f37a27519a7e [file] [log] [blame]
/*
* Copyright (C) 2009 Google Inc.
*
* 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 com.google.caliper.runner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import com.google.caliper.Benchmark;
import com.google.caliper.Param;
import com.google.caliper.config.InvalidConfigurationException;
import com.google.caliper.util.InvalidCommandException;
import junit.framework.AssertionFailedError;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* Unit test covering common user mistakes in benchmark classes.
*/
@RunWith(JUnit4.class)
public class MalformedBenchmarksTest {
// Put the expected messages together here, which may promote some kind of
// consistency in their wording. :)
private static final String ABSTRACT =
"Class '%s' is abstract";
private static final String NO_CONSTRUCTOR =
"Benchmark class %s does not have a publicly visible default constructor";
private static final String NO_METHODS =
"There were no experiments to be performed for the class %s using the instruments " +
"[allocation, runtime]";
private static final String STATIC_BENCHMARK =
"Benchmark methods must not be static: timeIt";
private static final String WRONG_ARGUMENTS =
"Benchmark methods must have no arguments or accept a single int or long parameter: timeIt";
private static final String STATIC_PARAM =
"Parameter field 'oops' must not be static";
private static final String RESERVED_PARAM =
"Class '%s' uses reserved parameter name 'vm'";
private static final String NO_CONVERSION = "Type 'Object' of parameter field 'oops' "
+ "has no recognized String-converting method; see <TODO> for details";
private static final String CONVERT_FAILED = // granted this one's a little weird (and brittle)
"Cannot convert value 'oops' to type 'int': For input string: \"oops\"";
@Test public void abstractBenchmark() throws Exception {
expectException(ABSTRACT, AbstractBenchmark.class);
}
abstract static class AbstractBenchmark {}
@Test public void noSuitableConstructor() throws Exception {
expectException(String.format(NO_CONSTRUCTOR, BadConstructorBenchmark.class.getName()),
BadConstructorBenchmark.class);
}
@SuppressWarnings("unused")
static class BadConstructorBenchmark {
BadConstructorBenchmark(String damnParam) {}
@Benchmark void timeIt(int reps) {}
}
@Test public void noBenchmarkMethods() throws Exception {
expectException(NO_METHODS, NoMethodsBenchmark.class);
}
@SuppressWarnings("unused")
static class NoMethodsBenchmark {
void timeIt(int reps) {} // not annotated
}
@Test public void staticBenchmarkMethod() throws Exception {
expectException(STATIC_BENCHMARK, StaticBenchmarkMethodBenchmark.class);
}
@SuppressWarnings("unused")
static class StaticBenchmarkMethodBenchmark {
@Benchmark public static void timeIt(int reps) {}
}
@Test public void wrongSignature() throws Exception {
expectException(WRONG_ARGUMENTS, BoxedParamBenchmark.class);
expectException(WRONG_ARGUMENTS, ExtraParamBenchmark.class);
}
@SuppressWarnings("unused")
static class BoxedParamBenchmark {
@Benchmark void timeIt(Integer reps) {}
}
@SuppressWarnings("unused")
static class ExtraParamBenchmark {
@Benchmark void timeIt(int reps, int what) {}
}
@Test public void hasBenchmarkOverloads() throws Exception {
// N.B. baz is fine since although it has an overload, its overload is not a benchmark method.
expectException(
"Overloads are disallowed for benchmark methods, found overloads of [bar, foo] in "
+ "benchmark OverloadsAnnotatedBenchmark",
OverloadsAnnotatedBenchmark.class);
}
@SuppressWarnings("unused")
static class OverloadsAnnotatedBenchmark {
@Benchmark public void foo(long reps) {}
@Benchmark public void foo(int reps) {}
@Benchmark public void bar(long reps) {}
@Benchmark public void bar(int reps) {}
@Benchmark public void baz(int reps) {}
public void baz(long reps, boolean thing) {}
public void baz(long reps) {}
}
@Test public void staticParam() throws Exception {
expectException(STATIC_PARAM, StaticParamBenchmark.class);
}
static class StaticParamBenchmark {
@Param static String oops;
}
@Test public void reservedParameterName() throws Exception {
expectException(RESERVED_PARAM, ReservedParamBenchmark.class);
}
static class ReservedParamBenchmark {
@Param String vm;
}
@Test public void unparsableParamType() throws Exception {
expectException(NO_CONVERSION, UnparsableParamTypeBenchmark.class);
}
static class UnparsableParamTypeBenchmark {
@Param Object oops;
}
@Test public void unparsableParamDefault() throws Exception {
expectException(CONVERT_FAILED, UnparsableParamDefaultBenchmark.class);
}
static class UnparsableParamDefaultBenchmark {
@Param({"1", "2", "oops"}) int number;
}
// end of tests
private void expectException(String expectedMessageFmt, Class<?> benchmarkClass)
throws InvalidCommandException, InvalidConfigurationException {
try {
CaliperMain.exitlessMain(
new String[] {"--instrument=allocation,runtime", "--dry-run", benchmarkClass.getName()},
new PrintWriter(new StringWriter()), new PrintWriter(new StringWriter()));
fail("no exception thrown");
} catch (InvalidBenchmarkException e) {
try {
String expectedMessageText =
String.format(expectedMessageFmt, benchmarkClass.getSimpleName());
assertEquals(expectedMessageText, e.getMessage());
// don't swallow our real stack trace
} catch (AssertionFailedError afe) {
afe.initCause(e);
throw afe;
}
}
}
}