From 1013c3eaeedbb942be0d97f60592b71b955b4a9c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matej=20Gradi=C4=8Dek?= <matej.gradicek@memgraph.io>
Date: Mon, 20 Mar 2017 12:32:09 +0000
Subject: [PATCH] Refactored kpi service

Reviewers: buda

Reviewed By: buda

Subscribers: matej.gradicek

Differential Revision: https://phabricator.memgraph.io/D142
---
 README.md                            |  15 +++
 kpi_service/kpi_service.py           | 160 ++++++++++++++++++---------
 tck_engine/environment.py            |  33 +++++-
 tck_engine/steps/binary_tree.py      |   4 +-
 tck_engine/steps/database.py         |  23 ++--
 tck_engine/steps/errors.py           |  48 +++++++-
 tck_engine/steps/graph.py            |  18 +--
 tck_engine/steps/graph_properties.py |  19 ++--
 tck_engine/steps/parser.py           |  34 +++---
 tck_engine/steps/query.py            |  93 ++++++++++------
 tck_engine/steps/test_parameters.py  |  25 +++--
 tck_engine/test_executor.py          |  60 ++++++----
 tck_engine/test_results.py           |   6 +-
 13 files changed, 364 insertions(+), 174 deletions(-)

diff --git a/README.md b/README.md
index f1ffca940..25eaec07e 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,21 @@ The following tck tests have been changed:
 Comparability.feature tests are failing because integers are compared to strings
 what is not allowed in openCypher.
 
+TCK Engine problems:
+
+    1. Comparing tables with ordering.
+         ORDER BY x DESC
+         | x | y |    | x | y |
+         | 3 | 2 |    | 3 | 1 |
+         | 3 | 1 |    | 3 | 2 |
+         | 1 | 4 |    | 1 | 4 |
+
+    2. Properties side effects
+         | +properties | 1 |
+         | -properties | 1 | 
+
+         Database is returning properties_set, not properties_created and properties_deleted.
+ 
 ## KPI Service
 
 Flask application used to get results from executing tests with TCK Engine. 
diff --git a/kpi_service/kpi_service.py b/kpi_service/kpi_service.py
index 6e3641bce..605da2351 100644
--- a/kpi_service/kpi_service.py
+++ b/kpi_service/kpi_service.py
@@ -1,22 +1,27 @@
 from flask import Flask, jsonify, request
-import os, json
+import os
+import json
 from argparse import ArgumentParser
 
 app = Flask(__name__)
 """
-Script runs web application used for getting test results. 
-File with results is "../tck_engine/results". 
+Script runs web application used for getting test results.
+Default file with results is "../tck_engine/results".
 Application lists files with results or returnes result
 file as json.
-Default host is 127.0.0.1, and default port is 5000. 
-Host and port can be passed as arguments of a script.
+Default host is 0.0.0.0, and default port is 5000.
+Host, port and result file can be passed as arguments of a script.
 """
 
+
 def parse_args():
     argp = ArgumentParser(description=__doc__)
-    argp.add_argument("--host", default="0.0.0.0", help="Application host ip, default is 127.0.0.1.")
-    argp.add_argument("--port", default="5000", help="Application host ip, default is 5000.")
-    argp.add_argument("--results", default="../tck_engine/results", help="Path where the results are stored.")
+    argp.add_argument("--host", default="0.0.0.0",
+                      help="Application host ip, default is 0.0.0.0.")
+    argp.add_argument("--port", default="5000",
+                      help="Application host ip, default is 5000.")
+    argp.add_argument("--results", default="tck_engine/results",
+                      help="Path where the results are stored.")
     return argp.parse_args()
 
 
@@ -24,83 +29,138 @@ def parse_args():
 def results():
     """
     Function accessed with route /result. Function lists
-    names of last tail result files added to results and 
-    separeted by whitespace. Tail is a parameter given in 
-    route. If tail is not given, function lists all files
-    from the last added file.
+    last tail result files added. Tail is a parameter given
+    in the route. If the tail is not given, function lists
+    last ten added files. If parameter last is true, only last
+    test result is returned.
 
     @return:
-        string of file names separated by whitespace
+        json list of test results
     """
-    tail = request.args.get("tail")
-    return list_to_str(os.listdir(app.config["RESULTS_PATH"]), tail)
+    l = [f for f in os.listdir(app.config["RESULTS_PATH"])
+         if f != ".gitignore"]
+    return get_ret_list(l)
+
 
 @app.route("/results/<dbname>")
 def results_for_db(dbname):
     """
-    Function accessed with route /result/<dbname>. Function 
-    lists names of last tail result files of database <dbname> 
-    added to results and separeted by whitespace. Tail is a 
-    parameter given in route. If tail is not given, function 
-    lists all files of database <dbname> from the last added 
-    file.
+    Function accessed with route /result/<dbname>. Function
+    lists last tail result files added of database <dbname.
+    Tail is a parameter given in the route. If tail is not
+    given, function lists last ten added files of database
+    <dbname>. If param last is true, only last test result
+    is returned.
 
     @param dbname:
         string, database name
     @return:
-        string of file names separated by whitespace
+        json list of test results
     """
-    tail = request.args.get("tail")
-    return list_to_str(([f for f in os.listdir(app.config["RESULTS_PATH"]) if f.startswith(dbname)]), tail)
+    print(os.listdir(app.config["RESULTS_PATH"]))
+    l = [f for f in os.listdir(app.config["RESULTS_PATH"])
+         if f != ".gitignore" and f.split('-')[1] == dbname]
+    return get_ret_list(l)
 
-@app.route("/results/<dbname>/<timestamp>")
-def result(dbname, timestamp):
+
+@app.route("/results/<dbname>/<test_suite>")
+def result(dbname, test_suite):
     """
-    Function accessed with route /results/<dbname>/<timestamp>
-    Returns json of result file with name <dbname>_<timestamp>.json. 
-    <timestamp> is in format yyyy_mm_dd__HH_MM.
+    Function accessed with route /results/<dbname>/<test_suite>
+    Function lists last tail result files added of database <dbname>
+    tested on <test_suite>. Tail is a parameter given in the
+    route. If tail is not given, function lists last ten results.
+    If param last is true, only last test result is returned.
 
     @param dbname:
         string, database name
-    @param timestamp:
-        string, timestamp from description
+    @param test_suite:
+        string, test suite of result file
     @return:
-        json of a file.
+        json list of test results
     """
-    fname = dbname + "_" + timestamp + ".json"
-    with open(app.config["RESULTS_PATH"] + "/" + fname) as f:
-        json_data = json.load(f)
-        return jsonify(json_data)
+    fname = dbname + "-" + test_suite + ".json"
+    l = [f for f in os.listdir(app.config["RESULTS_PATH"])
+         if f.endswith(fname)]
+    return get_ret_list(l)
 
-def list_to_str(l, tail):
+
+def get_ret_list(l):
     """
-    Function returns first tail results of list l in decreasing 
-    order as string separated by whitespace. If tail is None, 
-    function returns string of whole list.
+    Function returns json list of test results of files given in
+    list l.
 
     @param l:
-        list to return as string
-    @param tail:
-        number of results
+        list of file names
     @return:
-        list as string
+        json list of test results
     """
     l.sort()
+    ret_list = []
+    for f in l:
+        ret_list.append(get_content(f))
+    return list_to_json(
+        ret_list,
+        request.args.get("last"),
+        request.args.get("tail")
+    )
+
+
+def get_content(fname):
+    """
+    Function returns data of the json file fname located in
+    results directory in json format.
+
+    @param fname:
+        string, name of the file
+    @return:
+        json of a file
+    """
+    with open(app.config["RESULTS_PATH"] + "/" + fname) as f:
+        json_data = json.load(f)
+        return json_data
+
+
+def list_to_json(l, last, tail):
+    """
+    Function converts list to json format. If last is true,
+    only the first item in list is returned list, else last
+    tail results are returned in json list. If tail is not
+    given, last ten results are returned in json list.
+
+    @param l:
+        list to convert to json format
+    @param last:
+        string from description
+    @param tail:
+        string from description
+    """
     l.reverse()
+    if len(l) == 0:
+        return jsonify(results=[])
+
+    if last == "true":
+        return jsonify(results=[l[0]])
+
     if tail is None:
-        tail = len(l)
-    tail = max(tail, len(l))
-    return ' '.join(l[0:int(tail)])
+        tail = 10
+    else:
+        tail = int(tail)
+        if tail > len(l):
+            tail = len(l)
+    return jsonify(results=l[0:tail])
+
 
 def main():
     args = parse_args()
     app.config.update(dict(
-        RESULTS_PATH=args.results
+        RESULTS_PATH=os.path.abspath(args.results)
     ))
     app.run(
-        host = args.host,
-        port = int(args.port)
+        host=args.host,
+        port=int(args.port)
     )
 
+
 if __name__ == "__main__":
     main()
diff --git a/tck_engine/environment.py b/tck_engine/environment.py
index 6de62eb5a..5cf029bc7 100644
--- a/tck_engine/environment.py
+++ b/tck_engine/environment.py
@@ -1,4 +1,7 @@
-import logging, datetime, time, json
+import logging
+import datetime
+import time
+import json
 from steps.test_parameters import TestParameters
 from neo4j.v1 import GraphDatabase, basic_auth
 from steps.graph_properties import GraphProperties
@@ -6,34 +9,54 @@ from test_results import TestResults
 
 test_results = TestResults()
 
+
 def before_scenario(context, step):
     context.test_parameters = TestParameters()
     context.graph_properties = GraphProperties()
     context.exception = None
 
+
 def after_scenario(context, scenario):
     test_results.add_test(scenario.status)
 
+
 def before_all(context):
     set_logging(context)
     context.driver = create_db_driver(context)
-    
+
+
 def after_all(context):
     ts = time.time()
     timestamp = datetime.datetime.fromtimestamp(ts).strftime("%Y_%m_%d__%H_%M")
-    file_name = context.config.output_folder + context.config.database + "_" + timestamp + ".json"
-    js = {"total": test_results.num_total(), "passed": test_results.num_passed(), "test_suite": context.config.root}
+
+    root = context.config.root
+
+    if root.endswith("/"):
+        root = root[0:len(root) - 1]
+    if root.endswith("features"):
+        root = root[0: len(root) - len("features") - 1]
+
+    test_suite = root.split('/')[-1]
+    file_name = context.config.output_folder + timestamp + \
+        "-" + context.config.database + "-" + test_suite + ".json"
+
+    js = {
+        "total": test_results.num_total(), "passed": test_results.num_passed(),
+         "test_suite": test_suite, "timestamp": timestamp, "db": context.config.database}
     with open(file_name, 'w') as f:
         json.dump(js, f)
 
+
 def set_logging(context):
     logging.basicConfig(level="DEBUG")
     log = logging.getLogger(__name__)
     context.log = log
 
+
 def create_db_driver(context):
     uri = context.config.database_uri
-    auth_token = basic_auth(context.config.database_username, context.config.database_password)
+    auth_token = basic_auth(
+        context.config.database_username, context.config.database_password)
     if context.config.database == "neo4j" or context.config.database == "memgraph":
         driver = GraphDatabase.driver(uri, auth=auth_token, encrypted=0)
     else:
diff --git a/tck_engine/steps/binary_tree.py b/tck_engine/steps/binary_tree.py
index a6a1c4025..300326edd 100644
--- a/tck_engine/steps/binary_tree.py
+++ b/tck_engine/steps/binary_tree.py
@@ -1,9 +1,11 @@
 from behave import *
 import graph
 
+
 @given(u'the binary-tree-1 graph')
 def step_impl(context):
-   graph.create_graph('binary-tree-1', context) 
+    graph.create_graph('binary-tree-1', context)
+
 
 @given(u'the binary-tree-2 graph')
 def step_impl(context):
diff --git a/tck_engine/steps/database.py b/tck_engine/steps/database.py
index 613532ad3..a87db9baa 100644
--- a/tck_engine/steps/database.py
+++ b/tck_engine/steps/database.py
@@ -1,10 +1,10 @@
 def query(q, context, params={}):
     """
-    Function used to execute query on database. Query results are 
+    Function used to execute query on database. Query results are
     set in context.result_list. If exception occurs, it is set on
     context.exception.
 
-    @param q: 
+    @param q:
         String, database query.
     @param context:
         behave.runner.Context, context of all tests.
@@ -17,7 +17,7 @@ def query(q, context, params={}):
     if context.config.database == "neo4j":
         session = driver.session()
         try:
-            #executing query
+            # executing query
             with session.begin_transaction() as tx:
                 results = tx.run(q, params)
                 summary = results.summary()
@@ -27,11 +27,11 @@ def query(q, context, params={}):
                 tx.success = True
             session.close()
         except Exception as e:
-            #exception
+            # exception
             context.exception = e
             context.log.info('%s', str(e))
             session.close()
-            #not working if removed
+            # not working if removed
             query("match (n) detach delete(n)", context)
     return results_list
 
@@ -44,23 +44,22 @@ def add_side_effects(context, counters):
         behave.runner.Context, context of all tests.
     """
     graph_properties = context.graph_properties
-    
-    #check nodes
+
+    # check nodes
     if counters.nodes_deleted > 0:
         graph_properties.change_nodes(-counters.nodes_deleted)
     if counters.nodes_created > 0:
         graph_properties.change_nodes(counters.nodes_created)
-    #check relationships
+    # check relationships
     if counters.relationships_deleted > 0:
         graph_properties.change_relationships(-counters.relationships_deleted)
     if counters.relationships_created > 0:
-        graph_properties.change_relationships(counters.relationships_created) 
-    #check labels
+        graph_properties.change_relationships(counters.relationships_created)
+    # check labels
     if counters.labels_removed > 0:
         graph_properties.change_labels(-counters.labels_removed)
     if counters.labels_added > 0:
         graph_properties.change_labels(counters.labels_added)
-    #check properties
+    # check properties
     if counters.properties_set > 0:
         graph_properties.change_properties(counters.properties_set)
-    
diff --git a/tck_engine/steps/errors.py b/tck_engine/steps/errors.py
index ef0c674ee..a77560482 100644
--- a/tck_engine/steps/errors.py
+++ b/tck_engine/steps/errors.py
@@ -1,6 +1,8 @@
 from behave import *
 
-#TODO check for exact error?
+# TODO check for exact error?
+
+
 def handle_error(context):
     """
     Function checks if exception exists in context.
@@ -11,167 +13,207 @@ def handle_error(context):
     """
     assert(context.exception is not None)
 
+
 @then('a SyntaxError should be raised at compile time: NestedAggregation')
 def syntax_error(context):
     handle_error(context)
 
+
 @then('TypeError should be raised at compile time: IncomparableValues')
 def type_error(context):
     handle_error(context)
 
+
 @then(u'a TypeError should be raised at compile time: IncomparableValues')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: RequiresDirectedRelationship')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: InvalidRelationshipPattern')
 def syntax_error(context):
     handle_error(context)
 
+
 @then(u'a TypeError should be raised at runtime: MapElementAccessByNonString')
 def type_error(context):
     handle_error(context)
 
+
 @then(u'a ConstraintVerificationFailed should be raised at runtime: DeleteConnectedNode')
 def step(context):
     handle_error(context)
 
+
 @then(u'a TypeError should be raised at runtime: ListElementAccessByNonInteger')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: InvalidArgumentType')
 def step(context):
     handle_error(context)
 
+
 @then(u'a TypeError should be raised at runtime: InvalidElementAccess')
 def step(context):
     handle_error(context)
 
+
 @then(u'a ArgumentError should be raised at runtime: NumberOutOfRange')
 def step(context):
     handle_error(context)
 
+
 @then(u'a TypeError should be raised at runtime: InvalidArgumentValue')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: VariableAlreadyBound')
 def step(context):
     handle_error(context)
 
+
 @then(u'a TypeError should be raised at runtime: IncomparableValues')
 def step(context):
     handle_error(context)
 
+
 @then(u'a TypeError should be raised at runtime: PropertyAccessOnNonMap')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: InvalidUnicodeLiteral')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SemanticError should be raised at compile time: MergeReadOwnWrites')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: InvalidAggregation')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: NoExpressionAlias')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: UndefinedVariable')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: VariableTypeConflict')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: DifferentColumnsInUnion')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: InvalidClauseComposition')
 def step(context):
     handle_error(context)
 
+
 @then(u'a TypeError should be raised at compile time: InvalidPropertyType')
-def step(context):  
+def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: ColumnNameConflict')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: NoVariablesInScope')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: InvalidDelete')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: NegativeIntegerArgument')
 def step(context):
     handle_error(context)
 
+
 @then(u'a EntityNotFound should be raised at runtime: DeletedEntityAccess')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: RelationshipUniquenessViolation')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: CreatingVarLength')
 def step_impl(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: InvalidParameterUse')
 def step_impl(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: FloatingPointOverflow')
 def step_impl(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time InvalidArgumentExpression')
 def step_impl(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time InvalidUnicodeCharacter')
 def step_impl(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: NonConstantExpression')
 def step_impl(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: NoSingleRelationshipType')
 def step_impl(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: UnknownFunction')
 def step_impl(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: InvalidNumberLiteral')
 def step_impl(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: InvalidArgumentExpression')
 def step(context):
     handle_error(context)
 
+
 @then(u'a SyntaxError should be raised at compile time: InvalidUnicodeCharacter')
 def step(context):
     handle_error(context)
-
diff --git a/tck_engine/steps/graph.py b/tck_engine/steps/graph.py
index a01016249..8bc93f302 100644
--- a/tck_engine/steps/graph.py
+++ b/tck_engine/steps/graph.py
@@ -1,5 +1,5 @@
-import database, os
-from graph_properties import GraphProperties
+import database
+import os
 from behave import *
 
 
@@ -8,23 +8,26 @@ def empty_graph_step(context):
     database.query("MATCH (n) DETACH DELETE n", context)
     context.graph_properties.set_beginning_parameters()
 
+
 @given('any graph')
 def any_graph_step(context):
     database.query("MATCH (n) DETACH DELETE n", context)
     context.graph_properties.set_beginning_parameters()
 
+
 @given('graph "{name}"')
 def graph_name(context, name):
     create_graph(name, context)
 
+
 def create_graph(name, context):
     """
     Function deletes everything from database and creates a new
-    graph. Graph file name is an argument of function. Function 
+    graph. Graph file name is an argument of function. Function
     executes queries written in a .cypher file separated by ';'
     and sets graph properties to beginning values.
     """
-    database.query("MATCH (n) DETACH DELETE n", context)  
+    database.query("MATCH (n) DETACH DELETE n", context)
     path = find_graph_path(name, context.config.graphs_root)
 
     q_marks = ["'", '"', '`']
@@ -36,8 +39,8 @@ def create_graph(name, context):
         i = 0
         while i < len(content):
             ch = content[i]
-            if ch == '\\' and i != len(content)-1 and content[i+1] in q_marks:
-                q += ch + content[i+1]
+            if ch == '\\' and i != len(content) - 1 and content[i + 1] in q_marks:
+                q += ch + content[i + 1]
                 i += 2
             else:
                 q += ch
@@ -45,7 +48,6 @@ def create_graph(name, context):
                     in_string = False
                 elif ch in q_marks:
                     in_string = True
-                    q_mark = ch
                 if ch == ';' and not in_string:
                     database.query(q, context)
                     q = ''
@@ -58,7 +60,7 @@ def create_graph(name, context):
 def find_graph_path(name, path):
     """
     Function returns path to .cypher file with given name in
-    given folder or subfolders. Argument path is path to a given 
+    given folder or subfolders. Argument path is path to a given
     folder.
     """
     for root, dirs, files in os.walk(path):
diff --git a/tck_engine/steps/graph_properties.py b/tck_engine/steps/graph_properties.py
index 42405f761..cc88e4dfb 100644
--- a/tck_engine/steps/graph_properties.py
+++ b/tck_engine/steps/graph_properties.py
@@ -1,15 +1,15 @@
-import steps.database
+class GraphProperties:
 
-class GraphProperties: 
     """
-    Class used to store changes(side effects of queries) 
-    to graph parameters(nodes, relationships, labels and 
+    Class used to store changes(side effects of queries)
+    to graph parameters(nodes, relationships, labels and
     properties) when executing queries.
     """
+
     def set_beginning_parameters(self):
         """
         Method sets parameters to empty lists.
-        
+
         @param self:
             Instance of a class.
         """
@@ -21,7 +21,7 @@ class GraphProperties:
     def __init__(self):
         """
         Method sets parameters to empty lists.
-            
+
         @param self:
             Instance of a class.
         """
@@ -49,7 +49,7 @@ class GraphProperties:
         @param self:
             Instance of a class.
         @param dif:
-            Int, difference between number of relationships 
+            Int, difference between number of relationships
             before and after executing query.
         """
         self.relationships.append(dif)
@@ -77,7 +77,8 @@ class GraphProperties:
         """
         self.properties.append(dif)
 
-    def compare(self, nodes_dif, relationships_dif, labels_dif, properties_dif):
+    def compare(self, nodes_dif, relationships_dif, labels_dif,
+                properties_dif):
         """
         Method used to compare side effects from executing
         queries and an expected result from a cucumber test.
@@ -88,7 +89,7 @@ class GraphProperties:
             List of all expected node side effects in order
             when executing query.
         @param relationships_dif:
-            List of all expected relationship side effects 
+            List of all expected relationship side effects
             in order when executing query.
         @param labels_dif:
             List of all expected label side effects in order
diff --git a/tck_engine/steps/parser.py b/tck_engine/steps/parser.py
index e7b78cde8..416152820 100644
--- a/tck_engine/steps/parser.py
+++ b/tck_engine/steps/parser.py
@@ -28,6 +28,7 @@ def parse(el, ignore_order):
             return parse_rel(el, ignore_order)
     return el
 
+
 def is_list(el):
     """
     Function returns true if string el is a list, else false.
@@ -40,6 +41,7 @@ def is_list(el):
         return False
     return True
 
+
 def parse_path(path, ignore_order):
     """
     Function used to parse path.
@@ -49,8 +51,8 @@ def parse_path(path, ignore_order):
         parsed path
     """
     parsed_path = '<'
-    dif_open_closed_brackets = 0;
-    for i in range(1, len(path)-1):
+    dif_open_closed_brackets = 0
+    for i in range(1, len(path) - 1):
         if path[i] == '(' or path[i] == '{' or path[i] == '[':
             dif_open_closed_brackets += 1
             if dif_open_closed_brackets == 1:
@@ -58,12 +60,13 @@ def parse_path(path, ignore_order):
         if path[i] == ')' or path[i] == '}' or path[i] == ']':
             dif_open_closed_brackets -= 1
             if dif_open_closed_brackets == 0:
-                parsed_path += parse(path[start:(i+1)], ignore_order)
+                parsed_path += parse(path[start:(i + 1)], ignore_order)
         elif dif_open_closed_brackets == 0:
             parsed_path += path[i]
     parsed_path += '>'
     return parsed_path
 
+
 def parse_node(node_str, ignore_order):
     """
     Function used to parse node.
@@ -75,7 +78,7 @@ def parse_node(node_str, ignore_order):
     label = ''
     labels = []
     props_start = None
-    for i in range(1,  len(node_str)):
+    for i in range(1, len(node_str)):
         if node_str[i] == ':' or node_str[i] == ')' or node_str[i] == '{':
             if label.startswith(':'):
                 labels.append(label)
@@ -88,13 +91,15 @@ def parse_node(node_str, ignore_order):
 
     labels.sort()
     parsed_node = '('
-    for label in labels: 
+    for label in labels:
         parsed_node += label
     if props_start is not None:
-        parsed_node += parse_map(node_str[props_start:len(node_str)-1], ignore_order) 
+        parsed_node += parse_map(
+            node_str[props_start:len(node_str) - 1], ignore_order)
     parsed_node += ')'
     return parsed_node
 
+
 def parse_map(props, ignore_order):
     """
     Function used to parse map.
@@ -106,7 +111,7 @@ def parse_map(props, ignore_order):
     dif_open_closed_brackets = 0
     prop = ''
     list_props = []
-    for i in range(1, len(props)-1):
+    for i in range(1, len(props) - 1):
         if props[i] == ',' and dif_open_closed_brackets == 0:
             list_props.append(prop_to_str(prop, ignore_order))
             prop = ''
@@ -119,10 +124,10 @@ def parse_map(props, ignore_order):
     if prop != '':
         list_props.append(prop_to_str(prop, ignore_order))
 
-
     list_props.sort()
     return '{' + ','.join(list_props) + '}'
 
+
 def prop_to_str(prop, ignore_order):
     """
     Function used to parse one pair of key, value in format 'key:value'.
@@ -137,6 +142,7 @@ def prop_to_str(prop, ignore_order):
     val = prop.split(':', 1)[1]
     return key + ":" + parse(val, ignore_order)
 
+
 def parse_list(l, ignore_order):
     """
     Function used to parse list.
@@ -148,7 +154,7 @@ def parse_list(l, ignore_order):
     dif_open_closed_brackets = 0
     el = ''
     list_el = []
-    for i in range(1, len(l)-1):
+    for i in range(1, len(l) - 1):
         if l[i] == ',' and dif_open_closed_brackets == 0:
             list_el.append(parse(el, ignore_order))
             el = ''
@@ -163,9 +169,10 @@ def parse_list(l, ignore_order):
 
     if ignore_order:
         list_el.sort()
-   
+
     return '[' + ','.join(list_el) + ']'
 
+
 def parse_rel(rel, ignore_order):
     """
     Function used to parse relationship.
@@ -177,7 +184,7 @@ def parse_rel(rel, ignore_order):
     label = ''
     labels = []
     props_start = None
-    for i in range(1,  len(rel)):
+    for i in range(1, len(rel)):
         if rel[i] == ':' or rel[i] == ']' or rel[i] == '{':
             if label.startswith(':'):
                 labels.append(label)
@@ -190,10 +197,9 @@ def parse_rel(rel, ignore_order):
 
     labels.sort()
     parsed_rel = '['
-    for label in labels: 
+    for label in labels:
         parsed_rel += label
     if props_start is not None:
-        parsed_rel += parse_map(rel[props_start:len(rel)-1], ignore_order) 
+        parsed_rel += parse_map(rel[props_start:len(rel) - 1], ignore_order)
     parsed_rel += ']'
     return parsed_rel
-
diff --git a/tck_engine/steps/query.py b/tck_engine/steps/query.py
index f0e1990a9..78a66da21 100644
--- a/tck_engine/steps/query.py
+++ b/tck_engine/steps/query.py
@@ -1,29 +1,38 @@
-import json
 
-import database, parser
+import database
+import parser
 from behave import *
 from neo4j.v1.types import Node, Path, Relationship
 
+
 @given('parameters are')
 def parameters_step(context):
     context.test_parameters.set_parameters_from_table(context.table)
 
-@then('parameters are') 
+
+@then('parameters are')
 def parameters_step(context):
     context.test_parameters.set_parameters_from_table(context.table)
 
+
 @step('having executed')
 def having_executed_step(context):
-    context.results = database.query(context.text, context, context.test_parameters.get_parameters())
+    context.results = database.query(
+        context.text, context, context.test_parameters.get_parameters())
     context.graph_properties.set_beginning_parameters()
 
+
 @when('executing query')
 def executing_query_step(context):
-    context.results = database.query(context.text, context, context.test_parameters.get_parameters())
+    context.results = database.query(
+        context.text, context, context.test_parameters.get_parameters())
+
 
 @when('executing control query')
 def executing_query_step(context):
-    context.results = database.query(context.text, context, context.test_parameters.get_parameters())
+    context.results = database.query(
+        context.text, context, context.test_parameters.get_parameters())
+
 
 def parse_props(prop_json):
     """
@@ -67,25 +76,25 @@ def to_string(element):
         String of parsed element.
     """
     if element is None:
-        #parsing None
+        # parsing None
         return "null"
 
     if isinstance(element, Node):
-        #parsing Node
+        # parsing Node
         sol = "("
         if element.labels:
             sol += ':' + ': '.join(element.labels)
-      
+
         if element.properties:
             if element.labels:
                 sol += ' '
-            sol +=  parse_props(element.properties)
+            sol += parse_props(element.properties)
 
         sol += ")"
         return sol
 
     elif isinstance(element, Relationship):
-        #parsing Relationship
+        # parsing Relationship
         sol = "[:"
         if element.type:
             sol += element.type
@@ -96,7 +105,7 @@ def to_string(element):
         return sol
 
     elif isinstance(element, Path):
-        #parsing Path
+        # parsing Path
         # TODO add longer paths
         edges = []
         nodes = []
@@ -113,18 +122,18 @@ def to_string(element):
                 sol += nodes[i][1] + "-" + edges[i][1] + "->"
             else:
                 sol += nodes[i][1] + "<-" + edges[i][1] + "-"
-            
+
         sol += nodes[len(edges)][1]
         sol += ">"
-        
+
         return sol
 
     elif isinstance(element, str):
-        #parsing string
+        # parsing string
         return "'" + element + "'"
-    
+
     elif isinstance(element, list):
-        #parsing list
+        # parsing list
         sol = '['
         el_str = []
         for el in element:
@@ -135,13 +144,13 @@ def to_string(element):
         return sol
 
     elif isinstance(element, bool):
-        #parsing bool
+        # parsing bool
         if element:
             return "true"
         return "false"
 
     elif isinstance(element, dict):
-        #parsing map
+        # parsing map
         if len(element) == 0:
             return '{}'
         sol = '{'
@@ -149,19 +158,19 @@ def to_string(element):
             sol += key + ':' + to_string(val) + ','
         sol = sol[:-1] + '}'
         return sol
-    
+
     elif isinstance(element, float):
-        #parsing float, scientific
+        # parsing float, scientific
         if 'e' in str(element):
             if str(element)[-3] == '-':
-                zeroes = int(str(element)[-2:])-1
+                zeroes = int(str(element)[-2:]) - 1
                 num_str = ''
                 if str(element)[0] == '-':
                     num_str += '-'
-                num_str += '.' + zeroes * '0' + str(element)[:-4].replace("-", "").replace(".", "")
+                num_str += '.' + zeroes * '0' + \
+                    str(element)[:-4].replace("-", "").replace(".", "")
                 return num_str
 
-
     return str(element)
 
 
@@ -174,7 +183,7 @@ def get_result_rows(context, ignore_order):
         behave.runner.Context, behave context.
     @param ignore_order:
         bool, ignore order in result and expected list.
-    @return 
+    @return
         Result rows.
     """
     result_rows = []
@@ -182,7 +191,8 @@ def get_result_rows(context, ignore_order):
         keys = result.keys()
         values = result.values()
         for i in range(0, len(keys)):
-            result_rows.append(keys[i] + ":" + parser.parse(to_string(values[i]).replace("\n", "\\n").replace(" ", ""), ignore_order))
+            result_rows.append(keys[i] + ":" + parser.parse(
+                to_string(values[i]).replace("\n", "\\n").replace(" ", ""), ignore_order))
     return result_rows
 
 
@@ -200,9 +210,11 @@ def get_expected_rows(context, ignore_order):
     expected_rows = []
     for row in context.table:
         for col in context.table.headings:
-            expected_rows.append(col + ":" + parser.parse(row[col].replace(" ", ""), ignore_order))
+            expected_rows.append(
+                col + ":" + parser.parse(row[col].replace(" ", ""), ignore_order))
     return expected_rows
 
+
 def validate(context, ignore_order):
     """
     Function used to check if results from database are same
@@ -218,7 +230,7 @@ def validate(context, ignore_order):
 
     context.log.info("Expected: %s", str(expected_rows))
     context.log.info("Results:  %s", str(result_rows))
-    assert(len(expected_rows) ==  len(result_rows))
+    assert(len(expected_rows) == len(result_rows))
 
     for i in range(0, len(expected_rows)):
         if expected_rows[i] in result_rows:
@@ -243,37 +255,43 @@ def validate_in_order(context, ignore_order):
 
     context.log.info("Expected: %s", str(expected_rows))
     context.log.info("Results:  %s", str(result_rows))
-    assert(len(expected_rows) ==  len(result_rows))
+    assert(len(expected_rows) == len(result_rows))
 
     for i in range(0, len(expected_rows)):
         if expected_rows[i] != result_rows[i]:
             assert(False)
 
+
 @then('the result should be')
 def expected_result_step(context):
     validate(context, False)
     check_exception(context)
 
+
 @then('the result should be, in order')
 def expected_result_step(context):
     validate_in_order(context, False)
     check_exception(context)
 
+
 @then('the result should be (ignoring element order for lists)')
 def expected_result_step(context):
     validate(context, True)
     check_exception(context)
 
+
 def check_exception(context):
     if context.exception is not None:
         context.log.info("Exception when eqecuting query!")
         assert(False)
 
+
 @then('the result should be empty')
 def empty_result_step(context):
     assert(len(context.results) == 0)
     check_exception(context)
 
+
 def side_effects_number(prop, table):
     """
     Function returns an expected list of side effects for property prop
@@ -284,7 +302,7 @@ def side_effects_number(prop, table):
         labels or properties.
     @param table:
         behave.model.Table, context table with side effects.
-    @return 
+    @return
         Description.
     """
     ret = []
@@ -293,32 +311,35 @@ def side_effects_number(prop, table):
         if row[0][0] == '+':
             sign = 1
         if row[0][1:] == prop:
-            ret.append(int(row[1])*sign)
+            ret.append(int(row[1]) * sign)
     sign = -1
     row = table.headings
     if row[0][0] == '+':
         sign = 1
     if row[0][1:] == prop:
-        ret.append(int(row[1])*sign)
+        ret.append(int(row[1]) * sign)
     ret.sort()
     return ret
 
+
 @then('the side effects should be')
 def side_effects_step(context):
     if context.config.no_side_effects:
         return
     table = context.table
-    #get side effects from db queries
+    # get side effects from db queries
     nodes_dif = side_effects_number("nodes", table)
     relationships_dif = side_effects_number("relationships", table)
     labels_dif = side_effects_number("labels", table)
     properties_dif = side_effects_number("properties", table)
-    #compare side effects
-    assert(context.graph_properties.compare(nodes_dif, relationships_dif, labels_dif, properties_dif) == True)
+    # compare side effects
+    assert(context.graph_properties.compare(nodes_dif,
+           relationships_dif, labels_dif, properties_dif) == True)
+
 
 @then('no side effects')
 def side_effects_step(context):
     if context.config.no_side_effects:
         return
-    #check if side effects are non existing
+    # check if side effects are non existing
     assert(context.graph_properties.compare([], [], [], []) == True)
diff --git a/tck_engine/steps/test_parameters.py b/tck_engine/steps/test_parameters.py
index a77f69c18..7fa7dbb52 100644
--- a/tck_engine/steps/test_parameters.py
+++ b/tck_engine/steps/test_parameters.py
@@ -1,9 +1,12 @@
 import yaml
 
+
 class TestParameters:
+
     """
     Class used to store parameters from a cucumber test.
     """
+
     def __init__(self):
         """
         Constructor initializes parameters to empty dict.
@@ -24,18 +27,22 @@ class TestParameters:
         par = dict()
         for row in table:
             par[row[0]] = self.parse_parameters(row[1])
-            if isinstance(par[row[0]], str) and par[row[0]].startswith("'") and par[row[0]].endswith("'"):
-                par[row[0]] = par[row[0]][1:len(par[row[0]])-1]
+            if isinstance(par[row[0]], str) and par[row[0]].startswith("'") \
+                    and par[row[0]].endswith("'"):
+                par[row[0]] = par[row[0]][1:len(par[row[0]]) - 1]
         par[table.headings[0]] = self.parse_parameters(table.headings[1])
-        if isinstance(par[table.headings[0]], str) and par[table.headings[0]].startswith("'") and par[table.headings[0]].endswith("'"):
-            par[table.headings[0]] = par[table.headings[0]][1:len(par[table.headings[0]])-1]
-            
+        if isinstance(par[table.headings[0]], str) and \
+                par[table.headings[0]].startswith("'") and \
+                par[table.headings[0]].endswith("'"):
+            par[table.headings[0]] = \
+                par[table.headings[0]][1:len(par[table.headings[0]]) - 1]
+
         self.parameters = par
 
     def get_parameters(self):
         """
         Method returns parameters.
-        
+
         @param self:
             Instance of a class.
         return:
@@ -45,10 +52,10 @@ class TestParameters:
 
     def parse_parameters(self, val):
         """
-        Method used for parsing parameters given in a cucumber test table 
+        Method used for parsing parameters given in a cucumber test table
         to a readable format for a database.
         Integers are parsed to int values, floats to float values, bools
-        to bool values, null to None and structures are recursively 
+        to bool values, null to None and structures are recursively
         parsed and returned.
 
         @param val:
@@ -58,5 +65,3 @@ class TestParameters:
         """
 
         return yaml.load(val)
-
-        
diff --git a/tck_engine/test_executor.py b/tck_engine/test_executor.py
index 32ad2da34..f717b7f10 100644
--- a/tck_engine/test_executor.py
+++ b/tck_engine/test_executor.py
@@ -3,44 +3,56 @@ from behave import configuration
 from argparse import ArgumentParser
 import os
 
+
 def parse_args():
     argp = ArgumentParser(description=__doc__)
-    argp.add_argument("--root", default="tck_engine/tests/openCypher_M05/tck/features", 
-            help="Path to folder where tests are located, default is openCypher_M05/tck/features.")
-    argp.add_argument("--graphs-root", default="tck_engine/tests/openCypher_M05/tck/graphs", 
+    argp.add_argument("--root", default="tck_engine/tests/openCypher_M05",
+                      help="Path to folder where tests are located, default is openCypher_M05/tck/features.")
+    argp.add_argument(
+        "--graphs-root", default="tck_engine/tests/openCypher_M05/tck/graphs",
             help="Path to folder where files with graphs queries are located, default is openCypher_M05/tck/graphs.")
-    argp.add_argument("--stop", action="store_true", help="Stop testing after first fail.")
-    argp.add_argument("--no-side-effects", action="store_true", help="Check for side effects in tests.")
-    argp.add_argument("--db", default="neo4j", choices=["neo4j", "memgraph"], help="Default is neo4j.")
+    argp.add_argument(
+        "--stop", action="store_true", help="Stop testing after first fail.")
+    argp.add_argument("--no-side-effects", action="store_true",
+                      help="Check for side effects in tests.")
+    argp.add_argument("--db", default="neo4j", choices=[
+                      "neo4j", "memgraph"], help="Default is neo4j.")
     argp.add_argument("--db-user", default="neo4j", help="Default is neo4j.")
-    argp.add_argument("--db-pass", default="memgraph", help="Default is memgraph.")
-    argp.add_argument("--db-uri", default="bolt://localhost:7687", help="Default is bolt://localhost:7687.")
-    argp.add_argument("--output-folder", default="tck_engine/results/", help="Test result output folder, default is results/.")
-    argp.add_argument("--logging", default="DEBUG", choices=["INFO", "DEBUG"], help="Logging level, default is DEBUG.")
+    argp.add_argument(
+        "--db-pass", default="memgraph", help="Default is memgraph.")
+    argp.add_argument("--db-uri", default="bolt://localhost:7687",
+                      help="Default is bolt://localhost:7687.")
+    argp.add_argument("--output-folder", default="tck_engine/results/",
+                      help="Test result output folder, default is results/.")
+    argp.add_argument("--logging", default="DEBUG", choices=[
+                      "INFO", "DEBUG"], help="Logging level, default is DEBUG.")
     return argp.parse_args()
 
+
 def main():
     """
-    Script used to run behave tests with given options. List of 
+    Script used to run behave tests with given options. List of
     options is available when running python test_executor.py -help.
     """
     args = parse_args()
 
     tests_root = os.path.abspath(args.root)
 
-    #adds options to cucumber configuration
-    add_config("--no-side-effects", dict(action="store_true", help = "Exclude side effects."))
-    add_config("--database", dict(help = "Choose database(memgraph/neo4j)."))
-    add_config("--database-password", dict(help = "Database password."))
-    add_config("--database-username", dict(help = "Database username."))
-    add_config("--database-uri", dict(help = "Database uri."))
-    add_config("--graphs-root", dict(help = "Path to folder where graphs are given."))
-    add_config("--output-folder", dict(help = "Folder where results of tests are written."))
-    add_config("--root", dict(help = "Folder with test features."))
+    # adds options to cucumber configuration
+    add_config("--no-side-effects",
+               dict(action="store_true", help="Exclude side effects."))
+    add_config("--database", dict(help="Choose database(memgraph/neo4j)."))
+    add_config("--database-password", dict(help="Database password."))
+    add_config("--database-username", dict(help="Database username."))
+    add_config("--database-uri", dict(help="Database uri."))
+    add_config("--graphs-root",
+               dict(help="Path to folder where graphs are given."))
+    add_config("--output-folder", dict(
+        help="Folder where results of tests are written."))
+    add_config("--root", dict(help="Folder with test features."))
 
-
-    #list with all options
-    #options will be passed to the cucumber engine
+    # list with all options
+    # options will be passed to the cucumber engine
     behave_options = [tests_root]
     if args.stop:
         behave_options.append("--stop")
@@ -61,7 +73,7 @@ def main():
     behave_options.append("--root")
     behave_options.append(args.root)
 
-    #runs tests with options
+    # runs tests with options
     behave_main(behave_options)
 
 
diff --git a/tck_engine/test_results.py b/tck_engine/test_results.py
index f21b5239c..7bc6cce13 100644
--- a/tck_engine/test_results.py
+++ b/tck_engine/test_results.py
@@ -1,6 +1,7 @@
 class TestResults:
+
     """
-    Clas used to store test results. It has parameters total 
+    Clas used to store test results. It has parameters total
     and passed.
 
     @attribute total:
@@ -8,6 +9,7 @@ class TestResults:
     @attribute passed:
         int, number of passed scenarios.
     """
+
     def __init__(self):
         self.total = 0
         self.passed = 0
@@ -26,7 +28,7 @@ class TestResults:
 
     def add_test(self, status):
         """
-        Method adds one scenario to current results. If 
+        Method adds one scenario to current results. If
         scenario passed, number of passed scenarios increases.
 
         @param status: