forceOnAll(ThrowingConsumer super T, ? ext
/**
* Create a stream which will yield the exceptions (if any) from invoking an {@link ThrowingConsumer action} on
- * several {@code instances}. Consuming the stream will ensure that all instances will have
- * the action invoked on them, and any exceptions happening will be available through the returned stream.
+ * several {@code instances}. This also includes exceptions thrown from traversing the given {@link Stream}
+ * of instances, i.e. should resolving an element from the {@code Stream} cause an exception, it will be caught and
+ * included in the returned {@code Stream}.
+ *
+ * Consuming the returned stream will ensure that all traversed instances will have
+ * the action attempted on them, and any exceptions happening will be available through the returned stream.
*
* @param action the action to execute for each provided instance
* @param instances the instances to act on with the provided {@code action}.
@@ -246,15 +252,49 @@ public static Stream forceOnAll(ThrowingConsumer super T, ? ext
* @return the Stream with exceptions, if any
*/
public static Stream forceOnAll(ThrowingConsumer super T, ? extends Exception> action, Stream instances) {
- return instances.filter(Objects::nonNull).flatMap(instance -> {
+ return StreamSupport.stream(
+ new FlatMapToExceptionSpliterator<>(action, instances.filter(Objects::nonNull).spliterator()),
+ instances.isParallel());
+ }
+
+ private static final class FlatMapToExceptionSpliterator implements Spliterator {
+
+ private final ThrowingConsumer super W, ? extends Exception> action;
+ private final Spliterator wrappedSpliterator;
+ private final int characteristics;
+
+ FlatMapToExceptionSpliterator(ThrowingConsumer super W, ? extends Exception> action, Spliterator wrappedSpliterator) {
+ this.action = action;
+ this.wrappedSpliterator = wrappedSpliterator;
+ this.characteristics = wrappedSpliterator.characteristics() & ~(SIZED | SUBSIZED | SORTED);
+ }
+
+ @Override
+ public boolean tryAdvance(Consumer super Exception> exceptionConsumer) {
try {
- action.accept(instance);
- } catch (Exception exception) {
- return Stream.of(exception);
+ return wrappedSpliterator.tryAdvance(action.ifException(exceptionConsumer::accept));
+ } catch (Exception e) {
+ exceptionConsumer.accept(e);
+ return true;
}
- return Stream.empty();
- });
- }
+ }
+
+ @Override
+ public Spliterator trySplit() {
+ Spliterator triedSplit = wrappedSpliterator.trySplit();
+ return triedSplit != null ? new FlatMapToExceptionSpliterator<>(action, triedSplit) : null;
+ }
+
+ @Override
+ public long estimateSize() {
+ return Long.MAX_VALUE;
+ }
+
+ @Override
+ public int characteristics() {
+ return characteristics;
+ }
+ };
/**
diff --git a/src/main/java/no/digipost/DiggStreams.java b/src/main/java/no/digipost/DiggStreams.java
index 5cac7b9..7ac88d9 100644
--- a/src/main/java/no/digipost/DiggStreams.java
+++ b/src/main/java/no/digipost/DiggStreams.java
@@ -19,6 +19,7 @@
import no.digipost.function.ObjLongFunction;
import java.util.Collection;
+import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.atomic.AtomicInteger;
@@ -31,6 +32,18 @@
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
+import static java.lang.Integer.toBinaryString;
+import static java.util.Spliterator.CONCURRENT;
+import static java.util.Spliterator.DISTINCT;
+import static java.util.Spliterator.IMMUTABLE;
+import static java.util.Spliterator.NONNULL;
+import static java.util.Spliterator.ORDERED;
+import static java.util.Spliterator.SIZED;
+import static java.util.Spliterator.SORTED;
+import static java.util.Spliterator.SUBSIZED;
+import static java.util.stream.Collectors.joining;
+import static no.digipost.DiggBase.friendlyName;
+
/**
* Utilities for working with {@link Stream}s.
*/
@@ -197,6 +210,35 @@ public boolean tryAdvance(Consumer super P> action) {
}
+ /**
+ * Get a description of {@link Spliterator#characteristics() characteristics} of a
+ * {@code Spliterator}. The returned text is is solely intended for debugging and
+ * logging purposes, and contents and format may change at any time.
+ *
+ * @param spliterator the Spliterator
+ * @return the description
+ */
+ public static String describeCharacteristics(Spliterator> spliterator) {
+ int value = spliterator.characteristics();
+ if (value == 0) {
+ return friendlyName(spliterator.getClass()) + " with no enabled characteristics";
+ } else {
+ String enabledCharacteristics = Stream.of(
+ spliterator.hasCharacteristics(SIZED) ? "sized" : null,
+ spliterator.hasCharacteristics(SUBSIZED) ? "subsized" : null,
+ spliterator.hasCharacteristics(DISTINCT) ? "distinct" : null,
+ spliterator.hasCharacteristics(NONNULL) ? "non-null" : null,
+ spliterator.hasCharacteristics(IMMUTABLE) ? "immutable" : null,
+ spliterator.hasCharacteristics(ORDERED) ? "ordered" : null,
+ spliterator.hasCharacteristics(CONCURRENT) ? "concurrent" : null,
+ spliterator.hasCharacteristics(SORTED) ? "sorted" : null)
+ .filter(Objects::nonNull)
+ .collect(joining(", ", "", ""));
+ return enabledCharacteristics + " " + friendlyName(spliterator.getClass()) + " (" + toBinaryString(value) + ")";
+ }
+ }
+
+
private DiggStreams() {
}
diff --git a/src/test/java/no/digipost/DiggBaseTest.java b/src/test/java/no/digipost/DiggBaseTest.java
index 10617e2..f35cfd4 100644
--- a/src/test/java/no/digipost/DiggBaseTest.java
+++ b/src/test/java/no/digipost/DiggBaseTest.java
@@ -17,44 +17,60 @@
import no.digipost.util.AutoClosed;
import no.digipost.util.ThrowingAutoClosed;
-import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
import org.quicktheories.WithQuickTheories;
import org.quicktheories.core.Gen;
import org.quicktheories.dsl.TheoryBuilder;
+import uk.co.probablyfine.matchers.StreamMatchers;
import java.io.IOException;
import java.io.InputStream;
+import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Stream;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList;
+import static java.util.stream.IntStream.iterate;
+import static java.util.stream.IntStream.rangeClosed;
import static java.util.stream.Stream.generate;
import static no.digipost.DiggBase.autoClose;
import static no.digipost.DiggBase.close;
+import static no.digipost.DiggBase.forceOnAll;
import static no.digipost.DiggBase.friendlyName;
import static no.digipost.DiggBase.nonNull;
import static no.digipost.DiggBase.throwingAutoClose;
+import static no.digipost.DiggCollectors.toSingleExceptionWithSuppressed;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isA;
import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static uk.co.probablyfine.matchers.Java8Matchers.where;
-import static uk.co.probablyfine.matchers.StreamMatchers.contains;
-import static uk.co.probablyfine.matchers.StreamMatchers.empty;
+@ExtendWith(MockitoExtension.class)
public class DiggBaseTest implements WithQuickTheories {
@Test
@@ -89,13 +105,20 @@ public void throwsExceptionWithDescriptionInMessage() {
@Test
public void extractOptionalValuesFromAnObject() {
- assertThat(DiggBase.extractIfPresent("abc", s -> Optional.of(s.charAt(0)), s -> Optional.empty(), s -> Optional.of(s.charAt(2))), contains('a', 'c'));
- assertThat(DiggBase.extractIfPresent("abc", s -> Optional.empty(), s -> Optional.empty()), empty());
+ assertThat(DiggBase.extractIfPresent("abc",
+ s -> Optional.of(s.charAt(0)),
+ s -> Optional.empty(),
+ s -> Optional.of(s.charAt(2))),
+ StreamMatchers.contains('a', 'c'));
+ assertThat(DiggBase.extractIfPresent("abc",
+ s -> Optional.empty(),
+ s -> Optional.empty()),
+ StreamMatchers.empty());
}
@Test
public void extractValuesIncludesEverythingEvenNulls() {
- assertThat(DiggBase.extract("abc", s -> s.charAt(0), s -> null), contains('a', null));
+ assertThat(DiggBase.extract("abc", s -> s.charAt(0), s -> null), StreamMatchers.contains('a', null));
}
@Test
@@ -124,12 +147,12 @@ public void objectManagedByAutoCloseIsSameInstanceAsGiven() {
}
+ interface MyResource {
+ void done();
+ }
+
@Test
- public void useArbitraryObjectWithTryWithResources() {
- abstract class MyResource {
- abstract void done();
- }
- MyResource resource = mock(MyResource.class);
+ public void useArbitraryObjectWithTryWithResources(@Mock MyResource resource) {
try (ThrowingAutoClosed managedResource = throwingAutoClose(resource, MyResource::done)) {
verifyNoInteractions(resource);
managedResource.object(); //just to avoid javac lint warning
@@ -138,14 +161,16 @@ abstract class MyResource {
verifyNoMoreInteractions(resource);
}
+
+ interface MyAutoCloseableResource extends AutoCloseable {
+ void done() throws IOException;
+ @Override
+ void close() throws RuntimeException;
+ }
+
@Test
- public void wrappingAnAlreadyAutoCloseableWithAutoCloseWillAlsoInvokeClose() throws Exception {
- abstract class MyResource implements AutoCloseable {
- abstract void done() throws IOException;
- @Override public abstract void close() throws IOException;
- }
- MyResource resource = mock(MyResource.class);
- try (ThrowingAutoClosed managedResource = throwingAutoClose(resource, MyResource::done)) {
+ public void wrappingAnAlreadyAutoCloseableWithAutoCloseWillAlsoInvokeClose(@Mock MyAutoCloseableResource resource) throws Exception {
+ try (ThrowingAutoClosed managedResource = throwingAutoClose(resource, MyAutoCloseableResource::done)) {
verifyNoInteractions(resource);
managedResource.object(); //just to avoid javac lint warning
}
@@ -156,11 +181,7 @@ abstract class MyResource implements AutoCloseable {
}
@Test
- public void autoCloseWithoutCheckedException() {
- abstract class MyResource {
- abstract void done();
- }
- MyResource resource = mock(MyResource.class);
+ public void autoCloseWithoutCheckedException(@Mock MyResource resource) {
try (AutoClosed managedResource = autoClose(resource, MyResource::done)) {
verifyNoInteractions(resource);
managedResource.object(); //just to avoid javac lint warning
@@ -170,8 +191,7 @@ abstract class MyResource {
}
@Test
- public void getAllExceptionsFromClosingSeveralAutoCloseables() throws Exception {
- AutoCloseable closeable = mock(AutoCloseable.class);
+ public void getAllExceptionsFromClosingSeveralAutoCloseables(@Mock AutoCloseable closeable) throws Exception {
doNothing()
.doThrow(new IOException())
.doNothing()
@@ -183,11 +203,94 @@ public void getAllExceptionsFromClosingSeveralAutoCloseables() throws Exception
Stream closeExceptionsStream = close(generate(() -> closeable).limit(5).toArray(AutoCloseable[]::new));
verifyNoInteractions(closeable);
List closeExceptions = closeExceptionsStream.collect(toList());
- assertThat(closeExceptions, Matchers.contains(asList(instanceOf(IOException.class), instanceOf(IllegalStateException.class))));
+ assertThat(closeExceptions, contains(asList(instanceOf(IOException.class), instanceOf(IllegalStateException.class))));
verify(closeable, times(5)).close();
verifyNoMoreInteractions(closeable);
}
+ @Nested
+ @Timeout(4)
+ class ForceOnAll {
+
+ @Test
+ void runsOperationOnMultipleElements() {
+ List consumed = new ArrayList<>();
+ List exceptions = forceOnAll(consumed::add, 1, 2, 3).collect(toList());
+ assertThat(consumed, contains(1, 2, 3));
+ assertThat(exceptions, empty());
+ }
+
+ @Test
+ void exceptionsFromOperationAreCollected() {
+ List onlyEvenNumbers = new ArrayList<>();
+ List exceptions = forceOnAll(i -> {
+ if (i % 2 != 0) throw new IllegalArgumentException(i + " is odd!");
+ onlyEvenNumbers.add(i);
+ }, 1, 2, 3, 4).collect(toList());
+ assertThat(onlyEvenNumbers, contains(2, 4));
+ assertThat(exceptions, contains(
+ where(Throwable::getMessage, is("1 is odd!")),
+ where(Throwable::getMessage, is("3 is odd!"))));
+ }
+
+ @Test
+ void exceptionsFromTraversingStreamIsCollected() {
+ List consumed = new ArrayList<>();
+ List exceptions = forceOnAll(consumed::add,
+ iterate(2, num -> num - 1).limit(5).mapToDouble(denominator -> 2 / denominator).boxed())
+ .collect(toList());
+
+ assertAll(
+ () -> assertThat(exceptions, contains(isA(ArithmeticException.class))),
+ () -> assertThat(consumed, contains(1.0, 2.0, -2.0, -1.0)));
+ }
+
+ @Test
+ void allElementsResolvedFromStreamException() {
+ List exceptions = forceOnAll(e -> fail("action should never be invoked"),
+ rangeClosed(1, 10).mapToObj(String::valueOf).map(s -> { throw new IllegalStateException(s); }))
+ .collect(toList());
+
+ assertThat(exceptions, hasSize(10));
+ }
+
+ @Test
+ void lastElementsResolvedFromStreamException() {
+ List consumed = new ArrayList<>();
+ List exceptions = forceOnAll(consumed::add,
+ iterate(2, i -> i + -1).limit(3).mapToObj(denominator -> 2 / denominator))
+ .collect(toList());
+ assertAll(
+ () -> assertThat(exceptions, contains(isA(ArithmeticException.class))),
+ () -> assertThat(consumed, contains(1, 2)));
+ }
+
+ @Test
+ void worksWithParalellStreams() {
+ AtomicLong successes = new AtomicLong();
+ long failures = forceOnAll(__ -> successes.incrementAndGet(),
+ iterate(0, i -> i + 1).limit(100_000).parallel()
+ .map(i -> i % 4) // 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, ...
+ .map(denominator -> 4 / denominator) // Fails with div by zero 1/4 of the times
+ .boxed())
+ .count();
+
+ assertThat("3 times as much successes as failures: " + successes + " / " + failures,
+ failures * 3, is(successes.get()));
+ }
+
+ @Test
+ void doesNotInvokeOperationOnNulls(@Mock AutoCloseable resource) throws Exception {
+ Optional exception = forceOnAll(AutoCloseable::close, resource, null, resource, null, resource)
+ .collect(toSingleExceptionWithSuppressed())
+ .map(DiggExceptions::asUnchecked);
+ assertAll(
+ () -> verify(resource, times(3)).close(),
+ () -> assertDoesNotThrow(() -> exception.ifPresent(e -> { throw e; })));
+ }
+
+ }
+
}
diff --git a/src/test/java/no/digipost/DiggCollectorsTest.java b/src/test/java/no/digipost/DiggCollectorsTest.java
index 49cac1e..2b70343 100644
--- a/src/test/java/no/digipost/DiggCollectorsTest.java
+++ b/src/test/java/no/digipost/DiggCollectorsTest.java
@@ -20,6 +20,9 @@
import no.digipost.tuple.ViewableAsTuple;
import no.digipost.util.ViewableAsOptional;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
import org.quicktheories.core.Gen;
import uk.co.probablyfine.matchers.OptionalMatchers;
@@ -53,7 +56,6 @@
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
import static org.quicktheories.QuickTheory.qt;
import static org.quicktheories.generators.SourceDSL.lists;
import static org.quicktheories.generators.SourceDSL.strings;
@@ -61,12 +63,13 @@
import static uk.co.probablyfine.matchers.Java8Matchers.whereNot;
import static uk.co.probablyfine.matchers.OptionalMatchers.contains;
-public class DiggCollectorsTest {
+@ExtendWith(MockitoExtension.class)
+class DiggCollectorsTest {
interface NumTranslation extends ViewableAsTuple> {}
@Test
- public void collectTuplesIntoMap() {
+ void collectTuplesIntoMap() {
NumTranslation oneInEnglish = () -> Tuple.of(1, Optional.of("one"));
NumTranslation twoInEnglish = () -> Tuple.of(2, Optional.of("two"));
NumTranslation twoInSpanish = () -> Tuple.of(2, Optional.of("dos"));
@@ -83,7 +86,7 @@ public void collectTuplesIntoMap() {
}
@Test
- public void collectMultitupleFromTuplesWithEqualFirstElement() {
+ void collectMultitupleFromTuplesWithEqualFirstElement() {
NumTranslation oneInEnglish = () -> Tuple.of(1, Optional.of("one"));
NumTranslation oneInSpanish = () -> Tuple.of(1, Optional.of("uno"));
NumTranslation oneInEsperanto = () -> Tuple.of(1, Optional.of("unu"));
@@ -96,13 +99,13 @@ public void collectMultitupleFromTuplesWithEqualFirstElement() {
}
@Test
- public void collectNoTuplesYieldsEmptyOptional() {
+ void collectNoTuplesYieldsEmptyOptional() {
Optional>> noTuple = Stream.>>empty().collect(toMultituple());
assertThat(noTuple, OptionalMatchers.empty());
}
@Test
- public void collectMultitupleFromTuplesWithNonDistinctFirstElementIsErroneous() {
+ void collectMultitupleFromTuplesWithNonDistinctFirstElementIsErroneous() {
NumTranslation oneInEnglish = () -> Tuple.of(1, Optional.of("one"));
NumTranslation missingOne = () -> Tuple.of(1, Optional.empty());
NumTranslation oneInEsperanto = () -> Tuple.of(1, Optional.of("unu"));
@@ -113,20 +116,20 @@ public void collectMultitupleFromTuplesWithNonDistinctFirstElementIsErroneous()
}
@Test
- public void collectTheValueOfSingleElementStream() {
+ void collectTheValueOfSingleElementStream() {
assertThat(Stream.of(42).collect(allowAtMostOne()), contains(is(42)));
assertThat(Stream.empty().collect(allowAtMostOne()), OptionalMatchers.empty());
assertThat(Stream.of((String) null).collect(allowAtMostOne()), OptionalMatchers.empty());
}
@Test
- public void allowAtMostOneFailsEvenIfExcessiveElementsAreNull() {
+ void allowAtMostOneFailsEvenIfExcessiveElementsAreNull() {
Stream xAndNull = Stream.of("x", null);
assertThrows(ViewableAsOptional.TooManyElements.class, () -> xAndNull.collect(allowAtMostOne()));
}
@Test
- public void convertTwoExceptionsToSingleWithSuppressed() {
+ void convertTwoExceptionsToSingleWithSuppressed() {
Exception mainException = new Exception("main");
Exception suppressedException = new Exception("suppressed");
Exception collatedException = Stream.of(mainException, suppressedException).collect(toSingleExceptionWithSuppressed()).get();
@@ -135,7 +138,7 @@ public void convertTwoExceptionsToSingleWithSuppressed() {
}
@Test
- public void convertLotsOfExceptionsToSingleWithTheRestSuppressed() {
+ void convertLotsOfExceptionsToSingleWithTheRestSuppressed() {
Stream exceptions = range(0, 300).mapToObj(n -> new Exception("exception-" + n));
Exception collatedException = exceptions.parallel().collect(toSingleExceptionWithSuppressed()).get();
assertThat(collatedException, where(Throwable::getSuppressed, arrayWithSize(299)));
@@ -143,7 +146,7 @@ public void convertLotsOfExceptionsToSingleWithTheRestSuppressed() {
}
@Test
- public void addLotsOfSuppressedToGivenException() {
+ void addLotsOfSuppressedToGivenException() {
Stream exceptions = range(0, 300).mapToObj(n -> new Exception("exception-" + n));
IOException collatedException = exceptions.parallel().collect(asSuppressedExceptionsOf(new IOException()));
assertThat(collatedException, where(Throwable::getSuppressed, arrayWithSize(300)));
@@ -151,9 +154,7 @@ public void addLotsOfSuppressedToGivenException() {
}
@Test
- public void suppressesExceptionsCorrectlyWithTryCatchBlocks() throws SQLException {
- Connection connection = mock(Connection.class);
- PreparedStatement pstmt = mock(PreparedStatement.class);
+ void suppressesExceptionsCorrectlyWithTryCatchBlocks(@Mock Connection connection, @Mock PreparedStatement pstmt) throws SQLException {
SQLException connectionCloseException = new SQLException();
BatchUpdateException statementCloseException = new BatchUpdateException();
doThrow(connectionCloseException).when(connection).close();
@@ -170,7 +171,7 @@ public void suppressesExceptionsCorrectlyWithTryCatchBlocks() throws SQLExceptio
}
@Test
- public void convertNoExceptionsToEmptyOptional() {
+ void convertNoExceptionsToEmptyOptional() {
Optional noException = range(0, 300).parallel().mapToObj(n -> new Exception("exception-" + n)).filter(e -> false).collect(toSingleExceptionWithSuppressed());
assertThat(noException, whereNot(Optional::isPresent));
}
@@ -179,7 +180,7 @@ public void convertNoExceptionsToEmptyOptional() {
private final Gen> listsWithAtLeastTwoElements = lists().of(strings().allPossible().ofLengthBetween(0, 10)).ofSizeBetween(2, 40);
@Test
- public void allowAtMostOneFails() {
+ void allowAtMostOneFails() {
qt()
.forAll(listsWithAtLeastTwoElements)
.check(list -> {
@@ -193,7 +194,7 @@ public void allowAtMostOneFails() {
}
@Test
- public void allowAtMostOneFailsWithCustomException() {
+ void allowAtMostOneFailsWithCustomException() {
IllegalStateException customException = new IllegalStateException();
qt()
.forAll(listsWithAtLeastTwoElements)
diff --git a/src/test/java/no/digipost/DiggEnumsTest.java b/src/test/java/no/digipost/DiggEnumsTest.java
index 91e7492..7f395e5 100644
--- a/src/test/java/no/digipost/DiggEnumsTest.java
+++ b/src/test/java/no/digipost/DiggEnumsTest.java
@@ -54,7 +54,7 @@ public void convertFromCommaSeparatedListOfEnumNames() {
qt()
.forAll(multipleEnums)
- .asWithPrecursor(enums -> Stream.of(enums).map(Enum::name).collect(joining(" , ", " ", " ")))
+ .asWithPrecursor(enums -> Stream.of(enums).map(MyEnum::name).collect(joining(" , ", " ", " ")))
.checkAssert((enums, commaSeparatedNames) -> assertThat(fromCommaSeparatedNames(commaSeparatedNames, MyEnum.class), contains(enums)));
}
@@ -74,11 +74,11 @@ public void convertToStringOfDelimiterSeparatedStrings() {
public void toStringConversionsAreSpecialCasesOfTheGenericBaseCase() {
qt()
.forAll(multipleEnums)
- .check(enums -> toCommaSeparatedNames(enums).equals(toStringOf(Enum::name, joining(","), enums)));
+ .check(enums -> toCommaSeparatedNames(enums).equals(toStringOf(MyEnum::name, joining(","), enums)));
qt()
.forAll(multipleEnums)
- .check(enums -> toNames(": ", enums).equals(toStringOf(Enum::name, joining(": "), enums)));
+ .check(enums -> toNames(": ", enums).equals(toStringOf(MyEnum::name, joining(": "), enums)));
qt()
.forAll(multipleEnums)
diff --git a/src/test/java/no/digipost/DiggExceptionsTest.java b/src/test/java/no/digipost/DiggExceptionsTest.java
index 2d08f8a..007b000 100644
--- a/src/test/java/no/digipost/DiggExceptionsTest.java
+++ b/src/test/java/no/digipost/DiggExceptionsTest.java
@@ -18,6 +18,9 @@
import no.digipost.concurrent.OneTimeToggle;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
import java.io.IOException;
import java.io.UncheckedIOException;
@@ -42,25 +45,25 @@
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static uk.co.probablyfine.matchers.Java8Matchers.where;
-public class DiggExceptionsTest {
+@ExtendWith(MockitoExtension.class)
+class DiggExceptionsTest {
@Test
- public void causalChainOfNullIsEmptyStream() {
+ void causalChainOfNullIsEmptyStream() {
assertThat(causalChainOf(null).collect(toList()), empty());
}
@Test
- public void returnsTheCausalChainOfExceptions() {
+ void returnsTheCausalChainOfExceptions() {
List exception = causalChainOf(new Exception(new IllegalStateException(new IOException()))).collect(toList());
assertThat(exception, contains(instanceOf(Exception.class), instanceOf(IllegalStateException.class), instanceOf(IOException.class)));
}
@Test
- public void runAThrowingRunnableUnchecked() {
+ void runAThrowingRunnableUnchecked() {
OneTimeToggle toggled = new OneTimeToggle();
DiggExceptions.runUnchecked(() -> toggled.nowOrIfAlreadyThenThrow(() -> new AssertionError("should not be run twice!")));
assertThat(toggled, where(OneTimeToggle::yet));
@@ -70,7 +73,7 @@ public void runAThrowingRunnableUnchecked() {
}
@Test
- public void getAThrowingSupplierUnchecked() {
+ void getAThrowingSupplierUnchecked() {
assertThat(getUnchecked(() -> 42), is(42));
Exception e = new Exception();
@@ -78,7 +81,7 @@ public void getAThrowingSupplierUnchecked() {
}
@Test
- public void applyAThrowingFunctionUnchecked() {
+ void applyAThrowingFunctionUnchecked() {
assertThat(applyUnchecked(Math::round, 4.6f), is(5));
assertThat(applyUnchecked(n -> n, null), nullValue());
assertThat(applyUnchecked(n -> n, Optional.empty()), is(Optional.empty()));
@@ -88,14 +91,12 @@ public void applyAThrowingFunctionUnchecked() {
}
@Test
- public void factoryMethodsForThrowingFunctionalInterfaces() throws Throwable {
+ void factoryMethodsForThrowingFunctionalInterfaces(@Mock Consumer exceptionHandler) throws Throwable {
assertThat(mayThrow((t) -> t).apply("a"), is("a"));
assertThat(mayThrow((t, u) -> t).apply("a", "b"), is("a"));
assertThat(mayThrow(() -> "a").get(), is("a"));
Exception ex = new Exception();
- @SuppressWarnings("unchecked")
- Consumer exceptionHandler = mock(Consumer.class);
mayThrow(t -> { if (t == null) throw ex; }).ifException(exceptionHandler).accept(null);
verify(exceptionHandler).accept(ex);
diff --git a/src/test/java/no/digipost/DiggIOTest.java b/src/test/java/no/digipost/DiggIOTest.java
index fe4ac2a..5bc0a0f 100644
--- a/src/test/java/no/digipost/DiggIOTest.java
+++ b/src/test/java/no/digipost/DiggIOTest.java
@@ -17,27 +17,28 @@
import no.digipost.function.ThrowingConsumer;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
import java.util.function.Consumer;
import static no.digipost.DiggIO.autoClosing;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-public class DiggIOTest {
+@ExtendWith(MockitoExtension.class)
+class DiggIOTest {
@Test
- public void closesResourceAfterSuccess() throws Exception {
- AutoCloseable resource = mock(AutoCloseable.class);
+ void closesResourceAfterSuccess(@Mock AutoCloseable resource) throws Exception {
autoClosing(r -> {}).accept(resource);
verify(resource, times(1)).close();
}
@Test
- public void closesResourceAfterFailure() throws Exception {
- AutoCloseable resource = mock(AutoCloseable.class);
+ void closesResourceAfterFailure(@Mock AutoCloseable resource) throws Exception {
Consumer autoClosingConsumer = autoClosing((ThrowingConsumer) r -> { throw new Exception(); });
assertThrows(RuntimeException.class, () -> autoClosingConsumer.accept(resource));
verify(resource, times(1)).close();
diff --git a/src/test/java/no/digipost/DiggStreamsTest.java b/src/test/java/no/digipost/DiggStreamsTest.java
index bda1f05..f9b639d 100644
--- a/src/test/java/no/digipost/DiggStreamsTest.java
+++ b/src/test/java/no/digipost/DiggStreamsTest.java
@@ -15,6 +15,7 @@
*/
package no.digipost;
+import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.quicktheories.WithQuickTheories;
import org.quicktheories.core.Gen;
@@ -22,26 +23,36 @@
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.Spliterators.AbstractSpliterator;
+import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
-import static uk.co.probablyfine.matchers.StreamMatchers.contains;
-import static uk.co.probablyfine.matchers.StreamMatchers.equalTo;
+import static java.lang.Integer.toBinaryString;
import static java.util.Arrays.asList;
+import static java.util.Spliterator.DISTINCT;
+import static java.util.Spliterator.NONNULL;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.IntStream.iterate;
+import static no.digipost.DiggBase.friendlyName;
import static no.digipost.DiggStreams.streamByIntIndex;
import static no.digipost.DiggStreams.streamByKey;
import static no.digipost.DiggStreams.streamByLongIndex;
import static no.digipost.DiggStreams.streamWhileNonEmpty;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.containsStringIgnoringCase;
import static org.hamcrest.Matchers.is;
+import static uk.co.probablyfine.matchers.Java8Matchers.where;
+import static uk.co.probablyfine.matchers.StreamMatchers.contains;
+import static uk.co.probablyfine.matchers.StreamMatchers.equalTo;
-public class DiggStreamsTest implements WithQuickTheories {
+class DiggStreamsTest implements WithQuickTheories {
@Test
- public void streamAllElementsByIntIndex() {
+ void streamAllElementsByIntIndex() {
Gen> listsOfStrings = lists().of(strings().allPossible().ofLengthBetween(0, 100)).ofSizeBetween(0, 20);
qt()
.forAll(listsOfStrings)
@@ -49,14 +60,14 @@ public void streamAllElementsByIntIndex() {
}
@Test
- public void streamValuesOfMap() {
+ void streamValuesOfMap() {
List chars = asList('A', 'B', 'C', 'D');
Map charAndString = chars.stream().collect(toMap(Function.identity(), String::valueOf));
assertThat(streamByKey(charAndString, chars.stream(), Map::get), contains("A", "B", "C", "D"));
}
@Test
- public void streamValuesByLongIndex() {
+ void streamValuesByLongIndex() {
assertThat(streamByLongIndex(new LongConverter(), 4, LongConverter::asString), contains("0", "1", "2", "3"));
}
@@ -67,7 +78,7 @@ String asString(long value) {
}
@Test
- public void streamPagesWhilePageHasContent() {
+ void streamPagesWhilePageHasContent() {
Gen alphabetLengths = integers().between(10, 200);
Gen pageSizes = integers().between(1, 13);
@@ -77,6 +88,41 @@ public void streamPagesWhilePageHasContent() {
.checkAssert(pagedAlphabet -> assertThat(streamWhileNonEmpty(pagedAlphabet::getPage), equalTo(pagedAlphabet.getEntireAlphabet())));
}
+ @Nested
+ class Spliterators {
+ final class CharacteristicsTester extends AbstractSpliterator {
+ public CharacteristicsTester(int characteristics) {
+ super(Long.MAX_VALUE, characteristics);
+ }
+ @Override
+ public boolean tryAdvance(Consumer super Void> action) {
+ return false;
+ }
+ }
+ @Test
+ void describeNoCharacteristics() {
+ assertThat(new CharacteristicsTester(0), where(DiggStreams::describeCharacteristics, allOf(
+ containsString(friendlyName(CharacteristicsTester.class)),
+ containsStringIgnoringCase("no enabled characteristics"))));
+ }
+
+ @Test
+ void describeOneEnabledCharacteristic() {
+ assertThat(new CharacteristicsTester(NONNULL), where(DiggStreams::describeCharacteristics, allOf(
+ containsString(friendlyName(CharacteristicsTester.class)),
+ containsStringIgnoringCase("non-null"),
+ containsString(toBinaryString(NONNULL)))));
+ }
+
+ @Test
+ void describeMultipleEnabledCharacteristics() {
+ assertThat(new CharacteristicsTester(NONNULL | DISTINCT), where(DiggStreams::describeCharacteristics, allOf(
+ containsString(friendlyName(CharacteristicsTester.class)),
+ containsStringIgnoringCase("non-null"), containsStringIgnoringCase("distinct"),
+ containsString(toBinaryString(DISTINCT | NONNULL)))));
+ }
+ }
+
private static final class PagedAlphabet {
final char firstChar = 'A';
final int length;
diff --git a/src/test/java/no/digipost/collection/BatchedIterableTest.java b/src/test/java/no/digipost/collection/BatchedIterableTest.java
index 7e5b46e..8bec45a 100644
--- a/src/test/java/no/digipost/collection/BatchedIterableTest.java
+++ b/src/test/java/no/digipost/collection/BatchedIterableTest.java
@@ -36,11 +36,13 @@ class BatchedIterableTest {
@Test
void emptyBatch() {
- Batches empty1 = new Batches<>();
+ @SuppressWarnings("unchecked")
+ Batches> empty1 = new Batches<>();
assertThat(batched(empty1, b -> false), is(emptyIterable()));
assertThat(empty1.fetches(), is(1));
- Batches empty2 = new Batches<>();
+ @SuppressWarnings("unchecked")
+ Batches> empty2 = new Batches<>();
assertThat(batched(empty2, b -> true), is(emptyIterable()));
assertThat(empty2.fetches(), is(2));
}
diff --git a/src/test/java/no/digipost/function/ThrowingFunctionTest.java b/src/test/java/no/digipost/function/ThrowingFunctionTest.java
index d690abe..d6f8c30 100644
--- a/src/test/java/no/digipost/function/ThrowingFunctionTest.java
+++ b/src/test/java/no/digipost/function/ThrowingFunctionTest.java
@@ -16,6 +16,9 @@
package no.digipost.function;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -26,10 +29,10 @@
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+@ExtendWith(MockitoExtension.class)
public class ThrowingFunctionTest {
private final RuntimeException ex = new RuntimeException("fail");
@@ -55,16 +58,15 @@ public void rethrowOriginalError() {
}
@Test
- public void translateToEmptyOptionalAndDelegateExceptionToHandler() {
+ public void translateToEmptyOptionalAndDelegateExceptionToHandler(
+ @Mock Consumer getException,
+ @Mock BiConsumer getValueAndException) {
+
ThrowingFunction fn = i -> {throw ex;};
- @SuppressWarnings("unchecked")
- Consumer getException = mock(Consumer.class);
assertThat(fn.ifException(getException).apply(42), is(empty()));
verify(getException, times(1)).accept(ex);
- @SuppressWarnings("unchecked")
- BiConsumer getValueAndException = mock(BiConsumer.class);
assertThat(fn.ifException(getValueAndException).apply(42), is(empty()));
verify(getValueAndException, times(1)).accept(42, ex);
}
diff --git a/src/test/java/no/digipost/function/ThrowingRunnableTest.java b/src/test/java/no/digipost/function/ThrowingRunnableTest.java
index 0cd4e04..bdf30a8 100644
--- a/src/test/java/no/digipost/function/ThrowingRunnableTest.java
+++ b/src/test/java/no/digipost/function/ThrowingRunnableTest.java
@@ -16,16 +16,19 @@
package no.digipost.function;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
import java.util.function.Consumer;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+@ExtendWith(MockitoExtension.class)
public class ThrowingRunnableTest {
private final RuntimeException ex = new RuntimeException();
@@ -45,10 +48,8 @@ public void rethrowOriginalError() {
}
@Test
- public void translateToEmptyOptionalAndDelegateExceptionToHandler() {
+ public void translateToEmptyOptionalAndDelegateExceptionToHandler(@Mock Consumer handler) {
ThrowingRunnable fn = () -> {throw ex;};
- @SuppressWarnings("unchecked")
- Consumer handler = mock(Consumer.class);
fn.ifException(handler).run();
verify(handler, times(1)).accept(ex);
diff --git a/src/test/java/no/digipost/function/ThrowingSupplierTest.java b/src/test/java/no/digipost/function/ThrowingSupplierTest.java
index a139503..4d83101 100644
--- a/src/test/java/no/digipost/function/ThrowingSupplierTest.java
+++ b/src/test/java/no/digipost/function/ThrowingSupplierTest.java
@@ -16,6 +16,9 @@
package no.digipost.function;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -26,11 +29,11 @@
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static uk.co.probablyfine.matchers.Java8Matchers.where;
+@ExtendWith(MockitoExtension.class)
class ThrowingSupplierTest {
private final RuntimeException ex = new RuntimeException();
@@ -57,10 +60,8 @@ void rethrowOriginalError() {
}
@Test
- void translateToEmptyOptionalAndDelegateExceptionToHandler() {
+ void translateToEmptyOptionalAndDelegateExceptionToHandler(@Mock Consumer handler) {
ThrowingSupplier, Exception> fn = () -> {throw ex;};
- @SuppressWarnings("unchecked")
- Consumer handler = mock(Consumer.class);
assertThat(fn.ifException(handler).get(), is(empty()));
verify(handler, times(1)).accept(ex);
diff --git a/src/test/java/no/digipost/io/InputStreamIteratorTest.java b/src/test/java/no/digipost/io/InputStreamIteratorTest.java
index 060520c..0524a29 100644
--- a/src/test/java/no/digipost/io/InputStreamIteratorTest.java
+++ b/src/test/java/no/digipost/io/InputStreamIteratorTest.java
@@ -71,7 +71,7 @@ void throws_if_next_is_called_with_no_more_elements() throws Exception {
InputStreamIterator iterator = new InputStreamIterator(inputStream, 2);
assertThat(consumeToString(iterator, UTF_8), is("Some data"));
- assertThat(iterator, whereNot(Iterator::hasNext));
+ assertThat(iterator, whereNot(Iterator::hasNext));
assertThrows(NoSuchElementException.class, iterator::next);
}