From ccd2949573203d8cb59c022c1a2acb472c02cfbf Mon Sep 17 00:00:00 2001 From: Dmytro Serdiuk Date: Fri, 8 Feb 2019 21:14:26 +0200 Subject: [PATCH] Support sequential execution of several tests suites Introduced SequentialExecution class gives ability to run several suites sequentially and reports aggregated report for them. This change gives more freedom in defining tests suites and the combinations of the tests setups. #134 --- docs/md/entry-points.md | 16 +++++ .../sunshine/core/CompositeStatus.java | 70 ++++++++++++++++++ .../org/tatools/sunshine/core/Kernel.java | 29 +++++++- .../sunshine/core/SequentialExecution.java | 44 ++++++++++++ .../sunshine/core/CompositeStatusTest.java | 72 +++++++++++++++++++ .../core/SequentialExecutionTest.java | 33 +++++++++ 6 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 sunshine-core/src/main/java/org/tatools/sunshine/core/CompositeStatus.java create mode 100644 sunshine-core/src/main/java/org/tatools/sunshine/core/SequentialExecution.java create mode 100644 sunshine-core/src/test/java/org/tatools/sunshine/core/CompositeStatusTest.java create mode 100644 sunshine-core/src/test/java/org/tatools/sunshine/core/SequentialExecutionTest.java diff --git a/docs/md/entry-points.md b/docs/md/entry-points.md index 9b8b098..c5a6b5a 100644 --- a/docs/md/entry-points.md +++ b/docs/md/entry-points.md @@ -115,3 +115,19 @@ new Sun( ) ).shine(); ``` + +### Support multiple `Kernel`s +There is a suite of tests which needs to be checked with several different listeners. For instance, +if a listener is not configured, the tests will use some cache, otherwise, no cache before each test. +And we have to run the suite with and without the listener at the same time. + +There is a specific `Kernel` called `SequentialExecution` which allows execution of described use case: +```java +final Kernel suite = new TestNGKernel(....); +new Sun( + new SequentialExecution<>( + suite, + suite.with(new CleanCacheListener()) + ) +).shine(); +``` diff --git a/sunshine-core/src/main/java/org/tatools/sunshine/core/CompositeStatus.java b/sunshine-core/src/main/java/org/tatools/sunshine/core/CompositeStatus.java new file mode 100644 index 0000000..04ad1dd --- /dev/null +++ b/sunshine-core/src/main/java/org/tatools/sunshine/core/CompositeStatus.java @@ -0,0 +1,70 @@ +package org.tatools.sunshine.core; + +import java.util.List; + +/** + * The class represents several {@link Status}es as a single instance. + * + * @author Dmytro Serdiuk (dmytro.serdiuk@gmail.com) + * @version $Id$ + */ +public final class CompositeStatus implements Status { + + private final List sources; + + /** + * Constructs a new instance. + * + * @param statuses available statuses + */ + public CompositeStatus(List statuses) { + this.sources = statuses; + } + + /** + * Returns the exit code of an execution provided by xunit tests runner. + *

+ * The code will be a maximum value from all available codes expect zero ones. If there is no + * maximal value, 0 is given (means all are passed), otherwise, a non-zero number (if there is at least one failure). + * + * @return a calculated exit code + */ + @Override + public short code() { + return this.sources.stream() + .map(Status::code) + .filter(code -> code != 0) + .max(Short::compareTo) + .orElse((short) 0); + } + + /** + * Returns a sum of all tests of all statuses. + * + * @return a count of total tests + */ + @Override + public int runCount() { + return this.sources.stream().mapToInt(Status::runCount).sum(); + } + + /** + * Returns a sum of failed tests of all statuses. + * + * @return a count of failed tests + */ + @Override + public int failureCount() { + return this.sources.stream().mapToInt(Status::failureCount).sum(); + } + + /** + * Returns a sum of ignored tests of all statuses. + * + * @return a count of ignored tests + */ + @Override + public int ignoreCount() { + return this.sources.stream().mapToInt(Status::ignoreCount).sum(); + } +} diff --git a/sunshine-core/src/main/java/org/tatools/sunshine/core/Kernel.java b/sunshine-core/src/main/java/org/tatools/sunshine/core/Kernel.java index d0bffe4..ead1d98 100644 --- a/sunshine-core/src/main/java/org/tatools/sunshine/core/Kernel.java +++ b/sunshine-core/src/main/java/org/tatools/sunshine/core/Kernel.java @@ -1,5 +1,9 @@ package org.tatools.sunshine.core; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * The {@link Kernel} interface declares a way to implement different xnit test runners. * @@ -28,9 +32,26 @@ public interface Kernel { final class Fake implements Kernel { private final Status result; + private final List listeners; + /** + * Constructs a new object. All listeners are stored internally. + * + * @param status a status of the execution + */ public Fake(Status status) { + this(status, new ArrayList<>()); + } + + /** + * Constructs a new object. + * + * @param status a status of the execution + * @param availableListeners a list which will store all listeners + */ + public Fake(Status status, List availableListeners) { this.result = status; + this.listeners = availableListeners; } @Override @@ -39,8 +60,12 @@ public Status status() { } @Override - public Kernel with(Object[] objects) { - return null; + public Kernel with(Object... objects) { + this.listeners.addAll(Arrays.asList(objects)); + return new Fake( + this.result, + this.listeners + ); } } } diff --git a/sunshine-core/src/main/java/org/tatools/sunshine/core/SequentialExecution.java b/sunshine-core/src/main/java/org/tatools/sunshine/core/SequentialExecution.java new file mode 100644 index 0000000..9ec2cca --- /dev/null +++ b/sunshine-core/src/main/java/org/tatools/sunshine/core/SequentialExecution.java @@ -0,0 +1,44 @@ +package org.tatools.sunshine.core; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * The class encapsulates several {@link Kernel}s and runs them sequentially. + * + * @author Dmytro Serdiuk (dmytro.serdiuk@gmail.com) + * @version $Id$ + */ +public class SequentialExecution implements Kernel { + + private final List> elements; + + @SafeVarargs + public SequentialExecution(Kernel... kernels) { + this(Arrays.asList(kernels)); + } + + public SequentialExecution(List> kernels) { + this.elements = kernels; + } + + @Override + public Status status() throws KernelException { + final List results = new ArrayList<>(); + for (Kernel kernel : this.elements) { + results.add(kernel.status()); + } + return new CompositeStatus(results); + } + + @Override + public Kernel with(Listener... listeners) { + return new SequentialExecution<>( + this.elements.stream() + .map(listenerKernel -> listenerKernel.with(listeners)) + .collect(Collectors.toList()) + ); + } +} diff --git a/sunshine-core/src/test/java/org/tatools/sunshine/core/CompositeStatusTest.java b/sunshine-core/src/test/java/org/tatools/sunshine/core/CompositeStatusTest.java new file mode 100644 index 0000000..46e2f54 --- /dev/null +++ b/sunshine-core/src/test/java/org/tatools/sunshine/core/CompositeStatusTest.java @@ -0,0 +1,72 @@ +package org.tatools.sunshine.core; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; + +import java.util.Arrays; + +/** + * @author Dmytro Serdiuk (dmytro.serdiuk@gmail.com) + * @version $Id$ + */ +public class CompositeStatusTest { + + @Test + public void testZeroCode() { + MatcherAssert.assertThat( + new CompositeStatus(Arrays.asList(new Status.Fake(), new Status.Fake())).code(), + Matchers.is((short) 0) + ); + } + + @Test + public void testNonZeroPositiveCode() { + MatcherAssert.assertThat( + new CompositeStatus(Arrays.asList( + new Status.Fake((short) 3, 0, 1, 1), new Status.Fake((short) 2, 0, 1, 1) + )).code(), + Matchers.is((short) 3) + ); + } + + @Test + public void testNonZeroNegativeCode() { + MatcherAssert.assertThat( + new CompositeStatus(Arrays.asList( + new Status.Fake((short) -3, 0, 1, 1), new Status.Fake((short) 0, 0, 1, 1) + )).code(), + Matchers.is((short) -3) + ); + } + + @Test + public void testSumOfRunCount() { + MatcherAssert.assertThat( + new CompositeStatus(Arrays.asList( + new Status.Fake((short) 3, 1, 2, 3), new Status.Fake((short) 2, 4, 5, 6) + )).runCount(), + Matchers.is(5) + ); + } + + @Test + public void testSumOfFailureCount() { + MatcherAssert.assertThat( + new CompositeStatus(Arrays.asList( + new Status.Fake((short) 3, 1, 2, 3), new Status.Fake((short) 2, 4, 5, 6) + )).failureCount(), + Matchers.is(7) + ); + } + + @Test + public void testSumOfIgnoreCount() { + MatcherAssert.assertThat( + new CompositeStatus(Arrays.asList( + new Status.Fake((short) 3, 1, 2, 3), new Status.Fake((short) 2, 4, 5, 6) + )).ignoreCount(), + Matchers.is(9) + ); + } +} \ No newline at end of file diff --git a/sunshine-core/src/test/java/org/tatools/sunshine/core/SequentialExecutionTest.java b/sunshine-core/src/test/java/org/tatools/sunshine/core/SequentialExecutionTest.java new file mode 100644 index 0000000..93eee6a --- /dev/null +++ b/sunshine-core/src/test/java/org/tatools/sunshine/core/SequentialExecutionTest.java @@ -0,0 +1,33 @@ +package org.tatools.sunshine.core; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Dmytro Serdiuk (dmytro.serdiuk@gmail.com) + * @version $Id$ + */ +public class SequentialExecutionTest { + + @Test + public void testStatus() throws KernelException { + MatcherAssert.assertThat( + new SequentialExecution( + new Kernel.Fake(new Status.Fake()), + new Kernel.Fake(new Status.Fake((short) 1, 2, 1, 1)) + ).status().code(), + Matchers.is((short) 1) + ); + } + + @Test + public void testWithListeners() { + final List listeners = new ArrayList<>(); + new SequentialExecution(new Kernel.Fake(new Status.Fake(), listeners)).with(new Object()); + MatcherAssert.assertThat(listeners, Matchers.hasSize(1)); + } +} \ No newline at end of file