aboutsummaryrefslogtreecommitdiff
path: root/qa/pull-tester
diff options
context:
space:
mode:
Diffstat (limited to 'qa/pull-tester')
-rwxr-xr-xqa/pull-tester/build-tests.sh.in74
-rwxr-xr-xqa/pull-tester/pull-tester.py184
-rwxr-xr-xqa/pull-tester/pull-tester.sh15
-rwxr-xr-xqa/pull-tester/run-bitcoind-for-test.sh.in27
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