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