Skip to content

Commit ab91f49

Browse files
authored
PYSCAN-24: Add test for scanner (#2)
1 parent ecae7cc commit ab91f49

File tree

4 files changed

+88
-69
lines changed

4 files changed

+88
-69
lines changed

pyproject.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ dependencies = [
6565
license = 'python -m licenseheaders -t license_header.tmpl -o "SonarSource SA" -y 2011-2023 -n "Sonar Scanner Python" -E .py'
6666
format = 'python -m black src/ tests/ --check'
6767

68+
[tool.pytest.ini_options]
69+
pythonpath = [
70+
"src",
71+
]
72+
addopts = [
73+
"--import-mode=importlib",
74+
]
75+
6876
[tool.black]
6977
line-length = 89
7078
target-version = ["py38", "py39", "py310", "py311", "py312"]

src/py_sonar_scanner/scanner.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
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 subprocess
2120
import threading
21+
from threading import Thread
22+
from subprocess import Popen, PIPE
2223

2324
from py_sonar_scanner.configuration import Configuration
2425

@@ -30,22 +31,30 @@ def __init__(self, cfg: Configuration):
3031
self.cfg = cfg
3132

3233
def scan(self):
33-
cmd = [self.cfg.sonar_scanner_executable_path] + self.cfg.scan_arguments
34-
process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
35-
stderr=subprocess.PIPE)
34+
process = self.execute_command()
35+
output_thread = threading.Thread(target=self._print_output, args=(process.stdout,))
36+
error_thread = threading.Thread(target=self._print_output, args=(process.stderr,))
37+
return self.process_output(output_thread, error_thread, process)
3638

37-
def print_output(stream):
38-
for line in stream:
39-
decoded_line = line.decode('utf-8')
40-
print(decoded_line, end='', flush=True)
41-
42-
output_thread = threading.Thread(target=print_output, args=(process.stdout,))
43-
error_thread = threading.Thread(target=print_output, args=(process.stderr,))
39+
def process_output(self, output_thread: Thread, error_thread: Thread, process: Popen) -> int:
4440
output_thread.start()
4541
error_thread.start()
46-
4742
process.wait()
4843
output_thread.join()
4944
error_thread.join()
5045

5146
return process.returncode
47+
48+
def compute_command(self) -> list[str]:
49+
if not self.cfg.sonar_scanner_executable_path:
50+
raise ValueError("No executable path provided")
51+
return [self.cfg.sonar_scanner_executable_path] + self.cfg.scan_arguments
52+
53+
def execute_command(self) -> Popen:
54+
cmd = self.compute_command()
55+
return Popen(cmd, stdout=PIPE, stderr=PIPE)
56+
57+
def _print_output(self, stream: list[str]):
58+
for line in stream:
59+
decoded_line = line.decode('utf-8')
60+
print(decoded_line, end='', flush=True)

tests/test_placeholder.py

Lines changed: 0 additions & 57 deletions
This file was deleted.

tests/test_scanner.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import unittest
2+
from unittest.mock import Mock
3+
import threading
4+
from py_sonar_scanner.scanner import Scanner
5+
from py_sonar_scanner.configuration import Configuration
6+
7+
8+
class TestScanner(unittest.TestCase):
9+
10+
def test_scanner_compute_command(self):
11+
cfg = Configuration()
12+
cfg.sonar_scanner_executable_path = "test"
13+
cfg.scan_arguments = ["a", "b"]
14+
scanner = Scanner(cfg)
15+
16+
assert scanner.compute_command() == ["test", "a", "b"]
17+
18+
cfg.sonar_scanner_executable_path = "test"
19+
cfg.scan_arguments = []
20+
scanner = Scanner(cfg)
21+
22+
assert scanner.compute_command() == ["test"]
23+
24+
cfg.sonar_scanner_executable_path = ""
25+
cfg.scan_arguments = []
26+
scanner = Scanner(cfg)
27+
28+
self.assertRaises(ValueError, scanner.compute_command)
29+
30+
def test_process_output(self):
31+
scanner = Scanner(Configuration())
32+
output_thread = threading.Thread()
33+
error_thread = threading.Thread()
34+
35+
process = Mock()
36+
output_thread.start = Mock()
37+
error_thread.start = Mock()
38+
output_thread.join = Mock()
39+
error_thread.join = Mock()
40+
process.wait = Mock()
41+
42+
return_code = scanner.process_output(output_thread, error_thread, process)
43+
44+
output_thread.start.assert_called_once()
45+
error_thread.start.assert_called_once()
46+
output_thread.join.assert_called_once()
47+
error_thread.join.assert_called_once()
48+
process.wait.assert_called_once()
49+
50+
def test_scan(self):
51+
scanner = Scanner(Configuration())
52+
53+
scanner.execute_command = Mock()
54+
scanner.process_output = Mock()
55+
56+
scanner.scan()
57+
58+
scanner.execute_command.assert_called_once()
59+
scanner.process_output.assert_called_once()

0 commit comments

Comments
 (0)