OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, 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 import 'dart:async'; | |
6 import 'package:barback/barback.dart'; | |
7 import 'package:observe/transformer.dart'; | |
8 import 'package:unittest/compact_vm_config.dart'; | |
9 import 'package:unittest/unittest.dart'; | |
10 import 'package:stack_trace/stack_trace.dart'; | |
11 | |
12 main() { | |
13 useCompactVMConfiguration(); | |
14 | |
15 group('replaces Observable for ChangeNotifier', () { | |
16 _testClause('extends Observable', 'extends ChangeNotifier'); | |
17 _testClause('extends Base with Observable', | |
18 'extends Base with ChangeNotifier'); | |
19 _testClause('extends Base<T> with Observable', | |
20 'extends Base<T> with ChangeNotifier'); | |
21 _testClause('extends Base with Mixin, Observable', | |
22 'extends Base with Mixin, ChangeNotifier'); | |
23 _testClause('extends Base with Observable, Mixin', | |
24 'extends Base with ChangeNotifier, Mixin'); | |
25 _testClause('extends Base with Mixin<T>, Observable', | |
26 'extends Base with Mixin<T>, ChangeNotifier'); | |
27 _testClause('extends Base with Mixin, Observable, Mixin2', | |
28 'extends Base with Mixin, ChangeNotifier, Mixin2'); | |
29 _testClause('extends Observable implements Interface', | |
30 'extends ChangeNotifier implements Interface'); | |
31 _testClause('extends Observable implements Interface<T>', | |
32 'extends ChangeNotifier implements Interface<T>'); | |
33 _testClause('extends Base with Observable implements Interface', | |
34 'extends Base with ChangeNotifier implements Interface'); | |
35 _testClause( | |
36 'extends Base with Mixin, Observable implements I1, I2', | |
37 'extends Base with Mixin, ChangeNotifier implements I1, I2'); | |
38 }); | |
39 | |
40 group('adds "with ChangeNotifier" given', () { | |
41 _testClause('', 'extends ChangeNotifier'); | |
42 _testClause('extends Base', 'extends Base with ChangeNotifier'); | |
43 _testClause('extends Base<T>', 'extends Base<T> with ChangeNotifier'); | |
44 _testClause('extends Base with Mixin', | |
45 'extends Base with Mixin, ChangeNotifier'); | |
46 _testClause('extends Base with Mixin<T>', | |
47 'extends Base with Mixin<T>, ChangeNotifier'); | |
48 _testClause('extends Base with Mixin, Mixin2', | |
49 'extends Base with Mixin, Mixin2, ChangeNotifier'); | |
50 _testClause('implements Interface', | |
51 'extends ChangeNotifier implements Interface'); | |
52 _testClause('implements Interface<T>', | |
53 'extends ChangeNotifier implements Interface<T>'); | |
54 _testClause('extends Base implements Interface', | |
55 'extends Base with ChangeNotifier implements Interface'); | |
56 _testClause('extends Base with Mixin implements I1, I2', | |
57 'extends Base with Mixin, ChangeNotifier implements I1, I2'); | |
58 }); | |
59 | |
60 group('fixes contructor calls ', () { | |
61 _testInitializers('this.a', '(a) : __\$a = a'); | |
62 _testInitializers('{this.a}', '({a}) : __\$a = a'); | |
63 _testInitializers('[this.a]', '([a]) : __\$a = a'); | |
64 _testInitializers('this.a, this.b', '(a, b) : __\$a = a, __\$b = b'); | |
65 _testInitializers('{this.a, this.b}', '({a, b}) : __\$a = a, __\$b = b'); | |
66 _testInitializers('[this.a, this.b]', '([a, b]) : __\$a = a, __\$b = b'); | |
67 _testInitializers('this.a, [this.b]', '(a, [b]) : __\$a = a, __\$b = b'); | |
68 _testInitializers('this.a, {this.b}', '(a, {b}) : __\$a = a, __\$b = b'); | |
69 }); | |
70 | |
71 var annotations = ['observable', 'published', | |
72 'ObservableProperty()', 'PublishedProperty(reflect: true)']; | |
73 for (var annotation in annotations) { | |
74 group('@$annotation full text', () { | |
75 test('with changes', () { | |
76 return _transform(_sampleObservable(annotation)).then( | |
77 (out) => expect(out, _sampleObservableOutput(annotation))); | |
78 }); | |
79 | |
80 test('complex with changes', () { | |
81 return _transform(_complexObservable(annotation)).then( | |
82 (out) => expect(out, _complexObservableOutput(annotation))); | |
83 }); | |
84 | |
85 test('no changes', () { | |
86 var input = 'class A {/*@$annotation annotation to trigger transform */;
}'; | |
87 return _transform(input).then((output) => expect(output, input)); | |
88 }); | |
89 }); | |
90 } | |
91 } | |
92 | |
93 _testClause(String clauses, String expected) { | |
94 test(clauses, () { | |
95 var className = 'MyClass'; | |
96 if (clauses.contains('<T>')) className += '<T>'; | |
97 var code = ''' | |
98 class $className $clauses { | |
99 @observable var field; | |
100 }'''; | |
101 | |
102 return _transform(code).then((output) { | |
103 var classPos = output.indexOf(className) + className.length; | |
104 var actualClauses = output.substring(classPos, | |
105 output.indexOf('{')).trim().replaceAll(' ', ' '); | |
106 expect(actualClauses, expected); | |
107 }); | |
108 }); | |
109 } | |
110 | |
111 _testInitializers(String args, String expected) { | |
112 test(args, () { | |
113 var constructor = 'MyClass('; | |
114 var code = ''' | |
115 class MyClass { | |
116 @observable var a; | |
117 @observable var b; | |
118 MyClass($args); | |
119 }'''; | |
120 | |
121 return _transform(code).then((output) { | |
122 var begin = output.indexOf(constructor) + constructor.length - 1; | |
123 var end = output.indexOf(';', begin); | |
124 if (end == -1) end = output.length; | |
125 var init = output.substring(begin, end).trim().replaceAll(' ', ' '); | |
126 expect(init, expected); | |
127 }); | |
128 }); | |
129 } | |
130 | |
131 /// Helper that applies the transform by creating mock assets. | |
132 Future<String> _transform(String code) { | |
133 return Chain.capture(() { | |
134 var id = new AssetId('foo', 'a/b/c.dart'); | |
135 var asset = new Asset.fromString(id, code); | |
136 var transformer = new ObservableTransformer(); | |
137 return transformer.isPrimary(asset).then((isPrimary) { | |
138 expect(isPrimary, isTrue); | |
139 var transform = new _MockTransform(asset); | |
140 return transformer.apply(transform).then((_) { | |
141 expect(transform.outs, hasLength(2)); | |
142 expect(transform.outs[0].id, id); | |
143 expect(transform.outs[1].id, id.addExtension('._buildLogs.1')); | |
144 return transform.outs.first.readAsString(); | |
145 }); | |
146 }); | |
147 }); | |
148 } | |
149 | |
150 class _MockTransform implements Transform { | |
151 bool shouldConsumePrimary = false; | |
152 List<Asset> outs = []; | |
153 Asset _asset; | |
154 TransformLogger logger = new TransformLogger(_mockLogFn); | |
155 Asset get primaryInput => _asset; | |
156 | |
157 _MockTransform(this._asset); | |
158 Future<Asset> getInput(AssetId id) { | |
159 if (id == primaryInput.id) return new Future.value(primaryInput); | |
160 fail('_MockTransform fail'); | |
161 } | |
162 | |
163 void addOutput(Asset output) { | |
164 outs.add(output); | |
165 } | |
166 | |
167 void consumePrimary() { | |
168 shouldConsumePrimary = true; | |
169 } | |
170 | |
171 readInput(id) => throw new UnimplementedError(); | |
172 readInputAsString(id, {encoding}) => throw new UnimplementedError(); | |
173 hasInput(id) => | |
174 new Future.value(id == _asset.id || outs.any((a) => a.id == id)); | |
175 | |
176 static void _mockLogFn(AssetId asset, LogLevel level, String message, | |
177 span) { | |
178 // Do nothing. | |
179 } | |
180 } | |
181 | |
182 String _sampleObservable(String annotation) => ''' | |
183 library A_foo; | |
184 import 'package:observe/observe.dart'; | |
185 | |
186 class A extends Observable { | |
187 @$annotation int foo; | |
188 A(this.foo); | |
189 } | |
190 '''; | |
191 | |
192 String _sampleObservableOutput(String annotation) => | |
193 "library A_foo;\n" | |
194 "import 'package:observe/observe.dart';\n\n" | |
195 "class A extends ChangeNotifier {\n" | |
196 " @reflectable @$annotation int get foo => __\$foo; int __\$foo; " | |
197 "${_makeSetter('int', 'foo')}\n" | |
198 " A(foo) : __\$foo = foo;\n" | |
199 "}\n"; | |
200 | |
201 _makeSetter(type, name) => '@reflectable set $name($type value) { ' | |
202 '__\$$name = notifyPropertyChange(#$name, __\$$name, value); }'; | |
203 | |
204 String _complexObservable(String annotation) => ''' | |
205 class Foo extends Observable { | |
206 @$annotation | |
207 @otherMetadata | |
208 Foo | |
209 foo/*D*/= 1, bar =/*A*/2/*B*/, | |
210 quux/*C*/; | |
211 | |
212 @$annotation var baz; | |
213 } | |
214 '''; | |
215 | |
216 String _complexObservableOutput(String meta) => | |
217 "class Foo extends ChangeNotifier {\n" | |
218 " @reflectable @$meta\n" | |
219 " @otherMetadata\n" | |
220 " Foo\n" | |
221 " get foo => __\$foo; Foo __\$foo/*D*/= 1; " | |
222 "${_makeSetter('Foo', 'foo')} " | |
223 "@reflectable @$meta @otherMetadata Foo get bar => __\$bar; " | |
224 "Foo __\$bar =/*A*/2/*B*/; ${_makeSetter('Foo', 'bar')}\n" | |
225 " @reflectable @$meta @otherMetadata Foo get quux => __\$quux; " | |
226 "Foo __\$quux/*C*/; ${_makeSetter('Foo', 'quux')}\n\n" | |
227 " @reflectable @$meta dynamic get baz => __\$baz; dynamic __\$baz; " | |
228 "${_makeSetter('dynamic', 'baz')}\n" | |
229 "}\n"; | |
OLD | NEW |