Skip to content

Commit d05678b

Browse files
[PATCH] Work as part of #199 and related tasks.
- [x] Includes PR #290 - [x] Includes PR #292 - [x] Includes PR #293 - [x] Includes PR #294 - [x] Includes Security Patch for [CVE-2024-53899](https://osv.dev/vulnerability/GHSA-rqc4-2hc7-8c8v) (- WIP #299 -)
4 parents 653d416 + adedd34 + aab22b7 + 2dabcf1 commit d05678b

26 files changed

+467
-64
lines changed

.coderabbit.yaml

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,13 +227,32 @@ reviews:
227227
- EN_QUOTES
228228
- CONSECUTIVE_SPACES
229229
enabled_rules:
230-
- STYLE
230+
# Grammar rules
231+
- BEEN_PART_AGREEMENT
232+
- COMMA_COMPOUND_SENTENCE
233+
# Compound word rules
234+
- COMPOUNDING
235+
- EN_COMPOUNDS
236+
- EN_COMPOUND_ADJECTIVE_INTERNAL
237+
# Word usage and consistency
231238
- EN_CONTRACTION_SPELLING
232239
- EN_WORD_COHERENCY
240+
- ENGLISH_WORD_REPEAT_RULE
241+
- EN_A_VS_AN
242+
# Style and semantics
233243
- IT_IS_OBVIOUS
234-
- TWELFTH_OF_NEVER
244+
- NONSTANDARD_PHRASES
235245
- OXFORD_SPELLING
236246
- PASSIVE_VOICE
247+
- REDUNDANCY
248+
- SEMANTICS
249+
- STYLE
250+
- TYPOGRAPHY
251+
# Time and date conventions
252+
- TWELFTH_OF_NEVER
253+
- WIKIPEDIA_12_AM
254+
- WIKIPEDIA_12_PM
255+
- WIKIPEDIA_CONTRACTIONS
237256
shellcheck:
238257
enabled: true
239258
ruff:

Makefile

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ ifeq "$(PYTHON)" ""
7777
#COV_CORE_SOURCE = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/
7878
COV_CORE_CONFIG = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/.coveragerc
7979
COV_CORE_DATAFILE = .coverage
80+
COVERAGE_ARGS := --cov=. --cov-append --cov-report=xml
81+
else
82+
COVERAGE_ARGS :=
8083
endif
8184
ifeq "$(COVERAGE)" ""
8285
COVERAGE!=$(COMMAND) coverage
@@ -87,9 +90,16 @@ else
8790
#COV_CORE_SOURCE = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/
8891
COV_CORE_CONFIG = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/.coveragerc
8992
COV_CORE_DATAFILE = .coverage
93+
COVERAGE_ARGS := --cov=. --cov-append --cov-report=xml
94+
else
95+
COVERAGE_ARGS :=
9096
endif
9197
endif
9298

99+
ifndef PYTEST
100+
PYTEST=$(PYTHON) -m pytest --cache-clear --junitxml=test-reports/junit.xml -v --rootdir=.
101+
endif
102+
93103
ifndef PIP_COMMON_FLAGS
94104
# Define common pip install flags
95105
PIP_COMMON_FLAGS := --use-pep517 --exists-action s --upgrade --upgrade-strategy eager
@@ -198,11 +208,6 @@ purge: legacy-purge
198208
$(QUIET)$(RMDIR) ./test-reports/ 2>$(ERROR_LOG_PATH) || :
199209
$(QUIET)$(ECHO) "$@: Done."
200210

201-
just-test: cleanup MANIFEST.in
202-
$(QUIET)$(COVERAGE) run -p --source=multicast -m unittest discover --verbose --buffer -s ./tests -t $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) || $(PYTHON) -m unittest discover --verbose --buffer -s ./tests -t ./ || DO_FAIL="exit 2" ;
203-
$(QUITE)$(WAIT) ;
204-
$(QUIET)$(DO_FAIL) ;
205-
206211
test: just-test
207212
$(QUIET)$(DO_FAIL) ;
208213
$(QUIET)$(COVERAGE) combine 2>$(ERROR_LOG_PATH) || : ;
@@ -227,8 +232,63 @@ docs-reqs: ./docs/ ./docs/requirements.txt init
227232
$(QUIET)$(PYTHON) -m pip install $(PIP_COMMON_FLAGS) $(PIP_ENV_FLAGS) -r docs/requirements.txt 2>$(ERROR_LOG_PATH) || : ;
228233
$(QUIET)$(WAIT) ;
229234

235+
# === Test Group Targets ===
236+
just-test: cleanup MANIFEST.in ## Run all minimum acceptance tests
237+
$(QUIET)if [ -n "$$TESTS_USE_PYTEST" ]; then \
238+
$(PYTEST) --doctest-glob=multicast/*.py,tests/*.py --doctest-modules $(COVERAGE_ARGS) || DO_FAIL="exit 2" ; \
239+
else \
240+
$(COVERAGE) run -p --source=multicast -m unittest discover --verbose --buffer -s ./tests -t $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) || $(PYTHON) -m unittest discover --verbose --buffer -s ./tests -t ./ || DO_FAIL="exit 2" ; \
241+
fi
242+
$(QUITE)$(WAIT) ;
243+
$(QUIET)$(DO_FAIL) ;
244+
245+
test-mat-%: MANIFEST.in ## Run specific MAT category (basic|doctests|say|hear|usage|build|bootstrap)
246+
$(QUIET)if [ -n "$$TESTS_USE_PYTEST" ]; then \
247+
$(PYTEST) tests/ --verbose $(COVERAGE_ARGS) --junitxml=test-reports/junit.xml -v --rootdir=. -m "mat and $*"; \
248+
else \
249+
$(COVERAGE) run -p --source=multicast -m tests.run_selective --group mat --category $*; \
250+
fi
251+
$(QUITE)$(WAIT) ;
252+
$(QUIET)$(DO_FAIL) ;
253+
254+
test-extra: ## Run all extra tests
255+
$(QUIET)if [ -n "$$TESTS_USE_PYTEST" ]; then \
256+
$(PYTEST) tests/ --verbose $(COVERAGE_ARGS) -m "extra"; \
257+
else \
258+
$(COVERAGE) run -p --source=multicast -m tests.run_selective --group extra; \
259+
fi
260+
$(QUITE)$(WAIT) ;
261+
$(QUIET)$(DO_FAIL) ;
262+
263+
test-extra-%: ## Run specific extra test category
264+
$(QUIET)if [ -n "$$TESTS_USE_PYTEST" ]; then \
265+
$(PYTEST) tests/ --verbose $(COVERAGE_ARGS) -m "extra and $*"; \
266+
else \
267+
$(COVERAGE) run -p --source=multicast -m tests.run_selective --group extra --category $*; \
268+
fi
269+
$(QUITE)$(WAIT) ;
270+
$(QUIET)$(DO_FAIL) ;
271+
272+
test-fuzzing: ## Run all fuzzing tests
273+
$(QUIET)if [ -n "$$TESTS_USE_PYTEST" ]; then \
274+
$(PYTEST) tests/ --verbose $(COVERAGE_ARGS) -m "fuzzing"; \
275+
else \
276+
$(COVERAGE) run -p --source=multicast -m tests.run_selective --group fuzzing; \
277+
fi
278+
$(QUITE)$(WAIT) ;
279+
$(QUIET)$(DO_FAIL) ;
280+
281+
test-perf: ## Run all performance tests
282+
$(QUIET)if [ -n "$$TESTS_USE_PYTEST" ]; then \
283+
$(PYTEST) tests/ --verbose $(COVERAGE_ARGS) -m "performance"; \
284+
else \
285+
$(COVERAGE) run -p --source=multicast -m tests.run_selective --group performance; \
286+
fi
287+
$(QUITE)$(WAIT) ;
288+
$(QUIET)$(DO_FAIL) ;
289+
230290
test-pytest: cleanup MANIFEST.in must_have_pytest test-reports
231-
$(QUIET)$(PYTHON) -m pytest --cache-clear --doctest-glob=multicast/*.py,tests/*.py --doctest-modules --cov=. --cov-append --cov-report=xml --junitxml=test-reports/junit.xml -v --rootdir=. || DO_FAIL="exit 2" ;
291+
$(QUIET)$(PYTEST) --doctest-glob=multicast/*.py,tests/*.py --doctest-modules $(COVERAGE_ARGS) || DO_FAIL="exit 2" ;
232292
$(QUITE)$(WAIT) ;
233293
$(QUIET)$(DO_FAIL) ;
234294
$(QUIET)$(ECHO) "$@: Done."

docs/utils.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,21 @@
1919
import re
2020

2121

22+
# Git reference validation pattern
23+
# Enforces:
24+
# - Must start with alphanumeric character
25+
# - Can contain alphanumeric characters, underscore, hyphen, forward slash, and dot
26+
GIT_REF_PATTERN = r'^[a-zA-Z0-9][a-zA-Z0-9_\-./]*$'
27+
28+
2229
def _validate_git_ref(ref: str) -> str:
2330
"""
2431
Validate if the provided string is a valid Git reference.
2532
33+
Git reference naming rules:
34+
- Must start with an alphanumeric character
35+
- Can contain alphanumeric characters, underscore, hyphen, forward slash, and dot
36+
- Cannot contain consecutive dots (..)
2637
Args:
2738
ref (str) -- The Git reference to validate.
2839
@@ -57,8 +68,15 @@ def _validate_git_ref(ref: str) -> str:
5768
Traceback (most recent call last):
5869
...
5970
ValueError: Invalid Git reference:...
71+
72+
Testcase 5: Invalid reference with disallowed dot-dot characters.
73+
74+
>>> _validate_git_ref('invalid..ref') #doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
75+
Traceback (most recent call last):
76+
...
77+
ValueError: Invalid Git reference: invalid..ref
6078
"""
61-
if not re.match(r'^[a-zA-Z0-9_\-./]+$', ref):
79+
if not re.match(GIT_REF_PATTERN, ref) or ".." in ref:
6280
raise ValueError(f"Invalid Git reference: {ref}")
6381
return ref
6482

multicast/hear.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,11 +427,14 @@ def handle(self):
427427
428428
Testcase 3: `handle` requires valid requests or ignores input.
429429
430-
>>> handler.request = ("The Test", multicast.genSocket())
430+
>>> tst_fixture_sock = multicast.genSocket()
431+
>>> handler.request = ("The Test", tst_fixture_sock)
431432
>>> handler.client_address = ("224.0.1.2", 51234)
432433
>>> handler.handle() is None
433434
True
434435
>>>
436+
>>> multicast.endSocket(tst_fixture_sock)
437+
>>>
435438
"""
436439
(data, sock) = self.request
437440
if data is None or not sock:

multicast/recv.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,12 @@ def joinstep(groups, port, iface=None, bind_group=None, isock=None):
233233
>>>
234234
235235
Testcase 1: Stability testing.
236+
A: Verify the multicast.recv module is properly initialized.
237+
B: Verify the joinstep function exists and has the expected type.
238+
C: Test socket creation with no groups (default behavior).
239+
D: Test socket creation with a specified multicast group.
240+
E: Test socket creation with a multicast group and binding to that group.
241+
F: Test socket creation using an existing socket handle.
236242
237243
>>> import multicast
238244
>>>
@@ -243,20 +249,36 @@ def joinstep(groups, port, iface=None, bind_group=None, isock=None):
243249
False
244250
>>> type(multicast.recv.joinstep)
245251
<class 'function'>
246-
>>> multicast.recv.joinstep(None, 59991) #doctest: -DONT_ACCEPT_BLANKLINE, +ELLIPSIS
247-
<socket.socket...>
252+
>>> tst_sk = multicast.recv.joinstep(None, 59991)
253+
>>> tst_sk #doctest: -DONT_ACCEPT_BLANKLINE, +ELLIPSIS
254+
<socket.socket...laddr=...>
255+
>>> multicast.endSocket(tst_sk)
256+
>>> tst_sk #doctest: -DONT_ACCEPT_BLANKLINE, +ELLIPSIS
257+
<socket.socket [closed]...>
248258
>>> tst_fxtr = multicast._MCAST_DEFAULT_GROUP
249-
>>> multicast.recv.joinstep([tst_fxtr], 59991) #doctest: -DONT_ACCEPT_BLANKLINE, +ELLIPSIS
259+
>>> tst_sk_2 = multicast.recv.joinstep([tst_fxtr], 59991)
260+
>>> tst_sk_2 #doctest: -DONT_ACCEPT_BLANKLINE, +ELLIPSIS
250261
<socket.socket...>
251-
>>> multicast.recv.joinstep(
262+
>>> multicast.endSocket(tst_sk_2)
263+
>>> tst_sk_2 #doctest: -DONT_ACCEPT_BLANKLINE, +ELLIPSIS
264+
<socket.socket [closed]...>
265+
>>> tst_sk_3 = multicast.recv.joinstep(
252266
... [tst_fxtr], 59991, None, tst_fxtr
253267
... ) #doctest: -DONT_ACCEPT_BLANKLINE, +ELLIPSIS
268+
>>> tst_sk_3 #doctest: -DONT_ACCEPT_BLANKLINE, +ELLIPSIS
254269
<socket.socket...>
270+
>>> multicast.endSocket(tst_sk_3)
271+
>>> tst_sk_3 #doctest: -DONT_ACCEPT_BLANKLINE, +ELLIPSIS
272+
<socket.socket [closed]...>
255273
>>> sk_fxtr = multicast.genSocket()
256-
>>> multicast.recv.joinstep(
274+
>>> tst_sk_4 = multicast.recv.joinstep(
257275
... [tst_fxtr], 59991, None, tst_fxtr, sk_fxtr
258-
... ) #doctest: -DONT_ACCEPT_BLANKLINE, +ELLIPSIS
276+
... )
277+
>>> tst_sk_4 #doctest: -DONT_ACCEPT_BLANKLINE, +ELLIPSIS
259278
<socket.socket...>
279+
>>> multicast.endSocket(tst_sk_4)
280+
>>> tst_sk_4 #doctest: -DONT_ACCEPT_BLANKLINE, +ELLIPSIS
281+
<socket.socket [closed]...>
260282
>>> sk_fxtr.close()
261283
>>>
262284

pyproject.toml

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,32 @@ extend-ignore = ["E117", "D203", "D208", "D212"]
2020
max-line-length = 100
2121
docstring-convention = "google"
2222

23-
[pytest.enabler.flake8]
23+
[tool.pytest]
24+
testpaths = "tests"
25+
python_files = "test_*.py"
26+
27+
[tool.pytest.markers]
28+
mat = "minimum acceptance tests"
29+
mat_basic = "basic component tests"
30+
mat_doctests = "documentation tests"
31+
mat_say = "send.py focused tests"
32+
mat_hear = "recv.py and hear.py focused tests"
33+
mat_usage = "__main__.py and API tests"
34+
mat_build = "build and packaging tests"
35+
mat_bootstrap = "init/exceptions/env/skt tests"
36+
extra = "additional important tests"
37+
extra_coverage = "coverage-focused tests"
38+
extra_linting = "linting tests"
39+
extra_security = "security tests"
40+
fuzzing = "fuzzing tests"
41+
fuzzing_basic = "smaller sub-set of fuzzing tests"
42+
performance = "performance and scalability tests"
43+
44+
[tool.pytest.enabler.flake8]
2445
addopts = "--flake8"
2546

26-
[pytest.enabler.doctest]
47+
[tool.pytest.enabler.doctest]
2748
addopts = "--doctest-glob=**/*.py --doctest-modules"
2849

29-
[pytest.enabler.cov]
50+
[tool.pytest.enabler.cov]
3051
addopts = "--cov=. --cov-append --cov-report=xml --junitxml=test-reports/junit.xml"

pytest.ini

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[pytest]
2+
addopts = --doctest-modules --cov=multicast --cov-append --cov-report=xml
3+
testpaths = tests
4+
python_files = test_*.py
5+
markers =
6+
mat: minimum acceptance tests
7+
mat_basic: basic component tests
8+
mat_doctests: documentation tests
9+
mat_say: send.py focused tests
10+
mat_hear: recv.py and hear.py focused tests
11+
mat_usage: __main__.py and API tests
12+
mat_build: build and packaging tests
13+
mat_bootstrap: init/exceptions/env/skt tests
14+
extra: additional important tests
15+
extra_coverage: coverage-focused tests
16+
extra_linting: linting tests
17+
extra_security: security tests
18+
fuzzing: fuzzing tests
19+
fuzzing_basic: smaller sub-set of fuzzing tests
20+
performance: performance and scalability tests

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
# setuptools - MIT license
2828
setuptools>=75.0
2929
# virtualenv - MIT license
30-
#virtualenv>=15.0.1
30+
#virtualenv>=20.26.6
3131
# pip - MIT license
3232
pip>=24.3.1
3333
# build - MIT license

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ testing =
8181
pytest-enabler >= 1.0.1
8282
# local
8383
flake8 >= 5.0
84-
virtualenv >= 20.26.5
84+
virtualenv >= 20.26.6
8585
wheel >= 0.44.0
8686
pip >= 24.3.1
8787
pytest-cov >= 4.0.0; \

0 commit comments

Comments
 (0)