From 5d13f315978f5d4defb1bb3e7fe4f3aa5b68d48f Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Thu, 17 Dec 2020 16:53:49 +0000 Subject: [PATCH 01/16] Add -very- basic BDD infrastructure --- BUILD | 2 +- behaviour/BUILD | 16 +++++++ behaviour/environment.py | 17 ++++++++ behaviour/steps/transaction_steps.py | 37 ++++++++++++++++ behaviour/transaction.feature | 10 +++++ requirements.txt | 14 +++++- tools/BUILD | 0 tools/behave_rule.bzl | 64 ++++++++++++++++++++++++++++ 8 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 behaviour/BUILD create mode 100644 behaviour/environment.py create mode 100644 behaviour/steps/transaction_steps.py create mode 100644 behaviour/transaction.feature create mode 100644 tools/BUILD create mode 100644 tools/behave_rule.bzl diff --git a/BUILD b/BUILD index 9431eadb..5bb18918 100644 --- a/BUILD +++ b/BUILD @@ -43,7 +43,7 @@ py_library( graknlabs_client_python_requirement("grpcio"), graknlabs_client_python_requirement("six"), ], - visibility =["//visibility:public"] + visibility = ["//visibility:public"] ) checkstyle_test( diff --git a/behaviour/BUILD b/behaviour/BUILD new file mode 100644 index 00000000..76e77c1c --- /dev/null +++ b/behaviour/BUILD @@ -0,0 +1,16 @@ +load("//tools:behave_rule.bzl", "py_cucumber_test") + +py_cucumber_test( + name = "tutorial", + feats = [ + "transaction.feature", + ], + steps = [ + "environment.py", # TODO: this should probably be a separate parameter? + "steps/transaction_steps.py", + ], + deps = [ + "//:client_python", + ], + size = "small", +) diff --git a/behaviour/environment.py b/behaviour/environment.py new file mode 100644 index 00000000..183bff68 --- /dev/null +++ b/behaviour/environment.py @@ -0,0 +1,17 @@ +from behave import * + +from grakn.client import GraknClient + + +def before_scenario(context, scenario): + context.client = GraknClient() + for database in context.client.databases().all(): + context.client.databases().delete(database) + context.sessions = [] + context.sessions_to_transactions = {} + + +def after_scenario(context, scenario): + for session in context.sessions: + session.close() + context.client.close() diff --git a/behaviour/steps/transaction_steps.py b/behaviour/steps/transaction_steps.py new file mode 100644 index 00000000..17152f2a --- /dev/null +++ b/behaviour/steps/transaction_steps.py @@ -0,0 +1,37 @@ +from behave import * + +from grakn.rpc.session import SessionType +from grakn.rpc.transaction import TransactionType + + +@step("connection does not have any database") +def step_impl(context): + assert len(context.client.databases().all()) == 0 + + +@step("connection create database: {database_name}") +def step_impl(context, database_name: str): + context.client.databases().create(database_name) + + +@step("connection open data session for database: {database_name}") +@step("connection open session for database: {database_name}") +def step_impl(context, database_name: str): + context.sessions.append(context.client.session(database_name, SessionType.DATA)) + + +@step("session opens transaction of type: {transaction_type}") +def step_impl(context, transaction_type): # TODO transaction_type should parse to type TransactionType by itself ideally + transaction_type = TransactionType.READ if transaction_type == "read" else "write" + for session in context.sessions: + transactions = [] + transactions.append(session.transaction(transaction_type)) + context.sessions_to_transactions[session] = transactions + + +@step("session transaction is open: {is_open}") +def step_impl(context, is_open): # TODO is_open should parse to boolean by itself + is_open = (is_open == "true") + for session in context.sessions: + for transaction in context.sessions_to_transactions[session]: + assert transaction.is_open() == is_open diff --git a/behaviour/transaction.feature b/behaviour/transaction.feature new file mode 100644 index 00000000..ef6fb9bd --- /dev/null +++ b/behaviour/transaction.feature @@ -0,0 +1,10 @@ +Feature: Connection Transaction + + Background: + Given connection does not have any database + + Scenario: one database, one session, one transaction to read + When connection create database: grakn + Given connection open session for database: grakn + When session opens transaction of type: read + Then session transaction is open: true diff --git a/requirements.txt b/requirements.txt index 1d655599..db1fca36 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,8 +17,20 @@ # under the License. # -# --extra-index-url https://repo.grakn.ai/repository/pypi-snapshot/simple + +## Configuration options + +# --extra-index-url https://repo.grakn.ai/repository/pypi-snapshot/simple # Allow importing of snapshots + + +## Dependencies + graknprotocol==2.0.0a3 grpcio==1.33.2 protobuf==3.6.1 six>=1.11.0 + + +# Dev dependencies (not required to use the grakn-client package, but necessary for its development) + +behave==1.2.6 diff --git a/tools/BUILD b/tools/BUILD new file mode 100644 index 00000000..e69de29b diff --git a/tools/behave_rule.bzl b/tools/behave_rule.bzl new file mode 100644 index 00000000..3b0178ee --- /dev/null +++ b/tools/behave_rule.bzl @@ -0,0 +1,64 @@ +# ============================================================================= +# Description: Adds a test rule for the BDD tool behave to the bazel rule set. +# Knowledge: +# * https://bazel.build/versions/master/docs/skylark/cookbook.html +# * https://bazel.build/versions/master/docs/skylark/rules.html +# * https://bazel.build/versions/master/docs/skylark/lib/ctx.html +# * http://pythonhosted.org/behave/gherkin.html +# ============================================================================= + +# TODO: this should probably live in graknlabs_dependencies +""" +Private implementation of the rule py_cucumber_test. +""" +def _rule_implementation(ctx): + + # Store the path of the first feature file + features_dir = ctx.files.feats[0].dirname + + # We want a test target so make it create an executable output. + # https://bazel.build/versions/master/docs/skylark/rules.html#test-rules + ctx.actions.write( + # Access the executable output file using ctx.outputs.executable. + output=ctx.outputs.executable, + content="behave %s" % features_dir, + is_executable=True + ) + # The executable output is added automatically to this target. + + # Add the feature and step files for behave to the runfiles. + # https://bazel.build/versions/master/docs/skylark/rules.html#runfiles + return [DefaultInfo( + # Create runfiles from the files specified in the data attribute. + # The shell executable - the output of this rule - can use them at runtime. + # It is also possible to define data_runfiles and default_runfiles. + # However if runfiles is specified it's not possible to define the above + # ones since runfiles sets them both. + runfiles = ctx.runfiles(files = ctx.files.feats + ctx.files.steps + ctx.files.deps) + )] + +""" +An example documentation. + +Args: + name: + A unique name for this rule. + feats: + Feature files used to run this target. + steps: + Files containing the mapping of feature steps to actual system API calls. + Note: Since this rule implicitely uses the BDD tool "behave" they must +be in the "steps" folder (https://pythonhosted.org/behave/gherkin.html). + deps: + System to test. +""" +py_cucumber_test = rule( + implementation=_rule_implementation, + attrs={ + # Do not declare "name": It is added automatically. + "feats": attr.label_list(allow_files=True), + "steps": attr.label_list(allow_files=True), + "deps": attr.label_list(mandatory=True,allow_empty=False), + }, + test=True, +) From 2ebfe78579786e061fcd2b0e085800d9efad6919 Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Thu, 17 Dec 2020 16:55:58 +0000 Subject: [PATCH 02/16] rename tutorial to transaction_test --- behaviour/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/behaviour/BUILD b/behaviour/BUILD index 76e77c1c..80f26dd8 100644 --- a/behaviour/BUILD +++ b/behaviour/BUILD @@ -1,7 +1,7 @@ load("//tools:behave_rule.bzl", "py_cucumber_test") py_cucumber_test( - name = "tutorial", + name = "transaction_test", feats = [ "transaction.feature", ], From 5d843836aa743f53c8236f4c41b9b8a8a93b3edc Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Wed, 30 Dec 2020 15:13:21 +0000 Subject: [PATCH 03/16] restructure BDD folders --- {behaviour => test/behaviour}/BUILD | 0 {behaviour => test/behaviour}/environment.py | 0 {behaviour => test/behaviour}/steps/transaction_steps.py | 0 {behaviour => test/behaviour}/transaction.feature | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename {behaviour => test/behaviour}/BUILD (100%) rename {behaviour => test/behaviour}/environment.py (100%) rename {behaviour => test/behaviour}/steps/transaction_steps.py (100%) rename {behaviour => test/behaviour}/transaction.feature (100%) diff --git a/behaviour/BUILD b/test/behaviour/BUILD similarity index 100% rename from behaviour/BUILD rename to test/behaviour/BUILD diff --git a/behaviour/environment.py b/test/behaviour/environment.py similarity index 100% rename from behaviour/environment.py rename to test/behaviour/environment.py diff --git a/behaviour/steps/transaction_steps.py b/test/behaviour/steps/transaction_steps.py similarity index 100% rename from behaviour/steps/transaction_steps.py rename to test/behaviour/steps/transaction_steps.py diff --git a/behaviour/transaction.feature b/test/behaviour/transaction.feature similarity index 100% rename from behaviour/transaction.feature rename to test/behaviour/transaction.feature From 526d0aab03a9fb89a1311f4e0e04f376cdfce0fd Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Wed, 30 Dec 2020 15:13:53 +0000 Subject: [PATCH 04/16] Fix process hanging for 5 seconds after completion --- grakn/rpc/session.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/grakn/rpc/session.py b/grakn/rpc/session.py index a39f0132..55726db7 100644 --- a/grakn/rpc/session.py +++ b/grakn/rpc/session.py @@ -52,10 +52,10 @@ def __init__(self, client, database: str, session_type: SessionType, options=Gra self._session_id = self._grpc_stub.session_open(open_req).session_id self._is_open = True - self._scheduler.enter(delay=self._PULSE_FREQUENCY_SECONDS, priority=1, action=self._transmit_pulse, argument=()) + self._pulse = self._scheduler.enter(delay=self._PULSE_FREQUENCY_SECONDS, priority=1, action=self._transmit_pulse, argument=()) # TODO: This thread blocks the process from closing. We should try cancelling the scheduled task when the # session closes. If that doesn't work, we need some other way of getting the thread to exit. - Thread(target=self._scheduler.run).start() + Thread(target=self._scheduler.run, daemon=True).start() def transaction(self, transaction_type: TransactionType, options=GraknOptions()): return Transaction(self._channel, self._session_id, transaction_type, options) @@ -67,6 +67,8 @@ def is_open(self): return self._is_open def close(self): if self._is_open: self._is_open = False + self._scheduler.cancel(self._pulse) + self._scheduler.empty() req = session_proto.Session.Close.Req() req.session_id = self._session_id self._grpc_stub.session_close(req) @@ -80,8 +82,8 @@ def _transmit_pulse(self): pulse_req.session_id = self._session_id res = self._grpc_stub.session_pulse(pulse_req) if res.alive: - self._scheduler.enter(delay=self._PULSE_FREQUENCY_SECONDS, priority=1, action=self._transmit_pulse, argument=()) - Thread(target=self._scheduler.run).start() + self._pulse = self._scheduler.enter(delay=self._PULSE_FREQUENCY_SECONDS, priority=1, action=self._transmit_pulse, argument=()) + Thread(target=self._scheduler.run, daemon=True).start() def __enter__(self): return self From 674ac66d66c868431e0f2b9585d4047764e50cc5 Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Wed, 30 Dec 2020 16:25:22 +0000 Subject: [PATCH 05/16] Try taking the BDD steps out of the steps folder --- test/behaviour/BUILD | 19 +- test/behaviour/connection/transaction/BUILD | 25 + .../transaction/transaction.feature | 586 ++++++++++++++++++ .../transaction}/transaction_steps.py | 0 test/behaviour/transaction.feature | 10 - tools/behave_rule.bzl | 8 +- 6 files changed, 622 insertions(+), 26 deletions(-) create mode 100644 test/behaviour/connection/transaction/BUILD create mode 100644 test/behaviour/connection/transaction/transaction.feature rename test/behaviour/{steps => connection/transaction}/transaction_steps.py (100%) delete mode 100644 test/behaviour/transaction.feature diff --git a/test/behaviour/BUILD b/test/behaviour/BUILD index 80f26dd8..bec0fbce 100644 --- a/test/behaviour/BUILD +++ b/test/behaviour/BUILD @@ -1,16 +1,7 @@ -load("//tools:behave_rule.bzl", "py_cucumber_test") +package(default_visibility = ["//test/behaviour:__subpackages__"]) -py_cucumber_test( - name = "transaction_test", - feats = [ - "transaction.feature", - ], - steps = [ - "environment.py", # TODO: this should probably be a separate parameter? - "steps/transaction_steps.py", - ], - deps = [ - "//:client_python", - ], - size = "small", +py_library( + name = "background", + srcs = ["environment.py"], + deps = [], ) diff --git a/test/behaviour/connection/transaction/BUILD b/test/behaviour/connection/transaction/BUILD new file mode 100644 index 00000000..a2666172 --- /dev/null +++ b/test/behaviour/connection/transaction/BUILD @@ -0,0 +1,25 @@ +package(default_visibility = ["//test/behaviour:__subpackages__"]) +load("//tools:behave_rule.bzl", "py_cucumber_test") + +py_library( + name = "steps", + srcs = ["transaction_steps.py"], + deps = [], +) + +py_cucumber_test( + name = "test", + feats = [ + "transaction.feature", + ], + background = [ + "//test/behaviour:background", + ], + steps = [ + ":steps", + ], + deps = [ + "//:client_python", + ], + size = "small", +) diff --git a/test/behaviour/connection/transaction/transaction.feature b/test/behaviour/connection/transaction/transaction.feature new file mode 100644 index 00000000..65176e9e --- /dev/null +++ b/test/behaviour/connection/transaction/transaction.feature @@ -0,0 +1,586 @@ +# +# Copyright (C) 2020 Grakn Labs +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +#noinspection CucumberUndefinedStep +Feature: Connection Transaction + + Background: + Given connection has been opened + Given connection does not have any database + + Scenario: one database, one session, one transaction to read + When connection create database: grakn + Given connection open session for database: grakn + When session opens transaction of type: read + Then session transaction is null: false + Then session transaction is open: true + Then session transaction has type: read + + Scenario: one database, one session, one transaction to write + When connection create database: grakn + Given connection open session for database: grakn + When session opens transaction of type: write + Then session transaction is null: false + Then session transaction is open: true + Then session transaction has type: write + + Scenario: one database, one session, one committed write transaction is closed + When connection create database: grakn + Given connection open session for database: grakn + When session opens transaction of type: write + Then session transaction commits + Then session transaction commits; throws exception + + Scenario: one database, one session, re-committing transaction throws + When connection create database: grakn + Given connection open session for database: grakn + When for each session, open transaction of type: write + Then for each session, transaction commits + Then for each session, transaction commits; throws exception + + Scenario: one database, one session, transaction close is idempotent + When connection create database: grakn + Given connection open session for database: grakn + When for each session, open transaction of type: write + Then for each session, transaction closes + Then for each session, transaction is open: false + Then for each session, transaction closes + Then for each session, transaction is open: false + + @ignore-grakn-core + Scenario: one database, one session, many transactions to read + When connection create database: grakn + Given connection open session for database: grakn + When for each session, open transactions of type: + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + Then for each session, transactions are null: false + Then for each session, transactions are open: true + Then for each session, transactions have type: + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + + @ignore-grakn-core + Scenario: one database, one session, many transactions to write + When connection create database: grakn + Given connection open session for database: grakn + When for each session, open transactions of type: + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + Then for each session, transactions are null: false + Then for each session, transactions are open: true + Then for each session, transactions have type: + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + + @ignore-grakn-core + Scenario: one database, one session, many transactions to read and write + When connection create database: grakn + Given connection open session for database: grakn + When for each session, open transactions of type: + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + Then for each session, transactions are null: false + Then for each session, transactions are open: true + Then for each session, transactions have type: + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + + Scenario: one database, one session, many transactions in parallel to read + When connection create database: grakn + Given connection open session for database: grakn + When for each session, open transactions in parallel of type: + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + Then for each session, transactions in parallel are null: false + Then for each session, transactions in parallel are open: true + Then for each session, transactions in parallel have type: + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + + Scenario: one database, one session, many transactions in parallel to write + When connection create database: grakn + Given connection open session for database: grakn + When for each session, open transactions in parallel of type: + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + Then for each session, transactions in parallel are null: false + Then for each session, transactions in parallel are open: true + Then for each session, transactions in parallel have type: + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + + Scenario: one database, one session, many transactions in parallel to read and write + When connection create database: grakn + Given connection open session for database: grakn + When for each session, open transactions in parallel of type: + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + Then for each session, transactions in parallel are null: false + Then for each session, transactions in parallel are open: true + Then for each session, transactions in parallel have type: + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + + Scenario: one database, many sessions, one transaction to read + When connection create database: grakn + Given connection open sessions for database: + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + When for each session, open transaction of type: read + Then for each session, transaction is null: false + Then for each session, transaction is open: true + Then for each session, transaction has type: read + + Scenario: one database, many sessions, one transaction to write + When connection create database: grakn + Given connection open sessions for database: + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + When for each session, open transaction of type: write + Then for each session, transaction is null: false + Then for each session, transaction is open: true + Then for each session, transaction has type: write + + @ignore-grakn-core + Scenario: one database, many sessions, many transactions to read + When connection create database: grakn + Given connection open sessions for database: + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + When for each session, open transactions of type: + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + Then for each session, transactions are null: false + Then for each session, transactions are open: true + Then for each session, transactions have type: + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + + @ignore-grakn-core + Scenario: one database, many sessions, many transactions to write + When connection create database: grakn + Given connection open sessions for database: + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + When for each session, open transactions of type: + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + Then for each session, transactions are null: false + Then for each session, transactions are open: true + Then for each session, transactions have type: + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + + @ignore-grakn-core + Scenario: one database, many sessions, many transactions to read and write + When connection create database: grakn + Given connection open sessions for database: + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + When for each session, open transactions of type: + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + Then for each session, transactions are null: false + Then for each session, transactions are open: true + Then for each session, transactions have type: + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + + Scenario: one database, many sessions, many transactions in parallel to read + When connection create database: grakn + Given connection open sessions for database: + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + When for each session, open transactions in parallel of type: + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + Then for each session, transactions in parallel are null: false + Then for each session, transactions in parallel are open: true + Then for each session, transactions in parallel have type: + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + | read | + + Scenario: one database, many sessions, many transactions in parallel to write + When connection create database: grakn + Given connection open sessions for database: + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + When for each session, open transactions in parallel of type: + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + Then for each session, transactions in parallel are null: false + Then for each session, transactions in parallel are open: true + Then for each session, transactions in parallel have type: + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + | write | + + Scenario: one database, many sessions, many transactions in parallel to read and write + When connection create database: grakn + Given connection open sessions for database: + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + | grakn | + When for each session, open transactions in parallel of type: + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + Then for each session, transactions in parallel are null: false + Then for each session, transactions in parallel are open: true + Then for each session, transactions in parallel have type: + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + | read | + | write | + +# Scenario: one database, many sessions in parallel, one transactions to read +# +# Scenario: one database, many sessions in parallel, one transactions to write +# +# Scenario: one database, many sessions in parallel, many transactions to read +# +# Scenario: one database, many sessions in parallel, many transactions to write +# +# Scenario: one database, many sessions in parallel, many transactions in parallel to read +# +# Scenario: one database, many sessions in parallel, many transactions in parallel to write + + + Scenario: write in a read transaction throws + When connection create database: grakn + Given connection open schema session for database: grakn + When session opens transaction of type: read + Then graql define; throws exception containing "is read only" + """ + define person sub entity; + """ diff --git a/test/behaviour/steps/transaction_steps.py b/test/behaviour/connection/transaction/transaction_steps.py similarity index 100% rename from test/behaviour/steps/transaction_steps.py rename to test/behaviour/connection/transaction/transaction_steps.py diff --git a/test/behaviour/transaction.feature b/test/behaviour/transaction.feature deleted file mode 100644 index ef6fb9bd..00000000 --- a/test/behaviour/transaction.feature +++ /dev/null @@ -1,10 +0,0 @@ -Feature: Connection Transaction - - Background: - Given connection does not have any database - - Scenario: one database, one session, one transaction to read - When connection create database: grakn - Given connection open session for database: grakn - When session opens transaction of type: read - Then session transaction is open: true diff --git a/tools/behave_rule.bzl b/tools/behave_rule.bzl index 3b0178ee..979a538f 100644 --- a/tools/behave_rule.bzl +++ b/tools/behave_rule.bzl @@ -24,6 +24,7 @@ def _rule_implementation(ctx): content="behave %s" % features_dir, is_executable=True ) + # The executable output is added automatically to this target. # Add the feature and step files for behave to the runfiles. @@ -34,7 +35,9 @@ def _rule_implementation(ctx): # It is also possible to define data_runfiles and default_runfiles. # However if runfiles is specified it's not possible to define the above # ones since runfiles sets them both. - runfiles = ctx.runfiles(files = ctx.files.feats + ctx.files.steps + ctx.files.deps) + runfiles = ctx.runfiles( + files = ctx.files.feats + ctx.files.background + ctx.files.steps + ctx.files.deps + ) )] """ @@ -57,7 +60,8 @@ py_cucumber_test = rule( attrs={ # Do not declare "name": It is added automatically. "feats": attr.label_list(allow_files=True), - "steps": attr.label_list(allow_files=True), + "steps": attr.label_list(), + "background": attr.label_list(), "deps": attr.label_list(mandatory=True,allow_empty=False), }, test=True, From fb5a22e347e58664f19f9b3465a2b1f347f4e215 Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Wed, 30 Dec 2020 18:57:22 +0000 Subject: [PATCH 06/16] Ensure behave has access to the BDD test steps --- test/behaviour/connection/transaction/BUILD | 12 +++---- tools/behave_rule.bzl | 40 +++++++++++---------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/test/behaviour/connection/transaction/BUILD b/test/behaviour/connection/transaction/BUILD index a2666172..652f2c98 100644 --- a/test/behaviour/connection/transaction/BUILD +++ b/test/behaviour/connection/transaction/BUILD @@ -1,5 +1,5 @@ package(default_visibility = ["//test/behaviour:__subpackages__"]) -load("//tools:behave_rule.bzl", "py_cucumber_test") +load("//tools:behave_rule.bzl", "py_behave_test") py_library( name = "steps", @@ -7,14 +7,10 @@ py_library( deps = [], ) -py_cucumber_test( +py_behave_test( name = "test", - feats = [ - "transaction.feature", - ], - background = [ - "//test/behaviour:background", - ], + feats = ["transaction.feature"], + background = ["//test/behaviour:background"], steps = [ ":steps", ], diff --git a/tools/behave_rule.bzl b/tools/behave_rule.bzl index 979a538f..39dae090 100644 --- a/tools/behave_rule.bzl +++ b/tools/behave_rule.bzl @@ -9,19 +9,27 @@ # TODO: this should probably live in graknlabs_dependencies """ -Private implementation of the rule py_cucumber_test. +Implementation of the rule py_behave_test. """ def _rule_implementation(ctx): - # Store the path of the first feature file - features_dir = ctx.files.feats[0].dirname + # behave requires a 'steps' folder to exist in the test root directory, which is the location of environment.py + steps_out_dir = ctx.files.background[0].dirname + "/steps" + + # Store the path of the first feature file. It is recommended to only have one feature file. + feats_dir = ctx.files.feats[0].dirname + + # TODO: If two step files have the same name, we should rename the second one to prevent conflict + cmd = "mkdir " + steps_out_dir + " && " + cmd += " && ".join(["cp %s %s" % (step_file.path, steps_out_dir) for step_file in ctx.files.steps]) + cmd += " && behave %s" % feats_dir # We want a test target so make it create an executable output. # https://bazel.build/versions/master/docs/skylark/rules.html#test-rules ctx.actions.write( # Access the executable output file using ctx.outputs.executable. output=ctx.outputs.executable, - content="behave %s" % features_dir, + content=cmd, is_executable=True ) @@ -30,18 +38,12 @@ def _rule_implementation(ctx): # Add the feature and step files for behave to the runfiles. # https://bazel.build/versions/master/docs/skylark/rules.html#runfiles return [DefaultInfo( - # Create runfiles from the files specified in the data attribute. - # The shell executable - the output of this rule - can use them at runtime. - # It is also possible to define data_runfiles and default_runfiles. - # However if runfiles is specified it's not possible to define the above - # ones since runfiles sets them both. - runfiles = ctx.runfiles( - files = ctx.files.feats + ctx.files.background + ctx.files.steps + ctx.files.deps - ) + # The shell executable - the output of this rule - can use these files at runtime. + runfiles = ctx.runfiles(files = ctx.files.feats + ctx.files.background + ctx.files.steps + ctx.files.deps) )] """ -An example documentation. +Documentation Args: name: @@ -50,18 +52,18 @@ Args: Feature files used to run this target. steps: Files containing the mapping of feature steps to actual system API calls. - Note: Since this rule implicitely uses the BDD tool "behave" they must -be in the "steps" folder (https://pythonhosted.org/behave/gherkin.html). + background: + The environment.py file containing test setup and tear-down methods. deps: System to test. """ -py_cucumber_test = rule( +py_behave_test = rule( implementation=_rule_implementation, attrs={ # Do not declare "name": It is added automatically. - "feats": attr.label_list(allow_files=True), - "steps": attr.label_list(), - "background": attr.label_list(), + "feats": attr.label_list(mandatory=True,allow_empty=False,allow_files=True), + "steps": attr.label_list(mandatory=True,allow_empty=False), + "background": attr.label_list(mandatory=True,allow_empty=False), "deps": attr.label_list(mandatory=True,allow_empty=False), }, test=True, From 885d1d7c22cdab16b29c6e308fdb795805bddd05 Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Wed, 30 Dec 2020 22:29:43 +0000 Subject: [PATCH 07/16] Add is_open to Client, expose GraknClientException --- grakn/client.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/grakn/client.py b/grakn/client.py index 7fa19424..3db52966 100644 --- a/grakn/client.py +++ b/grakn/client.py @@ -23,9 +23,10 @@ from grakn.rpc.database_manager import DatabaseManager as _DatabaseManager from grakn.rpc.session import Session as _Session, SessionType -# Repackaging these enums allows users to import everything they (most likely) need from "grakn.client" -from grakn.rpc.transaction import TransactionType # noqa # pylint: disable=unused-import +# Repackaging these symbols allows users to import everything they (most likely) need from "grakn.client" +from grakn.common.exception import GraknClientException # noqa # pylint: disable=unused-import from grakn.concept.type.attribute_type import ValueType # noqa # pylint: disable=unused-import +from grakn.rpc.transaction import TransactionType # noqa # pylint: disable=unused-import class GraknClient(object): @@ -34,6 +35,7 @@ class GraknClient(object): def __init__(self, address=DEFAULT_URI): self._channel = grpc.insecure_channel(address) self._databases = _DatabaseManager(self._channel) + self._is_open = True def session(self, database: str, session_type: SessionType, options=GraknOptions()): return _Session(self, database, session_type, options) @@ -43,6 +45,10 @@ def databases(self): def close(self): self._channel.close() + self._is_open = False + + def is_open(self): + return self._is_open def __enter__(self): return self From 6f3171159f71fae25b525ccbf586afcc1a66412f Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Wed, 30 Dec 2020 22:30:02 +0000 Subject: [PATCH 08/16] Make Graql Define, Undefine and Delete awaitable --- grakn/query/query_manager.py | 8 ++++---- grakn/rpc/stream.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/grakn/query/query_manager.py b/grakn/query/query_manager.py index 0428fdd6..a79888d3 100644 --- a/grakn/query/query_manager.py +++ b/grakn/query/query_manager.py @@ -51,28 +51,28 @@ def delete(self, query: str, options=GraknOptions()): delete_req = query_proto.Graql.Delete.Req() delete_req.query = query request.delete_req.CopyFrom(delete_req) - self._run_query(request, options) + return self._run_query(request, options) def define(self, query: str, options=GraknOptions()): request = query_proto.Query.Req() define_req = query_proto.Graql.Define.Req() define_req.query = query request.define_req.CopyFrom(define_req) - self._run_query(request, options) + return self._run_query(request, options) def undefine(self, query: str, options=GraknOptions()): request = query_proto.Query.Req() undefine_req = query_proto.Graql.Undefine.Req() undefine_req.query = query request.undefine_req.CopyFrom(undefine_req) - self._run_query(request, options) + return self._run_query(request, options) def _run_query(self, query_req: query_proto.Query.Req, options: GraknOptions): req = transaction_proto.Transaction.Req() query_req.options.CopyFrom(grakn_proto_builder.options(options)) req.query_req.CopyFrom(query_req) # Using stream makes this request asynchronous. - self._transaction._stream(req) + return self._transaction._stream(req) def _iterate_query(self, query_req: query_proto.Query.Req, response_reader: Callable[[transaction_proto.Transaction.Res], List], options: GraknOptions): req = transaction_proto.Transaction.Req() diff --git a/grakn/rpc/stream.py b/grakn/rpc/stream.py index 0fb214c4..52c4406f 100644 --- a/grakn/rpc/stream.py +++ b/grakn/rpc/stream.py @@ -31,7 +31,7 @@ class Stream(six.Iterator): _CONTINUE = "continue" _DONE = "done" - def __init__(self, transaction, request_id: str, transform_response: Callable[[transaction_proto.Transaction.Res], List] = None): + def __init__(self, transaction, request_id: str, transform_response: Callable[[transaction_proto.Transaction.Res], List] = lambda res: None): self._transaction = transaction self._request_id = request_id self._transform_response = transform_response From db95ffb5cbac0652bef2586213ca6dbcf355368d Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Wed, 30 Dec 2020 22:30:33 +0000 Subject: [PATCH 09/16] Implement all Transaction BDD steps --- test/behaviour/connection/BUILD | 7 + test/behaviour/connection/connection_steps.py | 11 + test/behaviour/connection/database/BUILD | 22 ++ .../connection/database/database_steps.py | 6 + test/behaviour/connection/session/BUILD | 22 ++ .../connection/session/session_steps.py | 9 + test/behaviour/connection/transaction/BUILD | 3 + .../transaction/transaction.feature | 3 +- .../transaction/transaction_steps.py | 223 ++++++++++++++++-- 9 files changed, 284 insertions(+), 22 deletions(-) create mode 100644 test/behaviour/connection/BUILD create mode 100644 test/behaviour/connection/connection_steps.py create mode 100644 test/behaviour/connection/database/BUILD create mode 100644 test/behaviour/connection/database/database_steps.py create mode 100644 test/behaviour/connection/session/BUILD create mode 100644 test/behaviour/connection/session/session_steps.py diff --git a/test/behaviour/connection/BUILD b/test/behaviour/connection/BUILD new file mode 100644 index 00000000..d184a243 --- /dev/null +++ b/test/behaviour/connection/BUILD @@ -0,0 +1,7 @@ +package(default_visibility = ["//test/behaviour:__subpackages__"]) + +py_library( + name = "steps", + srcs = ["connection_steps.py"], + deps = [], +) diff --git a/test/behaviour/connection/connection_steps.py b/test/behaviour/connection/connection_steps.py new file mode 100644 index 00000000..3fcf49d2 --- /dev/null +++ b/test/behaviour/connection/connection_steps.py @@ -0,0 +1,11 @@ +from behave import * + + +@step("connection has been opened") +def step_impl(context): + assert context.client and context.client.is_open() + + +@step("connection does not have any database") +def step_impl(context): + assert len(context.client.databases().all()) == 0 diff --git a/test/behaviour/connection/database/BUILD b/test/behaviour/connection/database/BUILD new file mode 100644 index 00000000..6b6f46d3 --- /dev/null +++ b/test/behaviour/connection/database/BUILD @@ -0,0 +1,22 @@ +package(default_visibility = ["//test/behaviour:__subpackages__"]) +load("//tools:behave_rule.bzl", "py_behave_test") + +py_library( + name = "steps", + srcs = ["database_steps.py"], + deps = [], +) + +#py_behave_test( +# name = "test", +# feats = ["transaction.feature"], +# background = ["//test/behaviour:background"], +# steps = [ +# ":steps", +# "//test/behaviour/connection:steps", +# ], +# deps = [ +# "//:client_python", +# ], +# size = "small", +#) diff --git a/test/behaviour/connection/database/database_steps.py b/test/behaviour/connection/database/database_steps.py new file mode 100644 index 00000000..ca724c59 --- /dev/null +++ b/test/behaviour/connection/database/database_steps.py @@ -0,0 +1,6 @@ +from behave import * + + +@step("connection create database: {database_name}") +def step_impl(context, database_name: str): + context.client.databases().create(database_name) diff --git a/test/behaviour/connection/session/BUILD b/test/behaviour/connection/session/BUILD new file mode 100644 index 00000000..95df5519 --- /dev/null +++ b/test/behaviour/connection/session/BUILD @@ -0,0 +1,22 @@ +package(default_visibility = ["//test/behaviour:__subpackages__"]) +load("//tools:behave_rule.bzl", "py_behave_test") + +py_library( + name = "steps", + srcs = ["session_steps.py"], + deps = [], +) + +#py_behave_test( +# name = "test", +# feats = ["transaction.feature"], +# background = ["//test/behaviour:background"], +# steps = [ +# ":steps", +# "//test/behaviour/connection:steps", +# ], +# deps = [ +# "//:client_python", +# ], +# size = "small", +#) diff --git a/test/behaviour/connection/session/session_steps.py b/test/behaviour/connection/session/session_steps.py new file mode 100644 index 00000000..530336b7 --- /dev/null +++ b/test/behaviour/connection/session/session_steps.py @@ -0,0 +1,9 @@ +from behave import * + +from grakn.rpc.session import SessionType + + +@step("connection open data session for database: {database_name}") +@step("connection open session for database: {database_name}") +def step_impl(context, database_name: str): + context.sessions.append(context.client.session(database_name, SessionType.DATA)) diff --git a/test/behaviour/connection/transaction/BUILD b/test/behaviour/connection/transaction/BUILD index 652f2c98..64abd822 100644 --- a/test/behaviour/connection/transaction/BUILD +++ b/test/behaviour/connection/transaction/BUILD @@ -13,6 +13,9 @@ py_behave_test( background = ["//test/behaviour:background"], steps = [ ":steps", + "//test/behaviour/connection:steps", + "//test/behaviour/connection/database:steps", + "//test/behaviour/connection/session:steps", ], deps = [ "//:client_python", diff --git a/test/behaviour/connection/transaction/transaction.feature b/test/behaviour/connection/transaction/transaction.feature index 65176e9e..1b598007 100644 --- a/test/behaviour/connection/transaction/transaction.feature +++ b/test/behaviour/connection/transaction/transaction.feature @@ -580,7 +580,8 @@ Feature: Connection Transaction When connection create database: grakn Given connection open schema session for database: grakn When session opens transaction of type: read - Then graql define; throws exception containing "is read only" + Then graql define """ define person sub entity; """ + Then transaction commits; throws exception containing "write transactions can be committed" diff --git a/test/behaviour/connection/transaction/transaction_steps.py b/test/behaviour/connection/transaction/transaction_steps.py index 17152f2a..e6a6c81d 100644 --- a/test/behaviour/connection/transaction/transaction_steps.py +++ b/test/behaviour/connection/transaction/transaction_steps.py @@ -1,37 +1,218 @@ +from concurrent.futures.thread import ThreadPoolExecutor +from typing import Callable + from behave import * -from grakn.rpc.session import SessionType -from grakn.rpc.transaction import TransactionType +from grakn.common.exception import GraknClientException +from grakn.rpc.transaction import TransactionType, Transaction -@step("connection does not have any database") -def step_impl(context): - assert len(context.client.databases().all()) == 0 +# TODO: this is implemented as open(s) in some clients - get rid of that, simplify them +@step("session opens transaction of type: {transaction_type}") +def step_impl(context, transaction_type): # TODO transaction_type should parse to type TransactionType by itself ideally + transaction_type = TransactionType.READ if transaction_type == "read" else TransactionType.WRITE + for session in context.sessions: + transactions = [] + transactions.append(session.transaction(transaction_type)) + context.sessions_to_transactions[session] = transactions -@step("connection create database: {database_name}") -def step_impl(context, database_name: str): - context.client.databases().create(database_name) +@step("for each session, open transaction of type:") +@step("for each session, open transactions of type:") +def step_impl(context): + for session in context.sessions: + transactions = [] + for row in context.table.headers + context.table.rows: + transaction_type = TransactionType.READ if row[0] == "read" else TransactionType.WRITE + transaction = session.transaction(transaction_type) + transactions.append(transaction) + context.sessions_to_transactions[session] = transactions -@step("connection open data session for database: {database_name}") -@step("connection open session for database: {database_name}") -def step_impl(context, database_name: str): - context.sessions.append(context.client.session(database_name, SessionType.DATA)) +@step("for each session, open transaction of type; throws exception") +@step("for each session, open transaction(s) of type; throws exception") +def step_impl(context): + for session in context.sessions: + for row in context.table.headers + context.table.rows: + transaction_type = TransactionType.READ if row[0] == "read" else TransactionType.WRITE + try: + session.transaction(transaction_type) + assert False + except GraknClientException: + pass -@step("session opens transaction of type: {transaction_type}") -def step_impl(context, transaction_type): # TODO transaction_type should parse to type TransactionType by itself ideally - transaction_type = TransactionType.READ if transaction_type == "read" else "write" +def for_each_session_transactions_are(context, assertion: Callable[[Transaction], None]): for session in context.sessions: - transactions = [] - transactions.append(session.transaction(transaction_type)) - context.sessions_to_transactions[session] = transactions + for transaction in context.sessions_to_transactions[session]: + assertion(transaction) + + +def assert_transaction_null(transaction: Transaction, is_null: bool): + assert (transaction is None) is is_null + + +@step("session transaction is null: {is_null}") +@step("for each session, transaction is null: {is_null}") +@step("for each session, transactions are null: {is_null}") +def step_impl(context, is_null): + is_null = is_null == "true" + for_each_session_transactions_are(context, lambda tx: assert_transaction_null(tx, is_null)) + + +def assert_transaction_open(transaction: Transaction, is_open: bool): + assert transaction.is_open() is is_open @step("session transaction is open: {is_open}") -def step_impl(context, is_open): # TODO is_open should parse to boolean by itself - is_open = (is_open == "true") +@step("for each session, transaction is open: {is_open}") +@step("for each session, transactions are open: {is_open}") +def step_impl(context, is_open): + is_open = is_open == "true" + for_each_session_transactions_are(context, lambda tx: assert_transaction_open(tx, is_open)) + + +@step("session transaction commits") +@step("transaction commits") +def step_impl(context): + context.sessions_to_transactions[context.sessions[0]][0].commit() + + +@step("session transaction commits; throws exception") +@step("transaction commits; throws exception") +def step_impl(context): + try: + context.execute_steps("Then transaction commits") + assert False + except GraknClientException: + pass + + +@step("for each session, transaction commits") +@step("for each session, transactions commit") +def step_impl(context): + for session in context.sessions: + for transaction in context.sessions_to_transactions[session]: + transaction.commit() + + +@step("for each session, transaction commits; throws exception") +@step("for each session, transactions commit; throws exception") +def step_impl(context): + for session in context.sessions: + for transaction in context.sessions_to_transactions[session]: + try: + transaction.commit() + assert False + except GraknClientException: + pass + + +# TODO: close(s) in other implementations - simplify +@step("for each session, transaction closes") +def step_impl(context): + for session in context.sessions: + for transaction in context.sessions_to_transactions[session]: + transaction.close() + + +@step("for each session, transaction has type:") +@step("for each session, transactions have type:") +def step_impl(context): + types = list(map(lambda row: TransactionType.READ if row[0] == "read" else TransactionType.WRITE, context.table.headers + context.table.rows)) + for session in context.sessions: + transactions = context.sessions_to_transactions[session] + assert len(types) == len(transactions) + transactions_iterator = iter(transactions) + for type_ in types: + assert next(transactions_iterator).transaction_type() == type_ + + +@step("session transaction has type: {transaction_type}") +def step_impl(context, transaction_type): + transaction_type = TransactionType.READ if transaction_type == "read" else TransactionType.WRITE + assert context.sessions_to_transactions[context.sessions[0]][0].transaction_type() == transaction_type + + +############################################## +# sequential sessions, parallel transactions # +############################################## + +# TODO: transaction(s) in other implementations - simplify +@step("for each session, open transactions in parallel of type:") +def step_impl(context): + types = list(map(lambda row: TransactionType.READ if row[0] == "read" else TransactionType.WRITE, context.table.headers + context.table.rows)) + assert context.THREAD_POOL_SIZE >= len(types) + with ThreadPoolExecutor(max_workers=context.THREAD_POOL_SIZE) as executor: + for session in context.sessions: + transactions_parallel = [] + for type_ in types: + transactions_parallel.append(executor.submit(lambda s: s.transaction(type_), args=session)) + context.sessions_to_transactions_parallel[session] = transactions_parallel + + +def for_each_session_transactions_in_parallel_are(context, assertion: Callable[[Transaction], None]): + for session in context.sessions: + for future_transaction in context.sessions_to_transactions_parallel[session]: + # TODO: Ideally we would concurrently await all Futures and join the assertions, but not sure this is easy + assertion(future_transaction.result()) + + +@step("for each session, transactions in parallel are null: {is_null}") +def step_impl(context, is_null): + is_null = is_null == "true" + for_each_session_transactions_in_parallel_are(context, lambda tx: assert_transaction_null(tx, is_null)) + + +@step("for each session, transactions in parallel are open: {is_open}") +def step_impl(context, is_open): + is_open = is_open == "true" + for_each_session_transactions_in_parallel_are(context, lambda tx: assert_transaction_open(tx, is_open)) + + +@step("for each session, transactions in parallel have type:") +def step_impl(context): + types = list(map(lambda row: TransactionType.READ if row[0] == "read" else TransactionType.WRITE, context.table.headers + context.table.rows)) + for session in context.sessions: + future_transactions = context.sessions_to_transactions_parallel[session] + assert len(types) == len(future_transactions) + transactions_iterator = iter(future_transactions) + for type_ in types: + assert next(transactions_iterator).result().transaction_type() == type_ + + +############################################ +# parallel sessions, parallel transactions # +############################################ + +def for_each_session_in_parallel_transactions_in_parallel_are(context, assertion): + for future_session in context.sessions_parallel: + for future_transaction in context.sessions_parallel_to_transactions_parallel[future_session]: + assertion(future_transaction.result()) + + +@step("for each session in parallel, transactions in parallel are null: {is_null}") +def step_impl(context, is_null): + is_null = is_null == "true" + for_each_session_in_parallel_transactions_in_parallel_are(context, lambda tx: assert_transaction_null(tx, is_null)) + + +@step("for each session in parallel, transactions in parallel are open: {is_open}") +def step_impl(context, is_open): + is_open = is_open == "true" + for_each_session_in_parallel_transactions_in_parallel_are(context, lambda tx: assert_transaction_open(tx, is_open)) + + +###################################### +# transaction behaviour with queries # +###################################### + +@step("for each transaction, define query; throws exception containing {expected_exception}") +def step_impl(context, expected_exception: str): for session in context.sessions: for transaction in context.sessions_to_transactions[session]: - assert transaction.is_open() == is_open + try: + next(transaction.query().define(context.text), default=None) + assert False + except GraknClientException as e: + assert expected_exception in str(e) From 9e954f4c052ef6c2d79d479dd49e5c1a441c762d Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Thu, 31 Dec 2020 19:42:51 +0000 Subject: [PATCH 10/16] Implement all Transaction steps --- .../connection/session/session_steps.py | 47 +- .../transaction/transaction.feature | 587 ------------------ .../transaction/transaction_steps.py | 86 ++- test/behaviour/environment.py | 15 +- 4 files changed, 113 insertions(+), 622 deletions(-) delete mode 100644 test/behaviour/connection/transaction/transaction.feature diff --git a/test/behaviour/connection/session/session_steps.py b/test/behaviour/connection/session/session_steps.py index 530336b7..f8ee1a7b 100644 --- a/test/behaviour/connection/session/session_steps.py +++ b/test/behaviour/connection/session/session_steps.py @@ -1,9 +1,54 @@ +from concurrent.futures.thread import ThreadPoolExecutor +from functools import partial + from behave import * from grakn.rpc.session import SessionType +def open_sessions_for_databases(context, names: list, session_type=SessionType.DATA): + for name in names: + context.sessions.append(context.client.session(name, session_type)) + + +@step("connection open schema session for database: {database_name}") +def step_impl(context, database_name): + open_sessions_for_databases(context, [database_name], SessionType.SCHEMA) + + @step("connection open data session for database: {database_name}") @step("connection open session for database: {database_name}") def step_impl(context, database_name: str): - context.sessions.append(context.client.session(database_name, SessionType.DATA)) + open_sessions_for_databases(context, [database_name], SessionType.DATA) + + +@step("connection open schema session for database") +@step("connection open schema session for databases") +@step("connection open schema sessions for database") +@step("connection open schema sessions for databases") +def step_impl(context): + names = [context.table.headings[0]] + list(map(lambda row: row[0], context.table.rows)) + open_sessions_for_databases(context, names, SessionType.SCHEMA) + + +@step("connection open data session for database") +@step("connection open data session for databases") +@step("connection open data sessions for database") +@step("connection open data sessions for databases") +@step("connection open session for database") +@step("connection open session for databases") +@step("connection open sessions for database") +@step("connection open sessions for databases") +def step_impl(context): + names = [context.table.headings[0]] + list(map(lambda row: row[0], context.table.rows)) + open_sessions_for_databases(context, names, SessionType.DATA) + + +@step("connection open data sessions in parallel for databases") +@step("connection open sessions in parallel for databases") +def step_impl(context): + names = [context.table.headings[0]] + list(map(lambda row: row[0], context.table.rows)) + assert context.THREAD_POOL_SIZE >= len(names) + with ThreadPoolExecutor(max_workers=context.THREAD_POOL_SIZE) as executor: + for name in names: + context.sessions_parallel.append(executor.submit(partial(context.client.session, name, SessionType.DATA))) diff --git a/test/behaviour/connection/transaction/transaction.feature b/test/behaviour/connection/transaction/transaction.feature deleted file mode 100644 index 1b598007..00000000 --- a/test/behaviour/connection/transaction/transaction.feature +++ /dev/null @@ -1,587 +0,0 @@ -# -# Copyright (C) 2020 Grakn Labs -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# - -#noinspection CucumberUndefinedStep -Feature: Connection Transaction - - Background: - Given connection has been opened - Given connection does not have any database - - Scenario: one database, one session, one transaction to read - When connection create database: grakn - Given connection open session for database: grakn - When session opens transaction of type: read - Then session transaction is null: false - Then session transaction is open: true - Then session transaction has type: read - - Scenario: one database, one session, one transaction to write - When connection create database: grakn - Given connection open session for database: grakn - When session opens transaction of type: write - Then session transaction is null: false - Then session transaction is open: true - Then session transaction has type: write - - Scenario: one database, one session, one committed write transaction is closed - When connection create database: grakn - Given connection open session for database: grakn - When session opens transaction of type: write - Then session transaction commits - Then session transaction commits; throws exception - - Scenario: one database, one session, re-committing transaction throws - When connection create database: grakn - Given connection open session for database: grakn - When for each session, open transaction of type: write - Then for each session, transaction commits - Then for each session, transaction commits; throws exception - - Scenario: one database, one session, transaction close is idempotent - When connection create database: grakn - Given connection open session for database: grakn - When for each session, open transaction of type: write - Then for each session, transaction closes - Then for each session, transaction is open: false - Then for each session, transaction closes - Then for each session, transaction is open: false - - @ignore-grakn-core - Scenario: one database, one session, many transactions to read - When connection create database: grakn - Given connection open session for database: grakn - When for each session, open transactions of type: - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - Then for each session, transactions are null: false - Then for each session, transactions are open: true - Then for each session, transactions have type: - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - - @ignore-grakn-core - Scenario: one database, one session, many transactions to write - When connection create database: grakn - Given connection open session for database: grakn - When for each session, open transactions of type: - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - Then for each session, transactions are null: false - Then for each session, transactions are open: true - Then for each session, transactions have type: - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - - @ignore-grakn-core - Scenario: one database, one session, many transactions to read and write - When connection create database: grakn - Given connection open session for database: grakn - When for each session, open transactions of type: - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - Then for each session, transactions are null: false - Then for each session, transactions are open: true - Then for each session, transactions have type: - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - - Scenario: one database, one session, many transactions in parallel to read - When connection create database: grakn - Given connection open session for database: grakn - When for each session, open transactions in parallel of type: - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - Then for each session, transactions in parallel are null: false - Then for each session, transactions in parallel are open: true - Then for each session, transactions in parallel have type: - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - - Scenario: one database, one session, many transactions in parallel to write - When connection create database: grakn - Given connection open session for database: grakn - When for each session, open transactions in parallel of type: - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - Then for each session, transactions in parallel are null: false - Then for each session, transactions in parallel are open: true - Then for each session, transactions in parallel have type: - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - - Scenario: one database, one session, many transactions in parallel to read and write - When connection create database: grakn - Given connection open session for database: grakn - When for each session, open transactions in parallel of type: - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - Then for each session, transactions in parallel are null: false - Then for each session, transactions in parallel are open: true - Then for each session, transactions in parallel have type: - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - - Scenario: one database, many sessions, one transaction to read - When connection create database: grakn - Given connection open sessions for database: - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - When for each session, open transaction of type: read - Then for each session, transaction is null: false - Then for each session, transaction is open: true - Then for each session, transaction has type: read - - Scenario: one database, many sessions, one transaction to write - When connection create database: grakn - Given connection open sessions for database: - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - When for each session, open transaction of type: write - Then for each session, transaction is null: false - Then for each session, transaction is open: true - Then for each session, transaction has type: write - - @ignore-grakn-core - Scenario: one database, many sessions, many transactions to read - When connection create database: grakn - Given connection open sessions for database: - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - When for each session, open transactions of type: - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - Then for each session, transactions are null: false - Then for each session, transactions are open: true - Then for each session, transactions have type: - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - - @ignore-grakn-core - Scenario: one database, many sessions, many transactions to write - When connection create database: grakn - Given connection open sessions for database: - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - When for each session, open transactions of type: - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - Then for each session, transactions are null: false - Then for each session, transactions are open: true - Then for each session, transactions have type: - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - - @ignore-grakn-core - Scenario: one database, many sessions, many transactions to read and write - When connection create database: grakn - Given connection open sessions for database: - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - When for each session, open transactions of type: - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - Then for each session, transactions are null: false - Then for each session, transactions are open: true - Then for each session, transactions have type: - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - - Scenario: one database, many sessions, many transactions in parallel to read - When connection create database: grakn - Given connection open sessions for database: - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - When for each session, open transactions in parallel of type: - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - Then for each session, transactions in parallel are null: false - Then for each session, transactions in parallel are open: true - Then for each session, transactions in parallel have type: - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - | read | - - Scenario: one database, many sessions, many transactions in parallel to write - When connection create database: grakn - Given connection open sessions for database: - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - When for each session, open transactions in parallel of type: - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - Then for each session, transactions in parallel are null: false - Then for each session, transactions in parallel are open: true - Then for each session, transactions in parallel have type: - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - | write | - - Scenario: one database, many sessions, many transactions in parallel to read and write - When connection create database: grakn - Given connection open sessions for database: - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - | grakn | - When for each session, open transactions in parallel of type: - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - Then for each session, transactions in parallel are null: false - Then for each session, transactions in parallel are open: true - Then for each session, transactions in parallel have type: - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - | read | - | write | - -# Scenario: one database, many sessions in parallel, one transactions to read -# -# Scenario: one database, many sessions in parallel, one transactions to write -# -# Scenario: one database, many sessions in parallel, many transactions to read -# -# Scenario: one database, many sessions in parallel, many transactions to write -# -# Scenario: one database, many sessions in parallel, many transactions in parallel to read -# -# Scenario: one database, many sessions in parallel, many transactions in parallel to write - - - Scenario: write in a read transaction throws - When connection create database: grakn - Given connection open schema session for database: grakn - When session opens transaction of type: read - Then graql define - """ - define person sub entity; - """ - Then transaction commits; throws exception containing "write transactions can be committed" diff --git a/test/behaviour/connection/transaction/transaction_steps.py b/test/behaviour/connection/transaction/transaction_steps.py index e6a6c81d..fe4efab3 100644 --- a/test/behaviour/connection/transaction/transaction_steps.py +++ b/test/behaviour/connection/transaction/transaction_steps.py @@ -1,4 +1,5 @@ from concurrent.futures.thread import ThreadPoolExecutor +from functools import partial from typing import Callable from behave import * @@ -7,34 +8,37 @@ from grakn.rpc.transaction import TransactionType, Transaction +def for_each_session_open_transaction_of_type(context, transaction_types: list): + for session in context.sessions: + transactions = [] + for transaction_type in transaction_types: + transaction = session.transaction(transaction_type) + transactions.append(transaction) + context.sessions_to_transactions[session] = transactions + + # TODO: this is implemented as open(s) in some clients - get rid of that, simplify them @step("session opens transaction of type: {transaction_type}") +@step("for each session, open transaction of type: {transaction_type}") def step_impl(context, transaction_type): # TODO transaction_type should parse to type TransactionType by itself ideally transaction_type = TransactionType.READ if transaction_type == "read" else TransactionType.WRITE - for session in context.sessions: - transactions = [] - transactions.append(session.transaction(transaction_type)) - context.sessions_to_transactions[session] = transactions + for_each_session_open_transaction_of_type(context, [transaction_type]) -@step("for each session, open transaction of type:") -@step("for each session, open transactions of type:") +@step("for each session, open transaction of type") +@step("for each session, open transactions of type") def step_impl(context): - for session in context.sessions: - transactions = [] - for row in context.table.headers + context.table.rows: - transaction_type = TransactionType.READ if row[0] == "read" else TransactionType.WRITE - transaction = session.transaction(transaction_type) - transactions.append(transaction) - context.sessions_to_transactions[session] = transactions + raw_values = [context.table.headings[0]] + list(map(lambda row: row[0], context.table.rows)) + transaction_types = list(map(lambda raw: TransactionType.READ if raw == "read" else TransactionType.WRITE, raw_values)) + for_each_session_open_transaction_of_type(context, transaction_types) @step("for each session, open transaction of type; throws exception") @step("for each session, open transaction(s) of type; throws exception") def step_impl(context): for session in context.sessions: - for row in context.table.headers + context.table.rows: - transaction_type = TransactionType.READ if row[0] == "read" else TransactionType.WRITE + for raw_type in [context.table.headings[0]] + list(map(lambda row: row[0], context.table.rows)): + transaction_type = TransactionType.READ if raw_type == "read" else TransactionType.WRITE try: session.transaction(transaction_type) assert False @@ -82,7 +86,7 @@ def step_impl(context): @step("transaction commits; throws exception") def step_impl(context): try: - context.execute_steps("Then transaction commits") + context.sessions_to_transactions[context.sessions[0]][0].commit() assert False except GraknClientException: pass @@ -116,22 +120,30 @@ def step_impl(context): transaction.close() -@step("for each session, transaction has type:") -@step("for each session, transactions have type:") -def step_impl(context): - types = list(map(lambda row: TransactionType.READ if row[0] == "read" else TransactionType.WRITE, context.table.headers + context.table.rows)) +def for_each_session_transaction_has_type(context, transaction_types: list): for session in context.sessions: transactions = context.sessions_to_transactions[session] - assert len(types) == len(transactions) + assert len(transaction_types) == len(transactions) transactions_iterator = iter(transactions) - for type_ in types: - assert next(transactions_iterator).transaction_type() == type_ + for transaction_type in transaction_types: + assert next(transactions_iterator).transaction_type() == transaction_type + +# NOTE: behave ignores trailing colons in feature files +@step("for each session, transaction has type") +@step("for each session, transactions have type") +def step_impl(context): + raw_values = [context.table.headings[0]] + list(map(lambda row: row[0], context.table.rows)) + transaction_types = list(map(lambda raw: TransactionType.READ if raw == "read" else TransactionType.WRITE, raw_values)) + for_each_session_transaction_has_type(context, transaction_types) + +# TODO: this is overcomplicated in some clients (has/have, transaction(s)) +@step("for each session, transaction has type: {transaction_type}") @step("session transaction has type: {transaction_type}") def step_impl(context, transaction_type): transaction_type = TransactionType.READ if transaction_type == "read" else TransactionType.WRITE - assert context.sessions_to_transactions[context.sessions[0]][0].transaction_type() == transaction_type + for_each_session_transaction_has_type(context, [transaction_type]) ############################################## @@ -139,16 +151,16 @@ def step_impl(context, transaction_type): ############################################## # TODO: transaction(s) in other implementations - simplify -@step("for each session, open transactions in parallel of type:") +@step("for each session, open transactions in parallel of type") def step_impl(context): - types = list(map(lambda row: TransactionType.READ if row[0] == "read" else TransactionType.WRITE, context.table.headers + context.table.rows)) + raw_values = [context.table.headings[0]] + list(map(lambda row: row[0], context.table.rows)) + types = list(map(lambda raw: TransactionType.READ if raw == "read" else TransactionType.WRITE, raw_values)) assert context.THREAD_POOL_SIZE >= len(types) with ThreadPoolExecutor(max_workers=context.THREAD_POOL_SIZE) as executor: for session in context.sessions: - transactions_parallel = [] + context.sessions_to_transactions_parallel[session] = [] for type_ in types: - transactions_parallel.append(executor.submit(lambda s: s.transaction(type_), args=session)) - context.sessions_to_transactions_parallel[session] = transactions_parallel + context.sessions_to_transactions_parallel[session].append(executor.submit(partial(session.transaction, type_))) def for_each_session_transactions_in_parallel_are(context, assertion: Callable[[Transaction], None]): @@ -170,15 +182,23 @@ def step_impl(context, is_open): for_each_session_transactions_in_parallel_are(context, lambda tx: assert_transaction_open(tx, is_open)) -@step("for each session, transactions in parallel have type:") +@step("for each session, transactions in parallel have type") def step_impl(context): - types = list(map(lambda row: TransactionType.READ if row[0] == "read" else TransactionType.WRITE, context.table.headers + context.table.rows)) + raw_values = [context.table.headings[0]] + list(map(lambda row: row[0], context.table.rows)) + types = list(map(lambda raw: TransactionType.READ if raw == "read" else TransactionType.WRITE, raw_values)) for session in context.sessions: future_transactions = context.sessions_to_transactions_parallel[session] assert len(types) == len(future_transactions) - transactions_iterator = iter(future_transactions) + transactions = [] + for future_tx in future_transactions: + transactions.append(future_tx.result()) + print("types") + print(types) + print("transactions") + print(list(map(lambda tx: tx.transaction_type(), transactions))) + transactions_iter = iter(transactions) for type_ in types: - assert next(transactions_iterator).result().transaction_type() == type_ + assert next(transactions_iter).transaction_type() == type_ ############################################ diff --git a/test/behaviour/environment.py b/test/behaviour/environment.py index 183bff68..693b515b 100644 --- a/test/behaviour/environment.py +++ b/test/behaviour/environment.py @@ -3,15 +3,28 @@ from grakn.client import GraknClient -def before_scenario(context, scenario): +def before_all(context): + context.THREAD_POOL_SIZE = 32 context.client = GraknClient() + + +def before_scenario(context, scenario): for database in context.client.databases().all(): context.client.databases().delete(database) context.sessions = [] context.sessions_to_transactions = {} + context.sessions_parallel = [] + context.sessions_to_transactions_parallel = {} + context.sessions_parallel_to_transactions_parallel = {} + context.tx = lambda: context.sessions_to_transactions[context.sessions[0]][0] def after_scenario(context, scenario): for session in context.sessions: session.close() + for database in context.client.databases().all(): + context.client.databases().delete(database) + + +def after_all(context): context.client.close() From f15ec905b9fd06250cddf76dee7f9e05ccc76c6a Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Thu, 31 Dec 2020 19:43:25 +0000 Subject: [PATCH 11/16] Pull in BDD features from graknlabs_behaviour --- WORKSPACE | 3 ++- dependencies/graknlabs/repositories.bzl | 7 +++++++ test/behaviour/connection/transaction/BUILD | 2 +- tools/behave_rule.bzl | 9 +++++---- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index 32bf782e..efedae6d 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -92,8 +92,9 @@ github_deps() # Load @graknlabs dependencies # ################################ -load("//dependencies/graknlabs:repositories.bzl", "graknlabs_common") +load("//dependencies/graknlabs:repositories.bzl", "graknlabs_common", "graknlabs_behaviour") graknlabs_common() +graknlabs_behaviour() # Load artifacts load("//dependencies/graknlabs:artifacts.bzl", "graknlabs_grakn_core_artifacts") diff --git a/dependencies/graknlabs/repositories.bzl b/dependencies/graknlabs/repositories.bzl index 6c5af694..b7b3ace8 100644 --- a/dependencies/graknlabs/repositories.bzl +++ b/dependencies/graknlabs/repositories.bzl @@ -32,3 +32,10 @@ def graknlabs_common(): remote = "https://github.com/graknlabs/common", commit = "cfd261fd5412a0b45cb5494555e4491dd3ce5f64" # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_common ) + +def graknlabs_behaviour(): + git_repository( + name = "graknlabs_behaviour", + remote = "https://github.com/graknlabs/behaviour", + commit = "7088622b9f4a3bd99cf4fb278130ad76ff35af6b" # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_behaviour + ) diff --git a/test/behaviour/connection/transaction/BUILD b/test/behaviour/connection/transaction/BUILD index 64abd822..9e6f91c5 100644 --- a/test/behaviour/connection/transaction/BUILD +++ b/test/behaviour/connection/transaction/BUILD @@ -9,7 +9,7 @@ py_library( py_behave_test( name = "test", - feats = ["transaction.feature"], + feats = ["@graknlabs_behaviour//connection:transaction.feature"], background = ["//test/behaviour:background"], steps = [ ":steps", diff --git a/tools/behave_rule.bzl b/tools/behave_rule.bzl index 39dae090..950361a9 100644 --- a/tools/behave_rule.bzl +++ b/tools/behave_rule.bzl @@ -13,14 +13,15 @@ Implementation of the rule py_behave_test. """ def _rule_implementation(ctx): - # behave requires a 'steps' folder to exist in the test root directory, which is the location of environment.py - steps_out_dir = ctx.files.background[0].dirname + "/steps" - # Store the path of the first feature file. It is recommended to only have one feature file. feats_dir = ctx.files.feats[0].dirname + # behave requires a 'steps' folder to exist in the test root directory. + steps_out_dir = ctx.files.feats[0].dirname + "/steps" + # TODO: If two step files have the same name, we should rename the second one to prevent conflict - cmd = "mkdir " + steps_out_dir + " && " + cmd = "cp %s %s" % (ctx.files.background[0].path, feats_dir) + cmd += " && mkdir " + steps_out_dir + " && " cmd += " && ".join(["cp %s %s" % (step_file.path, steps_out_dir) for step_file in ctx.files.steps]) cmd += " && behave %s" % feats_dir From 73b955560369d29ea61f5cf1ad5320b14452ff0f Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Thu, 31 Dec 2020 22:34:01 +0000 Subject: [PATCH 12/16] BDD step parameter parsing --- test/behaviour/BUILD | 7 --- test/behaviour/connection/session/BUILD | 22 ------- {test => tests}/BUILD | 5 +- tests/behaviour/BUILD | 7 +++ tests/behaviour/config/BUILD | 7 +++ tests/behaviour/config/parameters.py | 16 +++++ {test => tests}/behaviour/connection/BUILD | 2 +- .../behaviour/connection/connection_steps.py | 0 .../behaviour/connection/database/BUILD | 6 +- .../connection/database/database_steps.py | 0 tests/behaviour/connection/session/BUILD | 24 ++++++++ .../connection/session/session_steps.py | 61 +++++++++++++++++++ .../behaviour/connection/transaction/BUILD | 11 ++-- .../transaction/transaction_steps.py | 50 ++++++--------- {test => tests}/behaviour/environment.py | 0 {test => tests}/deployment/requirements.txt | 0 {test => tests}/deployment/test.py | 0 {test => tests}/integration/base.py | 0 {test => tests}/integration/test_concept.py | 0 .../integration/test_connection.py | 0 {test => tests}/integration/test_grakn.py | 0 {test => tests}/integration/test_query.py | 0 22 files changed, 148 insertions(+), 70 deletions(-) delete mode 100644 test/behaviour/BUILD delete mode 100644 test/behaviour/connection/session/BUILD rename {test => tests}/BUILD (91%) create mode 100644 tests/behaviour/BUILD create mode 100644 tests/behaviour/config/BUILD create mode 100644 tests/behaviour/config/parameters.py rename {test => tests}/behaviour/connection/BUILD (55%) rename {test => tests}/behaviour/connection/connection_steps.py (100%) rename {test => tests}/behaviour/connection/database/BUILD (66%) rename {test => tests}/behaviour/connection/database/database_steps.py (100%) create mode 100644 tests/behaviour/connection/session/BUILD rename {test => tests}/behaviour/connection/session/session_steps.py (54%) rename {test => tests}/behaviour/connection/transaction/BUILD (52%) rename {test => tests}/behaviour/connection/transaction/transaction_steps.py (80%) rename {test => tests}/behaviour/environment.py (100%) rename {test => tests}/deployment/requirements.txt (100%) rename {test => tests}/deployment/test.py (100%) rename {test => tests}/integration/base.py (100%) rename {test => tests}/integration/test_concept.py (100%) rename {test => tests}/integration/test_connection.py (100%) rename {test => tests}/integration/test_grakn.py (100%) rename {test => tests}/integration/test_query.py (100%) diff --git a/test/behaviour/BUILD b/test/behaviour/BUILD deleted file mode 100644 index bec0fbce..00000000 --- a/test/behaviour/BUILD +++ /dev/null @@ -1,7 +0,0 @@ -package(default_visibility = ["//test/behaviour:__subpackages__"]) - -py_library( - name = "background", - srcs = ["environment.py"], - deps = [], -) diff --git a/test/behaviour/connection/session/BUILD b/test/behaviour/connection/session/BUILD deleted file mode 100644 index 95df5519..00000000 --- a/test/behaviour/connection/session/BUILD +++ /dev/null @@ -1,22 +0,0 @@ -package(default_visibility = ["//test/behaviour:__subpackages__"]) -load("//tools:behave_rule.bzl", "py_behave_test") - -py_library( - name = "steps", - srcs = ["session_steps.py"], - deps = [], -) - -#py_behave_test( -# name = "test", -# feats = ["transaction.feature"], -# background = ["//test/behaviour:background"], -# steps = [ -# ":steps", -# "//test/behaviour/connection:steps", -# ], -# deps = [ -# "//:client_python", -# ], -# size = "small", -#) diff --git a/test/BUILD b/tests/BUILD similarity index 91% rename from test/BUILD rename to tests/BUILD index 709acfda..fa302bcc 100644 --- a/test/BUILD +++ b/tests/BUILD @@ -17,7 +17,8 @@ # under the License. # -load("@graknlabs_common//test/server:rules.bzl", "native_grakn_artifact") +# Note: Do NOT rename this folder to 'test' as it conflicts with a built-in Python package! +load("@graknlabs_common//tests/server:rules.bzl", "native_grakn_artifact") load("@graknlabs_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") load("@rules_python//python:defs.bzl", "py_library", "py_test") @@ -26,7 +27,7 @@ native_grakn_artifact( mac_artifact = "@graknlabs_grakn_core_artifact_mac//file", linux_artifact = "@graknlabs_grakn_core_artifact_linux//file", windows_artifact = "@graknlabs_grakn_core_artifact_windows//file", - visibility = ["//test:__subpackages__"], + visibility = ["//tests:__subpackages__"], ) py_test( diff --git a/tests/behaviour/BUILD b/tests/behaviour/BUILD new file mode 100644 index 00000000..a82d203b --- /dev/null +++ b/tests/behaviour/BUILD @@ -0,0 +1,7 @@ +package(default_visibility = ["//tests/behaviour:__subpackages__"]) + +py_library( + name = "background", + srcs = glob(["*.py"]), + deps = [], +) diff --git a/tests/behaviour/config/BUILD b/tests/behaviour/config/BUILD new file mode 100644 index 00000000..8d4cc2e8 --- /dev/null +++ b/tests/behaviour/config/BUILD @@ -0,0 +1,7 @@ +package(default_visibility = ["//tests/behaviour:__subpackages__"]) + +py_library( + name = "parameters", + srcs = ["parameters.py"], + deps = [], +) diff --git a/tests/behaviour/config/parameters.py b/tests/behaviour/config/parameters.py new file mode 100644 index 00000000..aecb909e --- /dev/null +++ b/tests/behaviour/config/parameters.py @@ -0,0 +1,16 @@ +from typing import List + +from grakn.rpc.transaction import TransactionType + + +def parse_bool(value: str) -> bool: + return value == "true" + + +def parse_list(table) -> List[str]: + return [table.headings[0]] + list(map(lambda row: row[0], table.rows)) + + +def parse_transaction_type(value: str) -> TransactionType: + return TransactionType.READ if value == "read" else TransactionType.WRITE + diff --git a/test/behaviour/connection/BUILD b/tests/behaviour/connection/BUILD similarity index 55% rename from test/behaviour/connection/BUILD rename to tests/behaviour/connection/BUILD index d184a243..6a4ed12c 100644 --- a/test/behaviour/connection/BUILD +++ b/tests/behaviour/connection/BUILD @@ -1,4 +1,4 @@ -package(default_visibility = ["//test/behaviour:__subpackages__"]) +package(default_visibility = ["//tests/behaviour:__subpackages__"]) py_library( name = "steps", diff --git a/test/behaviour/connection/connection_steps.py b/tests/behaviour/connection/connection_steps.py similarity index 100% rename from test/behaviour/connection/connection_steps.py rename to tests/behaviour/connection/connection_steps.py diff --git a/test/behaviour/connection/database/BUILD b/tests/behaviour/connection/database/BUILD similarity index 66% rename from test/behaviour/connection/database/BUILD rename to tests/behaviour/connection/database/BUILD index 6b6f46d3..78f8639b 100644 --- a/test/behaviour/connection/database/BUILD +++ b/tests/behaviour/connection/database/BUILD @@ -1,4 +1,4 @@ -package(default_visibility = ["//test/behaviour:__subpackages__"]) +package(default_visibility = ["//tests/behaviour:__subpackages__"]) load("//tools:behave_rule.bzl", "py_behave_test") py_library( @@ -10,10 +10,10 @@ py_library( #py_behave_test( # name = "test", # feats = ["transaction.feature"], -# background = ["//test/behaviour:background"], +# background = ["//tests/behaviour:background"], # steps = [ # ":steps", -# "//test/behaviour/connection:steps", +# "//tests/behaviour/connection:steps", # ], # deps = [ # "//:client_python", diff --git a/test/behaviour/connection/database/database_steps.py b/tests/behaviour/connection/database/database_steps.py similarity index 100% rename from test/behaviour/connection/database/database_steps.py rename to tests/behaviour/connection/database/database_steps.py diff --git a/tests/behaviour/connection/session/BUILD b/tests/behaviour/connection/session/BUILD new file mode 100644 index 00000000..b7519d55 --- /dev/null +++ b/tests/behaviour/connection/session/BUILD @@ -0,0 +1,24 @@ +package(default_visibility = ["//tests/behaviour:__subpackages__"]) +load("//tools:behave_rule.bzl", "py_behave_test") + +py_library( + name = "steps", + srcs = ["session_steps.py"], + deps = [], +) + +py_behave_test( + name = "test", + feats = ["@graknlabs_behaviour//connection:session.feature"], + background = ["//tests/behaviour:background"], + steps = [ + ":steps", + "//tests/behaviour/connection:steps", + "//tests/behaviour/connection/database:steps", + ], + deps = [ + "//:client_python", + "//tests/behaviour/config:parameters", + ], + size = "small", +) diff --git a/test/behaviour/connection/session/session_steps.py b/tests/behaviour/connection/session/session_steps.py similarity index 54% rename from test/behaviour/connection/session/session_steps.py rename to tests/behaviour/connection/session/session_steps.py index f8ee1a7b..fa4eba08 100644 --- a/test/behaviour/connection/session/session_steps.py +++ b/tests/behaviour/connection/session/session_steps.py @@ -1,9 +1,11 @@ from concurrent.futures.thread import ThreadPoolExecutor from functools import partial +from typing import List from behave import * from grakn.rpc.session import SessionType +from tests.behaviour.config.parameters import parse_bool, parse_list def open_sessions_for_databases(context, names: list, session_type=SessionType.DATA): @@ -52,3 +54,62 @@ def step_impl(context): with ThreadPoolExecutor(max_workers=context.THREAD_POOL_SIZE) as executor: for name in names: context.sessions_parallel.append(executor.submit(partial(context.client.session, name, SessionType.DATA))) + + +@step("connection close all sessions") +def step_impl(context): + for session in context.sessions: + session.close() + context.sessions = [] + + +@step("session is null: {is_null}") +@step("sessions are null: {is_null}") +def step_impl(context, is_null): + is_null = parse_bool(is_null) + for session in context.sessions: + assert (session is None) == is_null + + +@step("session is open: {is_open}") +@step("sessions are open: {is_open}") +def step_impl(context, is_open): + is_open = parse_bool(is_open) + for session in context.sessions: + assert session.is_open() == is_open + + +@step("sessions in parallel are null: {is_null}") +def step_impl(context, is_null): + is_null = parse_bool(is_null) + for future_session in context.sessions_parallel: + assert (future_session.result() is None) == is_null + + +@step("sessions in parallel are open: {is_open}") +def step_impl(context, is_open): + is_open = parse_bool(is_open) + for future_session in context.sessions_parallel: + assert future_session.result().is_open() == is_open + + +def sessions_have_databases(context, names: List[str]): + assert len(names) == len(context.sessions) + session_iter = iter(context.sessions) + for name in names: + assert name == next(session_iter).database() + + +@step("session has database: {database_name}") +@step("sessions have database: {database_name}") +def step_impl(context, database_name: str): + sessions_have_databases(context, list(database_name)) + + +@step("sessions in parallel have databases") +def step_impl(context): + database_names = parse_list(context.table) + assert len(database_names) == len(context.sessions_parallel) + future_session_iter = iter(context.sessions_parallel) + for name in database_names: + assert name == next(future_session_iter).result().database() diff --git a/test/behaviour/connection/transaction/BUILD b/tests/behaviour/connection/transaction/BUILD similarity index 52% rename from test/behaviour/connection/transaction/BUILD rename to tests/behaviour/connection/transaction/BUILD index 9e6f91c5..d17f2155 100644 --- a/test/behaviour/connection/transaction/BUILD +++ b/tests/behaviour/connection/transaction/BUILD @@ -1,4 +1,4 @@ -package(default_visibility = ["//test/behaviour:__subpackages__"]) +package(default_visibility = ["//tests/behaviour:__subpackages__"]) load("//tools:behave_rule.bzl", "py_behave_test") py_library( @@ -10,15 +10,16 @@ py_library( py_behave_test( name = "test", feats = ["@graknlabs_behaviour//connection:transaction.feature"], - background = ["//test/behaviour:background"], + background = ["//tests/behaviour:background"], steps = [ ":steps", - "//test/behaviour/connection:steps", - "//test/behaviour/connection/database:steps", - "//test/behaviour/connection/session:steps", + "//tests/behaviour/connection:steps", + "//tests/behaviour/connection/database:steps", + "//tests/behaviour/connection/session:steps", ], deps = [ "//:client_python", + "//tests/behaviour/config:parameters", ], size = "small", ) diff --git a/test/behaviour/connection/transaction/transaction_steps.py b/tests/behaviour/connection/transaction/transaction_steps.py similarity index 80% rename from test/behaviour/connection/transaction/transaction_steps.py rename to tests/behaviour/connection/transaction/transaction_steps.py index fe4efab3..7304d882 100644 --- a/test/behaviour/connection/transaction/transaction_steps.py +++ b/tests/behaviour/connection/transaction/transaction_steps.py @@ -1,14 +1,15 @@ from concurrent.futures.thread import ThreadPoolExecutor from functools import partial -from typing import Callable +from typing import Callable, List from behave import * from grakn.common.exception import GraknClientException from grakn.rpc.transaction import TransactionType, Transaction +from tests.behaviour.config.parameters import parse_transaction_type, parse_list, parse_bool -def for_each_session_open_transaction_of_type(context, transaction_types: list): +def for_each_session_open_transaction_of_type(context, transaction_types: List[TransactionType]): for session in context.sessions: transactions = [] for transaction_type in transaction_types: @@ -20,16 +21,15 @@ def for_each_session_open_transaction_of_type(context, transaction_types: list): # TODO: this is implemented as open(s) in some clients - get rid of that, simplify them @step("session opens transaction of type: {transaction_type}") @step("for each session, open transaction of type: {transaction_type}") -def step_impl(context, transaction_type): # TODO transaction_type should parse to type TransactionType by itself ideally - transaction_type = TransactionType.READ if transaction_type == "read" else TransactionType.WRITE +def step_impl(context, transaction_type: str): + transaction_type = parse_transaction_type(transaction_type) for_each_session_open_transaction_of_type(context, [transaction_type]) @step("for each session, open transaction of type") @step("for each session, open transactions of type") def step_impl(context): - raw_values = [context.table.headings[0]] + list(map(lambda row: row[0], context.table.rows)) - transaction_types = list(map(lambda raw: TransactionType.READ if raw == "read" else TransactionType.WRITE, raw_values)) + transaction_types = list(map(parse_transaction_type, parse_list(context.table))) for_each_session_open_transaction_of_type(context, transaction_types) @@ -37,8 +37,8 @@ def step_impl(context): @step("for each session, open transaction(s) of type; throws exception") def step_impl(context): for session in context.sessions: - for raw_type in [context.table.headings[0]] + list(map(lambda row: row[0], context.table.rows)): - transaction_type = TransactionType.READ if raw_type == "read" else TransactionType.WRITE + for raw_type in parse_list(context.table): + transaction_type = parse_transaction_type(raw_type) try: session.transaction(transaction_type) assert False @@ -60,7 +60,7 @@ def assert_transaction_null(transaction: Transaction, is_null: bool): @step("for each session, transaction is null: {is_null}") @step("for each session, transactions are null: {is_null}") def step_impl(context, is_null): - is_null = is_null == "true" + is_null = parse_bool(is_null) for_each_session_transactions_are(context, lambda tx: assert_transaction_null(tx, is_null)) @@ -72,7 +72,7 @@ def assert_transaction_open(transaction: Transaction, is_open: bool): @step("for each session, transaction is open: {is_open}") @step("for each session, transactions are open: {is_open}") def step_impl(context, is_open): - is_open = is_open == "true" + is_open = parse_bool(is_open) for_each_session_transactions_are(context, lambda tx: assert_transaction_open(tx, is_open)) @@ -133,8 +133,7 @@ def for_each_session_transaction_has_type(context, transaction_types: list): @step("for each session, transaction has type") @step("for each session, transactions have type") def step_impl(context): - raw_values = [context.table.headings[0]] + list(map(lambda row: row[0], context.table.rows)) - transaction_types = list(map(lambda raw: TransactionType.READ if raw == "read" else TransactionType.WRITE, raw_values)) + transaction_types = list(map(parse_transaction_type, parse_list(context.table))) for_each_session_transaction_has_type(context, transaction_types) @@ -142,7 +141,7 @@ def step_impl(context): @step("for each session, transaction has type: {transaction_type}") @step("session transaction has type: {transaction_type}") def step_impl(context, transaction_type): - transaction_type = TransactionType.READ if transaction_type == "read" else TransactionType.WRITE + transaction_type = parse_transaction_type(transaction_type) for_each_session_transaction_has_type(context, [transaction_type]) @@ -153,8 +152,7 @@ def step_impl(context, transaction_type): # TODO: transaction(s) in other implementations - simplify @step("for each session, open transactions in parallel of type") def step_impl(context): - raw_values = [context.table.headings[0]] + list(map(lambda row: row[0], context.table.rows)) - types = list(map(lambda raw: TransactionType.READ if raw == "read" else TransactionType.WRITE, raw_values)) + types = list(map(parse_transaction_type, parse_list(context.table))) assert context.THREAD_POOL_SIZE >= len(types) with ThreadPoolExecutor(max_workers=context.THREAD_POOL_SIZE) as executor: for session in context.sessions: @@ -172,33 +170,25 @@ def for_each_session_transactions_in_parallel_are(context, assertion: Callable[[ @step("for each session, transactions in parallel are null: {is_null}") def step_impl(context, is_null): - is_null = is_null == "true" + is_null = parse_bool(is_null) for_each_session_transactions_in_parallel_are(context, lambda tx: assert_transaction_null(tx, is_null)) @step("for each session, transactions in parallel are open: {is_open}") def step_impl(context, is_open): - is_open = is_open == "true" + is_open = parse_bool(is_open) for_each_session_transactions_in_parallel_are(context, lambda tx: assert_transaction_open(tx, is_open)) @step("for each session, transactions in parallel have type") def step_impl(context): - raw_values = [context.table.headings[0]] + list(map(lambda row: row[0], context.table.rows)) - types = list(map(lambda raw: TransactionType.READ if raw == "read" else TransactionType.WRITE, raw_values)) + types = list(map(parse_transaction_type, parse_list(context.table))) for session in context.sessions: future_transactions = context.sessions_to_transactions_parallel[session] assert len(types) == len(future_transactions) - transactions = [] - for future_tx in future_transactions: - transactions.append(future_tx.result()) - print("types") - print(types) - print("transactions") - print(list(map(lambda tx: tx.transaction_type(), transactions))) - transactions_iter = iter(transactions) + future_transactions_iter = iter(future_transactions) for type_ in types: - assert next(transactions_iter).transaction_type() == type_ + assert next(future_transactions_iter).result().transaction_type() == type_ ############################################ @@ -213,13 +203,13 @@ def for_each_session_in_parallel_transactions_in_parallel_are(context, assertion @step("for each session in parallel, transactions in parallel are null: {is_null}") def step_impl(context, is_null): - is_null = is_null == "true" + is_null = parse_bool(is_null) for_each_session_in_parallel_transactions_in_parallel_are(context, lambda tx: assert_transaction_null(tx, is_null)) @step("for each session in parallel, transactions in parallel are open: {is_open}") def step_impl(context, is_open): - is_open = is_open == "true" + is_open = parse_bool(is_open) for_each_session_in_parallel_transactions_in_parallel_are(context, lambda tx: assert_transaction_open(tx, is_open)) diff --git a/test/behaviour/environment.py b/tests/behaviour/environment.py similarity index 100% rename from test/behaviour/environment.py rename to tests/behaviour/environment.py diff --git a/test/deployment/requirements.txt b/tests/deployment/requirements.txt similarity index 100% rename from test/deployment/requirements.txt rename to tests/deployment/requirements.txt diff --git a/test/deployment/test.py b/tests/deployment/test.py similarity index 100% rename from test/deployment/test.py rename to tests/deployment/test.py diff --git a/test/integration/base.py b/tests/integration/base.py similarity index 100% rename from test/integration/base.py rename to tests/integration/base.py diff --git a/test/integration/test_concept.py b/tests/integration/test_concept.py similarity index 100% rename from test/integration/test_concept.py rename to tests/integration/test_concept.py diff --git a/test/integration/test_connection.py b/tests/integration/test_connection.py similarity index 100% rename from test/integration/test_connection.py rename to tests/integration/test_connection.py diff --git a/test/integration/test_grakn.py b/tests/integration/test_grakn.py similarity index 100% rename from test/integration/test_grakn.py rename to tests/integration/test_grakn.py diff --git a/test/integration/test_query.py b/tests/integration/test_query.py similarity index 100% rename from test/integration/test_query.py rename to tests/integration/test_query.py From 400f9501a9b97050bdc4e826286be9eecda932c8 Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Tue, 5 Jan 2021 15:40:08 +0000 Subject: [PATCH 13/16] Add hamcrest assertions --- requirements.txt | 1 + .../connection/database/database_steps.py | 18 +++++++++++- .../connection/session/session_steps.py | 28 ++++++++++++------- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/requirements.txt b/requirements.txt index 19f5b64c..c56bcd0e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -34,3 +34,4 @@ six>=1.11.0 # Dev dependencies (not required to use the grakn-client package, but necessary for its development) behave==1.2.6 +PyHamcrest==2.0.2 diff --git a/tests/behaviour/connection/database/database_steps.py b/tests/behaviour/connection/database/database_steps.py index ca724c59..9c03b331 100644 --- a/tests/behaviour/connection/database/database_steps.py +++ b/tests/behaviour/connection/database/database_steps.py @@ -1,6 +1,22 @@ +from typing import List + from behave import * +from tests.behaviour.config.parameters import parse_list + + +def create_databases(context, names: List[str]): + for name in names: + context.client.databases().create(name) + @step("connection create database: {database_name}") def step_impl(context, database_name: str): - context.client.databases().create(database_name) + create_databases(context, [database_name]) + + +# TODO: connection create database(s) in other implementations, simplify +@step("connection create databases") +def step_impl(context): + names = parse_list(context.table) + create_databases(context, names) diff --git a/tests/behaviour/connection/session/session_steps.py b/tests/behaviour/connection/session/session_steps.py index fa4eba08..1c704d98 100644 --- a/tests/behaviour/connection/session/session_steps.py +++ b/tests/behaviour/connection/session/session_steps.py @@ -3,6 +3,7 @@ from typing import List from behave import * +from hamcrest import * from grakn.rpc.session import SessionType from tests.behaviour.config.parameters import parse_bool, parse_list @@ -50,7 +51,7 @@ def step_impl(context): @step("connection open sessions in parallel for databases") def step_impl(context): names = [context.table.headings[0]] + list(map(lambda row: row[0], context.table.rows)) - assert context.THREAD_POOL_SIZE >= len(names) + assert_that(len(names), is_(less_than_or_equal_to(context.THREAD_POOL_SIZE))) with ThreadPoolExecutor(max_workers=context.THREAD_POOL_SIZE) as executor: for name in names: context.sessions_parallel.append(executor.submit(partial(context.client.session, name, SessionType.DATA))) @@ -68,7 +69,7 @@ def step_impl(context): def step_impl(context, is_null): is_null = parse_bool(is_null) for session in context.sessions: - assert (session is None) == is_null + assert_that(session is None, is_(is_null)) @step("session is open: {is_open}") @@ -76,40 +77,47 @@ def step_impl(context, is_null): def step_impl(context, is_open): is_open = parse_bool(is_open) for session in context.sessions: - assert session.is_open() == is_open + assert_that(session.is_open(), is_(is_open)) @step("sessions in parallel are null: {is_null}") def step_impl(context, is_null): is_null = parse_bool(is_null) for future_session in context.sessions_parallel: - assert (future_session.result() is None) == is_null + assert_that(future_session.result() is None, is_(is_null)) @step("sessions in parallel are open: {is_open}") def step_impl(context, is_open): is_open = parse_bool(is_open) for future_session in context.sessions_parallel: - assert future_session.result().is_open() == is_open + assert_that(future_session.result().is_open(), is_(is_open)) def sessions_have_databases(context, names: List[str]): - assert len(names) == len(context.sessions) + assert_that(context.sessions, has_length(equal_to(len(names)))) session_iter = iter(context.sessions) for name in names: - assert name == next(session_iter).database() + assert_that(next(session_iter).database(), is_(name)) @step("session has database: {database_name}") @step("sessions have database: {database_name}") def step_impl(context, database_name: str): - sessions_have_databases(context, list(database_name)) + sessions_have_databases(context, [database_name]) + + +# TODO: session(s) has/have databases in other implementations, simplify +@step("sessions have databases") +def step_impl(context): + database_names = parse_list(context.table) + sessions_have_databases(context, database_names) @step("sessions in parallel have databases") def step_impl(context): database_names = parse_list(context.table) - assert len(database_names) == len(context.sessions_parallel) + assert_that(context.sessions_parallel, has_length(equal_to(len(database_names)))) future_session_iter = iter(context.sessions_parallel) for name in database_names: - assert name == next(future_session_iter).result().database() + assert_that(next(future_session_iter).result().database(), is_(name)) From 09c04bcbedf98057a86c404ec7b9067101a002a9 Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Thu, 7 Jan 2021 12:13:23 +0000 Subject: [PATCH 14/16] bump From 642c0246e79962a7bb25d7c95203e2059177fe00 Mon Sep 17 00:00:00 2001 From: Alex Walker Date: Thu, 7 Jan 2021 12:23:56 +0000 Subject: [PATCH 15/16] Add checkstyle tests and licenses --- tests/BUILD | 2 +- tests/behaviour/BUILD | 28 ++++++++++++++++++++ tests/behaviour/config/BUILD | 28 ++++++++++++++++++++ tests/behaviour/connection/BUILD | 28 ++++++++++++++++++++ tests/behaviour/connection/database/BUILD | 27 +++++++++++++++++++ tests/behaviour/connection/session/BUILD | 27 +++++++++++++++++++ tests/behaviour/connection/transaction/BUILD | 27 +++++++++++++++++++ tools/BUILD | 27 +++++++++++++++++++ tools/behave_rule.bzl | 19 +++++++++++++ 9 files changed, 212 insertions(+), 1 deletion(-) diff --git a/tests/BUILD b/tests/BUILD index fa302bcc..528a2df8 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -18,7 +18,7 @@ # # Note: Do NOT rename this folder to 'test' as it conflicts with a built-in Python package! -load("@graknlabs_common//tests/server:rules.bzl", "native_grakn_artifact") +load("@graknlabs_common//test/server:rules.bzl", "native_grakn_artifact") load("@graknlabs_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") load("@rules_python//python:defs.bzl", "py_library", "py_test") diff --git a/tests/behaviour/BUILD b/tests/behaviour/BUILD index a82d203b..e2f612eb 100644 --- a/tests/behaviour/BUILD +++ b/tests/behaviour/BUILD @@ -1,7 +1,35 @@ +# +# 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. +# + package(default_visibility = ["//tests/behaviour:__subpackages__"]) +load("@graknlabs_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") + py_library( name = "background", srcs = glob(["*.py"]), deps = [], ) + +checkstyle_test( + name = "checkstyle", + include = glob(["*"]), + license_type = "apache", + size = "small", +) diff --git a/tests/behaviour/config/BUILD b/tests/behaviour/config/BUILD index 8d4cc2e8..9bf025d6 100644 --- a/tests/behaviour/config/BUILD +++ b/tests/behaviour/config/BUILD @@ -1,7 +1,35 @@ +# +# 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. +# + package(default_visibility = ["//tests/behaviour:__subpackages__"]) +load("@graknlabs_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") + py_library( name = "parameters", srcs = ["parameters.py"], deps = [], ) + +checkstyle_test( + name = "checkstyle", + include = glob(["*"]), + license_type = "apache", + size = "small", +) diff --git a/tests/behaviour/connection/BUILD b/tests/behaviour/connection/BUILD index 6a4ed12c..9e41a1f6 100644 --- a/tests/behaviour/connection/BUILD +++ b/tests/behaviour/connection/BUILD @@ -1,3 +1,24 @@ +# +# 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. +# + +load("@graknlabs_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") + package(default_visibility = ["//tests/behaviour:__subpackages__"]) py_library( @@ -5,3 +26,10 @@ py_library( srcs = ["connection_steps.py"], deps = [], ) + +checkstyle_test( + name = "checkstyle", + include = glob(["*"]), + license_type = "apache", + size = "small", +) diff --git a/tests/behaviour/connection/database/BUILD b/tests/behaviour/connection/database/BUILD index 78f8639b..78cdf802 100644 --- a/tests/behaviour/connection/database/BUILD +++ b/tests/behaviour/connection/database/BUILD @@ -1,5 +1,25 @@ +# +# 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. +# + package(default_visibility = ["//tests/behaviour:__subpackages__"]) load("//tools:behave_rule.bzl", "py_behave_test") +load("@graknlabs_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") py_library( name = "steps", @@ -20,3 +40,10 @@ py_library( # ], # size = "small", #) + +checkstyle_test( + name = "checkstyle", + include = glob(["*"]), + license_type = "apache", + size = "small", +) diff --git a/tests/behaviour/connection/session/BUILD b/tests/behaviour/connection/session/BUILD index b7519d55..f51af388 100644 --- a/tests/behaviour/connection/session/BUILD +++ b/tests/behaviour/connection/session/BUILD @@ -1,5 +1,25 @@ +# +# 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. +# + package(default_visibility = ["//tests/behaviour:__subpackages__"]) load("//tools:behave_rule.bzl", "py_behave_test") +load("@graknlabs_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") py_library( name = "steps", @@ -22,3 +42,10 @@ py_behave_test( ], size = "small", ) + +checkstyle_test( + name = "checkstyle", + include = glob(["*"]), + license_type = "apache", + size = "small", +) diff --git a/tests/behaviour/connection/transaction/BUILD b/tests/behaviour/connection/transaction/BUILD index d17f2155..5ab357a9 100644 --- a/tests/behaviour/connection/transaction/BUILD +++ b/tests/behaviour/connection/transaction/BUILD @@ -1,5 +1,25 @@ +# +# 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. +# + package(default_visibility = ["//tests/behaviour:__subpackages__"]) load("//tools:behave_rule.bzl", "py_behave_test") +load("@graknlabs_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") py_library( name = "steps", @@ -23,3 +43,10 @@ py_behave_test( ], size = "small", ) + +checkstyle_test( + name = "checkstyle", + include = glob(["*"]), + license_type = "apache", + size = "small", +) diff --git a/tools/BUILD b/tools/BUILD index e69de29b..25baa638 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -0,0 +1,27 @@ +# +# 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. +# + +load("@graknlabs_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") + +checkstyle_test( + name = "checkstyle", + include = glob(["*"]), + license_type = "apache", + size = "small", +) diff --git a/tools/behave_rule.bzl b/tools/behave_rule.bzl index 950361a9..69d3792d 100644 --- a/tools/behave_rule.bzl +++ b/tools/behave_rule.bzl @@ -1,3 +1,22 @@ +# +# 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. +# + # ============================================================================= # Description: Adds a test rule for the BDD tool behave to the bazel rule set. # Knowledge: From 2f1f774a13edd93bb76d90263ea987b6c9d6aeed Mon Sep 17 00:00:00 2001 From: Ganeshwara Hananda Date: Thu, 7 Jan 2021 13:06:45 +0000 Subject: [PATCH 16/16] Add missing licenses --- .grabl/automation.yml | 2 +- tests/behaviour/config/parameters.py | 19 +++++++++++++++++++ .../behaviour/connection/connection_steps.py | 19 +++++++++++++++++++ .../connection/database/database_steps.py | 18 ++++++++++++++++++ .../connection/session/session_steps.py | 19 +++++++++++++++++++ .../transaction/transaction_steps.py | 19 +++++++++++++++++++ tests/behaviour/environment.py | 18 ++++++++++++++++++ 7 files changed, 113 insertions(+), 1 deletion(-) diff --git a/.grabl/automation.yml b/.grabl/automation.yml index ad65ed7c..9d889ba3 100644 --- a/.grabl/automation.yml +++ b/.grabl/automation.yml @@ -46,7 +46,7 @@ build: bazel run @graknlabs_dependencies//distribution/artifact:create-netrc bazel build //... bazel run @graknlabs_dependencies//tool/checkstyle:test-coverage - bazel test $(bazel query 'kind(checkstyle_test, //...)') + bazel test $(bazel query 'kind(checkstyle_test, //...)') --test_output=errors # test-concept: # image: graknlabs-ubuntu-20.04 # type: foreground diff --git a/tests/behaviour/config/parameters.py b/tests/behaviour/config/parameters.py index aecb909e..29ceef8b 100644 --- a/tests/behaviour/config/parameters.py +++ b/tests/behaviour/config/parameters.py @@ -1,3 +1,22 @@ +# +# 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. +# + from typing import List from grakn.rpc.transaction import TransactionType diff --git a/tests/behaviour/connection/connection_steps.py b/tests/behaviour/connection/connection_steps.py index 3fcf49d2..11ce1465 100644 --- a/tests/behaviour/connection/connection_steps.py +++ b/tests/behaviour/connection/connection_steps.py @@ -1,3 +1,22 @@ +# +# 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. +# + from behave import * diff --git a/tests/behaviour/connection/database/database_steps.py b/tests/behaviour/connection/database/database_steps.py index 9c03b331..37e761f3 100644 --- a/tests/behaviour/connection/database/database_steps.py +++ b/tests/behaviour/connection/database/database_steps.py @@ -1,3 +1,21 @@ +# +# 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. +# from typing import List from behave import * diff --git a/tests/behaviour/connection/session/session_steps.py b/tests/behaviour/connection/session/session_steps.py index 1c704d98..aab0153f 100644 --- a/tests/behaviour/connection/session/session_steps.py +++ b/tests/behaviour/connection/session/session_steps.py @@ -1,3 +1,22 @@ +# +# 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. +# + from concurrent.futures.thread import ThreadPoolExecutor from functools import partial from typing import List diff --git a/tests/behaviour/connection/transaction/transaction_steps.py b/tests/behaviour/connection/transaction/transaction_steps.py index 7304d882..d548ec6e 100644 --- a/tests/behaviour/connection/transaction/transaction_steps.py +++ b/tests/behaviour/connection/transaction/transaction_steps.py @@ -1,3 +1,22 @@ +# +# 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. +# + from concurrent.futures.thread import ThreadPoolExecutor from functools import partial from typing import Callable, List diff --git a/tests/behaviour/environment.py b/tests/behaviour/environment.py index 693b515b..27a75582 100644 --- a/tests/behaviour/environment.py +++ b/tests/behaviour/environment.py @@ -1,3 +1,21 @@ +# +# 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. +# from behave import * from grakn.client import GraknClient