-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Repeat on failure #53 #214
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.xceptance.neodymium.module.statement.repeat; | ||
|
||
import static java.lang.annotation.ElementType.METHOD; | ||
import static java.lang.annotation.ElementType.TYPE; | ||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | ||
|
||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.Target; | ||
|
||
@Retention(RUNTIME) | ||
@Target( | ||
{ | ||
TYPE, METHOD | ||
}) | ||
public @interface RepeatOnFailure | ||
{ | ||
int value() default 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The value currently represents the number of maximum runs. So if we have it set to 1, we'll run only once even if the test fails. The semantic of |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.xceptance.neodymium.module.statement.repeat; | ||
|
||
public class RepeatOnFailureData | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need some JavaDoc here. |
||
{ | ||
private int iterationNumber; | ||
|
||
private int maxNumber; | ||
|
||
public RepeatOnFailureData(int iterationNumber, int maxNumber) | ||
{ | ||
this.iterationNumber = iterationNumber; | ||
this.maxNumber = maxNumber; | ||
} | ||
|
||
public int getIterationNumber() | ||
{ | ||
return iterationNumber; | ||
} | ||
|
||
public int getMaxNumber() | ||
{ | ||
return maxNumber; | ||
} | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should investigate if we can improve the current implementation in terms of reporting. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please investigate if removing the "skipped" and "failed" runs from the reporting can be done easily, otherwise we can add an improvement in a separate task. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package com.xceptance.neodymium.module.statement.repeat; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.WeakHashMap; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.IntStream; | ||
|
||
import org.junit.Assume; | ||
import org.junit.runners.model.FrameworkMethod; | ||
import org.junit.runners.model.Statement; | ||
import org.junit.runners.model.TestClass; | ||
|
||
import com.xceptance.neodymium.module.StatementBuilder; | ||
|
||
public class RepeatStatement extends StatementBuilder | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also JavaDoc |
||
{ | ||
private static final Map<Thread, List<Boolean>> CONTEXTS = Collections.synchronizedMap(new WeakHashMap<>()); | ||
|
||
private Statement next; | ||
|
||
private int iterationIndex; | ||
|
||
private int maxRepetitionNumber; | ||
|
||
public RepeatStatement(Statement next, RepeatOnFailureData data) | ||
{ | ||
this.next = next; | ||
this.iterationIndex = data.getIterationNumber(); | ||
this.maxRepetitionNumber = data.getMaxNumber(); | ||
} | ||
|
||
public RepeatStatement() | ||
{ | ||
} | ||
|
||
private static List<Boolean> getContext() | ||
{ | ||
return CONTEXTS.computeIfAbsent(Thread.currentThread(), key -> { | ||
return new ArrayList<>(); | ||
}); | ||
} | ||
|
||
@Override | ||
public List<Object> createIterationData(TestClass testClass, FrameworkMethod method) throws Throwable | ||
{ | ||
RepeatOnFailure classRepeatOnFailure = testClass.getAnnotation(RepeatOnFailure.class); | ||
List<RepeatOnFailure> methodRepeatOnFailures = getAnnotations(method.getMethod(), RepeatOnFailure.class); | ||
int numberOfRepetitions = 0; | ||
if (classRepeatOnFailure != null) | ||
{ | ||
numberOfRepetitions = classRepeatOnFailure.value(); | ||
|
||
} | ||
if (!methodRepeatOnFailures.isEmpty()) | ||
{ | ||
numberOfRepetitions = methodRepeatOnFailures.get(0).value(); | ||
} | ||
final int maxRepetitionNumber = numberOfRepetitions; | ||
return IntStream.rangeClosed(1, numberOfRepetitions) | ||
.boxed().map(x -> new RepeatOnFailureData(x, maxRepetitionNumber)).collect(Collectors.toList()); | ||
} | ||
|
||
@Override | ||
public StatementBuilder createStatement(Object testClassInstance, Statement next, Object parameter) | ||
{ | ||
return new RepeatStatement(next, (RepeatOnFailureData) parameter); | ||
} | ||
|
||
@Override | ||
public String getTestName(Object data) | ||
{ | ||
RepeatOnFailureData d = (RepeatOnFailureData) data; | ||
return "Run number " + d.getIterationNumber(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
|
||
@Override | ||
public String getCategoryName(Object data) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This leads to a separate category in the junit test report for all tests using this annotation. It would be nicer if only test reruns land in a separate category and tests which ran successfully just go into a default category. |
||
{ | ||
return "Repeat on failure test"; | ||
} | ||
|
||
@Override | ||
public void evaluate() throws Throwable | ||
{ | ||
getContext().add(true); | ||
boolean toExecute = getContext().size() < 2 || | ||
!getContext().get(iterationIndex - 2); | ||
if (iterationIndex == maxRepetitionNumber) | ||
{ | ||
CONTEXTS.remove(Thread.currentThread()); | ||
} | ||
Assume.assumeTrue("Original test succeeded", toExecute); | ||
try | ||
{ | ||
next.evaluate(); | ||
} | ||
catch (Throwable t) | ||
{ | ||
if (iterationIndex != maxRepetitionNumber) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When we interact with a browser, we will need a fresh browser instance here. Otherwise we will reuse the same browser with cookies and session data from the previously failed test. |
||
{ | ||
getContext().set(iterationIndex - 1, false); | ||
} | ||
throw t; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.xceptance.neodymium.testclasses.repeat.classlevel; | ||
|
||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
import org.junit.Assert; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
|
||
import com.xceptance.neodymium.NeodymiumRunner; | ||
import com.xceptance.neodymium.module.statement.browser.multibrowser.Browser; | ||
import com.xceptance.neodymium.module.statement.repeat.RepeatOnFailure; | ||
|
||
@RepeatOnFailure(10) | ||
@Browser("Chrome_headless") | ||
@Browser("Chrome_1500x1000_headless") | ||
@RunWith(NeodymiumRunner.class) | ||
public class ClassRepeatOnFailureBrowserCombinationTest | ||
{ | ||
public static AtomicInteger val = new AtomicInteger(0); | ||
|
||
@Test | ||
public void testVisitingHomepage() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might want to have some more meaningful test case names here. |
||
{ | ||
int i = val.getAndIncrement(); | ||
if (val.get() == 6) | ||
{ | ||
val.set(0); | ||
} | ||
Assert.assertFalse("Produce test failure number " + i, i < 5); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.xceptance.neodymium.testclasses.repeat.classlevel; | ||
|
||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
import org.junit.Assert; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
|
||
import com.xceptance.neodymium.NeodymiumRunner; | ||
import com.xceptance.neodymium.module.statement.repeat.RepeatOnFailure; | ||
|
||
@RepeatOnFailure(10) | ||
@RunWith(NeodymiumRunner.class) | ||
public class ClassRepeatOnFailureTest | ||
{ | ||
public static AtomicInteger val = new AtomicInteger(0); | ||
|
||
@Test | ||
public void testVisitingHomepage() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might want to have some more meaningful test case names here. |
||
{ | ||
int i = val.getAndIncrement(); | ||
if (val.get() == 6) | ||
{ | ||
val.set(0); | ||
} | ||
Assert.assertFalse("Produce test failure number " + i, i < 5); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.xceptance.neodymium.testclasses.repeat.classlevel; | ||
|
||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
import org.junit.Assert; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
|
||
import com.xceptance.neodymium.NeodymiumRunner; | ||
import com.xceptance.neodymium.module.statement.repeat.RepeatOnFailure; | ||
|
||
@RepeatOnFailure(10) | ||
@RunWith(NeodymiumRunner.class) | ||
public class ClassRepeatOnFailureTestdataCombinationTest | ||
{ | ||
public static AtomicInteger val = new AtomicInteger(0); | ||
|
||
@Test | ||
public void testVisitingHomepage() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might want to have some more meaningful test case names here. |
||
{ | ||
int i = val.getAndIncrement(); | ||
if (val.get() == 6) | ||
{ | ||
val.set(0); | ||
} | ||
Assert.assertFalse("Produce test failure number " + i, i < 5); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.xceptance.neodymium.testclasses.repeat.methodlevel; | ||
|
||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
import org.junit.Assert; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
|
||
import com.xceptance.neodymium.NeodymiumRunner; | ||
import com.xceptance.neodymium.module.statement.browser.multibrowser.Browser; | ||
import com.xceptance.neodymium.module.statement.repeat.RepeatOnFailure; | ||
|
||
@Browser("Chrome_headless") | ||
@Browser("Chrome_1500x1000_headless") | ||
@RunWith(NeodymiumRunner.class) | ||
public class MethodRepeatOnFailureBrowserCombinationTest | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please test if for every repeat the browser is actually a new one. |
||
{ | ||
public static AtomicInteger val = new AtomicInteger(0); | ||
|
||
@Test | ||
@RepeatOnFailure(10) | ||
public void testVisitingHomepage() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might want to have some more meaningful test case names here. |
||
{ | ||
int i = val.getAndIncrement(); | ||
if (val.get() == 6) | ||
{ | ||
val.set(0); | ||
} | ||
Assert.assertFalse("Produce test failure number " + i, i < 5); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package com.xceptance.neodymium.testclasses.repeat.methodlevel; | ||
|
||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
import org.junit.Assert; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
|
||
import com.xceptance.neodymium.NeodymiumRunner; | ||
import com.xceptance.neodymium.module.statement.repeat.RepeatOnFailure; | ||
|
||
@RepeatOnFailure(3) | ||
@RunWith(NeodymiumRunner.class) | ||
public class MethodRepeatOnFailureTest | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be good to check if this works for several methods with different repeat counts. |
||
{ | ||
public static AtomicInteger val = new AtomicInteger(0); | ||
|
||
@Test | ||
@RepeatOnFailure(10) | ||
public void testVisitingHomepage() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might want to have some more meaningful test case names here. |
||
{ | ||
int i = val.getAndIncrement(); | ||
if (val.get() == 6) | ||
{ | ||
val.set(0); | ||
} | ||
Assert.assertFalse("Produce test failure number " + i, i < 5); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.xceptance.neodymium.testclasses.repeat.methodlevel; | ||
|
||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
import org.junit.Assert; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
|
||
import com.xceptance.neodymium.NeodymiumRunner; | ||
import com.xceptance.neodymium.module.statement.repeat.RepeatOnFailure; | ||
|
||
@RunWith(NeodymiumRunner.class) | ||
public class MethodRepeatOnFailureTestdataCombinationTest | ||
{ | ||
public static AtomicInteger val = new AtomicInteger(0); | ||
|
||
@Test | ||
@RepeatOnFailure(10) | ||
public void testVisitingHomepage() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might want to have some more meaningful test case names here. |
||
{ | ||
int i = val.getAndIncrement(); | ||
if (val.get() == 6) | ||
{ | ||
val.set(0); | ||
} | ||
Assert.assertFalse("Produce test failure number " + i, i < 5); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.xceptance.neodymium.testclasses.repeat.mix; | ||
|
||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
import org.junit.Assert; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
|
||
import com.xceptance.neodymium.NeodymiumRunner; | ||
import com.xceptance.neodymium.module.statement.repeat.RepeatOnFailure; | ||
|
||
@RunWith(NeodymiumRunner.class) | ||
public class OverridingRepeatOnFailureTest | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test is not testing any override. |
||
{ | ||
public static AtomicInteger val = new AtomicInteger(0); | ||
|
||
@Test | ||
@RepeatOnFailure(10) | ||
public void testVisitingHomepage() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might want to have some more meaningful test case names here. |
||
{ | ||
int i = val.getAndIncrement(); | ||
if (val.get() == 6) | ||
{ | ||
val.set(0); | ||
} | ||
Assert.assertFalse("Produce test failure number " + i, i < 5); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need JavaDoc here.