Skip to content

Commit 99c73c5

Browse files
Fix SonarQube double indexing in flat layout by excluding tests from sources (#1091)
* Initial plan * Fix SonarQube config to exclude tests dir in flat layout Co-authored-by: nathanjmcdougall <18602289+nathanjmcdougall@users.noreply.github.com> * Address PR feedback: remove redundant cast, prepend ./tests to exclusions Co-authored-by: nathanjmcdougall <18602289+nathanjmcdougall@users.noreply.github.com> * Change exclusion syntax from ./tests to tests/* Co-authored-by: nathanjmcdougall <18602289+nathanjmcdougall@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: nathanjmcdougall <18602289+nathanjmcdougall@users.noreply.github.com> Co-authored-by: Nathan McDougall <nathan.j.mcdougall@gmail.com>
1 parent 73f05e1 commit 99c73c5

2 files changed

Lines changed: 49 additions & 2 deletions

File tree

src/usethis/_integrations/sonarqube/config.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class _NonstandardPythonVersionError(Exception):
2020
"""Raised when a non-standard Python version is detected."""
2121

2222

23-
def get_sonar_project_properties() -> str:
23+
def get_sonar_project_properties() -> str: # noqa: PLR0912
2424
"""Get contents for (or from) the sonar-project.properties file."""
2525
path = usethis_config.cpd() / "sonar-project.properties"
2626
if path.exists() and path.is_file():
@@ -59,6 +59,7 @@ def get_sonar_project_properties() -> str:
5959
]
6060
except (FileNotFoundError, KeyError):
6161
exclusions = []
62+
# TypeAdapter(list).validate_python() ensures we have a list and returns a new list
6263
exclusions = TypeAdapter(list).validate_python(exclusions)
6364
for exclusion in exclusions:
6465
TypeAdapter(str).validate_python(exclusion)
@@ -77,6 +78,9 @@ def get_sonar_project_properties() -> str:
7778
source_dir_str = get_source_dir_str()
7879
if source_dir_str == ".":
7980
sources = "./"
81+
# When using flat layout, exclude tests directory to avoid double indexing
82+
if "tests/*" not in exclusions:
83+
exclusions.insert(0, "tests/*")
8084
else:
8185
sources = f"./{source_dir_str}"
8286

tests/usethis/_integrations/sonarqube/test_sonarqube_config.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ def test_different_python_version(self, tmp_path: Path):
100100
sonar.tests=./tests
101101
sonar.python.coverage.reportPaths=coverage.xml
102102
sonar.verbose=false
103+
sonar.exclusions=tests/*
103104
"""
104105
)
105106

@@ -128,6 +129,7 @@ def test_no_pin_python(self, tmp_path: Path):
128129
sonar.tests=./tests
129130
sonar.python.coverage.reportPaths=coverage.xml
130131
sonar.verbose=false
132+
sonar.exclusions=tests/*
131133
"""
132134
)
133135

@@ -157,6 +159,7 @@ def test_different_project_key(self, tmp_path: Path):
157159
sonar.tests=./tests
158160
sonar.python.coverage.reportPaths=coverage.xml
159161
sonar.verbose=false
162+
sonar.exclusions=tests/*
160163
"""
161164
)
162165

@@ -189,6 +192,7 @@ def test_set_verbose_true(self, tmp_path: Path):
189192
sonar.tests=./tests
190193
sonar.python.coverage.reportPaths=coverage.xml
191194
sonar.verbose=true
195+
sonar.exclusions=tests/*
192196
"""
193197
)
194198

@@ -235,6 +239,7 @@ def test_patch_version_ignored(self, tmp_path: Path):
235239
sonar.tests=./tests
236240
sonar.python.coverage.reportPaths=coverage.xml
237241
sonar.verbose=false
242+
sonar.exclusions=tests/*
238243
"""
239244
)
240245

@@ -271,7 +276,7 @@ def test_exclusions(self, tmp_path: Path):
271276
sonar.tests=./tests
272277
sonar.python.coverage.reportPaths=coverage.xml
273278
sonar.verbose=false
274-
sonar.exclusions=**/Dockerfile, src/notebooks/**/*
279+
sonar.exclusions=tests/*, **/Dockerfile, src/notebooks/**/*
275280
"""
276281
)
277282

@@ -302,6 +307,7 @@ def test_different_coverage_file_location(self, tmp_path: Path):
302307
sonar.tests=./tests
303308
sonar.python.coverage.reportPaths=./test-reports/cov.xml
304309
sonar.verbose=false
310+
sonar.exclusions=tests/*
305311
"""
306312
)
307313

@@ -318,6 +324,43 @@ def test_missing_coverage_file_location_error(self, tmp_path: Path):
318324
with pytest.raises(CoverageReportConfigNotFoundError):
319325
get_sonar_project_properties()
320326

327+
def test_flat_layout_exclusions_already_has_tests(self, tmp_path: Path):
328+
# When using flat layout and tests/* is already in exclusions,
329+
# it should not be added again.
330+
331+
with change_cwd(tmp_path), PyprojectTOMLManager():
332+
# Arrange
333+
uv_python_pin("3.12")
334+
ensure_pyproject_toml()
335+
PyprojectTOMLManager().set_value(
336+
keys=["tool", "usethis", "sonarqube", "project-key"], value="foobar"
337+
)
338+
PyprojectTOMLManager().set_value(
339+
keys=["tool", "usethis", "sonarqube", "exclusions"],
340+
value=["tests/*", "**/Dockerfile"],
341+
)
342+
PyprojectTOMLManager().set_value(
343+
keys=["tool", "coverage", "xml", "output"], value="coverage.xml"
344+
)
345+
346+
# Act
347+
result = get_sonar_project_properties()
348+
349+
# Assert
350+
assert (
351+
result
352+
== """\
353+
sonar.projectKey=foobar
354+
sonar.language=py
355+
sonar.python.version=3.12
356+
sonar.sources=./
357+
sonar.tests=./tests
358+
sonar.python.coverage.reportPaths=coverage.xml
359+
sonar.verbose=false
360+
sonar.exclusions=tests/*, **/Dockerfile
361+
"""
362+
)
363+
321364

322365
class TestValidateProjectKey:
323366
def test_valid(self):

0 commit comments

Comments
 (0)