Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions backend_server/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ def test_case_log_messages(self, test_run_id, test_case):
sql = sql_queries.log_messages(test_run_id, test_id=test_case)
return self.session.query(sql), list_of_dicts

def test_case_log_messages_with_build(self, series, build_number, test_case):
sql = sql_queries.log_messages_with_build(series, build_number, test_id=test_case)
return self.session.query(sql), list_of_dicts

def keyword_tree(self, fingerprint):
sql = "SELECT * FROM keyword_tree WHERE fingerprint=%(fingerprint)s"
return self.session.query(sql, {'fingerprint': fingerprint}), single_dict
Expand All @@ -119,6 +123,11 @@ def subtrees(self, fingerprint):
def keyword_analysis(self, test_series, build_number):
return self.session.query(sql_queries.keyword_analysis(test_series, build_number)), list_of_dicts

def keyword_tree_with_test_id(self, series, build_number, test_id):
return self.session.query(sql_queries.fingerprints_with_id(series, build_number, test_id)), list_of_dicts

def keyword_tree_with_test_run_and_id(self, test_run_id, test_id):
return self.session.query(sql_queries.fingerprints_with_run_and_id(test_run_id, test_id)), list_of_dicts

def list_of_dicts(rows):
results = []
Expand Down
163 changes: 158 additions & 5 deletions backend_server/server.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import argparse
import json
import sys

import copy
import tornado.httpserver
import tornado.ioloop
import tornado.web
Expand Down Expand Up @@ -336,7 +336,15 @@ def __init__(self, database):
SuiteLogMessageDataHandler),
url(r"/data/test_runs/(?P<test_run>[0-9]+)/test_cases/(?P<test>[0-9]+)/log_messages?$",
TestCaseLogMessageDataHandler),
url(r"/data/keyword_tree/(?P<fingerprint>[0-9a-fA-F]{40})/?$", KeywordTreeDataHandler),
url(
r"/data/keyword_tree/(?P<fingerprint>[0-9a-fA-F]{40})/?$", KeywordTreeDataHandler),
url(
r"/data/testcase_keywords/series/(?P<series>[0-9]+)/builds/(?P<build_number>[0-9]+)/test_id/(?P<test_id>[0-9]+)", TestcaseKeywordHandler),
url(
r"/data/testcase_keywords/test_run_id/(?P<test_run_id>[0-9]+)/test_id/(?P<test_id>[0-9]+)", TestcaseKeywordHandlerWithRun),

# url(r"/data/history/?$", OldHistoryDataHandler), # Depricated see HistoryDataHandler
# url(r"/data/metadata/?$", OldMetaDataHandler), # Depricated see MetaDataHandler

# For query testing purposes only
url(r"/data/foo/?$", FooDataHandler)
Expand Down Expand Up @@ -423,6 +431,7 @@ def keyword_tree(self, fingerprint):
if keyword_tree:
keyword_tree['children'] = []
keyword_tree = yield self.child_trees(keyword_tree)
keyword_tree['log_messages'] = []
return keyword_tree
return None

Expand All @@ -435,13 +444,14 @@ def child_trees(self, keyword_tree):
child_tree = self.tree_from_cache(child['fingerprint'])
if not child_tree:
child_tree = yield self.child_trees(child)
child_tree['log_messages'] = []
self._keyword_tree_cache[child['fingerprint']] = child_tree
keyword_tree['children'].append(child_tree)
return keyword_tree

def tree_from_cache(self, fingerprint):
try:
return self._keyword_tree_cache.get(fingerprint, None)
return copy.deepcopy(self._keyword_tree_cache.get(fingerprint, None))
except AttributeError:
self._keyword_tree_cache = {}
return None
Expand Down Expand Up @@ -1521,13 +1531,156 @@ def get(self, series, build_number):
statistics = yield coroutine_query(self.database.keyword_analysis, series, build_number)
self.write({'statistics': statistics})

class KeywordLogHandler(BaseHandler):
def generate_keyword_array(self, testcase_fingerprints, log_messages):
keyword_array = {'setup': None, 'execution': None, 'teardown': None}

setup_fingerprint=(testcase_fingerprints[0]['setup_fingerprint'])
execution_fingerprint=(testcase_fingerprints[0]['execution_fingerprint'])
teardown_fingerprint=(testcase_fingerprints[0]['teardown_fingerprint'])
if setup_fingerprint:
keyword_array['setup'] = yield self.keyword_tree(setup_fingerprint.lower())
if execution_fingerprint:
keyword_array['execution'] = yield self.keyword_tree(execution_fingerprint.lower())
if teardown_fingerprint:
keyword_array['teardown'] = yield self.keyword_tree(teardown_fingerprint.lower())

amount_of_setup= 1 if not keyword_array['setup'] == None else 0
amount_of_execution=int(len(keyword_array['execution']['children']))

for log in log_messages:
parsed_execution_path = (self.parse_execution_path(log['execution_path']))
if(int(parsed_execution_path[0]) <= amount_of_setup):
if(len(parsed_execution_path) == 1):
keyword_array['setup']['log_messages'].append(log['message'])
else:
self.set_log(keyword_array['setup'], parsed_execution_path, log, 'setup')
elif(int(parsed_execution_path[0]) <= (amount_of_setup + amount_of_execution)):
if amount_of_setup == 1:
self.set_log(keyword_array['execution'], parsed_execution_path, log, 'execution')
else:
self.set_log(keyword_array['execution'], parsed_execution_path, log, 'execution_no_setup')
else:
if(len(parsed_execution_path) == 1):
keyword_array['teardown']['log_messages'].append(log['message'])
else:
self.set_log(keyword_array['teardown'], parsed_execution_path, log, 'teardown')

return keyword_array

@classmethod
def set_log(self, tree, path, log, state):
if state == 'setup' or state =='teardown':
path=path[1:]
else:
# If the state is only execution we assume there is a setup and fix indexes accordingly
if(state == 'execution'):
path[0]=(path[0]-1)
for number in path:
if(number-1 < 0):
tree=tree['children'][number]
else:
tree=tree['children'][(number-1)]

tree['log_messages'].append(log['message'])

@classmethod
def parse_execution_path(self, execution_path):
remove_test_case = execution_path.split('-k')
test_cases_int = list(map(lambda x: int(x), remove_test_case[1:]))
return test_cases_int

class TestcaseKeywordHandler(KeywordLogHandler):
@tornado.gen.coroutine
def get(self, series, build_number, test_id):
"""
---
tags:
- Test Case Keywords
summary: Keywords of Test Cases
description: List of keywords within a Test Case Setup, Execution, Teardown and their logs, in the order of execution
produces:
- application/json
parameters:
- name: series
in: path
description: series id
required: true
type: integer
- name: build_number
in: path
description: build number
required: true
type: integer
- name: test_id
in: path
description: test id
required: true
type: integer
responses:
200:
description: List of keywords within a Test Case Setup, Execution, Teardown and their logs
schema:
type: object
properties:
statistics:
type: array
items:
$ref: '#/definitions/BuildKeywordAnalysisObjectModel'
"""
try:
testcase_fingerprints = yield coroutine_query(self.database.keyword_tree_with_test_id, series, build_number, test_id)
log_messages = yield coroutine_query(self.database.test_case_log_messages_with_build, series, build_number, test_id)
keyword_array = yield from self.generate_keyword_array(testcase_fingerprints, log_messages)
self.write({'keywords': keyword_array})
except IndexError:
self.send_not_found_response()

class TestcaseKeywordHandlerWithRun(KeywordLogHandler):
@tornado.gen.coroutine
def get(self, test_run_id, test_id):
"""
---
tags:
- Test Case Keywords
summary: Keywords of Test Cases With Test Run ID
description: List of keywords within a Test Case Setup, Execution, Teardown and their logs, in the order of execution
produces:
- application/json
parameters:
- name: test_run_id
in: path
description: test run id
required: true
type: integer
- name: test_id
in: path
description: test id
required: true
type: integer
responses:
200:
description: List of keywords within a Test Case Setup, Execution, Teardown and their logs
schema:
type: object
properties:
statistics:
type: array
items:
$ref: '#/definitions/BuildKeywordAnalysisObjectModel'
"""
try:
testcase_fingerprints = yield coroutine_query(self.database.keyword_tree_with_test_run_and_id, test_run_id, test_id)
log_messages = yield coroutine_query(self.database.test_case_log_messages, test_run_id, test_id)
keyword_array = yield from self.generate_keyword_array(testcase_fingerprints, log_messages)
self.write({'keywords': keyword_array})
except IndexError:
self.send_not_found_response()

class FooDataHandler(BaseHandler):
def get(self):
self.write({'suites': []})



def main():
parser = argparse.ArgumentParser(
description='Test manager 2.0 backend server')
Expand Down
34 changes: 34 additions & 0 deletions backend_server/sql_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,20 @@ def log_messages(test_run_id, suite_id=None, test_id=None):
suite_filter="AND suite_id={}".format(int(suite_id)) if suite_id else '',
test_filter="test_id={}".format(int(test_id)) if test_id else 'test_id IS NULL')

def log_messages_with_build(series, build_number, suite_id=None, test_id=None):
return """
SELECT *
FROM log_message
WHERE test_run_id IN ({test_run_ids})
AND {test_filter}
{suite_filter}
ORDER BY timestamp, id
""".format(suite_filter="AND suite_id={}".format(int(suite_id)) if suite_id else '',
test_filter="test_id={}".format(int(test_id)) if test_id else 'test_id IS NULL',
test_run_ids=test_run_ids(series, build_num=build_number))



def most_stable_tests(series, start_from, last, offset, limit, limit_offset, stable):
return """
SELECT suite_id, suite_name, suite_full_name,
Expand Down Expand Up @@ -544,3 +558,23 @@ def keyword_analysis(series, build_number):
GROUP BY tree.library, tree.keyword, total_elapsed.total
ORDER BY total DESC
""".format(test_run_ids=test_run_ids(series, build_num=build_number))

def fingerprints_with_run_and_id(test_run_id, test_id):
return """
SELECT setup_fingerprint, execution_fingerprint,
teardown_fingerprint, test_id,
test_run_id, execution_path
FROM test_result
WHERE test_id={test_id} and test_run_id={test_run_id}
""".format(test_id=test_id,
test_run_id= test_run_id)

def fingerprints_with_id(series, build_number,test_id):
return """
SELECT setup_fingerprint, execution_fingerprint,
teardown_fingerprint, test_id,
test_run_id, execution_path
FROM test_result
WHERE test_id={test_id} and test_run_id IN ({test_run_ids})
""".format(test_id=test_id,
test_run_ids=test_run_ids(series, build_num=build_number))
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
*** Settings ***
Force Tags exec_path

*** Test cases ***

API documentation page
[Setup] Run Keywords Empty Keyword Info Log Keyword
[Teardown] Run Keywords Empty Keyword Info Log Keyword
Empty Keyword
Info Log Keyword
Warn Log Keyword
Empty Keyword
Info Log Keyword

*** Keywords ***


Empty Keyword
No Operation

Info Log Keyword
Log Keyword INFO

Warn Log Keyword
Log Keyword WARN