You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/10/18 23:28:34 UTC
[sling-org-apache-sling-junit-performance] 01/14: SLING-3756 -
Create an improved JUnit test runner for performance tests
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-junit-performance.git
commit 918b6403c2d293a8c257a91549a557a5691e16b6
Author: Antonio Sanso <as...@apache.org>
AuthorDate: Thu Jul 10 14:09:33 2014 +0000
SLING-3756 - Create an improved JUnit test runner for performance tests
* applied patch from Francesco Mari (Thanks!!)
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1609464 13f79535-47bb-0310-9956-ffa450edef68
---
pom.xml | 70 ++++++
.../performance/impl/InvokePerformanceBlock.java | 85 +++++++
.../performance/impl/InvokePerformanceMethod.java | 48 ++++
.../sling/junit/performance/impl/Listeners.java | 119 ++++++++++
.../junit/performance/impl/PerformanceMethod.java | 61 +++++
.../performance/listener/StatisticsListener.java | 68 ++++++
.../sling/junit/performance/runner/Listen.java | 30 +++
.../sling/junit/performance/runner/Listener.java | 58 +++++
.../performance/runner/PerformanceRunner.java | 145 ++++++++++++
.../junit/performance/runner/PerformanceTest.java | 37 +++
.../performance/runner/PerformanceRunnerTest.java | 247 +++++++++++++++++++++
11 files changed, 968 insertions(+)
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..cc2f17e
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>sling</artifactId>
+ <version>19</version>
+ <relativePath>../../../parent/pom.xml</relativePath>
+ </parent>
+
+ <artifactId>org.apache.sling.junit.performance</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <name>Apache Sling JUnit Performance</name>
+ <description>Provides utilities for JUnit to run performance tests and report results</description>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.11</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-core</artifactId>
+ <version>1.3</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-math</artifactId>
+ <version>2.2</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/junit/performance/impl/InvokePerformanceBlock.java b/src/main/java/org/apache/sling/junit/performance/impl/InvokePerformanceBlock.java
new file mode 100644
index 0000000..8af19e5
--- /dev/null
+++ b/src/main/java/org/apache/sling/junit/performance/impl/InvokePerformanceBlock.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.sling.junit.performance.impl;
+
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+import org.junit.runners.model.TestClass;
+
+public class InvokePerformanceBlock extends Statement {
+
+ private final PerformanceMethod method;
+
+ private final Statement inner;
+
+ private final Listeners listeners;
+
+ private final TestClass testClass;
+
+ public InvokePerformanceBlock(TestClass testClass, FrameworkMethod method, Statement inner, Listeners listeners) {
+ this.testClass = testClass;
+ this.method = new PerformanceMethod(method);
+ this.inner = inner;
+ this.listeners = listeners;
+ }
+
+ @Override
+ public void evaluate() throws Throwable {
+
+ // Run warm-up invocations
+
+ listeners.warmUpStarted(testClass.getName(), method.getName());
+ run(method.getWarmUpInvocations(), method.getWarmUpTime());
+ listeners.warmUpFinished(testClass.getName(), method.getName());
+
+ // Run performance invocations
+
+ listeners.executionStarted(testClass.getName(), method.getName());
+ run(method.getRunInvocations(), method.getRunTime());
+ listeners.executionFinished(testClass.getName(), method.getName());
+ }
+
+ private void run(int invocations, int time) throws Throwable {
+ if (invocations > 0) {
+ runByInvocations(invocations);
+ return;
+ }
+
+ if (time > 0) {
+ runByTime(time);
+ return;
+ }
+
+ throw new IllegalArgumentException("no time or number of invocations specified");
+ }
+
+ private void runByInvocations(int invocations) throws Throwable {
+ for (int i = 0; i < invocations; i++) {
+ inner.evaluate();
+ }
+ }
+
+ private void runByTime(int seconds) throws Throwable {
+ long end = System.currentTimeMillis() + seconds * 1000;
+
+ while (System.currentTimeMillis() < end) {
+ inner.evaluate();
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/junit/performance/impl/InvokePerformanceMethod.java b/src/main/java/org/apache/sling/junit/performance/impl/InvokePerformanceMethod.java
new file mode 100644
index 0000000..ed8b5c7
--- /dev/null
+++ b/src/main/java/org/apache/sling/junit/performance/impl/InvokePerformanceMethod.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.sling.junit.performance.impl;
+
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+import org.junit.runners.model.TestClass;
+
+public class InvokePerformanceMethod extends Statement {
+
+ private final TestClass testClass;
+
+ private final PerformanceMethod method;
+
+ private final Statement inner;
+
+ private final Listeners listeners;
+
+ public InvokePerformanceMethod(TestClass testClass, FrameworkMethod method, Statement inner, Listeners listeners) {
+ this.testClass = testClass;
+ this.method = new PerformanceMethod(method);
+ this.inner = inner;
+ this.listeners = listeners;
+ }
+
+ @Override
+ public void evaluate() throws Throwable {
+ listeners.iterationStarted(testClass.getName(), method.getName());
+ inner.evaluate();
+ listeners.iterationFinished(testClass.getName(), method.getName());
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/junit/performance/impl/Listeners.java b/src/main/java/org/apache/sling/junit/performance/impl/Listeners.java
new file mode 100644
index 0000000..1a7b861
--- /dev/null
+++ b/src/main/java/org/apache/sling/junit/performance/impl/Listeners.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.sling.junit.performance.impl;
+
+import org.apache.sling.junit.performance.runner.Listener;
+
+import java.util.List;
+
+public class Listeners {
+
+ private boolean isWarmUp;
+
+ private final List<Listener> listeners;
+
+ public Listeners(List<Listener> listeners) {
+ this.listeners = listeners;
+ }
+
+ private interface Invoker {
+
+ void invoke(Listener listener) throws Exception;
+
+ }
+
+ private void invoke(Invoker invoker) throws Exception {
+ for (Listener listener : listeners) {
+ invoker.invoke(listener);
+ }
+
+ }
+
+ public void warmUpStarted(final String className, final String testName) throws Exception {
+ isWarmUp = true;
+
+ invoke(new Invoker() {
+
+ public void invoke(Listener listener) throws Exception {
+ listener.warmUpStarted(className, testName);
+ }
+
+ });
+ }
+
+ public void warmUpFinished(final String className, final String testName) throws Exception {
+ isWarmUp = false;
+
+ invoke(new Invoker() {
+
+ public void invoke(Listener listener) throws Exception {
+ listener.warmUpFinished(className, testName);
+ }
+
+ });
+ }
+
+ public void executionStarted(final String className, final String testName) throws Exception {
+ invoke(new Invoker() {
+
+ public void invoke(Listener listener) throws Exception {
+ listener.executionStarted(className, testName);
+ }
+
+ });
+ }
+
+ public void executionFinished(final String className, final String testName) throws Exception {
+ invoke(new Invoker() {
+
+ public void invoke(Listener listener) throws Exception {
+ listener.executionFinished(className, testName);
+ }
+
+ });
+ }
+
+ public void iterationStarted(final String className, final String testName) throws Exception {
+ invoke(new Invoker() {
+
+ public void invoke(Listener listener) throws Exception {
+ if (isWarmUp) {
+ listener.warmUpIterationStarted(className, testName);
+ } else {
+ listener.executionIterationStarted(className, testName);
+ }
+ }
+
+ });
+ }
+
+ public void iterationFinished(final String className, final String testName) throws Exception {
+ invoke(new Invoker() {
+
+ public void invoke(Listener listener) throws Exception {
+ if (isWarmUp) {
+ listener.warmUpIterationFinished(className, testName);
+ } else {
+ listener.executionIterationFinished(className, testName);
+ }
+ }
+
+ });
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/junit/performance/impl/PerformanceMethod.java b/src/main/java/org/apache/sling/junit/performance/impl/PerformanceMethod.java
new file mode 100644
index 0000000..296a030
--- /dev/null
+++ b/src/main/java/org/apache/sling/junit/performance/impl/PerformanceMethod.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.sling.junit.performance.impl;
+
+import org.apache.sling.junit.performance.runner.PerformanceTest;
+import org.junit.runners.model.FrameworkMethod;
+
+public class PerformanceMethod {
+
+ private final FrameworkMethod method;
+
+ public PerformanceMethod(FrameworkMethod method) {
+ this.method = method;
+ }
+
+ private PerformanceTest getPerformanceTestAnnotation() {
+ PerformanceTest performanceTest = method.getAnnotation(PerformanceTest.class);
+
+ if (performanceTest == null) {
+ throw new IllegalStateException("a performance method should be annotated with @PerformanceTest");
+ }
+
+ return performanceTest;
+ }
+
+ public int getWarmUpTime() {
+ return getPerformanceTestAnnotation().warmUpTime();
+ }
+
+ public int getWarmUpInvocations() {
+ return getPerformanceTestAnnotation().warmUpInvocations();
+ }
+
+ public int getRunTime() {
+ return getPerformanceTestAnnotation().runTime();
+ }
+
+ public int getRunInvocations() {
+ return getPerformanceTestAnnotation().runInvocations();
+ }
+
+ public String getName() {
+ return method.getName();
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/junit/performance/listener/StatisticsListener.java b/src/main/java/org/apache/sling/junit/performance/listener/StatisticsListener.java
new file mode 100644
index 0000000..3bd7ad9
--- /dev/null
+++ b/src/main/java/org/apache/sling/junit/performance/listener/StatisticsListener.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.sling.junit.performance.listener;
+
+import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
+import org.apache.sling.junit.performance.runner.Listener;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A performance test listener which computes statistics about the execution of a test and makes them available in form
+ * of a {@link org.apache.commons.math.stat.descriptive.DescriptiveStatistics} object.
+ * <p/>
+ * Clients of this listener are supposed to subclass it and to override the {@link #executionStatistics} method to react
+ * when new statistics for a method are available.
+ */
+public abstract class StatisticsListener extends Listener {
+
+ private DescriptiveStatistics statistics;
+
+ private long begin;
+
+ @Override
+ public void executionStarted(String className, String testName) throws Exception {
+ statistics = new DescriptiveStatistics();
+ }
+
+ @Override
+ public void executionIterationStarted(String className, String testName) throws Exception {
+ begin = System.nanoTime();
+ }
+
+ @Override
+ public void executionIterationFinished(String className, String testName) throws Exception {
+ statistics.addValue(TimeUnit.MILLISECONDS.convert(System.nanoTime() - begin, TimeUnit.NANOSECONDS));
+ }
+
+ @Override
+ public void executionFinished(String className, String testName) throws Exception {
+ executionStatistics(className, testName, statistics);
+ }
+
+ /**
+ * This method is called when new statistics are available for a performance method.
+ *
+ * @param className Name of the class containing the performance test.
+ * @param testName Name of the method implementing the performance test.
+ * @param statistics Statistics about the executions of the performance test.
+ * @throws Exception
+ */
+ protected abstract void executionStatistics(String className, String testName, DescriptiveStatistics statistics) throws Exception;
+
+}
diff --git a/src/main/java/org/apache/sling/junit/performance/runner/Listen.java b/src/main/java/org/apache/sling/junit/performance/runner/Listen.java
new file mode 100644
index 0000000..5d9b422
--- /dev/null
+++ b/src/main/java/org/apache/sling/junit/performance/runner/Listen.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.sling.junit.performance.runner;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Mark a static method or a static instance variable as a listener for a performance test. The variable or the result
+ * type of the method must be a {@link Listener} or a subclass of it.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Listen {
+
+}
diff --git a/src/main/java/org/apache/sling/junit/performance/runner/Listener.java b/src/main/java/org/apache/sling/junit/performance/runner/Listener.java
new file mode 100644
index 0000000..a5d859c
--- /dev/null
+++ b/src/main/java/org/apache/sling/junit/performance/runner/Listener.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.sling.junit.performance.runner;
+
+/**
+ * Base class for Listener classes with empty methods for every possible event. A listener is made available to the
+ * {@link PerformanceRunner} using the {@link Listen} annotation.
+ */
+public class Listener {
+
+ public void warmUpStarted(String className, String testName) throws Exception {
+
+ }
+
+ public void warmUpFinished(String className, String testName) throws Exception {
+
+ }
+
+ public void executionStarted(String className, String testName) throws Exception {
+
+ }
+
+ public void executionFinished(String className, String testName) throws Exception {
+
+ }
+
+ public void warmUpIterationStarted(String className, String testName) throws Exception {
+
+ }
+
+ public void executionIterationStarted(String className, String testName) throws Exception {
+
+ }
+
+ public void warmUpIterationFinished(String className, String testName) throws Exception {
+
+ }
+
+ public void executionIterationFinished(String className, String testName) throws Exception {
+
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/junit/performance/runner/PerformanceRunner.java b/src/main/java/org/apache/sling/junit/performance/runner/PerformanceRunner.java
new file mode 100644
index 0000000..d9aa3de
--- /dev/null
+++ b/src/main/java/org/apache/sling/junit/performance/runner/PerformanceRunner.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.sling.junit.performance.runner;
+
+import org.apache.sling.junit.performance.impl.InvokePerformanceBlock;
+import org.apache.sling.junit.performance.impl.InvokePerformanceMethod;
+import org.apache.sling.junit.performance.impl.Listeners;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.FrameworkField;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Custom runner to execute performance tests using JUnit.
+ * <p/>
+ * For a method to be executed as a performance test, it must be annotated with {@link PerformanceTest}. Every time this
+ * annotation is specified, the user must also specify the warm up and execution strategy, because these information are
+ * mandatory for the runner to work properly. The warm up and execution strategy can be provided in two ways: by
+ * specifying the number of executions to run, or by specifying the amount of time the method should run.
+ * <p/>
+ * The runner can also invoke one or more {@link Listener}. The listener is specified as a static variable of the test
+ * class or as the result of a static method. The listeners are made available to the runner by annotating them with the
+ * {@link Listen} annotation.
+ */
+public class PerformanceRunner extends BlockJUnit4ClassRunner {
+
+ private Listeners listeners;
+
+ public PerformanceRunner(Class<?> testClass) throws InitializationError {
+ super(testClass);
+
+ try {
+ listeners = new Listeners(readListeners());
+ } catch (Exception e) {
+ throw new InitializationError(e);
+ }
+ }
+
+ @Override
+ protected List<FrameworkMethod> computeTestMethods() {
+ return getTestClass().getAnnotatedMethods(PerformanceTest.class);
+ }
+
+ @Override
+ protected Statement methodBlock(FrameworkMethod method) {
+ return new InvokePerformanceBlock(getTestClass(), method, super.methodBlock(method), listeners);
+ }
+
+ @Override
+ protected Statement methodInvoker(FrameworkMethod method, Object test) {
+ return new InvokePerformanceMethod(getTestClass(), method, super.methodInvoker(method, test), listeners);
+ }
+
+ private List<Listener> readListeners() throws Exception {
+ List<Listener> listeners = new ArrayList<Listener>();
+
+ listeners.addAll(readListenersFromStaticFields());
+ listeners.addAll(readListenersFromStaticMethods());
+
+ return listeners;
+ }
+
+ private List<Listener> readListenersFromStaticMethods() throws Exception {
+ List<Listener> listeners = new ArrayList<Listener>();
+
+ for (FrameworkMethod method : getTestClass().getAnnotatedMethods(Listen.class)) {
+ if (!method.isPublic()) {
+ throw new IllegalArgumentException("a @Listen method must be public");
+ }
+
+ if (!method.isStatic()) {
+ throw new IllegalArgumentException("a @Listen method must be static");
+ }
+
+ if (!Listener.class.isAssignableFrom(method.getReturnType())) {
+ throw new IllegalArgumentException("a @Listen method must be of type Listener");
+ }
+
+ Listener listener = null;
+
+ try {
+ listener = (Listener) method.invokeExplosively(null);
+ } catch (Throwable throwable) {
+ throw new RuntimeException("error while invoking the @Listen method", throwable);
+ }
+
+ if (listener == null) {
+ throw new IllegalArgumentException("a @Listen method must return a non-null value");
+ }
+
+ listeners.add(listener);
+ }
+
+ return listeners;
+ }
+
+ private List<Listener> readListenersFromStaticFields() throws Exception {
+ List<Listener> reporters = new ArrayList<Listener>();
+
+ for (FrameworkField field : getTestClass().getAnnotatedFields(Listen.class)) {
+ if (!field.isPublic()) {
+ throw new IllegalArgumentException("a @Listen field must be public");
+ }
+
+ if (!field.isStatic()) {
+ throw new IllegalArgumentException("a @Listen field must be static");
+ }
+
+ if (!Listener.class.isAssignableFrom(field.getType())) {
+ throw new IllegalArgumentException("a @Listen field must be of type Listener");
+ }
+
+ Listener listener = (Listener) field.get(null);
+
+ if (listener == null) {
+ throw new IllegalArgumentException("a @Listen field must not be null");
+ }
+
+ reporters.add(listener);
+ }
+
+ return reporters;
+ }
+
+}
+
diff --git a/src/main/java/org/apache/sling/junit/performance/runner/PerformanceTest.java b/src/main/java/org/apache/sling/junit/performance/runner/PerformanceTest.java
new file mode 100644
index 0000000..19442a4
--- /dev/null
+++ b/src/main/java/org/apache/sling/junit/performance/runner/PerformanceTest.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.sling.junit.performance.runner;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Annotate a method to mark it as a performance test. It also specifies the warm up and execution strategy.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface PerformanceTest {
+
+ int warmUpTime() default 0;
+
+ int runTime() default 0;
+
+ int runInvocations() default 0;
+
+ int warmUpInvocations() default 0;
+
+}
diff --git a/src/test/java/org/apache/sling/junit/performance/runner/PerformanceRunnerTest.java b/src/test/java/org/apache/sling/junit/performance/runner/PerformanceRunnerTest.java
new file mode 100644
index 0000000..5141b5a
--- /dev/null
+++ b/src/test/java/org/apache/sling/junit/performance/runner/PerformanceRunnerTest.java
@@ -0,0 +1,247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.sling.junit.performance.runner;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Result;
+import org.junit.runner.RunWith;
+import org.junit.runner.notification.Failure;
+
+public class PerformanceRunnerTest {
+
+ private Result runTest(Class<?> testClass) {
+ return JUnitCore.runClasses(testClass);
+ }
+
+ private void assertTestFails(Class<?> testClass, String message) {
+ Result result = runTest(testClass);
+
+ boolean isInitializationError = false;
+
+ for (Failure failure : result.getFailures()) {
+ isInitializationError = isInitializationError || failure.getException().getMessage().equals(message);
+ }
+
+ Assert.assertEquals(true, isInitializationError);
+ }
+
+ @RunWith(PerformanceRunner.class)
+ public static class PrivateListenerField {
+
+ @Listen
+ private static Listener listener = new Listener();
+
+ @PerformanceTest
+ public void test() {
+
+ }
+
+ }
+
+ @Test
+ public void testPrivateListenerField() {
+ assertTestFails(PrivateListenerField.class, "a @Listen field must be public");
+ }
+
+ @RunWith(PerformanceRunner.class)
+ public static class InstanceListenerField {
+
+ @Listen
+ public Listener listener = new Listener();
+
+ @PerformanceTest
+ public void test() {
+
+ }
+
+ }
+
+ @Test
+ public void testInstanceListenerField() {
+ assertTestFails(InstanceListenerField.class, "a @Listen field must be static");
+ }
+
+ @RunWith(PerformanceRunner.class)
+ public static class WrongTypeListenerField {
+
+ @Listen
+ public static Integer listener = 42;
+
+ @PerformanceTest
+ public void test() {
+
+ }
+
+ }
+
+ @Test
+ public void testWrongTypeListenerField() {
+ assertTestFails(WrongTypeListenerField.class, "a @Listen field must be of type Listener");
+ }
+
+ @RunWith(PerformanceRunner.class)
+ public static class NullListenerField {
+
+ @Listen
+ public static Listener listener = null;
+
+ @PerformanceTest
+ public void test() {
+
+ }
+
+ }
+
+ @Test
+ public void testNullListenerField() {
+ assertTestFails(NullListenerField.class, "a @Listen field must not be null");
+ }
+
+ @RunWith(PerformanceRunner.class)
+ public static class PrivateListenerMethod {
+
+ @Listen
+ private static Listener listener() {
+ return new Listener();
+ }
+
+ @PerformanceTest
+ public void test() {
+
+ }
+
+ }
+
+ @Test
+ public void testPrivateListenerMethod() {
+ assertTestFails(PrivateListenerMethod.class, "a @Listen method must be public");
+ }
+
+ @RunWith(PerformanceRunner.class)
+ public static class InstanceListenerMethod {
+
+ @Listen
+ public Listener listener() {
+ return new Listener();
+ }
+
+ @PerformanceTest
+ public void test() {
+
+ }
+
+ }
+
+ @Test
+ public void testInstanceListenerMethod() {
+ assertTestFails(InstanceListenerMethod.class, "a @Listen method must be static");
+ }
+
+ @RunWith(PerformanceRunner.class)
+ public static class WrongTypeListenerMethod {
+
+ @Listen
+ public static Integer listener() {
+ return 42;
+ }
+
+ @PerformanceTest
+ public void test() {
+
+ }
+
+ }
+
+ @Test
+ public void testWrongTypeListenerMethod() {
+ assertTestFails(WrongTypeListenerMethod.class, "a @Listen method must be of type Listener");
+ }
+
+ @RunWith(PerformanceRunner.class)
+ public static class BuggyListenerMethod {
+
+ @Listen
+ public static Listener listener() {
+ throw new RuntimeException();
+ }
+
+ @PerformanceTest
+ public void test() {
+
+ }
+
+ }
+
+ @Test
+ public void testBuggyListenerMethod() {
+ assertTestFails(BuggyListenerMethod.class, "error while invoking the @Listen method");
+ }
+
+ @RunWith(PerformanceRunner.class)
+ public static class NullListenerMethod {
+
+ @Listen
+ public static Listener listener() {
+ return null;
+ }
+
+ @PerformanceTest
+ public void test() {
+
+ }
+
+ }
+
+ @Test
+ public void testNullListenerMethod() {
+ assertTestFails(NullListenerMethod.class, "a @Listen method must return a non-null value");
+ }
+
+ @RunWith(PerformanceRunner.class)
+ public static class ExecutionStrategyNotSpecified {
+
+ @PerformanceTest(warmUpInvocations = 10)
+ public void test() {
+
+ }
+
+ }
+
+ @Test
+ public void testExecutionStrategyNotSpecified() {
+ assertTestFails(ExecutionStrategyNotSpecified.class, "no time or number of invocations specified");
+ }
+
+ @RunWith(PerformanceRunner.class)
+ public static class WarmUpStrategyNotSpecified {
+
+ @PerformanceTest(runInvocations = 10)
+ public void test() {
+
+ }
+
+ }
+
+ @Test
+ public void testWarmUpStrategyNotSpecified() {
+ assertTestFails(WarmUpStrategyNotSpecified.class, "no time or number of invocations specified");
+ }
+
+}
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.