Skip to content

Commit 2fe7c5b

Browse files
[DOCUMENTATION] Another minor chore (- WIP #275 -)
* minor work of chore: GHI #275 * partial work on chore: GHI #204 Changes in file .github/workflows/Tests.yml: * possible fix for code coverage regressions in CI/CD (- WIP #204 -) Changes in file tests/check_integration_coverage: * possible fix for code coverage regressions in CI/CD (- WIP #204 -) * related work Changes in file tests/test_hear_cleanup.py: * added documentation for module as per GHI #275 (main chore)
1 parent a3eaa66 commit 2fe7c5b

File tree

3 files changed

+91
-27
lines changed

3 files changed

+91
-27
lines changed

.github/workflows/Tests.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ jobs:
117117
uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # v5.4.0
118118
with:
119119
token: ${{ secrets.CODECOV_TOKEN }}
120+
job_code: ${{ join( [github.run_id, github.run_number], '-' ) }}
121+
os: ${{ matrix.os }}
120122
files: ./coverage.xml
121123
directory: .
122124
flags: multicast,${{ matrix.os }},${{ matrix.python-version }}
@@ -267,7 +269,9 @@ jobs:
267269
uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # v5.4.0
268270
with:
269271
token: ${{ secrets.CODECOV_TOKEN }}
270-
files: ./test-reports/coverage_supplement.xml
272+
job_code: ${{ join( [github.run_id, github.run_number], '-' ) }}
273+
os: ${{ matrix.os }}
274+
files: ./test-reports/coverage_supplement.xml,./test-reports/coverage.xml
271275
directory: .
272276
flags: multicast,${{ matrix.os }},${{ matrix.python-version }}
273277
name: multicast-github-${{ matrix.os }}-${{ matrix.python-version }}

tests/check_integration_coverage

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,15 @@ fi
144144

145145
TEST_MCAST_PORT=$(((RANDOM % 16384) + 49152));
146146
TEST_MCAST_GROUP='224.0.0.1' ;
147-
PY_3_CMD=$(command -v python3);
147+
COVERAGE_DATA_FILE="./coverage_integration" ;
148+
if [[ -x "${PYTHON}" ]] ; then
149+
PY_3_CMD="${PYTHON}"
150+
else
151+
PY_3_CMD=$(command -v python3);
152+
fi ;
153+
printf "%s\n" "::debug:: Will Use ${PY_3_CMD}"
148154
# shellcheck disable=SC2086
149-
COVERAGE_CMD="${PY_3_CMD} -m coverage run --source=multicast -p -m"
155+
COVERAGE_CMD="${PY_3_CMD} -m coverage run -p --source=multicast -m";
150156
export COVERAGE_CMD
151157

152158
# start test-suite
@@ -155,7 +161,8 @@ make -j1 -f Makefile test-reports || EXIT_CODE=3 ;
155161
printf "%s\n\n" "Start of Log:" > ./"${LOG_FILE}" ; wait ;
156162
printf "%s\n\n" "Will use host-port: [${TEST_MCAST_GROUP} : ${TEST_MCAST_PORT}]" >> ./"${LOG_FILE}" ; wait ;
157163
printf "%s\n\n" "Start of Integration Test one-shot:" >> ./"${LOG_FILE}" ; wait ;
158-
( sleep 1 ; "${COVERAGE_CMD} multicast --use-std RECV --port ${TEST_MCAST_PORT} --groups ${TEST_MCAST_GROUP} --group ${TEST_MCAST_GROUP}" 2>./"${ERR_FILE}" & sleep 6 ; kill -9 $! 2>/dev/null ) >> ./"${LOG_FILE}" & true ;
164+
# shellcheck disable=SC2086
165+
( sleep 1 ; ${COVERAGE_CMD} multicast --use-std RECV --port "${TEST_MCAST_PORT}" --groups "${TEST_MCAST_GROUP}" --group "${TEST_MCAST_GROUP}" 2>./"${ERR_FILE}" & sleep 6 ; kill -9 $! 2>/dev/null ) >> ./"${LOG_FILE}" & true ;
159166

160167
for PCOUNT in $(seq 1 5) ; do
161168
# shellcheck disable=SC2086
@@ -184,14 +191,15 @@ printf "%s\n\n" "End of Integration Test daemon" >> ./"${LOG_FILE}" ; wait ;
184191

185192
# cleanup from test-suite
186193

187-
COVERAGE_DATE_FILE="./coverage_integration"
188-
189-
$(command -v python3) -m coverage combine --data-file="${COVERAGE_DATE_FILE}" 2>/dev/null || EXIT_CODE=2 ;
190-
if [[ -r "${COVERAGE_DATE_FILE}" ]] ; then
191-
$(command -v python3) -m coverage report -m --data-file="${COVERAGE_DATE_FILE}" 2>/dev/null || EXIT_CODE=2 ;
192-
$(command -v python3) -m coverage xml -o ./test-reports/coverage_supplement.xml --include=multicast/* --data-file="${COVERAGE_DATE_FILE}" || EXIT_CODE=2 ;
194+
# shellcheck disable=SC2086
195+
${PY_3_CMD} -m coverage combine --data-file="${COVERAGE_DATA_FILE}" ./.coverage.* || EXIT_CODE=2 ;
196+
if [[ -r "${COVERAGE_DATA_FILE}" ]] ; then
197+
# shellcheck disable=SC2086
198+
${PY_3_CMD} -m coverage report -m --data-file="${COVERAGE_DATA_FILE}" 2>/dev/null || EXIT_CODE=2 ;
199+
# shellcheck disable=SC2086
200+
${PY_3_CMD} -m coverage xml -o ./test-reports/coverage_supplement.xml --include=multicast/* --data-file="${COVERAGE_DATA_FILE}" || EXIT_CODE=2 ;
193201
else
194-
printf "%s\n" "Coverage collection FAILED!" 2>/dev/null || EXIT_CODE=2 ;
202+
printf "%s\n" "::error file='${0}',title='No Coverage':: Coverage collection FAILED!" >>./"${ERR_FILE}" || EXIT_CODE=2 ;
195203
fi ;
196204
wait ;
197205
cp -f ./"${LOG_FILE}" ./test-reports/integration_data_log.log 2>/dev/null ; wait ;

tests/test_hear_cleanup.py

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,27 @@
1717
# See the License for the specific language governing permissions and
1818
# limitations under the License.
1919

20+
"""
21+
Test module for verifying cleanup behavior of the multicast hearing mechanism.
22+
23+
This module contains test suites that verify proper resource cleanup and process
24+
termination when the multicast hearing process receives shutdown signals.
25+
"""
26+
2027
__module__ = "tests"
2128

2229
try:
30+
"""Handle imports with CWE-758 mitigation.
31+
32+
This implementation uses a nested try-except pattern to:
33+
1. Attempt direct context import
34+
2. Fallback to relative import
35+
3. Validate context module integrity
36+
4. Import required dependencies
37+
38+
References:
39+
- CWE-758: Reliance on Undefined, Unspecified, or Implementation-Defined Behavior
40+
"""
2341
try:
2442
import context
2543
except Exception as _: # pragma: no branch
@@ -51,12 +69,39 @@ class HearCleanupTestSuite(context.BasicUsageTestSuite):
5169

5270
__name__ = "tests.test_hear_cleanup.HearCleanupTestSuite"
5371

54-
# Class-level constants
55-
QUICK_JOIN_TIMEOUT = 1 # Quick check for process termination
56-
ERROR_JOIN_TIMEOUT = 3 # Timeout when handling errors
57-
FINAL_JOIN_TIMEOUT = 15 # Final wait for process cleanup
72+
# Constants for test configuration
73+
STOP_DELAY_SECONDS: int = 1
74+
"""
75+
Time to wait for server cleanup after sending `STOP`.
76+
77+
Must be > 0 to ensure server has an opportunity to handle messages.
78+
"""
79+
80+
KILL_DELAY_SECONDS: int = 3
81+
"""
82+
Average time to wait for process completion after sending `STOP` before sending `SIGKILL`.
83+
84+
Should be sufficient for handling `STOP` messages but not too long.
85+
"""
86+
87+
PROCESS_TIMEOUT_SECONDS: int = 15
88+
"""
89+
Maximum time to wait for process completion after sending `STOP`.
5890
59-
def test_cleanup_on_exit(self):
91+
Should be sufficient for cleanup but not too long.
92+
"""
93+
94+
EXPECTED_STOP_EXIT_CODE: int = 0
95+
"""
96+
Expected exit code when process receives `STOP` messages.
97+
98+
`0` = `success` as per POSIX convention.
99+
"""
100+
101+
TEST_MULTICAST_GROUP: str = "224.0.0.1"
102+
"""Standard multicast group address for testing."""
103+
104+
def test_cleanup_on_exit(self) -> None:
60105
"""Test proper cleanup of McastHEAR when receiving STOP message.
61106
62107
Prerequisites:
@@ -73,15 +118,15 @@ def test_cleanup_on_exit(self):
73118
- Process exits with code 0
74119
- No lingering processes or sockets
75120
"""
76-
theResult = False
77-
fail_fixture = "STOP --> HEAR == error"
78-
_fixture_port_num = self._the_test_port
121+
theResult: bool = False
122+
fail_fixture: str = "STOP --> HEAR == error"
123+
_fixture_port_num: int = self._the_test_port
79124
try:
80125
self.assertIsNotNone(_fixture_port_num)
81126
self.assertEqual(type(_fixture_port_num), type(int(0)))
82127
_fixture_HEAR_kwargs = {
83128
"port": _fixture_port_num,
84-
"group": "224.0.0.1",
129+
"group": self.TEST_MULTICAST_GROUP,
85130
}
86131
self.assertIsNotNone(_fixture_HEAR_kwargs)
87132
p = Process(
@@ -95,25 +140,32 @@ def test_cleanup_on_exit(self):
95140
sender = multicast.send.McastSAY()
96141
self.assertIsNotNone(sender)
97142
while p.is_alive():
98-
sender(group="224.0.0.1", port=_fixture_port_num, ttl=1, data="STOP Test")
99-
p.join(self.QUICK_JOIN_TIMEOUT)
143+
sender(
144+
group=self.TEST_MULTICAST_GROUP, port=_fixture_port_num,
145+
ttl=1, data="STOP Test",
146+
)
147+
p.join(self.STOP_DELAY_SECONDS)
100148
self.assertFalse(p.is_alive())
101149
except Exception as _cause:
102-
p.join(self.ERROR_JOIN_TIMEOUT)
150+
p.join(self.KILL_DELAY_SECONDS)
103151
if p.is_alive():
104152
p.terminate()
105153
p.close()
106154
raise unittest.SkipTest(fail_fixture) from _cause
107-
p.join(self.FINAL_JOIN_TIMEOUT)
155+
p.join(self.PROCESS_TIMEOUT_SECONDS)
108156
self.assertIsNotNone(p.exitcode)
109-
self.assertEqual(int(p.exitcode), int(0))
110-
theResult = (int(p.exitcode) <= int(0))
157+
self.assertEqual(
158+
int(p.exitcode),
159+
int(self.EXPECTED_STOP_EXIT_CODE),
160+
"CEP-8 VIOLATION."
161+
)
162+
theResult = (int(p.exitcode) <= int(self.EXPECTED_STOP_EXIT_CODE))
111163
except Exception as err:
112164
context.debugtestError(err)
113165
self.fail(fail_fixture)
114166
theResult = False
115167
self.assertTrue(theResult, fail_fixture)
116168

117169

118-
if __name__ == '__main__':
170+
if __name__ == "__main__":
119171
unittest.main()

0 commit comments

Comments
 (0)