1 /* 2 * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.io.ByteArrayInputStream; 25 import java.io.ByteArrayOutputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.PrintStream; 29 import java.io.StringWriter; 30 import java.lang.reflect.Method; 31 import java.lang.module.Configuration; 32 import java.lang.module.ModuleFinder; 33 import java.nio.file.Paths; 34 import java.nio.file.Path; 35 import java.util.ArrayList; 36 import java.util.Arrays; 37 import java.util.Collection; 38 import java.util.Collections; 39 import java.util.HashMap; 40 import java.util.LinkedHashMap; 41 import java.util.LinkedHashSet; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Set; 45 import java.util.TreeMap; 46 import java.util.function.Consumer; 47 import java.util.function.Predicate; 48 import java.util.function.Supplier; 49 import java.util.stream.Collectors; 50 import java.util.stream.Stream; 51 52 import javax.tools.Diagnostic; 53 54 import jdk.jshell.EvalException; 55 import jdk.jshell.JShell; 56 import jdk.jshell.JShell.Subscription; 57 import jdk.jshell.Snippet; 58 import jdk.jshell.DeclarationSnippet; 59 import jdk.jshell.ExpressionSnippet; 60 import jdk.jshell.ImportSnippet; 61 import jdk.jshell.Snippet.Kind; 62 import jdk.jshell.MethodSnippet; 63 import jdk.jshell.Snippet.Status; 64 import jdk.jshell.Snippet.SubKind; 65 import jdk.jshell.TypeDeclSnippet; 66 import jdk.jshell.VarSnippet; 67 import jdk.jshell.SnippetEvent; 68 import jdk.jshell.SourceCodeAnalysis; 69 import jdk.jshell.SourceCodeAnalysis.CompletionInfo; 70 import jdk.jshell.SourceCodeAnalysis.Completeness; 71 import jdk.jshell.SourceCodeAnalysis.QualifiedNames; 72 import jdk.jshell.SourceCodeAnalysis.Suggestion; 73 import jdk.jshell.UnresolvedReferenceException; 74 import org.testng.annotations.AfterMethod; 75 import org.testng.annotations.BeforeMethod; 76 77 import jdk.jshell.Diag; 78 79 import static java.util.stream.Collectors.toList; 80 import static java.util.stream.Collectors.toSet; 81 82 import static jdk.jshell.Snippet.Status.*; 83 import static org.testng.Assert.*; 84 import static jdk.jshell.Snippet.SubKind.METHOD_SUBKIND; 85 import jdk.jshell.SourceCodeAnalysis.Documentation; 86 87 public class KullaTesting { 88 89 public static final String IGNORE_VALUE = "<ignore-value>"; 90 public static final Class<? extends Throwable> IGNORE_EXCEPTION = (new Throwable() {}).getClass(); 91 public static final Snippet MAIN_SNIPPET; 92 93 private SourceCodeAnalysis analysis = null; 94 private JShell state = null; 95 private InputStream inStream = null; 96 private ByteArrayOutputStream outStream = null; 97 private ByteArrayOutputStream errStream = null; 98 99 private Map<String, Snippet> idToSnippet = new LinkedHashMap<>(); 100 private Set<Snippet> allSnippets = new LinkedHashSet<>(); 101 102 static { 103 JShell js = JShell.create(); 104 MAIN_SNIPPET = js.eval("MAIN_SNIPPET").get(0).snippet(); js.close()105 js.close(); 106 assertTrue(MAIN_SNIPPET != null, "Bad MAIN_SNIPPET set-up -- must not be null"); 107 } 108 109 public enum DiagCheck { 110 DIAG_OK, 111 DIAG_WARNING, 112 DIAG_ERROR, 113 DIAG_IGNORE 114 } 115 setInput(String s)116 public void setInput(String s) { 117 setInput(new ByteArrayInputStream(s.getBytes())); 118 } 119 setInput(InputStream in)120 public void setInput(InputStream in) { 121 inStream = in; 122 } 123 getOutput()124 public String getOutput() { 125 String s = outStream.toString(); 126 outStream.reset(); 127 return s; 128 } 129 getErrorOutput()130 public String getErrorOutput() { 131 String s = errStream.toString(); 132 errStream.reset(); 133 return s; 134 } 135 136 /** 137 * @return the analysis 138 */ getAnalysis()139 public SourceCodeAnalysis getAnalysis() { 140 if (analysis == null) { 141 analysis = state.sourceCodeAnalysis(); 142 } 143 return analysis; 144 } 145 146 /** 147 * @return the state 148 */ getState()149 public JShell getState() { 150 return state; 151 } 152 getActiveKeys()153 public List<Snippet> getActiveKeys() { 154 return allSnippets.stream() 155 .filter(k -> getState().status(k).isActive()) 156 .collect(Collectors.toList()); 157 } 158 addToClasspath(String path)159 public void addToClasspath(String path) { 160 getState().addToClasspath(path); 161 } 162 addToClasspath(Path path)163 public void addToClasspath(Path path) { 164 addToClasspath(path.toString()); 165 } 166 167 @BeforeMethod setUp()168 public void setUp() { 169 setUp(b -> {}); 170 } 171 setUp(Consumer<JShell.Builder> bc)172 public void setUp(Consumer<JShell.Builder> bc) { 173 InputStream in = new InputStream() { 174 @Override 175 public int read() throws IOException { 176 assertNotNull(inStream); 177 return inStream.read(); 178 } 179 @Override 180 public int read(byte[] b) throws IOException { 181 assertNotNull(inStream); 182 return inStream.read(b); 183 } 184 @Override 185 public int read(byte[] b, int off, int len) throws IOException { 186 assertNotNull(inStream); 187 return inStream.read(b, off, len); 188 } 189 }; 190 outStream = new ByteArrayOutputStream(); 191 errStream = new ByteArrayOutputStream(); 192 JShell.Builder builder = JShell.builder() 193 .in(in) 194 .out(new PrintStream(outStream)) 195 .err(new PrintStream(errStream)); 196 bc.accept(builder); 197 state = builder.build(); 198 allSnippets = new LinkedHashSet<>(); 199 idToSnippet = new LinkedHashMap<>(); 200 } 201 202 @AfterMethod tearDown()203 public void tearDown() { 204 if (state != null) state.close(); 205 state = null; 206 analysis = null; 207 allSnippets = null; 208 idToSnippet = null; 209 } 210 createAndRunFromModule(String moduleName, Path modPath)211 public ClassLoader createAndRunFromModule(String moduleName, Path modPath) { 212 ModuleFinder finder = ModuleFinder.of(modPath); 213 ModuleLayer parent = ModuleLayer.boot(); 214 Configuration cf = parent.configuration() 215 .resolve(finder, ModuleFinder.of(), Set.of(moduleName)); 216 ClassLoader scl = ClassLoader.getSystemClassLoader(); 217 ModuleLayer layer = parent.defineModulesWithOneLoader(cf, scl); 218 ClassLoader loader = layer.findLoader(moduleName); 219 ClassLoader ccl = Thread.currentThread().getContextClassLoader(); 220 Thread.currentThread().setContextClassLoader(loader); 221 return ccl; 222 } 223 assertUnresolvedDependencies(DeclarationSnippet key, int unresolvedSize)224 public List<String> assertUnresolvedDependencies(DeclarationSnippet key, int unresolvedSize) { 225 List<String> unresolved = getState().unresolvedDependencies(key).collect(toList()); 226 assertEquals(unresolved.size(), unresolvedSize, "Input: " + key.source() + ", checking unresolved: "); 227 return unresolved; 228 } 229 assertUnresolvedDependencies1(DeclarationSnippet key, Status status, String name)230 public DeclarationSnippet assertUnresolvedDependencies1(DeclarationSnippet key, Status status, String name) { 231 List<String> unresolved = assertUnresolvedDependencies(key, 1); 232 String input = key.source(); 233 assertEquals(unresolved.size(), 1, "Given input: " + input + ", checking unresolved"); 234 assertEquals(unresolved.get(0), name, "Given input: " + input + ", checking unresolved: "); 235 assertEquals(getState().status(key), status, "Given input: " + input + ", checking status: "); 236 return key; 237 } 238 assertEvalUnresolvedException(String input, String name, int unresolvedSize, int diagnosticsSize)239 public DeclarationSnippet assertEvalUnresolvedException(String input, String name, int unresolvedSize, int diagnosticsSize) { 240 List<SnippetEvent> events = assertEval(input, null, UnresolvedReferenceException.class, DiagCheck.DIAG_OK, DiagCheck.DIAG_OK, null); 241 SnippetEvent ste = events.get(0); 242 DeclarationSnippet sn = ((UnresolvedReferenceException) ste.exception()).getSnippet(); 243 assertEquals(sn.name(), name, "Given input: " + input + ", checking name"); 244 assertEquals(getState().unresolvedDependencies(sn).count(), unresolvedSize, "Given input: " + input + ", checking unresolved"); 245 assertEquals(getState().diagnostics(sn).count(), (long) diagnosticsSize, "Given input: " + input + ", checking diagnostics"); 246 return sn; 247 } 248 assertKeyMatch(String input, boolean isExecutable, SubKind expectedSubKind, STEInfo mainInfo, STEInfo... updates)249 public Snippet assertKeyMatch(String input, boolean isExecutable, SubKind expectedSubKind, STEInfo mainInfo, STEInfo... updates) { 250 Snippet key = key(assertEval(input, IGNORE_VALUE, mainInfo, updates)); 251 String source = key.source(); 252 assertEquals(source, input, "Key \"" + input + "\" source mismatch, got: " + source + ", expected: " + input); 253 SubKind subkind = key.subKind(); 254 assertEquals(subkind, expectedSubKind, "Key \"" + input + "\" subkind mismatch, got: " 255 + subkind + ", expected: " + expectedSubKind); 256 assertEquals(subkind.isExecutable(), isExecutable, "Key \"" + input + "\", expected isExecutable: " 257 + isExecutable + ", got: " + subkind.isExecutable()); 258 Snippet.Kind expectedKind = getKind(key); 259 assertEquals(key.kind(), expectedKind, "Checking kind: "); 260 assertEquals(expectedSubKind.kind(), expectedKind, "Checking kind: "); 261 return key; 262 } 263 getKind(Snippet key)264 private Kind getKind(Snippet key) { 265 SubKind expectedSubKind = key.subKind(); 266 Kind expectedKind; 267 switch (expectedSubKind) { 268 case SINGLE_TYPE_IMPORT_SUBKIND: 269 case SINGLE_STATIC_IMPORT_SUBKIND: 270 case TYPE_IMPORT_ON_DEMAND_SUBKIND: 271 case STATIC_IMPORT_ON_DEMAND_SUBKIND: 272 expectedKind = Kind.IMPORT; 273 break; 274 case CLASS_SUBKIND: 275 case INTERFACE_SUBKIND: 276 case ENUM_SUBKIND: 277 case ANNOTATION_TYPE_SUBKIND: 278 expectedKind = Kind.TYPE_DECL; 279 break; 280 case METHOD_SUBKIND: 281 expectedKind = Kind.METHOD; 282 break; 283 case VAR_DECLARATION_SUBKIND: 284 case TEMP_VAR_EXPRESSION_SUBKIND: 285 case VAR_DECLARATION_WITH_INITIALIZER_SUBKIND: 286 expectedKind = Kind.VAR; 287 break; 288 case VAR_VALUE_SUBKIND: 289 case ASSIGNMENT_SUBKIND: 290 expectedKind = Kind.EXPRESSION; 291 break; 292 case STATEMENT_SUBKIND: 293 expectedKind = Kind.STATEMENT; 294 break; 295 case UNKNOWN_SUBKIND: 296 expectedKind = Kind.ERRONEOUS; 297 break; 298 default: 299 throw new AssertionError("Unsupported key: " + key.getClass().getCanonicalName()); 300 } 301 return expectedKind; 302 } 303 assertImportKeyMatch(String input, String name, SubKind subkind, STEInfo mainInfo, STEInfo... updates)304 public ImportSnippet assertImportKeyMatch(String input, String name, SubKind subkind, STEInfo mainInfo, STEInfo... updates) { 305 Snippet key = assertKeyMatch(input, false, subkind, mainInfo, updates); 306 307 assertTrue(key instanceof ImportSnippet, "Expected an ImportKey, got: " + key.getClass().getName()); 308 ImportSnippet importKey = (ImportSnippet) key; 309 assertEquals(importKey.name(), name, "Input \"" + input + 310 "\" name mismatch, got: " + importKey.name() + ", expected: " + name); 311 assertEquals(importKey.kind(), Kind.IMPORT, "Checking kind: "); 312 return importKey; 313 } 314 assertDeclarationKeyMatch(String input, boolean isExecutable, String name, SubKind subkind, STEInfo mainInfo, STEInfo... updates)315 public DeclarationSnippet assertDeclarationKeyMatch(String input, boolean isExecutable, String name, SubKind subkind, STEInfo mainInfo, STEInfo... updates) { 316 Snippet key = assertKeyMatch(input, isExecutable, subkind, mainInfo, updates); 317 318 assertTrue(key instanceof DeclarationSnippet, "Expected a DeclarationKey, got: " + key.getClass().getName()); 319 DeclarationSnippet declKey = (DeclarationSnippet) key; 320 assertEquals(declKey.name(), name, "Input \"" + input + 321 "\" name mismatch, got: " + declKey.name() + ", expected: " + name); 322 return declKey; 323 } 324 assertVarKeyMatch(String input, boolean isExecutable, String name, SubKind kind, String typeName, STEInfo mainInfo, STEInfo... updates)325 public VarSnippet assertVarKeyMatch(String input, boolean isExecutable, String name, SubKind kind, String typeName, STEInfo mainInfo, STEInfo... updates) { 326 Snippet sn = assertDeclarationKeyMatch(input, isExecutable, name, kind, mainInfo, updates); 327 assertTrue(sn instanceof VarSnippet, "Expected a VarKey, got: " + sn.getClass().getName()); 328 VarSnippet variableKey = (VarSnippet) sn; 329 String signature = variableKey.typeName(); 330 assertEquals(signature, typeName, "Key \"" + input + 331 "\" typeName mismatch, got: " + signature + ", expected: " + typeName); 332 assertEquals(variableKey.kind(), Kind.VAR, "Checking kind: "); 333 return variableKey; 334 } 335 assertExpressionKeyMatch(String input, String name, SubKind kind, String typeName)336 public void assertExpressionKeyMatch(String input, String name, SubKind kind, String typeName) { 337 Snippet key = assertKeyMatch(input, true, kind, added(VALID)); 338 assertTrue(key instanceof ExpressionSnippet, "Expected a ExpressionKey, got: " + key.getClass().getName()); 339 ExpressionSnippet exprKey = (ExpressionSnippet) key; 340 assertEquals(exprKey.name(), name, "Input \"" + input + 341 "\" name mismatch, got: " + exprKey.name() + ", expected: " + name); 342 assertEquals(exprKey.typeName(), typeName, "Key \"" + input + 343 "\" typeName mismatch, got: " + exprKey.typeName() + ", expected: " + typeName); 344 assertEquals(exprKey.kind(), Kind.EXPRESSION, "Checking kind: "); 345 } 346 347 // For expressions throwing an EvalException assertEvalException(String input)348 public SnippetEvent assertEvalException(String input) { 349 List<SnippetEvent> events = assertEval(input, null, EvalException.class, 350 DiagCheck.DIAG_OK, DiagCheck.DIAG_OK, null); 351 return events.get(0); 352 } 353 354 assertEvalFail(String input)355 public List<SnippetEvent> assertEvalFail(String input) { 356 return assertEval(input, null, null, 357 DiagCheck.DIAG_ERROR, DiagCheck.DIAG_IGNORE, added(REJECTED)); 358 } 359 assertEval(String input)360 public List<SnippetEvent> assertEval(String input) { 361 return assertEval(input, IGNORE_VALUE, null, DiagCheck.DIAG_OK, DiagCheck.DIAG_OK, added(VALID)); 362 } 363 assertEval(String input, String value)364 public List<SnippetEvent> assertEval(String input, String value) { 365 return assertEval(input, value, null, DiagCheck.DIAG_OK, DiagCheck.DIAG_OK, added(VALID)); 366 } 367 assertEval(String input, STEInfo mainInfo, STEInfo... updates)368 public List<SnippetEvent> assertEval(String input, STEInfo mainInfo, STEInfo... updates) { 369 return assertEval(input, IGNORE_VALUE, null, DiagCheck.DIAG_OK, DiagCheck.DIAG_OK, mainInfo, updates); 370 } 371 assertEval(String input, String value, STEInfo mainInfo, STEInfo... updates)372 public List<SnippetEvent> assertEval(String input, String value, 373 STEInfo mainInfo, STEInfo... updates) { 374 return assertEval(input, value, null, DiagCheck.DIAG_OK, DiagCheck.DIAG_OK, mainInfo, updates); 375 } 376 assertEval(String input, DiagCheck diagMain, DiagCheck diagUpdates)377 public List<SnippetEvent> assertEval(String input, DiagCheck diagMain, DiagCheck diagUpdates) { 378 return assertEval(input, IGNORE_VALUE, null, diagMain, diagUpdates, added(VALID)); 379 } 380 assertEval(String input, DiagCheck diagMain, DiagCheck diagUpdates, STEInfo mainInfo, STEInfo... updates)381 public List<SnippetEvent> assertEval(String input, DiagCheck diagMain, DiagCheck diagUpdates, 382 STEInfo mainInfo, STEInfo... updates) { 383 return assertEval(input, IGNORE_VALUE, null, diagMain, diagUpdates, mainInfo, updates); 384 } 385 assertEval(String input, String value, Class<? extends Throwable> exceptionClass, DiagCheck diagMain, DiagCheck diagUpdates, STEInfo mainInfo, STEInfo... updates)386 public List<SnippetEvent> assertEval(String input, 387 String value, Class<? extends Throwable> exceptionClass, 388 DiagCheck diagMain, DiagCheck diagUpdates, 389 STEInfo mainInfo, STEInfo... updates) { 390 return assertEval(input, diagMain, diagUpdates, new EventChain(mainInfo, value, exceptionClass, updates)); 391 } 392 393 // Use this directly or usually indirectly for all non-empty calls to eval() assertEval(String input, DiagCheck diagMain, DiagCheck diagUpdates, EventChain... eventChains)394 public List<SnippetEvent> assertEval(String input, 395 DiagCheck diagMain, DiagCheck diagUpdates, EventChain... eventChains) { 396 return checkEvents(() -> getState().eval(input), "eval(" + input + ")", diagMain, diagUpdates, eventChains); 397 } 398 assertStreamMatch(Stream<T> result, T... expected)399 <T> void assertStreamMatch(Stream<T> result, T... expected) { 400 Set<T> sns = result.collect(toSet()); 401 Set<T> exp = Stream.of(expected).collect(toSet()); 402 assertEquals(sns, exp); 403 } 404 closure(List<SnippetEvent> events)405 private Map<Snippet, Snippet> closure(List<SnippetEvent> events) { 406 Map<Snippet, Snippet> transitions = new HashMap<>(); 407 for (SnippetEvent event : events) { 408 transitions.put(event.snippet(), event.causeSnippet()); 409 } 410 Map<Snippet, Snippet> causeSnippets = new HashMap<>(); 411 for (Map.Entry<Snippet, Snippet> entry : transitions.entrySet()) { 412 Snippet snippet = entry.getKey(); 413 Snippet cause = getInitialCause(transitions, entry.getValue()); 414 causeSnippets.put(snippet, cause); 415 } 416 return causeSnippets; 417 } 418 getInitialCause(Map<Snippet, Snippet> transitions, Snippet snippet)419 private Snippet getInitialCause(Map<Snippet, Snippet> transitions, Snippet snippet) { 420 Snippet result; 421 while ((result = transitions.get(snippet)) != null) { 422 snippet = result; 423 } 424 return snippet; 425 } 426 groupByCauseSnippet(List<SnippetEvent> events)427 private Map<Snippet, List<SnippetEvent>> groupByCauseSnippet(List<SnippetEvent> events) { 428 Map<Snippet, List<SnippetEvent>> map = new TreeMap<>((a, b) -> a.id().compareTo(b.id())); 429 for (SnippetEvent event : events) { 430 if (event == null) { 431 throw new InternalError("null event found in " + events); 432 } 433 if (event.snippet() == null) { 434 throw new InternalError("null event Snippet found in " + events); 435 } 436 if (event.snippet().id() == null) { 437 throw new InternalError("null event Snippet id() found in " + events); 438 } 439 } 440 for (SnippetEvent event : events) { 441 if (event.causeSnippet() == null) { 442 map.computeIfAbsent(event.snippet(), ($) -> new ArrayList<>()).add(event); 443 } 444 } 445 Map<Snippet, Snippet> causeSnippets = closure(events); 446 for (SnippetEvent event : events) { 447 Snippet causeSnippet = causeSnippets.get(event.snippet()); 448 if (causeSnippet != null) { 449 map.get(causeSnippet).add(event); 450 } 451 } 452 for (Map.Entry<Snippet, List<SnippetEvent>> entry : map.entrySet()) { 453 Collections.sort(entry.getValue(), 454 (a, b) -> a.causeSnippet() == null 455 ? -1 : b.causeSnippet() == null 456 ? 1 : a.snippet().id().compareTo(b.snippet().id())); 457 } 458 return map; 459 } 460 getInfos(EventChain... eventChains)461 private List<STEInfo> getInfos(EventChain... eventChains) { 462 List<STEInfo> list = new ArrayList<>(); 463 for (EventChain i : eventChains) { 464 list.add(i.mainInfo); 465 Collections.addAll(list, i.updates); 466 } 467 return list; 468 } 469 checkEvents(Supplier<List<SnippetEvent>> toTest, String descriptor, DiagCheck diagMain, DiagCheck diagUpdates, EventChain... eventChains)470 private List<SnippetEvent> checkEvents(Supplier<List<SnippetEvent>> toTest, 471 String descriptor, 472 DiagCheck diagMain, DiagCheck diagUpdates, 473 EventChain... eventChains) { 474 List<SnippetEvent> dispatched = new ArrayList<>(); 475 Subscription token = getState().onSnippetEvent(kse -> { 476 if (dispatched.size() > 0 && dispatched.get(dispatched.size() - 1) == null) { 477 throw new RuntimeException("dispatch event after done"); 478 } 479 dispatched.add(kse); 480 }); 481 List<SnippetEvent> events = toTest.get(); 482 getState().unsubscribe(token); 483 assertEquals(dispatched.size(), events.size(), "dispatched event size not the same as event size"); 484 for (int i = events.size() - 1; i >= 0; --i) { 485 assertEquals(dispatched.get(i), events.get(i), "Event element " + i + " does not match"); 486 } 487 dispatched.add(null); // mark end of dispatchs 488 489 for (SnippetEvent evt : events) { 490 assertTrue(evt.snippet() != null, "key must never be null, but it was for: " + descriptor); 491 assertTrue(evt.previousStatus() != null, "previousStatus must never be null, but it was for: " + descriptor); 492 assertTrue(evt.status() != null, "status must never be null, but it was for: " + descriptor); 493 assertTrue(evt.status() != NONEXISTENT, "status must not be NONEXISTENT: " + descriptor); 494 if (evt.previousStatus() != NONEXISTENT) { 495 Snippet old = idToSnippet.get(evt.snippet().id()); 496 if (old != null) { 497 switch (evt.status()) { 498 case DROPPED: 499 assertEquals(old, evt.snippet(), 500 "Drop: Old snippet must be what is dropped -- input: " + descriptor); 501 break; 502 case OVERWRITTEN: 503 assertEquals(old, evt.snippet(), 504 "Overwrite: Old snippet (" + old 505 + ") must be what is overwritten -- input: " 506 + descriptor + " -- " + evt); 507 break; 508 default: 509 if (evt.causeSnippet() == null) { 510 // New source 511 assertNotEquals(old, evt.snippet(), 512 "New source: Old snippet must be different from the replacing -- input: " 513 + descriptor); 514 } else { 515 // An update (key Overwrite??) 516 assertEquals(old, evt.snippet(), 517 "Update: Old snippet must be equal to the replacing -- input: " 518 + descriptor); 519 } 520 break; 521 } 522 } 523 } 524 } 525 for (SnippetEvent evt : events) { 526 if (evt.causeSnippet() == null && evt.status() != DROPPED) { 527 allSnippets.add(evt.snippet()); 528 idToSnippet.put(evt.snippet().id(), evt.snippet()); 529 } 530 } 531 assertTrue(events.size() >= 1, "Expected at least one event, got none."); 532 List<STEInfo> all = getInfos(eventChains); 533 if (events.size() != all.size()) { 534 StringBuilder sb = new StringBuilder(); 535 sb.append("Got events --\n"); 536 for (SnippetEvent evt : events) { 537 sb.append(" key: ").append(evt.snippet()); 538 sb.append(" before: ").append(evt.previousStatus()); 539 sb.append(" status: ").append(evt.status()); 540 sb.append(" isSignatureChange: ").append(evt.isSignatureChange()); 541 sb.append(" cause: "); 542 if (evt.causeSnippet() == null) { 543 sb.append("direct"); 544 } else { 545 sb.append(evt.causeSnippet()); 546 } 547 sb.append("\n"); 548 } 549 sb.append("Expected ").append(all.size()); 550 sb.append(" events, got: ").append(events.size()); 551 fail(sb.toString()); 552 } 553 554 int impactId = 0; 555 Map<Snippet, List<SnippetEvent>> groupedEvents = groupByCauseSnippet(events); 556 assertEquals(groupedEvents.size(), eventChains.length, "Number of main events"); 557 for (Map.Entry<Snippet, List<SnippetEvent>> entry : groupedEvents.entrySet()) { 558 EventChain eventChain = eventChains[impactId++]; 559 SnippetEvent main = entry.getValue().get(0); 560 Snippet mainKey = main.snippet(); 561 if (eventChain.mainInfo != null) { 562 eventChain.mainInfo.assertMatch(entry.getValue().get(0), mainKey); 563 if (eventChain.updates.length > 0) { 564 if (eventChain.updates.length == 1) { 565 eventChain.updates[0].assertMatch(entry.getValue().get(1), mainKey); 566 } else { 567 Arrays.sort(eventChain.updates, (a, b) -> ((a.snippet() == MAIN_SNIPPET) 568 ? mainKey 569 : a.snippet()).id().compareTo(b.snippet().id())); 570 List<SnippetEvent> updateEvents = new ArrayList<>(entry.getValue().subList(1, entry.getValue().size())); 571 int idx = 0; 572 for (SnippetEvent ste : updateEvents) { 573 eventChain.updates[idx++].assertMatch(ste, mainKey); 574 } 575 } 576 } 577 } 578 if (((Object) eventChain.value) != IGNORE_VALUE) { 579 assertEquals(main.value(), eventChain.value, "Expected execution value of: " + eventChain.value + 580 ", but got: " + main.value()); 581 } 582 if (eventChain.exceptionClass != IGNORE_EXCEPTION) { 583 if (main.exception() == null) { 584 assertEquals(eventChain.exceptionClass, null, "Expected an exception of class " 585 + eventChain.exceptionClass + " got no exception"); 586 } else if (eventChain.exceptionClass == null) { 587 fail("Expected no exception but got " + main.exception().toString()); 588 } else { 589 assertTrue(eventChain.exceptionClass.isInstance(main.exception()), 590 "Expected an exception of class " + eventChain.exceptionClass + 591 " got: " + main.exception().toString()); 592 } 593 } 594 List<Diag> diagnostics = getState().diagnostics(mainKey).collect(toList()); 595 switch (diagMain) { 596 case DIAG_OK: 597 assertEquals(diagnostics.size(), 0, "Expected no diagnostics, got: " + diagnosticsToString(diagnostics)); 598 break; 599 case DIAG_WARNING: 600 assertFalse(hasFatalError(diagnostics), "Expected no errors, got: " + diagnosticsToString(diagnostics)); 601 break; 602 case DIAG_ERROR: 603 assertTrue(hasFatalError(diagnostics), "Expected errors, got: " + diagnosticsToString(diagnostics)); 604 break; 605 } 606 if (eventChain.mainInfo != null) { 607 for (STEInfo ste : eventChain.updates) { 608 diagnostics = getState().diagnostics(ste.snippet()).collect(toList()); 609 switch (diagUpdates) { 610 case DIAG_OK: 611 assertEquals(diagnostics.size(), 0, "Expected no diagnostics, got: " + diagnosticsToString(diagnostics)); 612 break; 613 case DIAG_WARNING: 614 assertFalse(hasFatalError(diagnostics), "Expected no errors, got: " + diagnosticsToString(diagnostics)); 615 break; 616 } 617 } 618 } 619 } 620 return events; 621 } 622 623 // Use this for all EMPTY calls to eval() assertEvalEmpty(String input)624 public void assertEvalEmpty(String input) { 625 List<SnippetEvent> events = getState().eval(input); 626 assertEquals(events.size(), 0, "Expected no events, got: " + events.size()); 627 } 628 varKey(List<SnippetEvent> events)629 public VarSnippet varKey(List<SnippetEvent> events) { 630 Snippet key = key(events); 631 assertTrue(key instanceof VarSnippet, "Expected a VariableKey, got: " + key); 632 return (VarSnippet) key; 633 } 634 methodKey(List<SnippetEvent> events)635 public MethodSnippet methodKey(List<SnippetEvent> events) { 636 Snippet key = key(events); 637 assertTrue(key instanceof MethodSnippet, "Expected a MethodKey, got: " + key); 638 return (MethodSnippet) key; 639 } 640 classKey(List<SnippetEvent> events)641 public TypeDeclSnippet classKey(List<SnippetEvent> events) { 642 Snippet key = key(events); 643 assertTrue(key instanceof TypeDeclSnippet, "Expected a ClassKey, got: " + key); 644 return (TypeDeclSnippet) key; 645 } 646 importKey(List<SnippetEvent> events)647 public ImportSnippet importKey(List<SnippetEvent> events) { 648 Snippet key = key(events); 649 assertTrue(key instanceof ImportSnippet, "Expected a ImportKey, got: " + key); 650 return (ImportSnippet) key; 651 } 652 key(List<SnippetEvent> events)653 public Snippet key(List<SnippetEvent> events) { 654 assertTrue(events.size() >= 1, "Expected at least one event, got none."); 655 return events.get(0).snippet(); 656 } 657 assertVarValue(Snippet key, String expected)658 public void assertVarValue(Snippet key, String expected) { 659 String value = state.varValue((VarSnippet) key); 660 assertEquals(value, expected, "Expected var value of: " + expected + ", but got: " + value); 661 } 662 assertDeclareFail(String input, String expectedErrorCode)663 public Snippet assertDeclareFail(String input, String expectedErrorCode) { 664 return assertDeclareFail(input, expectedErrorCode, added(REJECTED)); 665 } 666 assertDeclareFail(String input, String expectedErrorCode, STEInfo mainInfo, STEInfo... updates)667 public Snippet assertDeclareFail(String input, String expectedErrorCode, 668 STEInfo mainInfo, STEInfo... updates) { 669 return assertDeclareFail(input, 670 new ExpectedDiagnostic(expectedErrorCode, -1, -1, -1, -1, -1, Diagnostic.Kind.ERROR), 671 mainInfo, updates); 672 } 673 assertDeclareFail(String input, ExpectedDiagnostic expectedDiagnostic)674 public Snippet assertDeclareFail(String input, ExpectedDiagnostic expectedDiagnostic) { 675 return assertDeclareFail(input, expectedDiagnostic, added(REJECTED)); 676 } 677 assertDeclareFail(String input, ExpectedDiagnostic expectedDiagnostic, STEInfo mainInfo, STEInfo... updates)678 public Snippet assertDeclareFail(String input, ExpectedDiagnostic expectedDiagnostic, 679 STEInfo mainInfo, STEInfo... updates) { 680 List<SnippetEvent> events = assertEval(input, null, null, 681 DiagCheck.DIAG_ERROR, DiagCheck.DIAG_IGNORE, mainInfo, updates); 682 SnippetEvent e = events.get(0); 683 Snippet key = e.snippet(); 684 assertEquals(getState().status(key), REJECTED); 685 List<Diag> diagnostics = getState().diagnostics(e.snippet()).collect(toList()); 686 assertTrue(diagnostics.size() > 0, "Expected diagnostics, got none"); 687 assertDiagnostic(input, diagnostics.get(0), expectedDiagnostic); 688 assertTrue(key != null, "key must never be null, but it was for: " + input); 689 return key; 690 } 691 assertDeclareWarn1(String input, String expectedErrorCode)692 public Snippet assertDeclareWarn1(String input, String expectedErrorCode) { 693 return assertDeclareWarn1(input, new ExpectedDiagnostic(expectedErrorCode, -1, -1, -1, -1, -1, Diagnostic.Kind.WARNING)); 694 } 695 assertDeclareWarn1(String input, ExpectedDiagnostic expectedDiagnostic)696 public Snippet assertDeclareWarn1(String input, ExpectedDiagnostic expectedDiagnostic) { 697 return assertDeclareWarn1(input, expectedDiagnostic, added(VALID)); 698 } 699 assertDeclareWarn1(String input, ExpectedDiagnostic expectedDiagnostic, STEInfo mainInfo, STEInfo... updates)700 public Snippet assertDeclareWarn1(String input, ExpectedDiagnostic expectedDiagnostic, STEInfo mainInfo, STEInfo... updates) { 701 List<SnippetEvent> events = assertEval(input, IGNORE_VALUE, null, 702 DiagCheck.DIAG_WARNING, DiagCheck.DIAG_IGNORE, mainInfo, updates); 703 SnippetEvent e = events.get(0); 704 List<Diag> diagnostics = getState().diagnostics(e.snippet()).collect(toList()); 705 if (expectedDiagnostic != null) assertDiagnostic(input, diagnostics.get(0), expectedDiagnostic); 706 return e.snippet(); 707 } 708 assertDiagnostic(String input, Diag diagnostic, ExpectedDiagnostic expectedDiagnostic)709 private void assertDiagnostic(String input, Diag diagnostic, ExpectedDiagnostic expectedDiagnostic) { 710 if (expectedDiagnostic != null) expectedDiagnostic.assertDiagnostic(diagnostic); 711 // assertEquals(diagnostic.getSource(), input, "Diagnostic source"); 712 } 713 assertTypeDeclSnippet(TypeDeclSnippet type, String expectedName, Status expectedStatus, SubKind expectedSubKind, int unressz, int othersz)714 public void assertTypeDeclSnippet(TypeDeclSnippet type, String expectedName, 715 Status expectedStatus, SubKind expectedSubKind, 716 int unressz, int othersz) { 717 assertDeclarationSnippet(type, expectedName, expectedStatus, 718 expectedSubKind, unressz, othersz); 719 } 720 assertMethodDeclSnippet(MethodSnippet method, String expectedName, String expectedSignature, Status expectedStatus, int unressz, int othersz)721 public void assertMethodDeclSnippet(MethodSnippet method, 722 String expectedName, String expectedSignature, 723 Status expectedStatus, int unressz, int othersz) { 724 assertDeclarationSnippet(method, expectedName, expectedStatus, 725 METHOD_SUBKIND, unressz, othersz); 726 String signature = method.signature(); 727 assertEquals(signature, expectedSignature, 728 "Expected " + method.source() + " to have the name: " + 729 expectedSignature + ", got: " + signature); 730 } 731 assertVariableDeclSnippet(VarSnippet var, String expectedName, String expectedTypeName, Status expectedStatus, SubKind expectedSubKind, int unressz, int othersz)732 public void assertVariableDeclSnippet(VarSnippet var, 733 String expectedName, String expectedTypeName, 734 Status expectedStatus, SubKind expectedSubKind, 735 int unressz, int othersz) { 736 assertDeclarationSnippet(var, expectedName, expectedStatus, 737 expectedSubKind, unressz, othersz); 738 String signature = var.typeName(); 739 assertEquals(signature, expectedTypeName, 740 "Expected " + var.source() + " to have the type name: " + 741 expectedTypeName + ", got: " + signature); 742 } 743 assertDeclarationSnippet(DeclarationSnippet declarationKey, String expectedName, Status expectedStatus, SubKind expectedSubKind, int unressz, int othersz)744 public void assertDeclarationSnippet(DeclarationSnippet declarationKey, 745 String expectedName, 746 Status expectedStatus, SubKind expectedSubKind, 747 int unressz, int othersz) { 748 assertKey(declarationKey, expectedStatus, expectedSubKind); 749 String source = declarationKey.source(); 750 assertEquals(declarationKey.name(), expectedName, 751 "Expected " + source + " to have the name: " + expectedName + ", got: " + declarationKey.name()); 752 long unresolved = getState().unresolvedDependencies(declarationKey).count(); 753 assertEquals(unresolved, unressz, "Expected " + source + " to have " + unressz 754 + " unresolved symbols, got: " + unresolved); 755 long otherCorralledErrorsCount = getState().diagnostics(declarationKey).count(); 756 assertEquals(otherCorralledErrorsCount, othersz, "Expected " + source + " to have " + othersz 757 + " other errors, got: " + otherCorralledErrorsCount); 758 } 759 assertKey(Snippet key, Status expectedStatus, SubKind expectedSubKind)760 public void assertKey(Snippet key, Status expectedStatus, SubKind expectedSubKind) { 761 String source = key.source(); 762 SubKind actualSubKind = key.subKind(); 763 assertEquals(actualSubKind, expectedSubKind, 764 "Expected " + source + " to have the subkind: " + expectedSubKind + ", got: " + actualSubKind); 765 Status status = getState().status(key); 766 assertEquals(status, expectedStatus, "Expected " + source + " to be " 767 + expectedStatus + ", but it is " + status); 768 Snippet.Kind expectedKind = getKind(key); 769 assertEquals(key.kind(), expectedKind, "Checking kind: "); 770 assertEquals(expectedSubKind.kind(), expectedKind, "Checking kind: "); 771 } 772 assertDrop(Snippet key, STEInfo mainInfo, STEInfo... updates)773 public void assertDrop(Snippet key, STEInfo mainInfo, STEInfo... updates) { 774 assertDrop(key, DiagCheck.DIAG_OK, DiagCheck.DIAG_OK, mainInfo, updates); 775 } 776 assertDrop(Snippet key, DiagCheck diagMain, DiagCheck diagUpdates, STEInfo mainInfo, STEInfo... updates)777 public void assertDrop(Snippet key, DiagCheck diagMain, DiagCheck diagUpdates, STEInfo mainInfo, STEInfo... updates) { 778 assertDrop(key, diagMain, diagUpdates, new EventChain(mainInfo, null, null, updates)); 779 } 780 assertDrop(Snippet key, DiagCheck diagMain, DiagCheck diagUpdates, EventChain... eventChains)781 public void assertDrop(Snippet key, DiagCheck diagMain, DiagCheck diagUpdates, EventChain... eventChains) { 782 checkEvents(() -> getState().drop(key), "drop(" + key + ")", diagMain, diagUpdates, eventChains); 783 } 784 assertAnalyze(String input, String source, String remaining, boolean isComplete)785 public void assertAnalyze(String input, String source, String remaining, boolean isComplete) { 786 assertAnalyze(input, null, source, remaining, isComplete); 787 } 788 assertAnalyze(String input, Completeness status, String source)789 public void assertAnalyze(String input, Completeness status, String source) { 790 assertAnalyze(input, status, source, null, null); 791 } 792 assertAnalyze(String input, Completeness status, String source, String remaining, Boolean isComplete)793 public void assertAnalyze(String input, Completeness status, String source, String remaining, Boolean isComplete) { 794 CompletionInfo ci = getAnalysis().analyzeCompletion(input); 795 if (status != null) assertEquals(ci.completeness(), status, "Input : " + input + ", status: "); 796 assertEquals(ci.source(), source, "Input : " + input + ", source: "); 797 if (remaining != null) assertEquals(ci.remaining(), remaining, "Input : " + input + ", remaining: "); 798 if (isComplete != null) { 799 boolean isExpectedComplete = isComplete; 800 assertEquals(ci.completeness().isComplete(), isExpectedComplete, "Input : " + input + ", isComplete: "); 801 } 802 } 803 assertNumberOfActiveVariables(int cnt)804 public void assertNumberOfActiveVariables(int cnt) { 805 assertEquals(getState().variables().count(), cnt, "Variables : " + getState().variables().collect(toList())); 806 } 807 assertNumberOfActiveMethods(int cnt)808 public void assertNumberOfActiveMethods(int cnt) { 809 assertEquals(getState().methods().count(), cnt, "Methods : " + getState().methods().collect(toList())); 810 } 811 assertNumberOfActiveClasses(int cnt)812 public void assertNumberOfActiveClasses(int cnt) { 813 assertEquals(getState().types().count(), cnt, "Types : " + getState().types().collect(toList())); 814 } 815 assertKeys(MemberInfo... expected)816 public void assertKeys(MemberInfo... expected) { 817 int index = 0; 818 List<Snippet> snippets = getState().snippets().collect(toList()); 819 assertEquals(allSnippets.size(), snippets.size()); 820 for (Snippet sn : snippets) { 821 if (sn.kind().isPersistent() && getState().status(sn).isActive()) { 822 MemberInfo actual = getMemberInfo(sn); 823 MemberInfo exp = expected[index]; 824 assertEquals(actual, exp, String.format("Difference in #%d. Expected: %s, actual: %s", 825 index, exp, actual)); 826 ++index; 827 } 828 } 829 } 830 assertActiveKeys()831 public void assertActiveKeys() { 832 Collection<Snippet> expected = getActiveKeys(); 833 assertActiveKeys(expected.toArray(new Snippet[expected.size()])); 834 } 835 assertActiveKeys(Snippet... expected)836 public void assertActiveKeys(Snippet... expected) { 837 int index = 0; 838 for (Snippet key : getState().snippets().collect(toList())) { 839 if (state.status(key).isActive()) { 840 assertEquals(expected[index], key, String.format("Difference in #%d. Expected: %s, actual: %s", index, key, expected[index])); 841 ++index; 842 } 843 } 844 } 845 assertActiveSnippets(Stream<? extends Snippet> snippets, Predicate<Snippet> p, String label)846 private void assertActiveSnippets(Stream<? extends Snippet> snippets, Predicate<Snippet> p, String label) { 847 Set<Snippet> active = getActiveKeys().stream() 848 .filter(p) 849 .collect(Collectors.toSet()); 850 Set<Snippet> got = snippets 851 .collect(Collectors.toSet()); 852 assertEquals(active, got, label); 853 } 854 assertVariables()855 public void assertVariables() { 856 assertActiveSnippets(getState().variables(), (key) -> key instanceof VarSnippet, "Variables"); 857 } 858 assertMethods()859 public void assertMethods() { 860 assertActiveSnippets(getState().methods(), (key) -> key instanceof MethodSnippet, "Methods"); 861 } 862 assertClasses()863 public void assertClasses() { 864 assertActiveSnippets(getState().types(), (key) -> key instanceof TypeDeclSnippet, "Classes"); 865 } 866 assertMembers(Stream<? extends Snippet> members, MemberInfo...expectedInfos)867 public void assertMembers(Stream<? extends Snippet> members, MemberInfo...expectedInfos) { 868 Set<MemberInfo> expected = Stream.of(expectedInfos).collect(Collectors.toSet()); 869 Set<MemberInfo> got = members 870 .map(this::getMemberInfo) 871 .collect(Collectors.toSet()); 872 assertEquals(got.size(), expected.size(), "Expected : " + expected + ", actual : " + members); 873 assertEquals(got, expected); 874 } 875 assertVariables(MemberInfo...expected)876 public void assertVariables(MemberInfo...expected) { 877 assertMembers(getState().variables(), expected); 878 } 879 assertMethods(MemberInfo...expected)880 public void assertMethods(MemberInfo...expected) { 881 assertMembers(getState().methods(), expected); 882 getState().methods().forEach(methodKey -> { 883 MemberInfo expectedInfo = null; 884 for (MemberInfo info : expected) { 885 if (info.name.equals(methodKey.name()) && info.type.equals(methodKey.signature())) { 886 expectedInfo = getMemberInfo(methodKey); 887 } 888 } 889 assertNotNull(expectedInfo, "Not found method: " + methodKey.name()); 890 int lastIndexOf = expectedInfo.type.lastIndexOf(')'); 891 assertEquals(methodKey.parameterTypes(), expectedInfo.type.substring(1, lastIndexOf), "Parameter types"); 892 }); 893 } 894 assertClasses(MemberInfo...expected)895 public void assertClasses(MemberInfo...expected) { 896 assertMembers(getState().types(), expected); 897 } 898 assertCompletion(String code, String... expected)899 public void assertCompletion(String code, String... expected) { 900 assertCompletion(code, null, expected); 901 } 902 assertCompletion(String code, Boolean isSmart, String... expected)903 public void assertCompletion(String code, Boolean isSmart, String... expected) { 904 List<String> completions = computeCompletions(code, isSmart); 905 assertEquals(completions, Arrays.asList(expected), "Input: " + code + ", " + completions.toString()); 906 } 907 assertCompletionIncludesExcludes(String code, Set<String> expected, Set<String> notExpected)908 public void assertCompletionIncludesExcludes(String code, Set<String> expected, Set<String> notExpected) { 909 assertCompletionIncludesExcludes(code, null, expected, notExpected); 910 } 911 assertCompletionIncludesExcludes(String code, Boolean isSmart, Set<String> expected, Set<String> notExpected)912 public void assertCompletionIncludesExcludes(String code, Boolean isSmart, Set<String> expected, Set<String> notExpected) { 913 List<String> completions = computeCompletions(code, isSmart); 914 assertTrue(completions.containsAll(expected), "Expected completions: " 915 + String.valueOf(expected) 916 + ", got: " 917 + String.valueOf(completions)); 918 assertTrue(Collections.disjoint(completions, notExpected), String.valueOf(completions)); 919 } 920 computeCompletions(String code, Boolean isSmart)921 private List<String> computeCompletions(String code, Boolean isSmart) { 922 waitIndexingFinished(); 923 924 int cursor = code.indexOf('|'); 925 code = code.replace("|", ""); 926 assertTrue(cursor > -1, "'|' expected, but not found in: " + code); 927 List<Suggestion> completions = 928 getAnalysis().completionSuggestions(code, cursor, new int[1]); //XXX: ignoring anchor for now 929 return completions.stream() 930 .filter(s -> isSmart == null || isSmart == s.matchesType()) 931 .map(s -> s.continuation()) 932 .distinct() 933 .collect(Collectors.toList()); 934 } 935 assertInferredType(String code, String expectedType)936 public void assertInferredType(String code, String expectedType) { 937 String inferredType = getAnalysis().analyzeType(code, code.length()); 938 939 assertEquals(inferredType, expectedType, "Input: " + code + ", " + inferredType); 940 } 941 assertInferredFQNs(String code, String... fqns)942 public void assertInferredFQNs(String code, String... fqns) { 943 assertInferredFQNs(code, code.length(), false, fqns); 944 } 945 assertInferredFQNs(String code, int simpleNameLen, boolean resolvable, String... fqns)946 public void assertInferredFQNs(String code, int simpleNameLen, boolean resolvable, String... fqns) { 947 waitIndexingFinished(); 948 949 QualifiedNames candidates = getAnalysis().listQualifiedNames(code, code.length()); 950 951 assertEquals(candidates.getNames(), Arrays.asList(fqns), "Input: " + code + ", candidates=" + candidates.getNames()); 952 assertEquals(candidates.getSimpleNameLength(), simpleNameLen, "Input: " + code + ", simpleNameLen=" + candidates.getSimpleNameLength()); 953 assertEquals(candidates.isResolvable(), resolvable, "Input: " + code + ", resolvable=" + candidates.isResolvable()); 954 } 955 waitIndexingFinished()956 protected void waitIndexingFinished() { 957 try { 958 Method waitBackgroundTaskFinished = getAnalysis().getClass().getDeclaredMethod("waitBackgroundTaskFinished"); 959 960 waitBackgroundTaskFinished.setAccessible(true); 961 waitBackgroundTaskFinished.invoke(getAnalysis()); 962 } catch (Exception ex) { 963 throw new AssertionError("Cannot wait for indexing end.", ex); 964 } 965 } 966 assertSignature(String code, String... expected)967 public void assertSignature(String code, String... expected) { 968 int cursor = code.indexOf('|'); 969 code = code.replace("|", ""); 970 assertTrue(cursor > -1, "'|' expected, but not found in: " + code); 971 List<Documentation> documentation = getAnalysis().documentation(code, cursor, false); 972 Set<String> docSet = documentation.stream().map(doc -> doc.signature()).collect(Collectors.toSet()); 973 Set<String> expectedSet = Stream.of(expected).collect(Collectors.toSet()); 974 assertEquals(docSet, expectedSet, "Input: " + code); 975 } 976 assertJavadoc(String code, String... expected)977 public void assertJavadoc(String code, String... expected) { 978 int cursor = code.indexOf('|'); 979 code = code.replace("|", ""); 980 assertTrue(cursor > -1, "'|' expected, but not found in: " + code); 981 List<Documentation> documentation = getAnalysis().documentation(code, cursor, true); 982 Set<String> docSet = documentation.stream() 983 .map(doc -> doc.signature() + "\n" + doc.javadoc()) 984 .collect(Collectors.toSet()); 985 Set<String> expectedSet = Stream.of(expected).collect(Collectors.toSet()); 986 assertEquals(docSet, expectedSet, "Input: " + code); 987 } 988 989 public enum ClassType { 990 CLASS("CLASS_SUBKIND", "class", "class"), 991 ENUM("ENUM_SUBKIND", "enum", "enum"), 992 INTERFACE("INTERFACE_SUBKIND", "interface", "interface"), 993 ANNOTATION("ANNOTATION_TYPE_SUBKIND", "@interface", "annotation interface"); 994 995 private final String classType; 996 private final String name; 997 private final String displayed; 998 ClassType(String classType, String name, String displayed)999 ClassType(String classType, String name, String displayed) { 1000 this.classType = classType; 1001 this.name = name; 1002 this.displayed = displayed; 1003 } 1004 getClassType()1005 public String getClassType() { 1006 return classType; 1007 } 1008 getDisplayed()1009 public String getDisplayed() { 1010 return displayed; 1011 } 1012 1013 @Override toString()1014 public String toString() { 1015 return name; 1016 } 1017 } 1018 variable(String type, String name)1019 public static MemberInfo variable(String type, String name) { 1020 return new MemberInfo(type, name); 1021 } 1022 method(String signature, String name)1023 public static MemberInfo method(String signature, String name) { 1024 return new MemberInfo(signature, name); 1025 } 1026 clazz(ClassType classType, String className)1027 public static MemberInfo clazz(ClassType classType, String className) { 1028 return new MemberInfo(classType.getClassType(), className); 1029 } 1030 1031 public static class MemberInfo { 1032 public final String type; 1033 public final String name; 1034 MemberInfo(String type, String name)1035 public MemberInfo(String type, String name) { 1036 this.type = type; 1037 this.name = name; 1038 } 1039 1040 @Override hashCode()1041 public int hashCode() { 1042 return type.hashCode() + 3 * name.hashCode(); 1043 } 1044 1045 @Override equals(Object o)1046 public boolean equals(Object o) { 1047 if (o instanceof MemberInfo) { 1048 MemberInfo other = (MemberInfo) o; 1049 return type.equals(other.type) && name.equals(other.name); 1050 } 1051 return false; 1052 } 1053 1054 @Override toString()1055 public String toString() { 1056 return String.format("%s %s", type, name); 1057 } 1058 } 1059 getMemberInfo(Snippet key)1060 public MemberInfo getMemberInfo(Snippet key) { 1061 SubKind subkind = key.subKind(); 1062 switch (subkind) { 1063 case CLASS_SUBKIND: 1064 case INTERFACE_SUBKIND: 1065 case ENUM_SUBKIND: 1066 case ANNOTATION_TYPE_SUBKIND: 1067 return new MemberInfo(subkind.name(), ((DeclarationSnippet) key).name()); 1068 case METHOD_SUBKIND: 1069 MethodSnippet method = (MethodSnippet) key; 1070 return new MemberInfo(method.signature(), method.name()); 1071 case VAR_DECLARATION_SUBKIND: 1072 case VAR_DECLARATION_WITH_INITIALIZER_SUBKIND: 1073 case TEMP_VAR_EXPRESSION_SUBKIND: 1074 VarSnippet var = (VarSnippet) key; 1075 return new MemberInfo(var.typeName(), var.name()); 1076 default: 1077 throw new AssertionError("Unknown snippet : " + key.kind() + " in expression " + key.toString()); 1078 } 1079 } 1080 diagnosticsToString(List<Diag> diagnostics)1081 public String diagnosticsToString(List<Diag> diagnostics) { 1082 StringWriter writer = new StringWriter(); 1083 for (Diag diag : diagnostics) { 1084 writer.write("Error --\n"); 1085 for (String line : diag.getMessage(null).split("\\r?\\n")) { 1086 writer.write(String.format("%s\n", line)); 1087 } 1088 } 1089 return writer.toString().replace("\n", System.lineSeparator()); 1090 } 1091 hasFatalError(List<Diag> diagnostics)1092 public boolean hasFatalError(List<Diag> diagnostics) { 1093 for (Diag diag : diagnostics) { 1094 if (diag.isError()) { 1095 return true; 1096 } 1097 } 1098 return false; 1099 } 1100 chain(STEInfo mainInfo, STEInfo... updates)1101 public static EventChain chain(STEInfo mainInfo, STEInfo... updates) { 1102 return chain(mainInfo, IGNORE_VALUE, null, updates); 1103 } 1104 chain(STEInfo mainInfo, String value, Class<? extends Throwable> exceptionClass, STEInfo... updates)1105 public static EventChain chain(STEInfo mainInfo, String value, Class<? extends Throwable> exceptionClass, STEInfo... updates) { 1106 return new EventChain(mainInfo, value, exceptionClass, updates); 1107 } 1108 ste(Snippet key, Status previousStatus, Status status, Boolean isSignatureChange, Snippet causeKey)1109 public static STEInfo ste(Snippet key, Status previousStatus, Status status, 1110 Boolean isSignatureChange, Snippet causeKey) { 1111 return new STEInfo(key, previousStatus, status, isSignatureChange, causeKey); 1112 } 1113 added(Status status)1114 public static STEInfo added(Status status) { 1115 return new STEInfo(MAIN_SNIPPET, NONEXISTENT, status, status.isDefined(), null); 1116 } 1117 1118 public static class EventChain { 1119 public final STEInfo mainInfo; 1120 public final STEInfo[] updates; 1121 public final String value; 1122 public final Class<? extends Throwable> exceptionClass; 1123 EventChain(STEInfo mainInfo, String value, Class<? extends Throwable> exceptionClass, STEInfo... updates)1124 public EventChain(STEInfo mainInfo, String value, Class<? extends Throwable> exceptionClass, STEInfo... updates) { 1125 this.mainInfo = mainInfo; 1126 this.updates = updates; 1127 this.value = value; 1128 this.exceptionClass = exceptionClass; 1129 } 1130 } 1131 1132 public static class STEInfo { 1133 STEInfo(Snippet snippet, Status previousStatus, Status status, Boolean isSignatureChange, Snippet causeSnippet)1134 STEInfo(Snippet snippet, Status previousStatus, Status status, 1135 Boolean isSignatureChange, Snippet causeSnippet) { 1136 this.snippet = snippet; 1137 this.previousStatus = previousStatus; 1138 this.status = status; 1139 this.checkIsSignatureChange = isSignatureChange != null; 1140 this.isSignatureChange = checkIsSignatureChange ? isSignatureChange : false; 1141 this.causeSnippet = causeSnippet; 1142 assertTrue(snippet != null, "Bad test set-up. The match snippet must not be null"); 1143 } 1144 1145 final Snippet snippet; 1146 final Status previousStatus; 1147 final Status status; 1148 final boolean isSignatureChange; 1149 final Snippet causeSnippet; 1150 1151 final boolean checkIsSignatureChange; snippet()1152 public Snippet snippet() { 1153 return snippet; 1154 } previousStatus()1155 public Status previousStatus() { 1156 return previousStatus; 1157 } status()1158 public Status status() { 1159 return status; 1160 } isSignatureChange()1161 public boolean isSignatureChange() { 1162 if (!checkIsSignatureChange) { 1163 throw new IllegalStateException("isSignatureChange value is undefined"); 1164 } 1165 return isSignatureChange; 1166 } causeSnippet()1167 public Snippet causeSnippet() { 1168 return causeSnippet; 1169 } value()1170 public String value() { 1171 return null; 1172 } exception()1173 public Exception exception() { 1174 return null; 1175 } 1176 assertMatch(SnippetEvent ste, Snippet mainSnippet)1177 public void assertMatch(SnippetEvent ste, Snippet mainSnippet) { 1178 assertKeyMatch(ste, ste.snippet(), snippet(), mainSnippet); 1179 assertStatusMatch(ste, ste.previousStatus(), previousStatus()); 1180 assertStatusMatch(ste, ste.status(), status()); 1181 if (checkIsSignatureChange) { 1182 assertEquals(ste.isSignatureChange(), isSignatureChange(), 1183 "Expected " + 1184 (isSignatureChange()? "" : "no ") + 1185 "signature-change, got: " + 1186 (ste.isSignatureChange()? "" : "no ") + 1187 "signature-change" + 1188 "\n expected-event: " + this + "\n got-event: " + toString(ste)); 1189 } 1190 assertKeyMatch(ste, ste.causeSnippet(), causeSnippet(), mainSnippet); 1191 } 1192 assertKeyMatch(SnippetEvent ste, Snippet sn, Snippet expected, Snippet mainSnippet)1193 private void assertKeyMatch(SnippetEvent ste, Snippet sn, Snippet expected, Snippet mainSnippet) { 1194 Snippet testKey = expected; 1195 if (testKey != null) { 1196 if (expected == MAIN_SNIPPET) { 1197 assertNotNull(mainSnippet, "MAIN_SNIPPET used, test must pass value to assertMatch"); 1198 testKey = mainSnippet; 1199 } 1200 if (ste.causeSnippet() == null && ste.status() != DROPPED && expected != MAIN_SNIPPET) { 1201 // Source change, always new snippet -- only match id() 1202 assertTrue(sn != testKey, 1203 "Main-event: Expected new snippet to be != : " + testKey 1204 + "\n got-event: " + toString(ste)); 1205 assertEquals(sn.id(), testKey.id(), "Expected IDs to match: " + testKey + ", got: " + sn 1206 + "\n expected-event: " + this + "\n got-event: " + toString(ste)); 1207 } else { 1208 assertEquals(sn, testKey, "Expected key to be: " + testKey + ", got: " + sn 1209 + "\n expected-event: " + this + "\n got-event: " + toString(ste)); 1210 } 1211 } 1212 } 1213 assertStatusMatch(SnippetEvent ste, Status status, Status expected)1214 private void assertStatusMatch(SnippetEvent ste, Status status, Status expected) { 1215 if (expected != null) { 1216 assertEquals(status, expected, "Expected status to be: " + expected + ", got: " + status + 1217 "\n expected-event: " + this + "\n got-event: " + toString(ste)); 1218 } 1219 } 1220 1221 @Override toString()1222 public String toString() { 1223 return "STEInfo key: " + 1224 (snippet()==MAIN_SNIPPET? "MAIN_SNIPPET" : (snippet()==null? "ignore" : snippet().id())) + 1225 " before: " + previousStatus() + 1226 " status: " + status() + " sig: " + isSignatureChange() + 1227 " cause: " + (causeSnippet()==null? "null" : causeSnippet().id()); 1228 } 1229 toString(SnippetEvent ste)1230 private String toString(SnippetEvent ste) { 1231 return "key: " + (ste.snippet()==MAIN_SNIPPET? "MAIN_SNIPPET" : ste.snippet().id()) + " before: " + ste.previousStatus() 1232 + " status: " + ste.status() + " sig: " + ste.isSignatureChange() 1233 + " cause: " + ste.causeSnippet(); 1234 } 1235 } 1236 } 1237