Skip to content

Commit 0563010

Browse files
committed
Create a self cleaning codechecker server
1 parent 9b4fd11 commit 0563010

3 files changed

Lines changed: 109 additions & 71 deletions

File tree

test/common/base.py

Lines changed: 1 addition & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -20,45 +20,12 @@
2020
import os
2121
import re
2222
import shlex
23-
import shutil
24-
import signal
25-
import socket
2623
import subprocess
27-
import tempfile
28-
import time
2924
import unittest
3025
import sys
3126
from typing import Optional
3227

3328

34-
# Based on:
35-
# https://dev.to/farcellier/wait-for-a-server-to-respond-in-python-488e
36-
def wait_port(
37-
port: int,
38-
host: str = "localhost",
39-
timeout: int = 3000,
40-
attempt_every: int = 100,
41-
) -> bool:
42-
"""
43-
Wait until a port would be open,
44-
for example the port 8001 for CodeChecker server
45-
"""
46-
start = time.monotonic()
47-
while True:
48-
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
49-
try:
50-
s.connect((host, port))
51-
s.close()
52-
return True
53-
except ConnectionRefusedError:
54-
if timeout is not None and time.monotonic() - start > (
55-
timeout / 1000
56-
):
57-
return False
58-
59-
time.sleep(attempt_every / 1000)
60-
61-
6229
class TestBase(unittest.TestCase):
6330
"""Unittest base abstract class"""
6431

@@ -170,42 +137,7 @@ def contains_regex_in_file(cls, file_path: str, regex: str) -> bool:
170137
"""
171138
return bool(cls.grep_file(file_path, regex))
172139

173-
@classmethod
174-
def start_codechecker_server(cls):
175-
"""
176-
Starts a CodeChecker server instance on port 8001
177-
This server must be shutdown with stop_codechecker_sever
178-
"""
179-
cls.temp_workspace = tempfile.mkdtemp()
180-
server_command = [
181-
"CodeChecker",
182-
"server",
183-
"--workspace",
184-
cls.temp_workspace,
185-
"--port",
186-
"8001", # user running unittest must make this port free!
187-
]
188-
# pylint: disable=consider-using-with
189-
cls.devnull = open(os.devnull, "w", encoding='utf-8')
190-
# pylint: disable=consider-using-with
191-
cls.server_process: subprocess.Popen = subprocess.Popen(
192-
server_command, stdout=cls.devnull
193-
)
194-
assert wait_port(
195-
port=8001, timeout=10000
196-
), "Failed to start CodeChecker server"
197-
198-
@classmethod
199-
def stop_codechecker_server(cls):
200-
"""
201-
Stops the CodeChecker server started by start_codechecker_server
202-
"""
203-
os.kill(cls.server_process.pid, signal.SIGTERM)
204-
cls.server_process.wait()
205-
cls.devnull.close()
206-
shutil.rmtree(cls.temp_workspace)
207-
208-
def check_store(self, path: str, name: str):
140+
def check_store(self, path : str, name : str):
209141
"""
210142
Tries to store the results on the codechecker server,
211143
asserts for successful storing.

test/common/codechecker_server.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Copyright 2023 Ericsson AB
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""
16+
Codechecker server functionality, and related functions
17+
"""
18+
19+
import os
20+
import shutil
21+
import signal
22+
import socket
23+
import subprocess
24+
import tempfile
25+
import time
26+
27+
28+
# Based on:
29+
# https://dev.to/farcellier/wait-for-a-server-to-respond-in-python-488e
30+
def wait_port(
31+
port: int,
32+
host: str = "localhost",
33+
timeout: int = 3000,
34+
attempt_every: int = 100,
35+
) -> bool:
36+
"""
37+
Wait until a port would be open,
38+
for example the port 8001 for CodeChecker server
39+
"""
40+
start = time.monotonic()
41+
while True:
42+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
43+
try:
44+
s.connect((host, port))
45+
s.close()
46+
return True
47+
except ConnectionRefusedError:
48+
if timeout is not None and time.monotonic() - start > (
49+
timeout / 1000
50+
):
51+
return False
52+
53+
time.sleep(attempt_every / 1000)
54+
55+
56+
class CodeCheckerServer:
57+
"""
58+
CodeCheckerServer object for testing.
59+
Cleans up after itself.
60+
"""
61+
def __init__(self, port="8001"):
62+
self.running = False
63+
self.port = port
64+
self.temp_workspace = tempfile.mkdtemp()
65+
self.start_codechecker_server()
66+
67+
def __del__(self):
68+
self.stop_codechecker_server()
69+
70+
def start_codechecker_server(self):
71+
"""
72+
Starts a CodeChecker server instance on port 8001
73+
This server must be shutdown with stop_codechecker_sever
74+
"""
75+
if self.running:
76+
return
77+
server_command = [
78+
"CodeChecker",
79+
"server",
80+
"--workspace",
81+
self.temp_workspace,
82+
"--port",
83+
self.port,
84+
]
85+
# These file/popen processes are closed when the object dies
86+
# pylint: disable=consider-using-with
87+
self.devnull = open(os.devnull, "w", encoding="utf-8")
88+
# pylint: disable=consider-using-with
89+
self.server_process: subprocess.Popen = subprocess.Popen(
90+
server_command, stdout=self.devnull
91+
)
92+
assert wait_port(
93+
port=8001, timeout=10000
94+
), "Failed to start CodeChecker server"
95+
self.running = True
96+
97+
def stop_codechecker_server(self):
98+
"""
99+
Stops the CodeChecker server started by start_codechecker_server
100+
"""
101+
os.kill(self.server_process.pid, signal.SIGTERM)
102+
self.server_process.wait()
103+
self.running = False
104+
self.devnull.close()
105+
shutil.rmtree(self.temp_workspace)

test/unit/parse/test_parse.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import unittest
2121
from typing import final
2222
from common.base import TestBase
23+
from common.codechecker_server import CodeCheckerServer
2324

2425

2526
class TestTemplate(TestBase):
@@ -39,13 +40,13 @@ class TestTemplate(TestBase):
3940
def setUpClass(cls):
4041
"""Start CodeChecker server"""
4142
super().setUpClass()
42-
cls.start_codechecker_server()
43+
cls.codechecker_server = CodeCheckerServer()
4344

4445
@final
4546
@classmethod
4647
def tearDownClass(cls):
4748
"""Stop CodeChecker server"""
48-
cls.stop_codechecker_server()
49+
del cls.codechecker_server
4950
super().tearDownClass()
5051

5152
def test_parse_html(self):

0 commit comments

Comments
 (0)