Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(408)

Side by Side Diff: pkg/polymer_expressions/test/eval_test.dart

Issue 22950008: move fancy_syntax into Dart SVN (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 library eval_test;
6
7 import 'dart:async';
8
9 import 'package:polymer_expressions/eval.dart';
10 import 'package:polymer_expressions/filter.dart';
11 import 'package:polymer_expressions/parser.dart';
12 import 'package:unittest/unittest.dart';
13 import 'package:observe/observe.dart';
14
15 main() {
16
17 group('eval', () {
18 test('should return the model for an empty expression', () {
19 expectEval('', 'model', 'model');
20 });
21
22 test('should handle the "this" keyword', () {
23 expectEval('this', 'model', 'model');
24 expectEval('this.name', 'foo', new Foo(name: 'foo'));
25 expectEval('this["a"]', 'x', {'a': 'x'});
26 });
27
28 test('should return a literal int', () {
29 expectEval('1', 1);
30 expectEval('+1', 1);
31 expectEval('-1', -1);
32 });
33
34 test('should return a literal double', () {
35 expectEval('1.2', 1.2);
36 expectEval('+1.2', 1.2);
37 expectEval('-1.2', -1.2);
38 });
39
40 test('should return a literal string', () {
41 expectEval('"hello"', "hello");
42 expectEval("'hello'", "hello");
43 });
44
45 test('should return a literal boolean', () {
46 expectEval('true', true);
47 expectEval('false', false);
48 });
49
50 test('should return a literal map', () {
51 expectEval('{"a": 1}', equals(new Map.from({'a': 1})));
52 expectEval('{"a": 1}', containsPair('a', 1));
53 });
54
55 test('should call methods on a literal map', () {
56 expectEval('{"a": 1}.length', 1);
57 });
58
59 test('should evaluate unary operators', () {
60 expectEval('+a', 2, null, {'a': 2});
61 expectEval('-a', -2, null, {'a': 2});
62 expectEval('!a', false, null, {'a': true});
63 });
64
65 test('should evaluate binary operators', () {
66 expectEval('1 + 2', 3);
67 expectEval('2 - 1', 1);
68 expectEval('4 / 2', 2);
69 expectEval('2 * 3', 6);
70
71 expectEval('1 == 1', true);
72 expectEval('1 == 2', false);
73 expectEval('1 != 1', false);
74 expectEval('1 != 2', true);
75
76 expectEval('1 > 1', false);
77 expectEval('1 > 2', false);
78 expectEval('2 > 1', true);
79 expectEval('1 >= 1', true);
80 expectEval('1 >= 2', false);
81 expectEval('2 >= 1', true);
82 expectEval('1 < 1', false);
83 expectEval('1 < 2', true);
84 expectEval('2 < 1', false);
85 expectEval('1 <= 1', true);
86 expectEval('1 <= 2', true);
87 expectEval('2 <= 1', false);
88
89 expectEval('true || true', true);
90 expectEval('true || false', true);
91 expectEval('false || true', true);
92 expectEval('false || false', false);
93
94 expectEval('true && true', true);
95 expectEval('true && false', false);
96 expectEval('false && true', false);
97 expectEval('false && false', false);
98 });
99
100 test('should invoke a method on the model', () {
101 var foo = new Foo(name: 'foo', age: 2);
102 expectEval('x()', foo.x(), foo);
103 expectEval('name', foo.name, foo);
104 });
105
106 test('should invoke chained methods', () {
107 var foo = new Foo(name: 'foo', age: 2);
108 expectEval('name.length', foo.name.length, foo);
109 expectEval('x().toString()', foo.x().toString(), foo);
110 expectEval('name.substring(2)', foo.name.substring(2), foo);
111 expectEval('a()()', 1, null, {'a': () => () => 1});
112 });
113
114 test('should invoke a top-level function', () {
115 expectEval('x()', 42, null, {'x': () => 42});
116 expectEval('x(5)', 5, null, {'x': (i) => i});
117 expectEval('y(5, 10)', 50, null, {'y': (i, j) => i * j});
118 });
119
120 test('should give precedence to top-level functions over methods', () {
121 var foo = new Foo(name: 'foo', age: 2);
122 expectEval('x()', 42, foo, {'x': () => 42});
123 });
124
125 test('should invoke the [] operator', () {
126 var map = {'a': 1, 'b': 2};
127 expectEval('map["a"]', 1, null, {'map': map});
128 expectEval('map["a"] + map["b"]', 3, null, {'map': map});
129 });
130
131 test('should call a filter', () {
132 var topLevel = {
133 'a': 'foo',
134 'uppercase': (s) => s.toUpperCase(),
135 };
136 expectEval('a | uppercase', 'FOO', null, topLevel);
137 });
138
139 test('should call a transformer', () {
140 var topLevel = {
141 'a': '42',
142 'parseInt': parseInt,
143 'add': add,
144 };
145 expectEval('a | parseInt()', 42, null, topLevel);
146 expectEval('a | parseInt(8)', 34, null, topLevel);
147 expectEval('a | parseInt() | add(10)', 52, null, topLevel);
148 });
149
150 test('should return null if the receiver of a method is null', () {
151 expectEval('a.b', null, null, {'a': null});
152 expectEval('a.b()', null, null, {'a': null});
153 });
154
155 test('should return null if null is invoked', () {
156 expectEval('a()', null, null, {'a': null});
157 });
158
159 test('should return null if an operand is null', () {
160 expectEval('a + b', null, null, {'a': null, 'b': null});
161 expectEval('+a', null, null, {'a': null});
162 });
163
164 test('should treat null as false', () {
165 expectEval('!a', true, null, {'a': null});
166
167 expectEval('a && b', false, null, {'a': null, 'b': true});
168 expectEval('a && b', false, null, {'a': true, 'b': null});
169 expectEval('a && b', false, null, {'a': null, 'b': false});
170 expectEval('a && b', false, null, {'a': false, 'b': null});
171 expectEval('a && b', false, null, {'a': null, 'b': null});
172
173 expectEval('a || b', true, null, {'a': null, 'b': true});
174 expectEval('a || b', true, null, {'a': true, 'b': null});
175 expectEval('a || b', false, null, {'a': null, 'b': false});
176 expectEval('a || b', false, null, {'a': false, 'b': null});
177 expectEval('a || b', false, null, {'a': null, 'b': null});
178 });
179
180 test('should evaluate an "in" expression', () {
181 var scope = new Scope(variables: {'items': [1, 2, 3]});
182 var comprehension = eval(parse('item in items'), scope);
183 expect(comprehension.iterable, orderedEquals([1, 2, 3]));
184 });
185
186 test('should handle null iterators in "in" expressions', () {
187 var scope = new Scope(variables: {'items': null});
188 var comprehension = eval(parse('item in items'), scope);
189 expect(comprehension, isNotNull);
190 expect(comprehension.iterable, null);
191 });
192
193 });
194
195 group('assign', () {
196
197 test('should assign a single identifier', () {
198 var foo = new Foo(name: 'a');
199 assign(parse('name'), 'b', new Scope(model: foo));
200 expect(foo.name, 'b');
201 });
202
203 test('should assign a sub-property', () {
204 var child = new Foo(name: 'child');
205 var parent = new Foo(child: child);
206 assign(parse('child.name'), 'Joe', new Scope(model: parent));
207 expect(parent.child.name, 'Joe');
208 });
209
210 test('should assign an index', () {
211 var foo = new Foo(items: [1, 2, 3]);
212 assign(parse('items[0]'), 4, new Scope(model: foo));
213 expect(foo.items[0], 4);
214 });
215
216 test('should assign through transformers', () {
217 var foo = new Foo(name: '42', age: 32);
218 var globals = {
219 'a': '42',
220 'parseInt': parseInt,
221 'add': add,
222 };
223 var scope = new Scope(model: foo, variables: globals);
224 assign(parse('age | add(7)'), 29, scope);
225 expect(foo.age, 22);
226 assign(parse('name | parseInt() | add(10)'), 29, scope);
227 expect(foo.name, '19');
228 });
229
230 });
231
232 group('scope', () {
233 test('should return fields on the model', () {
234 var foo = new Foo(name: 'a', age: 1);
235 var scope = new Scope(model: foo);
236 expect(scope['name'], 'a');
237 expect(scope['age'], 1);
238 });
239
240 test('should throw for undefined names', () {
241 var scope = new Scope();
242 expect(() => scope['a'], throwsException);
243 });
244
245 test('should return variables', () {
246 var scope = new Scope(variables: {'a': 'A'});
247 expect(scope['a'], 'A');
248 });
249
250 test("should a field from the parent's model", () {
251 var parent = new Scope(variables: {'a': 'A', 'b': 'B'});
252 var child = new Scope(variables: {'a': 'a'}, parent: parent);
253 expect(child['a'], 'a');
254 expect(parent['a'], 'A');
255 expect(child['b'], 'B');
256 });
257
258 });
259
260 group('observe', () {
261 test('should observe an identifier', () {
262 var foo = new Foo(name: 'foo');
263 return expectObserve('name',
264 model: foo,
265 beforeMatcher: 'foo',
266 mutate: () {
267 foo.name = 'fooz';
268 },
269 afterMatcher: 'fooz'
270 );
271 });
272
273 test('should observe an invocation', () {
274 var foo = new Foo(name: 'foo');
275 return expectObserve('foo.name',
276 variables: {'foo': foo},
277 beforeMatcher: 'foo',
278 mutate: () {
279 foo.name = 'fooz';
280 },
281 afterMatcher: 'fooz'
282 );
283 });
284
285 test('should observe map access', () {
286 var foo = toObservable({'one': 'one', 'two': 'two'});
287 return expectObserve('foo["one"]',
288 variables: {'foo': foo},
289 beforeMatcher: 'one',
290 mutate: () {
291 foo['one'] = '1';
292 },
293 afterMatcher: '1'
294 );
295 });
296
297 test('should observe an comprehension', () {
298 var items = new ObservableList();
299 var foo = new Foo(name: 'foo');
300 return expectObserve('item in items',
301 variables: {'items': items},
302 beforeMatcher: (c) => c.iterable.isEmpty,
303 mutate: () {
304 items.add(foo);
305 },
306 afterMatcher: (c) => c.iterable.contains(foo)
307 );
308 });
309
310 });
311
312 }
313
314 class Foo extends Object with ChangeNotifierMixin {
315 String _name;
316 String get name => _name;
317 void set name(String n) {
318 _name = notifyPropertyChange(const Symbol('name'), _name, n);
319 }
320
321 int age;
322 Foo child;
323 List<int> items;
324
325 Foo({name, this.age, this.child, this.items}) : _name = name;
326
327 int x() => age * age;
328 }
329
330 parseInt([int radix = 10]) => new IntToString(radix: radix);
331
332 class IntToString extends Transformer<int, String> {
333 final int radix;
334 IntToString({this.radix: 10});
335 int forward(String s) => int.parse(s, radix: radix);
336 String reverse(int i) => '$i';
337 }
338
339 add(int i) => new Add(i);
340
341 class Add extends Transformer<int, int> {
342 final int i;
343 Add(this.i);
344 int forward(int x) => x + i;
345 int reverse(int x) => x - i;
346 }
347
348 Object evalString(String s, [Object model, Map vars]) =>
349 eval(new Parser(s).parse(), new Scope(model: model, variables: vars));
350
351 expectEval(String s, dynamic matcher, [Object model, Map vars = const {}]) =>
352 expect(
353 eval(new Parser(s).parse(), new Scope(model: model, variables: vars)),
354 matcher,
355 reason: s);
356
357 expectObserve(String s, {
358 Object model,
359 Map variables: const {},
360 dynamic beforeMatcher,
361 mutate(),
362 dynamic afterMatcher}) {
363
364 var observer = observe(new Parser(s).parse(),
365 new Scope(model: model, variables: variables));
366 expect(observer.currentValue, beforeMatcher);
367 var passed = false;
368 var future = observer.onUpdate.first.then((value) {
369 expect(value, afterMatcher);
370 expect(observer.currentValue, afterMatcher);
371 passed = true;
372 });
373 mutate();
374 // fail if we don't receive an update by the next event loop
375 return Future.wait([future, new Future(() {
376 expect(passed, true, reason: "Didn't receive a change notification on $s");
377 })]);
378 }
OLDNEW
« no previous file with comments | « pkg/polymer_expressions/test/all_tests.dart ('k') | pkg/polymer_expressions/test/parser_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698