scripts/tests/proof_generator/test_infer_extract_functions.py
scripts/tests/proof_generator/test_infer_extract_functions.pyBrowse 41 files
2,488 tokens
10,649 bytes
Token encoding: o200k_base
Snapshot a9fb1c3
← Back to SKILL.md
1import subprocess2import sys3from pathlib import Path4 5import pytest6 7sys.path.insert(0, str(Path(__file__).resolve().parents[2]))8 9from generator_testlib import _commit, _git, _write # noqa: F40110from mechanical_refactor_proof_generator import (11 build_repro,12 infer_recipe,13 recipe_to_script,14)15 16 17def test_infer_extract_function_with_returned_local(repo: Path) -> None:18 """A block ending in ``pool = make(...)`` carved into a helper that returns ``pool`` infers19 an extract_function whose body is the verbatim block and whose return_text is authored.20 """21 _write(22 repo,23 **{24 "kv.py": (25 "class C:\n"26 " def dispatch(self, n):\n"27 " base = self.setup()\n"28 " if self.flag:\n"29 " x = self.a\n"30 " y = x + n\n"31 " pool = make(\n"32 " a=x,\n"33 " b=y,\n"34 " )\n"35 " return pool\n"36 "\n"37 " def keep(self):\n"38 " return 0\n"39 )40 },41 )42 _commit(repo, "base")43 _write(44 repo,45 **{46 "kv.py": (47 "class C:\n"48 " def dispatch(self, n):\n"49 " base = self.setup()\n"50 " if self.flag:\n"51 " pool = self._build_pool(n=n)\n"52 " return pool\n"53 "\n"54 " def _build_pool(self, *, n):\n"55 " x = self.a\n"56 " y = x + n\n"57 " pool = make(\n"58 " a=x,\n"59 " b=y,\n"60 " )\n"61 " return pool\n"62 "\n"63 " def keep(self):\n"64 " return 0\n"65 )66 },67 )68 _commit(repo, "extract _build_pool from dispatch")69 recipe = infer_recipe("HEAD", str(repo))70 assert recipe.supported71 assert recipe.moves == []72 assert len(recipe.extract_functions) == 173 ex = recipe.extract_functions[0]74 assert ex["name"] == "_build_pool"75 assert ex["src"] == "kv.py" and ex["dst"] == "kv.py"76 assert ex["into_class"] == "C"77 assert ex["before"] == "keep"78 assert ex["body_indent"] == 1279 assert ex["body"] == (80 " x = self.a\n"81 " y = x + n\n"82 " pool = make(\n"83 " a=x,\n"84 " b=y,\n"85 " )\n"86 )87 assert ex["call"] == " pool = self._build_pool(n=n)\n"88 assert ex["return_text"] == " return pool"89 assert ex["signature"] == " def _build_pool(self, *, n):\n"90 91 92def test_infer_extract_function_keeps_leading_comment_in_body(repo: Path) -> None:93 """A block whose first line is a comment extracts with that comment in the body, not94 absorbed into the authored signature (which is the def header through its colon only).95 """96 _write(97 repo,98 **{99 "kv.py": (100 "class C:\n"101 " def dispatch(self, n):\n"102 " if self.flag:\n"103 " # pick the pool class for this backend\n"104 " cls = PoolA\n"105 " pool = cls(n)\n"106 " return pool\n"107 )108 },109 )110 _commit(repo, "base")111 _write(112 repo,113 **{114 "kv.py": (115 "class C:\n"116 " def dispatch(self, n):\n"117 " if self.flag:\n"118 " pool = self._build_pool(n=n)\n"119 " return pool\n"120 "\n"121 " def _build_pool(self, *, n):\n"122 " # pick the pool class for this backend\n"123 " cls = PoolA\n"124 " pool = cls(n)\n"125 " return pool\n"126 )127 },128 )129 commit = _commit(repo, "extract _build_pool with a leading comment")130 recipe = infer_recipe(commit, str(repo))131 assert len(recipe.extract_functions) == 1132 ex = recipe.extract_functions[0]133 assert ex["signature"] == " def _build_pool(self, *, n):\n"134 assert ex["body"].lstrip().startswith("# pick the pool class")135 assert build_repro(recipe, repo_root=str(repo)).run() == ""136 137 138def test_infer_extract_function_no_return_text_when_body_is_whole_helper(139 repo: Path,140) -> None:141 """When the helper body reproduces the source block with no trailing return, return_text142 is None (the block is a side-effecting statement sequence, not a value producer)."""143 _write(144 repo,145 **{146 "kv.py": (147 "class C:\n"148 " def run(self):\n"149 " self.pre()\n"150 " self.log(1)\n"151 " self.log(2)\n"152 " self.post()\n"153 )154 },155 )156 _commit(repo, "base")157 _write(158 repo,159 **{160 "kv.py": (161 "class C:\n"162 " def run(self):\n"163 " self.pre()\n"164 " self._emit()\n"165 " self.post()\n"166 "\n"167 " def _emit(self):\n"168 " self.log(1)\n"169 " self.log(2)\n"170 )171 },172 )173 _commit(repo, "extract _emit from run")174 recipe = infer_recipe("HEAD", str(repo))175 assert recipe.supported176 assert len(recipe.extract_functions) == 1177 ex = recipe.extract_functions[0]178 assert ex["name"] == "_emit"179 assert ex["return_text"] is None180 assert ex["call"] == " self._emit()\n"181 182 183def test_infer_extract_function_edited_body_does_not_pass(repo: Path) -> None:184 """A helper whose body was edited (not a verbatim cut) never yields a false pass: the185 reproduction's byte-diff surfaces the bundled change as a non-empty residual."""186 _write(187 repo,188 **{189 "kv.py": (190 "class C:\n"191 " def run(self):\n"192 " self.pre()\n"193 " self.log(1)\n"194 " self.post()\n"195 )196 },197 )198 _commit(repo, "base")199 _write(200 repo,201 **{202 "kv.py": (203 "class C:\n"204 " def run(self):\n"205 " self.pre()\n"206 " self._emit()\n"207 " self.post()\n"208 "\n"209 " def _emit(self):\n"210 " self.log(2)\n"211 )212 },213 )214 commit = _commit(repo, "extract _emit but change the arg")215 recipe = infer_recipe(commit, str(repo))216 residual = build_repro(recipe, repo_root=str(repo)).run()217 assert residual != ""218 219 220def test_infer_extract_function_when_block_and_call_share_closing_paren(221 repo: Path,222) -> None:223 """The removed block and its replacement call both end in a lone ``)``; the prefix/suffix224 split must not absorb that shared line, or the extracted body loses its final line.225 """226 _write(227 repo,228 **{229 "kv.py": (230 "class C:\n"231 " def dispatch(self, n):\n"232 " if self.flag:\n"233 " pool = make_pool(\n"234 " a=n,\n"235 " b=self.b,\n"236 " )\n"237 " return pool\n"238 )239 },240 )241 _commit(repo, "base")242 _write(243 repo,244 **{245 "kv.py": (246 "class C:\n"247 " def dispatch(self, n):\n"248 " if self.flag:\n"249 " pool = self._build_pool(\n"250 " n=n,\n"251 " )\n"252 " return pool\n"253 "\n"254 " def _build_pool(self, *, n):\n"255 " pool = make_pool(\n"256 " a=n,\n"257 " b=self.b,\n"258 " )\n"259 " return pool\n"260 )261 },262 )263 commit = _commit(repo, "extract _build_pool")264 recipe = infer_recipe(commit, str(repo))265 assert len(recipe.extract_functions) == 1266 ex = recipe.extract_functions[0]267 assert ex["body"].rstrip().endswith(")")268 assert ex["return_text"] == " return pool"269 assert build_repro(recipe, repo_root=str(repo)).run() == ""270 271 272def test_emitted_script_passes_on_extract_function(repo: Path, tmp_path: Path) -> None:273 """The recipe for an extract_function reproduces the commit byte-for-byte (bare repo, no274 formatter) so build_repro returns an empty residual."""275 _write(276 repo,277 **{278 "kv.py": (279 "class C:\n"280 " def dispatch(self, n):\n"281 " base = self.setup()\n"282 " if self.flag:\n"283 " x = self.a\n"284 " pool = make(\n"285 " a=x,\n"286 " )\n"287 " return pool\n"288 "\n"289 " def keep(self):\n"290 " return 0\n"291 )292 },293 )294 _commit(repo, "base")295 _write(296 repo,297 **{298 "kv.py": (299 "class C:\n"300 " def dispatch(self, n):\n"301 " base = self.setup()\n"302 " if self.flag:\n"303 " pool = self._build_pool(n=n)\n"304 " return pool\n"305 "\n"306 " def _build_pool(self, *, n):\n"307 " x = self.a\n"308 " pool = make(\n"309 " a=x,\n"310 " )\n"311 " return pool\n"312 "\n"313 " def keep(self):\n"314 " return 0\n"315 )316 },317 )318 commit = _commit(repo, "extract _build_pool from dispatch")319 recipe = infer_recipe(commit, str(repo))320 residual = build_repro(recipe, repo_root=str(repo)).run()321 assert residual == "", residual322 assert "extract_function" in recipe_to_script(recipe, "extract")323