返回 CodeWhale
test_check_blocking_calls_budget.py
根目录 / scripts / test_check_blocking_calls_budget.py
1 #!/usr/bin/env python3
2 """Hermetic tests for the blocking-calls budget gate (#6149)."""
3
4 from __future__ import annotations
5
6 import importlib.util
7 import sys
8 import tempfile
9 import unittest
10 from pathlib import Path
11
12 ROOT = Path(__file__).resolve().parents[1]
13 SCRIPT = ROOT / "scripts" / "check-blocking-calls-budget.py"
14 SPEC = importlib.util.spec_from_file_location("blocking_calls", SCRIPT)
15 assert SPEC and SPEC.loader
16 mod = importlib.util.module_from_spec(SPEC)
17 sys.modules[SPEC.name] = mod
18 SPEC.loader.exec_module(mod)
19
20
21 def counts(source: str) -> dict[str, int]:
22 with tempfile.TemporaryDirectory() as tmp:
23 victim = Path(tmp) / "victim.rs"
24 victim.write_text(source, encoding="utf-8")
25 return mod.file_counts(victim)
26
27
28 class BlockingCallScopeTests(unittest.TestCase):
29 def test_sleep_in_plain_fn_counts(self) -> None:
30 self.assertEqual(
31 counts("fn wait() {\n std::thread::sleep(std::time::Duration::from_millis(1));\n}\n"),
32 {"thread_sleep": 1},
33 )
34
35 def test_sleep_in_async_fn_counts(self) -> None:
36 self.assertEqual(
37 counts("async fn run() {\n std::thread::sleep(std::time::Duration::from_millis(1));\n}\n"),
38 {"thread_sleep": 1},
39 )
40
41 def test_sleep_in_spawn_blocking_is_exempt(self) -> None:
42 self.assertEqual(
43 counts(
44 "async fn run() {\n"
45 " tokio::task::spawn_blocking(move || {\n"
46 " std::thread::sleep(std::time::Duration::from_millis(1));\n"
47 " });\n"
48 "}\n"
49 ),
50 {},
51 )
52
53 def test_sleep_in_dedicated_thread_is_exempt(self) -> None:
54 self.assertEqual(
55 counts(
56 "fn pump() {\n"
57 " std::thread::Builder::new().spawn(move || {\n"
58 " std::thread::sleep(std::time::Duration::from_millis(5));\n"
59 " });\n"
60 "}\n"
61 ),
62 {},
63 )
64
65 def test_sleep_in_tests_mod_is_exempt(self) -> None:
66 self.assertEqual(
67 counts(
68 "fn prod() {}\n"
69 "#[cfg(test)]\n"
70 "mod tests {\n"
71 " fn probe() { std::thread::sleep(std::time::Duration::from_millis(1)); }\n"
72 "}\n"
73 ),
74 {},
75 )
76
77 def test_sleep_in_cfg_test_fn_is_exempt(self) -> None:
78 self.assertEqual(
79 counts(
80 "#[cfg(test)]\n"
81 "fn helper() { std::thread::sleep(std::time::Duration::from_millis(1)); }\n"
82 ),
83 {},
84 )
85
86 def test_std_fs_call_counts(self) -> None:
87 self.assertEqual(
88 counts("async fn go() {\n let _ = std::fs::read_to_string(p).unwrap();\n}\n"),
89 {"std_fs": 1},
90 )
91
92 def test_comment_and_string_literals_do_not_count(self) -> None:
93 self.assertEqual(
94 counts(
95 "fn doc() {\n"
96 " // std::thread::sleep(std::time::Duration::from_millis(1));\n"
97 ' let s = "std::fs::read_to_string(p)";\n'
98 " let t = r#\"std::fs::write(a, b)\"#;\n"
99 "}\n"
100 ),
101 {},
102 )
103
104 def test_tokio_equivalents_do_not_count(self) -> None:
105 self.assertEqual(
106 counts(
107 "async fn go() {\n"
108 " tokio::time::sleep(std::time::Duration::from_millis(1)).await;\n"
109 " let _ = tokio::fs::read_to_string(p).await;\n"
110 "}\n"
111 ),
112 {},
113 )
114
115
116 class CfgTestModuleExclusion(unittest.TestCase):
117 """A file that is wholly a `#[cfg(test)]` module is test code (#6149).
118
119 The per-file scanner only sees test scope declared *inside* a file, so an
120 extracted test suite looked like brand-new unprotected call sites even
121 though nothing moved onto an async path. PR #6096's
122 `session_export_*_tests.rs` reddened `main` this way.
123 """
124
125 def _crates(self, tmp: Path, files: dict[str, str]) -> Path:
126 crates = tmp / "crates" / "demo" / "src"
127 crates.mkdir(parents=True)
128 for name, body in files.items():
129 (crates / name).write_text(body, encoding="utf-8")
130 return tmp / "crates"
131
132 def test_whole_file_cfg_test_module_is_excluded(self) -> None:
133 with tempfile.TemporaryDirectory() as tmp:
134 root = Path(tmp)
135 crates = self._crates(
136 root,
137 {
138 "lib.rs": "#[cfg(test)]\nmod suite;\n",
139 "suite.rs": "fn helper() { let _ = std::fs::read_to_string(p); }\n",
140 },
141 )
142 original = mod.CRATES
143 try:
144 mod.CRATES = crates
145 excluded = mod.cfg_test_module_files()
146 finally:
147 mod.CRATES = original
148 self.assertIn((crates / "demo" / "src" / "suite.rs").resolve(), excluded)
149
150 def test_plain_mod_declaration_is_not_excluded(self) -> None:
151 with tempfile.TemporaryDirectory() as tmp:
152 root = Path(tmp)
153 crates = self._crates(
154 root,
155 {
156 "lib.rs": "mod production;\n",
157 "production.rs": "fn helper() { let _ = std::fs::read_to_string(p); }\n",
158 },
159 )
160 original = mod.CRATES
161 try:
162 mod.CRATES = crates
163 excluded = mod.cfg_test_module_files()
164 finally:
165 mod.CRATES = original
166 self.assertNotIn(
167 (crates / "demo" / "src" / "production.rs").resolve(), excluded
168 )
169
170
171 if __name__ == "__main__":
172 unittest.main()
173
173 lines PYTHON