#97 - Remove Microbenchmark runner in favor of mp911de/microbenchmark-runner.
This commit is contained in:
@@ -45,6 +45,12 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.mp911de.microbenchmark-runner</groupId>
|
||||
<artifactId>microbenchmark-runner-junit4</artifactId>
|
||||
<version>0.1.0.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
@@ -119,6 +125,10 @@
|
||||
<id>spring-libs-snapshot</id>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
|
||||
@@ -21,6 +21,11 @@
|
||||
<artifactId>spring-data-mongodb</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.mp911de.microbenchmark-runner</groupId>
|
||||
<artifactId>microbenchmark-runner-junit4</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.microbenchmark.common;
|
||||
|
||||
import jmh.mbr.junit4.Microbenchmark;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
@@ -29,9 +31,10 @@ import org.openjdk.jmh.annotations.Warmup;
|
||||
* @author Mark Paluch
|
||||
* @see Microbenchmark
|
||||
*/
|
||||
@Warmup(iterations = JmhSupport.WARMUP_ITERATIONS)
|
||||
@Measurement(iterations = JmhSupport.MEASUREMENT_ITERATIONS)
|
||||
@Fork(JmhSupport.FORKS)
|
||||
@Warmup(iterations = 5)
|
||||
@Measurement(iterations = 10)
|
||||
@Fork(value = 1, jvmArgs = { "-server", "-XX:+HeapDumpOnOutOfMemoryError", "-Xms1024m", "-Xmx1024m",
|
||||
"-XX:MaxDirectMemorySize=1024m" })
|
||||
@State(Scope.Thread)
|
||||
@RunWith(Microbenchmark.class)
|
||||
public abstract class AbstractMicrobenchmark {
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.microbenchmark.common;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import jmh.mbr.core.ResultsWriter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
@@ -26,30 +28,37 @@ import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.openjdk.jmh.results.RunResult;
|
||||
import org.openjdk.jmh.runner.format.OutputFormat;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* {@link ResultsWriter} implementation of {@link URLConnection}.
|
||||
*
|
||||
* @since 2.0
|
||||
* {@link ResultsWriterOld} implementation of {@link URLConnection}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
class HttpResultsWriter implements ResultsWriter {
|
||||
|
||||
private final String url;
|
||||
|
||||
HttpResultsWriter(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void write(Collection<RunResult> results) {
|
||||
public void write(OutputFormat output, Collection<RunResult> results) {
|
||||
|
||||
if (CollectionUtils.isEmpty(results)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
doWrite(results);
|
||||
} catch (IOException e) {
|
||||
output.println("Failed to write results: " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void doWrite(Collection<RunResult> results) throws IOException {
|
||||
|
||||
StandardEnvironment env = new StandardEnvironment();
|
||||
|
||||
String projectVersion = env.getProperty("project.version", "unknown");
|
||||
|
||||
@@ -1,312 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.springframework.data.microbenchmark.common;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.openjdk.jmh.results.RunResult;
|
||||
import org.openjdk.jmh.results.format.ResultFormatType;
|
||||
import org.openjdk.jmh.runner.Runner;
|
||||
import org.openjdk.jmh.runner.options.ChainedOptionsBuilder;
|
||||
import org.openjdk.jmh.runner.options.OptionsBuilder;
|
||||
import org.openjdk.jmh.runner.options.TimeValue;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class JmhSupport {
|
||||
|
||||
static final int WARMUP_ITERATIONS = 5;
|
||||
static final int MEASUREMENT_ITERATIONS = 10;
|
||||
static final int FORKS = 1;
|
||||
static final String[] JVM_ARGS = { "-server", "-XX:+HeapDumpOnOutOfMemoryError", "-Xms1024m", "-Xmx1024m",
|
||||
"-XX:MaxDirectMemorySize=1024m" };
|
||||
|
||||
private final StandardEnvironment environment = new StandardEnvironment();
|
||||
|
||||
/**
|
||||
* Get the regex for all benchmarks to be included in the run. By default every benchmark within classes matching the
|
||||
* fqcn. <br />
|
||||
* The {@literal benchmark} command line argument allows overriding the defaults using {@code #} as class / method
|
||||
* name separator.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @param name
|
||||
* @param methods
|
||||
*/
|
||||
protected List<String> includes(Class<?> testClass, Collection<FrameworkMethod> methods) {
|
||||
|
||||
String tests = environment.getProperty("benchmark", String.class);
|
||||
|
||||
if (!StringUtils.hasText(tests)) {
|
||||
|
||||
return methods.stream()
|
||||
.map(it -> Pattern.quote(it.getDeclaringClass().getName()) + "\\." + Pattern.quote(it.getName()) + "$")
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (tests.contains(testClass.getName()) || tests.contains(testClass.getSimpleName())) {
|
||||
if (!tests.contains("#")) {
|
||||
return Collections.singletonList(".*" + tests + ".*");
|
||||
}
|
||||
|
||||
String[] args = tests.split("#");
|
||||
return Collections.singletonList(".*" + args[0] + "." + args[1]);
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all options for the {@link Runner}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @throws Exception
|
||||
*/
|
||||
protected ChainedOptionsBuilder options(Class<?> jmhTestClass) throws Exception {
|
||||
|
||||
ChainedOptionsBuilder optionsBuilder = new OptionsBuilder().jvmArgs(jvmArgs());
|
||||
|
||||
optionsBuilder = warmup(optionsBuilder);
|
||||
optionsBuilder = measure(optionsBuilder);
|
||||
optionsBuilder = forks(optionsBuilder);
|
||||
optionsBuilder = report(optionsBuilder, jmhTestClass);
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* JVM args to apply to {@link Runner} via its {@link org.openjdk.jmh.runner.options.Options}.
|
||||
*
|
||||
* @return {@link #JVM_ARGS} by default.
|
||||
*/
|
||||
protected String[] jvmArgs() {
|
||||
|
||||
String[] args = new String[JVM_ARGS.length];
|
||||
System.arraycopy(JVM_ARGS, 0, args, 0, JVM_ARGS.length);
|
||||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read {@code warmupIterations} property from {@link org.springframework.core.env.Environment}.
|
||||
*
|
||||
* @return -1 if not set.
|
||||
*/
|
||||
protected int getWarmupIterations() {
|
||||
return environment.getProperty("warmupIterations", Integer.class, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read {@code measurementIterations} property from {@link org.springframework.core.env.Environment}.
|
||||
*
|
||||
* @return -1 if not set.
|
||||
*/
|
||||
protected int getMeasurementIterations() {
|
||||
return environment.getProperty("measurementIterations", Integer.class, -1);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Read {@code forks} property from {@link org.springframework.core.env.Environment}.
|
||||
*
|
||||
* @return -1 if not set.
|
||||
*/
|
||||
protected int getForksCount() {
|
||||
return environment.getProperty("forks", Integer.class, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read {@code benchmarkReportDir} property from {@link org.springframework.core.env.Environment}.
|
||||
*
|
||||
* @return {@literal null} if not set.
|
||||
*/
|
||||
protected String getReportDirectory() {
|
||||
return environment.getProperty("benchmarkReportDir");
|
||||
}
|
||||
|
||||
/**
|
||||
* Read {@code measurementTime} property from {@link org.springframework.core.env.Environment}.
|
||||
*
|
||||
* @return -1 if not set.
|
||||
*/
|
||||
protected long getMeasurementTime() {
|
||||
return environment.getProperty("measurementTime", Long.class, -1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read {@code warmupTime} property from {@link org.springframework.core.env.Environment}.
|
||||
*
|
||||
* @return -1 if not set.
|
||||
*/
|
||||
protected long getWarmupTime() {
|
||||
return environment.getProperty("warmupTime", Long.class, -1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code project.version_yyyy-MM-dd_ClassName.json} eg.
|
||||
* {@literal 1.11.0.BUILD-SNAPSHOT_2017-03-07_MappingMongoConverterBenchmark.json}
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected String reportFilename(Class<?> jmhTestClass) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (environment.containsProperty("project.version")) {
|
||||
|
||||
sb.append(environment.getProperty("project.version"));
|
||||
sb.append("_");
|
||||
}
|
||||
|
||||
sb.append(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
|
||||
sb.append("_");
|
||||
sb.append(org.springframework.util.ClassUtils.getShortName(jmhTestClass));
|
||||
sb.append(".json");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply measurement options to {@link ChainedOptionsBuilder}.
|
||||
*
|
||||
* @param optionsBuilder must not be {@literal null}.
|
||||
* @return {@link ChainedOptionsBuilder} with options applied.
|
||||
* @see #getMeasurementIterations()
|
||||
* @see #getMeasurementTime()
|
||||
*/
|
||||
private ChainedOptionsBuilder measure(ChainedOptionsBuilder optionsBuilder) {
|
||||
|
||||
int measurementIterations = getMeasurementIterations();
|
||||
long measurementTime = getMeasurementTime();
|
||||
|
||||
if (measurementIterations > 0) {
|
||||
optionsBuilder = optionsBuilder.measurementIterations(measurementIterations);
|
||||
}
|
||||
|
||||
if (measurementTime > 0) {
|
||||
optionsBuilder = optionsBuilder.measurementTime(TimeValue.seconds(measurementTime));
|
||||
}
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply warmup options to {@link ChainedOptionsBuilder}.
|
||||
*
|
||||
* @param optionsBuilder must not be {@literal null}.
|
||||
* @return {@link ChainedOptionsBuilder} with options applied.
|
||||
* @see #getWarmupIterations()
|
||||
* @see #getWarmupTime()
|
||||
*/
|
||||
private ChainedOptionsBuilder warmup(ChainedOptionsBuilder optionsBuilder) {
|
||||
|
||||
int warmupIterations = getWarmupIterations();
|
||||
long warmupTime = getWarmupTime();
|
||||
|
||||
if (warmupIterations > 0) {
|
||||
optionsBuilder = optionsBuilder.warmupIterations(warmupIterations);
|
||||
}
|
||||
|
||||
if (warmupTime > 0) {
|
||||
optionsBuilder = optionsBuilder.warmupTime(TimeValue.seconds(warmupTime));
|
||||
}
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply forks option to {@link ChainedOptionsBuilder}.
|
||||
*
|
||||
* @param optionsBuilder must not be {@literal null}.
|
||||
* @return {@link ChainedOptionsBuilder} with options applied.
|
||||
* @see #getForksCount()
|
||||
*/
|
||||
private ChainedOptionsBuilder forks(ChainedOptionsBuilder optionsBuilder) {
|
||||
|
||||
int forks = getForksCount();
|
||||
|
||||
if (forks <= 0) {
|
||||
return optionsBuilder;
|
||||
}
|
||||
|
||||
return optionsBuilder.forks(forks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply report option to {@link ChainedOptionsBuilder}.
|
||||
*
|
||||
* @param optionsBuilder must not be {@literal null}.
|
||||
* @return {@link ChainedOptionsBuilder} with options applied.
|
||||
* @throws IOException if report file cannot be created.
|
||||
* @see #getReportDirectory()
|
||||
*/
|
||||
private ChainedOptionsBuilder report(ChainedOptionsBuilder optionsBuilder, Class<?> jmhTestClass) throws IOException {
|
||||
|
||||
String reportDir = getReportDirectory();
|
||||
|
||||
if (!StringUtils.hasText(reportDir)) {
|
||||
return optionsBuilder;
|
||||
}
|
||||
|
||||
String reportFilePath = reportDir + (reportDir.endsWith(File.separator) ? "" : File.separator) + reportFilename(jmhTestClass);
|
||||
File file = ResourceUtils.getFile(reportFilePath);
|
||||
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
} else {
|
||||
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
}
|
||||
|
||||
optionsBuilder.resultFormat(ResultFormatType.JSON);
|
||||
optionsBuilder.result(reportFilePath);
|
||||
|
||||
return optionsBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish results to an external system.
|
||||
*
|
||||
* @param results must not be {@literal null}.
|
||||
*/
|
||||
void publishResults(Collection<RunResult> results) {
|
||||
|
||||
if (CollectionUtils.isEmpty(results) || !environment.containsProperty("publishTo")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String uri = environment.getProperty("publishTo");
|
||||
try {
|
||||
ResultsWriter.forUri(uri).write(results);
|
||||
} catch (Exception e) {
|
||||
System.err.println(String.format("Cannot save benchmark results to '%s'. Error was %s.", uri, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,498 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.springframework.data.microbenchmark.common;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.manipulation.Filter;
|
||||
import org.junit.runner.manipulation.NoTestsRemainException;
|
||||
import org.junit.runner.manipulation.Sorter;
|
||||
import org.junit.runner.notification.Failure;
|
||||
import org.junit.runner.notification.RunNotifier;
|
||||
import org.junit.runners.BlockJUnit4ClassRunner;
|
||||
import org.junit.runners.model.FrameworkMethod;
|
||||
import org.junit.runners.model.InitializationError;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.infra.BenchmarkParams;
|
||||
import org.openjdk.jmh.infra.IterationParams;
|
||||
import org.openjdk.jmh.results.BenchmarkResult;
|
||||
import org.openjdk.jmh.results.IterationResult;
|
||||
import org.openjdk.jmh.results.RunResult;
|
||||
import org.openjdk.jmh.runner.Defaults;
|
||||
import org.openjdk.jmh.runner.NoBenchmarksException;
|
||||
import org.openjdk.jmh.runner.Runner;
|
||||
import org.openjdk.jmh.runner.format.OutputFormat;
|
||||
import org.openjdk.jmh.runner.format.OutputFormatFactory;
|
||||
import org.openjdk.jmh.runner.options.ChainedOptionsBuilder;
|
||||
import org.openjdk.jmh.runner.options.Options;
|
||||
import org.openjdk.jmh.util.UnCloseablePrintStream;
|
||||
import org.openjdk.jmh.util.Utils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* JMH Microbenchmark runner that turns methods annotated with {@link Benchmark} into runnable methods allowing
|
||||
* execution through JUnit.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class Microbenchmark extends BlockJUnit4ClassRunner {
|
||||
|
||||
private final Object childrenLock = new Object();
|
||||
private final JmhSupport jmhRunner = new JmhSupport();
|
||||
|
||||
private Collection<FrameworkMethod> filteredChildren;
|
||||
|
||||
/**
|
||||
* Creates a {@link Microbenchmark} to run {@link Class test class}.
|
||||
*
|
||||
* @param testClass
|
||||
* @throws InitializationError if the test class is malformed.
|
||||
*/
|
||||
public Microbenchmark(Class<?> testClass) throws InitializationError {
|
||||
super(testClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore JUnit validation as we're using JMH here.
|
||||
*
|
||||
* @param errors
|
||||
*/
|
||||
@Override
|
||||
protected void collectInitializationErrors(List<Throwable> errors) {}
|
||||
|
||||
/**
|
||||
* JMH has no means to exclude benchmark methods.
|
||||
*/
|
||||
@Override
|
||||
protected boolean isIgnored(FrameworkMethod child) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the methods that run tests. Default implementation returns all methods annotated with {@code @Test} on this
|
||||
* class and superclasses that are not overridden.
|
||||
*/
|
||||
protected List<FrameworkMethod> computeTestMethods() {
|
||||
|
||||
List<FrameworkMethod> annotatedMethods = new ArrayList<>(getTestClass().getAnnotatedMethods(Benchmark.class));
|
||||
|
||||
annotatedMethods.sort(Comparator.comparing(Microbenchmark::getBenchmarkName));
|
||||
|
||||
return Collections.unmodifiableList(annotatedMethods);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.junit.runners.ParentRunner#classBlock(org.junit.runner.notification.RunNotifier)
|
||||
*/
|
||||
@Override
|
||||
protected Statement classBlock(RunNotifier notifier) {
|
||||
return childrenInvoker(notifier);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.junit.runners.ParentRunner#getDescription()
|
||||
*/
|
||||
@Override
|
||||
public Description getDescription() {
|
||||
|
||||
Description description = Description.createSuiteDescription(getName(), getRunnerAnnotations());
|
||||
|
||||
getFilteredChildren().stream().map(this::describeChild).forEach(description::addChild);
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.junit.runners.ParentRunner#filter(org.junit.runner.manipulation.Filter)
|
||||
*/
|
||||
public void filter(Filter filter) throws NoTestsRemainException {
|
||||
|
||||
synchronized (childrenLock) {
|
||||
|
||||
List<FrameworkMethod> children = new ArrayList<>(getFilteredChildren());
|
||||
List<FrameworkMethod> filtered = children.stream().filter(it -> {
|
||||
|
||||
if (filter.shouldRun(describeChild(it))) {
|
||||
try {
|
||||
filter.apply(it);
|
||||
return true;
|
||||
} catch (NoTestsRemainException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
if (filtered.isEmpty()) {
|
||||
throw new NoTestsRemainException();
|
||||
}
|
||||
|
||||
filteredChildren = Collections.unmodifiableCollection(filtered);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.junit.runners.ParentRunner#sort(org.junit.runner.manipulation.Sorter)
|
||||
*/
|
||||
public void sort(Sorter sorter) {
|
||||
|
||||
synchronized (childrenLock) {
|
||||
|
||||
getFilteredChildren().forEach(sorter::apply);
|
||||
|
||||
List<FrameworkMethod> sortedChildren = new ArrayList<>(getFilteredChildren());
|
||||
|
||||
sortedChildren.sort((o1, o2) -> sorter.compare(describeChild(o1), describeChild(o2)));
|
||||
|
||||
filteredChildren = Collections.unmodifiableCollection(sortedChildren);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run matching {@link org.openjdk.jmh.annotations.Benchmark} methods with options collected from
|
||||
* {@link org.springframework.core.env.Environment}.
|
||||
*/
|
||||
@Override
|
||||
protected Statement childrenInvoker(RunNotifier notifier) {
|
||||
|
||||
Collection<FrameworkMethod> methods = getFilteredChildren();
|
||||
CacheFunction cache = new CacheFunction(methods, this::describeChild);
|
||||
|
||||
if (methods.isEmpty()) {
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() {}
|
||||
};
|
||||
}
|
||||
|
||||
return new Statement() {
|
||||
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
try {
|
||||
doRun(notifier, methods, cache);
|
||||
} catch (NoBenchmarksException | NoTestsRemainException e) {
|
||||
methods.forEach(it -> notifier.fireTestIgnored(describeChild(it)));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void doRun(RunNotifier notifier, Collection<FrameworkMethod> methods, CacheFunction cache) throws Exception {
|
||||
|
||||
Class<?> jmhTestClass = getTestClass().getJavaClass();
|
||||
List<String> includes = jmhRunner.includes(jmhTestClass, methods);
|
||||
|
||||
if (includes.isEmpty()) {
|
||||
throw new NoTestsRemainException();
|
||||
}
|
||||
|
||||
ChainedOptionsBuilder optionsBuilder = jmhRunner.options(jmhTestClass);
|
||||
|
||||
includes.forEach(optionsBuilder::include);
|
||||
|
||||
Options options = optionsBuilder.build();
|
||||
NotifyingOutputFormat notifyingOutputFormat = new NotifyingOutputFormat(notifier, cache,
|
||||
createOutputFormat(options));
|
||||
|
||||
jmhRunner.publishResults(new Runner(options, notifyingOutputFormat).run());
|
||||
}
|
||||
|
||||
private Collection<FrameworkMethod> getFilteredChildren() {
|
||||
|
||||
if (filteredChildren == null) {
|
||||
synchronized (childrenLock) {
|
||||
if (filteredChildren == null) {
|
||||
filteredChildren = Collections.unmodifiableCollection(getChildren());
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredChildren;
|
||||
}
|
||||
|
||||
private static OutputFormat createOutputFormat(Options options) {
|
||||
|
||||
// sadly required here as the check cannot be made before calling this method in constructor
|
||||
if (options == null) {
|
||||
throw new IllegalArgumentException("Options not allowed to be null.");
|
||||
}
|
||||
|
||||
PrintStream out;
|
||||
if (options.getOutput().hasValue()) {
|
||||
try {
|
||||
out = new PrintStream(options.getOutput().get());
|
||||
} catch (FileNotFoundException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
} else {
|
||||
// Protect the System.out from accidental closing
|
||||
try {
|
||||
out = new UnCloseablePrintStream(System.out, Utils.guessConsoleEncoding());
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return OutputFormatFactory.createFormatInstance(out, options.verbosity().orElse(Defaults.VERBOSITY));
|
||||
}
|
||||
|
||||
private static String getBenchmarkName(FrameworkMethod it) {
|
||||
return it.getDeclaringClass().getName() + "." + it.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link OutputFormat} that delegates to another {@link OutputFormat} and notifies {@link RunNotifier} about the
|
||||
* progress.
|
||||
*/
|
||||
static class NotifyingOutputFormat implements OutputFormat {
|
||||
|
||||
private final RunNotifier notifier;
|
||||
private final Function<String, Description> descriptionResolver;
|
||||
private final OutputFormat delegate;
|
||||
private final List<String> log = new CopyOnWriteArrayList<>();
|
||||
|
||||
private volatile String lastKnownBenchmark;
|
||||
private volatile boolean recordOutput;
|
||||
|
||||
NotifyingOutputFormat(RunNotifier notifier, Function<String, Description> methods, OutputFormat delegate) {
|
||||
this.notifier = notifier;
|
||||
this.descriptionResolver = methods;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.openjdk.jmh.runner.format.OutputFormat#iteration(org.openjdk.jmh.infra.BenchmarkParams, org.openjdk.jmh.infra.IterationParams, int)
|
||||
*/
|
||||
@Override
|
||||
public void iteration(BenchmarkParams benchParams, IterationParams params, int iteration) {
|
||||
delegate.iteration(benchParams, params, iteration);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.openjdk.jmh.runner.format.OutputFormat#iterationResult(org.openjdk.jmh.infra.BenchmarkParams, org.openjdk.jmh.infra.IterationParams, int, org.openjdk.jmh.results.IterationResult)
|
||||
*/
|
||||
@Override
|
||||
public void iterationResult(BenchmarkParams benchParams, IterationParams params, int iteration,
|
||||
IterationResult data) {
|
||||
delegate.iterationResult(benchParams, params, iteration, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.openjdk.jmh.runner.format.OutputFormat#startBenchmark(org.openjdk.jmh.infra.BenchmarkParams)
|
||||
*/
|
||||
@Override
|
||||
public void startBenchmark(BenchmarkParams benchParams) {
|
||||
|
||||
log.clear();
|
||||
|
||||
lastKnownBenchmark = benchParams.getBenchmark();
|
||||
notifier.fireTestStarted(descriptionResolver.apply(benchParams.getBenchmark()));
|
||||
|
||||
delegate.startBenchmark(benchParams);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.openjdk.jmh.runner.format.OutputFormat#endBenchmark(org.openjdk.jmh.results.BenchmarkResult)
|
||||
*/
|
||||
@Override
|
||||
public void endBenchmark(BenchmarkResult result) {
|
||||
|
||||
recordOutput = false;
|
||||
String lastKnownBenchmark = this.lastKnownBenchmark;
|
||||
if (result != null) {
|
||||
notifier.fireTestFinished(descriptionResolver.apply(result.getParams().getBenchmark()));
|
||||
} else if (lastKnownBenchmark != null) {
|
||||
|
||||
String output = StringUtils.collectionToDelimitedString(log, System.getProperty("line.separator"));
|
||||
notifier.fireTestFailure(
|
||||
new Failure(descriptionResolver.apply(lastKnownBenchmark), new JmhRunnerException(output)));
|
||||
}
|
||||
|
||||
log.clear();
|
||||
delegate.endBenchmark(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.openjdk.jmh.runner.format.OutputFormat#startRun()
|
||||
*/
|
||||
@Override
|
||||
public void startRun() {
|
||||
delegate.startRun();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.openjdk.jmh.runner.format.OutputFormat#endRun(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public void endRun(Collection<RunResult> result) {
|
||||
delegate.endRun(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.openjdk.jmh.runner.format.OutputFormat#print(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void print(String s) {
|
||||
delegate.print(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.openjdk.jmh.runner.format.OutputFormat#println(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void println(String s) {
|
||||
|
||||
if (recordOutput && StringUtils.hasText(s)) {
|
||||
log.add(s);
|
||||
}
|
||||
|
||||
if (s.equals("<failure>")) {
|
||||
recordOutput = true;
|
||||
}
|
||||
|
||||
delegate.println(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.openjdk.jmh.runner.format.OutputFormat#flush()
|
||||
*/
|
||||
@Override
|
||||
public void flush() {
|
||||
delegate.flush();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.openjdk.jmh.runner.format.OutputFormat#close()
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
delegate.close();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.openjdk.jmh.runner.format.OutputFormat#verbosePrintln(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void verbosePrintln(String s) {
|
||||
delegate.verbosePrintln(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.openjdk.jmh.runner.format.OutputFormat#write(int)
|
||||
*/
|
||||
@Override
|
||||
public void write(int b) {
|
||||
delegate.write(b);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.openjdk.jmh.runner.format.OutputFormat#write(byte[])
|
||||
*/
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException {
|
||||
delegate.write(b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception proxy without stack trace.
|
||||
*/
|
||||
static class JmhRunnerException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = -1385006784559013618L;
|
||||
|
||||
JmhRunnerException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Throwable#fillInStackTrace()
|
||||
*/
|
||||
@Override
|
||||
public synchronized Throwable fillInStackTrace() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache {@link Function} for benchmark names to {@link Description}.
|
||||
*/
|
||||
static class CacheFunction implements Function<String, Description> {
|
||||
|
||||
private final Map<String, FrameworkMethod> methodMap = new ConcurrentHashMap<>();
|
||||
private final Collection<FrameworkMethod> methods;
|
||||
private final Function<FrameworkMethod, Description> describeFunction;
|
||||
|
||||
CacheFunction(Collection<FrameworkMethod> methods, Function<FrameworkMethod, Description> describeFunction) {
|
||||
this.methods = methods;
|
||||
this.describeFunction = describeFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a benchmark name (fqcn + "." + method name) to a {@link Description}.
|
||||
*
|
||||
* @param benchmarkName
|
||||
* @return
|
||||
*/
|
||||
public Description apply(String benchmarkName) {
|
||||
|
||||
FrameworkMethod frameworkMethod = methodMap.computeIfAbsent(benchmarkName, key -> {
|
||||
|
||||
Optional<FrameworkMethod> method = methods.stream().filter(it -> getBenchmarkName(it).equals(key)).findFirst();
|
||||
|
||||
return method.orElseThrow(() -> new IllegalArgumentException(
|
||||
String.format("Cannot resolve %s to a FrameworkMethod!", benchmarkName)));
|
||||
});
|
||||
|
||||
return describeFunction.apply(frameworkMethod);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.data.microbenchmark.common;
|
||||
|
||||
import jmh.mbr.core.ResultsWriter;
|
||||
import jmh.mbr.core.ResultsWriterFactory;
|
||||
|
||||
/**
|
||||
* {@link ResultsWriterFactory} plugin via {@link java.util.ServiceLoader}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class MicrobenchmarkResultsWriterFactory implements ResultsWriterFactory {
|
||||
|
||||
@Override
|
||||
public ResultsWriter forUri(String uri) {
|
||||
|
||||
if (uri.startsWith("http")) {
|
||||
return new HttpResultsWriter(uri);
|
||||
}
|
||||
|
||||
if (uri.startsWith("mongo")) {
|
||||
return new MongoResultsWriter(uri);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -15,13 +15,18 @@
|
||||
*/
|
||||
package org.springframework.data.microbenchmark.common;
|
||||
|
||||
import jmh.mbr.core.ResultsWriter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.openjdk.jmh.results.RunResult;
|
||||
import org.openjdk.jmh.runner.format.OutputFormat;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -32,21 +37,31 @@ import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.util.JSON;
|
||||
|
||||
/**
|
||||
* MongoDB specific {@link ResultsWriter} implementation.
|
||||
* MongoDB specific {@link ResultsWriterOld} implementation.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
class MongoResultsWriter implements ResultsWriter {
|
||||
|
||||
private final String uri;
|
||||
|
||||
MongoResultsWriter(String uri) {
|
||||
this.uri = uri;
|
||||
@Override
|
||||
public void write(OutputFormat output, Collection<RunResult> results) {
|
||||
|
||||
if (CollectionUtils.isEmpty(results)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
doWrite(results);
|
||||
} catch (RuntimeException e) {
|
||||
output.println("Failed to write results: " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Collection<RunResult> results) {
|
||||
private void doWrite(Collection<RunResult> results) {
|
||||
|
||||
Date now = new Date();
|
||||
StandardEnvironment env = new StandardEnvironment();
|
||||
@@ -89,7 +104,7 @@ class MongoResultsWriter implements ResultsWriter {
|
||||
* @param doc
|
||||
* @return
|
||||
*/
|
||||
private Document fixDocumentKeys(Document doc) {
|
||||
private static Document fixDocumentKeys(Document doc) {
|
||||
|
||||
Document sanitized = new Document();
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.springframework.data.microbenchmark.common;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.openjdk.jmh.results.RunResult;
|
||||
import org.openjdk.jmh.results.format.ResultFormatFactory;
|
||||
import org.openjdk.jmh.results.format.ResultFormatType;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
interface ResultsWriter {
|
||||
|
||||
/**
|
||||
* Write the {@link RunResult}s.
|
||||
*
|
||||
* @param results can be {@literal null}.
|
||||
*/
|
||||
void write(Collection<RunResult> results);
|
||||
|
||||
/**
|
||||
* Get the uri specific {@link ResultsWriter}.
|
||||
*
|
||||
* @param uri must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
static ResultsWriter forUri(String uri) {
|
||||
return uri.startsWith("mongodb:") ? new MongoResultsWriter(uri) : new HttpResultsWriter(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert {@link RunResult}s to JMH Json representation.
|
||||
*
|
||||
* @param results
|
||||
* @return json string representation of results.
|
||||
* @see org.openjdk.jmh.results.format.JSONResultFormat
|
||||
*/
|
||||
@SneakyThrows
|
||||
static String jsonifyResults(Collection<RunResult> results) {
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ResultFormatFactory.getInstance(ResultFormatType.JSON, new PrintStream(baos, true, "UTF-8")).writeOut(results);
|
||||
|
||||
return new String(baos.toByteArray(), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.data.microbenchmark.common.MicrobenchmarkResultsWriterFactory
|
||||
Reference in New Issue
Block a user