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 <[email protected]> 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 "[email protected]" <[email protected]>.
