diff options
| author | Xavier Morel <xmo@openerp.com> | 2012-03-22 15:33:31 +0100 |
|---|---|---|
| committer | Xavier Morel <xmo@openerp.com> | 2012-03-22 15:33:31 +0100 |
| commit | 7353e02e2ef4a403d4b79c1b157f2ca00b77d2fc (patch) | |
| tree | a319552e3664ea272fdfb8abf31c961bcd5e1e6e /addons/web/static/lib/py.js/test | |
| parent | 514c63288c7d5d88d764b44696c5e3a5fc148dfb (diff) | |
| parent | 1da49a44a57aa413e5eb96afe9c6d294e09848fd (diff) | |
[MERGE] from trunk
bzr revid: xmo@openerp.com-20120322143331-iajlifomw1hohbht
Diffstat (limited to 'addons/web/static/lib/py.js/test')
| -rw-r--r-- | addons/web/static/lib/py.js/test/parser.js | 132 | ||||
| -rw-r--r-- | addons/web/static/lib/py.js/test/test.js | 379 |
2 files changed, 511 insertions, 0 deletions
diff --git a/addons/web/static/lib/py.js/test/parser.js b/addons/web/static/lib/py.js/test/parser.js new file mode 100644 index 00000000000..bbbd5d7663d --- /dev/null +++ b/addons/web/static/lib/py.js/test/parser.js @@ -0,0 +1,132 @@ +var py = require('../lib/py.js'), + expect = require('expect.js'); + +expect.Assertion.prototype.tokens = function (n) { + var length = this.obj.length; + this.assert(length === n + 1, + 'expected ' + this.obj + ' to have ' + n + ' tokens', + 'expected ' + this.obj + ' to not have ' + n + ' tokens'); + this.assert(this.obj[length-1].id === '(end)', + 'expected ' + this.obj + ' to have and end token', + 'expected ' + this.obj + ' to not have an end token'); +}; + +expect.Assertion.prototype.named = function (value) { + this.assert(this.obj.id === '(name)', + 'expected ' + this.obj + ' to be a name token', + 'expected ' + this.obj + ' not to be a name token'); + this.assert(this.obj.value === value, + 'expected ' + this.obj + ' to have tokenized ' + value, + 'expected ' + this.obj + ' not to have tokenized ' + value); +}; +expect.Assertion.prototype.constant = function (value) { + this.assert(this.obj.id === '(constant)', + 'expected ' + this.obj + ' to be a constant token', + 'expected ' + this.obj + ' not to be a constant token'); + this.assert(this.obj.value === value, + 'expected ' + this.obj + ' to have tokenized ' + value, + 'expected ' + this.obj + ' not to have tokenized ' + value); +}; +expect.Assertion.prototype.number = function (value) { + this.assert(this.obj.id === '(number)', + 'expected ' + this.obj + ' to be a number token', + 'expected ' + this.obj + ' not to be a number token'); + this.assert(this.obj.value === value, + 'expected ' + this.obj + ' to have tokenized ' + value, + 'expected ' + this.obj + ' not to have tokenized ' + value); +}; +expect.Assertion.prototype.string = function (value) { + this.assert(this.obj.id === '(string)', + 'expected ' + this.obj + ' to be a string token', + 'expected ' + this.obj + ' not to be a string token'); + this.assert(this.obj.value === value, + 'expected ' + this.obj + ' to have tokenized ' + value, + 'expected ' + this.obj + ' not to have tokenized ' + value); +}; + +describe('Tokenizer', function () { + describe('simple literals', function () { + it('tokenizes numbers', function () { + var toks = py.tokenize('1'); + expect(toks).to.have.tokens(1); + expect(toks[0]).to.be.number(1); + + var toks = py.tokenize('-1'); + expect(toks).to.have.tokens(2); + expect(toks[0].id).to.be('-'); + expect(toks[1]).to.be.number(1); + + var toks = py.tokenize('1.2'); + expect(toks).to.have.tokens(1); + expect(toks[0]).to.be.number(1.2); + + var toks = py.tokenize('.42'); + expect(toks).to.have.tokens(1); + expect(toks[0]).to.be.number(0.42); + }); + it('tokenizes strings', function () { + var toks = py.tokenize('"foo"'); + expect(toks).to.have.tokens(1); + expect(toks[0]).to.be.string('foo'); + + var toks = py.tokenize("'foo'"); + expect(toks).to.have.tokens(1); + expect(toks[0]).to.be.string('foo'); + }); + it('tokenizes bare names', function () { + var toks = py.tokenize('foo'); + expect(toks).to.have.tokens(1); + expect(toks[0].id).to.be('(name)'); + expect(toks[0].value).to.be('foo'); + }); + it('tokenizes constants', function () { + var toks = py.tokenize('None'); + expect(toks).to.have.tokens(1); + expect(toks[0]).to.be.constant('None'); + + var toks = py.tokenize('True'); + expect(toks).to.have.tokens(1); + expect(toks[0]).to.be.constant('True'); + + var toks = py.tokenize('False'); + expect(toks).to.have.tokens(1); + expect(toks[0]).to.be.constant('False'); + }); + it('does not fuck up on trailing spaces', function () { + var toks = py.tokenize('None '); + expect(toks).to.have.tokens(1); + expect(toks[0]).to.be.constant('None'); + }); + }); + describe('collections', function () { + it('tokenizes opening and closing symbols', function () { + var toks = py.tokenize('()'); + expect(toks).to.have.tokens(2); + expect(toks[0].id).to.be('('); + expect(toks[1].id).to.be(')'); + }); + }); + describe('functions', function () { + it('tokenizes kwargs', function () { + var toks = py.tokenize('foo(bar=3, qux=4)'); + expect(toks).to.have.tokens(10); + }); + }); +}); + +describe('Parser', function () { + describe('functions', function () { + var ast = py.parse(py.tokenize('foo(bar=3, qux=4)')); + expect(ast.id).to.be('('); + expect(ast.first).to.be.named('foo'); + + args = ast.second; + expect(args[0].id).to.be('='); + expect(args[0].first).to.be.named('bar'); + expect(args[0].second).to.be.number(3); + + expect(args[1].id).to.be('='); + expect(args[1].first).to.be.named('qux'); + expect(args[1].second).to.be.number(4); + }); +});
\ No newline at end of file diff --git a/addons/web/static/lib/py.js/test/test.js b/addons/web/static/lib/py.js/test/test.js new file mode 100644 index 00000000000..31fccc23240 --- /dev/null +++ b/addons/web/static/lib/py.js/test/test.js @@ -0,0 +1,379 @@ +var py = require('../lib/py.js'), + expect = require('expect.js'); + +var ev = function (str, context) { + return py.evaluate(py.parse(py.tokenize(str)), context); +}; + +describe('Literals', function () { + describe('Number', function () { + it('should have the right type', function () { + expect(ev('1')).to.be.a(py.float); + }); + it('should yield the corresponding JS value', function () { + expect(py.eval('1')).to.be(1); + expect(py.eval('42')).to.be(42); + expect(py.eval('9999')).to.be(9999); + }); + it('should correctly handle negative literals', function () { + expect(py.eval('-1')).to.be(-1); + expect(py.eval('-42')).to.be(-42); + expect(py.eval('-9999')).to.be(-9999); + }); + it('should correctly handle float literals', function () { + expect(py.eval('.42')).to.be(0.42); + expect(py.eval('1.2')).to.be(1.2); + }); + }); + describe('Booleans', function () { + it('should have the right type', function () { + expect(ev('False')).to.be.a(py.bool); + expect(ev('True')).to.be.a(py.bool); + }); + it('should yield the corresponding JS value', function () { + expect(py.eval('False')).to.be(false); + expect(py.eval('True')).to.be(true); + }); + }); + describe('None', function () { + it('should have the right type', function () { + expect(ev('None')).to.be.a(py.object) + }); + it('should yield a JS null', function () { + expect(py.eval('None')).to.be(null); + }); + }); + describe('String', function () { + it('should have the right type', function () { + expect(ev('"foo"')).to.be.a(py.str); + expect(ev("'foo'")).to.be.a(py.str); + }); + it('should yield the corresponding JS string', function () { + expect(py.eval('"somestring"')).to.be('somestring'); + expect(py.eval("'somestring'")).to.be('somestring'); + }); + }); + describe('Tuple', function () { + it('shoud have the right type', function () { + expect(ev('()')).to.be.a(py.tuple); + }); + it('should map to a JS array', function () { + expect(py.eval('()')).to.eql([]); + expect(py.eval('(1, 2, 3)')).to.eql([1, 2, 3]); + }); + }); + describe('List', function () { + it('shoud have the right type', function () { + expect(ev('[]')).to.be.a(py.list); + }); + it('should map to a JS array', function () { + expect(py.eval('[]')).to.eql([]); + expect(py.eval('[1, 2, 3]')).to.eql([1, 2, 3]); + }); + }); + describe('Dict', function () { + it('shoud have the right type', function () { + expect(ev('{}')).to.be.a(py.dict); + }); + it('should map to a JS object', function () { + expect(py.eval("{}")).to.eql({}); + expect(py.eval("{'foo': 1, 'bar': 2}")) + .to.eql({foo: 1, bar: 2}); + }); + }); +}); +describe('Free variables', function () { + it('should return its identity', function () { + expect(py.eval('foo', {foo: 1})).to.be(1); + expect(py.eval('foo', {foo: true})).to.be(true); + expect(py.eval('foo', {foo: false})).to.be(false); + expect(py.eval('foo', {foo: null})).to.be(null); + expect(py.eval('foo', {foo: 'bar'})).to.be('bar'); + }); +}); +describe('Comparisons', function () { + describe('equality', function () { + it('should work with literals', function () { + expect(py.eval('1 == 1')).to.be(true); + expect(py.eval('"foo" == "foo"')).to.be(true); + expect(py.eval('"foo" == "bar"')).to.be(false); + }); + it('should work with free variables', function () { + expect(py.eval('1 == a', {a: 1})).to.be(true); + expect(py.eval('foo == "bar"', {foo: 'bar'})).to.be(true); + expect(py.eval('foo == "bar"', {foo: 'qux'})).to.be(false); + }); + }); + describe('inequality', function () { + it('should work with literals', function () { + expect(py.eval('1 != 2')).to.be(true); + expect(py.eval('"foo" != "foo"')).to.be(false); + expect(py.eval('"foo" != "bar"')).to.be(true); + }); + it('should work with free variables', function () { + expect(py.eval('1 != a', {a: 42})).to.be(true); + expect(py.eval('foo != "bar"', {foo: 'bar'})).to.be(false); + expect(py.eval('foo != "bar"', {foo: 'qux'})).to.be(true); + expect(py.eval('foo != bar', {foo: 'qux', bar: 'quux'})) + .to.be(true); + }); + }); + describe('rich comparisons', function () { + it('should work with numbers', function () { + expect(py.eval('3 < 5')).to.be(true); + expect(py.eval('5 >= 3')).to.be(true); + expect(py.eval('3 >= 3')).to.be(true); + expect(py.eval('3 > 5')).to.be(false); + }); + it('should support comparison chains', function () { + expect(py.eval('1 < 3 < 5')).to.be(true); + expect(py.eval('5 > 3 > 1')).to.be(true); + expect(py.eval('1 < 3 > 2 == 2 > -2')).to.be(true); + }); + it('should compare strings', function () { + expect(py.eval('date >= current', + {date: '2010-06-08', current: '2010-06-05'})) + .to.be(true); + expect(py.eval('state == "cancel"', {state: 'cancel'})) + .to.be(true); + expect(py.eval('state == "cancel"', {state: 'open'})) + .to.be(false); + }); + }); + describe('missing eq/neq', function () { + it('should fall back on identity', function () { + var typ = new py.type(function MyType() {}); + expect(py.eval('MyType() == MyType()', {MyType: typ})).to.be(false); + }); + }); + describe('un-comparable types', function () { + it('should default to type-name ordering', function () { + var t1 = new py.type(function Type1() {}); + var t2 = new py.type(function Type2() {}); + expect(py.eval('T1() < T2()', {T1: t1, T2: t2})).to.be(true); + expect(py.eval('T1() > T2()', {T1: t1, T2: t2})).to.be(false); + }); + it('should handle native stuff', function () { + expect(py.eval('None < 42')).to.be(true); + expect(py.eval('42 > None')).to.be(true); + expect(py.eval('None > 42')).to.be(false); + + expect(py.eval('None < False')).to.be(true); + expect(py.eval('None < True')).to.be(true); + expect(py.eval('False > None')).to.be(true); + expect(py.eval('True > None')).to.be(true); + expect(py.eval('None > False')).to.be(false); + expect(py.eval('None > True')).to.be(false); + + expect(py.eval('False < ""')).to.be(true); + expect(py.eval('"" > False')).to.be(true); + expect(py.eval('False > ""')).to.be(false); + }); + }); +}); +describe('Boolean operators', function () { + it('should work', function () { + expect(py.eval("foo == 'foo' or foo == 'bar'", + {foo: 'bar'})) + .to.be(true);; + expect(py.eval("foo == 'foo' and bar == 'bar'", + {foo: 'foo', bar: 'bar'})) + .to.be(true);; + }); + it('should be lazy', function () { + // second clause should nameerror if evaluated + expect(py.eval("foo == 'foo' or bar == 'bar'", + {foo: 'foo'})) + .to.be(true);; + expect(py.eval("foo == 'foo' and bar == 'bar'", + {foo: 'bar'})) + .to.be(false);; + }); + it('should return the actual object', function () { + expect(py.eval('"foo" or "bar"')).to.be('foo'); + expect(py.eval('None or "bar"')).to.be('bar'); + expect(py.eval('False or None')).to.be(null); + expect(py.eval('0 or 1')).to.be(1); + }); +}); +describe('Containment', function () { + describe('in sequences', function () { + it('should match collection items', function () { + expect(py.eval("'bar' in ('foo', 'bar')")) + .to.be(true); + expect(py.eval('1 in (1, 2, 3, 4)')) + .to.be(true);; + expect(py.eval('1 in (2, 3, 4)')) + .to.be(false);; + expect(py.eval('"url" in ("url",)')) + .to.be(true); + expect(py.eval('"foo" in ["foo", "bar"]')) + .to.be(true); + }); + it('should not be recursive', function () { + expect(py.eval('"ur" in ("url",)')) + .to.be(false);; + }); + it('should be negatable', function () { + expect(py.eval('1 not in (2, 3, 4)')).to.be(true); + expect(py.eval('"ur" not in ("url",)')).to.be(true); + expect(py.eval('-2 not in (1, 2, 3)')).to.be(true); + }); + }); + describe('in dict', function () { + // TODO + }); + describe('in strings', function () { + it('should match the whole string', function () { + expect(py.eval('"view" in "view"')).to.be(true); + expect(py.eval('"bob" in "view"')).to.be(false); + }); + it('should match substrings', function () { + expect(py.eval('"ur" in "url"')).to.be(true); + }); + }); +}); +describe('Conversions', function () { + describe('to bool', function () { + describe('strings', function () { + it('should be true if non-empty', function () { + expect(py.eval('bool(date_deadline)', + {date_deadline: '2008'})) + .to.be(true); + }); + it('should be false if empty', function () { + expect(py.eval('bool(s)', {s: ''})) .to.be(false); + }); + }); + }); +}); +describe('Attribute access', function () { + it("should return the attribute's value", function () { + var o = new py.object(); + o.bar = py.True; + expect(py.eval('foo.bar', {foo: o})).to.be(true); + o.bar = py.False; + expect(py.eval('foo.bar', {foo: o})).to.be(false); + }); + it("should work with functions", function () { + var o = new py.object(); + o.bar = new py.def(function () { + return new py.str("ok"); + }); + expect(py.eval('foo.bar()', {foo: o})).to.be('ok'); + }); + it('should not convert function attributes into methods', function () { + var o = new py.object(); + o.bar = new py.type(function bar() {}); + o.bar.__getattribute__ = function () { + return o.bar.baz; + } + o.bar.baz = py.True; + expect(py.eval('foo.bar.baz', {foo: o})).to.be(true); + }); + it('should work on instance attributes', function () { + var typ = py.type(function MyType() { + this.attr = new py.float(3); + }, py.object, {}); + expect(py.eval('MyType().attr', {MyType: typ})).to.be(3); + }); + it('should work on class attributes', function () { + var typ = py.type(function MyType() {}, py.object, { + attr: new py.float(3) + }); + expect(py.eval('MyType().attr', {MyType: typ})).to.be(3); + }); + it('should work with methods', function () { + var typ = py.type(function MyType() { + this.attr = new py.float(3); + }, py.object, { + some_method: function () { return new py.str('ok'); }, + get_attr: function () { return this.attr; } + }); + expect(py.eval('MyType().some_method()', {MyType: typ})).to.be('ok'); + expect(py.eval('MyType().get_attr()', {MyType: typ})).to.be(3); + }); +}); +describe('Callables', function () { + it('should wrap JS functions', function () { + expect(py.eval('foo()', {foo: function foo() { return new py.float(3); }})) + .to.be(3); + }); + it('should work on custom types', function () { + var typ = py.type(function MyType() {}, py.object, { + toJSON: function () { return true; } + }); + expect(py.eval('MyType()', {MyType: typ})).to.be(true); + }); + it('should accept kwargs', function () { + expect(py.eval('foo(ok=True)', { + foo: function foo() { return py.True; } + })).to.be(true); + }); + it('should be able to get its kwargs', function () { + expect(py.eval('foo(ok=True)', { + foo: function foo(args, kwargs) { return kwargs.ok; } + })).to.be(true); + }); + it('should be able to have both args and kwargs', function () { + expect(py.eval('foo(1, 2, 3, ok=True, nok=False)', { + foo: function (args, kwargs) { + expect(args).to.have.length(3); + expect(args[0].toJSON()).to.be(1); + expect(kwargs).to.only.have.keys('ok', 'nok') + expect(kwargs.nok.toJSON()).to.be(false); + return kwargs.ok; + } + })).to.be(true); + }); +}); +describe('issubclass', function () { + it('should say a type is its own subclass', function () { + expect(py.issubclass.__call__([py.dict, py.dict]).toJSON()) + .to.be(true); + expect(py.eval('issubclass(dict, dict)')) + .to.be(true); + }); + it('should work with subtypes', function () { + expect(py.issubclass.__call__([py.bool, py.object]).toJSON()) + .to.be(true); + }); +}); +describe('builtins', function () { + it('should aways be available', function () { + expect(py.eval('bool("foo")')).to.be(true); + }); +}); + +describe('numerical protocols', function () { + describe('True numbers (float)', function () { + describe('Basic arithmetic', function () { + it('can be added', function () { + expect(py.eval('1 + 1')).to.be(2); + expect(py.eval('1.5 + 2')).to.be(3.5); + expect(py.eval('1 + -1')).to.be(0); + }); + it('can be subtracted', function () { + expect(py.eval('1 - 1')).to.be(0); + expect(py.eval('1.5 - 2')).to.be(-0.5); + expect(py.eval('2 - 1.5')).to.be(0.5); + }); + it('can be multiplied', function () { + expect(py.eval('1 * 3')).to.be(3); + expect(py.eval('0 * 5')).to.be(0); + expect(py.eval('42 * -2')).to.be(-84); + }); + it('can be divided', function () { + expect(py.eval('1 / 2')).to.be(0.5); + expect(py.eval('2 / 1')).to.be(2); + }); + }); + }); + describe('Strings', function () { + describe('Basic arithmetics operators', function () { + it('can be added (concatenation)', function () { + expect(py.eval('"foo" + "bar"')).to.be('foobar'); + }); + }); + }); +});
\ No newline at end of file |
