-
-
Save Ladsgroup/bfb4f4b66384efb8b8d3efea338b6e6b to your computer and use it in GitHub Desktop.
# License: MIT | |
# Run like: "python phpunit4_killer.py /var/lib/mediawiki/extensions/Wikibase/" | |
import os | |
import sys | |
import re | |
import subprocess | |
def find_files(path): | |
files = [] | |
for r, d, f in os.walk(path): | |
for file_name in f: | |
if file_name.endswith('.php'): | |
files.append(os.path.join(r, file_name)) | |
return files | |
def check_file(path): | |
with open(path, 'r', encoding="utf-8") as f: | |
content = f.read() | |
old_content = content | |
cases = re.findall(r'\n\t*?use \\?PHPUnit4And6Compat;', content) | |
literal = '\'(?:[^\'\\\\]|\\\\\')+\'|"(?:[^"\\\\]|\\\\")+"' | |
clname = '(?:[a-zA-Z\\\\]+::class|\$\w+|' + literal + ')' | |
content = re.sub(r'(\$this->)setExpected(Exception\(\s+' + clname + '\s+\);)', r'\1expect\2', content) | |
content = re.sub(r'^(\\t*)(\$this->)setExpectedException\(\s+(' + clname + '),\s+(' + clname + ')\s+\);', r'\1\2expectException( \3 );\n\1\$this->expectExceptionMessage( \4 );', content) | |
content = re.sub(r'(\$this->)get(Mock\(\s+' + clname + '\s+\))', r'\1create\2', content) | |
content = re.sub(r'(\$this->)getMock\(\s+(' + clname + '),\s+\[\],\s+\[\],\s+[\'"]{2},\s+false\s+\)', r'\1createMock( \2 )', content) | |
content = re.sub(r'^(\\t*)(\$this->)getMock\(\s+(' + clname + '),\s+(\[(?:\s*' + literal + ',?)+\])\s+\);', r'\1\2createMock( \3 )\n\1\t->setMethods( \4 )\n\1\t->getMock();', content) | |
if '$this->getMock(' in content or '$this->setExpectedException(' in content: | |
print('Nooooo /o\\') | |
with open(path, 'w', encoding="utf-8") as f: | |
print(path) | |
f.write(content) | |
return | |
for case in cases: | |
content = content.replace(case, '') | |
content = content.replace(', PHPUnit4And6Compat, ', ', ') | |
content = content.replace('use PHPUnit4And6Compat, ', 'use ') | |
content = content.replace(', PHPUnit4And6Compat;', ';') | |
if content == old_content: | |
return | |
with open(path, 'w', encoding="utf-8") as f: | |
print(path) | |
f.write(content) | |
for f in find_files(sys.argv[1]): | |
check_file(f) | |
subprocess.run(["composer", "update"], cwd=sys.argv[1], shell=True) | |
subprocess.run(["composer", "fix"], cwd=sys.argv[1], shell=True) |
Some other improvements (includes the ones suggested above).
Before line 21, add:
literal = '\'(?:[^\'\\\\]|\\\\\')+\'|"(?:[^"\\\\]|\\\\")+"'
clname = '(?:[a-zA-Z\\\\]+::class|\$\w+|' + literal + ')'
Line21 -->content = re.sub(r'(\$this->)setExpected(Exception\(\s+' + clname + '\s+\);)', r'\1expect\2', content)
Line22 -->content = re.sub(r'^(\\t*)(\$this->)setExpectedException\(\s+(' + clname + '),\s+(' + clname + ')\s+\);', r'\1\2expectException( \3 );\n\1\$this->expectExceptionMessage( \4 );', content)
Line23 -->content = re.sub(r'(\$this->)get(Mock\(\s+' + clname + '\s+\))', r'\1create\2', content)
And after line 23:
content = re.sub(r'(\$this->)get(Mock\(\s+' + clname + '),\s+\[\],\s+\[\],\s+[\'"]{2},\s+false\s+\)', r'\1create\2 )', content)
content = re.sub(r'^(\\t*)(\$this->)get(Mock\(\s+' + clname + '),\s+(\[(?:\s*' + literal + ',?)+\])\s+\);', r'\1\2create\3 )\n\1\t->setMethods( \4 )\n\1\t->getMock();', content)
These will still leave some edge cases (e.g.: func call, arrays, class members, string concat), but we can fix them manually. We should also replace literal class names with ::class, but I didn't add it because you'd also have to use
the classes etc.
Note: Untested
Applied your suggestions, regarding removing phpunit trait, I will explain it further in the ticket.
Cool, thanks :) I'm gonna (ab)use it then
Line 21: you can replace it with
content = re.sub(r'(\$this->)setExpected(Exception\( (?:[a-zA-Z\\]+::class|\$\w+) \);)', r'\1expect\2', content)
to take variables into account. Note this is still leaving plain literals (and edge cases) out. But for the former, it's probably a good idea to fix them manually AND replace them with MyClass::class.
Line 22: Likewise, it can be replaced with
content = re.sub(r"""^(\t*)(\$this->)setExpectedException\(\s+([a-zA-Z\\]+::class|\$\w+),\s+('(?:[^'\\]|\\')+'|"(?:[^"\\]|\\")+"|\$\w+)\s+\);""", r'\1\2expectException( \3 );\n\1\$this->expectExceptionMessage( \4 );', content)
Line 23: In theory, the semicolon after the closing parentheses is not necessary (e.g. to catch
some_func( $this->getMock( MyClass::class ) )
).Lines 20, 31-33: I'd suggest not doing it here. Per wikitech-l, those should be handled together with killing CoversValidator, changing inheritance, and making sure that the directory structure isn't tested. Or we could to that here, too, as long as we do it all together.