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 
30 import java.util.TreeMap;
31 import java.util.Comparator;
32 import org.testng.annotations.Test;
33 
34 import java.util.function.Function;
35 import java.util.function.ToIntFunction;
36 import java.util.function.ToLongFunction;
37 import java.util.function.ToDoubleFunction;
38 
39 import static org.testng.Assert.assertEquals;
40 import static org.testng.Assert.assertTrue;
41 import static org.testng.Assert.fail;
42 
43 @Test(groups = "unit")
44 public 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 
Thing(int intField, long longField, double doubleField, String stringField)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 
getIntField()58         public int getIntField() {
59             return intField;
60         }
61 
getLongField()62         public long getLongField() {
63             return longField;
64         }
65 
getDoubleField()66         public double getDoubleField() {
67             return doubleField;
68         }
69 
getStringField()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 
assertComparisons(T[] things, Comparator<T> comp, int[] comparisons)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 
testIntComparator()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 
testLongComparator()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 
testDoubleComparator()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 
testComparing()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 
testNaturalOrderComparator()145     public void testNaturalOrderComparator() {
146         Comparator<String> comp = Comparator.naturalOrder();
147 
148         assertComparisons(stringValues, comp, comparisons);
149     }
150 
testReverseComparator()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 
testNulls()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