##// END OF EJS Templates
Remove Python 3.1 from PR testing
Remove Python 3.1 from PR testing

File last commit:

r7864:262c4232
r7864:262c4232
Show More
test_pr.py
266 lines | 9.1 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
"""
from __future__ import print_function
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 json
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
PR tester can now post results to Github as a comment.
r6694 import requests
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
Remove Python 3.1 from PR testing
r7864 supported_pythons = ['python2.6', 'python2.7', 'python3.2']
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):
def __init__(self, pr_num):
self.unavailable_pythons = []
self.venvs = []
self.pr_num = pr_num
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 :
check_call(['git', 'pull', ipy_repository, 'master'])
except CalledProcessError :
check_call(['git', 'pull', ipy_http_repository, 'master'])
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']:
com = self.pr['head']['sha'][:7] + " merged into master"
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] + \
["",
"Not available for testing: " + ", ".join(self.unavailable_pythons)]
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")
if pr['mergeable']:
print("**Test results for commit %s merged into master**" % pr['head']['sha'][:7])
else:
print("**Test results for commit %s (can't merge cleanly)**" % pr['head']['sha'][:7])
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)
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()
passed, log = run_tests(venv)
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
def run_tests(venv):
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')
Thomas Kluyver
Add script to test pull request
r6686 check_call([py, 'setup.py', 'install'])
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)
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:
return True, check_output([iptest], stderr=STDOUT).decode('utf-8')
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
Add option to test without posting results.
r6711 def test_pr(num, post_results=True):
# 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
Refactor test_pr.py to a more coherent design.
r7863 testrun = TestRun(num)
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
Refactor test_pr.py to a more coherent design.
r7863 results_urls = testrun.post_logs
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")
args = parser.parse_args()
Thomas Kluyver
Pickle PR test logs so they can be posted without rerunning tests.
r6713 test_pr(args.number, post_results=args.publish)