1616
1717from robocop import __version__ , config
1818from robocop .formatter .formatters import FORMATTERS
19+ from robocop .linter .utils .version_matching import Version
1920from robocop .run import check_files , format_files
2021from tests import working_directory
2122
2223LINTER_TESTS_DIR = Path (__file__ ).parent .parent / "linter"
2324TEST_DATA = Path (__file__ ).parent / "test_data"
25+ FEATURES_MIN_VER = {
26+ "cache" : Version ("7.1.0" ),
27+ }
2428REPORTS = {}
2529
2630
27- def performance_report (runs : int = 100 ):
28- """Use as decorator to measure performance of a function and store results."""
31+ def is_feature_enabled (feature : str ) -> bool :
32+ """
33+ Check if the feature is enabled based on a Robocop version.
34+
35+ It is used to determine which tests should be run based on a Robocop version.
36+ """
37+ if feature not in FEATURES_MIN_VER :
38+ return True
39+ robocop_version = Version (__version__ )
40+ return FEATURES_MIN_VER [feature ] <= robocop_version
41+
42+
43+ def performance_report (runs : int = 100 , cut_off : int = 0 ):
44+ """
45+ Use as decorator to measure performance of a function and store results.
46+
47+ Args:
48+ runs: Number of runs to take into account when calculating the average.
49+ cut_off: Number of slowest and fastest runs to exclude from the average.
50+
51+ """
2952
3053 def decorator (func ):
3154 @wraps (func )
@@ -38,18 +61,13 @@ def wrapper(*args, **kwargs):
3861 print (f"Run { run + 1 } / { runs } of { func .__name__ } " )
3962 start = time .perf_counter ()
4063 counter = func (* args , ** kwargs )
41- end = time .perf_counter ()
42- time_taken = end - start
64+ time_taken = time .perf_counter () - start
4365 run_times .append (time_taken )
4466 print (f" Execution time: { time_taken :.6f} seconds" )
4567 run_times .sort ()
46- cut_off = int (runs * 0.1 )
47- if cut_off + 2 > runs :
48- cut_off = 0
49- if len (run_times ) > 2 :
50- avg_time = sum (run_times [cut_off :- cut_off ]) / (len (run_times ) - 2 * cut_off )
51- else :
52- avg_time = sum (run_times ) / len (run_times )
68+ if cut_off :
69+ run_times = run_times [cut_off :- cut_off ]
70+ avg_time = sum (run_times ) / len (run_times )
5371 print (f"Mean average execution time over { runs } runs: { avg_time :.6f} seconds" )
5472 if report_name :
5573 if func .__name__ not in REPORTS :
@@ -63,7 +81,7 @@ def wrapper(*args, **kwargs):
6381 return decorator
6482
6583
66- @performance_report (runs = 50 )
84+ @performance_report (runs = 10 , cut_off = 2 )
6785def project_traversing_report () -> int :
6886 """
6987 Measure how long it takes to traverse Robocop repository files.
@@ -90,17 +108,17 @@ def project_traversing_report() -> int:
90108 return files_count
91109
92110
93- @performance_report (runs = 50 )
94- def formatter_report (formatter : str , report_name : str , cache : bool = True ) -> int : # noqa: ARG001
111+ @performance_report (runs = 10 , cut_off = 2 )
112+ def formatter_report (formatter : str , report_name : str , ** kwargs ) -> int : # noqa: ARG001
95113 main_dir = Path (__file__ ).parent .parent .parent
96114 formatter_dir = main_dir / "tests" / "formatter" / "formatters" / formatter
97115 with working_directory (formatter_dir ):
98- format_files (["source" ], select = [formatter ], overwrite = False , return_result = True , silent = True , cache = cache )
116+ format_files (["source" ], select = [formatter ], overwrite = False , return_result = True , silent = True , ** kwargs )
99117 source_dir = formatter_dir / "source"
100118 return len (list (source_dir .iterdir ()))
101119
102120
103- @performance_report (runs = 10 )
121+ @performance_report (runs = 5 )
104122def linter_report (report_name : str , ** kwargs ) -> int : # noqa: ARG001
105123 main_dir = Path (__file__ ).parent .parent .parent
106124 linter_dir = main_dir / "tests" / "linter"
@@ -109,10 +127,13 @@ def linter_report(report_name: str, **kwargs) -> int: # noqa: ARG001
109127 return len (list (linter_dir .glob ("**/*.robot" )))
110128
111129
112- @performance_report (runs = 2 )
130+ @performance_report (runs = 1 )
113131def lint_large_file (report_name : str , lint_dir : Path , ** kwargs ) -> int : # noqa: ARG001
114132 with working_directory (lint_dir ):
115- check_files (return_result = True , select = ["ALL" ], cache = False , ** kwargs )
133+ if is_feature_enabled ("cache" ):
134+ check_files (return_result = True , select = ["ALL" ], cache = False , ** kwargs )
135+ else :
136+ check_files (return_result = True , select = ["ALL" ], ** kwargs )
116137 return 1
117138
118139
@@ -135,30 +156,34 @@ def generate_large_file(template_path: Path, output_dir: Path) -> None:
135156 f .write (rendered_content )
136157
137158
138- if __name__ == "__main__" :
139- # TODO: prepare i.e. nox script to install external robocops and run this script
140- # So we can generate reports for multiple past versions. It is important since the actual seconds change depending
141- # on where we run the script from, but the % change between version should be comparable. Also we can use new tests
142- # on old versions
143- linter_report (report_name = "with_print_cache" , cache = True )
144- linter_report (report_name = "with_print_no_cache" , cache = False )
145- linter_report (report_name = "without_print_cache" , silent = True , cache = True )
146- linter_report (report_name = "without_print_no_cache" , silent = True , cache = False )
147- for formatter in FORMATTERS :
148- formatter_report (formatter = formatter , report_name = formatter )
149- formatter_report (formatter = formatter , report_name = f"{ formatter } _no_cache" , cache = False )
159+ def generate_reports () -> None :
160+ if is_feature_enabled ("cache" ):
161+ linter_report (report_name = "with_print_cache" , cache = True )
162+ linter_report (report_name = "with_print_no_cache" , cache = False )
163+ linter_report (report_name = "without_print_cache" , silent = True , cache = True )
164+ linter_report (report_name = "without_print_no_cache" , silent = True , cache = False )
165+ for formatter in FORMATTERS :
166+ formatter_report (formatter = formatter , report_name = formatter , cache = True )
167+ formatter_report (formatter = formatter , report_name = f"{ formatter } _no_cache" , cache = False )
168+ else :
169+ linter_report (report_name = "with_print_no_cache" )
170+ linter_report (report_name = "without_print_no_cache" , silent = True )
171+ for formatter in FORMATTERS :
172+ formatter_report (formatter = formatter , report_name = f"{ formatter } _no_cache" )
150173 project_traversing_report ()
151174 with tempfile .TemporaryDirectory () as temp_dir :
152175 temp_dir = Path (temp_dir )
153176 generate_large_file (TEST_DATA / "large_file.robot" , temp_dir )
154177 lint_large_file (report_name = "large_file_with_print" , lint_dir = temp_dir )
155178 lint_large_file (report_name = "large_file_without_print" , lint_dir = temp_dir , silent = True )
156179
157- report_path = Path (__file__ ).parent / "reports" / f"robocop_{ __version__ .replace ('.' , '_' )} .json"
158- if report_path .exists ():
159- with open (report_path ) as fp :
160- prev_report = json .load (fp )
161- REPORTS = merge_dictionaries (prev_report , REPORTS )
162180
163- with open (report_path , "w" ) as fp :
164- json .dump (REPORTS , fp , indent = 4 )
181+ if __name__ == "__main__" :
182+ whole_run_start = time .perf_counter ()
183+ report_path = Path (__file__ ).parent / "reports" / f"robocop_{ __version__ .replace ('.' , '_' )} .json"
184+ if not report_path .exists ():
185+ generate_reports ()
186+ print (f"Generating report in { report_path } " )
187+ with open (report_path , "w" ) as fp :
188+ json .dump (REPORTS , fp , indent = 4 )
189+ print (f"Took { time .perf_counter () - whole_run_start :.2f} seconds to generate report." )
0 commit comments