1 /*
   2  * Copyright (c) 2012, 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 8001667 8010279
  27  * @run testng BasicTest
  28  */
  29 
  30 import java.util.Comparator;
  31 import java.util.Comparators;
  32 import java.util.AbstractMap;
  33 import java.util.Map;
  34 import org.testng.annotations.Test;
  35 
  36 import java.util.function.BinaryOperator;
  37 import java.util.function.Function;
  38 import java.util.function.ToIntFunction;
  39 import java.util.function.ToLongFunction;
  40 import java.util.function.ToDoubleFunction;
  41 
  42 import static org.testng.Assert.assertEquals;
  43 import static org.testng.Assert.assertTrue;
  44 import static org.testng.Assert.assertSame;
  45 import static org.testng.Assert.fail;
  46 
  47 /**
  48  * Unit tests for helper methods in Comparators
  49  */
  50 @Test(groups = "unit")
  51 public class BasicTest {
  52     private static class Thing {
  53         public final int intField;
  54         public final long longField;
  55         public final double doubleField;
  56         public final String stringField;
  57 
  58         private Thing(int intField, long longField, double doubleField, String stringField) {
  59             this.intField = intField;
  60             this.longField = longField;
  61             this.doubleField = doubleField;
  62             this.stringField = stringField;
  63         }
  64 
  65         public int getIntField() {
  66             return intField;
  67         }
  68 
  69         public long getLongField() {
  70             return longField;
  71         }
  72 
  73         public double getDoubleField() {
  74             return doubleField;
  75         }
  76 
  77         public String getStringField() {
  78             return stringField;
  79         }
  80     }
  81 
  82     private final int[] intValues = { -2, -2, -1, -1, 0, 0, 1, 1, 2, 2 };
  83     private final long[] longValues = { -2, -2, -1, -1, 0, 0, 1, 1, 2, 2 };
  84     private final double[] doubleValues = { -2, -2, -1, -1, 0, 0, 1, 1, 2, 2 };
  85     private final String[] stringValues = { "a", "a", "b", "b", "c", "c", "d", "d", "e", "e" };
  86     private final int[] comparisons = { 0, -1, 0, -1, 0, -1, 0, -1, 0 };
  87 
  88     private<T> void assertComparisons(T[] things, Comparator<T> comp, int[] comparisons) {
  89         for (int i=0; i<comparisons.length; i++) {
  90             assertEquals(comparisons.length + 1, things.length);
  91             assertEquals(comparisons[i], comp.compare(things[i], things[i+1]));
  92             assertEquals(-comparisons[i], comp.compare(things[i+1], things[i]));
  93         }
  94     }
  95 
  96     public void testIntComparator() {
  97         Thing[] things = new Thing[intValues.length];
  98         for (int i=0; i<intValues.length; i++)
  99             things[i] = new Thing(intValues[i], 0L, 0.0, null);
 100         Comparator<Thing> comp = Comparators.comparing(new ToIntFunction<BasicTest.Thing>() {
 101             @Override
 102             public int applyAsInt(Thing thing) {
 103                 return thing.getIntField();
 104             }
 105         });
 106 
 107         assertComparisons(things, comp, comparisons);
 108     }
 109 
 110     public void testLongComparator() {
 111         Thing[] things = new Thing[longValues.length];
 112         for (int i=0; i<longValues.length; i++)
 113             things[i] = new Thing(0, longValues[i], 0.0, null);
 114         Comparator<Thing> comp = Comparators.comparing(new ToLongFunction<BasicTest.Thing>() {
 115             @Override
 116             public long applyAsLong(Thing thing) {
 117                 return thing.getLongField();
 118             }
 119         });
 120 
 121         assertComparisons(things, comp, comparisons);
 122     }
 123 
 124     public void testDoubleComparator() {
 125         Thing[] things = new Thing[doubleValues.length];
 126         for (int i=0; i<doubleValues.length; i++)
 127             things[i] = new Thing(0, 0L, doubleValues[i], null);
 128         Comparator<Thing> comp = Comparators.comparing(new ToDoubleFunction<BasicTest.Thing>() {
 129             @Override
 130             public double applyAsDouble(Thing thing) {
 131                 return thing.getDoubleField();
 132             }
 133         });
 134 
 135         assertComparisons(things, comp, comparisons);
 136     }
 137 
 138     public void testComparing() {
 139         Thing[] things = new Thing[doubleValues.length];
 140         for (int i=0; i<doubleValues.length; i++)
 141             things[i] = new Thing(0, 0L, 0.0, stringValues[i]);
 142         Comparator<Thing> comp = Comparators.comparing(new Function<Thing, String>() {
 143             @Override
 144             public String apply(Thing thing) {
 145                 return thing.getStringField();
 146             }
 147         });
 148 
 149         assertComparisons(things, comp, comparisons);
 150     }
 151 
 152     public void testNaturalOrderComparator() {
 153         Comparator<String> comp = Comparators.naturalOrder();
 154 
 155         assertComparisons(stringValues, comp, comparisons);
 156     }
 157 
 158     public void testReverseComparator() {
 159         Comparator<String> cmpr = Comparators.reverseOrder();
 160         Comparator<String> cmp = cmpr.reverseOrder();
 161 
 162         assertEquals(cmp.reverseOrder(), cmpr);
 163         assertEquals(0, cmp.compare("a", "a"));
 164         assertEquals(0, cmpr.compare("a", "a"));
 165         assertTrue(cmp.compare("a", "b") < 0);
 166         assertTrue(cmpr.compare("a", "b") > 0);
 167         assertTrue(cmp.compare("b", "a") > 0);
 168         assertTrue(cmpr.compare("b", "a") < 0);
 169     }
 170 
 171     public void testReverseComparator2() {
 172         Comparator<String> cmp = (s1, s2) -> s1.length() - s2.length();
 173         Comparator<String> cmpr = cmp.reverseOrder();
 174 
 175         assertEquals(cmpr.reverseOrder(), cmp);
 176         assertEquals(0, cmp.compare("abc", "def"));
 177         assertEquals(0, cmpr.compare("abc", "def"));
 178         assertTrue(cmp.compare("abcd", "def") > 0);
 179         assertTrue(cmpr.compare("abcd", "def") < 0);
 180         assertTrue(cmp.compare("abc", "defg") < 0);
 181         assertTrue(cmpr.compare("abc", "defg") > 0);
 182     }
 183 
 184     @Test(expectedExceptions=NullPointerException.class)
 185     public void testReverseComparatorNPE() {
 186         Comparator<String> cmp = Comparators.reverseOrder(null);
 187     }
 188 
 189     public void testComposeComparator() {
 190         // Longer string in front
 191         Comparator<String> first = (s1, s2) -> s2.length() - s1.length();
 192         Comparator<String> second = Comparators.naturalOrder();
 193         Comparator<String> composed = Comparators.compose(first, second);
 194 
 195         assertTrue(composed.compare("abcdefg", "abcdef") < 0);
 196         assertTrue(composed.compare("abcdef", "abcdefg") > 0);
 197         assertTrue(composed.compare("abcdef", "abcdef") == 0);
 198         assertTrue(composed.compare("abcdef", "ghijkl") < 0);
 199         assertTrue(composed.compare("ghijkl", "abcdefg") > 0);
 200     }
 201 
 202     private <K, V> void assertPairComparison(K k1, V v1, K k2, V v2,
 203                                         Comparator<Map.Entry<K, V>> ck,
 204                                         Comparator<Map.Entry<K, V>> cv) {
 205         final Map.Entry<K, V> p11 = new AbstractMap.SimpleImmutableEntry<>(k1, v1);
 206         final Map.Entry<K, V> p12 = new AbstractMap.SimpleImmutableEntry<>(k1, v2);
 207         final Map.Entry<K, V> p21 = new AbstractMap.SimpleImmutableEntry<>(k2, v1);
 208         final Map.Entry<K, V> p22 = new AbstractMap.SimpleImmutableEntry<>(k2, v2);
 209 
 210         assertTrue(ck.compare(p11, p11) == 0);
 211         assertTrue(ck.compare(p12, p11) == 0);
 212         assertTrue(ck.compare(p11, p12) == 0);
 213         assertTrue(ck.compare(p12, p22) < 0);
 214         assertTrue(ck.compare(p12, p21) < 0);
 215         assertTrue(ck.compare(p21, p11) > 0);
 216         assertTrue(ck.compare(p21, p12) > 0);
 217 
 218         assertTrue(cv.compare(p11, p11) == 0);
 219         assertTrue(cv.compare(p12, p11) > 0);
 220         assertTrue(cv.compare(p11, p12) < 0);
 221         assertTrue(cv.compare(p12, p22) == 0);
 222         assertTrue(cv.compare(p12, p21) > 0);
 223         assertTrue(cv.compare(p21, p11) == 0);
 224         assertTrue(cv.compare(p21, p12) < 0);
 225 
 226         Comparator<Map.Entry<K, V>> cmp = Comparators.compose(ck, cv);
 227         assertTrue(cmp.compare(p11, p11) == 0);
 228         assertTrue(cmp.compare(p12, p11) > 0);
 229         assertTrue(cmp.compare(p11, p12) < 0);
 230         assertTrue(cmp.compare(p12, p22) < 0);
 231         assertTrue(cmp.compare(p12, p21) < 0);
 232         assertTrue(cmp.compare(p21, p11) > 0);
 233         assertTrue(cmp.compare(p21, p12) > 0);
 234 
 235         cmp = Comparators.compose(cv, ck);
 236         assertTrue(cmp.compare(p11, p11) == 0);
 237         assertTrue(cmp.compare(p12, p11) > 0);
 238         assertTrue(cmp.compare(p11, p12) < 0);
 239         assertTrue(cmp.compare(p12, p22) < 0);
 240         assertTrue(cmp.compare(p12, p21) > 0);
 241         assertTrue(cmp.compare(p21, p11) > 0);
 242         assertTrue(cmp.compare(p21, p12) < 0);
 243     }
 244 
 245     public void testKVComparatorable() {
 246         assertPairComparison(1, "ABC", 2, "XYZ",
 247                          Comparators.<Integer, String>naturalOrderKeys(),
 248                          Comparators.<Integer, String>naturalOrderValues());
 249     }
 250 
 251     private static class People {
 252         final String firstName;
 253         final String lastName;
 254         final int age;
 255 
 256         People(String first, String last, int age) {
 257             firstName = first;
 258             lastName = last;
 259             this.age = age;
 260         }
 261 
 262         String getFirstName() { return firstName; }
 263         String getLastName() { return lastName; }
 264         int getAge() { return age; }
 265         long getAgeAsLong() { return (long) age; };
 266         double getAgeAsDouble() { return (double) age; };
 267     }
 268 
 269     private final People people[] = {
 270         new People("John", "Doe", 34),
 271         new People("Mary", "Doe", 30),
 272         new People("Maria", "Doe", 14),
 273         new People("Jonah", "Doe", 10),
 274         new People("John", "Cook", 54),
 275         new People("Mary", "Cook", 50),
 276     };
 277 
 278     public void testKVComparators() {
 279         // Comparator<People> cmp = Comparators.naturalOrder(); // Should fail to compiler as People is not comparable
 280         // We can use simple comparator, but those have been tested above.
 281         // Thus choose to do compose for some level of interation.
 282         Comparator<People> cmp1 = Comparators.comparing((Function<People, String>) People::getFirstName);
 283         Comparator<People> cmp2 = Comparators.comparing((Function<People, String>) People::getLastName);
 284         Comparator<People> cmp = Comparators.compose(cmp1, cmp2);
 285 
 286         assertPairComparison(people[0], people[0], people[1], people[1],
 287                          Comparators.<People, People>byKey(cmp),
 288                          Comparators.<People, People>byValue(cmp));
 289 
 290     }
 291 
 292     private <T> void assertComparison(Comparator<T> cmp, T less, T greater) {
 293         assertTrue(cmp.compare(less, greater) < 0, "less");
 294         assertTrue(cmp.compare(less, less) == 0, "equal");
 295         assertTrue(cmp.compare(greater, less) > 0, "greater");
 296     }
 297 
 298     public void testComparatorDefaultMethods() {
 299         Comparator<People> cmp = Comparators.comparing((Function<People, String>) People::getFirstName);
 300         Comparator<People> cmp2 = Comparators.comparing((Function<People, String>) People::getLastName);
 301         // reverseOrder
 302         assertComparison(cmp.reverseOrder(), people[1], people[0]);
 303         // thenComparing(Comparator)
 304         assertComparison(cmp.thenComparing(cmp2), people[0], people[1]);
 305         assertComparison(cmp.thenComparing(cmp2), people[4], people[0]);
 306         // thenComparing(Function)
 307         assertComparison(cmp.thenComparing(People::getLastName), people[0], people[1]);
 308         assertComparison(cmp.thenComparing(People::getLastName), people[4], people[0]);
 309         // thenComparing(ToIntFunction)
 310         assertComparison(cmp.thenComparing(People::getAge), people[0], people[1]);
 311         assertComparison(cmp.thenComparing(People::getAge), people[1], people[5]);
 312         // thenComparing(ToLongFunction)
 313         assertComparison(cmp.thenComparing(People::getAgeAsLong), people[0], people[1]);
 314         assertComparison(cmp.thenComparing(People::getAgeAsLong), people[1], people[5]);
 315         // thenComparing(ToDoubleFunction)
 316         assertComparison(cmp.thenComparing(People::getAgeAsDouble), people[0], people[1]);
 317         assertComparison(cmp.thenComparing(People::getAgeAsDouble), people[1], people[5]);
 318     }
 319 
 320     public void testGreaterOf() {
 321         // lesser
 322         assertSame(Comparators.greaterOf(Comparators.comparing(
 323                                     (Function<People, String>) People::getFirstName))
 324                               .apply(people[0], people[1]),
 325                    people[1]);
 326         // euqal
 327         assertSame(Comparators.greaterOf(Comparators.comparing(
 328                                     (Function<People, String>) People::getLastName))
 329                               .apply(people[0], people[1]),
 330                    people[0]);
 331         // greater
 332         assertSame(Comparators.greaterOf(Comparators.comparing(
 333                                     (ToIntFunction<People>) People::getAge))
 334                               .apply(people[0], people[1]),
 335                    people[0]);
 336     }
 337 
 338     public void testLesserOf() {
 339         // lesser
 340         assertSame(Comparators.lesserOf(Comparators.comparing(
 341                                     (Function<People, String>) People::getFirstName))
 342                               .apply(people[0], people[1]),
 343                    people[0]);
 344         // euqal
 345         assertSame(Comparators.lesserOf(Comparators.comparing(
 346                                     (Function<People, String>) People::getLastName))
 347                               .apply(people[0], people[1]),
 348                    people[0]);
 349         // greater
 350         assertSame(Comparators.lesserOf(Comparators.comparing(
 351                                     (ToIntFunction<People>) People::getAge))
 352                               .apply(people[0], people[1]),
 353                    people[1]);
 354     }
 355 
 356     public void testNulls() {
 357         try {
 358             Comparators.<String>naturalOrder().compare("abc", (String) null);
 359             fail("expected NPE with naturalOrder");
 360         } catch (NullPointerException npe) {}
 361         try {
 362             Comparators.<String>naturalOrder().compare((String) null, "abc");
 363             fail("expected NPE with naturalOrder");
 364         } catch (NullPointerException npe) {}
 365 
 366         try {
 367             Comparators.<String>reverseOrder().compare("abc", (String) null);
 368             fail("expected NPE with naturalOrder");
 369         } catch (NullPointerException npe) {}
 370         try {
 371             Comparators.<String>reverseOrder().compare((String) null, "abc");
 372             fail("expected NPE with naturalOrder");
 373         } catch (NullPointerException npe) {}
 374 
 375         try {
 376             Comparator<Map.Entry<String, String>> cmp = Comparators.byKey(null);
 377             fail("byKey(null) should throw NPE");
 378         } catch (NullPointerException npe) {}
 379 
 380         try {
 381             Comparator<Map.Entry<String, String>> cmp = Comparators.byValue(null);
 382             fail("byValue(null) should throw NPE");
 383         } catch (NullPointerException npe) {}
 384 
 385         try {
 386             Comparator<People> cmp = Comparators.comparing((Function<People, String>) null);
 387             fail("comparing(null) should throw NPE");
 388         } catch (NullPointerException npe) {}
 389         try {
 390             Comparator<People> cmp = Comparators.comparing((ToIntFunction<People>) null);
 391             fail("comparing(null) should throw NPE");
 392         } catch (NullPointerException npe) {}
 393         try {
 394             Comparator<People> cmp = Comparators.comparing((ToLongFunction<People>) null);
 395             fail("comparing(null) should throw NPE");
 396         } catch (NullPointerException npe) {}
 397         try {
 398             Comparator<People> cmp = Comparators.comparing((ToDoubleFunction<People>) null);
 399             fail("comparing(null) should throw NPE");
 400         } catch (NullPointerException npe) {}
 401 
 402         try {
 403             BinaryOperator<String> op = Comparators.lesserOf(null);
 404             fail("lesserOf(null) should throw NPE");
 405         } catch (NullPointerException npe) {}
 406 
 407         try {
 408             BinaryOperator<String> op = Comparators.greaterOf(null);
 409             fail("lesserOf(null) should throw NPE");
 410         } catch (NullPointerException npe) {}
 411     }
 412 }