You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ratis.apache.org by el...@apache.org on 2018/06/06 22:35:42 UTC

incubator-ratis git commit: RATIS-247. Add a JUnit RunListener to dump all threads when test timeout. Contributed by Tsz Wo Nicholas Sze.

Repository: incubator-ratis
Updated Branches:
  refs/heads/master cf1e6a3c8 -> 038ef310c


RATIS-247. Add a JUnit RunListener to dump all threads when test timeout. Contributed by Tsz Wo Nicholas Sze.


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

Branch: refs/heads/master
Commit: 038ef310cab0043a630bcf00f490ec334e358b5e
Parents: cf1e6a3
Author: Márton Elek <el...@apache.org>
Authored: Thu Jun 7 00:09:33 2018 +0200
Committer: Márton Elek <el...@apache.org>
Committed: Thu Jun 7 00:09:33 2018 +0200

----------------------------------------------------------------------
 pom.xml                                         |  33 +--
 .../java/org/apache/ratis/util/JavaUtils.java   |   9 +
 .../java/org/apache/ratis/JUnitRunListener.java |  89 ++++++++
 .../apache/ratis/util/TestTimeoutScheduler.java | 210 +++++++++++++++++++
 ratis-hadoop-shaded/pom.xml                     |  12 ++
 ratis-proto-shaded/pom.xml                      |  12 ++
 .../ratis/server/impl/TestRaftServerJmx.java    |   4 +-
 .../apache/ratis/util/TestTimeoutScheduler.java | 210 -------------------
 8 files changed, 341 insertions(+), 238 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/038ef310/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index e6386b5..4c069fd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -149,7 +149,7 @@
     <maven-site-plugin.version>3.5</maven-site-plugin.version>
     <maven-source-plugin.version>2.3</maven-source-plugin.version>
     <maven-stylus-skin.version>1.5</maven-stylus-skin.version>
-    <maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version>
+    <maven-surefire-plugin.version>2.21.0</maven-surefire-plugin.version>
 
     <protobuf-maven-plugin.version>0.5.1</protobuf-maven-plugin.version>
 
@@ -522,6 +522,12 @@
               <exclude>**/Test*$*.java</exclude>
               <exclude>${test.exclude.pattern}</exclude>
             </excludes>
+            <properties>
+              <property>
+                <name>listener</name>
+                <value>org.apache.ratis.JUnitRunListener</value>
+              </property>
+            </properties>
           </configuration>
         </plugin>
         <plugin>
@@ -695,31 +701,6 @@
       </properties>
     </profile>
     <profile>
-      <id>test-patch</id>
-      <activation>
-        <activeByDefault>false</activeByDefault>
-      </activation>
-      <build>
-        <plugins>
-          <plugin>
-            <groupId>org.apache.maven.plugins</groupId>
-            <artifactId>maven-compiler-plugin</artifactId>
-            <configuration>
-              <fork>true</fork>
-              <source>${javac.version}</source>
-              <target>${javac.version}</target>
-              <fork>true</fork>
-              <meminitial>512m</meminitial>
-              <maxmem>2048m</maxmem>
-              <compilerArgs>
-                <arg>-Xlint:all,-options,-path</arg>
-              </compilerArgs>
-            </configuration>
-          </plugin>
-        </plugins>
-      </build>
-    </profile>
-    <profile>
       <id>release</id>
       <build>
         <plugins>

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/038ef310/ratis-common/src/main/java/org/apache/ratis/util/JavaUtils.java
----------------------------------------------------------------------
diff --git a/ratis-common/src/main/java/org/apache/ratis/util/JavaUtils.java b/ratis-common/src/main/java/org/apache/ratis/util/JavaUtils.java
index 926bab8..15cb4f6 100644
--- a/ratis-common/src/main/java/org/apache/ratis/util/JavaUtils.java
+++ b/ratis-common/src/main/java/org/apache/ratis/util/JavaUtils.java
@@ -26,6 +26,9 @@ import org.slf4j.LoggerFactory;
 import java.lang.management.ManagementFactory;
 import java.lang.management.ThreadInfo;
 import java.lang.management.ThreadMXBean;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.List;
 import java.util.Objects;
 import java.util.Timer;
@@ -44,6 +47,12 @@ import java.util.function.Supplier;
 public interface JavaUtils {
   Logger LOG = LoggerFactory.getLogger(JavaUtils.class);
 
+  DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss,SSS");
+
+  static String date() {
+    return DATE_FORMAT.format(new Date());
+  }
+
   /**
    * The same as {@link Class#cast(Object)} except that
    * this method returns null (but not throw {@link ClassCastException})

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/038ef310/ratis-common/src/test/java/org/apache/ratis/JUnitRunListener.java
----------------------------------------------------------------------
diff --git a/ratis-common/src/test/java/org/apache/ratis/JUnitRunListener.java b/ratis-common/src/test/java/org/apache/ratis/JUnitRunListener.java
new file mode 100644
index 0000000..723eeab
--- /dev/null
+++ b/ratis-common/src/test/java/org/apache/ratis/JUnitRunListener.java
@@ -0,0 +1,89 @@
+/**
+ * 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.ratis;
+
+import org.apache.ratis.util.JavaUtils;
+import org.junit.internal.runners.statements.FailOnTimeout;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+import org.junit.runners.model.Statement;
+
+import java.io.PrintStream;
+
+/**
+ * A {@link RunListener} to dump all threads after a test timeout failure.
+ */
+public class JUnitRunListener extends RunListener {
+  private static final Throwable TIMEOUT_EXCEPTION = getTimeoutException();
+  private static final String TIMEOUT_EXCEPTION_PREFIX = getTimeoutExceptionPrefix(TIMEOUT_EXCEPTION);
+
+  private static Throwable getTimeoutException() {
+    final FailOnTimeout f = new FailOnTimeout(new Statement() {
+      @Override
+      public void evaluate() throws InterruptedException {
+        Thread.sleep(1000);
+      }
+    }, 1);
+    try {
+      f.evaluate();
+    } catch(Throwable throwable) {
+      return throwable;
+    }
+    return null;
+  }
+
+  private static String getTimeoutExceptionPrefix(Throwable timeoutException) {
+    final String message = timeoutException.getMessage();
+    return message.substring(0, message.indexOf('1'));
+  }
+
+  private final PrintStream out = System.out;
+
+  @Override
+  public void testFailure(Failure failure) {
+    final Throwable timeoutException = getTimeoutException(failure);
+    if (timeoutException != null) {
+      out.format("%n%s ", JavaUtils.date());
+      timeoutException.printStackTrace(out);
+      JavaUtils.dumpAllThreads(out::println);
+    }
+  }
+
+  private static Throwable getTimeoutException(Failure failure) {
+    if (failure == null) {
+      return null;
+    }
+    final Throwable throwable = failure.getException();
+    if (throwable.getClass() != TIMEOUT_EXCEPTION.getClass()) {
+      return null;
+    }
+    final String message = throwable.getMessage();
+    if (message == null || !message.startsWith(TIMEOUT_EXCEPTION_PREFIX)) {
+      return null;
+    }
+    return throwable;
+  }
+
+  public static void main(String[] args) {
+    final JUnitRunListener listener = new JUnitRunListener();
+    listener.out.println("TIMEOUT_EXCEPTION_PREFIX = " + TIMEOUT_EXCEPTION_PREFIX);
+    TIMEOUT_EXCEPTION.printStackTrace(listener.out);
+
+    listener.testFailure(new Failure(null, new Exception(TIMEOUT_EXCEPTION_PREFIX + "999 milliseconds")));
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/038ef310/ratis-common/src/test/java/org/apache/ratis/util/TestTimeoutScheduler.java
----------------------------------------------------------------------
diff --git a/ratis-common/src/test/java/org/apache/ratis/util/TestTimeoutScheduler.java b/ratis-common/src/test/java/org/apache/ratis/util/TestTimeoutScheduler.java
new file mode 100644
index 0000000..6a63569
--- /dev/null
+++ b/ratis-common/src/test/java/org/apache/ratis/util/TestTimeoutScheduler.java
@@ -0,0 +1,210 @@
+/**
+ * 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.ratis.util;
+
+import org.apache.log4j.Level;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+public class TestTimeoutScheduler {
+  {
+    LogUtils.setLogLevel(TimeoutScheduler.LOG, Level.ALL);
+  }
+
+  static class ErrorHandler implements Consumer<RuntimeException> {
+    private final AtomicBoolean hasError = new AtomicBoolean(false);
+
+    @Override
+    public void accept(RuntimeException e) {
+      hasError.set(true);
+      TimeoutScheduler.LOG.error("Failed", e);
+    }
+
+    void assertNoError() {
+      Assert.assertFalse(hasError.get());
+    }
+  }
+
+  @Test(timeout = 1000)
+  public void testSingleTask() throws Exception {
+    final TimeoutScheduler scheduler = TimeoutScheduler.newInstance(1);
+    final TimeDuration grace = TimeDuration.valueOf(100, TimeUnit.MILLISECONDS);
+    scheduler.setGracePeriod(grace);
+    Assert.assertFalse(scheduler.hasScheduler());
+
+    final ErrorHandler errorHandler = new ErrorHandler();
+
+    final AtomicBoolean fired = new AtomicBoolean(false);
+    scheduler.onTimeout(TimeDuration.valueOf(250, TimeUnit.MILLISECONDS), () -> {
+      Assert.assertFalse(fired.get());
+      fired.set(true);
+    }, errorHandler);
+    Assert.assertTrue(scheduler.hasScheduler());
+
+    Thread.sleep(100);
+    Assert.assertFalse(fired.get());
+    Assert.assertTrue(scheduler.hasScheduler());
+
+    Thread.sleep(100);
+    Assert.assertFalse(fired.get());
+    Assert.assertTrue(scheduler.hasScheduler());
+
+    Thread.sleep(100);
+    Assert.assertTrue(fired.get());
+    Assert.assertTrue(scheduler.hasScheduler());
+
+    Thread.sleep(100);
+    Assert.assertTrue(fired.get());
+    Assert.assertFalse(scheduler.hasScheduler());
+
+    errorHandler.assertNoError();
+  }
+
+  @Test(timeout = 1000)
+  public void testMultipleTasks() throws Exception {
+    final TimeoutScheduler scheduler = TimeoutScheduler.newInstance(1);
+    final TimeDuration grace = TimeDuration.valueOf(100, TimeUnit.MILLISECONDS);
+    scheduler.setGracePeriod(grace);
+    Assert.assertFalse(scheduler.hasScheduler());
+
+    final ErrorHandler errorHandler = new ErrorHandler();
+
+    final AtomicBoolean[] fired = new AtomicBoolean[3];
+    for(int i = 0; i < fired.length; i++) {
+      final AtomicBoolean f = fired[i] = new AtomicBoolean(false);
+      scheduler.onTimeout(TimeDuration.valueOf(100*i + 50, TimeUnit.MILLISECONDS), () -> {
+        Assert.assertFalse(f.get());
+        f.set(true);
+      }, errorHandler);
+      Assert.assertTrue(scheduler.hasScheduler());
+    }
+
+    Thread.sleep(100);
+    Assert.assertTrue(fired[0].get());
+    Assert.assertFalse(fired[1].get());
+    Assert.assertFalse(fired[2].get());
+    Assert.assertTrue(scheduler.hasScheduler());
+
+    Thread.sleep(100);
+    Assert.assertTrue(fired[0].get());
+    Assert.assertTrue(fired[1].get());
+    Assert.assertFalse(fired[2].get());
+    Assert.assertTrue(scheduler.hasScheduler());
+
+    Thread.sleep(100);
+    Assert.assertTrue(fired[0].get());
+    Assert.assertTrue(fired[1].get());
+    Assert.assertTrue(fired[2].get());
+    Assert.assertTrue(scheduler.hasScheduler());
+
+    Thread.sleep(100);
+    Assert.assertTrue(fired[0].get());
+    Assert.assertTrue(fired[1].get());
+    Assert.assertTrue(fired[2].get());
+    Assert.assertFalse(scheduler.hasScheduler());
+
+    errorHandler.assertNoError();
+  }
+
+  @Test(timeout = 1000)
+  public void testExtendingGracePeriod() throws Exception {
+    final TimeoutScheduler scheduler = TimeoutScheduler.newInstance(1);
+    final TimeDuration grace = TimeDuration.valueOf(100, TimeUnit.MILLISECONDS);
+    scheduler.setGracePeriod(grace);
+    Assert.assertFalse(scheduler.hasScheduler());
+
+    final ErrorHandler errorHandler = new ErrorHandler();
+
+    {
+      final AtomicBoolean fired = new AtomicBoolean(false);
+      scheduler.onTimeout(TimeDuration.valueOf(150, TimeUnit.MILLISECONDS), () -> {
+        Assert.assertFalse(fired.get());
+        fired.set(true);
+      }, errorHandler);
+      Assert.assertTrue(scheduler.hasScheduler());
+
+      Thread.sleep(100);
+      Assert.assertFalse(fired.get());
+      Assert.assertTrue(scheduler.hasScheduler());
+
+      Thread.sleep(100);
+      Assert.assertTrue(fired.get());
+      Assert.assertTrue(scheduler.hasScheduler());
+    }
+
+    {
+      // submit another task during grace period
+      final AtomicBoolean fired2 = new AtomicBoolean(false);
+      scheduler.onTimeout(TimeDuration.valueOf(150, TimeUnit.MILLISECONDS), () -> {
+        Assert.assertFalse(fired2.get());
+        fired2.set(true);
+      }, errorHandler);
+
+      Thread.sleep(100);
+      Assert.assertFalse(fired2.get());
+      Assert.assertTrue(scheduler.hasScheduler());
+
+      Thread.sleep(100);
+      Assert.assertTrue(fired2.get());
+      Assert.assertTrue(scheduler.hasScheduler());
+
+      Thread.sleep(100);
+      Assert.assertTrue(fired2.get());
+      Assert.assertFalse(scheduler.hasScheduler());
+    }
+
+    errorHandler.assertNoError();
+  }
+
+  @Test(timeout = 1000)
+  public void testRestartingScheduler() throws Exception {
+    final TimeoutScheduler scheduler = TimeoutScheduler.newInstance(1);
+    final TimeDuration grace = TimeDuration.valueOf(100, TimeUnit.MILLISECONDS);
+    scheduler.setGracePeriod(grace);
+    Assert.assertFalse(scheduler.hasScheduler());
+
+    final ErrorHandler errorHandler = new ErrorHandler();
+
+    for(int i = 0; i < 2; i++) {
+      final AtomicBoolean fired = new AtomicBoolean(false);
+      scheduler.onTimeout(TimeDuration.valueOf(150, TimeUnit.MILLISECONDS), () -> {
+        Assert.assertFalse(fired.get());
+        fired.set(true);
+      }, errorHandler);
+      Assert.assertTrue(scheduler.hasScheduler());
+
+      Thread.sleep(100);
+      Assert.assertFalse(fired.get());
+      Assert.assertTrue(scheduler.hasScheduler());
+
+      Thread.sleep(100);
+      Assert.assertTrue(fired.get());
+      Assert.assertTrue(scheduler.hasScheduler());
+
+      Thread.sleep(100);
+      Assert.assertTrue(fired.get());
+      Assert.assertFalse(scheduler.hasScheduler());
+    }
+
+    errorHandler.assertNoError();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/038ef310/ratis-hadoop-shaded/pom.xml
----------------------------------------------------------------------
diff --git a/ratis-hadoop-shaded/pom.xml b/ratis-hadoop-shaded/pom.xml
index bfaad9c..95342a8 100644
--- a/ratis-hadoop-shaded/pom.xml
+++ b/ratis-hadoop-shaded/pom.xml
@@ -42,6 +42,18 @@
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <compilerArgs>
+            <!-- disable all javac warnings for shaded sources -->
+            <arg>-Xlint:none</arg>
+            <arg>-XDignore.symbol.file</arg>
+          </compilerArgs>
+          <showWarnings>false</showWarnings>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-checkstyle-plugin</artifactId>
         <configuration>
           <excludes>org/apache/ratis/shaded/**/*</excludes>

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/038ef310/ratis-proto-shaded/pom.xml
----------------------------------------------------------------------
diff --git a/ratis-proto-shaded/pom.xml b/ratis-proto-shaded/pom.xml
index e49428b..a24975c 100644
--- a/ratis-proto-shaded/pom.xml
+++ b/ratis-proto-shaded/pom.xml
@@ -53,6 +53,18 @@
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <compilerArgs>
+            <!-- disable all javac warnings for shaded sources -->
+            <arg>-Xlint:none</arg>
+            <arg>-XDignore.symbol.file</arg>
+          </compilerArgs>
+          <showWarnings>false</showWarnings>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-checkstyle-plugin</artifactId>
         <configuration>
           <excludes>org/apache/ratis/shaded/**/*</excludes>

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/038ef310/ratis-server/src/test/java/org/apache/ratis/server/impl/TestRaftServerJmx.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/test/java/org/apache/ratis/server/impl/TestRaftServerJmx.java b/ratis-server/src/test/java/org/apache/ratis/server/impl/TestRaftServerJmx.java
index d300a27..8ec420c 100644
--- a/ratis-server/src/test/java/org/apache/ratis/server/impl/TestRaftServerJmx.java
+++ b/ratis-server/src/test/java/org/apache/ratis/server/impl/TestRaftServerJmx.java
@@ -38,7 +38,7 @@ import java.util.Set;
 import static org.apache.ratis.RaftTestUtil.waitForLeader;
 
 public class TestRaftServerJmx extends BaseTest {
-  @Test
+  @Test(timeout = 10000)
   public void testJmxBeans() throws Exception {
     final int NUM_SERVERS = 3;
     final MiniRaftClusterWithSimulatedRpc cluster
@@ -57,7 +57,7 @@ public class TestRaftServerJmx extends BaseTest {
     cluster.shutdown();
   }
 
-  @Test
+  @Test(timeout = 10000)
   public void testRegister() throws JMException {
     {
       final JmxRegister jmx = new JmxRegister();

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/038ef310/ratis-server/src/test/java/org/apache/ratis/util/TestTimeoutScheduler.java
----------------------------------------------------------------------
diff --git a/ratis-server/src/test/java/org/apache/ratis/util/TestTimeoutScheduler.java b/ratis-server/src/test/java/org/apache/ratis/util/TestTimeoutScheduler.java
deleted file mode 100644
index 6a63569..0000000
--- a/ratis-server/src/test/java/org/apache/ratis/util/TestTimeoutScheduler.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/**
- * 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.ratis.util;
-
-import org.apache.log4j.Level;
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Consumer;
-
-public class TestTimeoutScheduler {
-  {
-    LogUtils.setLogLevel(TimeoutScheduler.LOG, Level.ALL);
-  }
-
-  static class ErrorHandler implements Consumer<RuntimeException> {
-    private final AtomicBoolean hasError = new AtomicBoolean(false);
-
-    @Override
-    public void accept(RuntimeException e) {
-      hasError.set(true);
-      TimeoutScheduler.LOG.error("Failed", e);
-    }
-
-    void assertNoError() {
-      Assert.assertFalse(hasError.get());
-    }
-  }
-
-  @Test(timeout = 1000)
-  public void testSingleTask() throws Exception {
-    final TimeoutScheduler scheduler = TimeoutScheduler.newInstance(1);
-    final TimeDuration grace = TimeDuration.valueOf(100, TimeUnit.MILLISECONDS);
-    scheduler.setGracePeriod(grace);
-    Assert.assertFalse(scheduler.hasScheduler());
-
-    final ErrorHandler errorHandler = new ErrorHandler();
-
-    final AtomicBoolean fired = new AtomicBoolean(false);
-    scheduler.onTimeout(TimeDuration.valueOf(250, TimeUnit.MILLISECONDS), () -> {
-      Assert.assertFalse(fired.get());
-      fired.set(true);
-    }, errorHandler);
-    Assert.assertTrue(scheduler.hasScheduler());
-
-    Thread.sleep(100);
-    Assert.assertFalse(fired.get());
-    Assert.assertTrue(scheduler.hasScheduler());
-
-    Thread.sleep(100);
-    Assert.assertFalse(fired.get());
-    Assert.assertTrue(scheduler.hasScheduler());
-
-    Thread.sleep(100);
-    Assert.assertTrue(fired.get());
-    Assert.assertTrue(scheduler.hasScheduler());
-
-    Thread.sleep(100);
-    Assert.assertTrue(fired.get());
-    Assert.assertFalse(scheduler.hasScheduler());
-
-    errorHandler.assertNoError();
-  }
-
-  @Test(timeout = 1000)
-  public void testMultipleTasks() throws Exception {
-    final TimeoutScheduler scheduler = TimeoutScheduler.newInstance(1);
-    final TimeDuration grace = TimeDuration.valueOf(100, TimeUnit.MILLISECONDS);
-    scheduler.setGracePeriod(grace);
-    Assert.assertFalse(scheduler.hasScheduler());
-
-    final ErrorHandler errorHandler = new ErrorHandler();
-
-    final AtomicBoolean[] fired = new AtomicBoolean[3];
-    for(int i = 0; i < fired.length; i++) {
-      final AtomicBoolean f = fired[i] = new AtomicBoolean(false);
-      scheduler.onTimeout(TimeDuration.valueOf(100*i + 50, TimeUnit.MILLISECONDS), () -> {
-        Assert.assertFalse(f.get());
-        f.set(true);
-      }, errorHandler);
-      Assert.assertTrue(scheduler.hasScheduler());
-    }
-
-    Thread.sleep(100);
-    Assert.assertTrue(fired[0].get());
-    Assert.assertFalse(fired[1].get());
-    Assert.assertFalse(fired[2].get());
-    Assert.assertTrue(scheduler.hasScheduler());
-
-    Thread.sleep(100);
-    Assert.assertTrue(fired[0].get());
-    Assert.assertTrue(fired[1].get());
-    Assert.assertFalse(fired[2].get());
-    Assert.assertTrue(scheduler.hasScheduler());
-
-    Thread.sleep(100);
-    Assert.assertTrue(fired[0].get());
-    Assert.assertTrue(fired[1].get());
-    Assert.assertTrue(fired[2].get());
-    Assert.assertTrue(scheduler.hasScheduler());
-
-    Thread.sleep(100);
-    Assert.assertTrue(fired[0].get());
-    Assert.assertTrue(fired[1].get());
-    Assert.assertTrue(fired[2].get());
-    Assert.assertFalse(scheduler.hasScheduler());
-
-    errorHandler.assertNoError();
-  }
-
-  @Test(timeout = 1000)
-  public void testExtendingGracePeriod() throws Exception {
-    final TimeoutScheduler scheduler = TimeoutScheduler.newInstance(1);
-    final TimeDuration grace = TimeDuration.valueOf(100, TimeUnit.MILLISECONDS);
-    scheduler.setGracePeriod(grace);
-    Assert.assertFalse(scheduler.hasScheduler());
-
-    final ErrorHandler errorHandler = new ErrorHandler();
-
-    {
-      final AtomicBoolean fired = new AtomicBoolean(false);
-      scheduler.onTimeout(TimeDuration.valueOf(150, TimeUnit.MILLISECONDS), () -> {
-        Assert.assertFalse(fired.get());
-        fired.set(true);
-      }, errorHandler);
-      Assert.assertTrue(scheduler.hasScheduler());
-
-      Thread.sleep(100);
-      Assert.assertFalse(fired.get());
-      Assert.assertTrue(scheduler.hasScheduler());
-
-      Thread.sleep(100);
-      Assert.assertTrue(fired.get());
-      Assert.assertTrue(scheduler.hasScheduler());
-    }
-
-    {
-      // submit another task during grace period
-      final AtomicBoolean fired2 = new AtomicBoolean(false);
-      scheduler.onTimeout(TimeDuration.valueOf(150, TimeUnit.MILLISECONDS), () -> {
-        Assert.assertFalse(fired2.get());
-        fired2.set(true);
-      }, errorHandler);
-
-      Thread.sleep(100);
-      Assert.assertFalse(fired2.get());
-      Assert.assertTrue(scheduler.hasScheduler());
-
-      Thread.sleep(100);
-      Assert.assertTrue(fired2.get());
-      Assert.assertTrue(scheduler.hasScheduler());
-
-      Thread.sleep(100);
-      Assert.assertTrue(fired2.get());
-      Assert.assertFalse(scheduler.hasScheduler());
-    }
-
-    errorHandler.assertNoError();
-  }
-
-  @Test(timeout = 1000)
-  public void testRestartingScheduler() throws Exception {
-    final TimeoutScheduler scheduler = TimeoutScheduler.newInstance(1);
-    final TimeDuration grace = TimeDuration.valueOf(100, TimeUnit.MILLISECONDS);
-    scheduler.setGracePeriod(grace);
-    Assert.assertFalse(scheduler.hasScheduler());
-
-    final ErrorHandler errorHandler = new ErrorHandler();
-
-    for(int i = 0; i < 2; i++) {
-      final AtomicBoolean fired = new AtomicBoolean(false);
-      scheduler.onTimeout(TimeDuration.valueOf(150, TimeUnit.MILLISECONDS), () -> {
-        Assert.assertFalse(fired.get());
-        fired.set(true);
-      }, errorHandler);
-      Assert.assertTrue(scheduler.hasScheduler());
-
-      Thread.sleep(100);
-      Assert.assertFalse(fired.get());
-      Assert.assertTrue(scheduler.hasScheduler());
-
-      Thread.sleep(100);
-      Assert.assertTrue(fired.get());
-      Assert.assertTrue(scheduler.hasScheduler());
-
-      Thread.sleep(100);
-      Assert.assertTrue(fired.get());
-      Assert.assertFalse(scheduler.hasScheduler());
-    }
-
-    errorHandler.assertNoError();
-  }
-}