Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add -i option to mimic GNU patch more and collect coverage for subprocesses #37

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions patch_ng.py
Original file line number Diff line number Diff line change
Expand Up @@ -1303,7 +1303,8 @@ def main():

opt = OptionParser(usage="1. %prog [options] unified.diff\n"
" 2. %prog [options] http://host/patch\n"
" 3. %prog [options] -- < unified.diff",
" 3. %prog [options] -- < unified.diff"
" 4. %prog [options] -i unified.diff",
version="python-patch %s" % __version__)
opt.add_option("-q", "--quiet", action="store_const", dest="verbosity",
const=0, help="print only warnings and errors", default=1)
Expand All @@ -1319,13 +1320,15 @@ def main():
opt.add_option("--revert", action="store_true",
help="apply patch in reverse order (unpatch)")
opt.add_option("-f", "--fuzz", action="store_true", dest="fuzz", help="Accept fuuzzy patches")
opt.add_option("-i", "--input", metavar='PATCHFILE',
help="Read patch from PATCHFILE instead of stdin.")
(options, args) = opt.parse_args()

if not args and sys.argv[-1:] != ['--']:
if not args and not options.input and sys.argv[-1:] != ['--']:
opt.print_version()
opt.print_help()
sys.exit()
readstdin = (sys.argv[-1:] == ['--'] and not args)
readstdin = (sys.argv[-1:] == ['--'] and not args and not options.input)

verbosity_levels = {0:logging.WARNING, 1:logging.INFO, 2:logging.DEBUG}
loglevel = verbosity_levels[options.verbosity]
Expand All @@ -1337,9 +1340,9 @@ def main():
setdebug() # this sets global debugmode variable

if readstdin:
patch = PatchSet(sys.stdin)
patch = PatchSet(sys.stdin.buffer)
else:
patchfile = args[0]
patchfile = options.input if options.input else args[0]
urltest = patchfile.split(':')[0]
if (':' in patchfile and urltest.isalpha()
and len(urltest) > 1): # one char before : is a windows drive letter
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
[tool.coverage.run]
parallel = true
1 change: 1 addition & 0 deletions tests/recoverage.bat
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
cd ..
python -m coverage run tests/run_tests.py
python -m coverage combine
python -m coverage html -d tests/coverage
python -m coverage report -m
42 changes: 32 additions & 10 deletions tests/run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@
http://pypi.python.org/pypi/coverage/ and run this file with:

coverage run run_tests.py
coverage combine
coverage html -d coverage

On Windows it may be more convenient instead of `coverage` call
`python -m coverage.__main__`
`python -m coverage`
"""
from __future__ import print_function

Expand All @@ -38,6 +39,7 @@
import unittest
import copy
import stat
import subprocess
from os import listdir, chmod
from os.path import abspath, dirname, exists, join, isdir, isfile
from tempfile import mkdtemp
Expand Down Expand Up @@ -113,7 +115,7 @@ def _assert_dirs_equal(self, dir1, dir2, ignore=[]):
self.fail("extra file or directory: %s" % e2)


def _run_test(self, testname):
def _run_test(self, testname, inputarg):
"""
boilerplate for running *.patch file tests
"""
Expand All @@ -126,6 +128,16 @@ def _run_test(self, testname):

tmpdir = mkdtemp(prefix="%s."%testname)

# Ensure we collect coverage for subprocesses into the parent
if 'coverage' in sys.modules.keys():
with open(join(tmpdir, ".coveragerc"), "w") as f:
f.write("[run]\n")
f.write("parallel = true\n")
f.write("data_file = " + join(dirname(TESTS), ".coverage") + "\n")
exe = [sys.executable, "-m", "coverage", "run"]
else:
exe = [sys.executable]

basepath = join(TESTS, testname)
basetmp = join(tmpdir, testname)

Expand Down Expand Up @@ -153,13 +165,20 @@ def _run_test(self, testname):
save_cwd = getcwdu()
os.chdir(tmpdir)
extra = "-f" if "10fuzzy" in testname else ""
if not verbose:
extra += " -q "
extra += " " + inputarg + " "
if "--" in inputarg:
cmd = '%s %s %s' % (" ".join(exe), patch_tool, extra)
with open(patch_file, "rb") as f:
input = f.read()
else:
cmd = '%s %s %s "%s"' % (" ".join(exe), patch_tool, extra, patch_file)
input = None
if verbose:
cmd = '%s %s %s "%s"' % (sys.executable, patch_tool, extra, patch_file)
print("\n"+cmd)
else:
cmd = '%s %s -q %s "%s"' % (sys.executable, patch_tool, extra, patch_file)
ret = os.system(cmd)
assert ret == 0, "Error %d running test %s" % (ret, testname)
proc = subprocess.run(cmd, shell=True, input=input)
assert proc.returncode == 0, "Error %d running test %s" % (proc.returncode, testname)
os.chdir(save_cwd)


Expand All @@ -171,7 +190,7 @@ def _run_test(self, testname):
# recursive comparison
self._assert_dirs_equal(join(basepath, "[result]"),
tmpdir,
ignore=["%s.patch" % testname, ".svn", ".gitkeep", "[result]"])
ignore=["%s.patch" % testname, ".svn", ".gitkeep", "[result]", ".coveragerc"])
remove_tree_force(tmpdir)
return 0

Expand All @@ -189,11 +208,14 @@ def add_test_methods(cls):
testset = [testptn.match(e).group('name') for e in listdir(TESTS) if testptn.match(e)]
testset = sorted(set(testset))

for filename in testset:
for idx, filename in enumerate(testset):
methname = 'test_' + filename
def create_closure():
name = filename
return lambda self: self._run_test(name)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixes a deprecation warning in 3.12: unittest/case.py:690: DeprecationWarning: It is deprecated to return a value that is not None from a test case (<bound method add_test_methods.<locals>.create_closure.<locals>.<lambda> of <__main__.TestPatchFiles testMethod=test_11permission>>)

inputarg = ["", "-i", "--"][idx % 3]
def test(self):
self._run_test(name, inputarg)
return test
test = create_closure()
setattr(cls, methname, test)
if verbose:
Expand Down