1 /*
   2  * Copyright (c) 2015, 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 import java.io.ByteArrayInputStream;
  25 import java.io.ByteArrayOutputStream;
  26 import java.io.IOException;
  27 import java.io.ObjectInputStream;
  28 import java.io.ObjectOutputStream;
  29 import java.util.AbstractMap;
  30 import java.util.ArrayList;
  31 import java.util.Arrays;
  32 import java.util.Collections;
  33 import java.util.Iterator;
  34 import java.util.HashMap;
  35 import java.util.List;
  36 import java.util.Map;
  37 import java.util.stream.Collectors;
  38 import java.util.stream.IntStream;
  39 
  40 import org.testng.annotations.DataProvider;
  41 import org.testng.annotations.Test;
  42 
  43 import static org.testng.Assert.assertEquals;
  44 import static org.testng.Assert.assertFalse;
  45 import static org.testng.Assert.assertTrue;
  46 import static org.testng.Assert.fail;
  47 
  48 /*
  49  * @test
  50  * @bug 8048330
  51  * @summary Test convenience static factory methods on Map.
  52  * @run testng MapFactories
  53  */
  54 
  55 public class MapFactories {
  56 
  57     static final int MAX_ENTRIES = 20; // should be larger than the largest fixed-arg overload
  58     static String valueFor(int i) {
  59         // the String literal below should be of length MAX_ENTRIES 
  60         return "abcdefghijklmnopqrst".substring(i, i+1);
  61     }
  62 
  63     // for "expected" values
  64     Map<Integer,String> genMap(int n) {
  65         Map<Integer,String> result = new HashMap<>();
  66         for (int i = 0; i < n; i++) {
  67             result.put(i, valueFor(i));
  68         }
  69         return result;
  70     }
  71     
  72     // for varargs Map.Entry methods
  73     @SuppressWarnings("unchecked")
  74     Map.Entry<Integer,String>[] genEntries(int n) {
  75         return IntStream.range(0, n)
  76             .mapToObj(i -> Map.entry(i, valueFor(i)))
  77             .toArray(len -> (Map.Entry<Integer,String>[])(new Map.Entry<?,?>[len]));
  78     }
  79 
  80     // returns array of [actual, expected]
  81     static Object[] a(Map<Integer,String> act, Map<Integer,String> exp) {
  82         return new Object[] { act, exp };
  83     }
  84 
  85     @DataProvider(name="empty")
  86     public Iterator<Object[]> empty() {
  87         return Collections.singletonList(
  88             a(Map.of(), genMap(0))
  89         ).iterator();
  90     }
  91 
  92     @DataProvider(name="nonempty")
  93     @SuppressWarnings("unchecked")
  94     public Iterator<Object[]> nonempty() {
  95         return Arrays.asList(
  96             a(Map.of(0, "a"), genMap(1)),
  97             a(Map.of(0, "a", 1, "b"), genMap(2)),
  98             a(Map.of(0, "a", 1, "b", 2, "c"), genMap(3)),
  99             a(Map.of(0, "a", 1, "b", 2, "c", 3, "d"), genMap(4)),
 100             a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e"), genMap(5)),
 101             a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f"), genMap(6)),
 102             a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g"), genMap(7)),
 103             a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7, "h"), genMap(8)),
 104             a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7, "h", 8, "i"), genMap(9)),
 105             a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7, "h", 8, "i", 9, "j"), genMap(10)),
 106             a(Map.ofEntries(genEntries(MAX_ENTRIES)), genMap(MAX_ENTRIES))
 107         ).iterator();
 108     }
 109 
 110     @DataProvider(name="all")
 111     public Iterator<Object[]> all() {
 112         List<Object[]> all = new ArrayList<>();
 113         empty().forEachRemaining(all::add);
 114         nonempty().forEachRemaining(all::add);
 115         return all.iterator();
 116     }
 117 
 118     @Test(dataProvider="all", expectedExceptions=UnsupportedOperationException.class)
 119     public void cannotPutNew(Map<Integer,String> act, Map<Integer,String> exp) {
 120         act.put(-1, "xyzzy");
 121     }
 122 
 123     @Test(dataProvider="nonempty", expectedExceptions=UnsupportedOperationException.class)
 124     public void cannotPutOld(Map<Integer,String> act, Map<Integer,String> exp) {
 125         act.put(0, "a");
 126     }
 127 
 128     @Test(dataProvider="nonempty", expectedExceptions=UnsupportedOperationException.class)
 129     public void cannotRemove(Map<Integer,String> act, Map<Integer,String> exp) {
 130         act.remove(act.keySet().iterator().next());
 131     }
 132 
 133     @Test(dataProvider="all")
 134     public void contentsMatch(Map<Integer,String> act, Map<Integer,String> exp) {
 135         assertEquals(act, exp);
 136     }
 137 
 138     @Test(expectedExceptions=IllegalArgumentException.class)
 139     public void dupKeysDisallowed2() {
 140         Map<Integer, String> map = Map.of(0, "a", 0, "b");
 141     }
 142 
 143     @Test(expectedExceptions=IllegalArgumentException.class)
 144     public void dupKeysDisallowed3() {
 145         Map<Integer, String> map = Map.of(0, "a", 1, "b", 0, "c");
 146     }
 147 
 148     @Test(expectedExceptions=IllegalArgumentException.class)
 149     public void dupKeysDisallowed4() {
 150         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 0, "d");
 151     }
 152 
 153     @Test(expectedExceptions=IllegalArgumentException.class)
 154     public void dupKeysDisallowed5() {
 155         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 0, "e");
 156     }
 157 
 158     @Test(expectedExceptions=IllegalArgumentException.class)
 159     public void dupKeysDisallowed6() {
 160         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e",
 161                                           0, "f");
 162     }
 163 
 164     @Test(expectedExceptions=IllegalArgumentException.class)
 165     public void dupKeysDisallowed7() {
 166         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e",
 167                                           5, "f", 0, "g");
 168     }
 169 
 170     @Test(expectedExceptions=IllegalArgumentException.class)
 171     public void dupKeysDisallowed8() {
 172         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e",
 173                                           5, "f", 6, "g", 0, "h");
 174     }
 175 
 176     @Test(expectedExceptions=IllegalArgumentException.class)
 177     public void dupKeysDisallowed9() {
 178         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e",
 179                                           5, "f", 6, "g", 7, "h", 0, "i");
 180     }
 181 
 182     @Test(expectedExceptions=IllegalArgumentException.class)
 183     public void dupKeysDisallowed10() {
 184         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e",
 185                                           5, "f", 6, "g", 7, "h", 8, "i", 0, "j");
 186     }
 187 
 188     @Test(expectedExceptions=IllegalArgumentException.class)
 189     public void dupKeysDisallowedN() {
 190         Map.Entry<Integer,String>[] entries = genEntries(MAX_ENTRIES);
 191         entries[MAX_ENTRIES-1] = Map.entry(0, "xxx");
 192         Map<Integer, String> map = Map.ofEntries(entries);
 193     }
 194 
 195     @Test(expectedExceptions=NullPointerException.class)
 196     public void nullKeyDisallowed1() {
 197         Map<Integer, String> map = Map.of(null, "a");
 198     }
 199 
 200     @Test(expectedExceptions=NullPointerException.class)
 201     public void nullValueDisallowed1() {
 202         Map<Integer, String> map = Map.of(0, null);
 203     }
 204 
 205     @Test(expectedExceptions=NullPointerException.class)
 206     public void nullKeyDisallowed2() {
 207         Map<Integer, String> map = Map.of(0, "a", null, "b");
 208     }
 209 
 210     @Test(expectedExceptions=NullPointerException.class)
 211     public void nullValueDisallowed2() {
 212         Map<Integer, String> map = Map.of(0, "a", 1, null);
 213     }
 214 
 215     @Test(expectedExceptions=NullPointerException.class)
 216     public void nullKeyDisallowed3() {
 217         Map<Integer, String> map = Map.of(0, "a", 1, "b", null, "c");
 218     }
 219 
 220     @Test(expectedExceptions=NullPointerException.class)
 221     public void nullValueDisallowed3() {
 222         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, null);
 223     }
 224 
 225     @Test(expectedExceptions=NullPointerException.class)
 226     public void nullKeyDisallowed4() {
 227         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", null, "d");
 228     }
 229 
 230     @Test(expectedExceptions=NullPointerException.class)
 231     public void nullValueDisallowed4() {
 232         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, null);
 233     }
 234 
 235     @Test(expectedExceptions=NullPointerException.class)
 236     public void nullKeyDisallowed5() {
 237         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", null, "e");
 238     }
 239 
 240     @Test(expectedExceptions=NullPointerException.class)
 241     public void nullValueDisallowed5() {
 242         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, null);
 243     }
 244 
 245     @Test(expectedExceptions=NullPointerException.class)
 246     public void nullKeyDisallowed6() {
 247         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e",
 248                                           null, "f");
 249     }
 250 
 251     @Test(expectedExceptions=NullPointerException.class)
 252     public void nullValueDisallowed6() {
 253         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e",
 254                                           5, null);
 255     }
 256 
 257     @Test(expectedExceptions=NullPointerException.class)
 258     public void nullKeyDisallowed7() {
 259         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e",
 260                                           5, "f", null, "g");
 261     }
 262 
 263     @Test(expectedExceptions=NullPointerException.class)
 264     public void nullValueDisallowed7() {
 265         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e",
 266                                           5, "f", 6, null);
 267     }
 268 
 269     @Test(expectedExceptions=NullPointerException.class)
 270     public void nullKeyDisallowed8() {
 271         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e",
 272                                           5, "f", 6, "g", null, "h");
 273     }
 274 
 275     @Test(expectedExceptions=NullPointerException.class)
 276     public void nullValueDisallowed8() {
 277         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e",
 278                                           5, "f", 6, "g", 7, null);
 279     }
 280 
 281     @Test(expectedExceptions=NullPointerException.class)
 282     public void nullKeyDisallowed9() {
 283         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e",
 284                                           5, "f", 6, "g", 7, "h", null, "i");
 285     }
 286 
 287     @Test(expectedExceptions=NullPointerException.class)
 288     public void nullValueDisallowed9() {
 289         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e",
 290                                           5, "f", 6, "g", 7, "h", 8, null);
 291     }
 292 
 293     @Test(expectedExceptions=NullPointerException.class)
 294     public void nullKeyDisallowed10() {
 295         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e",
 296                                           5, "f", 6, "g", 7, "h", 8, "i", null, "j");
 297     }
 298 
 299     @Test(expectedExceptions=NullPointerException.class)
 300     public void nullValueDisallowed10() {
 301         Map<Integer, String> map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e",
 302                                           5, "f", 6, "g", 7, "h", 8, "i", 9, null);
 303     }
 304 
 305     @Test(expectedExceptions=NullPointerException.class)
 306     public void nullKeyDisallowedN() {
 307         Map.Entry<Integer,String>[] entries = genEntries(MAX_ENTRIES);
 308         entries[0] = new AbstractMap.SimpleImmutableEntry(null, "a");
 309         Map<Integer, String> map = Map.ofEntries(entries);
 310     }
 311 
 312     @Test(expectedExceptions=NullPointerException.class)
 313     public void nullValueDisallowedN() {
 314         Map.Entry<Integer,String>[] entries = genEntries(MAX_ENTRIES);
 315         entries[0] = new AbstractMap.SimpleImmutableEntry(0, null);
 316         Map<Integer, String> map = Map.ofEntries(entries);
 317     }
 318 
 319     @Test(expectedExceptions=NullPointerException.class)
 320     public void nullEntryDisallowedN() {
 321         Map.Entry<Integer,String>[] entries = genEntries(MAX_ENTRIES);
 322         entries[5] = null;
 323         Map<Integer, String> map = Map.ofEntries(entries);
 324     }
 325 
 326     @Test(expectedExceptions=NullPointerException.class)
 327     public void nullArrayDisallowed() {
 328         Map.ofEntries(null);
 329     }
 330 
 331     @Test(dataProvider="all")
 332     public void serialEquality(Map<Integer, String> act, Map<Integer, String> exp) {
 333         // assume that act.equals(exp) tested elsewhere
 334         Map<Integer, String> copy = serialClone(act);
 335         assertEquals(act, copy);
 336         assertEquals(copy, exp);
 337     }
 338 
 339     @SuppressWarnings("unchecked")
 340     static <T> T serialClone(T obj) {
 341         try {
 342             ByteArrayOutputStream baos = new ByteArrayOutputStream();
 343             ObjectOutputStream oos = new ObjectOutputStream(baos);
 344             oos.writeObject(obj);
 345             oos.close();
 346             ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
 347             ObjectInputStream ois = new ObjectInputStream(bais);
 348             return (T) ois.readObject();
 349         } catch (IOException | ClassNotFoundException e) {
 350             throw new AssertionError(e);
 351         }
 352     }
 353 
 354     // Map.entry() tests
 355 
 356     @Test(expectedExceptions=NullPointerException.class)
 357     public void entryWithNullKeyDisallowed() {
 358         Map.Entry<Integer,String> e = Map.entry(null, "x");
 359     }
 360     
 361     @Test(expectedExceptions=NullPointerException.class)
 362     public void entryWithNullValueDisallowed() {
 363         Map.Entry<Integer,String> e = Map.entry(0, null);
 364     }
 365 
 366     @Test
 367     public void entryBasicTests() {
 368         Map.Entry<String,String> kvh1 = Map.entry("xyzzy", "plugh");
 369         Map.Entry<String,String> kvh2 = Map.entry("foobar", "blurfl");
 370         Map.Entry<String,String> sie = new AbstractMap.SimpleImmutableEntry("xyzzy", "plugh");
 371 
 372         assertTrue(kvh1.equals(sie));
 373         assertTrue(sie.equals(kvh1));
 374         assertFalse(kvh2.equals(sie));
 375         assertFalse(sie.equals(kvh2));
 376         assertEquals(sie.hashCode(), kvh1.hashCode());
 377         assertEquals(sie.toString(), kvh1.toString());
 378     }
 379 
 380 }