This is an automated email from the ASF dual-hosted git repository. spmallette pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/master by this push: new 42d9adb Added error message assertion in gherkin 42d9adb is described below commit 42d9adb5cc29bf994bdba32038d5e06eeb29080f Author: Stephen Mallette <stepm...@amazon.com> AuthorDate: Tue Mar 29 09:15:19 2022 -0400 Added error message assertion in gherkin Having the option to assert the error message made it possible to add more tests to the Gherkin suite and remove more from the standard JVM suite. Added ReadOnlyStrategy tests to Gherkin and added tags for strategies. CTR --- docs/src/dev/developer/for-committers.asciidoc | 6 ++ gremlin-dotnet/build/generate.groovy | 1 + .../Gherkin/CommonSteps.cs | 24 +++++ .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 8 ++ gremlin-javascript/build/generate.groovy | 2 +- .../gremlin-javascript/package-lock.json | 17 ++++ .../javascript/gremlin-javascript/package.json | 1 + .../test/cucumber/feature-steps.js | 17 +++- .../gremlin-javascript/test/cucumber/gremlin.js | 9 +- .../src/main/python/radish/feature_steps.py | 73 +++++++++------ gremlin-python/src/main/python/radish/gremlin.py | 7 ++ gremlin-test/features/filter/Coin.feature | 2 +- gremlin-test/features/filter/Dedup.feature | 1 + gremlin-test/features/filter/Sample.feature | 3 + gremlin-test/features/filter/Where.feature | 2 +- .../features/integrated/ReadOnlyStrategy.feature | 100 +++++++++++++++++++++ gremlin-test/features/map/Max.feature | 4 + gremlin-test/features/map/Mean.feature | 4 + gremlin-test/features/map/Min.feature | 4 + gremlin-test/features/map/Order.feature | 1 + gremlin-test/features/map/Path.feature | 2 +- gremlin-test/features/map/Project.feature | 1 + gremlin-test/features/map/Select.feature | 2 +- gremlin-test/features/map/Sum.feature | 4 + gremlin-test/features/sideEffect/Aggregate.feature | 4 +- gremlin-test/features/sideEffect/Fail.feature | 2 +- gremlin-test/features/sideEffect/Group.feature | 1 + .../features/sideEffect/GroupCount.feature | 1 + .../tinkerpop/gremlin/features/StepDefinition.java | 26 ++++++ .../process/ProcessLimitedComputerSuite.java | 2 - .../process/ProcessLimitedStandardSuite.java | 6 -- 31 files changed, 292 insertions(+), 45 deletions(-) diff --git a/docs/src/dev/developer/for-committers.asciidoc b/docs/src/dev/developer/for-committers.asciidoc index fdca4e8..d442e30 100644 --- a/docs/src/dev/developer/for-committers.asciidoc +++ b/docs/src/dev/developer/for-committers.asciidoc @@ -452,6 +452,8 @@ The "Then" options handle the assertion of the result. There are several options in it * "the result should be empty" - no results * "the traversal will raise an error" - an exception is thrown as a result of traversal iteration +* "the traversal will raise an error with message _comparison_ text of _message_" - an exception is thrown as a result +of traversal iteration where "_comparison_" may be one of "containing", "starting", or "ending". * "the result should be ordered" - the exact results and should appear in the order presented * "the result should be unordered" - the exact results but can appear any order * "the result should be of" - results can be any of the specified values and in any order (use when guarantees @@ -535,6 +537,10 @@ best example of this sort of test would be one that uses the remote `Lambda` syn * `@UserSuppliedVertexIds` - The scenario relies on the vertex IDs specified in the dataset used by the scenario. * `@UserSuppliedEdgeIds` - The scenario relies on the edge IDs specified in the dataset used by the scenario. * `@UserSuppliedVertexPropertyIds` - The scenario relies on the vertex property IDs specified in the dataset used by the scenario. +* `@With*` - The scenario uses some `with()` based configuration like strategies: +** `@WithProductiveByStrategy` +** `@WithReadOnlyStrategy` +** `@WithSeedStrategy` Tag filters can be applied to Intellij at execution time by adding a system properties of `-Dcucumber.filter.tags=<step-filter>`. diff --git a/gremlin-dotnet/build/generate.groovy b/gremlin-dotnet/build/generate.groovy index 7b3a620..6360c3a 100644 --- a/gremlin-dotnet/build/generate.groovy +++ b/gremlin-dotnet/build/generate.groovy @@ -96,6 +96,7 @@ radishGremlinFile.withWriter('UTF-8') { Writer writer -> 'using Gremlin.Net.Structure;\n' + 'using Gremlin.Net.Process.Traversal;\n' + 'using Gremlin.Net.Process.Traversal.Strategy.Optimization;\n' + + 'using Gremlin.Net.Process.Traversal.Strategy.Verification;\n' + 'using Gremlin.Net.Process.Traversal.Strategy.Decoration;\n') writer.writeLine('namespace Gremlin.Net.IntegrationTest.Gherkin\n' + '{\n' + diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs index 4a73b11..fc6bceb 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs @@ -219,6 +219,30 @@ namespace Gremlin.Net.IntegrationTest.Gherkin _error = null; } + [Then("the traversal will raise an error with message (\\w+) text of \"(.*)\"")] + public void TraversalWillRaiseErrorWithMessage(string comparison, string expectedMessage) + { + Assert.NotNull(_error); + + switch (comparison) { + case "containing": + Assert.Equal(true, _error.Message.Contains(expectedMessage)); + break; + case "starting": + Assert.Equal(true, _error.Message.StartsWith(expectedMessage)); + break; + case "ending": + Assert.Equal(true, _error.Message.EndsWith(expectedMessage)); + break; + default: + throw new NotSupportedException( + "Unknown comparison of " + comparison + " - must be one of: containing, starting or ending"); + } + + // consume the error now that it has been asserted + _error = null; + } + [Then("the result should be (\\w+)")] public void AssertResult(string characterizedAs, DataTable table = null) { diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index cbb0a26..acbe204 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -33,6 +33,7 @@ using System.Collections.Generic; using Gremlin.Net.Structure; using Gremlin.Net.Process.Traversal; using Gremlin.Net.Process.Traversal.Strategy.Optimization; +using Gremlin.Net.Process.Traversal.Strategy.Verification; using Gremlin.Net.Process.Traversal.Strategy.Decoration; namespace Gremlin.Net.IntegrationTest.Gherkin @@ -343,6 +344,13 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_V_coworker_with_midV", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("person").Filter(__.OutE("created")).As("p1").V().HasLabel("person").Where(P.Neq("p1")).Filter(__.OutE("created")).As("p2").Map<object>(__.Out("created").Where(__.In("created").As("p1")).Values<object>("name").Fold()).Group<object,object>().By(__.Select<object>("p1").By("name")).By(__.Group<object,object>().By(__.Select<object>("p2").By("name")). [...] {"g_V_playlist_paths", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("name","Bob_Dylan").In("sungBy").Order().By("name").As("a").Repeat(__.Out().Order().By("name").SimplePath().From("a")).Until(__.Out("writtenBy").Has("name","Johnny_Cash")).Limit<object>(1).As("b").Repeat(__.Out().Order().By("name").As("c").SimplePath().From("b").To("c")).Until(__.Out("sungBy").Has("name","Grateful_Dead")).Limit<object>(1).Path().From("a") [...] {"g_V_shortestpath", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().As("v").Both().As("v").Project<object>("src","tgt","p").By(__.Select<object>(Pop.First,"v")).By(__.Select<object>(Pop.Last,"v")).By(__.Select<object>(Pop.All,"v")).As("triple").Group("x").By(__.Select<object>("src","tgt")).By(__.Select<object>("p").Fold()).Select<object>("tgt").Barrier().Repeat(__.Both().As("v").Project<object>("src","tgt","p").By(__.Select<obj [...] + {"g_withStrategiesXReadOnlyStrategyX_V", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ReadOnlyStrategy()).V()}}, + {"g_withStrategiesXReadOnlyStrategyX_V_outXknowsX_name", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ReadOnlyStrategy()).V().Out("knows").Values<object>("name")}}, + {"g_withStrategiesXReadOnlyStrategyX_addVXpersonX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ReadOnlyStrategy()).AddV("person")}}, + {"g_withStrategiesXReadOnlyStrategyX_addVXpersonX_fromXVX1XX_toXVX2XX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ReadOnlyStrategy()).AddE("link").From(__.V(1)).To(__.V(2))}}, + {"g_withStrategiesXReadOnlyStrategyX_V_addVXpersonX_fromXVX1XX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ReadOnlyStrategy()).V().AddE("link").From(__.V(1))}}, + {"g_withStrategiesXReadOnlyStrategyX_V_propertyXname_joshX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ReadOnlyStrategy()).V().Property("name","josh")}}, + {"g_withStrategiesXReadOnlyStrategyX_E_propertyXweight_0X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithStrategies(new ReadOnlyStrategy()).E().Property("weight",0)}}, {"g_V_classic_recommendation", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Has("name","DARK STAR").As("a").Out("followedBy").Aggregate("stash").In("followedBy").Where(P.Neq("a").And(P.Without(new List<object> {"stash"}))).GroupCount<object>().Unfold<object>().Project<object>("x","y","z").By(__.Select<object>(Column.Keys).Values<object>("name")).By(__.Select<object>(Column.Keys).Values<object>("performances")).By(__.Select<ob [...] {"g_VX1X_asXaX_outXcreatedX_addEXcreatedByX_toXaX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko").AddV("person").Property("name","vadas").Property("age",27).As("vadas").AddV("software").Property("name","lop").Property("lang","java").As("lop").AddV("person").Property("name","josh").Property("age",32).As("josh").AddV("software").Property("name","ripple").Property [...] {"g_VX1X_asXaX_outXcreatedX_addEXcreatedByX_toXaX_propertyXweight_2X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.AddV("person").Property("name","marko").Property("age",29).As("marko").AddV("person").Property("name","vadas").Property("age",27).As("vadas").AddV("software").Property("name","lop").Property("lang","java").As("lop").AddV("person").Property("name","josh").Property("age",32).As("josh").AddV("software").Property("name" [...] diff --git a/gremlin-javascript/build/generate.groovy b/gremlin-javascript/build/generate.groovy index 540e7c3..6eaaf94 100644 --- a/gremlin-javascript/build/generate.groovy +++ b/gremlin-javascript/build/generate.groovy @@ -91,7 +91,7 @@ radishGremlinFile.withWriter('UTF-8') { Writer writer -> writer.writeLine( 'const graphTraversalModule = require(\'../../lib/process/graph-traversal\');\n' + 'const traversalModule = require(\'../../lib/process/traversal\');\n' + - 'const { TraversalStrategies, VertexProgramStrategy, OptionsStrategy, SeedStrategy, ProductiveByStrategy } = require(\'../../lib/process/traversal-strategy\');\n' + + 'const { TraversalStrategies, VertexProgramStrategy, OptionsStrategy, ReadOnlyStrategy, SeedStrategy, ProductiveByStrategy } = require(\'../../lib/process/traversal-strategy\');\n' + 'const __ = graphTraversalModule.statics;\n' + 'const Barrier = traversalModule.barrier\n' + 'const Cardinality = traversalModule.cardinality\n' + diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/package-lock.json b/gremlin-javascript/src/main/javascript/gremlin-javascript/package-lock.json index c2366e0..94788a9 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/package-lock.json +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/package-lock.json @@ -13,6 +13,7 @@ }, "devDependencies": { "chai": "~4.1.2", + "chai-string": "~1.5.0", "colors": "1.4.0", "cucumber": "~4.2.1", "eslint": "^7.32.0", @@ -579,6 +580,15 @@ "node": ">=4" } }, + "node_modules/chai-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/chai-string/-/chai-string-1.5.0.tgz", + "integrity": "sha512-sydDC3S3pNAQMYwJrs6dQX0oBQ6KfIPuOZ78n7rocW0eJJlsHPh2t3kwW7xfwYA/1Bf6/arGtSUo16rxR2JFlw==", + "dev": true, + "peerDependencies": { + "chai": "^4.1.2" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -4740,6 +4750,13 @@ "type-detect": "^4.0.0" } }, + "chai-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/chai-string/-/chai-string-1.5.0.tgz", + "integrity": "sha512-sydDC3S3pNAQMYwJrs6dQX0oBQ6KfIPuOZ78n7rocW0eJJlsHPh2t3kwW7xfwYA/1Bf6/arGtSUo16rxR2JFlw==", + "dev": true, + "requires": {} + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/package.json b/gremlin-javascript/src/main/javascript/gremlin-javascript/package.json index 72ba43c..11fca74 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/package.json +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/package.json @@ -18,6 +18,7 @@ }, "devDependencies": { "chai": "~4.1.2", + "chai-string": "~1.5.0", "colors": "1.4.0", "cucumber": "~4.2.1", "eslint": "^7.32.0", 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 6b85eb3..c5faddc 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js @@ -23,7 +23,9 @@ 'use strict'; const {Given, Then, When} = require('cucumber'); -const expect = require('chai').expect; +const chai = require('chai') +chai.use(require('chai-string')); +const expect = chai.expect; const util = require('util'); const gremlin = require('./gremlin').gremlin; const graphModule = require('../../lib/structure/graph'); @@ -147,6 +149,19 @@ Then('the traversal will raise an error', function() { expect(this.result).to.be.a.instanceof(Error); }); +Then(/^the traversal will raise an error with message (\w+) text of "(.+)"$/, function(comparison, expectedMessage) { + expect(this.result).to.be.a.instanceof(Error); + if (comparison === "containing") { + expect(this.result.message).to.contain(expectedMessage) + } else if (comparison === "starting") { + expect(this.result.message).to.startWith(expectedMessage) + } else if (comparison === "ending") { + expect(this.result.message).to.endWith(expectedMessage) + } else { + throw new Error('unknown comparison \'' + comparison + '\'- must be: containing, ending or starting'); + } +}); + Then(/^the result should be (\w+)$/, function assertResult(characterizedAs, resultTable) { expect(this.result).to.not.be.a.instanceof(Error); diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js index 31387de..b66244e 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js @@ -26,7 +26,7 @@ const graphTraversalModule = require('../../lib/process/graph-traversal'); const traversalModule = require('../../lib/process/traversal'); -const { TraversalStrategies, VertexProgramStrategy, OptionsStrategy, SeedStrategy, ProductiveByStrategy } = require('../../lib/process/traversal-strategy'); +const { TraversalStrategies, VertexProgramStrategy, OptionsStrategy, ReadOnlyStrategy, SeedStrategy, ProductiveByStrategy } = require('../../lib/process/traversal-strategy'); const __ = graphTraversalModule.statics; const Barrier = traversalModule.barrier const Cardinality = traversalModule.cardinality @@ -334,6 +334,13 @@ const gremlins = { g_V_coworker_with_midV: [function({g}) { return g.V().hasLabel("person").filter(__.outE("created")).as("p1").V().hasLabel("person").where(P.neq("p1")).filter(__.outE("created")).as("p2").map(__.out("created").where(__.in_("created").as("p1")).values("name").fold()).group().by(__.select("p1").by("name")).by(__.group().by(__.select("p2").by("name")).by(__.project("numCoCreated","coCreated").by(__.count(Scope.local)).by())).unfold() }], g_V_playlist_paths: [function({g}) { return g.V().has("name","Bob_Dylan").in_("sungBy").order().by("name").as("a").repeat(__.out().order().by("name").simplePath().from_("a")).until(__.out("writtenBy").has("name","Johnny_Cash")).limit(1).as("b").repeat(__.out().order().by("name").as("c").simplePath().from_("b").to("c")).until(__.out("sungBy").has("name","Grateful_Dead")).limit(1).path().from_("a").unfold().project("song","artists").by("name").by(__.coalesce(__.out("sungBy","writtenBy" [...] g_V_shortestpath: [function({g}) { return g.V().as("v").both().as("v").project("src","tgt","p").by(__.select(Pop.first,"v")).by(__.select(Pop.last,"v")).by(__.select(Pop.all,"v")).as("triple").group("x").by(__.select("src","tgt")).by(__.select("p").fold()).select("tgt").barrier().repeat(__.both().as("v").project("src","tgt","p").by(__.select(Pop.first,"v")).by(__.select(Pop.last,"v")).by(__.select(Pop.all,"v")).as("t").filter(__.select(Pop.all,"p").count(Scope.local).as("l").select(P [...] + g_withStrategiesXReadOnlyStrategyX_V: [function({g}) { return g.withStrategies(new ReadOnlyStrategy()).V() }], + g_withStrategiesXReadOnlyStrategyX_V_outXknowsX_name: [function({g}) { return g.withStrategies(new ReadOnlyStrategy()).V().out("knows").values("name") }], + g_withStrategiesXReadOnlyStrategyX_addVXpersonX: [function({g}) { return g.withStrategies(new ReadOnlyStrategy()).addV("person") }], + g_withStrategiesXReadOnlyStrategyX_addVXpersonX_fromXVX1XX_toXVX2XX: [function({g}) { return g.withStrategies(new ReadOnlyStrategy()).addE("link").from_(__.V(1)).to(__.V(2)) }], + g_withStrategiesXReadOnlyStrategyX_V_addVXpersonX_fromXVX1XX: [function({g}) { return g.withStrategies(new ReadOnlyStrategy()).V().addE("link").from_(__.V(1)) }], + g_withStrategiesXReadOnlyStrategyX_V_propertyXname_joshX: [function({g}) { return g.withStrategies(new ReadOnlyStrategy()).V().property("name","josh") }], + g_withStrategiesXReadOnlyStrategyX_E_propertyXweight_0X: [function({g}) { return g.withStrategies(new ReadOnlyStrategy()).E().property("weight",0) }], g_V_classic_recommendation: [function({g}) { return g.V().has("name","DARK STAR").as("a").out("followedBy").aggregate("stash").in_("followedBy").where(P.neq("a").and(P.without(["stash"]))).groupCount().unfold().project("x","y","z").by(__.select(Column.keys).values("name")).by(__.select(Column.keys).values("performances")).by(__.select(Column.values)).order().by(__.select("z"),Order.desc).by(__.select("y"),Order.asc).limit(5).aggregate(Scope.local,"m").select("x") }], g_VX1X_asXaX_outXcreatedX_addEXcreatedByX_toXaX: [function({g, vid1}) { return g.addV("person").property("name","marko").property("age",29).as("marko").addV("person").property("name","vadas").property("age",27).as("vadas").addV("software").property("name","lop").property("lang","java").as("lop").addV("person").property("name","josh").property("age",32).as("josh").addV("software").property("name","ripple").property("lang","java").as("ripple").addV("person").property("name","peter").pr [...] g_VX1X_asXaX_outXcreatedX_addEXcreatedByX_toXaX_propertyXweight_2X: [function({g, vid1}) { return g.addV("person").property("name","marko").property("age",29).as("marko").addV("person").property("name","vadas").property("age",27).as("vadas").addV("software").property("name","lop").property("lang","java").as("lop").addV("person").property("name","josh").property("age",32).as("josh").addV("software").property("name","ripple").property("lang","java").as("ripple").addV("person").property [...] diff --git a/gremlin-python/src/main/python/radish/feature_steps.py b/gremlin-python/src/main/python/radish/feature_steps.py index 4a4bc70..4227f56 100644 --- a/gremlin-python/src/main/python/radish/feature_steps.py +++ b/gremlin-python/src/main/python/radish/feature_steps.py @@ -23,7 +23,8 @@ from gremlin_python.statics import long from gremlin_python.structure.graph import Path, Vertex from gremlin_python.process.anonymous_traversal import traversal from gremlin_python.process.graph_traversal import __ -from gremlin_python.process.traversal import Barrier, Cardinality, P, TextP, Pop, Scope, Column, Order, Direction, T, Pick, Operator, IO, WithOptions +from gremlin_python.process.traversal import Barrier, Cardinality, P, TextP, Pop, Scope, Column, Order, Direction, T, \ + Pick, Operator, IO, WithOptions from radish import given, when, then, world from hamcrest import * @@ -87,7 +88,7 @@ def translate_traversal(step): step.context.ignore = step.text in ignores p = step.context.traversal_params if hasattr(step.context, "traversal_params") else {} localg = step.context.g - tagset = [ tag.name for tag in step.all_tags ] + tagset = [tag.name for tag in step.all_tags] if not step.context.ignore: step.context.ignore = "AllowNullPropertyValues" in tagset @@ -106,8 +107,9 @@ def iterate_the_traversal(step): try: step.context.result = list(map(lambda x: _convert_results(x), step.context.traversal.toList())) step.context.failed = False - except: + except Exception as e: step.context.failed = True + step.context.failed_message = getattr(e, 'message', repr(e)) @when("iterated next") @@ -118,8 +120,9 @@ def next_the_traversal(step): try: step.context.result = list(map(lambda x: _convert_results(x), step.context.traversal.next())) step.context.failed = False - except: + except Exception as e: step.context.failed = True + step.context.failed_message = getattr(e, 'message', repr(e)) @then("the traversal will raise an error") @@ -127,6 +130,20 @@ def raise_an_error(step): assert_that(step.context.failed, equal_to(True)) +@then("the traversal will raise an error with message {comparison:w} text of {expected_message:QuotedString}") +def raise_an_error_with_message(step, comparison, expected_message): + assert_that(step.context.failed, equal_to(True)) + + if comparison == "containing": + assert_that(step.context.failed_message, contains_string(expected_message)) + elif comparison == "ending": + assert_that(step.context.failed_message, ends_with(expected_message)) + elif comparison == "starting": + assert_that(step.context.failed_message, starts_with(expected_message)) + else: + raise ValueError("unknown comparison '" + comparison + "'- must be: containing, ending or starting") + + @then("the result should be {characterized_as:w}") def assert_result(step, characterized_as): if step.context.ignore: @@ -134,13 +151,13 @@ def assert_result(step, characterized_as): assert_that(step.context.failed, equal_to(False)) - if characterized_as == "empty": # no results + if characterized_as == "empty": # no results assert_that(len(step.context.result), equal_to(0)) - elif characterized_as == "ordered": # results asserted in the order of the data table + elif characterized_as == "ordered": # results asserted in the order of the data table _table_assertion(step.table, step.context.result, step.context, True) elif characterized_as == "unordered": # results asserted in any order _table_assertion(step.table, step.context.result, step.context, False) - elif characterized_as == "of": # results may be of any of the specified items in the data table + elif characterized_as == "of": # results may be of any of the specified items in the data table _any_assertion(step.table, step.context.result, step.context) else: raise ValueError("unknown data characterization of " + characterized_as) @@ -177,55 +194,55 @@ def nothing_happening(step): def _convert(val, ctx): graph_name = ctx.graph_name - if isinstance(val, dict): # convert dictionary keys/values + if isinstance(val, dict): # convert dictionary keys/values n = {} for key, value in val.items(): k = _convert(key, ctx) # convert to tuple key if list/set as neither are hashable n[tuple(k) if isinstance(k, (set, list)) else k] = _convert(value, ctx) return n - elif isinstance(val, str) and re.match(r"^l\[.*\]$", val): # parse list + elif isinstance(val, str) and re.match(r"^l\[.*\]$", val): # parse list return [] if val == "l[]" else list(map((lambda x: _convert(x, ctx)), val[2:-1].split(","))) - elif isinstance(val, str) and re.match(r"^s\[.*\]$", val): # parse set + elif isinstance(val, str) and re.match(r"^s\[.*\]$", val): # parse set return set() if val == "s[]" else set(map((lambda x: _convert(x, ctx)), val[2:-1].split(","))) - elif isinstance(val, str) and re.match(r"^d\[NaN\]$", val): # parse nan + elif isinstance(val, str) and re.match(r"^d\[NaN\]$", val): # parse nan return float("nan") - elif isinstance(val, str) and re.match(r"^d\[Infinity\]$", val): # parse +inf + elif isinstance(val, str) and re.match(r"^d\[Infinity\]$", val): # parse +inf return float("inf") - elif isinstance(val, str) and re.match(r"^d\[-Infinity\]$", val): # parse -inf + elif isinstance(val, str) and re.match(r"^d\[-Infinity\]$", val): # parse -inf return float("-inf") elif isinstance(val, str) and re.match(r"^d\[.*\]\.[bsilfdmn]$", val): # parse numeric return float(val[2:-3]) if val[2:-3].__contains__(".") else long(val[2:-3]) - elif isinstance(val, str) and re.match(r"^v\[.*\]\.id$", val): # parse vertex id + elif isinstance(val, str) and re.match(r"^v\[.*\]\.id$", val): # parse vertex id return __find_cached_element(ctx, graph_name, val[2:-4], "v").id - elif isinstance(val, str) and re.match(r"^v\[.*\]\.sid$", val): # parse vertex id as string + elif isinstance(val, str) and re.match(r"^v\[.*\]\.sid$", val): # parse vertex id as string return str(__find_cached_element(ctx, graph_name, val[2:-5], "v").id) - elif isinstance(val, str) and re.match(r"^v\[.*\]$", val): # parse vertex + elif isinstance(val, str) and re.match(r"^v\[.*\]$", val): # parse vertex return __find_cached_element(ctx, graph_name, val[2:-1], "v") - elif isinstance(val, str) and re.match(r"^e\[.*\]\.id$", val): # parse edge id + elif isinstance(val, str) and re.match(r"^e\[.*\]\.id$", val): # parse edge id return __find_cached_element(ctx, graph_name, val[2:-4], "e").id - elif isinstance(val, str) and re.match(r"^e\[.*\]\.sid$", val): # parse edge id as string + elif isinstance(val, str) and re.match(r"^e\[.*\]\.sid$", val): # parse edge id as string return str(__find_cached_element(ctx, graph_name, val[2:-5], "e").id) - elif isinstance(val, str) and re.match(r"^e\[.*\]$", val): # parse edge + elif isinstance(val, str) and re.match(r"^e\[.*\]$", val): # parse edge return __find_cached_element(ctx, graph_name, val[2:-1], "e") - elif isinstance(val, str) and re.match(r"^vp\[.*\]$", val): # parse vertexproperty + elif isinstance(val, str) and re.match(r"^vp\[.*\]$", val): # parse vertexproperty return __find_cached_element(ctx, graph_name, val[3:-1], "vp") - elif isinstance(val, str) and re.match(r"^m\[.*\]$", val): # parse json as a map + elif isinstance(val, str) and re.match(r"^m\[.*\]$", val): # parse json as a map return _convert(json.loads(val[2:-1]), ctx) - elif isinstance(val, str) and re.match(r"^p\[.*\]$", val): # parse path + elif isinstance(val, str) and re.match(r"^p\[.*\]$", val): # parse path path_objects = list(map((lambda x: _convert(x, ctx)), val[2:-1].split(","))) return Path([set([])], path_objects) - elif isinstance(val, str) and re.match(r"^c\[.*\]$", val): # parse lambda/closure + elif isinstance(val, str) and re.match(r"^c\[.*\]$", val): # parse lambda/closure return lambda: (val[2:-1], "gremlin-groovy") - elif isinstance(val, str) and re.match(r"^t\[.*\]$", val): # parse instance of T enum + elif isinstance(val, str) and re.match(r"^t\[.*\]$", val): # parse instance of T enum return T[val[2:-1]] - elif isinstance(val, str) and re.match(r"^D\[.*\]$", val): # parse instance of Direction enum + elif isinstance(val, str) and re.match(r"^D\[.*\]$", val): # parse instance of Direction enum return direction[__alias_direction(val[2:-1])] - elif isinstance(val, str) and re.match(r"^null$", val): # parse null to None + elif isinstance(val, str) and re.match(r"^null$", val): # parse null to None return None - elif isinstance(val, str) and re.match(r"^true$", val): # parse to boolean + elif isinstance(val, str) and re.match(r"^true$", val): # parse to boolean return True - elif isinstance(val, str) and re.match(r"^false$", val): # parse to boolean + elif isinstance(val, str) and re.match(r"^false$", val): # parse to boolean return False else: return val diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py index a91dfad..7c779aa 100644 --- a/gremlin-python/src/main/python/radish/gremlin.py +++ b/gremlin-python/src/main/python/radish/gremlin.py @@ -316,6 +316,13 @@ world.gremlins = { 'g_V_coworker_with_midV': [(lambda g:g.V().hasLabel('person').filter_(__.outE('created')).as_('p1').V().hasLabel('person').where(P.neq('p1')).filter_(__.outE('created')).as_('p2').map(__.out('created').where(__.in_('created').as_('p1')).name.fold()).group().by(__.select('p1').by('name')).by(__.group().by(__.select('p2').by('name')).by(__.project('numCoCreated','coCreated').by(__.count(Scope.local)).by())).unfold())], 'g_V_playlist_paths': [(lambda g:g.V().has('name','Bob_Dylan').in_('sungBy').order().by('name').as_('a').repeat(__.out().order().by('name').simplePath().from_('a')).until(__.out('writtenBy').has('name','Johnny_Cash'))[0:1].as_('b').repeat(__.out().order().by('name').as_('c').simplePath().from_('b').to('c')).until(__.out('sungBy').has('name','Grateful_Dead'))[0:1].path().from_('a').unfold().project('song','artists').by('name').by(__.coalesce(__.out('sungBy','writtenBy').dedup().name.o [...] 'g_V_shortestpath': [(lambda g:g.V().as_('v').both().as_('v').project('src','tgt','p').by(__.select(Pop.first,'v')).by(__.select(Pop.last,'v')).by(__.select(Pop.all_,'v')).as_('triple').group('x').by(__.select('src','tgt')).by(__.select('p').fold()).select('tgt').barrier().repeat(__.both().as_('v').project('src','tgt','p').by(__.select(Pop.first,'v')).by(__.select(Pop.last,'v')).by(__.select(Pop.all_,'v')).as_('t').filter_(__.select(Pop.all_,'p').count(Scope.local).as_('l').select(Po [...] + 'g_withStrategiesXReadOnlyStrategyX_V': [(lambda g:g.withStrategies(*[TraversalStrategy('ReadOnlyStrategy', None, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy')]).V())], + 'g_withStrategiesXReadOnlyStrategyX_V_outXknowsX_name': [(lambda g:g.withStrategies(*[TraversalStrategy('ReadOnlyStrategy', None, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy')]).V().out('knows').name)], + 'g_withStrategiesXReadOnlyStrategyX_addVXpersonX': [(lambda g:g.withStrategies(*[TraversalStrategy('ReadOnlyStrategy', None, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy')]).addV('person'))], + 'g_withStrategiesXReadOnlyStrategyX_addVXpersonX_fromXVX1XX_toXVX2XX': [(lambda g:g.withStrategies(*[TraversalStrategy('ReadOnlyStrategy', None, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy')]).addE('link').from_(__.V(1)).to(__.V(2)))], + 'g_withStrategiesXReadOnlyStrategyX_V_addVXpersonX_fromXVX1XX': [(lambda g:g.withStrategies(*[TraversalStrategy('ReadOnlyStrategy', None, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy')]).V().addE('link').from_(__.V(1)))], + 'g_withStrategiesXReadOnlyStrategyX_V_propertyXname_joshX': [(lambda g:g.withStrategies(*[TraversalStrategy('ReadOnlyStrategy', None, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy')]).V().property('name','josh'))], + 'g_withStrategiesXReadOnlyStrategyX_E_propertyXweight_0X': [(lambda g:g.withStrategies(*[TraversalStrategy('ReadOnlyStrategy', None, 'org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy')]).E().property('weight',0))], 'g_V_classic_recommendation': [(lambda g:g.V().has('name','DARK STAR').as_('a').out('followedBy').aggregate('stash').in_('followedBy').where(P.neq('a').and_(P.without(['stash']))).groupCount().unfold().project('x','y','z').by(__.select(Column.keys).name).by(__.select(Column.keys).performances).by(__.select(Column.values)).order().by(__.select('z'),Order.desc).by(__.select('y'),Order.asc)[0:5].aggregate(Scope.local,'m').select('x'))], 'g_VX1X_asXaX_outXcreatedX_addEXcreatedByX_toXaX': [(lambda g, vid1=None:g.addV('person').property('name','marko').property('age',29).as_('marko').addV('person').property('name','vadas').property('age',27).as_('vadas').addV('software').property('name','lop').property('lang','java').as_('lop').addV('person').property('name','josh').property('age',32).as_('josh').addV('software').property('name','ripple').property('lang','java').as_('ripple').addV('person').property('name','peter').pro [...] 'g_VX1X_asXaX_outXcreatedX_addEXcreatedByX_toXaX_propertyXweight_2X': [(lambda g, vid1=None:g.addV('person').property('name','marko').property('age',29).as_('marko').addV('person').property('name','vadas').property('age',27).as_('vadas').addV('software').property('name','lop').property('lang','java').as_('lop').addV('person').property('name','josh').property('age',32).as_('josh').addV('software').property('name','ripple').property('lang','java').as_('ripple').addV('person').property( [...] diff --git a/gremlin-test/features/filter/Coin.feature b/gremlin-test/features/filter/Coin.feature index 8def707..802fa9d 100644 --- a/gremlin-test/features/filter/Coin.feature +++ b/gremlin-test/features/filter/Coin.feature @@ -43,7 +43,7 @@ Feature: Step - coin() When iterated to list Then the result should be empty - @GraphComputerVerificationStrategyNotSupported + @GraphComputerVerificationStrategyNotSupported @WithSeedStrategy Scenario: g_withStrategiesXSeedStrategyX_V_coinX50X Given the modern graph And the traversal of diff --git a/gremlin-test/features/filter/Dedup.feature b/gremlin-test/features/filter/Dedup.feature index 8136730..e93d1d7 100644 --- a/gremlin-test/features/filter/Dedup.feature +++ b/gremlin-test/features/filter/Dedup.feature @@ -313,6 +313,7 @@ Feature: Step - dedup() | marko | | josh | + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_order_byXname_descX_barrier_dedup_age_name Given the modern graph And the traversal of diff --git a/gremlin-test/features/filter/Sample.feature b/gremlin-test/features/filter/Sample.feature index 3b807e8..8a8bee7 100644 --- a/gremlin-test/features/filter/Sample.feature +++ b/gremlin-test/features/filter/Sample.feature @@ -45,6 +45,7 @@ Feature: Step - sample() When iterated to list Then the result should have a count of 3 + @WithSeedStrategy Scenario: g_withStrategiesXSeedStrategyX_V_group_byXlabelX_byXbothE_weight_order_sampleX2X_foldXunfold Given the modern graph And the traversal of @@ -57,6 +58,7 @@ Feature: Step - sample() | m[{"software":"l[d[1.0].d,d[0.4].d]"}] | | m[{"person":"l[d[0.5].d,d[1.0].d]"}] | + @WithSeedStrategy Scenario: g_withStrategiesXSeedStrategyX_V_group_byXlabelX_byXbothE_weight_order_fold_sampleXlocal_5XXunfold Given the modern graph And the traversal of @@ -69,6 +71,7 @@ Feature: Step - sample() | m[{"software":"l[d[0.2].d,d[0.4].d,d[0.4].d,d[1.0].d]"}] | | m[{"person":"l[d[0.5].d,d[1.0].d,d[0.4].d,d[0.2].d,d[1.0].d]"}] | + @WithSeedStrategy Scenario: g_withStrategiesXSeedStrategyX_V_order_byXlabel_descX_sampleX1X_byXageX Given the modern graph And the traversal of diff --git a/gremlin-test/features/filter/Where.feature b/gremlin-test/features/filter/Where.feature index 137ed5c..e8e102b 100644 --- a/gremlin-test/features/filter/Where.feature +++ b/gremlin-test/features/filter/Where.feature @@ -345,7 +345,7 @@ Feature: Step - where() Then the result should be empty # comparison of null "age" values in where() - @GraphComputerVerificationReferenceOnly + @GraphComputerVerificationReferenceOnly @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_VX3X_asXaX_in_out_asXbX_whereXa_eqXbXX_byXageX_name Given the modern graph And using the parameter vid3 defined as "v[lop].id" diff --git a/gremlin-test/features/integrated/ReadOnlyStrategy.feature b/gremlin-test/features/integrated/ReadOnlyStrategy.feature new file mode 100644 index 0000000..9feaba4 --- /dev/null +++ b/gremlin-test/features/integrated/ReadOnlyStrategy.feature @@ -0,0 +1,100 @@ +# 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. + +@StepClassIntegrated +Feature: Step - ReadOnlyStrategy + + @WithReadOnlyStrategy + Scenario: g_withStrategiesXReadOnlyStrategyX_V + Given the modern graph + And the traversal of + """ + g.withStrategies(ReadOnlyStrategy).V() + """ + When iterated to list + Then the result should be unordered + | result | + | v[marko] | + | v[vadas] | + | v[lop] | + | v[josh] | + | v[ripple] | + | v[peter] | + + @WithReadOnlyStrategy + Scenario: g_withStrategiesXReadOnlyStrategyX_V_outXknowsX_name + Given the modern graph + And the traversal of + """ + g.withStrategies(ReadOnlyStrategy).V().out("knows").values("name") + """ + When iterated to list + Then the result should be unordered + | result | + | vadas | + | josh | + + @WithReadOnlyStrategy + Scenario: g_withStrategiesXReadOnlyStrategyX_addVXpersonX + Given the empty graph + And the traversal of + """ + g.withStrategies(ReadOnlyStrategy).addV("person") + """ + When iterated to list + Then the traversal will raise an error with message containing text of "The provided traversal has a mutating step and thus is not read only" + + @WithReadOnlyStrategy + Scenario: g_withStrategiesXReadOnlyStrategyX_addVXpersonX_fromXVX1XX_toXVX2XX + Given the empty graph + And the traversal of + """ + g.withStrategies(ReadOnlyStrategy).addE("link").from(__.V(1)).to(__.V(2)) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "The provided traversal has a mutating step and thus is not read only" + + @WithReadOnlyStrategy + Scenario: g_withStrategiesXReadOnlyStrategyX_V_addVXpersonX_fromXVX1XX + Given the empty graph + And the traversal of + """ + g.withStrategies(ReadOnlyStrategy).V().addE("link").from(__.V(1)) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "The provided traversal has a mutating step and thus is not read only" + + @WithReadOnlyStrategy + Scenario: g_withStrategiesXReadOnlyStrategyX_V_propertyXname_joshX + Given the empty graph + And the traversal of + """ + g.withStrategies(ReadOnlyStrategy).V().property("name","josh") + """ + When iterated to list + Then the traversal will raise an error with message containing text of "The provided traversal has a mutating step and thus is not read only" + + @WithReadOnlyStrategy + Scenario: g_withStrategiesXReadOnlyStrategyX_E_propertyXweight_0X + Given the empty graph + And the traversal of + """ + g.withStrategies(ReadOnlyStrategy).E().property("weight", 0) + """ + When iterated to list + Then the traversal will raise an error with message containing text of "The provided traversal has a mutating step and thus is not read only" + diff --git a/gremlin-test/features/map/Max.feature b/gremlin-test/features/map/Max.feature index ef5a127..38fb7db 100644 --- a/gremlin-test/features/map/Max.feature +++ b/gremlin-test/features/map/Max.feature @@ -71,6 +71,7 @@ Feature: Step - max() | result | | d[35].i | + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_maxXlocalX Given the modern graph And the traversal of @@ -93,6 +94,7 @@ Feature: Step - max() | result | | d[35].i | + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_max Given the modern graph And the traversal of @@ -113,6 +115,7 @@ Feature: Step - max() When iterated to list Then the result should be empty + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_maxXlocalX Given the modern graph And the traversal of @@ -133,6 +136,7 @@ Feature: Step - max() When iterated to list Then the result should be empty + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_max Given the modern graph And the traversal of diff --git a/gremlin-test/features/map/Mean.feature b/gremlin-test/features/map/Mean.feature index 1b1de2f..7190ac4 100644 --- a/gremlin-test/features/map/Mean.feature +++ b/gremlin-test/features/map/Mean.feature @@ -82,6 +82,7 @@ Feature: Step - mean() | d[30.75].d | # null values are ignored in mean() which is similar to sum aggregation works in SQL + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_meanXlocalX Given the modern graph And the traversal of @@ -106,6 +107,7 @@ Feature: Step - mean() | d[30.75].d | # null values are ignored in mean() which is similar to sum aggregation works in SQL + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_mean Given the modern graph And the traversal of @@ -128,6 +130,7 @@ Feature: Step - mean() Then the result should be empty # if all values are null then the result is null + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_meanXlocalX Given the modern graph And the traversal of @@ -150,6 +153,7 @@ Feature: Step - mean() Then the result should be empty # if all values are null then the result is null + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_mean Given the modern graph And the traversal of diff --git a/gremlin-test/features/map/Min.feature b/gremlin-test/features/map/Min.feature index 3146aa9..3134809 100644 --- a/gremlin-test/features/map/Min.feature +++ b/gremlin-test/features/map/Min.feature @@ -71,6 +71,7 @@ Feature: Step - min() | result | | d[27].i | + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_minXlocalX Given the modern graph And the traversal of @@ -93,6 +94,7 @@ Feature: Step - min() | result | | d[27].i | + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_min Given the modern graph And the traversal of @@ -113,6 +115,7 @@ Feature: Step - min() When iterated to list Then the result should be empty + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_minXlocalX Given the modern graph And the traversal of @@ -133,6 +136,7 @@ Feature: Step - min() When iterated to list Then the result should be empty + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_min Given the modern graph And the traversal of diff --git a/gremlin-test/features/map/Order.feature b/gremlin-test/features/map/Order.feature index e1298e6..84b4d61 100644 --- a/gremlin-test/features/map/Order.feature +++ b/gremlin-test/features/map/Order.feature @@ -368,6 +368,7 @@ Feature: Step - order() | v[peter] | # tests order().by() where a property isn't present to ensure null comes first + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_orXhasLabelXpersonX_hasXsoftware_name_lopXX_order_byXageX Given the modern graph And the traversal of diff --git a/gremlin-test/features/map/Path.feature b/gremlin-test/features/map/Path.feature index a5e5605..3563cd7 100644 --- a/gremlin-test/features/map/Path.feature +++ b/gremlin-test/features/map/Path.feature @@ -123,7 +123,7 @@ Feature: Step - path() | p[d[29].i,d[27].i] | | p[d[29].i,d[32].i] | - @GraphComputerVerificationReferenceOnly + @GraphComputerVerificationReferenceOnly @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_VX1X_out_path_byXageX Given the modern graph And using the parameter vid1 defined as "v[marko].id" diff --git a/gremlin-test/features/map/Project.feature b/gremlin-test/features/map/Project.feature index 645450f..0e6e875 100644 --- a/gremlin-test/features/map/Project.feature +++ b/gremlin-test/features/map/Project.feature @@ -89,6 +89,7 @@ Feature: Step - project() | m[{"a":"d[1].l", "b":"d[32].i"}] | | m[{"a":"d[0].l", "b":"d[35].i"}] | + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_projectXa_bX_byXinE_countX_byXageX Given the modern graph And the traversal of diff --git a/gremlin-test/features/map/Select.feature b/gremlin-test/features/map/Select.feature index de599f2..b5dabb5 100644 --- a/gremlin-test/features/map/Select.feature +++ b/gremlin-test/features/map/Select.feature @@ -847,7 +847,7 @@ Feature: Step - select() | m[{"a":"d[32].i","n":"josh"}] | | m[{"a":"d[35].i","n":"peter"}] | - @GraphComputerVerificationReferenceOnly + @GraphComputerVerificationReferenceOnly @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_asXaX_selectXaX_byXageX Given the modern graph And the traversal of diff --git a/gremlin-test/features/map/Sum.feature b/gremlin-test/features/map/Sum.feature index cf1c3c9..b2f23ac 100644 --- a/gremlin-test/features/map/Sum.feature +++ b/gremlin-test/features/map/Sum.feature @@ -82,6 +82,7 @@ Feature: Step - sum() | d[123].i | # null values are ignored in sum() which is similar to sum aggregation works in SQL + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_sumXlocalX Given the modern graph And the traversal of @@ -106,6 +107,7 @@ Feature: Step - sum() | d[123].l | # null values are ignored in sum() which is similar to sum aggregation works in SQL + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXageX_capXaX_unfold_sum Given the modern graph And the traversal of @@ -127,6 +129,7 @@ Feature: Step - sum() Then the result should be empty # if all values are null then the result is null + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_sumXlocalX Given the modern graph And the traversal of @@ -149,6 +152,7 @@ Feature: Step - sum() Then the result should be empty # if all values are null then the result is null + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXaX_byXfooX_capXaX_unfold_sum Given the modern graph And the traversal of diff --git a/gremlin-test/features/sideEffect/Aggregate.feature b/gremlin-test/features/sideEffect/Aggregate.feature index b3de3ce..5f8287b 100644 --- a/gremlin-test/features/sideEffect/Aggregate.feature +++ b/gremlin-test/features/sideEffect/Aggregate.feature @@ -124,6 +124,7 @@ Feature: Step - aggregate() | d[32].i | | d[35].i | + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXlocal_xX_byXageX_capXxX Given the modern graph And the traversal of @@ -222,6 +223,7 @@ Feature: Step - aggregate() | d[32].i | | d[35].i | + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXxX_byXvaluesXageX_isXgtX29XXX_capXxX Given the modern graph And the traversal of @@ -252,7 +254,7 @@ Feature: Step - aggregate() | v[lop] | | v[lop] | - @GraphComputerVerificationReferenceOnly + @GraphComputerVerificationReferenceOnly @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_aggregateXxX_byXout_order_byXnameXX_capXxX Given the modern graph And the traversal of diff --git a/gremlin-test/features/sideEffect/Fail.feature b/gremlin-test/features/sideEffect/Fail.feature index 53f2d56..0820f82 100644 --- a/gremlin-test/features/sideEffect/Fail.feature +++ b/gremlin-test/features/sideEffect/Fail.feature @@ -34,7 +34,7 @@ Feature: Step - fail() g.V().fail("msg") """ When iterated to list - Then the traversal will raise an error + Then the traversal will raise an error with message containing text of "msg" Scenario: g_V_unionXout_failX Given the modern graph diff --git a/gremlin-test/features/sideEffect/Group.feature b/gremlin-test/features/sideEffect/Group.feature index f4937cd..429f9ad 100644 --- a/gremlin-test/features/sideEffect/Group.feature +++ b/gremlin-test/features/sideEffect/Group.feature @@ -40,6 +40,7 @@ Feature: Step - group() | result | | m[{"d[35].i":"l[v[peter]]", "d[27].i":"l[v[vadas]]", "d[32].i": "l[v[josh]]", "d[29].i":"l[v[marko]]"}] | + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_group_byXageX Given the modern graph And the traversal of diff --git a/gremlin-test/features/sideEffect/GroupCount.feature b/gremlin-test/features/sideEffect/GroupCount.feature index e87d253..8298d21 100644 --- a/gremlin-test/features/sideEffect/GroupCount.feature +++ b/gremlin-test/features/sideEffect/GroupCount.feature @@ -40,6 +40,7 @@ Feature: Step - groupCount() | result | | m[{"d[27].i":"d[1].l", "d[29].i":"d[1].l", "d[32].i":"d[1].l", "d[35].i":"d[1].l"}] | + @WithProductiveByStrategy Scenario: g_withStrategiesXProductiveByStrategyX_V_groupCount_byXageX Given the modern graph And the traversal of diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java index bfec6f9..54150ea 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java @@ -57,6 +57,9 @@ import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInA import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import static org.hamcrest.core.Every.everyItem; import static org.hamcrest.core.IsInstanceOf.instanceOf; +import static org.hamcrest.core.StringContains.containsString; +import static org.hamcrest.core.StringEndsWith.endsWith; +import static org.hamcrest.core.StringStartsWith.startsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; @@ -370,6 +373,29 @@ public final class StepDefinition { error = null; } + @Then("the traversal will raise an error with message {word} text of {string}") + public void theTraversalWillRaiseAnErrorWithMessage(final String comparison, final String expectedMessage) { + assertNotNull(error); + + switch (comparison) { + case "containing": + assertThat(error.getMessage(), containsString(expectedMessage)); + break; + case "starting": + assertThat(error.getMessage(), startsWith(expectedMessage)); + break; + case "ending": + assertThat(error.getMessage(), endsWith(expectedMessage)); + break; + default: + throw new IllegalStateException(String.format( + "Unknown comparison of %s - must be one of: containing, starting or ending", comparison)); + } + + // consume the error now that it has been asserted + error = null; + } + ////////////////////////////////////////////// @Given("an unsupported test") diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessLimitedComputerSuite.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessLimitedComputerSuite.java index bf3f357..24641f0 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessLimitedComputerSuite.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessLimitedComputerSuite.java @@ -138,8 +138,6 @@ public class ProcessLimitedComputerSuite extends AbstractGremlinSuite { TranslationStrategyProcessTest.class, // decorations - ReadOnlyStrategyProcessTest.class, - SeedStrategyProcessTest.class, SubgraphStrategyProcessTest.class, // optimizations diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessLimitedStandardSuite.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessLimitedStandardSuite.java index b84cea2..19978db 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessLimitedStandardSuite.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessLimitedStandardSuite.java @@ -79,18 +79,12 @@ public class ProcessLimitedStandardSuite extends AbstractGremlinSuite { // decorations ElementIdStrategyProcessTest.class, EventStrategyProcessTest.class, - ReadOnlyStrategyProcessTest.class, PartitionStrategyProcessTest.class, - SeedStrategyProcessTest.class, SubgraphStrategyProcessTest.class, // optimizations IncidentToAdjacentStrategyProcessTest.class, EarlyLimitStrategyProcessTest.class, - - // semantics - OrderabilityTest.Traversals.class, - TernaryBooleanLogicsTest.class, }; /**