Skip to content

Commit 838ffd1

Browse files
Jsostmannjostmann
and
jostmann
authored
Added option to specify diff file location (#410)
* Added option to specify diff-file * Added section for diff-file in README * Add unit tests for GitDiffFileTool class * Reformatted files * Ordered imports --------- Co-authored-by: jostmann <[email protected]>
1 parent 4f49610 commit 838ffd1

File tree

4 files changed

+150
-6
lines changed

4 files changed

+150
-6
lines changed

README.rst

+27
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,33 @@ By default, ``diff-cover`` compares the current branch to ``origin/main``. To s
190190
191191
diff-cover coverage.xml --compare-branch=origin/release
192192
193+
Diff File
194+
--------------
195+
196+
You may provide a file containing the output of ``git diff`` to ``diff-cover`` instead of using a branch name.
197+
198+
For example, Say you have 2 branches ``main`` and ``feature``. Lets say after creating and checking out the feature branch,
199+
you make commits ``A``, ``B``, and ``C`` in that order.
200+
201+
202+
If you want to see all changes between the ``feature`` and ``main`` branch, you can generate a diff file like this:
203+
204+
.. code:: bash
205+
206+
git diff main..feature > diff.txt
207+
208+
If you want to see the changes between the ``feature`` branch and the commit ``A``, you can generate a diff file using the following command:
209+
210+
.. code:: bash
211+
212+
git diff A..feature > diff.txt
213+
214+
You can then run ``diff-cover`` with the diff file as an argument:
215+
216+
.. code:: bash
217+
218+
diff-cover coverage.xml --diff-file=diff.txt
219+
193220
Fail Under
194221
----------
195222

diff_cover/diff_cover_tool.py

+16-6
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from diff_cover import DESCRIPTION, VERSION
99
from diff_cover.config_parser import Tool, get_config
1010
from diff_cover.diff_reporter import GitDiffReporter
11-
from diff_cover.git_diff import GitDiffTool
11+
from diff_cover.git_diff import GitDiffFileTool, GitDiffTool
1212
from diff_cover.git_path import GitPathTool
1313
from diff_cover.report_generator import (
1414
HtmlReportGenerator,
@@ -43,6 +43,7 @@
4343
SHOW_UNCOVERED = "Show uncovered lines on the console"
4444
INCLUDE_UNTRACKED_HELP = "Include untracked files"
4545
CONFIG_FILE_HELP = "The configuration file to use"
46+
DIFF_FILE_HELP = "The diff file to use"
4647

4748
LOGGER = logging.getLogger(__name__)
4849

@@ -169,6 +170,8 @@ def parse_coverage_args(argv):
169170
"-c", "--config-file", help=CONFIG_FILE_HELP, metavar="CONFIG_FILE"
170171
)
171172

173+
parser.add_argument("--diff-file", type=str, default=None, help=DIFF_FILE_HELP)
174+
172175
defaults = {
173176
"show_uncovered": False,
174177
"compare_branch": "origin/main",
@@ -188,6 +191,7 @@ def parse_coverage_args(argv):
188191
def generate_coverage_report(
189192
coverage_files,
190193
compare_branch,
194+
diff_tool,
191195
html_report=None,
192196
css_file=None,
193197
json_report=None,
@@ -198,8 +202,6 @@ def generate_coverage_report(
198202
exclude=None,
199203
include=None,
200204
src_roots=None,
201-
diff_range_notation=None,
202-
ignore_whitespace=False,
203205
quiet=False,
204206
show_uncovered=False,
205207
):
@@ -208,7 +210,7 @@ def generate_coverage_report(
208210
"""
209211
diff = GitDiffReporter(
210212
compare_branch,
211-
git_diff=GitDiffTool(diff_range_notation, ignore_whitespace),
213+
git_diff=diff_tool,
212214
ignore_staged=ignore_staged,
213215
ignore_unstaged=ignore_unstaged,
214216
include_untracked=include_untracked,
@@ -281,9 +283,19 @@ def main(argv=None, directory=None):
281283

282284
GitPathTool.set_cwd(directory)
283285
fail_under = arg_dict.get("fail_under")
286+
diff_tool = None
287+
288+
if not arg_dict["diff_file"]:
289+
diff_tool = GitDiffTool(
290+
arg_dict["diff_range_notation"], arg_dict["ignore_whitespace"]
291+
)
292+
else:
293+
diff_tool = GitDiffFileTool(arg_dict["diff_file"])
294+
284295
percent_covered = generate_coverage_report(
285296
arg_dict["coverage_file"],
286297
arg_dict["compare_branch"],
298+
diff_tool,
287299
html_report=arg_dict["html_report"],
288300
json_report=arg_dict["json_report"],
289301
markdown_report=arg_dict["markdown_report"],
@@ -294,8 +306,6 @@ def main(argv=None, directory=None):
294306
exclude=arg_dict["exclude"],
295307
include=arg_dict["include"],
296308
src_roots=arg_dict["src_roots"],
297-
diff_range_notation=arg_dict["diff_range_notation"],
298-
ignore_whitespace=arg_dict["ignore_whitespace"],
299309
quiet=quiet,
300310
show_uncovered=arg_dict["show_uncovered"],
301311
)

diff_cover/git_diff.py

+35
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,38 @@ def untracked(self):
110110
if not output:
111111
return []
112112
return [line for line in output.splitlines() if line]
113+
114+
115+
class GitDiffFileTool(GitDiffTool):
116+
117+
def __init__(self, diff_file_path):
118+
119+
self.diff_file_path = diff_file_path
120+
super().__init__("...", False)
121+
122+
def diff_committed(self, compare_branch="origin/main"):
123+
"""
124+
Returns the contents of a diff file.
125+
126+
Raises a `GitDiffError` if the file cannot be read.
127+
"""
128+
try:
129+
with open(self.diff_file_path, "r") as file:
130+
return file.read()
131+
except IOError as e:
132+
raise ValueError(
133+
dedent(
134+
f"""
135+
Could not read the diff file. Make sure '{self.diff_file_path}' exists?
136+
"""
137+
)
138+
)
139+
140+
def diff_unstaged(self):
141+
return ""
142+
143+
def diff_staged(self):
144+
return ""
145+
146+
def untracked(self):
147+
return ""

tests/test_git_diff_file.py

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# pylint: disable=missing-function-docstring
2+
3+
"""Test for diff_cover.git_diff.GitDiffFileTool"""
4+
5+
import pytest
6+
7+
from diff_cover.git_diff import GitDiffFileTool
8+
9+
10+
@pytest.fixture
11+
def mock_file(mocker):
12+
def _inner(file_content):
13+
mock_open = mocker.mock_open(read_data=file_content)
14+
mocker.patch("builtins.open", mock_open)
15+
16+
return _inner
17+
18+
19+
@pytest.fixture
20+
def diff_tool():
21+
def _inner(file):
22+
return GitDiffFileTool(file)
23+
24+
return _inner
25+
26+
27+
def test_diff_file_not_found(mocker, diff_tool):
28+
mocker.patch("builtins.open", side_effect=IOError)
29+
30+
_diff_tool = diff_tool("non_existent_diff_file.txt")
31+
32+
with pytest.raises(ValueError) as excinfo:
33+
_diff_tool.diff_committed()
34+
35+
assert (
36+
f"Could not read the diff file. Make sure '{_diff_tool.diff_file_path}' exists?"
37+
in str(excinfo.value)
38+
)
39+
assert _diff_tool.diff_file_path == "non_existent_diff_file.txt"
40+
41+
42+
def test_large_diff_file(mock_file, diff_tool):
43+
large_diff = "diff --git a/file1 b/file2\n" * 1000000
44+
45+
mock_file(large_diff)
46+
47+
_diff_tool = diff_tool("large_diff_file.txt")
48+
49+
assert _diff_tool.diff_committed() == large_diff
50+
assert _diff_tool.diff_file_path == "large_diff_file.txt"
51+
52+
53+
def test_diff_committed(mock_file, diff_tool):
54+
diff = "diff --git a/file1 b/file2\n"
55+
56+
mock_file(diff)
57+
58+
_diff_tool = diff_tool("diff_file.txt")
59+
60+
assert _diff_tool.diff_committed() == diff
61+
assert _diff_tool.diff_file_path == "diff_file.txt"
62+
63+
64+
def test_empty_diff_file(mock_file, diff_tool):
65+
empty_diff = ""
66+
67+
mock_file(empty_diff)
68+
69+
_diff_tool = diff_tool("empty_diff.txt")
70+
71+
assert _diff_tool.diff_committed() == empty_diff
72+
assert _diff_tool.diff_file_path == "empty_diff.txt"

0 commit comments

Comments
 (0)