001/**
002 * Powerunit - A JDK1.8 test framework
003 * Copyright (C) 2014 Mathieu Boretti.
004 *
005 * This file is part of Powerunit
006 *
007 * Powerunit is free software: you can redistribute it and/or modify
008 * it under the terms of the GNU General Public License as published by
009 * the Free Software Foundation, either version 3 of the License, or
010 * (at your option) any later version.
011 *
012 * Powerunit is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015 * GNU General Public License for more details.
016 *
017 * You should have received a copy of the GNU General Public License
018 * along with Powerunit. If not, see <http://www.gnu.org/licenses/>.
019 */
020package ch.powerunit.collector;
021
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.Collections;
025import java.util.List;
026import java.util.Objects;
027import java.util.stream.Collector;
028import java.util.stream.Collector.Characteristics;
029import java.util.stream.Stream;
030
031import org.hamcrest.Matcher;
032
033import ch.powerunit.TestInterface;
034import ch.powerunit.TestSuite;
035import ch.powerunit.collector.impl.CollectorTesterImpl;
036import ch.powerunit.collector.lang.CollectorTesterDSL0;
037import ch.powerunit.collector.lang.CollectorTesterDSL1;
038import ch.powerunit.collector.lang.CollectorTesterDSL2;
039import ch.powerunit.collector.lang.CollectorTesterDSL3;
040
041/**
042 * This is a tester for {@link Collector}.
043 * <p>
044 * The goal of this tester is to validate a {@link Collector}. The following
045 * tests are done :
046 * <ol>
047 * <li><code>{@link Collector#accumulator() accumulator()}</code> must return a
048 * not null result.</li>
049 * <li><code>{@link Collector#combiner() combiner()}</code> must return a not
050 * null result.</li>
051 * <li><code>{@link Collector#finisher() finisher()}</code> must return a not
052 * null result.</li>
053 * <li><code>{@link Collector#supplier() supplier()}</code> must return a not
054 * null result.</li>
055 * <li><code>{@link Collector#characteristics() characteristics()}</code> must
056 * return a not null result and be the same list that is specified in this
057 * tester.</li>
058 * <li>For each received sample either use it -
059 * {@link ch.powerunit.collector.lang.CollectorTesterDSL0#withInput(Stream)
060 * withInput} - or create a stream -
061 * {@link ch.powerunit.collector.lang.CollectorTesterDSL0#withStreamFromList(List)
062 * withStreamFromList} or
063 * {@link ch.powerunit.collector.lang.CollectorTesterDSL0#withParallelStreamFromList(List)
064 * withParallelStreamFromList} - and then execute the method
065 * {@link java.util.stream.Stream#collect(Collector) collect} with the
066 * {@link Collector} under test and validate the returned result.</li>
067 * </ol>
068 * 
069 * @author borettim
070 * @since 0.4.0
071 * @param <T>
072 *            the input type of the {@link java.util.stream.Collector Collector}
073 *            .
074 * @param <A>
075 *            the accumulator type of the {@link java.util.stream.Collector
076 *            Collector}.
077 * @param <R>
078 *            the return type of the {@link java.util.stream.Collector
079 *            Collector}.
080 */
081@TestInterface(CollectorTesterImpl.class)
082public final class CollectorTester<T, A, R> {
083        private final Collector<T, A, R> collectorToTest;
084
085        private final List<Stream<T>> inputs;
086
087        private final List<Matcher<? super R>> results;
088
089        private final Matcher<Iterable<? extends Characteristics>> expectedCharacteristics;
090
091        private CollectorTester(Collector<T, A, R> collectorToTest,
092                        List<Stream<T>> inputs, List<Matcher<? super R>> results,
093                        Matcher<Iterable<? extends Characteristics>> expectedCharacteristics) {
094                this.collectorToTest = collectorToTest;
095                this.inputs = inputs;
096                this.results = results;
097                this.expectedCharacteristics = expectedCharacteristics;
098        }
099
100        /**
101         * Return a builder to create a tester of {@link java.util.stream.Collector
102         * Collector}.
103         * 
104         * @param collectorToTest
105         *            the {@link java.util.stream.Collector Collector} to be tested.
106         * @param <T>
107         *            the input type of the {@link java.util.stream.Collector
108         *            Collector} .
109         * 
110         * @param <R>
111         *            the return type of the {@link java.util.stream.Collector
112         *            Collector}.
113         * @return {@link CollectorTesterDSL0 the DSL to build the tester}
114         */
115        @SuppressWarnings({ "rawtypes", "unchecked" })
116        public static <T, R> CollectorTesterDSL0<T, ?, R> of(
117                        Collector<T, ?, R> collectorToTest) {
118                return new CollectorTesterDSL(collectorToTest);
119        }
120
121        /**
122         * Return a builder to create a tester of {@link java.util.stream.Collector
123         * Collector}.
124         * 
125         * @param inputClass
126         *            the class of the input of the
127         *            {@link java.util.stream.Collector Collector}.
128         * @param outputClass
129         *            the class of the output of the
130         *            {@link java.util.stream.Collector Collector}.
131         * @param collectorToTest
132         *            the {@link java.util.stream.Collector Collector} to be tested.
133         * @param <T>
134         *            the input type of the {@link java.util.stream.Collector
135         *            Collector}.
136         * 
137         * @param <R>
138         *            the return type of the {@link java.util.stream.Collector
139         *            Collector}.
140         * @return {@link CollectorTesterDSL0 the DSL to build the tester}
141         */
142        @SuppressWarnings({ "rawtypes", "unchecked" })
143        public static <T, R> CollectorTesterDSL0<T, ?, R> of(Class<T> inputClass,
144                        Class<T> outputClass, Collector<T, ?, R> collectorToTest) {
145                return new CollectorTesterDSL(collectorToTest);
146        }
147
148        private static class CollectorTesterDSL<T, A, R> implements
149                        CollectorTesterDSL0<T, A, R>, CollectorTesterDSL1<T, A, R>,
150                        CollectorTesterDSL2<T, A, R> {
151                private final Collector<T, A, R> collectorToTest;
152
153                private final List<Stream<T>> inputs = new ArrayList<>();
154
155                private final List<Matcher<? super R>> results = new ArrayList<>();
156
157                private Matcher<Iterable<? extends Characteristics>> expectedCharacteristics;
158
159                public CollectorTesterDSL(Collector<T, A, R> collectorToTest) {
160                        this.collectorToTest = collectorToTest;
161                }
162
163                @Override
164                public CollectorTesterDSL3<T, A, R> havingCharacteristics(
165                                Characteristics... expectedCharacteristics) {
166                        this.expectedCharacteristics = TestSuite.DSL
167                                        .<Collector.Characteristics> containsInAnyOrder(expectedCharacteristics);
168                        return this;
169                }
170
171                @Override
172                public CollectorTesterDSL2<T, A, R> withInput(Stream<T> input) {
173                        inputs.add(Objects.requireNonNull(input, "input can't be null"));
174                        return this;
175                }
176
177                @Override
178                public CollectorTesterDSL2<T, A, R> withStreamFromList(List<T> input) {
179                        return withInput(Objects.requireNonNull(input,
180                                        "input can't be null").stream());
181                }
182
183                @Override
184                public CollectorTesterDSL2<T, A, R> withParallelStreamFromList(
185                                List<T> input) {
186                        return withInput(Objects.requireNonNull(input,
187                                        "input can't be null").parallelStream());
188                }
189
190                @Override
191                public CollectorTesterDSL2<T, A, R> withStreamFromArray(
192                                @SuppressWarnings("unchecked") T... input) {
193                        return withInput(Arrays.stream(input));
194                }
195
196                @SuppressWarnings("unchecked")
197                @Override
198                public CollectorTesterDSL2<T, A, R> withStreamFromArray(T first) {
199                        return withStreamFromArray((T[]) new Object[] { first });
200                }
201
202                @SuppressWarnings("unchecked")
203                @Override
204                public CollectorTesterDSL2<T, A, R> withStreamFromArray(T first,
205                                T second) {
206                        return withStreamFromArray((T[]) new Object[] { first, second });
207                }
208
209                @SuppressWarnings("unchecked")
210                @Override
211                public CollectorTesterDSL2<T, A, R> withStreamFromArray(T first,
212                                T second, T third) {
213                        return withStreamFromArray((T[]) new Object[] { first, second,
214                                        third });
215                }
216
217                @SuppressWarnings("unchecked")
218                @Override
219                public CollectorTesterDSL2<T, A, R> withStreamFromArray(T first,
220                                T second, T third, T fourth) {
221                        return withStreamFromArray((T[]) new Object[] { first, second,
222                                        third, fourth });
223                }
224
225                @SuppressWarnings("unchecked")
226                @Override
227                public CollectorTesterDSL2<T, A, R> withStreamFromArray(T first,
228                                T second, T third, T fourth, T fifth) {
229                        return withStreamFromArray((T[]) new Object[] { first, second,
230                                        third, fourth, fifth });
231                }
232
233                @Override
234                public CollectorTesterDSL1<T, A, R> expecting(
235                                Matcher<? super R> matching) {
236                        results.add(Objects.requireNonNull(matching,
237                                        "matching can't be null"));
238                        return this;
239                }
240
241                @Override
242                public CollectorTesterDSL1<T, A, R> expecting(R value) {
243                        return expecting(TestSuite.DSL.equalTo(value));
244                }
245
246                @Override
247                public CollectorTesterDSL1<T, A, R> expectingNull() {
248                        return expecting(TestSuite.DSL.nullValue());
249                }
250
251                @Override
252                public CollectorTester<T, A, R> build() {
253                        return new CollectorTester<T, A, R>(collectorToTest, inputs,
254                                        results,
255                                        expectedCharacteristics == null ? TestSuite.DSL
256                                                        .emptyIterable() : expectedCharacteristics);
257                }
258
259        }
260
261        /**
262         * Used by the framework.
263         * 
264         * @return the collectorToTest
265         */
266        public Collector<T, A, R> getCollectorToTest() {
267                return collectorToTest;
268        }
269
270        /**
271         * Used by the framework.
272         * 
273         * @return the inputs
274         */
275        public List<Stream<T>> getInputs() {
276                return Collections.unmodifiableList(inputs);
277        }
278
279        /**
280         * Used by the framework.
281         * 
282         * @return the results
283         */
284        public List<Matcher<? super R>> getResults() {
285                return Collections.unmodifiableList(results);
286        }
287
288        /**
289         * Used by the framework.
290         * 
291         * @return the expectedCharacteristics
292         */
293        public Matcher<Iterable<? extends Characteristics>> getExpectedCharacteristics() {
294                return expectedCharacteristics;
295        }
296
297}