1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 */
24package org.openjdk.tests.java.util.stream;
25
26import org.testng.annotations.DataProvider;
27import org.testng.annotations.Factory;
28import org.testng.annotations.Test;
29import org.testng.annotations.BeforeClass;
30
31import java.util.Arrays;
32import java.util.Collection;
33import java.util.Collections;
34import java.util.LinkedHashSet;
35import java.util.LinkedList;
36import java.util.List;
37import java.util.Spliterator;
38import java.util.TreeSet;
39import java.util.stream.DoubleStream;
40import java.util.stream.IntStream;
41import java.util.stream.LongStream;
42import java.util.stream.Stream;
43
44import static org.openjdk.testlib.java.util.stream.LambdaTestHelpers.*;
45import static org.testng.Assert.*;
46
47@Test
48public class ConcatTest {
49    private static Object[][] cases;
50
51    static {
52        List<Integer> part1 = Arrays.asList(5, 3, 4, 1, 2, 6, 2, 4);
53        List<Integer> part2 = Arrays.asList(8, 8, 6, 6, 9, 7, 10, 9);
54        List<Integer> p1p2 = Arrays.asList(5, 3, 4, 1, 2, 6, 2, 4, 8, 8, 6, 6, 9, 7, 10, 9);
55        List<Integer> p2p1 = Arrays.asList(8, 8, 6, 6, 9, 7, 10, 9, 5, 3, 4, 1, 2, 6, 2, 4);
56        List<Integer> empty = new LinkedList<>(); // To be ordered
57        assertTrue(empty.isEmpty());
58        LinkedHashSet<Integer> distinctP1 = new LinkedHashSet<>(part1);
59        LinkedHashSet<Integer> distinctP2 = new LinkedHashSet<>(part2);
60        TreeSet<Integer> sortedP1 = new TreeSet<>(part1);
61        TreeSet<Integer> sortedP2 = new TreeSet<>(part2);
62
63        cases = new Object[][] {
64            { "regular", part1, part2, p1p2 },
65            { "reverse regular", part2, part1, p2p1 },
66            { "front distinct", distinctP1, part2, Arrays.asList(5, 3, 4, 1, 2, 6, 8, 8, 6, 6, 9, 7, 10, 9) },
67            { "back distinct", part1, distinctP2, Arrays.asList(5, 3, 4, 1, 2, 6, 2, 4, 8, 6, 9, 7, 10) },
68            { "both distinct", distinctP1, distinctP2, Arrays.asList(5, 3, 4, 1, 2, 6, 8, 6, 9, 7, 10) },
69            { "front sorted", sortedP1, part2, Arrays.asList(1, 2, 3, 4, 5, 6, 8, 8, 6, 6, 9, 7, 10, 9) },
70            { "back sorted", part1, sortedP2, Arrays.asList(5, 3, 4, 1, 2, 6, 2, 4, 6, 7, 8, 9, 10) },
71            { "both sorted", sortedP1, sortedP2, Arrays.asList(1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10) },
72            { "reverse both sorted", sortedP2, sortedP1, Arrays.asList(6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6) },
73            { "empty something", empty, part1, part1 },
74            { "something empty", part1, empty, part1 },
75            { "empty empty", empty, empty, empty }
76        };
77    }
78
79    @DataProvider(name = "cases")
80    private static Object[][] getCases() {
81        return cases;
82    }
83
84    @Factory(dataProvider = "cases")
85    public static Object[] createTests(String scenario, Collection<Integer> c1, Collection<Integer> c2, Collection<Integer> expected) {
86        return new Object[] {
87            new ConcatTest(scenario, c1, c2, expected)
88        };
89    }
90
91    protected final String scenario;
92    protected final Collection<Integer> c1;
93    protected final Collection<Integer> c2;
94    protected final Collection<Integer> expected;
95
96    public ConcatTest(String scenario, Collection<Integer> c1, Collection<Integer> c2, Collection<Integer> expected) {
97        this.scenario = scenario;
98        this.c1 = c1;
99        this.c2 = c2;
100        this.expected = expected;
101    }
102
103    // Android-changed: Factor out the prerequisites check out of the constructor.
104    // TestNG crashes hard if an assert is thrown in the constructor.
105    // This is done for test readability only and doesn't impact test logic.
106    @BeforeClass
107    public void checkPrerequisites() {
108        // verify prerequisite
109        Stream<Integer> s1s = c1.stream();
110        Stream<Integer> s2s = c2.stream();
111        Stream<Integer> s1p = c1.parallelStream();
112        Stream<Integer> s2p = c2.parallelStream();
113        assertTrue(s1p.isParallel());
114        assertTrue(s2p.isParallel());
115        assertFalse(s1s.isParallel());
116        assertFalse(s2s.isParallel());
117
118        // Android-changed: Also add the class name to easier debug. Doesn't impact logic.
119        assertTrue(s1s.spliterator().hasCharacteristics(Spliterator.ORDERED), c1.getClass().toString());
120        assertTrue(s1p.spliterator().hasCharacteristics(Spliterator.ORDERED), c2.getClass().toString());
121        assertTrue(s2s.spliterator().hasCharacteristics(Spliterator.ORDERED), c1.getClass().toString());
122        assertTrue(s2p.spliterator().hasCharacteristics(Spliterator.ORDERED), c2.getClass().toString());
123    }
124
125    private <T> void assertConcatContent(Spliterator<T> sp, boolean ordered, Spliterator<T> expected) {
126        // concat stream cannot guarantee uniqueness
127        assertFalse(sp.hasCharacteristics(Spliterator.DISTINCT), scenario);
128        // concat stream cannot guarantee sorted
129        assertFalse(sp.hasCharacteristics(Spliterator.SORTED), scenario);
130        // concat stream is ordered if both are ordered
131        assertEquals(sp.hasCharacteristics(Spliterator.ORDERED), ordered, scenario);
132
133        // Verify elements
134        if (ordered) {
135            assertEquals(toBoxedList(sp),
136                         toBoxedList(expected),
137                         scenario);
138        } else {
139            assertEquals(toBoxedMultiset(sp),
140                         toBoxedMultiset(expected),
141                         scenario);
142        }
143    }
144
145    private void assertRefConcat(Stream<Integer> s1, Stream<Integer> s2, boolean parallel, boolean ordered) {
146        Stream<Integer> result = Stream.concat(s1, s2);
147        assertEquals(result.isParallel(), parallel);
148        assertConcatContent(result.spliterator(), ordered, expected.spliterator());
149    }
150
151    private void assertIntConcat(Stream<Integer> s1, Stream<Integer> s2, boolean parallel, boolean ordered) {
152        IntStream result = IntStream.concat(s1.mapToInt(Integer::intValue),
153                                            s2.mapToInt(Integer::intValue));
154        assertEquals(result.isParallel(), parallel);
155        assertConcatContent(result.spliterator(), ordered,
156                            expected.stream().mapToInt(Integer::intValue).spliterator());
157    }
158
159    private void assertLongConcat(Stream<Integer> s1, Stream<Integer> s2, boolean parallel, boolean ordered) {
160        LongStream result = LongStream.concat(s1.mapToLong(Integer::longValue),
161                                              s2.mapToLong(Integer::longValue));
162        assertEquals(result.isParallel(), parallel);
163        assertConcatContent(result.spliterator(), ordered,
164                            expected.stream().mapToLong(Integer::longValue).spliterator());
165    }
166
167    private void assertDoubleConcat(Stream<Integer> s1, Stream<Integer> s2, boolean parallel, boolean ordered) {
168        DoubleStream result = DoubleStream.concat(s1.mapToDouble(Integer::doubleValue),
169                                                  s2.mapToDouble(Integer::doubleValue));
170        assertEquals(result.isParallel(), parallel);
171        assertConcatContent(result.spliterator(), ordered,
172                            expected.stream().mapToDouble(Integer::doubleValue).spliterator());
173    }
174
175    public void testRefConcat() {
176        // sequential + sequential -> sequential
177        assertRefConcat(c1.stream(), c2.stream(), false, true);
178        // parallel + parallel -> parallel
179        assertRefConcat(c1.parallelStream(), c2.parallelStream(), true, true);
180        // sequential + parallel -> parallel
181        assertRefConcat(c1.stream(), c2.parallelStream(), true, true);
182        // parallel + sequential -> parallel
183        assertRefConcat(c1.parallelStream(), c2.stream(), true, true);
184
185        // not ordered
186        assertRefConcat(c1.stream().unordered(), c2.stream(), false, false);
187        assertRefConcat(c1.stream(), c2.stream().unordered(), false, false);
188        assertRefConcat(c1.parallelStream().unordered(), c2.stream().unordered(), true, false);
189    }
190
191    public void testIntConcat() {
192        // sequential + sequential -> sequential
193        assertIntConcat(c1.stream(), c2.stream(), false, true);
194        // parallel + parallel -> parallel
195        assertIntConcat(c1.parallelStream(), c2.parallelStream(), true, true);
196        // sequential + parallel -> parallel
197        assertIntConcat(c1.stream(), c2.parallelStream(), true, true);
198        // parallel + sequential -> parallel
199        assertIntConcat(c1.parallelStream(), c2.stream(), true, true);
200
201        // not ordered
202        assertIntConcat(c1.stream().unordered(), c2.stream(), false, false);
203        assertIntConcat(c1.stream(), c2.stream().unordered(), false, false);
204        assertIntConcat(c1.parallelStream().unordered(), c2.stream().unordered(), true, false);
205    }
206
207    public void testLongConcat() {
208        // sequential + sequential -> sequential
209        assertLongConcat(c1.stream(), c2.stream(), false, true);
210        // parallel + parallel -> parallel
211        assertLongConcat(c1.parallelStream(), c2.parallelStream(), true, true);
212        // sequential + parallel -> parallel
213        assertLongConcat(c1.stream(), c2.parallelStream(), true, true);
214        // parallel + sequential -> parallel
215        assertLongConcat(c1.parallelStream(), c2.stream(), true, true);
216
217        // not ordered
218        assertLongConcat(c1.stream().unordered(), c2.stream(), false, false);
219        assertLongConcat(c1.stream(), c2.stream().unordered(), false, false);
220        assertLongConcat(c1.parallelStream().unordered(), c2.stream().unordered(), true, false);
221    }
222
223    public void testDoubleConcat() {
224        // sequential + sequential -> sequential
225        assertDoubleConcat(c1.stream(), c2.stream(), false, true);
226        // parallel + parallel -> parallel
227        assertDoubleConcat(c1.parallelStream(), c2.parallelStream(), true, true);
228        // sequential + parallel -> parallel
229        assertDoubleConcat(c1.stream(), c2.parallelStream(), true, true);
230        // parallel + sequential -> parallel
231        assertDoubleConcat(c1.parallelStream(), c2.stream(), true, true);
232
233        // not ordered
234        assertDoubleConcat(c1.stream().unordered(), c2.stream(), false, false);
235        assertDoubleConcat(c1.stream(), c2.stream().unordered(), false, false);
236        assertDoubleConcat(c1.parallelStream().unordered(), c2.stream().unordered(), true, false);
237    }
238}
239