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 |