-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
163 additions
and
0 deletions.
There are no files selected for viewing
63 changes: 63 additions & 0 deletions
63
src/main/java/net/openhft/chronicle/testframework/FlakyTestRunner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package net.openhft.chronicle.testframework; | ||
|
||
import net.openhft.chronicle.testframework.internal.VanillaFlakyTestRunner; | ||
import org.jetbrains.annotations.NotNull; | ||
|
||
public final class FlakyTestRunner { | ||
|
||
// Suppresses default constructor, ensuring non-instantiability. | ||
private FlakyTestRunner() { | ||
} | ||
|
||
/** | ||
* Runs the provided {@code action} trying at most 2 times. | ||
* | ||
* @param action non-null action to perform | ||
* @param <X> exception type | ||
* @throws X if an underlying exception is thrown despite retrying the specified number of times | ||
*/ | ||
public static <X extends Throwable> void run(@NotNull final RunnableThrows<X> action) throws X { | ||
new VanillaFlakyTestRunner().run(action, 2); | ||
} | ||
|
||
/** | ||
* Runs the provided {@code action} trying at most 2 times if the provided {@code flakyOnTHisArch} is true, | ||
* otherwise just runs the provided {@code action} once. | ||
* | ||
* @param flakyOnThisArch indicating if the provided action is | ||
* @param action non-null action to perform | ||
* @param <X> exception type | ||
* @throws X if an underlying exception is thrown despite retrying the specified number of times | ||
*/ | ||
public static <X extends Throwable> void run(final boolean flakyOnThisArch, | ||
@NotNull final RunnableThrows<X> action) throws X { | ||
new VanillaFlakyTestRunner().run(action, flakyOnThisArch ? 2 : 1); | ||
} | ||
|
||
/** | ||
* Runs the provided {@code action} trying at most the provided {@code maxIteration} times if the | ||
* provided {@code flakyOnTHisArch} is true, otherwise just runs the provided {@code action} once. | ||
* | ||
* @param flakyOnThisArch indicating if the provided action is | ||
* @param action non-null action to perform | ||
* @param <X> exception type | ||
* @throws X if an underlying exception is thrown despite retrying the specified number of times | ||
*/ | ||
public static <X extends Throwable> void run(final boolean flakyOnThisArch, | ||
@NotNull final RunnableThrows<X> action, | ||
final int maxIterations) throws X { | ||
new VanillaFlakyTestRunner().run(action, flakyOnThisArch ? maxIterations : 1); | ||
} | ||
|
||
@FunctionalInterface | ||
public interface RunnableThrows<T extends Throwable> { | ||
|
||
/** | ||
* Performs an action. | ||
* | ||
* @throws T if an underlying exception is thrown | ||
*/ | ||
void run() throws T; | ||
} | ||
|
||
} |
46 changes: 46 additions & 0 deletions
46
src/main/java/net/openhft/chronicle/testframework/internal/VanillaFlakyTestRunner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package net.openhft.chronicle.testframework.internal; | ||
|
||
import net.openhft.chronicle.testframework.FlakyTestRunner; | ||
|
||
import java.util.concurrent.atomic.AtomicBoolean; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
|
||
public final class VanillaFlakyTestRunner { | ||
|
||
private final AtomicBoolean inRun = new AtomicBoolean(); | ||
|
||
public <X extends Throwable> void run(final FlakyTestRunner.RunnableThrows<X> action, | ||
final int maxIterations) throws X { | ||
requireNonNull(action); | ||
if (maxIterations <= 0) { | ||
throw new IllegalArgumentException("maxIterations is non positive " + maxIterations); | ||
} | ||
if (!inRun.compareAndSet(false, true)) | ||
throw new AssertionError("Can't run nested"); | ||
try { | ||
for (int i = 0; i < maxIterations; i++) { | ||
try { | ||
action.run(); | ||
if (i > 0) { | ||
System.out.println("Flaky test threw an error " + i + " run(s), but passed on run " + (i + 1)); | ||
} | ||
break; | ||
} catch (Throwable t) { | ||
if (i == (maxIterations - 1)) { | ||
throw t; | ||
} | ||
System.err.println("Rerunning failing test run " + (i + 2)); | ||
System.gc(); | ||
try { | ||
Thread.sleep(500); | ||
} catch (InterruptedException ignore) { | ||
// do nothing | ||
} | ||
} | ||
} | ||
} finally { | ||
inRun.set(false); | ||
} | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
src/test/java/net/openhft/chronicle/testframework/internal/VanillaFlakyTestRunnerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package net.openhft.chronicle.testframework.internal; | ||
|
||
import net.openhft.chronicle.testframework.FlakyTestRunner.RunnableThrows; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
|
||
class VanillaFlakyTestRunnerTest { | ||
|
||
@Test | ||
void runZero() { | ||
RunnableThrows<IllegalStateException> action = new MyAction(0); | ||
assertThrows(IllegalArgumentException.class, () -> | ||
new VanillaFlakyTestRunner().run(action, 0) | ||
); | ||
} | ||
|
||
@Test | ||
void run() { | ||
RunnableThrows<IllegalStateException> action = new MyAction(0); | ||
new VanillaFlakyTestRunner().run(action, 1); | ||
} | ||
|
||
@Test | ||
void runTry2() { | ||
RunnableThrows<IllegalStateException> action = new MyAction(1); | ||
new VanillaFlakyTestRunner().run(action, 2); | ||
} | ||
|
||
@Test | ||
void runTry3() { | ||
RunnableThrows<IllegalStateException> action = new MyAction(2); | ||
assertThrows(IllegalStateException.class, () -> | ||
new VanillaFlakyTestRunner().run(action, 2) | ||
); | ||
} | ||
|
||
private static final class MyAction implements RunnableThrows<IllegalStateException> { | ||
|
||
private int countDown; | ||
|
||
public MyAction(int countDown) { | ||
this.countDown = countDown; | ||
} | ||
|
||
@Override | ||
public void run() throws IllegalStateException { | ||
if (countDown <= 0) | ||
return; | ||
throw new IllegalStateException("" + countDown--); | ||
} | ||
} | ||
|
||
} |