##// END OF EJS Templates
the __future__ is now.
the __future__ is now.

File last commit:

r22963:2961b531
r22963:2961b531
Show More
test_pr.py
288 lines | 10.2 KiB | text/x-python | PythonLexer
Fernando Perez
Give test_pr proper env call to python interpreter....
r6745 #!/usr/bin/env python
Thomas Kluyver
Add script to test pull request
r6686 """
This is a script for testing pull requests for IPython. It merges the pull
request with current master, installs and tests on all available versions of
Python, and posts the results to Gist if any tests fail.
Usage:
python test_pr.py 1657
"""
import errno
from glob import glob
Thomas Kluyver
Add option to test without posting results.
r6711 import io
Thomas Kluyver
Add script to test pull request
r6686 import os
Thomas Kluyver
Pickle PR test logs so they can be posted without rerunning tests.
r6713 import pickle
Thomas Kluyver
Print missing libraries and platform.
r6692 import re
Thomas Kluyver
Add script to test pull request
r6686 import shutil
MinRK
time test run
r6736 import time
Thomas Kluyver
Add script to test pull request
r6686 from subprocess import call, check_call, check_output, PIPE, STDOUT, CalledProcessError
Thomas Kluyver
Add option to test without posting results.
r6711 import sys
Thomas Kluyver
Add script to test pull request
r6686
Thomas Kluyver
Add option to test without posting results.
r6711 import gh_api
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863 from gh_api import Obj
Thomas Kluyver
PR tester can now post results to Github as a comment.
r6694
Thomas Kluyver
Add script to test pull request
r6686 basedir = os.path.join(os.path.expanduser("~"), ".ipy_pr_tests")
repodir = os.path.join(basedir, "ipython")
ipy_repository = 'git://github.com/ipython/ipython.git'
Matthias BUSSONNIER
test_pr, fallback on http if git protocol fail
r6896 ipy_http_repository = 'http://github.com/ipython/ipython.git'
Thomas Kluyver
PR tester can now post results to Github as a comment.
r6694 gh_project="ipython/ipython"
Thomas Kluyver
Add script to test pull request
r6686
Thomas Kluyver
Update supported Python versions in tools/test_pr
r12250 supported_pythons = ['python2.7', 'python3.3']
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863
Thomas Kluyver
Print missing libraries and platform.
r6692 missing_libs_re = re.compile(r"Tools and libraries NOT available at test time:\n"
r"\s*(.*?)\n")
def get_missing_libraries(log):
m = missing_libs_re.search(log)
if m:
return m.group(1)
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863 class TestRun(object):
Thomas Kluyver
Update test_pr to allow passing args to iptest
r8125 def __init__(self, pr_num, extra_args):
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863 self.unavailable_pythons = []
self.venvs = []
self.pr_num = pr_num
Thomas Kluyver
Update test_pr to allow passing args to iptest
r8125 self.extra_args = extra_args
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863
self.pr = gh_api.get_pull_request(gh_project, pr_num)
self.setup()
self.results = []
def available_python_versions(self):
"""Get the executable names of available versions of Python on the system.
"""
for py in supported_pythons:
try:
check_call([py, '-c', 'import nose'], stdout=PIPE)
yield py
except (OSError, CalledProcessError):
self.unavailable_pythons.append(py)
def setup(self):
"""Prepare the repository and virtualenvs."""
try:
os.mkdir(basedir)
except OSError as e:
if e.errno != errno.EEXIST:
raise
os.chdir(basedir)
# Delete virtualenvs and recreate
for venv in glob('venv-*'):
shutil.rmtree(venv)
for py in self.available_python_versions():
check_call(['virtualenv', '-p', py, '--system-site-packages', 'venv-%s' % py])
self.venvs.append((py, 'venv-%s' % py))
# Check out and update the repository
if not os.path.exists('ipython'):
try :
check_call(['git', 'clone', ipy_repository])
except CalledProcessError :
check_call(['git', 'clone', ipy_http_repository])
os.chdir(repodir)
check_call(['git', 'checkout', 'master'])
try :
MinRK
use origin as first pull try in test_pr...
r8114 check_call(['git', 'pull', 'origin', 'master'])
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863 except CalledProcessError :
check_call(['git', 'pull', ipy_http_repository, 'master'])
MinRK
track sha of master in test_pr messages
r8134 self.master_sha = check_output(['git', 'log', '-1', '--format=%h']).decode('ascii').strip()
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863 os.chdir(basedir)
def get_branch(self):
repo = self.pr['head']['repo']['clone_url']
branch = self.pr['head']['ref']
owner = self.pr['head']['repo']['owner']['login']
mergeable = self.pr['mergeable']
os.chdir(repodir)
if mergeable:
merged_branch = "%s-%s" % (owner, branch)
# Delete the branch first
call(['git', 'branch', '-D', merged_branch])
check_call(['git', 'checkout', '-b', merged_branch])
check_call(['git', 'pull', '--no-ff', '--no-commit', repo, branch])
check_call(['git', 'commit', '-m', "merge %s/%s" % (repo, branch)])
else:
# Fetch the branch without merging it.
check_call(['git', 'fetch', repo, branch])
check_call(['git', 'checkout', 'FETCH_HEAD'])
os.chdir(basedir)
def markdown_format(self):
def format_result(result):
s = "* %s: " % result.py
if result.passed:
s += "OK"
else:
s += "Failed, log at %s" % result.log_url
if result.missing_libraries:
s += " (libraries not available: " + result.missing_libraries + ")"
return s
if self.pr['mergeable']:
MinRK
track sha of master in test_pr messages
r8134 com = self.pr['head']['sha'][:7] + " merged into master (%s)" % self.master_sha
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863 else:
com = self.pr['head']['sha'][:7] + " (can't merge cleanly)"
lines = ["**Test results for commit %s**" % com,
"Platform: " + sys.platform,
""] + \
[format_result(r) for r in self.results] + \
Thomas Kluyver
Update test_pr to allow passing args to iptest
r8125 [""]
if self.extra_args:
lines.append("Extra args: %r" % self.extra_args),
lines.append("Not available for testing: " + ", ".join(self.unavailable_pythons))
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863 return "\n".join(lines)
def post_results_comment(self):
body = self.markdown_format()
gh_api.post_issue_comment(gh_project, self.pr_num, body)
def print_results(self):
pr = self.pr
print("\n")
MinRK
track sha of master in test_pr messages
r8134 msg = "**Test results for commit %s" % pr['head']['sha'][:7]
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863 if pr['mergeable']:
MinRK
track sha of master in test_pr messages
r8134 msg += " merged into master (%s)**" % self.master_sha
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863 else:
MinRK
track sha of master in test_pr messages
r8134 msg += " (can't merge cleanly)**"
print(msg)
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863 print("Platform:", sys.platform)
for result in self.results:
if result.passed:
print(result.py, ":", "OK")
else:
print(result.py, ":", "Failed")
print(" Test log:", result.get('log_url') or result.log_file)
if result.missing_libraries:
print(" Libraries not available:", result.missing_libraries)
Thomas Kluyver
Update test_pr to allow passing args to iptest
r8125
if self.extra_args:
print("Extra args:", self.extra_args)
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863 print("Not available for testing:", ", ".join(self.unavailable_pythons))
def dump_results(self):
with open(os.path.join(basedir, 'lastresults.pkl'), 'wb') as f:
pickle.dump(self, f)
@staticmethod
def load_results():
with open(os.path.join(basedir, 'lastresults.pkl'), 'rb') as f:
return pickle.load(f)
def save_logs(self):
for result in self.results:
if not result.passed:
result_locn = os.path.abspath(os.path.join('venv-%s' % result.py,
self.pr['head']['sha'][:7]+".log"))
with io.open(result_locn, 'w', encoding='utf-8') as f:
f.write(result.log)
result.log_file = result_locn
def post_logs(self):
for result in self.results:
if not result.passed:
result.log_url = gh_api.post_gist(result.log,
description='IPython test log',
filename="results.log", auth=True)
def run(self):
for py, venv in self.venvs:
tic = time.time()
Thomas Kluyver
Update test_pr to allow passing args to iptest
r8125 passed, log = run_tests(venv, self.extra_args)
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863 elapsed = int(time.time() - tic)
print("Ran tests with %s in %is" % (py, elapsed))
missing_libraries = get_missing_libraries(log)
self.results.append(Obj(py=py,
passed=passed,
log=log,
missing_libraries=missing_libraries
)
)
Thomas Kluyver
Add script to test pull request
r6686
Thomas Kluyver
Update test_pr to allow passing args to iptest
r8125 def run_tests(venv, extra_args):
Thomas Kluyver
Add script to test pull request
r6686 py = os.path.join(basedir, venv, 'bin', 'python')
print(py)
os.chdir(repodir)
MinRK
clean build dir in test_pr
r6734 # cleanup build-dir
if os.path.exists('build'):
shutil.rmtree('build')
MinRK
swallow stdout and report time for build/install step in test_pr...
r8113 tic = time.time()
print ("\nInstalling IPython with %s" % py)
logfile = os.path.join(basedir, venv, 'install.log')
print ("Install log at %s" % logfile)
with open(logfile, 'wb') as f:
check_call([py, 'setup.py', 'install'], stdout=f)
toc = time.time()
print ("Installed IPython in %.1fs" % (toc-tic))
Thomas Kluyver
Add script to test pull request
r6686 os.chdir(basedir)
Thomas Kluyver
test_pr script prepares $PATH and $PYTHONPATH for the tests...
r6992 # Environment variables:
orig_path = os.environ["PATH"]
os.environ["PATH"] = os.path.join(basedir, venv, 'bin') + ':' + os.environ["PATH"]
os.environ.pop("PYTHONPATH", None)
MinRK
swallow stdout and report time for build/install step in test_pr...
r8113 # check that the right IPython is imported
ipython_file = check_output([py, '-c', 'import IPython; print (IPython.__file__)'])
ipython_file = ipython_file.strip().decode('utf-8')
if not ipython_file.startswith(os.path.join(basedir, venv)):
Thomas Kluyver
Update test_pr to allow passing args to iptest
r8125 msg = "IPython does not appear to be in the venv: %s" % ipython_file
msg += "\nDo you use setupegg.py develop?"
MinRK
swallow stdout and report time for build/install step in test_pr...
r8113 print(msg, file=sys.stderr)
return False, msg
Thomas Kluyver
Add script to test pull request
r6686 iptest = os.path.join(basedir, venv, 'bin', 'iptest')
if not os.path.exists(iptest):
iptest = os.path.join(basedir, venv, 'bin', 'iptest3')
print("\nRunning tests, this typically takes a few minutes...")
try:
Thomas Kluyver
Update test_pr to allow passing args to iptest
r8125 return True, check_output([iptest] + extra_args, stderr=STDOUT).decode('utf-8')
Thomas Kluyver
Add script to test pull request
r6686 except CalledProcessError as e:
Thomas Kluyver
PR tester can now post results to Github as a comment.
r6694 return False, e.output.decode('utf-8')
Thomas Kluyver
test_pr script prepares $PATH and $PYTHONPATH for the tests...
r6992 finally:
# Restore $PATH
os.environ["PATH"] = orig_path
Thomas Kluyver
Add script to test pull request
r6686
Thomas Kluyver
Pickle PR test logs so they can be posted without rerunning tests.
r6713
Thomas Kluyver
Update test_pr to allow passing args to iptest
r8125 def test_pr(num, post_results=True, extra_args=None):
Thomas Kluyver
Add option to test without posting results.
r6711 # Get Github authorisation first, so that the user is prompted straight away
# if their login is needed.
if post_results:
gh_api.get_auth_token()
Thomas Kluyver
Update test_pr to allow passing args to iptest
r8125 testrun = TestRun(num, extra_args or [])
Thomas Kluyver
Add script to test pull request
r6686
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863 testrun.get_branch()
Thomas Kluyver
Pickle PR test logs so they can be posted without rerunning tests.
r6713
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863 testrun.run()
testrun.dump_results()
Thomas Kluyver
Pickle PR test logs so they can be posted without rerunning tests.
r6713
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863 testrun.save_logs()
testrun.print_results()
Thomas Kluyver
Add script to test pull request
r6686
Thomas Kluyver
Add option to test without posting results.
r6711 if post_results:
Thomas Kluyver
Fix for test_pr script
r8080 testrun.post_logs()
Thomas Kluyver
Refactor test_pr.py to a more coherent design.
r7863 testrun.post_results_comment()
Thomas Kluyver
Add option to test without posting results.
r6711 print("(Posted to Github)")
Thomas Kluyver
Pickle PR test logs so they can be posted without rerunning tests.
r6713 else:
post_script = os.path.join(os.path.dirname(sys.argv[0]), "post_pr_test.py")
print("To post the results to Github, run", post_script)
Thomas Kluyver
Add option to test without posting results.
r6711
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(description="Test an IPython pull request")
Thomas Kluyver
Pickle PR test logs so they can be posted without rerunning tests.
r6713 parser.add_argument('-p', '--publish', action='store_true',
help="Publish the results to Github")
Thomas Kluyver
Add option to test without posting results.
r6711 parser.add_argument('number', type=int, help="The pull request number")
Thomas Kluyver
Update test_pr to allow passing args to iptest
r8125 args, extra_args = parser.parse_known_args()
test_pr(args.number, post_results=args.publish, extra_args=extra_args)