Skip to content
This repository was archived by the owner on Jul 10, 2023. It is now read-only.
Draft
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
3 changes: 2 additions & 1 deletion foresight/pytest_integration/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
THUNDRA_TEST_RESULTED = "thundra_test_resulted"
THUNDRA_TEST_FINISH_IN_HELPER = "thundra_test_finish_in_helper"
THUNDRA_FIXTURE_PREFIX = "x_thundra"
THUNDRA_SCOPE = "x-thundra-scope"
THUNDRA_SCOPE = "x-thundra-scope"
THUNDRA_TEST_CASE_CONTEXT = "thundra_test_case-context"
24 changes: 20 additions & 4 deletions foresight/pytest_integration/plugin.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import pytest, logging, os

from foresight.pytest_integration.utils import (patch, check_test_case_result,
update_test_status, set_attributes_test_item, check_test_status_state)
from foresight.pytest_integration.pytest_helper import PytestHelper
from foresight import foresight_executor
import foresight.pytest_integration.constants as pytest_constants
from thundra.context.execution_context_manager import ExecutionContextManager

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -153,6 +151,18 @@ def x_thundra_finish_test(request):
logger.error("Pytest x_thundra_finish_test error: {}".format(e))
pass

"""
Create a dict that stores module: tests
"""
def pytest_collection_finish(session):
if session.config.pluginmanager.has_plugin("parallel"):
for item in session.items:
current_count = PytestHelper.PYTEST_TEST_MODULES_TEST_COUNTER.get(
item.getparent(pytest.Module).nodeid, 0
)
PytestHelper.PYTEST_TEST_MODULES_TEST_COUNTER[item.getparent(pytest.Module).nodeid] = current_count + 1


# Perform the runtest protocol for a single test item
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_protocol(item, nextitem):
Expand All @@ -178,7 +188,13 @@ def pytest_runtest_protocol(item, nextitem):
else:
delattr(item, pytest_constants.THUNDRA_TEST_FINISH_IN_HELPER)
if not nextitem or item.getparent(pytest.Module).nodeid != nextitem.getparent(pytest.Module).nodeid:
PytestHelper.finish_test_suite_span()
with PytestHelper.PYTEST_COUNTER_LOCK:
test_suite_id = item.getparent(pytest.Module).nodeid
if test_suite_id in PytestHelper.PYTEST_TEST_MODULES_TEST_COUNTER:
PytestHelper.PYTEST_TEST_MODULES_TEST_COUNTER[test_suite_id] -= 1
if PytestHelper.PYTEST_TEST_MODULES_TEST_COUNTER[test_suite_id] == 0:
del PytestHelper.PYTEST_TEST_MODULES_TEST_COUNTER[test_suite_id]
PytestHelper.finish_test_suite_span(test_suite_id)
except Exception as e:
logger.error("Pytest runtest_protocol error: {}".format(e))
pass
Expand All @@ -204,7 +220,7 @@ def pytest_runtest_makereport(item, call):
if not status:
return
result = outcome.get_result()
execution_context = ExecutionContextManager.get()
execution_context = getattr(item, pytest_constants.THUNDRA_TEST_CASE_CONTEXT)
# After Function call report to get test status(success, failed, aborted, skipped , ignored)
test_status = check_test_case_result(item, execution_context, result, exception)
update_test_status(item, test_status, execution_context)
Expand Down
20 changes: 14 additions & 6 deletions foresight/pytest_integration/pytest_helper.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import threading
from foresight.test_runner_support import TestRunnerSupport
from thundra.application.application_info import ApplicationInfo
from thundra.application.application_info_provider import ApplicationInfoProvider
from foresight.test_runner_tags import TestRunnerTags
Expand Down Expand Up @@ -50,7 +52,8 @@ class PytestHelper:
TEST_CASE = "function"
PYTEST_STARTED = False
PYTEST_COLLECT_ONLY = False

PYTEST_TEST_MODULES_TEST_COUNTER = {} # module_id: count
PYTEST_COUNTER_LOCK = threading.Lock()

@staticmethod
def get_test_application_name(request):
Expand Down Expand Up @@ -167,6 +170,7 @@ def clear_test_case_state_for_thundra(item):
delattr(item, pytest_constants.THUNDRA_TEST_RESULTED)
delattr(item, pytest_constants.THUNDRA_TEST_STARTED)
delattr(item, pytest_constants.THUNDRA_TEST_ALREADY_FINISHED)
delattr(item, pytest_constants.THUNDRA_TEST_CASE_CONTEXT)
setattr(item, pytest_constants.THUNDRA_TEST_FINISH_IN_HELPER, True)


Expand Down Expand Up @@ -226,7 +230,7 @@ def start_after_all_span(cls, request):
try:
app_info = cls.get_test_fixture_application_info(request).to_json()
span_tags = {TestRunnerTags.TEST_SUITE: request.node.nodeid, TestRunnerTags.TEST_FIXTURE: request.fixturename}
SpanManager.handle_fixture_and_inject_span(HandlerUtils.start_after_all_span, app_info, span_tags,
SpanManager.handle_fixture_and_inject_span(HandlerUtils.start_after_all_span, request.node.nodeid, app_info, span_tags,
request)
except Exception as err:
logger.error("Couldn't start after all span for pytest: {}".format(err), err)
Expand All @@ -244,9 +248,10 @@ def finish_after_all_span(request):


@staticmethod
def finish_test_suite_span():
def finish_test_suite_span(test_suite_id):
try:
HandlerUtils.finish_test_suite_span()
context = TestRunnerSupport.get_test_suite_context(test_suite_id)
HandlerUtils.finish_test_suite_span(context)
except Exception as err:
logger.error("Couldn't finish test suite span for pytest: {}".format(err))
pass
Expand All @@ -259,9 +264,11 @@ def start_test_span(cls, item):
setattr(item, pytest_constants.THUNDRA_TEST_STARTED, True)
name = item.location[cls.TEST_OPERATION_NAME_INDEX]
test_suite_name = item.location[cls.TEST_SUITE_PATH]
test_suite_id = item.getparent(pytest.Module).nodeid
test_case_id = item.nodeid
app_info = cls.get_test_application_info(item)
HandlerUtils.start_test_span(name, test_suite_name, test_case_id, app_info)
test_case_context = HandlerUtils.start_test_span(name, test_suite_id, test_suite_name, test_case_id, app_info)
setattr(item, pytest_constants.THUNDRA_TEST_CASE_CONTEXT, test_case_context)
except Exception as err:
logger.error("Couldn't start test span for pytest: {}".format(err))
pass
Expand Down Expand Up @@ -318,7 +325,8 @@ def finish_test_span(cls, item):
try:
if not hasattr(item, pytest_constants.THUNDRA_TEST_ALREADY_FINISHED):
setattr(item, pytest_constants.THUNDRA_TEST_ALREADY_FINISHED, True)
HandlerUtils.finish_test_span()
module_item = item.getparent(pytest.Module)
HandlerUtils.finish_test_span(module_item.nodeid)
cls.clear_test_case_state_for_thundra(item)
except Exception as err:
logger.error("Couldn't finish test span for pytest: {}".format(err))
Expand Down
4 changes: 3 additions & 1 deletion foresight/pytest_integration/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ def update_test_status(item, test_status, execution_context):
execution_context.set_status(test_status)
if increase_action:
setattr(item, constants.THUNDRA_TEST_RESULTED, True)
increase_action()
import pytest
module_item = item.getparent(pytest.Module)
increase_action(module_item.nodeid)
except Exception as err:
logger.error("Couldn't update test status for pytest: {}".format(err))
pass
Expand Down
30 changes: 22 additions & 8 deletions foresight/test_runner_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ class TestRunnerSupport:
Test suite execution context and test case execution context may be
stored in pytest item object.
'''
test_suite_execution_context = None
test_suite_application_info = None
CONTEXT_INDEX = 0
APP_INFO_INDEX = 1
test_suite_contexts = {} # test_suite_node_id : [context, app_info]
test_run_scope = None
status_reporter = None

Expand All @@ -99,13 +100,27 @@ def get_project_max_span_count():


@classmethod
def set_test_suite_execution_context(cls, execution_context):
cls.test_suite_execution_context = execution_context
def set_test_suite_contexts(cls, test_suite_id, execution_context, app_info):
cls.test_suite_contexts[test_suite_id] = [ execution_context, app_info ]


@classmethod
def set_test_suite_application_info(cls, application_info):
cls.test_suite_application_info = application_info
def get_test_suite_context(cls, test_suite_id):
if test_suite_id in cls.test_suite_contexts:
return cls.test_suite_contexts[test_suite_id][cls.CONTEXT_INDEX]
return

@classmethod
def get_test_suite_app_info(cls, test_suite_id):
if test_suite_id in cls.test_suite_contexts:
return cls.test_suite_contexts[test_suite_id][cls.APP_INFO_INDEX]
return

@classmethod
def complete_test_suite_contexts(cls, finish_test_suite_func):
for key, value in cls.test_suite_contexts.items():
if value[cls.CONTEXT_INDEX].completed == False:
finish_test_suite_func(value[cls.CONTEXT_INDEX])


@classmethod
Expand All @@ -115,8 +130,7 @@ def clear_test_run(cls):

@classmethod
def clear_state(cls):
cls.test_suite_execution_context = None
cls.test_suite_application_info = None
cls.test_suite_contexts = {}


@staticmethod
Expand Down
16 changes: 8 additions & 8 deletions foresight/test_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ class TestStatus:
SKIPPED = "SKIPPED"
TOTAL = "TOTAL"

def increase_successful_count():
def increase_successful_count(test_suite_id):
try:
test_run_context = TestRunnerSupport.test_run_scope
test_suite_context = TestRunnerSupport.test_suite_execution_context
test_suite_context = TestRunnerSupport.get_test_suite_context(test_suite_id)

test_run_context.context.increase_successful_count()
test_suite_context.increase_successful_count()
Expand All @@ -23,10 +23,10 @@ def increase_successful_count():
pass


def increase_failed_count():
def increase_failed_count(test_suite_id):
try:
test_run_context = TestRunnerSupport.test_run_scope
test_suite_context = TestRunnerSupport.test_suite_execution_context
test_suite_context = TestRunnerSupport.get_test_suite_context(test_suite_id)

test_run_context.context.increase_failed_count()
test_suite_context.increase_failed_count()
Expand All @@ -35,10 +35,10 @@ def increase_failed_count():
logger.error("increase_failed_count error: {}".format(err))
pass

def increase_aborted_count():
def increase_aborted_count(test_suite_id):
try:
test_run_context = TestRunnerSupport.test_run_scope
test_suite_context = TestRunnerSupport.test_suite_execution_context
test_suite_context = TestRunnerSupport.get_test_suite_context(test_suite_id)

test_run_context.context.increase_aborted_count()
test_suite_context.increase_aborted_count()
Expand All @@ -48,10 +48,10 @@ def increase_aborted_count():
pass


def increase_skipped_count():
def increase_skipped_count(test_suite_id):
try:
test_run_context = TestRunnerSupport.test_run_scope
test_suite_context = TestRunnerSupport.test_suite_execution_context
test_suite_context = TestRunnerSupport.get_test_suite_context(test_suite_id)

test_run_context.context.increase_ignored_count()
test_suite_context.increase_ignored_count()
Expand Down
27 changes: 12 additions & 15 deletions foresight/utils/handler_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,7 @@ def test_teardown(cls):
It should be changed for concurrent python test framework.
"""
try:
if (TestRunnerSupport.test_suite_execution_context and
not TestRunnerSupport.test_suite_execution_context.completed):
HandlerUtils.finish_test_suite_span()
TestRunnerSupport.complete_test_suite_contexts(HandlerUtils.finish_test_suite_span)
TestRunnerSupport.finish_current_test_run()
terminator = Terminator()
terminator.wait(timeout=30)
Expand All @@ -113,13 +111,12 @@ def start_test_suite_span(cls, test_suite_id, app_info):
It should be changed for concurrent python test framework.
"""
try:
if not TestRunnerSupport.test_suite_execution_context:
if not TestRunnerSupport.get_test_suite_context(test_suite_id):
test_wrapper = TestWrapper.get_instance()
context = test_wrapper.create_test_suite_execution_context(test_suite_id)
ExecutionContextManager.set(context)
test_wrapper.change_app_info(app_info)
TestRunnerSupport.set_test_suite_application_info(app_info)
TestRunnerSupport.set_test_suite_execution_context(context)
TestRunnerSupport.set_test_suite_contexts(test_suite_id, context, app_info)
test_wrapper.before_test_process(context)
except Exception as e:
logger.error("Handler_utils start_test_suite_span error: {}".format(e))
Expand All @@ -143,12 +140,12 @@ def finish_before_all_span(cls, scope):
pass

@classmethod
def start_after_all_span(cls, app_info, span_tags):
def start_after_all_span(cls, test_suite_id, app_info, span_tags):
"""after all executed after test cases. That is why context should be getting from TestRunnerSupport and
set into ExecutionContextManager.
"""
try:
context = TestRunnerSupport.test_suite_execution_context
context = TestRunnerSupport.get_test_suite_context(test_suite_id)
ExecutionContextManager.set(context)
return cls.create_span(cls.TEST_AFTER_ALL_OPERATION_NAME, app_info, span_tags)
except Exception as e:
Expand All @@ -165,10 +162,9 @@ def finish_after_all_span(cls, scope):
pass

@staticmethod
def finish_test_suite_span():
def finish_test_suite_span(context):
try:
test_wrapper = TestWrapper.get_instance()
context = ExecutionContextManager.get()
context.completed = True
test_wrapper.after_test_process(context)
TestRunnerSupport.clear_state()
Expand All @@ -177,15 +173,16 @@ def finish_test_suite_span():
pass

@classmethod
def start_test_span(cls, name, test_suite_name, test_case_id, app_info):
def start_test_span(cls, name, test_suite_id, test_suite_name, test_case_id, app_info):
try:
test_wrapper = TestWrapper.get_instance()
test_wrapper.change_app_info(app_info)
current_context = ExecutionContextManager.get()
current_context = TestRunnerSupport.get_test_suite_context(test_suite_id)
parent_transaction_id = current_context.invocation_data.get("transactionId")
context = test_wrapper.create_test_case_execution_context(name, test_suite_name, test_case_id, app_info, parent_transaction_id)
ExecutionContextManager.set(context)
test_wrapper.before_test_process(context)
return context
except Exception as e:
logger.error("Handler_utils start_test_span error: {}".format(e))
pass
Expand Down Expand Up @@ -225,16 +222,16 @@ def finish_after_each_span(cls, scope):
pass

@staticmethod
def finish_test_span():
def finish_test_span(test_suite_id):
"""Setup TestRunnerSupport for test suite. It's executed after all process has been done
for test case such as before_each, after each etc.
"""
try:
test_wrapper = TestWrapper.get_instance()
context = ExecutionContextManager.get()
test_wrapper.after_test_process(context)
app_info = TestRunnerSupport.test_suite_application_info
context = TestRunnerSupport.test_suite_execution_context
app_info = TestRunnerSupport.get_test_suite_app_info(test_suite_id)
context = TestRunnerSupport.get_test_suite_context(test_suite_id)
test_wrapper.change_app_info(app_info)
ExecutionContextManager.set(context)
except Exception as e:
Expand Down