Coverage for src / renaissance / recipes / taut2pyunit.py: 75%

352 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-09-09 14:04 +0000

1import re 

2import textwrap 

3from datetime import datetime 

4from pathlib import Path 

5 

6import test_data.test_class as tst_class 

7import test_data.test_insert as tst_insert 

8from renaissance.integrations.types import Attribute, FunctionDef, ImportFrom, ImportStatement, Name 

9from renaissance.recipes.python_refactoring import PythonRefactoring 

10from renaissance.syntax_tree.match_finder import match_pattern 

11 

12 

13class Taut2Pyunit(PythonRefactoring): 

14 def __init__(self, file): 

15 super().__init__(file) 

16 self.white_list_reg = r"_test|_unittest|_tests" 

17 self.black_list_reg = r"_migrated|_after|_original" 

18 self.comp = "ABCD" 

19 

20 def run(self): 

21 if re.search(self.black_list_reg, self.filename): 

22 print(f"skipping: {Path(self.filename).resolve()}") 

23 return 

24 if not re.search(self.white_list_reg, self.filename): 

25 print(f"skipping: {Path(self.filename).resolve()}") 

26 return 

27 print(f"Taut to pyunit migration: {Path(self.filename).resolve()}") 

28 

29 # conditional refactor 

30 if "AP_core_functionality_test" in self.filename: 

31 self.insert_asserter() 

32 self.remove_assert_func() 

33 self.replace_unittest_with_asserter() 

34 self.assert_func() 

35 self.commit() 

36 

37 self.replace_mock() 

38 self.remove_stubserver() 

39 self.replace_taut() 

40 self.remove_decorator() 

41 self.add_self() 

42 self.convert_assert() 

43 self.convert_testdoubles_fun() 

44 

45 self.replace_log_compxtl("emrw") 

46 self.replace_log_compxtl("abcd") 

47 self.remove_taut_import() 

48 self.replace_taut_import() 

49 self.convert_setup_common() 

50 self.convert_teardown_common() 

51 self.convert_add_patcher() 

52 self.convert_teardown() 

53 self.convert_setup() 

54 self.convert_import_verify() 

55 self.shared_setup() 

56 self.commit() 

57 self.with_testdoubles() 

58 self.commit() 

59 

60 if self.root.signature.find("self.patches = []") > 0 or self.root.signature.find("patch.object") > 0: 

61 self.insert_patch_import() 

62 self.commit() 

63 

64 try: 

65 # result = insert_doc(result, "01-22-2026") 

66 with self.get_migrated_path(self.filename).open("w") as f: 

67 f.write(self.apply_to_string()) 

68 except FileNotFoundError: 

69 print(f"Error: File '{self.filename}' not found.") 

70 

71 def get_migrated_path(self, file_path): 

72 """Convert a file path to add '_migrated' before the extension. 

73 

74 Example: 'taut.py' -> 'taut_migrated.py' 

75 """ 

76 path = Path(file_path) 

77 return path.with_stem(f"{path.stem}_migrated") 

78 

79 def replace_taut(self): 

80 """Replace TAUT.TestCase by unittest.TestCase.""" 

81 [self.replace("unittest.TestCase", node, False, False) for node in self.find_ast_type(Attribute) if node.name == "TAUT.TestCase"] 

82 [self.replace("unittest.TestCase", node, False, False) for node in self.find_ast_type(Name) if node.name == "TestCase"] 

83 

84 def remove_decorator(self): 

85 [self.remove(node, False, False) for node in self.find_ast_type(Attribute) if node.name == "TAUT.log_stub"] 

86 

87 def add_self(self): 

88 matching = [ 

89 "emrwxread", 

90 "emrwxwidxread", 

91 "emrwxviprxinterface", 

92 "whxstream2", 

93 "gtaaxtxmark", 

94 "mark_upd_q", 

95 "gtaaxtxmark", 

96 "gtaaxtxmrkxadv", 

97 "emrwxwidxcfg", 

98 "wlxload", 

99 "wlxclear", 

100 "gtmwxtxws", 

101 "emtlxt", 

102 "emtlxtxmc", 

103 "emtlxtxwid", 

104 "emrwxviprxtestlog", 

105 "emrwxviprxwh", 

106 ] 

107 parent_func = ["setUpCommon", "setUp"] 

108 [self.replace("self." + node.name, node, False, False) for node in self.find_ast_type(Name) if node.name in matching] 

109 

110 matching2 = ["EMRWxREAD.emrwxread"] 

111 [ 

112 self.replace("self." + node.name.split(".")[1], node, False, False) 

113 for node in self.find_ast_type(Attribute) 

114 if node.name in matching2 and node.get_ancestor("FunctionDef").name not in parent_func 

115 ] 

116 

117 def convert_assert(self): 

118 [self.replace("self.assertFalse", node, False, False) for node in self.find_ast_type(Attribute) if node.name == "self.assert_false"] 

119 [self.replace("self.assertTrue", node, False, False) for node in self.find_ast_type(Attribute) if node.name == "self.assert_true"] 

120 [self.replace("self.assertEqual", node, False, False) for node in self.find_ast_type(Attribute) if node.name == "self.assert_equal"] 

121 

122 def remove_stubserver(self): 

123 [self.remove(node, False, False) for node in self.find_ast_type(Attribute) if node.name == "TAUT.StubServer"] 

124 

125 def replace_mock(self): 

126 [ 

127 self.replace("patch", node, False, False) 

128 for node in self.find_ast_type(Attribute) 

129 if node.name == "mock.patch" and node.parent.parent.name == "decorator_list" 

130 ] 

131 

132 def replace_log_compxtl(self, comp): 

133 func_call = self.pattern_factory.create_statements(f"{comp}xtl.$a($$bb)") 

134 for call in match_pattern(self.root.children, func_call): 

135 repl = call.signature.replace(f"{comp}xtl", f"fake_{comp}xtl") 

136 self.replace(repl, call.nodes, False, False) 

137 

138 assign = self.pattern_factory.create_statements(f"$c = {comp}xtl.$a($$bb)") 

139 for match in match_pattern(self.root.children, assign): 

140 repl = match.signature.replace(f"{comp}xtl", f"fake_{comp}xtl") 

141 self.replace(repl, match.nodes, False, False) 

142 self.commit() 

143 taut_test_doubles = self.pattern_factory.create_statements( 

144 f"with TAUT.TestDoubles({comp}xtl=Fake{comp.upper()}xTL(None)):\n log = TAUT.Logger()\n $$aa", 

145 ) 

146 for match in match_pattern(self.root.children, taut_test_doubles): 

147 repl = f"fake_{comp}xtl = Fake{comp.upper()}xTL(None)\n{match['$$aa']}" 

148 self.replace(repl, match.nodes, False, False) 

149 self.commit() 

150 

151 def remove_taut_import(self): 

152 taut_import = self.pattern_factory.create_statements("import TAUT\n") 

153 for match in match_pattern(self.root.children, taut_import): 

154 self.remove(match.nodes, False, False) 

155 

156 def replace_taut_import(self): 

157 """Replace mock by unittest.mock and using patch.""" 

158 mock = self.pattern_factory.create_statements("import mock\n") 

159 for match in match_pattern(self.root.children, mock): 

160 self.remove(match.nodes, False, False) 

161 

162 test_case = self.pattern_factory.create_statements("from TAUT import TestCase") 

163 for match in match_pattern(self.root.children, test_case): 

164 self.remove(match.nodes, False, False) 

165 import_taut = self.pattern_factory.create_statements("from TAUT import TestCase, TestDoubles") 

166 for match in match_pattern(self.root.children, import_taut): 

167 repl = "try:\n from unittest.mock import patch\nexcept ImportError:\n from mock import patch\n" 

168 self.replace(repl, match.nodes, False, False) 

169 import_doubles = self.pattern_factory.create_statements("from TAUT import TestDoubles") 

170 for match in match_pattern(self.root.children, import_doubles): 

171 repl = "try:\n from unittest.mock import patch\nexcept ImportError:\n from mock import patch\n" 

172 self.replace(repl, match.nodes, False, False) 

173 

174 def convert_tds(self): 

175 tds = self.pattern_factory.create_statements("self.tds.append(TestDoubles($a, $b=$c))") 

176 for match in match_pattern(self.root.children, tds): 

177 repl = f"self.add_patcher({match['$a']}, '{match['$b']}', {match['$c']})" 

178 self.replace(repl, match.nodes, False, False) 

179 

180 tds2 = self.pattern_factory.create_statements("self.tds.append(TestDoubles($a=ImprovedStub($b)))") 

181 for match in match_pattern(self.root.children, tds2): 

182 repl = f"self.{match['$a']} = ImprovedStub({match['$b']})" 

183 self.replace(repl, match.nodes, False, False) 

184 

185 def convert_setup_common(self): 

186 insert_code = """ImprovedStub.ret_vals = {} 

187ImprovedStub.ret_vals_ex = {} 

188ImprovedStub.call_logs = {} 

189ImprovedStub.store_args = {} 

190 

191""" 

192 p_start = """for p in self.patchers: 

193 p.start() 

194""" 

195 tds_pattern = self.pattern_factory.create_statements("self.tds = [$$aa]") 

196 for match in match_pattern(self.root.children, tds_pattern): 

197 init_stubs = "" 

198 repl = "self.patchers = [\n" 

199 doubles_pattern = self.pattern_factory.create_expression("TestDoubles($a=ImprovedStub($b))") 

200 for matched_doubles in match_pattern(match.expansions["$$aa"], [doubles_pattern]): 

201 init_stubs += ( 

202 f"self.{matched_doubles.expansions['$a'][0]} = ImprovedStub({matched_doubles.expansions['$b'][0].signature})\n" 

203 ) 

204 interface_stub = self.find_import_interface(matched_doubles.expansions["$b"][0].signature) 

205 repl += ( 

206 f" patch.object({interface_stub}, '{matched_doubles.expansions['$a'][0]}', " 

207 f"self.{matched_doubles.expansions['$a'][0]}),\n" 

208 ) 

209 repl += "]\n\n" 

210 repl = insert_code + init_stubs + repl + p_start 

211 self.replace(repl, match.nodes, False, False) 

212 

213 def convert_teardown_common(self): 

214 teardown_common = self.pattern_factory.create_statements("def tearDownCommon(self):\n $$aa") 

215 repl = """def tearDownCommon(self): 

216 for p in self.patchers: 

217 try: 

218 p.stop() 

219 except RuntimeError: 

220 pass 

221""" 

222 for match in match_pattern(self.root.children, teardown_common): 

223 self.replace(repl, match.nodes, False, False) 

224 

225 def convert_add_patcher(self): 

226 pattern = self.pattern_factory.create_statements("def tearDownCommon(self):\n $$aa") 

227 for match in match_pattern(self.root.children, pattern): 

228 patcher_pattern = [node for node in self.find_ast_type(FunctionDef) if node.name == "add_patcher"] 

229 if len(patcher_pattern) == 0: 

230 self.insert_after(tst_class.insert_add_patcher, match.nodes) 

231 

232 def find_import_interface(self, name: str): 

233 interface = name 

234 if name.islower(): 

235 node_list = [node for node in self.find_ast_type(ImportStatement) if node.name == name] 

236 if node_list: 

237 if node_list[0].ast_type == ImportFrom: 

238 interface = node_list[0].properties["module"] 

239 else: 

240 interface = node_list[0].name if node_list else name 

241 return interface.split(".")[0] 

242 

243 def convert_setup(self): 

244 # remove doubles init 

245 pattern1 = self.pattern_factory.create_statements("doubles = []") 

246 replacement = "self.patches = []" 

247 for match in match_pattern(self.root.children, pattern1): 

248 self.replace(replacement, match.nodes, False, False) 

249 

250 pattern2 = self.pattern_factory.create_statements("self.doubles = []") 

251 for match in match_pattern(self.root.children, pattern2): 

252 self.replace(replacement, match.nodes, False, False) 

253 

254 # convert doubles to patch 

255 self.convert_test_doubles("doubles.append(TAUT.TestDoubles($a=$b))") 

256 self.convert_test_doubles("self.doubles.append(TAUT.TestDoubles($a=$b))") 

257 

258 # convert doubles to patch.object 

259 insert_node = None 

260 pattern_outer = self.pattern_factory.create_statements("def setUp(self):\n $$aa") 

261 for setup_func in match_pattern(self.root.children, pattern_outer): 

262 pattern4 = self.pattern_factory.create_statements("doubles.append(TAUT.TestDoubles(module=$mod, $b=$c))") 

263 matched_pattern = match_pattern(setup_func.nodes, pattern4) 

264 for index, match in enumerate(matched_pattern): 

265 repl_pattern = ( 

266 f"self.patches.append(patch.object({match.expansions['$mod'][0].name}, " 

267 f"'{match.expansions['$b'][0]}', {match.expansions['$c'][0].signature}))" 

268 ) 

269 repl_pattern = repl_pattern.replace("context_stub", "self.context_stub") 

270 self.replace(repl_pattern, match.nodes, False, False) 

271 if index == len(matched_pattern) - 1: 

272 insert_node = match.nodes[-1] 

273 insert_code = """\nfor p in self.patches: 

274 p.start()""" 

275 self.insert_after(insert_code, insert_node, False, False) 

276 

277 pattern4_1 = self.pattern_factory.create_statements("self.doubles.append(TAUT.TestDoubles(module=$mod, $b=$c))") 

278 matched_pattern_1 = match_pattern(setup_func.nodes, pattern4_1) 

279 for index, match in enumerate(matched_pattern_1): 

280 repl_pattern = ( 

281 f"self.patches.append(patch.object({match.expansions['$mod'][0].name}, " 

282 f"'{match.expansions['$b'][0]}', {match.expansions['$c'][0].signature}))" 

283 ) 

284 repl_pattern = repl_pattern.replace("context_stub", "self.context_stub") 

285 self.replace(repl_pattern, match.nodes, False, False) 

286 if index == len(matched_pattern_1) - 1: 

287 insert_node = match.nodes[-1] 

288 insert_code = """\nfor p in self.patches: 

289 p.start()""" 

290 self.insert_after(insert_code, insert_node, False, False) 

291 

292 pattern5 = self.pattern_factory.create_statements("self.doubles = doubles") 

293 for match in match_pattern(self.root.children, pattern5): 

294 self.remove(match.nodes, False, False) 

295 self.commit() 

296 [self.replace("self.context_stub", node, False, False) for node in self.find_ast_type(Name) if node.name == "context_stub"] 

297 

298 def convert_teardown(self): 

299 matched_pattern = self.pattern_factory.create_statements("def tearDown(self):\n $$aa") 

300 repl_pattern = """def tearDown(self): 

301 for p in self.patches: 

302 p.stop()""" 

303 for match in match_pattern(self.root.children, matched_pattern): 

304 self.replace(repl_pattern, match.nodes, False, False) 

305 

306 def refactor_teardown(self): 

307 self.comp = "abcd" 

308 pattern1 = self.pattern_factory.create_statements("for double in self.doubles:\n double.exit()") 

309 replace_pattern = "patch.stopall()" 

310 for match in match_pattern(self.root.children, pattern1): 

311 self.replace(replace_pattern, match.nodes, False, False) 

312 

313 insert_code = f"""{self.comp.upper()}xCONTEXT.{self.comp}xcontext.reset_method_attributes("start_wafer") 

314{self.comp.upper()}xCONTEXT.{self.comp}xcontext.reset_method_attributes("finish_wafer") 

315{self.comp.upper()}xCONTEXT.{self.comp}xcontext.reset_method_attributes("start_lot") 

316{self.comp.upper()}xCONTEXT.{self.comp}xcontext.reset_method_attributes("finish_lot") 

317 

318""" 

319 pattern2 = self.pattern_factory.create_statements("self._patch_readout_data_filler.stop()") 

320 for match in match_pattern(self.root.children, pattern2): 

321 self.insert_before(insert_code, match.nodes, False, False) 

322 

323 def convert_test_doubles(self, doubles: str): 

324 mappings: dict[str, str] = { 

325 "emrmxcontext": "EMRMxCONTEXT", 

326 "acbdxcontext": "ACBDxCONTEXT", 

327 # Add more mappings here 

328 } 

329 doubles_pattern = self.pattern_factory.create_statements(doubles) 

330 for match in match_pattern(self.root.children, doubles_pattern): 

331 keyword = match.expansions["$a"][0] 

332 if match.expansions["$a"][0] in mappings: 

333 keyword = mappings[match.expansions["$a"][0]] 

334 repl_pattern = f"self.patches.append(patch('{keyword}.{match.expansions['$a'][0]}', {match.expansions['$b'][0].name}))" 

335 repl_pattern = repl_pattern.replace("context_stub", "self.context_stub") 

336 self.replace(repl_pattern, match.nodes, False, False) 

337 

338 def insert_patch_import(self): 

339 insert = "\ntry:\n from unittest.mock import patch\nexcept ImportError:\n from mock import patch" 

340 insert_pattern = self.pattern_factory.create_statements(insert) 

341 if len(match_pattern(self.root.children, insert_pattern)) == 0: 

342 pattern = self.pattern_factory.create_statements("import unittest\n") 

343 for match in match_pattern(self.root.children, pattern): 

344 self.insert_after(insert, match.nodes, False, False) 

345 

346 def replace_taut_skip(self): 

347 """Replace @TAUT.skip_test by @unittest.skip.""" 

348 [self.replace("@unittest.skip", node) for node in self.find_ast_type(Attribute) if node.name == "TAUT.skip_test"] 

349 

350 def convert_import_verify(self): 

351 import_verify = self.pattern_factory.create_statements("self.import_and_verify_module('$a')") 

352 for match in match_pattern(self.root.children, import_verify): 

353 repl = f"import {match.expansions['$a'][0]}\nself.assertIsNotNone({match.expansions['$a'][0]})" 

354 self.replace(repl, match.nodes, False, False) 

355 

356 def with_testdoubles(self): 

357 pattern1 = self.pattern_factory.create_statements("with TAUT.TestDoubles(module=$a, $b=$c):\n $$ee") 

358 for match in match_pattern(self.root.children, pattern1): 

359 repl_pattern = f"with patch.object({match['$a']}, '{match['$b']}', new={match['$c']}):\n {match['$$ee']}" 

360 self.replace(repl_pattern, match.nodes, False, False) 

361 

362 def shared_setup(self): 

363 setup_function = self.pattern_factory.create_statements("def sharedSetUp(self):\n $$stmts") 

364 for match in match_pattern(self.root.children, setup_function): 

365 repl = match.signature.replace("def sharedSetUp", " def setUp") 

366 self.replace(textwrap.dedent(repl), match.nodes, False, False) 

367 

368 def insert_class(self): 

369 class_pattern = self.pattern_factory.create_statements("class Asserter(unittest.TestCase):\n $$aa") 

370 if len(match_pattern(self.root.children, class_pattern)) == 0: 

371 insert_pattern = self.pattern_factory.create_statements("def b():\n $$bb") 

372 insert_code = tst_insert.insert_code 

373 for match in match_pattern(self.root.children, insert_pattern): 

374 self.insert_after(insert_code, match.nodes, False, False) 

375 

376 def insert_asserter(self): 

377 insert_pattern = self.pattern_factory.create_statements("def assert_double_equal($$arg, $$other=$$value):\n $$bb") 

378 insert_code = tst_insert.insert_code 

379 for match in match_pattern(self.root.children, insert_pattern): 

380 self.insert_after(insert_code, match.nodes, False, False) 

381 

382 def remove_assert_func(self): 

383 pattern = self.pattern_factory.create_statements("def assert_double_equal($$arg, $$other=$$value):\n $$bb") 

384 for match in match_pattern(self.root.children, pattern): 

385 self.remove(match.nodes, False, False) 

386 self.commit() 

387 

388 def replace_unittest_with_asserter(self): 

389 pattern = self.pattern_factory.create_statements("class $a(TAUT.TestCase):\n $$bb") 

390 for match in match_pattern(self.root.children, pattern): 

391 if match["$a"] != "Asserter" and ("assert_raises" in match["$$bb"] or "assert_double_equal" in match["$$bb"]): 

392 repl = f"{match.signature.replace('TAUT.TestCase', 'Asserter')}" 

393 self.replace(repl, match.nodes, False, False) 

394 self.commit() 

395 

396 def assert_func(self): 

397 matching = [ 

398 "assert_raises", 

399 "assert_double_equal", 

400 ] 

401 [self.replace("self." + node.name, node, False, False) for node in self.find_ast_type(Name) if node.name in matching] 

402 

403 def move_indent(self, indent): 

404 pattern1 = self.pattern_factory.create_statements("""def $a($$b): 

405 self.doubles.append(TAUT.TestDoubles($mod, $e, $f)) 

406 $$c""") 

407 for match in match_pattern(self.root.children, pattern1): 

408 double_pattern = f" self.doubles.append(TAUT.TestDoubles({match['$mod']}, {match['$e']}, {match['$f']}))\n" 

409 func_header_index = match.signature.index("):\n") 

410 repl = f""" with patch.object({match["$mod"]}, '{match["$e"]}', {match["$f"]}):\n""" 

411 replace_pattern = ( 

412 match.signature[: func_header_index + 3] + repl + textwrap.indent(match.signature[func_header_index + 3 :], indent) 

413 ) 

414 replace_pattern = replace_pattern.replace(double_pattern, "") 

415 self.replace(replace_pattern, match.nodes, False, False) 

416 

417 def convert_testdoubles_fun(self): 

418 """This is used for taut migration, where the function pattern is found in a class.""" 

419 # case1 two TestDoubles are defined 

420 pattern1 = self.pattern_factory.create_statements("""def $a($$b): 

421 self.doubles.append( 

422 TAUT.TestDoubles( 

423 module=$mod1, $e1=$f1 

424 ) 

425 ) 

426 self.doubles.append( 

427 TAUT.TestDoubles( 

428 module=$mod2, $e2=$f2 

429 ) 

430 ) 

431 $$c 

432 """) 

433 for match in match_pattern(self.root.children, pattern1): 

434 double_pattern = f""" self.doubles.append( 

435 TAUT.TestDoubles( 

436 module={match["$mod1"]}, 

437 {match["$e1"]}={match["$f1"]}, 

438 ), 

439 ) 

440 self.doubles.append( 

441 TAUT.TestDoubles( 

442 module={match["$mod2"]}, 

443 {match["$e2"]}={match["$f2"]}, 

444 ), 

445 ) 

446""" 

447 func_header_index = match.signature.index("):\n") 

448 repl = f"""with patch.object({match["$mod1"]}, '{match["$e1"]}', {match["$f1"]}), \\ 

449 patch.object({match["$mod2"]}, '{match["$e2"]}', {match["$f2"]}):\n""" 

450 replace_pattern = ( 

451 match.signature[: func_header_index + 3] + textwrap.indent(repl, " ") + match.signature[func_header_index + 3 :] 

452 ) 

453 replace_pattern = replace_pattern.replace(textwrap.indent(double_pattern, " "), "") 

454 self.replace(replace_pattern, match.nodes, False, False) 

455 self.commit() 

456 

457 pattern2 = self.pattern_factory.create_statements("""def $a($$b): 

458 self.doubles.append( 

459 TAUT.TestDoubles( 

460 module=$mod, $e=$f 

461 ) 

462 ) 

463 $$c 

464 """) 

465 for match in match_pattern(self.root.children, pattern2): 

466 double_pattern = f""" self.doubles.append( 

467 TAUT.TestDoubles( 

468 module={match["$mod"]}, {match["$e"]}={match["$f"]} 

469 ) 

470 ) 

471""" 

472 double_pattern1 = f""" self.doubles.append( 

473 TAUT.TestDoubles( 

474 module={match["$mod"]}, 

475 {match["$e"]}={match["$f"]}, 

476 ) 

477 ) 

478""" 

479 func_header_index = match.signature.index("):\n") 

480 repl = f"""with patch.object({match["$mod"]}, '{match["$e"]}', {match["$f"]}):\n""" 

481 replace_pattern = ( 

482 match.signature[: func_header_index + 3] + textwrap.indent(repl, " ") + match.signature[func_header_index + 3 :] 

483 ) 

484 double_pattern2 = f" self.doubles.append(TAUT.TestDoubles(module={match['$mod']}, {match['$e']}={match['$f']}))\n" 

485 replace_pattern = replace_pattern.replace(textwrap.indent(double_pattern, " "), "") 

486 replace_pattern = replace_pattern.replace(textwrap.indent(double_pattern1, " "), "") 

487 replace_pattern = replace_pattern.replace(textwrap.indent(double_pattern2, " "), "") 

488 self.replace(replace_pattern, match.nodes, False, False) 

489 self.commit() 

490 

491 def refactor_testdoubles_fun(self): 

492 """This is used for unittest, where the function pattern is not found in a class.""" 

493 # case1 two TestDoubles are defined 

494 pattern1 = self.pattern_factory.create_statements("""def $a($$b): 

495 self.doubles.append( 

496 TAUT.TestDoubles( 

497 module=$mod1, $e1=$f1 

498 ) 

499 ) 

500 self.doubles.append( 

501 TAUT.TestDoubles( 

502 module=$mod2, $e2=$f2 

503 ) 

504 ) 

505 $$c""") 

506 for match in match_pattern(self.root.children, pattern1): 

507 double_pattern = f""" self.doubles.append( 

508 TAUT.TestDoubles( 

509 module={match["$mod1"]}, {match["$e1"]}={match["$f1"]} 

510 ) 

511 ) 

512 self.doubles.append( 

513 TAUT.TestDoubles( 

514 module={match["$mod2"]}, {match["$e2"]}={match["$f2"]} 

515 ) 

516 ) 

517""" 

518 func_header_index = match.signature.index("):\n") 

519 repl = f"""with patch.object({match["$mod1"]}, '{match["$e1"]}', {match["$f1"]}), \\ 

520 patch.object({match["$mod2"]}, '{match["$e2"]}', {match["$f2"]}): 

521 """ 

522 replace_pattern = match.signature[: func_header_index + 3] + textwrap.indent( 

523 repl + match.signature[func_header_index + 3 :], 

524 " ", 

525 ) 

526 replace_pattern = replace_pattern.replace(double_pattern, "") 

527 replace_pattern = replace_pattern.replace(textwrap.indent(double_pattern, " "), "") 

528 self.replace(replace_pattern, match.nodes, False, False) 

529 self.commit() 

530 pattern2 = self.pattern_factory.create_statements("""def $a($$b): 

531 self.doubles.append( 

532 TAUT.TestDoubles( 

533 module=$mod, $e=$f 

534 ) 

535 ) 

536 $$c 

537""") 

538 for match in match_pattern(self.root.children, pattern2): 

539 double_pattern = f""" self.doubles.append( 

540 TAUT.TestDoubles( 

541 module={match["$mod"]}, {match["$e"]}={match["$f"]} 

542 ) 

543 ) 

544""" 

545 func_header_index = match.signature.index("):\n") 

546 repl = f"""with patch.object({match["$mod"]}, '{match["$e"]}', {match["$f"]}):\n""" 

547 replace_pattern = match.signature[: func_header_index + 3] + textwrap.indent( 

548 repl + match.signature[func_header_index + 3 :], 

549 " ", 

550 ) 

551 replace_pattern = replace_pattern.replace(double_pattern, "") 

552 replace_pattern = replace_pattern.replace(textwrap.indent(double_pattern, " "), "") 

553 self.replace(replace_pattern, match.nodes, False, False) 

554 

555 def refactor_testdoubles_class(self): 

556 pattern = self.pattern_factory.create_statements("""class $a(TAUT.TestCase): 

557 

558 def setUp(self): 

559 $$bb 

560 self.doubles = [] 

561 $$cc 

562 self.doubles.append( 

563 TAUT.TestDoubles( 

564 module=$mod1, 

565 $e1=$f1, 

566 ) 

567 ) 

568 self.doubles.append( 

569 TAUT.TestDoubles( 

570 module=$mod2, 

571 $e2=$f2, 

572 ) 

573 ) 

574 $$dd 

575 

576 def tearDown(self): 

577 $$gg 

578 for double in self.doubles: 

579 double.exit()""") 

580 

581 for match in match_pattern(self.root.children, pattern): 

582 replace_pattern = f"""class {match["$a"]}(unittest.TestCase): 

583 

584 def setUp(self): 

585{textwrap.indent(match["$$bb"], " ")} 

586{textwrap.indent(match["$$cc"], " ")} 

587 self.patches = [ 

588 patch.object({match["$mod1"]}, '{match["$e1"]}', {match["$f1"]}), 

589 patch.object({match["$mod2"]}, '{match["$e2"]}', {match["$f2"]}), 

590 ] 

591 for p in self.patches: 

592 p.start() 

593 

594{textwrap.indent(match["$$dd"], " ")} 

595 

596 def tearDown(self): 

597{textwrap.indent(match["$$gg"], " ")} 

598 for p in self.patches: 

599 p.stop()""" 

600 self.replace(replace_pattern, match.nodes, False, False) 

601 

602 def insert_doc_func(self): 

603 pattern = self.pattern_factory.create_statements("""# -----------------------------------------------------------------------------# 

604# # 

605# Copyright (c) 2016, XXXX Netherlands B.V. # 

606""") 

607 insert_code = get_change_comment() 

608 for match in match_pattern(self.root.children, pattern): 

609 self.insert_before(insert_code, match.nodes, False, False) 

610 

611 

612def insert_doc(content: str, date): 

613 pattern = r"# -+(#)?\n(#\s+#\n)?#\s+Copyright \(c\) \d{4}, XXXX" 

614 match = re.search(pattern, content) 

615 

616 if not match: 

617 print("Comment block not found.") 

618 return content 

619 

620 # Find the beginning of the line containing the comment 

621 position = match.start() 

622 line_start = content.rfind("\n", 0, position) + 1 

623 if line_start == 0: # If comment is at the beginning of the file 

624 line_start = 0 

625 

626 # Insert the new line before the comment block 

627 print(get_change_comment(date)) 

628 modified_content = content[:line_start] + get_change_comment(date) + "\n" + content[line_start:] 

629 return modified_content 

630 

631 

632def get_change_comment(date=None): 

633 """Generate a formatted change comment with today's date. 

634 

635 Args: 

636 change_id (str): The change ID (e.g., 'SWCHGxxxxxxxx') 

637 description (str): The description of the change 

638 

639 Returns: 

640 str: Formatted change comment string 

641 

642 """ 

643 change_id = "SWCHGxxxxxxxx" 

644 description = "Add assert_raises method to Asserter class." 

645 

646 # When no date provided, use today 

647 formatted_date = datetime.now() if date is None else datetime.strptime(date, "%m-%d-%Y") 

648 

649 return f"# {formatted_date.strftime('%m-%d-%Y')} : {change_id} SBYN {description}"