From af73768d1433e3c8fdf65b0a262740a4631cd94e Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 12 Mar 2024 10:53:55 +0100 Subject: [PATCH] Declare HTTPDownloaderTest as flaky (#9339) `HTTPDownloaderTest` failed recently transiently. Let's declare it as flaky. --- build.sbt | 9 ++- .../downloader/http/HttpDownloaderTest.java | 4 + .../java/org/enso/testkit/RetryTestRule.java | 74 +++++++++++++++++++ 3 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 lib/scala/testkit/src/main/java/org/enso/testkit/RetryTestRule.java diff --git a/build.sbt b/build.sbt index e6df7771fc..cd67f6c731 100644 --- a/build.sbt +++ b/build.sbt @@ -1092,9 +1092,11 @@ lazy val testkit = project .settings( frgaalJavaCompilerSetting, libraryDependencies ++= Seq( - "org.apache.commons" % "commons-lang3" % commonsLangVersion, - "commons-io" % "commons-io" % commonsIoVersion, - "org.scalatest" %% "scalatest" % scalatestVersion + "org.apache.commons" % "commons-lang3" % commonsLangVersion, + "commons-io" % "commons-io" % commonsIoVersion, + "org.scalatest" %% "scalatest" % scalatestVersion, + "junit" % "junit" % junitVersion, + "com.github.sbt" % "junit-interface" % junitIfVersion ) ) @@ -2551,6 +2553,7 @@ lazy val downloader = (project in file("lib/scala/downloader")) ) .dependsOn(cli) .dependsOn(`http-test-helper`) + .dependsOn(testkit % Test) lazy val `edition-updater` = project .in(file("lib/scala/edition-updater")) diff --git a/lib/scala/downloader/src/test/java/org/enso/downloader/http/HttpDownloaderTest.java b/lib/scala/downloader/src/test/java/org/enso/downloader/http/HttpDownloaderTest.java index 37d40d3643..793f42ff0d 100644 --- a/lib/scala/downloader/src/test/java/org/enso/downloader/http/HttpDownloaderTest.java +++ b/lib/scala/downloader/src/test/java/org/enso/downloader/http/HttpDownloaderTest.java @@ -27,8 +27,10 @@ import org.enso.cli.task.TaskProgress; import org.enso.shttp.HTTPTestHelperServer; import org.enso.shttp.HybridHTTPServer; import org.enso.shttp.SimpleHttpHandler; +import org.enso.testkit.RetryTestRule; import org.junit.AfterClass; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; import scala.Option; import scala.util.Try; @@ -41,6 +43,8 @@ public class HttpDownloaderTest { private static HybridHTTPServer server; private static ExecutorService serverExecutor; + @Rule public RetryTestRule retry = new RetryTestRule(3); + @BeforeClass public static void initServer() throws URISyntaxException, IOException { serverExecutor = Executors.newSingleThreadExecutor(); diff --git a/lib/scala/testkit/src/main/java/org/enso/testkit/RetryTestRule.java b/lib/scala/testkit/src/main/java/org/enso/testkit/RetryTestRule.java new file mode 100644 index 0000000000..cbe68427d4 --- /dev/null +++ b/lib/scala/testkit/src/main/java/org/enso/testkit/RetryTestRule.java @@ -0,0 +1,74 @@ +package org.enso.testkit; + +import java.util.ArrayList; +import java.util.List; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * Flaky test specification for JUnit. + * + *

Inspired by this SO answer. + * + *

Use it like this: + * + *

+ * public class MyTest {
+ *   @Rule
+ *   public RetryTestRule retry = new RetryTestRule(3);
+ *   @Test
+ *   public void myTest() {...}
+ * }
+ * 
+ */ +public class RetryTestRule implements TestRule { + private int retryCount; + + public RetryTestRule(int retryCount) { + this.retryCount = retryCount; + } + + @Override + public Statement apply(Statement base, Description description) { + return statement(base, description); + } + + private Statement statement(final Statement base, final Description description) { + return new Statement() { + @Override + public void evaluate() { + List caughtThrowables = new ArrayList<>(); + + for (int i = 0; i < retryCount; i++) { + try { + base.evaluate(); + return; + } catch (Throwable t) { + caughtThrowables.add(t); + System.err.println( + description.getClassName() + + "." + + description.getDisplayName() + + ": run " + + (i + 1) + + " failed"); + } + } + var err = + new AssertionError( + description.getDisplayName() + ": giving up after " + retryCount + " failures"); + for (var t : caughtThrowables) { + Throwable n = err; + while (n.getCause() != null) { + n = n.getCause(); + } + n.initCause(t); + } + System.err.println( + description.getDisplayName() + ": giving up after " + retryCount + " failures"); + throw err; + } + }; + } +}