OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 import 'package:kernel/type_algebra.dart'; | |
5 import 'package:test/test.dart'; | |
6 import 'type_parser.dart'; | |
7 import 'dart:io'; | |
8 | |
9 final List<TestCase> testCases = <TestCase>[ | |
10 successCase('List<T>', 'List<String>', {'T': 'String'}), | |
11 successCase('List<String>', 'List<T>', {'T': 'String'}), | |
12 successCase('List<T>', 'List<T>', {'T': null}), | |
13 successCase('List<S>', 'List<T>', {'S': 'T'}), | |
14 successCase('List<S>', 'List<T>', {'T': 'S'}), | |
15 successCase( | |
16 'List<S>', 'List<T>', {'S': 'T', 'T': null}), // Require left bias. | |
17 failureCase('List<S>', 'List<T>', []), | |
18 | |
19 failureCase('List<T>', 'T', ['T']), | |
20 failureCase('List<List<T>>', 'List<T>', ['T']), | |
21 failureCase('Map<S, T>', 'Map<List<T>, List<S>>', ['T', 'S']), | |
22 | |
23 failureCase('Map<Map<S,String>, Map<int,S>>', | |
24 'Map<Map<int, S>, Map<S, String>>', ['S']), | |
25 successCase('Map<Map<S, int>, Map<int, S>>', 'Map<Map<int, S>, Map<S, int>>', | |
26 {'S': 'int'}), | |
27 successCase('Map<Map<S, String>, Map<int, T>>', | |
28 'Map<Map<int, T>, Map<S, String>>', {'S': 'int', 'T': 'String'}), | |
29 | |
30 successCase('Map<S, List<T>>', 'Map<T, List<S>>', {'S': 'T'}), | |
31 successCase('Map<S, T>', 'Map<S, List<S>>', {'T': 'List<S>'}), | |
32 successCase('Map<S, T>', 'Map<S, List<S>>', {'T': 'List<S>', 'S': null}), | |
33 successCase('Map<List<S>, T>', 'Map<T, List<S>>', {'T': 'List<S>'}), | |
34 successCase( | |
35 'Map<List<S>, T>', 'Map<T, List<S>>', {'T': 'List<S>', 'S': null}), | |
36 | |
37 successCase('<E>(E) => E', '<T>(T) => T', {}), | |
38 successCase('<E>(E, S) => E', '<T>(T, int) => T', {'S': 'int'}), | |
39 failureCase('<E>(E, S) => E', '<T>(T, T) => T', ['S']), | |
40 successCase( | |
41 '<E>(E) => <T>(T) => Map<E,T>', '<E>(E) => <T>(T) => Map<E,T>', {}), | |
42 successCase('<E>(E,_) => E', '<T>(T,_) => T', {}), | |
43 | |
44 successCase('(x:int,y:String) => int', '(y:String,x:int) => int', {}), | |
45 successCase('<S,T>(x:S,y:T) => S', '<S,T>(y:T,x:S) => S', {}), | |
46 successCase('(x:<T>(T)=>T,y:<S>(S)=>S) => int', '(y:<S>(S)=>S,x:<T>(T)=>T) =>
int', {}), | |
47 successCase('(x:<T>(T)=>T,y:<S>(S,S,S)=>S) => int', '(y:<S>(S,S,S)=>S,x:<T>(T)
=>T) => int', {}), | |
48 ]; | |
49 | |
50 class TestCase { | |
51 String type1; | |
52 String type2; | |
53 Iterable<String> quantifiedVariables; | |
54 Map<String, String> expectedSubstitution; // Null if unification should fail. | |
55 | |
56 TestCase.success(this.type1, this.type2, this.expectedSubstitution) { | |
57 quantifiedVariables = expectedSubstitution.keys; | |
58 } | |
59 | |
60 TestCase.fail(this.type1, this.type2, this.quantifiedVariables); | |
61 | |
62 bool get shouldSucceed => expectedSubstitution != null; | |
63 | |
64 String toString() => '∃ ${quantifiedVariables.join(',')}. $type1 = $type2'; | |
65 } | |
66 | |
67 TestCase successCase(String type1, String type2, Map<String, String> expected, | |
68 {bool debug: false}) { | |
69 return new TestCase.success(type1, type2, expected); | |
70 } | |
71 | |
72 TestCase failureCase( | |
73 String type1, String type2, List<String> quantifiedVariables, | |
74 {bool debug: false}) { | |
75 return new TestCase.fail(type1, type2, quantifiedVariables); | |
76 } | |
77 | |
78 int numFailures = 0; | |
79 | |
80 void reportFailure(TestCase testCase, String message) { | |
81 ++numFailures; | |
82 fail('$message in `$testCase`'); | |
83 } | |
84 | |
85 main() { | |
86 for (TestCase testCase in testCases) { | |
87 test('$testCase', () { | |
88 var env = new LazyTypeEnvironment(); | |
89 var type1 = env.parse(testCase.type1); | |
90 var type2 = env.parse(testCase.type2); | |
91 var quantifiedVariables = | |
92 testCase.quantifiedVariables.map(env.getTypeParameter).toSet(); | |
93 var substitution = unifyTypes(type1, type2, quantifiedVariables); | |
94 if (testCase.shouldSucceed) { | |
95 if (substitution == null) { | |
96 reportFailure(testCase, 'Unification failed'); | |
97 } else { | |
98 for (var key in testCase.expectedSubstitution.keys) { | |
99 var typeParameter = env.getTypeParameter(key); | |
100 if (testCase.expectedSubstitution[key] == null) { | |
101 if (substitution.containsKey(key)) { | |
102 var actualType = substitution[typeParameter]; | |
103 reportFailure( | |
104 testCase, | |
105 'Incorrect substitution ' | |
106 '`$key = $actualType` should be unbound'); | |
107 } | |
108 } else { | |
109 var expectedType = env.parse(testCase.expectedSubstitution[key]); | |
110 var actualType = substitution[typeParameter]; | |
111 if (actualType != expectedType) { | |
112 reportFailure( | |
113 testCase, | |
114 'Incorrect substitution ' | |
115 '`$key = $actualType` should be `$key = $expectedType`'); | |
116 } | |
117 } | |
118 } | |
119 var boundTypeVariables = testCase.expectedSubstitution.keys | |
120 .where((name) => testCase.expectedSubstitution[name] != null); | |
121 if (substitution.length != boundTypeVariables.length) { | |
122 reportFailure( | |
123 testCase, | |
124 'Substituted `${substitution.keys.join(',')}` ' | |
125 'but should only substitute `${boundTypeVariables.join(',')}`'); | |
126 } | |
127 } | |
128 } else { | |
129 if (substitution != null) { | |
130 reportFailure(testCase, 'Unification was supposed to fail'); | |
131 } | |
132 } | |
133 }); | |
134 } | |
135 if (numFailures > 0) { | |
136 exit(1); | |
137 } | |
138 } | |
OLD | NEW |