Author: kwall
Date: Wed Nov  9 19:44:55 2011
New Revision: 1199932

URL: http://svn.apache.org/viewvc?rev=1199932&view=rev
Log:
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.

Added:
    qpid/trunk/qpid/python/qpid-python-test-ant.xml
Modified:
    qpid/trunk/qpid/python/qpid-python-test

Modified: qpid/trunk/qpid/python/qpid-python-test
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid-python-test?rev=1199932&r1=1199931&r2=1199932&view=diff
==============================================================================
--- qpid/trunk/qpid/python/qpid-python-test (original)
+++ qpid/trunk/qpid/python/qpid-python-test Wed Nov  9 19:44:55 2011
@@ -64,6 +64,8 @@ parser.add_option("-t", "--time", action
                   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_inc
 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:

Added: qpid/trunk/qpid/python/qpid-python-test-ant.xml
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid-python-test-ant.xml?rev=1199932&view=auto
==============================================================================
--- qpid/trunk/qpid/python/qpid-python-test-ant.xml (added)
+++ qpid/trunk/qpid/python/qpid-python-test-ant.xml Wed Nov  9 19:44:55 2011
@@ -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>



---------------------------------------------------------------------
Apache Qpid - AMQP Messaging Implementation
Project:      http://qpid.apache.org
Use/Interact: mailto:commits-subscr...@qpid.apache.org

Reply via email to