You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by kl...@apache.org on 2015/08/21 22:29:35 UTC

[8/9] incubator-geode git commit: More examples and rules

More examples and rules


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

Branch: refs/heads/feature/GEODE-217
Commit: d0c033074b239162ea6d2f5fde7ac8e329402a24
Parents: 33d2c1c
Author: Kirk Lund <kl...@pivotal.io>
Authored: Fri Aug 21 13:27:27 2015 -0700
Committer: Kirk Lund <kl...@pivotal.io>
Committed: Fri Aug 21 13:27:27 2015 -0700

----------------------------------------------------------------------
 .../test/assertj/AssertJExampleJUnitTest.java   | 119 ++++++++
 .../CatchExceptionExampleDUnitTest.java         |  59 ++++
 .../CatchExceptionExampleJUnitTest.java         |  99 +++++++
 .../JUnitParamsExampleJUnitTest.java            |  36 +++
 .../com/gemstone/gemfire/test/junit/Repeat.java |  26 ++
 .../test/junit/rules/ExpectedTimeoutRule.java   | 164 +++++++++++
 .../gemfire/test/junit/rules/RepeatRule.java    |  57 ++++
 .../tests/ExpectedTimeoutRuleJUnitTest.java     | 206 ++++++++++++++
 .../junit/rules/tests/JUnitRuleTestSuite.java   |  16 ++
 .../junit/rules/tests/RepeatRuleJUnitTest.java  | 278 +++++++++++++++++++
 .../RetryRuleGlobalWithErrorJUnitTest.java      | 247 ++++++++++++++++
 .../RetryRuleGlobalWithExceptionJUnitTest.java  | 253 +++++++++++++++++
 .../tests/RetryRuleLocalWithErrorJUnitTest.java | 206 ++++++++++++++
 .../RetryRuleLocalWithExceptionJUnitTest.java   | 221 +++++++++++++++
 14 files changed, 1987 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/d0c03307/gemfire-core/src/test/java/com/gemstone/gemfire/test/assertj/AssertJExampleJUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/test/assertj/AssertJExampleJUnitTest.java b/gemfire-core/src/test/java/com/gemstone/gemfire/test/assertj/AssertJExampleJUnitTest.java
new file mode 100755
index 0000000..aaa6a84
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/test/assertj/AssertJExampleJUnitTest.java
@@ -0,0 +1,119 @@
+package com.gemstone.gemfire.test.assertj;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+
+@Category(UnitTest.class)
+public class AssertJExampleJUnitTest {
+
+  private Character aragorn;
+  private Character boromir;
+  private Character elrond;
+  private Character frodo;
+  private Character galadriel;
+  private Character gandalf;
+  private Character gimli;
+  private Character legolas;
+  private Character merry;
+  private Character pippin;
+  private Character sauron;
+  private Character sam;
+  
+  private Ring narya;
+  private Ring nenya;
+  private Ring oneRing;
+  private Ring vilya;
+  
+  private Set<Character> fellowshipOfTheRing;
+  private Map<Ring, Character> ringBearers;
+  
+  @Before
+  public void setUp() {
+    this.aragorn = new Character("Aragorn");
+    this.boromir = new Character("Boromir");
+    this.elrond = new Character("Elrond");
+    this.frodo = new Character("Frodo");
+    this.galadriel = new Character("Galadriel");
+    this.gandalf = new Character("Gandalf");
+    this.gimli = new Character("Gimli");
+    this.legolas = new Character("Legolas");
+    this.merry = new Character("Merry");
+    this.pippin = new Character("Pippin");
+    this.sauron = new Character("Sauron");
+    this.sam = new Character("Sam");
+    
+    this.narya = new Ring();
+    this.nenya = new Ring();
+    this.oneRing = new Ring();
+    this.vilya = new Ring();
+    
+    this.fellowshipOfTheRing = new HashSet<Character>();
+    this.fellowshipOfTheRing.add(this.aragorn);
+    this.fellowshipOfTheRing.add(this.boromir);
+    this.fellowshipOfTheRing.add(this.frodo);
+    this.fellowshipOfTheRing.add(this.gandalf);
+    this.fellowshipOfTheRing.add(this.gimli);
+    this.fellowshipOfTheRing.add(this.legolas);
+    this.fellowshipOfTheRing.add(this.merry);
+    this.fellowshipOfTheRing.add(this.pippin);
+    this.fellowshipOfTheRing.add(this.sam);
+
+    this.ringBearers = new HashMap<Ring, Character>();
+    this.ringBearers.put(this.oneRing, this.frodo);
+    this.ringBearers.put(this.nenya, this.galadriel);
+    this.ringBearers.put(this.narya, this.gandalf);
+    this.ringBearers.put(this.vilya, this.elrond);
+  }
+  
+  @Test
+  public void exampleShouldPass() {
+    // common assertions
+    assertThat(frodo.getName()).isEqualTo("Frodo");
+    assertThat(frodo).isNotEqualTo(sauron)
+                     .isIn(fellowshipOfTheRing);
+
+    // String specific assertions
+    assertThat(frodo.getName()).startsWith("Fro")
+                               .endsWith("do")
+                               .isEqualToIgnoringCase("frodo");
+
+    // collection specific assertions
+    assertThat(fellowshipOfTheRing).hasSize(9)
+                                   .contains(frodo, sam)
+                                   .doesNotContain(sauron);
+
+    // using extracting magical feature to check fellowshipOfTheRing characters name :)
+    assertThat(fellowshipOfTheRing).extracting("name").contains("Boromir", "Gandalf", "Frodo", "Legolas")
+                                                      .doesNotContain("Sauron", "Elrond");
+
+    // map specific assertions, ringBearers initialized with the elves rings and the one ring bearers.
+    assertThat(ringBearers).hasSize(4)
+                           .contains(entry(oneRing, frodo), entry(nenya, galadriel))
+                           .doesNotContainEntry(oneRing, aragorn);  
+  }
+  
+  protected static class Character {
+    private final String name;
+    public Character(final String name) {
+      this.name = name;
+    }
+    public String getName() {
+      return this.name;
+    }
+  }
+  
+  protected static class Ring {
+    public Ring() {
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/d0c03307/gemfire-core/src/test/java/com/gemstone/gemfire/test/catchexception/CatchExceptionExampleDUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/test/catchexception/CatchExceptionExampleDUnitTest.java b/gemfire-core/src/test/java/com/gemstone/gemfire/test/catchexception/CatchExceptionExampleDUnitTest.java
new file mode 100755
index 0000000..845c46d
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/test/catchexception/CatchExceptionExampleDUnitTest.java
@@ -0,0 +1,59 @@
+package com.gemstone.gemfire.test.catchexception;
+
+import static com.googlecode.catchexception.CatchException.*;
+import static com.googlecode.catchexception.apis.BDDCatchException.when;
+import static org.assertj.core.api.BDDAssertions.*;
+
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.gemstone.gemfire.test.dunit.DistributedTestCase;
+import com.gemstone.gemfire.test.dunit.Host;
+import com.gemstone.gemfire.test.dunit.RMIException;
+import com.gemstone.gemfire.test.dunit.SerializableCallable;
+import com.gemstone.gemfire.test.dunit.VM;
+import com.gemstone.gemfire.test.junit.categories.DistributedTest;
+
+/**
+ * Using Catch-Exception works well for remote exceptions and asserting details
+ * about root cause of RMIExceptions in DUnit tests.
+ */
+@Category(DistributedTest.class)
+public class CatchExceptionExampleDUnitTest extends DistributedTestCase {
+  private static final long serialVersionUID = 1L;
+
+  private static final String REMOTE_THROW_EXCEPTION_MESSAGE = "Throwing remoteThrowException";
+
+  @Test
+  public void testRemoteInvocationWithException() {
+    Host host = Host.getHost(0);
+    VM vm = host.getVM(0);
+
+    when(vm).invoke(new ThrowBasicTestException());
+
+    then(caughtException())
+        .isInstanceOf(RMIException.class)
+        .hasCause(new BasicTestException(REMOTE_THROW_EXCEPTION_MESSAGE));
+  }
+  
+  protected static class ThrowBasicTestException extends SerializableCallable<Object> {
+    private static final long serialVersionUID = 1L;
+    
+    @Override
+    public Object call() throws Exception {
+      throw new BasicTestException(REMOTE_THROW_EXCEPTION_MESSAGE);
+    }
+  }
+  
+  protected static class BasicTestException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+
+    public BasicTestException() {
+      super();
+    }
+    
+    public BasicTestException(String message) {
+      super(message);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/d0c03307/gemfire-core/src/test/java/com/gemstone/gemfire/test/catchexception/CatchExceptionExampleJUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/test/catchexception/CatchExceptionExampleJUnitTest.java b/gemfire-core/src/test/java/com/gemstone/gemfire/test/catchexception/CatchExceptionExampleJUnitTest.java
new file mode 100755
index 0000000..73dc361
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/test/catchexception/CatchExceptionExampleJUnitTest.java
@@ -0,0 +1,99 @@
+package com.gemstone.gemfire.test.catchexception;
+
+import static com.googlecode.catchexception.CatchException.*;
+import static com.googlecode.catchexception.apis.BDDCatchException.when;
+import static com.googlecode.catchexception.apis.CatchExceptionHamcrestMatchers.*;
+import static org.assertj.core.api.BDDAssertions.*;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+
+/**
+ * Simple unit tests exercising Catch-Exception with AssertJ, Hamcrest and JUnit.
+ */
+@Category(UnitTest.class)
+public class CatchExceptionExampleJUnitTest {
+
+  @Test
+  public void catchExceptionShouldCatchException() {
+    List<?> myList = new ArrayList<Object>();
+
+    // when: we try to get the first element of the list
+    // then: catch the exception if any is thrown
+    catchException(myList).get(1);
+    
+    // then: we expect an IndexOutOfBoundsException
+    assertThat(caughtException(), is(instanceOf(IndexOutOfBoundsException.class)));
+  }
+  
+  @Test
+  public void verifyExceptionShouldCatchException() {
+    List<?> myList = new ArrayList<Object>();
+
+    // when: we try to get the first element of the list
+    // then: catch the exception if any is thrown
+    // then: we expect an IndexOutOfBoundsException
+    verifyException(myList, IndexOutOfBoundsException.class).get(1);
+  }
+  
+  @Test
+  public void whenShouldCatchExceptionAndUseAssertJAssertion() {
+    // given: an empty list
+    List<?> myList = new ArrayList<Object>();
+
+    // when: we try to get the first element of the list
+    when(myList).get(1);
+
+    // then: we expect an IndexOutOfBoundsException
+    then(caughtException())
+            .isInstanceOf(IndexOutOfBoundsException.class)
+            .hasMessage("Index: 1, Size: 0")
+            .hasNoCause();
+  }
+  
+  @Test
+  public void catchExceptionShouldCatchExceptionAndUseHamcrestAssertion() {
+    // given: an empty list
+    List<?> myList = new ArrayList<Object>();
+
+    // when: we try to get the first element of the list
+    catchException(myList).get(1);
+
+    // then: we expect an IndexOutOfBoundsException with message "Index: 1, Size: 0"
+    assertThat(caughtException(),
+      allOf(
+        instanceOf(IndexOutOfBoundsException.class),
+        hasMessage("Index: 1, Size: 0"),
+        hasNoCause()
+      )
+    );
+  }
+  
+  @Test
+  public void shouldCatchFromThrowException() throws Exception {
+    String message = "error message";
+    
+    catchException(this).throwException(message);
+    
+    assertThat(caughtException(), is(instanceOf(Exception.class)));
+  }
+  
+  @Test
+  public void shouldVerifyFromThrowException() throws Exception {
+    String message = "error message";
+
+    verifyException(this).throwException(message);
+  }
+  
+  // fails if private
+  protected void throwException(final String message) throws Exception {
+    throw new Exception(message);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/d0c03307/gemfire-core/src/test/java/com/gemstone/gemfire/test/junitparams/JUnitParamsExampleJUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/test/junitparams/JUnitParamsExampleJUnitTest.java b/gemfire-core/src/test/java/com/gemstone/gemfire/test/junitparams/JUnitParamsExampleJUnitTest.java
new file mode 100755
index 0000000..4e14fa7
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/test/junitparams/JUnitParamsExampleJUnitTest.java
@@ -0,0 +1,36 @@
+package com.gemstone.gemfire.test.junitparams;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+@Category(UnitTest.class)
+@RunWith(JUnitParamsRunner.class)
+public class JUnitParamsExampleJUnitTest {
+  @Test
+  @Parameters({"17, false", 
+               "22, true" })
+  public void personIsAdult(int age, boolean valid) throws Exception {
+    assertThat(true, is(true));
+    assertThat(new Person(age).isAdult(), is(valid));
+  }
+  
+  protected static class Person {
+    private static final int MIN_AGE_OF_ADULT = 18;
+    private final int age;
+    public Person(final int age) {
+      this.age = age;
+    }
+    public Boolean isAdult() {
+      return this.age >= MIN_AGE_OF_ADULT;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/d0c03307/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..b76d160
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/Repeat.java
@@ -0,0 +1,26 @@
+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;
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/d0c03307/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..bf6456f
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/ExpectedTimeoutRule.java
@@ -0,0 +1,164 @@
+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/d0c03307/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..ef66d48
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/RepeatRule.java
@@ -0,0 +1,57 @@
+package com.gemstone.gemfire.test.junit.rules;
+
+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.
+ * 
+ * TODO: disallow 0 because @Retry(0) is equivalent to @Ignore apparently
+ *
+ * @author John Blum
+ * @see org.junit.rules.TestRule
+ * @see org.junit.runner.Description
+ * @see org.junit.runners.model.Statement
+ */
+@SuppressWarnings("unused")
+public class RepeatRule implements TestRule {
+
+  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) {
+      repetitions = repeat.value();
+    }
+
+    return repetitions;
+  }
+
+  private boolean isTest(final Description description) {
+    return (description.isSuite() || description.isTest());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/d0c03307/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/ExpectedTimeoutRuleJUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/ExpectedTimeoutRuleJUnitTest.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/ExpectedTimeoutRuleJUnitTest.java
new file mode 100755
index 0000000..8a14f67
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/ExpectedTimeoutRuleJUnitTest.java
@@ -0,0 +1,206 @@
+package com.gemstone.gemfire.test.junit.rules.tests;
+
+import static org.hamcrest.core.StringContains.*;
+import static org.hamcrest.core.Is.*;
+import static org.hamcrest.core.IsInstanceOf.*;
+import static org.junit.Assert.*;
+
+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.JUnitCore;
+import org.junit.runner.Request;
+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 ExpectedTimeoutRuleJUnitTest {
+
+  @Test
+  public void passesUnused() {
+    Result result = runTest(PassingTestShouldPassWhenUnused.class);
+    
+    assertTrue(result.wasSuccessful());
+  }
+  
+  @Test
+  public void failsWithoutExpectedException() {
+    Result result = runTest(FailsWithoutExpectedException.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals(1, failures.size());
+    
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(AssertionError.class)));
+    assertThat(failure.getException().getMessage(), containsString("Expected test to throw an instance of " + TimeoutException.class.getName()));
+  }
+  
+  @Test
+  public void failsWithoutExpectedTimeoutException() {
+    Result result = runTest(FailsWithoutExpectedTimeoutException.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals(1, failures.size());
+    
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(AssertionError.class)));
+    assertThat(failure.getException().getMessage(), containsString("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);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals(1, failures.size());
+    
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(AssertionError.class)));
+    assertThat(failure.getException().getMessage(), containsString(NullPointerException.class.getName()));
+  }
+  
+  @Test
+  public void passesWithExpectedTimeoutAndTimeoutException() {
+    Result result = runTest(PassesWithExpectedTimeoutAndTimeoutException.class);
+    
+    assertTrue(result.wasSuccessful());
+  }
+  
+  @Test
+  public void failsWhenTimeoutIsEarly() {
+    Result result = runTest(FailsWhenTimeoutIsEarly.class);
+   
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals(1, failures.size());
+    
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(AssertionError.class)));
+    assertThat(failure.getException().getMessage(), containsString("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);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals(1, failures.size());
+    
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(AssertionError.class)));
+    assertThat(failure.getException().getMessage(), containsString("Expected test to throw (an instance of " + TimeoutException.class.getName() + " and exception with message a string containing \"" + FailsWhenTimeoutIsLate.message + "\")"));
+  }
+  
+  private static Result runTest(Class<?> test) {
+    JUnitCore junitCore = new JUnitCore();
+    return junitCore.run(Request.aClass(test).getRunner());
+  }
+  
+  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/d0c03307/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..ff102f7
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/JUnitRuleTestSuite.java
@@ -0,0 +1,16 @@
+package com.gemstone.gemfire.test.junit.rules.tests;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+  ExpectedTimeoutRuleJUnitTest.class,
+  RepeatRuleJUnitTest.class,
+  RetryRuleGlobalWithErrorJUnitTest.class,
+  RetryRuleGlobalWithExceptionJUnitTest.class,
+  RetryRuleLocalWithErrorJUnitTest.class,
+  RetryRuleLocalWithExceptionJUnitTest.class,
+})
+public class JUnitRuleTestSuite {
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/d0c03307/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RepeatRuleJUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RepeatRuleJUnitTest.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RepeatRuleJUnitTest.java
new file mode 100755
index 0000000..5e69f03
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RepeatRuleJUnitTest.java
@@ -0,0 +1,278 @@
+package com.gemstone.gemfire.test.junit.rules.tests;
+
+import static org.hamcrest.Matchers.*;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assert.*;
+
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Request;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+
+import com.gemstone.gemfire.test.junit.Repeat;
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+import com.gemstone.gemfire.test.junit.rules.RepeatRule;
+
+/**
+ * Unit tests for Repeat JUnit Rule.
+ * 
+ * @author Kirk Lund
+ */
+@Category(UnitTest.class)
+public class RepeatRuleJUnitTest {
+
+  private static final String ASSERTION_ERROR_MESSAGE = "failing test";
+  
+  @Test
+  public void failingTestShouldFailOneTimeWhenRepeatIsUnused() {
+    Result result = runTest(FailingTestShouldFailOneTimeWhenRepeatIsUnused.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals("Failures: " + failures, 1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(AssertionError.class)));
+    assertThat(failure.getException().getMessage(), containsString(ASSERTION_ERROR_MESSAGE));
+    assertThat(FailingTestShouldFailOneTimeWhenRepeatIsUnused.count, is(1));
+  }
+
+  @Test
+  public void passingTestShouldPassOneTimeWhenRepeatIsUnused() {
+    Result result = runTest(PassingTestShouldPassOneTimeWhenRepeatIsUnused.class);
+    
+    assertTrue(result.wasSuccessful());
+    assertThat(PassingTestShouldPassOneTimeWhenRepeatIsUnused.count, is(1));
+  }
+
+  @Test
+  public void failingTestShouldBeSkippedWhenRepeatIsZero() {
+    Result result = runTest(FailingTestShouldBeSkippedWhenRepeatIsZero.class);
+    
+    assertTrue(result.wasSuccessful());
+    assertThat(FailingTestShouldBeSkippedWhenRepeatIsZero.count, is(0));
+  }
+
+  @Test
+  public void passingTestShouldBeSkippedWhenRepeatIsZero() {
+    Result result = runTest(PassingTestShouldBeSkippedWhenRepeatIsZero.class);
+    
+    assertTrue(result.wasSuccessful());
+    assertThat(PassingTestShouldBeSkippedWhenRepeatIsZero.count, is(0));
+  }
+
+  @Test
+  public void failingTestShouldFailOneTimeWhenRepeatIsOne() {
+    Result result = runTest(FailingTestShouldFailOneTimeWhenRepeatIsOne.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals("Failures: " + failures, 1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(AssertionError.class)));
+    assertThat(failure.getException().getMessage(), containsString(ASSERTION_ERROR_MESSAGE));
+    assertThat(FailingTestShouldFailOneTimeWhenRepeatIsOne.count, is(1));
+  }
+
+  @Test
+  public void passingTestShouldPassOneTimeWhenRepeatIsOne() {
+    Result result = runTest(PassingTestShouldPassOneTimeWhenRepeatIsOne.class);
+    
+    assertTrue(result.wasSuccessful());
+    assertThat(PassingTestShouldPassOneTimeWhenRepeatIsOne.count, is(1));
+  }
+
+  @Test
+  public void failingTestShouldFailOneTimeWhenRepeatIsTwo() {
+    Result result = runTest(FailingTestShouldFailOneTimeWhenRepeatIsTwo.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals("Failures: " + failures, 1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(AssertionError.class)));
+    assertThat(failure.getException().getMessage(), containsString(ASSERTION_ERROR_MESSAGE));
+    assertThat(FailingTestShouldFailOneTimeWhenRepeatIsTwo.count, is(1));
+  }
+
+  @Test
+  public void passingTestShouldPassTwoTimesWhenRepeatIsTwo() {
+    Result result = runTest(PassingTestShouldPassTwoTimesWhenRepeatIsTwo.class);
+    
+    assertTrue(result.wasSuccessful());
+    assertThat(PassingTestShouldPassTwoTimesWhenRepeatIsTwo.count, is(2));
+  }
+
+  @Test
+  public void failingTestShouldFailOneTimeWhenRepeatIsThree() {
+    Result result = runTest(FailingTestShouldFailOneTimeWhenRepeatIsThree.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals("Failures: " + failures, 1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(AssertionError.class)));
+    assertThat(failure.getException().getMessage(), containsString(ASSERTION_ERROR_MESSAGE));
+    assertThat(FailingTestShouldFailOneTimeWhenRepeatIsThree.count, is(1));
+  }
+
+  @Test
+  public void passingTestShouldPassThreeTimesWhenRepeatIsThree() {
+    Result result = runTest(PassingTestShouldPassThreeTimesWhenRepeatIsThree.class);
+    
+    assertTrue(result.wasSuccessful());
+    assertThat(PassingTestShouldPassThreeTimesWhenRepeatIsThree.count, is(3));
+  }
+
+  private static Result runTest(Class<?> test) {
+    JUnitCore junitCore = new JUnitCore();
+    return junitCore.run(Request.aClass(test).getRunner());
+  }
+  
+  public static class FailingTestShouldFailOneTimeWhenRepeatIsUnused {
+    protected static int count = 0;
+    
+    @Rule
+    public RepeatRule repeat = new RepeatRule();
+
+    @Test
+    public void doTest() throws Exception {
+      count++;
+      fail(ASSERTION_ERROR_MESSAGE);
+    }
+  }
+
+  public static class PassingTestShouldPassOneTimeWhenRepeatIsUnused {
+    protected static int count = 0;
+    
+    @Rule
+    public RepeatRule repeat = new RepeatRule();
+
+    @Test
+    public void doTest() throws Exception {
+      count++;
+    }
+  }
+
+  public static class FailingTestShouldBeSkippedWhenRepeatIsZero {
+    protected static int count = 0;
+    
+    @Rule
+    public RepeatRule repeat = new RepeatRule();
+
+    @Test
+    @Repeat(0)
+    public void doTest() throws Exception {
+      count++;
+      fail(ASSERTION_ERROR_MESSAGE);
+    }
+  }
+
+  public static class PassingTestShouldBeSkippedWhenRepeatIsZero {
+    protected static int count = 0;
+    
+    @Rule
+    public RepeatRule repeat = new RepeatRule();
+
+    @Test
+    @Repeat(0)
+    public void doTest() throws Exception {
+      count++;
+    }
+  }
+  
+  public static class FailingTestShouldFailOneTimeWhenRepeatIsOne {
+    protected static int count = 0;
+    
+    @Rule
+    public RepeatRule repeat = new RepeatRule();
+
+    @Test
+    @Repeat(1)
+    public void doTest() throws Exception {
+      count++;
+      fail(ASSERTION_ERROR_MESSAGE);
+    }
+  }
+
+  public static class PassingTestShouldPassOneTimeWhenRepeatIsOne {
+    protected static int count = 0;
+    
+    @Rule
+    public RepeatRule repeat = new RepeatRule();
+
+    @Test
+    @Repeat(1)
+    public void doTest() throws Exception {
+      count++;
+    }
+  }
+
+  public static class FailingTestShouldFailOneTimeWhenRepeatIsTwo {
+    protected static int count = 0;
+    
+    @Rule
+    public RepeatRule repeat = new RepeatRule();
+
+    @Test
+    @Repeat(2)
+    public void doTest() throws Exception {
+      count++;
+      fail(ASSERTION_ERROR_MESSAGE);
+    }
+  }
+
+  public static class PassingTestShouldPassTwoTimesWhenRepeatIsTwo {
+    protected static int count = 0;
+    
+    @Rule
+    public RepeatRule repeat = new RepeatRule();
+
+    @Test
+    @Repeat(2)
+    public void doTest() throws Exception {
+      count++;
+    }
+  }
+
+  public static class FailingTestShouldFailOneTimeWhenRepeatIsThree {
+    protected static int count = 0;
+    
+    @Rule
+    public RepeatRule repeat = new RepeatRule();
+
+    @Test
+    @Repeat(3)
+    public void doTest() throws Exception {
+      count++;
+      fail(ASSERTION_ERROR_MESSAGE);
+    }
+  }
+
+  public static class PassingTestShouldPassThreeTimesWhenRepeatIsThree {
+    protected static int count = 0;
+    
+    @Rule
+    public RepeatRule repeat = new RepeatRule();
+
+    @Test
+    @Repeat(3)
+    public void doTest() throws Exception {
+      count++;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/d0c03307/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RetryRuleGlobalWithErrorJUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RetryRuleGlobalWithErrorJUnitTest.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RetryRuleGlobalWithErrorJUnitTest.java
new file mode 100755
index 0000000..ce473e8
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RetryRuleGlobalWithErrorJUnitTest.java
@@ -0,0 +1,247 @@
+package com.gemstone.gemfire.test.junit.rules.tests;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Request;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+
+import com.gemstone.gemfire.test.junit.Retry;
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+import com.gemstone.gemfire.test.junit.rules.RetryRule;
+
+/**
+ * Unit tests for Retry JUnit Rule involving global scope (ie Rule affects all 
+ * tests in the test case) with failures due to an Exception.
+ * 
+ * @author Kirk Lund
+ */
+@Category(UnitTest.class)
+public class RetryRuleGlobalWithErrorJUnitTest {
+  
+  @Test
+  public void zeroIsIllegal() {
+    Result result = runTest(ZeroIsIllegal.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals("Failures: " + failures, 1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(IllegalArgumentException.class)));
+    assertThat(failure.getException().getMessage(), containsString("Retry count must be greater than zero"));
+    assertThat(ZeroIsIllegal.count, is(0));
+  }
+  
+  @Test
+  public void failsWithOne() {
+    Result result = runTest(FailsWithOne.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals("Failures: " + failures, 1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(AssertionError.class)));
+    assertThat(failure.getException().getMessage(), containsString(FailsWithOne.message));
+    assertThat(FailsWithOne.count, is(1));
+  }
+  
+  @Test
+  public void passesWithOne() {
+    Result result = runTest(PassesWithOne.class);
+    
+    assertTrue(result.wasSuccessful());
+  }
+  
+  @Test
+  public void passesWithUnused() {
+    Result result = runTest(PassesWhenUnused.class);
+    
+    assertTrue(result.wasSuccessful());
+  }
+  
+  @Test
+  public void failsOnSecondAttempt() {
+    Result result = runTest(FailsOnSecondAttempt.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals(1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(AssertionError.class)));
+    assertThat(failure.getException().getMessage(), containsString(FailsOnSecondAttempt.message));
+    assertThat(FailsOnSecondAttempt.count, is(2));
+  }
+
+  @Test
+  public void passesOnSecondAttempt() {
+    Result result = runTest(PassesOnSecondAttempt.class);
+    
+    assertTrue(result.wasSuccessful());
+    assertThat(PassesOnSecondAttempt.count, is(2));
+  }
+  
+  @Test
+  public void failsOnThirdAttempt() {
+    Result result = runTest(FailsOnThirdAttempt.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals(1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(AssertionError.class)));
+    assertThat(failure.getException().getMessage(), containsString(FailsOnThirdAttempt.message));
+    assertThat(FailsOnThirdAttempt.count, is(3));
+  }
+
+  @Test
+  public void passesOnThirdAttempt() {
+    Result result = runTest(PassesOnThirdAttempt.class);
+    
+    assertTrue(result.wasSuccessful());
+    assertThat(PassesOnThirdAttempt.count, is(3));
+  }
+  
+  private static Result runTest(Class<?> test) {
+    JUnitCore junitCore = new JUnitCore();
+    return junitCore.run(Request.aClass(test).getRunner());
+  }
+  
+  public static class ZeroIsIllegal {
+    protected static int count;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule(0);
+
+    @Test
+    public void zeroIsIllegal() throws Exception {
+      count++;
+    }
+  }
+  
+  public static class FailsWithOne {
+    protected static int count;
+    protected static String message;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule(1);
+
+    @Test
+    public void failsWithOne() throws Exception {
+      count++;
+      message = "Failing " + count;
+      fail(message);
+    }
+  }
+  
+  public static class PassesWithOne {
+    protected static int count;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule(1);
+
+    @Test
+    public void passesWithOne() throws Exception {
+      count++;
+    }
+  }
+  
+  public static class PassesWhenUnused {
+    protected static int count;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule(2);
+
+    @Test
+    public void passesWithUnused() throws Exception {
+      count++;
+    }
+  }
+  
+  public static class FailsOnSecondAttempt {
+    protected static int count;
+    protected static String message;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule(2);
+
+    @Test
+    @Retry(2)
+    public void failsOnSecondAttempt() {
+      count++;
+      message = "Failing " + count;
+      fail(message);
+    }
+  }
+  
+  public static class PassesOnSecondAttempt {
+    protected static int count;
+    protected static String message;
+    
+    @Rule
+    public RetryRule retryRule = new RetryRule(2);
+
+    @Test
+    @Retry(2)
+    public void failsOnSecondAttempt() {
+      count++;
+      if (count < 2) {
+        message = "Failing " + count;
+        fail(message);
+      }
+    }
+  }
+  
+  public static class FailsOnThirdAttempt {
+    protected static int count;
+    protected static String message;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule(3);
+
+    @Test
+    @Retry(3)
+    public void failsOnThirdAttempt() {
+      count++;
+      message = "Failing " + count;
+      fail(message);
+    }
+  }
+
+  public static class PassesOnThirdAttempt {
+    protected static int count;
+    protected static String message;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule(3);
+
+    @Test
+    public void failsOnThirdAttempt() {
+      count++;
+      if (count < 3) {
+        message = "Failing " + count;
+        fail(message);
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/d0c03307/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RetryRuleGlobalWithExceptionJUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RetryRuleGlobalWithExceptionJUnitTest.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RetryRuleGlobalWithExceptionJUnitTest.java
new file mode 100755
index 0000000..b67880c
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RetryRuleGlobalWithExceptionJUnitTest.java
@@ -0,0 +1,253 @@
+package com.gemstone.gemfire.test.junit.rules.tests;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Request;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+
+import com.gemstone.gemfire.test.junit.Retry;
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+import com.gemstone.gemfire.test.junit.rules.RetryRule;
+
+/**
+ * Unit tests for Retry JUnit Rule involving global scope (ie Rule affects all 
+ * tests in the test case) with failures due to an Exception.
+ * 
+ * @author Kirk Lund
+ */
+@Category(UnitTest.class)
+public class RetryRuleGlobalWithExceptionJUnitTest {
+  
+  @Test
+  public void zeroIsIllegal() {
+    Result result = runTest(ZeroIsIllegal.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals("Failures: " + failures, 1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(IllegalArgumentException.class)));
+    assertThat(failure.getException().getMessage(), containsString("Retry count must be greater than zero"));
+    assertThat(ZeroIsIllegal.count, is(0));
+  }
+  
+  @Test
+  public void failsWithOne() {
+    Result result = runTest(FailsWithOne.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals("Failures: " + failures, 1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(CustomException.class)));
+    assertThat(failure.getException().getMessage(), containsString(FailsWithOne.message));
+    assertThat(FailsWithOne.count, is(1));
+  }
+  
+  @Test
+  public void passesWithOne() {
+    Result result = runTest(PassesWithOne.class);
+    
+    assertTrue(result.wasSuccessful());
+  }
+  
+  @Test
+  public void passesWithUnused() {
+    Result result = runTest(PassesWhenUnused.class);
+    
+    assertTrue(result.wasSuccessful());
+  }
+  
+  @Test
+  public void failsOnSecondAttempt() {
+    Result result = runTest(FailsOnSecondAttempt.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals(1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(CustomException.class)));
+    assertThat(failure.getException().getMessage(), containsString(FailsOnSecondAttempt.message));
+    assertThat(FailsOnSecondAttempt.count, is(2));
+  }
+
+  @Test
+  public void passesOnSecondAttempt() {
+    Result result = runTest(PassesOnSecondAttempt.class);
+    
+    assertTrue(result.wasSuccessful());
+    assertThat(PassesOnSecondAttempt.count, is(2));
+  }
+  
+  @Test
+  public void failsOnThirdAttempt() {
+    Result result = runTest(FailsOnThirdAttempt.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals(1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(CustomException.class)));
+    assertThat(failure.getException().getMessage(), containsString(FailsOnThirdAttempt.message));
+    assertThat(FailsOnThirdAttempt.count, is(3));
+  }
+
+  @Test
+  public void passesOnThirdAttempt() {
+    Result result = runTest(PassesOnThirdAttempt.class);
+    
+    assertTrue(result.wasSuccessful());
+    assertThat(PassesOnThirdAttempt.count, is(3));
+  }
+  
+  private static Result runTest(Class<?> test) {
+    JUnitCore junitCore = new JUnitCore();
+    return junitCore.run(Request.aClass(test).getRunner());
+  }
+  
+  public static class CustomException extends Exception {
+    private static final long serialVersionUID = 1L;
+    public CustomException(final String message) {
+      super(message);
+    }
+  }
+  
+  public static class ZeroIsIllegal {
+    protected static int count;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule(0);
+
+    @Test
+    public void zeroIsIllegal() throws Exception {
+      count++;
+    }
+  }
+  
+  public static class FailsWithOne {
+    protected static int count;
+    protected static String message;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule(1);
+
+    @Test
+    public void failsWithOne() throws Exception {
+      count++;
+      message = "Failing " + count;
+      throw new CustomException(message);
+    }
+  }
+  
+  public static class PassesWithOne {
+    protected static int count;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule(1);
+
+    @Test
+    public void passesWithOne() throws Exception {
+      count++;
+    }
+  }
+  
+  public static class PassesWhenUnused {
+    protected static int count;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule(2);
+
+    @Test
+    public void passesWithUnused() throws Exception {
+      count++;
+    }
+  }
+  
+  public static class FailsOnSecondAttempt {
+    protected static int count;
+    protected static String message;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule(2);
+
+    @Test
+    @Retry(2)
+    public void failsOnSecondAttempt() throws Exception {
+      count++;
+      message = "Failing " + count;
+      throw new CustomException(message);
+    }
+  }
+  
+  public static class PassesOnSecondAttempt {
+    protected static int count;
+    protected static String message;
+    
+    @Rule
+    public RetryRule retryRule = new RetryRule(2);
+
+    @Test
+    @Retry(2)
+    public void failsOnSecondAttempt() throws Exception {
+      count++;
+      if (count < 2) {
+        message = "Failing " + count;
+        throw new CustomException(message);
+      }
+    }
+  }
+  
+  public static class FailsOnThirdAttempt {
+    protected static int count;
+    protected static String message;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule(3);
+
+    @Test
+    @Retry(3)
+    public void failsOnThirdAttempt() throws Exception {
+      count++;
+      message = "Failing " + count;
+      throw new CustomException(message);
+    }
+  }
+
+  public static class PassesOnThirdAttempt {
+    protected static int count;
+    protected static String message;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule(3);
+
+    @Test
+    public void failsOnThirdAttempt() throws Exception {
+      count++;
+      if (count < 3) {
+        message = "Failing " + count;
+        throw new CustomException(message);
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/d0c03307/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RetryRuleLocalWithErrorJUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RetryRuleLocalWithErrorJUnitTest.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RetryRuleLocalWithErrorJUnitTest.java
new file mode 100755
index 0000000..b06575c
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RetryRuleLocalWithErrorJUnitTest.java
@@ -0,0 +1,206 @@
+package com.gemstone.gemfire.test.junit.rules.tests;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Request;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+
+import com.gemstone.gemfire.test.junit.Retry;
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+import com.gemstone.gemfire.test.junit.rules.RetryRule;
+
+/**
+ * Unit tests for Retry JUnit Rule involving local scope (ie Rule affects 
+ * test methods annotated with @Retry) with failures due to an Error.
+ * 
+ * @author Kirk Lund
+ */
+@Category(UnitTest.class)
+public class RetryRuleLocalWithErrorJUnitTest {
+
+  @Test
+  public void failsUnused() {
+    Result result = runTest(FailsUnused.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals("Failures: " + failures, 1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(AssertionError.class)));
+    assertThat(failure.getException().getMessage(), containsString(FailsUnused.message));
+    assertThat(FailsUnused.count, is(1));
+  }
+  
+  @Test
+  public void passesUnused() {
+    Result result = runTest(PassesUnused.class);
+    
+    assertTrue(result.wasSuccessful());
+    assertThat(PassesUnused.count, is(1));
+  }
+  
+  @Test
+  public void failsOnSecondAttempt() {
+    Result result = runTest(FailsOnSecondAttempt.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals(1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(AssertionError.class)));
+    assertThat(failure.getException().getMessage(), containsString(FailsOnSecondAttempt.message));
+    assertThat(FailsOnSecondAttempt.count, is(2));
+  }
+
+  @Test
+  public void passesOnSecondAttempt() {
+    Result result = runTest(PassesOnSecondAttempt.class);
+    
+    assertTrue(result.wasSuccessful());
+    assertThat(PassesOnSecondAttempt.count, is(2));
+  }
+  
+  @Test
+  public void failsOnThirdAttempt() {
+    Result result = runTest(FailsOnThirdAttempt.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals(1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(AssertionError.class)));
+    assertThat(failure.getException().getMessage(), containsString(FailsOnThirdAttempt.message));
+    assertThat(FailsOnThirdAttempt.count, is(3));
+  }
+
+  @Test
+  public void passesOnThirdAttempt() {
+    Result result = runTest(PassesOnThirdAttempt.class);
+    
+    assertTrue(result.wasSuccessful());
+    assertThat(PassesOnThirdAttempt.count, is(3));
+  }
+  
+  private static Result runTest(Class<?> test) {
+    JUnitCore junitCore = new JUnitCore();
+    return junitCore.run(Request.aClass(test).getRunner());
+  }
+  
+  public static class FailsUnused {
+    protected static int count;
+    protected static String message;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule();
+
+    @Test
+    public void failsUnused() throws Exception {
+      count++;
+      message = "Failing " + count;
+      fail(message);
+    }
+  }
+  
+  public static class PassesUnused {
+    protected static int count;
+    protected static String message;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule();
+
+    @Test
+    public void passesUnused() throws Exception {
+      count++;
+    }
+  }
+  
+  public static class FailsOnSecondAttempt {
+    protected static int count;
+    protected static String message;
+    
+    @Rule
+    public RetryRule retryRule = new RetryRule();
+
+    @Test
+    @Retry(2)
+    public void failsOnSecondAttempt() {
+      count++;
+      message = "Failing " + count;
+      fail(message);
+    }
+  }
+  
+  public static class PassesOnSecondAttempt {
+    protected static int count;
+    protected static String message;
+    
+    @Rule
+    public RetryRule retryRule = new RetryRule();
+
+    @Test
+    @Retry(2)
+    public void failsOnSecondAttempt() {
+      count++;
+      if (count < 2) {
+        message = "Failing " + count;
+        fail(message);
+      }
+    }
+  }
+  
+  public static class FailsOnThirdAttempt {
+    protected static int count;
+    protected static String message;
+    
+    @Rule
+    public RetryRule retryRule = new RetryRule();
+
+    @Test
+    @Retry(3)
+    public void failsOnThirdAttempt() {
+      count++;
+
+      message = "Failing " + count;
+      fail(message);
+    }
+  }
+
+  public static class PassesOnThirdAttempt {
+    protected static int count;
+    protected static String message;
+    
+    @Rule
+    public RetryRule retryRule = new RetryRule();
+
+    @Test
+    @Retry(3)
+    public void failsOnThirdAttempt() {
+      count++;
+
+      if (count < 3) {
+        message = "Failing " + count;
+        fail(message);
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/d0c03307/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RetryRuleLocalWithExceptionJUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RetryRuleLocalWithExceptionJUnitTest.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RetryRuleLocalWithExceptionJUnitTest.java
new file mode 100755
index 0000000..4cff4bb
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/rules/tests/RetryRuleLocalWithExceptionJUnitTest.java
@@ -0,0 +1,221 @@
+package com.gemstone.gemfire.test.junit.rules.tests;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Request;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+
+import com.gemstone.gemfire.test.junit.Retry;
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+import com.gemstone.gemfire.test.junit.rules.RetryRule;
+import com.gemstone.gemfire.test.junit.rules.tests.RetryRuleLocalWithErrorJUnitTest.FailsOnSecondAttempt;
+import com.gemstone.gemfire.test.junit.rules.tests.RetryRuleLocalWithErrorJUnitTest.FailsOnThirdAttempt;
+import com.gemstone.gemfire.test.junit.rules.tests.RetryRuleLocalWithErrorJUnitTest.FailsUnused;
+import com.gemstone.gemfire.test.junit.rules.tests.RetryRuleLocalWithErrorJUnitTest.PassesOnSecondAttempt;
+import com.gemstone.gemfire.test.junit.rules.tests.RetryRuleLocalWithErrorJUnitTest.PassesOnThirdAttempt;
+import com.gemstone.gemfire.test.junit.rules.tests.RetryRuleLocalWithErrorJUnitTest.PassesUnused;
+
+/**
+ * Unit tests for Retry JUnit Rule involving local scope (ie Rule affects 
+ * test methods annotated with @Retry) with failures due to an Exception.
+ * 
+ * @author Kirk Lund
+ */
+@Category(UnitTest.class)
+public class RetryRuleLocalWithExceptionJUnitTest {
+
+  @Test
+  public void failsUnused() {
+    Result result = runTest(FailsUnused.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals("Failures: " + failures, 1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(CustomException.class)));
+    assertThat(failure.getException().getMessage(), containsString(FailsUnused.message));
+    assertThat(FailsUnused.count, is(1));
+  }
+  
+  @Test
+  public void passesUnused() {
+    Result result = runTest(PassesUnused.class);
+    
+    assertTrue(result.wasSuccessful());
+    assertThat(PassesUnused.count, is(1));
+  }
+  
+  @Test
+  public void failsOnSecondAttempt() {
+    Result result = runTest(FailsOnSecondAttempt.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals(1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(CustomException.class)));
+    assertThat(failure.getException().getMessage(), containsString(FailsOnSecondAttempt.message));
+    assertThat(FailsOnSecondAttempt.count, is(2));
+  }
+
+  @Test
+  public void passesOnSecondAttempt() {
+    Result result = runTest(PassesOnSecondAttempt.class);
+    
+    assertTrue(result.wasSuccessful());
+    assertThat(PassesOnSecondAttempt.count, is(2));
+  }
+  
+  @Test
+  public void failsOnThirdAttempt() {
+    Result result = runTest(FailsOnThirdAttempt.class);
+    
+    assertFalse(result.wasSuccessful());
+    
+    List<Failure> failures = result.getFailures();
+    assertEquals(1, failures.size());
+
+    Failure failure = failures.get(0);
+    assertThat(failure.getException(), is(instanceOf(CustomException.class)));
+    assertThat(failure.getException().getMessage(), containsString(FailsOnThirdAttempt.message));
+    assertThat(FailsOnThirdAttempt.count, is(3));
+  }
+
+  @Test
+  public void passesOnThirdAttempt() {
+    Result result = runTest(PassesOnThirdAttempt.class);
+    
+    assertTrue(result.wasSuccessful());
+    assertThat(PassesOnThirdAttempt.count, is(3));
+  }
+  
+  private static Result runTest(Class<?> test) {
+    JUnitCore junitCore = new JUnitCore();
+    return junitCore.run(Request.aClass(test).getRunner());
+  }
+  
+  public static class CustomException extends Exception {
+    private static final long serialVersionUID = 1L;
+    public CustomException(final String message) {
+      super(message);
+    }
+  }
+  
+  public static class FailsUnused {
+    protected static int count;
+    protected static String message;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule();
+
+    @Test
+    public void failsUnused() throws Exception {
+      count++;
+      message = "Failing " + count;
+      throw new CustomException(message);
+    }
+  }
+  
+  public static class PassesUnused {
+    protected static int count;
+    protected static String message;
+
+    @Rule
+    public RetryRule retryRule = new RetryRule();
+
+    @Test
+    public void passesUnused() throws Exception {
+      count++;
+    }
+  }
+  
+  public static class FailsOnSecondAttempt {
+    protected static int count;
+    protected static String message;
+    
+    @Rule
+    public RetryRule retryRule = new RetryRule();
+
+    @Test
+    @Retry(2)
+    public void failsOnSecondAttempt() throws Exception {
+      count++;
+      message = "Failing " + count;
+      throw new CustomException(message);
+    }
+  }
+  
+  public static class PassesOnSecondAttempt {
+    protected static int count;
+    protected static String message;
+    
+    @Rule
+    public RetryRule retryRule = new RetryRule();
+
+    @Test
+    @Retry(2)
+    public void failsOnSecondAttempt() throws Exception {
+      count++;
+      if (count < 2) {
+        message = "Failing " + count;
+        throw new CustomException(message);
+      }
+    }
+  }
+  
+  public static class FailsOnThirdAttempt {
+    protected static int count;
+    protected static String message;
+    
+    @Rule
+    public RetryRule retryRule = new RetryRule();
+
+    @Test
+    @Retry(3)
+    public void failsOnThirdAttempt() throws Exception {
+      count++;
+
+      message = "Failing " + count;
+      throw new CustomException(message);
+    }
+  }
+
+  public static class PassesOnThirdAttempt {
+    protected static int count;
+    protected static String message;
+    
+    @Rule
+    public RetryRule retryRule = new RetryRule();
+
+    @Test
+    @Retry(3)
+    public void failsOnThirdAttempt() throws Exception {
+      count++;
+
+      if (count < 3) {
+        message = "Failing " + count;
+        throw new CustomException(message);
+      }
+    }
+  }
+}