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
« 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
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
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"
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()}")
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()
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()
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()
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()
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.")
71 def get_migrated_path(self, file_path):
72 """Convert a file path to add '_migrated' before the extension.
74 Example: 'taut.py' -> 'taut_migrated.py'
75 """
76 path = Path(file_path)
77 return path.with_stem(f"{path.stem}_migrated")
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"]
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"]
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]
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 ]
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"]
122 def remove_stubserver(self):
123 [self.remove(node, False, False) for node in self.find_ast_type(Attribute) if node.name == "TAUT.StubServer"]
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 ]
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)
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()
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)
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)
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)
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)
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)
185 def convert_setup_common(self):
186 insert_code = """ImprovedStub.ret_vals = {}
187ImprovedStub.ret_vals_ex = {}
188ImprovedStub.call_logs = {}
189ImprovedStub.store_args = {}
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)
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)
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)
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]
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)
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)
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))")
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)
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)
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"]
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)
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)
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")
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)
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)
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)
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"]
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)
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)
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)
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)
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)
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()
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()
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]
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)
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()
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()
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)
555 def refactor_testdoubles_class(self):
556 pattern = self.pattern_factory.create_statements("""class $a(TAUT.TestCase):
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
576 def tearDown(self):
577 $$gg
578 for double in self.doubles:
579 double.exit()""")
581 for match in match_pattern(self.root.children, pattern):
582 replace_pattern = f"""class {match["$a"]}(unittest.TestCase):
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()
594{textwrap.indent(match["$$dd"], " ")}
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)
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)
612def insert_doc(content: str, date):
613 pattern = r"# -+(#)?\n(#\s+#\n)?#\s+Copyright \(c\) \d{4}, XXXX"
614 match = re.search(pattern, content)
616 if not match:
617 print("Comment block not found.")
618 return content
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
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
632def get_change_comment(date=None):
633 """Generate a formatted change comment with today's date.
635 Args:
636 change_id (str): The change ID (e.g., 'SWCHGxxxxxxxx')
637 description (str): The description of the change
639 Returns:
640 str: Formatted change comment string
642 """
643 change_id = "SWCHGxxxxxxxx"
644 description = "Add assert_raises method to Asserter class."
646 # When no date provided, use today
647 formatted_date = datetime.now() if date is None else datetime.strptime(date, "%m-%d-%Y")
649 return f"# {formatted_date.strftime('%m-%d-%Y')} : {change_id} SBYN {description}"