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