From a25566152c2b082c53c80289e3bd781246fcc995 Mon Sep 17 00:00:00 2001 From: RagnarW Date: Wed, 23 Dec 2015 15:32:21 +0100 Subject: [PATCH 1/3] Added TCK for python. TCK tests are set to run with runtests.sh --- runtests.sh | 6 + test/tck/__init__.py | 0 test/tck/configure_feature_files.py | 30 ++++ test/tck/environment.py | 23 +++ test/tck/steps/bolt_type_steps.py | 241 ++++++++++++++++++++++++++++ test/tck/tck_util.py | 120 ++++++++++++++ 6 files changed, 420 insertions(+) create mode 100644 test/tck/__init__.py create mode 100644 test/tck/configure_feature_files.py create mode 100644 test/tck/environment.py create mode 100644 test/tck/steps/bolt_type_steps.py create mode 100644 test/tck/tck_util.py diff --git a/runtests.sh b/runtests.sh index d5b823e1e..98e195714 100755 --- a/runtests.sh +++ b/runtests.sh @@ -62,6 +62,7 @@ echo "Running tests with $(python --version)" pip install --upgrade -r ${DRIVER_HOME}/test_requirements.txt echo "" TEST_RUNNER="coverage run -m ${UNITTEST} discover -vfs ${TEST}" +BEHAVE_RUNNER="behave test/tck" if [ ${RUNNING} -eq 1 ] then ${TEST_RUNNER} @@ -73,6 +74,11 @@ else then coverage report --show-missing fi + python -c 'from test.tck.configure_feature_files import *; set_up()' + echo "Feature files downloaded" + neokit/neorun ${NEORUN_OPTIONS} "${BEHAVE_RUNNER}" ${VERSIONS} + python -c 'from test.tck.configure_feature_files import *; clean_up()' + echo "Feature files removed" fi # Exit correctly diff --git a/test/tck/__init__.py b/test/tck/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/tck/configure_feature_files.py b/test/tck/configure_feature_files.py new file mode 100644 index 000000000..21eb04abf --- /dev/null +++ b/test/tck/configure_feature_files.py @@ -0,0 +1,30 @@ +import os +import tarfile +import urllib2 + + +def clean_up(): + dir_path = (os.path.dirname(os.path.realpath(__file__)))#'test/tck/' + files = os.listdir(dir_path) + for f in files: + if not os.path.isdir(f) and ".feature" in f: + os.remove(os.path.join(dir_path, f)) + + +def set_up(): + dir_path = (os.path.dirname(os.path.realpath(__file__))) + feature_url = "https://s3-eu-west-1.amazonaws.com/remoting.neotechnology.com/driver-compliance/tck.tar.gz" + file_name = feature_url.split('/')[-1] + tar = open(file_name, 'w') + response = urllib2.urlopen(feature_url) + block_sz = 1024 + while True: + buffer = response.read(block_sz) + if not buffer: + break + tar.write(buffer) + tar.close() + tar = tarfile.open(file_name) + tar.extractall(dir_path) + tar.close() + os.remove(file_name) diff --git a/test/tck/environment.py b/test/tck/environment.py new file mode 100644 index 000000000..b40770d1b --- /dev/null +++ b/test/tck/environment.py @@ -0,0 +1,23 @@ +import logging + +from test.tck import tck_util +from behave.log_capture import capture + + +def before_all(context): + # -- SET LOG LEVEL: behave --logging-level=ERROR ... + # on behave command-line or in "behave.ini". + context.config.setup_logging() + + +@capture +def after_scenario(context, scenario): + for step in scenario.steps: + if step.status == 'failed': + logging.error("Scenario :'%s' at step: '%s' failed! ", scenario.name, step.name) + logging.debug("Expected result: %s", tck_util.as_cypger_text(context.expected)) + logging.debug("Actual result: %s", tck_util.as_cypger_text(context.results)) + if step.status == 'skipped': + logging.warn("Scenario :'%s' at step: '%s' was skipped! ", scenario.name, step.name) + if step.status == 'passed': + logging.debug("Scenario :'%s' at step: '%s' was passed! ", scenario.name, step.name) diff --git a/test/tck/steps/bolt_type_steps.py b/test/tck/steps/bolt_type_steps.py new file mode 100644 index 000000000..b27430f1e --- /dev/null +++ b/test/tck/steps/bolt_type_steps.py @@ -0,0 +1,241 @@ +from behave import * + +from test.tck import tck_util + +from neo4j.v1.typesystem import Node, Relationship, Path + +use_step_matcher("re") + + +@given("A running database") +def step_impl(context): + return None + # check if running + + +@given("a value (?P.+) of type (?P.+)") +def step_impl(context, Input, BoltType): + context.expected = tck_util.get_bolt_value(BoltType, Input) + + +@given("a value of type (?P.+)") +def step_impl(context, BoltType): + context.expected = tck_util.get_bolt_value(BoltType, u' ') + + +@given("a list value (?P.+) of type (?P.+)") +def step_impl(context, Input, BoltType): + context.expected = tck_util.get_list_from_feature_file(Input, BoltType) + + +@given("an empty list L") +def step_impl(context): + context.L = [] + + +@given("an empty map M") +def step_impl(context): + context.M = {} + + +@given("an empty node N") +def step_impl(context): + context.N = Node() + + +@given("a node N with properties and labels") +def step_impl(context): + context.N = Node({"Person"}, {"name": "Alice", "age": 33}) + + +@given("a String of size (?P\d+)") +def step_impl(context, size): + context.expected = tck_util.get_random_string(int(size)) + + +@given("a List of size (?P\d+) and type (?P.+)") +def step_impl(context, size, Type): + context.expected = tck_util.get_list_of_random_type(int(size), Type) + + +@given("a Map of size (?P\d+) and type (?P.+)") +def step_impl(context, size, Type): + context.expected = tck_util.get_dict_of_random_type(int(size), Type) + + +@step("adding a table of lists to the list L") +def step_impl(context): + for row in context.table: + context.L.append(tck_util.get_list_from_feature_file(row[1], row[0])) + + +@step("adding a table of values to the list L") +def step_impl(context): + for row in context.table: + context.L.append(tck_util.get_bolt_value(row[0], row[1])) + + +@step("adding a table of values to the map M") +def step_impl(context): + for row in context.table: + context.M['a' + str(len(context.M))] = tck_util.get_bolt_value(row[0], row[1]) + + +@step("adding map M to list L") +def step_impl(context): + context.L.append(context.M) + + +@when("adding a table of lists to the map M") +def step_impl(context): + for row in context.table: + context.M['a' + str(len(context.M))] = tck_util.get_list_from_feature_file(row[1], row[0]) + + +@step("adding a copy of map M to map M") +def step_impl(context): + context.M['a' + str(len(context.M))] = context.M.copy() + + +@when("the driver asks the server to echo this value back") +def step_impl(context): + context.results = {} + context.results["as_string"] = tck_util.send_string("RETURN " + tck_util.as_cypger_text(context.expected)) + context.results["as_parameters"] = tck_util.send_parameters("RETURN {input}", {'input': context.expected}) + + +@when("the driver asks the server to echo this list back") +def step_impl(context): + context.expected = context.L + context.results = {} + context.results["as_string"] = tck_util.send_string("RETURN " + tck_util.as_cypger_text(context.expected)) + context.results["as_parameters"] = tck_util.send_parameters("RETURN {input}", {'input': context.expected}) + + +@when("the driver asks the server to echo this map back") +def step_impl(context): + context.expected = context.M + context.results = {} + context.results["as_string"] = tck_util.send_string("RETURN " + tck_util.as_cypger_text(context.expected)) + context.results["as_parameters"] = tck_util.send_parameters("RETURN {input}", {'input': context.expected}) + + +@when("the driver asks the server to echo this node back") +def step_impl(context): + context.expected = context.N + context.results = {} + context.results["as_parameters"] = tck_util.send_parameters("RETURN {input}", {'input': context.expected}) + + +@then("the result returned from the server should be a single record with a single value") +def step_impl(context): + assert len(context.results) > 0 + for result in context.results.values(): + assert len(result) == 1 + assert len(result[0]) == 1 + + +@step("the value given in the result should be the same as what was sent") +def step_impl(context): + assert len(context.results) > 0 + for result in context.results.values(): + result_value = result[0].values()[0] + assert result_value == context.expected + + +@step("the node value given in the result should be the same as what was sent") +def step_impl(context): + assert len(context.results) > 0 + for result in context.results.values(): + result_value = result[0].values()[0] + assert result_value == context.expected + assert result_value.labels == context.expected.labels + assert result_value.keys() == context.expected.keys() + assert result_value.values() == context.expected.values() + assert result_value.items() == context.expected.items() + assert len(result_value) == len(context.expected) + assert iter(result_value) == iter(context.expected) + + +##CURRENTLY NOT SUPPORTED IN PYTHON DRIVERS + +@given("a relationship R") +def step_impl(context): + alice = Node({"Person"}, {"name": "Alice", "age": 33}) + bob = Node({"Person"}, {"name": "Bob", "age": 44}) + context.R = Relationship(alice, bob, "KNOWS", {"since": 1999}) + + +@when("the driver asks the server to echo this relationship R back") +def step_impl(context): + context.expected = context.R + context.results = {} + context.results["as_parameters"] = tck_util.send_parameters("RETURN {input}", {'input': context.expected}) + + +@step("the relationship value given in the result should be the same as what was sent") +def step_impl(context): + assert len(context.results) > 0 + for result in context.results.values(): + result_value = result[0].values()[0] + assert result_value == context.expected + assert result_value.start == context.expected.start + assert result_value.type == context.expected.type + assert result_value.end == context.expected.end + assert result_value.items() == context.expected.items() + assert result_value.keys() == context.expected.keys() + assert result_value.values() == context.expected.values() + + +@given("a zero length path P") +def step_impl(context): + context.P = Path(Node({"Person"}, {"name": "Alice", "age": 33})) + + +@given("a arbitrary long path P") +def step_impl(context): + alice = Node({"Person"}, {"name": "Alice", "age": 33}) + bob = Node({"Person"}, {"name": "Bob", "age": 44}) + carol = Node({"Person"}, {"name": "Carol", "age": 55}) + alice_knows_bob = Relationship(alice, bob, "KNOWS", {"since": 1999}) + carol_dislikes_bob = Relationship(carol, bob, "DISLIKES") + context.P = Path(alice, alice_knows_bob, bob, carol_dislikes_bob, carol) + + +@when("the driver asks the server to echo this path back") +def step_impl(context): + context.expected = context.P + context.results = {} + context.results["as_parameters"] = tck_util.send_parameters("RETURN {input}", {'input': context.expected}) + + +@step("the path value given in the result should be the same as what was sent") +def step_impl(context): + assert len(context.results) > 0 + for result in context.results.values(): + result_value = result[0].values()[0] + assert result_value == context.expected + assert result_value.start == context.expected.start + assert result_value.end == context.expected.end + assert result_value.nodes == context.expected.nodes + assert result_value.relationships == context.expected.relationships + assert list(result_value) == list(context.expected) + + +@given("a Node with great amount of properties and labels") +def step_impl(context): + context.N = Node(tck_util.get_list_of_random_type(1000, "String"), + tck_util.get_dict_of_random_type(1000, "String")) + + +@given("a path P of size (?P\d+)") +def step_impl(context, size): + nodes_and_rels = [Node({tck_util.get_random_string(5)}, tck_util.get_dict_of_random_type(3, "String"))] + for i in range(1, int(12)): + n = nodes_and_rels.append(Node({tck_util.get_random_string(5)}, tck_util.get_dict_of_random_type(3, "String"))) + r = Relationship(nodes_and_rels[-1], n, tck_util.get_random_string(4), + tck_util.get_dict_of_random_type(3, "String")) + nodes_and_rels.append(r) + nodes_and_rels.append(n) + + context.P = Path(nodes_and_rels[0], nodes_and_rels[1:]) diff --git a/test/tck/tck_util.py b/test/tck/tck_util.py new file mode 100644 index 000000000..b192e9986 --- /dev/null +++ b/test/tck/tck_util.py @@ -0,0 +1,120 @@ +import string +import random + +import sys + +from neo4j.v1 import GraphDatabase + +driver = GraphDatabase.driver("bolt://localhost") + + +def send_string(text): + session = driver.session() + result = session.run(text) + session.close() + return result + + +def send_parameters(statement, parameters): + session = driver.session() + result = session.run(statement, parameters) + session.close() + return result + + +def get_bolt_value(type, value): + if type == 'Integer': + return int(value) + if type == 'Float': + return float(value) + if type == 'String': + return value + if type == 'Null': + return None + if type == 'Boolean': + return bool(value) + raise ValueError('No such type : %s', type) + + +def as_cypger_text(expected): + if expected is None: + return "Null" + if isinstance(expected, unicode): + return '"' + expected + '"' + if isinstance(expected, float): + return repr(expected).replace('+', '') + if isinstance(expected, list): + l = u'[' + for i, val in enumerate(expected): + l += as_cypger_text(val) + if i < len(expected)-1: + l+= u',' + l += u']' + return l + if isinstance(expected, dict): + d = u'{' + for i, (key, val) in enumerate(expected.items()): + d += unicode(key) + ':' + d += as_cypger_text(val) + if i < len(expected.items())-1: + d+= u',' + d += u'}' + return d + else: + return unicode(expected) + + +def get_list_from_feature_file(string_list, bolt_type): + inputs = string_list.strip('[]') + inputs = inputs.split(',') + list_to_return = [] + for value in inputs: + list_to_return.append(get_bolt_value(bolt_type, value)) + return list_to_return + + +def get_random_string(size): + return u''.join( + random.SystemRandom().choice(list(string.ascii_uppercase + string.digits + string.ascii_lowercase)) for _ in + range(size)) + + +def get_random_bool(): + return bool(random.randint(0, 1)) + + +def _get_random_func(type): + def get_none(): + return None + + if type == 'Integer': + fu = random.randint + args = [-sys.maxint - 1, sys.maxint] + elif type == 'Float': + fu = random.random + args = [] + elif type == 'String': + fu = get_random_string + args = [3] + elif type == 'Null': + fu = get_none + args = [] + elif type == 'Boolean': + fu = get_random_bool + args = [] + else: + raise ValueError('No such type : %s', type) + return (fu, args) + + +def get_list_of_random_type(size, type): + fu, args = _get_random_func(type) + return [fu(*args) for _ in range(size)] + + +def get_dict_of_random_type(size, type): + fu, args = _get_random_func(type) + map = {} + for i in range(size): + map['a' + str(i)] = fu(*args) + return map From f0c01e3b6c17c1ebb9718762ddf95355e885d2f4 Mon Sep 17 00:00:00 2001 From: RagnarW Date: Tue, 5 Jan 2016 14:23:53 +0100 Subject: [PATCH 2/3] Now supports both Python 2.7 and 3.5. Polished code from feedback on prvious commit --- test/tck/configure_feature_files.py | 34 ++++++++++------ test/tck/environment.py | 4 +- test/tck/steps/bolt_type_steps.py | 62 +++++++++++------------------ test/tck/tck_util.py | 37 +++++++++-------- 4 files changed, 66 insertions(+), 71 deletions(-) diff --git a/test/tck/configure_feature_files.py b/test/tck/configure_feature_files.py index 93cae074c..9face3062 100644 --- a/test/tck/configure_feature_files.py +++ b/test/tck/configure_feature_files.py @@ -1,7 +1,5 @@ import os import tarfile -import urllib2 - def clean_up(): dir_path = (os.path.dirname(os.path.realpath(__file__))) @@ -13,18 +11,28 @@ def clean_up(): def set_up(): dir_path = (os.path.dirname(os.path.realpath(__file__))) - feature_url = "https://s3-eu-west-1.amazonaws.com/remoting.neotechnology.com/driver-compliance/tck.tar.gz" - file_name = feature_url.split('/')[-1] - tar = open(file_name, 'w') - response = urllib2.urlopen(feature_url) - block_sz = 1024 - while True: - buffer = response.read(block_sz) - if not buffer: - break - tar.write(buffer) - tar.close() + url = "https://s3-eu-west-1.amazonaws.com/remoting.neotechnology.com/driver-compliance/tck.tar.gz" + file_name = url.split('/')[-1] + _download_tar(url,file_name) + tar = tarfile.open(file_name) tar.extractall(dir_path) tar.close() os.remove(file_name) + + +def _download_tar(url, file_name): + try: + import urllib2 + tar = open(file_name, 'w') + response = urllib2.urlopen(url) + block_sz = 1024 + while True: + buffer = response.read(block_sz) + if not buffer: + break + tar.write(buffer) + tar.close() + except ImportError: + from urllib import request + request.urlretrieve(url, file_name) \ No newline at end of file diff --git a/test/tck/environment.py b/test/tck/environment.py index b40770d1b..3ce81c9d1 100644 --- a/test/tck/environment.py +++ b/test/tck/environment.py @@ -15,8 +15,8 @@ def after_scenario(context, scenario): for step in scenario.steps: if step.status == 'failed': logging.error("Scenario :'%s' at step: '%s' failed! ", scenario.name, step.name) - logging.debug("Expected result: %s", tck_util.as_cypger_text(context.expected)) - logging.debug("Actual result: %s", tck_util.as_cypger_text(context.results)) + logging.debug("Expected result: %s", tck_util.as_cypher_text(context.expected)) + logging.debug("Actual result: %s", tck_util.as_cypher_text(context.results)) if step.status == 'skipped': logging.warn("Scenario :'%s' at step: '%s' was skipped! ", scenario.name, step.name) if step.status == 'passed': diff --git a/test/tck/steps/bolt_type_steps.py b/test/tck/steps/bolt_type_steps.py index 9f1a04b8c..8b67f758c 100644 --- a/test/tck/steps/bolt_type_steps.py +++ b/test/tck/steps/bolt_type_steps.py @@ -2,8 +2,6 @@ from test.tck import tck_util -from neo4j.v1.typesystem import Node, Relationship, Path - use_step_matcher("re") @@ -13,19 +11,19 @@ def step_impl(context): # check if running -@given("a value (?P.+) of type (?P.+)") -def step_impl(context, Input, BoltType): - context.expected = tck_util.get_bolt_value(BoltType, Input) +@given("a value (?P.+) of type (?P.+)") +def step_impl(context, input, bolt_type): + context.expected = tck_util.get_bolt_value(bolt_type, input) -@given("a value of type (?P.+)") -def step_impl(context, BoltType): - context.expected = tck_util.get_bolt_value(BoltType, u' ') +@given("a value of type (?P.+)") +def step_impl(context, bolt_type): + context.expected = tck_util.get_bolt_value(bolt_type, u' ') -@given("a list value (?P.+) of type (?P.+)") -def step_impl(context, Input, BoltType): - context.expected = tck_util.get_list_from_feature_file(Input, BoltType) +@given("a list value (?P.+) of type (?P.+)") +def step_impl(context, input, bolt_type): + context.expected = tck_util.get_list_from_feature_file(input, bolt_type) @given("an empty list L") @@ -43,14 +41,14 @@ def step_impl(context, size): context.expected = tck_util.get_random_string(int(size)) -@given("a List of size (?P\d+) and type (?P.+)") -def step_impl(context, size, Type): - context.expected = tck_util.get_list_of_random_type(int(size), Type) +@given("a List of size (?P\d+) and type (?P.+)") +def step_impl(context, size, type): + context.expected = tck_util.get_list_of_random_type(int(size), type) -@given("a Map of size (?P\d+) and type (?P.+)") -def step_impl(context, size, Type): - context.expected = tck_util.get_dict_of_random_type(int(size), Type) +@given("a Map of size (?P\d+) and type (?P.+)") +def step_impl(context, size, type): + context.expected = tck_util.get_dict_of_random_type(int(size), type) @step("adding a table of lists to the list L") @@ -68,7 +66,7 @@ def step_impl(context): @step("adding a table of values to the map M") def step_impl(context): for row in context.table: - context.M['a' + str(len(context.M))] = tck_util.get_bolt_value(row[0], row[1]) + context.M['a%d' % len(context.M)] = tck_util.get_bolt_value(row[0], row[1]) @step("adding map M to list L") @@ -79,18 +77,18 @@ def step_impl(context): @when("adding a table of lists to the map M") def step_impl(context): for row in context.table: - context.M['a' + str(len(context.M))] = tck_util.get_list_from_feature_file(row[1], row[0]) + context.M['a%d' % len(context.M)] = tck_util.get_list_from_feature_file(row[1], row[0]) @step("adding a copy of map M to map M") def step_impl(context): - context.M['a' + str(len(context.M))] = context.M.copy() + context.M['a%d' % len(context.M)] = context.M.copy() @when("the driver asks the server to echo this value back") def step_impl(context): context.results = {} - context.results["as_string"] = tck_util.send_string("RETURN " + tck_util.as_cypger_text(context.expected)) + context.results["as_string"] = tck_util.send_string("RETURN " + tck_util.as_cypher_text(context.expected)) context.results["as_parameters"] = tck_util.send_parameters("RETURN {input}", {'input': context.expected}) @@ -98,7 +96,7 @@ def step_impl(context): def step_impl(context): context.expected = context.L context.results = {} - context.results["as_string"] = tck_util.send_string("RETURN " + tck_util.as_cypger_text(context.expected)) + context.results["as_string"] = tck_util.send_string("RETURN " + tck_util.as_cypher_text(context.expected)) context.results["as_parameters"] = tck_util.send_parameters("RETURN {input}", {'input': context.expected}) @@ -106,13 +104,13 @@ def step_impl(context): def step_impl(context): context.expected = context.M context.results = {} - context.results["as_string"] = tck_util.send_string("RETURN " + tck_util.as_cypger_text(context.expected)) + context.results["as_string"] = tck_util.send_string("RETURN " + tck_util.as_cypher_text(context.expected)) context.results["as_parameters"] = tck_util.send_parameters("RETURN {input}", {'input': context.expected}) @then("the result returned from the server should be a single record with a single value") def step_impl(context): - assert len(context.results) > 0 + assert context.results for result in context.results.values(): assert len(result) == 1 assert len(result[0]) == 1 @@ -123,18 +121,4 @@ def step_impl(context): assert len(context.results) > 0 for result in context.results.values(): result_value = result[0].values()[0] - assert result_value == context.expected - - -@step("the node value given in the result should be the same as what was sent") -def step_impl(context): - assert len(context.results) > 0 - for result in context.results.values(): - result_value = result[0].values()[0] - assert result_value == context.expected - assert result_value.labels == context.expected.labels - assert result_value.keys() == context.expected.keys() - assert result_value.values() == context.expected.values() - assert result_value.items() == context.expected.items() - assert len(result_value) == len(context.expected) - assert iter(result_value) == iter(context.expected) \ No newline at end of file + assert result_value == context.expected \ No newline at end of file diff --git a/test/tck/tck_util.py b/test/tck/tck_util.py index b192e9986..155ec08b6 100644 --- a/test/tck/tck_util.py +++ b/test/tck/tck_util.py @@ -1,7 +1,6 @@ import string import random - -import sys +from neo4j.v1 import compat from neo4j.v1 import GraphDatabase @@ -28,25 +27,25 @@ def get_bolt_value(type, value): if type == 'Float': return float(value) if type == 'String': - return value + return to_unicode(value) if type == 'Null': return None if type == 'Boolean': return bool(value) - raise ValueError('No such type : %s', type) + raise ValueError('No such type : %s' % type) -def as_cypger_text(expected): +def as_cypher_text(expected): if expected is None: return "Null" - if isinstance(expected, unicode): - return '"' + expected + '"' + if isinstance(expected, (str, compat.string)): + return '"' + expected + '"' if isinstance(expected, float): return repr(expected).replace('+', '') if isinstance(expected, list): l = u'[' for i, val in enumerate(expected): - l += as_cypger_text(val) + l += as_cypher_text(val) if i < len(expected)-1: l+= u',' l += u']' @@ -54,14 +53,14 @@ def as_cypger_text(expected): if isinstance(expected, dict): d = u'{' for i, (key, val) in enumerate(expected.items()): - d += unicode(key) + ':' - d += as_cypger_text(val) + d += to_unicode(key) + ':' + d += as_cypher_text(val) if i < len(expected.items())-1: d+= u',' d += u'}' return d else: - return unicode(expected) + return to_unicode(expected) def get_list_from_feature_file(string_list, bolt_type): @@ -89,7 +88,7 @@ def get_none(): if type == 'Integer': fu = random.randint - args = [-sys.maxint - 1, sys.maxint] + args = [-9223372036854775808, 9223372036854775808] elif type == 'Float': fu = random.random args = [] @@ -103,7 +102,7 @@ def get_none(): fu = get_random_bool args = [] else: - raise ValueError('No such type : %s', type) + raise ValueError('No such type : %s' % type) return (fu, args) @@ -114,7 +113,11 @@ def get_list_of_random_type(size, type): def get_dict_of_random_type(size, type): fu, args = _get_random_func(type) - map = {} - for i in range(size): - map['a' + str(i)] = fu(*args) - return map + return {'a%d' % i: fu(*args) for i in range(size)} + +def to_unicode(val): + try: + return unicode(val) + except NameError: + return str(val) + From 89a0324fcbf796b8cd651815f259fd6bb22122a4 Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Thu, 7 Jan 2016 13:12:08 +0000 Subject: [PATCH 3/3] Added behave to requirements --- test_requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test_requirements.txt b/test_requirements.txt index 1e90d7db5..85278c304 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -1,2 +1,3 @@ +behave coverage teamcity-messages