1/*
2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/**
25 * @test
26 * @summary Comparator default method tests
27 * @run testng BasicTest
28 */
29
30import java.util.TreeMap;
31import java.util.Comparator;
32import org.testng.annotations.Test;
33
34import java.util.function.Function;
35import java.util.function.ToIntFunction;
36import java.util.function.ToLongFunction;
37import java.util.function.ToDoubleFunction;
38
39import static org.testng.Assert.assertEquals;
40import static org.testng.Assert.assertTrue;
41import static org.testng.Assert.fail;
42
43@Test(groups = "unit")
44public class BasicTest {
45    private static class Thing {
46        public final int intField;
47        public final long longField;
48        public final double doubleField;
49        public final String stringField;
50
51        private Thing(int intField, long longField, double doubleField, String stringField) {
52            this.intField = intField;
53            this.longField = longField;
54            this.doubleField = doubleField;
55            this.stringField = stringField;
56        }
57
58        public int getIntField() {
59            return intField;
60        }
61
62        public long getLongField() {
63            return longField;
64        }
65
66        public double getDoubleField() {
67            return doubleField;
68        }
69
70        public String getStringField() {
71            return stringField;
72        }
73    }
74
75    private final int[] intValues = { -2, -2, -1, -1, 0, 0, 1, 1, 2, 2 };
76    private final long[] longValues = { -2, -2, -1, -1, 0, 0, 1, 1, 2, 2 };
77    private final double[] doubleValues = { -2, -2, -1, -1, 0, 0, 1, 1, 2, 2 };
78    private final String[] stringValues = { "a", "a", "b", "b", "c", "c", "d", "d", "e", "e" };
79    private final int[] comparisons = { 0, -1, 0, -1, 0, -1, 0, -1, 0 };
80
81    private<T> void assertComparisons(T[] things, Comparator<T> comp, int[] comparisons) {
82        for (int i=0; i<comparisons.length; i++) {
83            assertEquals(comparisons.length + 1, things.length);
84            assertEquals(comparisons[i], comp.compare(things[i], things[i+1]));
85            assertEquals(-comparisons[i], comp.compare(things[i+1], things[i]));
86        }
87    }
88
89    public void testIntComparator() {
90        Thing[] things = new Thing[intValues.length];
91        for (int i=0; i<intValues.length; i++)
92            things[i] = new Thing(intValues[i], 0L, 0.0, null);
93        Comparator<Thing> comp = Comparator.comparingInt(new ToIntFunction<Thing>() {
94            @Override
95            public int applyAsInt(Thing thing) {
96                return thing.getIntField();
97            }
98        });
99
100        assertComparisons(things, comp, comparisons);
101    }
102
103    public void testLongComparator() {
104        Thing[] things = new Thing[longValues.length];
105        for (int i=0; i<longValues.length; i++)
106            things[i] = new Thing(0, longValues[i], 0.0, null);
107        Comparator<Thing> comp = Comparator.comparingLong(new ToLongFunction<Thing>() {
108            @Override
109            public long applyAsLong(Thing thing) {
110                return thing.getLongField();
111            }
112        });
113
114        assertComparisons(things, comp, comparisons);
115    }
116
117    public void testDoubleComparator() {
118        Thing[] things = new Thing[doubleValues.length];
119        for (int i=0; i<doubleValues.length; i++)
120            things[i] = new Thing(0, 0L, doubleValues[i], null);
121        Comparator<Thing> comp = Comparator.comparingDouble(new ToDoubleFunction<Thing>() {
122            @Override
123            public double applyAsDouble(Thing thing) {
124                return thing.getDoubleField();
125            }
126        });
127
128        assertComparisons(things, comp, comparisons);
129    }
130
131    public void testComparing() {
132        Thing[] things = new Thing[doubleValues.length];
133        for (int i=0; i<doubleValues.length; i++)
134            things[i] = new Thing(0, 0L, 0.0, stringValues[i]);
135        Comparator<Thing> comp = Comparator.comparing(new Function<Thing, String>() {
136            @Override
137            public String apply(Thing thing) {
138                return thing.getStringField();
139            }
140        });
141
142        assertComparisons(things, comp, comparisons);
143    }
144
145    public void testNaturalOrderComparator() {
146        Comparator<String> comp = Comparator.naturalOrder();
147
148        assertComparisons(stringValues, comp, comparisons);
149    }
150
151    public void testReverseComparator() {
152        Comparator<String> cmpr = Comparator.reverseOrder();
153        Comparator<String> cmp = cmpr.reversed();
154
155        assertEquals(cmp.reversed(), cmpr);
156        assertEquals(0, cmp.compare("a", "a"));
157        assertEquals(0, cmpr.compare("a", "a"));
158        assertTrue(cmp.compare("a", "b") < 0);
159        assertTrue(cmpr.compare("a", "b") > 0);
160        assertTrue(cmp.compare("b", "a") > 0);
161        assertTrue(cmpr.compare("b", "a") < 0);
162    }
163
164    public void testReverseComparator2() {
165        Comparator<String> cmp = (s1, s2) -> s1.length() - s2.length();
166        Comparator<String> cmpr = cmp.reversed();
167
168        assertEquals(cmpr.reversed(), cmp);
169        assertEquals(0, cmp.compare("abc", "def"));
170        assertEquals(0, cmpr.compare("abc", "def"));
171        assertTrue(cmp.compare("abcd", "def") > 0);
172        assertTrue(cmpr.compare("abcd", "def") < 0);
173        assertTrue(cmp.compare("abc", "defg") < 0);
174        assertTrue(cmpr.compare("abc", "defg") > 0);
175    }
176
177    private <T> void assertComparison(Comparator<T> cmp, T less, T greater) {
178        assertTrue(cmp.compare(less, greater) < 0, "less");
179        assertTrue(cmp.compare(less, less) == 0, "equal");
180        assertTrue(cmp.compare(greater, greater) == 0, "equal");
181        assertTrue(cmp.compare(greater, less) > 0, "greater");
182    }
183
184    private static class People {
185        final String firstName;
186        final String lastName;
187        final int age;
188
189        People(String first, String last, int age) {
190            firstName = first;
191            lastName = last;
192            this.age = age;
193        }
194
195        String getFirstName() { return firstName; }
196        String getLastName() { return lastName; }
197        int getAge() { return age; }
198        long getAgeAsLong() { return (long) age; };
199        double getAgeAsDouble() { return (double) age; };
200    }
201
202    private final People people[] = {
203        new People("John", "Doe", 34),
204        new People("Mary", "Doe", 30),
205        new People("Maria", "Doe", 14),
206        new People("Jonah", "Doe", 10),
207        new People("John", "Cook", 54),
208        new People("Mary", "Cook", 50),
209        new People("Mary", null, 25),
210        new People("John", null, 27)
211    };
212
213    public void testComparatorDefaultMethods() {
214        Comparator<People> cmp = Comparator.comparing(People::getFirstName);
215        Comparator<People> cmp2 = Comparator.comparing(People::getLastName);
216        // reverseOrder
217        assertComparison(cmp.reversed(), people[1], people[0]);
218        // thenComparing(Comparator)
219        assertComparison(cmp.thenComparing(cmp2), people[0], people[1]);
220        assertComparison(cmp.thenComparing(cmp2), people[4], people[0]);
221        // thenComparing(Function)
222        assertComparison(cmp.thenComparing(People::getLastName), people[0], people[1]);
223        assertComparison(cmp.thenComparing(People::getLastName), people[4], people[0]);
224        // thenComparing(ToIntFunction)
225        assertComparison(cmp.thenComparingInt(People::getAge), people[0], people[1]);
226        assertComparison(cmp.thenComparingInt(People::getAge), people[1], people[5]);
227        // thenComparing(ToLongFunction)
228        assertComparison(cmp.thenComparingLong(People::getAgeAsLong), people[0], people[1]);
229        assertComparison(cmp.thenComparingLong(People::getAgeAsLong), people[1], people[5]);
230        // thenComparing(ToDoubleFunction)
231        assertComparison(cmp.thenComparingDouble(People::getAgeAsDouble), people[0], people[1]);
232        assertComparison(cmp.thenComparingDouble(People::getAgeAsDouble), people[1], people[5]);
233    }
234
235
236    public void testNullsFirst() {
237        Comparator<String> strcmp = Comparator.nullsFirst(Comparator.naturalOrder());
238        Comparator<People> cmp = Comparator.comparing(People::getLastName, strcmp)
239                                           .thenComparing(People::getFirstName, strcmp);
240        // Mary.null vs Mary.Cook - solve by last name
241        assertComparison(cmp, people[6], people[5]);
242        // John.null vs Mary.null - solve by first name
243        assertComparison(cmp, people[7], people[6]);
244
245        // More than one thenComparing
246        strcmp = Comparator.nullsFirst(Comparator.comparingInt(String::length)
247                                                 .thenComparing(String.CASE_INSENSITIVE_ORDER));
248        assertComparison(strcmp, null, "abc");
249        assertComparison(strcmp, "ab", "abc");
250        assertComparison(strcmp, "abc", "def");
251        assertEquals(0, strcmp.compare("abc", "ABC"));
252
253        // Ensure reverse still handle null properly
254        Comparator<String> strcmp2 = strcmp.reversed().thenComparing(Comparator.naturalOrder());
255        assertComparison(strcmp2, "abc", null);
256        assertComparison(strcmp2, "abc", "ab");
257        assertComparison(strcmp2, "def", "abc");
258        assertComparison(strcmp2, "ABC", "abc");
259
260        // Considering non-null values to be equal
261        Comparator<String> blind = Comparator.nullsFirst(null);
262        assertComparison(blind, null, "abc");
263        assertEquals(0, blind.compare("abc", "def"));
264        // reverse still consider non-null values to be equal
265        strcmp = blind.reversed();
266        assertComparison(strcmp, "abc", null);
267        assertEquals(0, strcmp.compare("abc", "def"));
268        // chain with another comparator to compare non-nulls
269        strcmp = blind.thenComparing(Comparator.naturalOrder());
270        assertComparison(strcmp, null, "abc");
271        assertComparison(strcmp, "abc", "def");
272    }
273
274    public void testNullsLast() {
275        Comparator<String> strcmp = Comparator.nullsLast(Comparator.naturalOrder());
276        Comparator<People> cmp = Comparator.comparing(People::getLastName, strcmp)
277                                           .thenComparing(People::getFirstName, strcmp);
278        // Mary.null vs Mary.Cook - solve by last name
279        assertComparison(cmp, people[5], people[6]);
280        // John.null vs Mary.null - solve by first name
281        assertComparison(cmp, people[7], people[6]);
282
283        // More than one thenComparing
284        strcmp = Comparator.nullsLast(Comparator.comparingInt(String::length)
285                                                .thenComparing(String.CASE_INSENSITIVE_ORDER));
286        assertComparison(strcmp, "abc", null);
287        assertComparison(strcmp, "ab", "abc");
288        assertComparison(strcmp, "abc", "def");
289
290        // Ensure reverse still handle null properly
291        Comparator<String> strcmp2 = strcmp.reversed().thenComparing(Comparator.naturalOrder());
292        assertComparison(strcmp2, null, "abc");
293        assertComparison(strcmp2, "abc", "ab");
294        assertComparison(strcmp2, "def", "abc");
295        assertComparison(strcmp2, "ABC", "abc");
296
297        // Considering non-null values to be equal
298        Comparator<String> blind = Comparator.nullsLast(null);
299        assertComparison(blind, "abc", null);
300        assertEquals(0, blind.compare("abc", "def"));
301        // reverse still consider non-null values to be equal
302        strcmp = blind.reversed();
303        assertComparison(strcmp, null, "abc");
304        assertEquals(0, strcmp.compare("abc", "def"));
305        // chain with another comparator to compare non-nulls
306        strcmp = blind.thenComparing(Comparator.naturalOrder());
307        assertComparison(strcmp, "abc", null);
308        assertComparison(strcmp, "abc", "def");
309    }
310
311    public void testComposeComparator() {
312        // Longer string in front
313        Comparator<String> first = (s1, s2) -> s2.length() - s1.length();
314        Comparator<String> second = Comparator.naturalOrder();
315        Comparator<String> composed = first.thenComparing(second);
316
317        assertTrue(composed.compare("abcdefg", "abcdef") < 0);
318        assertTrue(composed.compare("abcdef", "abcdefg") > 0);
319        assertTrue(composed.compare("abcdef", "abcdef") == 0);
320        assertTrue(composed.compare("abcdef", "ghijkl") < 0);
321        assertTrue(composed.compare("ghijkl", "abcdefg") > 0);
322    }
323
324    public void testNulls() {
325        try {
326            Comparator.<String>naturalOrder().compare("abc", (String) null);
327            fail("expected NPE with naturalOrder");
328        } catch (NullPointerException npe) {}
329        try {
330            Comparator.<String>naturalOrder().compare((String) null, "abc");
331            fail("expected NPE with naturalOrder");
332        } catch (NullPointerException npe) {}
333
334        try {
335            Comparator.<String>reverseOrder().compare("abc", (String) null);
336            fail("expected NPE with naturalOrder");
337        } catch (NullPointerException npe) {}
338        try {
339            Comparator.<String>reverseOrder().compare((String) null, "abc");
340            fail("expected NPE with naturalOrder");
341        } catch (NullPointerException npe) {}
342
343        try {
344            Comparator<People> cmp = Comparator.comparing(null, Comparator.<String>naturalOrder());
345            fail("comparing(null, cmp) should throw NPE");
346        } catch (NullPointerException npe) {}
347        try {
348            Comparator<People> cmp = Comparator.comparing(People::getFirstName, null);
349            fail("comparing(f, null) should throw NPE");
350        } catch (NullPointerException npe) {}
351
352        try {
353            Comparator<People> cmp = Comparator.comparing(null);
354            fail("comparing(null) should throw NPE");
355        } catch (NullPointerException npe) {}
356        try {
357            Comparator<People> cmp = Comparator.comparingInt(null);
358            fail("comparing(null) should throw NPE");
359        } catch (NullPointerException npe) {}
360        try {
361            Comparator<People> cmp = Comparator.comparingLong(null);
362            fail("comparing(null) should throw NPE");
363        } catch (NullPointerException npe) {}
364        try {
365            Comparator<People> cmp = Comparator.comparingDouble(null);
366            fail("comparing(null) should throw NPE");
367        } catch (NullPointerException npe) {}
368    }
369}
370