geode-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kl...@apache.org
Subject [18/19] incubator-geode git commit: GEODE-772: Add custom annotations and rules for tests
Date Thu, 21 Jan 2016 19:09:04 GMT
GEODE-772: Add custom annotations and rules for tests


Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/1be4f932
Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/1be4f932
Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/1be4f932

Branch: refs/heads/feature/GEODE-715
Commit: 1be4f932a80f53c0588b49227e7daa518eb389c5
Parents: c0c64de
Author: Kirk Lund <klund@pivotal.io>
Authored: Wed Jan 20 16:30:20 2016 -0800
Committer: Kirk Lund <klund@pivotal.io>
Committed: Thu Jan 21 10:48:44 2016 -0800

----------------------------------------------------------------------
 .../gemfire/test/junit/ConditionalIgnore.java   |  49 +++
 .../gemfire/test/junit/IgnoreCondition.java     |  32 ++
 .../gemfire/test/junit/IgnoreUntil.java         |  49 +++
 .../com/gemstone/gemfire/test/junit/Repeat.java |  43 +++
 .../com/gemstone/gemfire/test/junit/Retry.java  |  38 +++
 .../test/junit/rules/ConditionalIgnoreRule.java | 123 ++++++++
 .../test/junit/rules/ExpectedTimeoutRule.java   | 180 +++++++++++
 .../test/junit/rules/IgnoreUntilRule.java       | 123 ++++++++
 .../gemfire/test/junit/rules/RepeatRule.java    |  81 +++++
 .../gemfire/test/junit/rules/RetryRule.java     | 181 +++++++++++
 .../rules/SerializableExternalResource.java     | 107 +++++++
 .../test/junit/rules/SerializableRuleChain.java | 119 ++++++++
 .../rules/SerializableTemporaryFolder.java      |  70 +++++
 .../test/junit/rules/SerializableTestName.java  |  54 ++++
 .../test/junit/rules/SerializableTestRule.java  |  33 ++
 .../junit/rules/SerializableTestWatcher.java    |  29 ++
 .../test/junit/rules/SerializableTimeout.java   | 119 ++++++++
 .../examples/RepeatingTestCasesExampleTest.java |  94 ++++++
 .../rules/examples/RetryRuleExampleTest.java    |  43 +++
 .../rules/tests/ExpectedTimeoutRuleTest.java    | 214 +++++++++++++
 .../junit/rules/tests/IgnoreUntilRuleTest.java  | 121 ++++++++
 .../junit/rules/tests/JUnitRuleTestSuite.java   |  33 ++
 .../test/junit/rules/tests/RepeatRuleTest.java  | 304 +++++++++++++++++++
 .../tests/RetryRuleGlobalWithErrorTest.java     | 250 +++++++++++++++
 .../tests/RetryRuleGlobalWithExceptionTest.java | 254 ++++++++++++++++
 .../tests/RetryRuleLocalWithErrorTest.java      | 207 +++++++++++++
 .../tests/RetryRuleLocalWithExceptionTest.java  | 213 +++++++++++++
 .../junit/rules/tests/RuleAndClassRuleTest.java | 138 +++++++++
 .../test/junit/rules/tests/TestRunner.java      |  37 +++
 .../junit/support/DefaultIgnoreCondition.java   |  57 ++++
 .../IgnoreConditionEvaluationException.java     |  43 +++
 31 files changed, 3438 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/ConditionalIgnore.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/ConditionalIgnore.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/ConditionalIgnore.java
new file mode 100755
index 0000000..b409cb1
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/ConditionalIgnore.java
@@ -0,0 +1,49 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.gemstone.gemfire.test.junit.support.DefaultIgnoreCondition;
+
+/**
+ * The ConditionalIgnore class is a Java Annotation used to annotated a test suite class test case method in order to
+ * conditionally ignore the test case for a fixed amount of time, or based on a predetermined condition provided by
+ * the IgnoreCondition interface.
+ *
+ * @author John Blum
+ * @see java.lang.annotation.Annotation
+ * @see com.gemstone.gemfire.test.junit.IgnoreCondition
+ * @see com.gemstone.gemfire.test.junit.support.DefaultIgnoreCondition
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD })
+@SuppressWarnings("unused")
+public @interface ConditionalIgnore {
+
+  Class<? extends IgnoreCondition> condition() default DefaultIgnoreCondition.class;
+
+  String until() default "1970-01-01";
+
+  String value() default "";
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/IgnoreCondition.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/IgnoreCondition.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/IgnoreCondition.java
new file mode 100755
index 0000000..0caa959
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/IgnoreCondition.java
@@ -0,0 +1,32 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit;
+
+import org.junit.runner.Description;
+
+/**
+ * The IgnoreCondition class...
+ *
+ * @author John Blum
+ * @see org.junit.runner.Description
+ */
+@SuppressWarnings("unused")
+public interface IgnoreCondition {
+
+  boolean evaluate(Description testCaseDescription);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/IgnoreUntil.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/IgnoreUntil.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/IgnoreUntil.java
new file mode 100755
index 0000000..5910d10
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/IgnoreUntil.java
@@ -0,0 +1,49 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.gemstone.gemfire.test.junit.support.DefaultIgnoreCondition;
+
+/**
+ * The IgnoreUntil class is a Java Annotation used to annotated a test suite class test case method in order to
+ * conditionally ignore the test case for a fixed amount of time, or based on a predetermined condition provided by
+ * the IgnoreCondition interface.
+ *
+ * @author John Blum
+ * @see java.lang.annotation.Annotation
+ * @see com.gemstone.gemfire.test.junit.IgnoreCondition
+ * @see com.gemstone.gemfire.test.junit.support.DefaultIgnoreCondition
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD })
+@SuppressWarnings("unused")
+public @interface IgnoreUntil {
+
+  Class<? extends IgnoreCondition> condition() default DefaultIgnoreCondition.class;
+
+  String until() default "1970-01-01";
+
+  String value() default "";
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/Repeat.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/Repeat.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/Repeat.java
new file mode 100755
index 0000000..5cfa321
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/Repeat.java
@@ -0,0 +1,43 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The Repeat class is a Java Annotation enabling an annotated test suite class test case method to be repeated
+ * a specified number of iterations.
+ *
+ * @author John Blum
+ * @see java.lang.annotation.Annotation
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+@SuppressWarnings("unused")
+public @interface Repeat {
+
+  public static int DEFAULT = 1;
+  
+  int value() default DEFAULT;
+
+  String property() default "";
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/Retry.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/Retry.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/Retry.java
new file mode 100755
index 0000000..65f3cf6
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/Retry.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 com.gemstone.gemfire.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Java Annotation used to annotate a test suite class test case method in order to
+ * retry it in case of failure up to the specified maximum attempts.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+public @interface Retry {
+  
+  public static int DEFAULT = 1;
+  
+  int value() default DEFAULT;
+  
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/ConditionalIgnoreRule.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/ConditionalIgnoreRule.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/ConditionalIgnoreRule.java
new file mode 100755
index 0000000..80898b5
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/ConditionalIgnoreRule.java
@@ -0,0 +1,123 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit.rules;
+
+import java.io.Serializable;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+import org.junit.AssumptionViolatedException;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import com.gemstone.gemfire.test.junit.ConditionalIgnore;
+import com.gemstone.gemfire.test.junit.IgnoreCondition;
+import com.gemstone.gemfire.test.junit.support.IgnoreConditionEvaluationException;
+
+/**
+ * The ConditionalIgnoreRule class...
+ *
+ * @author John Blum
+ * @see org.junit.rules.TestRule
+ * @see org.junit.runner.Description
+ * @see org.junit.runners.model.Statement
+ * @see com.gemstone.gemfire.test.junit.ConditionalIgnore
+ * @see com.gemstone.gemfire.test.junit.IgnoreCondition
+ */
+@SuppressWarnings({ "serial", "unused" })
+public class ConditionalIgnoreRule implements TestRule, Serializable {
+
+  protected static final String DATE_FORMAT_PATTERN = "yyyy-MM-dd";
+  protected static final String DEFAULT_MESSAGE = "Ignoring test case (%1$s) of test class (%2$s)!";
+
+  protected static final DateFormat DATE_FORMAT = new SimpleDateFormat(DATE_FORMAT_PATTERN);
+
+  @Override
+  public Statement apply(final Statement base, final Description description) {
+    return new Statement() {
+      @Override public void evaluate() throws Throwable {
+        ConditionalIgnoreRule.this.evaluate(base, description);
+      }
+    };
+  }
+
+  public final void evaluate(Statement statement, Description description) throws Throwable {
+    throwOnIgnoreTest(statement, description).evaluate();
+  }
+
+  protected Statement throwOnIgnoreTest(Statement statement, Description description) {
+    if (isTest(description)) {
+      boolean ignoreTest = false;
+      String message = "";
+
+      ConditionalIgnore testCaseAnnotation = description.getAnnotation(ConditionalIgnore.class);
+
+      if (testCaseAnnotation != null) {
+        ignoreTest = evaluate(testCaseAnnotation, description);
+        message = testCaseAnnotation.value();
+      }
+      else if (description.getTestClass().isAnnotationPresent(ConditionalIgnore.class)) {
+        ConditionalIgnore testClassAnnotation = description.getTestClass().getAnnotation(ConditionalIgnore.class);
+
+        ignoreTest = evaluate(testClassAnnotation, description);
+        message = testClassAnnotation.value();
+      }
+
+      if (ignoreTest) {
+        throw new AssumptionViolatedException(format(message, description));
+      }
+    }
+
+    return statement;
+  }
+
+  protected boolean isTest(final Description description) {
+    return (description.isSuite() || description.isTest());
+  }
+
+  protected String format(String message, Description description) {
+    message = (!message.isEmpty() ? message : DEFAULT_MESSAGE);
+    return String.format(message, description.getMethodName(), description.getClassName());
+  }
+
+  protected boolean evaluate(ConditionalIgnore conditionalIgnoreAnnotation, Description description) {
+    return (evaluateCondition(conditionalIgnoreAnnotation.condition(), description)
+      || evaluateUntil(conditionalIgnoreAnnotation.until()));
+  }
+
+  protected boolean evaluateCondition(Class<? extends IgnoreCondition> ignoreConditionType, Description description) {
+    try {
+      return ignoreConditionType.newInstance().evaluate(description);
+    }
+    catch (Exception e) {
+      throw new IgnoreConditionEvaluationException(String.format("failed to evaluate IgnoreCondition: %1$s",
+        ignoreConditionType.getName()), e);
+    }
+  }
+
+  protected boolean evaluateUntil(String timestamp) {
+    try {
+      return DATE_FORMAT.parse(timestamp).after(Calendar.getInstance().getTime());
+    }
+    catch (ParseException e) {
+      return false;
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/ExpectedTimeoutRule.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/ExpectedTimeoutRule.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/ExpectedTimeoutRule.java
new file mode 100755
index 0000000..7b6d345
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/ExpectedTimeoutRule.java
@@ -0,0 +1,180 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit.rules;
+
+import static org.junit.Assert.assertThat;
+
+import java.util.concurrent.TimeUnit;
+
+import org.hamcrest.Matcher;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Expect an Exception within a specified timeout.
+ * 
+ * @author Kirk Lund
+ * @since 8.2
+ */
+public class ExpectedTimeoutRule implements TestRule {
+
+  /**
+   * @return a Rule that expects no timeout (identical to behavior without this Rule)
+   */
+  public static ExpectedTimeoutRule none() {
+    return new ExpectedTimeoutRule();
+  }
+  
+  private ExpectedException delegate;
+  private boolean expectsThrowable;
+  private long minDuration;
+  private long maxDuration;
+  private TimeUnit timeUnit;
+  
+  private ExpectedTimeoutRule() {
+    this.delegate = ExpectedException.none();
+  }
+
+  public ExpectedTimeoutRule expectMinimumDuration(final long minDuration) {
+    this.minDuration = minDuration;
+    return this;
+  }
+  public ExpectedTimeoutRule expectMaximumDuration(final long maxDuration) {
+    this.maxDuration = maxDuration;
+    return this;
+  }
+  public ExpectedTimeoutRule expectTimeUnit(final TimeUnit timeUnit) {
+    this.timeUnit = timeUnit;
+    return this;
+  }
+
+  public ExpectedTimeoutRule handleAssertionErrors() {
+    this.delegate.handleAssertionErrors();
+    return this;
+  }
+  
+  public ExpectedTimeoutRule handleAssumptionViolatedExceptions() {
+    this.delegate.handleAssumptionViolatedExceptions();
+    return this;
+  }
+  
+  /**
+   * Adds {@code matcher} to the list of requirements for any thrown
+   * exception.
+   */
+  public void expect(final Matcher<?> matcher) {
+    this.delegate.expect(matcher);
+  }
+
+  /**
+   * Adds to the list of requirements for any thrown exception that it should
+   * be an instance of {@code type}
+   */
+  public void expect(final Class<? extends Throwable> type) {
+    this.delegate.expect(type);
+    this.expectsThrowable = true;
+  }
+
+  /**
+   * Adds to the list of requirements for any thrown exception that it should
+   * <em>contain</em> string {@code substring}
+   */
+  public void expectMessage(final String substring) {
+    this.delegate.expectMessage(substring);
+  }
+
+  /**
+   * Adds {@code matcher} to the list of requirements for the message returned
+   * from any thrown exception.
+   */
+  public void expectMessage(final Matcher<String> matcher) {
+    this.delegate.expectMessage(matcher);
+  }
+
+  /**
+   * Adds {@code matcher} to the list of requirements for the cause of
+   * any thrown exception.
+   */
+  public void expectCause(final Matcher<? extends Throwable> expectedCause) {
+    this.delegate.expectCause(expectedCause);
+  }
+
+  public boolean expectsTimeout() {
+    return minDuration > 0 || maxDuration > 0;
+  }
+  
+  public boolean expectsThrowable() {
+    return expectsThrowable = true;
+  }
+  
+  @Override
+  public Statement apply(final Statement base, final Description description) {
+    Statement next = delegate.apply(base, description);
+    return new ExpectedTimeoutStatement(next);
+  }
+  
+  private void handleTime(final Long duration) {
+    if (expectsTimeout()) {
+      assertThat(timeUnit.convert(duration, TimeUnit.NANOSECONDS), new TimeMatcher(timeUnit, minDuration, maxDuration));
+    }
+  }
+  
+  private static class TimeMatcher extends org.hamcrest.TypeSafeMatcher<Long> {
+    
+    private final TimeUnit timeUnit;
+    private final long minDuration;
+    private final long maxDuration;
+ 
+    public TimeMatcher(final TimeUnit timeUnit, final long minDuration, final long maxDuration) {
+      this.timeUnit = timeUnit;
+      this.minDuration = minDuration;
+      this.maxDuration = maxDuration;
+    }
+ 
+    @Override
+    public boolean matchesSafely(final Long duration) {
+      return duration >= this.minDuration && duration <= this.maxDuration;
+    }
+
+    @Override
+    public void describeTo(final org.hamcrest.Description description) {
+      description.appendText("expects duration to be greater than or equal to ")
+          .appendValue(this.minDuration)
+          .appendText(" and less than or equal to ")
+          .appendValue(this.maxDuration)
+          .appendText(" ")
+          .appendValue(this.timeUnit);
+    }
+  }
+  
+  private class ExpectedTimeoutStatement extends Statement {
+    private final Statement next;
+
+    public ExpectedTimeoutStatement(final Statement base) {
+      next = base;
+    }
+
+    @Override
+    public void evaluate() throws Throwable {
+      long start = System.nanoTime();
+      next.evaluate();
+      handleTime(System.nanoTime() - start);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/IgnoreUntilRule.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/IgnoreUntilRule.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/IgnoreUntilRule.java
new file mode 100755
index 0000000..bf4ec3f
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/IgnoreUntilRule.java
@@ -0,0 +1,123 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit.rules;
+
+import java.io.Serializable;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+import org.junit.AssumptionViolatedException;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import com.gemstone.gemfire.test.junit.IgnoreUntil;
+import com.gemstone.gemfire.test.junit.IgnoreCondition;
+import com.gemstone.gemfire.test.junit.support.IgnoreConditionEvaluationException;
+
+/**
+ * The IgnoreUntilRule class...
+ *
+ * @author John Blum
+ * @see org.junit.rules.TestRule
+ * @see org.junit.runner.Description
+ * @see org.junit.runners.model.Statement
+ * @see com.gemstone.gemfire.test.junit.IgnoreUntil
+ * @see com.gemstone.gemfire.test.junit.IgnoreCondition
+ */
+@SuppressWarnings({ "serial", "unused" })
+public class IgnoreUntilRule implements TestRule, Serializable {
+
+  protected static final String DATE_FORMAT_PATTERN = "yyyy-MM-dd";
+  protected static final String DEFAULT_MESSAGE = "Ignoring test case (%1$s) of test class (%2$s)!";
+
+  protected static final DateFormat DATE_FORMAT = new SimpleDateFormat(DATE_FORMAT_PATTERN);
+
+  @Override
+  public Statement apply(final Statement base, final Description description) {
+    return new Statement() {
+      @Override public void evaluate() throws Throwable {
+        IgnoreUntilRule.this.evaluate(base, description);
+      }
+    };
+  }
+
+  public final void evaluate(Statement statement, Description description) throws Throwable {
+    throwOnIgnoreTest(statement, description).evaluate();
+  }
+
+  protected Statement throwOnIgnoreTest(Statement statement, Description description) {
+    if (isTest(description)) {
+      boolean ignoreTest = false;
+      String message = "";
+
+      IgnoreUntil testCaseAnnotation = description.getAnnotation(IgnoreUntil.class);
+
+      if (testCaseAnnotation != null) {
+        ignoreTest = evaluate(testCaseAnnotation, description);
+        message = testCaseAnnotation.value();
+      }
+      else if (description.getTestClass().isAnnotationPresent(IgnoreUntil.class)) {
+        IgnoreUntil testClassAnnotation = description.getTestClass().getAnnotation(IgnoreUntil.class);
+
+        ignoreTest = evaluate(testClassAnnotation, description);
+        message = testClassAnnotation.value();
+      }
+
+      if (ignoreTest) {
+        throw new AssumptionViolatedException(format(message, description));
+      }
+    }
+
+    return statement;
+  }
+
+  protected boolean isTest(final Description description) {
+    return (description.isSuite() || description.isTest());
+  }
+
+  protected String format(String message, Description description) {
+    message = (!message.isEmpty() ? message : DEFAULT_MESSAGE);
+    return String.format(message, description.getMethodName(), description.getClassName());
+  }
+
+  protected boolean evaluate(IgnoreUntil conditionalIgnoreAnnotation, Description description) {
+    return (evaluateCondition(conditionalIgnoreAnnotation.condition(), description)
+      || evaluateUntil(conditionalIgnoreAnnotation.until()));
+  }
+
+  protected boolean evaluateCondition(Class<? extends IgnoreCondition> ignoreConditionType, Description description) {
+    try {
+      return ignoreConditionType.newInstance().evaluate(description);
+    }
+    catch (Exception e) {
+      throw new IgnoreConditionEvaluationException(String.format("failed to evaluate IgnoreCondition: %1$s",
+        ignoreConditionType.getName()), e);
+    }
+  }
+
+  protected boolean evaluateUntil(String timestamp) {
+    try {
+      return DATE_FORMAT.parse(timestamp).after(Calendar.getInstance().getTime());
+    }
+    catch (ParseException e) {
+      return false;
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/RepeatRule.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/RepeatRule.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/RepeatRule.java
new file mode 100755
index 0000000..7bfe538
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/RepeatRule.java
@@ -0,0 +1,81 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit.rules;
+
+import java.io.Serializable;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import com.gemstone.gemfire.test.junit.Repeat;
+
+/**
+ * The RepeatRule class is a JUnit TestRule that enables an appropriately @Repeat annotated test case method
+ * to be repeated a specified number of times.
+ *
+ * @author John Blum
+ * @see org.junit.rules.TestRule
+ * @see org.junit.runner.Description
+ * @see org.junit.runners.model.Statement
+ */
+@SuppressWarnings({ "serial", "unused" })
+public class RepeatRule implements TestRule, Serializable {
+
+  protected static final int DEFAULT_REPETITIONS = 1;
+
+  @Override
+  public Statement apply(final Statement statement, final Description description) {
+    return new Statement() {
+      @Override public void evaluate() throws Throwable {
+        RepeatRule.this.evaluate(statement, description);
+      }
+    };
+  }
+
+  protected void evaluate(final Statement statement, final Description description) throws Throwable {
+    if (isTest(description)) {
+      Repeat repeat = description.getAnnotation(Repeat.class);
+
+      for (int count = 0, repetitions = getRepetitions(repeat); count < repetitions; count++) {
+        statement.evaluate();
+      }
+    }
+  }
+
+  private int getRepetitions(final Repeat repeat) {
+    int repetitions = DEFAULT_REPETITIONS;
+
+    if (repeat != null) {
+      if (!"".equals(repeat.property())) {
+        repetitions = Integer.getInteger(repeat.property(), DEFAULT_REPETITIONS);
+      } else {
+        repetitions = repeat.value();
+      }
+    }
+    
+    if (repetitions < 1) {
+      throw new IllegalArgumentException("Repeat value must be a positive integer");
+    }
+
+    return repetitions;
+  }
+
+  private boolean isTest(final Description description) {
+    return (description.isSuite() || description.isTest());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/RetryRule.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/RetryRule.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/RetryRule.java
new file mode 100755
index 0000000..4a26d5e
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/RetryRule.java
@@ -0,0 +1,181 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit.rules;
+
+import java.io.Serializable;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import com.gemstone.gemfire.test.junit.Retry;
+
+/**
+ * JUnit Rule that enables retrying a failed test up to a maximum number of retries.
+ * </p> 
+ * RetryRule can be used globally for all tests in a test case by specifying a 
+ * retryCount when instantiating it:
+ * <pre>
+ * @Rule
+ * public final RetryRule retryRule = new RetryRule(3);
+ * 
+ * @Test
+ * public void shouldBeRetriedUntilPasses() {
+ *   ...
+ * }
+ * </pre>
+ * </p> 
+ * The above will result in 3 retries for every test in the test case.
+ * </p> 
+ * RetryRule can be used locally for specific tests by annotating the test 
+ * method with @Rule and specifying a retryCount for that test:
+ * <pre>
+ * @Rule
+ * public final RetryRule retryRule = new RetryRule();
+ * 
+ * @Test
+ * @Retry(3)
+ * public void shouldBeRetriedUntilPasses() {
+ *   ...
+ * }
+ * </pre>
+ * </p>
+ * This version of RetryRule will retry a test that fails because of any kind 
+ * of Throwable.
+ */
+@SuppressWarnings("serial")
+public class RetryRule implements TestRule, Serializable {
+  
+  /**
+   * Enables printing of failures to System.err even if test passes on a retry
+   */
+  private static final boolean LOG = false;
+  
+  private final AbstractRetryRule implementation;
+
+  public RetryRule() {
+    this.implementation = new LocalRetryRule();
+  }
+
+  public RetryRule(final int retryCount) {
+    this.implementation = new GlobalRetryRule(retryCount);
+  }
+
+  @Override
+  public Statement apply(final Statement base, final Description description) {
+    return this.implementation.apply(base, description);
+  }
+
+  protected abstract class AbstractRetryRule implements TestRule {
+    protected AbstractRetryRule() {
+    }
+    protected void evaluate(final Statement base, final Description description, final int retryCount) throws Throwable {
+      if (retryCount == 0) {
+        
+      }
+      Throwable caughtThrowable = null;
+      
+      for (int count = 0; count < retryCount; count++) {
+        try {
+          base.evaluate();
+          return;
+        } catch (Throwable t) {
+          caughtThrowable = t;
+          debug(description.getDisplayName() + ": run " + (count + 1) + " failed");
+        }
+      }
+      
+      debug(description.getDisplayName() + ": giving up after " + retryCount + " failures");
+      throw caughtThrowable;
+    }
+    private void debug(final String message) {
+      if (LOG) {
+        System.err.println(message);
+      }
+    }
+  }
+  
+  /**
+   * Implementation of RetryRule for all test methods in a test case
+   */
+  protected class GlobalRetryRule extends AbstractRetryRule {
+    
+    private final int retryCount;
+
+    protected GlobalRetryRule(final int retryCount) {
+      if (retryCount < 1) {
+        throw new IllegalArgumentException("Retry count must be greater than zero");
+      }
+      this.retryCount = retryCount;
+    }
+    
+    @Override
+    public Statement apply(final Statement base, final Description description) {
+      return new Statement() {
+        @Override
+        public void evaluate() throws Throwable {
+          GlobalRetryRule.this.evaluatePerCase(base, description);
+        }
+      };
+    }
+
+    protected void evaluatePerCase(final Statement base, final Description description) throws Throwable {
+      evaluate(base, description, this.retryCount);
+    }
+  }
+
+  /**
+   * Implementation of RetryRule for test methods annotated with Retry
+   */
+  protected class LocalRetryRule extends AbstractRetryRule {
+    
+    protected LocalRetryRule() {
+    }
+    
+    @Override
+    public Statement apply(final Statement base, final Description description) {
+      return new Statement() {
+        @Override 
+        public void evaluate() throws Throwable {
+          LocalRetryRule.this.evaluatePerTest(base, description);
+        }
+      };
+    }
+
+    protected void evaluatePerTest(final Statement base, final Description description) throws Throwable {
+      if (isTest(description)) {
+        Retry retry = description.getAnnotation(Retry.class);
+        int retryCount = getRetryCount(retry);
+        evaluate(base, description, retryCount);
+      }
+    }
+
+    private int getRetryCount(final Retry retry) {
+      int retryCount = Retry.DEFAULT;
+
+      if (retry != null) {
+        retryCount = retry.value();
+      }
+
+      return retryCount;
+    }
+
+    private boolean isTest(final Description description) {
+      return (description.isSuite() || description.isTest());
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableExternalResource.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableExternalResource.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableExternalResource.java
new file mode 100755
index 0000000..37d8eb5
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableExternalResource.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 com.gemstone.gemfire.test.junit.rules;
+
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Serializable version of ExternalResource JUnit Rule. JUnit lifecycle is not
+ * executed in remote JVMs. The <tt>after()</tt> callback has a throws-clause 
+ * that matches <tt>before()</tt>.
+ * 
+ * Implementation copied from <tt>org.junit.rules.ExternalResource</tt>.
+ * 
+ * @author Kirk Lund
+ */
+@SuppressWarnings("serial")
+public abstract class SerializableExternalResource implements SerializableTestRule {
+  
+  @Override
+  public Statement apply(final Statement base, final Description description) {
+    if (description.isTest()) {
+      return statement(base);
+    } else if (description.isSuite()) {
+      return statementClass(base);
+    }
+    return base;
+  }
+
+  private Statement statement(final Statement base) {
+    return new Statement() {
+      @Override
+      public void evaluate() throws Throwable {
+        before();
+        try {
+          base.evaluate();
+        } finally {
+          after();
+        }
+      }
+    };
+  }
+
+  private Statement statementClass(final Statement base) {
+    return new Statement() {
+      @Override
+      public void evaluate() throws Throwable {
+        beforeClass();
+        try {
+          base.evaluate();
+        } finally {
+          afterClass();
+        }
+      }
+    };
+  }
+
+  /**
+   * Override to set up your specific external resource.
+   *
+   * @throws Throwable if setup fails (which will disable {@code after}
+   */
+  protected void before() throws Throwable {
+    // do nothing
+  }
+
+  /**
+   * Override to tear down your specific external resource.
+   * 
+   * @throws Throwable if teardown fails (which will disable {@code after}
+   */
+  protected void after() throws Throwable {
+    // do nothing
+  }
+
+  /**
+   * Override to set up your specific external resource.
+   *
+   * @throws Throwable if setup fails (which will disable {@code after}
+   */
+  protected void beforeClass() throws Throwable {
+    // do nothing
+  }
+
+  /**
+   * Override to tear down your specific external resource.
+   *
+   * @throws Throwable if teardown fails (which will disable {@code after}
+   */
+  protected void afterClass() throws Throwable {
+    // do nothing
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableRuleChain.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableRuleChain.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableRuleChain.java
new file mode 100755
index 0000000..936345e
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableRuleChain.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 com.gemstone.gemfire.test.junit.rules;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Serializable version of TemporaryFolder JUnit Rule. JUnit lifecycle is not
+ * executed in remote JVMs.
+ * 
+ * Implementation copied from <tt>org.junit.rules.RuleChain</tt>.
+ * 
+ * The SerializableRuleChain rule allows ordering of TestRules. You create a
+ * {@code RuleChain} with {@link #outerRule(TestRule)} and subsequent calls of
+ * {@link #around(TestRule)}:
+ *
+ * <pre>
+ * public static class UseRuleChain {
+ *  &#064;Rule
+ *  public RuleChain chain= RuleChain
+ *                         .outerRule(new LoggingRule("outer rule")
+ *                         .around(new LoggingRule("middle rule")
+ *                         .around(new LoggingRule("inner rule");
+ *
+ *  &#064;Test
+ *  public void example() {
+ *    assertTrue(true);
+ *     }
+ * }
+ * </pre>
+ *
+ * writes the log
+ *
+ * <pre>
+ * starting outer rule
+ * starting middle rule
+ * starting inner rule
+ * finished inner rule
+ * finished middle rule
+ * finished outer rule
+ * </pre>
+ *
+ * @author Kirk Lund
+ */
+@SuppressWarnings("serial")
+public class SerializableRuleChain implements SerializableTestRule {
+  private static final SerializableRuleChain EMPTY_CHAIN = new SerializableRuleChain(Collections.<TestRule>emptyList());
+
+  private transient List<TestRule> rulesStartingWithInnerMost;
+
+  /**
+  * Returns a {@code SerializableRuleChain} without a {@link TestRule}. This method may
+  * be the starting point of a {@code SerializableRuleChain}.
+  *
+  * @return a {@code SerializableRuleChain} without a {@link TestRule}.
+  */
+  public static SerializableRuleChain emptyRuleChain() {
+    return EMPTY_CHAIN;
+  }
+
+  /**
+  * Returns a {@code SerializableRuleChain} with a single {@link TestRule}. This method
+  * is the usual starting point of a {@code SerializableRuleChain}.
+  *
+  * @param outerRule the outer rule of the {@code SerializableRuleChain}.
+  * @return a {@code SerializableRuleChain} with a single {@link TestRule}.
+  */
+  public static SerializableRuleChain outerRule(TestRule outerRule) {
+    return emptyRuleChain().around(outerRule);
+  }
+
+  private SerializableRuleChain(List<TestRule> rules) {
+    this.rulesStartingWithInnerMost = rules;
+  }
+
+  /**
+  * Create a new {@code SerializableRuleChain}, which encloses the {@code nextRule} with
+  * the rules of the current {@code SerializableRuleChain}.
+  *
+  * @param enclosedRule the rule to enclose.
+  * @return a new {@code SerializableRuleChain}.
+  */
+  public SerializableRuleChain around(TestRule enclosedRule) {
+    List<TestRule> rulesOfNewChain = new ArrayList<TestRule>();
+    rulesOfNewChain.add(enclosedRule);
+    rulesOfNewChain.addAll(rulesStartingWithInnerMost);
+    return new SerializableRuleChain(rulesOfNewChain);
+  }
+
+  /**
+  * {@inheritDoc}
+  */
+  public Statement apply(Statement base, Description description) {
+    for (TestRule each : rulesStartingWithInnerMost) {
+      base = each.apply(base, description);
+    }
+    return base;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTemporaryFolder.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTemporaryFolder.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTemporaryFolder.java
new file mode 100755
index 0000000..0e796b3
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTemporaryFolder.java
@@ -0,0 +1,70 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit.rules;
+
+import java.io.File;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Field;
+
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Serializable version of TemporaryFolder JUnit Rule. JUnit lifecycle is not
+ * executed in remote JVMs.
+ * 
+ * @author Kirk Lund
+ */
+@SuppressWarnings("serial")
+public class SerializableTemporaryFolder extends TemporaryFolder implements SerializableTestRule {
+
+  private void writeObject(final ObjectOutputStream out) throws Exception {
+    writeParentFolder(out);
+    writeFolder(out);
+  }
+
+  private void readObject(final ObjectInputStream in) throws Exception {
+    readParentFolder(in);
+    readFolder(in);
+  }
+  
+  private void readParentFolder(final ObjectInputStream in) throws Exception {
+    final Field parentFolderField = TemporaryFolder.class.getDeclaredField("parentFolder");
+    parentFolderField.setAccessible(true);
+    parentFolderField.set(this, (File) in.readObject());
+  }
+  
+  private void readFolder(final ObjectInputStream in) throws Exception {
+    final Field folderField = TemporaryFolder.class.getDeclaredField("folder");
+    folderField.setAccessible(true);
+    folderField.set(this, (File) in.readObject());
+  }
+  
+  private void writeParentFolder(final ObjectOutputStream out) throws Exception {
+    final Field parentFolderField = TemporaryFolder.class.getDeclaredField("parentFolder");
+    parentFolderField.setAccessible(true);
+    final File parentFolderFieldValue = (File) parentFolderField.get(this);
+    out.writeObject(parentFolderFieldValue);
+  }
+  
+  private void writeFolder(final ObjectOutputStream out) throws Exception {
+    final Field folderField = TemporaryFolder.class.getDeclaredField("folder");
+    folderField.setAccessible(true);
+    final File folderFieldValue = (File) folderField.get(this);
+    out.writeObject(folderFieldValue);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTestName.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTestName.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTestName.java
new file mode 100755
index 0000000..1fd255f
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTestName.java
@@ -0,0 +1,54 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit.rules;
+
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Field;
+
+import org.junit.rules.TestName;
+
+/**
+ * Serializable version of TestName JUnit Rule. JUnit lifecycle is not
+ * executed in remote JVMs.
+ * 
+ * @author Kirk Lund
+ */
+@SuppressWarnings("serial")
+public class SerializableTestName extends TestName implements SerializableTestRule {
+
+  private void writeObject(final ObjectOutputStream out) throws Exception {
+    writeName(out);
+  }
+
+  private void readObject(final ObjectInputStream in) throws Exception {
+    readName(in);
+  }
+  
+  private void writeName(final ObjectOutputStream out) throws Exception {
+    final Field nameField = TestName.class.getDeclaredField("name");
+    nameField.setAccessible(true);
+    final String nameValue = (String) nameField.get(this);
+    out.writeObject(nameValue);
+  }
+  
+  private void readName(final ObjectInputStream in) throws Exception {
+    Field nameField = TestName.class.getDeclaredField("name");
+    nameField.setAccessible(true);
+    nameField.set(this, (String) in.readObject());
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTestRule.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTestRule.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTestRule.java
new file mode 100755
index 0000000..354c38a
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTestRule.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit.rules;
+
+import java.io.Serializable;
+
+import org.junit.rules.TestRule;
+
+/**
+ * Serializable version of JUnit TestRule. JUnit lifecycle is not
+ * executed in remote JVMs.
+ * 
+ * The simplest way to satisfy this interface is to apply <tt>transient</tt>
+ * to every instance field.
+ * 
+ * @author Kirk Lund
+ */
+public interface SerializableTestRule extends Serializable, TestRule {
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTestWatcher.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTestWatcher.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTestWatcher.java
new file mode 100755
index 0000000..5bcf686
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTestWatcher.java
@@ -0,0 +1,29 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit.rules;
+
+import org.junit.rules.TestWatcher;
+
+/**
+ * Serializable version of TestWatcher JUnit Rule. JUnit lifecycle is not
+ * executed in remote JVMs.
+ * 
+ * @author Kirk Lund
+ */
+@SuppressWarnings("serial")
+public class SerializableTestWatcher extends TestWatcher implements SerializableTestRule {
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTimeout.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTimeout.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTimeout.java
new file mode 100755
index 0000000..3136a1c
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/SerializableTimeout.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 com.gemstone.gemfire.test.junit.rules;
+
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Field;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.rules.TemporaryFolder;
+import org.junit.rules.TestName;
+import org.junit.rules.Timeout;
+
+/**
+ * Serializable version of Timeout JUnit Rule. JUnit lifecycle is not
+ * executed in remote JVMs.
+ * 
+ * @author Kirk Lund
+ */
+@SuppressWarnings("serial")
+public class SerializableTimeout extends Timeout implements SerializableTestRule {
+
+  public static Builder builder() {
+    return new Builder();
+  }
+  
+  public SerializableTimeout(final long timeout, final TimeUnit timeUnit) {
+    super(timeout, timeUnit);
+  }
+  
+  protected SerializableTimeout(final Builder builder) {
+    super(builder);
+  }
+  
+  public static class Builder extends Timeout.Builder {
+    
+    protected Builder() {
+      super();
+    }
+    
+    @Override
+    public SerializableTimeout build() {
+      return new SerializableTimeout(this);
+    }
+  }
+
+  private void writeObject(final ObjectOutputStream out) throws Exception {
+    writeTimeout(out);
+    writeTimeUnit(out);
+    writeLookForStuckThread(out);
+  }
+
+  private void readObject(final ObjectInputStream in) throws Exception {
+    readTimeout(in);
+    readTimeUnit(in);
+    readLookForStuckThread(in);
+  }
+  
+  private void writeTimeout(final ObjectOutputStream out) throws Exception {
+    final Field timeoutField = TestName.class.getDeclaredField("timeout");
+    timeoutField.setAccessible(true);
+    final Long timeoutValue = (Long) timeoutField.get(this);
+    out.writeLong(timeoutValue);
+  }
+  
+  private void writeTimeUnit(final ObjectOutputStream out) throws Exception {
+    final Field timeoutField = TestName.class.getDeclaredField("timeUnit");
+    timeoutField.setAccessible(true);
+    final TimeUnit timeoutValue = (TimeUnit) timeoutField.get(this);
+    out.writeObject(timeoutValue);
+  }
+
+  private void writeLookForStuckThread(final ObjectOutputStream out) throws Exception {
+    try {
+      final Field lookForStuckThreadField = TemporaryFolder.class.getDeclaredField("lookForStuckThread");
+      lookForStuckThreadField.setAccessible(true);
+      final Boolean lookForStuckThreadValue = (Boolean) lookForStuckThreadField.get(this);
+      out.writeBoolean(lookForStuckThreadValue);
+    } catch (NoSuchFieldException e) {
+      out.writeBoolean(false);
+    }
+  }
+  
+  private void readTimeout(final ObjectInputStream in) throws Exception {
+    Field timeoutField = TestName.class.getDeclaredField("timeout");
+    timeoutField.setAccessible(true);
+    timeoutField.set(this, (Long) in.readObject());
+  }
+
+  private void readTimeUnit(final ObjectInputStream in) throws Exception {
+    Field timeUnitField = TestName.class.getDeclaredField("timeUnit");
+    timeUnitField.setAccessible(true);
+    timeUnitField.set(this, (TimeUnit) in.readObject());
+  }
+
+  private void readLookForStuckThread(final ObjectInputStream in) throws Exception {
+    try {
+      final Field lookForStuckThreadField = TemporaryFolder.class.getDeclaredField("lookForStuckThread");
+      lookForStuckThreadField.setAccessible(true);
+      lookForStuckThreadField.set(this, (Boolean) in.readObject());
+    } catch (NoSuchFieldException e) {
+      final boolean value = (Boolean) in.readObject();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/examples/RepeatingTestCasesExampleTest.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/examples/RepeatingTestCasesExampleTest.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/examples/RepeatingTestCasesExampleTest.java
new file mode 100755
index 0000000..5ee647b
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/examples/RepeatingTestCasesExampleTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit.rules.examples;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.assertThat;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.gemstone.gemfire.test.junit.Repeat;
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+import com.gemstone.gemfire.test.junit.rules.RepeatRule;
+
+/**
+ * The RepeatingTestCasesExampleTest class is a test suite of test cases testing the contract and functionality
+ * of the JUnit @Repeat annotation on a test suite class test case methods.
+ *
+ * @author John Blum
+ * @see org.junit.Test
+ * @see com.gemstone.gemfire.test.junit.Repeat
+ * @see com.gemstone.gemfire.test.junit.rules.RepeatRule
+ */
+@Category(UnitTest.class)
+public class RepeatingTestCasesExampleTest {
+
+  private static AtomicInteger repeatOnceCounter = new AtomicInteger(0);
+  private static AtomicInteger repeatOnlyOnceCounter = new AtomicInteger(0);
+  private static AtomicInteger repeatTenTimesCounter = new AtomicInteger(0);
+  private static AtomicInteger repeatTwiceCounter = new AtomicInteger(0);
+
+  @Rule
+  public RepeatRule repeatRule = new RepeatRule();
+
+  @BeforeClass
+  public static void setupBeforeClass() {
+    System.setProperty("tdd.example.test.case.two.repetitions", "2");
+  }
+
+  @AfterClass
+  public static void tearDownAfterClass() {
+    assertThat(repeatOnceCounter.get(), is(equalTo(1)));
+    assertThat(repeatOnlyOnceCounter.get(), is(equalTo(1)));
+    assertThat(repeatTenTimesCounter.get(), is(equalTo(10)));
+    assertThat(repeatTwiceCounter.get(), is(equalTo(2)));
+  }
+
+  @Test
+  @Repeat
+  public void repeatOnce() {
+    repeatOnceCounter.incrementAndGet();
+    assertThat(repeatOnceCounter.get() <= 1, is(true));
+  }
+
+  @Test
+  @Repeat(property = "tdd.example.test.case.with.non-existing.system.property")
+  public void repeatOnlyOnce() {
+    repeatOnlyOnceCounter.incrementAndGet();
+    assertThat(repeatOnlyOnceCounter.get() <= 1, is(true));
+  }
+
+  @Test
+  @Repeat(10)
+  public void repeatTenTimes() {
+    repeatTenTimesCounter.incrementAndGet();
+    assertThat(repeatTenTimesCounter.get() <= 10, is(true));
+  }
+
+  @Test
+  @Repeat(property = "tdd.example.test.case.two.repetitions")
+  public void repeatTwiceCounter() {
+    repeatTwiceCounter.incrementAndGet();
+    assertThat(repeatTwiceCounter.get() <= 2, is(true));
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/examples/RetryRuleExampleTest.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/examples/RetryRuleExampleTest.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/examples/RetryRuleExampleTest.java
new file mode 100755
index 0000000..f6d70a2
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/examples/RetryRuleExampleTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit.rules.examples;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+import com.gemstone.gemfire.test.junit.rules.RetryRule;
+
+@Category(UnitTest.class)
+public class RetryRuleExampleTest {
+
+  @Rule
+  public final transient RetryRule retry = new RetryRule(2);
+  
+  private static int count = 0;
+
+  @Test
+  public void unreliableTestWithRaceConditions() {
+    count++;
+    if (count < 2) {
+      assertThat(count).isEqualTo(2); // doomed to fail
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/ExpectedTimeoutRuleTest.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/ExpectedTimeoutRuleTest.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/ExpectedTimeoutRuleTest.java
new file mode 100755
index 0000000..b67d1eb
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/ExpectedTimeoutRuleTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit.rules.tests;
+
+import static com.gemstone.gemfire.test.junit.rules.tests.TestRunner.*;
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+import com.gemstone.gemfire.test.junit.rules.ExpectedTimeoutRule;
+
+/**
+ * Unit tests for ExpectedTimeout JUnit Rule.
+ * 
+ * @author Kirk Lund
+ * @since 8.2
+ */
+@Category(UnitTest.class)
+public class ExpectedTimeoutRuleTest {
+
+  @Test
+  public void passesUnused() {
+    Result result = runTest(PassingTestShouldPassWhenUnused.class);
+    
+    assertThat(result.wasSuccessful()).isTrue();
+  }
+  
+  @Test
+  public void failsWithoutExpectedException() {
+    Result result = runTest(FailsWithoutExpectedException.class);
+    
+    assertThat(result.wasSuccessful()).isFalse();
+    
+    List<Failure> failures = result.getFailures();
+    assertThat(failures.size()).as("Failures: " + failures).isEqualTo(1);
+    
+    Failure failure = failures.get(0);
+    assertThat(failure.getException()).isExactlyInstanceOf(AssertionError.class).hasMessage("Expected test to throw an instance of " + TimeoutException.class.getName());
+  }
+  
+  @Test
+  public void failsWithoutExpectedTimeoutException() {
+    Result result = runTest(FailsWithoutExpectedTimeoutException.class);
+    
+    assertThat(result.wasSuccessful()).isFalse();
+    
+    List<Failure> failures = result.getFailures();
+    assertThat(failures.size()).as("Failures: " + failures).isEqualTo(1);
+    
+    Failure failure = failures.get(0);
+    assertThat(failure.getException()).isExactlyInstanceOf(AssertionError.class).hasMessage("Expected test to throw (an instance of " + TimeoutException.class.getName() + " and exception with message a string containing \"" + FailsWithoutExpectedTimeoutException.message + "\")");
+  }
+  
+  @Test
+  public void failsWithExpectedTimeoutButWrongError() {
+    Result result = runTest(FailsWithExpectedTimeoutButWrongError.class);
+    
+    assertThat(result.wasSuccessful()).isFalse();
+    
+    List<Failure> failures = result.getFailures();
+    assertThat(failures.size()).as("Failures: " + failures).isEqualTo(1);
+    
+    Failure failure = failures.get(0);
+    String expectedMessage = 
+        "\n" + 
+        "Expected: (an instance of java.util.concurrent.TimeoutException and exception with message a string containing \"this is a message for FailsWithExpectedTimeoutButWrongError\")" +
+        "\n" + 
+        "     " +
+        "but: an instance of java.util.concurrent.TimeoutException <java.lang.NullPointerException> is a java.lang.NullPointerException";
+    assertThat(failure.getException()).isExactlyInstanceOf(AssertionError.class).hasMessageContaining(expectedMessage);
+  }
+  
+  @Test
+  public void passesWithExpectedTimeoutAndTimeoutException() {
+    Result result = runTest(PassesWithExpectedTimeoutAndTimeoutException.class);
+    
+    assertThat(result.wasSuccessful()).isTrue();
+  }
+  
+  @Test
+  public void failsWhenTimeoutIsEarly() {
+    Result result = runTest(FailsWhenTimeoutIsEarly.class);
+   
+    assertThat(result.wasSuccessful()).isFalse();
+    
+    List<Failure> failures = result.getFailures();
+    assertThat(failures.size()).as("Failures: " + failures).isEqualTo(1);
+    
+    Failure failure = failures.get(0);
+    assertThat(failure.getException()).isExactlyInstanceOf(AssertionError.class).hasMessage("Expected test to throw (an instance of " + TimeoutException.class.getName() + " and exception with message a string containing \"" + FailsWhenTimeoutIsEarly.message + "\")");
+  }
+  
+  @Test
+  public void failsWhenTimeoutIsLate() {
+    Result result = runTest(FailsWhenTimeoutIsLate.class);
+    
+    assertThat(result.wasSuccessful()).isFalse();
+    
+    List<Failure> failures = result.getFailures();
+    assertThat(failures.size()).as("Failures: " + failures).isEqualTo(1);
+    
+    Failure failure = failures.get(0);
+    assertThat(failure.getException()).isExactlyInstanceOf(AssertionError.class).hasMessage("Expected test to throw (an instance of " + TimeoutException.class.getName() + " and exception with message a string containing \"" + FailsWhenTimeoutIsLate.message + "\")");
+  }
+  
+  public static class AbstractExpectedTimeoutRuleTest {
+    @Rule
+    public ExpectedTimeoutRule timeout = ExpectedTimeoutRule.none();
+  }
+  
+  public static class PassingTestShouldPassWhenUnused extends AbstractExpectedTimeoutRuleTest {
+    @Test
+    public void passesUnused() throws Exception {
+    }
+  }
+  
+  public static class FailsWithoutExpectedException extends AbstractExpectedTimeoutRuleTest {
+    @Test
+    public void failsWithoutExpectedException() throws Exception {
+      timeout.expect(TimeoutException.class);
+    }
+  }
+  
+  public static class FailsWithoutExpectedTimeoutException extends AbstractExpectedTimeoutRuleTest {
+    public static final String message = "this is a message for FailsWithoutExpectedTimeoutException";
+    @Test
+    public void failsWithoutExpectedTimeoutAndTimeoutException() throws Exception {
+      timeout.expect(TimeoutException.class);
+      timeout.expectMessage(message);
+      timeout.expectMinimumDuration(10);
+      timeout.expectMaximumDuration(1000);
+      timeout.expectTimeUnit(TimeUnit.MILLISECONDS);
+      Thread.sleep(100);
+    }
+  }
+  
+  public static class FailsWithExpectedTimeoutButWrongError extends AbstractExpectedTimeoutRuleTest {
+    public static final String message = "this is a message for FailsWithExpectedTimeoutButWrongError";
+    @Test
+    public void failsWithExpectedTimeoutButWrongError() throws Exception {
+      timeout.expect(TimeoutException.class);
+      timeout.expectMessage(message);
+      timeout.expectMinimumDuration(10);
+      timeout.expectMaximumDuration(1000);
+      timeout.expectTimeUnit(TimeUnit.MILLISECONDS);
+      Thread.sleep(100);
+      throw new NullPointerException();
+    }
+  }
+
+  public static class PassesWithExpectedTimeoutAndTimeoutException extends AbstractExpectedTimeoutRuleTest {
+    public static final String message = "this is a message for PassesWithExpectedTimeoutAndTimeoutException";
+    public static final Class<TimeoutException> exceptionClass = TimeoutException.class;
+    @Test
+    public void passesWithExpectedTimeoutAndTimeoutException() throws Exception {
+      timeout.expect(exceptionClass);
+      timeout.expectMessage(message);
+      timeout.expectMinimumDuration(10);
+      timeout.expectMaximumDuration(1000);
+      timeout.expectTimeUnit(TimeUnit.MILLISECONDS);
+      Thread.sleep(100);
+      throw new TimeoutException(message);
+    }
+  }
+
+  public static class FailsWhenTimeoutIsEarly extends AbstractExpectedTimeoutRuleTest {
+    public static final String message = "this is a message for FailsWhenTimeoutIsEarly";
+    @Test
+    public void failsWhenTimeoutIsEarly() throws Exception {
+      timeout.expect(TimeoutException.class);
+      timeout.expectMessage(message);
+      timeout.expectMinimumDuration(1000);
+      timeout.expectMaximumDuration(2000);
+      timeout.expectTimeUnit(TimeUnit.MILLISECONDS);
+      Thread.sleep(10);
+    }
+  }
+
+  public static class FailsWhenTimeoutIsLate extends AbstractExpectedTimeoutRuleTest {
+    public static final String message = "this is a message for FailsWhenTimeoutIsLate";
+    @Test
+    public void failsWhenTimeoutIsLate() throws Exception {
+      timeout.expect(TimeoutException.class);
+      timeout.expectMessage(message);
+      timeout.expectMinimumDuration(10);
+      timeout.expectMaximumDuration(20);
+      timeout.expectTimeUnit(TimeUnit.MILLISECONDS);
+      Thread.sleep(100);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/IgnoreUntilRuleTest.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/IgnoreUntilRuleTest.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/IgnoreUntilRuleTest.java
new file mode 100755
index 0000000..a984d1d
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/IgnoreUntilRuleTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit.rules.tests;
+
+import static com.gemstone.gemfire.test.junit.rules.tests.TestRunner.*;
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+
+import com.gemstone.gemfire.test.junit.IgnoreUntil;
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+import com.gemstone.gemfire.test.junit.rules.IgnoreUntilRule;
+
+/**
+ * Unit tests for IgnoreUntil JUnit Rule
+ * 
+ * @author Kirk Lund
+ */
+@Category(UnitTest.class)
+public class IgnoreUntilRuleTest {
+
+  private static final String ASSERTION_ERROR_MESSAGE = "failing test";
+  
+  @Test
+  public void shouldIgnoreWhenUntilIsInFuture() {
+    Result result = runTest(ShouldIgnoreWhenUntilIsInFuture.class);
+    
+    assertThat(result.wasSuccessful()).isTrue();
+    assertThat(ShouldIgnoreWhenUntilIsInFuture.count).isEqualTo(0);
+  }
+  
+  @Test
+  public void shouldExecuteWhenUntilIsInPast() {
+    Result result = runTest(ShouldExecuteWhenUntilIsInPast.class);
+    
+    assertThat(result.wasSuccessful()).isFalse();
+    
+    List<Failure> failures = result.getFailures();
+    assertThat(failures.size()).as("Failures: " + failures).isEqualTo(1);
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException()).isExactlyInstanceOf(AssertionError.class).hasMessage(ASSERTION_ERROR_MESSAGE);
+    assertThat(ShouldExecuteWhenUntilIsInPast.count).isEqualTo(1);
+  }
+  
+  @Test
+  public void shouldExecuteWhenUntilIsDefault() {
+    Result result = runTest(ShouldExecuteWhenUntilIsDefault.class);
+    
+    assertThat(result.wasSuccessful()).isFalse();
+    
+    List<Failure> failures = result.getFailures();
+    assertThat(failures.size()).as("Failures: " + failures).isEqualTo(1);
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException()).isExactlyInstanceOf(AssertionError.class).hasMessage(ASSERTION_ERROR_MESSAGE);
+    assertThat(ShouldExecuteWhenUntilIsDefault.count).isEqualTo(1);
+  }
+  
+  public static class ShouldIgnoreWhenUntilIsInFuture {
+    private static int count;
+    
+    @Rule
+    public final IgnoreUntilRule ignoreUntilRule = new IgnoreUntilRule();
+    
+    @Test
+    @IgnoreUntil(value = "description", until = "3000-01-01")
+    public void doTest() throws Exception {
+      count++;
+      fail(ASSERTION_ERROR_MESSAGE);
+    }
+  }
+
+  public static class ShouldExecuteWhenUntilIsInPast {
+    private static int count;
+    
+    @Rule
+    public final IgnoreUntilRule ignoreUntilRule = new IgnoreUntilRule();
+    
+    @Test
+    @IgnoreUntil(value = "description", until = "1980-01-01")
+    public void doTest() throws Exception {
+      count++;
+      fail(ASSERTION_ERROR_MESSAGE);
+    }
+  }
+
+  public static class ShouldExecuteWhenUntilIsDefault {
+    private static int count;
+    
+    @Rule
+    public final IgnoreUntilRule ignoreUntilRule = new IgnoreUntilRule();
+    
+    @Test
+    @IgnoreUntil(value = "description")
+    public void doTest() throws Exception {
+      count++;
+      fail(ASSERTION_ERROR_MESSAGE);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/1be4f932/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/JUnitRuleTestSuite.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/JUnitRuleTestSuite.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/JUnitRuleTestSuite.java
new file mode 100755
index 0000000..4c9e315
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/JUnitRuleTestSuite.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.gemstone.gemfire.test.junit.rules.tests;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+  ExpectedTimeoutRuleTest.class,
+  IgnoreUntilRuleTest.class,
+  RepeatRuleTest.class,
+  RetryRuleGlobalWithErrorTest.class,
+  RetryRuleGlobalWithExceptionTest.class,
+  RetryRuleLocalWithErrorTest.class,
+  RetryRuleLocalWithExceptionTest.class,
+})
+public class JUnitRuleTestSuite {
+}


Mime
View raw message