Monday 7 December 2009

Using grinder and junit.

It may be convenient to use junit tests for stress tests sometimes.

junit.py

from net.grinder.script.Grinder import grinder
from net.grinder.script import Test
from java.lang import Class
from java.lang import System
from java.lang import Integer
from java.lang import Runnable
from java.lang import Thread
from java.lang import Exception
from java.lang import Throwable
from org.junit.runner import JUnitCore
from org.junit.runner import Request
from org.junit.runner import Result
from org.junit.runner.notification import RunListener

class MyRunListener(RunListener):

def testFailure(self,failure):
print "jUniTFailure: " + failure.toString() + failure.trace
grinder.logger.error("jUniTFailure: " + failure.toString() + failure.trace)

def testFinished(self,description):
print "jUniTFinished: " + description.displayName
grinder.logger.output("jUniTFinished: " + description.displayName)

def testRunFinished(self,result):
grinder.logger.output("jUniTRunFinished: ")

def testRunStarted(self,description):
print "jUniTRunStarted: "
grinder.logger.output("jUniTRunStarted: ")

def testStarted(self,description):
print "jUniTestStarted: " + description.displayName
grinder.logger.output("jUniTestStarted: " + description.displayName)

# recursive method to get all tests regardless of what test description is used
# The description has a tree structure that has to be traversed. The actual tests are
# the leaves with no children
def getTests(description):
if (description.children == None or description.getChildren().size() == 0):
return [description]

childDescs = description.children
idx=0

children=[]
while idx < childDescs.size():
if (childDescs.get(idx).children == None or childDescs.get(idx).getChildren().size() == 0):
children.append(childDescs.get(idx))
else:
children.extend(getTests(childDescs.get(idx)))

idx=idx+1
return children

class TestRunner:
def __call__(self):

#wait with writing the reports
grinder.statistics.delayReports = 1

# get settings from the start script
testClassName = System.getProperty("grinder.test")
offsetStr = System.getProperty("grinder.offset")
offset=0
if offsetStr != None:
offset=int(offsetStr)


jUnitCore=JUnitCore()
#jUnitCore.addListener(MyRunListener())

mainRequest=Request.aClass(Class.forName(testClassName));

description= mainRequest.getRunner().getDescription()

testDecs=getTests(description)
idx=0
for testDec in testDecs:
#print "running test "+childDescs.get(idx).toString()

# we only have the display name for each test. The syntax is method_name(test_class_name)
displayName=testDec.displayName
method=displayName[:displayName.index('(')]
testClass=displayName[displayName.index('(')+1:displayName.index(')')]

#print "method: "+displayName[:displayName.index('(')]
# lookup the junit test
childReq=Request.method(Class.forName(testClass), displayName[:displayName.index('(')])
print "running test "+testClass+"."+method
print "#tests "+str(childReq.getRunner().testCount())
print "runner description "+childReq.getRunner().getDescription().displayName

# wrap the junit test with the grinder test. This basically triggers the statitics in
# grinder when we call any method on the wrapped object
wrappedJunitCore = Test(offset+idx, testDec.displayName).wrap(jUnitCore)

# start junit test and collect statistics for grinder
result=wrappedJunitCore.run(childReq)
idx=idx+1
if result.failureCount > 0:
grinder.statistics.forLastTest.setSuccess(0)
failures=result.failures
failure=failures.get(0)
print "failure: "+failure.toString()
print "failure trace: "+failure.getTrace()
grinder.logger.error("jUniTFailure: " + failure.toString() + failure.trace)



starting grinder using classpath from maven and ant
<?xml version="1.0"?>
<project name="main" basedir="." xmlns:artifact="antlib:org.apache.maven.artifact.ant">


<property name="script_folder" value="."/>
<property name="project_dir" value="."/>


<property name="repo_server" value="x.y.z"/>
<property name="repo_url" value="http://${repo_server}:8081"/>

<property name="m2_repo_path" value="${user.home}/.m2/repository"/>

<!-- handle artifacts -->
<!--artifact:remoteRepository id="public.repository" url="http://10.71.64.68:8081/nexus/content/repositories/central-m1/"/-->
<artifact:remoteRepository id="central" url="${repo_url}/nexus/content/groups/public"/>
<artifact:remoteRepository id="snapshot.repository" url="${repo_url}/nexus/content/groups/public-snapshots/"/>
<artifact:localRepository id="local.repository" path="${m2_repo_path}"/>

<artifact:dependencies pathId="deps.classpath" filesetId="deps.fileset" useScope="test">
<pom file="${project_dir}/pom.xml">
</pom>
<localRepository refid="local.repository"/>
<remoteRepository refid="central"/>
<remoteRepository refid="snapshot.repository"/>
</artifact:dependencies>


<artifact:dependencies pathId="grinder.classpath">
<dependency groupId="junit" artifactId="junit" version="4.7"/>
<dependency groupId="grinder" artifactId="grinder-j2se5" version="3.1"/>
<dependency groupId="grinder" artifactId="grinder" version="3.1"/>
<dependency groupId="grinder" artifactId="grinder-xmlbeans" version="3.1"/>
<dependency groupId="grinder" artifactId="picocontainer" version="1.3"/>
<dependency groupId="javax.xml.bind" artifactId="jsr173_api" version="1.0"/>
<dependency groupId="grinder" artifactId="jython" version="2.2.1"/>
<dependency groupId="grinder" artifactId="xmlbeans-xbean" version="2.3.0"/>
<dependency groupId="grinder" artifactId="picocontainer" version="1.3"/>


<localRepository refid="local.repository"/>
<remoteRepository refid="central"/>
<remoteRepository refid="snapshot.repository"/>
</artifact:dependencies>


<artifact:dependencies pathId="antcontrib.classpath">
<dependency groupId="ant-contrib"
artifactId="ant-contrib"
version="1.0b3"/>
<localRepository refid="local.repository"/>
<remoteRepository refid="central"/>
<remoteRepository refid="snapshot.repository"/>
</artifact:dependencies>


<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<path refid="antcontrib.classpath"/>
</classpath>
</taskdef>


<property name="testClassPattern" value="**/JUnit*IntegrationTest.class"/>
<property name="noTestsInParallell" value="1"/>
<target name="run-tests">
<var name="idx" value="0"/>
<for param="integrationtestfull" parallel="true" threadCount="${noTestsInParallell}">
<path>
<fileset dir="${project_dir}/target/test-classes" includes="${testClassPattern}"/>
</path>
<sequential>

<propertyregex property="integrationtesttmp"
input="@{integrationtestfull}"
override="true"
regexp=".+[\/\\]target[\/\\]test-classes[\/\\](.+)\.class"
select="\1"/>
<propertyregex property="integrationtest"
input="${integrationtesttmp}"
override="true"
regexp="[\/\\]"
replace="\."/>
<!--var name="offset" unset="true"/-->
<math result="offset" operand1="${idx}" operation="*" operand2="100" datatype="int"/>
<math result="idx" operand1="${idx}" operation="+" operand2="1" datatype="int"/>
<antcall target="run-agent">
<param name="grinder.test" value="${integrationtest}"/>
<param name="grinder.offset" value="${offset}"/>
</antcall>
</sequential>
</for>
</target>

<!-- Default values sent to the agent process -->
<property name="grinder.processes" value="1"/>
<property name="grinder.threads" value="1"/>
<property name="grinder.runs" value="1"/>
<property name="grinder.consoleHost" value="localhost"/>
<property name="grinder.consolePort" value="6372"/>

<target name="run-agent">
<echo message="grinder.test ${grinder.test} offset ${grinder.offset}"/>
<echo message="args to gridner agent -Dgrinder.runs=${grinder.runs} -Dgrinder.processes=${grinder.processes} -Dgrinder.threads=${grinder.threads} -Dgrinder.test=${grinder.test} -Dmts.integrationtest.enabled=true -Dmts.integrationtest.jnp.url=${mts.integrationtest.jnp.url} -Dgrinder.offset=${grinder.offset}"/>
<echo message="property file ${script_folder}/grinder.properties"/>
<java classname="net.grinder.Grinder"
fork="true"
failonerror="true"
maxmemory="64m">
<classpath>
<path refid="grinder.classpath"/>
<path refid="deps.classpath"/>
</classpath>
<arg value="${script_folder}/grinder.properties"/>
<!-- Args sent to the agent worker processes -->
<sysproperty key="grinder.jvm.arguments" value=" -Dgrinder.runs=${grinder.runs} -Dgrinder.processes=${grinder.processes} -Dgrinder.threads=${grinder.threads} -Dgrinder.test=${grinder.test} -Dmts.integrationtest.enabled=true -Dmts.integrationtest.jnp.url=${mts.integrationtest.jnp.url} -Dgrinder.offset=${grinder.offset}"/>
<!-- Args sent to the agent process -->
<sysproperty key="grinder.runs" value="${grinder.runs}"/>
<sysproperty key="grinder.processes" value="${grinder.processes}"/>
<sysproperty key="grinder.threads" value="${grinder.threads}"/>
<sysproperty key="grinder.consoleHost" value="${grinder.consoleHost}"/>
<sysproperty key="grinder.consolePort" value="${grinder.consolePort}"/>
</java>
</target>

<target name="run-console">
<java classname="net.grinder.Console"
fork="true"
failonerror="true"
maxmemory="64m">
<classpath>
<path refid="grinder.classpath"/>
</classpath>
</java>
</target>
<path id="jython.classpath">
<path refid="grinder.classpath"/>
<!--path refid="deps.classpath"/-->
<pathelement location="target/classes/"/>
<pathelement location="target/test-classes/"/>
</path>

</project>