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