From d14c81d0667d97f6b8e1760a01ff657288eebaee Mon Sep 17 00:00:00 2001 From: Markus Kreusch Date: Thu, 14 Jan 2016 23:18:45 +0100 Subject: [PATCH] Tests for AutoClosingStream --- .../streams/AutoClosingDoubleStream.java | 10 - .../streams/DelegatingStreamFactory.java | 29 --- .../streams/AutoClosingDoubleStreamTest.java | 227 +++++++++++++++++ .../streams/AutoClosingIntStreamTest.java | 228 ++++++++++++++++++ .../streams/AutoClosingLongStreamTest.java | 228 ++++++++++++++++++ .../common/streams/AutoClosingStreamTest.java | 203 ++++++++++++++-- 6 files changed, 870 insertions(+), 55 deletions(-) create mode 100644 main/commons/src/test/java/org/cryptomator/common/streams/AutoClosingDoubleStreamTest.java create mode 100644 main/commons/src/test/java/org/cryptomator/common/streams/AutoClosingIntStreamTest.java create mode 100644 main/commons/src/test/java/org/cryptomator/common/streams/AutoClosingLongStreamTest.java diff --git a/main/commons/src/main/java/org/cryptomator/common/streams/AutoClosingDoubleStream.java b/main/commons/src/main/java/org/cryptomator/common/streams/AutoClosingDoubleStream.java index ec0a98b6b..60f571b6a 100644 --- a/main/commons/src/main/java/org/cryptomator/common/streams/AutoClosingDoubleStream.java +++ b/main/commons/src/main/java/org/cryptomator/common/streams/AutoClosingDoubleStream.java @@ -11,7 +11,6 @@ import java.util.function.DoublePredicate; import java.util.function.ObjDoubleConsumer; import java.util.function.Supplier; import java.util.stream.DoubleStream; -import java.util.stream.Stream; public class AutoClosingDoubleStream extends DelegatingDoubleStream { @@ -176,13 +175,4 @@ public class AutoClosingDoubleStream extends DelegatingDoubleStream { } } - @Override - public Stream boxed() { - try { - return super.boxed(); - } finally { - close(); - } - } - } diff --git a/main/commons/src/main/java/org/cryptomator/common/streams/DelegatingStreamFactory.java b/main/commons/src/main/java/org/cryptomator/common/streams/DelegatingStreamFactory.java index 04b584180..24203baf8 100644 --- a/main/commons/src/main/java/org/cryptomator/common/streams/DelegatingStreamFactory.java +++ b/main/commons/src/main/java/org/cryptomator/common/streams/DelegatingStreamFactory.java @@ -1,6 +1,5 @@ package org.cryptomator.common.streams; -import java.util.function.Function; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; @@ -8,34 +7,6 @@ import java.util.stream.Stream; public interface DelegatingStreamFactory { - public static DelegatingStreamFactory of( // - ObjectStreamWrapper objectStreamWrapper, // - Function intStreamWrapper, // - Function longStreamWrapper, // - Function doubleStreamWrapper) { - return new DelegatingStreamFactory() { - @Override - public DoubleStream from(DoubleStream other) { - return doubleStreamWrapper.apply(other); - } - - @Override - public LongStream from(LongStream other) { - return longStreamWrapper.apply(other); - } - - @Override - public IntStream from(IntStream other) { - return intStreamWrapper.apply(other); - } - - @Override - public Stream from(Stream other) { - return objectStreamWrapper.from(other); - } - }; - } - Stream from(Stream other); IntStream from(IntStream other); diff --git a/main/commons/src/test/java/org/cryptomator/common/streams/AutoClosingDoubleStreamTest.java b/main/commons/src/test/java/org/cryptomator/common/streams/AutoClosingDoubleStreamTest.java new file mode 100644 index 000000000..5a8215ad8 --- /dev/null +++ b/main/commons/src/test/java/org/cryptomator/common/streams/AutoClosingDoubleStreamTest.java @@ -0,0 +1,227 @@ +package org.cryptomator.common.streams; + +import static org.hamcrest.CoreMatchers.anyOf; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.DoubleSummaryStatistics; +import java.util.List; +import java.util.OptionalDouble; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.DoubleBinaryOperator; +import java.util.function.DoubleConsumer; +import java.util.function.DoubleFunction; +import java.util.function.DoublePredicate; +import java.util.function.DoubleToIntFunction; +import java.util.function.DoubleToLongFunction; +import java.util.function.DoubleUnaryOperator; +import java.util.function.Function; +import java.util.function.ObjDoubleConsumer; +import java.util.function.Supplier; +import java.util.stream.BaseStream; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +import org.hamcrest.Matcher; +import org.junit.Before; +import org.junit.Rule; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.FromDataPoints; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.InOrder; + +@SuppressWarnings({"unchecked", "rawtypes"}) +@RunWith(Theories.class) +public class AutoClosingDoubleStreamTest { + + private static final DoublePredicate A_DOUBLE_PREDICATE = any -> true; + private static final DoubleFunction A_DOUBLE_FUNCTION = i -> null; + private static final BiConsumer A_BICONSUMER = (a, b) -> { + }; + private static final Supplier A_SUPPLIER = () -> null; + + @DataPoints("intermediateOperations") + public static final List> INTERMEDIATE_OPERATIONS = new ArrayList<>(); + + @DataPoints("terminalOperations") + public static final List> TERMINAL_OPERATIONS = new ArrayList<>(); + private static final DoubleUnaryOperator A_DOUBLE_UNARY_OPERATOR = i -> 3; + private static final DoubleToLongFunction A_DOUBLE_TO_LONG_FUNCTION = i -> 3L; + private static final DoubleToIntFunction A_DOUBLE_TO_INT_FUNCTION = i -> 5; + private static final DoubleConsumer A_DOUBLE_CONSUMER = i -> { + }; + private static final ObjDoubleConsumer AN_OBJ_DOUBLE_CONSUMER = (a, b) -> { + }; + private static final DoubleBinaryOperator A_DOUBLE_BINARY_OPERATOR = (a, b) -> a; + + static { + // define intermediate operations + test(DoubleStream.class, DoubleStream::distinct); + test(DoubleStream.class, stream -> stream.filter(A_DOUBLE_PREDICATE)); + test(DoubleStream.class, stream -> stream.flatMap(A_DOUBLE_FUNCTION)); + test(DoubleStream.class, stream -> stream.limit(5)); + test(DoubleStream.class, stream -> stream.map(A_DOUBLE_UNARY_OPERATOR)); + test(LongStream.class, stream -> stream.mapToLong(A_DOUBLE_TO_LONG_FUNCTION)); + test(Stream.class, stream -> stream.mapToObj(A_DOUBLE_FUNCTION)); + test(IntStream.class, stream -> stream.mapToInt(A_DOUBLE_TO_INT_FUNCTION)); + test(DoubleStream.class, DoubleStream::parallel); + test(DoubleStream.class, stream -> stream.peek(A_DOUBLE_CONSUMER)); + test(DoubleStream.class, DoubleStream::sequential); + test(DoubleStream.class, stream -> stream.skip(5)); + test(DoubleStream.class, DoubleStream::sorted); + test(DoubleStream.class, DoubleStream::unordered); + test(Stream.class, DoubleStream::boxed); + + // define terminal operations + test(stream -> stream.allMatch(A_DOUBLE_PREDICATE), true); + test(stream -> stream.anyMatch(A_DOUBLE_PREDICATE), true); + test(stream -> stream.collect(A_SUPPLIER, AN_OBJ_DOUBLE_CONSUMER, A_BICONSUMER), 7d); + test(DoubleStream::count, 3L); + test(DoubleStream::findAny, OptionalDouble.of(3)); + test(DoubleStream::findFirst, OptionalDouble.of(3)); + test(stream -> stream.forEach(A_DOUBLE_CONSUMER)); + test(stream -> stream.forEachOrdered(A_DOUBLE_CONSUMER)); + test(stream -> stream.max(), OptionalDouble.of(3)); + test(stream -> stream.min(), OptionalDouble.of(3)); + test(stream -> stream.noneMatch(A_DOUBLE_PREDICATE), true); + test(stream -> stream.reduce(A_DOUBLE_BINARY_OPERATOR), OptionalDouble.of(3)); + test(stream -> stream.reduce(1, A_DOUBLE_BINARY_OPERATOR), 3d); + test(DoubleStream::toArray, new double[1]); + test(DoubleStream::sum, 1d); + test(DoubleStream::average, OptionalDouble.of(3d)); + test(DoubleStream::summaryStatistics, new DoubleSummaryStatistics()); + } + + private static void test(Consumer consumer) { + TERMINAL_OPERATIONS.add(new TerminalOperation() { + @Override + public T result() { + return null; + } + + @Override + public T apply(DoubleStream stream) { + consumer.accept(stream); + return null; + } + }); + } + + private static void test(Function function, T result) { + TERMINAL_OPERATIONS.add(new TerminalOperation() { + @Override + public T result() { + return result; + } + + @Override + public T apply(DoubleStream stream) { + return function.apply(stream); + } + }); + } + + private static void test(Class type, Function function) { + INTERMEDIATE_OPERATIONS.add(new DoubleermediateOperation() { + @Override + public Class type() { + return type; + } + + @Override + public T apply(DoubleStream stream) { + return function.apply(stream); + } + }); + } + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private DoubleStream delegate; + private DoubleStream inTest; + + @Before + public void setUp() { + delegate = mock(DoubleStream.class); + inTest = AutoClosingDoubleStream.from(delegate); + } + + @Theory + public void testIntermediateOperationReturnsNewAutoClosingStream(@FromDataPoints("intermediateOperations") DoubleermediateOperation intermediateOperation) { + BaseStream newDelegate = (BaseStream) mock(intermediateOperation.type()); + when(intermediateOperation.apply(delegate)).thenReturn(newDelegate); + + BaseStream result = intermediateOperation.apply(inTest); + + assertThat(result, isAutoClosing()); + verifyDelegate(result, newDelegate); + } + + @Theory + public void testTerminalOperationDelegatesToAndClosesDelegate(@FromDataPoints("terminalOperations") TerminalOperation terminalOperation) { + Object expectedResult = terminalOperation.result(); + if (expectedResult != null) { + when(terminalOperation.apply(delegate)).thenReturn(expectedResult); + } + + Object result = terminalOperation.apply(inTest); + + InOrder inOrder = inOrder(delegate); + assertThat(result, is(expectedResult)); + inOrder.verify(delegate).close(); + } + + @Theory + public void testTerminalOperationClosesDelegateEvenOnException(@FromDataPoints("terminalOperations") TerminalOperation terminalOperation) { + RuntimeException exception = new RuntimeException(); + terminalOperation.apply(doThrow(exception).when(delegate)); + + thrown.expect(is(exception)); + + try { + terminalOperation.apply(inTest); + } finally { + verify(delegate).close(); + } + } + + private Matcher isAutoClosing() { + return is(anyOf(instanceOf(AutoClosingStream.class), instanceOf(AutoClosingDoubleStream.class), instanceOf(AutoClosingIntStream.class), instanceOf(AutoClosingLongStream.class))); + } + + private void verifyDelegate(BaseStream result, BaseStream newDelegate) { + result.close(); + verify(newDelegate).close(); + } + + private interface TerminalOperation { + + T result(); + + T apply(DoubleStream stream); + + } + + private interface DoubleermediateOperation { + + Class type(); + + T apply(DoubleStream stream); + + } + +} diff --git a/main/commons/src/test/java/org/cryptomator/common/streams/AutoClosingIntStreamTest.java b/main/commons/src/test/java/org/cryptomator/common/streams/AutoClosingIntStreamTest.java new file mode 100644 index 000000000..d86718b62 --- /dev/null +++ b/main/commons/src/test/java/org/cryptomator/common/streams/AutoClosingIntStreamTest.java @@ -0,0 +1,228 @@ +package org.cryptomator.common.streams; + +import static org.hamcrest.CoreMatchers.anyOf; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.IntSummaryStatistics; +import java.util.List; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntBinaryOperator; +import java.util.function.IntConsumer; +import java.util.function.IntFunction; +import java.util.function.IntPredicate; +import java.util.function.IntToDoubleFunction; +import java.util.function.IntToLongFunction; +import java.util.function.IntUnaryOperator; +import java.util.function.ObjIntConsumer; +import java.util.function.Supplier; +import java.util.stream.BaseStream; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +import org.hamcrest.Matcher; +import org.junit.Before; +import org.junit.Rule; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.FromDataPoints; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.InOrder; + +@SuppressWarnings({"unchecked", "rawtypes"}) +@RunWith(Theories.class) +public class AutoClosingIntStreamTest { + + private static final IntPredicate AN_INT_PREDICATE = any -> true; + private static final IntFunction AN_INT_FUNCTION = i -> null; + private static final BiConsumer A_BICONSUMER = (a, b) -> { + }; + private static final Supplier A_SUPPLIER = () -> null; + + @DataPoints("intermediateOperations") + public static final List> INTERMEDIATE_OPERATIONS = new ArrayList<>(); + + @DataPoints("terminalOperations") + public static final List> TERMINAL_OPERATIONS = new ArrayList<>(); + private static final IntUnaryOperator AN_INT_UNARY_OPERATOR = i -> 3; + private static final IntToDoubleFunction AN_INT_TO_DOUBLE_FUNCTION = i -> 3d; + private static final IntToLongFunction AN_INT_TO_LONG_FUNCTION = i -> 5L; + private static final IntConsumer AN_INT_CONSUMER = i -> { + }; + private static final ObjIntConsumer AN_OBJ_INT_CONSUMER = (a, b) -> { + }; + private static final IntBinaryOperator AN_INT_BINARY_OPERATOR = (a, b) -> a; + + static { + // define intermediate operations + test(IntStream.class, IntStream::distinct); + test(IntStream.class, stream -> stream.filter(AN_INT_PREDICATE)); + test(IntStream.class, stream -> stream.flatMap(AN_INT_FUNCTION)); + test(IntStream.class, stream -> stream.limit(5)); + test(IntStream.class, stream -> stream.map(AN_INT_UNARY_OPERATOR)); + test(DoubleStream.class, stream -> stream.mapToDouble(AN_INT_TO_DOUBLE_FUNCTION)); + test(Stream.class, stream -> stream.mapToObj(AN_INT_FUNCTION)); + test(LongStream.class, stream -> stream.mapToLong(AN_INT_TO_LONG_FUNCTION)); + test(IntStream.class, IntStream::parallel); + test(IntStream.class, stream -> stream.peek(AN_INT_CONSUMER)); + test(IntStream.class, IntStream::sequential); + test(IntStream.class, stream -> stream.skip(5)); + test(IntStream.class, IntStream::sorted); + test(IntStream.class, IntStream::unordered); + test(Stream.class, IntStream::boxed); + + // define terminal operations + test(stream -> stream.allMatch(AN_INT_PREDICATE), true); + test(stream -> stream.anyMatch(AN_INT_PREDICATE), true); + test(stream -> stream.collect(A_SUPPLIER, AN_OBJ_INT_CONSUMER, A_BICONSUMER), 7); + test(IntStream::count, 3L); + test(IntStream::findAny, OptionalInt.of(3)); + test(IntStream::findFirst, OptionalInt.of(3)); + test(stream -> stream.forEach(AN_INT_CONSUMER)); + test(stream -> stream.forEachOrdered(AN_INT_CONSUMER)); + test(stream -> stream.max(), OptionalInt.of(3)); + test(stream -> stream.min(), OptionalInt.of(3)); + test(stream -> stream.noneMatch(AN_INT_PREDICATE), true); + test(stream -> stream.reduce(AN_INT_BINARY_OPERATOR), OptionalInt.of(3)); + test(stream -> stream.reduce(1, AN_INT_BINARY_OPERATOR), 3); + test(IntStream::toArray, new int[1]); + test(IntStream::sum, 1); + test(IntStream::average, OptionalDouble.of(3d)); + test(IntStream::summaryStatistics, new IntSummaryStatistics()); + } + + private static void test(Consumer consumer) { + TERMINAL_OPERATIONS.add(new TerminalOperation() { + @Override + public T result() { + return null; + } + + @Override + public T apply(IntStream stream) { + consumer.accept(stream); + return null; + } + }); + } + + private static void test(Function function, T result) { + TERMINAL_OPERATIONS.add(new TerminalOperation() { + @Override + public T result() { + return result; + } + + @Override + public T apply(IntStream stream) { + return function.apply(stream); + } + }); + } + + private static void test(Class type, Function function) { + INTERMEDIATE_OPERATIONS.add(new IntermediateOperation() { + @Override + public Class type() { + return type; + } + + @Override + public T apply(IntStream stream) { + return function.apply(stream); + } + }); + } + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private IntStream delegate; + private IntStream inTest; + + @Before + public void setUp() { + delegate = mock(IntStream.class); + inTest = AutoClosingIntStream.from(delegate); + } + + @Theory + public void testIntermediateOperationReturnsNewAutoClosingStream(@FromDataPoints("intermediateOperations") IntermediateOperation intermediateOperation) { + BaseStream newDelegate = (BaseStream) mock(intermediateOperation.type()); + when(intermediateOperation.apply(delegate)).thenReturn(newDelegate); + + BaseStream result = intermediateOperation.apply(inTest); + + assertThat(result, isAutoClosing()); + verifyDelegate(result, newDelegate); + } + + @Theory + public void testTerminalOperationDelegatesToAndClosesDelegate(@FromDataPoints("terminalOperations") TerminalOperation terminalOperation) { + Object expectedResult = terminalOperation.result(); + if (expectedResult != null) { + when(terminalOperation.apply(delegate)).thenReturn(expectedResult); + } + + Object result = terminalOperation.apply(inTest); + + InOrder inOrder = inOrder(delegate); + assertThat(result, is(expectedResult)); + inOrder.verify(delegate).close(); + } + + @Theory + public void testTerminalOperationClosesDelegateEvenOnException(@FromDataPoints("terminalOperations") TerminalOperation terminalOperation) { + RuntimeException exception = new RuntimeException(); + terminalOperation.apply(doThrow(exception).when(delegate)); + + thrown.expect(is(exception)); + + try { + terminalOperation.apply(inTest); + } finally { + verify(delegate).close(); + } + } + + private Matcher isAutoClosing() { + return is(anyOf(instanceOf(AutoClosingStream.class), instanceOf(AutoClosingDoubleStream.class), instanceOf(AutoClosingIntStream.class), instanceOf(AutoClosingLongStream.class))); + } + + private void verifyDelegate(BaseStream result, BaseStream newDelegate) { + result.close(); + verify(newDelegate).close(); + } + + private interface TerminalOperation { + + T result(); + + T apply(IntStream stream); + + } + + private interface IntermediateOperation { + + Class type(); + + T apply(IntStream stream); + + } + +} diff --git a/main/commons/src/test/java/org/cryptomator/common/streams/AutoClosingLongStreamTest.java b/main/commons/src/test/java/org/cryptomator/common/streams/AutoClosingLongStreamTest.java new file mode 100644 index 000000000..02601f231 --- /dev/null +++ b/main/commons/src/test/java/org/cryptomator/common/streams/AutoClosingLongStreamTest.java @@ -0,0 +1,228 @@ +package org.cryptomator.common.streams; + +import static org.hamcrest.CoreMatchers.anyOf; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; +import java.util.LongSummaryStatistics; +import java.util.OptionalDouble; +import java.util.OptionalLong; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.LongBinaryOperator; +import java.util.function.LongConsumer; +import java.util.function.LongFunction; +import java.util.function.LongPredicate; +import java.util.function.LongToDoubleFunction; +import java.util.function.LongToIntFunction; +import java.util.function.LongUnaryOperator; +import java.util.function.ObjLongConsumer; +import java.util.function.Supplier; +import java.util.stream.BaseStream; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +import org.hamcrest.Matcher; +import org.junit.Before; +import org.junit.Rule; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.FromDataPoints; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.InOrder; + +@SuppressWarnings({"unchecked", "rawtypes"}) +@RunWith(Theories.class) +public class AutoClosingLongStreamTest { + + private static final LongPredicate AN_LONG_PREDICATE = any -> true; + private static final LongFunction AN_LONG_FUNCTION = i -> null; + private static final BiConsumer A_BICONSUMER = (a, b) -> { + }; + private static final Supplier A_SUPPLIER = () -> null; + + @DataPoints("intermediateOperations") + public static final List> INTERMEDIATE_OPERATIONS = new ArrayList<>(); + + @DataPoints("terminalOperations") + public static final List> TERMINAL_OPERATIONS = new ArrayList<>(); + private static final LongUnaryOperator AN_LONG_UNARY_OPERATOR = i -> 3; + private static final LongToDoubleFunction AN_LONG_TO_DOUBLE_FUNCTION = i -> 3d; + private static final LongToIntFunction AN_LONG_TO_INT_FUNCTION = i -> 5; + private static final LongConsumer AN_LONG_CONSUMER = i -> { + }; + private static final ObjLongConsumer AN_OBJ_LONG_CONSUMER = (a, b) -> { + }; + private static final LongBinaryOperator AN_LONG_BINARY_OPERATOR = (a, b) -> a; + + static { + // define intermediate operations + test(LongStream.class, LongStream::distinct); + test(LongStream.class, stream -> stream.filter(AN_LONG_PREDICATE)); + test(LongStream.class, stream -> stream.flatMap(AN_LONG_FUNCTION)); + test(LongStream.class, stream -> stream.limit(5)); + test(LongStream.class, stream -> stream.map(AN_LONG_UNARY_OPERATOR)); + test(DoubleStream.class, stream -> stream.mapToDouble(AN_LONG_TO_DOUBLE_FUNCTION)); + test(Stream.class, stream -> stream.mapToObj(AN_LONG_FUNCTION)); + test(IntStream.class, stream -> stream.mapToInt(AN_LONG_TO_INT_FUNCTION)); + test(LongStream.class, LongStream::parallel); + test(LongStream.class, stream -> stream.peek(AN_LONG_CONSUMER)); + test(LongStream.class, LongStream::sequential); + test(LongStream.class, stream -> stream.skip(5)); + test(LongStream.class, LongStream::sorted); + test(LongStream.class, LongStream::unordered); + test(Stream.class, LongStream::boxed); + + // define terminal operations + test(stream -> stream.allMatch(AN_LONG_PREDICATE), true); + test(stream -> stream.anyMatch(AN_LONG_PREDICATE), true); + test(stream -> stream.collect(A_SUPPLIER, AN_OBJ_LONG_CONSUMER, A_BICONSUMER), 7L); + test(LongStream::count, 3L); + test(LongStream::findAny, OptionalLong.of(3)); + test(LongStream::findFirst, OptionalLong.of(3)); + test(stream -> stream.forEach(AN_LONG_CONSUMER)); + test(stream -> stream.forEachOrdered(AN_LONG_CONSUMER)); + test(stream -> stream.max(), OptionalLong.of(3)); + test(stream -> stream.min(), OptionalLong.of(3)); + test(stream -> stream.noneMatch(AN_LONG_PREDICATE), true); + test(stream -> stream.reduce(AN_LONG_BINARY_OPERATOR), OptionalLong.of(3)); + test(stream -> stream.reduce(1, AN_LONG_BINARY_OPERATOR), 3L); + test(LongStream::toArray, new long[1]); + test(LongStream::sum, 1L); + test(LongStream::average, OptionalDouble.of(3d)); + test(LongStream::summaryStatistics, new LongSummaryStatistics()); + } + + private static void test(Consumer consumer) { + TERMINAL_OPERATIONS.add(new TerminalOperation() { + @Override + public T result() { + return null; + } + + @Override + public T apply(LongStream stream) { + consumer.accept(stream); + return null; + } + }); + } + + private static void test(Function function, T result) { + TERMINAL_OPERATIONS.add(new TerminalOperation() { + @Override + public T result() { + return result; + } + + @Override + public T apply(LongStream stream) { + return function.apply(stream); + } + }); + } + + private static void test(Class type, Function function) { + INTERMEDIATE_OPERATIONS.add(new LongermediateOperation() { + @Override + public Class type() { + return type; + } + + @Override + public T apply(LongStream stream) { + return function.apply(stream); + } + }); + } + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private LongStream delegate; + private LongStream inTest; + + @Before + public void setUp() { + delegate = mock(LongStream.class); + inTest = AutoClosingLongStream.from(delegate); + } + + @Theory + public void testIntermediateOperationReturnsNewAutoClosingStream(@FromDataPoints("intermediateOperations") LongermediateOperation intermediateOperation) { + BaseStream newDelegate = (BaseStream) mock(intermediateOperation.type()); + when(intermediateOperation.apply(delegate)).thenReturn(newDelegate); + + BaseStream result = intermediateOperation.apply(inTest); + + assertThat(result, isAutoClosing()); + verifyDelegate(result, newDelegate); + } + + @Theory + public void testTerminalOperationDelegatesToAndClosesDelegate(@FromDataPoints("terminalOperations") TerminalOperation terminalOperation) { + Object expectedResult = terminalOperation.result(); + if (expectedResult != null) { + when(terminalOperation.apply(delegate)).thenReturn(expectedResult); + } + + Object result = terminalOperation.apply(inTest); + + InOrder inOrder = inOrder(delegate); + assertThat(result, is(expectedResult)); + inOrder.verify(delegate).close(); + } + + @Theory + public void testTerminalOperationClosesDelegateEvenOnException(@FromDataPoints("terminalOperations") TerminalOperation terminalOperation) { + RuntimeException exception = new RuntimeException(); + terminalOperation.apply(doThrow(exception).when(delegate)); + + thrown.expect(is(exception)); + + try { + terminalOperation.apply(inTest); + } finally { + verify(delegate).close(); + } + } + + private Matcher isAutoClosing() { + return is(anyOf(instanceOf(AutoClosingStream.class), instanceOf(AutoClosingDoubleStream.class), instanceOf(AutoClosingIntStream.class), instanceOf(AutoClosingLongStream.class))); + } + + private void verifyDelegate(BaseStream result, BaseStream newDelegate) { + result.close(); + verify(newDelegate).close(); + } + + private interface TerminalOperation { + + T result(); + + T apply(LongStream stream); + + } + + private interface LongermediateOperation { + + Class type(); + + T apply(LongStream stream); + + } + +} diff --git a/main/commons/src/test/java/org/cryptomator/common/streams/AutoClosingStreamTest.java b/main/commons/src/test/java/org/cryptomator/common/streams/AutoClosingStreamTest.java index 475f2fcc1..5f9f27fc8 100644 --- a/main/commons/src/test/java/org/cryptomator/common/streams/AutoClosingStreamTest.java +++ b/main/commons/src/test/java/org/cryptomator/common/streams/AutoClosingStreamTest.java @@ -1,24 +1,160 @@ package org.cryptomator.common.streams; +import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.ToDoubleFunction; +import java.util.function.ToIntFunction; +import java.util.function.ToLongFunction; +import java.util.stream.BaseStream; +import java.util.stream.Collector; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; import java.util.stream.Stream; -import org.cryptomator.common.streams.AutoClosingStream; +import org.hamcrest.Matcher; import org.junit.Before; -import org.junit.Test; +import org.junit.Rule; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.FromDataPoints; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; import org.mockito.InOrder; -@SuppressWarnings("unchecked") +@SuppressWarnings({"unchecked", "rawtypes"}) +@RunWith(Theories.class) public class AutoClosingStreamTest { + private static final Predicate A_PREDICATE = any -> true; + private static final Function A_FUNCTION = any -> null; + private static final ToDoubleFunction A_TO_DOUBLE_FUNCTION = any -> 0d; + private static final ToIntFunction A_TO_INT_FUNCTION = any -> 1; + private static final ToLongFunction A_TO_LONG_FUNCTION = any -> 1L; + private static final Consumer A_CONSUMER = any -> { + }; + private static final Comparator A_COMPARATOR = (left, right) -> 0; + private static final Collector A_COLLECTOR = mock(Collector.class); + private static final BinaryOperator A_BINARY_OPERATOR = (left, right) -> null; + private static final Object AN_OBJECT = new Object(); + private static final IntFunction AN_INT_FUNCTION = i -> null; + private static final BiConsumer A_BICONSUMER = (a, b) -> { + }; + private static final Supplier A_SUPPLIER = () -> null; + + @DataPoints("intermediateOperations") + public static final List> INTERMEDIATE_OPERATIONS = new ArrayList<>(); + + @DataPoints("terminalOperations") + public static final List> TERMINAL_OPERATIONS = new ArrayList<>(); + + static { + // define intermediate operations + test(Stream.class, Stream::distinct); + test(Stream.class, stream -> stream.filter(A_PREDICATE)); + test(Stream.class, stream -> stream.flatMap(A_FUNCTION)); + test(DoubleStream.class, stream -> stream.flatMapToDouble(A_FUNCTION)); + test(IntStream.class, stream -> stream.flatMapToInt(A_FUNCTION)); + test(LongStream.class, stream -> stream.flatMapToLong(A_FUNCTION)); + test(Stream.class, stream -> stream.limit(5)); + test(Stream.class, stream -> stream.map(A_FUNCTION)); + test(DoubleStream.class, stream -> stream.mapToDouble(A_TO_DOUBLE_FUNCTION)); + test(IntStream.class, stream -> stream.mapToInt(A_TO_INT_FUNCTION)); + test(LongStream.class, stream -> stream.mapToLong(A_TO_LONG_FUNCTION)); + test(Stream.class, Stream::parallel); + test(Stream.class, stream -> stream.peek(A_CONSUMER)); + test(Stream.class, Stream::sequential); + test(Stream.class, stream -> stream.skip(5)); + test(Stream.class, Stream::sorted); + test(Stream.class, stream -> stream.sorted(A_COMPARATOR)); + test(Stream.class, Stream::unordered); + + // define terminal operations + test(stream -> stream.allMatch(A_PREDICATE), true); + test(stream -> stream.anyMatch(A_PREDICATE), true); + test(stream -> stream.collect(A_COLLECTOR), new Object()); + test(stream -> stream.collect(A_SUPPLIER, A_BICONSUMER, A_BICONSUMER), new Object()); + test(Stream::count, 3L); + test(Stream::findAny, Optional.of(new Object())); + test(Stream::findFirst, Optional.of(new Object())); + test(stream -> stream.forEach(A_CONSUMER)); + test(stream -> stream.forEachOrdered(A_CONSUMER)); + test(stream -> stream.max(A_COMPARATOR), Optional.of(new Object())); + test(stream -> stream.min(A_COMPARATOR), Optional.of(new Object())); + test(stream -> stream.noneMatch(A_PREDICATE), true); + test(stream -> stream.reduce(A_BINARY_OPERATOR), Optional.of(new Object())); + test(stream -> stream.reduce(AN_OBJECT, A_BINARY_OPERATOR), Optional.of(new Object())); + test(stream -> stream.reduce(AN_OBJECT, A_BINARY_OPERATOR, A_BINARY_OPERATOR), Optional.of(new Object())); + test(Stream::toArray, new Object[1]); + test(stream -> stream.toArray(AN_INT_FUNCTION), new Object[1]); + } + + private static void test(Consumer consumer) { + TERMINAL_OPERATIONS.add(new TerminalOperation() { + @Override + public T result() { + return null; + } + + @Override + public T apply(Stream stream) { + consumer.accept(stream); + return null; + } + }); + } + + private static void test(Function function, T result) { + TERMINAL_OPERATIONS.add(new TerminalOperation() { + @Override + public T result() { + return result; + } + + @Override + public T apply(Stream stream) { + return function.apply(stream); + } + }); + } + + private static void test(Class type, Function function) { + INTERMEDIATE_OPERATIONS.add(new IntermediateOperation() { + @Override + public Class type() { + return type; + } + + @Override + public T apply(Stream stream) { + return function.apply(stream); + } + }); + } + + @Rule + public ExpectedException thrown = ExpectedException.none(); + private Stream delegate; private Stream inTest; @@ -28,33 +164,68 @@ public class AutoClosingStreamTest { inTest = AutoClosingStream.from(delegate); } - @Test - public void testSequentialReturnsNewAutoClosingStream() { - Stream newDelegate = mock(Stream.class); - when(delegate.sequential()).thenReturn(newDelegate); + @Theory + public void testIntermediateOperationReturnsNewAutoClosingStream(@FromDataPoints("intermediateOperations") IntermediateOperation intermediateOperation) { + BaseStream newDelegate = (BaseStream) mock(intermediateOperation.type()); + when(intermediateOperation.apply(delegate)).thenReturn(newDelegate); - Stream result = inTest.sequential(); + BaseStream result = intermediateOperation.apply(inTest); - assertThat(result, is(instanceOf(AutoClosingStream.class))); + assertThat(result, isAutoClosing()); verifyDelegate(result, newDelegate); } - @Test - public void testForEachDelegatesToAndClosesDelegate() { - Consumer consumer = mock(Consumer.class); + @Theory + public void testTerminalOperationDelegatesToAndClosesDelegate(@FromDataPoints("terminalOperations") TerminalOperation terminalOperation) { + Object expectedResult = terminalOperation.result(); + if (expectedResult != null) { + when(terminalOperation.apply(delegate)).thenReturn(expectedResult); + } - inTest.forEach(consumer); + Object result = terminalOperation.apply(inTest); InOrder inOrder = inOrder(delegate); - inOrder.verify(delegate).forEach(consumer); + assertThat(result, is(expectedResult)); inOrder.verify(delegate).close(); } - private void verifyDelegate(Stream result, Stream newDelegate) { + @Theory + public void testTerminalOperationClosesDelegateEvenOnException(@FromDataPoints("terminalOperations") TerminalOperation terminalOperation) { + RuntimeException exception = new RuntimeException(); + terminalOperation.apply(doThrow(exception).when(delegate)); + + thrown.expect(is(exception)); + + try { + terminalOperation.apply(inTest); + } finally { + verify(delegate).close(); + } + } + + private Matcher isAutoClosing() { + return is(anyOf(instanceOf(AutoClosingStream.class), instanceOf(AutoClosingDoubleStream.class), instanceOf(AutoClosingIntStream.class), instanceOf(AutoClosingLongStream.class))); + } + + private void verifyDelegate(BaseStream result, BaseStream newDelegate) { result.close(); verify(newDelegate).close(); } - // TODO Markus Kreusch test additional methods + private interface TerminalOperation { + + T result(); + + T apply(Stream stream); + + } + + private interface IntermediateOperation { + + Class type(); + + T apply(Stream stream); + + } }