blob: feacf222c43c0ca5a79457805bd95b10f63578ac [file] [log] [blame]
#
# 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.
#
import json
import re
from gremlin_python.structure.graph import Path
from gremlin_python.process.anonymous_traversal import traversal
from gremlin_python.process.graph_traversal import __
from gremlin_python.process.traversal import Barrier, Cardinality, P, Pop, Scope, Column, Order, Direction, T, Pick, Operator
from radish import given, when, then
from hamcrest import *
regex_all = re.compile(r"Pop\.all")
regex_and = re.compile(r"([(.,\s])and\(")
regex_as = re.compile(r"([(.,\s])as\(")
regex_from = re.compile(r"([(.,\s])from\(")
regex_global = re.compile(r"([(.,\s])global")
regex_in = re.compile(r"([(.,\s])in\(")
regex_is = re.compile(r"([(.,\s])is\(")
regex_not = re.compile(r"([(.,\s])not\(")
regex_or = re.compile(r"([(.,\s])or\(")
regex_true = re.compile(r"(true)")
regex_false = re.compile(r"(false)")
outV = __.outV
label = __.label
inV = __.inV
project = __.project
tail = __.tail
ignores = []
@given("the {graph_name:w} graph")
def choose_graph(step, graph_name):
step.context.graph_name = graph_name
step.context.g = traversal().withRemote(step.context.remote_conn[graph_name])
@given("the graph initializer of")
def initialize_graph(step):
t = _make_traversal(step.context.g, step.text, {})
# just be sure that the traversal returns something to prove that it worked to some degree. probably
# is overkill to try to assert the complete success of this init operation. presumably the test
# suite would fail elsewhere if this didn't work which would help identify a problem.
result = t.toList()
assert len(result) > 0
# add the first result - if a map - to the bindings. this is useful for cases when parameters for
# the test traversal need to come from the original graph initializer (i.e. a new graph is created
# and you need the id of a vertex from that graph)
@given("an unsupported test")
def unsupported_scenario(step):
# this is a do nothing step as the test can't be supported for whatever reason
return
@given("using the parameter {param_name:w} defined as {param:QuotedString}")
def add_parameter(step, param_name, param):
if not hasattr(step.context, "traversal_params"):
step.context.traversal_params = {}
step.context.traversal_params[param_name] = _convert(param, step.context)
@given("the traversal of")
def translate_traversal(step):
step.context.ignore = ignores.__contains__(step.text)
step.context.traversal = _make_traversal(
step.context.g, step.text,
step.context.traversal_params if hasattr(step.context, "traversal_params") else {})
@when("iterated to list")
def iterate_the_traversal(step):
if step.context.ignore:
return
step.context.result = map(lambda x: _convert_results(x), step.context.traversal.toList())
@when("iterated next")
def next_the_traversal(step):
if step.context.ignore:
return
step.context.result = map(lambda x: _convert_results(x), step.context.traversal.next())
@then("the result should be {characterized_as:w}")
def assert_result(step, characterized_as):
if step.context.ignore:
return
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
_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
_any_assertion(step.table, step.context.result, step.context)
else:
raise ValueError("unknown data characterization of " + characterized_as)
@then("the graph should return {count:d} for count of {traversal_string:QuotedString}")
def assert_side_effects(step, count, traversal_string):
if step.context.ignore:
return
t = _make_traversal(step.context.g, traversal_string.replace('\\"', '"'),
step.context.traversal_params if hasattr(step.context, "traversal_params") else {})
assert_that(t.count().next(), equal_to(count))
@then("the result should have a count of {count:d}")
def assert_count(step, count):
assert_that(len(step.context.result), equal_to(count))
@then("nothing should happen because")
def nothing_happening(step):
return
def _convert(val, ctx):
graph_name = ctx.graph_name
if isinstance(val, dict): # convert dictionary keys/values
n = {}
for key, value in val.items():
n[_convert(key, ctx)] = _convert(value, ctx)
return n
elif isinstance(val, unicode): # convert annoying python 2.x unicode nonsense
return _convert(val.encode('utf-8'), ctx)
elif isinstance(val, str) and re.match("^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("^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("^d\[.*\]\.[ilfdm]$", 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("^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("^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("^v\[.*\]$", val): # parse vertex
return __find_cached_element(ctx, graph_name, val[2:-1], "v")
elif isinstance(val, str) and re.match("^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("^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("^e\[.*\]$", val): # parse edge
return __find_cached_element(ctx, graph_name, val[2:-1], "e")
elif isinstance(val, str) and re.match("^m\[.*\]$", val): # parse json as a map
return _convert(json.loads(val[2:-1].replace('\\"', '"')), ctx)
elif isinstance(val, str) and re.match("^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("^c\[.*\]$", val): # parse lambda/closure
return lambda: (val[2:-1], "gremlin-groovy")
elif isinstance(val, str) and re.match("^t\[.*\]$", val): # parse instance of T enum
return T[val[2:-1]]
else:
return val
def __find_cached_element(ctx, graph_name, identifier, element_type):
if graph_name == "empty":
cache = __create_lookup_v(ctx.remote_conn["empty"]) if element_type == "v" else __create_lookup_e(ctx.remote_conn["empty"])
else:
cache = ctx.lookup_v[graph_name] if element_type == "v" else ctx.lookup_e[graph_name]
return cache[identifier]
def _convert_results(val):
if isinstance(val, Path):
# kill out labels as they aren't in the assertion logic
return Path([set([])], map(lambda p: p.encode("utf-8") if isinstance(p, unicode) else p, val.objects))
else:
return val
def _any_assertion(data, result, ctx):
converted = [_convert(line['result'], ctx) for line in data]
for r in result:
assert_that(r, is_in(converted))
def _table_assertion(data, result, ctx, ordered):
# results from traversal should have the same number of entries as the feature data table
assert_that(len(result), equal_to(len(data)), "result:" + str(result))
results_to_test = list(result)
# finds a match in the results for each line of data to assert and then removes that item
# from the list - in the end there should be no items left over and each will have been asserted
for ix, line in enumerate(data):
val = _convert(line['result'], ctx)
# clear the labels since we don't define them in .feature files
if isinstance(val, Path):
val.labels = [set([])]
if ordered:
assert_that(results_to_test[ix], equal_to(val))
else:
assert_that(val, is_in(results_to_test))
results_to_test.remove(val)
if not ordered:
assert_that(len(results_to_test), is_(0))
def _translate(traversal_):
replaced = traversal_.replace("\n", "")
replaced = regex_all.sub(r"Pop.all_", replaced)
replaced = regex_and.sub(r"\1and_(", replaced)
replaced = regex_from.sub(r"\1from_(", replaced)
replaced = regex_global.sub(r"\1global_", replaced)
replaced = regex_as.sub(r"\1as_(", replaced)
replaced = regex_is.sub(r"\1is_(", replaced)
replaced = regex_not.sub(r"\1not_(", replaced)
replaced = regex_or.sub(r"\1or_(", replaced)
replaced = regex_in.sub(r"\1in_(", replaced)
replaced = regex_true.sub(r"True", replaced)
return regex_false.sub(r"False", replaced)
def _make_traversal(g, traversal_string, params):
b = {"g": g,
"__": __,
"Barrier": Barrier,
"Cardinality": Cardinality,
"Column": Column,
"Direction": Direction,
"Order": Order,
"P": P,
"Pick": Pick,
"Pop": Pop,
"Scope": Scope,
"Operator": Operator,
"T": T}
b.update(params)
return eval(_translate(traversal_string), b)
def __create_lookup_v(remote):
g = traversal().withRemote(remote)
# hold a map of name/vertex for use in asserting results
return g.V().group().by('name').by(tail()).next()
def __create_lookup_e(remote):
g = traversal().withRemote(remote)
# hold a map of the "name"/edge for use in asserting results - "name" in this context is in the form of
# outgoingV-label->incomingV
return g.E().group(). \
by(lambda: ("it.outVertex().value('name') + '-' + it.label() + '->' + it.inVertex().value('name')", "gremlin-groovy")). \
by(tail()).next()