Parse traversal parameters and use empty graph
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/251ccbab Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/251ccbab Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/251ccbab Branch: refs/heads/TINKERPOP-1489 Commit: 251ccbabf61b6b013f99e09db3a68a6077679ee5 Parents: f80e448 Author: Jorge Bay Gondra <jorgebaygon...@gmail.com> Authored: Fri Nov 24 15:04:41 2017 +0100 Committer: Jorge Bay Gondra <jorgebaygon...@gmail.com> Committed: Mon Jan 8 11:30:47 2018 +0100 ---------------------------------------------------------------------- gremlin-javascript/glv/PackageJson.template | 5 +- .../gremlin-javascript/lib/structure/graph.js | 31 ++++ .../javascript/gremlin-javascript/package.json | 5 +- .../test/cucumber/feature-steps.js | 156 +++++++++++++++---- .../gremlin-javascript/test/cucumber/world.js | 33 +++- .../test/integration/remote-connection-tests.js | 2 +- .../test/integration/traversal-test.js | 2 +- 7 files changed, 195 insertions(+), 39 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/251ccbab/gremlin-javascript/glv/PackageJson.template ---------------------------------------------------------------------- diff --git a/gremlin-javascript/glv/PackageJson.template b/gremlin-javascript/glv/PackageJson.template index 52d2c1d..4f1ac05 100644 --- a/gremlin-javascript/glv/PackageJson.template +++ b/gremlin-javascript/glv/PackageJson.template @@ -37,7 +37,8 @@ }, "devDependencies": { "mocha": "~4.0.1", - "cucumber": "~3.1.0" + "cucumber": "~3.1.0", + "chai": "~4.1.2" }, "repository": { "type": "git", @@ -48,7 +49,7 @@ }, "scripts": { "test": "./node_modules/.bin/mocha test/unit test/integration -t 5000", - "features": "./node_modules/.bin/cucumber.js --require test/cucumber ../../../../../gremlin-test/features/map/Count.feature", + "features": "./node_modules/.bin/cucumber.js --require test/cucumber ../../../../../gremlin-test/features/map/AddVertex.feature", "unit-test": "./node_modules/.bin/mocha test/unit" }, "engines": { http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/251ccbab/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/graph.js ---------------------------------------------------------------------- diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/graph.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/graph.js index 4ef3fd5..95fd30f 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/graph.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/graph.js @@ -128,6 +128,37 @@ function Path(labels, objects) { this.objects = objects; } +Path.prototype.equals = function (other) { + if (!(other instanceof Path)) { + return false; + } + if (other === this) { + return true; + } + return areEqual(this.objects, other.objects) && areEqual(this.labels, other.labels); +}; + +function areEqual(obj1, obj2) { + if (obj1 === obj2) { + return true; + } + if (typeof obj1.equals === 'function') { + return obj1.equals(obj2); + } + if (Array.isArray(obj1) && Array.isArray(obj2)) { + if (obj1.length !== obj2.length) { + return false; + } + for (let i = 0; i < obj1.length; i++) { + if (!areEqual(obj1[i], obj2[i])){ + return false; + } + } + return true; + } + return false; +} + module.exports = { Edge: Edge, Graph: Graph, http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/251ccbab/gremlin-javascript/src/main/javascript/gremlin-javascript/package.json ---------------------------------------------------------------------- diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/package.json b/gremlin-javascript/src/main/javascript/gremlin-javascript/package.json index 43cf35f..aae9eb9 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/package.json +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/package.json @@ -18,7 +18,8 @@ }, "devDependencies": { "mocha": "~4.0.1", - "cucumber": "~3.1.0" + "cucumber": "~3.1.0", + "chai": "~4.1.2" }, "repository": { "type": "git", @@ -29,7 +30,7 @@ }, "scripts": { "test": "./node_modules/.bin/mocha test/unit test/integration -t 5000", - "features": "./node_modules/.bin/cucumber.js --require test/cucumber ../../../../../gremlin-test/features/map/Count.feature", + "features": "./node_modules/.bin/cucumber.js --require test/cucumber ../../../../../gremlin-test/features/", "unit-test": "./node_modules/.bin/mocha test/unit" }, "engines": { http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/251ccbab/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js ---------------------------------------------------------------------- diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js index 5270b39..6625f29 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js @@ -23,36 +23,60 @@ 'use strict'; const defineSupportCode = require('cucumber').defineSupportCode; -const assert = require('assert'); const vm = require('vm'); +const expect = require('chai').expect; const graphModule = require('../../lib/structure/graph'); const graphTraversalModule = require('../../lib/process/graph-traversal'); const traversalModule = require('../../lib/process/traversal'); const Graph = graphModule.Graph; +const Path = graphModule.Path; const __ = graphTraversalModule.statics; +const parsers = [ + [ 'd\\[([\\d.]+)\\]\\.[ilfdm]', toNumeric ], + [ 'v\\[(.+)\\]', toVertex ], + [ 'v\\[(.+)\\]\\.id', toVertexId ], + [ 'v\\[(.+)\\]\\.sid', toVertexIdString ], + [ 'e\\[(.+)\\]', toEdge ], + [ 'e\\[(.+)\\]\\.id', toEdgeId ], + [ 'e\\[(.+)\\]\\.sid', toEdgeIdString ], + [ 'p\\[(.+)\\]', toPath ], + [ 'l\\[(.*)\\]', toArray ], + [ 's\\[(.*)\\]', toArray ], + [ 'm\\[(.+)\\]', toMap ], + [ 'c\\[(.+)\\]', toLambda ] +].map(x => [ new RegExp('^' + x[0] + '$'), x[1] ]); + defineSupportCode(function(methods) { methods.Given(/^the (.+) graph$/, function (graphName) { + this.graphName = graphName; + const data = this.getData(); + this.g = new Graph().traversal().withRemote(data.connection); if (graphName === 'empty') { - //TODO + return this.cleanEmptyGraph(); } - const data = this.getDataByGraphName(graphName); - this.graphName = graphName; - this.g = new Graph().traversal().withRemote(data.connection) }); - methods.Given('the graph initializer of', function () { - //TODO + methods.Given('the graph initializer of', function (traversalText) { + const traversal = vm.runInNewContext(translate(traversalText), getSandbox(this.g, this.parameters)); + return traversal.toList(); }); methods.Given('an unsupported test', () => {}); methods.Given('the traversal of', function (traversalText) { - this.traversal = vm.runInNewContext(translate(traversalText), getSandbox(this.g)); + this.traversal = vm.runInNewContext(translate(traversalText), getSandbox(this.g, this.parameters)); }); - methods.Given(/^$/, function (paramName, stringValue) { - //TODO: Add parameter + methods.Given(/^using the parameter (.+) defined as "(.+)"$/, function (paramName, stringValue) { + let p = Promise.resolve(); + if (this.graphName === 'empty') { + p = this.loadEmptyGraphData(); + } + const self = this; + return p.then(() => { + self.parameters[paramName] = parseValue.call(self, stringValue); + }); }); methods.When('iterated to list', function () { @@ -63,35 +87,44 @@ defineSupportCode(function(methods) { return this.traversal.next().then(it => this.result = it.value); }); - methods.Then(/^the result should be (\w+)$/, function (characterizedAs, resultTable) { + methods.Then(/^the result should be (\w+)$/, function assertResult(characterizedAs, resultTable) { + if (characterizedAs === 'empty') { + expect(this.result).to.be.empty; + if (typeof resultTable === 'function'){ + return resultTable(); + } + return; + } + const expectedResult = resultTable.rows().map(row => parseRow.call(this, row)); switch (characterizedAs) { - case 'empty': - assert.strictEqual(0, result.length); - break; case 'ordered': - const expectedResult = resultTable.rows().map(parseRow); console.log('--- ordered', expectedResult); + expect(this.result).to.have.deep.ordered.members(expectedResult); + break; + case 'unordered': + console.log('--- unordered expected:', expectedResult); + console.log('--- obtained:', this.result); + expect(this.result).to.have.deep.members(expectedResult); break; - } - //TODO - if (typeof resultTable === 'function'){ - return resultTable(); } }); - methods.Then(/^the graph should return (\d+) for count of (.+)$/, function (stringCount, traversalString) { - + methods.Then(/^the graph should return (\d+) for count of "(.+)"$/, function (stringCount, traversalText) { + const traversal = vm.runInNewContext(translate(traversalText), getSandbox(this.g, this.parameters)); + return traversal.toList().then(list => { + expect(list).to.have.lengthOf(parseInt(stringCount, 10)); + }); }); methods.Then(/^the result should have a count of (\d+)$/, function (stringCount) { - + expect(this.result).to.have.lengthOf(parseInt(stringCount, 10)); }); - methods.Then('nothing should happen because', () => {}); + methods.Then('nothing should happen because', _ => {}); }); -function getSandbox(g) { - return { +function getSandbox(g, parameters) { + const sandbox = { g: g, __: __, Cardinality: traversalModule.cardinality, @@ -104,14 +137,79 @@ function getSandbox(g) { Operator: traversalModule.operator, T: traversalModule.t, }; + // Pass the parameter to the sandbox + Object.keys(parameters).forEach(paramName => sandbox[paramName] = parameters[paramName]); + return sandbox; } function translate(traversalText) { - traversalText = traversalText.replace('.in(', '.in_('); - traversalText = traversalText.replace('.from(', '.from_('); + // Remove escaped chars + traversalText = traversalText.replace(/\\"/g, '"'); + // Change according to naming convention + traversalText = traversalText.replace(/\.in\(/g, '.in_('); + traversalText = traversalText.replace(/\.from\(/g, '.from_('); return traversalText; } function parseRow(row) { - return row[0]; -} \ No newline at end of file + return parseValue.call(this, row[0]); +} + +function parseValue(stringValue) { + let extractedValue = null; + let parser = null; + for (let item of parsers) { + let re = item[0]; + let match = re.exec(stringValue); + if (match && match.length > 1) { + parser = item[1]; + extractedValue = match[1]; + break; + } + } + return parser !== null ? parser.call(this, extractedValue) : stringValue; +} + +function toNumeric(stringValue) { + return parseFloat(stringValue); +} + +function toVertex(name) { + return this.getData().vertices[name]; +} + +function toVertexId(name) { + return toVertex.call(this, name).id; +} + +function toVertexIdString(name) { + return toVertex.call(this, name).id.toString(); +} + +function toEdge(name) { + return this.getData().edges[name]; +} + +function toEdgeId(name) { + return toEdge.call(this, name).id; +} + +function toEdgeIdString(name) { + return toEdge.call(this, name).id.toString(); +} + +function toPath(value) { + const parts = value.split(','); + return new Path(parts.map(_ => new Array(0)), parts.map(x => parseValue.call(this, x))); +} + +function toArray(stringList) { + if (stringList === '') { + return new Array(0); + } + return stringList.split(',').map(x => parseValue.call(this, x)); +} + +function toMap() {} + +function toLambda() {} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/251ccbab/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/world.js ---------------------------------------------------------------------- diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/world.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/world.js index 8f72c58..946d124 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/world.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/world.js @@ -37,18 +37,43 @@ defineSupportCode(function (methods) { this.traversal = null; this.result = null; this.cache = null; + this.graphName = null; + this.parameters = {}; } - TinkerPopWorld.prototype.getDataByGraphName = function (name) { - return this.cache[name]; + TinkerPopWorld.prototype.getData = function () { + if (!this.graphName) { + throw new Error("Graph name is not set"); + } + return this.cache[this.graphName]; + }; + + TinkerPopWorld.prototype.cleanEmptyGraph = function () { + const connection = this.cache['empty'].connection; + const g = new Graph().traversal().withRemote(connection); + return g.V().drop().toList(); + }; + + TinkerPopWorld.prototype.loadEmptyGraphData = function () { + const cacheData = this.cache['empty']; + const c = cacheData.connection; + return Promise.all([ getVertices(c), getEdges(c) ]).then(values => { + cacheData.vertices = values[0]; + cacheData.edges = values[1]; + }); }; methods.setWorldConstructor(TinkerPopWorld); methods.BeforeAll(function () { // load all traversals - const promises = ['modern', 'classic', 'crew', 'grateful'].map(graphName => { - const connection = helper.getConnection('g' + graphName); + const promises = ['modern', 'classic', 'crew', 'grateful', 'empty'].map(graphName => { + let connection = null; + if (graphName === 'empty') { + connection = helper.getConnection('ggraph'); + return connection.open().then(() => cache['empty'] = { connection: connection }); + } + connection = helper.getConnection('g' + graphName); return connection.open() .then(() => Promise.all([getVertices(connection), getEdges(connection)])) .then(values => { http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/251ccbab/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/remote-connection-tests.js ---------------------------------------------------------------------- diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/remote-connection-tests.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/remote-connection-tests.js index 268773c..e4895fe 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/remote-connection-tests.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/remote-connection-tests.js @@ -39,7 +39,7 @@ describe('DriverRemoteConnection', function () { }); describe('#submit()', function () { it('should send the request and parse the response', function () { - return connection.submit(new Bytecode().addStep('addV', [ 'person' ]).addStep('property', [ 'name', 'marko'])) + return connection.submit(new Bytecode().addStep('V', []).addStep('tail', [])) .then(function (response) { assert.ok(response); assert.ok(response.traversers); http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/251ccbab/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js ---------------------------------------------------------------------- diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js index adb7d6c..7109427 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js @@ -44,7 +44,7 @@ describe('Traversal', function () { var g = new Graph().traversal().withRemote(connection); return g.V().toList().then(function (list) { assert.ok(list); - assert.strictEqual(list.length, 7); + assert.strictEqual(list.length, 6); list.forEach(v => assert.ok(v instanceof Vertex)); }); });