1 /*
2  * Copyright (c) 2013, 2017, 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  * @bug 8171826
27  * @summary Comparator default method tests
28  * @run testng BasicTest
29  */
30 
31 import org.testng.annotations.Test;
32 
33 import java.util.Collections;
34 import java.util.Comparator;
35 import java.util.function.Function;
36 import java.util.function.ToDoubleFunction;
37 import java.util.function.ToIntFunction;
38 import java.util.function.ToLongFunction;
39 
40 import static org.testng.Assert.*;
41 
42 @Test(groups = "unit")
43 public class BasicTest {
44     private static class Thing {
45         public final int intField;
46         public final long longField;
47         public final double doubleField;
48         public final String stringField;
49 
Thing(int intField, long longField, double doubleField, String stringField)50         private Thing(int intField, long longField, double doubleField, String stringField) {
51             this.intField = intField;
52             this.longField = longField;
53             this.doubleField = doubleField;
54             this.stringField = stringField;
55         }
56 
getIntField()57         public int getIntField() {
58             return intField;
59         }
60 
getLongField()61         public long getLongField() {
62             return longField;
63         }
64 
getDoubleField()65         public double getDoubleField() {
66             return doubleField;
67         }
68 
getStringField()69         public String getStringField() {
70             return stringField;
71         }
72     }
73 
74     private final int[] intValues = { -2, -2, -1, -1, 0, 0, 1, 1, 2, 2 };
75     private final long[] longValues = { -2, -2, -1, -1, 0, 0, 1, 1, 2, 2 };
76     private final double[] doubleValues = { -2, -2, -1, -1, 0, 0, 1, 1, 2, 2 };
77     private final String[] stringValues = { "a", "a", "b", "b", "c", "c", "d", "d", "e", "e" };
78     private final int[] comparisons = { 0, -1, 0, -1, 0, -1, 0, -1, 0 };
79 
assertComparisons(T[] things, Comparator<T> comp, int[] comparisons)80     private<T> void assertComparisons(T[] things, Comparator<T> comp, int[] comparisons) {
81         for (int i=0; i<comparisons.length; i++) {
82             assertEquals(comparisons.length + 1, things.length);
83             assertEquals(comparisons[i], comp.compare(things[i], things[i+1]));
84             assertEquals(-comparisons[i], comp.compare(things[i+1], things[i]));
85         }
86     }
87 
testIntComparator()88     public void testIntComparator() {
89         Thing[] things = new Thing[intValues.length];
90         for (int i=0; i<intValues.length; i++)
91             things[i] = new Thing(intValues[i], 0L, 0.0, null);
92         Comparator<Thing> comp = Comparator.comparingInt(new ToIntFunction<Thing>() {
93             @Override
94             public int applyAsInt(Thing thing) {
95                 return thing.getIntField();
96             }
97         });
98 
99         assertComparisons(things, comp, comparisons);
100     }
101 
testLongComparator()102     public void testLongComparator() {
103         Thing[] things = new Thing[longValues.length];
104         for (int i=0; i<longValues.length; i++)
105             things[i] = new Thing(0, longValues[i], 0.0, null);
106         Comparator<Thing> comp = Comparator.comparingLong(new ToLongFunction<Thing>() {
107             @Override
108             public long applyAsLong(Thing thing) {
109                 return thing.getLongField();
110             }
111         });
112 
113         assertComparisons(things, comp, comparisons);
114     }
115 
testDoubleComparator()116     public void testDoubleComparator() {
117         Thing[] things = new Thing[doubleValues.length];
118         for (int i=0; i<doubleValues.length; i++)
119             things[i] = new Thing(0, 0L, doubleValues[i], null);
120         Comparator<Thing> comp = Comparator.comparingDouble(new ToDoubleFunction<Thing>() {
121             @Override
122             public double applyAsDouble(Thing thing) {
123                 return thing.getDoubleField();
124             }
125         });
126 
127         assertComparisons(things, comp, comparisons);
128     }
129 
testComparing()130     public void testComparing() {
131         Thing[] things = new Thing[doubleValues.length];
132         for (int i=0; i<doubleValues.length; i++)
133             things[i] = new Thing(0, 0L, 0.0, stringValues[i]);
134         Comparator<Thing> comp = Comparator.comparing(new Function<Thing, String>() {
135             @Override
136             public String apply(Thing thing) {
137                 return thing.getStringField();
138             }
139         });
140 
141         assertComparisons(things, comp, comparisons);
142     }
143 
testNaturalOrderComparator()144     public void testNaturalOrderComparator() {
145         Comparator<String> comp = Comparator.naturalOrder();
146 
147         assertComparisons(stringValues, comp, comparisons);
148     }
149 
testReverseComparator()150     public void testReverseComparator() {
151         Comparator<String> cmpr = Comparator.reverseOrder();
152         Comparator<String> cmp = cmpr.reversed();
153 
154         assertEquals(cmp.reversed(), cmpr);
155         assertEquals(0, cmp.compare("a", "a"));
156         assertEquals(0, cmpr.compare("a", "a"));
157         assertTrue(cmp.compare("a", "b") < 0);
158         assertTrue(cmpr.compare("a", "b") > 0);
159         assertTrue(cmp.compare("b", "a") > 0);
160         assertTrue(cmpr.compare("b", "a") < 0);
161     }
162 
163     public void testReverseComparator2() {
164         Comparator<String> cmp = (s1, s2) -> s1.length() - s2.length();
165         Comparator<String> cmpr = cmp.reversed();
166 
167         assertEquals(cmpr.reversed(), cmp);
168         assertEquals(0, cmp.compare("abc", "def"));
169         assertEquals(0, cmpr.compare("abc", "def"));
170         assertTrue(cmp.compare("abcd", "def") > 0);
171         assertTrue(cmpr.compare("abcd", "def") < 0);
172         assertTrue(cmp.compare("abc", "defg") < 0);
173         assertTrue(cmpr.compare("abc", "defg") > 0);
174     }
175 
176     private <T> void assertComparison(Comparator<T> cmp, T less, T greater) {
177         assertTrue(cmp.compare(less, greater) < 0, "less");
178         assertTrue(cmp.compare(less, less) == 0, "equal");
179         assertTrue(cmp.compare(greater, greater) == 0, "equal");
180         assertTrue(cmp.compare(greater, less) > 0, "greater");
181     }
182 
183     private static class People {
184         final String firstName;
185         final String lastName;
186         final int age;
187 
188         People(String first, String last, int age) {
189             firstName = first;
190             lastName = last;
191             this.age = age;
192         }
193 
194         String getFirstName() { return firstName; }
195         String getLastName() { return lastName; }
196         int getAge() { return age; }
197         long getAgeAsLong() { return (long) age; };
198         double getAgeAsDouble() { return (double) age; };
199     }
200 
201     private final People people[] = {
202         new People("John", "Doe", 34),
203         new People("Mary", "Doe", 30),
204         new People("Maria", "Doe", 14),
205         new People("Jonah", "Doe", 10),
206         new People("John", "Cook", 54),
207         new People("Mary", "Cook", 50),
208         new People("Mary", null, 25),
209         new People("John", null, 27)
210     };
211 
212     public void testComparatorDefaultMethods() {
213         Comparator<People> cmp = Comparator.comparing(People::getFirstName);
214         Comparator<People> cmp2 = Comparator.comparing(People::getLastName);
215         // reverseOrder
216         assertComparison(cmp.reversed(), people[1], people[0]);
217         // thenComparing(Comparator)
218         assertComparison(cmp.thenComparing(cmp2), people[0], people[1]);
219         assertComparison(cmp.thenComparing(cmp2), people[4], people[0]);
220         // thenComparing(Function)
221         assertComparison(cmp.thenComparing(People::getLastName), people[0], people[1]);
222         assertComparison(cmp.thenComparing(People::getLastName), people[4], people[0]);
223         // thenComparing(ToIntFunction)
224         assertComparison(cmp.thenComparingInt(People::getAge), people[0], people[1]);
225         assertComparison(cmp.thenComparingInt(People::getAge), people[1], people[5]);
226         // thenComparing(ToLongFunction)
227         assertComparison(cmp.thenComparingLong(People::getAgeAsLong), people[0], people[1]);
228         assertComparison(cmp.thenComparingLong(People::getAgeAsLong), people[1], people[5]);
229         // thenComparing(ToDoubleFunction)
230         assertComparison(cmp.thenComparingDouble(People::getAgeAsDouble), people[0], people[1]);
231         assertComparison(cmp.thenComparingDouble(People::getAgeAsDouble), people[1], people[5]);
232     }
233 
234 
235     public void testNullsFirst() {
236         Comparator<String> strcmp = Comparator.nullsFirst(Comparator.naturalOrder());
237         Comparator<People> cmp = Comparator.comparing(People::getLastName, strcmp)
238                                            .thenComparing(People::getFirstName, strcmp);
239         // Mary.null vs Mary.Cook - solve by last name
240         assertComparison(cmp, people[6], people[5]);
241         // John.null vs Mary.null - solve by first name
242         assertComparison(cmp, people[7], people[6]);
243 
244         // More than one thenComparing
245         strcmp = Comparator.nullsFirst(Comparator.comparingInt(String::length)
246                                                  .thenComparing(String.CASE_INSENSITIVE_ORDER));
247         assertComparison(strcmp, null, "abc");
248         assertComparison(strcmp, "ab", "abc");
249         assertComparison(strcmp, "abc", "def");
250         assertEquals(0, strcmp.compare("abc", "ABC"));
251 
252         // Ensure reverse still handle null properly
253         Comparator<String> strcmp2 = strcmp.reversed().thenComparing(Comparator.naturalOrder());
254         assertComparison(strcmp2, "abc", null);
255         assertComparison(strcmp2, "abc", "ab");
256         assertComparison(strcmp2, "def", "abc");
257         assertComparison(strcmp2, "ABC", "abc");
258 
259         // Considering non-null values to be equal
260         Comparator<String> blind = Comparator.nullsFirst(null);
261         assertComparison(blind, null, "abc");
262         assertEquals(0, blind.compare("abc", "def"));
263         // reverse still consider non-null values to be equal
264         strcmp = blind.reversed();
265         assertComparison(strcmp, "abc", null);
266         assertEquals(0, strcmp.compare("abc", "def"));
267         // chain with another comparator to compare non-nulls
268         strcmp = blind.thenComparing(Comparator.naturalOrder());
269         assertComparison(strcmp, null, "abc");
270         assertComparison(strcmp, "abc", "def");
271     }
272 
273     public void testNullsLast() {
274         Comparator<String> strcmp = Comparator.nullsLast(Comparator.naturalOrder());
275         Comparator<People> cmp = Comparator.comparing(People::getLastName, strcmp)
276                                            .thenComparing(People::getFirstName, strcmp);
277         // Mary.null vs Mary.Cook - solve by last name
278         assertComparison(cmp, people[5], people[6]);
279         // John.null vs Mary.null - solve by first name
280         assertComparison(cmp, people[7], people[6]);
281 
282         // More than one thenComparing
283         strcmp = Comparator.nullsLast(Comparator.comparingInt(String::length)
284                                                 .thenComparing(String.CASE_INSENSITIVE_ORDER));
285         assertComparison(strcmp, "abc", null);
286         assertComparison(strcmp, "ab", "abc");
287         assertComparison(strcmp, "abc", "def");
288 
289         // Ensure reverse still handle null properly
290         Comparator<String> strcmp2 = strcmp.reversed().thenComparing(Comparator.naturalOrder());
291         assertComparison(strcmp2, null, "abc");
292         assertComparison(strcmp2, "abc", "ab");
293         assertComparison(strcmp2, "def", "abc");
294         assertComparison(strcmp2, "ABC", "abc");
295 
296         // Considering non-null values to be equal
297         Comparator<String> blind = Comparator.nullsLast(null);
298         assertComparison(blind, "abc", null);
299         assertEquals(0, blind.compare("abc", "def"));
300         // reverse still consider non-null values to be equal
301         strcmp = blind.reversed();
302         assertComparison(strcmp, null, "abc");
303         assertEquals(0, strcmp.compare("abc", "def"));
304         // chain with another comparator to compare non-nulls
305         strcmp = blind.thenComparing(Comparator.naturalOrder());
306         assertComparison(strcmp, "abc", null);
307         assertComparison(strcmp, "abc", "def");
308     }
309 
310     public void testComposeComparator() {
311         // Longer string in front
312         Comparator<String> first = (s1, s2) -> s2.length() - s1.length();
313         Comparator<String> second = Comparator.naturalOrder();
314         Comparator<String> composed = first.thenComparing(second);
315 
316         assertTrue(composed.compare("abcdefg", "abcdef") < 0);
317         assertTrue(composed.compare("abcdef", "abcdefg") > 0);
318         assertTrue(composed.compare("abcdef", "abcdef") == 0);
319         assertTrue(composed.compare("abcdef", "ghijkl") < 0);
320         assertTrue(composed.compare("ghijkl", "abcdefg") > 0);
321     }
322 
testNulls()323     public void testNulls() {
324         try {
325             Comparator.<String>naturalOrder().compare("abc", (String) null);
326             fail("expected NPE with naturalOrder");
327         } catch (NullPointerException npe) {}
328         try {
329             Comparator.<String>naturalOrder().compare((String) null, "abc");
330             fail("expected NPE with naturalOrder");
331         } catch (NullPointerException npe) {}
332 
333         try {
334             Comparator.<String>reverseOrder().compare("abc", (String) null);
335             fail("expected NPE with naturalOrder");
336         } catch (NullPointerException npe) {}
337         try {
338             Comparator.<String>reverseOrder().compare((String) null, "abc");
339             fail("expected NPE with naturalOrder");
340         } catch (NullPointerException npe) {}
341 
342         try {
343             Comparator<People> cmp = Comparator.comparing(null, Comparator.<String>naturalOrder());
344             fail("comparing(null, cmp) should throw NPE");
345         } catch (NullPointerException npe) {}
346         try {
347             Comparator<People> cmp = Comparator.comparing(People::getFirstName, null);
348             fail("comparing(f, null) should throw NPE");
349         } catch (NullPointerException npe) {}
350 
351         try {
352             Comparator<People> cmp = Comparator.comparing(null);
353             fail("comparing(null) should throw NPE");
354         } catch (NullPointerException npe) {}
355         try {
356             Comparator<People> cmp = Comparator.comparingInt(null);
357             fail("comparing(null) should throw NPE");
358         } catch (NullPointerException npe) {}
359         try {
360             Comparator<People> cmp = Comparator.comparingLong(null);
361             fail("comparing(null) should throw NPE");
362         } catch (NullPointerException npe) {}
363         try {
364             Comparator<People> cmp = Comparator.comparingDouble(null);
365             fail("comparing(null) should throw NPE");
366         } catch (NullPointerException npe) {}
367     }
368 
testNaturalAndReverseIdentity()369     public void testNaturalAndReverseIdentity() {
370         var naturalOrder = Comparator.<String>naturalOrder();
371         var reverseOrder = Comparator.<String>reverseOrder();
372 
373         assertEquals(
374                 naturalOrder,
375                 Collections.reverseOrder(reverseOrder),
376                 "Comparator.naturalOrder() and Collections.reverseOrder(Comparator.reverseOrder()) not equal");
377 
378         assertEquals(
379                 reverseOrder,
380                 Collections.reverseOrder(naturalOrder),
381                 "Comparator.reverseOrder() and Collections.reverseOrder(Comparator.naturalOrder()) not equal");
382 
383         assertEquals(
384                 naturalOrder.reversed(),
385                 reverseOrder,
386                 "Comparator.naturalOrder().reversed() amd Comparator.reverseOrder() not equal");
387 
388         assertEquals(
389                 reverseOrder.reversed(),
390                 naturalOrder,
391                 "Comparator.reverseOrder().reversed() and Comparator.naturalOrder() not equal");
392     }
393 }
394