From 6f5e6ca32c21b9c43d902c6306aed636497028b4 Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Fri, 31 Jul 2026 01:36:11 -0700 Subject: [PATCH 1/2] fix(test): mock the DEEPGEMM_RUBIN pin in test_get_subdir_file_list #4280 added ArtifactPath.DEEPGEMM_RUBIN to the cubin_dirs list walked by get_subdir_file_list(), and registered `responses` mocks for the two other new Rubin pins (TRTLLM_GEN_BMM_RUBIN / TRTLLM_GEN_GEMM_RUBIN) but not for the deep-gemm one. Under @responses.activate the unregistered URL raises ConnectionError, so download_file() exhausts its retries and returns False, and get_checksums() then open()s a checksums.txt that was never written: FileNotFoundError: .../cubins/7ec7ac40.../deep-gemm/checksums.txt The artifact itself is fine -- the live manifest SHA matches the pin in CheckSumHash.DEEPGEMM_RUBIN; only the unit test's mock registry was stale. Changes: - Register the DEEPGEMM_RUBIN directory index and checksums.txt, mirroring the BMM/GEMM Rubin pattern (same index body, distinct per-pin hashes). - Extend the per-pin-hash-collision assertion to a shared deep-gemm kernel name, so deep-gemm gets the coverage the trtllm-gen pins already have. - get_checksums(): raise a RuntimeError naming the pin and URL when the manifest download fails, instead of falling through to a bare FileNotFoundError on a local cache path that hides the real cause. AI-assisted (Claude Code): diagnosis and patch. Co-Authored-By: Claude Opus 5 (1M context) --- flashinfer/artifacts.py | 10 +++++++++- tests/test_artifacts.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/flashinfer/artifacts.py b/flashinfer/artifacts.py index 246b6a3f8bf..58ff03065d4 100644 --- a/flashinfer/artifacts.py +++ b/flashinfer/artifacts.py @@ -226,7 +226,15 @@ def get_checksums(subdirs): FLASHINFER_CUBINS_REPOSITORY, safe_urljoin(subdir, "checksums.txt") ) checksum_path = FLASHINFER_CUBIN_DIR / safe_urljoin(subdir, "checksums.txt") - download_file(uri, checksum_path) + if not download_file(uri, checksum_path) and not checksum_path.is_file(): + # Without this the next open() fails with a bare FileNotFoundError on + # the local cache path, which hides the real cause: the artifact pin + # is unreachable (typo'd/unpublished pin, or network/mirror failure). + raise RuntimeError( + f"Failed to fetch the checksum manifest for artifact pin '{subdir}' " + f"from {uri}. Check that the pin exists in " + f"{FLASHINFER_CUBINS_REPOSITORY} and is reachable." + ) with open(checksum_path, "r") as f: for line in f: sha256, filename = line.strip().split() diff --git a/tests/test_artifacts.py b/tests/test_artifacts.py index 2c658694b84..558e143d9a3 100644 --- a/tests/test_artifacts.py +++ b/tests/test_artifacts.py @@ -239,6 +239,12 @@ def _mock_file_index_responses(): responses.add( responses.GET, gemm_rubin_source, body=success_gemm_response, status=200 ) + deepgemm_rubin_source = safe_urljoin( + test_cubin_repository, artifact_paths.DEEPGEMM_RUBIN + ) + responses.add( + responses.GET, deepgemm_rubin_source, body=success_deepgemm_response, status=200 + ) @responses.activate @@ -347,6 +353,13 @@ def test_get_subdir_file_list(monkeypatch, tmp_path): d7e8f9a0b1c2 kernel.fp8_m_grouped_gemm.007d9ebdca7e.cubin e8f9a0b1c2d3 kernel.fp8_m_grouped_gemm.02acb2ba71fd.cubin f9a0b1c2d3e4 kernel.fp8_m_grouped_gemm.0457375eb02f.cubin +""" + + checksums_deepgemm_rubin = """3333333333333333333333333333333333333333333333333333333333333333 kernel_map.json +1111aaaabbbbcccc kernel.fp8_m_grouped_gemm.007404769193.cubin +2222aaaabbbbcccc kernel.fp8_m_grouped_gemm.007d9ebdca7e.cubin +3333aaaabbbbcccc kernel.fp8_m_grouped_gemm.02acb2ba71fd.cubin +4444aaaabbbbcccc kernel.fp8_m_grouped_gemm.0457375eb02f.cubin """ # Add mock responses for checksums.txt files @@ -391,6 +404,17 @@ def test_get_subdir_file_list(monkeypatch, tmp_path): responses.GET, deepgemm_checksums_url, body=checksums_deepgemm, status=200 ) + deepgemm_rubin_checksums_url = safe_urljoin( + test_cubin_repository, + safe_urljoin(artifact_paths.DEEPGEMM_RUBIN, "checksums.txt"), + ) + responses.add( + responses.GET, + deepgemm_rubin_checksums_url, + body=checksums_deepgemm_rubin, + status=200, + ) + # Mock DSL_FMHA checksums + directory index for the host cpu_arch. # Pin to x86_64 so the test is deterministic regardless of the runner arch. monkeypatch.setattr(artifacts, "_get_host_cpu_arch", lambda: "x86_64") @@ -480,6 +504,11 @@ def test_get_subdir_file_list(monkeypatch, tmp_path): artifact_paths.TRTLLM_GEN_GEMM, artifact_paths.TRTLLM_GEN_GEMM_RUBIN, ), + ( + "kernel.fp8_m_grouped_gemm.007d9ebdca7e.cubin", + artifact_paths.DEEPGEMM, + artifact_paths.DEEPGEMM_RUBIN, + ), ): plain_path = safe_urljoin(plain_dir, shared_name) rubin_path = safe_urljoin(rubin_dir, shared_name) From 7b1981e62429e9136fc081ea9c25acf184d9371d Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Fri, 31 Jul 2026 01:48:41 -0700 Subject: [PATCH 2/2] test(artifacts): cover unreachable-pin and cached-manifest paths Adds the negative-path coverage the mock fix implies: - test_get_checksums_unreachable_pin_raises: a pin whose manifest cannot be fetched must raise, and the error must name the pin. Stubs download_file instead of mocking HTTP so the test does not pay 4 rounds of exponential backoff (~70s), which is what made the original CI failure so slow. - test_get_checksums_falls_back_to_cached_manifest: pins the case that *should* degrade gracefully -- a failed refresh with a manifest already on disk still works, so the new raise does not break offline / FLASHINFER_NO_DOWNLOAD setups. AI-assisted (Claude Code). Co-Authored-By: Claude Opus 5 (1M context) --- tests/test_artifacts.py | 45 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/test_artifacts.py b/tests/test_artifacts.py index 558e143d9a3..6a20ea63acc 100644 --- a/tests/test_artifacts.py +++ b/tests/test_artifacts.py @@ -4,6 +4,7 @@ get_subdir_file_list, ) +import pytest import responses from flashinfer.jit.cubin_loader import safe_urljoin @@ -299,6 +300,50 @@ def test_get_available_cubin_files_non_200_response(): assert available_cubin_files == () +def test_get_checksums_unreachable_pin_raises(monkeypatch, tmp_path): + """An artifact pin whose checksums.txt cannot be fetched must fail loudly. + + Guards the diagnosis path exercised by #4280: a pin added to `cubin_dirs` + without a published (or, in tests, mocked) manifest used to surface as a bare + FileNotFoundError on a local cache path, which reads like a corrupt cache + rather than an unreachable pin. `download_file` is stubbed rather than mocked + over HTTP so the test does not pay its 4 retries of exponential backoff. + """ + from flashinfer import artifacts + + monkeypatch.setattr(artifacts, "FLASHINFER_CUBIN_DIR", tmp_path / "cubins") + monkeypatch.setattr(artifacts, "download_file", lambda *args, **kwargs: False) + + with pytest.raises(RuntimeError) as excinfo: + artifacts.get_checksums([artifact_paths.DEEPGEMM_RUBIN]) + # The pin must be named -- that is the whole point of the error. + assert artifact_paths.DEEPGEMM_RUBIN in str(excinfo.value) + + +def test_get_checksums_falls_back_to_cached_manifest(monkeypatch, tmp_path): + """A failed refresh must not invalidate an already-cached manifest. + + Offline / FLASHINFER_NO_DOWNLOAD setups rely on the on-disk copy. + """ + from flashinfer import artifacts + + cubin_dir = tmp_path / "cubins" + monkeypatch.setattr(artifacts, "FLASHINFER_CUBIN_DIR", cubin_dir) + monkeypatch.setattr(artifacts, "download_file", lambda *args, **kwargs: False) + + cached = cubin_dir / safe_urljoin(artifact_paths.DEEPGEMM_RUBIN, "checksums.txt") + cached.parent.mkdir(parents=True) + cached.write_text("abc123 kernel.fp8_m_grouped_gemm.007d9ebdca7e.cubin\n") + + checksums = artifacts.get_checksums([artifact_paths.DEEPGEMM_RUBIN]) + assert checksums == { + safe_urljoin( + artifact_paths.DEEPGEMM_RUBIN, + "kernel.fp8_m_grouped_gemm.007d9ebdca7e.cubin", + ): "abc123" + } + + @responses.activate def test_get_subdir_file_list(monkeypatch, tmp_path): _mock_file_index_responses()