1 /*
   2  * Copyright (c) 2011, 2018, 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 7073631 7159445 7156633 8028235 8065753 8205418 8205913
  27  * @summary tests error and diagnostics positions
  28  * @author  Jan Lahoda
  29  * @modules jdk.compiler/com.sun.tools.javac.api
  30  *          jdk.compiler/com.sun.tools.javac.main
  31  *          jdk.compiler/com.sun.tools.javac.tree
  32  */
  33 
  34 import com.sun.source.tree.BinaryTree;
  35 import com.sun.source.tree.BlockTree;
  36 import com.sun.source.tree.ClassTree;
  37 import com.sun.source.tree.CompilationUnitTree;
  38 import com.sun.source.tree.ErroneousTree;
  39 import com.sun.source.tree.ExpressionStatementTree;
  40 import com.sun.source.tree.ExpressionTree;
  41 import com.sun.source.tree.IfTree;
  42 import com.sun.source.tree.LambdaExpressionTree;
  43 import com.sun.source.tree.MethodInvocationTree;
  44 import com.sun.source.tree.MethodTree;
  45 import com.sun.source.tree.ModifiersTree;
  46 import com.sun.source.tree.PrimitiveTypeTree;
  47 import com.sun.source.tree.StatementTree;
  48 import com.sun.source.tree.Tree;
  49 import com.sun.source.tree.Tree.Kind;
  50 import com.sun.source.tree.VariableTree;
  51 import com.sun.source.tree.WhileLoopTree;
  52 import com.sun.source.util.JavacTask;
  53 import com.sun.source.util.SourcePositions;
  54 import com.sun.source.util.TreePath;
  55 import com.sun.source.util.TreePathScanner;
  56 import com.sun.source.util.TreeScanner;
  57 import com.sun.source.util.Trees;
  58 import com.sun.tools.javac.api.JavacTaskImpl;
  59 import com.sun.tools.javac.main.Main;
  60 import com.sun.tools.javac.main.Main.Result;
  61 import com.sun.tools.javac.tree.JCTree;
  62 import java.io.IOException;
  63 import java.io.StringWriter;
  64 import java.lang.annotation.ElementType;
  65 import java.lang.annotation.Retention;
  66 import java.lang.annotation.RetentionPolicy;
  67 import java.lang.annotation.Target;
  68 import java.lang.reflect.Method;
  69 import java.net.URI;
  70 import java.util.ArrayList;
  71 import java.util.Arrays;
  72 import java.util.LinkedList;
  73 import java.util.List;
  74 import java.util.regex.Pattern;
  75 import javax.lang.model.type.TypeKind;
  76 import javax.tools.Diagnostic;
  77 import javax.tools.DiagnosticCollector;
  78 import javax.tools.DiagnosticListener;
  79 import javax.tools.JavaCompiler;
  80 import javax.tools.JavaFileManager;
  81 import javax.tools.JavaFileObject;
  82 import javax.tools.SimpleJavaFileObject;
  83 import javax.tools.ToolProvider;
  84 
  85 public class JavacParserTest extends TestCase {
  86     static final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
  87     static final JavaFileManager fm = tool.getStandardFileManager(null, null, null);
  88 
  89     private JavacParserTest(){}
  90 
  91     public static void main(String... args) throws Exception {
  92         try {
  93             new JavacParserTest().run(args);
  94         } finally {
  95             fm.close();
  96         }
  97     }
  98 
  99     class MyFileObject extends SimpleJavaFileObject {
 100 
 101         private String text;
 102 
 103         public MyFileObject(String text) {
 104             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
 105             this.text = text;
 106         }
 107 
 108         @Override
 109         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
 110             return text;
 111         }
 112     }
 113     /*
 114      * converts Windows to Unix style LFs for comparing strings
 115      */
 116     String normalize(String in) {
 117         return in.replace(System.getProperty("line.separator"), "\n");
 118     }
 119 
 120     CompilationUnitTree getCompilationUnitTree(String code) throws IOException {
 121 
 122         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, null, null,
 123                 null, Arrays.asList(new MyFileObject(code)));
 124         CompilationUnitTree cut = ct.parse().iterator().next();
 125         return cut;
 126     }
 127 
 128     List<String> getErroneousTreeValues(ErroneousTree node) {
 129 
 130         List<String> values = new ArrayList<>();
 131         if (node.getErrorTrees() != null) {
 132             for (Tree t : node.getErrorTrees()) {
 133                 values.add(t.toString());
 134             }
 135         } else {
 136             throw new RuntimeException("ERROR: No Erroneous tree "
 137                     + "has been created.");
 138         }
 139         return values;
 140     }
 141 
 142     @Test
 143     void testPositionForSuperConstructorCalls() throws IOException {
 144         assert tool != null;
 145 
 146         String code = "package test; public class Test {public Test() {super();}}";
 147 
 148         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, null, null,
 149                 null, Arrays.asList(new MyFileObject(code)));
 150         CompilationUnitTree cut = ct.parse().iterator().next();
 151         SourcePositions pos = Trees.instance(ct).getSourcePositions();
 152 
 153         MethodTree method =
 154                 (MethodTree) ((ClassTree) cut.getTypeDecls().get(0)).getMembers().get(0);
 155         ExpressionStatementTree es =
 156                 (ExpressionStatementTree) method.getBody().getStatements().get(0);
 157 
 158         final int esStartPos = code.indexOf(es.toString());
 159         final int esEndPos = esStartPos + es.toString().length();
 160         assertEquals("testPositionForSuperConstructorCalls",
 161                 esStartPos, pos.getStartPosition(cut, es));
 162         assertEquals("testPositionForSuperConstructorCalls",
 163                 esEndPos, pos.getEndPosition(cut, es));
 164 
 165         MethodInvocationTree mit = (MethodInvocationTree) es.getExpression();
 166 
 167         final int mitStartPos = code.indexOf(mit.toString());
 168         final int mitEndPos = mitStartPos + mit.toString().length();
 169         assertEquals("testPositionForSuperConstructorCalls",
 170                 mitStartPos, pos.getStartPosition(cut, mit));
 171         assertEquals("testPositionForSuperConstructorCalls",
 172                 mitEndPos, pos.getEndPosition(cut, mit));
 173 
 174         final int methodStartPos = mitStartPos;
 175         final int methodEndPos = methodStartPos + mit.getMethodSelect().toString().length();
 176         assertEquals("testPositionForSuperConstructorCalls",
 177                 methodStartPos, pos.getStartPosition(cut, mit.getMethodSelect()));
 178         assertEquals("testPositionForSuperConstructorCalls",
 179                 methodEndPos, pos.getEndPosition(cut, mit.getMethodSelect()));
 180     }
 181 
 182     @Test
 183     void testPositionForEnumModifiers() throws IOException {
 184         final String theString = "public";
 185         String code = "package test; " + theString + " enum Test {A;}";
 186 
 187         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, null, null,
 188                 null, Arrays.asList(new MyFileObject(code)));
 189         CompilationUnitTree cut = ct.parse().iterator().next();
 190         SourcePositions pos = Trees.instance(ct).getSourcePositions();
 191 
 192         ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
 193         ModifiersTree mt = clazz.getModifiers();
 194         int spos = code.indexOf(theString);
 195         int epos = spos + theString.length();
 196         assertEquals("testPositionForEnumModifiers",
 197                 spos, pos.getStartPosition(cut, mt));
 198         assertEquals("testPositionForEnumModifiers",
 199                 epos, pos.getEndPosition(cut, mt));
 200     }
 201 
 202     @Test
 203     void testNewClassWithEnclosing() throws IOException {
 204 
 205         final String theString = "Test.this.new d()";
 206         String code = "package test; class Test { " +
 207                 "class d {} private void method() { " +
 208                 "Object o = " + theString + "; } }";
 209 
 210         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, null, null,
 211                 null, Arrays.asList(new MyFileObject(code)));
 212         CompilationUnitTree cut = ct.parse().iterator().next();
 213         SourcePositions pos = Trees.instance(ct).getSourcePositions();
 214 
 215         ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
 216         ExpressionTree est =
 217                 ((VariableTree) ((MethodTree) clazz.getMembers().get(1)).getBody().getStatements().get(0)).getInitializer();
 218 
 219         final int spos = code.indexOf(theString);
 220         final int epos = spos + theString.length();
 221         assertEquals("testNewClassWithEnclosing",
 222                 spos, pos.getStartPosition(cut, est));
 223         assertEquals("testNewClassWithEnclosing",
 224                 epos, pos.getEndPosition(cut, est));
 225     }
 226 
 227     @Test
 228     void testPreferredPositionForBinaryOp() throws IOException {
 229 
 230         String code = "package test; public class Test {"
 231                 + "private void test() {"
 232                 + "Object o = null; boolean b = o != null && o instanceof String;"
 233                 + "} private Test() {}}";
 234 
 235         CompilationUnitTree cut = getCompilationUnitTree(code);
 236         ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
 237         MethodTree method = (MethodTree) clazz.getMembers().get(0);
 238         VariableTree condSt = (VariableTree) method.getBody().getStatements().get(1);
 239         BinaryTree cond = (BinaryTree) condSt.getInitializer();
 240 
 241         JCTree condJC = (JCTree) cond;
 242         int condStartPos = code.indexOf("&&");
 243         assertEquals("testPreferredPositionForBinaryOp",
 244                 condStartPos, condJC.pos);
 245     }
 246 
 247     @Test
 248     void testErrorRecoveryForEnhancedForLoop142381() throws IOException {
 249 
 250         String code = "package test; class Test { " +
 251                 "private void method() { " +
 252                 "java.util.Set<String> s = null; for (a : s) {} } }";
 253 
 254         final List<Diagnostic<? extends JavaFileObject>> errors =
 255                 new LinkedList<Diagnostic<? extends JavaFileObject>>();
 256 
 257         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm,
 258                 new DiagnosticListener<JavaFileObject>() {
 259             public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
 260                 errors.add(diagnostic);
 261             }
 262         }, null, null, Arrays.asList(new MyFileObject(code)));
 263 
 264         CompilationUnitTree cut = ct.parse().iterator().next();
 265 
 266         ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
 267         StatementTree forStatement =
 268                 ((MethodTree) clazz.getMembers().get(0)).getBody().getStatements().get(1);
 269 
 270         assertEquals("testErrorRecoveryForEnhancedForLoop142381",
 271                 Kind.ENHANCED_FOR_LOOP, forStatement.getKind());
 272         assertFalse("testErrorRecoveryForEnhancedForLoop142381", errors.isEmpty());
 273     }
 274 
 275     @Test
 276     void testPositionAnnotationNoPackage187551() throws IOException {
 277 
 278         String code = "\n@interface Test {}";
 279 
 280         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, null, null,
 281                 null, Arrays.asList(new MyFileObject(code)));
 282 
 283         CompilationUnitTree cut = ct.parse().iterator().next();
 284         ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
 285         Trees t = Trees.instance(ct);
 286 
 287         assertEquals("testPositionAnnotationNoPackage187551",
 288                 1, t.getSourcePositions().getStartPosition(cut, clazz));
 289     }
 290 
 291     @Test
 292     void testPositionMissingStatement() throws IOException {
 293         String code = "class C { void t() { if (true) } }";
 294         DiagnosticCollector<JavaFileObject> dc = new DiagnosticCollector<>();
 295 
 296         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, dc, null,
 297                 null, Arrays.asList(new MyFileObject(code)));
 298 
 299         CompilationUnitTree cut = ct.parse().iterator().next();
 300         Trees trees = Trees.instance(ct);
 301         SourcePositions positions = trees.getSourcePositions();
 302 
 303         new TreeScanner<Void, Void>() {
 304             @Override
 305             public Void visitIf(IfTree it, Void v) {
 306                 StatementTree st = it.getThenStatement();
 307                 int startpos = (int) positions.getStartPosition(cut, st);
 308                 int endpos = (int) positions.getEndPosition(cut, st);
 309                 assertEquals("testPositionMissingStatement.execpos", startpos, endpos);
 310                 assertEquals("testPositionMissingStatement.execkind",
 311                              Kind.EXPRESSION_STATEMENT,
 312                              st.getKind());
 313                 Tree err = ((ExpressionStatementTree) st).getExpression();
 314                 startpos = (int) positions.getStartPosition(cut, err);
 315                 endpos = (int) positions.getEndPosition(cut, err);
 316                 assertEquals("testPositionMissingStatement.errpos", startpos, endpos);
 317                 assertEquals("testPositionMissingStatement.errkind",
 318                              Kind.ERRONEOUS,
 319                              err.getKind());
 320                 return super.visitIf(it, v);
 321             }
 322         }.scan(cut, null);
 323 
 324         assertEquals("testPositionMissingStatement.diags", 1, dc.getDiagnostics().size());
 325         Diagnostic<? extends JavaFileObject> d = dc.getDiagnostics().get(0);
 326         int startpos = (int) d.getStartPosition();
 327         int pos = (int) d.getPosition();
 328         int endpos = (int) d.getEndPosition();
 329         assertEquals("testPositionMissingStatement.diagspan", startpos, endpos);
 330         assertEquals("testPositionMissingStatement.diagpref", startpos, pos);
 331     }
 332 
 333     @Test
 334     void testPositionsSane1() throws IOException {
 335         performPositionsSanityTest("package test; class Test { " +
 336                 "private void method() { " +
 337                 "java.util.List<? extends java.util.List<? extends String>> l; " +
 338                 "} }");
 339     }
 340 
 341     @Test
 342     void testPositionsSane2() throws IOException {
 343         performPositionsSanityTest("package test; class Test { " +
 344                 "private void method() { " +
 345                 "java.util.List<? super java.util.List<? super String>> l; " +
 346                 "} }");
 347     }
 348 
 349     @Test
 350     void testPositionsSane3() throws IOException {
 351         performPositionsSanityTest("package test; class Test { " +
 352                 "private void method() { " +
 353                 "java.util.List<? super java.util.List<?>> l; } }");
 354     }
 355 
 356     private void performPositionsSanityTest(String code) throws IOException {
 357 
 358         final List<Diagnostic<? extends JavaFileObject>> errors =
 359                 new LinkedList<Diagnostic<? extends JavaFileObject>>();
 360 
 361         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm,
 362                 new DiagnosticListener<JavaFileObject>() {
 363 
 364             public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
 365                 errors.add(diagnostic);
 366             }
 367         }, null, null, Arrays.asList(new MyFileObject(code)));
 368 
 369         final CompilationUnitTree cut = ct.parse().iterator().next();
 370         final Trees trees = Trees.instance(ct);
 371 
 372         new TreeScanner<Void, Void>() {
 373 
 374             private long parentStart = 0;
 375             private long parentEnd = Integer.MAX_VALUE;
 376 
 377             @Override
 378             public Void scan(Tree node, Void p) {
 379                 if (node == null) {
 380                     return null;
 381                 }
 382 
 383                 long start = trees.getSourcePositions().getStartPosition(cut, node);
 384 
 385                 if (start == (-1)) {
 386                     return null; // synthetic tree
 387                 }
 388                 assertTrue(node.toString() + ":" + start + "/" + parentStart,
 389                         parentStart <= start);
 390 
 391                 long prevParentStart = parentStart;
 392 
 393                 parentStart = start;
 394 
 395                 long end = trees.getSourcePositions().getEndPosition(cut, node);
 396 
 397                 assertTrue(node.toString() + ":" + end + "/" + parentEnd,
 398                         end <= parentEnd);
 399 
 400                 long prevParentEnd = parentEnd;
 401 
 402                 parentEnd = end;
 403 
 404                 super.scan(node, p);
 405 
 406                 parentStart = prevParentStart;
 407                 parentEnd = prevParentEnd;
 408 
 409                 return null;
 410             }
 411 
 412             private void assertTrue(String message, boolean b) {
 413                 if (!b) fail(message);
 414             }
 415         }.scan(cut, null);
 416     }
 417 
 418     @Test
 419     void testCorrectWildcardPositions1() throws IOException {
 420         performWildcardPositionsTest("package test; import java.util.List; " +
 421                 "class Test { private void method() { List<? extends List<? extends String>> l; } }",
 422 
 423                 Arrays.asList("List<? extends List<? extends String>> l;",
 424                 "List<? extends List<? extends String>>",
 425                 "List",
 426                 "? extends List<? extends String>",
 427                 "List<? extends String>",
 428                 "List",
 429                 "? extends String",
 430                 "String"));
 431     }
 432 
 433     @Test
 434     void testCorrectWildcardPositions2() throws IOException {
 435         performWildcardPositionsTest("package test; import java.util.List; "
 436                 + "class Test { private void method() { List<? super List<? super String>> l; } }",
 437                 Arrays.asList("List<? super List<? super String>> l;",
 438                 "List<? super List<? super String>>",
 439                 "List",
 440                 "? super List<? super String>",
 441                 "List<? super String>",
 442                 "List",
 443                 "? super String",
 444                 "String"));
 445     }
 446 
 447     @Test
 448     void testCorrectWildcardPositions3() throws IOException {
 449         performWildcardPositionsTest("package test; import java.util.List; " +
 450                 "class Test { private void method() { List<? super List<?>> l; } }",
 451 
 452                 Arrays.asList("List<? super List<?>> l;",
 453                 "List<? super List<?>>",
 454                 "List",
 455                 "? super List<?>",
 456                 "List<?>",
 457                 "List",
 458                 "?"));
 459     }
 460 
 461     @Test
 462     void testCorrectWildcardPositions4() throws IOException {
 463         performWildcardPositionsTest("package test; import java.util.List; " +
 464                 "class Test { private void method() { " +
 465                 "List<? extends List<? extends List<? extends String>>> l; } }",
 466 
 467                 Arrays.asList("List<? extends List<? extends List<? extends String>>> l;",
 468                 "List<? extends List<? extends List<? extends String>>>",
 469                 "List",
 470                 "? extends List<? extends List<? extends String>>",
 471                 "List<? extends List<? extends String>>",
 472                 "List",
 473                 "? extends List<? extends String>",
 474                 "List<? extends String>",
 475                 "List",
 476                 "? extends String",
 477                 "String"));
 478     }
 479 
 480     @Test
 481     void testCorrectWildcardPositions5() throws IOException {
 482         performWildcardPositionsTest("package test; import java.util.List; " +
 483                 "class Test { private void method() { " +
 484                 "List<? extends List<? extends List<? extends String   >>> l; } }",
 485                 Arrays.asList("List<? extends List<? extends List<? extends String   >>> l;",
 486                 "List<? extends List<? extends List<? extends String   >>>",
 487                 "List",
 488                 "? extends List<? extends List<? extends String   >>",
 489                 "List<? extends List<? extends String   >>",
 490                 "List",
 491                 "? extends List<? extends String   >",
 492                 "List<? extends String   >",
 493                 "List",
 494                 "? extends String",
 495                 "String"));
 496     }
 497 
 498     void performWildcardPositionsTest(final String code,
 499             List<String> golden) throws IOException {
 500 
 501         final List<Diagnostic<? extends JavaFileObject>> errors =
 502                 new LinkedList<Diagnostic<? extends JavaFileObject>>();
 503 
 504         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm,
 505                 new DiagnosticListener<JavaFileObject>() {
 506                     public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
 507                         errors.add(diagnostic);
 508                     }
 509                 }, null, null, Arrays.asList(new MyFileObject(code)));
 510 
 511         final CompilationUnitTree cut = ct.parse().iterator().next();
 512         final List<String> content = new LinkedList<String>();
 513         final Trees trees = Trees.instance(ct);
 514 
 515         new TreeScanner<Void, Void>() {
 516             @Override
 517             public Void scan(Tree node, Void p) {
 518                 if (node == null) {
 519                     return null;
 520                 }
 521                 long start = trees.getSourcePositions().getStartPosition(cut, node);
 522 
 523                 if (start == (-1)) {
 524                     return null; // synthetic tree
 525                 }
 526                 long end = trees.getSourcePositions().getEndPosition(cut, node);
 527                 String s = code.substring((int) start, (int) end);
 528                 content.add(s);
 529 
 530                 return super.scan(node, p);
 531             }
 532         }.scan(((MethodTree) ((ClassTree) cut.getTypeDecls().get(0)).getMembers().get(0)).getBody().getStatements().get(0), null);
 533 
 534         assertEquals("performWildcardPositionsTest",golden.toString(),
 535                 content.toString());
 536     }
 537 
 538     @Test
 539     void testStartPositionForMethodWithoutModifiers() throws IOException {
 540 
 541         String code = "package t; class Test { <T> void t() {} }";
 542 
 543         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, null, null,
 544                 null, Arrays.asList(new MyFileObject(code)));
 545         CompilationUnitTree cut = ct.parse().iterator().next();
 546         ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
 547         MethodTree mt = (MethodTree) clazz.getMembers().get(0);
 548         Trees t = Trees.instance(ct);
 549         int start = (int) t.getSourcePositions().getStartPosition(cut, mt);
 550         int end = (int) t.getSourcePositions().getEndPosition(cut, mt);
 551 
 552         assertEquals("testStartPositionForMethodWithoutModifiers",
 553                 "<T> void t() {}", code.substring(start, end));
 554     }
 555 
 556     @Test
 557     void testVariableInIfThen1() throws IOException {
 558 
 559         String code = "package t; class Test { " +
 560                 "private static void t(String name) { " +
 561                 "if (name != null) String nn = name.trim(); } }";
 562 
 563         DiagnosticCollector<JavaFileObject> coll =
 564                 new DiagnosticCollector<JavaFileObject>();
 565 
 566         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, coll, null,
 567                 null, Arrays.asList(new MyFileObject(code)));
 568 
 569         ct.parse();
 570 
 571         List<String> codes = new LinkedList<String>();
 572 
 573         for (Diagnostic<? extends JavaFileObject> d : coll.getDiagnostics()) {
 574             codes.add(d.getCode());
 575         }
 576 
 577         assertEquals("testVariableInIfThen1",
 578                 Arrays.<String>asList("compiler.err.variable.not.allowed"),
 579                 codes);
 580     }
 581 
 582     @Test
 583    void testVariableInIfThen2() throws IOException {
 584 
 585         String code = "package t; class Test { " +
 586                 "private static void t(String name) { " +
 587                 "if (name != null) class X {} } }";
 588         DiagnosticCollector<JavaFileObject> coll =
 589                 new DiagnosticCollector<JavaFileObject>();
 590         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, coll, null,
 591                 null, Arrays.asList(new MyFileObject(code)));
 592 
 593         ct.parse();
 594 
 595         List<String> codes = new LinkedList<String>();
 596 
 597         for (Diagnostic<? extends JavaFileObject> d : coll.getDiagnostics()) {
 598             codes.add(d.getCode());
 599         }
 600 
 601         assertEquals("testVariableInIfThen2",
 602                 Arrays.<String>asList("compiler.err.class.not.allowed"), codes);
 603     }
 604 
 605     @Test
 606     void testVariableInIfThen3() throws IOException {
 607 
 608         String code = "package t; class Test { "+
 609                 "private static void t() { " +
 610                 "if (true) abstract class F {} }}";
 611         DiagnosticCollector<JavaFileObject> coll =
 612                 new DiagnosticCollector<JavaFileObject>();
 613         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, coll, null,
 614                 null, Arrays.asList(new MyFileObject(code)));
 615 
 616         ct.parse();
 617 
 618         List<String> codes = new LinkedList<String>();
 619 
 620         for (Diagnostic<? extends JavaFileObject> d : coll.getDiagnostics()) {
 621             codes.add(d.getCode());
 622         }
 623 
 624         assertEquals("testVariableInIfThen3",
 625                 Arrays.<String>asList("compiler.err.class.not.allowed"), codes);
 626     }
 627 
 628     @Test
 629     void testVariableInIfThen4() throws IOException {
 630 
 631         String code = "package t; class Test { "+
 632                 "private static void t(String name) { " +
 633                 "if (name != null) interface X {} } }";
 634         DiagnosticCollector<JavaFileObject> coll =
 635                 new DiagnosticCollector<JavaFileObject>();
 636         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, coll, null,
 637                 null, Arrays.asList(new MyFileObject(code)));
 638 
 639         ct.parse();
 640 
 641         List<String> codes = new LinkedList<String>();
 642 
 643         for (Diagnostic<? extends JavaFileObject> d : coll.getDiagnostics()) {
 644             codes.add(d.getCode());
 645         }
 646 
 647         assertEquals("testVariableInIfThen4",
 648                 Arrays.<String>asList("compiler.err.class.not.allowed"), codes);
 649     }
 650 
 651     @Test
 652     void testVariableInIfThen5() throws IOException {
 653 
 654         String code = "package t; class Test { "+
 655                 "private static void t() { " +
 656                 "if (true) } }";
 657         DiagnosticCollector<JavaFileObject> coll =
 658                 new DiagnosticCollector<JavaFileObject>();
 659         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, coll, null,
 660                 null, Arrays.asList(new MyFileObject(code)));
 661 
 662         ct.parse();
 663 
 664         List<String> codes = new LinkedList<String>();
 665 
 666         for (Diagnostic<? extends JavaFileObject> d : coll.getDiagnostics()) {
 667             codes.add(d.getCode());
 668         }
 669 
 670         assertEquals("testVariableInIfThen5",
 671                 Arrays.<String>asList("compiler.err.illegal.start.of.stmt"),
 672                 codes);
 673     }
 674 
 675     // see javac bug #6882235, NB bug #98234:
 676     @Test
 677     void testMissingExponent() throws IOException {
 678 
 679         String code = "\nclass Test { { System.err.println(0e); } }";
 680 
 681         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, null, null,
 682                 null, Arrays.asList(new MyFileObject(code)));
 683 
 684         assertNotNull(ct.parse().iterator().next());
 685     }
 686 
 687     @Test
 688     void testTryResourcePos() throws IOException {
 689 
 690         final String code = "package t; class Test { " +
 691                 "{ try (java.io.InputStream in = null) { } } }";
 692 
 693         CompilationUnitTree cut = getCompilationUnitTree(code);
 694 
 695         new TreeScanner<Void, Void>() {
 696             @Override
 697             public Void visitVariable(VariableTree node, Void p) {
 698                 if ("in".contentEquals(node.getName())) {
 699                     JCTree.JCVariableDecl var = (JCTree.JCVariableDecl) node;
 700                     assertEquals("testTryResourcePos", "in = null) { } } }",
 701                             code.substring(var.pos));
 702                 }
 703                 return super.visitVariable(node, p);
 704             }
 705         }.scan(cut, null);
 706     }
 707 
 708     @Test
 709     void testVarPos() throws IOException {
 710 
 711         final String code = "package t; class Test { " +
 712                 "{ java.io.InputStream in = null; } }";
 713 
 714         CompilationUnitTree cut = getCompilationUnitTree(code);
 715 
 716         new TreeScanner<Void, Void>() {
 717 
 718             @Override
 719             public Void visitVariable(VariableTree node, Void p) {
 720                 if ("in".contentEquals(node.getName())) {
 721                     JCTree.JCVariableDecl var = (JCTree.JCVariableDecl) node;
 722                     assertEquals("testVarPos","in = null; } }",
 723                             code.substring(var.pos));
 724                 }
 725                 return super.visitVariable(node, p);
 726             }
 727         }.scan(cut, null);
 728     }
 729 
 730     // expected erroneous tree: int x = y;(ERROR);
 731     @Test
 732     void testOperatorMissingError() throws IOException {
 733 
 734         String code = "package test; public class ErrorTest { "
 735                 + "void method() { int x = y  z } }";
 736         CompilationUnitTree cut = getCompilationUnitTree(code);
 737         final List<String> values = new ArrayList<>();
 738         final List<String> expectedValues =
 739                 new ArrayList<>(Arrays.asList("[z]"));
 740 
 741         new TreeScanner<Void, Void>() {
 742             @Override
 743             public Void visitErroneous(ErroneousTree node, Void p) {
 744                 values.add(getErroneousTreeValues(node).toString());
 745                 return null;
 746 
 747             }
 748         }.scan(cut, null);
 749 
 750         assertEquals("testOperatorMissingError: The Erroneous tree "
 751                 + "error values: " + values
 752                 + " do not match expected error values: "
 753                 + expectedValues, values, expectedValues);
 754     }
 755 
 756     // expected erroneous tree:  String s = (ERROR);
 757     @Test
 758     void testMissingParenthesisError() throws IOException {
 759 
 760         String code = "package test; public class ErrorTest { "
 761                 + "void f() {String s = new String; } }";
 762         CompilationUnitTree cut = getCompilationUnitTree(code);
 763         final List<String> values = new ArrayList<>();
 764         final List<String> expectedValues =
 765                 new ArrayList<>(Arrays.asList("[new String()]"));
 766 
 767         new TreeScanner<Void, Void>() {
 768             @Override
 769             public Void visitErroneous(ErroneousTree node, Void p) {
 770                 values.add(getErroneousTreeValues(node).toString());
 771                 return null;
 772             }
 773         }.scan(cut, null);
 774 
 775         assertEquals("testMissingParenthesisError: The Erroneous tree "
 776                 + "error values: " + values
 777                 + " do not match expected error values: "
 778                 + expectedValues, values, expectedValues);
 779     }
 780 
 781     // expected erroneous tree: package test; (ERROR)(ERROR)
 782     @Test
 783     void testMissingClassError() throws IOException {
 784 
 785         String code = "package Test; clas ErrorTest {  "
 786                 + "void f() {String s = new String(); } }";
 787         CompilationUnitTree cut = getCompilationUnitTree(code);
 788         final List<String> values = new ArrayList<>();
 789         final List<String> expectedValues =
 790                 new ArrayList<>(Arrays.asList("[, clas]", "[]"));
 791 
 792         new TreeScanner<Void, Void>() {
 793             @Override
 794             public Void visitErroneous(ErroneousTree node, Void p) {
 795                 values.add(getErroneousTreeValues(node).toString());
 796                 return null;
 797             }
 798         }.scan(cut, null);
 799 
 800         assertEquals("testMissingClassError: The Erroneous tree "
 801                 + "error values: " + values
 802                 + " do not match expected error values: "
 803                 + expectedValues, values, expectedValues);
 804     }
 805 
 806     // expected erroneous tree: void m1(int i) {(ERROR);{(ERROR);}
 807     @Test
 808     void testSwitchError() throws IOException {
 809 
 810         String code = "package test; public class ErrorTest { "
 811                 + "int numDays; void m1(int i) { switchh {i} { case 1: "
 812                 + "numDays = 31; break; } } }";
 813         CompilationUnitTree cut = getCompilationUnitTree(code);
 814         final List<String> values = new ArrayList<>();
 815         final List<String> expectedValues =
 816                 new ArrayList<>(Arrays.asList("[switchh]", "[i]"));
 817 
 818         new TreeScanner<Void, Void>() {
 819             @Override
 820             public Void visitErroneous(ErroneousTree node, Void p) {
 821                 values.add(getErroneousTreeValues(node).toString());
 822                 return null;
 823             }
 824         }.scan(cut, null);
 825 
 826         assertEquals("testSwitchError: The Erroneous tree "
 827                 + "error values: " + values
 828                 + " do not match expected error values: "
 829                 + expectedValues, values, expectedValues);
 830     }
 831 
 832     // expected erroneous tree: class ErrorTest {(ERROR)
 833     @Test
 834     void testMethodError() throws IOException {
 835 
 836         String code = "package Test; class ErrorTest {  "
 837                 + "static final void f) {String s = new String(); } }";
 838         CompilationUnitTree cut = cut = getCompilationUnitTree(code);
 839 
 840         final List<String> values = new ArrayList<>();
 841         final List<String> expectedValues =
 842                 new ArrayList<>(Arrays.asList("[\nstatic final void f();]"));
 843 
 844         new TreeScanner<Void, Void>() {
 845             @Override
 846             public Void visitErroneous(ErroneousTree node, Void p) {
 847                 values.add(normalize(getErroneousTreeValues(node).toString()));
 848                 return null;
 849             }
 850         }.scan(cut, null);
 851 
 852         assertEquals("testMethodError: The Erroneous tree "
 853                 + "error value: " + values
 854                 + " does not match expected error values: "
 855                 + expectedValues, values, expectedValues);
 856     }
 857 
 858     /*
 859      * The following tests do not work just yet with nb-javac nor javac,
 860      * they need further investigation, see CR: 7167356
 861      */
 862 
 863     void testPositionBrokenSource126732a() throws IOException {
 864         String[] commands = new String[]{
 865             "return Runnable()",
 866             "do { } while (true)",
 867             "throw UnsupportedOperationException()",
 868             "assert true",
 869             "1 + 1",};
 870 
 871         for (String command : commands) {
 872 
 873             String code = "package test;\n"
 874                     + "public class Test {\n"
 875                     + "    public static void test() {\n"
 876                     + "        " + command + " {\n"
 877                     + "                new Runnable() {\n"
 878                     + "        };\n"
 879                     + "    }\n"
 880                     + "}";
 881             JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, null,
 882                     null, null, Arrays.asList(new MyFileObject(code)));
 883             CompilationUnitTree cut = ct.parse().iterator().next();
 884 
 885             ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
 886             MethodTree method = (MethodTree) clazz.getMembers().get(0);
 887             List<? extends StatementTree> statements =
 888                     method.getBody().getStatements();
 889 
 890             StatementTree ret = statements.get(0);
 891             StatementTree block = statements.get(1);
 892 
 893             Trees t = Trees.instance(ct);
 894             int len = code.indexOf(command + " {") + (command + " ").length();
 895             assertEquals(command, len,
 896                     t.getSourcePositions().getEndPosition(cut, ret));
 897             assertEquals(command, len,
 898                     t.getSourcePositions().getStartPosition(cut, block));
 899         }
 900     }
 901 
 902     void testPositionBrokenSource126732b() throws IOException {
 903         String[] commands = new String[]{
 904             "break",
 905             "break A",
 906             "continue ",
 907             "continue A",};
 908 
 909         for (String command : commands) {
 910 
 911             String code = "package test;\n"
 912                     + "public class Test {\n"
 913                     + "    public static void test() {\n"
 914                     + "        while (true) {\n"
 915                     + "            " + command + " {\n"
 916                     + "                new Runnable() {\n"
 917                     + "        };\n"
 918                     + "        }\n"
 919                     + "    }\n"
 920                     + "}";
 921 
 922             JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, null,
 923                     null, null, Arrays.asList(new MyFileObject(code)));
 924             CompilationUnitTree cut = ct.parse().iterator().next();
 925 
 926             ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
 927             MethodTree method = (MethodTree) clazz.getMembers().get(0);
 928             List<? extends StatementTree> statements =
 929                     ((BlockTree) ((WhileLoopTree) method.getBody().getStatements().get(0)).getStatement()).getStatements();
 930 
 931             StatementTree ret = statements.get(0);
 932             StatementTree block = statements.get(1);
 933 
 934             Trees t = Trees.instance(ct);
 935             int len = code.indexOf(command + " {") + (command + " ").length();
 936             assertEquals(command, len,
 937                     t.getSourcePositions().getEndPosition(cut, ret));
 938             assertEquals(command, len,
 939                     t.getSourcePositions().getStartPosition(cut, block));
 940         }
 941     }
 942 
 943     void testStartPositionEnumConstantInit() throws IOException {
 944 
 945         String code = "package t; enum Test { AAA; }";
 946 
 947         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, null, null,
 948                 null, Arrays.asList(new MyFileObject(code)));
 949         CompilationUnitTree cut = ct.parse().iterator().next();
 950         ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
 951         VariableTree enumAAA = (VariableTree) clazz.getMembers().get(0);
 952         Trees t = Trees.instance(ct);
 953         int start = (int) t.getSourcePositions().getStartPosition(cut,
 954                 enumAAA.getInitializer());
 955 
 956         assertEquals("testStartPositionEnumConstantInit", -1, start);
 957     }
 958 
 959     @Test
 960     void testVoidLambdaParameter() throws IOException {
 961         String code = "package t; class Test { " +
 962                 "Runnable r = (void v) -> { };" +
 963                 "}";
 964         DiagnosticCollector<JavaFileObject> coll =
 965                 new DiagnosticCollector<>();
 966         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, coll, null,
 967                 null, Arrays.asList(new MyFileObject(code)));
 968 
 969         CompilationUnitTree cut = ct.parse().iterator().next();
 970         ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
 971         VariableTree field = (VariableTree) clazz.getMembers().get(0);
 972 
 973         assertEquals("actual kind: " + field.getInitializer().getKind(),
 974                      field.getInitializer().getKind(),
 975                      Kind.LAMBDA_EXPRESSION);
 976 
 977         LambdaExpressionTree lambda = (LambdaExpressionTree) field.getInitializer();
 978 
 979         assertEquals("actual parameters: " + lambda.getParameters().size(),
 980                      lambda.getParameters().size(),
 981                      1);
 982 
 983         Tree paramType = lambda.getParameters().get(0).getType();
 984 
 985         assertEquals("actual parameter type: " + paramType.getKind(),
 986                      paramType.getKind(),
 987                      Kind.PRIMITIVE_TYPE);
 988 
 989         TypeKind primitiveTypeKind = ((PrimitiveTypeTree) paramType).getPrimitiveTypeKind();
 990 
 991         assertEquals("actual parameter type: " + primitiveTypeKind,
 992                      primitiveTypeKind,
 993                      TypeKind.VOID);
 994     }
 995 
 996     @Test //JDK-8065753
 997     void testWrongFirstToken() throws IOException {
 998         String code = "<";
 999         String expectedErrors = "Test.java:1:1: compiler.err.expected3: class, interface, enum\n" +
1000                                 "1 error\n";
1001         StringWriter out = new StringWriter();
1002         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(out, fm, null,
1003                 Arrays.asList("-XDrawDiagnostics"), null, Arrays.asList(new MyFileObject(code)));
1004 
1005         Result errorCode = ct.doCall();
1006         assertEquals("the error code is not correct; actual:" + errorCode, Main.Result.ERROR, errorCode);
1007         String actualErrors = normalize(out.toString());
1008         assertEquals("the error message is not correct, actual: " + actualErrors, expectedErrors, actualErrors);
1009     }
1010 
1011     @Test //JDK-8205913
1012     void testForInit() throws IOException {
1013         String code = "class T { void t() { for (n : ns) { } } }";
1014         String expectedErrors = "Test.java:1:27: compiler.err.bad.initializer: for-loop\n";
1015         StringWriter out = new StringWriter();
1016         JavacTask ct = (JavacTask) tool.getTask(out, fm, null,
1017                 Arrays.asList("-XDrawDiagnostics"), null, Arrays.asList(new MyFileObject(code)));
1018 
1019         Iterable<? extends CompilationUnitTree> cuts = ct.parse();
1020         boolean[] foundVar = new boolean[1];
1021 
1022         new TreePathScanner<Void, Void>() {
1023             @Override public Void visitVariable(VariableTree vt, Void p) {
1024                 assertNotNull(vt.getModifiers());
1025                 assertNotNull(vt.getType());
1026                 assertNotNull(vt.getName());
1027                 assertEquals("name should be <error>", "<error>", vt.getName().toString());
1028                 foundVar[0] = true;
1029                 return super.visitVariable(vt, p);
1030             }
1031         }.scan(cuts, null);
1032 
1033         if (!foundVar[0]) {
1034             fail("haven't found a variable");
1035         }
1036 
1037         String actualErrors = normalize(out.toString());
1038         assertEquals("the error message is not correct, actual: " + actualErrors, expectedErrors, actualErrors);
1039     }
1040 
1041     @Test
1042     void testTypeParamsWithoutMethod() throws IOException {
1043         assert tool != null;
1044 
1045         String code = "package test; class Test { /**javadoc*/ |public <T> |}";
1046         String[] parts = code.split("\\|");
1047 
1048         code = parts[0] + parts[1] + parts[2];
1049 
1050         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, null, null,
1051                 null, Arrays.asList(new MyFileObject(code)));
1052         Trees trees = Trees.instance(ct);
1053         SourcePositions pos = trees.getSourcePositions();
1054         CompilationUnitTree cut = ct.parse().iterator().next();
1055         ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
1056         ErroneousTree err = (ErroneousTree) clazz.getMembers().get(0);
1057         MethodTree method = (MethodTree) err.getErrorTrees().get(0);
1058 
1059         final int methodStart = parts[0].length();
1060         final int methodEnd = parts[0].length() + parts[1].length();
1061         assertEquals("testTypeParamsWithoutMethod",
1062                 methodStart, pos.getStartPosition(cut, method));
1063         assertEquals("testTypeParamsWithoutMethod",
1064                 methodEnd, pos.getEndPosition(cut, method));
1065 
1066         TreePath path2Method = new TreePath(new TreePath(new TreePath(cut), clazz), method);
1067         String javadoc = trees.getDocComment(path2Method);
1068 
1069         if (!"javadoc".equals(javadoc)) {
1070             throw new AssertionError("Expected javadoc not found, actual javadoc: " + javadoc);
1071         }
1072     }
1073 
1074     @Test
1075     void testAnalyzeParensWithComma1() throws IOException {
1076         assert tool != null;
1077 
1078         String code = "package test; class Test { FI fi = |(s, |";
1079         String[] parts = code.split("\\|", 3);
1080 
1081         code = parts[0] + parts[1] + parts[2];
1082 
1083         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, null, null,
1084                 null, Arrays.asList(new MyFileObject(code)));
1085         Trees trees = Trees.instance(ct);
1086         SourcePositions pos = trees.getSourcePositions();
1087         CompilationUnitTree cut = ct.parse().iterator().next();
1088         boolean[] found = new boolean[1];
1089 
1090         new TreeScanner<Void, Void>() {
1091             @Override
1092             public Void visitLambdaExpression(LambdaExpressionTree tree, Void v) {
1093                 found[0] = true;
1094                 int lambdaStart = parts[0].length();
1095                 int lambdaEnd = parts[0].length() + parts[1].length();
1096                 assertEquals("testAnalyzeParensWithComma1",
1097                         lambdaStart, pos.getStartPosition(cut, tree));
1098                 assertEquals("testAnalyzeParensWithComma1",
1099                         lambdaEnd, pos.getEndPosition(cut, tree));
1100                 return null;
1101             }
1102         }.scan(cut, null);
1103 
1104         assertTrue("testAnalyzeParensWithComma1", found[0]);
1105     }
1106 
1107     @Test
1108     void testAnalyzeParensWithComma2() throws IOException {
1109         assert tool != null;
1110 
1111         String code = "package test; class Test { FI fi = |(s, o)|";
1112         String[] parts = code.split("\\|", 3);
1113 
1114         code = parts[0] + parts[1] + parts[2];
1115 
1116         JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, fm, null, null,
1117                 null, Arrays.asList(new MyFileObject(code)));
1118         Trees trees = Trees.instance(ct);
1119         SourcePositions pos = trees.getSourcePositions();
1120         CompilationUnitTree cut = ct.parse().iterator().next();
1121         boolean[] found = new boolean[1];
1122 
1123         new TreeScanner<Void, Void>() {
1124             @Override
1125             public Void visitLambdaExpression(LambdaExpressionTree tree, Void v) {
1126                 found[0] = true;
1127                 int lambdaStart = parts[0].length();
1128                 int lambdaEnd = parts[0].length() + parts[1].length();
1129                 assertEquals("testAnalyzeParensWithComma2",
1130                         lambdaStart, pos.getStartPosition(cut, tree));
1131                 assertEquals("testAnalyzeParensWithComma2",
1132                         lambdaEnd, pos.getEndPosition(cut, tree));
1133                 return null;
1134             }
1135         }.scan(cut, null);
1136 
1137         assertTrue("testAnalyzeParensWithComma2", found[0]);
1138     }
1139 
1140     void run(String[] args) throws Exception {
1141         int passed = 0, failed = 0;
1142         final Pattern p = (args != null && args.length > 0)
1143                 ? Pattern.compile(args[0])
1144                 : null;
1145         for (Method m : this.getClass().getDeclaredMethods()) {
1146             boolean selected = (p == null)
1147                     ? m.isAnnotationPresent(Test.class)
1148                     : p.matcher(m.getName()).matches();
1149             if (selected) {
1150                 try {
1151                     m.invoke(this, (Object[]) null);
1152                     System.out.println(m.getName() + ": OK");
1153                     passed++;
1154                 } catch (Throwable ex) {
1155                     System.out.printf("Test %s failed: %s %n", m, ex.getCause());
1156                     failed++;
1157                 }
1158             }
1159         }
1160         System.out.printf("Passed: %d, Failed %d%n", passed, failed);
1161         if (failed > 0) {
1162             throw new RuntimeException("Tests failed: " + failed);
1163         }
1164         if (passed == 0 && failed == 0) {
1165             throw new AssertionError("No test(s) selected: passed = " +
1166                     passed + ", failed = " + failed + " ??????????");
1167         }
1168     }
1169 }
1170 
1171 abstract class TestCase {
1172 
1173     void assertEquals(String message, int i, int pos) {
1174         if (i != pos) {
1175             fail(message);
1176         }
1177     }
1178 
1179     void assertFalse(String message, boolean bvalue) {
1180         if (bvalue == true) {
1181             fail(message);
1182         }
1183     }
1184 
1185     void assertTrue(String message, boolean bvalue) {
1186         if (bvalue == false) {
1187             fail(message);
1188         }
1189     }
1190 
1191     void assertEquals(String message, int i, long l) {
1192         if (i != l) {
1193             fail(message + ":" + i + ":" + l);
1194         }
1195     }
1196 
1197     void assertEquals(String message, Object o1, Object o2) {
1198         if (o1 != null && o2 != null && !o1.equals(o2)) {
1199             fail(message);
1200         }
1201         if (o1 == null && o2 != null) {
1202             fail(message);
1203         }
1204     }
1205 
1206     void assertNotNull(Object o) {
1207         if (o == null) {
1208             fail();
1209         }
1210     }
1211 
1212     void fail() {
1213         fail("test failed");
1214     }
1215 
1216     void fail(String message) {
1217         throw new RuntimeException(message);
1218     }
1219 
1220     /**
1221      * Indicates that the annotated method is a test method.
1222      */
1223     @Retention(RetentionPolicy.RUNTIME)
1224     @Target(ElementType.METHOD)
1225     public @interface Test {}
1226 }