diff options
Diffstat (limited to 'qa/pull-tester')
-rwxr-xr-x | qa/pull-tester/build-tests.sh.in | 74 | ||||
-rwxr-xr-x | qa/pull-tester/pull-tester.py | 184 | ||||
-rwxr-xr-x | qa/pull-tester/pull-tester.sh | 15 | ||||
-rwxr-xr-x | qa/pull-tester/run-bitcoind-for-test.sh.in | 27 |
4 files changed, 300 insertions, 0 deletions
diff --git a/qa/pull-tester/build-tests.sh.in b/qa/pull-tester/build-tests.sh.in new file mode 100755 index 0000000000..461e7be048 --- /dev/null +++ b/qa/pull-tester/build-tests.sh.in @@ -0,0 +1,74 @@ +#!/bin/bash +# Param1: The prefix to mingw staging +# Param2: Path to java comparison tool +# Param3: Number of make jobs. Defaults to 1. + +# Exit immediately if anything fails: +set -e +set -o xtrace + +MINGWPREFIX=$1 +JAVA_COMPARISON_TOOL=$2 +JOBS=${3-1} +OUT_DIR=${4-} + +if [ $# -lt 2 ]; then + echo "Usage: $0 [mingw-prefix] [java-comparison-tool] <make jobs> <save output dir>" + exit 1 +fi + +DISTDIR=@PACKAGE@-@VERSION@ + +# Cross-compile for windows first (breaking the mingw/windows build is most common) +cd @abs_top_srcdir@ +make distdir +mv $DISTDIR win32-build +cd win32-build +./configure --disable-silent-rules --disable-ccache --prefix=$MINGWPREFIX --host=i586-mingw32msvc --with-qt-bindir=$MINGWPREFIX/host/bin --with-qt-plugindir=$MINGWPREFIX/plugins --with-qt-incdir=$MINGWPREFIX/include --with-boost=$MINGWPREFIX --with-protoc-bindir=$MINGWPREFIX/host/bin CPPFLAGS=-I$MINGWPREFIX/include LDFLAGS=-L$MINGWPREFIX/lib +make -j$JOBS + +# And compile for Linux: +cd @abs_top_srcdir@ +make distdir +mv $DISTDIR linux-build +cd linux-build +# TODO: re-enable blockchain tester tool, as of 11 Oct 2013 is it not working properly +# on the pull-tester machine. +#./configure --disable-silent-rules --disable-ccache --with-comparison-tool="$JAVA_COMPARISON_TOOL" +./configure --disable-silent-rules --disable-ccache +make -j$JOBS + +# link interesting binaries to parent out/ directory, if it exists. Do this before +# running unit tests (we want bad binaries to be easy to find) +if [ -d "$OUT_DIR" -a -w "$OUT_DIR" ]; then + set +e + # Windows: + cp @abs_top_srcdir@/win32-build/src/bitcoind.exe $OUT_DIR/bitcoind.exe + cp @abs_top_srcdir@/win32-build/src/test/test_bitcoin.exe $OUT_DIR/test_bitcoin.exe + cp @abs_top_srcdir@/win32-build/src/qt/bitcoind-qt.exe $OUT_DIR/bitcoin-qt.exe + # Linux: + cp @abs_top_srcdir@/linux-build/src/bitcoind $OUT_DIR/bitcoind + cp @abs_top_srcdir@/linux-build/src/test/test_bitcoin $OUT_DIR/test_bitcoin + cp @abs_top_srcdir@/linux-build/src/qt/bitcoind-qt $OUT_DIR/bitcoin-qt + set -e +fi + +# Run unit tests and blockchain-tester on Linux: +cd @abs_top_srcdir@/linux-build +make check + +# Clean up builds (pull-tester machine doesn't have infinite disk space) +cd @abs_top_srcdir@/linux-build +make clean +cd @abs_top_srcdir@/win32-build +make clean + +# TODO: Fix code coverage builds on pull-tester machine +# # Test code coverage +# cd @abs_top_srcdir@ +# make distdir +# mv $DISTDIR linux-coverage-build +# cd linux-coverage-build +# ./configure --enable-lcov --disable-silent-rules --disable-ccache --with-comparison-tool="$JAVA_COMPARISON_TOOL" +# make -j$JOBS +# make cov diff --git a/qa/pull-tester/pull-tester.py b/qa/pull-tester/pull-tester.py new file mode 100755 index 0000000000..34dd74c7e0 --- /dev/null +++ b/qa/pull-tester/pull-tester.py @@ -0,0 +1,184 @@ +#!/usr/bin/python +import json +from urllib import urlopen +import requests +import getpass +from string import Template +import sys +import os +import subprocess + +class RunError(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) + +def run(command, **kwargs): + fail_hard = kwargs.pop("fail_hard", True) + # output to /dev/null by default: + kwargs.setdefault("stdout", open('/dev/null', 'w')) + kwargs.setdefault("stderr", open('/dev/null', 'w')) + command = Template(command).substitute(os.environ) + if "TRACE" in os.environ: + if 'cwd' in kwargs: + print("[cwd=%s] %s"%(kwargs['cwd'], command)) + else: print(command) + try: + process = subprocess.Popen(command.split(' '), **kwargs) + process.wait() + except KeyboardInterrupt: + process.terminate() + raise + if process.returncode != 0 and fail_hard: + raise RunError("Failed: "+command) + return process.returncode + +def checkout_pull(clone_url, commit, out): + # Init + build_dir=os.environ["BUILD_DIR"] + run("umount ${CHROOT_COPY}/proc", fail_hard=False) + run("rsync --delete -apv ${CHROOT_MASTER}/ ${CHROOT_COPY}") + run("rm -rf ${CHROOT_COPY}${SCRIPTS_DIR}") + run("cp -a ${SCRIPTS_DIR} ${CHROOT_COPY}${SCRIPTS_DIR}") + # Merge onto upstream/master + run("rm -rf ${BUILD_DIR}") + run("mkdir -p ${BUILD_DIR}") + run("git clone ${CLONE_URL} ${BUILD_DIR}") + run("git remote add pull "+clone_url, cwd=build_dir, stdout=out, stderr=out) + run("git fetch pull", cwd=build_dir, stdout=out, stderr=out) + if run("git merge "+ commit, fail_hard=False, cwd=build_dir, stdout=out, stderr=out) != 0: + return False + run("chown -R ${BUILD_USER}:${BUILD_GROUP} ${BUILD_DIR}", stdout=out, stderr=out) + run("mount --bind /proc ${CHROOT_COPY}/proc") + return True + +def commentOn(commentUrl, success, inMerge, needTests, linkUrl): + common_message = """ +This test script verifies pulls every time they are updated. It, however, dies sometimes and fails to test properly. If you are waiting on a test, please check timestamps to verify that the test.log is moving at http://jenkins.bluematt.me/pull-tester/current/ +Contact BlueMatt on freenode if something looks broken.""" + + # Remove old BitcoinPullTester comments (I'm being lazy and not paginating here) + recentcomments = requests.get(commentUrl+"?sort=created&direction=desc", + auth=(os.environ['GITHUB_USER'], os.environ["GITHUB_AUTH_TOKEN"])).json + for comment in recentcomments: + if comment["user"]["login"] == os.environ["GITHUB_USER"] and common_message in comment["body"]: + requests.delete(comment["url"], + auth=(os.environ['GITHUB_USER'], os.environ["GITHUB_AUTH_TOKEN"])) + + if success == True: + post_data = { "body" : "Automatic sanity-testing: PASSED, see " + linkUrl + " for binaries and test log." + common_message} + elif inMerge: + post_data = { "body" : "Automatic sanity-testing: FAILED MERGE, see " + linkUrl + " for test log." + """ + +This pull does not merge cleanly onto current master""" + common_message} + else: + post_data = { "body" : "Automatic sanity-testing: FAILED BUILD/TEST, see " + linkUrl + " for binaries and test log." + """ + +This could happen for one of several reasons: +1. It chanages paths in makefile.linux-mingw or otherwise changes build scripts in a way that made them incompatible with the automated testing scripts (please tweak those patches in qa/pull-tester) +2. It adds/modifies tests which test network rules (thanks for doing that), which conflicts with a patch applied at test time +3. It does not build on either Linux i386 or Win32 (via MinGW cross compile) +4. The test suite fails on either Linux i386 or Win32 +5. The block test-cases failed (lookup the first bNN identifier which failed in https://github.com/TheBlueMatt/test-scripts/blob/master/FullBlockTestGenerator.java) + +If you believe this to be in error, please ping BlueMatt on freenode or TheBlueMatt here. +""" + common_message} + + resp = requests.post(commentUrl, json.dumps(post_data), auth=(os.environ['GITHUB_USER'], os.environ["GITHUB_AUTH_TOKEN"])) + +def testpull(number, comment_url, clone_url, commit): + print("Testing pull %d: %s : %s"%(number, clone_url,commit)) + + dir = os.environ["RESULTS_DIR"] + "/" + commit + "/" + print(" ouput to %s"%dir) + if os.path.exists(dir): + os.system("rm -r " + dir) + os.makedirs(dir) + currentdir = os.environ["RESULTS_DIR"] + "/current" + os.system("rm -r "+currentdir) + os.system("ln -s " + dir + " " + currentdir) + out = open(dir + "test.log", 'w+') + + resultsurl = os.environ["RESULTS_URL"] + commit + checkedout = checkout_pull(clone_url, commit, out) + if checkedout != True: + print("Failed to test pull - sending comment to: " + comment_url) + commentOn(comment_url, False, True, False, resultsurl) + open(os.environ["TESTED_DB"], "a").write(commit + "\n") + return + + run("rm -rf ${CHROOT_COPY}/${OUT_DIR}", fail_hard=False); + run("mkdir -p ${CHROOT_COPY}/${OUT_DIR}", fail_hard=False); + run("chown -R ${BUILD_USER}:${BUILD_GROUP} ${CHROOT_COPY}/${OUT_DIR}", fail_hard=False) + + script = os.environ["BUILD_PATH"]+"/qa/pull-tester/pull-tester.sh" + script += " ${BUILD_PATH} ${MINGW_DEPS_DIR} ${SCRIPTS_DIR}/BitcoindComparisonTool.jar 6 ${OUT_DIR}" + returncode = run("chroot ${CHROOT_COPY} sudo -u ${BUILD_USER} -H timeout ${TEST_TIMEOUT} "+script, + fail_hard=False, stdout=out, stderr=out) + + run("mv ${CHROOT_COPY}/${OUT_DIR} " + dir) + run("mv ${BUILD_DIR} " + dir) + + if returncode == 42: + print("Successfully tested pull (needs tests) - sending comment to: " + comment_url) + commentOn(comment_url, True, False, True, resultsurl) + elif returncode != 0: + print("Failed to test pull - sending comment to: " + comment_url) + commentOn(comment_url, False, False, False, resultsurl) + else: + print("Successfully tested pull - sending comment to: " + comment_url) + commentOn(comment_url, True, False, False, resultsurl) + open(os.environ["TESTED_DB"], "a").write(commit + "\n") + +def environ_default(setting, value): + if not setting in os.environ: + os.environ[setting] = value + +if getpass.getuser() != "root": + print("Run me as root!") + sys.exit(1) + +if "GITHUB_USER" not in os.environ or "GITHUB_AUTH_TOKEN" not in os.environ: + print("GITHUB_USER and/or GITHUB_AUTH_TOKEN environment variables not set") + sys.exit(1) + +environ_default("CLONE_URL", "https://github.com/bitcoin/bitcoin.git") +environ_default("MINGW_DEPS_DIR", "/mnt/w32deps") +environ_default("SCRIPTS_DIR", "/mnt/test-scripts") +environ_default("CHROOT_COPY", "/mnt/chroot-tmp") +environ_default("CHROOT_MASTER", "/mnt/chroot") +environ_default("OUT_DIR", "/mnt/out") +environ_default("BUILD_PATH", "/mnt/bitcoin") +os.environ["BUILD_DIR"] = os.environ["CHROOT_COPY"] + os.environ["BUILD_PATH"] +environ_default("RESULTS_DIR", "/mnt/www/pull-tester") +environ_default("RESULTS_URL", "http://jenkins.bluematt.me/pull-tester/") +environ_default("GITHUB_REPO", "bitcoin/bitcoin") +environ_default("TESTED_DB", "/mnt/commits-tested.txt") +environ_default("BUILD_USER", "matt") +environ_default("BUILD_GROUP", "matt") +environ_default("TEST_TIMEOUT", str(60*60*2)) + +print("Optional usage: pull-tester.py 2112") + +f = open(os.environ["TESTED_DB"]) +tested = set( line.rstrip() for line in f.readlines() ) +f.close() + +if len(sys.argv) > 1: + pull = requests.get("https://api.github.com/repos/"+os.environ["GITHUB_REPO"]+"/pulls/"+sys.argv[1], + auth=(os.environ['GITHUB_USER'], os.environ["GITHUB_AUTH_TOKEN"])).json + testpull(pull["number"], pull["_links"]["comments"]["href"], + pull["head"]["repo"]["clone_url"], pull["head"]["sha"]) + +else: + for page in range(1,100): + result = requests.get("https://api.github.com/repos/"+os.environ["GITHUB_REPO"]+"/pulls?state=open&page=%d"%(page,), + auth=(os.environ['GITHUB_USER'], os.environ["GITHUB_AUTH_TOKEN"])).json + if len(result) == 0: break; + for pull in result: + if pull["head"]["sha"] in tested: + print("Pull %d already tested"%(pull["number"],)) + continue + testpull(pull["number"], pull["_links"]["comments"]["href"], + pull["head"]["repo"]["clone_url"], pull["head"]["sha"]) diff --git a/qa/pull-tester/pull-tester.sh b/qa/pull-tester/pull-tester.sh new file mode 100755 index 0000000000..13c800c16a --- /dev/null +++ b/qa/pull-tester/pull-tester.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# Helper script for pull-tester. +#Param 1: path to bitcoin srcroot +#Param ...: arguments for build-test.sh + +if [ $# -lt 1 ]; then + echo "usage: $0 [bitcoin srcroot] build-test arguments..." +fi + +cd $1 +shift + +./autogen.sh +./configure +./qa/pull-tester/build-tests.sh "$@" diff --git a/qa/pull-tester/run-bitcoind-for-test.sh.in b/qa/pull-tester/run-bitcoind-for-test.sh.in new file mode 100755 index 0000000000..e02fef3b56 --- /dev/null +++ b/qa/pull-tester/run-bitcoind-for-test.sh.in @@ -0,0 +1,27 @@ +#!/bin/bash +DATADIR="@abs_top_builddir@/.bitcoin" +rm -rf "$DATADIR" +mkdir -p "$DATADIR"/regtest +touch "$DATADIR/regtest/debug.log" +tail -q -n 1 -F "$DATADIR/regtest/debug.log" | grep -m 1 -q "Done loading" & +WAITER=$! +"@abs_top_builddir@/src/bitcoind@EXEEXT@" -connect=0.0.0.0 -datadir="$DATADIR" -rpcuser=user -rpcpassword=pass -listen -keypool=3 -debug -logtimestamps -port=18444 -regtest & +BITCOIND=$! + +#Install a watchdog. +(sleep 10 && kill -0 $WAITER 2>/dev/null && kill -9 $BITCOIND $$)& +wait $WAITER + +if [ -n "$TIMEOUT" ]; then + timeout "$TIMEOUT"s "$@" + RETURN=$? +else + "$@" + RETURN=$? +fi + +(sleep 15 && kill -0 $BITCOIND 2>/dev/null && kill -9 $BITCOIND $$)& +kill $BITCOIND && wait $BITCOIND + +# timeout returns 124 on timeout, otherwise the return value of the child +exit $RETURN |