| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| import {Dsl, KIND, DslParser, Tokenizer, DslError} from './dsl.model'; |
| import {Entity} from './entity.model'; |
| |
| describe('Dsl Component Model', ()=>{ |
| describe('Dsl Initialization and expression generation', ()=>{ |
| it('should initialize correctly', ()=> { |
| let dsl1 = new Dsl(); |
| expect(dsl1._id).not.toBeNull(); |
| expect(dsl1.kind).toBe(KIND.STRING); |
| expect(dsl1.hasName()).toBe(true); |
| expect(dsl1.hasParent()).toBe(false); |
| expect(dsl1.hasRef()).toBe(false); |
| |
| let dsl2 = new Dsl(KIND.STRING, 'hello'); |
| expect(dsl2._id).not.toBeNull(); |
| expect(dsl2.kind).toBe(KIND.STRING); |
| expect(dsl2.name).toBe('hello'); |
| expect(dsl2.hasParent()).toBe(false); |
| expect(dsl2.hasRef()).toBe(false); |
| |
| let dsl3 = new Dsl(KIND.UTILITY, 'formatString'); |
| expect(dsl3._id).not.toBeNull(); |
| expect(dsl3.kind).toBe(KIND.UTILITY); |
| expect(dsl3.name).toBe('formatString'); |
| }); |
| |
| it('should set and get name correctly', ()=> { |
| let dsl = new Dsl(); |
| dsl.name = 'Test' + 'Name'; |
| expect(dsl.name).toBe('TestName'); |
| }); |
| |
| it('should set and get ref correctly', ()=> { |
| let entity = new Entity(); |
| const CONFIG_OBJECT = { |
| textKey: 'textValue', |
| boolKey: false, |
| numKey: 123456789, |
| nullKey: null, |
| objectKey: { |
| key: 'val', |
| } |
| }; |
| entity.setEntityFromJson(CONFIG_OBJECT); |
| |
| let dsl = new Dsl(); |
| dsl.ref = entity; |
| |
| expect(dsl.ref).toEqual(entity); |
| expect(dsl.kind).toBe(KIND.ENTITY); |
| }); |
| |
| it('should set and get parent hierarchy correctly', ()=> { |
| let dsl = new Dsl(); |
| let child = new Dsl(); |
| let grandchild = new Dsl(); |
| |
| child.parent = dsl; |
| grandchild.parent = child; |
| |
| expect(child.parent).toBe(dsl); |
| expect(grandchild.parent).toBe(child); |
| expect(grandchild.parent.parent).toBe(dsl); |
| expect(grandchild.parent.parent.parent).toBeUndefined(); |
| |
| expect(dsl.getRoot()).toBe(dsl); |
| expect(child.getRoot()).toBe(dsl); |
| expect(grandchild.getRoot()).toBe(dsl); |
| }); |
| |
| it('should handle params correctly', ()=> { |
| let dsl = new Dsl(KIND.METHOD); |
| |
| expect(dsl.params.length).toBe(0); |
| |
| let param1 = new Dsl(); |
| let param2 = new Dsl(); |
| let param3 = new Dsl(); |
| |
| dsl.param(param1); |
| dsl.param(param2); |
| dsl.param(param3); |
| |
| expect(dsl.params).toEqual([param1, param2, param3]); |
| |
| expect(param1.parent).toBe(dsl); |
| expect(param1.getRoot()).toBe(dsl); |
| expect(param2.parent).toBe(dsl); |
| expect(param2.getRoot()).toBe(dsl); |
| expect(param3.parent).toBe(dsl); |
| expect(param3.getRoot()).toBe(dsl); |
| }); |
| |
| it('should handle prev and next correctly', ()=> { |
| let dsl = new Dsl(KIND.METHOD); |
| |
| expect(dsl.next).toBeUndefined(); |
| expect(dsl.prev).toBeUndefined(); |
| |
| let meth1 = new Dsl(KIND.METHOD); |
| let meth2 = new Dsl(KIND.METHOD); |
| let meth3 = new Dsl(KIND.METHOD); |
| |
| dsl.chain(meth1); |
| expect(dsl.next).toBe(meth1); |
| expect(meth1.prev).toBe(dsl); |
| |
| dsl.chain(meth2).chain(meth3); |
| expect(dsl.prev).toBeUndefined(); |
| expect(dsl.next).toBe(meth1); |
| expect(dsl.getLastMethod()).toBe(meth3); |
| expect(meth1.prev).toBe(dsl); |
| expect(meth1.next).toBe(meth2); |
| expect(meth2.prev).toBe(meth1); |
| expect(meth2.next).toBe(meth3); |
| expect(meth3.prev).toBe(meth2); |
| expect(meth3.next).toBeUndefined(); |
| |
| dsl.popChainedMethod(); |
| expect(dsl.prev).toBeUndefined(); |
| expect(dsl.next).toBe(meth1); |
| expect(dsl.getLastMethod()).toBe(meth2); |
| expect(meth1.prev).toBe(dsl); |
| expect(meth1.next).toBe(meth2); |
| expect(meth2.prev).toBe(meth1); |
| expect(meth2.next).toBeUndefined(); |
| |
| expect(dsl.equals(dsl)); |
| }); |
| |
| it('should generate YAML correctly', ()=> { |
| // constants |
| let dsl1 = new Dsl(KIND.STRING, 'hello world'); |
| |
| expect(dsl1.toString()).toEqual('"hello world"'); |
| expect(dsl1.generate()).toEqual('"hello world"'); |
| |
| let dsl2 = new Dsl(KIND.NUMBER, 10.2); |
| |
| expect(dsl2.toString()).toEqual('10.2'); |
| expect(dsl2.generate()).toEqual('10.2'); |
| |
| // target function without parameters |
| let dsl3 = new Dsl(KIND.TARGET, 'self'); |
| expect(dsl3.generateParams()).toEqual(''); |
| expect(dsl3.toString()).toEqual('$brooklyn:self()'); |
| expect(dsl3.generate()).toEqual('self()'); |
| |
| // method with one parameter |
| let awr1 = new Dsl(KIND.METHOD, 'attributeWhenReady'); |
| let par1 = new Dsl(KIND.STRING, 'http.port'); |
| awr1.param(par1); |
| dsl3.chain(awr1); |
| expect(awr1.generate()).toEqual('attributeWhenReady("http.port")'); |
| |
| // the full DSL expression |
| expect(dsl3.toString()).toEqual('$brooklyn:self().attributeWhenReady("http.port")'); |
| |
| // chained target functions and methods: parent().sibling(...).attributeWhenReady(...) |
| let dsl4 = new Dsl(KIND.TARGET, 'parent'); |
| let sib1 = new Dsl(KIND.TARGET, 'sibling'); |
| let par2 = new Dsl(KIND.STRING, 'rootNode'); |
| sib1.param(par2); |
| dsl4.chain(sib1); |
| expect(sib1.generate()).toEqual('sibling("rootNode")'); |
| |
| let awr2 = new Dsl(KIND.METHOD, 'attributeWhenReady'); |
| let par3 = new Dsl(KIND.STRING, 'http.port'); |
| awr2.param(par3); |
| dsl4.chain(awr2); |
| expect(awr2.generate()).toEqual('attributeWhenReady("http.port")'); |
| |
| expect(dsl4.toString()).toEqual('$brooklyn:parent().sibling("rootNode").attributeWhenReady("http.port")'); |
| |
| // utility function with multiple parameters |
| let func1 = new Dsl(KIND.UTILITY, 'formatString'); |
| let s1 = new Dsl(KIND.STRING, '%s:%s'); |
| let s2 = new Dsl(KIND.STRING, 'localhost'); |
| let s3 = new Dsl(KIND.STRING, '8081'); |
| func1.param(s1); |
| func1.param(s2); |
| func1.param(s3); |
| expect(func1.generate()).toEqual('formatString("%s:%s", "localhost", "8081")'); |
| |
| // the full DSL expression |
| expect(func1.toString()).toEqual('$brooklyn:formatString("%s:%s", "localhost", "8081")'); |
| |
| // utility function with multiple parameters and nested calls |
| let func2 = new Dsl(KIND.UTILITY, 'formatString'); |
| let ss1 = new Dsl(KIND.STRING, '%s:%s'); |
| let ss2 = new Dsl(KIND.STRING, 'localhost'); |
| func2.param(ss1); |
| func2.param(ss2); |
| func2.param(dsl4); |
| expect(func2.generate()).toEqual('formatString("%s:%s", "localhost", $brooklyn:parent().sibling("rootNode").attributeWhenReady("http.port"))'); |
| |
| // the full DSL expression |
| expect(func2.toString()).toEqual('$brooklyn:formatString("%s:%s", "localhost", $brooklyn:parent().sibling("rootNode").attributeWhenReady("http.port"))'); |
| |
| // utility function with multiple port parameters |
| let func3 = new Dsl(KIND.UTILITY, 'formatString'); |
| let sss1 = new Dsl(KIND.STRING, 'Using ports %s or %s'); |
| let sss2 = new Dsl(KIND.PORT, '8080+'); |
| let sss3 = new Dsl(KIND.PORT, '8080-10010'); |
| |
| // The ports only |
| expect(sss2.generate()).toEqual('8080+'); |
| expect(sss2.toString()).toEqual('8080+'); |
| expect(sss3.generate()).toEqual('8080-10010'); |
| expect(sss3.toString()).toEqual('8080-10010'); |
| |
| func3.param(sss1); |
| func3.param(sss2); |
| func3.param(sss3); |
| expect(func3.generate()).toEqual('formatString("Using ports %s or %s", "8080+", "8080-10010")'); |
| |
| // the full DSL expression |
| expect(func3.toString()).toEqual('$brooklyn:formatString("Using ports %s or %s", "8080+", "8080-10010")'); |
| }); |
| |
| it('should handle equals correctly', ()=> { |
| let dsl1 = new Dsl(KIND.METHOD, 'hello'); |
| let dsl2 = new Dsl(KIND.METHOD, 'hello'); |
| |
| expect(dsl1.equals(dsl2)).toBe(true); |
| expect(dsl2.equals(dsl1)).toBe(true); |
| |
| let meth1 = new Dsl(KIND.METHOD, "print"); |
| let meth2 = new Dsl(KIND.METHOD, "print"); |
| |
| dsl1.chain(meth1); |
| dsl2.chain(meth2); |
| |
| expect(dsl1.equals(dsl2)).toBe(true); |
| expect(dsl2.equals(dsl1)).toBe(true); |
| |
| let param1 = new Dsl(KIND.STRING, "world"); |
| meth1.param(param1); |
| |
| let param2 = new Dsl(KIND.STRING, "world"); |
| meth2.param(param2); |
| |
| expect(dsl1.equals(dsl2)).toBe(true); |
| |
| expect(meth1.equals(meth2)).toBe(true); |
| |
| }); |
| }); |
| }); |
| |
| describe('Dsl parser', ()=> { |
| |
| describe('Tokenizer', () => { |
| let qs = '"hello world"'; // a quoted string |
| let words = 'word1 word2 word3 '; |
| let wsep = 'word1 , word2, word3 ,word4 '; |
| |
| it('should initialize correctly', () => { |
| let t = new Tokenizer(qs); |
| }); |
| |
| it('should tokenize a double-quoted string', () => { |
| let t = new Tokenizer(qs); |
| expect(t.atEndOfInput()).toBe(false); |
| expect(t.peek('"')).toBe(true); |
| expect(t.nextQuotedString()).toEqual(qs); |
| }); |
| |
| it('should tokenize a single-quoted string', () => { |
| let sqs = "'I Love Single ''Quotes'''"; |
| let t = new Tokenizer(sqs); |
| expect(t.nextSingleQuotedString()).toEqual(sqs); |
| }); |
| |
| it('should tokenize words', () => { |
| let t = new Tokenizer(words); |
| expect(t.atEndOfInput()).toBe(false); |
| expect(t.peek('"')).toBe(false); |
| expect(t.nextIdentifier()).toEqual('word1'); |
| expect(t.nextIdentifier()).toEqual('word2'); |
| expect(t.nextIdentifier()).toEqual('word3'); |
| expect(t.atEndOfInput()).toBe(true); |
| }); |
| |
| it('should tokenize words with separators', () => { |
| let t = new Tokenizer(wsep); |
| expect(t.atEndOfInput()).toBe(false); |
| expect(t.nextIdentifier()).toEqual('word1'); |
| expect(t.next(',')).toEqual(','); |
| expect(t.nextIdentifier()).toEqual('word2'); |
| expect(t.next(',')).toEqual(','); |
| expect(t.nextIdentifier()).toEqual('word3'); |
| expect(t.next(',')).toEqual(','); |
| expect(t.nextIdentifier()).toEqual('word4'); |
| expect(t.atEndOfInput()).toBe(true); |
| }); |
| }); |
| |
| describe('DslParser', () => { |
| |
| it('should initialize correctly', () => { |
| let p = new DslParser("test"); |
| }); |
| |
| it('should parse a string literal', () => { |
| let p = new DslParser('"Hello"'); |
| let dsl = p.parse(); |
| expect(dsl).toBeDefined(); |
| expect(dsl.kind).toBe(KIND.STRING); |
| expect(dsl.name).toEqual('Hello'); |
| }); |
| |
| it('should parse a string literal in double quotes containing escaped quotes', () => { |
| let cstr2 = '"This is \\"quoted\\""'; |
| let p = new DslParser(cstr2); |
| let dsl = p.parse(); |
| expect(dsl).toBeDefined(); |
| expect(dsl.kind).toBe(KIND.STRING); |
| expect(dsl.name).toEqual('This is "quoted"'); |
| expect(dsl.toString()).toEqual(cstr2); // round-trip check |
| }); |
| |
| it('should parse a string literal in single quotes containing escaped quotes', () => { |
| let cstrYAML = "'This \"thing\" is ''quoted'''"; // YAML: 'This "thing" is ''quoted''' |
| let p = new DslParser(cstrYAML); |
| let dsl = p.parse(); |
| expect(dsl).toBeDefined(); |
| expect(dsl.kind).toBe(KIND.STRING); |
| expect(dsl.name).toEqual("This \"thing\" is 'quoted'"); |
| expect(dsl.toString()).toEqual('"This \\"thing\\" is \'quoted\'"'); // JSON: "This \"thing\" is 'quoted'" |
| }); |
| |
| it('should parse a variety of number literals', () => { |
| let nums = ['1', '123', '-1', '-123', |
| '.345', '-.345', '0.', '0.0', '-0.01', '+0.01', |
| '9999999999999.123', '-9999999999999.123']; |
| for (let cnum of nums) { |
| let p = new DslParser(cnum); |
| let dsl = p.parse(); |
| expect(dsl).toBeDefined(); |
| expect(dsl.kind).toBe(KIND.NUMBER); |
| expect(dsl.name).toEqual(Number(cnum).toString()); |
| expect(dsl.toString()).toEqual(Number(cnum).toString()); |
| } |
| }); |
| |
| it('should parse a variety of port ranges', () => { |
| let portRanges = ['0+', '8080+', '0-65535', '1024-4096']; |
| for (let range of portRanges) { |
| let p = new DslParser(range); |
| let dsl = p.parse(); |
| expect(dsl).toBeDefined(); |
| expect(dsl.kind).toBe(KIND.PORT); |
| expect(dsl.name).toEqual(range); |
| expect(dsl.toString()).toEqual(range); |
| } |
| }); |
| |
| it('should parse a utility function call with no params', () => { |
| let expr1 = '$brooklyn:formatString()'; |
| let p = new DslParser(expr1); |
| let dsl = p.parse(); |
| expect(dsl).toBeDefined(); |
| expect(dsl.kind).toBe(KIND.UTILITY); |
| expect(dsl.name).toEqual('formatString'); |
| expect(dsl.params.length).toBe(0); |
| expect(dsl.toString()).toEqual(expr1); // round-trip check |
| }); |
| |
| it('should parse a utility function call with one param', () => { |
| let expr2 = '$brooklyn:formatString("hello")'; |
| let p = new DslParser(expr2); |
| let dsl = p.parse(); |
| expect(dsl).toBeDefined(); |
| expect(dsl.kind).toBe(KIND.UTILITY); |
| expect(dsl.name).toEqual('formatString'); |
| expect(dsl.params.length).toBe(1); |
| expect(dsl.params[0].kind).toBe(KIND.STRING); |
| expect(dsl.toString()).toEqual(expr2); // round-trip check |
| }); |
| |
| it('should parse a utility function call with params', () => { |
| let expr3 = '$brooklyn:formatString("%s", "hello")'; |
| let p = new DslParser(expr3); |
| let dsl = p.parse(); |
| expect(dsl).toBeDefined(); |
| expect(dsl.kind).toBe(KIND.UTILITY); |
| expect(dsl.name).toEqual('formatString'); |
| expect(dsl.params.length).toBe(2); |
| expect(dsl.toString()).toEqual(expr3); // round-trip check |
| }); |
| |
| it('should parse a method function call with params', () => { |
| let method_expr = '$brooklyn:attributeWhenReady("sensor1")'; |
| let p = new DslParser(method_expr); |
| let dsl = p.parse(); |
| expect(dsl).toBeDefined(); |
| expect(dsl.kind).toBe(KIND.METHOD); |
| expect(dsl.name).toEqual('attributeWhenReady'); |
| expect(dsl.params.length).toBe(1); |
| expect(dsl.toString()).toEqual(method_expr); // round-trip check |
| }); |
| |
| it('should parse a target function call with no params', () => { |
| let target_expr = '$brooklyn:parent()'; |
| let p = new DslParser(target_expr); |
| let dsl = p.parse(); |
| expect(dsl).toBeDefined(); |
| expect(dsl.kind).toBe(KIND.TARGET); |
| expect(dsl.name).toEqual('parent'); |
| expect(dsl.params.length).toBe(0); |
| expect(dsl.toString()).toEqual(target_expr); // round-trip check |
| }); |
| |
| it('should parse a complex function call', () => { |
| let target_expr = '$brooklyn:formatString("%s:%s", "localhost", $brooklyn:parent().sibling("rootNode").attributeWhenReady("http.port"))'; |
| let p = new DslParser(target_expr); |
| let dsl = p.parse(); |
| expect(dsl).toBeDefined(); |
| expect(dsl.toString()).toEqual(target_expr); // round-trip check |
| }); |
| |
| it('should get references', () => { |
| let target_expr = '$brooklyn:formatString("%s:%s", $brooklyn:component("db").attributeWhenReady("host.address"), $brooklyn:component("nginx").attributeWhenReady("http.port"))'; |
| let p = new DslParser(target_expr); |
| let dsl = p.parse(); |
| expect(dsl).toBeDefined(); |
| expect(dsl.toString()).toEqual(target_expr); // round-trip check |
| expect(dsl.getReferences().length).toEqual(2); |
| }); |
| |
| }); |
| }); |
| |