| 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 |