You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ta...@apache.org on 2015/10/27 22:33:10 UTC

qpid-jms git commit: QPIDJMS-132 Adds an experimental JUnit test runner along with a test rule and annotation value for defining repeat counts on tests to use when attempting to reproduce test failures that occurs sporadically.

Repository: qpid-jms
Updated Branches:
  refs/heads/master fc9544ecf -> acd2ebbaa


QPIDJMS-132 Adds an experimental JUnit test runner along with a test
rule and annotation value for defining repeat counts on tests to use
when attempting to reproduce test failures that occurs sporadically.

Also adds some intial support for configuring test timeout extensions on
slow CI machines.

Project: http://git-wip-us.apache.org/repos/asf/qpid-jms/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-jms/commit/acd2ebba
Tree: http://git-wip-us.apache.org/repos/asf/qpid-jms/tree/acd2ebba
Diff: http://git-wip-us.apache.org/repos/asf/qpid-jms/diff/acd2ebba

Branch: refs/heads/master
Commit: acd2ebbaae8822008036a52df4720a3e7be1245e
Parents: fc9544e
Author: Timothy Bish <ta...@gmail.com>
Authored: Tue Oct 27 17:33:00 2015 -0400
Committer: Timothy Bish <ta...@gmail.com>
Committed: Tue Oct 27 17:33:00 2015 -0400

----------------------------------------------------------------------
 .../jms/integration/SessionIntegrationTest.java |   6 +
 .../apache/qpid/jms/util/QPidJMSTestRunner.java | 107 ++++++++++++++
 .../java/org/apache/qpid/jms/util/Repeat.java   |  34 +++++
 .../org/apache/qpid/jms/util/RepeatRule.java    |  38 +++++
 .../apache/qpid/jms/util/RepeatStatement.java   | 138 +++++++++++++++++++
 5 files changed, 323 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/acd2ebba/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SessionIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SessionIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SessionIntegrationTest.java
index c8603f8..38a2cf7 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SessionIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SessionIntegrationTest.java
@@ -77,14 +77,19 @@ import org.apache.qpid.jms.test.testpeer.matchers.sections.MessageAnnotationsSec
 import org.apache.qpid.jms.test.testpeer.matchers.sections.MessageHeaderSectionMatcher;
 import org.apache.qpid.jms.test.testpeer.matchers.sections.TransferPayloadCompositeMatcher;
 import org.apache.qpid.jms.test.testpeer.matchers.types.EncodedAmqpValueMatcher;
+import org.apache.qpid.jms.util.QPidJMSTestRunner;
+import org.apache.qpid.jms.util.Repeat;
 import org.apache.qpid.proton.amqp.Binary;
 import org.apache.qpid.proton.amqp.Symbol;
 import org.apache.qpid.proton.amqp.UnsignedInteger;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@RunWith(QPidJMSTestRunner.class)
 public class SessionIntegrationTest extends QpidJmsTestCase {
+
     private static final Logger LOG = LoggerFactory.getLogger(SessionIntegrationTest.class);
 
     private final IntegrationTestFixture testFixture = new IntegrationTestFixture();
@@ -1015,6 +1020,7 @@ public class SessionIntegrationTest extends QpidJmsTestCase {
     }
 
     @Test(timeout = 20000)
+    @Repeat(repetitions = 1)
     public void testRemotelyEndSessionWithProducer() throws Exception {
         final String BREAD_CRUMB = "ErrorMessage";
 

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/acd2ebba/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/QPidJMSTestRunner.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/QPidJMSTestRunner.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/QPidJMSTestRunner.java
new file mode 100644
index 0000000..2e94944
--- /dev/null
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/QPidJMSTestRunner.java
@@ -0,0 +1,107 @@
+/*
+ * 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.qpid.jms.util;
+
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Test;
+import org.junit.internal.runners.statements.FailOnTimeout;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A Custom JUnit test runner for customizing JUnit tests run in QPid JMS.
+ */
+public class QPidJMSTestRunner extends BlockJUnit4ClassRunner {
+
+    private static final Logger LOG = LoggerFactory.getLogger(QPidJMSTestRunner.class);
+
+    public QPidJMSTestRunner(Class<?> klass) throws InitializationError {
+        super(klass);
+    }
+
+    @Override
+    protected Statement methodBlock(final FrameworkMethod method) {
+        Statement statement = super.methodBlock(method);
+
+        // Check for repeats needed
+        statement = withPotentialRepeat(method, statement);
+
+        return statement;
+    }
+
+    /**
+     * Perform the same logic as
+     * {@link BlockJUnit4ClassRunner#withPotentialTimeout(FrameworkMethod, Object, Statement)}
+     * but with additional support for changing the coded timeout with an extended value.
+     *
+     * @return either a {@link FailOnTimeout}, or the supplied {@link Statement} as appropriate.
+     */
+    @SuppressWarnings("deprecation")
+    @Override
+    protected Statement withPotentialTimeout(FrameworkMethod frameworkMethod, Object testInstance, Statement next) {
+        long originalTimeout = getOriginalTimeout(frameworkMethod);
+
+        if (originalTimeout > 0) {
+            long additionalTimeUnderCI = Long.getLong("org.apache.qpid.jms.testTimeoutIncrement", 0);
+
+            if (additionalTimeUnderCI > 0) {
+                LOG.info("Adding additional time {}ms to test timeout {}ms", additionalTimeUnderCI, originalTimeout);
+                originalTimeout += additionalTimeUnderCI;
+            }
+
+            next = FailOnTimeout.builder().
+                withTimeout(originalTimeout, TimeUnit.MILLISECONDS).build(next);
+        } else {
+            next = super.withPotentialTimeout(frameworkMethod, testInstance, next);
+        }
+
+        return next;
+    }
+
+    /**
+     * Check for the presence of a {@link Repeat} annotation and return a {@link RepeatStatement}
+     * to handle executing the test repeated or the original value if not repeating.
+     *
+     * @return either a {@link RepeatStatement}, or the supplied {@link Statement} as appropriate.
+     */
+    protected Statement withPotentialRepeat(FrameworkMethod frameworkMethod, Statement next) {
+
+        Repeat repeatAnnotation = frameworkMethod.getAnnotation(Repeat.class);
+
+        if (repeatAnnotation != null) {
+            next = RepeatStatement.builder().build(repeatAnnotation, next);
+        }
+
+        return next;
+    }
+
+    /**
+     * Retrieve the original JUnit {@code timeout} from the {@link Test @Test}
+     * annotation on the incoming {@linkplain FrameworkMethod test method}.
+     *
+     * @return the timeout, or {@code 0} if none was specified
+     */
+    protected long getOriginalTimeout(FrameworkMethod frameworkMethod) {
+        Test test = frameworkMethod.getAnnotation(Test.class);
+        return (test != null && test.timeout() > 0 ? test.timeout() : 0);
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/acd2ebba/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/Repeat.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/Repeat.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/Repeat.java
new file mode 100644
index 0000000..43b4d80
--- /dev/null
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/Repeat.java
@@ -0,0 +1,34 @@
+/*
+ * 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.qpid.jms.util;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A Custom Test annotation used to repeat a troublesome test multiple
+ * times when attempting to reproduce an intermittent failure.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ java.lang.annotation.ElementType.METHOD })
+public @interface Repeat {
+
+    int repetitions() default 1;
+
+    boolean untilFailure() default false;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/acd2ebba/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/RepeatRule.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/RepeatRule.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/RepeatRule.java
new file mode 100644
index 0000000..f0f870d
--- /dev/null
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/RepeatRule.java
@@ -0,0 +1,38 @@
+/*
+ * 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.qpid.jms.util;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Test rule used to allow a test to have the Repeat annotation applied.
+ */
+public class RepeatRule implements TestRule {
+
+    @Override
+    public Statement apply(Statement statement, Description description) {
+        Repeat repeat = description.getAnnotation(Repeat.class);
+
+        if (repeat != null) {
+            statement = RepeatStatement.builder().build(repeat, statement);
+        }
+
+        return statement;
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/acd2ebba/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/RepeatStatement.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/RepeatStatement.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/RepeatStatement.java
new file mode 100644
index 0000000..9440da2
--- /dev/null
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/util/RepeatStatement.java
@@ -0,0 +1,138 @@
+/*
+ * 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.qpid.jms.util;
+
+import org.junit.runners.model.Statement;
+
+public final class RepeatStatement extends Statement {
+
+    private final int repetitions;
+    private final boolean untilFailure;
+    private final Statement statement;
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public RepeatStatement(int times, boolean untilFailure, Statement statement) {
+        this.repetitions = times;
+        this.untilFailure = untilFailure;
+        this.statement = statement;
+    }
+
+    protected RepeatStatement(Builder builder, Statement next) {
+        this.repetitions = builder.getRepetitions();
+        this.untilFailure = builder.isUntilFailure();
+        this.statement = next;
+    }
+
+    @Override
+    public void evaluate() throws Throwable {
+        for (int i = 0; i < repetitions && !untilFailure; i++) {
+            statement.evaluate();
+        }
+    }
+
+    /**
+     * Builder for {@link Repeat}.
+     */
+    public static class Builder {
+        private int repetitions = 1;
+        private boolean untilFailure = false;
+
+        protected Builder() {}
+
+        /**
+         * Specifies the number of times to run the test.
+         *
+         * @param repetitions
+         *      The number of times to run the test.
+         *
+         * @return {@code this} for method chaining.
+         */
+        public Builder withRepetitions(int repetitions) {
+            if (repetitions <= 0) {
+                throw new IllegalArgumentException("repetitions must be greater than zero");
+            }
+
+            this.repetitions = repetitions;
+            return this;
+        }
+
+        /**
+         * Specifies the number of times to run the test.
+         *
+         * @param untilFailure
+         *      true if the test should run until a failure occurs.
+         *
+         * @return {@code this} for method chaining.
+         */
+        public Builder withRunUntilFailure(boolean untilFailure) {
+            this.untilFailure = untilFailure;
+            return this;
+        }
+
+        protected int getRepetitions() {
+            return repetitions;
+        }
+
+        protected boolean isUntilFailure()  {
+            return untilFailure;
+        }
+
+        /**
+         * Builds a {@link RepeatStatement} instance using the values in this builder.
+         *
+         * @param next
+         *      The statement instance to wrap with the newly create repeat statement.
+         *
+         * @return a new {@link RepeatStatement} that wraps the given {@link Statement}.
+         */
+        public RepeatStatement build(Statement next) {
+            if (next == null) {
+                throw new NullPointerException("statement cannot be null");
+            }
+
+            return new RepeatStatement(this, next);
+        }
+
+        /**
+         * Builds a {@link RepeatStatement} instance using the values in this builder.
+         *
+         * @param annotation
+         *      The {@link Repeat} annotation that triggered this statement being created.
+         * @param next
+         *      The statement instance to wrap with the newly create repeat statement.
+         *
+         * @return a new {@link RepeatStatement} that wraps the given {@link Statement}.
+         */
+        public RepeatStatement build(Repeat annotation, Statement next) {
+            if (next == null) {
+                throw new NullPointerException("statement cannot be null");
+            }
+
+            if (annotation == null) {
+                throw new NullPointerException("annotation cannot be null");
+            }
+
+            withRepetitions(annotation.repetitions());
+            withRunUntilFailure(annotation.untilFailure());
+
+            return new RepeatStatement(this, next);
+        }
+    }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org