diff-cover 9.4.1__tar.gz → 9.6.0__tar.gz

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.
Files changed (157) hide show
  1. {diff_cover-9.4.1 → diff_cover-9.6.0}/PKG-INFO +2 -2
  2. {diff_cover-9.4.1 → diff_cover-9.6.0}/README.rst +1 -1
  3. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/diff_cover_tool.py +11 -1
  4. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/diff_quality_tool.py +2 -0
  5. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/report_generator.py +21 -0
  6. diff_cover-9.6.0/diff_cover/templates/github_coverage_annotations.txt +10 -0
  7. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/violationsreporters/violations_reporter.py +64 -0
  8. {diff_cover-9.4.1 → diff_cover-9.6.0}/pyproject.toml +1 -1
  9. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/test_integration.py +91 -11
  10. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/test_report_generator.py +59 -0
  11. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/test_violations_reporter.py +21 -0
  12. {diff_cover-9.4.1 → diff_cover-9.6.0}/LICENSE +0 -0
  13. {diff_cover-9.4.1 → diff_cover-9.6.0}/NOTICE +0 -0
  14. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/__init__.py +0 -0
  15. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/command_runner.py +0 -0
  16. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/config_parser.py +0 -0
  17. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/diff_reporter.py +0 -0
  18. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/git_diff.py +0 -0
  19. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/git_path.py +0 -0
  20. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/hook.py +0 -0
  21. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/hookspecs.py +0 -0
  22. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/snippets.py +0 -0
  23. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/templates/console_coverage_report.txt +0 -0
  24. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/templates/console_quality_report.txt +0 -0
  25. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/templates/external_style.css +0 -0
  26. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/templates/html_coverage_report.html +0 -0
  27. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/templates/html_quality_report.html +0 -0
  28. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/templates/markdown_coverage_report.md +0 -0
  29. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/templates/markdown_quality_report.md +0 -0
  30. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/templates/snippet_content.html +0 -0
  31. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/templates/snippet_content.md +0 -0
  32. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/templates/snippet_content.txt +0 -0
  33. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/templates/snippet_style.html +0 -0
  34. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/util.py +0 -0
  35. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/violationsreporters/__init__.py +0 -0
  36. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/violationsreporters/base.py +0 -0
  37. {diff_cover-9.4.1 → diff_cover-9.6.0}/diff_cover/violationsreporters/java_violations_reporter.py +0 -0
  38. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/__init__.py +0 -0
  39. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/add_console_report.txt +0 -0
  40. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/add_html_report.html +0 -0
  41. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/add_json_report.json +0 -0
  42. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/add_markdown_report.md +0 -0
  43. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/changed_console_report.txt +0 -0
  44. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/changed_html_report.html +0 -0
  45. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/coverage.xml +0 -0
  46. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/coverage1.xml +0 -0
  47. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/coverage2.xml +0 -0
  48. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/coverage_missing_lines.xml +0 -0
  49. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/cpp_functions_console_report.txt +0 -0
  50. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/cpp_functions_coverage.lcov +0 -0
  51. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/delete_console_report.txt +0 -0
  52. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/delete_html_report.html +0 -0
  53. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/dotnet_coverage.xml +0 -0
  54. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/dotnet_coverage_console_report.txt +0 -0
  55. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/empty.txt +0 -0
  56. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/empty_pycodestyle_violations.txt +0 -0
  57. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/expand_console_report.txt +0 -0
  58. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/external_css_html_report.html +0 -0
  59. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/external_style.css +0 -0
  60. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_add.txt +0 -0
  61. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_changed.txt +0 -0
  62. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_code_dupe.txt +0 -0
  63. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_cpp.txt +0 -0
  64. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_cpp_functions.txt +0 -0
  65. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_delete.txt +0 -0
  66. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_dotnet.txt +0 -0
  67. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_external_css.txt +0 -0
  68. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_lua.txt +0 -0
  69. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_moved.txt +0 -0
  70. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_mult.txt +0 -0
  71. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_python.txt +0 -0
  72. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_subdir.txt +0 -0
  73. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_typescript.txt +0 -0
  74. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_typescript_branches.txt +0 -0
  75. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_unicode.txt +0 -0
  76. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_violations.txt +0 -0
  77. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/git_diff_violations_two_files.txt +0 -0
  78. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/hello.py +0 -0
  79. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/hi.py +0 -0
  80. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/html_report.html +0 -0
  81. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/html_report_empty.html +0 -0
  82. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/html_report_one_snippet.html +0 -0
  83. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/html_report_two_snippets.html +0 -0
  84. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/lcov.info +0 -0
  85. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/lua_console_report.txt +0 -0
  86. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/luacoverage.xml +0 -0
  87. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/markdown_report_one_snippet.md +0 -0
  88. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/markdown_report_two_snippets.md +0 -0
  89. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/moved_console_report.txt +0 -0
  90. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/moved_coverage.xml +0 -0
  91. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/moved_html_report.html +0 -0
  92. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/mult_inputs_console_report.txt +0 -0
  93. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/mult_inputs_html_report.html +0 -0
  94. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/pycodestyle_report.txt +0 -0
  95. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/pycodestyle_violations_report.html +0 -0
  96. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/pycodestyle_violations_report.json +0 -0
  97. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/pycodestyle_violations_report.md +0 -0
  98. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/pycodestyle_violations_report.txt +0 -0
  99. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/pycodestyle_violations_report_external_css.html +0 -0
  100. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/pyflakes_two_files.txt +0 -0
  101. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/pyflakes_violations_report.html +0 -0
  102. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/pyflakes_violations_report.txt +0 -0
  103. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/pylint_dupe.txt +0 -0
  104. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/pylint_dupe_violations_report.txt +0 -0
  105. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/pylint_report.txt +0 -0
  106. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/pylint_violations_console_report.txt +0 -0
  107. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/pylint_violations_report.html +0 -0
  108. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/pylint_violations_report.txt +0 -0
  109. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/pylintrc +0 -0
  110. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/real_cpp_console_report.txt +0 -0
  111. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/real_cpp_coverage.lcov +0 -0
  112. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/real_python_console_report.txt +0 -0
  113. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/real_python_coverage.lcov +0 -0
  114. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/real_typescript_console_report.txt +0 -0
  115. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/real_typescript_coverage.lcov +0 -0
  116. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/show_uncovered_lines_console.txt +0 -0
  117. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/snippet.css +0 -0
  118. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/snippet_8859.py +0 -0
  119. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/snippet_arabic_output.html +0 -0
  120. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/snippet_default.html +0 -0
  121. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/snippet_invalid_violations.html +0 -0
  122. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/snippet_list.html +0 -0
  123. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/snippet_list.md +0 -0
  124. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/snippet_list2.md +0 -0
  125. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/snippet_list3.md +0 -0
  126. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/snippet_no_filename_ext.html +0 -0
  127. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/snippet_src.py +0 -0
  128. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/snippet_src2.cpp +0 -0
  129. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/snippet_src3.cpp +0 -0
  130. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/snippet_unicode.html +0 -0
  131. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/snippet_unicode.py +0 -0
  132. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/snippet_unicode_html_output.html +0 -0
  133. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/subdir_coverage_console_report.txt +0 -0
  134. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/subdir_coverage_html_report.html +0 -0
  135. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/test_src.txt +0 -0
  136. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/typescript_branches_console_report.txt +0 -0
  137. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/typescript_branches_coverage.lcov +0 -0
  138. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/unicode_console_report.txt +0 -0
  139. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/unicode_coverage.xml +0 -0
  140. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/unicode_html_report.html +0 -0
  141. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/unicode_test_src.txt +0 -0
  142. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/fixtures/violations_test_file.py +0 -0
  143. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/helpers.py +0 -0
  144. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/snippet_list_unicode.html +0 -0
  145. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/test_clover_violations_reporter/test.xml +0 -0
  146. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/test_clover_violations_reporter.py +0 -0
  147. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/test_config_parser.py +0 -0
  148. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/test_diff_cover_main.py +0 -0
  149. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/test_diff_cover_tool.py +0 -0
  150. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/test_diff_quality_main.py +0 -0
  151. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/test_diff_reporter.py +0 -0
  152. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/test_git_diff.py +0 -0
  153. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/test_git_diff_file.py +0 -0
  154. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/test_git_path.py +0 -0
  155. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/test_java_violations_reporter.py +0 -0
  156. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/test_snippets.py +0 -0
  157. {diff_cover-9.4.1 → diff_cover-9.6.0}/tests/test_util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: diff_cover
3
- Version: 9.4.1
3
+ Version: 9.6.0
4
4
  Summary: Run coverage and linting reports on diffs
5
5
  License: Apache-2.0
6
6
  Author: See Contributors
@@ -161,7 +161,7 @@ You can use diff-cover to see quality reports on the diff as well by running
161
161
  diff-quality --violations=<tool>
162
162
 
163
163
  Where ``tool`` is the quality checker to use. Currently ``pycodestyle``, ``pyflakes``,
164
- ``flake8``, ``pylint``, ``checkstyle``, ``checkstylexml``, ``ruff check`` are supported, but more
164
+ ``flake8``, ``pylint``, ``checkstyle``, ``checkstylexml``, ``ruff.check``, ``clang`` are supported, but more
165
165
  checkers can (and should!) be supported. See the section "Adding `diff-quality``
166
166
  Support for a New Quality Checker".
167
167
 
@@ -129,7 +129,7 @@ You can use diff-cover to see quality reports on the diff as well by running
129
129
  diff-quality --violations=<tool>
130
130
 
131
131
  Where ``tool`` is the quality checker to use. Currently ``pycodestyle``, ``pyflakes``,
132
- ``flake8``, ``pylint``, ``checkstyle``, ``checkstylexml``, ``ruff check`` are supported, but more
132
+ ``flake8``, ``pylint``, ``checkstyle``, ``checkstylexml``, ``ruff.check``, ``clang`` are supported, but more
133
133
  checkers can (and should!) be supported. See the section "Adding `diff-quality``
134
134
  Support for a New Quality Checker".
135
135
 
@@ -12,6 +12,7 @@ from diff_cover.diff_reporter import GitDiffReporter
12
12
  from diff_cover.git_diff import GitDiffFileTool, GitDiffTool
13
13
  from diff_cover.git_path import GitPathTool
14
14
  from diff_cover.report_generator import (
15
+ GitHubAnnotationsReportGenerator,
15
16
  HtmlReportGenerator,
16
17
  JsonReportGenerator,
17
18
  MarkdownReportGenerator,
@@ -266,9 +267,18 @@ def generate_coverage_report(
266
267
  if "markdown" in report_formats:
267
268
  markdown_report = report_formats["markdown"] or MARKDOWN_REPORT_DEFAULT_PATH
268
269
  reporter = MarkdownReportGenerator(coverage, diff)
269
- with open(markdown_report, "wb") as output_file:
270
+ with open_file(markdown_report, "wb") as output_file:
270
271
  reporter.generate_report(output_file)
271
272
 
273
+ if "github-annotations" in report_formats:
274
+ # Github annotations are always written to stdout, but we can use different types
275
+ reporter = GitHubAnnotationsReportGenerator(
276
+ coverage,
277
+ diff,
278
+ report_formats["github-annotations"],
279
+ )
280
+ reporter.generate_report(sys.stdout.buffer)
281
+
272
282
  # Generate the report for stdout
273
283
  reporter = StringReportGenerator(coverage, diff, show_uncovered)
274
284
  output_file = io.BytesIO() if quiet else sys.stdout.buffer
@@ -50,6 +50,7 @@ from diff_cover.violationsreporters.java_violations_reporter import (
50
50
  checkstyle_driver,
51
51
  )
52
52
  from diff_cover.violationsreporters.violations_reporter import (
53
+ ClangFormatDriver,
53
54
  CppcheckDriver,
54
55
  EslintDriver,
55
56
  PylintDriver,
@@ -63,6 +64,7 @@ from diff_cover.violationsreporters.violations_reporter import (
63
64
  )
64
65
 
65
66
  QUALITY_DRIVERS = {
67
+ "clang": ClangFormatDriver(),
66
68
  "cppcheck": CppcheckDriver(),
67
69
  "pycodestyle": pycodestyle_driver,
68
70
  "pyflakes": pyflakes_driver,
@@ -420,6 +420,27 @@ class StringReportGenerator(TemplateReportGenerator):
420
420
  self.include_snippets = show_uncovered
421
421
 
422
422
 
423
+ class GitHubAnnotationsReportGenerator(TemplateReportGenerator):
424
+ """
425
+ Generate a diff coverage report for GitHub annotations.
426
+ https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-debug-message
427
+ https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-notice-message
428
+ https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message
429
+ https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message
430
+ """
431
+
432
+ template_path = "github_coverage_annotations.txt"
433
+
434
+ def __init__(self, violations_reporter, diff_reporter, annotations_type):
435
+ super().__init__(violations_reporter, diff_reporter)
436
+ self.annotations_type = annotations_type
437
+
438
+ def _context(self):
439
+ context = super().report_dict()
440
+ context.update({"annotations_type": self.annotations_type})
441
+ return context
442
+
443
+
423
444
  class HtmlReportGenerator(TemplateReportGenerator):
424
445
  """
425
446
  Generate an HTML formatted diff coverage report.
@@ -0,0 +1,10 @@
1
+ {% if src_stats %}
2
+ {% for src_path, stats in src_stats|dictsort %}
3
+ {% if stats.percent_covered < 100 %}
4
+ {% for line in stats.violation_lines %}
5
+ {% set splitLines = line.split("-") %}
6
+ ::{{ annotations_type }} file={{ src_path }},line={{ splitLines[0] }}{% if splitLines[1] %},endLine={{ splitLines[1] }}{% endif %},title=Missing Coverage::Line {{ line }} missing coverage
7
+ {% endfor %}
8
+ {% endif %}
9
+ {% endfor %}
10
+ {% endif %}
@@ -831,3 +831,67 @@ class CppcheckDriver(QualityDriver):
831
831
  Returns: boolean True if installed
832
832
  """
833
833
  return run_command_for_code(self.command_to_check_install) == 0
834
+
835
+
836
+ class ClangFormatDriver(QualityDriver):
837
+ """
838
+ Driver for clang-format
839
+ """
840
+
841
+ def __init__(self):
842
+ """
843
+ args:
844
+ expression: regex used to parse report
845
+ See super for other args
846
+ """
847
+ super().__init__(
848
+ "clang",
849
+ ["c", "cpp", "h", "hpp"],
850
+ ["clang-format", "--dry-run"], # Use dry-run option to not modify files
851
+ output_stderr=True,
852
+ )
853
+
854
+ # Errors look like:
855
+ # src/foo.c:8:1: warning: code should be clang-formatted [-Wclang-format-violations]
856
+ # int foo;
857
+ # ^
858
+ # Match for everything, including ":" in the file name (first capturing
859
+ # group), in case there are pathological path names with ":"
860
+ self.clang_expression = re.compile(
861
+ r"^(.*?):(\d+):\d+: ([^[]* \[[^]]*\])\n(.+)\n(.+)$",
862
+ re.MULTILINE,
863
+ )
864
+ self.command_to_check_install = ["clang-format", "--version"]
865
+
866
+ def parse_reports(self, reports):
867
+ """
868
+ Args:
869
+ reports: list[str] - output from the report
870
+ Return:
871
+ A dict[Str:Violation]
872
+ Violation is a simple named tuple Defined above
873
+ """
874
+ violations_dict = defaultdict(list)
875
+ for report in reports:
876
+
877
+ matches = list(re.finditer(self.clang_expression, report))
878
+ for match in matches:
879
+ if match is not None:
880
+ (
881
+ clang_src_path,
882
+ line_number,
883
+ message,
884
+ code_extract,
885
+ cursor_error,
886
+ ) = match.groups()
887
+ full_message = f"{message}\n{code_extract}\n{cursor_error}"
888
+ violation = Violation(int(line_number), full_message)
889
+ violations_dict[clang_src_path].append(violation)
890
+ return violations_dict
891
+
892
+ def installed(self):
893
+ """
894
+ Method checks if the provided tool is installed.
895
+ Returns: boolean True if installed
896
+ """
897
+ return run_command_for_code(self.command_to_check_install) == 0
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "diff_cover"
3
- version = "9.4.1"
3
+ version = "9.6.0"
4
4
  description = "Run coverage and linting reports on diffs"
5
5
  authors = ["See Contributors"]
6
6
  homepage = "https://github.com/Bachmann1234/diff-cover"
@@ -2,12 +2,12 @@
2
2
  # pylint: disable=use-implicit-booleaness-not-comparison
3
3
 
4
4
  """High-level integration tests of diff-cover tool."""
5
-
6
5
  import json
7
6
  import os
8
7
  import os.path
9
8
  import re
10
9
  import shutil
10
+ import textwrap
11
11
  from collections import defaultdict
12
12
  from pathlib import Path
13
13
  from subprocess import Popen
@@ -96,21 +96,23 @@ def patch_git_command(patch_popen, mocker):
96
96
  return helper
97
97
 
98
98
 
99
+ def clean_html(html, clear_inline_css):
100
+ if clear_inline_css:
101
+ html = re.sub("<style>.*</style>", "", html, flags=re.DOTALL)
102
+ return html.strip()
103
+
104
+
99
105
  def compare_html(expected_html_path, html_report_path, clear_inline_css=True):
100
- clean_content = re.compile("<style>.*</style>", flags=re.DOTALL)
101
106
  expected_file = open(expected_html_path, encoding="utf-8")
102
107
  html_report = open(html_report_path, encoding="utf-8")
103
108
 
104
109
  with expected_file, html_report:
105
- html = html_report.read()
106
- expected = expected_file.read()
107
- if clear_inline_css:
108
- # The CSS is provided by pygments and changes fairly often.
109
- # Im ok with simply saying "There was css"
110
- # Perhaps I will eat these words
111
- html = clean_content.sub("", html)
112
- expected = clean_content.sub("", expected)
113
- assert expected.strip() == html.strip()
110
+ # The CSS is provided by pygments and changes fairly often.
111
+ # Im ok with simply saying "There was css"
112
+ # Perhaps I will eat these words
113
+ html = clean_html(html_report.read(), clear_inline_css)
114
+ expected = clean_html(expected_file.read(), clear_inline_css)
115
+ assert expected == html
114
116
 
115
117
 
116
118
  def compare_markdown(expected_file_path, actual_file_path):
@@ -179,6 +181,36 @@ class TestDiffCoverIntegration:
179
181
  compare_json("add_json_report.json", "dummy/diff_coverage.json")
180
182
  compare_markdown("add_markdown_report.md", "dummy/diff_coverage.md")
181
183
 
184
+ def test_all_reports_with_stdout(self, runbin, patch_git_command, capsys):
185
+ patch_git_command.set_stdout("git_diff_add.txt")
186
+ assert (
187
+ runbin(
188
+ [
189
+ "coverage.xml",
190
+ "--format",
191
+ "html:-,json:-,markdown:-",
192
+ ]
193
+ )
194
+ == 0
195
+ )
196
+ output = capsys.readouterr().out
197
+ with open("add_console_report.txt", "r", encoding="utf-8") as f:
198
+ actual = f.read()
199
+ assert actual in output
200
+ with open("add_html_report.html", "r", encoding="utf-8") as f:
201
+ actual = clean_html(f.read(), clear_inline_css=True)
202
+ # can't compare the output inside <head> since it changes
203
+ # a lot so we just compare the body
204
+ actual = actual[actual.find("</head>") :]
205
+ assert len(actual) > 20
206
+ assert actual in output
207
+ with open("add_json_report.json", "r", encoding="utf-8") as f:
208
+ actual = json.dumps(json.loads(f.read()))
209
+ assert actual in output
210
+ with open("add_markdown_report.md", "r", encoding="utf-8") as f:
211
+ actual = f.read()
212
+ assert actual in output
213
+
182
214
  def test_added_file_console_lcov(self, runbin, patch_git_command, capsys):
183
215
  patch_git_command.set_stdout("git_diff_add.txt")
184
216
  assert runbin(["lcov.info"]) == 0
@@ -397,6 +429,54 @@ class TestDiffCoverIntegration:
397
429
  assert runbin(["coverage_missing_lines.xml", "--expand-coverage-report"]) == 0
398
430
  compare_console("expand_console_report.txt", capsys.readouterr().out)
399
431
 
432
+ def test_github_silent(self, runbin, patch_git_command, capsys):
433
+ patch_git_command.set_stdout("git_diff_add.txt")
434
+ assert (
435
+ runbin(["coverage.xml", "--format", "github-annotations:notice", "-q"]) == 0
436
+ )
437
+ expected = textwrap.dedent(
438
+ """\
439
+ ::notice file=test_src.txt,line=2,title=Missing Coverage::Line 2 missing coverage
440
+ ::notice file=test_src.txt,line=4,title=Missing Coverage::Line 4 missing coverage
441
+ ::notice file=test_src.txt,line=6,title=Missing Coverage::Line 6 missing coverage
442
+ ::notice file=test_src.txt,line=8,title=Missing Coverage::Line 8 missing coverage
443
+ ::notice file=test_src.txt,line=10,title=Missing Coverage::Line 10 missing coverage
444
+ """
445
+ )
446
+ assert capsys.readouterr().out == expected
447
+
448
+ @pytest.mark.usefixtures("patch_git_command")
449
+ def test_github_fully_covered(self, runbin, capsys):
450
+ assert runbin(["coverage2.xml", "--format", "github-annotations:notice"]) == 0
451
+ expected = textwrap.dedent(
452
+ """\
453
+ -------------
454
+ Diff Coverage
455
+ Diff: origin/main...HEAD, staged and unstaged changes
456
+ -------------
457
+ No lines with coverage information in this diff.
458
+ -------------
459
+
460
+ """
461
+ )
462
+ assert capsys.readouterr().out == expected
463
+
464
+ def test_github_empty_diff(self, runbin, patch_git_command, capsys):
465
+ patch_git_command.set_stdout("")
466
+ assert runbin(["coverage.xml", "--format", "github-annotations:notice"]) == 0
467
+ expected = textwrap.dedent(
468
+ """\
469
+ -------------
470
+ Diff Coverage
471
+ Diff: origin/main...HEAD, staged and unstaged changes
472
+ -------------
473
+ No lines with coverage information in this diff.
474
+ -------------
475
+
476
+ """
477
+ )
478
+ assert capsys.readouterr().out == expected
479
+
400
480
  def test_real_world_cpp_lcov_coverage(self, runbin, patch_git_command, capsys):
401
481
  """Test with real C++ LCOV coverage data"""
402
482
  # Create git diff for C++ files
@@ -10,6 +10,7 @@ import pytest
10
10
  from diff_cover.diff_reporter import BaseDiffReporter
11
11
  from diff_cover.report_generator import (
12
12
  BaseReportGenerator,
13
+ GitHubAnnotationsReportGenerator,
13
14
  HtmlReportGenerator,
14
15
  JsonReportGenerator,
15
16
  MarkdownReportGenerator,
@@ -407,6 +408,64 @@ class TestStringReportGenerator(BaseReportGeneratorTest):
407
408
  self.assert_report(expected)
408
409
 
409
410
 
411
+ class TestGitHubAnnotationsReportGenerator(BaseReportGeneratorTest):
412
+
413
+ @pytest.fixture
414
+ def report(self, coverage, diff):
415
+ # Create a concrete instance of a report generator
416
+ return GitHubAnnotationsReportGenerator(coverage, diff, "warning")
417
+
418
+ @pytest.mark.usefixtures("use_default_values")
419
+ def test_generate_report(self):
420
+ # Verify that we got the expected string
421
+ expected = dedent(
422
+ """
423
+ ::warning file=file1.py,line=10,endLine=11,title=Missing Coverage::Line 10-11 missing coverage
424
+ ::warning file=subdir/file2.py,line=10,endLine=11,title=Missing Coverage::Line 10-11 missing coverage
425
+ """
426
+ ).strip()
427
+
428
+ self.assert_report(expected)
429
+
430
+ def test_single_line(
431
+ self, diff, diff_lines_changed, coverage_violations, coverage_measured_lines
432
+ ):
433
+ diff.src_paths_changed.return_value = ["file.py"]
434
+ diff_lines_changed.update({"file.py": list(range(100))})
435
+ coverage_violations.update({"file.py": [Violation(10, None)]})
436
+ coverage_measured_lines.update({"file.py": [2]})
437
+
438
+ # Verify that we got the expected string
439
+ expected = dedent(
440
+ """
441
+ ::warning file=file.py,line=10,title=Missing Coverage::Line 10 missing coverage
442
+ """
443
+ ).strip()
444
+
445
+ self.assert_report(expected)
446
+
447
+ def test_hundred_percent(
448
+ self, diff, diff_lines_changed, coverage_violations, coverage_measured_lines
449
+ ):
450
+ # Have the dependencies return an empty report
451
+ diff.src_paths_changed.return_value = ["file.py"]
452
+ diff_lines_changed.update({"file.py": list(range(100))})
453
+ coverage_violations.update({"file.py": []})
454
+ coverage_measured_lines.update({"file.py": [2]})
455
+
456
+ expected = ""
457
+
458
+ self.assert_report(expected)
459
+
460
+ def test_empty_report(self):
461
+ # Have the dependencies return an empty report
462
+ # (this is the default)
463
+
464
+ expected = ""
465
+
466
+ self.assert_report(expected)
467
+
468
+
410
469
  class TestHtmlReportGenerator(BaseReportGeneratorTest):
411
470
 
412
471
  @pytest.fixture
@@ -17,6 +17,7 @@ from diff_cover.command_runner import CommandError, run_command_for_code
17
17
  from diff_cover.violationsreporters import base
18
18
  from diff_cover.violationsreporters.base import QualityReporter
19
19
  from diff_cover.violationsreporters.violations_reporter import (
20
+ ClangFormatDriver,
20
21
  CppcheckDriver,
21
22
  EslintDriver,
22
23
  LcovCoverageReporter,
@@ -2238,3 +2239,23 @@ class TestRuffCheckQualityDriverTest:
2238
2239
 
2239
2240
  assert quality.name() == "ruff.check"
2240
2241
  assert actual_violations == expected_violations
2242
+
2243
+
2244
+ class TestClangFormatcheckQualityDriverTest:
2245
+ """Tests for clang-format quality driver."""
2246
+
2247
+ def test_parse_report(self):
2248
+ """Basic report test parse"""
2249
+ expected_violations = {
2250
+ "src/foo.c": [
2251
+ Violation(
2252
+ 12,
2253
+ "warning: code should be clang-formatted [-Wclang-format-violations]\n int bar;\n ^",
2254
+ ),
2255
+ ]
2256
+ }
2257
+ report = "src/foo.c:12:1: warning: code should be clang-formatted [-Wclang-format-violations]\n int bar;\n ^"
2258
+
2259
+ driver = ClangFormatDriver()
2260
+ actual_violations = driver.parse_reports([report])
2261
+ assert actual_violations == expected_violations
File without changes
File without changes
File without changes
File without changes