NIHVIVO-1129 First stab at a load-testing framework.

This commit is contained in:
jeb228 2011-01-03 17:11:46 +00:00
parent e8d37f9a88
commit 018f26a712
7 changed files with 599 additions and 0 deletions

View file

@ -5,6 +5,7 @@ Apache License 2.0
------------------ ------------------
licenses/apache.LICENSE.txt licenses/apache.LICENSE.txt
ant-jmeter
commons-beanutils-core commons-beanutils-core
commons-betwixt commons-betwixt
commons-cli commons-cli

View file

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<!-- ======================================================================
vivoLoadTesting
Run JMeter tests and summarize the results.
====================================================================== -->
<project name="vivoLoadTesting" default="describe">
<description>
Run JMeter tests and summarize the results.
</description>
<taskdef name="jmeter"
classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask"
classpath="lib/ant-jmeter-1.0.9.jar" />
<!-- =================================
target: describe
================================= -->
<target name="describe" description="--> Describe the targets (this is the default).">
<echo>
all - Run the tests and summarize the output.
clean - Remove any existing output and summaries.
run - Just run the tests.
report - Just summarize output from previously run tests.
</echo>
</target>
<!-- =================================
target: all
================================= -->
<target name="all"
depends="clean, run, report"
description="--> Run the tests and summarize the output." />
<!-- =================================
target: clean
================================= -->
<target name="clean"
depends="properties"
description="--> Remove any existing output and summaries.">
<delete dir="${results.dir}" />
</target>
<!-- - - - - - - - - - - - - - - - - -
target: properties
- - - - - - - - - - - - - - - - - -->
<target name="properties">
<property name="test.name" value="firstTest" />
<property name="tests.dir" value="tests" />
<property name="results.dir" value=".results" />
<property name="properties.file" location="loadtesting.properties" />
<fail message="You must create a &quot;${properties.file}&quot; file.">
<condition>
<not>
<available file="${properties.file}" />
</not>
</condition>
</fail>
<property file="${properties.file}" />
<fail unless="jmeter.home.dir"
message="${properties.file} must contain a value for jmeter.home.dir" />
<fail unless="webapp.host"
message="${properties.file} must contain a value for webapp.host" />
<fail unless="webapp.port"
message="${properties.file} must contain a value for webapp.port" />
<fail unless="webapp.name"
message="${properties.file} must contain a value for webapp.name" />
<property name="webapp.url" value="http://${webapp.host}:${webapp.port}/${webapp.name}" />
</target>
<!-- - - - - - - - - - - - - - - - - -
target: prepare
- - - - - - - - - - - - - - - - - -->
<target name="prepare" depends="properties">
<mkdir dir="${results.dir}" />
</target>
<!-- =================================
target: run
================================= -->
<target name="run" depends="prepare" description="--> Just run the tests.">
<delete file="${results.dir}/${test.name}.jtl" />
<delete file="${results.dir}/${test.name}.html" />
<condition property="webapp.is.available">
<http url="${webapp.url}" />
</condition>
<fail unless="webapp.is.available" message="Webapp is not available at ${webapp.url}" />
<jmeter jmeterhome="${jmeter.home.dir}"
testplan="${tests.dir}/${test.name}.jmx"
resultlogdir="${results.dir}">
<!-- Set parameters for the tests -->
<property name="webapp.host" value="${webapp.host}" />
<property name="webapp.port" value="${webapp.port}" />
<property name="webapp.name" value="${webapp.name}" />
<!-- Set parameters for the output -->
<property name="jmeter.save.saveservice.output_format" value="xml" />
<property name="jmeter.save.saveservice.response_data.on_error" value="true" />
<property name="jmeter.save.saveservice.url" value="true" />
<property name="jmeter.save.saveservice.bytes" value="true" />
<!-- Show a summary line periodically, so we know its running. -->
<property name="summariser.name" value="summary" />
<property name="summariser.out" value="true" />
<property name="summariser.log" value="true" />
<property name="summariser.interval" value="5" />
</jmeter>
</target>
<!-- =================================
target: report
================================= -->
<target name="report" depends="prepare" description="--> Just summarize the output.">
<xslt in="${results.dir}/${test.name}.jtl"
out="${results.dir}/${test.name}.html"
style="jmeter-results-report.xsl" />
</target>
</project>

View file

@ -0,0 +1,22 @@
# -----------------------------------------------------------------------------
#
# VIVO load-testing properties
#
# This file is provided as example.loadtesting.properties.
#
# Save a copy of this file as loadtesting.properties, and edit the properties
# as needed for your deployment.
#
# -----------------------------------------------------------------------------
#
# The base install directory for JMeter.
#
jmeter.home.dir = /Users/jeb228/Downloads/JMeter/jakarta-jmeter-2.4
#
# Where do we find the Vivo instance that we are testing?
#
webapp.host = localhost
webapp.port = 8080
webapp.name = vivo

View file

@ -0,0 +1,299 @@
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!--
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.
-->
<!--
Stylesheet for processing 2.1 output format test result files
To uses this directly in a browser, add the following to the JTL file as line 2:
<? xml-stylesheet type="text/xsl" href="../extras/jmeter-results-report_21.xsl" ?>
and you can then view the JTL in a browser
-->
<xsl:output method="html" indent="yes" encoding="US-ASCII" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" />
<xsl:template match="testResults">
<html>
<head>
<title>Load Test Results</title>
<style type="text/css">
body {
font:normal 68% verdana,arial,helvetica;
color:#000000;
}
table tr td, table tr th {
font-size: 68%;
}
table.details tr th{
font-weight: bold;
text-align:left;
background:#a6caf0;
white-space: nowrap;
}
table.details tr td{
background:#eeeee0;
white-space: nowrap;
}
h1 {
margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
}
h2 {
margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
}
h3 {
margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
}
.Failure {
font-weight:bold; color:red;
}
</style>
</head>
<body>
<xsl:call-template name="pageHeader" />
<xsl:call-template name="summary" />
<hr size="1" width="95%" align="left" />
<xsl:call-template name="pagelist" />
<hr size="1" width="95%" align="left" />
<xsl:call-template name="detail" />
</body>
</html>
</xsl:template>
<xsl:template name="pageHeader">
<h1>Load Test Results</h1>
<table width="100%">
<tr>
<td align="left"></td>
<td align="right">Designed for use with <a href="http://jakarta.apache.org/jmeter">JMeter</a> and <a href="http://ant.apache.org">Ant</a>.</td>
</tr>
</table>
<hr size="1" />
</xsl:template>
<xsl:template name="summary">
<h2>Summary</h2>
<table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
<tr valign="top">
<th>Tests</th>
<th>Failures</th>
<th>Success Rate</th>
<th>Average Time</th>
<th>Min Time</th>
<th>Max Time</th>
</tr>
<tr valign="top">
<xsl:variable name="allCount" select="count(/testResults/*)" />
<xsl:variable name="allFailureCount" select="count(/testResults/*[attribute::s='false'])" />
<xsl:variable name="allSuccessCount" select="count(/testResults/*[attribute::s='true'])" />
<xsl:variable name="allSuccessPercent" select="$allSuccessCount div $allCount" />
<xsl:variable name="allTotalTime" select="sum(/testResults/*/@t)" />
<xsl:variable name="allAverageTime" select="$allTotalTime div $allCount" />
<xsl:variable name="allMinTime">
<xsl:call-template name="min">
<xsl:with-param name="nodes" select="/testResults/*/@t" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="allMaxTime">
<xsl:call-template name="max">
<xsl:with-param name="nodes" select="/testResults/*/@t" />
</xsl:call-template>
</xsl:variable>
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="$allFailureCount &gt; 0">Failure</xsl:when>
</xsl:choose>
</xsl:attribute>
<td>
<xsl:value-of select="$allCount" />
</td>
<td>
<xsl:value-of select="$allFailureCount" />
</td>
<td>
<xsl:call-template name="display-percent">
<xsl:with-param name="value" select="$allSuccessPercent" />
</xsl:call-template>
</td>
<td>
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$allAverageTime" />
</xsl:call-template>
</td>
<td>
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$allMinTime" />
</xsl:call-template>
</td>
<td>
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$allMaxTime" />
</xsl:call-template>
</td>
</tr>
</table>
</xsl:template>
<xsl:template name="pagelist">
<h2>Pages</h2>
<table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
<tr valign="top">
<th>URL</th>
<th>Tests</th>
<th>Failures</th>
<th>Success Rate</th>
<th>Average Time</th>
<th>Min Time</th>
<th>Max Time</th>
</tr>
<xsl:for-each select="/testResults/*[not(@lb = preceding::*/@lb)]">
<xsl:variable name="label" select="@lb" />
<xsl:variable name="count" select="count(../*[@lb = current()/@lb])" />
<xsl:variable name="failureCount" select="count(../*[@lb = current()/@lb][attribute::s='false'])" />
<xsl:variable name="successCount" select="count(../*[@lb = current()/@lb][attribute::s='true'])" />
<xsl:variable name="successPercent" select="$successCount div $count" />
<xsl:variable name="totalTime" select="sum(../*[@lb = current()/@lb]/@t)" />
<xsl:variable name="averageTime" select="$totalTime div $count" />
<xsl:variable name="minTime">
<xsl:call-template name="min">
<xsl:with-param name="nodes" select="../*[@lb = current()/@lb]/@t" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="maxTime">
<xsl:call-template name="max">
<xsl:with-param name="nodes" select="../*[@lb = current()/@lb]/@t" />
</xsl:call-template>
</xsl:variable>
<tr valign="top">
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
</xsl:choose>
</xsl:attribute>
<td>
<xsl:value-of select="$label" />
</td>
<td>
<xsl:value-of select="$count" />
</td>
<td>
<xsl:value-of select="$failureCount" />
</td>
<td>
<xsl:call-template name="display-percent">
<xsl:with-param name="value" select="$successPercent" />
</xsl:call-template>
</td>
<td>
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$averageTime" />
</xsl:call-template>
</td>
<td>
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$minTime" />
</xsl:call-template>
</td>
<td>
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="$maxTime" />
</xsl:call-template>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template name="detail">
<xsl:variable name="allFailureCount" select="count(/testResults/*[attribute::s='false'])" />
<xsl:if test="$allFailureCount > 0">
<h2>Failure Detail</h2>
<xsl:for-each select="/testResults/*[not(@lb = preceding::*/@lb)]">
<xsl:variable name="failureCount" select="count(../*[@lb = current()/@lb][attribute::s='false'])" />
<xsl:if test="$failureCount > 0">
<h3><xsl:value-of select="@lb" /></h3>
<table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
<tr valign="top">
<th>Response</th>
<th>Failure Message</th>
</tr>
<xsl:for-each select="/testResults/*[@lb = current()/@lb][attribute::s='false']">
<tr>
<td><xsl:value-of select="@rc | @rs" /> - <xsl:value-of select="@rm" /></td>
<td><xsl:value-of select="assertionResult/failureMessage" /></td>
</tr>
</xsl:for-each>
</table>
</xsl:if>
</xsl:for-each>
</xsl:if>
</xsl:template>
<xsl:template name="min">
<xsl:param name="nodes" select="/.." />
<xsl:choose>
<xsl:when test="not($nodes)">NaN</xsl:when>
<xsl:otherwise>
<xsl:for-each select="$nodes">
<xsl:sort data-type="number" />
<xsl:if test="position() = 1">
<xsl:value-of select="number(.)" />
</xsl:if>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="max">
<xsl:param name="nodes" select="/.." />
<xsl:choose>
<xsl:when test="not($nodes)">NaN</xsl:when>
<xsl:otherwise>
<xsl:for-each select="$nodes">
<xsl:sort data-type="number" order="descending" />
<xsl:if test="position() = 1">
<xsl:value-of select="number(.)" />
</xsl:if>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="display-percent">
<xsl:param name="value" />
<xsl:value-of select="format-number($value,'0.00%')" />
</xsl:template>
<xsl:template name="display-time">
<xsl:param name="value" />
<xsl:value-of select="format-number($value,'0 ms')" />
</xsl:template>
</xsl:stylesheet>

Binary file not shown.

View file

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="2.1">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Basic tests" enabled="true">
<stringProp name="TestPlan.comments">$This file is distributed under the terms of the license in /doc/license.txt$</stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="WEBAPP_HOST" elementType="Argument">
<stringProp name="Argument.name">WEBAPP_HOST</stringProp>
<stringProp name="Argument.value">${__property(webapp.host, localhost)}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="WEBAPP_PORT" elementType="Argument">
<stringProp name="Argument.name">WEBAPP_PORT</stringProp>
<stringProp name="Argument.value">${__property(webapp.port, 8080)}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="WEBAPP_NAME" elementType="Argument">
<stringProp name="Argument.name">WEBAPP_NAME</stringProp>
<stringProp name="Argument.value">${__property(webapp.name, vivo)}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Main thread group" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">10</stringProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">1</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<longProp name="ThreadGroup.start_time">1293123271000</longProp>
<longProp name="ThreadGroup.end_time">1293123271000</longProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
</ThreadGroup>
<hashTree>
<CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true">
<collectionProp name="CookieManager.cookies"/>
<boolProp name="CookieManager.clearEachIteration">false</boolProp>
<stringProp name="CookieManager.policy">rfc2109</stringProp>
</CookieManager>
<hashTree/>
<ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="HTTP Request Defaults" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">${WEBAPP_HOST}</stringProp>
<stringProp name="HTTPSampler.port">${WEBAPP_PORT}</stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path"></stringProp>
<boolProp name="HTTPSampler.image_parser">true</boolProp>
</ConfigTestElement>
<hashTree/>
<HTTPSampler guiclass="HttpTestSampleGui" testclass="HTTPSampler" testname="Home page" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain"></stringProp>
<stringProp name="HTTPSampler.port"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">${WEBAPP_NAME}/</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.monitor">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
</HTTPSampler>
<hashTree/>
<HTTPSampler guiclass="HttpTestSampleGui" testclass="HTTPSampler" testname="About page" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain"></stringProp>
<stringProp name="HTTPSampler.port"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">${WEBAPP_NAME}/about</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.monitor">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
</HTTPSampler>
<hashTree/>
<HTTPSampler guiclass="HttpTestSampleGui" testclass="HTTPSampler" testname="Profile page" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain"></stringProp>
<stringProp name="HTTPSampler.port"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">${WEBAPP_NAME}/display/${INDIVIDUAL_URI}</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.monitor">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
</HTTPSampler>
<hashTree>
<CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="Individual URIs from CSV" enabled="true">
<stringProp name="delimiter">,</stringProp>
<stringProp name="fileEncoding">utf8</stringProp>
<stringProp name="filename">individualUris.csv</stringProp>
<boolProp name="quotedData">false</boolProp>
<boolProp name="recycle">true</boolProp>
<stringProp name="shareMode">Current thread group</stringProp>
<boolProp name="stopThread">false</boolProp>
<stringProp name="variableNames">INDIVIDUAL_URI</stringProp>
</CSVDataSet>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>

View file

@ -0,0 +1,9 @@
n3148
n1613
n1451
n1748
n2936
n3084
n2731
n432
n1048
1 n3148
2 n1613
3 n1451
4 n1748
5 n2936
6 n3084
7 n2731
8 n432
9 n1048