SmokeCrossFit 1.0.0a1__py3-none-any.whl → 1.0.0b6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- crossfit/__init__.py +1 -1
- crossfit/commands/__init__.py +1 -2
- crossfit/commands/command.py +6 -6
- crossfit/commands/command_builder.py +8 -9
- crossfit/executors/executor_factory.py +0 -8
- crossfit/executors/local_executor.py +2 -9
- crossfit/models/command_models.py +1 -0
- crossfit/tools/dotnet_coverage.py +36 -48
- crossfit/tools/jacoco.py +13 -31
- crossfit/tools/tool.py +22 -39
- crossfit/tools/tool_factory.py +8 -11
- {smokecrossfit-1.0.0a1.dist-info → smokecrossfit-1.0.0b6.dist-info}/METADATA +1 -12
- smokecrossfit-1.0.0b6.dist-info/RECORD +23 -0
- smokecrossfit-1.0.0a1.dist-info/RECORD +0 -23
- {smokecrossfit-1.0.0a1.dist-info → smokecrossfit-1.0.0b6.dist-info}/WHEEL +0 -0
- {smokecrossfit-1.0.0a1.dist-info → smokecrossfit-1.0.0b6.dist-info}/top_level.txt +0 -0
crossfit/__init__.py
CHANGED
crossfit/commands/__init__.py
CHANGED
crossfit/commands/command.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
import glob
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Optional, Self
|
|
4
|
+
from typing import Optional, List, Tuple, Self
|
|
5
5
|
from typeguard import typechecked
|
|
6
6
|
|
|
7
7
|
COMMAND_DELIMITER = " "
|
|
@@ -11,9 +11,9 @@ class Command:
|
|
|
11
11
|
next_command: Optional[Self]
|
|
12
12
|
execution_call: Optional[str]
|
|
13
13
|
command_to_execute: Optional[str]
|
|
14
|
-
command_body:
|
|
15
|
-
options:
|
|
16
|
-
arguments:
|
|
14
|
+
command_body: List[str]
|
|
15
|
+
options: List[Tuple[str, Optional[str]]]
|
|
16
|
+
arguments: List[str]
|
|
17
17
|
values_delimiter: Optional[str]
|
|
18
18
|
|
|
19
19
|
@typechecked()
|
|
@@ -30,14 +30,14 @@ class Command:
|
|
|
30
30
|
self.values_delimiter = None
|
|
31
31
|
|
|
32
32
|
@property
|
|
33
|
-
def command(self) ->
|
|
33
|
+
def command(self) -> List[str]:
|
|
34
34
|
"""
|
|
35
35
|
:returns: The command itself as a list of strings
|
|
36
36
|
"""
|
|
37
37
|
return list(filter(lambda s: s is not None, [self.execution_call, self.command_to_execute, *self.command_body]))
|
|
38
38
|
|
|
39
39
|
@command.setter
|
|
40
|
-
def command(self, value:
|
|
40
|
+
def command(self, value: List[str]):
|
|
41
41
|
"""
|
|
42
42
|
Sets the command body from a list of strings
|
|
43
43
|
:param value: The command body as list of strings
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
import glob
|
|
3
3
|
import os.path
|
|
4
|
-
|
|
5
4
|
from pathlib import Path
|
|
6
|
-
from typing import Optional, Self
|
|
5
|
+
from typing import List, Optional, Self, Tuple
|
|
7
6
|
from typeguard import typechecked
|
|
8
7
|
from crossfit.commands.command import Command
|
|
9
8
|
|
|
@@ -27,7 +26,7 @@ class CommandBuilder:
|
|
|
27
26
|
self._command.next_command = command
|
|
28
27
|
|
|
29
28
|
@typechecked()
|
|
30
|
-
def with_command(self, command:
|
|
29
|
+
def with_command(self, command: List[str]) -> Self:
|
|
31
30
|
"""
|
|
32
31
|
Initializes the command from a list of strings.
|
|
33
32
|
:param command: A list containing at least two strings - execution call and command to execute
|
|
@@ -50,7 +49,7 @@ class CommandBuilder:
|
|
|
50
49
|
:param path: Optional path to prepend to the execution call
|
|
51
50
|
:returns: Self for method chaining
|
|
52
51
|
"""
|
|
53
|
-
self._command.execution_call = execution_call if path is None else
|
|
52
|
+
self._command.execution_call = execution_call if path is None else os.path.relpath(path / execution_call)
|
|
54
53
|
return self
|
|
55
54
|
|
|
56
55
|
@typechecked()
|
|
@@ -64,7 +63,7 @@ class CommandBuilder:
|
|
|
64
63
|
return self
|
|
65
64
|
|
|
66
65
|
@typechecked()
|
|
67
|
-
def set_command_body(self, command_body:
|
|
66
|
+
def set_command_body(self, command_body: List[str]) -> Self:
|
|
68
67
|
"""
|
|
69
68
|
Sets the command body containing arguments and options.
|
|
70
69
|
:param command_body: A list of strings representing the command body
|
|
@@ -84,7 +83,7 @@ class CommandBuilder:
|
|
|
84
83
|
if self._command.values_delimiter != delimiter:
|
|
85
84
|
self._command.values_delimiter = delimiter
|
|
86
85
|
if update_current_values:
|
|
87
|
-
self.
|
|
86
|
+
self.__update_all_options()
|
|
88
87
|
return self
|
|
89
88
|
|
|
90
89
|
@typechecked()
|
|
@@ -100,7 +99,7 @@ class CommandBuilder:
|
|
|
100
99
|
return self
|
|
101
100
|
|
|
102
101
|
@typechecked()
|
|
103
|
-
def add_options(self, *args:
|
|
102
|
+
def add_options(self, *args: Tuple[str, Optional[str]]) -> Self:
|
|
104
103
|
"""
|
|
105
104
|
Adds multiple options to the command.
|
|
106
105
|
:param args: Variable number of tuples, each containing (option, value) pairs
|
|
@@ -145,12 +144,12 @@ class CommandBuilder:
|
|
|
145
144
|
"""
|
|
146
145
|
return copy.copy(self._command)
|
|
147
146
|
|
|
148
|
-
def
|
|
147
|
+
def __update_all_options(self) -> Self:
|
|
149
148
|
"""
|
|
150
149
|
Updates all existing options with the current delimiter.
|
|
151
150
|
:returns: Self for method chaining
|
|
152
151
|
"""
|
|
153
|
-
self._command.command_body = self._command.
|
|
152
|
+
self._command.command_body = self._command.command_body[:-sum(len(option) for option in self._command.options)]
|
|
154
153
|
options = self._command.options.copy()
|
|
155
154
|
self._command.options = []
|
|
156
155
|
self.add_options(*options)
|
|
@@ -2,14 +2,6 @@ from crossfit.models.executor_models import ExecutorType
|
|
|
2
2
|
from crossfit.executors.local_executor import LocalExecutor
|
|
3
3
|
|
|
4
4
|
def create_executor(executor_type: ExecutorType, logger = None, catch: bool = True, **kwargs):
|
|
5
|
-
"""
|
|
6
|
-
Factory method to create an executor based on the specified type.
|
|
7
|
-
:param executor_type: The type of executor to create.
|
|
8
|
-
:param logger: Optional logger instance for logging execution details.
|
|
9
|
-
:param catch: Whether to catch exceptions during execution.
|
|
10
|
-
:param kwargs: Additional keyword arguments for executor initialization.
|
|
11
|
-
:return: An instance of the specified executor type.
|
|
12
|
-
"""
|
|
13
5
|
if executor_type == ExecutorType.Local:
|
|
14
6
|
return LocalExecutor(logger, catch, **kwargs)
|
|
15
7
|
else:
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import subprocess
|
|
2
2
|
from logging import Logger
|
|
3
3
|
import shlex
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
4
|
from crossfit.commands.command import Command
|
|
7
5
|
from crossfit.executors.executor import Executor
|
|
8
6
|
from crossfit.models.command_models import CommandResult
|
|
@@ -11,25 +9,20 @@ from crossfit.models.command_models import CommandResult
|
|
|
11
9
|
class LocalExecutor(Executor):
|
|
12
10
|
"""Executor that runs commands locally via subprocess."""
|
|
13
11
|
|
|
14
|
-
def __init__(self, logger: Logger, catch: bool = True,
|
|
12
|
+
def __init__(self, logger: Logger, catch: bool = True, **execution_kwargs):
|
|
15
13
|
"""
|
|
16
14
|
:param logger: Logger instance for logging execution details (required)
|
|
17
15
|
:param catch: If True, catches exceptions and returns error in CommandResult.
|
|
18
16
|
If False, re-raises exceptions.
|
|
19
17
|
:param execution_kwargs: Additional arguments passed to subprocess.run
|
|
20
18
|
"""
|
|
21
|
-
self._workdir = workdir
|
|
22
|
-
if workdir is not None:
|
|
23
|
-
execution_kwargs["cwd"] = str(workdir)
|
|
24
|
-
|
|
25
19
|
super().__init__(logger, catch)
|
|
26
|
-
|
|
27
20
|
self._exec_kwargs = {
|
|
28
21
|
"capture_output": True,
|
|
29
22
|
"check": True,
|
|
30
23
|
"text": True,
|
|
31
|
-
**execution_kwargs,
|
|
32
24
|
}
|
|
25
|
+
self._exec_kwargs.update(execution_kwargs)
|
|
33
26
|
|
|
34
27
|
def _execute_single(self, command: Command) -> CommandResult:
|
|
35
28
|
"""
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from typing import Optional
|
|
2
|
+
from typing import List, Tuple, Optional
|
|
3
3
|
|
|
4
4
|
from crossfit import Command
|
|
5
5
|
from crossfit.models import ReportFormat, ToolType
|
|
@@ -9,54 +9,16 @@ from crossfit.tools.tool import Tool
|
|
|
9
9
|
class DotnetCoverage(Tool):
|
|
10
10
|
_tool_type = ToolType.DotnetCoverage
|
|
11
11
|
|
|
12
|
-
def
|
|
13
|
-
coverage_files,
|
|
14
|
-
target_dir,
|
|
15
|
-
sourcecode_dir=None,
|
|
16
|
-
report_format: ReportFormat = None,
|
|
17
|
-
report_formats: list[ReportFormat] = None,
|
|
18
|
-
*extras: tuple[str, Optional[str]]) -> Command:
|
|
19
|
-
"""
|
|
20
|
-
Creates a dotnet-coverage report from coverage files to the given path.
|
|
21
|
-
:param coverage_files: File paths (can handle wildcards) to create dotnet-coverage report from.
|
|
22
|
-
:param target_dir: Targeted directory to save the dotnet-coverage report to.
|
|
23
|
-
:param sourcecode_dir: Directory containing the covered source code files.
|
|
24
|
-
:param report_format: Primary format of the dotnet-coverage report.
|
|
25
|
-
:param report_formats: Additional formats of dotnet-coverage reports to create.
|
|
26
|
-
:param extras: Extra options to pass to the dotnet CLI's report command.
|
|
27
|
-
:return: A Command object configured to generate the coverage report.
|
|
28
|
-
"""
|
|
29
|
-
multiple_values_delimiter = ';'
|
|
30
|
-
if sourcecode_dir:
|
|
31
|
-
extras += ("-sourcedirs", str(sourcecode_dir)),
|
|
32
|
-
extras += ("-targetdir", str(target_dir)),
|
|
33
|
-
command_builder = (
|
|
34
|
-
self._create_command_builder("", ToolType.DotnetReportGenerator, None, *extras)
|
|
35
|
-
.set_values_delimiter(":", True)
|
|
36
|
-
.add_option("-reports", f"\"{multiple_values_delimiter.join(map(str, coverage_files))}\""))
|
|
37
|
-
combined_formats = set((report_formats or []) + [report_format])
|
|
38
|
-
command_builder = command_builder.add_option(
|
|
39
|
-
"-reporttypes",f"\"{multiple_values_delimiter.join([rf.value for rf in combined_formats if rf])}\"")
|
|
40
|
-
|
|
41
|
-
command = command_builder.build_command()
|
|
42
|
-
command.command = [kw.replace("--", "-") for kw in command.command[2:]]
|
|
43
|
-
|
|
44
|
-
return command
|
|
45
|
-
|
|
46
|
-
def snapshot_coverage(self,
|
|
47
|
-
session,
|
|
48
|
-
target_dir,
|
|
49
|
-
target_file,
|
|
50
|
-
*extras: tuple[str, Optional[str]]) -> Command:
|
|
12
|
+
def snapshot_coverage(self, session, target_dir, target_file, *extras: Tuple[str, Optional[str]]) -> Command:
|
|
51
13
|
"""
|
|
52
14
|
Triggers dotnet-coverage agent to save cobertura formatted coverage files to the given path.
|
|
53
|
-
:param session: Session id of the dotnet-coverage agent collected-coverage
|
|
15
|
+
:param session: Session id of the dotnet-coverage agent collected-coverage to snapshot.
|
|
54
16
|
:param target_dir: Targeted directory to save the dotnet-coverage collection to.
|
|
55
17
|
:param target_file: Specified snapshot file name - when not given, uses default with .xml suffix.
|
|
56
18
|
:param extras: Extra options to pass to the dotnet-coverage CLI's snapshot command.
|
|
57
19
|
:return: A Command object configured to snapshot coverage data.
|
|
58
20
|
"""
|
|
59
|
-
target_path = target_dir / (
|
|
21
|
+
target_path = Path(target_dir) / (
|
|
60
22
|
target_file if target_file is not None else self._get_default_target_filename())
|
|
61
23
|
if not target_path.suffix:
|
|
62
24
|
target_path = target_path.with_suffix(".xml")
|
|
@@ -66,11 +28,7 @@ class DotnetCoverage(Tool):
|
|
|
66
28
|
|
|
67
29
|
return command_builder.build_command()
|
|
68
30
|
|
|
69
|
-
def merge_coverage(self,
|
|
70
|
-
coverage_files,
|
|
71
|
-
target_dir,
|
|
72
|
-
target_file,
|
|
73
|
-
*extras: tuple[str, Optional[str]]) -> Command:
|
|
31
|
+
def merge_coverage(self, coverage_files, target_dir, target_file, *extras: Tuple[str, Optional[str]]) -> Command:
|
|
74
32
|
"""
|
|
75
33
|
Merges multiple coverage files into a single unified coverage file.
|
|
76
34
|
:param coverage_files: File paths to coverage files to merge.
|
|
@@ -79,7 +37,7 @@ class DotnetCoverage(Tool):
|
|
|
79
37
|
:param extras: Extra options to pass to the dotnet-coverage CLI's merge command.
|
|
80
38
|
:return: A Command object configured to merge coverage files.
|
|
81
39
|
"""
|
|
82
|
-
extras += ("--output", str(target_dir / (
|
|
40
|
+
extras += ("--output", str(Path(target_dir) / (
|
|
83
41
|
target_file if target_file is not None else Path(self._get_default_target_filename()).with_suffix(
|
|
84
42
|
".xml")))),
|
|
85
43
|
command_builder = self._create_command_builder("merge", None, coverage_files, *extras)
|
|
@@ -87,3 +45,33 @@ class DotnetCoverage(Tool):
|
|
|
87
45
|
command_builder = command_builder.add_option("--output-format", ReportFormat.Cobertura.value.lower())
|
|
88
46
|
|
|
89
47
|
return command_builder.build_command()
|
|
48
|
+
|
|
49
|
+
def save_report(self, coverage_files, target_dir, sourcecode_dir,
|
|
50
|
+
report_format: ReportFormat = None, report_formats: List[ReportFormat] = None,
|
|
51
|
+
*extras: Tuple[str, Optional[str]]) -> Command:
|
|
52
|
+
"""
|
|
53
|
+
Creates a dotnet-coverage report from coverage files to the given path.
|
|
54
|
+
:param coverage_files: File paths (can handle wildcards) to create dotnet-coverage report from.
|
|
55
|
+
:param target_dir: Targeted directory to save the dotnet-coverage report to.
|
|
56
|
+
:param sourcecode_dir: Directory containing the covered source code files.
|
|
57
|
+
:param report_format: Primary format of the dotnet-coverage report.
|
|
58
|
+
:param report_formats: Additional formats of dotnet-coverage reports to create.
|
|
59
|
+
:param extras: Extra options to pass to the dotnet CLI's report command.
|
|
60
|
+
:return: A Command object configured to generate the coverage report.
|
|
61
|
+
"""
|
|
62
|
+
multiple_values_delimiter = ';'
|
|
63
|
+
if sourcecode_dir:
|
|
64
|
+
extras += ("-sourcedirs", str(sourcecode_dir)),
|
|
65
|
+
extras += ("-targetdir", str(target_dir)),
|
|
66
|
+
command_builder = (
|
|
67
|
+
self._create_command_builder("", ToolType.DotnetReportGenerator, None, *extras)
|
|
68
|
+
.set_values_delimiter(":", True)
|
|
69
|
+
.add_option("-reports", f"\"{multiple_values_delimiter.join(map(str, coverage_files))}\""))
|
|
70
|
+
combined_formats = set((report_formats or []) + [report_format])
|
|
71
|
+
command_builder = command_builder.add_option(
|
|
72
|
+
"-reporttypes",f"\"{multiple_values_delimiter.join([rf.value for rf in combined_formats if rf])}\"")
|
|
73
|
+
|
|
74
|
+
command = command_builder.build_command()
|
|
75
|
+
command.command = [kw.replace("--", "-") for kw in command.command[2:]]
|
|
76
|
+
|
|
77
|
+
return command
|
crossfit/tools/jacoco.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os.path
|
|
2
|
-
|
|
3
|
-
from typing import Optional
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Optional, Tuple
|
|
4
4
|
from crossfit import Command
|
|
5
5
|
from crossfit.commands.command_builder import CommandBuilder
|
|
6
6
|
from crossfit.models.tool_models import ReportFormat, ToolType
|
|
@@ -11,12 +11,8 @@ class Jacoco(Tool):
|
|
|
11
11
|
"""JaCoCo coverage tool implementation for Java projects."""
|
|
12
12
|
_tool_type = ToolType.Jacoco
|
|
13
13
|
|
|
14
|
-
def _create_command_builder(self,
|
|
15
|
-
|
|
16
|
-
tool_type = None,
|
|
17
|
-
path_arguments = None,
|
|
18
|
-
required_flags = None,
|
|
19
|
-
*extras: tuple[str, Optional[str]]) -> CommandBuilder:
|
|
14
|
+
def _create_command_builder(self, command, tool_type = None, path_arguments = None, required_flags = None,
|
|
15
|
+
*extras: Tuple[str, Optional[str]]) -> CommandBuilder:
|
|
20
16
|
"""
|
|
21
17
|
Creates a CommandBuilder for JaCoCo CLI commands.
|
|
22
18
|
:param command: The JaCoCo command to execute (e.g., 'report', 'dump', 'merge').
|
|
@@ -29,11 +25,11 @@ class Jacoco(Tool):
|
|
|
29
25
|
"""
|
|
30
26
|
tool_type = tool_type or self._tool_type
|
|
31
27
|
command_builder = (super()._create_command_builder(command, tool_type, path_arguments, *extras)
|
|
32
|
-
.set_execution_call(f"java -jar {os.path.relpath(self._path / str(tool_type.value))}"))
|
|
28
|
+
.set_execution_call(f"java -jar {os.path.relpath(Path(self._path) / str(tool_type.value))}"))
|
|
33
29
|
|
|
34
30
|
required_flags = required_flags or []
|
|
35
31
|
for required_flag in required_flags:
|
|
36
|
-
if required_flag not in [extra[0] for extra in extras]:
|
|
32
|
+
if required_flag not in [extra[0] for extra in list(extras)]:
|
|
37
33
|
msg = (f"Encountered error while building {tool_type.name} command. "
|
|
38
34
|
f"JaCoCo flag option {required_flag} is required for command '{command}'.")
|
|
39
35
|
self._logger.error(msg)
|
|
@@ -43,14 +39,8 @@ class Jacoco(Tool):
|
|
|
43
39
|
|
|
44
40
|
return command_builder
|
|
45
41
|
|
|
46
|
-
def save_report(self,
|
|
47
|
-
|
|
48
|
-
target_dir,
|
|
49
|
-
sourcecode_dir=None,
|
|
50
|
-
report_format = None,
|
|
51
|
-
report_formats = None,
|
|
52
|
-
build_dir = None,
|
|
53
|
-
*extras) -> Command:
|
|
42
|
+
def save_report(self, coverage_files, target_dir, report_format, report_formats, sourcecode_dir, build_dir, *extras)\
|
|
43
|
+
-> Command:
|
|
54
44
|
"""
|
|
55
45
|
Creates a JaCoCo coverage report from coverage files to the given path.
|
|
56
46
|
:param coverage_files: File paths to JaCoCo .exec coverage files to create the report from.
|
|
@@ -74,15 +64,11 @@ class Jacoco(Tool):
|
|
|
74
64
|
command = command.add_option(f"--{rf.name.lower()}", str(target_dir))
|
|
75
65
|
elif rf is not None:
|
|
76
66
|
command = command.add_option(f"--{rf.name.lower()}",
|
|
77
|
-
str((target_dir / self._get_default_target_filename())
|
|
67
|
+
str((Path(target_dir) / self._get_default_target_filename())
|
|
78
68
|
.with_suffix(f".{rf.value.lower()}")))
|
|
79
69
|
return command.build_command()
|
|
80
70
|
|
|
81
|
-
def snapshot_coverage(self,
|
|
82
|
-
session,
|
|
83
|
-
target_dir,
|
|
84
|
-
target_file,
|
|
85
|
-
*extras) -> Command:
|
|
71
|
+
def snapshot_coverage(self, session, target_dir, target_file, *extras) -> Command:
|
|
86
72
|
"""
|
|
87
73
|
Triggers JaCoCo agent to dump coverage data to the given path.
|
|
88
74
|
:param session: Session identifier (not used by JaCoCo dump but kept for interface consistency).
|
|
@@ -91,7 +77,7 @@ class Jacoco(Tool):
|
|
|
91
77
|
:param extras: Extra options to pass to the JaCoCo CLI's dump command.
|
|
92
78
|
:return: A Command object configured to dump coverage data.
|
|
93
79
|
"""
|
|
94
|
-
target_path = target_dir / (
|
|
80
|
+
target_path = Path(target_dir) / (
|
|
95
81
|
target_file if target_file is not None else self._get_default_target_filename())
|
|
96
82
|
if not target_path.suffix:
|
|
97
83
|
target_path = target_path.with_suffix(".exec")
|
|
@@ -100,11 +86,7 @@ class Jacoco(Tool):
|
|
|
100
86
|
"dump", None, None, None, *extras)
|
|
101
87
|
return command.build_command()
|
|
102
88
|
|
|
103
|
-
def merge_coverage(self,
|
|
104
|
-
coverage_files,
|
|
105
|
-
target_dir,
|
|
106
|
-
target_file,
|
|
107
|
-
*extras) -> Command:
|
|
89
|
+
def merge_coverage(self, coverage_files, target_dir, target_file, *extras) -> Command:
|
|
108
90
|
"""
|
|
109
91
|
Merges multiple JaCoCo coverage files into a single unified coverage file.
|
|
110
92
|
:param coverage_files: File paths to JaCoCo .exec coverage files to merge.
|
|
@@ -113,7 +95,7 @@ class Jacoco(Tool):
|
|
|
113
95
|
:param extras: Extra options to pass to the JaCoCo CLI's merge command.
|
|
114
96
|
:return: A Command object configured to merge coverage files.
|
|
115
97
|
"""
|
|
116
|
-
target_path = target_dir / (
|
|
98
|
+
target_path = Path(target_dir) / (
|
|
117
99
|
target_file if target_file is not None else self._get_default_target_filename())
|
|
118
100
|
if not target_path.suffix:
|
|
119
101
|
target_path = target_path.with_suffix(".exec")
|
crossfit/tools/tool.py
CHANGED
|
@@ -1,28 +1,27 @@
|
|
|
1
1
|
import tempfile
|
|
2
|
-
|
|
2
|
+
import logging
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
|
-
from logging import Logger
|
|
5
4
|
from pathlib import Path
|
|
6
|
-
from typing import Optional
|
|
7
|
-
from crossfit import Command
|
|
8
|
-
from crossfit.commands import CommandBuilder
|
|
9
|
-
from crossfit.models import ToolType
|
|
5
|
+
from typing import Collection, Optional, Tuple, List, Union
|
|
6
|
+
from crossfit.commands.command import Command
|
|
7
|
+
from crossfit.commands.command_builder import CommandBuilder
|
|
8
|
+
from crossfit.models.tool_models import ToolType
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
class Tool(ABC):
|
|
13
12
|
"""Abstract base class for coverage tools. Tools build and return Commands."""
|
|
14
13
|
_tool_type: ToolType
|
|
15
14
|
_path: Optional[Path]
|
|
16
|
-
_logger: Logger
|
|
15
|
+
_logger: logging.Logger
|
|
17
16
|
_catch: bool
|
|
18
17
|
|
|
19
|
-
def __init__(self, logger: Logger, path: Optional[Path] = None, catch: bool = True):
|
|
18
|
+
def __init__(self, logger: logging.Logger, path: Optional[Path] = None, catch: bool = True):
|
|
20
19
|
"""
|
|
21
20
|
:param logger: Logger instance for logging (required).
|
|
22
21
|
:param path: The path to the tool executable/jar.
|
|
23
22
|
:param catch: If True, catches exceptions and returns fallback. If False, re-raises.
|
|
24
23
|
"""
|
|
25
|
-
self._logger: Logger = logger
|
|
24
|
+
self._logger: logging.Logger = logger
|
|
26
25
|
self._path: Optional[Path] = path
|
|
27
26
|
self._catch: bool = catch
|
|
28
27
|
|
|
@@ -30,11 +29,9 @@ class Tool(ABC):
|
|
|
30
29
|
"""Returns the default target filename for this tool."""
|
|
31
30
|
return f"cross-{self._tool_type.name}".lower()
|
|
32
31
|
|
|
33
|
-
def _create_command_builder(self,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
path_arguments: Optional[list[Path]] = None,
|
|
37
|
-
*extras: tuple[str, Optional[str]]) -> CommandBuilder:
|
|
32
|
+
def _create_command_builder(self, command: str, tool_type: Optional[ToolType] = None,
|
|
33
|
+
path_arguments: Optional[Collection[Path]] = None,
|
|
34
|
+
*extras: Tuple[str, Optional[str]]) -> CommandBuilder:
|
|
38
35
|
"""
|
|
39
36
|
Creates a CommandBuilder for the tool's wanted functionality.
|
|
40
37
|
:param command: The command of the tool to build on.
|
|
@@ -48,10 +45,8 @@ class Tool(ABC):
|
|
|
48
45
|
|
|
49
46
|
command_builder = CommandBuilder().set_execution_call(str(tool_type.value), self._path)
|
|
50
47
|
try:
|
|
51
|
-
return (
|
|
52
|
-
|
|
53
|
-
.add_path_arguments(*path_arguments)
|
|
54
|
-
.add_options(*extras))
|
|
48
|
+
return command_builder.set_command_to_execute(command).add_path_arguments(*path_arguments).add_options(
|
|
49
|
+
*extras)
|
|
55
50
|
except FileNotFoundError as e:
|
|
56
51
|
self._logger.error(f"Encountered exception while building {tool_type.name} command. Error - {e}")
|
|
57
52
|
if not self._catch:
|
|
@@ -59,37 +54,25 @@ class Tool(ABC):
|
|
|
59
54
|
return command_builder.set_command_body(["--help"])
|
|
60
55
|
|
|
61
56
|
@abstractmethod
|
|
62
|
-
def save_report(self,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
sourcecode_dir: Optional[Path] = None,
|
|
66
|
-
report_format: ReportFormat = None,
|
|
67
|
-
report_formats: list[ReportFormat] = None,
|
|
68
|
-
*extras: tuple[str, Optional[str]]) -> Command:
|
|
57
|
+
def save_report(self, coverage_files: List[Union[Path, str]], target_dir: Path, report_format, report_formats,
|
|
58
|
+
sourcecode_dir: Optional[Path], build_dir: Optional[Path],
|
|
59
|
+
*extras: Tuple[str, Optional[str]]) -> Command:
|
|
69
60
|
"""Builds a command to create a coverage report."""
|
|
70
61
|
raise NotImplementedError
|
|
71
62
|
|
|
72
63
|
@abstractmethod
|
|
73
|
-
def snapshot_coverage(self,
|
|
74
|
-
|
|
75
|
-
target_dir: Path,
|
|
76
|
-
target_file: Optional[Path],
|
|
77
|
-
*extras: tuple[str, Optional[str]]) -> Command:
|
|
64
|
+
def snapshot_coverage(self, session: str, target_dir: Path, target_file: Optional[Path],
|
|
65
|
+
*extras: Tuple[str, Optional[str]]) -> Command:
|
|
78
66
|
"""Builds a command to snapshot coverage data."""
|
|
79
67
|
raise NotImplementedError
|
|
80
68
|
|
|
81
69
|
@abstractmethod
|
|
82
|
-
def merge_coverage(self,
|
|
83
|
-
|
|
84
|
-
target_dir: Path,
|
|
85
|
-
target_file: Optional[Path],
|
|
86
|
-
*extras: tuple[str, Optional[str]]) -> Command:
|
|
70
|
+
def merge_coverage(self, coverage_files: List[Union[Path, str]], target_dir: Path, target_file: Optional[Path],
|
|
71
|
+
*extras: Tuple[str, Optional[str]]) -> Command:
|
|
87
72
|
"""Builds a command to merge coverage files."""
|
|
88
73
|
raise NotImplementedError
|
|
89
74
|
|
|
90
|
-
def reset_coverage(self,
|
|
91
|
-
session: str,
|
|
92
|
-
*extras: tuple[str, Optional[str]]) -> Command:
|
|
75
|
+
def reset_coverage(self, session: str, *extras: Tuple[str, Optional[str]]) -> Command:
|
|
93
76
|
"""
|
|
94
77
|
Builds a command to reset coverage data.
|
|
95
78
|
Uses next_command chaining to snapshot with --reset flag and then clean up temp file.
|
|
@@ -98,7 +81,7 @@ class Tool(ABC):
|
|
|
98
81
|
:returns: A Command with chained cleanup command via next_command.
|
|
99
82
|
"""
|
|
100
83
|
target_dir = Path(tempfile.gettempdir()) / r"crossfit"
|
|
101
|
-
extras
|
|
84
|
+
extras = extras + (("--reset", None),)
|
|
102
85
|
|
|
103
86
|
snapshot_command = self.snapshot_coverage(session, target_dir, None, *extras)
|
|
104
87
|
cleanup_command = CommandBuilder().with_command(["rm", "-f", str(target_dir)]).build_command()
|
crossfit/tools/tool_factory.py
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from logging import Logger
|
|
2
3
|
from pathlib import Path
|
|
4
|
+
from typing import Union
|
|
3
5
|
|
|
4
6
|
import crossfit.refs
|
|
5
7
|
from crossfit.tools import Jacoco, DotnetCoverage
|
|
6
8
|
from crossfit.models import ToolType
|
|
7
9
|
|
|
8
10
|
|
|
9
|
-
def create_tool(tool_type: ToolType, tool_path:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
:param tool_path: Optional path to the tool executable.
|
|
14
|
-
:param logger: Optional logger instance for logging tool operations.
|
|
15
|
-
:param catch: Whether to catch exceptions during tool operations.
|
|
16
|
-
:return: An instance of the specified tool type.
|
|
17
|
-
"""
|
|
11
|
+
def create_tool(tool_type: ToolType, tool_path: str = None, cwd: Union[str, os.PathLike] = None, logger: Logger = None,
|
|
12
|
+
catch: bool = True, **kwargs):
|
|
13
|
+
if cwd:
|
|
14
|
+
kwargs["cwd"] = cwd
|
|
18
15
|
if tool_type == ToolType.Jacoco:
|
|
19
|
-
return Jacoco(logger, tool_path or crossfit.refs.tools_dir, catch)
|
|
16
|
+
return Jacoco(logger, Path(tool_path) or Path(crossfit.refs.tools_dir), catch)
|
|
20
17
|
elif tool_type == ToolType.DotnetCoverage:
|
|
21
|
-
return DotnetCoverage(logger, tool_path or crossfit.refs.tools_dir, catch)
|
|
18
|
+
return DotnetCoverage(logger, Path(tool_path) or Path(crossfit.refs.tools_dir), catch)
|
|
22
19
|
else:
|
|
23
20
|
raise ValueError(f"Unknown tool type: {tool_type}")
|
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: SmokeCrossFit
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.0b6
|
|
4
4
|
Summary: A unified interface for various code coverage tools (JaCoCo, dotnet-coverage)
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
|
-
Requires-Dist: annotated-types~=0.7.0
|
|
8
|
-
Requires-Dist: colorama~=0.4.6
|
|
9
|
-
Requires-Dist: pydantic~=2.11.7
|
|
10
|
-
Requires-Dist: pydantic_core~=2.33.2
|
|
11
|
-
Requires-Dist: Pygments~=2.19.2
|
|
12
|
-
Requires-Dist: typeguard~=4.4.4
|
|
13
|
-
Requires-Dist: typing-inspection~=0.4.1
|
|
14
|
-
Requires-Dist: typing_extensions~=4.14.1
|
|
15
|
-
Requires-Dist: pytest>=8.4.3
|
|
16
|
-
Requires-Dist: build>=1.2.0
|
|
17
|
-
Requires-Dist: twine>=5.0.0
|
|
18
7
|
|
|
19
8
|
# CrossFit Coverage Tools
|
|
20
9
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
crossfit/__init__.py,sha256=Bdtz57-tapFnCYiIa9V5k_edCRkXWKqbp13xODBEv4c,382
|
|
2
|
+
crossfit/bin/tools/jacococli.jar,sha256=gRx_jGs1jF1oqJc8-oZ_aJK-emcbaXpLE8S0R-ba91w,607951
|
|
3
|
+
crossfit/commands/__init__.py,sha256=Hiu6s_GoE0cxhtbgnc8rUXPUxRejnHOQAAfhpSHheLI,51
|
|
4
|
+
crossfit/commands/command.py,sha256=0ujP5wvzfA0P9WJfzJrucgGWBEuiZjbxs9oMtdTD8p0,2752
|
|
5
|
+
crossfit/commands/command_builder.py,sha256=GLfvt0zXDQYEirQd8VGOjAUQvnpeslk2KLQp4KMYCAw,7128
|
|
6
|
+
crossfit/executors/__init__.py,sha256=7lUt1jO6RQwY_6xorlXaDBsOLlBradx9bKxzS-VSms0,179
|
|
7
|
+
crossfit/executors/executor.py,sha256=xPDTuP8wb9OQNC44Hy6yy9w9tDTT3NuH6oS_fXs_gp0,1665
|
|
8
|
+
crossfit/executors/executor_factory.py,sha256=xSsTU3f8AgeMiFCwyGM4ZOm3p9Lp--KQRIqNY5ueaZI,389
|
|
9
|
+
crossfit/executors/local_executor.py,sha256=dNkHoY5bQxRQbniec1ZwGKt56QKbZP_qwXSW3avTfno,3435
|
|
10
|
+
crossfit/models/__init__.py,sha256=awbAmN1HTp01PWgTB4JNwlg1EF_9w9ecSxqOaUVme4E,205
|
|
11
|
+
crossfit/models/command_models.py,sha256=0bK7deXh16SRMPtHXTyYmqScAVoRzyycO3uNRgImBHo,818
|
|
12
|
+
crossfit/models/executor_models.py,sha256=9d4UYXKvzIMwf_COBHxg23bvBxJp21A97gJP9oAkvb0,92
|
|
13
|
+
crossfit/models/tool_models.py,sha256=CM-JKXzM563BGverMP4jQFWcqcplA5RXcNJwhoWjHHg,266
|
|
14
|
+
crossfit/refs/__init__.py,sha256=OEUVIOc-CDNp7KmmcDzKfwkz9LA7SlRtrD3Y5rjQIIU,270
|
|
15
|
+
crossfit/tools/__init__.py,sha256=YtIhLrhdEtDOOH_TMGr9_tgt59vxN-kM97I5ajeN9LY,196
|
|
16
|
+
crossfit/tools/dotnet_coverage.py,sha256=1NBkj6Inb-qf3LITciu1248oboMhQTShFqveu-tkW0U,4359
|
|
17
|
+
crossfit/tools/jacoco.py,sha256=oLw895WvaVk_2mSeI3vAh7vWswg-daMzTEnl26tx1cY,5992
|
|
18
|
+
crossfit/tools/tool.py,sha256=OL3xVMf56UdCnahwX1rz-U7qZcXWFo7ep5B0CCu6RFs,4296
|
|
19
|
+
crossfit/tools/tool_factory.py,sha256=Fstp2NlIOsfYY6UX8vefJ7_ClrojnsbOMA7J63dDl58,741
|
|
20
|
+
smokecrossfit-1.0.0b6.dist-info/METADATA,sha256=0pkusKoQbFT0XGisi9hTfyv47sh9UJgn7NvnU__WQm4,5650
|
|
21
|
+
smokecrossfit-1.0.0b6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
22
|
+
smokecrossfit-1.0.0b6.dist-info/top_level.txt,sha256=rDrcypRByvkrDsESoQmYOSkOKMO7eBPRToxHNQflYOI,9
|
|
23
|
+
smokecrossfit-1.0.0b6.dist-info/RECORD,,
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
crossfit/__init__.py,sha256=yGycXJNRgaQySCTqhwaBsOaX8J6GWqXtEVpIcVVnAzI,374
|
|
2
|
-
crossfit/bin/tools/jacococli.jar,sha256=gRx_jGs1jF1oqJc8-oZ_aJK-emcbaXpLE8S0R-ba91w,607951
|
|
3
|
-
crossfit/commands/__init__.py,sha256=mRsGTLGRJ9f1lJ7J55AbG65z-sq33u9p_hPkFURyeqo,114
|
|
4
|
-
crossfit/commands/command.py,sha256=ZjAj5a14AMOkFm0bfIrXqUMdplSeEG0M18llmP8Ol8M,2739
|
|
5
|
-
crossfit/commands/command_builder.py,sha256=v8Nd8GAifGUew1eavTP1SMD11sPipsjvwvjydXovlM0,7060
|
|
6
|
-
crossfit/executors/__init__.py,sha256=7lUt1jO6RQwY_6xorlXaDBsOLlBradx9bKxzS-VSms0,179
|
|
7
|
-
crossfit/executors/executor.py,sha256=xPDTuP8wb9OQNC44Hy6yy9w9tDTT3NuH6oS_fXs_gp0,1665
|
|
8
|
-
crossfit/executors/executor_factory.py,sha256=Zps02ob42zM6EtuR_NOkpW2s6fxXUWUgYHOW_HlmzGk,806
|
|
9
|
-
crossfit/executors/local_executor.py,sha256=MSYo7QfF3puPBL7XaO7az-isPFXOLgla3hJduyv-RHM,3581
|
|
10
|
-
crossfit/models/__init__.py,sha256=awbAmN1HTp01PWgTB4JNwlg1EF_9w9ecSxqOaUVme4E,205
|
|
11
|
-
crossfit/models/command_models.py,sha256=FIkha9E_NKw2dP1Ax6WSUhP0EoG9z0-PwKWpEA-NUBE,817
|
|
12
|
-
crossfit/models/executor_models.py,sha256=9d4UYXKvzIMwf_COBHxg23bvBxJp21A97gJP9oAkvb0,92
|
|
13
|
-
crossfit/models/tool_models.py,sha256=CM-JKXzM563BGverMP4jQFWcqcplA5RXcNJwhoWjHHg,266
|
|
14
|
-
crossfit/refs/__init__.py,sha256=OEUVIOc-CDNp7KmmcDzKfwkz9LA7SlRtrD3Y5rjQIIU,270
|
|
15
|
-
crossfit/tools/__init__.py,sha256=YtIhLrhdEtDOOH_TMGr9_tgt59vxN-kM97I5ajeN9LY,196
|
|
16
|
-
crossfit/tools/dotnet_coverage.py,sha256=2oY1b4ON3WrjzleB5ZEfU4vaVrelMKsCqaZehzZE9tg,4616
|
|
17
|
-
crossfit/tools/jacoco.py,sha256=0ogqV_mSr3pbeCulaq6jZXgw9xk5Lqd-4_n8FKQ85hM,6408
|
|
18
|
-
crossfit/tools/tool.py,sha256=Ee_srBabhtRJ2VtwPfyrThHm8rVc-OpMbRfzJTIxLGQ,4601
|
|
19
|
-
crossfit/tools/tool_factory.py,sha256=jyXAQsIBjCp3qCvmH5AudlQxv5yHf3uik0EvWW9BAoM,967
|
|
20
|
-
smokecrossfit-1.0.0a1.dist-info/METADATA,sha256=OxIc4lWiwXRumKq2vBu2H2vNAPRePMiXoR9gg_MMJGE,6018
|
|
21
|
-
smokecrossfit-1.0.0a1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
22
|
-
smokecrossfit-1.0.0a1.dist-info/top_level.txt,sha256=rDrcypRByvkrDsESoQmYOSkOKMO7eBPRToxHNQflYOI,9
|
|
23
|
-
smokecrossfit-1.0.0a1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|