Skip to content

Commit

Permalink
Updated gremlin-go code to support new godog tests that were added fo…
Browse files Browse the repository at this point in the history
…r 3.6.0. Also added Merge steps, correct a bug in MergeV, fixed Regex, and updated test generation.

Signed-off-by: Lyndon Bauto <lyndonb@bitquilltech.com>
  • Loading branch information
lyndonbauto committed Apr 4, 2022
1 parent 414684e commit f9b81cc
Show file tree
Hide file tree
Showing 8 changed files with 298 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Script;
import org.apache.tinkerpop.gremlin.process.traversal.Text;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
import org.apache.tinkerpop.gremlin.process.traversal.Translator;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
Expand All @@ -36,6 +37,7 @@
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.apache.tinkerpop.gremlin.util.NumberHelper;
import org.apache.tinkerpop.gremlin.util.function.Lambda;

import java.sql.Timestamp;
Expand All @@ -48,6 +50,7 @@
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.BiPredicate;

/**
* Translates Gremlin {@link Bytecode} into a Golang string representation.
Expand Down Expand Up @@ -158,7 +161,15 @@ protected String getSyntax(final Lambda o) {

@Override
protected String getSyntax(final Number o) {
return o.toString();
if (o instanceof Float || o instanceof Double) {
if (NumberHelper.isNaN(o))
return "math.NaN()";
else if (NumberHelper.isPositiveInfinity(o))
return "math.Inf(1)";
else if (NumberHelper.isNegativeInfinity(o))
return "math.Inf(-11)";
}
return o.toString();
}

@Override
Expand Down Expand Up @@ -293,8 +304,19 @@ protected Script produceScript(final String traversalSource, final Bytecode o) {

@Override
protected Script produceScript(final P<?> p) {

if (p instanceof TextP) {
script.append(GO_PACKAGE_NAME + "TextP.").append(resolveSymbol(p.getBiPredicate().toString())).append("(");
// special case the RegexPredicate since it isn't an enum. toString() for the final default will
// typically cover implementations (generally worked for Text prior to 3.6.0)
final BiPredicate<?, ?> tp = p.getBiPredicate();
if (tp instanceof Text.RegexPredicate) {
final String regexToken = ((Text.RegexPredicate) p.getBiPredicate()).isNegate() ? "NotRegex" : "Regex";
script.append(GO_PACKAGE_NAME + "TextP.").append(regexToken).append("(");
} else if (tp instanceof Text) {
script.append(GO_PACKAGE_NAME + "TextP.").append(resolveSymbol(p.getBiPredicate().toString())).append("(");
} else {
script.append(GO_PACKAGE_NAME + "TextP.").append(resolveSymbol(p.getBiPredicate().toString())).append("(");
}
convertToScript(p.getValue());
} else if (p instanceof ConnectiveP) {
// ConnectiveP gets some special handling because it's reduced to and(P, P, P) and we want it
Expand Down
27 changes: 6 additions & 21 deletions gremlin-go/build/generate.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ radishGremlinFile.withWriter('UTF-8') { Writer writer ->
'\n' +
'import (\n' +
'\t \"errors\"\n' +
'\t \"math\"\n' +
'\t \"github.com/apache/tinkerpop/gremlin-go/driver\"\n' +
')\n'
)
Expand Down Expand Up @@ -129,27 +130,11 @@ radishGremlinFile.withWriter('UTF-8') { Writer writer ->
writer.write("func(g *gremlingo.GraphTraversalSource, p map[string]interface{}) *gremlingo.GraphTraversal {return ")
try {
writer.write(translator.translate(t.bytecode).script.
replace("xx1", "p[\"xx1\"]").
replace("xx2", "p[\"xx2\"]").
replace("xx3", "p[\"xx3\"]").
replace("v1", "p[\"v1\"]").
replace("v2", "p[\"v2\"]").
replace("v3", "p[\"v3\"]").
replace("v4", "p[\"v4\"]").
replace("v5", "p[\"v5\"]").
replace("v6", "p[\"v6\"]").
replace("vid1", "p[\"vid1\"]").
replace("vid2", "p[\"vid2\"]").
replace("vid3", "p[\"vid3\"]").
replace("vid4", "p[\"vid4\"]").
replace("vid5", "p[\"vid5\"]").
replace("vid6", "p[\"vid6\"]").
replace("e7", "p[\"e7\"]").
replace("e10", "p[\"e10\"]").
replace("e11", "p[\"e11\"]").
replace("eid7", "p[\"eid7\"]").
replace("eid10", "p[\"eid10\"]").
replace("eid11", "p[\"eid11\"]").
replaceAll("xx([0-9]+)", "p[\"xx\$1\"]").
replaceAll("v([0-9]+)", "p[\"v\$1\"]").
replaceAll("vid([0-9]+)", "p[\"vid\$1\"]").
replaceAll("e([0-9]+)", "p[\"e\$1\"]").
replaceAll("eid([0-9]+)", "p[\"eid\$1\"]").
replace("l1", "p[\"l1\"]").
replace("l2", "p[\"l2\"]").
replace("pred1", "p[\"pred1\"]").
Expand Down
163 changes: 139 additions & 24 deletions gremlin-go/cucumber/featureSteps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"fmt"
"github.com/apache/tinkerpop/gremlin-go/driver"
"github.com/cucumber/godog"
"math"
"reflect"
"regexp"
"strconv"
Expand All @@ -41,30 +42,47 @@ var parsers map[*regexp.Regexp]func(string, string) interface{}

func init() {
parsers = map[*regexp.Regexp]func(string, string) interface{}{
regexp.MustCompile(`^d\[(.*)]\.[lfdm]$`): toNumeric,
regexp.MustCompile(`^d\[(.*)]\.[i]$`): toInt32,
regexp.MustCompile(`^v\[(.+)]$`): toVertex,
regexp.MustCompile(`^v\[(.+)]\.id$`): toVertexId,
regexp.MustCompile(`^e\[(.+)]$`): toEdge,
regexp.MustCompile(`^v\[(.+)]\.sid$`): toVertexIdString,
regexp.MustCompile(`^e\[(.+)]\.id$`): toEdgeId,
regexp.MustCompile(`^e\[(.+)]\.sid$`): toEdgeIdString,
regexp.MustCompile(`^p\[(.+)]$`): toPath,
regexp.MustCompile(`^l\[(.*)]$`): toList,
regexp.MustCompile(`^s\[(.*)]$`): toSet,
regexp.MustCompile(`^m\[(.+)]$`): toMap,
regexp.MustCompile(`^c\[(.+)]$`): toLambda,
regexp.MustCompile(`^t\[(.+)]$`): toT,
regexp.MustCompile(`^D\[(.+)]$`): toDirection,
regexp.MustCompile(`^d\[(.*)]\.[bslfdmn]$`): toNumeric,
regexp.MustCompile(`^d\[(.*)]\.[i]$`): toInt32,
regexp.MustCompile(`^vp\[(.+)]$`): toVertexProperty,
regexp.MustCompile(`^v\[(.+)]$`): toVertex,
regexp.MustCompile(`^v\[(.+)]\.id$`): toVertexId,
regexp.MustCompile(`^e\[(.+)]$`): toEdge,
regexp.MustCompile(`^v\[(.+)]\.sid$`): toVertexIdString,
regexp.MustCompile(`^e\[(.+)]\.id$`): toEdgeId,
regexp.MustCompile(`^e\[(.+)]\.sid$`): toEdgeIdString,
regexp.MustCompile(`^p\[(.+)]$`): toPath,
regexp.MustCompile(`^l\[(.*)]$`): toList,
regexp.MustCompile(`^s\[(.*)]$`): toSet,
regexp.MustCompile(`^m\[(.+)]$`): toMap,
regexp.MustCompile(`^c\[(.+)]$`): toLambda,
regexp.MustCompile(`^t\[(.+)]$`): toT,
regexp.MustCompile(`^D\[(.+)]$`): toDirection,
}
}

func parseValue(value string, graphName string) interface{} {
var extractedValue string
var parser func(string, string) interface{}
if regexp.MustCompile(`^null$`).MatchString(value) {
return nil
}
var extractedValue string
var parser func(string, string) interface{}
if regexp.MustCompile(`^true$`).MatchString(value) {
return true
}
if regexp.MustCompile(`^false$`).MatchString(value) {
return false
}
if regexp.MustCompile(`^d\[NaN]$`).MatchString(value) {
return math.NaN()
}
if regexp.MustCompile(`^d\[Infinity]$`).MatchString(value) {
return math.Inf(1)
}
if regexp.MustCompile(`^d\[-Infinity]$`).MatchString(value) {
return math.Inf(-1)
}

for key, element := range parsers {
var match = key.FindAllStringSubmatch(value, -1)
if len(match) > 0 {
Expand Down Expand Up @@ -105,9 +123,24 @@ func toInt32(stringVal, graphName string) interface{} {
return int32(val)
}

// Parse vertex property.
func toVertexProperty(name, graphName string) interface{} {
if vp, ok := tg.getDataGraphFromMap(graphName).vertexProperties[name]; ok {
return vp
} else {
return fmt.Errorf("VertexProperty with key %s not found", name)
}
}

// Parse vertex.
func toVertex(name, graphName string) interface{} {
return tg.getDataGraphFromMap(graphName).vertices[name]
if v, ok := tg.getDataGraphFromMap(graphName).vertices[name]; ok {
return v
} else {
return &gremlingo.Vertex{
Element: gremlingo.Element{Id: name, Label: "vertex"},
}
}
}

// Parse vertex id.
Expand All @@ -128,7 +161,11 @@ func toVertexIdString(name, graphName string) interface{} {

// Parse edge.
func toEdge(name, graphName string) interface{} {
return tg.getDataGraphFromMap(graphName).edges[name]
if e, ok := tg.getDataGraphFromMap(graphName).edges[name]; ok {
return e
} else {
return fmt.Errorf("edge with key %s not found", name)
}
}

// Parse edge id.
Expand Down Expand Up @@ -240,12 +277,34 @@ func toLambda(name, graphName string) interface{} {

func toT(name, graphName string) interface{} {
// Return as is, since T values are just strings.
return name
if name == "label" {
return gremlingo.Label
} else if name == "id" {
return gremlingo.Id
} else if name == "key" {
return gremlingo.Key
} else if name == "value" {
return gremlingo.Value
} else {
return name
}
}

func toDirection(name, graphName string) interface{} {
// Return as is, since Direction values are just strings.
return name
if name == "IN" {
return gremlingo.In
} else if name == "OUT" {
return gremlingo.Out
} else if name == "BOTH" {
return gremlingo.Both
} else if name == "from" {
return gremlingo.From
} else if name == "to" {
return gremlingo.To
} else {
return name
}
}

func (tg *tinkerPopGraph) anUnsupportedTest() error {
Expand All @@ -259,7 +318,8 @@ func (tg *tinkerPopGraph) iteratedNext() error {
}
result, err := tg.traversal.Next()
if err != nil {
return err
tg.error[true] = err.Error()
return nil
}
var nextResults []interface{}
switch result.GetType().Kind() {
Expand Down Expand Up @@ -288,7 +348,8 @@ func (tg *tinkerPopGraph) iteratedToList() error {
}
results, err := tg.traversal.ToList()
if err != nil {
return err
tg.error[true] = err.Error()
return nil
}
var listResults []interface{}
for _, res := range results {
Expand All @@ -313,6 +374,18 @@ func (tg *tinkerPopGraph) chooseGraph(graphName string) error {
return err
}
}

// TODO: Uncoment code here to use WithComputer once this is implemented.
// In this version strategies are not implemented (and therefore WithComputer also isn't implmented).
for _, tag := range tg.scenario.Tags {
if tag.Name == "@GraphComputerOnly" {
return godog.ErrPending
// tg.g.WithComputer()
} else if tag.Name == "@AllowNullPropertyValues" {
// The GLV suite does not test against a graph that has null property values enabled, skipping via Pending Error
return godog.ErrPending
}
}
return nil
}

Expand Down Expand Up @@ -361,7 +434,7 @@ func (tg *tinkerPopGraph) theGraphShouldReturnForCountOf(expectedCount int, trav
return err
}
if len(results) != expectedCount {
return errors.New("graph did not return the correct count")
return errors.New(fmt.Sprintf("graph returned count of %d when %d was expected", len(results), expectedCount))
}
return nil
}
Expand Down Expand Up @@ -544,6 +617,11 @@ func compareListEqualsWithoutOrder(expected []interface{}, actual []interface{})
// 2. Create a new slice with the index removed when we fix the item we want to delete.
// To do an orderless copy, a copy of the expected result is created. Results are removed as they are found. This stops
// the following from returning equal [1 2 2 2] and [1 1 1 2]

// Shortcut.
if fmt.Sprint(expected) == fmt.Sprint(actual) {
return true
}
expectedCopy := make([]interface{}, len(expected))
copy(expectedCopy, expected)
for _, a := range actual {
Expand Down Expand Up @@ -694,6 +772,41 @@ func (tg *tinkerPopGraph) usingTheParameterOfP(paramName, pVal, stringVal string
return nil
}

func (tg *tinkerPopGraph) theTraversalWillRaiseAnError() error {
if _, ok := tg.error[true]; ok {
return nil
}
return fmt.Errorf("expected the traversal to raise an error")
}

func (tg *tinkerPopGraph) theTraversalWillRaiseAnErrorWithMessageContainingTextOf(comparison, expectedMessage string) error {
if _, ok := tg.error[true]; !ok {
return fmt.Errorf("expected the traversal to raise an error")
}
switch comparison {
case "containing":
if strings.Contains(tg.error[true], expectedMessage) {
return nil
} else {
return fmt.Errorf("traversal error message must contain %s", expectedMessage)
}
case "starting":
if strings.Contains(tg.error[true], expectedMessage) {
return nil
} else {
return fmt.Errorf("traversal error message must contain %s", expectedMessage)
}
case "ending":
if strings.Contains(tg.error[true], expectedMessage) {
return nil
} else {
return fmt.Errorf("traversal error message must contain %s", expectedMessage)
}
default:
return fmt.Errorf("unknow comparison %s - must be: containing, ending or starting", comparison)
}
}

var tg = &tinkerPopGraph{
NewTinkerPopWorld(),
}
Expand Down Expand Up @@ -732,4 +845,6 @@ func InitializeScenario(ctx *godog.ScenarioContext) {
ctx.Step(`^the traversal of$`, tg.theTraversalOf)
ctx.Step(`^using the parameter (.+) defined as "(.+)"$`, tg.usingTheParameterDefined)
ctx.Step(`^using the parameter (.+) of P\.(.+)\("(.+)"\)$`, tg.usingTheParameterOfP)
ctx.Step(`^the traversal will raise an error$`, tg.theTraversalWillRaiseAnError)
ctx.Step(`^the traversal will raise an error with message (\w+) text of "(.+)"$`, tg.theTraversalWillRaiseAnErrorWithMessageContainingTextOf)
}
Loading

0 comments on commit f9b81cc

Please sign in to comment.