From 40bbbfa847b7ffe6302875f0d60c524639e2d677 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Fri, 18 Sep 2026 19:21:43 +0000 Subject: [PATCH 1/6] feat(crc32c): add unit and cover nox sessions --- packages/google-crc32c/.coveragerc | 4 ++- packages/google-crc32c/noxfile.py | 28 ++++++++++++++++--- .../google-crc32c/src/google_crc32c/cext.py | 6 ++-- packages/google-crc32c/tests/test___init__.py | 9 ++++-- 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/packages/google-crc32c/.coveragerc b/packages/google-crc32c/.coveragerc index ab4a10fdea4b..75185c2e7aa1 100644 --- a/packages/google-crc32c/.coveragerc +++ b/packages/google-crc32c/.coveragerc @@ -17,12 +17,14 @@ # Generated by synthtool. DO NOT EDIT! [run] branch = True +source = + src/google_crc32c omit = google/__init__.py google/cloud/__init__.py [report] -fail_under = 0 +fail_under = 100 show_missing = True exclude_lines = # Re-enable the standard pragma diff --git a/packages/google-crc32c/noxfile.py b/packages/google-crc32c/noxfile.py index 2a268734c5b5..df0acb7e797e 100644 --- a/packages/google-crc32c/noxfile.py +++ b/packages/google-crc32c/noxfile.py @@ -41,6 +41,7 @@ "blacken", "format", "lint_setup_py", + "cover", "mypy", "prerelease_deps", "core_deps_from_source", @@ -67,11 +68,13 @@ def build_libcrc32c(session): @nox.session(python=UNIT_TEST_PYTHON_VERSIONS) def check(session): - session.install("pytest") + session.install("pytest", "pytest-cov") session.install("--no-index", f"--find-links={HERE}/wheels", "google-crc32c") # Run py.test against the unit tests. - session.run("py.test", "tests") + session.run( + "pytest", "--cov=google_crc32c", "--cov=tests", "tests", *session.posargs + ) session.run("python", f"{HERE}/scripts/check_crc32c_extension.py", *session.posargs) @@ -168,10 +171,27 @@ def core_deps_from_source(session): session.skip("Core deps from source tests are not yet supported") -@nox.session(python=ALL_PYTHON) +@nox.session(python=UNIT_TEST_PYTHON_VERSIONS) def unit(session): """Run all unit tests.""" - session.skip("Unit tests are not supported") + session.env["CRC32C_PURE_PYTHON"] = "1" + session.install("pytest", "pytest-cov") + session.install("-e", ".") + session.run( + "pytest", "--cov=google_crc32c", "--cov=tests", "tests", *session.posargs + ) + + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.install("coverage", "pytest-cov") + session.run("coverage", "report", "--show-missing", "--fail-under=100") + session.run("coverage", "erase") @nox.session(python="3.10") diff --git a/packages/google-crc32c/src/google_crc32c/cext.py b/packages/google-crc32c/src/google_crc32c/cext.py index 1ace01c48106..31895740fe75 100644 --- a/packages/google-crc32c/src/google_crc32c/cext.py +++ b/packages/google-crc32c/src/google_crc32c/cext.py @@ -16,8 +16,10 @@ # modify the search path used to locate shared libraries. import google_crc32c.__config__ # noqa: F401 from google_crc32c._checksum import CommonChecksum -from google_crc32c._crc32c import extend # type: ignore -from google_crc32c._crc32c import value # type: ignore +from google_crc32c._crc32c import ( + extend, # type: ignore + value, # type: ignore +) class Checksum(CommonChecksum): diff --git a/packages/google-crc32c/tests/test___init__.py b/packages/google-crc32c/tests/test___init__.py index 68bed918ffa4..aca1254fde43 100644 --- a/packages/google-crc32c/tests/test___init__.py +++ b/packages/google-crc32c/tests/test___init__.py @@ -206,10 +206,13 @@ def _crc32c(request): return python elif request.param == "cext": - from google_crc32c import cext + try: + from google_crc32c import cext - return cext - else: + return cext # pragma: NO COVER + except ImportError: # pragma: NO COVER + pytest.skip("C extension not compiled") # pragma: NO COVER + else: # pragma: NO COVER raise ValueError("invalid internal test config") From 851f38ffb95f91c05c649633e661231459e434cf Mon Sep 17 00:00:00 2001 From: ohmayr Date: Sat, 19 Sep 2026 04:56:47 +0000 Subject: [PATCH 2/6] fix(crc32c): fix mypy import ignore in cext.py --- packages/google-crc32c/src/google_crc32c/cext.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/google-crc32c/src/google_crc32c/cext.py b/packages/google-crc32c/src/google_crc32c/cext.py index 31895740fe75..ec22abec5c1c 100644 --- a/packages/google-crc32c/src/google_crc32c/cext.py +++ b/packages/google-crc32c/src/google_crc32c/cext.py @@ -16,9 +16,9 @@ # modify the search path used to locate shared libraries. import google_crc32c.__config__ # noqa: F401 from google_crc32c._checksum import CommonChecksum -from google_crc32c._crc32c import ( - extend, # type: ignore - value, # type: ignore +from google_crc32c._crc32c import ( # type: ignore + extend, + value, ) From a7e476882891f1f12c6230b7aa6b3fd87ca5f71b Mon Sep 17 00:00:00 2001 From: ohmayr Date: Sat, 19 Sep 2026 06:25:32 +0000 Subject: [PATCH 3/6] chore(crc32c): update nox session skip messages Remove outdated TODO comments and specify technical rationale for skipping core and prerelease dependency sessions. --- packages/google-crc32c/noxfile.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/google-crc32c/noxfile.py b/packages/google-crc32c/noxfile.py index c6ef084fdd83..0f392385a3c1 100644 --- a/packages/google-crc32c/noxfile.py +++ b/packages/google-crc32c/noxfile.py @@ -177,9 +177,9 @@ def lint_setup_py(session): @nox.session(python=DEFAULT_PYTHON_VERSION) def prerelease_deps(session): """Run all tests with prerelease versions of dependencies installed.""" - # TODO(https://github.com/googleapis/google-cloud-python/issues/16014): - # Add prerelease deps tests - session.skip("prerelease deps tests are not yet supported") + session.skip( + "prerelease_deps session is not applicable as google-crc32c has no runtime dependencies" + ) @nox.session(python=DEFAULT_PYTHON_VERSION) @@ -187,9 +187,9 @@ def core_deps_from_source(session): """Run all tests with core dependencies installed from source rather than pulling the dependencies from PyPI. """ - # TODO(https://github.com/googleapis/google-cloud-python/issues/16014): - # Add core deps from source tests - session.skip("Core deps from source tests are not yet supported") + session.skip( + "core_deps_from_source session is not applicable as google-crc32c has no core dependencies" + ) @nox.session(python=UNIT_TEST_PYTHON_VERSIONS) From c5dc129cdf01b520d502f73e92fffc7af9212bfe Mon Sep 17 00:00:00 2001 From: ohmayr Date: Sat, 19 Sep 2026 06:37:39 +0000 Subject: [PATCH 4/6] ci: re-trigger presubmits From 45676e443783690cf83bf9a8118f453d7a0b30fb Mon Sep 17 00:00:00 2001 From: ohmayr Date: Mon, 21 Sep 2026 19:46:34 +0000 Subject: [PATCH 5/6] test(crc32c): ensure unit tests measure package coverage Configure PYTHONPATH to point directly to src/ and target coverage at src/google_crc32c to avoid editable install issues. Add unit tests for array checksums and CommonChecksum interface contract, document omissions for Windows DLL helper and C extension wrapper in .coveragerc, and annotate pure-Python C fallback import with pragma NO COVER. Fixes #17052 --- packages/google-crc32c/.coveragerc | 8 +++++++ packages/google-crc32c/noxfile.py | 6 ++--- .../src/google_crc32c/__init__.py | 7 +++--- packages/google-crc32c/tests/test___init__.py | 23 +++++++++++++++++++ 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/packages/google-crc32c/.coveragerc b/packages/google-crc32c/.coveragerc index 75185c2e7aa1..35f44c901a9b 100644 --- a/packages/google-crc32c/.coveragerc +++ b/packages/google-crc32c/.coveragerc @@ -22,6 +22,10 @@ source = omit = google/__init__.py google/cloud/__init__.py + # Windows-only DLL bootstrap logic (not executed on Linux/macOS) + src/google_crc32c/__config__.py + # Uncompiled C extension wrapper (exercised in wheel check session, not pure-Python unit tests) + src/google_crc32c/cext.py [report] fail_under = 100 @@ -39,3 +43,7 @@ omit = */core/*.py */site-packages/*.py google/cloud/__init__.py + # Windows-only DLL bootstrap logic (not executed on Linux/macOS) + src/google_crc32c/__config__.py + # Uncompiled C extension wrapper (exercised in wheel check session, not pure-Python unit tests) + src/google_crc32c/cext.py diff --git a/packages/google-crc32c/noxfile.py b/packages/google-crc32c/noxfile.py index 0f392385a3c1..bf0c56e740c4 100644 --- a/packages/google-crc32c/noxfile.py +++ b/packages/google-crc32c/noxfile.py @@ -196,11 +196,9 @@ def core_deps_from_source(session): def unit(session): """Run all unit tests.""" session.env["CRC32C_PURE_PYTHON"] = "1" + session.env["PYTHONPATH"] = "src" session.install("pytest", "pytest-cov") - session.install("-e", ".") - session.run( - "pytest", "--cov=google_crc32c", "--cov=tests", "tests", *session.posargs - ) + session.run("pytest", "--cov=src/google_crc32c", "tests", *session.posargs) @nox.session(python=DEFAULT_PYTHON_VERSION) diff --git a/packages/google-crc32c/src/google_crc32c/__init__.py b/packages/google-crc32c/src/google_crc32c/__init__.py index bba10d5c4b1c..0acf5476ba2f 100644 --- a/packages/google-crc32c/src/google_crc32c/__init__.py +++ b/packages/google-crc32c/src/google_crc32c/__init__.py @@ -20,11 +20,12 @@ "please configure a c build environment and compile the extension" ) -# Default to C exstension Implementation, falling back to pure python. +# Default to C extension implementation, falling back to pure Python. try: - from google_crc32c import cext as impl + # Pure-Python unit test sessions test python.py; cext is tested during wheel checks. + from google_crc32c import cext as impl # pragma: NO COVER - implementation = "c" + implementation = "c" # pragma: NO COVER except ImportError: from google_crc32c import python as impl # type: ignore diff --git a/packages/google-crc32c/tests/test___init__.py b/packages/google-crc32c/tests/test___init__.py index aca1254fde43..8c43558b7afa 100644 --- a/packages/google-crc32c/tests/test___init__.py +++ b/packages/google-crc32c/tests/test___init__.py @@ -228,6 +228,14 @@ def test_ctor_explicit(_crc32c): helper = google_crc32c.Checksum(chunk) assert helper._crc == google_crc32c.value(chunk) + @staticmethod + def test_ctor_array(_crc32c): + import array + + chunk = array.array("B", b"DEADBEEF") + helper = google_crc32c.Checksum(chunk) + assert helper._crc == google_crc32c.value(b"DEADBEEF") + @staticmethod def test_update(_crc32c): chunk = b"DEADBEEF" @@ -290,3 +298,18 @@ def test_consume_stream(_crc32c, chunksize): assert found == expected for call in stream.read.call_args_list: assert call == mock.call(chunksize) + + +def test_common_checksum(): + from google_crc32c._checksum import CommonChecksum + + class DummyChecksum(CommonChecksum): + __slots__ = ("_crc",) + + checksum = DummyChecksum() + assert checksum._crc == 0 + with pytest.raises(NotImplementedError): + checksum.update(b"data") + + with pytest.raises(NotImplementedError): + DummyChecksum(b"foo") From 8e20d65fa8f7ac84ca0d610d351ff505690c774a Mon Sep 17 00:00:00 2001 From: ohmayr Date: Mon, 21 Sep 2026 19:58:36 +0000 Subject: [PATCH 6/6] fix(crc32c): test array buffer support on pure-python implementation Directly test array.array buffer updating against the python Checksum implementation instead of the parameterized _crc32c fixture, avoiding TypeError when executed against the C-extension on Kokoro runners. --- packages/google-crc32c/tests/test___init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/google-crc32c/tests/test___init__.py b/packages/google-crc32c/tests/test___init__.py index 8c43558b7afa..8d108ac91657 100644 --- a/packages/google-crc32c/tests/test___init__.py +++ b/packages/google-crc32c/tests/test___init__.py @@ -229,12 +229,15 @@ def test_ctor_explicit(_crc32c): assert helper._crc == google_crc32c.value(chunk) @staticmethod - def test_ctor_array(_crc32c): + def test_update_array(): import array + from google_crc32c import python + chunk = array.array("B", b"DEADBEEF") - helper = google_crc32c.Checksum(chunk) - assert helper._crc == google_crc32c.value(b"DEADBEEF") + helper = python.Checksum() + helper.update(chunk) + assert helper._crc == python.value(b"DEADBEEF") @staticmethod def test_update(_crc32c):