From 8f22814fae6478ba466a4808dfce0fe638eab762 Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Thu, 28 Dec 2023 15:02:49 -0800 Subject: [PATCH 01/18] Implement comparison and feature test --- .../test/cucumber/element-comparison.js | 120 +++++++++ .../test/unit/element-comparison-test.js | 236 ++++++++++++++++++ 2 files changed, 356 insertions(+) create mode 100644 gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js create mode 100644 gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js new file mode 100644 index 00000000000..de741507e80 --- /dev/null +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js @@ -0,0 +1,120 @@ +/* + * 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. + */ + +function isElement(obj) { + return obj.hasOwnProperty('id') && obj.hasOwnProperty('label'); +} + +function deepSort(obj) { + if (!Array.isArray(obj)) { + return obj; + } else { + obj.map((item) => deepSort(item)); + } + return obj.sort(); +} + +function deepCopy(obj) { + if (typeof obj !== 'object' || obj === null) { + return obj; + } + let copy; + if (obj instanceof Map) { + copy = new Map(); + for (const [k, v] of obj.entries()) { + copy.set(k, deepCopy(v)); + } + return copy; + } + if (Array.isArray(obj)) { + copy = []; + for (let i = 0; i < obj.length; i++) { + copy[i] = deepCopy(obj[i]); + } + } else { + copy = {}; + for (let k in obj) { + if (obj.hasOwnProperty(k)) { + copy[k] = deepCopy(obj[k]); + } + } + } + return copy; +} + +function deepMembersById(a, b, ordered=false) { + a = deepCopy(a); + b = deepCopy(b); + console.log(a) + console.log(b) + if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) { + return a === b; + } + if (typeof a !== typeof b) { + return false; + } + if (isElement(a) || isElement(b)) { + return a.id === b.id; + } + if (a instanceof Map) { + a = Array.from(a.entries()); + b = Array.from(b.entries()); + } + if (Array.isArray(a)) { + if (a.length !== b.length) { + return false; + } + if (!ordered) { + a = deepSort(a); + b = deepSort(b); + } + for (let i = 0; i < a.length; i++) { + if (!deepMembersById(a[i], b[i], ordered)) { + return false; + } + } + return true; + } + const keysA = Object.keys(a); + const keysB = Object.keys(b); + if (keysA.length !== keysB.length) { + return false; + } + for (let k of keysA) { + if (!b.hasOwnProperty(k)) { + return false; + } + if (!deepMembersById(a[k], b[k], ordered)) { + return false; + } + } + return true; +} + +function deepMembersByIdOrdered(a, b) { + return deepMembersById(a, b, true) +} + +module.exports = { + deepCopy, + deepMembersById, + deepMembersByIdOrdered, + deepSort, + isElement +} \ No newline at end of file diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js new file mode 100644 index 00000000000..b34308b91d0 --- /dev/null +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js @@ -0,0 +1,236 @@ +/* + * 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. + */ + +const { expect } = require('chai'); +const { VertexProperty, Property, Vertex, Edge, Path } = require('../../lib/structure/graph'); +const { deepCopy, deepMembersById, deepMembersByIdOrdered, deepSort, isElement} = require('../cucumber/element-comparison'); + + +describe('primitives', function () { + it('should pass', function () { + expect(deepMembersById(1, 1)).to.be.true; + expect(deepMembersById(false, false)).to.be.true; + expect(deepMembersById(null, null)).to.be.true; + }); + + it('should fail', function () { + expect(deepMembersById(1, 2)).to.be.false; + expect(deepMembersById(true, false)).to.be.false; + expect(deepMembersById(0, "0")).to.be.false; + expect(deepMembersById(0, false)).to.be.false; + expect(deepMembersById(0, null)).to.be.false; + expect(deepMembersById(false, null)).to.be.false; + }); +}); + +describe('elements', function () { + const v1 = new Vertex(1, "dog", undefined); + const v2 = new Vertex(1, "cat", undefined); + const v3 = new Vertex(2, "cat", undefined); + + const e1 = new Edge(2, v1, "chases", v3, undefined); + const e2 = new Edge(3, v1, "chases", v3, undefined); + const e3 = new Edge(3, v2, "chases", v3, undefined); + + const vp1 = new VertexProperty(3, "size", "small", undefined); + const vp2 = new VertexProperty(3, "size", "large", undefined); + const vp3 = new VertexProperty(4, "size", "large", undefined); + + it('should pass with same id, different values', function () { + expect(deepMembersById(v1, v2)).to.be.true; + expect(deepMembersById(v3, e1)).to.be.true; + expect(deepMembersById(e2, e3)).to.be.true; + expect(deepMembersById(e3, vp1)).to.be.true; + expect(deepMembersById(vp1, vp2)).to.be.true; + }); + + it('should fail with different id, same values', function () { + expect(deepMembersById(v2, v3)).to.be.false; + expect(deepMembersById(e1, e2)).to.be.false; + expect(deepMembersById(vp2, vp3)).to.be.false; + }); +}); + +describe('property', function () { + const p1 = new Property("a", "1"); + const p2 = new Property("a", "1"); + const p3 = new Property("a", 1); + const p4 = new Property(1, 1); + const p5 = new Property(1, "a"); + + it('should pass only properties that match exactly', function () { + expect(deepMembersById(p1, p2)).to.be.true; + expect(deepMembersById(p1, p3)).to.be.false; + expect(deepMembersById(p1, p4)).to.be.false; + expect(deepMembersById(p3, p5)).to.be.false; + expect(deepMembersById(p3, p5)).to.be.false; + }); +}); + +describe('arrays', function () { + const a1 = [1, 2, 3]; + const a2 = [1, 2, 3]; + const a3 = [2, 1, 3]; + const a4 = [2, 2, 3]; + const a5 = [1, 2, 3, 4]; + + it('unordered', function () { + expect(deepMembersById(a1, a2)).to.be.true; + expect(deepMembersById(a1, a3)).to.be.true; + expect(deepMembersById(a1, a4)).to.be.false; + expect(deepMembersById(a1, a5)).to.be.false; + expect(deepMembersById(a5, a1)).to.be.false; + }); + + it('ordered', function () { + expect(deepMembersByIdOrdered(a1, a2)).to.be.true; + expect(deepMembersByIdOrdered(a1, a3)).to.be.false; + expect(deepMembersByIdOrdered(a1, a4)).to.be.false; + expect(deepMembersByIdOrdered(a1, a5)).to.be.false; + expect(deepMembersByIdOrdered(a5, a1)).to.be.false; + }); + + const a6 = [1, 2, a1]; + const a7 = [1, a3, 2]; + const a8 = [1, 2, a5]; + + const a9 = [1, a1, a1]; + const a10 = [1, a1, a3]; + + const a11 = [a9] + const a12 = [a10] + + it('unordered nested', function () { + expect(deepMembersById(a6, a7)).to.be.true; + expect(deepMembersById(a6, a8)).to.be.false; + expect(deepMembersById(a9, a10)).to.be.true; + expect(deepMembersById(a11, a12)).to.be.true; + }); + + it('ordered nested', function () { + expect(deepMembersByIdOrdered(a6, a6)).to.be.true; + expect(deepMembersByIdOrdered(a6, a7)).to.be.false; + expect(deepMembersByIdOrdered(a6, a8)).to.be.false; + expect(deepMembersByIdOrdered(a9, a10)).to.be.false; + expect(deepMembersByIdOrdered(a10, a10)).to.be.true; + expect(deepMembersByIdOrdered(a11, a11)).to.be.true; + expect(deepMembersByIdOrdered(a11, a12)).to.be.false; + }); +}); + +describe('map', function () { + const m1 = new Map([ + ['ripple', [new Vertex(5, 'software', undefined)]], + ['peter', [new Vertex(6, 'person', undefined)]], + ['vadas', [new Vertex(2, 'person', undefined)]] + ]); + const m2 = new Map([ + ['ripple', [new Vertex(5, 'software', undefined)]], + ['peter', [new Vertex(6, 'person', undefined)]] + ]); + const m3 = new Map([ + ['ripple', [new Vertex(5, 'software', undefined)]], + ['vadas', [new Vertex(2, 'person', undefined)]], + ['peter', [new Vertex(6, 'person', undefined)]] + ]); + const m4 = new Map([ + ['ripple', [new Vertex(5, 'software', undefined)]], + ['vadas', [new Vertex(6, 'person', undefined)]], + ['peter', [new Vertex(2, 'person', undefined)]] + ]); + + it('unordered', function () { + expect(deepMembersById(m1, m1)).to.be.true; + expect(deepMembersById(m1, m2)).to.be.false; + expect(deepMembersById(m1, m3)).to.be.true; + expect(deepMembersById(m1, m4)).to.be.false; + }); + + it('ordered', function () { + expect(deepMembersByIdOrdered(m1, m1)).to.be.true; + expect(deepMembersByIdOrdered(m1, m2)).to.be.false; + expect(deepMembersByIdOrdered(m1, m3)).to.be.false; + expect(deepMembersByIdOrdered(m1, m4)).to.be.false; + }); +}); + +describe('objects', function () { + const obj1 = { + k1: "v1", + k2: "v2", + k3: { k4: "v4", k5: "v5" } + }; + const obj2 = { + k1: "v1", + k2: "v2", + k3: { k4: "v4", k5: "v5" } + }; + const obj3 = { + k2: "v2", + k1: "v1", + k3: { k5: "v5", k4: "v4" } + }; + const obj4 = { + k1: "v1", + k2: "v2", + k3: { k4: "v4" } + }; + const obj5 = { + k1: "v1", + k2: "v2", + k3: { k4: "v5", k5: "v4" } + }; + + it('should pass', function () { + expect(deepMembersById(obj1, obj2)).to.be.true; // identical + expect(deepMembersById(obj1, obj3)).to.be.true; // order swapped + }); + + it('should fail', function () { + expect(deepMembersById(obj1, obj4)).to.be.false; // missing nested kvp + expect(deepMembersById(obj1, obj5)).to.be.false; // values swapped + }); + + const obj6 = { + k1: "v1", + k2: "v2", + k3: [ 1, 2, 3 ] + }; + + const obj7 = { + k3: [ 1, 2, 3 ], + k1: "v1", + k2: "v2" + }; + const obj8 = { + k1: "v1", + k2: "v2", + k3: [ 2, 1, 3 ] + } + + it('unordered', function () { + expect(deepMembersById(obj6, obj7)).to.be.true; + expect(deepMembersById(obj6, obj8)).to.be.true; + }); + + it('ordered', function () { + expect(deepMembersByIdOrdered(obj6, obj7)).to.be.true; // kvp can be unordered + expect(deepMembersByIdOrdered(obj6, obj8)).to.be.false; // array must be ordered + }); +}); From f0d22d3506806e8c91e81ef84ebcd0ab67953ee3 Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Thu, 28 Dec 2023 15:06:36 -0800 Subject: [PATCH 02/18] Use new comparison functions --- .../gremlin-javascript/test/cucumber/feature-steps.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 8794efc456d..3d6c977fcc3 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 @@ -41,7 +41,7 @@ const t = traversalModule.t; const P = traversalModule.P; const direction = traversalModule.direction; const merge = traversalModule.merge; - +const { deepMembersById, deepMembersByIdOrdered } = require('../unit/element-comparison') const parsers = [ [ 'str\\[(.*)\\]', (stringValue) => stringValue ], //returns the string value as is [ 'vp\\[(.+)\\]', toVertexProperty ], @@ -237,10 +237,10 @@ Then(/^the result should be (\w+)$/, function assertResult(characterizedAs, resu const expectedResult = resultTable.rows().map(row => parseRow.call(this, row)); switch (characterizedAs) { case 'ordered': - expect(toCompare(this.result)).to.have.deep.ordered.members(expectedResult); + expect(deepMembersByIdOrdered(toCompare(this.result), expectedResult)); break; case 'unordered': - expect(toCompare(this.result)).to.have.deep.members(expectedResult); + expect(deepMembersById(toCompare(this.result), expectedResult)); break; case 'of': // result is a subset of the expected From a0353115913a5e37d523d562be73a21d7dcb62af Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Tue, 2 Jan 2024 11:50:27 -0800 Subject: [PATCH 03/18] Add path object unit tests --- .../test/unit/element-comparison-test.js | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js index b34308b91d0..e3d12cfdd7e 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js @@ -212,7 +212,6 @@ describe('objects', function () { k2: "v2", k3: [ 1, 2, 3 ] }; - const obj7 = { k3: [ 1, 2, 3 ], k1: "v1", @@ -233,4 +232,23 @@ describe('objects', function () { expect(deepMembersByIdOrdered(obj6, obj7)).to.be.true; // kvp can be unordered expect(deepMembersByIdOrdered(obj6, obj8)).to.be.false; // array must be ordered }); + + const path1 = new Path(["path"], [new Vertex(1, 'person', undefined), + new Vertex(2, 'person', undefined)]); + const path2 = new Path(["path"], [new Vertex(1, 'person', undefined)]); + const path3 = new Path(["path"], [new Vertex(2, 'person', undefined), + new Vertex(1, 'person', undefined)]); + + it('unordered', function () { + expect(deepMembersById(path1, path1)).to.be.true; + expect(deepMembersById(path1, path2)).to.be.false; + expect(deepMembersById(path1, path3)).to.be.false; + }); + + it('ordered', function () { + expect(deepMembersById(path1, path1)).to.be.true; + expect(deepMembersById(path1, path2)).to.be.false; + expect(deepMembersById(path1, path3)).to.be.false; + }); + }); From a441a5543d9033a330c691bb495f4ce6b949b7bd Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Tue, 2 Jan 2024 11:51:34 -0800 Subject: [PATCH 04/18] Implement includes comparison --- .../test/cucumber/element-comparison.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js index de741507e80..6f53ab89580 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js @@ -25,9 +25,8 @@ function deepSort(obj) { if (!Array.isArray(obj)) { return obj; } else { - obj.map((item) => deepSort(item)); + return obj.map((item) => deepSort(item)).sort(); } - return obj.sort(); } function deepCopy(obj) { @@ -111,8 +110,21 @@ function deepMembersByIdOrdered(a, b) { return deepMembersById(a, b, true) } +function deepIncludesById(array, subset) { + console.log(array); + console.log(subset); + for (let item of subset) { + const included = array.some((arrayItem) => deepMembersById(item, arrayItem)); + if (!included) { + return false; + } + } + return true; +} + module.exports = { deepCopy, + deepIncludesById, deepMembersById, deepMembersByIdOrdered, deepSort, From ee3a41bdd9894e95219d8825e432b3160d529d89 Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Thu, 4 Jan 2024 13:53:56 -0800 Subject: [PATCH 05/18] checkpoint --- .../test/cucumber/element-comparison.js | 145 ++++- .../test/cucumber/element-deep-eql.js | 550 ++++++++++++++++++ .../test/cucumber/feature-steps.js | 17 +- .../gremlin-javascript/test/cucumber/world.js | 3 + .../test/unit/element-comparison-test.js | 13 + .../gremlin-javascript/test/unit/t.js | 226 +++++++ 6 files changed, 947 insertions(+), 7 deletions(-) create mode 100644 gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-deep-eql.js create mode 100644 gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/t.js diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js index 6f53ab89580..4e063483c0b 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js @@ -17,8 +17,12 @@ * under the License. */ +const chai = require('chai'); +//const { deepEqual, isElement } = require('./element-deep-eql'); +const deepEqual = require('deep-eql'); + function isElement(obj) { - return obj.hasOwnProperty('id') && obj.hasOwnProperty('label'); + return obj.hasOwnProperty('id') && obj.hasOwnProperty('label'); } function deepSort(obj) { @@ -106,6 +110,55 @@ function deepMembersById(a, b, ordered=false) { return true; } +function deepMembersById3(a, b) { + a = deepCopy(a); + b = deepCopy(b); + console.log(a) + console.log(b) + if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) { + return a === b; + } + if (typeof a !== typeof b) { + return false; + } + if (isElement(a) || isElement(b)) { + return a.id === b.id; + } + if (a instanceof Map) { + a = Array.from(a.entries()); + b = Array.from(b.entries()); + } + if (Array.isArray(a)) { + if (a.length !== b.length) { + return false; + } + if (!ordered) { + a = deepSort(a); + b = deepSort(b); + } + for (let i = 0; i < a.length; i++) { + if (!deepMembersById(a[i], b[i], ordered)) { + return false; + } + } + return true; + } + const keysA = Object.keys(a); + const keysB = Object.keys(b); + if (keysA.length !== keysB.length) { + return false; + } + for (let k of keysA) { + if (!b.hasOwnProperty(k)) { + return false; + } + if (!deepMembersById(a[k], b[k], ordered)) { + return false; + } + } + return true; +} + function deepMembersByIdOrdered(a, b) { return deepMembersById(a, b, true) } @@ -122,11 +175,95 @@ function deepIncludesById(array, subset) { return true; } +function isSubsetOf(subset, superset, cmp, contains, ordered) { + if (!contains) { + if (subset.length !== superset.length) return false; + superset = superset.slice(); + } + + return subset.every(function(elem, idx) { + if (ordered) return cmp ? cmp(elem, superset[idx], compareElements) : elem === superset[idx]; + + if (!cmp) { + var matchIdx = superset.indexOf(elem); + if (matchIdx === -1) return false; + + // Remove match from superset so not counted twice if duplicate in subset. + if (!contains) superset.splice(matchIdx, 1); + return true; + } + + return superset.some(function(elem2, matchIdx) { + if (!cmp(elem, elem2, compareElements)) return false; + + // Remove match from superset so not counted twice if duplicate in subset. + if (!contains) superset.splice(matchIdx, 1); + return true; + }); + }); + } + +function flag(obj, key, value) { + var flags = obj.__flags || (obj.__flags = Object.create(null)); + if (arguments.length === 3) { + flags[key] = value; + } else { + return flags[key]; + } +}; + +function deepMembersById2 (subset, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); + + new chai.Assertion(obj, flagMsg, ssfi, true).to.be.an('array'); + new chai.Assertion(subset, flagMsg, ssfi, true).to.be.an('array'); + + var contains = flag(this, 'contains'); + var ordered = flag(this, 'ordered'); + + var subject, failMsg, failNegateMsg; + + if (contains) { + subject = ordered ? 'an ordered superset' : 'a superset'; + failMsg = 'expected #{this} to be ' + subject + ' of #{exp}'; + failNegateMsg = 'expected #{this} to not be ' + subject + ' of #{exp}'; + } else { + subject = ordered ? 'ordered members' : 'members'; + failMsg = 'expected #{this} to have the same ' + subject + ' as #{exp}'; + failNegateMsg = 'expected #{this} to not have the same ' + subject + ' as #{exp}'; + } + + var cmp = flag(this, 'deep') ? deepEqual : undefined; + + this.assert( + isSubsetOf(subset, obj, cmp, contains, ordered) + , failMsg + , failNegateMsg + , subset + , obj + , true + ); + } + +function compareElements(a, b) { + if (!isElement(a) || !isElement(b)) { + return null; + } else { + return a.id === b.id; + } +} + + module.exports = { - deepCopy, deepIncludesById, deepMembersById, - deepMembersByIdOrdered, + isElement, + deepMembersById2, + deepCopy, deepSort, - isElement + deepMembersByIdOrdered, + compareElements } \ No newline at end of file diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-deep-eql.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-deep-eql.js new file mode 100644 index 00000000000..deda06af000 --- /dev/null +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-deep-eql.js @@ -0,0 +1,550 @@ +/* + * 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. + */ + +/* globals Symbol: false, Uint8Array: false, WeakMap: false */ +/*! + * deep-eql + * Copyright(c) 2013 Jake Luer + * MIT Licensed + */ + +function isElement(obj) { + return obj.hasOwnProperty('id') && obj.hasOwnProperty('label'); +} + +function type(obj) { + if (typeof obj === 'undefined') { + return 'undefined'; + } + + if (obj === null) { + return 'null'; + } + + const stringTag = obj[Symbol.toStringTag]; + if (typeof stringTag === 'string') { + return stringTag; + } + const sliceStart = 8; + const sliceEnd = -1; + return Object.prototype.toString.call(obj).slice(sliceStart, sliceEnd); +} + +function FakeMap() { + this._key = 'chai/deep-eql__' + Math.random() + Date.now(); +} + +FakeMap.prototype = { + get: function get(key) { + return key[this._key]; + }, + set: function set(key, value) { + if (Object.isExtensible(key)) { + Object.defineProperty(key, this._key, { + value: value, + configurable: true, + }); + } + }, +}; + +var MemoizeMap = typeof WeakMap === 'function' ? WeakMap : FakeMap; +/*! + * Check to see if the MemoizeMap has recorded a result of the two operands + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {MemoizeMap} memoizeMap + * @returns {Boolean|null} result +*/ +function memoizeCompare(leftHandOperand, rightHandOperand, memoizeMap) { + // Technically, WeakMap keys can *only* be objects, not primitives. + if (!memoizeMap || isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { + return null; + } + var leftHandMap = memoizeMap.get(leftHandOperand); + if (leftHandMap) { + var result = leftHandMap.get(rightHandOperand); + if (typeof result === 'boolean') { + return result; + } + } + return null; +} + +/*! + * Set the result of the equality into the MemoizeMap + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {MemoizeMap} memoizeMap + * @param {Boolean} result +*/ +function memoizeSet(leftHandOperand, rightHandOperand, memoizeMap, result) { + // Technically, WeakMap keys can *only* be objects, not primitives. + if (!memoizeMap || isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { + return; + } + var leftHandMap = memoizeMap.get(leftHandOperand); + if (leftHandMap) { + leftHandMap.set(rightHandOperand, result); + } else { + leftHandMap = new MemoizeMap(); + leftHandMap.set(rightHandOperand, result); + memoizeMap.set(leftHandOperand, leftHandMap); + } +} + +/** + * Assert deeply nested sameValue equality between two objects of any type. + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Object} [options] (optional) Additional options + * @param {Array} [options.comparator] (optional) Override default algorithm, determining custom equality. + * @param {Array} [options.memoize] (optional) Provide a custom memoization object which will cache the results of + complex objects for a speed boost. By passing `false` you can disable memoization, but this will cause circular + references to blow the stack. + * @return {Boolean} equal match + */ +function deepEqual(leftHandOperand, rightHandOperand, options) { + // If we have a comparator, we can't assume anything; so bail to its check first. + if (options && options.comparator) { + return extensiveDeepEqual(leftHandOperand, rightHandOperand, options); + } + + var simpleResult = simpleEqual(leftHandOperand, rightHandOperand); + if (simpleResult !== null) { + return simpleResult; + } + + // Deeper comparisons are pushed through to a larger function + return extensiveDeepEqual(leftHandOperand, rightHandOperand, options); +} + +/** + * Many comparisons can be canceled out early via simple equality or primitive checks. + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @return {Boolean|null} equal match + */ +function simpleEqual(leftHandOperand, rightHandOperand) { + // Equal references (except for Numbers) can be returned early + if (leftHandOperand === rightHandOperand) { + // Handle +-0 cases + return leftHandOperand !== 0 || 1 / leftHandOperand === 1 / rightHandOperand; + } + + // handle NaN cases + if ( + leftHandOperand !== leftHandOperand && // eslint-disable-line no-self-compare + rightHandOperand !== rightHandOperand // eslint-disable-line no-self-compare + ) { + return true; + } + + // Anything that is not an 'object', i.e. symbols, functions, booleans, numbers, + // strings, and undefined, can be compared by reference. + if (isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { + // Easy out b/c it would have passed the first equality check + return false; + } + return null; +} + +/*! + * The main logic of the `deepEqual` function. + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Object} [options] (optional) Additional options + * @param {Array} [options.comparator] (optional) Override default algorithm, determining custom equality. + * @param {Array} [options.memoize] (optional) Provide a custom memoization object which will cache the results of + complex objects for a speed boost. By passing `false` you can disable memoization, but this will cause circular + references to blow the stack. + * @return {Boolean} equal match +*/ +function extensiveDeepEqual(leftHandOperand, rightHandOperand, options) { + options = options || {}; + options.memoize = options.memoize === false ? false : options.memoize || new MemoizeMap(); + var comparator = options && options.comparator; + + // Check if a memoized result exists. + var memoizeResultLeft = memoizeCompare(leftHandOperand, rightHandOperand, options.memoize); + if (memoizeResultLeft !== null) { + return memoizeResultLeft; + } + var memoizeResultRight = memoizeCompare(rightHandOperand, leftHandOperand, options.memoize); + if (memoizeResultRight !== null) { + return memoizeResultRight; + } + + // If a comparator is present, use it. + if (comparator) { + var comparatorResult = comparator(leftHandOperand, rightHandOperand); + // Comparators may return null, in which case we want to go back to default behavior. + if (comparatorResult === false || comparatorResult === true) { + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, comparatorResult); + return comparatorResult; + } + // To allow comparators to override *any* behavior, we ran them first. Since it didn't decide + // what to do, we need to make sure to return the basic tests first before we move on. + var simpleResult = simpleEqual(leftHandOperand, rightHandOperand); + if (simpleResult !== null) { + // Don't memoize this, it takes longer to set/retrieve than to just compare. + return simpleResult; + } + } + + var leftHandType = type(leftHandOperand); + if (leftHandType !== type(rightHandOperand)) { + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, false); + return false; + } + + // Temporarily set the operands in the memoize object to prevent blowing the stack + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, true); + + var result = extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandType, options); + memoizeSet(leftHandOperand, rightHandOperand, options.memoize, result); + return result; +} + +function extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandType, options) { + switch (leftHandType) { + case 'String': + case 'Number': + case 'Boolean': + case 'Date': + // If these types are their instance types (e.g. `new Number`) then re-deepEqual against their values + return deepEqual(leftHandOperand.valueOf(), rightHandOperand.valueOf()); + case 'Promise': + case 'Symbol': + case 'function': + case 'WeakMap': + case 'WeakSet': + return leftHandOperand === rightHandOperand; + case 'Error': + return keysEqual(leftHandOperand, rightHandOperand, [ 'name', 'message', 'code' ], options); + case 'Arguments': + case 'Int8Array': + case 'Uint8Array': + case 'Uint8ClampedArray': + case 'Int16Array': + case 'Uint16Array': + case 'Int32Array': + case 'Uint32Array': + case 'Float32Array': + case 'Float64Array': + case 'Array': + return iterableEqual(leftHandOperand, rightHandOperand, options); + case 'RegExp': + return regexpEqual(leftHandOperand, rightHandOperand); + case 'Generator': + return generatorEqual(leftHandOperand, rightHandOperand, options); + case 'DataView': + return iterableEqual(new Uint8Array(leftHandOperand.buffer), new Uint8Array(rightHandOperand.buffer), options); + case 'ArrayBuffer': + return iterableEqual(new Uint8Array(leftHandOperand), new Uint8Array(rightHandOperand), options); + case 'Set': + return entriesEqual(leftHandOperand, rightHandOperand, options); + case 'Map': + return entriesEqual(leftHandOperand, rightHandOperand, options); + case 'Temporal.PlainDate': + case 'Temporal.PlainTime': + case 'Temporal.PlainDateTime': + case 'Temporal.Instant': + case 'Temporal.ZonedDateTime': + case 'Temporal.PlainYearMonth': + case 'Temporal.PlainMonthDay': + return leftHandOperand.equals(rightHandOperand); + case 'Temporal.Duration': + return leftHandOperand.total('nanoseconds') === rightHandOperand.total('nanoseconds'); + case 'Temporal.TimeZone': + case 'Temporal.Calendar': + return leftHandOperand.toString() === rightHandOperand.toString(); + default: + return objectEqual(leftHandOperand, rightHandOperand, options); + } +} + +/*! + * Compare two Elements for equality. + * + * @param {Element} leftHandOperand + * @param {Element} rightHandOperand + * @return {Boolean} result + */ + +function elementEqual(leftHandOperand, rightHandOperand) { + return leftHandOperand.id() === rightHandOperand.id(); +} + +/*! + * Compare two Regular Expressions for equality. + * + * @param {RegExp} leftHandOperand + * @param {RegExp} rightHandOperand + * @return {Boolean} result + */ + +function regexpEqual(leftHandOperand, rightHandOperand) { + return leftHandOperand.toString() === rightHandOperand.toString(); +} + +/*! + * Compare two Sets/Maps for equality. Faster than other equality functions. + * + * @param {Set} leftHandOperand + * @param {Set} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function entriesEqual(leftHandOperand, rightHandOperand, options) { + // IE11 doesn't support Set#entries or Set#@@iterator, so we need manually populate using Set#forEach + if (leftHandOperand.size !== rightHandOperand.size) { + return false; + } + if (leftHandOperand.size === 0) { + return true; + } + var leftHandItems = []; + var rightHandItems = []; + leftHandOperand.forEach(function gatherEntries(key, value) { + leftHandItems.push([ key, value ]); + }); + rightHandOperand.forEach(function gatherEntries(key, value) { + rightHandItems.push([ key, value ]); + }); + return iterableEqual(leftHandItems.sort(), rightHandItems.sort(), options); +} + +/*! + * Simple equality for flat iterable objects such as Arrays, TypedArrays or Node.js buffers. + * + * @param {Iterable} leftHandOperand + * @param {Iterable} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function iterableEqual(leftHandOperand, rightHandOperand, options) { + var length = leftHandOperand.length; + if (length !== rightHandOperand.length) { + return false; + } + if (length === 0) { + return true; + } + var index = -1; + while (++index < length) { + if (deepEqual(leftHandOperand[index], rightHandOperand[index], options) === false) { + return false; + } + } + return true; +} + +/*! + * Simple equality for generator objects such as those returned by generator functions. + * + * @param {Iterable} leftHandOperand + * @param {Iterable} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ + +function generatorEqual(leftHandOperand, rightHandOperand, options) { + return iterableEqual(getGeneratorEntries(leftHandOperand), getGeneratorEntries(rightHandOperand), options); +} + +/*! + * Determine if the given object has an @@iterator function. + * + * @param {Object} target + * @return {Boolean} `true` if the object has an @@iterator function. + */ +function hasIteratorFunction(target) { + return typeof Symbol !== 'undefined' && + typeof target === 'object' && + typeof Symbol.iterator !== 'undefined' && + typeof target[Symbol.iterator] === 'function'; +} + +/*! + * Gets all iterator entries from the given Object. If the Object has no @@iterator function, returns an empty array. + * This will consume the iterator - which could have side effects depending on the @@iterator implementation. + * + * @param {Object} target + * @returns {Array} an array of entries from the @@iterator function + */ +function getIteratorEntries(target) { + if (hasIteratorFunction(target)) { + try { + return getGeneratorEntries(target[Symbol.iterator]()); + } catch (iteratorError) { + return []; + } + } + return []; +} + +/*! + * Gets all entries from a Generator. This will consume the generator - which could have side effects. + * + * @param {Generator} target + * @returns {Array} an array of entries from the Generator. + */ +function getGeneratorEntries(generator) { + var generatorResult = generator.next(); + var accumulator = [ generatorResult.value ]; + while (generatorResult.done === false) { + generatorResult = generator.next(); + accumulator.push(generatorResult.value); + } + return accumulator; +} + +/*! + * Gets all own and inherited enumerable keys from a target. + * + * @param {Object} target + * @returns {Array} an array of own and inherited enumerable keys from the target. + */ +function getEnumerableKeys(target) { + var keys = []; + for (var key in target) { + keys.push(key); + } + return keys; +} + +function getEnumerableSymbols(target) { + var keys = []; + var allKeys = Object.getOwnPropertySymbols(target); + for (var i = 0; i < allKeys.length; i += 1) { + var key = allKeys[i]; + if (Object.getOwnPropertyDescriptor(target, key).enumerable) { + keys.push(key); + } + } + return keys; +} + +/*! + * Determines if two objects have matching values, given a set of keys. Defers to deepEqual for the equality check of + * each key. If any value of the given key is not equal, the function will return false (early). + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Array} keys An array of keys to compare the values of leftHandOperand and rightHandOperand against + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ +function keysEqual(leftHandOperand, rightHandOperand, keys, options) { + var length = keys.length; + if (length === 0) { + return true; + } + for (var i = 0; i < length; i += 1) { + if (deepEqual(leftHandOperand[keys[i]], rightHandOperand[keys[i]], options) === false) { + return false; + } + } + return true; +} + +/*! + * Recursively check the equality of two Objects. Once basic sameness has been established it will defer to `deepEqual` + * for each enumerable key in the object. + * + * @param {Mixed} leftHandOperand + * @param {Mixed} rightHandOperand + * @param {Object} [options] (Optional) + * @return {Boolean} result + */ +function objectEqual(leftHandOperand, rightHandOperand, options) { + var leftHandKeys = getEnumerableKeys(leftHandOperand); + var rightHandKeys = getEnumerableKeys(rightHandOperand); + var leftHandSymbols = getEnumerableSymbols(leftHandOperand); + var rightHandSymbols = getEnumerableSymbols(rightHandOperand); + leftHandKeys = leftHandKeys.concat(leftHandSymbols); + rightHandKeys = rightHandKeys.concat(rightHandSymbols); + + if (isElement(leftHandOperand)) { + return leftHandOperand.id === rightHandOperand.id; + } + + if (leftHandKeys.length && leftHandKeys.length === rightHandKeys.length) { + if (iterableEqual(mapSymbols(leftHandKeys).sort(), mapSymbols(rightHandKeys).sort()) === false) { + return false; + } + return keysEqual(leftHandOperand, rightHandOperand, leftHandKeys, options); + } + + var leftHandEntries = getIteratorEntries(leftHandOperand); + var rightHandEntries = getIteratorEntries(rightHandOperand); + if (leftHandEntries.length && leftHandEntries.length === rightHandEntries.length) { + leftHandEntries.sort(); + rightHandEntries.sort(); + return iterableEqual(leftHandEntries, rightHandEntries, options); + } + + if (leftHandKeys.length === 0 && + leftHandEntries.length === 0 && + rightHandKeys.length === 0 && + rightHandEntries.length === 0) { + return true; + } + + return false; +} + +/*! + * Returns true if the argument is a primitive. + * + * This intentionally returns true for all objects that can be compared by reference, + * including functions and symbols. + * + * @param {Mixed} value + * @return {Boolean} result + */ +function isPrimitive(value) { + return value === null || typeof value !== 'object'; +} + +function mapSymbols(arr) { + return arr.map(function mapSymbol(entry) { + if (typeof entry === 'symbol') { + return entry.toString(); + } + + return entry; + }); +} + +/*! + * Primary Export + */ + +module.exports = { + deepEqual, + isElement +}; \ No newline at end of file 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 3d6c977fcc3..a2e42262860 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 @@ -41,7 +41,7 @@ const t = traversalModule.t; const P = traversalModule.P; const direction = traversalModule.direction; const merge = traversalModule.merge; -const { deepMembersById, deepMembersByIdOrdered } = require('../unit/element-comparison') +const { deepMembersById, deepMembersById2 } = require('./element-comparison'); const parsers = [ [ 'str\\[(.*)\\]', (stringValue) => stringValue ], //returns the string value as is [ 'vp\\[(.+)\\]', toVertexProperty ], @@ -64,6 +64,12 @@ const parsers = [ [ 'M\\[(.+)\\]', toMerge ] ].map(x => [ new RegExp('^' + x[0] + '$'), x[1] ]); +chai.use(function (chai, chaiUtils) { + chai.Assertion.overwriteMethod('members', function (_super) { + return deepMembersById2; + }); +}); + const ignoreReason = { nullKeysInMapNotSupportedWell: "Javascript does not nicely support 'null' as a key in Map instances", setNotSupported: "There is no Set support in gremlin-javascript", @@ -237,14 +243,19 @@ Then(/^the result should be (\w+)$/, function assertResult(characterizedAs, resu const expectedResult = resultTable.rows().map(row => parseRow.call(this, row)); switch (characterizedAs) { case 'ordered': - expect(deepMembersByIdOrdered(toCompare(this.result), expectedResult)); + expect(expectedResult).to.have.deep.ordered.members(toCompare(this.result)); +// expect(expectedResult).to.test(toCompare(this.result)); +// expect(deepMembersByIdOrdered(toCompare(this.result), expectedResult)).to.be.true; break; case 'unordered': - expect(deepMembersById(toCompare(this.result), expectedResult)); + expect(expectedResult).to.have.deep.members(toCompare(this.result)); +// expect(expectedResult).to.test(toCompare(this.result)); +// expect(deepMembersById(toCompare(this.result), expectedResult)).to.be.true; break; case 'of': // result is a subset of the expected expect(expectedResult).to.include.deep.members(toCompare(this.result)); +// expect(deepIncludesById(expectedResult, toCompare(this.result))).to.be.true; break; } }); 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 0296a7da94e..357b2b15a88 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 @@ -69,12 +69,15 @@ setWorldConstructor(TinkerPopWorld); BeforeAll(function () { // load all traversals const promises = ['modern', 'classic', 'crew', 'grateful', 'sink', 'empty'].map(graphName => { + console.log("🚀 ~ file: world.js:72 ~ promises ~ graphName:", graphName) let connection = null; if (graphName === 'empty') { connection = helper.getConnection('ggraph'); + console.log("🚀 ~ file: world.js:75 ~ promises ~ connection:", connection) return connection.open().then(() => cache['empty'] = { connection: connection }); } connection = helper.getConnection('g' + graphName); + console.log("🚀 ~ file: world.js:78 ~ promises ~ connection:", connection) return connection.open() .then(() => Promise.all([getVertices(connection), getEdges(connection), getVertexProperties(connection)])) .then(values => { diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js index e3d12cfdd7e..b2c666ea140 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js @@ -17,13 +17,26 @@ * under the License. */ +const chai = require('chai') const { expect } = require('chai'); const { VertexProperty, Property, Vertex, Edge, Path } = require('../../lib/structure/graph'); const { deepCopy, deepMembersById, deepMembersByIdOrdered, deepSort, isElement} = require('../cucumber/element-comparison'); +chai.use(function (chai, utils) { + chai.Assertion.addChainableMethod('test', function (expected) { + const obj = this._obj; + const result = deepMembersById(obj, expected); + this.assert( + result, + `expected ${obj} to deeply equal ${expected}`, + `expected ${obj} not to deeply equal ${expected}` + ); + }); +}); describe('primitives', function () { it('should pass', function () { + expect(1).to.test(1); expect(deepMembersById(1, 1)).to.be.true; expect(deepMembersById(false, false)).to.be.true; expect(deepMembersById(null, null)).to.be.true; diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/t.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/t.js new file mode 100644 index 00000000000..a789c130389 --- /dev/null +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/t.js @@ -0,0 +1,226 @@ +///* +// * 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. +// */ +//'use strict'; +// +//const util = require('util'); +//const gremlin = require('../cucumber/gremlin').gremlin; +//const utils = require('../../lib/utils'); +//const traversal = require('../../lib/process/anonymous-traversal').traversal; +//const { VertexProperty, Property, Vertex, Edge, Path } = require('../../lib/structure/graph'); +//const { deepCopy, deepMembersById, deepMembersByIdOrdered, deepSort, isElement} = require('../cucumber/element-comparison') +// +////const m1 = new Map([ +//// ['ripple', [new Vertex(5, 'software', undefined)]], +//// ['peter', [new Vertex(6, 'person', undefined)]], +//// ['vadas', [new Vertex(2, 'person', undefined)]], +//// ['josh', [new Vertex(4, 'person', undefined)]], +//// ['lop', [new Vertex(3, 'software', undefined)]], +//// ['marko', [new Vertex(1, 'person', undefined)]] +////]); +//// +////const m2 = new Map([ +//// ['ripple', [new Vertex(5, 'software', undefined)]], +//// ['peter', [new Vertex(6, 'person', undefined)]], +//// ['vadas', [new Vertex(2, 'person', undefined)]], +//// ['josh', [new Vertex(4, 'person', undefined)]], +//// ['lop', [new Vertex(3, 'software', undefined)]], +////]); +//// +////const m3 = new Map([ +//// ['ripple', [new Vertex(5, 'software', undefined)]], +//// ['peter', [new Vertex(6, 'person', undefined)]], +//// ['vadas', [new Vertex(2, 'person', undefined)]], +//// ['lop', [new Vertex(3, 'software', undefined)]], +//// ['josh', [new Vertex(4, 'person', undefined)]], +//// ['marko', [new Vertex(1, 'person', undefined)]] +////]); +// +//const a1 = [ +// new Vertex(5, 'software', undefined), +// new Vertex(6, 'person', undefined), +// new Vertex(2, 'person', undefined), +// new Vertex(4, 'person', undefined), +// new Vertex(3, 'software', undefined), +// new Vertex(1, 'person', undefined) +//]; +// +//const a2 = [ +// new Vertex(5, 'software', undefined), +// new Vertex(6, 'person', undefined), +// new Vertex(2, 'person', undefined), +// new Vertex(4, 'person', undefined), +// new Vertex(3, 'software', undefined) +//]; +// +//const a3 = [ +// new Vertex(5, 'software', undefined), +// new Vertex(6, 'person', undefined), +// new Vertex(2, 'person', undefined), +// new Vertex(3, 'software', undefined), +// new Vertex(4, 'person', undefined), +// new Vertex(1, 'person', undefined) +//]; +// +//const v1 = new Vertex(8, "dog", undefined) +//const v2 = new Vertex(9, "cat", undefined) +// +//const pl1 = [ +// new Path([], [ 'daniel', 6 ]), +// new Path([], [ v1, v2, 'lop', 3 ]), +// new Path([], [ v1, v2, 'vadas', 5 ]), +// new Path([], [ v1, v2, 'josh', 4 ]) +//] +// +//const pl2 = [ +// new Path([], [ 'daniel', 6 ]), +// new Path([], [ v1, v2, 'lop', 3 ]), +// new Path([], [ v1, v2, 'josh', 4 ]) +//] +// +//const pl3 = [ +// new Path([], [ 'daniel', 6 ]), +// new Path([], [ v1, v2, 'lop', 3 ]), +// new Path([], [ v1, v2, 'josh', 4 ]), +// new Path([], [ v1, v2, 'vadas', 5 ]), +//] +// +//const p1 = new Path([], +// [new Vertex(1, 'person', undefined), +// new Vertex(2, 'person', undefined) +// ]); +// +//const p2 = new Path([], +// [new Vertex(1, 'person', undefined), +// ]); +// +//const p3 = new Path([], +// new Vertex(2, 'person', undefined), +// [new Vertex(1, 'person', undefined), +// ]); +////console.log(v1) +////console.log(v2) +////console.log(deepMembersById(v1, v2)) +//// +////console.log(m1) +////console.log(m2) +// +////const m4 = [ +//// new Map([["name", "marko"], ["friendRank", 15.0]]), +//// new Map([["name", "vadas"], ["friendRank", 21.0]]), +//// new Map([["name", "lop"], ["friendRank", 15.0]]), +//// new Map([["name", "josh"], ["friendRank", 21.0]]), +//// new Map([["name", "ripple"], ["friendRank", 15.0]]), +//// new Map([["name", "peter"], ["friendRank", 15.0]]), +////]; +//// +////const m5 = [ +//// new Map([["name", "marko"], ["friendRank", 15.0]]), +//// new Map([["name", "vadas"], ["friendRank", 21.0]]), +//// new Map([["name", "lop"], ["friendRank", 15.0]]), +//// new Map([["name", "josh"], ["friendRank", 21.0]]), +////]; +// +// +//function assert(expected, actual) { +//// console.log("expected: " + expected, "actual: " + actual); +// console.log(expected, actual); +// if (expected !== actual) console.log('!!!!!!!!!!!!!!!!!!!!!!') +//} +//function tests(t, base, missing, swapped) { +// console.log(t, 'happy ordered'); +// assert(true, deepMembersById(base, base, true)); +// assert(true, deepMembersById(missing, missing, true)); +// assert(true, deepMembersById(swapped, swapped, true)); +// console.log(t, 'missing 1 ordered'); +// assert(false, deepMembersById(base, missing, true)); +// if (t !== "path") { +// console.log(t, 'swapped ordered'); +// assert(false, deepMembersById(base, swapped, true)); +// } +// console.log(t, 'happy uo'); +// assert(true, deepMembersById(base, base, false)); +// assert(true, deepMembersById(missing, missing, false)); +// assert(true, deepMembersById(swapped, swapped, false)); +// console.log(t, 'missing 1 uo'); +// assert(false, deepMembersById(base, missing, false)); +// if (t !== "path") { +// console.log(t, 'swapped uo'); +// assert(true, deepMembersById(base, swapped, false)); +// } +// console.log(t, 'other') +// assert(false, deepMembersById(missing, swapped, true)); +// assert(false, deepMembersById(missing, swapped, false)); +//} +// +////tests("map", m1, m2, m3) +//tests("array", a1, a2, a3) +////tests("pathlist", pl1, pl2, pl3) +//tests("path", p1, p2, p3) +// +//let t = "array of maps" +////console.log(t, 'missing'); +//// assert(false, deepMembersById(m4, m5, false)); +//// assert(false, deepMembersById(m4, m5, true)); +// +//t = "non objects" +//assert(true, deepMembersById(1, 1, true)) +//assert(true, deepMembersById("abc", "abc", true)) +//assert(false, deepMembersById(1, "a", true)) +//assert(false, deepMembersById(1, 2, true)) +//assert(false, deepMembersById(2, "lop", true)) +// +//const pr1 = new Property("a", "1"); +//const pr2 = new Property("a", "1"); +//const aa1 = [1,2,[1,2,3]]; +//const aa2 = [1,[2,1,3],2]; +//const aaa1 = [1, 2, 3]; +// const aaa2 = [1, 2, 3]; +// const aaa3 = [2, 1, 3]; +//console.log(deepMembersById(deepSort(aa1), deepSort(aa2))); +//console.log(deepMembersByIdOrdered(aaa1, aaa3)); +// +//const m1 = new Map([ +// ['ripple', [new Vertex(5, 'software', undefined)]], +// ['peter', [new Vertex(6, 'person', undefined)]], +// ['vadas', [new Vertex(2, 'person', undefined)]] +// ]); +// const m2 = new Map([ +// ['ripple', [new Vertex(5, 'software', undefined)]], +// ['peter', [new Vertex(6, 'person', undefined)]] +// ]); +// const m3 = new Map([ +// ['ripple', [new Vertex(5, 'software', undefined)]], +// ['vadas', [new Vertex(2, 'person', undefined)]], +// ['peter', [new Vertex(6, 'person', undefined)]] +// ]); +// const m4 = new Map([ +// ['ripple', [new Vertex(5, 'software', undefined)]], +// ['vadas', [new Vertex(6, 'person', undefined)]], +// ['peter', [new Vertex(2, 'person', undefined)]] +// ]); +// console.log(deepMembersById(m1, m1)) +// console.log(deepMembersById(m1, m2)) +// const path1 = new Path(["path"], [new Vertex(1, 'person', undefined), +// new Vertex(2, 'person', undefined)]); +// const path2 = new Path(["path"], [new Vertex(1, 'person', undefined)]); +// const path3 = new Path(["path"], [new Vertex(2, 'person', undefined), +// new Vertex(1, 'person', undefined)]); +// console.log(deepMembersById(path1, path3)) +// console.log([new Vertex(2, 'person', undefined), +// new Vertex(1, 'person', undefined)].sort()) From aa35ce4a7cb7664b37dac8e986f46963ec14accd Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Thu, 4 Jan 2024 21:39:27 -0800 Subject: [PATCH 06/18] Update to use deep-eql custom option --- .../test/cucumber/element-comparison.js | 268 ++------- .../test/cucumber/element-deep-eql.js | 550 ------------------ .../test/cucumber/feature-steps.js | 11 +- .../gremlin-javascript/test/cucumber/world.js | 3 - .../test/unit/element-comparison-test.js | 529 +++++++++-------- 5 files changed, 321 insertions(+), 1040 deletions(-) delete mode 100644 gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-deep-eql.js diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js index 4e063483c0b..60abdbe3874 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js @@ -18,235 +18,85 @@ */ const chai = require('chai'); -//const { deepEqual, isElement } = require('./element-deep-eql'); const deepEqual = require('deep-eql'); +const opt = {comparator: compareElements}; function isElement(obj) { - return obj.hasOwnProperty('id') && obj.hasOwnProperty('label'); -} - -function deepSort(obj) { - if (!Array.isArray(obj)) { - return obj; - } else { - return obj.map((item) => deepSort(item)).sort(); - } -} - -function deepCopy(obj) { - if (typeof obj !== 'object' || obj === null) { - return obj; - } - let copy; - if (obj instanceof Map) { - copy = new Map(); - for (const [k, v] of obj.entries()) { - copy.set(k, deepCopy(v)); - } - return copy; - } - if (Array.isArray(obj)) { - copy = []; - for (let i = 0; i < obj.length; i++) { - copy[i] = deepCopy(obj[i]); - } - } else { - copy = {}; - for (let k in obj) { - if (obj.hasOwnProperty(k)) { - copy[k] = deepCopy(obj[k]); - } - } - } - return copy; -} - -function deepMembersById(a, b, ordered=false) { - a = deepCopy(a); - b = deepCopy(b); - console.log(a) - console.log(b) - if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) { - return a === b; - } - if (typeof a !== typeof b) { - return false; - } - if (isElement(a) || isElement(b)) { - return a.id === b.id; - } - if (a instanceof Map) { - a = Array.from(a.entries()); - b = Array.from(b.entries()); - } - if (Array.isArray(a)) { - if (a.length !== b.length) { - return false; - } - if (!ordered) { - a = deepSort(a); - b = deepSort(b); - } - for (let i = 0; i < a.length; i++) { - if (!deepMembersById(a[i], b[i], ordered)) { - return false; - } - } - return true; - } - const keysA = Object.keys(a); - const keysB = Object.keys(b); - if (keysA.length !== keysB.length) { - return false; - } - for (let k of keysA) { - if (!b.hasOwnProperty(k)) { - return false; - } - if (!deepMembersById(a[k], b[k], ordered)) { - return false; - } - } - return true; -} - -function deepMembersById3(a, b) { - a = deepCopy(a); - b = deepCopy(b); - console.log(a) - console.log(b) - if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) { - return a === b; - } - if (typeof a !== typeof b) { - return false; - } - if (isElement(a) || isElement(b)) { - return a.id === b.id; - } - if (a instanceof Map) { - a = Array.from(a.entries()); - b = Array.from(b.entries()); - } - if (Array.isArray(a)) { - if (a.length !== b.length) { - return false; - } - if (!ordered) { - a = deepSort(a); - b = deepSort(b); - } - for (let i = 0; i < a.length; i++) { - if (!deepMembersById(a[i], b[i], ordered)) { - return false; - } - } - return true; - } - const keysA = Object.keys(a); - const keysB = Object.keys(b); - if (keysA.length !== keysB.length) { - return false; - } - for (let k of keysA) { - if (!b.hasOwnProperty(k)) { - return false; - } - if (!deepMembersById(a[k], b[k], ordered)) { - return false; - } - } - return true; -} - -function deepMembersByIdOrdered(a, b) { - return deepMembersById(a, b, true) -} - -function deepIncludesById(array, subset) { - console.log(array); - console.log(subset); - for (let item of subset) { - const included = array.some((arrayItem) => deepMembersById(item, arrayItem)); - if (!included) { - return false; - } - } - return true; + return obj !== null && obj.hasOwnProperty('id') && obj.hasOwnProperty('label'); } function isSubsetOf(subset, superset, cmp, contains, ordered) { if (!contains) { - if (subset.length !== superset.length) return false; - superset = superset.slice(); + if (subset.length !== superset.length) return false; + superset = superset.slice(); } return subset.every(function(elem, idx) { - if (ordered) return cmp ? cmp(elem, superset[idx], compareElements) : elem === superset[idx]; + if (ordered) return cmp ? cmp(elem, superset[idx], opt) : elem === superset[idx]; - if (!cmp) { - var matchIdx = superset.indexOf(elem); - if (matchIdx === -1) return false; + if (!cmp) { + var matchIdx = superset.indexOf(elem); + if (matchIdx === -1) return false; - // Remove match from superset so not counted twice if duplicate in subset. - if (!contains) superset.splice(matchIdx, 1); - return true; - } + // Remove match from superset so not counted twice if duplicate in subset. + if (!contains) superset.splice(matchIdx, 1); + return true; + } - return superset.some(function(elem2, matchIdx) { - if (!cmp(elem, elem2, compareElements)) return false; + return superset.some(function(elem2, matchIdx) { + if (!cmp(elem, elem2, opt)) return false; - // Remove match from superset so not counted twice if duplicate in subset. - if (!contains) superset.splice(matchIdx, 1); - return true; - }); + // Remove match from superset so not counted twice if duplicate in subset. + if (!contains) superset.splice(matchIdx, 1); + return true; + }); }); - } +} function flag(obj, key, value) { - var flags = obj.__flags || (obj.__flags = Object.create(null)); - if (arguments.length === 3) { - flags[key] = value; - } else { - return flags[key]; - } + var flags = obj.__flags || (obj.__flags = Object.create(null)); + if (arguments.length === 3) { + flags[key] = value; + } else { + return flags[key]; + } }; -function deepMembersById2 (subset, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object') - , flagMsg = flag(this, 'message') - , ssfi = flag(this, 'ssfi'); +function deepMembersById (subset, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , flagMsg = flag(this, 'message') + , ssfi = flag(this, 'ssfi'); - new chai.Assertion(obj, flagMsg, ssfi, true).to.be.an('array'); - new chai.Assertion(subset, flagMsg, ssfi, true).to.be.an('array'); + new chai.Assertion(obj, flagMsg, ssfi, true).to.be.an('array'); + new chai.Assertion(subset, flagMsg, ssfi, true).to.be.an('array'); - var contains = flag(this, 'contains'); - var ordered = flag(this, 'ordered'); + var contains = flag(this, 'contains'); + var ordered = flag(this, 'ordered'); - var subject, failMsg, failNegateMsg; + var subject, failMsg, failNegateMsg; - if (contains) { - subject = ordered ? 'an ordered superset' : 'a superset'; - failMsg = 'expected #{this} to be ' + subject + ' of #{exp}'; - failNegateMsg = 'expected #{this} to not be ' + subject + ' of #{exp}'; - } else { - subject = ordered ? 'ordered members' : 'members'; - failMsg = 'expected #{this} to have the same ' + subject + ' as #{exp}'; - failNegateMsg = 'expected #{this} to not have the same ' + subject + ' as #{exp}'; - } + if (contains) { + subject = ordered ? 'an ordered superset' : 'a superset'; + failMsg = 'expected #{this} to be ' + subject + ' of #{exp}'; + failNegateMsg = 'expected #{this} to not be ' + subject + ' of #{exp}'; + } else { + subject = ordered ? 'ordered members' : 'members'; + failMsg = 'expected #{this} to have the same ' + subject + ' as #{exp}'; + failNegateMsg = 'expected #{this} to not have the same ' + subject + ' as #{exp}'; + } - var cmp = flag(this, 'deep') ? deepEqual : undefined; + var cmp = flag(this, 'deep') ? deepEqual : undefined; - this.assert( - isSubsetOf(subset, obj, cmp, contains, ordered) - , failMsg - , failNegateMsg - , subset - , obj - , true - ); - } + this.assert( + isSubsetOf(subset, obj, cmp, contains, ordered) + , failMsg + , failNegateMsg + , subset + , obj + , true + ); +} function compareElements(a, b) { if (!isElement(a) || !isElement(b)) { @@ -256,14 +106,8 @@ function compareElements(a, b) { } } - module.exports = { - deepIncludesById, deepMembersById, - isElement, - deepMembersById2, - deepCopy, - deepSort, - deepMembersByIdOrdered, - compareElements + compareElements, + opt } \ No newline at end of file diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-deep-eql.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-deep-eql.js deleted file mode 100644 index deda06af000..00000000000 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-deep-eql.js +++ /dev/null @@ -1,550 +0,0 @@ -/* - * 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. - */ - -/* globals Symbol: false, Uint8Array: false, WeakMap: false */ -/*! - * deep-eql - * Copyright(c) 2013 Jake Luer - * MIT Licensed - */ - -function isElement(obj) { - return obj.hasOwnProperty('id') && obj.hasOwnProperty('label'); -} - -function type(obj) { - if (typeof obj === 'undefined') { - return 'undefined'; - } - - if (obj === null) { - return 'null'; - } - - const stringTag = obj[Symbol.toStringTag]; - if (typeof stringTag === 'string') { - return stringTag; - } - const sliceStart = 8; - const sliceEnd = -1; - return Object.prototype.toString.call(obj).slice(sliceStart, sliceEnd); -} - -function FakeMap() { - this._key = 'chai/deep-eql__' + Math.random() + Date.now(); -} - -FakeMap.prototype = { - get: function get(key) { - return key[this._key]; - }, - set: function set(key, value) { - if (Object.isExtensible(key)) { - Object.defineProperty(key, this._key, { - value: value, - configurable: true, - }); - } - }, -}; - -var MemoizeMap = typeof WeakMap === 'function' ? WeakMap : FakeMap; -/*! - * Check to see if the MemoizeMap has recorded a result of the two operands - * - * @param {Mixed} leftHandOperand - * @param {Mixed} rightHandOperand - * @param {MemoizeMap} memoizeMap - * @returns {Boolean|null} result -*/ -function memoizeCompare(leftHandOperand, rightHandOperand, memoizeMap) { - // Technically, WeakMap keys can *only* be objects, not primitives. - if (!memoizeMap || isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { - return null; - } - var leftHandMap = memoizeMap.get(leftHandOperand); - if (leftHandMap) { - var result = leftHandMap.get(rightHandOperand); - if (typeof result === 'boolean') { - return result; - } - } - return null; -} - -/*! - * Set the result of the equality into the MemoizeMap - * - * @param {Mixed} leftHandOperand - * @param {Mixed} rightHandOperand - * @param {MemoizeMap} memoizeMap - * @param {Boolean} result -*/ -function memoizeSet(leftHandOperand, rightHandOperand, memoizeMap, result) { - // Technically, WeakMap keys can *only* be objects, not primitives. - if (!memoizeMap || isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { - return; - } - var leftHandMap = memoizeMap.get(leftHandOperand); - if (leftHandMap) { - leftHandMap.set(rightHandOperand, result); - } else { - leftHandMap = new MemoizeMap(); - leftHandMap.set(rightHandOperand, result); - memoizeMap.set(leftHandOperand, leftHandMap); - } -} - -/** - * Assert deeply nested sameValue equality between two objects of any type. - * - * @param {Mixed} leftHandOperand - * @param {Mixed} rightHandOperand - * @param {Object} [options] (optional) Additional options - * @param {Array} [options.comparator] (optional) Override default algorithm, determining custom equality. - * @param {Array} [options.memoize] (optional) Provide a custom memoization object which will cache the results of - complex objects for a speed boost. By passing `false` you can disable memoization, but this will cause circular - references to blow the stack. - * @return {Boolean} equal match - */ -function deepEqual(leftHandOperand, rightHandOperand, options) { - // If we have a comparator, we can't assume anything; so bail to its check first. - if (options && options.comparator) { - return extensiveDeepEqual(leftHandOperand, rightHandOperand, options); - } - - var simpleResult = simpleEqual(leftHandOperand, rightHandOperand); - if (simpleResult !== null) { - return simpleResult; - } - - // Deeper comparisons are pushed through to a larger function - return extensiveDeepEqual(leftHandOperand, rightHandOperand, options); -} - -/** - * Many comparisons can be canceled out early via simple equality or primitive checks. - * @param {Mixed} leftHandOperand - * @param {Mixed} rightHandOperand - * @return {Boolean|null} equal match - */ -function simpleEqual(leftHandOperand, rightHandOperand) { - // Equal references (except for Numbers) can be returned early - if (leftHandOperand === rightHandOperand) { - // Handle +-0 cases - return leftHandOperand !== 0 || 1 / leftHandOperand === 1 / rightHandOperand; - } - - // handle NaN cases - if ( - leftHandOperand !== leftHandOperand && // eslint-disable-line no-self-compare - rightHandOperand !== rightHandOperand // eslint-disable-line no-self-compare - ) { - return true; - } - - // Anything that is not an 'object', i.e. symbols, functions, booleans, numbers, - // strings, and undefined, can be compared by reference. - if (isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { - // Easy out b/c it would have passed the first equality check - return false; - } - return null; -} - -/*! - * The main logic of the `deepEqual` function. - * - * @param {Mixed} leftHandOperand - * @param {Mixed} rightHandOperand - * @param {Object} [options] (optional) Additional options - * @param {Array} [options.comparator] (optional) Override default algorithm, determining custom equality. - * @param {Array} [options.memoize] (optional) Provide a custom memoization object which will cache the results of - complex objects for a speed boost. By passing `false` you can disable memoization, but this will cause circular - references to blow the stack. - * @return {Boolean} equal match -*/ -function extensiveDeepEqual(leftHandOperand, rightHandOperand, options) { - options = options || {}; - options.memoize = options.memoize === false ? false : options.memoize || new MemoizeMap(); - var comparator = options && options.comparator; - - // Check if a memoized result exists. - var memoizeResultLeft = memoizeCompare(leftHandOperand, rightHandOperand, options.memoize); - if (memoizeResultLeft !== null) { - return memoizeResultLeft; - } - var memoizeResultRight = memoizeCompare(rightHandOperand, leftHandOperand, options.memoize); - if (memoizeResultRight !== null) { - return memoizeResultRight; - } - - // If a comparator is present, use it. - if (comparator) { - var comparatorResult = comparator(leftHandOperand, rightHandOperand); - // Comparators may return null, in which case we want to go back to default behavior. - if (comparatorResult === false || comparatorResult === true) { - memoizeSet(leftHandOperand, rightHandOperand, options.memoize, comparatorResult); - return comparatorResult; - } - // To allow comparators to override *any* behavior, we ran them first. Since it didn't decide - // what to do, we need to make sure to return the basic tests first before we move on. - var simpleResult = simpleEqual(leftHandOperand, rightHandOperand); - if (simpleResult !== null) { - // Don't memoize this, it takes longer to set/retrieve than to just compare. - return simpleResult; - } - } - - var leftHandType = type(leftHandOperand); - if (leftHandType !== type(rightHandOperand)) { - memoizeSet(leftHandOperand, rightHandOperand, options.memoize, false); - return false; - } - - // Temporarily set the operands in the memoize object to prevent blowing the stack - memoizeSet(leftHandOperand, rightHandOperand, options.memoize, true); - - var result = extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandType, options); - memoizeSet(leftHandOperand, rightHandOperand, options.memoize, result); - return result; -} - -function extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandType, options) { - switch (leftHandType) { - case 'String': - case 'Number': - case 'Boolean': - case 'Date': - // If these types are their instance types (e.g. `new Number`) then re-deepEqual against their values - return deepEqual(leftHandOperand.valueOf(), rightHandOperand.valueOf()); - case 'Promise': - case 'Symbol': - case 'function': - case 'WeakMap': - case 'WeakSet': - return leftHandOperand === rightHandOperand; - case 'Error': - return keysEqual(leftHandOperand, rightHandOperand, [ 'name', 'message', 'code' ], options); - case 'Arguments': - case 'Int8Array': - case 'Uint8Array': - case 'Uint8ClampedArray': - case 'Int16Array': - case 'Uint16Array': - case 'Int32Array': - case 'Uint32Array': - case 'Float32Array': - case 'Float64Array': - case 'Array': - return iterableEqual(leftHandOperand, rightHandOperand, options); - case 'RegExp': - return regexpEqual(leftHandOperand, rightHandOperand); - case 'Generator': - return generatorEqual(leftHandOperand, rightHandOperand, options); - case 'DataView': - return iterableEqual(new Uint8Array(leftHandOperand.buffer), new Uint8Array(rightHandOperand.buffer), options); - case 'ArrayBuffer': - return iterableEqual(new Uint8Array(leftHandOperand), new Uint8Array(rightHandOperand), options); - case 'Set': - return entriesEqual(leftHandOperand, rightHandOperand, options); - case 'Map': - return entriesEqual(leftHandOperand, rightHandOperand, options); - case 'Temporal.PlainDate': - case 'Temporal.PlainTime': - case 'Temporal.PlainDateTime': - case 'Temporal.Instant': - case 'Temporal.ZonedDateTime': - case 'Temporal.PlainYearMonth': - case 'Temporal.PlainMonthDay': - return leftHandOperand.equals(rightHandOperand); - case 'Temporal.Duration': - return leftHandOperand.total('nanoseconds') === rightHandOperand.total('nanoseconds'); - case 'Temporal.TimeZone': - case 'Temporal.Calendar': - return leftHandOperand.toString() === rightHandOperand.toString(); - default: - return objectEqual(leftHandOperand, rightHandOperand, options); - } -} - -/*! - * Compare two Elements for equality. - * - * @param {Element} leftHandOperand - * @param {Element} rightHandOperand - * @return {Boolean} result - */ - -function elementEqual(leftHandOperand, rightHandOperand) { - return leftHandOperand.id() === rightHandOperand.id(); -} - -/*! - * Compare two Regular Expressions for equality. - * - * @param {RegExp} leftHandOperand - * @param {RegExp} rightHandOperand - * @return {Boolean} result - */ - -function regexpEqual(leftHandOperand, rightHandOperand) { - return leftHandOperand.toString() === rightHandOperand.toString(); -} - -/*! - * Compare two Sets/Maps for equality. Faster than other equality functions. - * - * @param {Set} leftHandOperand - * @param {Set} rightHandOperand - * @param {Object} [options] (Optional) - * @return {Boolean} result - */ - -function entriesEqual(leftHandOperand, rightHandOperand, options) { - // IE11 doesn't support Set#entries or Set#@@iterator, so we need manually populate using Set#forEach - if (leftHandOperand.size !== rightHandOperand.size) { - return false; - } - if (leftHandOperand.size === 0) { - return true; - } - var leftHandItems = []; - var rightHandItems = []; - leftHandOperand.forEach(function gatherEntries(key, value) { - leftHandItems.push([ key, value ]); - }); - rightHandOperand.forEach(function gatherEntries(key, value) { - rightHandItems.push([ key, value ]); - }); - return iterableEqual(leftHandItems.sort(), rightHandItems.sort(), options); -} - -/*! - * Simple equality for flat iterable objects such as Arrays, TypedArrays or Node.js buffers. - * - * @param {Iterable} leftHandOperand - * @param {Iterable} rightHandOperand - * @param {Object} [options] (Optional) - * @return {Boolean} result - */ - -function iterableEqual(leftHandOperand, rightHandOperand, options) { - var length = leftHandOperand.length; - if (length !== rightHandOperand.length) { - return false; - } - if (length === 0) { - return true; - } - var index = -1; - while (++index < length) { - if (deepEqual(leftHandOperand[index], rightHandOperand[index], options) === false) { - return false; - } - } - return true; -} - -/*! - * Simple equality for generator objects such as those returned by generator functions. - * - * @param {Iterable} leftHandOperand - * @param {Iterable} rightHandOperand - * @param {Object} [options] (Optional) - * @return {Boolean} result - */ - -function generatorEqual(leftHandOperand, rightHandOperand, options) { - return iterableEqual(getGeneratorEntries(leftHandOperand), getGeneratorEntries(rightHandOperand), options); -} - -/*! - * Determine if the given object has an @@iterator function. - * - * @param {Object} target - * @return {Boolean} `true` if the object has an @@iterator function. - */ -function hasIteratorFunction(target) { - return typeof Symbol !== 'undefined' && - typeof target === 'object' && - typeof Symbol.iterator !== 'undefined' && - typeof target[Symbol.iterator] === 'function'; -} - -/*! - * Gets all iterator entries from the given Object. If the Object has no @@iterator function, returns an empty array. - * This will consume the iterator - which could have side effects depending on the @@iterator implementation. - * - * @param {Object} target - * @returns {Array} an array of entries from the @@iterator function - */ -function getIteratorEntries(target) { - if (hasIteratorFunction(target)) { - try { - return getGeneratorEntries(target[Symbol.iterator]()); - } catch (iteratorError) { - return []; - } - } - return []; -} - -/*! - * Gets all entries from a Generator. This will consume the generator - which could have side effects. - * - * @param {Generator} target - * @returns {Array} an array of entries from the Generator. - */ -function getGeneratorEntries(generator) { - var generatorResult = generator.next(); - var accumulator = [ generatorResult.value ]; - while (generatorResult.done === false) { - generatorResult = generator.next(); - accumulator.push(generatorResult.value); - } - return accumulator; -} - -/*! - * Gets all own and inherited enumerable keys from a target. - * - * @param {Object} target - * @returns {Array} an array of own and inherited enumerable keys from the target. - */ -function getEnumerableKeys(target) { - var keys = []; - for (var key in target) { - keys.push(key); - } - return keys; -} - -function getEnumerableSymbols(target) { - var keys = []; - var allKeys = Object.getOwnPropertySymbols(target); - for (var i = 0; i < allKeys.length; i += 1) { - var key = allKeys[i]; - if (Object.getOwnPropertyDescriptor(target, key).enumerable) { - keys.push(key); - } - } - return keys; -} - -/*! - * Determines if two objects have matching values, given a set of keys. Defers to deepEqual for the equality check of - * each key. If any value of the given key is not equal, the function will return false (early). - * - * @param {Mixed} leftHandOperand - * @param {Mixed} rightHandOperand - * @param {Array} keys An array of keys to compare the values of leftHandOperand and rightHandOperand against - * @param {Object} [options] (Optional) - * @return {Boolean} result - */ -function keysEqual(leftHandOperand, rightHandOperand, keys, options) { - var length = keys.length; - if (length === 0) { - return true; - } - for (var i = 0; i < length; i += 1) { - if (deepEqual(leftHandOperand[keys[i]], rightHandOperand[keys[i]], options) === false) { - return false; - } - } - return true; -} - -/*! - * Recursively check the equality of two Objects. Once basic sameness has been established it will defer to `deepEqual` - * for each enumerable key in the object. - * - * @param {Mixed} leftHandOperand - * @param {Mixed} rightHandOperand - * @param {Object} [options] (Optional) - * @return {Boolean} result - */ -function objectEqual(leftHandOperand, rightHandOperand, options) { - var leftHandKeys = getEnumerableKeys(leftHandOperand); - var rightHandKeys = getEnumerableKeys(rightHandOperand); - var leftHandSymbols = getEnumerableSymbols(leftHandOperand); - var rightHandSymbols = getEnumerableSymbols(rightHandOperand); - leftHandKeys = leftHandKeys.concat(leftHandSymbols); - rightHandKeys = rightHandKeys.concat(rightHandSymbols); - - if (isElement(leftHandOperand)) { - return leftHandOperand.id === rightHandOperand.id; - } - - if (leftHandKeys.length && leftHandKeys.length === rightHandKeys.length) { - if (iterableEqual(mapSymbols(leftHandKeys).sort(), mapSymbols(rightHandKeys).sort()) === false) { - return false; - } - return keysEqual(leftHandOperand, rightHandOperand, leftHandKeys, options); - } - - var leftHandEntries = getIteratorEntries(leftHandOperand); - var rightHandEntries = getIteratorEntries(rightHandOperand); - if (leftHandEntries.length && leftHandEntries.length === rightHandEntries.length) { - leftHandEntries.sort(); - rightHandEntries.sort(); - return iterableEqual(leftHandEntries, rightHandEntries, options); - } - - if (leftHandKeys.length === 0 && - leftHandEntries.length === 0 && - rightHandKeys.length === 0 && - rightHandEntries.length === 0) { - return true; - } - - return false; -} - -/*! - * Returns true if the argument is a primitive. - * - * This intentionally returns true for all objects that can be compared by reference, - * including functions and symbols. - * - * @param {Mixed} value - * @return {Boolean} result - */ -function isPrimitive(value) { - return value === null || typeof value !== 'object'; -} - -function mapSymbols(arr) { - return arr.map(function mapSymbol(entry) { - if (typeof entry === 'symbol') { - return entry.toString(); - } - - return entry; - }); -} - -/*! - * Primary Export - */ - -module.exports = { - deepEqual, - isElement -}; \ No newline at end of file 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 a2e42262860..38d0d084c75 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 @@ -41,7 +41,7 @@ const t = traversalModule.t; const P = traversalModule.P; const direction = traversalModule.direction; const merge = traversalModule.merge; -const { deepMembersById, deepMembersById2 } = require('./element-comparison'); +const deepMembersById = require('./element-comparison').deepMembersById; const parsers = [ [ 'str\\[(.*)\\]', (stringValue) => stringValue ], //returns the string value as is [ 'vp\\[(.+)\\]', toVertexProperty ], @@ -66,8 +66,8 @@ const parsers = [ chai.use(function (chai, chaiUtils) { chai.Assertion.overwriteMethod('members', function (_super) { - return deepMembersById2; - }); + return deepMembersById; + }); }); const ignoreReason = { @@ -244,18 +244,13 @@ Then(/^the result should be (\w+)$/, function assertResult(characterizedAs, resu switch (characterizedAs) { case 'ordered': expect(expectedResult).to.have.deep.ordered.members(toCompare(this.result)); -// expect(expectedResult).to.test(toCompare(this.result)); -// expect(deepMembersByIdOrdered(toCompare(this.result), expectedResult)).to.be.true; break; case 'unordered': expect(expectedResult).to.have.deep.members(toCompare(this.result)); -// expect(expectedResult).to.test(toCompare(this.result)); -// expect(deepMembersById(toCompare(this.result), expectedResult)).to.be.true; break; case 'of': // result is a subset of the expected expect(expectedResult).to.include.deep.members(toCompare(this.result)); -// expect(deepIncludesById(expectedResult, toCompare(this.result))).to.be.true; break; } }); 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 357b2b15a88..0296a7da94e 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 @@ -69,15 +69,12 @@ setWorldConstructor(TinkerPopWorld); BeforeAll(function () { // load all traversals const promises = ['modern', 'classic', 'crew', 'grateful', 'sink', 'empty'].map(graphName => { - console.log("🚀 ~ file: world.js:72 ~ promises ~ graphName:", graphName) let connection = null; if (graphName === 'empty') { connection = helper.getConnection('ggraph'); - console.log("🚀 ~ file: world.js:75 ~ promises ~ connection:", connection) return connection.open().then(() => cache['empty'] = { connection: connection }); } connection = helper.getConnection('g' + graphName); - console.log("🚀 ~ file: world.js:78 ~ promises ~ connection:", connection) return connection.open() .then(() => Promise.all([getVertices(connection), getEdges(connection), getVertexProperties(connection)])) .then(values => { diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js index b2c666ea140..be8a1f2c679 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js @@ -1,267 +1,262 @@ -/* - * 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. - */ - -const chai = require('chai') -const { expect } = require('chai'); -const { VertexProperty, Property, Vertex, Edge, Path } = require('../../lib/structure/graph'); -const { deepCopy, deepMembersById, deepMembersByIdOrdered, deepSort, isElement} = require('../cucumber/element-comparison'); - -chai.use(function (chai, utils) { - chai.Assertion.addChainableMethod('test', function (expected) { - const obj = this._obj; - const result = deepMembersById(obj, expected); - this.assert( - result, - `expected ${obj} to deeply equal ${expected}`, - `expected ${obj} not to deeply equal ${expected}` - ); - }); -}); - -describe('primitives', function () { - it('should pass', function () { - expect(1).to.test(1); - expect(deepMembersById(1, 1)).to.be.true; - expect(deepMembersById(false, false)).to.be.true; - expect(deepMembersById(null, null)).to.be.true; - }); - - it('should fail', function () { - expect(deepMembersById(1, 2)).to.be.false; - expect(deepMembersById(true, false)).to.be.false; - expect(deepMembersById(0, "0")).to.be.false; - expect(deepMembersById(0, false)).to.be.false; - expect(deepMembersById(0, null)).to.be.false; - expect(deepMembersById(false, null)).to.be.false; - }); -}); - -describe('elements', function () { - const v1 = new Vertex(1, "dog", undefined); - const v2 = new Vertex(1, "cat", undefined); - const v3 = new Vertex(2, "cat", undefined); - - const e1 = new Edge(2, v1, "chases", v3, undefined); - const e2 = new Edge(3, v1, "chases", v3, undefined); - const e3 = new Edge(3, v2, "chases", v3, undefined); - - const vp1 = new VertexProperty(3, "size", "small", undefined); - const vp2 = new VertexProperty(3, "size", "large", undefined); - const vp3 = new VertexProperty(4, "size", "large", undefined); - - it('should pass with same id, different values', function () { - expect(deepMembersById(v1, v2)).to.be.true; - expect(deepMembersById(v3, e1)).to.be.true; - expect(deepMembersById(e2, e3)).to.be.true; - expect(deepMembersById(e3, vp1)).to.be.true; - expect(deepMembersById(vp1, vp2)).to.be.true; - }); - - it('should fail with different id, same values', function () { - expect(deepMembersById(v2, v3)).to.be.false; - expect(deepMembersById(e1, e2)).to.be.false; - expect(deepMembersById(vp2, vp3)).to.be.false; - }); -}); - -describe('property', function () { - const p1 = new Property("a", "1"); - const p2 = new Property("a", "1"); - const p3 = new Property("a", 1); - const p4 = new Property(1, 1); - const p5 = new Property(1, "a"); - - it('should pass only properties that match exactly', function () { - expect(deepMembersById(p1, p2)).to.be.true; - expect(deepMembersById(p1, p3)).to.be.false; - expect(deepMembersById(p1, p4)).to.be.false; - expect(deepMembersById(p3, p5)).to.be.false; - expect(deepMembersById(p3, p5)).to.be.false; - }); -}); - -describe('arrays', function () { - const a1 = [1, 2, 3]; - const a2 = [1, 2, 3]; - const a3 = [2, 1, 3]; - const a4 = [2, 2, 3]; - const a5 = [1, 2, 3, 4]; - - it('unordered', function () { - expect(deepMembersById(a1, a2)).to.be.true; - expect(deepMembersById(a1, a3)).to.be.true; - expect(deepMembersById(a1, a4)).to.be.false; - expect(deepMembersById(a1, a5)).to.be.false; - expect(deepMembersById(a5, a1)).to.be.false; - }); - - it('ordered', function () { - expect(deepMembersByIdOrdered(a1, a2)).to.be.true; - expect(deepMembersByIdOrdered(a1, a3)).to.be.false; - expect(deepMembersByIdOrdered(a1, a4)).to.be.false; - expect(deepMembersByIdOrdered(a1, a5)).to.be.false; - expect(deepMembersByIdOrdered(a5, a1)).to.be.false; - }); - - const a6 = [1, 2, a1]; - const a7 = [1, a3, 2]; - const a8 = [1, 2, a5]; - - const a9 = [1, a1, a1]; - const a10 = [1, a1, a3]; - - const a11 = [a9] - const a12 = [a10] - - it('unordered nested', function () { - expect(deepMembersById(a6, a7)).to.be.true; - expect(deepMembersById(a6, a8)).to.be.false; - expect(deepMembersById(a9, a10)).to.be.true; - expect(deepMembersById(a11, a12)).to.be.true; - }); - - it('ordered nested', function () { - expect(deepMembersByIdOrdered(a6, a6)).to.be.true; - expect(deepMembersByIdOrdered(a6, a7)).to.be.false; - expect(deepMembersByIdOrdered(a6, a8)).to.be.false; - expect(deepMembersByIdOrdered(a9, a10)).to.be.false; - expect(deepMembersByIdOrdered(a10, a10)).to.be.true; - expect(deepMembersByIdOrdered(a11, a11)).to.be.true; - expect(deepMembersByIdOrdered(a11, a12)).to.be.false; - }); -}); - -describe('map', function () { - const m1 = new Map([ - ['ripple', [new Vertex(5, 'software', undefined)]], - ['peter', [new Vertex(6, 'person', undefined)]], - ['vadas', [new Vertex(2, 'person', undefined)]] - ]); - const m2 = new Map([ - ['ripple', [new Vertex(5, 'software', undefined)]], - ['peter', [new Vertex(6, 'person', undefined)]] - ]); - const m3 = new Map([ - ['ripple', [new Vertex(5, 'software', undefined)]], - ['vadas', [new Vertex(2, 'person', undefined)]], - ['peter', [new Vertex(6, 'person', undefined)]] - ]); - const m4 = new Map([ - ['ripple', [new Vertex(5, 'software', undefined)]], - ['vadas', [new Vertex(6, 'person', undefined)]], - ['peter', [new Vertex(2, 'person', undefined)]] - ]); - - it('unordered', function () { - expect(deepMembersById(m1, m1)).to.be.true; - expect(deepMembersById(m1, m2)).to.be.false; - expect(deepMembersById(m1, m3)).to.be.true; - expect(deepMembersById(m1, m4)).to.be.false; - }); - - it('ordered', function () { - expect(deepMembersByIdOrdered(m1, m1)).to.be.true; - expect(deepMembersByIdOrdered(m1, m2)).to.be.false; - expect(deepMembersByIdOrdered(m1, m3)).to.be.false; - expect(deepMembersByIdOrdered(m1, m4)).to.be.false; - }); -}); - -describe('objects', function () { - const obj1 = { - k1: "v1", - k2: "v2", - k3: { k4: "v4", k5: "v5" } - }; - const obj2 = { - k1: "v1", - k2: "v2", - k3: { k4: "v4", k5: "v5" } - }; - const obj3 = { - k2: "v2", - k1: "v1", - k3: { k5: "v5", k4: "v4" } - }; - const obj4 = { - k1: "v1", - k2: "v2", - k3: { k4: "v4" } - }; - const obj5 = { - k1: "v1", - k2: "v2", - k3: { k4: "v5", k5: "v4" } - }; - - it('should pass', function () { - expect(deepMembersById(obj1, obj2)).to.be.true; // identical - expect(deepMembersById(obj1, obj3)).to.be.true; // order swapped - }); - - it('should fail', function () { - expect(deepMembersById(obj1, obj4)).to.be.false; // missing nested kvp - expect(deepMembersById(obj1, obj5)).to.be.false; // values swapped - }); - - const obj6 = { - k1: "v1", - k2: "v2", - k3: [ 1, 2, 3 ] - }; - const obj7 = { - k3: [ 1, 2, 3 ], - k1: "v1", - k2: "v2" - }; - const obj8 = { - k1: "v1", - k2: "v2", - k3: [ 2, 1, 3 ] - } - - it('unordered', function () { - expect(deepMembersById(obj6, obj7)).to.be.true; - expect(deepMembersById(obj6, obj8)).to.be.true; - }); - - it('ordered', function () { - expect(deepMembersByIdOrdered(obj6, obj7)).to.be.true; // kvp can be unordered - expect(deepMembersByIdOrdered(obj6, obj8)).to.be.false; // array must be ordered - }); - - const path1 = new Path(["path"], [new Vertex(1, 'person', undefined), - new Vertex(2, 'person', undefined)]); - const path2 = new Path(["path"], [new Vertex(1, 'person', undefined)]); - const path3 = new Path(["path"], [new Vertex(2, 'person', undefined), - new Vertex(1, 'person', undefined)]); - - it('unordered', function () { - expect(deepMembersById(path1, path1)).to.be.true; - expect(deepMembersById(path1, path2)).to.be.false; - expect(deepMembersById(path1, path3)).to.be.false; - }); - - it('ordered', function () { - expect(deepMembersById(path1, path1)).to.be.true; - expect(deepMembersById(path1, path2)).to.be.false; - expect(deepMembersById(path1, path3)).to.be.false; - }); - -}); +///* +// * 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. +// */ +// +//const chai = require('chai') +//const { expect } = require('chai'); +//const { VertexProperty, Property, Vertex, Edge, Path } = require('../../lib/structure/graph'); +//const { deepMembersById, compareElements, opt } = require('../cucumber/element-comparison'); +//const deepEqual = require('deep-eql'); +// +//chai.use(function (chai, chaiUtils) { +// chai.Assertion.overwriteMethod('members', function (_super) { +// return deepMembersById; +// }); +//}); +// +//describe('primitives', function () { +// it('should pass', function () { +// expect(deepEqual(1, 1, opt)).to.be.true; +// expect(deepEqual(false, false, opt)).to.be.true; +// expect(deepEqual(null, null, opt)).to.be.true; +// }); +// +// it('should fail', function () { +// expect(deepEqual(1, 2, opt)).to.be.false; +// expect(deepEqual(true, false, opt)).to.be.false; +// expect(deepEqual(0, "0", opt)).to.be.false; +// expect(deepEqual(0, false, opt)).to.be.false; +// expect(deepEqual(0, null, opt)).to.be.false; +// expect(deepEqual(false, null, opt)).to.be.false; +// }); +//}); +// +//describe('elements', function () { +// const v1 = new Vertex(1, "dog", undefined); +// const v2 = new Vertex(1, "cat", undefined); +// const v3 = new Vertex(2, "cat", undefined); +// +// const e1 = new Edge(2, v1, "chases", v3, undefined); +// const e2 = new Edge(3, v1, "chases", v3, undefined); +// const e3 = new Edge(3, v2, "chases", v3, undefined); +// +// const vp1 = new VertexProperty(3, "size", "small", undefined); +// const vp2 = new VertexProperty(3, "size", "large", undefined); +// const vp3 = new VertexProperty(4, "size", "large", undefined); +// +// it('should pass with same id, different values', function () { +// expect(deepEqual(v1, v2, opt)).to.be.true; +// expect([v1]).to.have.deep.ordered.members([v2]); +// expect(deepEqual(v3, e1, opt)).to.be.true; +// expect(deepEqual(e2, e3, opt)).to.be.true; +// expect(deepEqual(e3, vp1, opt)).to.be.true; +// expect(deepEqual(vp1, vp2, opt)).to.be.true; +// }); +// +// it('should fail with different id, same values', function () { +// expect(deepEqual(v2, v3, opt)).to.be.false; +// expect(deepEqual(e1, e2, opt)).to.be.false; +// expect(deepEqual(vp2, vp3, opt)).to.be.false; +// }); +//}); +// +//describe('property', function () { +// const p1 = new Property("a", "1"); +// const p2 = new Property("a", "1"); +// const p3 = new Property("a", 1); +// const p4 = new Property(1, 1); +// const p5 = new Property(1, "a"); +// +// it('should pass only properties that match exactly', function () { +// expect(deepEqual(p1, p2, opt)).to.be.true; +// expect(deepEqual(p1, p3, opt)).to.be.false; +// expect(deepEqual(p1, p4, opt)).to.be.false; +// expect(deepEqual(p3, p5, opt)).to.be.false; +// expect(deepEqual(p3, p5, opt)).to.be.false; +// }); +//}); +// +//describe('arrays', function () { +// const a1 = [1, 2, 3]; +// const a2 = [1, 2, 3]; +// const a3 = [2, 1, 3]; +// const a4 = [2, 2, 3]; +// const a5 = [1, 2, 3, 4]; +// +// it('unordered', function () { +// expect(a1).to.have.deep.members(a2); +// expect(a1).to.have.deep.members(a3); +// expect(a1).to.not.have.deep.members(a4); +// expect(a1).to.not.have.deep.members(a5); +// expect(a5).to.not.have.deep.members(a1); +// }); +// +// it('ordered', function () { +// expect(a1).to.have.deep.ordered.members(a2); +// expect(a1).to.not.have.deep.ordered.members(a3); +// expect(a1).to.not.have.deep.ordered.members(a4); +// expect(a1).to.not.have.deep.ordered.members(a5); +// expect(a5).to.not.have.deep.ordered.members(a1); +// }); +// +// const a6 = [1, 2, a1]; +// const a7 = [1, a3, 2]; +// const a8 = [1, 2, a5]; +// +// const a9 = [1, a1, a1]; +// const a10 = [1, a1, a3]; +// +// const a11 = [a9] +// const a12 = [a10] +// +// it('unordered nested', function () { +// expect(a6).to.have.deep.members(a7); +// expect(a6).to.not.have.deep.members(a8); +// expect(a9).to.have.deep.members(a10); +// expect(a11).to.have.deep.members(a12); +// }); +// +// it('ordered nested', function () { +// expect(a6).to.have.deep.ordered.members(a6); +// expect(a6).to.not.have.deep.ordered.members(a7); +// expect(a6).to.not.have.deep.ordered.members(a8); +// expect(a9).to.not.have.deep.ordered.members(a10); +// expect(a10).to.have.deep.ordered.members(a10); +// expect(a11).to.have.deep.ordered.members(a11); +// expect(a11).to.not.have.deep.ordered.members(a12); +// }); +//}); +// +//describe('map', function () { +// const m1 = [new Map([ +// ['ripple', [new Vertex(5, 'software', undefined)]], +// ['peter', [new Vertex(6, 'person', undefined)]], +// ['vadas', [new Vertex(2, 'person', undefined)]] +// ])]; +// const m2 = [new Map([ +// ['ripple', [new Vertex(5, 'software', undefined)]], +// ['peter', [new Vertex(6, 'person', undefined)]] +// ])]; +// const m3 = [new Map([ +// ['ripple', [new Vertex(5, 'software', undefined)]], +// ['vadas', [new Vertex(2, 'person', undefined)]], +// ['peter', [new Vertex(6, 'person', undefined)]] +// ])]; +// const m4 = [new Map([ +// ['ripple', [new Vertex(5, 'software', undefined)]], +// ['vadas', [new Vertex(6, 'person', undefined)]], +// ['peter', [new Vertex(2, 'person', undefined)]] +// ])]; +// +// it('unordered', function () { +// expect(m1).to.have.deep.members(m1); +// expect(m1).to.not.have.deep.members(m2); +// expect(m1).to.have.deep.members(m3); +// expect(m1).to.not.have.deep.members(m4); +// }); +// +// it('ordered', function () { +// expect(m1).to.have.deep.ordered.members(m1); +// expect(m1).to.not.have.deep.ordered.members(m2); +// expect(m1).to.not.have.deep.ordered.members(m3); +// expect(m1).to.not.have.deep.ordered.members(m4); +// }); +//}); +// +//describe('objects', function () { +// const obj1 = [{ +// k1: "v1", +// k2: "v2", +// k3: { k4: "v4", k5: "v5" } +// }]; +// const obj2 = [{ +// k1: "v1", +// k2: "v2", +// k3: { k4: "v4", k5: "v5" } +// }]; +// const obj3 = [{ +// k2: "v2", +// k1: "v1", +// k3: { k5: "v5", k4: "v4" } +// }]; +// const obj4 = [{ +// k1: "v1", +// k2: "v2", +// k3: { k4: "v4" } +// }]; +// const obj5 = [{ +// k1: "v1", +// k2: "v2", +// k3: { k4: "v5", k5: "v4" } +// }]; +// +// it('should pass', function () { +// expect(obj1).to.have.deep.members(obj2); // identical +// expect(obj1).to.have.deep.members(obj3); // order swapped +// }); +// +// it('should fail', function () { +// expect(obj1).to.not.have.deep.members(obj4); // missing nested kvp +// expect(obj1).to.not.have.deep.members(obj5); // values swapped +// }); +// +// const obj6 = [{ +// k1: "v1", +// k2: "v2", +// k3: [ 1, 2, 3 ] +// }]; +// const obj7 = [{ +// k3: [ 1, 2, 3 ], +// k1: "v1", +// k2: "v2" +// }]; +// const obj8 = [{ +// k1: "v1", +// k2: "v2", +// k3: [ 2, 1, 3 ] +// }]; +// +// it('unordered', function () { +// expect(obj6).to.have.deep.members(obj7); +// expect(obj6).to.have.deep.members(obj8); +// }); +// +// it('ordered', function () { +// expect(obj6).to.have.deep.ordered.members(obj7); // kvp can be unordered +// expect(obj6).to.not.have.deep.ordered.members(obj8); // array must be ordered +// }); +// +// const path1 = new Path(["path"], [new Vertex(1, 'person', undefined), +// new Vertex(2, 'person', undefined)]); +// const path2 = new Path(["path"], [new Vertex(1, 'person', undefined)]); +// const path3 = new Path(["path"], [new Vertex(2, 'person', undefined), +// new Vertex(1, 'person', undefined)]); +// +// it('unordered', function () { +// expect(deepEqual(path1, path1)).to.be.true; +// expect(deepEqual(path1, path2)).to.be.false; // unsure +// expect(deepEqual(path1, path3)).to.be.false; +// }); +// +// it('ordered', function () { // unsure +// expect(deepEqual(path1, path1)).to.be.true; +// expect(deepEqual(path1, path2)).to.be.false; +// expect(deepEqual(path1, path3)).to.be.false; +// }); +// +//}); From e96eef5354cdcf922671c59a1af4e9fa2f00f9bc Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Fri, 5 Jan 2024 12:56:49 -0800 Subject: [PATCH 07/18] Add unit tests --- .../test/cucumber/element-comparison.js | 3 +- .../test/unit/element-comparison-test.js | 524 +++++++++--------- 2 files changed, 264 insertions(+), 263 deletions(-) diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js index 60abdbe3874..81a65d611f7 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js @@ -19,12 +19,13 @@ const chai = require('chai'); const deepEqual = require('deep-eql'); -const opt = {comparator: compareElements}; function isElement(obj) { return obj !== null && obj.hasOwnProperty('id') && obj.hasOwnProperty('label'); } +const opt = {comparator: compareElements}; + function isSubsetOf(subset, superset, cmp, contains, ordered) { if (!contains) { if (subset.length !== superset.length) return false; diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js index be8a1f2c679..0db86135c6f 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js @@ -1,262 +1,262 @@ -///* -// * 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. -// */ -// -//const chai = require('chai') -//const { expect } = require('chai'); -//const { VertexProperty, Property, Vertex, Edge, Path } = require('../../lib/structure/graph'); -//const { deepMembersById, compareElements, opt } = require('../cucumber/element-comparison'); -//const deepEqual = require('deep-eql'); -// -//chai.use(function (chai, chaiUtils) { -// chai.Assertion.overwriteMethod('members', function (_super) { -// return deepMembersById; -// }); -//}); -// -//describe('primitives', function () { -// it('should pass', function () { -// expect(deepEqual(1, 1, opt)).to.be.true; -// expect(deepEqual(false, false, opt)).to.be.true; -// expect(deepEqual(null, null, opt)).to.be.true; -// }); -// -// it('should fail', function () { -// expect(deepEqual(1, 2, opt)).to.be.false; -// expect(deepEqual(true, false, opt)).to.be.false; -// expect(deepEqual(0, "0", opt)).to.be.false; -// expect(deepEqual(0, false, opt)).to.be.false; -// expect(deepEqual(0, null, opt)).to.be.false; -// expect(deepEqual(false, null, opt)).to.be.false; -// }); -//}); -// -//describe('elements', function () { -// const v1 = new Vertex(1, "dog", undefined); -// const v2 = new Vertex(1, "cat", undefined); -// const v3 = new Vertex(2, "cat", undefined); -// -// const e1 = new Edge(2, v1, "chases", v3, undefined); -// const e2 = new Edge(3, v1, "chases", v3, undefined); -// const e3 = new Edge(3, v2, "chases", v3, undefined); -// -// const vp1 = new VertexProperty(3, "size", "small", undefined); -// const vp2 = new VertexProperty(3, "size", "large", undefined); -// const vp3 = new VertexProperty(4, "size", "large", undefined); -// -// it('should pass with same id, different values', function () { -// expect(deepEqual(v1, v2, opt)).to.be.true; -// expect([v1]).to.have.deep.ordered.members([v2]); -// expect(deepEqual(v3, e1, opt)).to.be.true; -// expect(deepEqual(e2, e3, opt)).to.be.true; -// expect(deepEqual(e3, vp1, opt)).to.be.true; -// expect(deepEqual(vp1, vp2, opt)).to.be.true; -// }); -// -// it('should fail with different id, same values', function () { -// expect(deepEqual(v2, v3, opt)).to.be.false; -// expect(deepEqual(e1, e2, opt)).to.be.false; -// expect(deepEqual(vp2, vp3, opt)).to.be.false; -// }); -//}); -// -//describe('property', function () { -// const p1 = new Property("a", "1"); -// const p2 = new Property("a", "1"); -// const p3 = new Property("a", 1); -// const p4 = new Property(1, 1); -// const p5 = new Property(1, "a"); -// -// it('should pass only properties that match exactly', function () { -// expect(deepEqual(p1, p2, opt)).to.be.true; -// expect(deepEqual(p1, p3, opt)).to.be.false; -// expect(deepEqual(p1, p4, opt)).to.be.false; -// expect(deepEqual(p3, p5, opt)).to.be.false; -// expect(deepEqual(p3, p5, opt)).to.be.false; -// }); -//}); -// -//describe('arrays', function () { -// const a1 = [1, 2, 3]; -// const a2 = [1, 2, 3]; -// const a3 = [2, 1, 3]; -// const a4 = [2, 2, 3]; -// const a5 = [1, 2, 3, 4]; -// -// it('unordered', function () { -// expect(a1).to.have.deep.members(a2); -// expect(a1).to.have.deep.members(a3); -// expect(a1).to.not.have.deep.members(a4); -// expect(a1).to.not.have.deep.members(a5); -// expect(a5).to.not.have.deep.members(a1); -// }); -// -// it('ordered', function () { -// expect(a1).to.have.deep.ordered.members(a2); -// expect(a1).to.not.have.deep.ordered.members(a3); -// expect(a1).to.not.have.deep.ordered.members(a4); -// expect(a1).to.not.have.deep.ordered.members(a5); -// expect(a5).to.not.have.deep.ordered.members(a1); -// }); -// -// const a6 = [1, 2, a1]; -// const a7 = [1, a3, 2]; -// const a8 = [1, 2, a5]; -// -// const a9 = [1, a1, a1]; -// const a10 = [1, a1, a3]; -// -// const a11 = [a9] -// const a12 = [a10] -// -// it('unordered nested', function () { -// expect(a6).to.have.deep.members(a7); -// expect(a6).to.not.have.deep.members(a8); -// expect(a9).to.have.deep.members(a10); -// expect(a11).to.have.deep.members(a12); -// }); -// -// it('ordered nested', function () { -// expect(a6).to.have.deep.ordered.members(a6); -// expect(a6).to.not.have.deep.ordered.members(a7); -// expect(a6).to.not.have.deep.ordered.members(a8); -// expect(a9).to.not.have.deep.ordered.members(a10); -// expect(a10).to.have.deep.ordered.members(a10); -// expect(a11).to.have.deep.ordered.members(a11); -// expect(a11).to.not.have.deep.ordered.members(a12); -// }); -//}); -// -//describe('map', function () { -// const m1 = [new Map([ -// ['ripple', [new Vertex(5, 'software', undefined)]], -// ['peter', [new Vertex(6, 'person', undefined)]], -// ['vadas', [new Vertex(2, 'person', undefined)]] -// ])]; -// const m2 = [new Map([ -// ['ripple', [new Vertex(5, 'software', undefined)]], -// ['peter', [new Vertex(6, 'person', undefined)]] -// ])]; -// const m3 = [new Map([ -// ['ripple', [new Vertex(5, 'software', undefined)]], -// ['vadas', [new Vertex(2, 'person', undefined)]], -// ['peter', [new Vertex(6, 'person', undefined)]] -// ])]; -// const m4 = [new Map([ -// ['ripple', [new Vertex(5, 'software', undefined)]], -// ['vadas', [new Vertex(6, 'person', undefined)]], -// ['peter', [new Vertex(2, 'person', undefined)]] -// ])]; -// -// it('unordered', function () { -// expect(m1).to.have.deep.members(m1); -// expect(m1).to.not.have.deep.members(m2); -// expect(m1).to.have.deep.members(m3); -// expect(m1).to.not.have.deep.members(m4); -// }); -// -// it('ordered', function () { -// expect(m1).to.have.deep.ordered.members(m1); -// expect(m1).to.not.have.deep.ordered.members(m2); -// expect(m1).to.not.have.deep.ordered.members(m3); -// expect(m1).to.not.have.deep.ordered.members(m4); -// }); -//}); -// -//describe('objects', function () { -// const obj1 = [{ -// k1: "v1", -// k2: "v2", -// k3: { k4: "v4", k5: "v5" } -// }]; -// const obj2 = [{ -// k1: "v1", -// k2: "v2", -// k3: { k4: "v4", k5: "v5" } -// }]; -// const obj3 = [{ -// k2: "v2", -// k1: "v1", -// k3: { k5: "v5", k4: "v4" } -// }]; -// const obj4 = [{ -// k1: "v1", -// k2: "v2", -// k3: { k4: "v4" } -// }]; -// const obj5 = [{ -// k1: "v1", -// k2: "v2", -// k3: { k4: "v5", k5: "v4" } -// }]; -// -// it('should pass', function () { -// expect(obj1).to.have.deep.members(obj2); // identical -// expect(obj1).to.have.deep.members(obj3); // order swapped -// }); -// -// it('should fail', function () { -// expect(obj1).to.not.have.deep.members(obj4); // missing nested kvp -// expect(obj1).to.not.have.deep.members(obj5); // values swapped -// }); -// -// const obj6 = [{ -// k1: "v1", -// k2: "v2", -// k3: [ 1, 2, 3 ] -// }]; -// const obj7 = [{ -// k3: [ 1, 2, 3 ], -// k1: "v1", -// k2: "v2" -// }]; -// const obj8 = [{ -// k1: "v1", -// k2: "v2", -// k3: [ 2, 1, 3 ] -// }]; -// -// it('unordered', function () { -// expect(obj6).to.have.deep.members(obj7); -// expect(obj6).to.have.deep.members(obj8); -// }); -// -// it('ordered', function () { -// expect(obj6).to.have.deep.ordered.members(obj7); // kvp can be unordered -// expect(obj6).to.not.have.deep.ordered.members(obj8); // array must be ordered -// }); -// -// const path1 = new Path(["path"], [new Vertex(1, 'person', undefined), -// new Vertex(2, 'person', undefined)]); -// const path2 = new Path(["path"], [new Vertex(1, 'person', undefined)]); -// const path3 = new Path(["path"], [new Vertex(2, 'person', undefined), -// new Vertex(1, 'person', undefined)]); -// -// it('unordered', function () { -// expect(deepEqual(path1, path1)).to.be.true; -// expect(deepEqual(path1, path2)).to.be.false; // unsure -// expect(deepEqual(path1, path3)).to.be.false; -// }); -// -// it('ordered', function () { // unsure -// expect(deepEqual(path1, path1)).to.be.true; -// expect(deepEqual(path1, path2)).to.be.false; -// expect(deepEqual(path1, path3)).to.be.false; -// }); -// -//}); +/* + * 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. + */ + +const chai = require('chai') +const { expect } = require('chai'); +const { VertexProperty, Property, Vertex, Edge, Path } = require('../../lib/structure/graph'); +const { deepMembersById, compareElements, opt } = require('../cucumber/element-comparison'); +const deepEqual = require('deep-eql'); + +chai.use(function (chai, chaiUtils) { + chai.Assertion.overwriteMethod('members', function (_super) { + return deepMembersById; + }); +}); + +describe('primitives', function () { + it('should pass', function () { + expect(deepEqual(1, 1, opt)).to.be.true; + expect(deepEqual(false, false, opt)).to.be.true; + expect(deepEqual(null, null, opt)).to.be.true; + }); + + it('should fail', function () { + expect(deepEqual(1, 2, opt)).to.be.false; + expect(deepEqual(true, false, opt)).to.be.false; + expect(deepEqual(0, "0", opt)).to.be.false; + expect(deepEqual(0, false, opt)).to.be.false; + expect(deepEqual(0, null, opt)).to.be.false; + expect(deepEqual(false, null, opt)).to.be.false; + }); +}); + +describe('elements', function () { + const v1 = new Vertex(1, "dog", undefined); + const v2 = new Vertex(1, "cat", undefined); + const v3 = new Vertex(2, "cat", undefined); + + const e1 = new Edge(2, v1, "chases", v3, undefined); + const e2 = new Edge(3, v1, "chases", v3, undefined); + const e3 = new Edge(3, v2, "chases", v3, undefined); + + const vp1 = new VertexProperty(3, "size", "small", undefined); + const vp2 = new VertexProperty(3, "size", "large", undefined); + const vp3 = new VertexProperty(4, "size", "large", undefined); + + it('should pass with same id, different values', function () { + expect(deepEqual(v1, v2, opt)).to.be.true; + expect([v1]).to.have.deep.ordered.members([v2]); + expect(deepEqual(v3, e1, opt)).to.be.true; + expect(deepEqual(e2, e3, opt)).to.be.true; + expect(deepEqual(e3, vp1, opt)).to.be.true; + expect(deepEqual(vp1, vp2, opt)).to.be.true; + }); + + it('should fail with different id, same values', function () { + expect(deepEqual(v2, v3, opt)).to.be.false; + expect(deepEqual(e1, e2, opt)).to.be.false; + expect(deepEqual(vp2, vp3, opt)).to.be.false; + }); +}); + +describe('property', function () { + const p1 = new Property("a", "1"); + const p2 = new Property("a", "1"); + const p3 = new Property("a", 1); + const p4 = new Property(1, 1); + const p5 = new Property(1, "a"); + + it('should pass only properties that match exactly', function () { + expect(deepEqual(p1, p2, opt)).to.be.true; + expect(deepEqual(p1, p3, opt)).to.be.false; + expect(deepEqual(p1, p4, opt)).to.be.false; + expect(deepEqual(p3, p5, opt)).to.be.false; + expect(deepEqual(p3, p5, opt)).to.be.false; + }); +}); + +describe('arrays', function () { + const a1 = [1, 2, 3]; + const a2 = [1, 2, 3]; + const a3 = [2, 1, 3]; + const a4 = [2, 2, 3]; + const a5 = [1, 2, 3, 4]; + + it('unordered', function () { + expect(a1).to.have.deep.members(a2); + expect(a1).to.have.deep.members(a3); + expect(a1).to.not.have.deep.members(a4); + expect(a1).to.not.have.deep.members(a5); + expect(a5).to.not.have.deep.members(a1); + }); + + it('ordered', function () { + expect(a1).to.have.deep.ordered.members(a2); + expect(a1).to.not.have.deep.ordered.members(a3); + expect(a1).to.not.have.deep.ordered.members(a4); + expect(a1).to.not.have.deep.ordered.members(a5); + expect(a5).to.not.have.deep.ordered.members(a1); + }); + + const a6 = [1, 2, a1]; + const a7 = [1, a3, 2]; + const a8 = [1, 2, a5]; + + const a9 = [1, a1, a1]; + const a10 = [1, a1, a3]; + + const a11 = [a9] + const a12 = [a10] + + it('unordered nested', function () { + expect(a6).to.have.deep.members(a7); + expect(a6).to.not.have.deep.members(a8); + expect(a9).to.have.deep.members(a10); + expect(a11).to.have.deep.members(a12); + }); + + it('ordered nested', function () { + expect(a6).to.have.deep.ordered.members(a6); + expect(a6).to.not.have.deep.ordered.members(a7); + expect(a6).to.not.have.deep.ordered.members(a8); + expect(a9).to.not.have.deep.ordered.members(a10); + expect(a10).to.have.deep.ordered.members(a10); + expect(a11).to.have.deep.ordered.members(a11); + expect(a11).to.not.have.deep.ordered.members(a12); + }); +}); + +describe('map', function () { + const m1 = [new Map([ + ['ripple', [new Vertex(5, 'software', undefined)]], + ['peter', [new Vertex(6, 'person', undefined)]], + ['vadas', [new Vertex(2, 'person', undefined)]] + ])]; + const m2 = [new Map([ + ['ripple', [new Vertex(5, 'software', undefined)]], + ['peter', [new Vertex(6, 'person', undefined)]] + ])]; + const m3 = [new Map([ + ['ripple', [new Vertex(5, 'software', undefined)]], + ['vadas', [new Vertex(2, 'person', undefined)]], + ['peter', [new Vertex(6, 'person', undefined)]] + ])]; + const m4 = [new Map([ + ['ripple', [new Vertex(5, 'software', undefined)]], + ['vadas', [new Vertex(6, 'person', undefined)]], + ['peter', [new Vertex(2, 'person', undefined)]] + ])]; + + it('unordered', function () { + expect(m1).to.have.deep.members(m1); + expect(m1).to.not.have.deep.members(m2); + expect(m1).to.have.deep.members(m3); + expect(m1).to.not.have.deep.members(m4); + }); + + it('ordered', function () { + expect(m1).to.have.deep.ordered.members(m1); + expect(m1).to.not.have.deep.ordered.members(m2); + expect(m1).to.not.have.deep.ordered.members(m3); + expect(m1).to.not.have.deep.ordered.members(m4); + }); +}); + +describe('objects', function () { + const obj1 = [{ + k1: "v1", + k2: "v2", + k3: { k4: "v4", k5: "v5" } + }]; + const obj2 = [{ + k1: "v1", + k2: "v2", + k3: { k4: "v4", k5: "v5" } + }]; + const obj3 = [{ + k2: "v2", + k1: "v1", + k3: { k5: "v5", k4: "v4" } + }]; + const obj4 = [{ + k1: "v1", + k2: "v2", + k3: { k4: "v4" } + }]; + const obj5 = [{ + k1: "v1", + k2: "v2", + k3: { k4: "v5", k5: "v4" } + }]; + + it('should pass', function () { + expect(obj1).to.have.deep.members(obj2); // identical + expect(obj1).to.have.deep.members(obj3); // order swapped + }); + + it('should fail', function () { + expect(obj1).to.not.have.deep.members(obj4); // missing nested kvp + expect(obj1).to.not.have.deep.members(obj5); // values swapped + }); + + const obj6 = [{ + k1: "v1", + k2: "v2", + k3: [ 1, 2, 3 ] + }]; + const obj7 = [{ + k3: [ 1, 2, 3 ], + k1: "v1", + k2: "v2" + }]; + const obj8 = [{ + k1: "v1", + k2: "v2", + k3: [ 2, 1, 3 ] + }]; + + it('unordered', function () { + expect(obj6).to.have.deep.members(obj7); + expect(obj6).to.not.have.deep.members(obj8); + }); + + it('ordered', function () { + expect(obj6).to.have.deep.ordered.members(obj7); // kvp can be unordered + expect(obj6).to.not.have.deep.ordered.members(obj8); // array must be ordered + }); + + const path1 = new Path(["path"], [new Vertex(1, 'person', undefined), + new Vertex(2, 'person', undefined)]); + const path2 = new Path(["path"], [new Vertex(1, 'person', undefined)]); + const path3 = new Path(["path"], [new Vertex(2, 'person', undefined), + new Vertex(1, 'person', undefined)]); + + it('unordered', function () { + expect(deepEqual(path1, path1)).to.be.true; + expect(deepEqual(path1, path2)).to.be.false; + expect(deepEqual(path1, path3)).to.be.false; + }); + + it('ordered', function () { + expect(deepEqual(path1, path1)).to.be.true; + expect(deepEqual(path1, path2)).to.be.false; + expect(deepEqual(path1, path3)).to.be.false; + }); + +}); From 5b566d02f8b2e0e7adb76ee49dff5c6eefd37723 Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Fri, 5 Jan 2024 12:58:11 -0800 Subject: [PATCH 08/18] Delete t.js --- .../gremlin-javascript/test/unit/t.js | 226 ------------------ 1 file changed, 226 deletions(-) delete mode 100644 gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/t.js diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/t.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/t.js deleted file mode 100644 index a789c130389..00000000000 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/t.js +++ /dev/null @@ -1,226 +0,0 @@ -///* -// * 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. -// */ -//'use strict'; -// -//const util = require('util'); -//const gremlin = require('../cucumber/gremlin').gremlin; -//const utils = require('../../lib/utils'); -//const traversal = require('../../lib/process/anonymous-traversal').traversal; -//const { VertexProperty, Property, Vertex, Edge, Path } = require('../../lib/structure/graph'); -//const { deepCopy, deepMembersById, deepMembersByIdOrdered, deepSort, isElement} = require('../cucumber/element-comparison') -// -////const m1 = new Map([ -//// ['ripple', [new Vertex(5, 'software', undefined)]], -//// ['peter', [new Vertex(6, 'person', undefined)]], -//// ['vadas', [new Vertex(2, 'person', undefined)]], -//// ['josh', [new Vertex(4, 'person', undefined)]], -//// ['lop', [new Vertex(3, 'software', undefined)]], -//// ['marko', [new Vertex(1, 'person', undefined)]] -////]); -//// -////const m2 = new Map([ -//// ['ripple', [new Vertex(5, 'software', undefined)]], -//// ['peter', [new Vertex(6, 'person', undefined)]], -//// ['vadas', [new Vertex(2, 'person', undefined)]], -//// ['josh', [new Vertex(4, 'person', undefined)]], -//// ['lop', [new Vertex(3, 'software', undefined)]], -////]); -//// -////const m3 = new Map([ -//// ['ripple', [new Vertex(5, 'software', undefined)]], -//// ['peter', [new Vertex(6, 'person', undefined)]], -//// ['vadas', [new Vertex(2, 'person', undefined)]], -//// ['lop', [new Vertex(3, 'software', undefined)]], -//// ['josh', [new Vertex(4, 'person', undefined)]], -//// ['marko', [new Vertex(1, 'person', undefined)]] -////]); -// -//const a1 = [ -// new Vertex(5, 'software', undefined), -// new Vertex(6, 'person', undefined), -// new Vertex(2, 'person', undefined), -// new Vertex(4, 'person', undefined), -// new Vertex(3, 'software', undefined), -// new Vertex(1, 'person', undefined) -//]; -// -//const a2 = [ -// new Vertex(5, 'software', undefined), -// new Vertex(6, 'person', undefined), -// new Vertex(2, 'person', undefined), -// new Vertex(4, 'person', undefined), -// new Vertex(3, 'software', undefined) -//]; -// -//const a3 = [ -// new Vertex(5, 'software', undefined), -// new Vertex(6, 'person', undefined), -// new Vertex(2, 'person', undefined), -// new Vertex(3, 'software', undefined), -// new Vertex(4, 'person', undefined), -// new Vertex(1, 'person', undefined) -//]; -// -//const v1 = new Vertex(8, "dog", undefined) -//const v2 = new Vertex(9, "cat", undefined) -// -//const pl1 = [ -// new Path([], [ 'daniel', 6 ]), -// new Path([], [ v1, v2, 'lop', 3 ]), -// new Path([], [ v1, v2, 'vadas', 5 ]), -// new Path([], [ v1, v2, 'josh', 4 ]) -//] -// -//const pl2 = [ -// new Path([], [ 'daniel', 6 ]), -// new Path([], [ v1, v2, 'lop', 3 ]), -// new Path([], [ v1, v2, 'josh', 4 ]) -//] -// -//const pl3 = [ -// new Path([], [ 'daniel', 6 ]), -// new Path([], [ v1, v2, 'lop', 3 ]), -// new Path([], [ v1, v2, 'josh', 4 ]), -// new Path([], [ v1, v2, 'vadas', 5 ]), -//] -// -//const p1 = new Path([], -// [new Vertex(1, 'person', undefined), -// new Vertex(2, 'person', undefined) -// ]); -// -//const p2 = new Path([], -// [new Vertex(1, 'person', undefined), -// ]); -// -//const p3 = new Path([], -// new Vertex(2, 'person', undefined), -// [new Vertex(1, 'person', undefined), -// ]); -////console.log(v1) -////console.log(v2) -////console.log(deepMembersById(v1, v2)) -//// -////console.log(m1) -////console.log(m2) -// -////const m4 = [ -//// new Map([["name", "marko"], ["friendRank", 15.0]]), -//// new Map([["name", "vadas"], ["friendRank", 21.0]]), -//// new Map([["name", "lop"], ["friendRank", 15.0]]), -//// new Map([["name", "josh"], ["friendRank", 21.0]]), -//// new Map([["name", "ripple"], ["friendRank", 15.0]]), -//// new Map([["name", "peter"], ["friendRank", 15.0]]), -////]; -//// -////const m5 = [ -//// new Map([["name", "marko"], ["friendRank", 15.0]]), -//// new Map([["name", "vadas"], ["friendRank", 21.0]]), -//// new Map([["name", "lop"], ["friendRank", 15.0]]), -//// new Map([["name", "josh"], ["friendRank", 21.0]]), -////]; -// -// -//function assert(expected, actual) { -//// console.log("expected: " + expected, "actual: " + actual); -// console.log(expected, actual); -// if (expected !== actual) console.log('!!!!!!!!!!!!!!!!!!!!!!') -//} -//function tests(t, base, missing, swapped) { -// console.log(t, 'happy ordered'); -// assert(true, deepMembersById(base, base, true)); -// assert(true, deepMembersById(missing, missing, true)); -// assert(true, deepMembersById(swapped, swapped, true)); -// console.log(t, 'missing 1 ordered'); -// assert(false, deepMembersById(base, missing, true)); -// if (t !== "path") { -// console.log(t, 'swapped ordered'); -// assert(false, deepMembersById(base, swapped, true)); -// } -// console.log(t, 'happy uo'); -// assert(true, deepMembersById(base, base, false)); -// assert(true, deepMembersById(missing, missing, false)); -// assert(true, deepMembersById(swapped, swapped, false)); -// console.log(t, 'missing 1 uo'); -// assert(false, deepMembersById(base, missing, false)); -// if (t !== "path") { -// console.log(t, 'swapped uo'); -// assert(true, deepMembersById(base, swapped, false)); -// } -// console.log(t, 'other') -// assert(false, deepMembersById(missing, swapped, true)); -// assert(false, deepMembersById(missing, swapped, false)); -//} -// -////tests("map", m1, m2, m3) -//tests("array", a1, a2, a3) -////tests("pathlist", pl1, pl2, pl3) -//tests("path", p1, p2, p3) -// -//let t = "array of maps" -////console.log(t, 'missing'); -//// assert(false, deepMembersById(m4, m5, false)); -//// assert(false, deepMembersById(m4, m5, true)); -// -//t = "non objects" -//assert(true, deepMembersById(1, 1, true)) -//assert(true, deepMembersById("abc", "abc", true)) -//assert(false, deepMembersById(1, "a", true)) -//assert(false, deepMembersById(1, 2, true)) -//assert(false, deepMembersById(2, "lop", true)) -// -//const pr1 = new Property("a", "1"); -//const pr2 = new Property("a", "1"); -//const aa1 = [1,2,[1,2,3]]; -//const aa2 = [1,[2,1,3],2]; -//const aaa1 = [1, 2, 3]; -// const aaa2 = [1, 2, 3]; -// const aaa3 = [2, 1, 3]; -//console.log(deepMembersById(deepSort(aa1), deepSort(aa2))); -//console.log(deepMembersByIdOrdered(aaa1, aaa3)); -// -//const m1 = new Map([ -// ['ripple', [new Vertex(5, 'software', undefined)]], -// ['peter', [new Vertex(6, 'person', undefined)]], -// ['vadas', [new Vertex(2, 'person', undefined)]] -// ]); -// const m2 = new Map([ -// ['ripple', [new Vertex(5, 'software', undefined)]], -// ['peter', [new Vertex(6, 'person', undefined)]] -// ]); -// const m3 = new Map([ -// ['ripple', [new Vertex(5, 'software', undefined)]], -// ['vadas', [new Vertex(2, 'person', undefined)]], -// ['peter', [new Vertex(6, 'person', undefined)]] -// ]); -// const m4 = new Map([ -// ['ripple', [new Vertex(5, 'software', undefined)]], -// ['vadas', [new Vertex(6, 'person', undefined)]], -// ['peter', [new Vertex(2, 'person', undefined)]] -// ]); -// console.log(deepMembersById(m1, m1)) -// console.log(deepMembersById(m1, m2)) -// const path1 = new Path(["path"], [new Vertex(1, 'person', undefined), -// new Vertex(2, 'person', undefined)]); -// const path2 = new Path(["path"], [new Vertex(1, 'person', undefined)]); -// const path3 = new Path(["path"], [new Vertex(2, 'person', undefined), -// new Vertex(1, 'person', undefined)]); -// console.log(deepMembersById(path1, path3)) -// console.log([new Vertex(2, 'person', undefined), -// new Vertex(1, 'person', undefined)].sort()) From b637ea8e3f4c2abca3a1fb994184899aa54b7ba5 Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Fri, 5 Jan 2024 13:00:40 -0800 Subject: [PATCH 09/18] Update feature-steps.js --- .../gremlin-javascript/test/cucumber/feature-steps.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 38d0d084c75..3e6ed1e5254 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 @@ -243,10 +243,10 @@ Then(/^the result should be (\w+)$/, function assertResult(characterizedAs, resu const expectedResult = resultTable.rows().map(row => parseRow.call(this, row)); switch (characterizedAs) { case 'ordered': - expect(expectedResult).to.have.deep.ordered.members(toCompare(this.result)); + expect(toCompare(this.result)).to.have.deep.ordered.members(expectedResult); break; case 'unordered': - expect(expectedResult).to.have.deep.members(toCompare(this.result)); + expect(toCompare(this.result)).to.have.deep.members(toCompare(expectedResult)); break; case 'of': // result is a subset of the expected From 648d00f9bdc46b06680a0276c8daa171a30b88e2 Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Fri, 5 Jan 2024 13:40:23 -0800 Subject: [PATCH 10/18] Add credit --- .../gremlin-javascript/test/cucumber/element-comparison.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js index 81a65d611f7..d243a32f0c3 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js @@ -17,6 +17,13 @@ * under the License. */ +/* + * Portions of this code are based on the Chai Assertion Library + * at https://www.chaijs.com/, which is licensed under the MIT License. + * The functions deepMembersById, flag, and isSubsetOf are adapted from + * Chai's source code. + */ + const chai = require('chai'); const deepEqual = require('deep-eql'); From 6a412bd4af823884bb9c960ac89fdbc5ca55585a Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Fri, 5 Jan 2024 13:56:24 -0800 Subject: [PATCH 11/18] Update tests --- .../gremlin-javascript/test/unit/element-comparison-test.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js index 0db86135c6f..b069dfb5984 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js @@ -61,7 +61,6 @@ describe('elements', function () { it('should pass with same id, different values', function () { expect(deepEqual(v1, v2, opt)).to.be.true; - expect([v1]).to.have.deep.ordered.members([v2]); expect(deepEqual(v3, e1, opt)).to.be.true; expect(deepEqual(e2, e3, opt)).to.be.true; expect(deepEqual(e3, vp1, opt)).to.be.true; @@ -125,7 +124,7 @@ describe('arrays', function () { const a12 = [a10] it('unordered nested', function () { - expect(a6).to.have.deep.members(a7); + expect(a6).to.have.not.deep.members(a7); // nested arrays ordered differently don't pass as the same item expect(a6).to.not.have.deep.members(a8); expect(a9).to.have.deep.members(a10); expect(a11).to.have.deep.members(a12); @@ -173,7 +172,7 @@ describe('map', function () { it('ordered', function () { expect(m1).to.have.deep.ordered.members(m1); expect(m1).to.not.have.deep.ordered.members(m2); - expect(m1).to.not.have.deep.ordered.members(m3); + expect(m1).to.have.deep.ordered.members(m3); // map entries can be unordered expect(m1).to.not.have.deep.ordered.members(m4); }); }); From 9edc84c06e8ab22db194a00a858c674a475308aa Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Fri, 5 Jan 2024 15:01:24 -0800 Subject: [PATCH 12/18] Update element-comparison-test.js --- .../gremlin-javascript/test/unit/element-comparison-test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js index b069dfb5984..ae026665400 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js @@ -126,8 +126,8 @@ describe('arrays', function () { it('unordered nested', function () { expect(a6).to.have.not.deep.members(a7); // nested arrays ordered differently don't pass as the same item expect(a6).to.not.have.deep.members(a8); - expect(a9).to.have.deep.members(a10); - expect(a11).to.have.deep.members(a12); + expect(a9).to.not.have.deep.members(a10); + expect(a11).to.not.have.deep.members(a12); }); it('ordered nested', function () { From 9b002d592b4d6165348756114967ab18e47891f9 Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Mon, 8 Jan 2024 11:59:57 -0800 Subject: [PATCH 13/18] Add licensing --- LICENSE | 4 ++-- .../javascript/gremlin-javascript/LICENSE | 10 ++++++++- .../gremlin-javascript/licenses/chai | 21 +++++++++++++++++++ .../test/cucumber/element-comparison.js | 1 + 4 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 gremlin-javascript/src/main/javascript/gremlin-javascript/licenses/chai diff --git a/LICENSE b/LICENSE index 47708b1f563..6317047c226 100644 --- a/LICENSE +++ b/LICENSE @@ -210,5 +210,5 @@ The Apache TinkerPop project bundles the following components under the MIT Lice bootstrap 5.0.0 (http://getbootstrap.com/) - for details, see licenses/bootstrap jquery 1.11.0 (https://jquery.com/) - for details, see licenses/jquery normalize.css 2.1.2 (https://necolas.github.io/normalize.css/) - for details, see licenses/normalize - prism.css/js 1.27.0 (https://prismjs.com) - for details, see licenses/prism - wow.js 1.1.2 (https://wowjs.uk/) - for details, see licenses/wow \ No newline at end of file + prism.css/js 1.27.0 (https://prismjs.com/) - for details, see licenses/prism + wow.js 1.1.2 (https://wowjs.uk/) - for details, see licenses/wow diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/LICENSE b/gremlin-javascript/src/main/javascript/gremlin-javascript/LICENSE index 7a4a3ea2424..1a1a07d01d5 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/LICENSE +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/LICENSE @@ -199,4 +199,12 @@ 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. \ No newline at end of file + limitations under the License. + + ======================================================================== + MIT Licenses + ======================================================================== + + The Apache TinkerPop project bundles the following components under the MIT License: + + chai 5.0.0 (https://www.chaijs.com/) - for details, see licenses/chai diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/licenses/chai b/gremlin-javascript/src/main/javascript/gremlin-javascript/licenses/chai new file mode 100644 index 00000000000..eedbe238aca --- /dev/null +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/licenses/chai @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Chai.js Assertion Library + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js index d243a32f0c3..5cddd49978b 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js @@ -22,6 +22,7 @@ * at https://www.chaijs.com/, which is licensed under the MIT License. * The functions deepMembersById, flag, and isSubsetOf are adapted from * Chai's source code. + * See licenses/chai for full license. */ const chai = require('chai'); From 3f22107fa8bd17eed847910e92c887c16732b3cc Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Wed, 10 Jan 2024 10:38:52 -0800 Subject: [PATCH 14/18] Change element check to use instanceof --- .../gremlin-javascript/test/cucumber/element-comparison.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js index 5cddd49978b..a95e452d39a 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js @@ -27,9 +27,10 @@ const chai = require('chai'); const deepEqual = require('deep-eql'); +const { Edge, Vertex, VertexProperty } = require('../../lib/structure/graph'); function isElement(obj) { - return obj !== null && obj.hasOwnProperty('id') && obj.hasOwnProperty('label'); + return obj instanceof Edge || obj instanceof Vertex || obj instanceof VertexProperty; } const opt = {comparator: compareElements}; From 33bc945e2d86d8a58b7dbaf3337baac8551edf90 Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Wed, 10 Jan 2024 10:40:00 -0800 Subject: [PATCH 15/18] Update element comparison to check for class --- .../gremlin-javascript/test/cucumber/element-comparison.js | 2 +- .../test/unit/element-comparison-test.js | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js index a95e452d39a..7f60f473968 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/element-comparison.js @@ -112,7 +112,7 @@ function compareElements(a, b) { if (!isElement(a) || !isElement(b)) { return null; } else { - return a.id === b.id; + return a.constructor === b.constructor && a.id === b.id; } } diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js index ae026665400..0292518bfd5 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js @@ -61,9 +61,7 @@ describe('elements', function () { it('should pass with same id, different values', function () { expect(deepEqual(v1, v2, opt)).to.be.true; - expect(deepEqual(v3, e1, opt)).to.be.true; expect(deepEqual(e2, e3, opt)).to.be.true; - expect(deepEqual(e3, vp1, opt)).to.be.true; expect(deepEqual(vp1, vp2, opt)).to.be.true; }); @@ -72,6 +70,11 @@ describe('elements', function () { expect(deepEqual(e1, e2, opt)).to.be.false; expect(deepEqual(vp2, vp3, opt)).to.be.false; }); + + it('should fail with same id, different type', function () { + expect(deepEqual(v3, e1, opt)).to.be.false; + expect(deepEqual(e3, vp1, opt)).to.be.false; + }); }); describe('property', function () { From 7d20257547e910633437c12f3cb42d2246f8b8a4 Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Wed, 10 Jan 2024 10:40:15 -0800 Subject: [PATCH 16/18] Add element array tests --- .../test/unit/element-comparison-test.js | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js index 0292518bfd5..b765e7d55ca 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js @@ -75,6 +75,29 @@ describe('elements', function () { expect(deepEqual(v3, e1, opt)).to.be.false; expect(deepEqual(e3, vp1, opt)).to.be.false; }); + + describe('element arrays', function () { + const ea1 = [v1, e2, v3]; + const ea2 = [v1, e2, v3]; + const ea3 = [e2, v1, v3]; + const ea4 = [e2, e2, v3]; + const ea5 = [v1, e2, v3, vp1]; + + it('unordered', function () { + expect(ea1).to.have.deep.members(ea2); + expect(ea1).to.have.deep.members(ea3); + expect(ea1).to.not.have.deep.members(ea4); + expect(ea1).to.not.have.deep.members(ea5); + expect(ea5).to.not.have.deep.members(ea1); + }); + + it('ordered', function () { + expect(ea1).to.have.deep.ordered.members(ea2); + expect(ea1).to.not.have.deep.ordered.members(ea3); + expect(ea1).to.not.have.deep.ordered.members(ea4); + expect(ea1).to.not.have.deep.ordered.members(ea5); + expect(ea5).to.not.have.deep.ordered.members(ea1); + }); }); describe('property', function () { From afaeb46b0b69de65f34cc35a590b04dfc120e7ff Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Wed, 10 Jan 2024 10:40:28 -0800 Subject: [PATCH 17/18] Add include deep member tests --- .../test/unit/element-comparison-test.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js index b765e7d55ca..fc459e57efc 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/element-comparison-test.js @@ -98,6 +98,19 @@ describe('elements', function () { expect(ea1).to.not.have.deep.ordered.members(ea5); expect(ea5).to.not.have.deep.ordered.members(ea1); }); + + it('include', function () { + expect(ea1).to.include.deep.members(ea2); + expect(ea1).to.include.deep.members(ea3); + expect(ea1).to.include.deep.members(ea4); + expect(ea1).to.not.include.deep.members(ea5); + + expect(ea2).to.include.deep.members(ea1); + expect(ea3).to.include.deep.members(ea1); + expect(ea4).to.not.include.deep.members(ea1); + expect(ea5).to.include.deep.members(ea1); + }); + }); }); describe('property', function () { @@ -112,7 +125,6 @@ describe('property', function () { expect(deepEqual(p1, p3, opt)).to.be.false; expect(deepEqual(p1, p4, opt)).to.be.false; expect(deepEqual(p3, p5, opt)).to.be.false; - expect(deepEqual(p3, p5, opt)).to.be.false; }); }); From 2f35346de6c804ddff0fe35b12806effeca1108d Mon Sep 17 00:00:00 2001 From: Ryan Tan Date: Thu, 11 Jan 2024 10:53:44 -0800 Subject: [PATCH 18/18] Update CHANGELOG.asciidoc --- CHANGELOG.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 4bd90644a84..2a170b639dd 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -55,6 +55,7 @@ This release also includes changes from <> and <