From 683e39e4a26e6d2c97a66fe41a155af0c6b9d54c Mon Sep 17 00:00:00 2001 From: RagnarW Date: Mon, 15 Feb 2016 16:27:54 +0100 Subject: [PATCH 1/5] Updated tck tests to use result parser in all Featuer files --- test/tck/resultparser.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/tck/resultparser.py diff --git a/test/tck/resultparser.py b/test/tck/resultparser.py new file mode 100644 index 000000000..e69de29bb From bb758ab83185a209e0398fc7eff0b8e6edb0a258 Mon Sep 17 00:00:00 2001 From: RagnarW Date: Mon, 15 Feb 2016 16:29:53 +0100 Subject: [PATCH 2/5] Updated tests after new feature file design --- test/tck/configure_feature_files.py | 2 +- test/tck/environment.py | 2 +- test/tck/resultparser.py | 202 ++++++++++++++++++++ test/tck/steps/bolt_compability_steps.py | 144 ++------------- test/tck/steps/cypher_compability_steps.py | 204 +++------------------ 5 files changed, 247 insertions(+), 307 deletions(-) diff --git a/test/tck/configure_feature_files.py b/test/tck/configure_feature_files.py index eec48766f..e0c2768ac 100644 --- a/test/tck/configure_feature_files.py +++ b/test/tck/configure_feature_files.py @@ -56,4 +56,4 @@ def _download_tar(url, file_name): tar.close() except ImportError: from urllib import request - request.urlretrieve(url, file_name) \ No newline at end of file + request.urlretrieve(url, file_name) diff --git a/test/tck/environment.py b/test/tck/environment.py index 3e08a3a56..222b71ebd 100644 --- a/test/tck/environment.py +++ b/test/tck/environment.py @@ -34,4 +34,4 @@ def before_feature(context, feature): def before_scenario(context, scenario): if "reset_database" in scenario.tags: - tck_util.send_string("MATCH (n) DETACH DELETE n") \ No newline at end of file + tck_util.send_string("MATCH (n) DETACH DELETE n") diff --git a/test/tck/resultparser.py b/test/tck/resultparser.py index e69de29bb..cc257eb59 100644 --- a/test/tck/resultparser.py +++ b/test/tck/resultparser.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +# Copyright (c) 2002-2016 "Neo Technology," +# Network Engine for Objects in Lund AB [http://neotechnology.com] +# +# This file is part of Neo4j. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import re +from neo4j.v1 import Node, Relationship, Path +from tck_util import Value + + +def parse_values_to_comparable(row): + return _parse(row, True) + + +def parse_values(row): + return _parse(row, False) + + +def _parse(row, comparable=False): + if is_array(row): + values = get_array(row) + result = [] + for val in values: + result.append(_parse(val, comparable)) + return result + elif is_map(row): + if comparable: + raise ValueError("No support for pasing maps of Node/Rels/paths atm") + return get_map(row) + else: + if comparable: + return value_to_comparable_object(row) + else: + return value_to_object(row) + + +def is_array(val): + return val[0] == '[' and val[-1] == ']' and val[1] != ':' + + +def get_array(val): + return val[1:-1].split(',') + + +def is_map(val): + return val[0] == '{' and val[-1] == '}' + + +def get_map(val): + return json.loads(val) + + +def value_to_comparable_object(val): + return Value(value_to_object(val)) + + +def value_to_object(val): + val = val.strip() + print("VAL") + print(val) + PATH = '^(<\().*(\)>)$' + NODE = '^(\().*(\))$' + RELATIONSHIP = '^(\[:).*(\])$' + INTEGER = '^(-?[0-9]+)$' + STRING = '^(").*(")$' + NULL = '^null$' + BOOLEAN = '^(false|true)$' + # TEST FLOAT AFTER INT + FLOAT = '^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$' + if re.match(PATH, val): + return get_path(val)[0] + if re.match(RELATIONSHIP, val): + return get_relationship(val)[0] + if re.match(NODE, val): + return get_node(val)[0] + if re.match(NULL, val): + return None + if re.match(BOOLEAN, val): + return bool(val) + if re.match(STRING, val): + return val[1:-1] + if re.match(INTEGER, val): + return int(val) + if re.match(FLOAT, val): + return float(val) + raise TypeError("String value does not match any type. Got: %s" % val) + + +def get_properties(prop): + split_at = prop.find('{') + if split_at != -1: + n_start, prop = split_two(prop, split_at) + split_at = prop.find('}') + if split_at == -1: + raise ValueError( + "Node properties not correctly representetd. Found starrting '{' but no ending '}' in : %s" % prop) + prop, n_end = split_two(prop, split_at + 1) + n = n_start + n_end + properties = json.loads(prop) + return properties, n + else: + return {}, prop + + +def get_labels(labels): + labels = labels.split(':') + if len(labels) == 1: + return [], labels[0] + n = labels[0] + labels = labels[1:] + if labels[-1].find(' ') != -1: + labels[-1], n_end = split_two(labels[-1], labels[-1].find(' ')) + n += n_end + return labels, n + + +def get_labels_and_properties(n): + prop, n = get_properties(n) + labels, n = get_labels(n) + return labels, prop, n + + +def split_two(string_entity, split_at): + return string_entity[:split_at], string_entity[split_at:] + + +def get_node(string_entity): + if string_entity[0] != "(": + raise ValueError("Excpected node which shuld start with '('. Got: %s" % string_entity[0]) + split_at = string_entity.find(')') + if split_at == -1: + raise ValueError("Excpected node which shuld end with ')'. Found no such character in: %s" % string_entity) + n, string_entity = split_two(string_entity, split_at + 1) + n = n[1:-1] + labels, properties, n = get_labels_and_properties(n) + node = Node(labels=labels, properties=properties) + return node, string_entity + + +def get_relationship(string_entity): + point_up = None + if string_entity[:3] == "<-[": + point_up = False + string_entity = string_entity[3:] + rel = string_entity[:string_entity.find(']-')] + string_entity = string_entity[string_entity.find(']-') + 2:] + elif string_entity[:2] == "-[": + point_up = True + string_entity = string_entity[2:] + rel = string_entity[:string_entity.find(']-')] + string_entity = string_entity[string_entity.find(']->') + 3:] + elif string_entity[0] == "[": + string_entity = string_entity[1:] + rel = string_entity[:string_entity.find(']')] + string_entity = string_entity[string_entity.find(']') + 1:] + else: + raise ValueError("Cant identify relationship from: %s" % string_entity) + type, properties, str = get_labels_and_properties(rel) + if len(type) > 1: + raise ValueError("Relationship can only have one type. Got: %s" % type) + relationship = Relationship(1, 2, type[0], properties=properties) + return relationship, string_entity, point_up + + +def get_path(string_path): + id = 0 + string_path = string_path[1:-1] + n, string_path = get_node(string_path) + list_of_nodes_and_rel = [n] + n.identity = ++id + while string_path != '': + r, string_path, point_up = get_relationship(string_path) + n, string_path = get_node(string_path) + n.identity = ++id + if point_up: + r.start = list_of_nodes_and_rel[-1].identity + r.end = n.identity + r.identity = 0 + else: + r.start = n.identity + r.end = list_of_nodes_and_rel[-1].identity + r.identity = 0 + list_of_nodes_and_rel.append(r) + list_of_nodes_and_rel.append(n) + path = Path(list_of_nodes_and_rel[0], *list_of_nodes_and_rel[1:]) + return path, string_path diff --git a/test/tck/steps/bolt_compability_steps.py b/test/tck/steps/bolt_compability_steps.py index a9cf16174..ea2b47f6e 100644 --- a/test/tck/steps/bolt_compability_steps.py +++ b/test/tck/steps/bolt_compability_steps.py @@ -20,10 +20,11 @@ import random import string +import copy from behave import * -from neo4j.v1 import GraphDatabase +from test.tck.resultparser import parse_values from test.tck.tck_util import to_unicode, Type, send_string, send_parameters, string_to_type from neo4j.v1 import compat @@ -35,29 +36,31 @@ def step_impl(context): send_string("RETURN 1") -@given("a value (?P.+) of type (?P.+)") -def step_impl(context, input, bolt_type): - context.expected = get_bolt_value(string_to_type(bolt_type), input) +@given("a value (?P.+)") +def step_impl(context, input): + context.expected = parse_values(input) -@given("a value of type (?P.+)") -def step_impl(context, bolt_type): - context.expected = get_bolt_value(string_to_type(bolt_type), u' ') +@given("a list containing") +def step_impl(context): + context.expected = [parse_values(row[0]) for row in context.table.rows] -@given("a list value (?P.+) of type (?P.+)") -def step_impl(context, input, bolt_type): - context.expected = get_list_from_feature_file(input, string_to_type(bolt_type)) +@step("adding this list to itself") +def step_impl(context): + clone = context.expected[:] + context.expected.append(clone) -@given("an empty list L") +@given("a map containing") def step_impl(context): - context.L = [] + context.expected = {parse_values(row[0]): parse_values(row[1]) for row in context.table.rows} -@given("an empty map M") -def step_impl(context): - context.M = {} +@step('adding this map to itself with key "(?P.+)"') +def step_impl(context, key): + clone = copy.deepcopy(context.expected) + context.expected[key] = clone @given("a String of size (?P\d+)") @@ -75,56 +78,8 @@ def step_impl(context, size, type): context.expected = get_dict_of_random_type(int(size), string_to_type(type)) -@step("adding a table of lists to the list L") -def step_impl(context): - for row in context.table: - context.L.append(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(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%d' % len(context.M)] = 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%d' % len(context.M)] = 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%d' % len(context.M)] = context.M.copy() - - -@when("the driver asks the server to echo this value back") -def step_impl(context): - context.results = {"as_string": send_string("RETURN " + as_cypher_text(context.expected)), - "as_parameters": 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 = {"as_string": send_string("RETURN " + as_cypher_text(context.expected)), - "as_parameters": 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 +@when("the driver asks the server to echo this (?P.+) back") +def step_impl(context, unused): context.results = {"as_string": send_string("RETURN " + as_cypher_text(context.expected)), "as_parameters": send_parameters("RETURN {input}", {'input': context.expected})} @@ -137,56 +92,6 @@ def step_impl(context): assert result_value == context.expected -@given("A driver containing a session pool of size (?P\d+)") -def step_impl(context, size): - context.driver = GraphDatabase.driver("bolt://localhost", max_pool_size=1) - - -@when("acquiring a session from the driver") -def step_impl(context): - context.session = context.driver.session() - - -@step('with the session running the Cypher statement "(?P.+)"') -def step_impl(context, statement): - context.cursor = context.session.run(statement) - - -@step("pulling the result records") -def step_impl(context): - context.cursor.consume() - - -@then("acquiring a session from the driver should not be possible") -def step_impl(context): - try: - context.session = context.driver.session() - except: - assert True - else: - assert False - - -@then("acquiring a session from the driver should be possible") -def step_impl(context): - _ = context.driver.session() - assert True - - -def get_bolt_value(type, value): - if type == Type.INTEGER: - return int(value) - if type == Type.FLOAT: - return float(value) - if type == Type.STRING: - return to_unicode(value) - if type == Type.NULL: - return None - if type == Type.BOOLEAN: - return bool(value) - raise ValueError('No such type : %s' % type) - - def as_cypher_text(expected): if expected is None: return "Null" @@ -215,15 +120,6 @@ def as_cypher_text(expected): return to_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 diff --git a/test/tck/steps/cypher_compability_steps.py b/test/tck/steps/cypher_compability_steps.py index a2baeb91b..9b3214c4d 100644 --- a/test/tck/steps/cypher_compability_steps.py +++ b/test/tck/steps/cypher_compability_steps.py @@ -18,219 +18,57 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json - from behave import * -from neo4j.v1 import compat, Node, Relationship, Path -from test.tck.tck_util import Value, Type, string_to_type, send_string, send_parameters +from test.tck.tck_util import Value, send_string, send_parameters +from test.tck.resultparser import parse_values, parse_values_to_comparable use_step_matcher("re") -@given("init: (?P.+);") +@given("init: (?P.+)") def step_impl(context, statement): send_string(statement) -@when("running: (?P.+);") +@when("running: (?P.+)") def step_impl(context, statement): context.results = {"as_string": send_string(statement)} -@then("result should be (?P.+)\(s\)") -def step_impl(context, type): - result = context.results["as_string"] - given = driver_result_to_comparable_result(result) - expected = table_to_comparable_result(context.table, type) - if not unordered_equal(given, expected): - raise Exception("%s does not match given: \n%s expected: \n%s" % (type, given, expected)) - - -@then("result should be empty") -def step_impl(context): - assert context.results - for result in context.results.values(): - assert len(result) == 0 - - -@then("result should map to types") -def step_impl(context): - keys = context.table.headings - values = context.table.rows[0] - context.types = {keys[i]: values[i] for i in range(len(keys))} - - -@then("result should be mixed") +@then("result") def step_impl(context): result = context.results["as_string"] given = driver_result_to_comparable_result(result) - expected = table_to_comparable_result(context.table, context.types) - if given != expected: - raise Exception("Mixed response does not match given: %s expected: %s" % (given, expected)) + expected = table_to_comparable_result(context.table) + if not unordered_equal(given, expected): + raise Exception("Does not match given: \n%s expected: \n%s" % (given, expected)) -@when('running parametrized: (?P.+);') +@when('running parametrized: (?P.+)') def step_impl(context, statement): - parameters = {} - keys = context.table.headings assert len(context.table.rows) == 1 - values = context.table[0] - for i, v in enumerate(values): - if v[0] == '"': - val = v[1:-1] - else: - val = int(v) - parameters[keys[i]] = val + keys = context.table.headings + values = context.table.rows[0] + parameters = {keys[i]: parse_values(values[i]) for i in range(len(keys))} context.results = {"as_string": send_parameters(statement, parameters)} -def get_properties(prop): - split_at = prop.find('{') - if split_at != -1: - n_start, prop = split_two(prop, split_at) - split_at = prop.find('}') - if split_at == -1: - raise ValueError( - "Node properties not correctly representetd. Found starrting '{' but no ending '}' in : %s" % prop) - prop, n_end = split_two(prop, split_at + 1) - n = n_start + n_end - properties = json.loads(prop) - return properties, n - else: - return {}, prop - - -def get_labels(labels): - labels = labels.split(':') - if len(labels) == 1: - return [], labels[0] - n = labels[0] - labels = labels[1:] - if labels[-1].find(' ') != -1: - labels[-1], n_end = split_two(labels[-1], labels[-1].find(' ')) - n += n_end - return labels, n - - -def get_labels_and_properties(n): - prop, n = get_properties(n) - labels, n = get_labels(n) - return labels, prop, n - - -def split_two(string_entity, split_at): - return string_entity[:split_at], string_entity[split_at:] - - -def get_node(string_entity): - if string_entity is None: - return None, '' - if string_entity[0] != "(": - raise ValueError("Excpected node which shuld start with '('. Got: %s" % string_entity[0]) - split_at = string_entity.find(')') - if split_at == -1: - raise ValueError("Excpected node which shuld end with ')'. Found no such character in: %s" % string_entity) - n, string_entity = split_two(string_entity, split_at + 1) - n = n[1:-1] - labels, properties, n = get_labels_and_properties(n) - node = Node(labels=labels, properties=properties) - return node, string_entity - - -def get_relationship(string_entity): - point_up = None - if string_entity[:3] == "<-[": - point_up = False - string_entity = string_entity[3:] - rel = string_entity[:string_entity.find(']-')] - string_entity = string_entity[string_entity.find(']-') + 2:] - elif string_entity[:2] == "-[": - point_up = True - string_entity = string_entity[2:] - rel = string_entity[:string_entity.find(']-')] - string_entity = string_entity[string_entity.find(']->') + 3:] - elif string_entity[0] == "[": - string_entity = string_entity[1:] - rel = string_entity[:string_entity.find(']')] - string_entity = string_entity[string_entity.find(']') + 1:] - else: - raise ValueError("Cant identify relationship from: %s" % string_entity) - type, properties, str = get_labels_and_properties(rel) - if len(type) > 1: - raise ValueError("Relationship can only have one type. Got: %s" % type) - relationship = Relationship(1, 2, type[0], properties=properties) - return relationship, string_entity, point_up - - -def get_path(string_path): - n, string_path = get_node(string_path) - list_of_nodes_and_rel = [n] - n.identity = 0 - while string_path != '': - r, string_path, point_up = get_relationship(string_path) - n, string_path = get_node(string_path) - n.identity = len(list_of_nodes_and_rel) - if point_up: - r.start = list_of_nodes_and_rel[-1].identity - r.end = n.identity - else: - r.start = n.identity - r.end = list_of_nodes_and_rel[-1].identity - list_of_nodes_and_rel.append(r) - list_of_nodes_and_rel.append(n) - path = Path(list_of_nodes_and_rel[0], *list_of_nodes_and_rel[1:]) - return path, string_path - - -def _string_value_to_comparable(val, type): - def get_val(v): - if type == Type.INTEGER: - return Value(int(v)) - elif type == Type.STRING: - return Value(v) - elif type == Type.NODE: - return Value(get_node(v)[0]) - elif type == Type.RELATIONSHIP: - return Value(get_relationship(v)[0]) - elif type == Type.PATH: - return Value(get_path(v)[0]) - else: - raise ValueError("Not recognized type: %s" % type) - - assert isinstance(type, compat.string) - if val == 'null': - return Value(None) - if val[0] == '[': - if type != Type.RELATIONSHIP or (type == Type.RELATIONSHIP and val[1] == '['): - val = val[1:-1].split(", ") - if isinstance(val, list): - return tuple([get_val(v) for v in val]) - else: - return get_val(val) - - def _driver_value_to_comparable(val): if isinstance(val, list): - return tuple([Value(v) for v in val]) + l = [_driver_value_to_comparable(v) for v in val] + return l else: return Value(val) -def table_to_comparable_result(table, types): +def table_to_comparable_result(table): result = [] keys = table.headings - if isinstance(types, compat.string): - types = {key: string_to_type(types) for key in keys} - elif isinstance(types, dict): - assert set(types.keys()) == set(keys) - else: - raise ValueError("types must be either string of single type or map of types corresponding to result keys Got:" - " %s" % types) for row in table: result.append( - {keys[i]: _string_value_to_comparable(row[i], string_to_type(types[keys[i]])) for i in range(len(row))}) + {keys[i]: parse_values_to_comparable(row[i]) for i in range(len(row))}) return result @@ -241,15 +79,19 @@ def driver_result_to_comparable_result(result): return records -def unordered_equal(one, two): - l1 = one[:] - l2 = two[:] +def unordered_equal(given, expected): + l1 = given[:] + l2 = expected[:] assert isinstance(l1, list) assert isinstance(l2, list) + print(len(l1)) + print(len(l2)) assert len(l1) == len(l2) for d1 in l1: size = len(l2) for d2 in l2: + print(d2) + print(d1) if d1 == d2: l2.remove(d2) break From 46d5402bf00e39326874a68aed1bf7b8397f7471 Mon Sep 17 00:00:00 2001 From: RagnarW Date: Tue, 16 Feb 2016 09:28:42 +0100 Subject: [PATCH 3/5] Changed name of value comparison class --- test/tck/tck_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tck/tck_util.py b/test/tck/tck_util.py index 4a1821ad0..d89777015 100644 --- a/test/tck/tck_util.py +++ b/test/tck/tck_util.py @@ -79,7 +79,7 @@ class Type: NULL = "Null" -class Value: +class TestValue: content = None def __init__(self, entity): @@ -100,7 +100,7 @@ def __hash__(self): return hash(repr(self)) def __eq__(self, other): - assert isinstance(other, Value) + assert isinstance(other, TestValue) return self.content == other.content def __repr__(self): From 3faa9588bc45e914936566229cd1a0525380c5bd Mon Sep 17 00:00:00 2001 From: RagnarW Date: Tue, 16 Feb 2016 09:29:48 +0100 Subject: [PATCH 4/5] Removed assertion to work with 3.5 --- test/tck/resultparser.py | 6 ++---- test/tck/steps/cypher_compability_steps.py | 8 ++------ test/tck/tck_util.py | 1 - 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/test/tck/resultparser.py b/test/tck/resultparser.py index cc257eb59..75bd3789a 100644 --- a/test/tck/resultparser.py +++ b/test/tck/resultparser.py @@ -21,7 +21,7 @@ import json import re from neo4j.v1 import Node, Relationship, Path -from tck_util import Value +from tck_util import TestValue def parse_values_to_comparable(row): @@ -67,13 +67,11 @@ def get_map(val): def value_to_comparable_object(val): - return Value(value_to_object(val)) + return TestValue(value_to_object(val)) def value_to_object(val): val = val.strip() - print("VAL") - print(val) PATH = '^(<\().*(\)>)$' NODE = '^(\().*(\))$' RELATIONSHIP = '^(\[:).*(\])$' diff --git a/test/tck/steps/cypher_compability_steps.py b/test/tck/steps/cypher_compability_steps.py index 9b3214c4d..db21f58e8 100644 --- a/test/tck/steps/cypher_compability_steps.py +++ b/test/tck/steps/cypher_compability_steps.py @@ -20,7 +20,7 @@ from behave import * -from test.tck.tck_util import Value, send_string, send_parameters +from test.tck.tck_util import TestValue, send_string, send_parameters from test.tck.resultparser import parse_values, parse_values_to_comparable use_step_matcher("re") @@ -60,7 +60,7 @@ def _driver_value_to_comparable(val): l = [_driver_value_to_comparable(v) for v in val] return l else: - return Value(val) + return TestValue(val) def table_to_comparable_result(table): @@ -84,14 +84,10 @@ def unordered_equal(given, expected): l2 = expected[:] assert isinstance(l1, list) assert isinstance(l2, list) - print(len(l1)) - print(len(l2)) assert len(l1) == len(l2) for d1 in l1: size = len(l2) for d2 in l2: - print(d2) - print(d1) if d1 == d2: l2.remove(d2) break diff --git a/test/tck/tck_util.py b/test/tck/tck_util.py index d89777015..d05913f75 100644 --- a/test/tck/tck_util.py +++ b/test/tck/tck_util.py @@ -100,7 +100,6 @@ def __hash__(self): return hash(repr(self)) def __eq__(self, other): - assert isinstance(other, TestValue) return self.content == other.content def __repr__(self): From 7e3d111c3cf0fff9b0c9cef13ee2c3e25e1d737c Mon Sep 17 00:00:00 2001 From: RagnarW Date: Tue, 16 Feb 2016 10:19:42 +0100 Subject: [PATCH 5/5] updated neokit --- neokit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neokit b/neokit index 225f07d2d..8fc30fd9f 160000 --- a/neokit +++ b/neokit @@ -1 +1 @@ -Subproject commit 225f07d2d8cd8082ac1bfb8229c8a413be82c546 +Subproject commit 8fc30fd9fa5145fdd8b6627f3525a66b0213becc