| #region License |
| |
| /* |
| * 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. |
| */ |
| |
| #endregion |
| |
| using System; |
| using System.Collections; |
| using System.Collections.Generic; |
| using System.Globalization; |
| using System.Linq; |
| using System.Text.RegularExpressions; |
| using Gherkin.Ast; |
| using Gremlin.Net.IntegrationTest.Gherkin.Attributes; |
| using Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation; |
| using Gremlin.Net.Process.Traversal; |
| using Gremlin.Net.Structure; |
| using Newtonsoft.Json.Linq; |
| using Xunit; |
| |
| using static Gremlin.Net.Process.Traversal.AnonymousTraversalSource; |
| |
| namespace Gremlin.Net.IntegrationTest.Gherkin |
| { |
| internal class CommonSteps : StepDefinition |
| { |
| private GraphTraversalSource _g; |
| private string _graphName; |
| private readonly IDictionary<string, object> _parameters = new Dictionary<string, object>(); |
| private ITraversal _traversal; |
| private object[] _result; |
| |
| private static readonly IDictionary<Regex, Func<string, string, object>> Parsers = |
| new Dictionary<string, Func<string, string, object>> |
| { |
| {@"d\[([\d.]+)\]\.([ilfdm])", ToNumber}, |
| {@"D\[(.+)\]", ToDirection}, |
| {@"v\[(.+)\]", ToVertex}, |
| {@"v\[(.+)\]\.id", (x, graphName) => ToVertex(x, graphName).Id}, |
| {@"v\[(.+)\]\.sid", (x, graphName) => ToVertex(x, graphName).Id.ToString()}, |
| {@"e\[(.+)\]", ToEdge}, |
| {@"e\[(.+)\].id", (x, graphName) => ToEdge(x, graphName).Id}, |
| {@"e\[(.+)\].sid", (x, graphName) => ToEdge(x, graphName).Id.ToString()}, |
| {@"p\[(.+)\]", ToPath}, |
| {@"l\[(.*)\]", ToList}, |
| {@"s\[(.*)\]", ToSet}, |
| {@"m\[(.+)\]", ToMap}, |
| {@"c\[(.+)\]", ToLambda}, |
| {@"t\[(.+)\]", ToT} |
| }.ToDictionary(kv => new Regex("^" + kv.Key + "$", RegexOptions.Compiled), kv => kv.Value); |
| |
| private static readonly IDictionary<char, Func<string, object>> NumericParsers = |
| new Dictionary<char, Func<string, object>> |
| { |
| { 'i', s => Convert.ToInt32(s) }, |
| { 'l', s => Convert.ToInt64(s) }, |
| { 'f', s => Convert.ToSingle(s, CultureInfo.InvariantCulture) }, |
| { 'd', s => Convert.ToDouble(s, CultureInfo.InvariantCulture) }, |
| { 'm', s => Convert.ToDecimal(s, CultureInfo.InvariantCulture) } |
| }; |
| |
| [Given("the (\\w+) graph")] |
| public void ChooseModernGraph(string graphName) |
| { |
| if (graphName == "empty") |
| { |
| ScenarioData.CleanEmptyData(); |
| } |
| var data = ScenarioData.GetByGraphName(graphName); |
| _graphName = graphName; |
| _g = Traversal().WithRemote(data.Connection); |
| } |
| |
| [Given("using the parameter (\\w+) defined as \"(.*)\"")] |
| public void UsingParameter(string name, string value) |
| { |
| var parsedValue = ParseValue(value.Replace("\\\"", "\""), _graphName); |
| _parameters.Add(name, parsedValue); |
| } |
| |
| [Given("the traversal of")] |
| public void TranslateTraversal(string traversalText) |
| { |
| if (_g == null) |
| { |
| throw new InvalidOperationException("g should be a traversal source"); |
| } |
| _traversal = TraversalParser.GetTraversal(traversalText, _g, _parameters); |
| } |
| |
| [Given("the graph initializer of")] |
| public void InitTraversal(string traversalText) |
| { |
| var traversal = TraversalParser.GetTraversal(traversalText, _g, _parameters); |
| traversal.Iterate(); |
| |
| // We may have modified the so-called `empty` graph |
| if (_graphName == "empty") |
| { |
| ScenarioData.ReloadEmptyData(); |
| } |
| } |
| |
| [Given("an unsupported test")] |
| public void UnsupportedTest() |
| { |
| |
| } |
| |
| [When("iterated to list")] |
| public void IterateToList() |
| { |
| if (_traversal == null) |
| { |
| throw new InvalidOperationException("Traversal should be set before iterating"); |
| } |
| ITraversal t = _traversal; |
| var list = new List<object>(); |
| while (t.MoveNext()) |
| { |
| list.Add(t.Current); |
| } |
| _result = list.ToArray(); |
| } |
| |
| [When("iterated next")] |
| public void IterateNext() |
| { |
| if (_traversal == null) |
| { |
| throw new InvalidOperationException("Traversal should be set before iterating"); |
| } |
| _traversal.MoveNext(); |
| var result = _traversal.Current; |
| switch (result) |
| { |
| case null: |
| _result = null; |
| return; |
| case object[] arrayResult: |
| _result = arrayResult; |
| return; |
| case IEnumerable enumerableResult: |
| _result = enumerableResult.Cast<object>().ToArray(); |
| return; |
| } |
| throw new InvalidCastException($"Can not convert instance of {result.GetType()} to object[]"); |
| } |
| |
| [Then("the result should be (\\w+)")] |
| public void AssertResult(string characterizedAs, DataTable table = null) |
| { |
| var ordered = characterizedAs == "ordered"; |
| switch (characterizedAs) |
| { |
| case "empty": |
| Assert.Equal(0, _result.Length); |
| return; |
| case "ordered": |
| case "unordered": |
| case "of": |
| Assert.NotNull(table); |
| var rows = table.Rows.ToArray(); |
| Assert.Equal("result", rows[0].Cells.First().Value); |
| var expected = rows.Skip(1).Select(x => ParseValue(x.Cells.First().Value, _graphName)); |
| if (ordered) |
| { |
| Assert.Equal(expected, _result); |
| } |
| else |
| { |
| var expectedArray = expected.ToArray(); |
| foreach (var resultItem in _result) |
| { |
| Assert.Contains(resultItem, expectedArray); |
| } |
| if (characterizedAs != "of") |
| { |
| Assert.Equal(expectedArray.Length, _result.Length); |
| } |
| } |
| break; |
| default: |
| throw new NotSupportedException($"Result as '{characterizedAs}' not supported"); |
| } |
| } |
| |
| [Then("the result should have a count of (\\d+)")] |
| public void AssertCount(int count) |
| { |
| Assert.Equal(count, _result.Length); |
| } |
| |
| [Then("the graph should return (\\d+) for count of (.+)")] |
| public void AssertTraversalCount(int expectedCount, string traversalText) |
| { |
| if (traversalText.StartsWith("\"")) |
| { |
| traversalText = traversalText.Substring(1, traversalText.Length - 2); |
| } |
| var traversal = TraversalParser.GetTraversal(traversalText, _g, _parameters); |
| var count = 0; |
| while (traversal.MoveNext()) |
| { |
| count++; |
| } |
| Assert.Equal(expectedCount, count); |
| } |
| |
| [Then("nothing should happen because")] |
| public void AssertNothing(string reason) |
| { |
| |
| } |
| |
| private static object ToMap(string stringMap, string graphName) |
| { |
| return ParseMapValue(JObject.Parse(stringMap), graphName); |
| } |
| |
| private static object ToLambda(string stringLambda, string graphName) |
| { |
| return Lambda.Groovy(stringLambda); |
| } |
| |
| private static object ToT(string enumName, string graphName) |
| { |
| return T.GetByValue(enumName); |
| } |
| |
| private static object ToDirection(string enumName, string graphName) |
| { |
| return Direction.GetByValue(enumName); |
| } |
| |
| private static object ToNumber(string stringNumber, string graphName) |
| { |
| return NumericParsers[stringNumber[stringNumber.Length - 1]]( |
| stringNumber.Substring(0, stringNumber.Length - 1)); |
| } |
| |
| private static object ParseMapValue(JToken value, string graphName) |
| { |
| if (value.Type == JTokenType.Object) |
| { |
| IDictionary<string, JToken> jsonMap = (JObject)value; |
| return jsonMap.ToDictionary(kv => ParseMapValue(kv.Key, graphName), |
| kv => ParseMapValue(kv.Value, graphName)); |
| } |
| if (value.Type == JTokenType.Array) |
| { |
| return value.Select(v => ParseMapValue(v, graphName)).ToArray(); |
| } |
| var objValue = value.ToObject<object>(); |
| if (objValue is long longValue) |
| { |
| // JSON Numeric values converted to int64 by default |
| return Convert.ToInt32(longValue); |
| } |
| if (objValue is string stringValue) |
| { |
| return ParseValue(stringValue, graphName); |
| } |
| return objValue; |
| } |
| |
| private static ISet<object> ToSet(string stringSet, string graphName) |
| { |
| return new HashSet<object>(ToList(stringSet, graphName)); |
| } |
| |
| private static IList<object> ToList(string stringList, string graphName) |
| { |
| if (stringList == "") |
| { |
| return new List<object>(0); |
| } |
| return stringList.Split(',').Select(x => ParseValue(x, graphName)).ToList(); |
| } |
| |
| private static Vertex ToVertex(string name, string graphName) |
| { |
| return ScenarioData.GetByGraphName(graphName).Vertices[name]; |
| } |
| |
| private static Edge ToEdge(string name, string graphName) |
| { |
| return ScenarioData.GetByGraphName(graphName).Edges[name]; |
| } |
| |
| private static Path ToPath(string value, string graphName) |
| { |
| return new Path(new List<ISet<string>>(0), value.Split(',').Select(x => ParseValue(x, graphName)).ToList()); |
| } |
| |
| private static object ParseValue(string stringValue, string graphName) |
| { |
| Func<string, string, object> parser = null; |
| string extractedValue = null; |
| foreach (var kv in Parsers) |
| { |
| var match = kv.Key.Match(stringValue); |
| if (match.Success) |
| { |
| parser = kv.Value; |
| extractedValue = match.Groups[1].Value; |
| if (match.Groups.Count > 2) |
| { |
| extractedValue += match.Groups[2].Value; |
| } |
| break; |
| } |
| } |
| return parser != null ? parser(extractedValue, graphName) : stringValue; |
| } |
| } |
| } |