From 1cb95ddd917317f189e1c082a0e283447570c39a Mon Sep 17 00:00:00 2001 From: pasverma Date: Wed, 13 Sep 2023 10:28:22 +0530 Subject: [PATCH 01/12] GTA for time.sleep --- cafy_pytest/cafy_gta.py | 123 ++++++++++++++++++++++++ cafy_pytest/plugin.py | 4 + cafy_pytest/resources/gta_template.html | 50 ++++++++++ 3 files changed, 177 insertions(+) create mode 100644 cafy_pytest/cafy_gta.py create mode 100644 cafy_pytest/resources/gta_template.html diff --git a/cafy_pytest/cafy_gta.py b/cafy_pytest/cafy_gta.py new file mode 100644 index 0000000..7748078 --- /dev/null +++ b/cafy_pytest/cafy_gta.py @@ -0,0 +1,123 @@ +import time +import builtins +import subprocess +import pytest +from logger.cafylog import CafyLog +import json +import os +import inspect +from jinja2 import Template + + +class TimeCollectorPlugin: + def __init__(self): + self.original_sleep = time.sleep + self.original_subprocess_run = subprocess.run + self.original_exec = builtins.exec + self.granular_time_testcase_dict = dict() + self.test_case_name = None + self.start_time = None + self.total_execution_time = None + + def measure_sleep_time(self, duration): + ''' + Method measure_sleep_time : it will measure the actual time taken by testcase method during sleep + param duration: duration or sleep time declared in TC fucntion's + return : Update the graunular time at test case level + ''' + start_time = time.perf_counter() + self.original_sleep(duration) + end_time = time.perf_counter() + elapsed_time = '%.2f' % (end_time - start_time) + self.update_granular_time("sleep_time", elapsed_time) + + def update_granular_time(self, category, elapsed_time): + ''' + Method update_granular_time : it will update the time at test case level + param category : category like bash time, sleep time etc. + param elapsed_time : time spend during event like sleep, bash etc + return : Update the graunular time at test case level + ''' + current_test = self.test_case_name + if current_test not in self.granular_time_testcase_dict: + self.granular_time_testcase_dict[current_test] = {category: dict()} + if category == "sleep_time": + self.granular_time_testcase_dict[current_test][category]["total_sleep_time"] = float(elapsed_time) + else: + if category in self.granular_time_testcase_dict[current_test]: + if category == "sleep_time": + self.granular_time_testcase_dict[current_test][category]["total_sleep_time"] += float(elapsed_time) + + def pytest_runtest_protocol(self, item, nextitem): + ''' + Method pytest_runtest_protocol : it will Monkey patch sleep , subprocess run etc + Monkey patching used for modifying the behavior of built-in classes or functions, or adding instrumentation or logging to existing code. + param item : test case item + param nextitem : test case nextitem + return : None + ''' + self.start_time = time.perf_counter() + # Monkey patch time.sleep + time.sleep = self.measure_sleep_time + #get class name of test case method + base_class_name = "" + if item.cls: + class_name = item.cls + base_classes = inspect.getmro(class_name) + base_class_name = base_classes[0].__name__ + if base_class_name: + self.test_case_name = f"{base_class_name}.{item.name}" + else: + self.test_case_name = f"{item.name}" + return None + + def pytest_runtest_teardown(self, item, nextitem): + ''' + Method pytest_runtest_call : will measure Total execution time of test case method + return : Update the graunular time at test case level + ''' + end_time = time.perf_counter() + self.total_execution_time ='%.2f' % (end_time - self.start_time) + current_test = self.test_case_name + self.granular_time_testcase_dict[current_test]["total_time"] = self.total_execution_time + self.total_execution_time = None + + def collect_granular_time_accouting_report(self): + ''' + Method collect_granular_time_accouting_report : it will create report and save in cafy work dir + return : create report for time accounting in cafy work dir as granular_time_report.json + ''' + time_report = dict() + for test_case, times in self.granular_time_testcase_dict.items(): + sleep_time = times.get("sleep_time", 0) + total_time = times.get("total_time",0) + time_report[test_case] = dict() + time_report[test_case]['Sleep time'] = sleep_time + time_report[test_case]['Bash time'] = None + time_report[test_case]['Exec Time'] = None + time_report[test_case]['Config Time'] = None + time_report[test_case]['Get level command Time'] = None + time_report[test_case]['Set level command Time'] = None + time_report[test_case]['Inter Commands Delay Time'] = None + time_report[test_case]['Total Execution Time'] = total_time + self.granular_time_testcase_dict = time_report + + def pytest_terminal_summary(self, terminalreporter): + ''' + Method pytest_terminal_summary : terminal reporting + return : None + ''' + self.collect_granular_time_accouting_report() + # Create a Jinja2 environment and load the HTML template + CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) + template_file = os.path.join(CURRENT_DIR,"resources/gta_template.html") + with open(template_file) as html_src: + html_template = html_src.read() + template = Template(html_template) + html_content = template.render(dictionary_data=self.granular_time_testcase_dict) + # Define the path to the output HTML file + path=CafyLog.work_dir + html_file_path = os.path.join(path, 'granular_time_report.html') + # Write the HTML content to the output file + with open(html_file_path, 'w') as html_file: + html_file.write(html_content) \ No newline at end of file diff --git a/cafy_pytest/plugin.py b/cafy_pytest/plugin.py index 5ad5ece..6abbad8 100644 --- a/cafy_pytest/plugin.py +++ b/cafy_pytest/plugin.py @@ -48,6 +48,7 @@ from utils.collectors.confest import Config from .cafy import Cafy +from .cafy_gta import TimeCollectorPlugin from .cafy_pdb import CafyPdb from .cafypdb_config import CafyPdb_Configs @@ -558,6 +559,7 @@ def pytest_configure(config): collection_list, cafypdb) config.pluginmanager.register(config._email) + config.pluginmanager.register(TimeCollectorPlugin()) #Write all.log path to terminal reporter = TerminalReporter(config, sys.stdout) @@ -585,6 +587,8 @@ def pytest_unconfigure(config): tmp_str_text = str(tmp_text) with open(os.path.join(CafyLog.work_dir, "env.txt"), "w") as f: f.write(tmp_str_text) + time_collector_plugin = config.pluginmanager.get_plugin(TimeCollectorPlugin) + config.pluginmanager.unregister(time_collector_plugin) except: pass diff --git a/cafy_pytest/resources/gta_template.html b/cafy_pytest/resources/gta_template.html new file mode 100644 index 0000000..df7bbbc --- /dev/null +++ b/cafy_pytest/resources/gta_template.html @@ -0,0 +1,50 @@ + + + + + + +

Granular Time Accounting Report

+ + + + + + + + + + + + + {% for key, values in dictionary_data.items() %} + + + + + + + + + + + + {% endfor %} +
Test CaseTotal Sleep Time(sec)Bash Time(sec)Exec Time(sec)Config Time(sec)Get Level Command Time(sec)Set Level Command Time(sec)Inter Commands Delay Time(sec)Total Execution Time(sec)
{{ key }}{{ values["Sleep time"]["total_sleep_time"] }}{{ values["Bash time"] }}{{ values["Exec Time"] }}{{ values["Config Time"] }}{{ values["Get level command Time"] }}{{ values["Set level command Time"] }}{{ values["Inter Commands Delay Time"] }}{{ values["Total Execution Time"] }}
+ + From 47d73a732ee8604931347fb87e1567fc2c3bf16d Mon Sep 17 00:00:00 2001 From: pasverma Date: Wed, 20 Sep 2023 11:56:39 +0530 Subject: [PATCH 02/12] updated bash time reporting --- cafy_pytest/cafy_gta.py | 85 +++++++++++++++++++------ cafy_pytest/resources/gta_template.html | 13 +++- 2 files changed, 79 insertions(+), 19 deletions(-) diff --git a/cafy_pytest/cafy_gta.py b/cafy_pytest/cafy_gta.py index 7748078..8987b1c 100644 --- a/cafy_pytest/cafy_gta.py +++ b/cafy_pytest/cafy_gta.py @@ -8,12 +8,10 @@ import inspect from jinja2 import Template - class TimeCollectorPlugin: def __init__(self): self.original_sleep = time.sleep self.original_subprocess_run = subprocess.run - self.original_exec = builtins.exec self.granular_time_testcase_dict = dict() self.test_case_name = None self.start_time = None @@ -31,22 +29,38 @@ def measure_sleep_time(self, duration): elapsed_time = '%.2f' % (end_time - start_time) self.update_granular_time("sleep_time", elapsed_time) - def update_granular_time(self, category, elapsed_time): + def measure_subprocess_run(self, *args, **kwargs): ''' - Method update_granular_time : it will update the time at test case level - param category : category like bash time, sleep time etc. - param elapsed_time : time spend during event like sleep, bash etc + Method measure_subprocess_run : it will measure the actual time taken by testcase function during executing subprocess run + param args : args + param kwargs : kwargs return : Update the graunular time at test case level ''' - current_test = self.test_case_name - if current_test not in self.granular_time_testcase_dict: - self.granular_time_testcase_dict[current_test] = {category: dict()} - if category == "sleep_time": - self.granular_time_testcase_dict[current_test][category]["total_sleep_time"] = float(elapsed_time) - else: - if category in self.granular_time_testcase_dict[current_test]: - if category == "sleep_time": - self.granular_time_testcase_dict[current_test][category]["total_sleep_time"] += float(elapsed_time) + start_time = time.perf_counter() + # Capture the command + command = args[0] if args else None + process = self.original_subprocess_run(*args, **kwargs) + end_time = time.perf_counter() + elapsed_time = '%.2f' % (end_time - start_time) + self.update_granular_time("bash_time", elapsed_time, command) + + def patch_subprocess(self): + ''' + Method patch_subprocess : it will Monkey patch subprocess gfor bash commands. + Monkey patching used for modifying the behavior of built-in classes or functions, or adding instrumentation or logging to existing code. + param item : test case item + param nextitem : test case nextitem + return : None + ''' + # Monkey patch subprocess.run + subprocess.run = self.measure_subprocess_run + + def unpatch_subprocess(self): + ''' + Method unpatch_subprocess : it will Restore the original subprocess functions + ''' + # Restore the original subprocess functions + subprocess.run = self.original_subprocess_run def pytest_runtest_protocol(self, item, nextitem): ''' @@ -59,6 +73,10 @@ def pytest_runtest_protocol(self, item, nextitem): self.start_time = time.perf_counter() # Monkey patch time.sleep time.sleep = self.measure_sleep_time + + #Monkey patch subprocess + self.patch_subprocess() + #get class name of test case method base_class_name = "" if item.cls: @@ -71,6 +89,30 @@ def pytest_runtest_protocol(self, item, nextitem): self.test_case_name = f"{item.name}" return None + def update_granular_time(self, category, elapsed_time, command = None): + ''' + Method update_granular_time : it will update the time at test case level + param category : category like bash time, sleep time etc. + param elapsed_time : time spend during event like sleep, bash etc + return : Update the graunular time at test case level + ''' + current_test = self.test_case_name + if current_test not in self.granular_time_testcase_dict: + self.granular_time_testcase_dict[current_test] = dict() + + if category not in self.granular_time_testcase_dict[current_test]: + self.granular_time_testcase_dict[current_test][category] = dict() + if category == "sleep_time": + self.granular_time_testcase_dict[current_test][category]["total_sleep_time"] = float(elapsed_time) + elif category == "bash_time" and command: + self.granular_time_testcase_dict[current_test][category][str(command)] = elapsed_time + else: + if category == "sleep_time": + self.granular_time_testcase_dict[current_test][category]["total_sleep_time"] += float(elapsed_time) + elif category == "bash_time" and command: + if str(command) not in self.granular_time_testcase_dict[current_test][category]: + self.granular_time_testcase_dict[current_test][category][str(command)] = elapsed_time + def pytest_runtest_teardown(self, item, nextitem): ''' Method pytest_runtest_call : will measure Total execution time of test case method @@ -91,9 +133,10 @@ def collect_granular_time_accouting_report(self): for test_case, times in self.granular_time_testcase_dict.items(): sleep_time = times.get("sleep_time", 0) total_time = times.get("total_time",0) + bash_time = times.get("bash_time",0) time_report[test_case] = dict() time_report[test_case]['Sleep time'] = sleep_time - time_report[test_case]['Bash time'] = None + time_report[test_case]['Bash time'] = bash_time time_report[test_case]['Exec Time'] = None time_report[test_case]['Config Time'] = None time_report[test_case]['Get level command Time'] = None @@ -101,7 +144,12 @@ def collect_granular_time_accouting_report(self): time_report[test_case]['Inter Commands Delay Time'] = None time_report[test_case]['Total Execution Time'] = total_time self.granular_time_testcase_dict = time_report - + + + def cleanup(self): + # Restore the original subprocess functions when done + self.unpatch_subprocess() + def pytest_terminal_summary(self, terminalreporter): ''' Method pytest_terminal_summary : terminal reporting @@ -120,4 +168,5 @@ def pytest_terminal_summary(self, terminalreporter): html_file_path = os.path.join(path, 'granular_time_report.html') # Write the HTML content to the output file with open(html_file_path, 'w') as html_file: - html_file.write(html_content) \ No newline at end of file + html_file.write(html_content) + self.cleanup() diff --git a/cafy_pytest/resources/gta_template.html b/cafy_pytest/resources/gta_template.html index df7bbbc..9c70941 100644 --- a/cafy_pytest/resources/gta_template.html +++ b/cafy_pytest/resources/gta_template.html @@ -36,7 +36,18 @@

Granular Time Accounting Report

{{ key }} {{ values["Sleep time"]["total_sleep_time"] }} - {{ values["Bash time"] }} + + {% if values['Bash time'] is mapping %} + + + {% else %} + {{ values['Bash time'] }} + {% endif %} + {{ values["Exec Time"] }} {{ values["Config Time"] }} {{ values["Get level command Time"] }} From 83a6696e77ab8bb0b9f4e7be756a6d8ecd4c488c Mon Sep 17 00:00:00 2001 From: pasverma Date: Tue, 16 Jan 2024 20:28:16 +0530 Subject: [PATCH 03/12] update --- cafy_pytest/cafy_gta.py | 34 +++++----- cafy_pytest/resources/gta_template.html | 87 +++++++++++++++---------- 2 files changed, 72 insertions(+), 49 deletions(-) diff --git a/cafy_pytest/cafy_gta.py b/cafy_pytest/cafy_gta.py index 8987b1c..93961f1 100644 --- a/cafy_pytest/cafy_gta.py +++ b/cafy_pytest/cafy_gta.py @@ -103,15 +103,17 @@ def update_granular_time(self, category, elapsed_time, command = None): if category not in self.granular_time_testcase_dict[current_test]: self.granular_time_testcase_dict[current_test][category] = dict() if category == "sleep_time": - self.granular_time_testcase_dict[current_test][category]["total_sleep_time"] = float(elapsed_time) + self.granular_time_testcase_dict[current_test][category]["sleep_time"] = list() + self.granular_time_testcase_dict[current_test][category]["sleep_time"].append(float(elapsed_time)) elif category == "bash_time" and command: - self.granular_time_testcase_dict[current_test][category][str(command)] = elapsed_time + self.granular_time_testcase_dict[current_test][category] = dict() + self.granular_time_testcase_dict[current_test][category][str(command)] = float(elapsed_time) else: if category == "sleep_time": - self.granular_time_testcase_dict[current_test][category]["total_sleep_time"] += float(elapsed_time) + self.granular_time_testcase_dict[current_test][category]["sleep_time"].append(float(elapsed_time)) elif category == "bash_time" and command: if str(command) not in self.granular_time_testcase_dict[current_test][category]: - self.granular_time_testcase_dict[current_test][category][str(command)] = elapsed_time + self.granular_time_testcase_dict[current_test][category][str(command)] = float(elapsed_time) def pytest_runtest_teardown(self, item, nextitem): ''' @@ -121,7 +123,7 @@ def pytest_runtest_teardown(self, item, nextitem): end_time = time.perf_counter() self.total_execution_time ='%.2f' % (end_time - self.start_time) current_test = self.test_case_name - self.granular_time_testcase_dict[current_test]["total_time"] = self.total_execution_time + self.granular_time_testcase_dict[current_test]["total_execution_time"] = self.total_execution_time self.total_execution_time = None def collect_granular_time_accouting_report(self): @@ -131,18 +133,17 @@ def collect_granular_time_accouting_report(self): ''' time_report = dict() for test_case, times in self.granular_time_testcase_dict.items(): - sleep_time = times.get("sleep_time", 0) - total_time = times.get("total_time",0) - bash_time = times.get("bash_time",0) + total_time = 0 time_report[test_case] = dict() - time_report[test_case]['Sleep time'] = sleep_time - time_report[test_case]['Bash time'] = bash_time - time_report[test_case]['Exec Time'] = None - time_report[test_case]['Config Time'] = None - time_report[test_case]['Get level command Time'] = None - time_report[test_case]['Set level command Time'] = None - time_report[test_case]['Inter Commands Delay Time'] = None - time_report[test_case]['Total Execution Time'] = total_time + if 'sleep_time' in times: + time_report[test_case]['sleep_time'] = times["sleep_time"] + else: + time_report[test_case]['sleep_time'] = [] + if 'bash_time' in times: + time_report[test_case]['bash_time'] = times['bash_time'] + else: + time_report[test_case]['bash_time'] = {} + time_report[test_case]['total_execution_time'] = times["total_execution_time"] self.granular_time_testcase_dict = time_report @@ -150,6 +151,7 @@ def cleanup(self): # Restore the original subprocess functions when done self.unpatch_subprocess() + def pytest_terminal_summary(self, terminalreporter): ''' Method pytest_terminal_summary : terminal reporting diff --git a/cafy_pytest/resources/gta_template.html b/cafy_pytest/resources/gta_template.html index 9c70941..d215e85 100644 --- a/cafy_pytest/resources/gta_template.html +++ b/cafy_pytest/resources/gta_template.html @@ -16,46 +16,67 @@ th { background-color: #f2f2f2; } + + .nested { + margin-left: 20px; + }

Granular Time Accounting Report

- - - - - - - - - + + + ̉ + + - {% for key, values in dictionary_data.items() %} - - - - + + + + + + + {% if time_values is iterable and time_values|length > 1 %} + {% for extra_time in time_values[1:] %} + + + + + + + + {% endfor %} + {% endif %} + {% endfor %} + {% elif 'bash_time' in step_key %} + {% for bash_key, bash_value in step_values.items() %} + + + + + + + + {% endfor %} {% endif %} - - - - - - - - - {% endfor %} -
Test CaseTotal Sleep Time(sec)Bash Time(sec)Exec Time(sec)Config Time(sec)Get Level Command Time(sec)Set Level Command Time(sec)Inter Commands Delay Time(sec)Total Execution Time(sec)TestcaseStepSleep TimeSet Level Command TimeTotal Time
{{ key }}{{ values["Sleep time"]["total_sleep_time"] }} - {% if values['Bash time'] is mapping %} - -
    - {% for bash_key, bash_value in values['Bash time'].items() %} -
  • {{ bash_key }}: {{ bash_value }}
  • - {% endfor %} -
- {% else %} - {{ values['Bash time'] }} + {% for key, values in dictionary_data.items() %} + {% for step_key, step_values in values.items() %} + {% if 'sleep_time' in step_key %} + {% for time_key, time_values in step_values.items() %} +
{{key}}{{ time_key }}{{ time_values[0] if time_values else 'NA' }}NANA
{{ time_key }}{{ extra_time }}NANA
{{ bash_key }}NA{{ bash_value }}NA
{{ values["Exec Time"] }}{{ values["Config Time"] }}{{ values["Get level command Time"] }}{{ values["Set level command Time"] }}{{ values["Inter Commands Delay Time"] }}{{ values["Total Execution Time"] }}
+ {% endfor %} + + + Total + NA + NA + + {{values["total_execution_time"]}} + + + {% endfor %} + From 7a894eab8e18e3f2824531055481d781d18166e0 Mon Sep 17 00:00:00 2001 From: pasverma Date: Mon, 29 Jan 2024 19:43:45 +0530 Subject: [PATCH 04/12] update --- cafy_pytest/cafy_gta.py | 144 ++++++++++++++---------- cafy_pytest/resources/gta_template.html | 78 ++++++++----- 2 files changed, 134 insertions(+), 88 deletions(-) diff --git a/cafy_pytest/cafy_gta.py b/cafy_pytest/cafy_gta.py index 93961f1..c171fd1 100644 --- a/cafy_pytest/cafy_gta.py +++ b/cafy_pytest/cafy_gta.py @@ -1,9 +1,6 @@ import time -import builtins -import subprocess import pytest from logger.cafylog import CafyLog -import json import os import inspect from jinja2 import Template @@ -11,11 +8,44 @@ class TimeCollectorPlugin: def __init__(self): self.original_sleep = time.sleep - self.original_subprocess_run = subprocess.run self.granular_time_testcase_dict = dict() self.test_case_name = None self.start_time = None self.total_execution_time = None + self.original_set_methods = {} + + def measure_time_for_set_methods(self, method): + """ + Measure the time taken by set methods. + This method wraps set methods to measure their execution time. + param method (function): The set method to be measured. + Returns: function: The wrapped method. + """ + def wrapper(*args, **kwargs): + start_time = time.perf_counter() + result = method(*args, **kwargs) + end_time = time.perf_counter() + elapsed_time = '%.2f' % (end_time - start_time) + # Update granular time at the test case level + current_test = self.test_case_name + if current_test not in self.granular_time_testcase_dict: + self.granular_time_testcase_dict[current_test] = dict() + method_name = method.__name__ + if method_name.startswith('set'): + if 'set_command' not in self.granular_time_testcase_dict[current_test]: + self.granular_time_testcase_dict[current_test]['set_command'] = dict() + if method_name not in self.granular_time_testcase_dict[current_test]['set_command']: + self.granular_time_testcase_dict[current_test]['set_command'][method_name] = [] + self.granular_time_testcase_dict[current_test]['set_command'][method_name].append(float(elapsed_time)) + elif method_name.startswith('get'): + if 'get_command' not in self.granular_time_testcase_dict[current_test]: + self.granular_time_testcase_dict[current_test]['get_command'] = dict() + if method_name not in self.granular_time_testcase_dict[current_test]['get_command']: + self.granular_time_testcase_dict[current_test]['get_command'][method_name] = [] + self.granular_time_testcase_dict[current_test]['get_command'][method_name].append(float(elapsed_time)) + return result + + return wrapper def measure_sleep_time(self, duration): ''' @@ -29,38 +59,38 @@ def measure_sleep_time(self, duration): elapsed_time = '%.2f' % (end_time - start_time) self.update_granular_time("sleep_time", elapsed_time) - def measure_subprocess_run(self, *args, **kwargs): + def patch_set_methods_for_test_instance(self, item): + """ + Perform setup and teardown actions for test cases. + This method performs setup and teardown actions for test cases,including monkey patching sleep and set methods. + param request: The test request. + Yields: None + """ + test_case_class = item.cls + if test_case_class: + # Get the module of the test case class + module = inspect.getmodule(test_case_class) + for class_name, class_obj in inspect.getmembers(module, inspect.isclass): + # Iterate over the attributes of the class + for method_name, method in inspect.getmembers(class_obj, inspect.isfunction): + # Check if the attribute is callable and its name starts with 'set' + if callable(method) and method_name.startswith('set') or method_name.startswith('get') : + original_method = getattr(class_obj, method_name) + setattr(class_obj, method_name, self.measure_time_for_set_methods(original_method)) + + @pytest.fixture(autouse=True) + def setup_and_teardown(self, request): + ''' + Method setup_and_teardown : it will Monkey patch the sleep time, set level command and get level command etc + param request: take request ''' - Method measure_subprocess_run : it will measure the actual time taken by testcase function during executing subprocess run - param args : args - param kwargs : kwargs - return : Update the graunular time at test case level - ''' - start_time = time.perf_counter() - # Capture the command - command = args[0] if args else None - process = self.original_subprocess_run(*args, **kwargs) - end_time = time.perf_counter() - elapsed_time = '%.2f' % (end_time - start_time) - self.update_granular_time("bash_time", elapsed_time, command) - - def patch_subprocess(self): - ''' - Method patch_subprocess : it will Monkey patch subprocess gfor bash commands. - Monkey patching used for modifying the behavior of built-in classes or functions, or adding instrumentation or logging to existing code. - param item : test case item - param nextitem : test case nextitem - return : None - ''' - # Monkey patch subprocess.run - subprocess.run = self.measure_subprocess_run - - def unpatch_subprocess(self): - ''' - Method unpatch_subprocess : it will Restore the original subprocess functions - ''' - # Restore the original subprocess functions - subprocess.run = self.original_subprocess_run + self.start_time = time.perf_counter() + # Monkey patch time.sleep + time.sleep = self.measure_sleep_time + # Monkey patch 'set' methods for all classes in the module + self.patch_set_methods_for_test_instance(request.node) + yield # This is where the test case runs + self.pytest_runtest_teardown(request.node, None) def pytest_runtest_protocol(self, item, nextitem): ''' @@ -70,14 +100,7 @@ def pytest_runtest_protocol(self, item, nextitem): param nextitem : test case nextitem return : None ''' - self.start_time = time.perf_counter() - # Monkey patch time.sleep - time.sleep = self.measure_sleep_time - - #Monkey patch subprocess - self.patch_subprocess() - - #get class name of test case method + # get class name of the test case method base_class_name = "" if item.cls: class_name = item.cls @@ -105,23 +128,26 @@ def update_granular_time(self, category, elapsed_time, command = None): if category == "sleep_time": self.granular_time_testcase_dict[current_test][category]["sleep_time"] = list() self.granular_time_testcase_dict[current_test][category]["sleep_time"].append(float(elapsed_time)) - elif category == "bash_time" and command: + elif category == "set_command" and command: self.granular_time_testcase_dict[current_test][category] = dict() self.granular_time_testcase_dict[current_test][category][str(command)] = float(elapsed_time) + elif category == "get_command" and command: + self.granular_time_testcase_dict[current_test][category] = dict() + self.granular_time_testcase_dict[current_test][category][str(command)] = float(elapsed_time) + else: if category == "sleep_time": self.granular_time_testcase_dict[current_test][category]["sleep_time"].append(float(elapsed_time)) - elif category == "bash_time" and command: + elif category == "set_command" and command: + if str(command) not in self.granular_time_testcase_dict[current_test][category]: + self.granular_time_testcase_dict[current_test][category][str(command)] = float(elapsed_time) + elif category == "get_command" and command: if str(command) not in self.granular_time_testcase_dict[current_test][category]: self.granular_time_testcase_dict[current_test][category][str(command)] = float(elapsed_time) def pytest_runtest_teardown(self, item, nextitem): - ''' - Method pytest_runtest_call : will measure Total execution time of test case method - return : Update the graunular time at test case level - ''' end_time = time.perf_counter() - self.total_execution_time ='%.2f' % (end_time - self.start_time) + self.total_execution_time = '%.2f' % (end_time - self.start_time) current_test = self.test_case_name self.granular_time_testcase_dict[current_test]["total_execution_time"] = self.total_execution_time self.total_execution_time = None @@ -133,25 +159,22 @@ def collect_granular_time_accouting_report(self): ''' time_report = dict() for test_case, times in self.granular_time_testcase_dict.items(): - total_time = 0 time_report[test_case] = dict() if 'sleep_time' in times: time_report[test_case]['sleep_time'] = times["sleep_time"] else: time_report[test_case]['sleep_time'] = [] - if 'bash_time' in times: - time_report[test_case]['bash_time'] = times['bash_time'] + if 'set_command' in times: + time_report[test_case]['set_command'] = times['set_command'] else: - time_report[test_case]['bash_time'] = {} + time_report[test_case]['set_command'] = {} + if 'get_command' in times: + time_report[test_case]['get_command'] = times['get_command'] + else: + time_report[test_case]['get_command'] = {} time_report[test_case]['total_execution_time'] = times["total_execution_time"] self.granular_time_testcase_dict = time_report - - def cleanup(self): - # Restore the original subprocess functions when done - self.unpatch_subprocess() - - def pytest_terminal_summary(self, terminalreporter): ''' Method pytest_terminal_summary : terminal reporting @@ -171,4 +194,3 @@ def pytest_terminal_summary(self, terminalreporter): # Write the HTML content to the output file with open(html_file_path, 'w') as html_file: html_file.write(html_content) - self.cleanup() diff --git a/cafy_pytest/resources/gta_template.html b/cafy_pytest/resources/gta_template.html index d215e85..a8c6a79 100644 --- a/cafy_pytest/resources/gta_template.html +++ b/cafy_pytest/resources/gta_template.html @@ -28,20 +28,23 @@

Granular Time Accounting Report

Testcase Step - Sleep Timẻ + Sleep Time Set Level Command Time + Get Level Command Time Total Time {% for key, values in dictionary_data.items() %} - {% for step_key, step_values in values.items() %} - {% if 'sleep_time' in step_key %} + {% for step_key, step_values in values.items() %} + {% if 'sleep_time' in step_key %} + {% if step_values is mapping %} {% for time_key, time_values in step_values.items() %} {{key}} {{ time_key }} {{ time_values[0] if time_values else 'NA' }} - NA - NA + - + - + - {% if time_values is iterable and time_values|length > 1 %} {% for extra_time in time_values[1:] %} @@ -49,34 +52,55 @@

Granular Time Accounting Report

{{ time_key }} {{ extra_time }} - NA - NA + - + - + - {% endfor %} {% endif %} {% endfor %} - {% elif 'bash_time' in step_key %} - {% for bash_key, bash_value in step_values.items() %} - - - {{ bash_key }} - NA - {{ bash_value }} - NA - + {% endif %} + {% elif 'set_command' in step_key %} + {% if step_values is mapping %} + {% for set_key, set_value in step_values.items() %} + {% for value in set_value %} + + + {{ set_key }} + - + {{ value }} + - + - + + {% endfor %} {% endfor %} {% endif %} - {% endfor %} - - - Total - NA - NA - - {{values["total_execution_time"]}} - - + {% elif 'get_command' in step_key %} + {% if step_values is mapping %} + {% for get_key, get_value in step_values.items() %} + {% for value in get_value %} + + + {{ get_key }} + - + - + {{ value }} + - + + {% endfor %} + {% endfor %} + {% endif %} + {% endif %} + {% endfor %} + + + Total + - + - + - + {{values["total_execution_time"]}} + {% endfor %} - + From 53ac6575067d59f97bd4a59913cc9cb197ebf9a4 Mon Sep 17 00:00:00 2001 From: pasverma Date: Fri, 2 Feb 2024 19:50:30 +0530 Subject: [PATCH 05/12] update --- cafy_pytest/cafy_gta.py | 40 ++++++++++++++++++++++--- cafy_pytest/resources/gta_template.html | 10 ++++++- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/cafy_pytest/cafy_gta.py b/cafy_pytest/cafy_gta.py index c171fd1..4792e26 100644 --- a/cafy_pytest/cafy_gta.py +++ b/cafy_pytest/cafy_gta.py @@ -4,6 +4,9 @@ import os import inspect from jinja2 import Template +import functools +import sys +import inspect class TimeCollectorPlugin: def __init__(self): @@ -14,13 +17,14 @@ def __init__(self): self.total_execution_time = None self.original_set_methods = {} - def measure_time_for_set_methods(self, method): + def measure_time_for_set_or_get_methods(self, method): """ Measure the time taken by set methods. This method wraps set methods to measure their execution time. param method (function): The set method to be measured. Returns: function: The wrapped method. """ + @functools.wraps(method) def wrapper(*args, **kwargs): start_time = time.perf_counter() result = method(*args, **kwargs) @@ -59,7 +63,7 @@ def measure_sleep_time(self, duration): elapsed_time = '%.2f' % (end_time - start_time) self.update_granular_time("sleep_time", elapsed_time) - def patch_set_methods_for_test_instance(self, item): + def patch_set_or_get_methods_for_test_instance(self, item): """ Perform setup and teardown actions for test cases. This method performs setup and teardown actions for test cases,including monkey patching sleep and set methods. @@ -76,7 +80,7 @@ def patch_set_methods_for_test_instance(self, item): # Check if the attribute is callable and its name starts with 'set' if callable(method) and method_name.startswith('set') or method_name.startswith('get') : original_method = getattr(class_obj, method_name) - setattr(class_obj, method_name, self.measure_time_for_set_methods(original_method)) + setattr(class_obj, method_name, self.measure_time_for_set_or_get_methods(original_method)) @pytest.fixture(autouse=True) def setup_and_teardown(self, request): @@ -88,7 +92,7 @@ def setup_and_teardown(self, request): # Monkey patch time.sleep time.sleep = self.measure_sleep_time # Monkey patch 'set' methods for all classes in the module - self.patch_set_methods_for_test_instance(request.node) + self.patch_set_or_get_methods_for_test_instance(request.node) yield # This is where the test case runs self.pytest_runtest_teardown(request.node, None) @@ -145,12 +149,39 @@ def update_granular_time(self, category, elapsed_time, command = None): if str(command) not in self.granular_time_testcase_dict[current_test][category]: self.granular_time_testcase_dict[current_test][category][str(command)] = float(elapsed_time) + def update_CafyLog_gta_dict(self, current_test): + """ + Update the CafyLog Granular Time Accounting (GTA) dictionary with timing information for the current test + :param current_test:The name of the current test being executed + :return : none + """ + if current_test not in self.granular_time_testcase_dict: + self.granular_time_testcase_dict[current_test] = dict() + if 'set_command' not in self.granular_time_testcase_dict[current_test]: + self.granular_time_testcase_dict[current_test]['set_command'] = dict() + if 'set_command' in CafyLog.gta_dict: + for key, value in CafyLog.gta_dict['set_command'].items(): + self.granular_time_testcase_dict[current_test]['set_command'][key] = value + if 'get_command' not in self.granular_time_testcase_dict[current_test]: + self.granular_time_testcase_dict[current_test]['get_command'] = dict() + if 'get_command' in CafyLog.gta_dict: + for key, value in CafyLog.gta_dict['get_command'].items(): + self.granular_time_testcase_dict[current_test]['get_command'][key] = value + def pytest_runtest_teardown(self, item, nextitem): + """ + Execute teardown actions after a test has been executed + :param item: The test item that was executed + :param nextitem: The next test item in the test suite + :return: None + """ end_time = time.perf_counter() self.total_execution_time = '%.2f' % (end_time - self.start_time) current_test = self.test_case_name self.granular_time_testcase_dict[current_test]["total_execution_time"] = self.total_execution_time self.total_execution_time = None + self.update_CafyLog_gta_dict(current_test) + CafyLog.gta_dict = {} def collect_granular_time_accouting_report(self): ''' @@ -175,6 +206,7 @@ def collect_granular_time_accouting_report(self): time_report[test_case]['total_execution_time'] = times["total_execution_time"] self.granular_time_testcase_dict = time_report + def pytest_terminal_summary(self, terminalreporter): ''' Method pytest_terminal_summary : terminal reporting diff --git a/cafy_pytest/resources/gta_template.html b/cafy_pytest/resources/gta_template.html index a8c6a79..f4d0302 100644 --- a/cafy_pytest/resources/gta_template.html +++ b/cafy_pytest/resources/gta_template.html @@ -34,12 +34,20 @@

Granular Time Accounting Report

Total Time {% for key, values in dictionary_data.items() %} + + {{key}} + + + + + + {% for step_key, step_values in values.items() %} {% if 'sleep_time' in step_key %} {% if step_values is mapping %} {% for time_key, time_values in step_values.items() %} - {{key}} + {{ time_key }} {{ time_values[0] if time_values else 'NA' }} - From a15506e00cd8ecaceb8e30403512c46a2c6b5679 Mon Sep 17 00:00:00 2001 From: pasverma Date: Sat, 10 Feb 2024 20:18:27 +0530 Subject: [PATCH 06/12] update --- cafy_pytest/cafy_gta.py | 138 ++++++++++++------------ cafy_pytest/resources/gta_template.html | 77 ++++++------- 2 files changed, 102 insertions(+), 113 deletions(-) diff --git a/cafy_pytest/cafy_gta.py b/cafy_pytest/cafy_gta.py index 4792e26..45c4bb6 100644 --- a/cafy_pytest/cafy_gta.py +++ b/cafy_pytest/cafy_gta.py @@ -13,11 +13,27 @@ def __init__(self): self.original_sleep = time.sleep self.granular_time_testcase_dict = dict() self.test_case_name = None - self.start_time = None - self.total_execution_time = None - self.original_set_methods = {} + self.total_sleep_time = 0 + self.total_set_command_time = 0 + self.total_get_command_time = 0 - def measure_time_for_set_or_get_methods(self, method): + def update_granular_time_testcase_dict(self, current_test, event, method_name, elapsed_time ): + """ + granular_time_testcase_dict + param current_test: current_test + param event: command like set , get or time.sleep + param method_name: method name + param elapsed_time: total time for each command ie, set or get + """ + if current_test not in self.granular_time_testcase_dict: + self.granular_time_testcase_dict[current_test] = dict() + if event not in self.granular_time_testcase_dict[current_test]: + self.granular_time_testcase_dict[current_test][event] = dict() + if method_name not in self.granular_time_testcase_dict[current_test][event]: + self.granular_time_testcase_dict[current_test][event][method_name] = [] + self.granular_time_testcase_dict[current_test][event][method_name].append(float(elapsed_time)) + + def measure_time_for_set_or_get_methods(self, method, cls_name): """ Measure the time taken by set methods. This method wraps set methods to measure their execution time. @@ -36,19 +52,10 @@ def wrapper(*args, **kwargs): self.granular_time_testcase_dict[current_test] = dict() method_name = method.__name__ if method_name.startswith('set'): - if 'set_command' not in self.granular_time_testcase_dict[current_test]: - self.granular_time_testcase_dict[current_test]['set_command'] = dict() - if method_name not in self.granular_time_testcase_dict[current_test]['set_command']: - self.granular_time_testcase_dict[current_test]['set_command'][method_name] = [] - self.granular_time_testcase_dict[current_test]['set_command'][method_name].append(float(elapsed_time)) + self.update_granular_time_testcase_dict(current_test,'set_command', ".".join([cls_name, method.__name__]), elapsed_time) elif method_name.startswith('get'): - if 'get_command' not in self.granular_time_testcase_dict[current_test]: - self.granular_time_testcase_dict[current_test]['get_command'] = dict() - if method_name not in self.granular_time_testcase_dict[current_test]['get_command']: - self.granular_time_testcase_dict[current_test]['get_command'][method_name] = [] - self.granular_time_testcase_dict[current_test]['get_command'][method_name].append(float(elapsed_time)) + self.update_granular_time_testcase_dict(current_test, 'get_command', ".".join([cls_name, method.__name__]), elapsed_time) return result - return wrapper def measure_sleep_time(self, duration): @@ -57,11 +64,17 @@ def measure_sleep_time(self, duration): param duration: duration or sleep time declared in TC fucntion's return : Update the graunular time at test case level ''' + caller_frame = inspect.currentframe().f_back + caller_method_name = caller_frame.f_code.co_name + caller_class = caller_frame.f_locals.get('self').__class__ + # Get the method of the caller's class + caller_method = getattr(caller_class, caller_method_name) + current_test = self.test_case_name start_time = time.perf_counter() self.original_sleep(duration) end_time = time.perf_counter() elapsed_time = '%.2f' % (end_time - start_time) - self.update_granular_time("sleep_time", elapsed_time) + self.update_granular_time_testcase_dict(current_test, "sleep_time", ".".join([caller_class.__name__, caller_method.__name__,"time.sleep"]), elapsed_time) def patch_set_or_get_methods_for_test_instance(self, item): """ @@ -80,7 +93,7 @@ def patch_set_or_get_methods_for_test_instance(self, item): # Check if the attribute is callable and its name starts with 'set' if callable(method) and method_name.startswith('set') or method_name.startswith('get') : original_method = getattr(class_obj, method_name) - setattr(class_obj, method_name, self.measure_time_for_set_or_get_methods(original_method)) + setattr(class_obj, method_name, self.measure_time_for_set_or_get_methods(original_method,class_name)) @pytest.fixture(autouse=True) def setup_and_teardown(self, request): @@ -88,7 +101,6 @@ def setup_and_teardown(self, request): Method setup_and_teardown : it will Monkey patch the sleep time, set level command and get level command etc param request: take request ''' - self.start_time = time.perf_counter() # Monkey patch time.sleep time.sleep = self.measure_sleep_time # Monkey patch 'set' methods for all classes in the module @@ -116,39 +128,6 @@ def pytest_runtest_protocol(self, item, nextitem): self.test_case_name = f"{item.name}" return None - def update_granular_time(self, category, elapsed_time, command = None): - ''' - Method update_granular_time : it will update the time at test case level - param category : category like bash time, sleep time etc. - param elapsed_time : time spend during event like sleep, bash etc - return : Update the graunular time at test case level - ''' - current_test = self.test_case_name - if current_test not in self.granular_time_testcase_dict: - self.granular_time_testcase_dict[current_test] = dict() - - if category not in self.granular_time_testcase_dict[current_test]: - self.granular_time_testcase_dict[current_test][category] = dict() - if category == "sleep_time": - self.granular_time_testcase_dict[current_test][category]["sleep_time"] = list() - self.granular_time_testcase_dict[current_test][category]["sleep_time"].append(float(elapsed_time)) - elif category == "set_command" and command: - self.granular_time_testcase_dict[current_test][category] = dict() - self.granular_time_testcase_dict[current_test][category][str(command)] = float(elapsed_time) - elif category == "get_command" and command: - self.granular_time_testcase_dict[current_test][category] = dict() - self.granular_time_testcase_dict[current_test][category][str(command)] = float(elapsed_time) - - else: - if category == "sleep_time": - self.granular_time_testcase_dict[current_test][category]["sleep_time"].append(float(elapsed_time)) - elif category == "set_command" and command: - if str(command) not in self.granular_time_testcase_dict[current_test][category]: - self.granular_time_testcase_dict[current_test][category][str(command)] = float(elapsed_time) - elif category == "get_command" and command: - if str(command) not in self.granular_time_testcase_dict[current_test][category]: - self.granular_time_testcase_dict[current_test][category][str(command)] = float(elapsed_time) - def update_CafyLog_gta_dict(self, current_test): """ Update the CafyLog Granular Time Accounting (GTA) dictionary with timing information for the current test @@ -175,51 +154,76 @@ def pytest_runtest_teardown(self, item, nextitem): :param nextitem: The next test item in the test suite :return: None """ - end_time = time.perf_counter() - self.total_execution_time = '%.2f' % (end_time - self.start_time) current_test = self.test_case_name - self.granular_time_testcase_dict[current_test]["total_execution_time"] = self.total_execution_time - self.total_execution_time = None self.update_CafyLog_gta_dict(current_test) CafyLog.gta_dict = {} + def get_time_data(self,data, event): + ''' + get time data + this method will take the timings list for each sleep_time , set_command or get_command + and it will club into as sum and occurence + param data : timings data + ''' + tmp_dict = {} + for command, timings_list in data.items(): + if isinstance(timings_list, list): + total_sum = sum(timings_list) + length = len(timings_list) + if event == 'sleep_time': + self.total_sleep_time = self.total_sleep_time + total_sum + elif event == 'set_command': + self.total_set_command_time = self.total_set_command_time + total_sum + elif event == 'get_command': + self.total_get_command_time = self.total_get_command_time + total_sum + tmp_dict[command] = ["{:.2f}".format(total_sum), length] + else: + tmp_dict[command] = timings_list + return tmp_dict + def collect_granular_time_accouting_report(self): ''' Method collect_granular_time_accouting_report : it will create report and save in cafy work dir return : create report for time accounting in cafy work dir as granular_time_report.json ''' time_report = dict() - for test_case, times in self.granular_time_testcase_dict.items(): + for test_case, events in self.granular_time_testcase_dict.items(): time_report[test_case] = dict() - if 'sleep_time' in times: - time_report[test_case]['sleep_time'] = times["sleep_time"] + if 'sleep_time' in events: + time_report[test_case]['sleep_time'] = self.get_time_data(events["sleep_time"],'sleep_time') else: - time_report[test_case]['sleep_time'] = [] - if 'set_command' in times: - time_report[test_case]['set_command'] = times['set_command'] + time_report[test_case]['sleep_time'] = {} + if 'set_command' in events: + time_report[test_case]['set_command'] = self.get_time_data(events["set_command"],'set_command') else: time_report[test_case]['set_command'] = {} - if 'get_command' in times: - time_report[test_case]['get_command'] = times['get_command'] + if 'get_command' in events: + time_report[test_case]['get_command'] = self.get_time_data(events["get_command"],'get_command') else: time_report[test_case]['get_command'] = {} - time_report[test_case]['total_execution_time'] = times["total_execution_time"] - self.granular_time_testcase_dict = time_report + time_report[test_case]['total_sleep_time'] = "{:.2f}".format(self.total_sleep_time) + time_report[test_case]['total_set_command_time'] = "{:.2f}".format(self.total_set_command_time) + time_report[test_case]['total_get_command_time'] = "{:.2f}".format(self.total_get_command_time) + time_report[test_case]['total_time'] = "{:.2f}".format(self.total_sleep_time+self.total_set_command_time+self.total_get_command_time) + self.total_sleep_time = 0 + self.total_set_command_time = 0 + self.total_get_command_time = 0 + return time_report def pytest_terminal_summary(self, terminalreporter): ''' Method pytest_terminal_summary : terminal reporting return : None ''' - self.collect_granular_time_accouting_report() + time_report = self.collect_granular_time_accouting_report() # Create a Jinja2 environment and load the HTML template CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) template_file = os.path.join(CURRENT_DIR,"resources/gta_template.html") with open(template_file) as html_src: html_template = html_src.read() template = Template(html_template) - html_content = template.render(dictionary_data=self.granular_time_testcase_dict) + html_content = template.render(dictionary_data=time_report) # Define the path to the output HTML file path=CafyLog.work_dir html_file_path = os.path.join(path, 'granular_time_report.html') diff --git a/cafy_pytest/resources/gta_template.html b/cafy_pytest/resources/gta_template.html index f4d0302..5f4e03d 100644 --- a/cafy_pytest/resources/gta_template.html +++ b/cafy_pytest/resources/gta_template.html @@ -31,7 +31,8 @@

Granular Time Accounting Report

Sleep Time Set Level Command Time Get Level Command Time - Total Time + Occurrence + Total Execution Time {% for key, values in dictionary_data.items() %} @@ -41,61 +42,44 @@

Granular Time Accounting Report

- + {% for step_key, step_values in values.items() %} - {% if 'sleep_time' in step_key %} - {% if step_values is mapping %} + {% if step_values is mapping %} + {% if 'sleep_time' in step_key %} {% for time_key, time_values in step_values.items() %} {{ time_key }} - {{ time_values[0] if time_values else 'NA' }} + {{ time_values[0] if time_values else '-' }} - - + {{ time_values[1] if time_values else '-' }} - - {% if time_values is iterable and time_values|length > 1 %} - {% for extra_time in time_values[1:] %} - - - {{ time_key }} - {{ extra_time }} - - - - - - - - {% endfor %} - {% endif %} {% endfor %} - {% endif %} - {% elif 'set_command' in step_key %} - {% if step_values is mapping %} + {% elif 'set_command' in step_key %} {% for set_key, set_value in step_values.items() %} - {% for value in set_value %} - - - {{ set_key }} - - - {{ value }} - - - - - - {% endfor %} + + + {{ set_key }} + - + {{ set_value[0] if set_value else '-' }} + - + {{ set_value[1] if set_value else '-' }} + - + {% endfor %} - {% endif %} - {% elif 'get_command' in step_key %} - {% if step_values is mapping %} + {% elif 'get_command' in step_key %} {% for get_key, get_value in step_values.items() %} - {% for value in get_value %} - - - {{ get_key }} - - - - - {{ value }} - - - - {% endfor %} + + + {{ get_key }} + - + - + {{ get_value[0] if get_value else '-' }} + {{ get_value[1] if get_value else '-' }} + - + {% endfor %} {% endif %} {% endif %} @@ -103,10 +87,11 @@

Granular Time Accounting Report

Total + {{ values["total_sleep_time"] }} + {{ values["total_set_command_time"] }} + {{ values["total_get_command_time"] }} - - - - - - {{values["total_execution_time"]}} + {{ values["total_time"] }} {% endfor %} From b8ceffff74194560f570b5f4836f52a37de4bba9 Mon Sep 17 00:00:00 2001 From: pasverma Date: Tue, 27 Feb 2024 19:52:08 +0530 Subject: [PATCH 07/12] fix --- cafy_pytest/cafy_gta.py | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/cafy_pytest/cafy_gta.py b/cafy_pytest/cafy_gta.py index 45c4bb6..2c9787d 100644 --- a/cafy_pytest/cafy_gta.py +++ b/cafy_pytest/cafy_gta.py @@ -95,19 +95,6 @@ def patch_set_or_get_methods_for_test_instance(self, item): original_method = getattr(class_obj, method_name) setattr(class_obj, method_name, self.measure_time_for_set_or_get_methods(original_method,class_name)) - @pytest.fixture(autouse=True) - def setup_and_teardown(self, request): - ''' - Method setup_and_teardown : it will Monkey patch the sleep time, set level command and get level command etc - param request: take request - ''' - # Monkey patch time.sleep - time.sleep = self.measure_sleep_time - # Monkey patch 'set' methods for all classes in the module - self.patch_set_or_get_methods_for_test_instance(request.node) - yield # This is where the test case runs - self.pytest_runtest_teardown(request.node, None) - def pytest_runtest_protocol(self, item, nextitem): ''' Method pytest_runtest_protocol : it will Monkey patch sleep , subprocess run etc @@ -116,6 +103,10 @@ def pytest_runtest_protocol(self, item, nextitem): param nextitem : test case nextitem return : None ''' + # Monkey patch time.sleep + time.sleep = self.measure_sleep_time + # Monkey patch 'set' methods for all classes in the module + self.patch_set_or_get_methods_for_test_instance(item) # get class name of the test case method base_class_name = "" if item.cls: @@ -138,12 +129,12 @@ def update_CafyLog_gta_dict(self, current_test): self.granular_time_testcase_dict[current_test] = dict() if 'set_command' not in self.granular_time_testcase_dict[current_test]: self.granular_time_testcase_dict[current_test]['set_command'] = dict() - if 'set_command' in CafyLog.gta_dict: + if hasattr(CafyLog,"gta_dict") and 'set_command' in CafyLog.gta_dict: for key, value in CafyLog.gta_dict['set_command'].items(): self.granular_time_testcase_dict[current_test]['set_command'][key] = value if 'get_command' not in self.granular_time_testcase_dict[current_test]: self.granular_time_testcase_dict[current_test]['get_command'] = dict() - if 'get_command' in CafyLog.gta_dict: + if hasattr(CafyLog,"gta_dict") and 'get_command' in CafyLog.gta_dict: for key, value in CafyLog.gta_dict['get_command'].items(): self.granular_time_testcase_dict[current_test]['get_command'][key] = value From 5d821ab6a93f1254d4f001ed2a60c8a46f41f6dd Mon Sep 17 00:00:00 2001 From: pasverma Date: Tue, 27 Feb 2024 20:36:12 +0530 Subject: [PATCH 08/12] . --- cafy_pytest/cafy_gta.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cafy_pytest/cafy_gta.py b/cafy_pytest/cafy_gta.py index 2c9787d..ecdbd4a 100644 --- a/cafy_pytest/cafy_gta.py +++ b/cafy_pytest/cafy_gta.py @@ -83,6 +83,9 @@ def patch_set_or_get_methods_for_test_instance(self, item): param request: The test request. Yields: None """ + # To avoid maximum recursion depth error + if getattr(item.cls, '_decorated', None): + return test_case_class = item.cls if test_case_class: # Get the module of the test case class @@ -94,6 +97,7 @@ def patch_set_or_get_methods_for_test_instance(self, item): if callable(method) and method_name.startswith('set') or method_name.startswith('get') : original_method = getattr(class_obj, method_name) setattr(class_obj, method_name, self.measure_time_for_set_or_get_methods(original_method,class_name)) + setattr(item.cls, '_decorated', True) def pytest_runtest_protocol(self, item, nextitem): ''' From 172041384f19f7806d2c7707b3cb149d074e1e6e Mon Sep 17 00:00:00 2001 From: pasverma Date: Mon, 4 Mar 2024 20:33:21 +0530 Subject: [PATCH 09/12] pytest_ fix --- cafy_pytest/cafy_gta.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cafy_pytest/cafy_gta.py b/cafy_pytest/cafy_gta.py index ecdbd4a..2f71a06 100644 --- a/cafy_pytest/cafy_gta.py +++ b/cafy_pytest/cafy_gta.py @@ -95,8 +95,12 @@ def patch_set_or_get_methods_for_test_instance(self, item): for method_name, method in inspect.getmembers(class_obj, inspect.isfunction): # Check if the attribute is callable and its name starts with 'set' if callable(method) and method_name.startswith('set') or method_name.startswith('get') : - original_method = getattr(class_obj, method_name) - setattr(class_obj, method_name, self.measure_time_for_set_or_get_methods(original_method,class_name)) + #skipping appyling timer on setup method of testclass + if method_name == 'setup_method': + continue + else: + original_method = getattr(class_obj, method_name) + setattr(class_obj, method_name, self.measure_time_for_set_or_get_methods(original_method,class_name)) setattr(item.cls, '_decorated', True) def pytest_runtest_protocol(self, item, nextitem): From bb35982d4328d5af8c8b28b5c6c1ff15826f4659 Mon Sep 17 00:00:00 2001 From: pasverma Date: Wed, 6 Mar 2024 15:30:45 +0530 Subject: [PATCH 10/12] fix --- cafy_pytest/cafy_gta.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/cafy_pytest/cafy_gta.py b/cafy_pytest/cafy_gta.py index 2f71a06..28f9123 100644 --- a/cafy_pytest/cafy_gta.py +++ b/cafy_pytest/cafy_gta.py @@ -68,13 +68,15 @@ def measure_sleep_time(self, duration): caller_method_name = caller_frame.f_code.co_name caller_class = caller_frame.f_locals.get('self').__class__ # Get the method of the caller's class - caller_method = getattr(caller_class, caller_method_name) - current_test = self.test_case_name - start_time = time.perf_counter() - self.original_sleep(duration) - end_time = time.perf_counter() - elapsed_time = '%.2f' % (end_time - start_time) - self.update_granular_time_testcase_dict(current_test, "sleep_time", ".".join([caller_class.__name__, caller_method.__name__,"time.sleep"]), elapsed_time) + if caller_class is not None: + caller_method = getattr(caller_class, caller_method_name, None) + if caller_method is not None: + current_test = self.test_case_name + start_time = time.perf_counter() + self.original_sleep(duration) + end_time = time.perf_counter() + elapsed_time = '%.2f' % (end_time - start_time) + self.update_granular_time_testcase_dict(current_test, "sleep_time", ".".join([caller_class.__name__, caller_method.__name__,"time.sleep"]), elapsed_time) def patch_set_or_get_methods_for_test_instance(self, item): """ From 2cb97cb62606d7082af3f7707ea36164f9ca4e12 Mon Sep 17 00:00:00 2001 From: pasverma Date: Tue, 12 Mar 2024 19:43:36 +0530 Subject: [PATCH 11/12] hot fix --- cafy_pytest/cafy_gta.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cafy_pytest/cafy_gta.py b/cafy_pytest/cafy_gta.py index 28f9123..78c79b2 100644 --- a/cafy_pytest/cafy_gta.py +++ b/cafy_pytest/cafy_gta.py @@ -103,7 +103,8 @@ def patch_set_or_get_methods_for_test_instance(self, item): else: original_method = getattr(class_obj, method_name) setattr(class_obj, method_name, self.measure_time_for_set_or_get_methods(original_method,class_name)) - setattr(item.cls, '_decorated', True) + if item.cls is not None: + setattr(item.cls, '_decorated', True) def pytest_runtest_protocol(self, item, nextitem): ''' From ca340f9eb91646b85ad9006d47972c40cbe9336c Mon Sep 17 00:00:00 2001 From: pasverma Date: Sun, 17 Mar 2024 10:12:20 +0530 Subject: [PATCH 12/12] removing gta code --- cafy_pytest/cafy_gta.py | 234 ------------------------ cafy_pytest/plugin.py | 4 - cafy_pytest/resources/gta_template.html | 99 ---------- 3 files changed, 337 deletions(-) delete mode 100644 cafy_pytest/cafy_gta.py delete mode 100644 cafy_pytest/resources/gta_template.html diff --git a/cafy_pytest/cafy_gta.py b/cafy_pytest/cafy_gta.py deleted file mode 100644 index 78c79b2..0000000 --- a/cafy_pytest/cafy_gta.py +++ /dev/null @@ -1,234 +0,0 @@ -import time -import pytest -from logger.cafylog import CafyLog -import os -import inspect -from jinja2 import Template -import functools -import sys -import inspect - -class TimeCollectorPlugin: - def __init__(self): - self.original_sleep = time.sleep - self.granular_time_testcase_dict = dict() - self.test_case_name = None - self.total_sleep_time = 0 - self.total_set_command_time = 0 - self.total_get_command_time = 0 - - def update_granular_time_testcase_dict(self, current_test, event, method_name, elapsed_time ): - """ - granular_time_testcase_dict - param current_test: current_test - param event: command like set , get or time.sleep - param method_name: method name - param elapsed_time: total time for each command ie, set or get - """ - if current_test not in self.granular_time_testcase_dict: - self.granular_time_testcase_dict[current_test] = dict() - if event not in self.granular_time_testcase_dict[current_test]: - self.granular_time_testcase_dict[current_test][event] = dict() - if method_name not in self.granular_time_testcase_dict[current_test][event]: - self.granular_time_testcase_dict[current_test][event][method_name] = [] - self.granular_time_testcase_dict[current_test][event][method_name].append(float(elapsed_time)) - - def measure_time_for_set_or_get_methods(self, method, cls_name): - """ - Measure the time taken by set methods. - This method wraps set methods to measure their execution time. - param method (function): The set method to be measured. - Returns: function: The wrapped method. - """ - @functools.wraps(method) - def wrapper(*args, **kwargs): - start_time = time.perf_counter() - result = method(*args, **kwargs) - end_time = time.perf_counter() - elapsed_time = '%.2f' % (end_time - start_time) - # Update granular time at the test case level - current_test = self.test_case_name - if current_test not in self.granular_time_testcase_dict: - self.granular_time_testcase_dict[current_test] = dict() - method_name = method.__name__ - if method_name.startswith('set'): - self.update_granular_time_testcase_dict(current_test,'set_command', ".".join([cls_name, method.__name__]), elapsed_time) - elif method_name.startswith('get'): - self.update_granular_time_testcase_dict(current_test, 'get_command', ".".join([cls_name, method.__name__]), elapsed_time) - return result - return wrapper - - def measure_sleep_time(self, duration): - ''' - Method measure_sleep_time : it will measure the actual time taken by testcase method during sleep - param duration: duration or sleep time declared in TC fucntion's - return : Update the graunular time at test case level - ''' - caller_frame = inspect.currentframe().f_back - caller_method_name = caller_frame.f_code.co_name - caller_class = caller_frame.f_locals.get('self').__class__ - # Get the method of the caller's class - if caller_class is not None: - caller_method = getattr(caller_class, caller_method_name, None) - if caller_method is not None: - current_test = self.test_case_name - start_time = time.perf_counter() - self.original_sleep(duration) - end_time = time.perf_counter() - elapsed_time = '%.2f' % (end_time - start_time) - self.update_granular_time_testcase_dict(current_test, "sleep_time", ".".join([caller_class.__name__, caller_method.__name__,"time.sleep"]), elapsed_time) - - def patch_set_or_get_methods_for_test_instance(self, item): - """ - Perform setup and teardown actions for test cases. - This method performs setup and teardown actions for test cases,including monkey patching sleep and set methods. - param request: The test request. - Yields: None - """ - # To avoid maximum recursion depth error - if getattr(item.cls, '_decorated', None): - return - test_case_class = item.cls - if test_case_class: - # Get the module of the test case class - module = inspect.getmodule(test_case_class) - for class_name, class_obj in inspect.getmembers(module, inspect.isclass): - # Iterate over the attributes of the class - for method_name, method in inspect.getmembers(class_obj, inspect.isfunction): - # Check if the attribute is callable and its name starts with 'set' - if callable(method) and method_name.startswith('set') or method_name.startswith('get') : - #skipping appyling timer on setup method of testclass - if method_name == 'setup_method': - continue - else: - original_method = getattr(class_obj, method_name) - setattr(class_obj, method_name, self.measure_time_for_set_or_get_methods(original_method,class_name)) - if item.cls is not None: - setattr(item.cls, '_decorated', True) - - def pytest_runtest_protocol(self, item, nextitem): - ''' - Method pytest_runtest_protocol : it will Monkey patch sleep , subprocess run etc - Monkey patching used for modifying the behavior of built-in classes or functions, or adding instrumentation or logging to existing code. - param item : test case item - param nextitem : test case nextitem - return : None - ''' - # Monkey patch time.sleep - time.sleep = self.measure_sleep_time - # Monkey patch 'set' methods for all classes in the module - self.patch_set_or_get_methods_for_test_instance(item) - # get class name of the test case method - base_class_name = "" - if item.cls: - class_name = item.cls - base_classes = inspect.getmro(class_name) - base_class_name = base_classes[0].__name__ - if base_class_name: - self.test_case_name = f"{base_class_name}.{item.name}" - else: - self.test_case_name = f"{item.name}" - return None - - def update_CafyLog_gta_dict(self, current_test): - """ - Update the CafyLog Granular Time Accounting (GTA) dictionary with timing information for the current test - :param current_test:The name of the current test being executed - :return : none - """ - if current_test not in self.granular_time_testcase_dict: - self.granular_time_testcase_dict[current_test] = dict() - if 'set_command' not in self.granular_time_testcase_dict[current_test]: - self.granular_time_testcase_dict[current_test]['set_command'] = dict() - if hasattr(CafyLog,"gta_dict") and 'set_command' in CafyLog.gta_dict: - for key, value in CafyLog.gta_dict['set_command'].items(): - self.granular_time_testcase_dict[current_test]['set_command'][key] = value - if 'get_command' not in self.granular_time_testcase_dict[current_test]: - self.granular_time_testcase_dict[current_test]['get_command'] = dict() - if hasattr(CafyLog,"gta_dict") and 'get_command' in CafyLog.gta_dict: - for key, value in CafyLog.gta_dict['get_command'].items(): - self.granular_time_testcase_dict[current_test]['get_command'][key] = value - - def pytest_runtest_teardown(self, item, nextitem): - """ - Execute teardown actions after a test has been executed - :param item: The test item that was executed - :param nextitem: The next test item in the test suite - :return: None - """ - current_test = self.test_case_name - self.update_CafyLog_gta_dict(current_test) - CafyLog.gta_dict = {} - - def get_time_data(self,data, event): - ''' - get time data - this method will take the timings list for each sleep_time , set_command or get_command - and it will club into as sum and occurence - param data : timings data - ''' - tmp_dict = {} - for command, timings_list in data.items(): - if isinstance(timings_list, list): - total_sum = sum(timings_list) - length = len(timings_list) - if event == 'sleep_time': - self.total_sleep_time = self.total_sleep_time + total_sum - elif event == 'set_command': - self.total_set_command_time = self.total_set_command_time + total_sum - elif event == 'get_command': - self.total_get_command_time = self.total_get_command_time + total_sum - tmp_dict[command] = ["{:.2f}".format(total_sum), length] - else: - tmp_dict[command] = timings_list - return tmp_dict - - def collect_granular_time_accouting_report(self): - ''' - Method collect_granular_time_accouting_report : it will create report and save in cafy work dir - return : create report for time accounting in cafy work dir as granular_time_report.json - ''' - time_report = dict() - for test_case, events in self.granular_time_testcase_dict.items(): - time_report[test_case] = dict() - if 'sleep_time' in events: - time_report[test_case]['sleep_time'] = self.get_time_data(events["sleep_time"],'sleep_time') - else: - time_report[test_case]['sleep_time'] = {} - if 'set_command' in events: - time_report[test_case]['set_command'] = self.get_time_data(events["set_command"],'set_command') - else: - time_report[test_case]['set_command'] = {} - if 'get_command' in events: - time_report[test_case]['get_command'] = self.get_time_data(events["get_command"],'get_command') - else: - time_report[test_case]['get_command'] = {} - - time_report[test_case]['total_sleep_time'] = "{:.2f}".format(self.total_sleep_time) - time_report[test_case]['total_set_command_time'] = "{:.2f}".format(self.total_set_command_time) - time_report[test_case]['total_get_command_time'] = "{:.2f}".format(self.total_get_command_time) - time_report[test_case]['total_time'] = "{:.2f}".format(self.total_sleep_time+self.total_set_command_time+self.total_get_command_time) - self.total_sleep_time = 0 - self.total_set_command_time = 0 - self.total_get_command_time = 0 - return time_report - - def pytest_terminal_summary(self, terminalreporter): - ''' - Method pytest_terminal_summary : terminal reporting - return : None - ''' - time_report = self.collect_granular_time_accouting_report() - # Create a Jinja2 environment and load the HTML template - CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) - template_file = os.path.join(CURRENT_DIR,"resources/gta_template.html") - with open(template_file) as html_src: - html_template = html_src.read() - template = Template(html_template) - html_content = template.render(dictionary_data=time_report) - # Define the path to the output HTML file - path=CafyLog.work_dir - html_file_path = os.path.join(path, 'granular_time_report.html') - # Write the HTML content to the output file - with open(html_file_path, 'w') as html_file: - html_file.write(html_content) diff --git a/cafy_pytest/plugin.py b/cafy_pytest/plugin.py index 31b7799..ab442c8 100644 --- a/cafy_pytest/plugin.py +++ b/cafy_pytest/plugin.py @@ -48,7 +48,6 @@ from utils.collectors.confest import Config from .cafy import Cafy -from .cafy_gta import TimeCollectorPlugin from .cafy_pdb import CafyPdb from .cafypdb_config import CafyPdb_Configs @@ -559,7 +558,6 @@ def pytest_configure(config): collection_list, cafypdb) config.pluginmanager.register(config._email) - config.pluginmanager.register(TimeCollectorPlugin()) #Write all.log path to terminal reporter = TerminalReporter(config, sys.stdout) @@ -587,8 +585,6 @@ def pytest_unconfigure(config): tmp_str_text = str(tmp_text) with open(os.path.join(CafyLog.work_dir, "env.txt"), "w") as f: f.write(tmp_str_text) - time_collector_plugin = config.pluginmanager.get_plugin(TimeCollectorPlugin) - config.pluginmanager.unregister(time_collector_plugin) except: pass diff --git a/cafy_pytest/resources/gta_template.html b/cafy_pytest/resources/gta_template.html deleted file mode 100644 index 5f4e03d..0000000 --- a/cafy_pytest/resources/gta_template.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - -

Granular Time Accounting Report

- - - - - - - - - - - {% for key, values in dictionary_data.items() %} - - - - - - - - - {% for step_key, step_values in values.items() %} - {% if step_values is mapping %} - {% if 'sleep_time' in step_key %} - {% for time_key, time_values in step_values.items() %} - - - - - - - - - - {% endfor %} - {% elif 'set_command' in step_key %} - {% for set_key, set_value in step_values.items() %} - - - - - - - - - - {% endfor %} - {% elif 'get_command' in step_key %} - {% for get_key, get_value in step_values.items() %} - - - - - - - - - - {% endfor %} - {% endif %} - {% endif %} - {% endfor %} - - - - - - - - - - {% endfor %} -
TestcaseStepSleep TimeSet Level Command TimeGet Level Command TimeOccurrenceTotal Execution Time
{{key}}
{{ time_key }}{{ time_values[0] if time_values else '-' }}--{{ time_values[1] if time_values else '-' }}-
{{ set_key }}-{{ set_value[0] if set_value else '-' }}-{{ set_value[1] if set_value else '-' }}-
{{ get_key }}--{{ get_value[0] if get_value else '-' }}{{ get_value[1] if get_value else '-' }}-
Total{{ values["total_sleep_time"] }}{{ values["total_set_command_time"] }}{{ values["total_get_command_time"] }}-{{ values["total_time"] }}
- -