summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith Wall <kwall@apache.org>2011-11-09 19:44:55 +0000
committerKeith Wall <kwall@apache.org>2011-11-09 19:44:55 +0000
commitc1d79073a5ef4f55f9e6f3b723cbc9d0ef6910a7 (patch)
tree0006f1852809fb4b68f2441fb3bdf66ab6f12527
parenta25345ca75d0134c372e274dff15b9ff291ac853 (diff)
downloadqpid-python-c1d79073a5ef4f55f9e6f3b723cbc9d0ef6910a7.tar.gz
QPID-3552: Changes to allow running of python test on Jenkins. New --xml argument now understood by qpid-python-test that produces test output compatible with Junit-like tools. New Ant script to control start/stop of Java/Cpp Brokers and the running of qpid-python-test.
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1199932 13f79535-47bb-0310-9956-ffa450edef68
-rwxr-xr-xqpid/python/qpid-python-test68
-rw-r--r--qpid/python/qpid-python-test-ant.xml164
2 files changed, 223 insertions, 9 deletions
diff --git a/qpid/python/qpid-python-test b/qpid/python/qpid-python-test
index a47f633565..1a0f711ace 100755
--- a/qpid/python/qpid-python-test
+++ b/qpid/python/qpid-python-test
@@ -64,6 +64,8 @@ parser.add_option("-t", "--time", action="store_true", default=False,
help="report timing information on test run")
parser.add_option("-D", "--define", metavar="DEFINE", dest="defines",
action="append", default=[], help="define test parameters")
+parser.add_option("-x", "--xml", metavar="XML", dest="xml",
+ help="write test results in Junit style xml suitable for use by CI tools etc")
class Config:
@@ -188,6 +190,33 @@ def indent(text):
lines = text.split("\n")
return " %s" % "\n ".join(lines)
+# Write a 'minimal' Junit xml style report file suitable for use by CI tools such as Jenkins.
+class JunitXmlStyleReporter:
+
+ def __init__(self, file):
+ self.f = open(file, "w");
+
+ def begin(self):
+ self.f.write('<?xml version="1.0" encoding="UTF-8" ?>\n')
+ self.f.write('<testsuite>\n')
+
+ def report(self, name, result):
+ parts = name.split(".")
+ method = parts[-1]
+ module = '.'.join(parts[0:-1])
+ self.f.write('<testcase classname="%s" name="%s" time="%f">\n' % (module, method, result.time))
+ if result.failed:
+ self.f.write('<failure>\n')
+ self.f.write('<![CDATA[\n')
+ self.f.write(result.exceptions)
+ self.f.write(']]>\n')
+ self.f.write('</failure>\n')
+ self.f.write('</testcase>\n')
+
+ def end(self):
+ self.f.write('</testsuite>\n')
+ self.f.close()
+
class Interceptor:
def __init__(self):
@@ -327,13 +356,14 @@ class Runner:
else:
return None
- def print_exceptions(self):
+ def get_formatted_exceptions(self):
for name, info in self.exceptions:
if issubclass(info[0], Skipped):
- print indent("".join(traceback.format_exception_only(*info[:2]))).rstrip()
+ output = indent("".join(traceback.format_exception_only(*info[:2]))).rstrip()
else:
- print "Error during %s:" % name
- print indent("".join(traceback.format_exception(*info))).rstrip()
+ output = "Error during %s:" % name
+ output += indent("".join(traceback.format_exception(*info))).rstrip()
+ return output
ST_WIDTH = 8
@@ -361,20 +391,31 @@ def run_test(name, test, config):
sys.stdout.write(output)
sys.stdout.flush()
interceptor.begin()
+ start = time.time()
try:
runner = test()
finally:
interceptor.reset()
+ end = time.time()
if interceptor.dirty:
if interceptor.last != "\n":
sys.stdout.write("\n")
sys.stdout.write(output)
print " %s" % colorize_word(runner.status())
if runner.failed() or runner.skipped():
- runner.print_exceptions()
+ print runner.get_formatted_exceptions()
root.setLevel(level)
filter.patterns = patterns
- return runner.status()
+ return TestResult(end - start, runner.passed(), runner.skipped(), runner.failed(), runner.get_formatted_exceptions())
+
+class TestResult:
+
+ def __init__(self, time, passed, skipped, failed, exceptions):
+ self.time = time
+ self.passed = passed
+ self.skipped = skipped
+ self.failed = failed
+ self.exceptions = exceptions
class FunctionTest:
@@ -526,6 +567,10 @@ filtered = [t for t in h.tests if is_included(t.name())]
ignored = [t for t in h.tests if is_ignored(t.name())]
total = len(filtered) + len(ignored)
+if opts.xml and not list_only:
+ xmlr = JunitXmlStyleReporter(opts.xml);
+ xmlr.begin();
+
passed = 0
failed = 0
skipped = 0
@@ -535,11 +580,13 @@ for t in filtered:
print t.name()
else:
st = t.run()
- if st == PASS:
+ if xmlr:
+ xmlr.report(t.name(), st)
+ if st.passed:
passed += 1
- elif st == SKIP:
+ elif st.skipped:
skipped += 1
- elif st == FAIL:
+ elif st.failed:
failed += 1
if opts.hoe:
break
@@ -581,6 +628,9 @@ if not list_only:
colorize_word("average", "%.2fs average" % ((end - start)/run))]
print ", ".join(timing)
+if xmlr:
+ xmlr.end()
+
if failed or skipped:
sys.exit(1)
else:
diff --git a/qpid/python/qpid-python-test-ant.xml b/qpid/python/qpid-python-test-ant.xml
new file mode 100644
index 0000000000..6a74e6ac2b
--- /dev/null
+++ b/qpid/python/qpid-python-test-ant.xml
@@ -0,0 +1,164 @@
+<!--
+ -
+ - Licensed to the Apache Software Foundation (ASF) under one
+ - or more contributor license agreements. See the NOTICE file
+ - distributed with this work for additional information
+ - regarding copyright ownership. The ASF licenses this file
+ - to you under the Apache License, Version 2.0 (the
+ - "License"); you may not use this file except in compliance
+ - with the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing,
+ - software distributed under the License is distributed on an
+ - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ - KIND, either express or implied. See the License for the
+ - specific language governing permissions and limitations
+ - under the License.
+ -
+ -->
+
+<project name="qpid-python-test-ant" default="test" >
+
+ <!-- Ant wrapper around qpid-python-test. Starts Qpid broker; runs
+ qpid-python-test, and formats the test output. -->
+
+ <!-- Directories etc -->
+ <property name="python.dir" value="${basedir}"/>
+ <property name="qpid.root.dir" value="${basedir}/.."/>
+ <property name="java.dir" value="${basedir}/../java"/>
+ <property name="cpp.dir" value="${basedir}/../cpp"/>
+ <property name="build.dir" value="${python.dir}/build"/>
+ <property name="test.results.dir" value="${build.dir}/results"/>
+ <property name="test.work.dir" value="${build.dir}/work"/>
+
+ <!-- Qpid Broker Executable/Url/Port -->
+ <property name="qpid.port" value="15672"/>
+ <property name="qpid.python.broker.url" value="amqp://guest/guest@localhost:${qpid.port}"/>
+ <property name="qpid.executable" value="${java.dir}/build/bin/qpid-server"/>
+ <property name="qpid.executable.args" value="-p ${qpid.port}"/>
+
+ <!-- Additional modules to be added to command. Property must include -M -->
+ <property name="python.test.modules" value=""/>
+ <!-- Ignore file. Property must include -I -->
+ <property name="python.test.ignore" value=""/>
+
+ <!-- Time to wait for socket to be bound -->
+ <property name="ensurefree.maxwait" value="1000"/>
+ <property name="start.maxwait" value="10000"/>
+ <property name="stop.maxwait" value="10000"/>
+ <property name="socket.checkevery" value="1000"/>
+
+ <!-- Success message -->
+ <property name="passed.message" value=" 0 failed"/>
+
+
+ <target name="test" depends="clean, init, ensure-port-free, start-broker, run-tests, stop-broker, kill-broker, report"/>
+
+ <target name="init">
+ <mkdir dir="${test.results.dir}"/>
+ <mkdir dir="${test.work.dir}"/>
+ </target>
+
+ <target name="clean">
+ <delete dir="${test.results.dir}"/>
+ <delete dir="${test.work.dir}"/>
+ </target>
+
+ <target name="ensure-port-free" depends="init" unless="skip.ensure-port-free">
+ <await-port-free port="${qpid.port}" maxwait="${ensurefree.maxwait}" checkevery="${socket.checkevery}" timeoutproperty="ensurefree.timeout"/>
+ <fail message="Broker port ${qpid.port} is not free" if="ensurefree.timeout"/>
+ </target>
+
+ <target name="start-broker" depends="init">
+ <echo>Starting Qpid with ${qpid.executable} ${qpid.executable.args}</echo>
+ <exec executable="${qpid.executable}" spawn="true">
+ <env key="QPID_WORK" value="${test.work.dir}"/>
+ <arg line="${qpid.executable.args}"/>
+ </exec>
+
+ <await-port-bound port="${qpid.port}" maxwait="${start.maxwait}" checkevery="${socket.checkevery}" timeoutproperty="start.timeout"/>
+ </target>
+
+ <target name="stop-broker" depends="init">
+ <get-pid port="${qpid.port}" targetProperty="pid"/>
+ <echo>Stopping Qpid ${pid}</echo>
+ <kill-pid pid="${pid}" signo="-15"/>
+
+ <await-port-free port="${qpid.port}" maxwait="${stop.maxwait}" checkevery="${socket.checkevery}" timeoutproperty="stop.timeout"/>
+ </target>
+
+ <target name="kill-broker" depends="init" if="stop.timeout">
+ <get-pid port="${qpid.port}" targetProperty="pid"/>
+ <echo>Killing Qpid ${pid}</echo>
+ <kill-pid pid="${pid}" signo="-9"/>
+ </target>
+
+ <target name="run-tests" depends="init" unless="start.timeout">
+ <echo>Running test-suite</echo>
+ <exec executable="${python.dir}/qpid-python-test" output="${test.results.dir}/results.out" error="${test.results.dir}/results.err">
+ <env key="PYTHONPATH" value="${qpid.root.dir}/tests/src/py:${qpid.root.dir}/extras/qmf/src/py"/>
+ <arg line="-b ${qpid.python.broker.url} -x ${test.results.dir}/TEST-python.xml ${python.test.modules} ${python.test.ignore}"/>
+ </exec>
+
+ <condition property="tests.passed">
+ <isfileselected file="${test.results.dir}/results.out">
+ <contains text="${passed.message}"/>
+ </isfileselected>
+ </condition>
+ </target>
+
+ <target name="report" depends="init" unless="tests.passed">
+ <fail message="Test(s) failed" unless="tests.passed"/>
+ <echo message="Test(s) passed" if="tests.passed"/>
+ </target>
+
+ <macrodef name="get-pid">
+ <attribute name="targetProperty"/>
+ <attribute name="port"/>
+ <sequential>
+ <exec executable="lsof" outputproperty="@{targetProperty}">
+ <arg value="-t"/> <!-- Terse output -->
+ <arg value="-i"/> <arg value=":@{port}"/>
+ </exec>
+ </sequential>
+ </macrodef>
+
+ <macrodef name="kill-pid">
+ <attribute name="pid"/>
+ <attribute name="signo"/>
+ <sequential>
+ <exec executable="kill">
+ <arg value="@{signo}"/>
+ <arg value="@{pid}"/>
+ </exec>
+ </sequential>
+ </macrodef>
+
+ <macrodef name="await-port-free">
+ <attribute name="maxwait"/>
+ <attribute name="checkevery"/>
+ <attribute name="timeoutproperty"/>
+ <attribute name="port"/>
+ <sequential>
+ <waitfor maxwait="@{maxwait}" maxwaitunit="millisecond" checkevery="@{checkevery}" checkeveryunit="millisecond" timeoutproperty="@timeoutproperty">
+ <not>
+ <socket server="localhost" port="@{port}"/>
+ </not>
+ </waitfor>
+ </sequential>
+ </macrodef>
+
+ <macrodef name="await-port-bound">
+ <attribute name="maxwait"/>
+ <attribute name="checkevery"/>
+ <attribute name="timeoutproperty"/>
+ <attribute name="port"/>
+ <sequential>
+ <waitfor maxwait="@{maxwait}" maxwaitunit="millisecond" checkevery="@{checkevery}" checkeveryunit="millisecond" timeoutproperty="@timeoutproperty">
+ <socket server="localhost" port="@{port}"/>
+ </waitfor>
+ </sequential>
+ </macrodef>
+</project>