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

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

Issue 2312183003: Removed Polymer from Observatory deps (Closed)
Patch Set: Created 4 years, 3 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
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 mirrors to cause all mirrors to be retained by dart2js.
10 // The tests reflect on LinkedHashMap.length and String.length.
11 import 'dart:mirrors';
12
13 import 'package:polymer_expressions/eval.dart';
14 import 'package:polymer_expressions/filter.dart';
15 import 'package:polymer_expressions/parser.dart';
16 import 'package:unittest/unittest.dart';
17 import 'package:observe/observe.dart';
18 import 'package:observe/mirrors_used.dart'; // make test smaller.
19
20 main() {
21 reflectClass(Object); // suppress unused import warning
22
23 group('eval', () {
24 test('should return the model for an empty expression', () {
25 expectEval('', 'model', 'model');
26 });
27
28 test('should handle the "this" keyword', () {
29 expectEval('this', 'model', 'model');
30 expectEval('this.name', 'foo', new Foo(name: 'foo'));
31 expectEval('this["a"]', 'x', {'a': 'x'});
32 });
33
34 test('should return a literal int', () {
35 expectEval('1', 1);
36 expectEval('+1', 1);
37 expectEval('-1', -1);
38 });
39
40 test('should return a literal double', () {
41 expectEval('1.2', 1.2);
42 expectEval('+1.2', 1.2);
43 expectEval('-1.2', -1.2);
44 });
45
46 test('should return a literal string', () {
47 expectEval('"hello"', "hello");
48 expectEval("'hello'", "hello");
49 });
50
51 test('should return a literal boolean', () {
52 expectEval('true', true);
53 expectEval('false', false);
54 });
55
56 test('should return a literal null', () {
57 expectEval('null', null);
58 });
59
60 test('should return a literal list', () {
61 expectEval('[1, 2, 3]', equals([1, 2, 3]));
62 });
63
64 test('should return a literal map', () {
65 expectEval('{"a": 1}', equals(new Map.from({'a': 1})));
66 expectEval('{"a": 1}', containsPair('a', 1));
67 });
68
69 test('should call methods on a literal map', () {
70 expectEval('{"a": 1}.length', 1);
71 });
72
73 test('should evaluate unary operators', () {
74 expectEval('+a', 2, null, {'a': 2});
75 expectEval('-a', -2, null, {'a': 2});
76 expectEval('!a', false, null, {'a': true});
77 });
78
79 test('should evaluate binary operators', () {
80 expectEval('1 + 2', 3);
81 expectEval('2 - 1', 1);
82 expectEval('4 / 2', 2);
83 expectEval('2 * 3', 6);
84 expectEval('5 % 2', 1);
85 expectEval('5 % -2', 1);
86 expectEval('-5 % 2', 1);
87
88 expectEval('1 == 1', true);
89 expectEval('1 == 2', false);
90 expectEval('1 == null', false);
91 expectEval('1 != 1', false);
92 expectEval('1 != 2', true);
93 expectEval('1 != null', true);
94
95 var x = {};
96 var y = {};
97 expectEval('x === y', true, null, {'x': x, 'y': x});
98 expectEval('x !== y', false, null, {'x': x, 'y': x});
99 expectEval('x === y', false, null, {'x': x, 'y': y});
100 expectEval('x !== y', true, null, {'x': x, 'y': y});
101
102 expectEval('1 > 1', false);
103 expectEval('1 > 2', false);
104 expectEval('2 > 1', true);
105 expectEval('1 >= 1', true);
106 expectEval('1 >= 2', false);
107 expectEval('2 >= 1', true);
108 expectEval('1 < 1', false);
109 expectEval('1 < 2', true);
110 expectEval('2 < 1', false);
111 expectEval('1 <= 1', true);
112 expectEval('1 <= 2', true);
113 expectEval('2 <= 1', false);
114
115 expectEval('true || true', true);
116 expectEval('true || false', true);
117 expectEval('false || true', true);
118 expectEval('false || false', false);
119
120 expectEval('true && true', true);
121 expectEval('true && false', false);
122 expectEval('false && true', false);
123 expectEval('false && false', false);
124 });
125
126 test('should evaulate ternary operators', () {
127 expectEval('true ? 1 : 2', 1);
128 expectEval('false ? 1 : 2', 2);
129 expectEval('true ? true ? 1 : 2 : 3', 1);
130 expectEval('true ? false ? 1 : 2 : 3', 2);
131 expectEval('false ? true ? 1 : 2 : 3', 3);
132 expectEval('false ? 1 : true ? 2 : 3', 2);
133 expectEval('false ? 1 : false ? 2 : 3', 3);
134 expectEval('null ? 1 : 2', 2);
135 // TODO(justinfagnani): re-enable and check for an EvalError when
136 // we implement the final bool conversion rules and this expression
137 // throws in both checked and unchecked mode
138 // expect(() => eval(parse('42 ? 1 : 2'), null), throws);
139 });
140
141 test('should invoke a method on the model', () {
142 var foo = new Foo(name: 'foo', age: 2);
143 expectEval('x()', foo.x(), foo);
144 expectEval('name', foo.name, foo);
145 });
146
147 test('should invoke chained methods', () {
148 var foo = new Foo(name: 'foo', age: 2);
149 expectEval('name.length', foo.name.length, foo);
150 expectEval('x().toString()', foo.x().toString(), foo);
151 expectEval('name.substring(2)', foo.name.substring(2), foo);
152 expectEval('a()()', 1, null, {'a': () => () => 1});
153 });
154
155 test('should invoke a top-level function', () {
156 expectEval('x()', 42, null, {'x': () => 42});
157 expectEval('x(5)', 5, null, {'x': (i) => i});
158 expectEval('y(5, 10)', 50, null, {'y': (i, j) => i * j});
159 });
160
161 test('should give precedence to top-level functions over methods', () {
162 var foo = new Foo(name: 'foo', age: 2);
163 expectEval('x()', 42, foo, {'x': () => 42});
164 });
165
166 test('should invoke the [] operator', () {
167 var map = {'a': 1, 'b': 2};
168 expectEval('map["a"]', 1, null, {'map': map});
169 expectEval('map["a"] + map["b"]', 3, null, {'map': map});
170 });
171
172 test('should call a filter', () {
173 var topLevel = {
174 'a': 'foo',
175 'uppercase': (s) => s.toUpperCase(),
176 };
177 expectEval('a | uppercase', 'FOO', null, topLevel);
178 });
179
180 test('should call a transformer', () {
181 var topLevel = {
182 'a': '42',
183 'parseInt': parseInt,
184 'add': add,
185 };
186 expectEval('a | parseInt()', 42, null, topLevel);
187 expectEval('a | parseInt(8)', 34, null, topLevel);
188 expectEval('a | parseInt() | add(10)', 52, null, topLevel);
189 });
190
191 test('should filter a list', () {
192 expectEval('chars1 | filteredList', ['a', 'b'], new WordElement());
193 });
194
195 test('should return null if the receiver of a method is null', () {
196 expectEval('a.b', null, null, {'a': null});
197 expectEval('a.b()', null, null, {'a': null});
198 });
199
200 test('should return null if null is invoked', () {
201 expectEval('a()', null, null, {'a': null});
202 });
203
204 test('should return null if an operand is null', () {
205 expectEval('a + b', null, null, {'a': null, 'b': null});
206 expectEval('+a', null, null, {'a': null});
207 });
208
209 test('should treat null as false', () {
210 expectEval('!null', true);
211 expectEval('true && null', false);
212 expectEval('null || false', false);
213
214 expectEval('!a', true, null, {'a': null});
215
216 expectEval('a && b', false, null, {'a': null, 'b': true});
217 expectEval('a && b', false, null, {'a': true, 'b': null});
218 expectEval('a && b', false, null, {'a': null, 'b': false});
219 expectEval('a && b', false, null, {'a': false, 'b': null});
220 expectEval('a && b', false, null, {'a': null, 'b': null});
221
222 expectEval('a || b', true, null, {'a': null, 'b': true});
223 expectEval('a || b', true, null, {'a': true, 'b': null});
224 expectEval('a || b', false, null, {'a': null, 'b': false});
225 expectEval('a || b', false, null, {'a': false, 'b': null});
226 expectEval('a || b', false, null, {'a': null, 'b': null});
227 });
228
229 test('should not evaluate "in" expressions', () {
230 expect(() => eval(parse('item in items'), null), throws);
231 });
232
233 });
234
235 group('assign', () {
236
237 test('should assign a single identifier', () {
238 var foo = new Foo(name: 'a');
239 assign(parse('name'), 'b', new Scope(model: foo));
240 expect(foo.name, 'b');
241 });
242
243 test('should assign a sub-property', () {
244 var child = new Foo(name: 'child');
245 var parent = new Foo(child: child);
246 assign(parse('child.name'), 'Joe', new Scope(model: parent));
247 expect(parent.child.name, 'Joe');
248 });
249
250 test('should assign an index', () {
251 var foo = new Foo(items: [1, 2, 3]);
252 assign(parse('items[0]'), 4, new Scope(model: foo));
253 expect(foo.items[0], 4);
254 assign(parse('items[a]'), 5, new Scope(model: foo, variables: {'a': 0}));
255 expect(foo.items[0], 5);
256 });
257
258 test('should assign with a function call subexpression', () {
259 var child = new Foo();
260 var foo = new Foo(items: [1, 2, 3], child: child);
261 assign(parse('getChild().name'), 'child', new Scope(model: foo));
262 expect(child.name, 'child');
263 });
264
265 test('should assign through transformers', () {
266 var foo = new Foo(name: '42', age: 32);
267 var globals = {
268 'a': '42',
269 'parseInt': parseInt,
270 'add': add,
271 };
272 var scope = new Scope(model: foo, variables: globals);
273 assign(parse('age | add(7)'), 29, scope);
274 expect(foo.age, 22);
275 assign(parse('name | parseInt() | add(10)'), 29, scope);
276 expect(foo.name, '19');
277 });
278
279 test('should not throw on assignments to properties on null', () {
280 assign(parse('name'), 'b', new Scope(model: null));
281 });
282
283 test('should throw on assignments to non-assignable expressions', () {
284 var foo = new Foo(name: 'a');
285 var scope = new Scope(model: foo);
286 expect(() => assign(parse('name + 1'), 1, scope),
287 throwsA(new isInstanceOf<EvalException>()));
288 expect(() => assign(parse('toString()'), 1, scope),
289 throwsA(new isInstanceOf<EvalException>()));
290 expect(() => assign(parse('name | filter'), 1, scope),
291 throwsA(new isInstanceOf<EvalException>()));
292 });
293
294 test('should not throw on assignments to non-assignable expressions if '
295 'checkAssignability is false', () {
296 var foo = new Foo(name: 'a');
297 var scope = new Scope(model: foo);
298 expect(
299 assign(parse('name + 1'), 1, scope, checkAssignability: false),
300 null);
301 expect(
302 assign(parse('toString()'), 1, scope, checkAssignability: false),
303 null);
304 expect(
305 assign(parse('name | filter'), 1, scope, checkAssignability: false),
306 null);
307 });
308
309 });
310
311 group('scope', () {
312 test('should return fields on the model', () {
313 var foo = new Foo(name: 'a', age: 1);
314 var scope = new Scope(model: foo);
315 expect(scope['name'], 'a');
316 expect(scope['age'], 1);
317 });
318
319 test('should throw for undefined names', () {
320 var scope = new Scope();
321 expect(() => scope['a'], throwsException);
322 });
323
324 test('should return variables', () {
325 var scope = new Scope(variables: {'a': 'A'});
326 expect(scope['a'], 'A');
327 });
328
329 test("should a field from the parent's model", () {
330 var parent = new Scope(variables: {'a': 'A', 'b': 'B'});
331 var child = parent.childScope('a', 'a');
332 expect(child['a'], 'a');
333 expect(parent['a'], 'A');
334 expect(child['b'], 'B');
335 });
336
337 });
338
339 group('observe', () {
340 test('should observe an identifier', () {
341 var foo = new Foo(name: 'foo');
342 return expectObserve('name',
343 model: foo,
344 beforeMatcher: 'foo',
345 mutate: () {
346 foo.name = 'fooz';
347 },
348 afterMatcher: 'fooz'
349 );
350 });
351
352 test('should observe an invocation', () {
353 var foo = new Foo(name: 'foo');
354 return expectObserve('foo.name',
355 variables: {'foo': foo},
356 beforeMatcher: 'foo',
357 mutate: () {
358 foo.name = 'fooz';
359 },
360 afterMatcher: 'fooz'
361 );
362 });
363
364 test('should observe map access', () {
365 var foo = toObservable({'one': 'one', 'two': 'two'});
366 return expectObserve('foo["one"]',
367 variables: {'foo': foo},
368 beforeMatcher: 'one',
369 mutate: () {
370 foo['one'] = '1';
371 },
372 afterMatcher: '1'
373 );
374 });
375
376 });
377
378 }
379
380 @reflectable
381 class Foo extends ChangeNotifier {
382 String _name;
383 String get name => _name;
384 void set name(String n) {
385 _name = notifyPropertyChange(#name, _name, n);
386 }
387
388 int age;
389 Foo child;
390 List<int> items;
391
392 Foo({name, this.age, this.child, this.items}) : _name = name;
393
394 int x() => age * age;
395
396 getChild() => child;
397
398 filter(i) => i;
399 }
400
401 @reflectable
402 class ListHolder {
403 List items;
404 ListHolder(this.items);
405 }
406
407 parseInt([int radix = 10]) => new IntToString(radix: radix);
408
409 class IntToString extends Transformer<int, String> {
410 final int radix;
411 IntToString({this.radix: 10});
412 int forward(String s) => int.parse(s, radix: radix);
413 String reverse(int i) => '$i';
414 }
415
416 add(int i) => new Add(i);
417
418 class Add extends Transformer<int, int> {
419 final int i;
420 Add(this.i);
421 int forward(int x) => x + i;
422 int reverse(int x) => x - i;
423 }
424
425 Object evalString(String s, [Object model, Map vars]) =>
426 eval(new Parser(s).parse(), new Scope(model: model, variables: vars));
427
428 expectEval(String s, dynamic matcher, [Object model, Map vars = const {}]) {
429 var expr = new Parser(s).parse();
430 var scope = new Scope(model: model, variables: vars);
431 expect(eval(expr, scope), matcher, reason: s);
432
433 var observer = observe(expr, scope);
434 new Updater(scope).visit(observer);
435 expect(observer.currentValue, matcher, reason: s);
436 }
437
438 expectObserve(String s, {
439 Object model,
440 Map variables: const {},
441 dynamic beforeMatcher,
442 mutate(),
443 dynamic afterMatcher}) {
444
445 var scope = new Scope(model: model, variables: variables);
446 var observer = observe(new Parser(s).parse(), scope);
447 update(observer, scope);
448 expect(observer.currentValue, beforeMatcher);
449 var passed = false;
450 var future = observer.onUpdate.first.then((value) {
451 expect(value, afterMatcher);
452 expect(observer.currentValue, afterMatcher);
453 passed = true;
454 });
455 mutate();
456 // fail if we don't receive an update by the next event loop
457 return Future.wait([future, new Future(() {
458 expect(passed, true, reason: "Didn't receive a change notification on $s");
459 })]);
460 }
461
462 // Regression test from https://code.google.com/p/dart/issues/detail?id=13459
463 class WordElement extends Observable {
464 @observable List chars1 = 'abcdefg'.split('');
465 @reflectable List filteredList(List original) => [original[0], original[1]];
466 }
OLDNEW
« no previous file with comments | « packages/polymer_expressions/test/bindings_test.html ('k') | packages/polymer_expressions/test/globals_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698