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 
  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 
  57         public int getIntField() {
  58             return intField;
  59         }
  60 
  61         public long getLongField() {
  62             return longField;
  63         }
  64 
  65         public double getDoubleField() {
  66             return doubleField;
  67         }
  68 
  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 
  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 
  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 
 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 
 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 
 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 
 144     public void testNaturalOrderComparator() {
 145         Comparator<String> comp = Comparator.naturalOrder();
 146 
 147         assertComparisons(stringValues, comp, comparisons);
 148     }
 149 
 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 
 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 
 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 }