|
3 | 3 | # SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE |
4 | 4 |
|
5 | 5 | import abc |
| 6 | +import time |
6 | 7 |
|
7 | 8 | import pytest |
8 | 9 |
|
@@ -376,3 +377,60 @@ def test_sqlite_cache_accepts_str_keys(tmp_path): |
376 | 377 | assert "str-key" in cache |
377 | 378 | # Same bytes representation so the corresponding bytes key also hits. |
378 | 379 | assert b"str-key" in cache |
| 380 | + |
| 381 | + |
| 382 | +def test_sqlite_cache_rejects_negative_size_cap(tmp_path): |
| 383 | + from cuda.core.utils import SQLiteProgramCache |
| 384 | + |
| 385 | + with pytest.raises(ValueError, match="non-negative"): |
| 386 | + SQLiteProgramCache(tmp_path / "cache.db", max_size_bytes=-1) |
| 387 | + |
| 388 | + |
| 389 | +# --------------------------------------------------------------------------- |
| 390 | +# SQLiteProgramCache -- LRU eviction |
| 391 | +# --------------------------------------------------------------------------- |
| 392 | + |
| 393 | + |
| 394 | +def test_sqlite_cache_evicts_under_size_cap(tmp_path): |
| 395 | + from cuda.core.utils import SQLiteProgramCache |
| 396 | + |
| 397 | + # Each payload pickles to > 2000 bytes; cap is 5000 so only ~2 fit. |
| 398 | + cap = 5000 |
| 399 | + db = tmp_path / "cache.db" |
| 400 | + with SQLiteProgramCache(db, max_size_bytes=cap) as cache: |
| 401 | + cache[b"a"] = _fake_object_code(b"A" * 2000, name="a") |
| 402 | + cache[b"b"] = _fake_object_code(b"B" * 2000, name="b") |
| 403 | + cache[b"c"] = _fake_object_code(b"C" * 2000, name="c") |
| 404 | + # Adding c must have evicted a (oldest by accessed_at). |
| 405 | + assert b"a" not in cache |
| 406 | + assert b"b" in cache |
| 407 | + assert b"c" in cache |
| 408 | + |
| 409 | + |
| 410 | +def test_sqlite_cache_lru_order_respects_reads(tmp_path): |
| 411 | + from cuda.core.utils import SQLiteProgramCache |
| 412 | + |
| 413 | + cap = 5000 |
| 414 | + db = tmp_path / "cache.db" |
| 415 | + with SQLiteProgramCache(db, max_size_bytes=cap) as cache: |
| 416 | + cache[b"a"] = _fake_object_code(b"A" * 2000, name="a") |
| 417 | + time.sleep(0.01) |
| 418 | + cache[b"b"] = _fake_object_code(b"B" * 2000, name="b") |
| 419 | + time.sleep(0.01) |
| 420 | + # Touch 'a' so it becomes MRU; 'b' must be evicted when 'c' is added. |
| 421 | + _ = cache[b"a"] |
| 422 | + time.sleep(0.01) |
| 423 | + cache[b"c"] = _fake_object_code(b"C" * 2000, name="c") |
| 424 | + assert b"a" in cache |
| 425 | + assert b"b" not in cache |
| 426 | + assert b"c" in cache |
| 427 | + |
| 428 | + |
| 429 | +def test_sqlite_cache_unbounded_by_default(tmp_path): |
| 430 | + from cuda.core.utils import SQLiteProgramCache |
| 431 | + |
| 432 | + db = tmp_path / "cache.db" |
| 433 | + with SQLiteProgramCache(db) as cache: |
| 434 | + for i in range(25): |
| 435 | + cache[f"k{i}".encode()] = _fake_object_code(b"X" * 1024, name=f"n{i}") |
| 436 | + assert len(cache) == 25 |
0 commit comments