1717# along with this program; if not, write to the Free Software Foundation,
1818# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1919#
20+ import unittest
21+ from unittest .mock import patch
2022
21- from unittest .mock import patch , call
22- from pyfakefs import fake_filesystem_unittest as pyfakefs
23+ import pyfakefs .fake_filesystem_unittest as pyfakefs
2324
2425from pysonar_scanner .__main__ import run_dry_run
2526from pysonar_scanner .configuration .properties import (
3940)
4041
4142
42- class TestValidationResult :
43+ class TestValidationResult ( unittest . TestCase ) :
4344
4445 def test_valid_when_no_errors (self ):
4546 result = ValidationResult ()
46- assert result .is_valid ()
47- assert len (result .errors ) == 0
48- assert len (result .warnings ) == 0
47+ self . assertTrue ( result .is_valid () )
48+ self . assertEqual ( len (result .errors ), 0 )
49+ self . assertEqual ( len (result .warnings ), 0 )
4950
5051 def test_invalid_when_errors_present (self ):
5152 result = ValidationResult ()
5253 result .add_error ("Test error" )
53- assert not result .is_valid ()
54- assert len (result .errors ) == 1
54+ self . assertFalse ( result .is_valid () )
55+ self . assertEqual ( len (result .errors ), 1 )
5556
5657 def test_can_add_warnings_without_becoming_invalid (self ):
5758 result = ValidationResult ()
5859 result .add_warning ("Test warning" )
59- assert result .is_valid ()
60- assert len (result .warnings ) == 1
60+ self .assertTrue (result .is_valid ())
61+ self .assertEqual (len (result .warnings ), 1 )
62+
63+ def test_can_add_infos (self ):
64+ result = ValidationResult ()
65+ result .add_info ("Test info" )
66+ self .assertTrue (result .is_valid ())
67+ self .assertEqual (len (result .infos ), 1 )
6168
6269 def test_multiple_errors_and_warnings (self ):
6370 result = ValidationResult ()
6471 result .add_error ("Error 1" )
6572 result .add_error ("Error 2" )
6673 result .add_warning ("Warning 1" )
67- assert not result .is_valid ()
68- assert len (result .errors ) == 2
69- assert len (result .warnings ) == 1
74+ self . assertFalse ( result .is_valid () )
75+ self . assertEqual ( len (result .errors ), 2 )
76+ self . assertEqual ( len (result .warnings ), 1 )
7077
7178
72- class TestDryRunReporter :
79+ class TestDryRunReporter ( unittest . TestCase ) :
7380
7481 @patch ("pysonar_scanner.dry_run_reporter.logging" )
7582 def test_report_configuration_logs_all_sections (self , mock_logging ):
@@ -87,90 +94,109 @@ def test_report_configuration_logs_all_sections(self, mock_logging):
8794
8895 logged_messages = [str (c ) for c in mock_logging .info .call_args_list ]
8996 joined = " " .join (logged_messages )
90- assert "DRY RUN MODE - Configuration Report" in joined
91- assert "my-project" in joined
92- assert "My Project" in joined
93- assert "my-org" in joined
94- assert "src" in joined
95- assert "tests" in joined
96- assert "coverage.xml" in joined
97- assert "https://sonarqube.example.com" in joined
97+ self . assertIn ( "DRY RUN MODE - Configuration Report" , joined )
98+ self . assertIn ( "my-project" , joined )
99+ self . assertIn ( "My Project" , joined )
100+ self . assertIn ( "my-org" , joined )
101+ self . assertIn ( "src" , joined )
102+ self . assertIn ( "tests" , joined )
103+ self . assertIn ( "coverage.xml" , joined )
104+ self . assertIn ( "https://sonarqube.example.com" , joined )
98105
99106 @patch ("pysonar_scanner.dry_run_reporter.logging" )
100107 def test_report_configuration_shows_na_for_missing_values (self , mock_logging ):
101- config = {}
102-
103- DryRunReporter .report_configuration (config )
108+ DryRunReporter .report_configuration ({})
104109
105110 logged_messages = [str (c ) for c in mock_logging .info .call_args_list ]
106111 joined = " " .join (logged_messages )
107- assert "N/A" in joined
112+ self . assertIn ( "N/A" , joined )
108113
109114 @patch ("pysonar_scanner.dry_run_reporter.logging" )
110115 def test_report_validation_results_valid (self , mock_logging ):
111116 result = ValidationResult ()
112117 exit_code = DryRunReporter .report_validation_results (result )
113118
114- assert exit_code == 0
119+ self . assertEqual ( exit_code , 0 )
115120 logged_messages = [str (c ) for c in mock_logging .info .call_args_list ]
116121 joined = " " .join (logged_messages )
117- assert "PASSED" in joined
122+ self .assertIn ("PASSED" , joined )
123+
124+ @patch ("pysonar_scanner.dry_run_reporter.logging" )
125+ def test_report_validation_results_valid_with_infos_and_warnings (self , mock_logging ):
126+ result = ValidationResult ()
127+ result .add_info ("Coverage report check passed: coverage.xml" )
128+ result .add_warning ("No tests directory specified" )
129+ exit_code = DryRunReporter .report_validation_results (result )
130+
131+ self .assertEqual (exit_code , 0 )
132+ logged_messages = [str (c ) for c in mock_logging .info .call_args_list ]
133+ self .assertIn ("PASSED" , " " .join (logged_messages ))
134+ mock_logging .warning .assert_called ()
118135
119136 @patch ("pysonar_scanner.dry_run_reporter.logging" )
120137 def test_report_validation_results_invalid (self , mock_logging ):
121138 result = ValidationResult ()
122139 result .add_error ("Coverage file not found" )
140+ result .add_warning ("Unexpected root element" )
123141 exit_code = DryRunReporter .report_validation_results (result )
124142
125- assert exit_code == 1
143+ self . assertEqual ( exit_code , 1 )
126144 mock_logging .warning .assert_called ()
127145 mock_logging .error .assert_called ()
128146
129147 def test_format_key_handles_camel_case (self ):
130- assert DryRunReporter ._format_key ("sonar.projectKey" ) == "Project Key"
131- assert DryRunReporter ._format_key ("sonar.projectName" ) == "Project Name"
148+ self . assertEqual ( DryRunReporter ._format_key ("sonar.projectKey" ), "Project Key" )
149+ self . assertEqual ( DryRunReporter ._format_key ("sonar.projectName" ), "Project Name" )
132150
133151 def test_format_key_handles_dotted_paths (self ):
134- assert DryRunReporter ._format_key ("sonar.host.url" ) == "Host Url"
135- assert DryRunReporter ._format_key ("sonar.python.coverage.reportPaths" ) == "Python Coverage Report Paths"
152+ self .assertEqual (DryRunReporter ._format_key ("sonar.host.url" ), "Host Url" )
153+ self .assertEqual (
154+ DryRunReporter ._format_key ("sonar.python.coverage.reportPaths" ), "Python Coverage Report Paths"
155+ )
136156
137157 def test_format_key_handles_simple_keys (self ):
138- assert DryRunReporter ._format_key ("sonar.sources" ) == "Sources"
139- assert DryRunReporter ._format_key ("sonar.organization" ) == "Organization"
158+ self . assertEqual ( DryRunReporter ._format_key ("sonar.sources" ), "Sources" )
159+ self . assertEqual ( DryRunReporter ._format_key ("sonar.organization" ), "Organization" )
140160
141161
142162class TestCoverageReportValidator (pyfakefs .TestCase ):
143163
144164 def setUp (self ):
145165 self .setUpPyfakefs ()
146166
147- def test_validate_coverage_reports_no_paths (self ):
167+ def test_validate_no_paths (self ):
148168 result = CoverageReportValidator .validate_coverage_reports (None , "." )
149169
150- assert result .is_valid ()
151- assert len (result .warnings ) == 1
152- assert "No coverage report paths specified" in result .warnings [0 ]
170+ self .assertTrue (result .is_valid ())
171+ self .assertEqual (len (result .warnings ), 1 )
172+ self .assertIn ("No coverage report paths specified" , result .warnings [0 ])
173+
174+ def test_validate_empty_string_paths (self ):
175+ result = CoverageReportValidator .validate_coverage_reports ("" , "." )
176+
177+ self .assertTrue (result .is_valid ())
178+ self .assertEqual (len (result .warnings ), 1 )
179+ self .assertIn ("No coverage report paths specified" , result .warnings [0 ])
153180
154181 def test_validate_single_report_file_not_found (self ):
155182 self .fs .create_dir ("/project" )
156183
157184 result = CoverageReportValidator .validate_coverage_reports ("coverage.xml" , "/project" )
158185
159- assert not result .is_valid ()
160- assert len (result .errors ) == 1
161- assert "not found" in result .errors [0 ]
186+ self . assertFalse ( result .is_valid () )
187+ self . assertEqual ( len (result .errors ), 1 )
188+ self . assertIn ( "not found" , result .errors [0 ])
162189
163- @patch ("pysonar_scanner.dry_run_reporter.logging" )
164- def test_validate_single_report_valid_cobertura (self , mock_logging ):
190+ def test_validate_single_report_valid_cobertura (self ):
165191 self .fs .create_dir ("/project" )
166192 self .fs .create_file ("/project/coverage.xml" , contents = '<?xml version="1.0"?>\n <coverage></coverage>' )
167193
168194 result = CoverageReportValidator .validate_coverage_reports ("coverage.xml" , "/project" )
169195
170- assert result .is_valid ()
171- assert len (result .warnings ) == 0
172- assert len (result .infos ) == 1
173- assert "Coverage report check passed" in result .infos [0 ]
196+ self . assertTrue ( result .is_valid () )
197+ self . assertEqual ( len (result .warnings ), 0 )
198+ self . assertEqual ( len (result .infos ), 1 )
199+ self . assertIn ( "Coverage report check passed" , result .infos [0 ])
174200
175201 def test_validate_multiple_coverage_reports (self ):
176202 self .fs .create_dir ("/project" )
@@ -179,46 +205,66 @@ def test_validate_multiple_coverage_reports(self):
179205
180206 result = CoverageReportValidator .validate_coverage_reports ("coverage1.xml, coverage2.xml" , "/project" )
181207
182- assert result .is_valid ()
208+ self . assertTrue ( result .is_valid () )
183209
184210 def test_validate_report_not_a_file (self ):
185211 self .fs .create_dir ("/project" )
186212 self .fs .create_dir ("/project/coverage.xml" )
187213
188214 result = CoverageReportValidator .validate_coverage_reports ("coverage.xml" , "/project" )
189215
190- assert not result .is_valid ()
191- assert "not a file" in result .errors [0 ]
216+ self . assertFalse ( result .is_valid () )
217+ self . assertIn ( "not a file" , result .errors [0 ])
192218
193219 def test_validate_report_invalid_xml (self ):
194220 self .fs .create_dir ("/project" )
195221 self .fs .create_file ("/project/coverage.xml" , contents = "not valid xml" )
196222
197223 result = CoverageReportValidator .validate_coverage_reports ("coverage.xml" , "/project" )
198224
199- assert not result .is_valid ()
200- assert "not valid XML" in result .errors [0 ]
225+ self . assertFalse ( result .is_valid () )
226+ self . assertIn ( "not valid XML" , result .errors [0 ])
201227
202228 def test_validate_report_wrong_root_element (self ):
203229 self .fs .create_dir ("/project" )
204230 self .fs .create_file ("/project/coverage.xml" , contents = '<?xml version="1.0"?>\n <report></report>' )
205231
206232 result = CoverageReportValidator .validate_coverage_reports ("coverage.xml" , "/project" )
207233
208- assert result .is_valid ()
209- assert len (result .warnings ) == 1
210- assert "report" in result .warnings [0 ]
211- assert "expected 'coverage'" in result .warnings [0 ]
234+ self .assertTrue (result .is_valid ())
235+ self .assertEqual (len (result .warnings ), 1 )
236+ self .assertIn ("report" , result .warnings [0 ])
237+ self .assertIn ("expected 'coverage'" , result .warnings [0 ])
238+
239+ def test_validate_report_permission_denied (self ):
240+ self .fs .create_dir ("/project" )
241+ self .fs .create_file ("/project/coverage.xml" , contents = '<?xml version="1.0"?>\n <coverage></coverage>' )
242+ self .fs .chmod ("/project/coverage.xml" , mode = 0o000 , force_unix_mode = True )
243+
244+ result = CoverageReportValidator .validate_coverage_reports ("coverage.xml" , "/project" )
245+
246+ self .assertFalse (result .is_valid ())
247+ self .assertIn ("permission denied" , result .errors [0 ])
248+
249+ def test_validate_report_binary_content (self ):
250+ self .fs .create_dir ("/project" )
251+ self .fs .create_file ("/project/coverage.xml" , contents = b"\x80 \x81 \x82 \x83 \xff \xfe " )
252+
253+ result = CoverageReportValidator .validate_coverage_reports ("coverage.xml" , "/project" )
254+
255+ has_unicode_warning = any ("binary format" in w for w in result .warnings )
256+ has_xml_error = any ("not valid XML" in e for e in result .errors )
257+ self .assertTrue (has_unicode_warning or has_xml_error )
212258
213259 def test_validate_mixed_valid_and_missing_reports (self ):
214260 self .fs .create_dir ("/project" )
215261 self .fs .create_file ("/project/exists.xml" , contents = '<?xml version="1.0"?>\n <coverage></coverage>' )
216262
217263 result = CoverageReportValidator .validate_coverage_reports ("exists.xml, missing.xml" , "/project" )
218264
219- assert not result .is_valid ()
220- assert len (result .errors ) == 1
221- assert "missing.xml" in result .errors [0 ]
265+ self . assertFalse ( result .is_valid () )
266+ self . assertEqual ( len (result .errors ), 1 )
267+ self . assertIn ( "missing.xml" , result .errors [0 ])
222268
223269
224270class TestRunDryRun (pyfakefs .TestCase ):
@@ -236,7 +282,7 @@ def test_run_dry_run_no_coverage_reports(self, mock_logging):
236282
237283 exit_code = run_dry_run (config )
238284
239- assert exit_code == 0
285+ self . assertEqual ( exit_code , 0 )
240286
241287 @patch ("pysonar_scanner.__main__.logging" )
242288 def test_run_dry_run_with_valid_coverage_reports (self , mock_logging ):
@@ -250,7 +296,7 @@ def test_run_dry_run_with_valid_coverage_reports(self, mock_logging):
250296
251297 exit_code = run_dry_run (config )
252298
253- assert exit_code == 0
299+ self . assertEqual ( exit_code , 0 )
254300
255301 @patch ("pysonar_scanner.__main__.logging" )
256302 def test_run_dry_run_with_missing_coverage_reports (self , mock_logging ):
@@ -263,7 +309,7 @@ def test_run_dry_run_with_missing_coverage_reports(self, mock_logging):
263309
264310 exit_code = run_dry_run (config )
265311
266- assert exit_code == 1
312+ self . assertEqual ( exit_code , 1 )
267313
268314 @patch ("pysonar_scanner.__main__.logging" )
269315 def test_run_dry_run_logs_dry_run_mode (self , mock_logging ):
0 commit comments