1 /* 2 * Copyright (c) 2014, 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 8015081 27 * @summary javax.security.auth.Subject.toString() throws NPE 28 */ 29 30 import java.io.File; 31 import java.io.FileInputStream; 32 import java.io.ObjectInputStream; 33 import java.io.IOException; 34 import java.lang.Exception; 35 import java.security.Principal; 36 import java.util.Arrays; 37 import java.util.Collection; 38 import java.util.Collections; 39 import java.util.HashSet; 40 import java.util.LinkedList; 41 import java.util.List; 42 import java.util.Set; 43 import javax.management.remote.JMXPrincipal; 44 import javax.security.auth.Subject; 45 import javax.security.auth.x500.X500Principal; 46 import javax.security.auth.kerberos.KerberosPrincipal; 47 48 public class SubjectNullTests { 49 50 // Value templates for the constructor 51 private static Principal[] princVals = { 52 new X500Principal("CN=Tom Sawyer, ST=Missouri, C=US"), 53 new JMXPrincipal("Huckleberry Finn"), 54 new KerberosPrincipal("mtwain/author@LITERATURE.US") 55 }; 56 private static String[] pubVals = {"tsawyer", "hfinn", "mtwain"}; 57 private static String[] privVals = {"th3R!v3r", "oNth3R4ft", "5Cl3M3nz"}; 58 59 // Templates for collection-based modifiers for the Subject 60 private static Principal[] tmplAddPrincs = { 61 new X500Principal("CN=John Doe, O=Bogus Corp."), 62 new KerberosPrincipal("jdoe/admin@BOGUSCORP.COM") 63 }; 64 private static String[] tmplAddPubVals = {"jdoe", "djoe"}; 65 private static String[] tmplAddPrvVals = {"b4dpa55w0rd", "pass123"}; 66 67 /** 68 * Construct a subject, and optionally place a null in any one 69 * of the three Sets used to initialize a Subject's values 70 */ 71 private static Subject makeSubj(boolean nullPrinc, boolean nullPub, 72 boolean nullPriv) { 73 Set<Principal> setPrinc = 74 new HashSet<Principal>(Arrays.asList(princVals)); 75 Set<String> setPubCreds = new HashSet<String>(Arrays.asList(pubVals)); 76 Set<String> setPrvCreds = new HashSet<String>(Arrays.asList(privVals)); 77 78 if (nullPrinc) { 79 setPrinc.add(null); 80 } 81 82 if (nullPub) { 83 setPubCreds.add(null); 84 } 85 86 if (nullPriv) { 87 setPrvCreds.add(null); 88 } 89 90 return (new Subject(false, setPrinc, setPubCreds, setPrvCreds)); 91 } 92 93 /** 94 * Provide a simple interface for abstracting collection-on-collection 95 * functions 96 */ 97 public interface Function { 98 boolean execCollection(Set<?> subjSet, Collection<?> actorData); 99 } 100 101 public static final Function methAdd = new Function() { 102 public boolean execCollection(Set<?> subjSet, Collection<?> actorData) { 103 return subjSet.addAll((Collection)actorData); 104 } 105 }; 106 107 public static final Function methContains = new Function() { 108 public boolean execCollection(Set<?> subjSet, Collection<?> actorData) { 109 return subjSet.containsAll(actorData); 110 } 111 }; 112 113 public static final Function methRemove = new Function() { 114 public boolean execCollection(Set<?> subjSet, Collection<?> actorData) { 115 return subjSet.removeAll(actorData); 116 } 117 }; 118 119 public static final Function methRetain = new Function() { 120 public boolean execCollection(Set<?> subjSet, Collection<?> actorData) { 121 return subjSet.retainAll(actorData); 122 } 123 }; 124 125 /** 126 * Run a test using a specified Collection method upon a Subject's 127 * SecureSet fields. This method expects NullPointerExceptions 128 * to be thrown, and throws RuntimeException when the operation 129 * succeeds 130 */ 131 private static void nullTestCollection(Function meth, Set<?> subjSet, 132 Collection<?> actorData) { 133 try { 134 meth.execCollection(subjSet, actorData); 135 throw new RuntimeException("Failed to throw NullPointerException"); 136 } catch (NullPointerException npe) { 137 System.out.println("Caught expected NullPointerException [PASS]"); 138 } 139 } 140 141 /** 142 * Run a test using a specified Collection method upon a Subject's 143 * SecureSet fields. This method expects the function and arguments 144 * passed in to complete without exception. It returns false 145 * if either an exception occurs or the result of the operation is 146 * false. 147 */ 148 private static boolean validTestCollection(Function meth, Set<?> subjSet, 149 Collection<?> actorData) { 150 boolean result = false; 151 152 try { 153 result = meth.execCollection(subjSet, actorData); 154 } catch (Exception exc) { 155 System.out.println("Caught exception " + exc); 156 } 157 158 return result; 159 } 160 161 /** 162 * Deserialize an object from a file. 163 * 164 * @param type The {@code Class} that the serialized file is supposed 165 * to contain. 166 * @param fileName The name of the file to be deserialized. 167 * 168 * @return An object of the type specified in the {@code type} parameter 169 */ 170 private static <T> T deserializeFile(Class type, String fileName) 171 throws IOException, ClassNotFoundException { 172 File fileObj = new File(System.getProperty("test.src", "."), fileName); 173 FileInputStream fis = new FileInputStream(fileObj); 174 ObjectInputStream ois = new ObjectInputStream(fis); 175 176 T newObj = (T)ois.readObject(); 177 ois.close(); 178 fis.close(); 179 180 return newObj; 181 } 182 183 private static void testCTOR() { 184 System.out.println("------ constructor ------"); 185 186 try { 187 // Case 1: Create a subject with a null principal 188 // Expected result: NullPointerException 189 Subject mtSubj = makeSubj(true, false, false); 190 throw new RuntimeException( 191 "constructor [principal w/ null]: Failed to throw NPE"); 192 } catch (NullPointerException npe) { 193 System.out.println("constructor [principal w/ null]: " + 194 "NullPointerException [PASS]"); 195 } 196 197 try { 198 // Case 2: Create a subject with a null public credential element 199 // Expected result: NullPointerException 200 Subject mtSubj = makeSubj(false, true, false); 201 throw new RuntimeException( 202 "constructor [pub cred w/ null]: Failed to throw NPE"); 203 } catch (NullPointerException npe) { 204 System.out.println("constructor [pub cred w/ null]: " + 205 "NullPointerException [PASS]"); 206 } 207 208 try { 209 // Case 3: Create a subject with a null private credential element 210 // Expected result: NullPointerException 211 Subject mtSubj = makeSubj(false, false, true); 212 throw new RuntimeException( 213 "constructor [priv cred w/ null]: Failed to throw NPE"); 214 } catch (NullPointerException npe) { 215 System.out.println("constructor [priv cred w/ null]: " + 216 "NullPointerException [PASS]"); 217 } 218 219 // Case 4: Create a new subject using the principals, public 220 // and private credentials from another well-formed subject 221 // Expected result: Successful construction 222 Subject srcSubj = makeSubj(false, false, false); 223 Subject mtSubj = new Subject(false, srcSubj.getPrincipals(), 224 srcSubj.getPublicCredentials(), 225 srcSubj.getPrivateCredentials()); 226 System.out.println("Construction from another well-formed Subject's " + 227 "principals/creds [PASS]"); 228 } 229 230 private static void testDeserialize() throws Exception { 231 System.out.println("------ deserialize -----"); 232 233 Subject subj = null; 234 Set<Principal> prin = null; 235 236 // Case 1: positive deserialization test of a Subject 237 // Expected result: well-formed Subject 238 subj = deserializeFile(Subject.class, "SubjNoNull.bin"); 239 System.out.println("Positive deserialization test (Subject) passed"); 240 241 // Case 2: positive deserialization test of a SecureSet 242 // Expected result: well-formed Set 243 prin = deserializeFile(Set.class, "PrinNoNull.bin"); 244 System.out.println("Positive deserialization test (SecureSet) passed"); 245 246 System.out.println( 247 "* Testing deserialization with null-poisoned objects"); 248 // Case 3: deserialization test of a null-poisoned Subject 249 // Expected result: NullPointerException 250 try { 251 subj = deserializeFile(Subject.class, "SubjWithNull.bin"); 252 throw new RuntimeException("Failed to throw NullPointerException"); 253 } catch (NullPointerException npe) { 254 System.out.println("Caught expected NullPointerException [PASS]"); 255 } 256 257 // Case 4: deserialization test of a null-poisoned SecureSet 258 // Expected result: NullPointerException 259 try { 260 prin = deserializeFile(Set.class, "PrinWithNull.bin"); 261 throw new RuntimeException("Failed to throw NullPointerException"); 262 } catch (NullPointerException npe) { 263 System.out.println("Caught expected NullPointerException [PASS]"); 264 } 265 } 266 267 private static void testAdd() { 268 System.out.println("------ add() ------"); 269 // Create a well formed subject 270 Subject mtSubj = makeSubj(false, false, false); 271 272 try { 273 // Case 1: Attempt to add null values to principal 274 // Expected result: NullPointerException 275 mtSubj.getPrincipals().add(null); 276 throw new RuntimeException( 277 "PRINCIPAL add(null): Failed to throw NPE"); 278 } catch (NullPointerException npe) { 279 System.out.println( 280 "PRINCIPAL add(null): NullPointerException [PASS]"); 281 } 282 283 try { 284 // Case 2: Attempt to add null into the public creds 285 // Expected result: NullPointerException 286 mtSubj.getPublicCredentials().add(null); 287 throw new RuntimeException( 288 "PUB CRED add(null): Failed to throw NPE"); 289 } catch (NullPointerException npe) { 290 System.out.println( 291 "PUB CRED add(null): NullPointerException [PASS]"); 292 } 293 294 try { 295 // Case 3: Attempt to add null into the private creds 296 // Expected result: NullPointerException 297 mtSubj.getPrivateCredentials().add(null); 298 throw new RuntimeException( 299 "PRIV CRED add(null): Failed to throw NPE"); 300 } catch (NullPointerException npe) { 301 System.out.println( 302 "PRIV CRED add(null): NullPointerException [PASS]"); 303 } 304 } 305 306 private static void testRemove() { 307 System.out.println("------ remove() ------"); 308 // Create a well formed subject 309 Subject mtSubj = makeSubj(false, false, false); 310 311 try { 312 // Case 1: Attempt to remove null values from principal 313 // Expected result: NullPointerException 314 mtSubj.getPrincipals().remove(null); 315 throw new RuntimeException( 316 "PRINCIPAL remove(null): Failed to throw NPE"); 317 } catch (NullPointerException npe) { 318 System.out.println( 319 "PRINCIPAL remove(null): NullPointerException [PASS]"); 320 } 321 322 try { 323 // Case 2: Attempt to remove null from the public creds 324 // Expected result: NullPointerException 325 mtSubj.getPublicCredentials().remove(null); 326 throw new RuntimeException( 327 "PUB CRED remove(null): Failed to throw NPE"); 328 } catch (NullPointerException npe) { 329 System.out.println( 330 "PUB CRED remove(null): NullPointerException [PASS]"); 331 } 332 333 try { 334 // Case 3: Attempt to remove null from the private creds 335 // Expected result: NullPointerException 336 mtSubj.getPrivateCredentials().remove(null); 337 throw new RuntimeException( 338 "PRIV CRED remove(null): Failed to throw NPE"); 339 } catch (NullPointerException npe) { 340 System.out.println( 341 "PRIV CRED remove(null): NullPointerException [PASS]"); 342 } 343 } 344 345 private static void testContains() { 346 System.out.println("------ contains() ------"); 347 // Create a well formed subject 348 Subject mtSubj = makeSubj(false, false, false); 349 350 try { 351 // Case 1: Attempt to check for null values in principals 352 // Expected result: NullPointerException 353 mtSubj.getPrincipals().contains(null); 354 throw new RuntimeException( 355 "PRINCIPAL contains(null): Failed to throw NPE"); 356 } catch (NullPointerException npe) { 357 System.out.println( 358 "PRINCIPAL contains(null): NullPointerException [PASS]"); 359 } 360 361 try { 362 // Case 2: Attempt to check for null in public creds 363 // Expected result: NullPointerException 364 mtSubj.getPublicCredentials().contains(null); 365 throw new RuntimeException( 366 "PUB CRED contains(null): Failed to throw NPE"); 367 } catch (NullPointerException npe) { 368 System.out.println( 369 "PUB CRED contains(null): NullPointerException [PASS]"); 370 } 371 372 try { 373 // Case 3: Attempt to check for null in private creds 374 // Expected result: NullPointerException 375 mtSubj.getPrivateCredentials().contains(null); 376 throw new RuntimeException( 377 "PRIV CRED contains(null): Failed to throw NPE"); 378 } catch (NullPointerException npe) { 379 System.out.println( 380 "PRIV CRED contains(null): NullPointerException [PASS]"); 381 } 382 } 383 384 private static void testAddAll() { 385 // Create a well formed subject and additional collections 386 Subject mtSubj = makeSubj(false, false, false); 387 Set<Principal> morePrincs = new HashSet<>(Arrays.asList(tmplAddPrincs)); 388 Set<Object> morePubVals = new HashSet<>(Arrays.asList(tmplAddPubVals)); 389 Set<Object> morePrvVals = new HashSet<>(Arrays.asList(tmplAddPrvVals)); 390 391 // Run one success test for each Subject family to verify the 392 // overloaded method works as intended. 393 Set<Principal> setPrin = mtSubj.getPrincipals(); 394 Set<Object> setPubCreds = mtSubj.getPublicCredentials(); 395 Set<Object> setPrvCreds = mtSubj.getPrivateCredentials(); 396 int prinOrigSize = setPrin.size(); 397 int pubOrigSize = setPubCreds.size(); 398 int prvOrigSize = setPrvCreds.size(); 399 400 System.out.println("------ addAll() -----"); 401 402 // Add the new members, then check the resulting size of the 403 // Subject attributes to verify they've increased by the proper 404 // amounts. 405 if ((validTestCollection(methAdd, setPrin, morePrincs) != true) || 406 (setPrin.size() != prinOrigSize + morePrincs.size())) 407 { 408 throw new RuntimeException("Failed addAll() on principals"); 409 } 410 if ((validTestCollection(methAdd, setPubCreds, 411 morePubVals) != true) || 412 (setPubCreds.size() != pubOrigSize + morePubVals.size())) 413 { 414 throw new RuntimeException("Failed addAll() on public creds"); 415 } 416 if ((validTestCollection(methAdd, setPrvCreds, 417 morePrvVals) != true) || 418 (setPrvCreds.size() != prvOrigSize + morePrvVals.size())) 419 { 420 throw new RuntimeException("Failed addAll() on private creds"); 421 } 422 System.out.println("Positive addAll() test passed"); 423 424 // Now add null elements into each container, then retest 425 morePrincs.add(null); 426 morePubVals.add(null); 427 morePrvVals.add(null); 428 429 System.out.println("* Testing addAll w/ null values on Principals"); 430 nullTestCollection(methAdd, mtSubj.getPrincipals(), null); 431 nullTestCollection(methAdd, mtSubj.getPrincipals(), morePrincs); 432 433 System.out.println("* Testing addAll w/ null values on Public Creds"); 434 nullTestCollection(methAdd, mtSubj.getPublicCredentials(), null); 435 nullTestCollection(methAdd, mtSubj.getPublicCredentials(), 436 morePubVals); 437 438 System.out.println("* Testing addAll w/ null values on Private Creds"); 439 nullTestCollection(methAdd, mtSubj.getPrivateCredentials(), null); 440 nullTestCollection(methAdd, mtSubj.getPrivateCredentials(), 441 morePrvVals); 442 } 443 444 private static void testRemoveAll() { 445 // Create a well formed subject and additional collections 446 Subject mtSubj = makeSubj(false, false, false); 447 Set<Principal> remPrincs = new HashSet<>(); 448 Set<Object> remPubVals = new HashSet<>(); 449 Set<Object> remPrvVals = new HashSet<>(); 450 451 remPrincs.add(new KerberosPrincipal("mtwain/author@LITERATURE.US")); 452 remPubVals.add("mtwain"); 453 remPrvVals.add("5Cl3M3nz"); 454 455 // Run one success test for each Subject family to verify the 456 // overloaded method works as intended. 457 Set<Principal> setPrin = mtSubj.getPrincipals(); 458 Set<Object> setPubCreds = mtSubj.getPublicCredentials(); 459 Set<Object> setPrvCreds = mtSubj.getPrivateCredentials(); 460 int prinOrigSize = setPrin.size(); 461 int pubOrigSize = setPubCreds.size(); 462 int prvOrigSize = setPrvCreds.size(); 463 464 System.out.println("------ removeAll() -----"); 465 466 // Remove the specified members, then check the resulting size of the 467 // Subject attributes to verify they've decreased by the proper 468 // amounts. 469 if ((validTestCollection(methRemove, setPrin, remPrincs) != true) || 470 (setPrin.size() != prinOrigSize - remPrincs.size())) 471 { 472 throw new RuntimeException("Failed removeAll() on principals"); 473 } 474 if ((validTestCollection(methRemove, setPubCreds, 475 remPubVals) != true) || 476 (setPubCreds.size() != pubOrigSize - remPubVals.size())) 477 { 478 throw new RuntimeException("Failed removeAll() on public creds"); 479 } 480 if ((validTestCollection(methRemove, setPrvCreds, 481 remPrvVals) != true) || 482 (setPrvCreds.size() != prvOrigSize - remPrvVals.size())) 483 { 484 throw new RuntimeException("Failed removeAll() on private creds"); 485 } 486 System.out.println("Positive removeAll() test passed"); 487 488 // Now add null elements into each container, then retest 489 remPrincs.add(null); 490 remPubVals.add(null); 491 remPrvVals.add(null); 492 493 System.out.println("* Testing removeAll w/ null values on Principals"); 494 nullTestCollection(methRemove, mtSubj.getPrincipals(), null); 495 nullTestCollection(methRemove, mtSubj.getPrincipals(), remPrincs); 496 497 System.out.println( 498 "* Testing removeAll w/ null values on Public Creds"); 499 nullTestCollection(methRemove, mtSubj.getPublicCredentials(), null); 500 nullTestCollection(methRemove, mtSubj.getPublicCredentials(), 501 remPubVals); 502 503 System.out.println( 504 "* Testing removeAll w/ null values on Private Creds"); 505 nullTestCollection(methRemove, mtSubj.getPrivateCredentials(), null); 506 nullTestCollection(methRemove, mtSubj.getPrivateCredentials(), 507 remPrvVals); 508 } 509 510 private static void testContainsAll() { 511 // Create a well formed subject and additional collections 512 Subject mtSubj = makeSubj(false, false, false); 513 Set<Principal> testPrincs = new HashSet<>(Arrays.asList(princVals)); 514 Set<Object> testPubVals = new HashSet<>(Arrays.asList(pubVals)); 515 Set<Object> testPrvVals = new HashSet<>(Arrays.asList(privVals)); 516 517 System.out.println("------ containsAll() -----"); 518 519 // Run one success test for each Subject family to verify the 520 // overloaded method works as intended. 521 if ((validTestCollection(methContains, mtSubj.getPrincipals(), 522 testPrincs) == false) && 523 (validTestCollection(methContains, mtSubj.getPublicCredentials(), 524 testPubVals) == false) && 525 (validTestCollection(methContains, 526 mtSubj.getPrivateCredentials(), testPrvVals) == false)) { 527 throw new RuntimeException("Valid containsAll() check failed"); 528 } 529 System.out.println("Positive containsAll() test passed"); 530 531 // Now let's add a null into each collection and watch the fireworks. 532 testPrincs.add(null); 533 testPubVals.add(null); 534 testPrvVals.add(null); 535 536 System.out.println( 537 "* Testing containsAll w/ null values on Principals"); 538 nullTestCollection(methContains, mtSubj.getPrincipals(), null); 539 nullTestCollection(methContains, mtSubj.getPrincipals(), testPrincs); 540 541 System.out.println( 542 "* Testing containsAll w/ null values on Public Creds"); 543 nullTestCollection(methContains, mtSubj.getPublicCredentials(), 544 null); 545 nullTestCollection(methContains, mtSubj.getPublicCredentials(), 546 testPubVals); 547 548 System.out.println( 549 "* Testing containsAll w/ null values on Private Creds"); 550 nullTestCollection(methContains, mtSubj.getPrivateCredentials(), 551 null); 552 nullTestCollection(methContains, mtSubj.getPrivateCredentials(), 553 testPrvVals); 554 } 555 556 private static void testRetainAll() { 557 // Create a well formed subject and additional collections 558 Subject mtSubj = makeSubj(false, false, false); 559 Set<Principal> remPrincs = new HashSet<>(Arrays.asList(tmplAddPrincs)); 560 Set<Object> remPubVals = new HashSet<>(Arrays.asList(tmplAddPubVals)); 561 Set<Object> remPrvVals = new HashSet<>(Arrays.asList(tmplAddPrvVals)); 562 563 // Add in values that exist within the Subject 564 remPrincs.add(princVals[2]); 565 remPubVals.add(pubVals[2]); 566 remPrvVals.add(privVals[2]); 567 568 // Run one success test for each Subject family to verify the 569 // overloaded method works as intended. 570 Set<Principal> setPrin = mtSubj.getPrincipals(); 571 Set<Object> setPubCreds = mtSubj.getPublicCredentials(); 572 Set<Object> setPrvCreds = mtSubj.getPrivateCredentials(); 573 int prinOrigSize = setPrin.size(); 574 int pubOrigSize = setPubCreds.size(); 575 int prvOrigSize = setPrvCreds.size(); 576 577 System.out.println("------ retainAll() -----"); 578 579 // Retain the specified members (those that exist in the Subject) 580 // and validate the results. 581 if (validTestCollection(methRetain, setPrin, remPrincs) == false || 582 setPrin.size() != 1 || setPrin.contains(princVals[2]) == false) 583 { 584 throw new RuntimeException("Failed retainAll() on principals"); 585 } 586 587 if (validTestCollection(methRetain, setPubCreds, 588 remPubVals) == false || 589 setPubCreds.size() != 1 || 590 setPubCreds.contains(pubVals[2]) == false) 591 { 592 throw new RuntimeException("Failed retainAll() on public creds"); 593 } 594 if (validTestCollection(methRetain, setPrvCreds, 595 remPrvVals) == false || 596 setPrvCreds.size() != 1 || 597 setPrvCreds.contains(privVals[2]) == false) 598 { 599 throw new RuntimeException("Failed retainAll() on private creds"); 600 } 601 System.out.println("Positive retainAll() test passed"); 602 603 // Now add null elements into each container, then retest 604 remPrincs.add(null); 605 remPubVals.add(null); 606 remPrvVals.add(null); 607 608 System.out.println("* Testing retainAll w/ null values on Principals"); 609 nullTestCollection(methRetain, mtSubj.getPrincipals(), null); 610 nullTestCollection(methRetain, mtSubj.getPrincipals(), remPrincs); 611 612 System.out.println( 613 "* Testing retainAll w/ null values on Public Creds"); 614 nullTestCollection(methRetain, mtSubj.getPublicCredentials(), null); 615 nullTestCollection(methRetain, mtSubj.getPublicCredentials(), 616 remPubVals); 617 618 System.out.println( 619 "* Testing retainAll w/ null values on Private Creds"); 620 nullTestCollection(methRetain, mtSubj.getPrivateCredentials(), null); 621 nullTestCollection(methRetain, mtSubj.getPrivateCredentials(), 622 remPrvVals); 623 } 624 625 public static void main(String[] args) throws Exception { 626 627 testCTOR(); 628 629 testDeserialize(); 630 631 testAdd(); 632 633 testRemove(); 634 635 testContains(); 636 637 testAddAll(); 638 639 testRemoveAll(); 640 641 testContainsAll(); 642 643 testRetainAll(); 644 645 } 646 }