Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(783)

Side by Side Diff: pkg/analysis_server/lib/src/status/validator.dart

Issue 1503893002: Add more (incomplete) diagnostic support to the status pages (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library analysis_server.src.status.validator;
6
7 import 'dart:collection';
8
9 import 'package:analyzer/src/context/cache.dart';
10 import 'package:analyzer/src/context/context.dart';
11 import 'package:analyzer/src/generated/ast.dart';
12 import 'package:analyzer/src/generated/constant.dart';
13 import 'package:analyzer/src/generated/element.dart';
14 import 'package:analyzer/src/generated/engine.dart'
15 show AnalysisEngine, AnalysisResult, CacheState, ChangeSet;
16 import 'package:analyzer/src/generated/error.dart';
17 import 'package:analyzer/src/generated/resolver.dart';
18 import 'package:analyzer/src/generated/scanner.dart';
19 import 'package:analyzer/src/generated/source.dart';
20 import 'package:analyzer/src/generated/utilities_collection.dart';
21 import 'package:analyzer/src/task/dart.dart';
22 import 'package:analyzer/src/task/html.dart';
23 import 'package:analyzer/task/dart.dart';
24 import 'package:analyzer/task/model.dart';
25 import 'package:html/dom.dart' as html;
26
27 /**
28 * A class used to compare two element models for equality.
29 */
30 class ElementComparator {
31 /**
32 * The buffer to which any discovered differences will be recorded.
33 */
34 final StringBuffer _buffer = new StringBuffer();
35
36 /**
37 * A flag indicating whether a line break should be added the next time data
38 * is written to the [_buffer].
39 */
40 bool _needsLineBreak = false;
41
42 /**
43 * Initialize a newly created comparator.
44 */
45 ElementComparator();
46
47 /**
48 * A textual description of the differences that were found.
49 */
50 String get description => _buffer.toString();
51
52 /**
53 * Return `true` if at least one difference was found between the expected and
54 * actual elements.
55 */
56 bool get hasDifference => _buffer.length > 0;
57
58 /**
59 * Compare the [expected] and [actual] elements. The results of the comparison
60 * can be accessed via the [hasDifference] and [description] getters.
61 */
62 void compareElements(Element expected, Element actual) {
63 if (expected == null) {
64 if (actual != null) {
65 _writeMismatch(expected, actual, (Element element) {
66 return element == null ? 'null' : 'non null ${element.runtimeType}';
67 });
68 }
69 } else if (actual == null) {
70 _writeMismatch(expected, actual, (Element element) {
71 return element == null ? 'null' : 'non null ${element.runtimeType}';
72 });
73 } else if (expected is ClassElement && actual is ClassElement) {
74 _compareClassElements(expected, actual);
75 } else if (expected is CompilationUnitElement &&
76 actual is CompilationUnitElement) {
77 _compareCompilationUnitElements(expected, actual);
78 } else if (expected is ConstructorElement && actual is ConstructorElement) {
79 _compareConstructorElements(expected, actual);
80 } else if (expected is ExportElement && actual is ExportElement) {
81 _compareExportElements(expected, actual);
82 } else if (expected is FieldElement && actual is FieldElement) {
83 _compareFieldElements(expected, actual);
84 } else if (expected is FieldFormalParameterElement &&
85 actual is FieldFormalParameterElement) {
86 _compareFieldFormalParameterElements(expected, actual);
87 } else if (expected is FunctionElement && actual is FunctionElement) {
88 _compareFunctionElements(expected, actual);
89 } else if (expected is FunctionTypeAliasElement &&
90 actual is FunctionTypeAliasElement) {
91 _compareFunctionTypeAliasElements(expected, actual);
92 } else if (expected is ImportElement && actual is ImportElement) {
93 _compareImportElements(expected, actual);
94 } else if (expected is LabelElement && actual is LabelElement) {
95 _compareLabelElements(expected, actual);
96 } else if (expected is LibraryElement && actual is LibraryElement) {
97 _compareLibraryElements(expected, actual);
98 } else if (expected is LocalVariableElement &&
99 actual is LocalVariableElement) {
100 _compareLocalVariableElements(expected, actual);
101 } else if (expected is MethodElement && actual is MethodElement) {
102 _compareMethodElements(expected, actual);
103 } else if (expected is MultiplyDefinedElement &&
104 actual is MultiplyDefinedElement) {
105 _compareMultiplyDefinedElements(expected, actual);
106 } else if (expected is ParameterElement && actual is ParameterElement) {
107 _compareParameterElements(expected, actual);
108 } else if (expected is PrefixElement && actual is PrefixElement) {
109 _comparePrefixElements(expected, actual);
110 } else if (expected is PropertyAccessorElement &&
111 actual is PropertyAccessorElement) {
112 _comparePropertyAccessorElements(expected, actual);
113 } else if (expected is TopLevelVariableElement &&
114 actual is TopLevelVariableElement) {
115 _compareTopLevelVariableElements(expected, actual);
116 } else if (expected is TypeParameterElement &&
117 actual is TypeParameterElement) {
118 _compareTypeParameterElements(expected, actual);
119 } else {
120 _write('Expected an instance of ');
121 _write(expected.runtimeType);
122 _write('; found an instance of ');
123 _writeln(actual.runtimeType);
124 }
125 }
126
127 void _compareClassElements(ClassElement expected, ClassElement actual) {
128 _compareGenericElements(expected, actual);
129 //
130 // Compare attributes.
131 //
132 if (expected.hasReferenceToSuper != actual.hasReferenceToSuper) {
133 _writeMismatch(
134 expected,
135 actual,
136 (ClassElement element) => element.hasReferenceToSuper
137 ? 'a class that references super'
138 : 'a class that does not reference super');
139 }
140 if (expected.isAbstract != actual.isAbstract) {
141 _writeMismatch(
142 expected,
143 actual,
144 (ClassElement element) =>
145 element.isAbstract ? 'an abstract class' : 'a concrete class');
146 }
147 if (expected.isEnum != actual.isEnum ||
148 expected.isMixinApplication != actual.isMixinApplication) {
149 _writeMismatch(
150 expected,
151 actual,
152 (ClassElement element) => element.isEnum
153 ? 'an enum'
154 : (element.isMixinApplication
155 ? 'a mixin application'
156 : 'a class'));
157 }
158 if (expected.isOrInheritsProxy != actual.isOrInheritsProxy) {
159 _writeMismatch(
160 expected,
161 actual,
162 (ClassElement element) => element.isOrInheritsProxy
163 ? 'a class that is marked as a proxy'
164 : 'a class that is not marked as a proxy');
165 }
166 if (expected.isValidMixin != actual.isValidMixin) {
167 _writeMismatch(
168 expected,
169 actual,
170 (ClassElement element) =>
171 element.isValidMixin ? 'a valid mixin' : 'an invalid mixin');
172 }
173 _compareTypes('supertype', expected.supertype, actual.supertype);
174 _compareTypeLists('mixin', expected.mixins, actual.mixins);
175 _compareTypeLists('interface', expected.interfaces, actual.interfaces);
176 //
177 // Compare children.
178 //
179 _compareElementLists(expected.accessors, actual.accessors);
180 _compareElementLists(expected.constructors, actual.constructors);
181 _compareElementLists(expected.fields, actual.fields);
182 _compareElementLists(expected.methods, actual.methods);
183 _compareElementLists(expected.typeParameters, actual.typeParameters);
184 }
185
186 void _compareCompilationUnitElements(
187 CompilationUnitElement expected, CompilationUnitElement actual) {
188 _compareGenericElements(expected, actual);
189 //
190 // Compare children.
191 //
192 _compareElementLists(expected.accessors, actual.accessors);
193 _compareElementLists(expected.enums, actual.enums);
194 _compareElementLists(expected.functions, actual.functions);
195 _compareElementLists(
196 expected.functionTypeAliases, actual.functionTypeAliases);
197 _compareElementLists(expected.topLevelVariables, actual.topLevelVariables);
198 _compareElementLists(expected.types, actual.types);
199 }
200
201 void _compareConstructorElements(
202 ConstructorElement expected, ConstructorElement actual) {
203 _compareExecutableElements(expected, actual, 'constructor');
204 //
205 // Compare attributes.
206 //
207 if (expected.isConst != actual.isConst) {
208 _writeMismatch(
209 expected,
210 actual,
211 (ConstructorElement element) => element.isConst
212 ? 'a const constructor'
213 : 'a non-const constructor');
214 }
215 if (expected.isFactory != actual.isFactory) {
216 _writeMismatch(
217 expected,
218 actual,
219 (ConstructorElement element) => element.isFactory
220 ? 'a factory constructor'
221 : 'a non-factory constructor');
222 }
223 if (expected.periodOffset != actual.periodOffset) {
224 _write('Expected a period offset of ');
225 _write(expected.periodOffset);
226 _write('; found ');
227 _writeln(actual.periodOffset);
228 }
229 if ((expected.redirectedConstructor == null) !=
230 (actual.redirectedConstructor == null)) {
231 _writeMismatch(
232 expected,
233 actual,
234 (ConstructorElement element) => element.redirectedConstructor == null
235 ? 'a redirecting constructor'
236 : 'a non-redirecting constructor');
237 }
238 }
239
240 void _compareElementLists(List expected, List actual) {
241 Set<Element> extraElements = new HashSet<Element>();
242 Map<Element, Element> commonElements = new HashMap<Element, Element>();
243
244 Map<String, Element> expectedElements = new HashMap<String, Element>();
245 for (Element expectedElement in expected) {
246 expectedElements[expectedElement.name] = expectedElement;
247 }
248 for (Element actualElement in actual) {
249 String name = actualElement.name;
250 Element expectedElement = expectedElements[name];
251 if (expectedElement == null) {
252 extraElements.add(actualElement);
253 } else {
254 commonElements[expectedElement] = actualElement;
255 expectedElements.remove(name);
256 }
257 }
258
259 commonElements.forEach((Element expected, Element actual) {
260 compareElements(expected, actual);
261 });
262 void writeElement(Element element) {
263 _write('an instance of ');
264 _write(element.runtimeType);
265 if (element.name == null) {
266 _write(' with no name');
267 } else {
268 _write(' named ');
269 _write(element.name);
270 }
271 }
272 expectedElements.forEach((String name, Element element) {
273 _write('Expected ');
274 writeElement(element);
275 _writeln('; found no match');
276 });
277 extraElements.forEach((Element element) {
278 _write('Expected nothing; found ');
279 writeElement(element);
280 });
281 }
282
283 void _compareExecutableElements(
284 ExecutableElement expected, ExecutableElement actual, String kind) {
285 _compareGenericElements(expected, actual);
286 //
287 // Compare attributes.
288 //
289 if (expected.hasImplicitReturnType != actual.hasImplicitReturnType) {
290 _writeMismatch(
291 expected,
292 actual,
293 (ExecutableElement element) => element.hasImplicitReturnType
294 ? 'an implicit return type'
295 : 'an explicit return type');
296 }
297 if (expected.isAbstract != actual.isAbstract) {
298 _writeMismatch(
299 expected,
300 actual,
301 (ExecutableElement element) =>
302 element.isAbstract ? 'an abstract $kind' : 'a concrete $kind');
303 }
304 if (expected.isAsynchronous != actual.isAsynchronous) {
305 _writeMismatch(
306 expected,
307 actual,
308 (ExecutableElement element) => element.isAsynchronous
309 ? 'an asynchronous $kind'
310 : 'a synchronous $kind');
311 }
312 if (expected.isExternal != actual.isExternal) {
313 _writeMismatch(
314 expected,
315 actual,
316 (ExecutableElement element) => element.isExternal
317 ? 'an external $kind'
318 : 'a non-external $kind');
319 }
320 if (expected.isGenerator != actual.isGenerator) {
321 _writeMismatch(
322 expected,
323 actual,
324 (ExecutableElement element) => element.isGenerator
325 ? 'a generator $kind'
326 : 'a non-generator $kind');
327 }
328 if (expected.isOperator != actual.isOperator) {
329 _writeMismatch(
330 expected,
331 actual,
332 (ExecutableElement element) =>
333 element.isOperator ? 'an operator' : 'a non-operator $kind');
334 }
335 if (expected.isStatic != actual.isStatic) {
336 _writeMismatch(
337 expected,
338 actual,
339 (ExecutableElement element) =>
340 element.isStatic ? 'a static $kind' : 'an instance $kind');
341 }
342 if ((expected.returnType == null) != (actual.returnType == null)) {
343 _writeMismatch(
344 expected,
345 actual,
346 (ExecutableElement element) => element.returnType == null
347 ? 'a $kind with no return type'
348 : 'a $kind with a return type');
349 } else {
350 _compareTypes('return type', expected.returnType, actual.returnType);
351 }
352 //
353 // Compare children.
354 //
355 _compareElementLists(expected.functions, actual.functions);
356 _compareElementLists(expected.labels, actual.labels);
357 _compareElementLists(expected.localVariables, actual.localVariables);
358 _compareElementLists(expected.parameters, actual.parameters);
359 _compareElementLists(expected.typeParameters, actual.typeParameters);
360 }
361
362 void _compareExportElements(ExportElement expected, ExportElement actual) {
363 _compareUriReferencedElements(expected, actual);
364 //
365 // Compare attributes.
366 //
367 if ((expected.exportedLibrary == null) !=
368 (actual.exportedLibrary == null)) {
369 // TODO(brianwilkerson) Check for more than existence?
370 _writeMismatch(
371 expected,
372 actual,
373 (ExportElement element) => element.exportedLibrary == null
374 ? 'unresolved uri'
375 : 'uri resolved to ${element.exportedLibrary.source.fullName}');
376 }
377 //
378 // Compare children.
379 //
380 _compareElementLists(expected.combinators, actual.combinators);
381 }
382
383 void _compareFieldElements(FieldElement expected, FieldElement actual) {
384 _comparePropertyInducingElements(expected, actual, 'field');
385 //
386 // Compare attributes.
387 //
388 if (expected.isEnumConstant != actual.isEnumConstant) {
389 _writeMismatch(
390 expected,
391 actual,
392 (FieldElement element) =>
393 element.isEnumConstant ? 'an enum constant' : 'a normal field');
394 }
395 }
396
397 void _compareFieldFormalParameterElements(
398 FieldFormalParameterElement expected,
399 FieldFormalParameterElement actual) {
400 // TODO(brianwilkerson) Implement this
401 _compareGenericElements(expected, actual);
402 }
403
404 void _compareFunctionElements(
405 FunctionElement expected, FunctionElement actual) {
406 // TODO(brianwilkerson) Implement this
407 _compareGenericElements(expected, actual);
408 }
409
410 void _compareFunctionTypeAliasElements(
411 FunctionTypeAliasElement expected, FunctionTypeAliasElement actual) {
412 // TODO(brianwilkerson) Implement this
413 _compareGenericElements(expected, actual);
414 }
415
416 void _compareGenericElements(Element expected, Element actual) {
417 _compareMetadata(expected.metadata, actual.metadata);
418 if (expected.nameOffset != actual.nameOffset) {
419 _write('Expected name offset of ');
420 _write(expected.nameOffset);
421 _write('; found ');
422 _writeln(actual.nameOffset);
423 }
424 SourceRange expectedRange = expected.docRange;
425 SourceRange actualRange = actual.docRange;
426 if (expectedRange.offset != actualRange.offset ||
427 expectedRange.length != actualRange.length) {
428 _write('Expected documentation range of ');
429 _write(expectedRange);
430 _write('; found ');
431 _writeln(actualRange);
432 }
433 }
434
435 void _compareImportElements(ImportElement expected, ImportElement actual) {
436 _compareUriReferencedElements(expected, actual);
437 //
438 // Compare attributes.
439 //
440 if (expected.isDeferred != actual.isDeferred) {
441 _writeMismatch(
442 expected,
443 actual,
444 (ImportElement element) => element.isDeferred
445 ? 'a deferred import'
446 : 'a non-deferred import');
447 }
448 if ((expected.importedLibrary == null) !=
449 (actual.importedLibrary == null)) {
450 _writeMismatch(
451 expected,
452 actual,
453 (ImportElement element) => element.importedLibrary == null
454 ? 'unresolved uri'
455 : 'uri resolved to ${element.importedLibrary.source.fullName}');
456 }
457 if ((expected.prefix == null) != (actual.prefix == null)) {
458 _writeMismatch(
459 expected,
460 actual,
461 (ImportElement element) => element.prefix == null
462 ? 'no prefix'
463 : 'a prefix named ${element.prefix.name}');
464 }
465 if (expected.prefixOffset != actual.prefixOffset) {
466 _write('Expected a prefix offset of ');
467 _write(expected.prefixOffset);
468 _write('; found ');
469 _writeln(actual.prefixOffset);
470 }
471 //
472 // Compare children.
473 //
474 _compareElementLists(expected.combinators, actual.combinators);
475 }
476
477 void _compareLabelElements(LabelElement expected, LabelElement actual) {
478 // TODO(brianwilkerson) Implement this
479 _compareGenericElements(expected, actual);
480 }
481
482 void _compareLibraryElements(LibraryElement expected, LibraryElement actual) {
483 _compareGenericElements(expected, actual);
484 //
485 // Compare attributes.
486 //
487 // TODO(brianwilkerson) Implement this
488 expected.hasLoadLibraryFunction;
489 expected.name;
490 expected.source;
491 //
492 // Compare children.
493 //
494 _compareElementLists(expected.imports, actual.imports);
495 _compareElementLists(expected.exports, actual.exports);
496 _compareElementLists(expected.units, actual.units);
497 }
498
499 void _compareLocalVariableElements(
500 LocalVariableElement expected, LocalVariableElement actual) {
501 // TODO(brianwilkerson) Implement this
502 _compareGenericElements(expected, actual);
503 }
504
505 void _compareMetadata(
506 List<ElementAnnotation> expected, List<ElementAnnotation> actual) {
507 // TODO(brianwilkerson) Implement this
508 }
509
510 void _compareMethodElements(MethodElement expected, MethodElement actual) {
511 // TODO(brianwilkerson) Implement this
512 _compareExecutableElements(expected, actual, 'method');
513 //
514 // Compare attributes.
515 //
516 if (expected.isStatic != actual.isStatic) {
517 _writeMismatch(
518 expected,
519 actual,
520 (FieldElement element) =>
521 element.isStatic ? 'a static field' : 'an instance field');
522 }
523 }
524
525 void _compareMultiplyDefinedElements(
526 MultiplyDefinedElement expected, MultiplyDefinedElement actual) {
527 // TODO(brianwilkerson) Implement this
528 }
529
530 void _compareParameterElements(
531 ParameterElement expected, ParameterElement actual) {
532 // TODO(brianwilkerson) Implement this
533 _compareGenericElements(expected, actual);
534 }
535
536 void _comparePrefixElements(PrefixElement expected, PrefixElement actual) {
537 // TODO(brianwilkerson) Implement this
538 _compareGenericElements(expected, actual);
539 }
540
541 void _comparePropertyAccessorElements(
542 PropertyAccessorElement expected, PropertyAccessorElement actual) {
543 // TODO(brianwilkerson) Implement this
544 _compareGenericElements(expected, actual);
545 }
546
547 void _comparePropertyInducingElements(PropertyInducingElement expected,
548 PropertyInducingElement actual, String kind) {
549 _compareVariableElements(expected, actual, kind);
550 }
551
552 void _compareTopLevelVariableElements(
553 TopLevelVariableElement expected, TopLevelVariableElement actual) {
554 // TODO(brianwilkerson) Implement this
555 _compareGenericElements(expected, actual);
556 }
557
558 void _compareTypeLists(String descriptor, List<InterfaceType> expected,
559 List<InterfaceType> actual) {
560 int expectedLength = expected.length;
561 if (expectedLength != actual.length) {
562 _write('Expected ');
563 _write(expectedLength);
564 _write(' ');
565 _write(descriptor);
566 _write('s; found ');
567 _write(actual.length);
568 } else {
569 for (int i = 0; i < expectedLength; i++) {
570 _compareTypes(descriptor, expected[i], actual[i]);
571 }
572 }
573 }
574
575 void _compareTypeParameterElements(
576 TypeParameterElement expected, TypeParameterElement actual) {
577 // TODO(brianwilkerson) Implement this
578 _compareGenericElements(expected, actual);
579 expected.bound;
580 }
581
582 void _compareTypes(String descriptor, DartType expected, DartType actual) {
583 void compareNames() {
584 if (expected.name != actual.name) {
585 _write('Expected a ');
586 _write(descriptor);
587 _write(' named ');
588 _write(expected.name);
589 _write('; found a ');
590 _write(descriptor);
591 _write(' named ');
592 _write(actual.name);
593 }
594 }
595 void compareTypeArguments(
596 ParameterizedType expected, ParameterizedType actual) {
597 List<DartType> expectedArguments = expected.typeArguments;
598 List<DartType> actualArguments = actual.typeArguments;
599 int expectedLength = expectedArguments.length;
600 if (expectedLength != actualArguments.length) {
601 _write('Expected ');
602 _write(expectedLength);
603 _write(' type arguments; found ');
604 _write(actualArguments.length);
605 } else {
606 for (int i = 0; i < expectedLength; i++) {
607 _compareTypes(
608 'type argument', expectedArguments[i], actualArguments[i]);
609 }
610 }
611 }
612
613 if (expected == null) {
614 if (actual != null) {
615 _write('Expected no ');
616 _write(descriptor);
617 _write('; found a ');
618 _write(descriptor);
619 _write(' named ');
620 _write(actual.name);
621 }
622 } else if (actual == null) {
623 _write('Expected a ');
624 _write(descriptor);
625 _write(' named ');
626 _write(expected.name);
627 _write('; found none');
628 } else if ((expected.isBottom && actual.isBottom) ||
629 (expected.isDynamic && actual.isDynamic) ||
630 (expected.isVoid && actual.isVoid)) {
631 // The types are the same
632 } else if (expected is InterfaceType && actual is InterfaceType) {
633 compareNames();
634 compareTypeArguments(expected, actual);
635 } else if (expected is FunctionType && actual is FunctionType) {
636 compareNames();
637 compareTypeArguments(expected, actual);
638 } else if (expected is TypeParameterType && actual is TypeParameterType) {
639 compareNames();
640 _compareTypes('bound', expected.element.bound, actual.element.bound);
641 } else {
642 _write('Expected an instance of ');
643 _write(expected.runtimeType);
644 _write(' named ');
645 _write(expected.name);
646 _write('; found an instance of ');
647 _writeln(actual.runtimeType);
648 _write(' named ');
649 _write(actual.name);
650 }
651 }
652
653 void _compareUriReferencedElements(
654 UriReferencedElement expected, UriReferencedElement actual) {
655 _compareGenericElements(expected, actual);
656 //
657 // Compare attributes.
658 //
659 if (expected.uri != actual.uri) {
660 _write('Expected a uri of ');
661 _write(expected.uri);
662 _write('; found ');
663 _writeln(actual.uri);
664 }
665 if (expected.uriOffset != actual.uriOffset) {
666 _write('Expected a uri offset of ');
667 _write(expected.uriOffset);
668 _write('; found ');
669 _writeln(actual.uriOffset);
670 }
671 }
672
673 void _compareVariableElements(
674 VariableElement expected, VariableElement actual, String kind) {
675 _compareGenericElements(expected, actual);
676 //
677 // Compare attributes.
678 //
679 if ((expected.constantValue == null) != (actual.constantValue == null)) {
680 // TODO(brianwilkerson) Check for more than existence.
681 _writeMismatch(
682 expected,
683 actual,
684 (VariableElement element) => element.constantValue == null
685 ? 'a $kind with no constant value'
686 : 'a $kind with a constant value');
687 }
688 if (expected.hasImplicitType != actual.hasImplicitType) {
689 _writeMismatch(
690 expected,
691 actual,
692 (VariableElement element) => element.hasImplicitType
693 ? 'a $kind with an implicit type'
694 : 'a $kind with an explicit type');
695 }
696 if (expected.isConst != actual.isConst) {
697 _writeMismatch(
698 expected,
699 actual,
700 (VariableElement element) =>
701 element.isConst ? 'a const $kind' : 'a non-const $kind');
702 }
703 if (expected.isFinal != actual.isFinal) {
704 _writeMismatch(
705 expected,
706 actual,
707 (VariableElement element) =>
708 element.isFinal ? 'a final $kind' : 'a non-final $kind');
709 }
710 if (expected.isPotentiallyMutatedInClosure !=
711 actual.isPotentiallyMutatedInClosure) {
712 _writeMismatch(
713 expected,
714 actual,
715 (VariableElement element) => element.isPotentiallyMutatedInClosure
716 ? 'a $kind that is potentially mutated in a closure'
717 : 'a $kind that is not mutated in a closure');
718 }
719 if (expected.isPotentiallyMutatedInScope !=
720 actual.isPotentiallyMutatedInScope) {
721 _writeMismatch(
722 expected,
723 actual,
724 (VariableElement element) => element.isPotentiallyMutatedInScope
725 ? 'a $kind that is potentially mutated in its scope'
726 : 'a $kind that is not mutated in its scope');
727 }
728 if (expected.isStatic != actual.isStatic) {
729 _writeMismatch(
730 expected,
731 actual,
732 (VariableElement element) =>
733 element.isStatic ? 'a static $kind' : 'an instance $kind');
734 }
735 //
736 // Compare children.
737 //
738 compareElements(expected.initializer, actual.initializer);
739 }
740
741 void _write(Object value) {
742 if (_needsLineBreak) {
743 _buffer.write('</p><p>');
744 _needsLineBreak = false;
745 }
746 _buffer.write(value);
747 }
748
749 void _writeln(Object value) {
750 _buffer.write(value);
751 _needsLineBreak = true;
752 }
753
754 /**
755 * Write a simple message explaining that the [expected] and [actual] values
756 * were different, using the [describe] function to describe the values.
757 */
758 void _writeMismatch /*<E>*/ (Object /*=E*/ expected, Object /*=E*/ actual,
759 String describe(Object /*=E*/ value)) {
760 _write('Expected ');
761 _write(describe(expected));
762 _write('; found ');
763 _writeln(describe(actual));
764 }
765 }
766
767 /**
768 * The comparison of two analyses of the same target.
769 */
770 class EntryComparison {
771 /**
772 * The target that was analyzed.
773 */
774 final AnalysisTarget target;
775
776 /**
777 * The cache entry from the original context.
778 */
779 final CacheEntry originalEntry;
780
781 /**
782 * The cache entry from the re-analysis in a cloned context.
783 */
784 final CacheEntry cloneEntry;
785
786 /**
787 * A flag indicating whether the target is obsolete. A target is obsolete if
788 * it is an element in an element model that was replaced at a some point.
789 */
790 bool obsoleteTarget = false;
791
792 /**
793 * A table mapping the results that were computed for the target to
794 * comparisons of the values of those results. The table only contains entries
795 * for results for which the comparison produced interesting data.
796 */
797 Map<ResultDescriptor, ResultComparison> resultMap =
798 new HashMap<ResultDescriptor, ResultComparison>();
799
800 /**
801 * Initialize a newly created comparison of the given [target]'s analysis,
802 * given the [originalEntry] from the original context and the [cloneEntry]
803 * from the cloned context.
804 */
805 EntryComparison(this.target, this.originalEntry, this.cloneEntry) {
806 _performComparison();
807 }
808
809 /**
810 * Return `true` if there is something interesting about the analysis of this
811 * target that should be reported.
812 */
813 bool hasInterestingState() => obsoleteTarget || resultMap.isNotEmpty;
814
815 /**
816 * Write an HTML formatted description of the validation results to the given
817 * [buffer].
818 */
819 void writeOn(StringBuffer buffer) {
820 buffer.write('<p>');
821 buffer.write(target);
822 buffer.write('</p>');
823 buffer.write('<blockquote>');
824 if (obsoleteTarget) {
825 buffer.write('<p><b>This target is obsolete.</b></p>');
826 }
827 List<ResultDescriptor> results = resultMap.keys.toList();
828 results.sort((ResultDescriptor first, ResultDescriptor second) =>
829 first.toString().compareTo(second.toString()));
830 for (ResultDescriptor result in results) {
831 resultMap[result].writeOn(buffer);
832 }
833 buffer.write('</blockquote>');
834 }
835
836 /**
837 * Compare all of the results that were computed in the two contexts, adding
838 * the interesting comparisons to the [resultMap].
839 */
840 void _compareResults() {
841 Set<ResultDescriptor> results = new Set<ResultDescriptor>();
842 results.addAll(originalEntry.nonInvalidResults);
843 results.addAll(cloneEntry.nonInvalidResults);
844
845 for (ResultDescriptor result in results) {
846 ResultComparison difference = new ResultComparison(this, result);
847 if (difference.hasInterestingState()) {
848 resultMap[result] = difference;
849 }
850 }
851 }
852
853 /**
854 * Return `true` if the target of this entry is an obsolete element.
855 */
856 bool _isTargetObsolete() {
857 if (target is Element) {
858 LibraryElement library = (target as Element).library;
859 AnalysisContextImpl context = library.context;
860 CacheEntry entry = context.analysisCache.get(library.source);
861 LibraryElement value = entry.getValue(LIBRARY_ELEMENT);
862 return value != library;
863 }
864 return false;
865 }
866
867 /**
868 * Determine whether or not there is any interesting difference between the
869 * original and cloned contexts.
870 */
871 void _performComparison() {
872 obsoleteTarget = _isTargetObsolete();
873 _compareResults();
874 }
875 }
876
877 /**
878 * The comparison of the value of a single result computed for a single target.
879 */
880 class ResultComparison {
881 /**
882 * The entry for the target for which the result was computed.
883 */
884 final EntryComparison entry;
885
886 /**
887 * The result that was computed for the target.
888 */
889 final ResultDescriptor result;
890
891 /**
892 * A flag indicating whether the state of the result is different.
893 */
894 bool differentStates = false;
895
896 /**
897 * The result of comparing the values of the results, or `null` if the states
898 * are different or if the values are the same.
899 */
900 ValueComparison valueComparison;
901
902 /**
903 * Initialize a newly created result comparison.
904 */
905 ResultComparison(this.entry, this.result) {
906 _performComparison();
907 }
908
909 /**
910 * Return `true` if this object represents a difference between the original
911 * and cloned contexts.
912 */
913 bool hasInterestingState() => differentStates || valueComparison != null;
914
915 /**
916 * Write an HTML formatted description of the validation results to the given
917 * [buffer].
918 */
919 void writeOn(StringBuffer buffer) {
920 buffer.write('<p>');
921 buffer.write(result);
922 buffer.write('</p>');
923 buffer.write('<blockquote>');
924 if (differentStates) {
925 CacheState originalState = entry.originalEntry.getState(result);
926 CacheState cloneState = entry.cloneEntry.getState(result);
927 buffer.write('<p>Original state = ');
928 buffer.write(originalState.name);
929 buffer.write('; clone state = ');
930 buffer.write(cloneState.name);
931 buffer.write('</p>');
932 }
933 if (valueComparison != null) {
934 valueComparison.writeOn(buffer);
935 }
936 buffer.write('</blockquote>');
937 }
938
939 /**
940 * Determine whether the state of the result is different between the
941 * original and cloned contexts.
942 */
943 bool _areStatesDifferent(CacheState originalState, CacheState cloneState) {
944 if (originalState == cloneState) {
945 return false;
946 } else if (originalState == CacheState.FLUSHED &&
947 cloneState == CacheState.VALID) {
948 return false;
949 } else if (originalState == CacheState.VALID &&
950 cloneState == CacheState.FLUSHED) {
951 return false;
952 }
953 return true;
954 }
955
956 /**
957 * Determine whether the value of the result is different between the
958 * original and cloned contexts.
959 */
960 void _compareValues(CacheState originalState, CacheState cloneState) {
961 if (originalState != cloneState || originalState != CacheState.VALID) {
962 return null;
963 }
964 ValueComparison comparison = new ValueComparison(
965 entry.originalEntry.getValue(result),
966 entry.cloneEntry.getValue(result));
967 if (comparison.hasInterestingState()) {
968 valueComparison = comparison;
969 }
970 }
971
972 /**
973 * Determine whether or not there is any interesting difference between the
974 * original and cloned contexts.
975 */
976 void _performComparison() {
977 CacheState originalState = entry.originalEntry.getState(result);
978 CacheState cloneState = entry.cloneEntry.getState(result);
979 if (_areStatesDifferent(originalState, cloneState)) {
980 differentStates = true;
981 _compareValues(originalState, cloneState);
982 }
983 }
984 }
985
986 /**
987 * The results of validating an analysis context.
988 *
989 * Validation is done by re-analyzing all of the explicitly added source in a
990 * new analysis context that is configured to be the same as the original
991 * context.
992 */
993 class ValidationResults {
994 /**
995 * A set of targets that were in the original context that were not included
996 * in the re-created context.
997 */
998 Set<AnalysisTarget> extraTargets;
999
1000 /**
1001 * A set of targets that were in the re-created context that were not included
1002 * in the original context.
1003 */
1004 Set<AnalysisTarget> missingTargets;
1005
1006 /**
1007 * A table, keyed by targets, whose values are comparisons of the analysis of
1008 * those targets. The table only contains entries for targets for which the
1009 * comparison produced interesting data.
1010 */
1011 Map<AnalysisTarget, EntryComparison> targetMap =
1012 new HashMap<AnalysisTarget, EntryComparison>();
1013
1014 /**
1015 * Initialize a newly created validation result by validating the given
1016 * [context].
1017 */
1018 ValidationResults(AnalysisContextImpl context) {
1019 _validate(context);
1020 }
1021
1022 /**
1023 * Write an HTML formatted description of the validation results to the given
1024 * [buffer].
1025 */
1026 void writeOn(StringBuffer buffer) {
1027 if (extraTargets.isEmpty && missingTargets.isEmpty && targetMap.isEmpty) {
1028 buffer.write('<p>No interesting results.</p>');
1029 return;
1030 }
1031 if (extraTargets.isNotEmpty) {
1032 buffer.write('<h4>Extra Targets</h4>');
1033 buffer.write('<p style="commentary">');
1034 buffer.write('Targets that exist in the original context that were not ');
1035 buffer.write('re-created in the cloned context.');
1036 buffer.write('</p>');
1037 _writeTargetList(buffer, extraTargets.toList());
1038 }
1039 if (missingTargets.isNotEmpty) {
1040 buffer.write('<h4>Missing Targets</h4>');
1041 buffer.write('<p style="commentary">');
1042 buffer.write('Targets that do <b>not</b> exist in the original context ');
1043 buffer.write('but do exist in the cloned context.');
1044 buffer.write('</p>');
1045 _writeTargetList(buffer, missingTargets.toList());
1046 }
1047 if (targetMap.isNotEmpty) {
1048 buffer.write('<h4>Differing Targets</h4>');
1049 // TODO(brianwilkerson) Sort the list of targets.
1050 for (EntryComparison comparison in targetMap.values) {
1051 comparison.writeOn(buffer);
1052 }
1053 }
1054 }
1055
1056 /**
1057 * Analyze all of the explicit sources in the given [context].
1058 */
1059 void _analyze(AnalysisContextImpl context) {
1060 while (true) {
1061 AnalysisResult result = context.performAnalysisTask();
1062 if (!result.hasMoreWork) {
1063 return;
1064 }
1065 }
1066 }
1067
1068 /**
1069 * Create and return a new analysis context that will analyze files in the
1070 * same way as the given [context].
1071 */
1072 AnalysisContextImpl _clone(AnalysisContextImpl context) {
1073 AnalysisContextImpl clone = AnalysisEngine.instance.createAnalysisContext();
1074
1075 clone.analysisOptions = context.analysisOptions;
1076 //clone.declaredVariables = context.declaredVariables;
1077 clone.sourceFactory = context.sourceFactory.clone();
1078 // TODO(brianwilkerson) Check content cache. We either need to copy the
1079 // cache into the clone or ensure that the context's cache is empty.
1080
1081 ChangeSet changeSet = new ChangeSet();
1082 for (AnalysisTarget target in context.explicitTargets) {
1083 if (target is Source) {
1084 changeSet.addedSource(target);
1085 }
1086 }
1087 clone.applyChanges(changeSet);
1088 return clone;
1089 }
1090
1091 /**
1092 * Compare the results produced in the [original] context to those produced in
1093 * the [clone].
1094 */
1095 void _compareContexts(
1096 AnalysisContextImpl original, AnalysisContextImpl clone) {
1097 AnalysisCache originalCache = original.analysisCache;
1098 AnalysisCache cloneCache = clone.analysisCache;
1099 List<AnalysisTarget> originalTargets = _getKeys(original, originalCache);
1100 List<AnalysisTarget> cloneTargets = _getKeys(clone, cloneCache);
1101
1102 extraTargets =
1103 new HashSet<AnalysisTarget>(equals: _equal, hashCode: _hashCode);
1104 extraTargets.addAll(originalTargets);
1105 extraTargets.removeAll(cloneTargets);
1106
1107 missingTargets =
1108 new HashSet<AnalysisTarget>(equals: _equal, hashCode: _hashCode);
1109 missingTargets.addAll(cloneTargets);
1110 missingTargets.removeAll(originalTargets);
1111
1112 for (AnalysisTarget cloneTarget in cloneTargets) {
1113 if (!missingTargets.contains(cloneTarget)) {
1114 AnalysisTarget originalTarget = _find(originalTargets, cloneTarget);
1115 CacheEntry originalEntry = originalCache.get(originalTarget);
1116 CacheEntry cloneEntry = cloneCache.get(cloneTarget);
1117 EntryComparison comparison =
1118 new EntryComparison(cloneTarget, originalEntry, cloneEntry);
1119 if (comparison.hasInterestingState()) {
1120 targetMap[cloneTarget] = comparison;
1121 }
1122 }
1123 }
1124 }
1125
1126 /**
1127 * Find the target in the list of [originalTargets] that is equal to the
1128 * [cloneTarget].
1129 */
1130 AnalysisTarget _find(
1131 List<AnalysisTarget> originalTargets, AnalysisTarget cloneTarget) {
1132 for (AnalysisTarget originalTarget in originalTargets) {
1133 if (_equal(originalTarget, cloneTarget)) {
1134 return originalTarget;
1135 }
1136 }
1137 return null;
1138 }
1139
1140 /**
1141 * Return a list of the analysis targets in the given [cache] that are owned
1142 * by the given [context].
1143 */
1144 List<AnalysisTarget> _getKeys(
1145 AnalysisContextImpl context, AnalysisCache cache) {
1146 List<AnalysisTarget> targets = <AnalysisTarget>[];
1147 MapIterator<AnalysisTarget, CacheEntry> iterator =
1148 cache.iterator(context: context);
1149 while (iterator.moveNext()) {
1150 targets.add(iterator.key);
1151 }
1152 return targets;
1153 }
1154
1155 /**
1156 * Validate the given [context].
1157 */
1158 void _validate(AnalysisContextImpl context) {
1159 AnalysisContextImpl clone = _clone(context);
1160 _analyze(clone);
1161 _compareContexts(context, clone);
1162 }
1163
1164 /**
1165 * Write the list of [targets] to the [buffer].
1166 */
1167 void _writeTargetList(StringBuffer buffer, List<AnalysisTarget> targets) {
1168 // TODO(brianwilkerson) Sort the list of targets.
1169 //targets.sort();
1170 for (AnalysisTarget target in targets) {
1171 buffer.write('<p>');
1172 buffer.write(target);
1173 buffer.write(' (');
1174 buffer.write(target.runtimeType);
1175 buffer.write(')');
1176 buffer.write('</p>');
1177 }
1178 }
1179
1180 /**
1181 * Return `true` if the [first] and [second] objects are equal.
1182 */
1183 static bool _equal(Object first, Object second) {
1184 //
1185 // Compare possible null values.
1186 //
1187 if (first == null) {
1188 return second == null;
1189 } else if (second == null) {
1190 return false;
1191 }
1192 //
1193 // Handle special cases.
1194 //
1195 if (first is ConstantEvaluationTarget_Annotation &&
1196 second is ConstantEvaluationTarget_Annotation) {
1197 return _equal(first.source, second.source) &&
1198 _equal(first.librarySource, second.librarySource) &&
1199 _equal(first.annotation, second.annotation);
1200 } else if (first is AstNode && second is AstNode) {
1201 return first.runtimeType == second.runtimeType &&
1202 first.offset == second.offset &&
1203 first.length == second.length;
1204 }
1205 //
1206 // Handle the general case.
1207 //
1208 return first == second;
1209 }
1210
1211 /**
1212 * Return a hash code for the given [object].
1213 */
1214 static int _hashCode(Object object) {
1215 //
1216 // Handle special cases.
1217 //
1218 if (object is ConstantEvaluationTarget_Annotation) {
1219 return object.source.hashCode;
1220 } else if (object is AstNode) {
1221 return object.offset;
1222 }
1223 //
1224 // Handle the general case.
1225 //
1226 return object.hashCode;
1227 }
1228 }
1229
1230 class ValueComparison {
1231 /**
1232 * The result value from the original context.
1233 */
1234 final Object originalValue;
1235
1236 /**
1237 * The result value from the cloned context.
1238 */
1239 final Object cloneValue;
1240
1241 /**
1242 * A description of the difference between the original and clone values, or
1243 * `null` if the values are equal.
1244 */
1245 String description = null;
1246
1247 /**
1248 * Initialize a newly created value comparison to represents the difference,
1249 * if any, between the [originalValue] and the [cloneValue].
1250 */
1251 ValueComparison(this.originalValue, this.cloneValue) {
1252 _performComparison();
1253 }
1254
1255 /**
1256 * Return `true` if this object represents a difference between the original
1257 * and cloned values.
1258 */
1259 bool hasInterestingState() => description != null;
1260
1261 /**
1262 * Write an HTML formatted description of the validation results to the given
1263 * [buffer].
1264 */
1265 void writeOn(StringBuffer buffer) {
1266 buffer.write('<p>');
1267 buffer.write(description);
1268 buffer.write('</p>');
1269 }
1270
1271 bool _compareAnalysisErrors(
1272 AnalysisError expected, AnalysisError actual, StringBuffer buffer) {
1273 if (actual.errorCode == expected.errorCode &&
1274 actual.source == expected.source &&
1275 actual.offset == expected.offset) {
1276 return true;
1277 }
1278 if (buffer != null) {
1279 void write(AnalysisError originalError) {
1280 buffer.write('a ');
1281 buffer.write(originalError.errorCode.uniqueName);
1282 buffer.write(' in ');
1283 buffer.write(originalError.source.fullName);
1284 buffer.write(' at ');
1285 buffer.write(originalError.offset);
1286 }
1287
1288 buffer.write('Expected ');
1289 write(expected);
1290 buffer.write('; found ');
1291 write(actual);
1292 }
1293 return false;
1294 }
1295
1296 bool _compareAstNodes(AstNode expected, AstNode actual, StringBuffer buffer) {
1297 if (AstComparator.equalNodes(actual, expected)) {
1298 return true;
1299 }
1300 if (buffer != null) {
1301 // TODO(brianwilkerson) Compute where the difference is rather than just
1302 // whether there is a difference.
1303 buffer.write('Different AST nodes');
1304 }
1305 return false;
1306 }
1307
1308 bool _compareConstantEvaluationTargets(ConstantEvaluationTarget expected,
1309 ConstantEvaluationTarget actual, StringBuffer buffer) {
1310 if (actual is ConstantEvaluationTarget_Annotation) {
1311 ConstantEvaluationTarget_Annotation expectedAnnotation = expected;
1312 ConstantEvaluationTarget_Annotation actualAnnotation = actual;
1313 if (actualAnnotation.source == expectedAnnotation.source &&
1314 actualAnnotation.librarySource == expectedAnnotation.librarySource &&
1315 actualAnnotation.annotation == expectedAnnotation.annotation) {
1316 return true;
1317 }
1318 if (buffer != null) {
1319 void write(ConstantEvaluationTarget_Annotation target) {
1320 Annotation annotation = target.annotation;
1321 buffer.write(annotation);
1322 buffer.write(' at ');
1323 buffer.write(annotation.offset);
1324 buffer.write(' in ');
1325 buffer.write(target.source);
1326 buffer.write(' in ');
1327 buffer.write(target.librarySource);
1328 }
1329
1330 buffer.write('Expected ');
1331 write(expectedAnnotation);
1332 buffer.write('; found ');
1333 write(actualAnnotation);
1334 }
1335 return false;
1336 }
1337 if (buffer != null) {
1338 buffer.write('Unknown class of ConstantEvaluationTarget: ');
1339 buffer.write(actual.runtimeType);
1340 }
1341 return false;
1342 }
1343
1344 bool _compareDartScripts(
1345 DartScript expected, DartScript actual, StringBuffer buffer) {
1346 // TODO(brianwilkerson) Implement this.
1347 return true;
1348 }
1349
1350 bool _compareDocuments(
1351 html.Document expected, html.Document actual, StringBuffer buffer) {
1352 // TODO(brianwilkerson) Implement this.
1353 return true;
1354 }
1355
1356 bool _compareElements(Element expected, Element actual, StringBuffer buffer) {
1357 ElementComparator comparator = new ElementComparator();
1358 comparator.compareElements(expected, actual);
1359 if (comparator.hasDifference) {
1360 if (buffer != null) {
1361 buffer.write(comparator.description);
1362 }
1363 return false;
1364 }
1365 return true;
1366 }
1367
1368 bool _compareLibrarySpecificUnits(LibrarySpecificUnit expected,
1369 LibrarySpecificUnit actual, StringBuffer buffer) {
1370 if (actual.library.fullName == expected.library.fullName &&
1371 actual.unit.fullName == expected.unit.fullName) {
1372 return true;
1373 }
1374 if (buffer != null) {
1375 buffer.write('Expected ');
1376 buffer.write(expected);
1377 buffer.write('; found ');
1378 buffer.write(actual);
1379 }
1380 return false;
1381 }
1382
1383 bool _compareLineInfos(
1384 LineInfo expected, LineInfo actual, StringBuffer buffer) {
1385 // TODO(brianwilkerson) Implement this.
1386 return true;
1387 }
1388
1389 bool _compareLists(List expected, List actual, StringBuffer buffer) {
1390 int expectedLength = expected.length;
1391 int actualLength = actual.length;
1392 int left = 0;
1393 while (left < expectedLength &&
1394 left < actualLength &&
1395 _compareObjects(expected[left], actual[left], null)) {
1396 left++;
1397 }
1398 if (left == actualLength) {
1399 if (left == expectedLength) {
1400 // The lists are the same length and the elements are equal.
1401 return true;
1402 }
1403 if (buffer != null) {
1404 buffer.write('Expected a list of length ');
1405 buffer.write(expectedLength);
1406 buffer.write('; found a list of length ');
1407 buffer.write(actualLength);
1408 buffer.write(' that was a prefix of the expected list');
1409 }
1410 return false;
1411 } else if (left == expectedLength) {
1412 if (buffer != null) {
1413 buffer.write('Expected a list of length ');
1414 buffer.write(expectedLength);
1415 buffer.write('; found a list of length ');
1416 buffer.write(actualLength);
1417 buffer.write(' that was an extension of the expected list');
1418 }
1419 return false;
1420 }
1421 int expectedRight = expectedLength - 1;
1422 int actualRight = actualLength - 1;
1423 while (expectedRight > left &&
1424 actualRight > left &&
1425 _compareObjects(expected[expectedRight], actual[actualRight], null)) {
1426 actualRight--;
1427 expectedRight--;
1428 }
1429 if (buffer != null) {
1430 void write(int left, int right, int length) {
1431 buffer.write('the elements (');
1432 buffer.write(left);
1433 buffer.write('..');
1434 buffer.write(right);
1435 buffer.write(') in a list of length ');
1436 buffer.write(length);
1437 }
1438
1439 buffer.write('Expected ');
1440 write(left, expectedRight, expectedLength);
1441 buffer.write(' to match ');
1442 write(left, actualRight, actualLength);
1443 buffer.write(' but they did not');
1444 }
1445 return false;
1446 }
1447
1448 /**
1449 * Return `true` if the [expected] and [actual] objects are equal. If they are
1450 * not equal, and the given [buffer] is not `null`, then a description of the
1451 * difference will be written to the [buffer].
1452 */
1453 bool _compareObjects(Object expected, Object actual, StringBuffer buffer) {
1454 //
1455 // Compare possible null values.
1456 //
1457 if (actual == null) {
1458 if (expected == null) {
1459 return true;
1460 } else {
1461 if (buffer != null) {
1462 buffer.write('Expected an instance of ');
1463 buffer.write(expected.runtimeType);
1464 buffer.write('; found null');
1465 }
1466 return false;
1467 }
1468 }
1469 Type actualType = actual.runtimeType;
1470 if (expected == null) {
1471 if (buffer != null) {
1472 buffer.write('Expected null; found an instance of ');
1473 buffer.write(actualType);
1474 }
1475 return false;
1476 }
1477 Type expectedType = expected.runtimeType;
1478 //
1479 // Compare the types.
1480 //
1481 if (expectedType != actualType) {
1482 if (buffer != null) {
1483 buffer.write('Expected an instance of ');
1484 buffer.write(expectedType);
1485 buffer.write('; found an instance of ');
1486 buffer.write(actualType);
1487 }
1488 return false;
1489 }
1490 //
1491 // Compare non-null values of the same type.
1492 //
1493 if (actual is bool) {
1494 return _comparePrimitives(expected, actual, buffer);
1495 } else if (actual is int) {
1496 return _comparePrimitives(expected, actual, buffer);
1497 } else if (actual is String) {
1498 return _compareStrings(expected, actual, buffer);
1499 } else if (actual is List) {
1500 return _compareLists(expected, actual, buffer);
1501 } else if (actual is AnalysisError) {
1502 return _compareAnalysisErrors(expected, actual, buffer);
1503 } else if (actual is AstNode) {
1504 return _compareAstNodes(expected, actual, buffer);
1505 } else if (actual is DartScript) {
1506 return _compareDartScripts(expected, actual, buffer);
1507 } else if (actual is html.Document) {
1508 return _compareDocuments(expected, actual, buffer);
1509 } else if (actual is Element) {
1510 return _compareElements(expected, actual, buffer);
1511 } else if (actual is LibrarySpecificUnit) {
1512 return _compareLibrarySpecificUnits(expected, actual, buffer);
1513 } else if (actual is LineInfo) {
1514 return _compareLineInfos(expected, actual, buffer);
1515 } else if (actual is ReferencedNames) {
1516 return _compareReferencedNames(expected, actual, buffer);
1517 } else if (actual is Source) {
1518 return _compareSources(expected, actual, buffer);
1519 } else if (actual is SourceKind) {
1520 return _comparePrimitives(expected, actual, buffer);
1521 } else if (actual is Token) {
1522 return _compareTokenStreams(expected, actual, buffer);
1523 } else if (actual is TypeProvider) {
1524 return true;
1525 } else if (actual is UsedLocalElements) {
1526 return _compareUsedLocalElements(expected, actual, buffer);
1527 } else if (actual is UsedImportedElements) {
1528 return _compareUsedImportedElements(expected, actual, buffer);
1529 } else if (actual is ConstantEvaluationTarget) {
1530 return _compareConstantEvaluationTargets(expected, actual, buffer);
1531 }
1532 if (buffer != null) {
1533 buffer.write('Cannot compare values of type ');
1534 buffer.write(actualType);
1535 }
1536 return false;
1537 }
1538
1539 bool _comparePrimitives(Object expected, Object actual, StringBuffer buffer) {
1540 if (actual == expected) {
1541 return true;
1542 }
1543 if (buffer != null) {
1544 buffer.write('Expected ');
1545 buffer.write(expected);
1546 buffer.write('; found ');
1547 buffer.write(actual);
1548 }
1549 return false;
1550 }
1551
1552 bool _compareReferencedNames(
1553 ReferencedNames expected, ReferencedNames actual, StringBuffer buffer) {
1554 Set<String> expectedNames = expected.names;
1555 Map<String, Set<String>> expectedUserToDependsOn = expected.userToDependsOn;
1556 Set<String> expectedKeys = expectedUserToDependsOn.keys.toSet();
1557
1558 Set<String> actualNames = actual.names;
1559 Map<String, Set<String>> actualUserToDependsOn = actual.userToDependsOn;
1560 Set<String> actualKeys = actualUserToDependsOn.keys.toSet();
1561
1562 Set<String> missingNames = expectedNames.difference(actualNames);
1563 Set<String> extraNames = actualNames.difference(expectedNames);
1564 Set<String> missingKeys = expectedKeys.difference(actualKeys);
1565 Set<String> extraKeys = actualKeys.difference(expectedKeys);
1566 Map<String, List<Set<String>>> mismatchedDependencies =
1567 new HashMap<String, List<Set<String>>>();
1568 Set<String> commonKeys = expectedKeys.intersection(actualKeys);
1569 for (String key in commonKeys) {
1570 Set<String> expectedDependencies = expectedUserToDependsOn[key];
1571 Set<String> actualDependencies = actualUserToDependsOn[key];
1572 Set<String> missingDependencies =
1573 expectedDependencies.difference(actualDependencies);
1574 Set<String> extraDependencies =
1575 actualDependencies.difference(expectedDependencies);
1576 if (missingDependencies.isNotEmpty || extraDependencies.isNotEmpty) {
1577 mismatchedDependencies[key] = [missingDependencies, extraDependencies];
1578 }
1579 }
1580
1581 if (missingNames.isEmpty &&
1582 extraNames.isEmpty &&
1583 missingKeys.isEmpty &&
1584 extraKeys.isEmpty &&
1585 mismatchedDependencies.isEmpty) {
1586 return true;
1587 }
1588 if (buffer != null) {
1589 void write(String title, Set<String> names) {
1590 buffer.write(names.length);
1591 buffer.write(' ');
1592 buffer.write(title);
1593 buffer.write(': {');
1594 bool first = true;
1595 for (String name in names) {
1596 if (first) {
1597 first = false;
1598 } else {
1599 buffer.write(', ');
1600 }
1601 buffer.write(name);
1602 }
1603 buffer.write('}');
1604 }
1605 bool needsNewline = false;
1606 if (missingNames.isNotEmpty) {
1607 buffer.write('Has ');
1608 write('missing names', missingNames);
1609 needsNewline = true;
1610 }
1611 if (extraNames.isNotEmpty) {
1612 if (needsNewline) {
1613 buffer.write('</p><p>');
1614 }
1615 buffer.write('Has ');
1616 write('extra names', extraNames);
1617 needsNewline = true;
1618 }
1619 if (missingKeys.isNotEmpty) {
1620 if (needsNewline) {
1621 buffer.write('</p><p>');
1622 }
1623 buffer.write('Has ');
1624 write('missing keys', missingKeys);
1625 needsNewline = true;
1626 }
1627 if (extraKeys.isNotEmpty) {
1628 if (needsNewline) {
1629 buffer.write('</p><p>');
1630 }
1631 buffer.write('Has ');
1632 write('extra keys', extraKeys);
1633 needsNewline = true;
1634 }
1635 mismatchedDependencies.forEach((String key, List<Set<String>> value) {
1636 Set<String> missingDependencies = value[0];
1637 Set<String> extraDependencies = value[1];
1638 if (needsNewline) {
1639 buffer.write('</p><p>');
1640 }
1641 buffer.write('The key ');
1642 buffer.write(key);
1643 buffer.write(' has ');
1644 bool needsConjunction = false;
1645 if (missingNames.isNotEmpty) {
1646 write('missing dependencies', missingDependencies);
1647 needsConjunction = true;
1648 }
1649 if (extraNames.isNotEmpty) {
1650 if (needsConjunction) {
1651 buffer.write(' and ');
1652 }
1653 write('extra dependencies', extraDependencies);
1654 }
1655 needsNewline = true;
1656 });
1657 }
1658 return true;
1659 }
1660
1661 bool _compareSources(Source expected, Source actual, StringBuffer buffer) {
1662 if (actual.fullName == expected.fullName) {
1663 return true;
1664 }
1665 if (buffer != null) {
1666 buffer.write('Expected a source for ');
1667 buffer.write(expected.fullName);
1668 buffer.write('; found a source for ');
1669 buffer.write(actual.fullName);
1670 }
1671 return false;
1672 }
1673
1674 bool _compareStrings(String expected, String actual, StringBuffer buffer) {
1675 if (actual == expected) {
1676 return true;
1677 }
1678 int expectedLength = expected.length;
1679 int actualLength = actual.length;
1680 int left = 0;
1681 while (left < actualLength &&
1682 left < expectedLength &&
1683 actual.codeUnitAt(left) == expected.codeUnitAt(left)) {
1684 left++;
1685 }
1686 if (left == actualLength) {
1687 if (buffer != null) {
1688 buffer.write('Expected ...[');
1689 buffer.write(expected.substring(left));
1690 buffer.write(']; found ...[]');
1691 }
1692 return false;
1693 } else if (left == expectedLength) {
1694 if (buffer != null) {
1695 buffer.write('Expected ...[]; found ...[');
1696 buffer.write(actual.substring(left));
1697 buffer.write(']');
1698 }
1699 return false;
1700 }
1701 int actualRight = actualLength - 1;
1702 int expectedRight = expectedLength - 1;
1703 while (actualRight > left &&
1704 expectedRight > left &&
1705 actual.codeUnitAt(actualRight) == expected.codeUnitAt(expectedRight)) {
1706 actualRight--;
1707 expectedRight--;
1708 }
1709 if (buffer != null) {
1710 void write(String string, int left, int right) {
1711 buffer.write('...[');
1712 buffer.write(string.substring(left, right));
1713 buffer.write(']... (');
1714 buffer.write(left);
1715 buffer.write('..');
1716 buffer.write(right);
1717 buffer.write(')');
1718 }
1719
1720 buffer.write('Expected ');
1721 write(expected, left, expectedRight);
1722 buffer.write('; found ');
1723 write(actual, left, actualRight);
1724 }
1725 return false;
1726 }
1727
1728 bool _compareTokenStreams(Token expected, Token actual, StringBuffer buffer) {
1729 bool equals(Token originalToken, Token cloneToken) {
1730 return originalToken.type == cloneToken.type &&
1731 originalToken.offset == cloneToken.offset &&
1732 originalToken.lexeme == cloneToken.lexeme;
1733 }
1734
1735 Token actualLeft = actual;
1736 Token expectedLeft = expected;
1737 while (actualLeft.type != TokenType.EOF &&
1738 expectedLeft.type != TokenType.EOF &&
1739 equals(actualLeft, expectedLeft)) {
1740 actualLeft = actualLeft.next;
1741 expectedLeft = expectedLeft.next;
1742 }
1743 if (actualLeft.type == TokenType.EOF &&
1744 expectedLeft.type == TokenType.EOF) {
1745 return true;
1746 }
1747 if (buffer != null) {
1748 void write(Token token) {
1749 buffer.write(token.type);
1750 buffer.write(' at ');
1751 buffer.write(token.offset);
1752 buffer.write(' (');
1753 buffer.write(token.lexeme);
1754 buffer.write(')');
1755 }
1756
1757 buffer.write('Expected ');
1758 write(expectedLeft);
1759 buffer.write('; found ');
1760 write(actualLeft);
1761 }
1762 return false;
1763 }
1764
1765 bool _compareUsedImportedElements(UsedImportedElements expected,
1766 UsedImportedElements actual, StringBuffer buffer) {
1767 // TODO(brianwilkerson) Implement this.
1768 return true;
1769 }
1770
1771 bool _compareUsedLocalElements(UsedLocalElements expected,
1772 UsedLocalElements actual, StringBuffer buffer) {
1773 // TODO(brianwilkerson) Implement this.
1774 return true;
1775 }
1776
1777 /**
1778 * Determine whether or not there is any interesting difference between the
1779 * original and cloned values.
1780 */
1781 void _performComparison() {
1782 StringBuffer buffer = new StringBuffer();
1783 if (!_compareObjects(cloneValue, originalValue, buffer)) {
1784 description = buffer.toString();
1785 }
1786 }
1787 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698