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 2017/10/04 23:58:24 UTC

[geode] branch develop updated: GEODE-3747: add SharedErrorCollector rule

This is an automated email from the ASF dual-hosted git repository.

klund pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new 9f9a58c  GEODE-3747: add SharedErrorCollector rule
9f9a58c is described below

commit 9f9a58cb88330635f71830f622febdc7c5e7072e
Author: Kirk Lund <kl...@apache.org>
AuthorDate: Wed Oct 4 11:00:49 2017 -0700

    GEODE-3747: add SharedErrorCollector rule
    
    * use for assertions in callbacks and in multiple JVMs
    * minor cleanup of related classes
---
 .../geode/test/dunit/cache/rules/CacheRule.java    |   4 +-
 .../dunit/rules/DistributedDisconnectRule.java     |   4 +-
 .../rules/DistributedRestoreSystemProperties.java  |   4 +-
 .../DistributedUseJacksonForJsonPathRule.java      |   4 +-
 .../geode/test/dunit/rules/RemoteInvoker.java      |  30 +-
 .../geode/test/dunit/rules/SharedCountersRule.java |   8 +-
 .../test/dunit/rules/SharedErrorCollector.java     | 160 ++++++++++
 ...ibutedTest.java => SharedCountersRuleTest.java} |   2 +-
 .../rules/tests/SharedErrorCollectorTest.java      | 353 +++++++++++++++++++++
 .../serializable/SerializableExternalResource.java |   2 -
 10 files changed, 552 insertions(+), 19 deletions(-)

diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/cache/rules/CacheRule.java b/geode-core/src/test/java/org/apache/geode/test/dunit/cache/rules/CacheRule.java
index 7d81ebd..454a21d 100644
--- a/geode-core/src/test/java/org/apache/geode/test/dunit/cache/rules/CacheRule.java
+++ b/geode-core/src/test/java/org/apache/geode/test/dunit/cache/rules/CacheRule.java
@@ -80,7 +80,7 @@ public class CacheRule extends DistributedExternalResource {
   @Override
   protected void before() {
     if (createCacheInAll) {
-      invoker().invokeInEveryVM(() -> createCache(config));
+      invoker().invokeInEveryVMAndController(() -> createCache(config));
     } else {
       if (createCache) {
         createCache(config);
@@ -94,7 +94,7 @@ public class CacheRule extends DistributedExternalResource {
   @Override
   protected void after() {
     closeAndNullCache();
-    invoker().invokeInEveryVM(() -> closeAndNullCache());
+    invoker().invokeInEveryVMAndController(() -> closeAndNullCache());
 
     if (disconnectAfter) {
       Disconnect.disconnectAllFromDS();
diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/DistributedDisconnectRule.java b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/DistributedDisconnectRule.java
index 46eaa98..41f7fc9 100755
--- a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/DistributedDisconnectRule.java
+++ b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/DistributedDisconnectRule.java
@@ -71,14 +71,14 @@ public class DistributedDisconnectRule extends DistributedExternalResource {
   @Override
   protected void before() throws Throwable {
     if (this.disconnectBefore) {
-      invoker().invokeInEveryVM(serializableRunnable());
+      invoker().invokeInEveryVMAndController(serializableRunnable());
     }
   }
 
   @Override
   protected void after() {
     if (this.disconnectAfter) {
-      invoker().invokeInEveryVM(serializableRunnable());
+      invoker().invokeInEveryVMAndController(serializableRunnable());
     }
   }
 
diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/DistributedRestoreSystemProperties.java b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/DistributedRestoreSystemProperties.java
index 322d0c6..5d6c6c4 100755
--- a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/DistributedRestoreSystemProperties.java
+++ b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/DistributedRestoreSystemProperties.java
@@ -46,7 +46,7 @@ public class DistributedRestoreSystemProperties extends RestoreSystemProperties
   @Override
   public void before() throws Throwable {
     super.before();
-    this.invoker.invokeInEveryVM(new SerializableRunnable() {
+    this.invoker.invokeInEveryVMAndController(new SerializableRunnable() {
       @Override
       public void run() {
         if (originalProperties == null) {
@@ -60,7 +60,7 @@ public class DistributedRestoreSystemProperties extends RestoreSystemProperties
   @Override
   public void after() {
     super.after();
-    this.invoker.invokeInEveryVM(new SerializableRunnable() {
+    this.invoker.invokeInEveryVMAndController(new SerializableRunnable() {
       @Override
       public void run() {
         if (originalProperties != null) {
diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/DistributedUseJacksonForJsonPathRule.java b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/DistributedUseJacksonForJsonPathRule.java
index 6b646af..8c85775 100644
--- a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/DistributedUseJacksonForJsonPathRule.java
+++ b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/DistributedUseJacksonForJsonPathRule.java
@@ -32,12 +32,12 @@ public class DistributedUseJacksonForJsonPathRule extends UseJacksonForJsonPathR
 
   @Override
   public void before() {
-    this.invoker.invokeInEveryVM(DistributedUseJacksonForJsonPathRule::invokeBefore);
+    this.invoker.invokeInEveryVMAndController(DistributedUseJacksonForJsonPathRule::invokeBefore);
   }
 
   @Override
   public void after() {
-    this.invoker.invokeInEveryVM(DistributedUseJacksonForJsonPathRule::invokeAfter);
+    this.invoker.invokeInEveryVMAndController(DistributedUseJacksonForJsonPathRule::invokeAfter);
   }
 
   private static void invokeBefore() {
diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/RemoteInvoker.java b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/RemoteInvoker.java
index cd9e729..e754cc6 100755
--- a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/RemoteInvoker.java
+++ b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/RemoteInvoker.java
@@ -20,6 +20,7 @@ import java.io.Serializable;
 
 import org.apache.geode.test.dunit.Invoke;
 import org.apache.geode.test.dunit.SerializableRunnableIF;
+import org.apache.geode.test.dunit.VM;
 
 /**
  * Provides remote invocation support to a {@code TestRule}. These methods will invoke a
@@ -29,14 +30,17 @@ public class RemoteInvoker implements Serializable {
 
   private static final long serialVersionUID = -1759722991299584649L;
 
-  // controller VM
-  // dunit VMs
-  // locator VM
-
   /**
    * Invokes in these VMs: controller VM and dunit VMs but not the dunit locator VM
    */
   public void invokeInEveryVM(final SerializableRunnableIF runnable) {
+    Invoke.invokeInEveryVM(runnable);
+  }
+
+  /**
+   * Invokes in these VMs: controller VM and dunit VMs but not the dunit locator VM
+   */
+  public void invokeInEveryVMAndController(final SerializableRunnableIF runnable) {
     try {
       runnable.run();
     } catch (Exception e) {
@@ -52,4 +56,22 @@ public class RemoteInvoker implements Serializable {
     Invoke.invokeInEveryVM(runnable);
     invokeInLocator(runnable);
   }
+
+  /**
+   * Invokes in specified VM
+   */
+  public void invoke(final SerializableRunnableIF runnable, final VM vm) {
+    vm.invoke(runnable);
+  }
+
+  /**
+   * Invokes in local VM (controller VM)
+   */
+  public void invoke(final SerializableRunnableIF runnable) {
+    try {
+      runnable.run();
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
 }
diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/SharedCountersRule.java b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/SharedCountersRule.java
index 5602bcb..16a0ae0 100644
--- a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/SharedCountersRule.java
+++ b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/SharedCountersRule.java
@@ -77,16 +77,16 @@ public class SharedCountersRule extends DistributedExternalResource {
 
   @Override
   protected void before() throws Exception {
-    invoker().invokeInEveryVM(() -> counters = new ConcurrentHashMap<>());
+    invoker().invokeInEveryVMAndController(() -> counters = new ConcurrentHashMap<>());
     for (Serializable id : idsToInitInBefore) {
-      invoker().invokeInEveryVM(() -> initialize(id));
+      invoker().invokeInEveryVMAndController(() -> initialize(id));
     }
     idsToInitInBefore.clear();
   }
 
   @Override
   protected void after() {
-    invoker().invokeInEveryVM(() -> counters = null);
+    invoker().invokeInEveryVMAndController(() -> counters = null);
   }
 
   /**
@@ -95,7 +95,7 @@ public class SharedCountersRule extends DistributedExternalResource {
    */
   public SharedCountersRule initialize(final Serializable id) {
     AtomicInteger value = new AtomicInteger();
-    invoker().invokeInEveryVM(() -> counters.putIfAbsent(id, value));
+    invoker().invokeInEveryVMAndController(() -> counters.putIfAbsent(id, value));
     return this;
   }
 
diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/SharedErrorCollector.java b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/SharedErrorCollector.java
new file mode 100644
index 0000000..93b9f94
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/SharedErrorCollector.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.test.dunit.rules;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.hamcrest.Matcher;
+import org.junit.rules.ErrorCollector;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import org.apache.geode.test.dunit.Host;
+import org.apache.geode.test.dunit.VM;
+import org.apache.geode.test.junit.rules.serializable.SerializableTestRule;
+
+/**
+ * JUnit Rule that provides a shared ErrorCollector in all DistributedTest VMs. In particular, this
+ * is a useful way to add assertions to CacheListener callbacks which are then registered in
+ * multiple DistributedTest VMs.
+ *
+ * <p>
+ * {@code SharedErrorCollector} can be used in DistributedTests as a {@code Rule}:
+ *
+ * <pre>
+ * {@literal @}ClassRule
+ * public static DistributedTestRule distributedTestRule = new DistributedTestRule();
+ *
+ * {@literal @}Rule
+ * public SharedErrorCollector errorCollector = new SharedErrorCollector();
+ *
+ * {@literal @}Test
+ * public void everyVMFailsAssertion() {
+ *   for (VM vm : Host.getHost(0).getAllVMs()) {
+ *     vm.invoke(() -> errorCollector.checkThat("Failure in VM-" + vm.getId(), false, is(true)));
+ *   }
+ * }
+ * </pre>
+ */
+@SuppressWarnings({"serial", "unused"})
+public class SharedErrorCollector implements SerializableTestRule {
+
+  private static volatile ProtectedErrorCollector errorCollector;
+
+  private final RemoteInvoker invoker;
+
+  public SharedErrorCollector() {
+    this(new RemoteInvoker());
+  }
+
+  SharedErrorCollector(final RemoteInvoker invoker) {
+    this.invoker = invoker;
+  }
+
+  @Override
+  public Statement apply(final Statement base, Description description) {
+    return new Statement() {
+      @Override
+      public void evaluate() throws Throwable {
+        before();
+        try {
+          base.evaluate();
+        } finally {
+          after();
+        }
+      }
+    };
+  }
+
+  protected void before() throws Throwable {
+    invoker.invokeInEveryVMAndController(() -> errorCollector = new ProtectedErrorCollector());
+  }
+
+  protected void after() throws Throwable {
+    ProtectedErrorCollector allErrors = errorCollector;
+    try {
+      for (VM vm : Host.getHost(0).getAllVMs()) {
+        List<Throwable> remoteFailures = new ArrayList<>();
+        remoteFailures.addAll(vm.invoke(() -> errorCollector.errors()));
+        for (Throwable t : remoteFailures) {
+          allErrors.addError(t);
+        }
+      }
+      invoker.invokeInEveryVMAndController(() -> errorCollector = null);
+    } finally {
+      allErrors.verify();
+    }
+  }
+
+  /**
+   * @see ErrorCollector#addError(Throwable)
+   */
+  public void addError(Throwable error) {
+    errorCollector.addError(error);
+  }
+
+  /**
+   * @see ErrorCollector#checkThat(Object, Matcher)
+   */
+  public <T> void checkThat(final T value, final Matcher<T> matcher) {
+    errorCollector.checkThat(value, matcher);
+  }
+
+  /**
+   * @see ErrorCollector#checkThat(String, Object, Matcher)
+   */
+  public <T> void checkThat(final String reason, final T value, final Matcher<T> matcher) {
+    errorCollector.checkThat(reason, value, matcher);
+  }
+
+  /**
+   * @see ErrorCollector#checkSucceeds(Callable)
+   */
+  public <T> T checkSucceeds(Callable<T> callable) {
+    return errorCollector.checkSucceeds(callable);
+  }
+
+  /**
+   * Uses reflection to acquire access to the {@code List} of {@code Throwable}s in
+   * {@link ErrorCollector}.
+   */
+  private static class ProtectedErrorCollector extends ErrorCollector {
+
+    protected final List<Throwable> protectedErrors;
+
+    public ProtectedErrorCollector() {
+      super();
+      try {
+        Field superErrors = ErrorCollector.class.getDeclaredField("errors");
+        superErrors.setAccessible(true);
+        this.protectedErrors = (List<Throwable>) superErrors.get(this);
+      } catch (IllegalAccessException | NoSuchFieldException e) {
+        throw new RuntimeException(e);
+      }
+    }
+
+    public List<Throwable> errors() {
+      return protectedErrors;
+    }
+
+    @Override
+    public void verify() throws Throwable {
+      super.verify();
+    }
+  }
+}
diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/tests/SharedCountersDistributedTest.java b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/tests/SharedCountersRuleTest.java
similarity index 98%
rename from geode-core/src/test/java/org/apache/geode/test/dunit/rules/tests/SharedCountersDistributedTest.java
rename to geode-core/src/test/java/org/apache/geode/test/dunit/rules/tests/SharedCountersRuleTest.java
index cf03fc5..7b1290e 100644
--- a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/tests/SharedCountersDistributedTest.java
+++ b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/tests/SharedCountersRuleTest.java
@@ -40,7 +40,7 @@ import org.apache.geode.test.junit.categories.DistributedTest;
 
 @Category(DistributedTest.class)
 @SuppressWarnings("serial")
-public class SharedCountersDistributedTest implements Serializable {
+public class SharedCountersRuleTest implements Serializable {
 
   private static final int TWO_MINUTES_MILLIS = 2 * 60 * 1000;
   private static final String ID1 = "ID1";
diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/tests/SharedErrorCollectorTest.java b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/tests/SharedErrorCollectorTest.java
new file mode 100644
index 0000000..2063025
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/tests/SharedErrorCollectorTest.java
@@ -0,0 +1,353 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.test.dunit.rules.tests;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.util.List;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.ErrorCollector;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+
+import org.apache.geode.test.dunit.Host;
+import org.apache.geode.test.dunit.RMIException;
+import org.apache.geode.test.dunit.VM;
+import org.apache.geode.test.dunit.rules.DistributedTestRule;
+import org.apache.geode.test.dunit.rules.SharedErrorCollector;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.apache.geode.test.junit.runners.TestRunner;
+
+@Category(DistributedTest.class)
+@SuppressWarnings("serial")
+public class SharedErrorCollectorTest {
+
+  static final String MESSAGE = "Failure message";
+
+  @ClassRule
+  public static DistributedTestRule distributedTestRule = new DistributedTestRule();
+
+  @Test
+  public void errorCollectorHasExpectedField() throws Exception {
+    Field errorsField = ErrorCollector.class.getDeclaredField("errors");
+    assertThat(errorsField.getDeclaringClass()).isEqualTo(ErrorCollector.class);
+  }
+
+  @Test
+  public void checkThatFailureInControllerIsReported() throws Exception {
+    Result result = TestRunner.runTest(CheckThatFailsInController.class);
+
+    assertThat(result.wasSuccessful()).isFalse();
+    List<Failure> failures = result.getFailures();
+    assertThat(failures).hasSize(1);
+    assertThat(failures.get(0).getException()).isInstanceOf(AssertionError.class)
+        .hasMessageContaining(MESSAGE);
+  }
+
+  @Test
+  public void addErrorInControllerIsReported() throws Exception {
+    Result result = TestRunner.runTest(AddErrorInController.class);
+
+    assertThat(result.wasSuccessful()).isFalse();
+    List<Failure> failures = result.getFailures();
+    assertThat(failures).hasSize(1);
+    assertThat(failures.get(0).getException()).isInstanceOf(NullPointerException.class)
+        .hasMessage(MESSAGE);
+  }
+
+  @Test
+  public void checkThatFailureInDUnitVMIsReported() throws Exception {
+    Result result = TestRunner.runTest(CheckThatFailsInDUnitVM.class);
+
+    assertThat(result.wasSuccessful()).isFalse();
+    List<Failure> failures = result.getFailures();
+    assertThat(failures).hasSize(1);
+    assertThat(failures.get(0).getException()).isInstanceOf(AssertionError.class)
+        .hasMessageContaining(MESSAGE);
+  }
+
+  @Test
+  public void checkThatFailureInEveryDUnitVMIsReported() throws Exception {
+    Result result = TestRunner.runTest(CheckThatFailsInEveryDUnitVM.class);
+
+    assertThat(result.wasSuccessful()).isFalse();
+    List<Failure> failures = result.getFailures();
+    assertThat(failures).hasSize(4);
+    int i = 0;
+    for (Failure failure : failures) {
+      assertThat(failure.getException()).isInstanceOf(AssertionError.class)
+          .hasMessageContaining(MESSAGE + " in VM-" + i++);
+    }
+  }
+
+  @Test
+  public void checkThatFailureInEveryDUnitVMAndControllerIsReported() throws Exception {
+    Result result = TestRunner.runTest(CheckThatFailsInEveryDUnitVMAndController.class);
+
+    assertThat(result.wasSuccessful()).isFalse();
+    List<Failure> failures = result.getFailures();
+    assertThat(failures).hasSize(5);
+    boolean first = true;
+    int i = 0;
+    for (Failure failure : failures) {
+      if (first) {
+        assertThat(failure.getException()).isInstanceOf(AssertionError.class)
+            .hasMessageContaining(MESSAGE + " in VM-CONTROLLER");
+        first = false;
+      } else {
+        assertThat(failure.getException()).isInstanceOf(AssertionError.class)
+            .hasMessageContaining(MESSAGE + " in VM-" + i++);
+      }
+    }
+  }
+
+  @Test
+  public void checkThatFailureInMethodInDUnitVMIsReported() throws Exception {
+    Result result = TestRunner.runTest(CheckThatFailsInMethodInDUnitVM.class);
+
+    assertThat(result.wasSuccessful()).isFalse();
+    List<Failure> failures = result.getFailures();
+    assertThat(failures).hasSize(1);
+    assertThat(failures.get(0).getException()).isInstanceOf(AssertionError.class)
+        .hasMessageContaining(MESSAGE);
+  }
+
+  @Test
+  public void addErrorInDUnitVMIsReported() throws Exception {
+    Result result = TestRunner.runTest(AddErrorInDUnitVM.class);
+
+    assertThat(result.wasSuccessful()).isFalse();
+    List<Failure> failures = result.getFailures();
+    assertThat(failures).hasSize(1);
+    assertThat(failures.get(0).getException()).isInstanceOf(NullPointerException.class)
+        .hasMessage(MESSAGE);
+  }
+
+  @Test
+  public void addErrorInEveryDUnitVMIsReported() throws Exception {
+    Result result = TestRunner.runTest(AddErrorInEveryDUnitVM.class);
+
+    assertThat(result.wasSuccessful()).isFalse();
+    List<Failure> failures = result.getFailures();
+    assertThat(failures).hasSize(4);
+    int i = 0;
+    for (Failure failure : failures) {
+      assertThat(failure.getException()).isInstanceOf(NullPointerException.class)
+          .hasMessageContaining(MESSAGE + " in VM-" + i++);
+    }
+  }
+
+  @Test
+  public void addErrorInEveryDUnitVMAndControllerIsReported() throws Exception {
+    Result result = TestRunner.runTest(AddErrorInEveryDUnitVMAndController.class);
+
+    assertThat(result.wasSuccessful()).isFalse();
+    List<Failure> failures = result.getFailures();
+    assertThat(failures).hasSize(5);
+    boolean first = true;
+    int i = 0;
+    for (Failure failure : failures) {
+      if (first) {
+        assertThat(failure.getException()).isInstanceOf(NullPointerException.class)
+            .hasMessageContaining(MESSAGE + " in VM-CONTROLLER");
+        first = false;
+      } else {
+        assertThat(failure.getException()).isInstanceOf(NullPointerException.class)
+            .hasMessageContaining(MESSAGE + " in VM-" + i++);
+      }
+    }
+  }
+
+  @Test
+  public void addErrorInMethodInDUnitVMIsReported() throws Exception {
+    Result result = TestRunner.runTest(AddErrorInMethodInDUnitVM.class);
+
+    assertThat(result.wasSuccessful()).isFalse();
+    List<Failure> failures = result.getFailures();
+    assertThat(failures).hasSize(1);
+    assertThat(failures.get(0).getException()).isInstanceOf(NullPointerException.class)
+        .hasMessage(MESSAGE);
+  }
+
+  /**
+   * Used by test {@link #checkThatFailureInControllerIsReported()}
+   */
+  public static class CheckThatFailsInController {
+
+    @Rule
+    public SharedErrorCollector errorCollector = new SharedErrorCollector();
+
+    @Test
+    public void assertionFailsInController() throws Exception {
+      errorCollector.checkThat(MESSAGE, false, is(true));
+    }
+  }
+
+  /**
+   * Used by test {@link #addErrorInControllerIsReported()}
+   */
+  public static class AddErrorInController {
+
+    @Rule
+    public SharedErrorCollector errorCollector = new SharedErrorCollector();
+
+    @Test
+    public void exceptionInController() throws Exception {
+      errorCollector.addError(new NullPointerException(MESSAGE));
+    }
+  }
+
+  /**
+   * Used by test {@link #checkThatFailureInDUnitVMIsReported()}
+   */
+  public static class CheckThatFailsInDUnitVM implements Serializable {
+
+    @Rule
+    public SharedErrorCollector errorCollector = new SharedErrorCollector();
+
+    @Test
+    public void assertionFailsInDUnitVM() throws Exception {
+      Host.getHost(0).getVM(0).invoke(() -> errorCollector.checkThat(MESSAGE, false, is(true)));
+    }
+  }
+
+  /**
+   * Used by test {@link #checkThatFailureInEveryDUnitVMIsReported()}
+   */
+  public static class CheckThatFailsInEveryDUnitVM implements Serializable {
+
+    @Rule
+    public SharedErrorCollector errorCollector = new SharedErrorCollector();
+
+    @Test
+    public void assertionFailsInEveryDUnitVM() throws Exception {
+      for (VM vm : Host.getHost(0).getAllVMs()) {
+        vm.invoke(
+            () -> errorCollector.checkThat(MESSAGE + " in VM-" + vm.getId(), false, is(true)));
+      }
+    }
+  }
+
+  /**
+   * Used by test {@link #checkThatFailureInEveryDUnitVMAndControllerIsReported()}
+   */
+  public static class CheckThatFailsInEveryDUnitVMAndController implements Serializable {
+
+    @Rule
+    public SharedErrorCollector errorCollector = new SharedErrorCollector();
+
+    @Test
+    public void assertionFailsInEveryDUnitVM() throws Exception {
+      errorCollector.checkThat(MESSAGE + " in VM-CONTROLLER", false, is(true));
+      for (VM vm : Host.getHost(0).getAllVMs()) {
+        vm.invoke(
+            () -> errorCollector.checkThat(MESSAGE + " in VM-" + vm.getId(), false, is(true)));
+      }
+    }
+  }
+
+  /**
+   * Used by test {@link #checkThatFailureInMethodInDUnitVMIsReported()}
+   */
+  public static class CheckThatFailsInMethodInDUnitVM implements Serializable {
+
+    @Rule
+    public SharedErrorCollector errorCollector = new SharedErrorCollector();
+
+    @Test
+    public void assertionFailsInDUnitVM() throws Exception {
+      Host.getHost(0).getVM(0).invoke(() -> checkThat());
+    }
+
+    private void checkThat() {
+      errorCollector.checkThat(MESSAGE, false, is(true));
+    }
+  }
+
+  /**
+   * Used by test {@link #addErrorInDUnitVMIsReported()}
+   */
+  public static class AddErrorInDUnitVM implements Serializable {
+
+    @Rule
+    public SharedErrorCollector errorCollector = new SharedErrorCollector();
+
+    @Test
+    public void exceptionInDUnitVM() throws Exception {
+      Host.getHost(0).getVM(0)
+          .invoke(() -> errorCollector.addError(new NullPointerException(MESSAGE)));
+    }
+  }
+
+  /**
+   * Used by test {@link #addErrorInEveryDUnitVMIsReported()}
+   */
+  public static class AddErrorInEveryDUnitVM implements Serializable {
+
+    @Rule
+    public SharedErrorCollector errorCollector = new SharedErrorCollector();
+
+    @Test
+    public void exceptionInEveryDUnitVM() throws Exception {
+      for (VM vm : Host.getHost(0).getAllVMs()) {
+        vm.invoke(() -> errorCollector
+            .addError(new NullPointerException(MESSAGE + " in VM-" + vm.getId())));
+      }
+    }
+  }
+
+  /**
+   * Used by test {@link #addErrorInEveryDUnitVMAndControllerIsReported()}
+   */
+  public static class AddErrorInEveryDUnitVMAndController implements Serializable {
+
+    @Rule
+    public SharedErrorCollector errorCollector = new SharedErrorCollector();
+
+    @Test
+    public void exceptionInEveryDUnitVM() throws Exception {
+      errorCollector.addError(new NullPointerException(MESSAGE + " in VM-CONTROLLER"));
+      for (VM vm : Host.getHost(0).getAllVMs()) {
+        vm.invoke(() -> errorCollector
+            .addError(new NullPointerException(MESSAGE + " in VM-" + vm.getId())));
+      }
+    }
+  }
+
+  /**
+   * Used by test {@link #addErrorInMethodInDUnitVMIsReported()}
+   */
+  public static class AddErrorInMethodInDUnitVM implements Serializable {
+
+    @Rule
+    public SharedErrorCollector errorCollector = new SharedErrorCollector();
+
+    @Test
+    public void exceptionInDUnitVM() throws Exception {
+      Host.getHost(0).getVM(0).invoke(() -> addError());
+    }
+
+    private void addError() {
+      errorCollector.addError(new NullPointerException(MESSAGE));
+    }
+  }
+}
diff --git a/geode-junit/src/main/java/org/apache/geode/test/junit/rules/serializable/SerializableExternalResource.java b/geode-junit/src/main/java/org/apache/geode/test/junit/rules/serializable/SerializableExternalResource.java
index 2bcce04..76f748e 100755
--- a/geode-junit/src/main/java/org/apache/geode/test/junit/rules/serializable/SerializableExternalResource.java
+++ b/geode-junit/src/main/java/org/apache/geode/test/junit/rules/serializable/SerializableExternalResource.java
@@ -14,8 +14,6 @@
  */
 package org.apache.geode.test.junit.rules.serializable;
 
-import java.io.Serializable;
-
 import org.junit.rules.ExternalResource;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;

-- 
To stop receiving notification emails like this one, please contact
['"commits@geode.apache.org" <co...@geode.apache.org>'].