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

Side by Side Diff: tests/compiler/dart2js/serialization/test_helper.dart

Issue 2879593004: Reorganize equivalence test helpers (Closed)
Patch Set: Created 3 years, 7 months 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
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library dart2js.serialization_test_helper; 5 library dart2js.serialization_test_helper;
6 6
7 import 'dart:collection'; 7 import 'dart:collection';
8 import 'package:compiler/src/common/resolution.dart'; 8 import 'package:compiler/src/common/resolution.dart';
9 import 'package:compiler/src/constants/expressions.dart'; 9 import 'package:compiler/src/constants/expressions.dart';
10 import 'package:compiler/src/constants/values.dart'; 10 import 'package:compiler/src/constants/values.dart';
11 import 'package:compiler/src/compiler.dart'; 11 import 'package:compiler/src/compiler.dart';
12 import 'package:compiler/src/elements/elements.dart'; 12 import 'package:compiler/src/elements/elements.dart';
13 import 'package:compiler/src/elements/entities.dart'; 13 import 'package:compiler/src/elements/entities.dart';
14 import 'package:compiler/src/elements/resolution_types.dart'; 14 import 'package:compiler/src/elements/resolution_types.dart';
15 import 'package:compiler/src/elements/types.dart'; 15 import 'package:compiler/src/elements/types.dart';
16 import 'package:compiler/src/kernel/elements.dart'; 16 import 'package:compiler/src/kernel/elements.dart';
17 import 'package:compiler/src/kernel/element_map_impl.dart'; 17 import 'package:compiler/src/kernel/element_map_impl.dart';
18 import 'package:compiler/src/serialization/equivalence.dart'; 18 import 'package:compiler/src/serialization/equivalence.dart';
19 import 'package:compiler/src/util/util.dart'; 19 import 'package:compiler/src/util/util.dart';
20 import 'package:expect/expect.dart'; 20 import 'package:expect/expect.dart';
21 import 'test_data.dart'; 21 import 'test_data.dart';
22 22
23 Check currentCheck;
24
25 class Check {
26 final Check parent;
27 final Object object1;
28 final Object object2;
29 final String property;
30 final Object value1;
31 final Object value2;
32
33 Check(this.parent, this.object1, this.object2, this.property, this.value1,
34 this.value2);
35
36 String printOn(StringBuffer sb, String indent) {
37 if (parent != null) {
38 indent = parent.printOn(sb, indent);
39 sb.write('\n$indent|\n');
40 }
41 sb.write("${indent}property='$property'\n ");
42 sb.write("${indent}object1=$object1 (${object1.runtimeType})\n ");
43 sb.write("${indent}value=${value1 == null ? "null" : "'$value1'"} ");
44 sb.write("(${value1.runtimeType}) vs\n ");
45 sb.write("${indent}object2=$object2 (${object2.runtimeType})\n ");
46 sb.write("${indent}value=${value2 == null ? "null" : "'$value2'"} ");
47 sb.write("(${value2.runtimeType})");
48 return ' $indent';
49 }
50
51 String toString() {
52 StringBuffer sb = new StringBuffer();
53 printOn(sb, '');
54 return sb.toString();
55 }
56 }
57
58 /// Strategy for checking equivalence.
59 ///
60 /// Use this strategy to fail early with contextual information in the event of
61 /// inequivalence.
62 class CheckStrategy extends TestStrategy {
63 const CheckStrategy(
64 {Equivalence<Entity> elementEquivalence: areElementsEquivalent,
65 Equivalence<DartType> typeEquivalence: areTypesEquivalent,
66 Equivalence<ConstantExpression> constantEquivalence:
67 areConstantsEquivalent,
68 Equivalence<ConstantValue> constantValueEquivalence:
69 areConstantValuesEquivalent})
70 : super(
71 elementEquivalence: elementEquivalence,
72 typeEquivalence: typeEquivalence,
73 constantEquivalence: constantEquivalence,
74 constantValueEquivalence: constantValueEquivalence);
75
76 TestStrategy get testOnly => new TestStrategy(
77 elementEquivalence: elementEquivalence,
78 typeEquivalence: typeEquivalence,
79 constantEquivalence: constantEquivalence,
80 constantValueEquivalence: constantValueEquivalence);
81
82 @override
83 bool test(var object1, var object2, String property, var value1, var value2,
84 [bool equivalence(a, b) = equality]) {
85 return check(object1, object2, property, value1, value2, equivalence);
86 }
87
88 @override
89 bool testLists(
90 Object object1, Object object2, String property, List list1, List list2,
91 [bool elementEquivalence(a, b) = equality]) {
92 return checkListEquivalence(object1, object2, property, list1, list2,
93 (o1, o2, p, v1, v2) {
94 if (!elementEquivalence(v1, v2)) {
95 throw "$o1.$p = '${v1}' <> "
96 "$o2.$p = '${v2}'";
97 }
98 return true;
99 });
100 }
101
102 @override
103 bool testSets(
104 var object1, var object2, String property, Iterable set1, Iterable set2,
105 [bool elementEquivalence(a, b) = equality]) {
106 return checkSetEquivalence(
107 object1, object2, property, set1, set2, elementEquivalence);
108 }
109
110 @override
111 bool testMaps(var object1, var object2, String property, Map map1, Map map2,
112 [bool keyEquivalence(a, b) = equality,
113 bool valueEquivalence(a, b) = equality]) {
114 return checkMapEquivalence(object1, object2, property, map1, map2,
115 keyEquivalence, valueEquivalence);
116 }
117 }
118
119 /// Check that the values [property] of [object1] and [object2], [value1] and
120 /// [value2] respectively, are equal and throw otherwise.
121 bool check(var object1, var object2, String property, var value1, var value2,
122 [bool equivalence(a, b) = equality]) {
123 currentCheck =
124 new Check(currentCheck, object1, object2, property, value1, value2);
125 if (!equivalence(value1, value2)) {
126 throw currentCheck;
127 }
128 currentCheck = currentCheck.parent;
129 return true;
130 }
131
132 /// Check equivalence of the two lists, [list1] and [list2], using
133 /// [checkEquivalence] to check the pair-wise equivalence.
134 ///
135 /// Uses [object1], [object2] and [property] to provide context for failures.
136 bool checkListEquivalence(
137 Object object1,
138 Object object2,
139 String property,
140 Iterable list1,
141 Iterable list2,
142 void checkEquivalence(o1, o2, property, a, b)) {
143 currentCheck =
144 new Check(currentCheck, object1, object2, property, list1, list2);
145 for (int i = 0; i < list1.length && i < list2.length; i++) {
146 checkEquivalence(
147 object1, object2, property, list1.elementAt(i), list2.elementAt(i));
148 }
149 for (int i = list1.length; i < list2.length; i++) {
150 throw 'Missing equivalent for element '
151 '#$i ${list2.elementAt(i)} in `${property}` on $object2.\n'
152 '`${property}` on $object1:\n ${list1.join('\n ')}\n'
153 '`${property}` on $object2:\n ${list2.join('\n ')}';
154 }
155 for (int i = list2.length; i < list1.length; i++) {
156 throw 'Missing equivalent for element '
157 '#$i ${list1.elementAt(i)} in `${property}` on $object1.\n'
158 '`${property}` on $object1:\n ${list1.join('\n ')}\n'
159 '`${property}` on $object2:\n ${list2.join('\n ')}';
160 }
161 currentCheck = currentCheck.parent;
162 return true;
163 }
164
165 /// Computes the set difference between [set1] and [set2] using
166 /// [elementEquivalence] to determine element equivalence.
167 ///
168 /// Elements both in [set1] and [set2] are added to [common], elements in [set1]
169 /// but not in [set2] are added to [unfound], and the set of elements in [set2]
170 /// but not in [set1] are returned.
171 Set computeSetDifference(
172 Iterable set1, Iterable set2, List<List> common, List unfound,
173 {bool sameElement(a, b): equality, void checkElements(a, b)}) {
174 // TODO(johnniwinther): Avoid the quadratic cost here. Some ideas:
175 // - convert each set to a list and sort it first, then compare by walking
176 // both lists in parallel
177 // - map each element to a canonical object, create a map containing those
178 // mappings, use the mapped sets to compare (then operations like
179 // set.difference would work)
180 Set remaining = set2.toSet();
181 for (var element1 in set1) {
182 bool found = false;
183 var correspondingElement;
184 for (var element2 in remaining) {
185 if (sameElement(element1, element2)) {
186 if (checkElements != null) {
187 checkElements(element1, element2);
188 }
189 found = true;
190 correspondingElement = element2;
191 remaining.remove(element2);
192 break;
193 }
194 }
195 if (found) {
196 common.add([element1, correspondingElement]);
197 } else {
198 unfound.add(element1);
199 }
200 }
201 return remaining;
202 }
203
204 /// Check equivalence of the two iterables, [set1] and [set1], as sets using
205 /// [elementEquivalence] to compute the pair-wise equivalence.
206 ///
207 /// Uses [object1], [object2] and [property] to provide context for failures.
208 bool checkSetEquivalence(var object1, var object2, String property,
209 Iterable set1, Iterable set2, bool sameElement(a, b),
210 {void onSameElement(a, b)}) {
211 List<List> common = <List>[];
212 List unfound = [];
213 Set remaining = computeSetDifference(set1, set2, common, unfound,
214 sameElement: sameElement, checkElements: onSameElement);
215 if (unfound.isNotEmpty || remaining.isNotEmpty) {
216 String message = "Set mismatch for `$property` on\n"
217 "$object1\n vs\n$object2:\n"
218 "Common:\n ${common.join('\n ')}\n"
219 "Unfound:\n ${unfound.join('\n ')}\n"
220 "Extra: \n ${remaining.join('\n ')}";
221 throw message;
222 }
223 return true;
224 }
225
226 /// Check equivalence of the two iterables, [set1] and [set1], as sets using
227 /// [elementEquivalence] to compute the pair-wise equivalence.
228 ///
229 /// Uses [object1], [object2] and [property] to provide context for failures.
230 bool checkMapEquivalence(var object1, var object2, String property, Map map1,
231 Map map2, bool sameKey(a, b), bool sameValue(a, b),
232 {bool allowExtra: false}) {
233 List<List> common = <List>[];
234 List unfound = [];
235 Set extra = computeSetDifference(map1.keys, map2.keys, common, unfound,
236 sameElement: sameKey);
237 if (unfound.isNotEmpty || (!allowExtra && extra.isNotEmpty)) {
238 String message =
239 "Map key mismatch for `$property` on $object1 vs $object2: \n"
240 "Common:\n ${common.join('\n ')}\n"
241 "Unfound:\n ${unfound.join('\n ')}\n"
242 "Extra: \n ${extra.join('\n ')}";
243 throw message;
244 }
245 for (List pair in common) {
246 check(pair[0], pair[1], 'Map value for `$property`', map1[pair[0]],
247 map2[pair[1]], sameValue);
248 }
249 return true;
250 }
251
252 /// Checks the equivalence of the identity (but not properties) of [element1]
253 /// and [element2].
254 ///
255 /// Uses [object1], [object2] and [property] to provide context for failures.
256 bool checkElementIdentities(Object object1, Object object2, String property,
257 Element element1, Element element2) {
258 if (identical(element1, element2)) return true;
259 return check(
260 object1, object2, property, element1, element2, areElementsEquivalent);
261 }
262
263 /// Checks the pair-wise equivalence of the identity (but not properties) of the
264 /// elements in [list] and [list2].
265 ///
266 /// Uses [object1], [object2] and [property] to provide context for failures.
267 bool checkElementListIdentities(Object object1, Object object2, String property,
268 Iterable<Element> list1, Iterable<Element> list2) {
269 return checkListEquivalence(
270 object1, object2, property, list1, list2, checkElementIdentities);
271 }
272
273 /// Checks the equivalence of [type1] and [type2].
274 ///
275 /// Uses [object1], [object2] and [property] to provide context for failures.
276 bool checkTypes(Object object1, Object object2, String property,
277 ResolutionDartType type1, ResolutionDartType type2) {
278 if (identical(type1, type2)) return true;
279 if (type1 == null || type2 == null) {
280 return check(object1, object2, property, type1, type2);
281 } else {
282 return check(object1, object2, property, type1, type2,
283 (a, b) => const TypeEquivalence(const CheckStrategy()).visit(a, b));
284 }
285 }
286
287 /// Checks the pair-wise equivalence of the types in [list1] and [list2].
288 ///
289 /// Uses [object1], [object2] and [property] to provide context for failures.
290 bool checkTypeLists(Object object1, Object object2, String property,
291 List<DartType> list1, List<DartType> list2) {
292 return checkListEquivalence(
293 object1, object2, property, list1, list2, checkTypes);
294 }
295
296 /// Checks the equivalence of [exp1] and [exp2].
297 ///
298 /// Uses [object1], [object2] and [property] to provide context for failures.
299 bool checkConstants(Object object1, Object object2, String property,
300 ConstantExpression exp1, ConstantExpression exp2) {
301 if (identical(exp1, exp2)) return true;
302 if (exp1 == null || exp2 == null) {
303 return check(object1, object2, property, exp1, exp2);
304 } else {
305 return check(object1, object2, property, exp1, exp2,
306 (a, b) => const ConstantEquivalence(const CheckStrategy()).visit(a, b));
307 }
308 }
309
310 /// Checks the equivalence of [value1] and [value2].
311 ///
312 /// Uses [object1], [object2] and [property] to provide context for failures.
313 bool checkConstantValues(Object object1, Object object2, String property,
314 ConstantValue value1, ConstantValue value2) {
315 if (identical(value1, value2)) return true;
316 if (value1 == null || value2 == null) {
317 return check(object1, object2, property, value1, value2);
318 } else {
319 return check(
320 object1,
321 object2,
322 property,
323 value1,
324 value2,
325 (a, b) =>
326 const ConstantValueEquivalence(const CheckStrategy()).visit(a, b));
327 }
328 }
329
330 /// Checks the pair-wise equivalence of the constants in [list1] and [list2].
331 ///
332 /// Uses [object1], [object2] and [property] to provide context for failures.
333 bool checkConstantLists(Object object1, Object object2, String property,
334 List<ConstantExpression> list1, List<ConstantExpression> list2) {
335 return checkListEquivalence(
336 object1, object2, property, list1, list2, checkConstants);
337 }
338
339 /// Checks the pair-wise equivalence of the constants values in [list1] and
340 /// [list2].
341 ///
342 /// Uses [object1], [object2] and [property] to provide context for failures.
343 bool checkConstantValueLists(Object object1, Object object2, String property,
344 List<ConstantValue> list1, List<ConstantValue> list2) {
345 return checkListEquivalence(
346 object1, object2, property, list1, list2, checkConstantValues);
347 }
348
349 /// Check member property equivalence between all members common to [compiler1]
350 /// and [compiler2].
351 void checkLoadedLibraryMembers(
352 Compiler compiler1,
353 Compiler compiler2,
354 bool hasProperty(Element member1),
355 void checkMemberProperties(Compiler compiler1, Element member1,
356 Compiler compiler2, Element member2,
357 {bool verbose}),
358 {bool verbose: false}) {
359 void checkMembers(Element member1, Element member2) {
360 if (member1.isClass && member2.isClass) {
361 ClassElement class1 = member1;
362 ClassElement class2 = member2;
363 if (!class1.isResolved) return;
364
365 if (hasProperty(member1)) {
366 if (areElementsEquivalent(member1, member2)) {
367 checkMemberProperties(compiler1, member1, compiler2, member2,
368 verbose: verbose);
369 }
370 }
371
372 class1.forEachLocalMember((m1) {
373 checkMembers(m1, class2.localLookup(m1.name));
374 });
375 ClassElement superclass1 = class1.superclass;
376 ClassElement superclass2 = class2.superclass;
377 while (superclass1 != null && superclass1.isUnnamedMixinApplication) {
378 for (ConstructorElement c1 in superclass1.constructors) {
379 checkMembers(c1, superclass2.lookupConstructor(c1.name));
380 }
381 superclass1 = superclass1.superclass;
382 superclass2 = superclass2.superclass;
383 }
384 return;
385 }
386
387 if (!hasProperty(member1)) {
388 return;
389 }
390
391 if (member2 == null) {
392 throw 'Missing member for ${member1}';
393 }
394
395 if (areElementsEquivalent(member1, member2)) {
396 checkMemberProperties(compiler1, member1, compiler2, member2,
397 verbose: verbose);
398 }
399 }
400
401 for (LibraryElement library1 in compiler1.libraryLoader.libraries) {
402 LibraryElement library2 =
403 compiler2.libraryLoader.lookupLibrary(library1.canonicalUri);
404 if (library2 != null) {
405 library1.forEachLocalMember((Element member1) {
406 checkMembers(member1, library2.localLookup(member1.name));
407 });
408 }
409 }
410 }
411
412 /// Check equivalence of all resolution impacts.
413 void checkAllImpacts(Compiler compiler1, Compiler compiler2,
414 {bool verbose: false}) {
415 checkLoadedLibraryMembers(compiler1, compiler2, (Element member1) {
416 return compiler1.resolution.hasResolutionImpact(member1);
417 }, checkImpacts, verbose: verbose);
418 }
419
420 /// Check equivalence of resolution impact for [member1] and [member2].
421 void checkImpacts(
422 Compiler compiler1, Element member1, Compiler compiler2, Element member2,
423 {bool verbose: false}) {
424 ResolutionImpact impact1 = compiler1.resolution.getResolutionImpact(member1);
425 ResolutionImpact impact2 = compiler2.resolution.getResolutionImpact(member2);
426
427 if (impact1 == null && impact2 == null) return;
428
429 if (verbose) {
430 print('Checking impacts for $member1 vs $member2');
431 }
432
433 if (impact1 == null) {
434 throw 'Missing impact for $member1. $member2 has $impact2';
435 }
436 if (impact2 == null) {
437 throw 'Missing impact for $member2. $member1 has $impact1';
438 }
439
440 testResolutionImpactEquivalence(impact1, impact2,
441 strategy: const CheckStrategy());
442 }
443
444 void checkSets(
445 Iterable set1, Iterable set2, String messagePrefix, bool sameElement(a, b),
446 {bool failOnUnfound: true,
447 bool failOnExtra: true,
448 bool verbose: false,
449 void onSameElement(a, b),
450 void onUnfoundElement(a),
451 void onExtraElement(b),
452 bool elementFilter(element),
453 elementConverter(element),
454 String elementToString(key): defaultToString}) {
455 if (elementFilter != null) {
456 set1 = set1.where(elementFilter);
457 set2 = set2.where(elementFilter);
458 }
459 if (elementConverter != null) {
460 set1 = set1.map(elementConverter);
461 set2 = set2.map(elementConverter);
462 }
463 List<List> common = <List>[];
464 List unfound = [];
465 Set remaining = computeSetDifference(set1, set2, common, unfound,
466 sameElement: sameElement, checkElements: onSameElement);
467 if (onUnfoundElement != null) {
468 unfound.forEach(onUnfoundElement);
469 }
470 if (onExtraElement != null) {
471 remaining.forEach(onExtraElement);
472 }
473 StringBuffer sb = new StringBuffer();
474 sb.write("$messagePrefix:");
475 if (verbose) {
476 sb.write("\n Common: \n");
477 for (List pair in common) {
478 var element1 = pair[0];
479 var element2 = pair[1];
480 sb.write(" [${elementToString(element1)},"
481 "${elementToString(element2)}]\n");
482 }
483 }
484 if (unfound.isNotEmpty || verbose) {
485 sb.write("\n Unfound:\n ${unfound.map(elementToString).join('\n ')}");
486 }
487 if (remaining.isNotEmpty || verbose) {
488 sb.write("\n Extra: \n ${remaining.map(elementToString).join('\n ')}");
489 }
490 String message = sb.toString();
491 if (unfound.isNotEmpty || remaining.isNotEmpty) {
492 if ((failOnUnfound && unfound.isNotEmpty) ||
493 (failOnExtra && remaining.isNotEmpty)) {
494 Expect.fail(message);
495 } else {
496 print(message);
497 }
498 } else if (verbose) {
499 print(message);
500 }
501 }
502
503 String defaultToString(obj) => '$obj';
504
505 void checkMaps(Map map1, Map map2, String messagePrefix, bool sameKey(a, b),
506 bool sameValue(a, b),
507 {bool failOnUnfound: true,
508 bool failOnMismatch: true,
509 bool verbose: false,
510 String keyToString(key): defaultToString,
511 String valueToString(key): defaultToString}) {
512 List<List> common = <List>[];
513 List unfound = [];
514 List<List> mismatch = <List>[];
515 Set remaining = computeSetDifference(map1.keys, map2.keys, common, unfound,
516 sameElement: sameKey, checkElements: (k1, k2) {
517 var v1 = map1[k1];
518 var v2 = map2[k2];
519 if (!sameValue(v1, v2)) {
520 mismatch.add([k1, k2]);
521 }
522 });
523 StringBuffer sb = new StringBuffer();
524 sb.write("$messagePrefix:");
525 if (verbose) {
526 sb.write("\n Common: \n");
527 for (List pair in common) {
528 var k1 = pair[0];
529 var k2 = pair[1];
530 var v1 = map1[k1];
531 var v2 = map2[k2];
532 sb.write(" key1 =${keyToString(k1)}\n");
533 sb.write(" key2 =${keyToString(k2)}\n");
534 sb.write(" value1=${valueToString(v1)}\n");
535 sb.write(" value2=${valueToString(v2)}\n");
536 }
537 }
538 if (unfound.isNotEmpty || verbose) {
539 sb.write("\n Unfound: \n");
540 for (var k1 in unfound) {
541 var v1 = map1[k1];
542 sb.write(" key1 =${keyToString(k1)}\n");
543 sb.write(" value1=${valueToString(v1)}\n");
544 }
545 }
546 if (remaining.isNotEmpty || verbose) {
547 sb.write("\n Extra: \n");
548 for (var k2 in remaining) {
549 var v2 = map2[k2];
550 sb.write(" key2 =${keyToString(k2)}\n");
551 sb.write(" value2=${valueToString(v2)}\n");
552 }
553 }
554 if (mismatch.isNotEmpty || verbose) {
555 sb.write("\n Mismatch: \n");
556 for (List pair in mismatch) {
557 var k1 = pair[0];
558 var k2 = pair[1];
559 var v1 = map1[k1];
560 var v2 = map2[k2];
561 sb.write(" key1 =${keyToString(k1)}\n");
562 sb.write(" key2 =${keyToString(k2)}\n");
563 sb.write(" value1=${valueToString(v1)}\n");
564 sb.write(" value2=${valueToString(v2)}\n");
565 }
566 }
567 String message = sb.toString();
568 if (unfound.isNotEmpty || mismatch.isNotEmpty || remaining.isNotEmpty) {
569 if ((unfound.isNotEmpty && failOnUnfound) ||
570 (mismatch.isNotEmpty && failOnMismatch) ||
571 remaining.isNotEmpty) {
572 Expect.fail(message);
573 } else {
574 print(message);
575 }
576 } else if (verbose) {
577 print(message);
578 }
579 }
580
581 void checkAllResolvedAsts(Compiler compiler1, Compiler compiler2,
582 {bool verbose: false}) {
583 checkLoadedLibraryMembers(compiler1, compiler2, (Element member1) {
584 return member1 is ExecutableElement &&
585 compiler1.resolution.hasResolvedAst(member1);
586 }, checkResolvedAsts, verbose: verbose);
587 }
588
589 /// Check equivalence of [impact1] and [impact2].
590 void checkResolvedAsts(
591 Compiler compiler1, Element member1, Compiler compiler2, Element member2,
592 {bool verbose: false}) {
593 if (!compiler2.serialization.isDeserialized(member2)) {
594 return;
595 }
596 ResolvedAst resolvedAst1 = compiler1.resolution.getResolvedAst(member1);
597 ResolvedAst resolvedAst2 = compiler2.serialization.getResolvedAst(member2);
598
599 if (resolvedAst1 == null || resolvedAst2 == null) return;
600
601 if (verbose) {
602 print('Checking resolved asts for $member1 vs $member2');
603 }
604
605 testResolvedAstEquivalence(resolvedAst1, resolvedAst2, const CheckStrategy());
606 }
607
608 /// Returns the test arguments for testing the [index]th skipped test. The 23 /// Returns the test arguments for testing the [index]th skipped test. The
609 /// [skip] count is used to check that [index] is a valid index. 24 /// [skip] count is used to check that [index] is a valid index.
610 List<String> testSkipped(int index, int skip) { 25 List<String> testSkipped(int index, int skip) {
611 if (index < 0 || index >= skip) { 26 if (index < 0 || index >= skip) {
612 throw new ArgumentError('Invalid skip index $index'); 27 throw new ArgumentError('Invalid skip index $index');
613 } 28 }
614 return ['${index}', '${index + 1}']; 29 return ['${index}', '${index + 1}'];
615 } 30 }
616 31
617 /// Return the test arguments for testing the [index]th segment (1-based) of 32 /// Return the test arguments for testing the [index]th segment (1-based) of
618 /// the [TESTS] split into [count] groups. The first [skip] tests are excluded 33 /// the [TESTS] split into [count] groups. The first [skip] tests are excluded
619 /// from the automatic grouping. 34 /// from the automatic grouping.
620 List<String> testSegment(int index, int count, int skip) { 35 List<String> testSegment(int index, int count, int skip) {
621 if (index < 0 || index > count) { 36 if (index < 0 || index > count) {
622 throw new ArgumentError('Invalid segment index $index'); 37 throw new ArgumentError('Invalid segment index $index');
623 } 38 }
624 39
625 String segmentNumber(int i) { 40 String segmentNumber(int i) {
626 return '${skip + i * (TESTS.length - skip) ~/ count}'; 41 return '${skip + i * (TESTS.length - skip) ~/ count}';
627 } 42 }
628 43
629 if (index == 1 && skip != 0) { 44 if (index == 1 && skip != 0) {
630 return ['${skip}', segmentNumber(index)]; 45 return ['${skip}', segmentNumber(index)];
631 } else if (index == count) { 46 } else if (index == count) {
632 return [segmentNumber(index - 1)]; 47 return [segmentNumber(index - 1)];
633 } else { 48 } else {
634 return [segmentNumber(index - 1), segmentNumber(index)]; 49 return [segmentNumber(index - 1), segmentNumber(index)];
635 } 50 }
636 } 51 }
637
638 class KernelEquivalence {
639 final WorldDeconstructionForTesting testing;
640
641 /// Set of mixin applications assumed to be equivalent.
642 ///
643 /// We need co-inductive reasoning because mixin applications are compared
644 /// structurally and therefore, in the case of generic mixin applications,
645 /// meet themselves through the equivalence check of their type variables.
646 Set<Pair<ClassEntity, ClassEntity>> assumedMixinApplications =
647 new Set<Pair<ClassEntity, ClassEntity>>();
648
649 KernelEquivalence(KernelToElementMapImpl builder)
650 : testing = new WorldDeconstructionForTesting(builder);
651
652 TestStrategy get defaultStrategy => new TestStrategy(
653 elementEquivalence: entityEquivalence,
654 typeEquivalence: typeEquivalence,
655 constantEquivalence: constantEquivalence,
656 constantValueEquivalence: constantValueEquivalence);
657
658 bool entityEquivalence(Element a, Entity b, {TestStrategy strategy}) {
659 if (identical(a, b)) return true;
660 if (a == null || b == null) return false;
661 strategy ??= defaultStrategy;
662 switch (a.kind) {
663 case ElementKind.GENERATIVE_CONSTRUCTOR:
664 if (b is KGenerativeConstructor) {
665 return strategy.test(a, b, 'name', a.name, b.name) &&
666 strategy.testElements(
667 a, b, 'enclosingClass', a.enclosingClass, b.enclosingClass);
668 }
669 return false;
670 case ElementKind.FACTORY_CONSTRUCTOR:
671 if (b is KFactoryConstructor) {
672 return strategy.test(a, b, 'name', a.name, b.name) &&
673 strategy.testElements(
674 a, b, 'enclosingClass', a.enclosingClass, b.enclosingClass);
675 }
676 return false;
677 case ElementKind.CLASS:
678 if (b is KClass) {
679 List<InterfaceType> aMixinTypes = [];
680 List<InterfaceType> bMixinTypes = [];
681 ClassElement aClass = a;
682 if (aClass.isUnnamedMixinApplication) {
683 if (!testing.isUnnamedMixinApplication(b)) {
684 return false;
685 }
686 while (aClass.isMixinApplication) {
687 MixinApplicationElement aMixinApplication = aClass;
688 aMixinTypes.add(aMixinApplication.mixinType);
689 aClass = aMixinApplication.superclass;
690 }
691 KClass bClass = b;
692 while (bClass != null) {
693 InterfaceType mixinType = testing.getMixinTypeForClass(bClass);
694 if (mixinType == null) break;
695 bMixinTypes.add(mixinType);
696 bClass = testing.getSuperclassForClass(bClass);
697 }
698 if (aMixinTypes.isNotEmpty || aMixinTypes.isNotEmpty) {
699 Pair<ClassEntity, ClassEntity> pair =
700 new Pair<ClassEntity, ClassEntity>(aClass, bClass);
701 if (assumedMixinApplications.contains(pair)) {
702 return true;
703 } else {
704 assumedMixinApplications.add(pair);
705 bool result = strategy.testTypeLists(
706 a, b, 'mixinTypes', aMixinTypes, bMixinTypes);
707 assumedMixinApplications.remove(pair);
708 return result;
709 }
710 }
711 } else {
712 if (testing.isUnnamedMixinApplication(b)) {
713 return false;
714 }
715 }
716 return strategy.test(a, b, 'name', a.name, b.name) &&
717 strategy.testElements(a, b, 'library', a.library, b.library);
718 }
719 return false;
720 case ElementKind.LIBRARY:
721 if (b is KLibrary) {
722 LibraryElement libraryA = a;
723 return libraryA.canonicalUri == b.canonicalUri;
724 }
725 return false;
726 case ElementKind.FUNCTION:
727 if (b is KMethod) {
728 return strategy.test(a, b, 'name', a.name, b.name) &&
729 strategy.testElements(
730 a, b, 'enclosingClass', a.enclosingClass, b.enclosingClass) &&
731 strategy.testElements(a, b, 'library', a.library, b.library);
732 } else if (b is KLocalFunction) {
733 LocalFunctionElement aLocalFunction = a;
734 return strategy.test(a, b, 'name', a.name, b.name ?? '') &&
735 strategy.testElements(a, b, 'executableContext',
736 aLocalFunction.executableContext, b.executableContext) &&
737 strategy.testElements(a, b, 'memberContext',
738 aLocalFunction.memberContext, b.memberContext);
739 }
740 return false;
741 case ElementKind.GETTER:
742 if (b is KGetter) {
743 return strategy.test(a, b, 'name', a.name, b.name) &&
744 strategy.testElements(
745 a, b, 'enclosingClass', a.enclosingClass, b.enclosingClass) &&
746 strategy.testElements(a, b, 'library', a.library, b.library);
747 }
748 return false;
749 case ElementKind.SETTER:
750 if (b is KSetter) {
751 return strategy.test(a, b, 'name', a.name, b.name) &&
752 strategy.testElements(
753 a, b, 'enclosingClass', a.enclosingClass, b.enclosingClass) &&
754 strategy.testElements(a, b, 'library', a.library, b.library);
755 }
756 return false;
757 case ElementKind.FIELD:
758 if (b is KField) {
759 return strategy.test(a, b, 'name', a.name, b.name) &&
760 strategy.testElements(
761 a, b, 'enclosingClass', a.enclosingClass, b.enclosingClass) &&
762 strategy.testElements(a, b, 'library', a.library, b.library);
763 }
764 return false;
765 case ElementKind.TYPE_VARIABLE:
766 if (b is KTypeVariable) {
767 TypeVariableElement aElement = a;
768 return strategy.test(a, b, 'index', aElement.index, b.index) &&
769 strategy.testElements(a, b, 'typeDeclaration',
770 aElement.typeDeclaration, b.typeDeclaration);
771 }
772 return false;
773 default:
774 throw new UnsupportedError('Unsupported equivalence: '
775 '$a (${a.runtimeType}) vs $b (${b.runtimeType})');
776 }
777 }
778
779 bool typeEquivalence(ResolutionDartType a, DartType b,
780 {TestStrategy strategy}) {
781 if (identical(a, b)) return true;
782 if (a == null || b == null) return false;
783 strategy ??= defaultStrategy;
784 switch (a.kind) {
785 case ResolutionTypeKind.DYNAMIC:
786 return b is DynamicType;
787 case ResolutionTypeKind.VOID:
788 return b is VoidType;
789 case ResolutionTypeKind.INTERFACE:
790 if (b is InterfaceType) {
791 ResolutionInterfaceType aType = a;
792 return strategy.testElements(a, b, 'element', a.element, b.element) &&
793 strategy.testTypeLists(
794 a, b, 'typeArguments', aType.typeArguments, b.typeArguments);
795 }
796 return false;
797 case ResolutionTypeKind.TYPE_VARIABLE:
798 if (b is TypeVariableType) {
799 return strategy.testElements(a, b, 'element', a.element, b.element);
800 }
801 return false;
802 case ResolutionTypeKind.FUNCTION:
803 if (b is FunctionType) {
804 ResolutionFunctionType aType = a;
805 return strategy.testTypes(
806 a, b, 'returnType', aType.returnType, b.returnType) &&
807 strategy.testTypeLists(a, b, 'parameterTypes',
808 aType.parameterTypes, b.parameterTypes) &&
809 strategy.testTypeLists(a, b, 'optionalParameterTypes',
810 aType.optionalParameterTypes, b.optionalParameterTypes) &&
811 strategy.testLists(a, b, 'namedParameters', aType.namedParameters,
812 b.namedParameters) &&
813 strategy.testTypeLists(a, b, 'namedParameterTypes',
814 aType.namedParameterTypes, b.namedParameterTypes);
815 }
816 return false;
817 default:
818 throw new UnsupportedError('Unsupported equivalence: '
819 '$a (${a.runtimeType}) vs $b (${b.runtimeType})');
820 }
821 }
822
823 bool constantEquivalence(ConstantExpression exp1, ConstantExpression exp2,
824 {TestStrategy strategy}) {
825 strategy ??= defaultStrategy;
826 return areConstantsEquivalent(exp1, exp2, strategy: strategy);
827 }
828
829 bool constantValueEquivalence(ConstantValue value1, ConstantValue value2,
830 {TestStrategy strategy}) {
831 strategy ??= defaultStrategy;
832 return areConstantValuesEquivalent(value1, value2, strategy: strategy);
833 }
834 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698