diff --git a/neo4j/.DS_Store b/neo4j/.DS_Store
new file mode 100644
index 00000000..d039ac90
Binary files /dev/null and b/neo4j/.DS_Store differ
diff --git a/neo4j/v1/session.py b/neo4j/v1/session.py
index 14bcf199..a6adb5e2 100644
--- a/neo4j/v1/session.py
+++ b/neo4j/v1/session.py
@@ -43,7 +43,7 @@ class which can be used to obtain `Driver` instances that are used for
STATEMENT_TYPE_READ_ONLY = "r"
STATEMENT_TYPE_READ_WRITE = "rw"
STATEMENT_TYPE_WRITE_ONLY = "w"
-STATEMENT_TYPE_SCHEMA_WRITE = "sw"
+STATEMENT_TYPE_SCHEMA_WRITE = "s"
def basic_auth(user, password):
@@ -333,11 +333,11 @@ def __repr__(self):
@property
def contains_updates(self):
- return self.nodes_created or self.nodes_deleted or \
+ return bool(self.nodes_created or self.nodes_deleted or \
self.relationships_created or self.relationships_deleted or \
self.properties_set or self.labels_added or self.labels_removed or \
self.indexes_added or self.indexes_removed or \
- self.constraints_added or self.constraints_removed
+ self.constraints_added or self.constraints_removed)
#: A plan describes how the database will execute your statement.
diff --git a/test/tck/environment.py b/test/tck/environment.py
index bbe8646f..3bbb7bae 100644
--- a/test/tck/environment.py
+++ b/test/tck/environment.py
@@ -20,9 +20,7 @@
from test.tck import tck_util
-
-def before_all(context):
- context.config.setup_logging()
+failing_features = {}
def before_feature(context, feature):
@@ -30,12 +28,38 @@ def before_feature(context, feature):
for scenario in feature.scenarios:
scenario.tags += feature.tags
+
def before_scenario(context, scenario):
+ context.runners = []
if "reset_database" in scenario.tags:
- tck_util.send_string("MATCH (n) DETACH DELETE n")
+ session = tck_util.driver.session()
+ session.run("MATCH (n) DETACH DELETE n")
+ session.close()
+ if "equality_test" in scenario.tags:
+ context.values = {}
+
+
+def after_feature(context, feature):
+ failed_scenarios = []
+ for scenario in feature.scenarios:
+ if scenario.status == "untested" or scenario.status == "failed" :
+ failed_scenarios.append(scenario.name)
+ if len(failed_scenarios) > 0:
+ failing_features[feature.name] = failed_scenarios
+
+
+def after_all(context):
+ if len(failing_features) != 0:
+ print("Following Features failed in TCK:")
+ for feature, list_of_scenarios in failing_features.items():
+ print("Feature: %s" %feature)
+ for scenario in list_of_scenarios:
+ print("Failing scenario: %s" % scenario)
+ raise Exception("\tTCK FAILED!")
def after_scenario(context, scenario):
- if scenario.status != "passed":
- raise Exception("%s did not pass" %scenario)
+ pass
+ for runner in tck_util.runners:
+ runner.close()
diff --git a/test/tck/resultparser.py b/test/tck/resultparser.py
index cb198bc6..44a30590 100644
--- a/test/tck/resultparser.py
+++ b/test/tck/resultparser.py
@@ -181,11 +181,13 @@ def get_path(string_path):
string_path = string_path[1:-1]
n, string_path = get_node(string_path)
list_of_nodes_and_rel = [n]
- n.id = ++id
+ id+=1
+ n.id = id
while string_path != '':
r, string_path, point_up = get_relationship(string_path)
n, string_path = get_node(string_path)
- n.id = ++id
+ id+=1
+ n.id = id
if point_up:
r.start = list_of_nodes_and_rel[-1].id
r.end = n.id
diff --git a/test/tck/steps/bolt_compability_steps.py b/test/tck/steps/bolt_compability_steps.py
index ea2b47f6..e56af162 100644
--- a/test/tck/steps/bolt_compability_steps.py
+++ b/test/tck/steps/bolt_compability_steps.py
@@ -24,8 +24,9 @@
from behave import *
+from test.tck import tck_util
from test.tck.resultparser import parse_values
-from test.tck.tck_util import to_unicode, Type, send_string, send_parameters, string_to_type
+from test.tck.tck_util import to_unicode, Type, string_to_type
from neo4j.v1 import compat
use_step_matcher("re")
@@ -33,7 +34,9 @@
@given("A running database")
def step_impl(context):
- send_string("RETURN 1")
+ session = tck_util.driver.session()
+ session.run("RETURN 1")
+ session.close()
@given("a value (?P.+)")
@@ -80,15 +83,20 @@ def step_impl(context, size, type):
@when("the driver asks the server to echo this (?P.+) back")
def step_impl(context, unused):
- context.results = {"as_string": send_string("RETURN " + as_cypher_text(context.expected)),
- "as_parameters": send_parameters("RETURN {input}", {'input': context.expected})}
+ str_runner = tck_util.Runner("RETURN " + as_cypher_text(context.expected)).run()
+ param_runner = tck_util.Runner("RETURN {input}", {'input': context.expected}).run()
+ context.runners += [str_runner, param_runner]
+ context.results = [str_runner.result, param_runner.result]
@step("the value given in the result should be the same as what was sent")
def step_impl(context):
assert len(context.results) > 0
- for result in context.results.values():
- result_value = result[0].values()[0]
+ for result in context.results:
+ records = list(result)
+ assert len(records) == 1
+ assert len(records[0].values()) == 1
+ result_value = records[0].values()[0]
assert result_value == context.expected
diff --git a/test/tck/steps/cypher_compability_steps.py b/test/tck/steps/cypher_compability_steps.py
index db21f58e..42055160 100644
--- a/test/tck/steps/cypher_compability_steps.py
+++ b/test/tck/steps/cypher_compability_steps.py
@@ -20,6 +20,7 @@
from behave import *
+from test.tck import tck_util
from test.tck.tck_util import TestValue, send_string, send_parameters
from test.tck.resultparser import parse_values, parse_values_to_comparable
@@ -28,31 +29,38 @@
@given("init: (?P.+)")
def step_impl(context, statement):
- send_string(statement)
+ session = tck_util.driver.session()
+ session.run(statement)
+ session.close()
-@when("running: (?P.+)")
+@step("running: (?P.+)")
def step_impl(context, statement):
- context.results = {"as_string": send_string(statement)}
+ runner = tck_util.Runner(statement).run()
+ context.runners.append(runner)
+ context.results = [runner.result]
-@then("result")
-def step_impl(context):
- result = context.results["as_string"]
- given = driver_result_to_comparable_result(result)
- expected = table_to_comparable_result(context.table)
- if not unordered_equal(given, expected):
- raise Exception("Does not match given: \n%s expected: \n%s" % (given, expected))
-
-
-@when('running parametrized: (?P.+)')
+@step('running parametrized: (?P.+)')
def step_impl(context, statement):
assert len(context.table.rows) == 1
keys = context.table.headings
values = context.table.rows[0]
parameters = {keys[i]: parse_values(values[i]) for i in range(len(keys))}
+ runner = tck_util.Runner(statement, parameters).run()
+ context.runners.append(runner)
+ context.results = [runner.result]
- context.results = {"as_string": send_parameters(statement, parameters)}
+
+@then("result")
+def step_impl(context):
+ expected = table_to_comparable_result(context.table)
+ assert(len(context.results) > 0)
+ for result in context.results:
+ records = list(result)
+ given = driver_result_to_comparable_result(records)
+ if not unordered_equal(given, expected):
+ raise Exception("Does not match given: \n%s expected: \n%s" % (given, expected))
def _driver_value_to_comparable(val):
diff --git a/test/tck/steps/driver_equality_steps.py b/test/tck/steps/driver_equality_steps.py
new file mode 100644
index 00000000..98c0b602
--- /dev/null
+++ b/test/tck/steps/driver_equality_steps.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+
+# Copyright (c) 2002-2016 "Neo Technology,"
+# Network Engine for Objects in Lund AB [http://neotechnology.com]
+#
+# This file is part of Neo4j.
+#
+# Licensed 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.
+
+from behave import *
+
+from test.tck.tck_util import send_string
+
+use_step_matcher("re")
+
+
+@step("`(?P.+)` is single value result of: (?P.+)")
+def step_impl(context, key, statement):
+ runner = send_string(statement)
+ records = list(runner.result)
+ assert len(records) == 1
+ assert len(records[0]) == 1
+ context.values[key] = records[0][0]
+
+
+@step("saved values should all equal")
+def step_impl(context):
+ values = list(context.values.values())
+ assert len(values) > 1
+ first_val = values.pop()
+ for item in values:
+ assert item == first_val
+
+
+@step("none of the saved values should be equal")
+def step_impl(context):
+ values = list(context.values.values())
+ assert len(values) > 1
+ first_val = values.pop()
+ for item in values:
+ assert item != first_val
diff --git a/test/tck/steps/driver_result_api_steps.py b/test/tck/steps/driver_result_api_steps.py
new file mode 100644
index 00000000..315701b5
--- /dev/null
+++ b/test/tck/steps/driver_result_api_steps.py
@@ -0,0 +1,162 @@
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+
+# Copyright (c) 2002-2016 "Neo Technology,"
+# Network Engine for Objects in Lund AB [http://neotechnology.com]
+#
+# This file is part of Neo4j.
+#
+# Licensed 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.
+
+from behave import *
+
+from neo4j.v1 import STATEMENT_TYPE_READ_ONLY, STATEMENT_TYPE_READ_WRITE, STATEMENT_TYPE_WRITE_ONLY, \
+ STATEMENT_TYPE_SCHEMA_WRITE
+
+from test.tck.resultparser import parse_values
+
+use_step_matcher("re")
+
+
+@step("the `Statement Result` is consumed a `Result Summary` is returned")
+def step_impl(context):
+ context.summaries = [x.consume() for x in context.results]
+ assert context.summaries[0] is not None
+
+
+@then("the `Statement Result` is closed")
+def step_impl(context):
+ for result in context.results:
+ assert result.connection is None
+
+
+@step("I request a `Statement` from the `Result Summary`")
+def step_impl(context):
+ context.statements = []
+ for summary in context.summaries:
+ context.statements.append(summary.statement)
+
+
+@then("requesting the `Statement` as text should give: (?P.+)")
+def step_impl(context, expected):
+ for statement in context.statements:
+ assert statement == expected
+
+
+@step("requesting the `Statement` parameter should give: (?P.+)")
+def step_impl(context, expected):
+ for summary in context.summaries:
+ assert summary.parameters == parse_values(expected)
+
+
+@step("requesting `Counters` from `Result Summary` should give")
+def step_impl(context):
+ for summary in context.summaries:
+ for row in context.table:
+ print(row[0].replace(" ","_"))
+ print(getattr(summary.counters, row[0].replace(" ","_")))
+ assert getattr(summary.counters, row[0].replace(" ","_")) == parse_values(row[1])
+
+
+@step("requesting the `Statement Type` should give (?P.+)")
+def step_impl(context, expected):
+ for summary in context.summaries:
+ if expected == "read only":
+ statement_type = STATEMENT_TYPE_READ_ONLY
+ elif expected == "read write":
+ statement_type = STATEMENT_TYPE_READ_WRITE
+ elif expected == "write only":
+ statement_type = STATEMENT_TYPE_WRITE_ONLY
+ elif expected == "schema write":
+ statement_type = STATEMENT_TYPE_SCHEMA_WRITE
+ else:
+ raise ValueError("Not recognisable statement type: %s" % expected)
+ assert summary.statement_type == statement_type
+
+
+@step("the `Result Summary` has a `Plan`")
+def step_impl(context):
+ for summary in context.summaries:
+ assert summary.plan is not None
+
+
+@step("the `Result Summary` has a `Profile`")
+def step_impl(context):
+ for summary in context.summaries:
+ assert summary.profile is not None
+
+
+@step("the `Result Summary` does not have a `Plan`")
+def step_impl(context):
+ for summary in context.summaries:
+ assert summary.plan is None
+
+
+@step("the `Result Summary` does not have a `Profile`")
+def step_impl(context):
+ for summary in context.summaries:
+ assert summary.profile is None
+
+
+@step("requesting the `(?P.+)` it contains")
+def step_impl(context, plan_type):
+ for summary in context.summaries:
+ if plan_type == "Plan":
+ plan = summary.plan
+ elif plan_type == "Profile":
+ plan = summary.profile
+ else:
+ raise ValueError("Expected 'plan' or 'profile'. Got: %s" % plan_type)
+ for row in context.table:
+ attr = row[0].replace(" ", "_")
+ if attr == 'records':
+ attr = 'rows'
+ assert getattr(plan, attr) == parse_values(row[1])
+
+
+@step("the `(?P.+)` also contains method calls for")
+def step_impl(context, plan_type):
+ for summary in context.summaries:
+ if plan_type == "Plan":
+ plan = summary.plan
+ elif plan_type == "Profile":
+ plan = summary.profile
+ else:
+ raise ValueError("Expected 'plan' or 'profile'. Got: %s" % plan_type)
+ for row in context.table:
+ assert getattr(plan, row[0].replace(" ", "_")) is not None
+
+
+@step("the `Result Summary` `Notifications` is empty")
+def step_impl(context):
+ for summary in context.summaries:
+ assert len(summary.notifications) == 0
+
+
+@step("the `Result Summary` `Notifications` has one notification with")
+def step_impl(context):
+
+ for summary in context.summaries:
+ assert len(summary.notifications) == 1
+ notification = summary.notifications[0]
+ for row in context.table:
+ if row[0] == 'position':
+ position = getattr(notification, row[0].replace(" ","_"))
+ expected_position = parse_values(row[1])
+ for position_key, value in expected_position.items():
+ assert value == getattr(position, position_key.replace(" ", "_"))
+ else:
+ assert getattr(notification, row[0].replace(" ","_")) == parse_values(row[1])
+
+
+
diff --git a/test/tck/tck_util.py b/test/tck/tck_util.py
index 186d6778..cce50c32 100644
--- a/test/tck/tck_util.py
+++ b/test/tck/tck_util.py
@@ -22,24 +22,20 @@
from neo4j.v1 import GraphDatabase, Relationship, Node, Path, basic_auth
from neo4j.v1.compat import string
-
driver = GraphDatabase.driver("bolt://localhost", auth=basic_auth("neo4j", "password"), encrypted=False)
+runners = []
-def send_string(text):
- session = driver.session()
- result = session.run(text)
- records = list(result)
- session.close()
- return records
+def send_string(statement):
+ runner = Runner(statement).run()
+ runners.append(runner)
+ return runner
def send_parameters(statement, parameters):
- session = driver.session()
- result = session.run(statement, parameters)
- records = list(result)
- session.close()
- return records
+ runner = Runner(statement, parameters).run()
+ runners.append(runner)
+ return runner
try:
@@ -81,6 +77,20 @@ class Type:
NULL = "Null"
+class Runner:
+ def __init__(self, statement, parameter=None):
+ self.session = driver.session()
+ self.statement = statement
+ self.parameter = parameter
+ self.result = None
+
+ def run(self):
+ self.result = self.session.run(self.statement, self.parameter)
+ return self
+
+ def close(self):
+ self.session.close()
+
class TestValue:
content = None