1 /*
   2  * Copyright (c) 2013, 2016, 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.security.CodeSource;
  25 import java.security.Permission;
  26 import java.security.PermissionCollection;
  27 import java.security.Permissions;
  28 import java.security.Policy;
  29 import java.security.ProtectionDomain;
  30 import java.util.EnumSet;
  31 import java.util.HashMap;
  32 import java.util.Map;
  33 import java.util.logging.LogManager;
  34 import java.util.logging.Logger;
  35 import java.util.logging.LoggingPermission;
  36 import jdk.internal.access.JavaAWTAccess;
  37 import jdk.internal.access.SharedSecrets;
  38 
  39 /*
  40  * @test
  41  * @bug 8017174 8010727 8019945
  42  * @summary  NPE when using Logger.getAnonymousLogger or
  43  *           LogManager.getLogManager().getLogger
  44  *
  45  * @modules java.base/jdk.internal.access
  46  *          java.logging
  47  * @run main/othervm -Dtest.security=off TestAppletLoggerContext LoadingApplet
  48  * @run main/othervm -Dtest.security=on TestAppletLoggerContext  LoadingApplet
  49  * @run main/othervm -Dtest.security=off TestAppletLoggerContext LoadingMain
  50  * @run main/othervm -Dtest.security=on TestAppletLoggerContext  LoadingMain
  51  * @run main/othervm -Dtest.security=off TestAppletLoggerContext One
  52  * @run main/othervm -Dtest.security=on TestAppletLoggerContext  One
  53  * @run main/othervm -Dtest.security=off TestAppletLoggerContext Two
  54  * @run main/othervm -Dtest.security=on TestAppletLoggerContext  Two
  55  * @run main/othervm -Dtest.security=off TestAppletLoggerContext Three
  56  * @run main/othervm -Dtest.security=on TestAppletLoggerContext  Three
  57  * @run main/othervm -Dtest.security=off TestAppletLoggerContext Four
  58  * @run main/othervm -Dtest.security=on TestAppletLoggerContext  Four
  59  * @run main/othervm -Dtest.security=off TestAppletLoggerContext Five
  60  * @run main/othervm -Dtest.security=on TestAppletLoggerContext  Five
  61  * @run main/othervm -Dtest.security=off TestAppletLoggerContext Six
  62  * @run main/othervm -Dtest.security=on TestAppletLoggerContext  Six
  63  * @run main/othervm -Dtest.security=off TestAppletLoggerContext Seven
  64  * @run main/othervm -Dtest.security=on TestAppletLoggerContext  Seven
  65  * @run main/othervm -Dtest.security=off TestAppletLoggerContext
  66  * @run main/othervm -Dtest.security=on TestAppletLoggerContext
  67  */
  68 
  69 // NOTE: We run in other VM in order to 1. switch security manager and 2. cause
  70 // LogManager class to be loaded anew.
  71 public class TestAppletLoggerContext {
  72 
  73     // Avoids the hassle of dealing with files and system props...
  74     static class SimplePolicy extends Policy {
  75         private final Permissions perms;
  76         public SimplePolicy(Permission... permissions) {
  77             perms = new Permissions();
  78             for (Permission permission : permissions) {
  79                 perms.add(permission);
  80             }
  81         }
  82         @Override
  83         public PermissionCollection getPermissions(CodeSource cs) {
  84             return perms;
  85         }
  86         @Override
  87         public PermissionCollection getPermissions(ProtectionDomain pd) {
  88             return perms;
  89         }
  90         @Override
  91         public boolean implies(ProtectionDomain pd, Permission p) {
  92            return perms.implies(p);
  93         }
  94     }
  95 
  96     // The bridge class initializes the logging system.
  97     // It stubs the applet context in order to simulate context changes.
  98     //
  99     public static class Bridge {
 100 
 101         private static class JavaAWTAccessStub implements JavaAWTAccess {
 102             boolean active = true;
 103 
 104             private static class TestExc {
 105                 private final Map<Object, Object> map = new HashMap<>();
 106                 void put(Object key, Object v) { map.put(key, v); }
 107                 Object get(Object key) { return map.get(key); }
 108                 void remove(Object o) { map.remove(o); }
 109                 public static TestExc exc(Object o) {
 110                     return TestExc.class.cast(o);
 111                 }
 112             }
 113 
 114             TestExc exc;
 115 
 116             @Override
 117             public Object getAppletContext() { return active ? exc : null; }
 118         }
 119 
 120         static final JavaAWTAccessStub javaAwtAccess = new JavaAWTAccessStub();
 121         public static void init() {
 122             SharedSecrets.setJavaAWTAccess(javaAwtAccess);
 123             if (System.getProperty("test.security", "on").equals("on")) {
 124                 Policy p = new SimplePolicy(new LoggingPermission("control", null),
 125                     new RuntimePermission("setContextClassLoader"),
 126                     new RuntimePermission("shutdownHooks"));
 127                 Policy.setPolicy(p);
 128                 System.setSecurityManager(new SecurityManager());
 129             }
 130         }
 131 
 132         public static void changeContext() {
 133             System.out.println("... Switching to a new applet context ...");
 134             javaAwtAccess.active = true;
 135             javaAwtAccess.exc = new JavaAWTAccessStub.TestExc();
 136         }
 137 
 138         public static void desactivate() {
 139             System.out.println("... Running with no applet context ...");
 140             javaAwtAccess.exc = null;
 141             javaAwtAccess.active = false;
 142         }
 143 
 144         public static class CustomAnonymousLogger extends Logger {
 145             public CustomAnonymousLogger() {
 146                 this("");
 147             }
 148             public CustomAnonymousLogger(String name) {
 149                 super(null, null);
 150                 System.out.println( " LogManager: " +LogManager.getLogManager());
 151                 System.out.println( " getLogger: " +LogManager.getLogManager().getLogger(name));
 152                 setParent(LogManager.getLogManager().getLogger(name));
 153             }
 154         }
 155 
 156         public static class CustomLogger extends Logger {
 157             CustomLogger(String name) {
 158                 super(name, null);
 159             }
 160         }
 161     }
 162 
 163     public static enum TestCase {
 164         LoadingApplet, LoadingMain, One, Two, Three, Four, Five, Six, Seven;
 165         public void test() {
 166             switch(this) {
 167                 // When run - each of these two tests must be
 168                 // run before any other tests and before each other.
 169                 case LoadingApplet: testLoadingApplet(); break;
 170                 case LoadingMain:   testLoadingMain(); break;
 171                 case One:   testOne(); break;
 172                 case Two:   testTwo(); break;
 173                 case Three: testThree(); break;
 174                 case Four:  testFour(); break;
 175                 case Five:  testFive(); break;
 176                 case Six:   testSix(); break;
 177                 case Seven: testSeven(); break;
 178             }
 179         }
 180         public String describe() {
 181             switch(this) {
 182                 case LoadingApplet:
 183                     return "Test that when the LogManager class is"
 184                         + " loaded in  an applet thread first,"
 185                         + "\n all LoggerContexts are correctly initialized";
 186                 case LoadingMain:
 187                     return "Test that when the LogManager class is"
 188                         + " loaded in  the main thread first,"
 189                         + "\n all LoggerContexts are correctly initialized";
 190                 case One:
 191                     return "Test that Logger.getAnonymousLogger()"
 192                         + " and new CustomAnonymousLogger() don't throw NPE";
 193                 case Two:
 194                     return "Test that Logger.getLogger(\"\")"
 195                             + " does not return null nor throws NPE";
 196                 case Three:
 197                     return "Test that LogManager.getLogManager().getLogger(\"\")"
 198                             + " does not return null nor throws NPE";
 199                 case Four:
 200                     return "Test that Logger.getLogger(Logger.GLOBAL_LOGGER_NAME)"
 201                             + " does not return null,\n and that"
 202                             + " new CustomAnonymousLogger(Logger.GLOBAL_LOGGER_NAME)"
 203                             + " does not throw NPE";
 204                 case Five:
 205                     return "Test that LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME)"
 206                             + "\n does not return null nor throws NPE";
 207                 case Six:
 208                     return "Test that manager.getLogger(Logger.GLOBAL_LOGGER_NAME)"
 209                             + " returns null\n when manager is not the default"
 210                             + " LogManager instance.\n"
 211                             + "Test adding a new logger named \"global\" in that"
 212                             + " non default instance.";
 213                 case Seven: return "Test that manager.getLogger(\"\")"
 214                             + " returns null\n when manager is not the default"
 215                             + " LogManager instance.\n"
 216                             + "Test adding a new logger named \"\" in that"
 217                             + " non default instance.";
 218                 default: return "Undefined";
 219             }
 220         }
 221     };
 222 
 223     /**
 224      * @param args the command line arguments
 225      */
 226     public static void main(String[] args) {
 227         Bridge.init();
 228         EnumSet<TestCase> tests = EnumSet.noneOf(TestCase.class);
 229         for (String arg : args) {
 230             tests.add(TestCase.valueOf(arg));
 231         }
 232         if (args.length == 0) {
 233             tests = EnumSet.complementOf(EnumSet.of(TestCase.LoadingMain));
 234         }
 235         final EnumSet<TestCase> loadingTests =
 236             EnumSet.of(TestCase.LoadingApplet, TestCase.LoadingMain);
 237         int testrun = 0;
 238         for (TestCase test : tests) {
 239             if (loadingTests.contains(test)) {
 240                 if (testrun > 0) {
 241                     throw new UnsupportedOperationException("Test case "
 242                           + test + " must be executed first!");
 243                 }
 244             }
 245             System.out.println("Testing "+ test+": ");
 246             System.out.println(test.describe());
 247             try {
 248                 test.test();
 249             } catch (Exception x) {
 250                throw new Error(String.valueOf(test)
 251                    + (System.getSecurityManager() == null ? " without " : " with ")
 252                    + "security failed: "+x+"\n "+"FAILED: "+test.describe()+"\n", x);
 253             } finally {
 254                 testrun++;
 255             }
 256             Bridge.changeContext();
 257             System.out.println("PASSED: "+ test);
 258         }
 259     }
 260 
 261     public static void testLoadingApplet() {
 262         Bridge.changeContext();
 263 
 264         Logger bar = new Bridge.CustomLogger("com.foo.Bar");
 265         LogManager.getLogManager().addLogger(bar);
 266         assertNotNull(bar.getParent());
 267         testParent(bar);
 268         testParent(LogManager.getLogManager().getLogger("global"));
 269         testParent(LogManager.getLogManager().getLogger(bar.getName()));
 270 
 271         Bridge.desactivate();
 272 
 273         Logger foo = new Bridge.CustomLogger("com.foo.Foo");
 274         boolean b = LogManager.getLogManager().addLogger(foo);
 275         assertEquals(Boolean.TRUE, Boolean.valueOf(b));
 276         assertNotNull(foo.getParent());
 277         testParent(foo);
 278         testParent(LogManager.getLogManager().getLogger("global"));
 279         testParent(LogManager.getLogManager().getLogger(foo.getName()));
 280     }
 281 
 282     public static void testLoadingMain() {
 283         Bridge.desactivate();
 284 
 285         Logger bar = new Bridge.CustomLogger("com.foo.Bar");
 286         LogManager.getLogManager().addLogger(bar);
 287         assertNotNull(bar.getParent());
 288         testParent(bar);
 289         testParent(LogManager.getLogManager().getLogger("global"));
 290         testParent(LogManager.getLogManager().getLogger(bar.getName()));
 291 
 292         Bridge.changeContext();
 293 
 294         Logger foo = new Bridge.CustomLogger("com.foo.Foo");
 295         boolean b = LogManager.getLogManager().addLogger(foo);
 296         assertEquals(Boolean.TRUE, Boolean.valueOf(b));
 297         assertNotNull(foo.getParent());
 298         testParent(foo);
 299         testParent(LogManager.getLogManager().getLogger("global"));
 300         testParent(LogManager.getLogManager().getLogger(foo.getName()));
 301 
 302     }
 303 
 304     public static void testOne() {
 305         for (int i=0; i<3 ; i++) {
 306             Logger logger1 = Logger.getAnonymousLogger();
 307             Logger logger1b = Logger.getAnonymousLogger();
 308             Bridge.changeContext();
 309             Logger logger2 = Logger.getAnonymousLogger();
 310             Logger logger2b = Logger.getAnonymousLogger();
 311             Bridge.changeContext();
 312             Logger logger3 = new Bridge.CustomAnonymousLogger();
 313             Logger logger3b = new Bridge.CustomAnonymousLogger();
 314             Bridge.changeContext();
 315             Logger logger4 = new Bridge.CustomAnonymousLogger();
 316             Logger logger4b = new Bridge.CustomAnonymousLogger();
 317         }
 318     }
 319 
 320 
 321     public static void testTwo() {
 322         for (int i=0; i<3 ; i++) {
 323             Logger logger1 = Logger.getLogger("");
 324             Logger logger1b = Logger.getLogger("");
 325             assertNotNull(logger1);
 326             assertNotNull(logger1b);
 327             assertEquals(logger1, logger1b);
 328             Bridge.changeContext();
 329             Logger logger2 = Logger.getLogger("");
 330             Logger logger2b = Logger.getLogger("");
 331             assertNotNull(logger2);
 332             assertNotNull(logger2b);
 333             assertEquals(logger2, logger2b);
 334             assertEquals(logger1, logger2);
 335         }
 336     }
 337 
 338     public static void testThree() {
 339         for (int i=0; i<3 ; i++) {
 340             Logger logger1 = LogManager.getLogManager().getLogger("");
 341             Logger logger1b = LogManager.getLogManager().getLogger("");
 342             assertNotNull(logger1);
 343             assertNotNull(logger1b);
 344             assertEquals(logger1, logger1b);
 345             Bridge.changeContext();
 346             Logger logger2 = LogManager.getLogManager().getLogger("");
 347             Logger logger2b = LogManager.getLogManager().getLogger("");
 348             assertNotNull(logger2);
 349             assertNotNull(logger2b);
 350             assertEquals(logger2, logger2b);
 351             assertEquals(logger1, logger2);
 352         }
 353     }
 354 
 355     public static void testFour() {
 356         for (int i=0; i<3 ; i++) {
 357             Logger logger1 = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
 358             Logger logger1b = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
 359             assertNotNull(logger1);
 360             assertNotNull(logger1b);
 361             assertEquals(logger1, logger1b);
 362             Bridge.changeContext();
 363 
 364             Logger logger2 = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
 365             Logger logger2b = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
 366             assertNotNull(logger2);
 367             assertNotNull(logger2b);
 368             assertEquals(logger2, logger2b);
 369 
 370             assertEquals(logger1, logger2);
 371 
 372             Bridge.changeContext();
 373             Logger logger3 = new Bridge.CustomAnonymousLogger(Logger.GLOBAL_LOGGER_NAME);
 374             Logger logger3b = new Bridge.CustomAnonymousLogger(Logger.GLOBAL_LOGGER_NAME);
 375             Bridge.changeContext();
 376             Logger logger4 = new Bridge.CustomAnonymousLogger(Logger.GLOBAL_LOGGER_NAME);
 377             Logger logger4b = new Bridge.CustomAnonymousLogger(Logger.GLOBAL_LOGGER_NAME);
 378         }
 379     }
 380 
 381     public static void testFive() {
 382         for (int i=0; i<3 ; i++) {
 383             Logger logger1 = LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME);
 384             Logger logger1b = LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME);
 385             assertNotNull(logger1);
 386             assertNotNull(logger1b);
 387             assertEquals(logger1, logger1b);
 388 
 389             Bridge.changeContext();
 390 
 391             Logger logger2 = LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME);
 392             Logger logger2b = LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME);
 393             assertNotNull(logger2);
 394             assertNotNull(logger2b);
 395             assertEquals(logger2, logger2b);
 396 
 397             assertEquals(logger1, logger2);
 398         }
 399     }
 400 
 401     /**
 402      * This test is designed to test the behavior of additional LogManager instances.
 403      * It must be noted that if the security manager is off, then calling
 404      * Bridge.changeContext() has actually no effect - which explains why we have
 405      * some differences between the cases security manager on & security manager
 406      * off.
 407      **/
 408     public static void testSix() {
 409         for (int i=0; i<3 ; i++) {
 410             Bridge.desactivate();
 411             LogManager manager = new LogManager() {};
 412             Logger logger1 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
 413             Logger logger1b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
 414             assertNull(logger1);
 415             assertNull(logger1b);
 416             Logger global = new Bridge.CustomLogger(Logger.GLOBAL_LOGGER_NAME);
 417             manager.addLogger(global);
 418             Logger logger2 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
 419             Logger logger2b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
 420             assertNotNull(logger2);
 421             assertNotNull(logger2b);
 422             assertEquals(logger2, global);
 423             assertEquals(logger2b, global);
 424             assertNull(manager.getLogger(""));
 425             assertNull(manager.getLogger(""));
 426 
 427             for (int j = 0; j<3; j++) {
 428                 Bridge.changeContext();
 429 
 430                 // this is not a supported configuration:
 431                 // We are in an applet context with several log managers.
 432                 // We however need to check our assumptions...
 433 
 434                 // Applet context => root logger and global logger should also be null.
 435 
 436                 Logger expected = (System.getSecurityManager() == null ? global : null);
 437                 Logger logger3 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
 438                 Logger logger3b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
 439                 assertEquals(expected, logger3);
 440                 assertEquals(expected, logger3b);
 441                 Logger global2 = new Bridge.CustomLogger(Logger.GLOBAL_LOGGER_NAME);
 442                 manager.addLogger(global2);
 443                 Logger logger4 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
 444                 Logger logger4b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
 445                 assertNotNull(logger4);
 446                 assertNotNull(logger4b);
 447                 expected = (System.getSecurityManager() == null ? global : global2);;
 448                 assertEquals(logger4,  expected);
 449                 assertEquals(logger4b, expected);
 450 
 451                 Logger logger5 = manager.getLogger("");
 452                 Logger logger5b = manager.getLogger("");
 453                 Logger expectedRoot = null;
 454                 assertEquals(logger5, expectedRoot);
 455                 assertEquals(logger5b, expectedRoot);
 456             }
 457 
 458         }
 459     }
 460 
 461     /**
 462      * This test is designed to test the behavior of additional LogManager instances.
 463      * It must be noted that if the security manager is off, then calling
 464      * Bridge.changeContext() has actually no effect - which explains why we have
 465      * some differences between the cases security manager on & security manager
 466      * off.
 467      **/
 468     public static void testSeven() {
 469         for (int i=0; i<3 ; i++) {
 470             Bridge.desactivate();
 471             LogManager manager = new LogManager() {};
 472             Logger logger1 = manager.getLogger("");
 473             Logger logger1b = manager.getLogger("");
 474             assertNull(logger1);
 475             assertNull(logger1b);
 476             Logger global = new Bridge.CustomLogger(Logger.GLOBAL_LOGGER_NAME);
 477             manager.addLogger(global);
 478             Logger logger2 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
 479             Logger logger2b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
 480             assertNotNull(logger2);
 481             assertNotNull(logger2b);
 482             assertEquals(logger2, global);
 483             assertEquals(logger2b, global);
 484             Logger logger3 = manager.getLogger("");
 485             Logger logger3b = manager.getLogger("");
 486             assertNull(logger3);
 487             assertNull(logger3b);
 488             Logger root = new Bridge.CustomLogger("");
 489             manager.addLogger(root);
 490             Logger logger4 = manager.getLogger("");
 491             Logger logger4b = manager.getLogger("");
 492             assertNotNull(logger4);
 493             assertNotNull(logger4b);
 494             assertEquals(logger4, root);
 495             assertEquals(logger4b, root);
 496 
 497             for (int j = 0 ; j < 3 ; j++) {
 498                 Bridge.changeContext();
 499 
 500                 // this is not a supported configuration:
 501                 // We are in an applet context with several log managers.
 502                 // We however need to check our assumptions...
 503 
 504                 // Applet context => root logger and global logger should also be null.
 505 
 506                 Logger logger5 = manager.getLogger("");
 507                 Logger logger5b = manager.getLogger("");
 508                 Logger expectedRoot = (System.getSecurityManager() == null ? root : null);
 509                 assertEquals(logger5, expectedRoot);
 510                 assertEquals(logger5b, expectedRoot);
 511 
 512                 if (System.getSecurityManager() != null) {
 513                     assertNull(manager.getLogger(Logger.GLOBAL_LOGGER_NAME));
 514                 } else {
 515                     assertEquals(global, manager.getLogger(Logger.GLOBAL_LOGGER_NAME));
 516                 }
 517 
 518                 Logger global2 = new Bridge.CustomLogger(Logger.GLOBAL_LOGGER_NAME);
 519                 manager.addLogger(global2);
 520                 Logger logger6 = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
 521                 Logger logger6b = manager.getLogger(Logger.GLOBAL_LOGGER_NAME);
 522                 Logger expectedGlobal = (System.getSecurityManager() == null ? global : global2);
 523 
 524                 assertNotNull(logger6);
 525                 assertNotNull(logger6b);
 526                 assertEquals(logger6, expectedGlobal);
 527                 assertEquals(logger6b, expectedGlobal);
 528                 if (System.getSecurityManager() != null) {
 529                     assertNull(manager.getLogger(""));
 530                 } else {
 531                     assertEquals(root, manager.getLogger(""));
 532                 }
 533 
 534                 Logger root2 = new Bridge.CustomLogger("");
 535                 manager.addLogger(root2);
 536                 expectedRoot = (System.getSecurityManager() == null ? root : root2);
 537                 Logger logger7 = manager.getLogger("");
 538                 Logger logger7b = manager.getLogger("");
 539                 assertNotNull(logger7);
 540                 assertNotNull(logger7b);
 541                 assertEquals(logger7, expectedRoot);
 542                 assertEquals(logger7b, expectedRoot);
 543             }
 544         }
 545     }
 546 
 547     public static void testParent(Logger logger) {
 548         Logger l = logger;
 549         while (l.getParent() != null) {
 550             l = l.getParent();
 551         }
 552         assertEquals("", l.getName());
 553     }
 554 
 555     public static class TestError extends RuntimeException {
 556         public TestError(String msg) {
 557             super(msg);
 558         }
 559     }
 560 
 561     public static void assertNotNull(Object obj) {
 562         if (obj == null) throw new NullPointerException();
 563     }
 564 
 565     public static void assertNull(Object obj) {
 566         if (obj != null) throw new TestError("Null expected, got "+obj);
 567     }
 568 
 569     public static void assertEquals(Object o1, Object o2) {
 570         if (o1 != o2) {
 571             throw new TestError(o1 + " != " + o2);
 572         }
 573     }
 574 
 575     public static void assertNotEquals(Object o1, Object o2) {
 576         if (o1 == o2) {
 577             throw new TestError(o1 + " == " + o2);
 578         }
 579     }
 580 }