Author: [email protected]
Date: Wed Mar 23 15:20:16 2011
New Revision: 894
Log:
[AMDATU-332] Added -verify option
Added:
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/ApplicationContext.java
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/TestContext.java
trunk/etc/performancetest/src/main/resources/report_template.html
Modified:
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/JMeterResultsParser.java
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/ReportSummary.java
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/Statistics.java
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/ZSamples.java
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/main/Main.java
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/AmdatuLauncher.java
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/JMeterRunner.java
Modified:
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/JMeterResultsParser.java
==============================================================================
---
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/JMeterResultsParser.java
(original)
+++
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/JMeterResultsParser.java
Wed Mar 23 15:20:16 2011
@@ -33,6 +33,7 @@
public class JMeterResultsParser extends DefaultHandler {
// Name of the JMeter plan
private String m_jmeterPlanName;
+ private String m_version;
private Map<String, List<XYSample>> m_results = new HashMap<String,
List<XYSample>>();
@@ -48,6 +49,10 @@
return m_jmeterPlanName;
}
+ public String getVersion() {
+ return m_version != null ? m_version : "unknown";
+ }
+
public double getThroughput() {
long diff = m_tsMax - m_tsMin;
double throughput = m_sampleCount/(diff/1000.0);
@@ -75,6 +80,8 @@
m_results.put(stepName, new ArrayList<XYSample>());
}
m_results.get(stepName).add(result);
+ } else if ("testResults".equals(qName)) {
+ m_version = attributes.getValue("amdatuversion");
}
}
Modified:
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/ReportSummary.java
==============================================================================
---
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/ReportSummary.java
(original)
+++
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/ReportSummary.java
Wed Mar 23 15:20:16 2011
@@ -19,12 +19,16 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
+import java.net.URL;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import org.amdatu.test.performance.runtest.ApplicationContext;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.math.MathException;
/**
@@ -33,21 +37,28 @@
* @author ivol
*/
public class ReportSummary {
- private String m_resultDir;
+ private ApplicationContext m_context;
private List<JMeterPlanReport> m_reports = new
ArrayList<JMeterPlanReport>();
-
- public ReportSummary(String resultDir) {
- m_resultDir = resultDir;
+ private String m_versionX;
+ private String m_versionY;
+ private String m_reportFileName;
+ private String m_template = null;
+
+ public ReportSummary(ApplicationContext context) {
+ m_context = context;
+ m_reportFileName = context.resultsDir + File.separator +
context.getReportName() + ".html";
}
public void mergeAndAdd(JMeterResultsParser x, JMeterResultsParser y)
throws IOException, MathException {
JMeterPlanReport report = new JMeterPlanReport(x.getName());
+ m_versionX = x.getVersion();
+ m_versionY = y.getVersion();
Map<String, List<XYSample>> resultsX = x.getResults();
Map<String, List<XYSample>> resultsY = y.getResults();
for (String key : resultsX.keySet()) {
List<XYSample> samplesX = resultsX.get(key);
List<XYSample> samplesY = resultsY.get(key);
- ZSamples mergedReport = new ZSamples(m_resultDir, samplesX,
samplesY);
+ ZSamples mergedReport = new ZSamples(m_context, samplesX,
samplesY);
report.addSampleResult(mergedReport);
}
report.setThroughput(x.getThroughput(), y.getThroughput());
@@ -82,174 +93,177 @@
System.out.println("--------------------------------------------------\n");
}
}
+
+ private String loadHtmlTemplate() throws IOException {
+ if (m_template == null) {
+ URL url =
getClass().getClassLoader().getResources("report_template.html").nextElement();
+ InputStream is = null;
+ try {
+ is = url.openStream();
+ m_template = IOUtils.toString(is);
+ } finally {
+ is.close();
+ }
+ }
+ return m_template;
+ }
- public void toHtmlFile(String fileName, int testLoops) throws IOException {
- FileOutputStream fos = null;
- PrintWriter pw = null;
- try {
- fos = new FileOutputStream(new File(fileName));
- pw = new PrintWriter(fos);
- pw.println("<html>");
- pw.println("<head>");
- pw.println("<style type=\"text/css\">");
- pw.println("body { background:#19233E; font-family: Arial,
Helvetica, sans-serif; font-size: 14px; color: #ffffff;}");
- pw.println("h1, h2 { margin: 0; font-weight: normal; color:
#ffffff;}");
- pw.println("table { border-top: 1px solid #24130F; border-bottom:
1px solid #24130F; border-left: 1px solid #24130F; border-right: 1px solid
#24130F; background: #323C52;}");
- pw.println("td.good {background: #325552;}");
- pw.println("td.reallygood {background: #007F0E;}");
- pw.println("td.bad {background: #5D3C52;}");
- pw.println("td.reallybad {background: #FF0000;}");
- pw.println("table.results th {width:120px;}");
- pw.println("table.results td {text-align:center;}");
- pw.println("th {border-right: 1px solid #24130F; background:
#323C62;}");
- pw.println("td {border-top: 1px solid #24130F; border-right: 1px
solid #24130F; }");
- pw.println("h1 { font-size: 44px; }");
- pw.println("h2 { font-size: 18px; }");
- pw.println("</style>");
- pw.println("</head><body>");
- pw.println("<h1>Performance test results</h1>");
- addLegend(pw);
- pw.println("<hr>");
- for (JMeterPlanReport report : m_reports) {
- pw.println("<h2>Results for JMeter plan '" + report.getName()
+ "'</h2>");
- pw.println("<p><table border=\"1\">");
- pw.println("<tr><td>#Processors</td><td>" +
Runtime.getRuntime().availableProcessors() + "</td></tr>");
- pw.println("<tr><td>Memory</td><td>" +
toMegaBytes(Runtime.getRuntime().freeMemory()) + " free of available " +
toMegaBytes(Runtime.getRuntime().maxMemory()) + "</td></tr>");
- pw.println("<tr><td>Test loops</td><td>" + testLoops +
"</td></tr>");
- pw.println("<tr><td>Throughput X</td><td>" +
report.getThroughputX() + " req/s</td></tr>");
- pw.println("<tr><td>Throughput Y</td><td>" +
report.getThroughputY() + " req/s</td></tr></table>");
- pw.println("<br/>");
- pw.println("<table class=\"results\">");
-
- // Print headers
- pw.println("<tr><th/>");
- for (ZSamples sample : report.getSamples()) {
- pw.println("<th>" + sample.name+ "</th>");
- }
- pw.println("</tr>");
+ private String getResultTable() {
+ StringBuffer table = new StringBuffer();
+ for (JMeterPlanReport report : m_reports) {
+
+ table.append("<table border=\"1\">");
+ table.append("<tr><td>#Processors</td><td>" +
Runtime.getRuntime().availableProcessors() + "</td></tr>");
+ table.append("<tr><td>Memory</td><td>" +
toMegaBytes(Runtime.getRuntime().freeMemory()) + " free of available " +
toMegaBytes(Runtime.getRuntime().maxMemory()) + "</td></tr>");
+ table.append("<tr><td>Test loops</td><td>" + m_context.testLoops +
"</td></tr>");
+ table.append("<tr><td>Throughput X</td><td>" +
report.getThroughputX() + " req/s</td></tr>");
+ table.append("<tr><td>Throughput Y</td><td>" +
report.getThroughputY() + " req/s</td></tr></table>");
+ table.append("<br/>");
+ table.append("<table class=\"results\">");
- // Sample mean size (m)
- pw.println("<tr><td>m</td>");
- for (ZSamples sample : report.getSamples()) {
- pw.println("<td>" + sample.sampleMeanSize + "</td>");
- }
- pw.println("</tr>");
+ // Print headers
+ table.append("<tr><th/>");
+ for (ZSamples sample : report.getSamples()) {
+ table.append("<th>" + sample.name+ "</th>");
+ }
+ table.append("</tr>");
- // Sample size (n)
- pw.println("<tr><td>n</td>");
- for (ZSamples sample : report.getSamples()) {
- pw.println("<td>" + sample.sampleSize + "</td>");
- }
- pw.println("</tr>");
+ // Sample mean size (m)
+ table.append("<tr><td>m</td>");
+ for (ZSamples sample : report.getSamples()) {
+ table.append("<td>" + sample.sampleMeanSize + "</td>");
+ }
+ table.append("</tr>");
- // Mean of X
- pw.println("<tr><td>M<sub>x</sub></td>");
- for (ZSamples sample : report.getSamples()) {
- pw.println("<td>" + round(sample.sampleMeanX) + "</td>");
- }
- pw.println("</tr>");
+ // Sample size (n)
+ table.append("<tr><td>n</td>");
+ for (ZSamples sample : report.getSamples()) {
+ table.append("<td>" + sample.sampleSize + "</td>");
+ }
+ table.append("</tr>");
- // Mean of Y
- pw.println("<tr><td>M<sub>y</sub></td>");
- for (ZSamples sample : report.getSamples()) {
- pw.println("<td>" + round(sample.sampleMeanY) + "</td>");
- }
- pw.println("</tr>");
+ // Mean of X
+ table.append("<tr><td>M<sub>x</sub></td>");
+ for (ZSamples sample : report.getSamples()) {
+ table.append("<td>" + round(sample.sampleMeanX) + "</td>");
+ }
+ table.append("</tr>");
- // Mean of Z
- pw.println("<tr><td>M<sub>z</sub></td>");
- for (ZSamples sample : report.getSamples()) {
- pw.println("<td>" + round(sample.sampleMean) + "</td>");
- }
- pw.println("</tr>");
+ // Mean of Y
+ table.append("<tr><td>n</td>");
+ for (ZSamples sample : report.getSamples()) {
+ table.append("<td>" + round(sample.sampleMeanY) + "</td>");
+ }
+ table.append("</tr>");
- // Standard deviation of Z
- pw.println("<tr><td>S<sub>z</sub></td>");
- for (ZSamples sample : report.getSamples()) {
- pw.println("<td>" + round(sample.sampleSD) + "</td>");
- }
- pw.println("</tr>");
+ // Mean of Z
+ table.append("<tr><td>M<sub>z</sub></td>");
+ for (ZSamples sample : report.getSamples()) {
+ table.append("<td>" + round(sample.sampleMean) + "</td>");
+ }
+ table.append("</tr>");
- // t-value of Z
- pw.println("<tr><td>t<sub>z</sub></td>");
- for (ZSamples sample : report.getSamples()) {
- pw.println("<td>" + round(sample.t) + "</td>");
- }
- pw.println("</tr>");
+ // Standard deviation of Z
+ table.append("<tr><td>S<sub>z</sub></td>");
+ for (ZSamples sample : report.getSamples()) {
+ table.append("<td>" + round(sample.sampleSD) + "</td>");
+ }
+ table.append("</tr>");
- // Probability of t-value
- pw.println("<tr><td>Pt<sub>z</sub></td>");
- for (ZSamples sample : report.getSamples()) {
- pw.println("<td>" + round(sample.p) + "</td>");
- }
- pw.println("</tr>");
+ // t-value of Z
+ table.append("<tr><td>t<sub>z</sub></td>");
+ for (ZSamples sample : report.getSamples()) {
+ table.append("<td>" + round(sample.t) + "</td>");
+ }
+ table.append("</tr>");
- // [power] difference between X and Y
- pw.println("<tr><td>D<sub>z</sub></td>");
- for (ZSamples sample : report.getSamples()) {
- if (sample.D < 0) {
- pw.println(good(round(sample.D)));
- } else {
- pw.println(bad(round(sample.D)));
- }
+ // Probability of t-value
+ table.append("<tr><td>Pt<sub>z</sub></td>");
+ for (ZSamples sample : report.getSamples()) {
+ table.append("<td>" + round(sample.p) + "</td>");
+ }
+ table.append("</tr>");
+ // [power] difference between X and Y
+ table.append("<tr><td>D<sub>z</sub></td>");
+ for (ZSamples sample : report.getSamples()) {
+ if (sample.D < 0) {
+ table.append(good(round(sample.D)));
+ } else {
+ table.append(bad(round(sample.D)));
}
- pw.println("</tr>");
- // Percentile [power] difference between X and Y
- pw.println("<tr><td>Δ<sub>z</sub></td>");
- for (ZSamples sample : report.getSamples()) {
- if (sample.delta < -0.05) {
- pw.println(reallyGood(round(100*sample.delta) + "%"));
- } else if (sample.delta <= 0) {
- pw.println(good(round(100*sample.delta) + "%"));
- } else if (sample.delta < 0.05) {
- pw.println(bad(round(100*sample.delta) + "%"));
- } else {
- pw.println(reallyBad(round(100*sample.delta) + "%"));
- }
- }
- pw.println("</tr>");
+ }
+ table.append("</tr>");
- // H0 hypothesis
- pw.println("<tr><td>H<sub>0</sub></td>");
- for (ZSamples sample : report.getSamples()) {
- pw.println("<td>" + (sample.H0 ? "accepted" : "rejected")
+ "</td>");
+ // Percentile [power] difference between X and Y
+ table.append("<tr><td>Δ<sub>z</sub></td>");
+ for (ZSamples sample : report.getSamples()) {
+ if (sample.delta < -0.05) {
+ table.append(reallyGood(round(100*sample.delta) + "%"));
+ } else if (sample.delta <= 0) {
+ table.append(good(round(100*sample.delta) + "%"));
+ } else if (sample.delta < 0.05) {
+ table.append(bad(round(100*sample.delta) + "%"));
+ } else {
+ table.append(reallyBad(round(100*sample.delta) + "%"));
}
- pw.println("</tr>");
+ }
+ table.append("</tr>");
- // H0 hypothesis power
- pw.println("<tr><td>H<sub>0</sub> power</td>");
- for (ZSamples sample : report.getSamples()) {
- pw.println("<td>" + sample.power + "</td>");
- }
- pw.println("</tr>");
+ // H0 hypothesis
+ table.append("<tr><td>H<sub>0</sub></td>");
+ for (ZSamples sample : report.getSamples()) {
+ table.append("<td>" + (sample.H0 ? "accepted" : "rejected") +
"</td>");
+ }
+ table.append("</tr>");
- // Success rate X
- pw.println("<tr><td>Success rate X</td>");
- for (ZSamples sample : report.getSamples()) {
- if (sample.successRateX != 1) {
- pw.println(reallyBad(round(100*sample.successRateX) +
"%"));
- } else {
- pw.println("<td>" + round(100*sample.successRateX) +
"%</td>");
- }
- }
- pw.println("</tr>");
+ // H0 hypothesis power
+ table.append("<tr><td>H<sub>0</sub> power</td>");
+ for (ZSamples sample : report.getSamples()) {
+ table.append("<td>" + sample.power + "</td>");
+ }
+ table.append("</tr>");
- // Success rate Y
- pw.println("<tr><td>Success rate Y</td>");
- for (ZSamples sample : report.getSamples()) {
- if (sample.successRateY != 1) {
- pw.println(reallyBad(round(100*sample.successRateY) +
"%"));
- } else {
- pw.println("<td>" + round(100*sample.successRateY) +
"%</td>");
- }
+ // Success rate X
+ table.append("<tr><td>Success rate X</td>");
+ for (ZSamples sample : report.getSamples()) {
+ if (sample.successRateX != 1) {
+ table.append(reallyBad(round(100*sample.successRateX) +
"%"));
+ } else {
+ table.append("<td>" + round(100*sample.successRateX) +
"%</td>");
}
- pw.println("</tr>");
+ }
+ table.append("</tr>");
- pw.println("</table></p>");
- }
- pw.println("</body></html>");
+ // Success rate Y
+ table.append("<tr><td>Success rate Y</td>");
+ for (ZSamples sample : report.getSamples()) {
+ if (sample.successRateY != 1) {
+ table.append(reallyBad(round(100*sample.successRateY) +
"%"));
+ } else {
+ table.append("<td>" + round(100*sample.successRateY) +
"%</td>");
+ }
+ }
+ table.append("</tr>");
+ table.append("</table>");
+ }
+ return table.toString();
+ }
+
+ public void toHtmlFile() throws IOException {
+ // Read the template
+ String report = loadHtmlTemplate();
+ report = report.replace("${x_versus_y}", "Amdatu " + m_versionX + "
(X) versus Amdatu " + m_versionY + " (Y)");
+ report = report.replace("${results_header}", "Results for JMeter plan
'" + m_reports.get(0).getName() + "'");
+ report = report.replace("${results}", getResultTable());
+
+ FileOutputStream fos = null;
+ PrintWriter pw = null;
+ try {
+ fos = new FileOutputStream(new File(m_reportFileName));
+ pw = new PrintWriter(fos);
+ pw.append(report);
} finally {
try {
if (pw != null) {
@@ -269,27 +283,6 @@
return mb + " Mb";
}
- private void addLegend(PrintWriter pw) {
- pw.println("<h2>Legend</h2>");
- pw.println("Version X represents the new version, compared against the
original version Y</br/><p>");
- pw.println("JMeter samples drawn from version X:
X<sub>0</sub>,...,X<sub>n</sub> sample mean: M<sub>x</sub> sample
standard deviation: S<sub>x</sub><br/>");
- pw.println("JMeter samples drawn from version Y:
Y<sub>0</sub>,...,Y<sub>n</sub> sample mean: M<sub>y</sub> sample
standard deviation: S<sub>y</sub><br/>");
- pw.println("Generated difference sample Z:
Z<sub>0</sub>,...,Z<sub>n</sub> :=
(X<sub>0</sub>-Y<sub>0</sub>),...,(X<sub>n</sub>-Y<sub>0</sub>) sample
mean: M<sub>z</sub> sample standard deviation: S<sub>z</sub><br/>");
- pw.println("Test statistic T<sub>z</sub> :=
M<sub>z</sub>√n/S<sub>z</sub><br/>");
- pw.println("Sample value of T := t<sub>z</sub><br/>");
- pw.println("Probability of meastured value Pt<sub>z</sub> :=
P[T<sub>z</sub>] <= t<sub>z</sub><br/>");
- pw.println("Under the assumption that X has a distribution with mean
µ<sub>x</sub> and Y has a dsitribution with mean µ<sub>y</sub>");
- pw.println(", we define the null hypothesis:<br/> H<sub>0</sub> :=
µ<sub>z</sub> = 0, where µ<sub>z</sub> := µ<sub>x</sub> -
µ<sub>y</sub><br/>");
- pw.println("We define D<sub>z</sub> to be the value of D for which
P[t<sub>dz</sub>] >= 0.01, where t<sub>dz</sub> :=
(M<sub>z</sub>-D)√n/S<sub>z</sub><br/>");
- pw.println("In other words, this value D tells us that the difference
between µ<sub>x</sub> and µ<sub>y</sub> is at least D<sub>z</sub>,
with 99% accuracy</br/>");
- pw.println("Δ<sub>z</sub> := D<sub>z</sub>/M<sub>y</sub>");
- pw.println("<p><table><tr>" + good("good") + "</tr>");
- pw.println("<tr>" + reallyGood("really good") + "</tr>");
- pw.println("<tr>" + bad("bad") + "</tr>");
- pw.println("<tr>" + reallyBad("really bad") + "</tr></table>");
- pw.println("</p>");
- }
-
private String round(double d) {
DecimalFormat df = new DecimalFormat("####0.00");
return df.format(d);
@@ -303,7 +296,7 @@
private String bad(Object value) {
return "<td class=\"bad\">" + value + "</td>";
}
-
+
private String reallyGood(Object value) {
return "<td class=\"reallygood\">" + value + "</td>";
}
Modified:
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/Statistics.java
==============================================================================
---
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/Statistics.java
(original)
+++
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/Statistics.java
Wed Mar 23 15:20:16 2011
@@ -25,7 +25,9 @@
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
+import org.amdatu.test.performance.runtest.ApplicationContext;
import org.amdatu.test.performance.runtest.JMeterRunner;
+import org.amdatu.test.performance.runtest.TestContext;
import org.apache.commons.math.MathException;
import org.xml.sax.SAXException;
@@ -38,16 +40,17 @@
* @author ivol
*/
public class Statistics {
- private String m_resultsDir;
- private ReportSummary m_report;
+ private ApplicationContext m_context;
- public Statistics(String resultsDir) {
- m_resultsDir = resultsDir;
+ private List<ReportSummary> m_reports = new ArrayList<ReportSummary>();
+
+ public Statistics(ApplicationContext context) {
+ m_context = context;
}
-
+
public void analyze() throws ParserConfigurationException, SAXException,
IOException, MathException {
// Analyze
- m_report = new ReportSummary(m_resultsDir);
+
for (String sample : getJMeterReports()) {
File reportX = new File(sample + "X");
File reportY = new File(sample + "Y");
@@ -58,28 +61,30 @@
sax.parse(reportX, resultsX);
sax.parse(reportY, resultsY);
+ TestContext x = new TestContext(resultsX.getVersion());
+ TestContext y = new TestContext(resultsY.getVersion());
+ m_context.setContext(x, y);
+
// Merge and add the JMeter results to the overall report
- m_report.mergeAndAdd(resultsX, resultsY);
+ ReportSummary report = new ReportSummary(m_context);
+ report.mergeAndAdd(resultsX, resultsY);
+ m_reports.add(report);
}
}
-
- public void print(int n) throws IOException {
- // Print the result to the console
- m_report.toConsole();
- printHtml(n);
- }
-
- public void printHtml(int n) throws IOException {
- m_report.toHtmlFile(m_resultsDir + File.separator + "report-testloop_"
+ n + ".html", n);
+
+ public void print() throws IOException {
+ printHtml();
}
-
- public ReportSummary getReport() {
- return m_report;
+
+ public void printHtml() throws IOException {
+ for (ReportSummary report : m_reports) {
+ report.toHtmlFile();
+ }
}
-
+
private List<String> getJMeterReports() {
List<String> samples = new ArrayList<String>();
- File resultDir = new File(m_resultsDir);
+ File resultDir = new File(m_context.resultsDir);
for (File file : resultDir.listFiles()) {
if (file.getName().endsWith(JMeterRunner.SAMPLES_X)) {
String path = file.getParent();
Modified:
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/ZSamples.java
==============================================================================
---
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/ZSamples.java
(original)
+++
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/ZSamples.java
Wed Mar 23 15:20:16 2011
@@ -23,6 +23,7 @@
import java.util.ArrayList;
import java.util.List;
+import org.amdatu.test.performance.runtest.ApplicationContext;
import org.apache.commons.math.MathException;
import org.apache.commons.math.distribution.TDistributionImpl;
@@ -32,8 +33,6 @@
*
*/
public class ZSamples {
- //private List<XYSample> m_samplesX;
- //private List<XYSample> m_samplesY;
private List<Double> m_samplesX;
private List<Double> m_samplesY;
@@ -52,11 +51,10 @@
double delta;
int sampleMeanSize = 0;
int sampleSize = 0;
- private String m_resultsDir;
-
- public ZSamples(String resultsDir, List<XYSample> x, List<XYSample> y)
throws IOException, MathException {
- m_resultsDir = resultsDir;
+ private ApplicationContext m_context;
+ public ZSamples(ApplicationContext context, List<XYSample> x,
List<XYSample> y) throws IOException, MathException {
+ m_context = context;
preProcess(x, y);
calculate();
@@ -113,7 +111,8 @@
private List<Double> getZ() throws IOException {
List<Double> results = new ArrayList<Double>();
- File samplesZOutput = new File(m_resultsDir, name + "-samples.Z");
+ String fileName = name + m_context.getSamplesPostFix() + ".Z";
+ File samplesZOutput = new File(m_context.resultsDir, fileName);
FileOutputStream fos = null;
PrintWriter pw = null;
try {
Modified:
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/main/Main.java
==============================================================================
---
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/main/Main.java
(original)
+++
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/main/Main.java
Wed Mar 23 15:20:16 2011
@@ -17,16 +17,21 @@
package org.amdatu.test.performance.main;
import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.amdatu.test.performance.analysis.JMeterPlanReport;
+import javax.xml.parsers.ParserConfigurationException;
+
import org.amdatu.test.performance.analysis.Statistics;
-import org.amdatu.test.performance.analysis.ZSamples;
import org.amdatu.test.performance.runtest.AmdatuLauncher;
+import org.amdatu.test.performance.runtest.ApplicationContext;
import org.amdatu.test.performance.runtest.JMeterRunner;
+import org.amdatu.test.performance.runtest.TestContext;
+import org.apache.commons.math.MathException;
+import org.xml.sax.SAXException;
/**
* Main command line client class.
@@ -34,18 +39,17 @@
* @author ivol
*/
public class Main {
- private final static int TEST_LOOPS = 1;
-
- private static String VERBOSE_ARG = "-verbose";
- private static String ANALYZE_ARG = "-analyze";
- private static String RUNTEST_ARG = "-runtest";
- private static String NOEXAMPLES_ARG = "-noexamples";
- private static String AMDATU_X_ARG = "-amdatuVersionX";
- private static String AMDATU_Y_ARG = "-amdatuVersionY";
- private static String JMETERPLANSDIR_ARG = "-jmeterplansdir";
- private static String RESULTSDIR_ARG = "-resultsdir";
- private static String TESTLOOPS_ARG = "-testloops";
- private static String OMIT_ARG = "-omit";
+ public static String VERBOSE_ARG = "-verbose";
+ public static String ANALYZE_ARG = "-analyze";
+ public static String RUNTEST_ARG = "-runtest";
+ public static String NOEXAMPLES_ARG = "-noexamples";
+ public static String VERIFY_ARG = "-verify";
+ public static String AMDATU_X_ARG = "-amdatuVersionX";
+ public static String AMDATU_Y_ARG = "-amdatuVersionY";
+ public static String JMETERPLANSDIR_ARG = "-jmeterplansdir";
+ public static String RESULTSDIR_ARG = "-resultsdir";
+ public static String TESTLOOPS_ARG = "-testloops";
+ public static String OMIT_ARG = "-omit";
// Wordy arguments
private final static List<String> BOOLEAN_ARGS = new ArrayList<String>();
@@ -54,6 +58,7 @@
BOOLEAN_ARGS.add(ANALYZE_ARG);
BOOLEAN_ARGS.add(RUNTEST_ARG);
BOOLEAN_ARGS.add(NOEXAMPLES_ARG);
+ BOOLEAN_ARGS.add(VERIFY_ARG);
}
// File arguments
@@ -132,89 +137,39 @@
printUsage();
}
- File rootTmpDir = getRootTmpDir();
- int n = 1;
+ ApplicationContext context = new ApplicationContext(arguments);
+
if (Boolean.TRUE.equals(arguments.get(RUNTEST_ARG))) {
if (verifyRunTest(arguments)) {
String amdatuX = arguments.get(AMDATU_X_ARG).toString();
String amdatuY = arguments.get(AMDATU_Y_ARG).toString();
- String jmeterPlanDir =
arguments.get(JMETERPLANSDIR_ARG).toString();
- String resultsDir =
arguments.get(RESULTSDIR_ARG).toString();
- boolean noExamples =
Boolean.TRUE.equals(arguments.get(NOEXAMPLES_ARG));
- int omit = arguments.containsKey(OMIT_ARG) ?
Integer.parseInt(arguments.get(OMIT_ARG).toString()) : 0;
- AmdatuLauncher launcherX = new AmdatuLauncher(amdatuX,
"X", rootTmpDir, noExamples);
- AmdatuLauncher launcherY = new AmdatuLauncher(amdatuY,
"Y", rootTmpDir, noExamples);
- JMeterRunner jmeterX = new JMeterRunner(jmeterPlanDir,
"X", resultsDir, rootTmpDir);
- JMeterRunner jmeterY = new JMeterRunner(jmeterPlanDir,
"Y", resultsDir, rootTmpDir);
- int loops = TEST_LOOPS;
- boolean testLoopAuto = false;
- Object testLoops = arguments.get(TESTLOOPS_ARG);
- if (testLoops != null && !testLoops.toString().isEmpty()) {
- if ("auto".equals(testLoops)) {
- testLoopAuto = true;
- loops = 1;
- } else {
- loops = Integer.parseInt(testLoops.toString());
- }
- }
- for (n=1; n<=loops; n++) {
-
System.out.println("*************************************************************");
- if (!testLoopAuto) {
- System.out.println("Running test loop " + n + " of
" + loops + "...");
- } else {
- System.out.println("Running test loop " + n + " of
<auto>...");
- }
- try {
- launcherX.start();
- jmeterX.run(omit);
- } finally {
- launcherX.stop();
- }
-
- try {
- launcherY.start();
- jmeterY.run(omit);
- } finally {
- launcherY.stop();
- }
+ TestContext contextX = new TestContext(new File(amdatuX));
+ TestContext contextY = new TestContext(new File(amdatuY));
+ context.setContext(contextX, contextY);
+
+ runTest(context);
+
+ if (Boolean.TRUE.equals(arguments.get(VERIFY_ARG))) {
+ // Run additional test runs to verify that the H0
tests of X versus X and Y versus Y
+ // are all accepted
+ // Verify X
+ context.setContext(contextX, contextX);
+ runTest(context);
- if (testLoopAuto) {
- int h0Count = 0;
- int h0Accepted = 0;
- Statistics stats = new Statistics(resultsDir);
- stats.analyze();
- stats.printHtml(n);
- for (JMeterPlanReport report :
stats.getReport().getReports()) {
- for (ZSamples sample : report.getSamples()) {
- h0Count++;
- if (sample.H0) {
- h0Accepted++;
- }
-
- }
- }
- if (h0Count != h0Accepted) {
- System.out.println("Continuing test loops, " +
h0Accepted + " out of " + h0Count + " null hypothesis were accepted");
- loops++;
- }
- }
+ // Verify Y
+ context.setContext(contextY, contextY);
+ runTest(context);
}
-
- launcherX.kill();
- launcherY.kill();
-
- System.out.println("Amdatu performance test completed.");
- }
+ }
}
if (Boolean.TRUE.equals(arguments.get(ANALYZE_ARG))) {
if (!arguments.containsKey(RESULTSDIR_ARG)) {
printUsage();
} else {
- String resultsDir =
arguments.get(RESULTSDIR_ARG).toString();
- Statistics stats = new Statistics(resultsDir);
+ Statistics stats = new Statistics(context);
stats.analyze();
- stats.print(n-1);
+ stats.print();
System.out.println("Amdatu performance test analysis
completed.");
}
}
@@ -223,13 +178,6 @@
t.printStackTrace();
}
}
-
- private static File getRootTmpDir() {
- String javaTmpDir = System.getProperty("java.io.tmpdir");
- File tmpDir = new File(javaTmpDir, "amdatu-performancetest");
- tmpDir.mkdir();
- return tmpDir;
- }
private static boolean verifyRunTest(Map<String, Object> arguments) {
boolean ok = true;
@@ -273,4 +221,38 @@
System.err.println("-jmeterplansdir Absolute path to a directory that
holds JMeter plans to execute during the performance test (required only for
-runtest)");
System.err.println("-resultsdir Absolute path to a directory to
which the results of JMeter executions will be written as well as analysis
results (required for both -runtest and -analyze)");
}
+
+ private static int runTest(ApplicationContext context) throws IOException,
InterruptedException, ParserConfigurationException, SAXException, MathException
{
+ // Build the context
+ AmdatuLauncher launcher = new AmdatuLauncher(context);
+ JMeterRunner jmeter = new JMeterRunner(context);
+
+ int n;
+ for (n=1; n<=context.testLoops; n++) {
+
System.out.println("*************************************************************");
+ System.out.println("Running test loop " + n + " of " +
context.testLoops + "...");
+
+ String[] versions = new String[]{ApplicationContext.VERSION_X,
ApplicationContext.VERSION_Y};
+ for (String version : versions) {
+ try {
+ context.setCurrentContext(version);
+ launcher.start();
+ jmeter.run();
+ } finally {
+ launcher.stop();
+ }
+ }
+ }
+
+ String[] versions = new String[]{ApplicationContext.VERSION_X,
ApplicationContext.VERSION_Y};
+ for (String version : versions) {
+ context.setCurrentContext(version);
+ launcher.kill();
+ }
+
+ System.out.println("Amdatu performance test completed.");
+ return n;
+ }
+
+
}
\ No newline at end of file
Modified:
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/AmdatuLauncher.java
==============================================================================
---
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/AmdatuLauncher.java
(original)
+++
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/AmdatuLauncher.java
Wed Mar 23 15:20:16 2011
@@ -38,33 +38,26 @@
"http://localhost:8080/dashboard/jsp/dashboard.jsp",
"http://localhost:8080/rest/authorization/status"
};
-
- private String m_releaseZip;
- private String m_version;
- private boolean m_noExamples;
- private File m_rootTmpDir;
+
+ private ApplicationContext m_context;
private File m_tmpDir;
private Process m_amdatuProcess;
-
- public AmdatuLauncher(String releaseZip, String version, File rootTmpDir,
boolean noExamples) {
- m_releaseZip = releaseZip;
- m_rootTmpDir = rootTmpDir;
- m_noExamples = noExamples;
- m_version = version;
+
+ public AmdatuLauncher(ApplicationContext context) {
+ m_context = context;
}
-
+
public void start() throws IOException, InterruptedException {
// First unzip the file to the java io tmpdir
- File zipFile = new File(m_releaseZip);
-
- String amdatuDir = zipFile.getName() + "-" + m_version;
- m_tmpDir = new File(m_rootTmpDir, amdatuDir);
+ File zipFile = new File(m_context.currentContext.binFile);
+ String amdatuDir = zipFile.getName() + "-" +
m_context.currentContextName;
+ m_tmpDir = new File(m_context.rootTmpDir, amdatuDir);
if (!m_tmpDir.exists()) {
// This is the first startup: setup Amdatu
m_tmpDir.mkdir();
System.out.println("Extracting '" + zipFile.getName() + "' to '" +
m_tmpDir.getAbsolutePath() + "'");
Utils.unzip(zipFile, m_tmpDir);
- if (m_noExamples) {
+ if (m_context.noExamples) {
removeExamples();
}
}
@@ -193,4 +186,6 @@
}
return null;
}
+
+
}
Added:
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/ApplicationContext.java
==============================================================================
--- (empty file)
+++
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/ApplicationContext.java
Wed Mar 23 15:20:16 2011
@@ -0,0 +1,63 @@
+package org.amdatu.test.performance.runtest;
+
+import java.io.File;
+import java.util.Map;
+
+import org.amdatu.test.performance.main.Main;
+
+public class ApplicationContext {
+ public String jmeterPlanDir; // directory to read JMeter plans from
+ public String resultsDir; // directory to write results to
+ public boolean noExamples; // Indication of examples should be removed
prior to running the tests
+ public int omit; // Amount of initial results to omit
+ public File rootTmpDir; // Root temporary directory
+ public int testLoops;
+
+ public final static String VERSION_X = "X";
+ public final static String VERSION_Y = "Y";
+
+ public TestContext contextX;
+ public TestContext contextY;
+ public String currentContextName;
+ public TestContext currentContext;
+
+ public ApplicationContext(Map<String, Object> arguments) {
+ if (arguments.containsKey(Main.JMETERPLANSDIR_ARG)) {
+ jmeterPlanDir = arguments.get(Main.JMETERPLANSDIR_ARG).toString();
+ }
+ resultsDir = arguments.get(Main.RESULTSDIR_ARG).toString();
+ noExamples = Boolean.TRUE.equals(arguments.get(Main.NOEXAMPLES_ARG));
+ omit = arguments.containsKey(Main.OMIT_ARG) ?
Integer.parseInt(arguments.get(Main.OMIT_ARG).toString()) : 0;
+ rootTmpDir = getRootTmpDir();
+ testLoops = arguments.containsKey(Main.TESTLOOPS_ARG) ?
Integer.parseInt(arguments.get(Main.TESTLOOPS_ARG).toString()) : 1;
+ }
+
+ public void setContext(TestContext x, TestContext y) {
+ contextX = x;
+ contextY = y;
+ }
+
+ public void setCurrentContext(String current) {
+ currentContextName = current;
+ if (current.equals(VERSION_X)) {
+ currentContext = contextX;
+ } else {
+ currentContext = contextY;
+ }
+ }
+
+ public String getReportName() {
+ return "report_" + contextX.label + "-vs-" + contextY.label;
+ }
+
+ public String getSamplesPostFix() {
+ return "_" + contextX.label + "-vs-" + contextY.label + "_samples";
+ }
+
+ private File getRootTmpDir() {
+ String javaTmpDir = System.getProperty("java.io.tmpdir");
+ File tmpDir = new File(javaTmpDir, "amdatu-performancetest");
+ tmpDir.mkdir();
+ return tmpDir;
+ }
+}
Modified:
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/JMeterRunner.java
==============================================================================
---
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/JMeterRunner.java
(original)
+++
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/JMeterRunner.java
Wed Mar 23 15:20:16 2011
@@ -39,36 +39,31 @@
* @author ivol
*/
public class JMeterRunner {
- public final static String SAMPLES = "-samples.";
+ public final static String SAMPLES = "_samples.";
public final static String SAMPLES_X = SAMPLES + "X";
public final static String SAMPLES_Y = SAMPLES + "Y";
- private File m_rootTmpDir;
- private String m_jmeterPlansDir;
- private String m_resultsDir;
- private String m_version;
private Process m_jmeterProcess;
private File m_sampleFile;
+ private ApplicationContext m_context;
- public JMeterRunner(String jmeterPlansDir, String version, String
resultsDir, File rootTmpDir) {
- m_jmeterPlansDir = jmeterPlansDir;
- m_version = version;
- m_resultsDir = resultsDir;
- m_rootTmpDir = rootTmpDir;
+ public JMeterRunner(ApplicationContext context) {
+ m_context = context;
}
- public void run(int omit) throws FileNotFoundException, IOException,
InterruptedException {
- File resultDir = new File(m_resultsDir);
+ public void run() throws FileNotFoundException, IOException,
InterruptedException {
+ File resultDir = new File(m_context.resultsDir);
File jmeterLogFile = new File(resultDir, "jmeter.log");
File jmeterDir = installJMeter();
jmeterDir = new File(jmeterDir, "bin");
- File plans = new File(m_jmeterPlansDir);
+ File plans = new File(m_context.jmeterPlanDir);
for (File jmeterPlan : plans.listFiles()) {
if (jmeterPlan.isFile()) {
// Run the JMeter plan!
- m_sampleFile = new File(resultDir, jmeterPlan.getName() +
SAMPLES + m_version);
+ String sampleName = jmeterPlan.getName().replace(".jmx", "") +
m_context.getSamplesPostFix()+ "." + m_context.currentContextName;
+ m_sampleFile = new File(resultDir, sampleName);
List<String> command = new ArrayList<String>();
command.add("cmd.exe");
command.add("/C");
@@ -77,7 +72,7 @@
command.add("-t");
command.add(jmeterPlan.getAbsolutePath());
command.add("-l");
- if (omit <= 0) {
+ if (m_context.omit <= 0) {
command.add(m_sampleFile.getAbsolutePath());
} else {
command.add(m_sampleFile.getAbsolutePath()+ ".tmp");
@@ -92,8 +87,8 @@
m_jmeterProcess.waitFor();
- if (omit > 0) {
- merge(m_sampleFile.getAbsolutePath()+ ".tmp", omit);
+ if (m_context.omit > 0) {
+ merge(m_sampleFile.getAbsolutePath()+ ".tmp",
m_context.omit);
new File(m_sampleFile.getAbsolutePath()+ ".tmp").delete();
}
}
@@ -102,18 +97,18 @@
private File installJMeter() throws FileNotFoundException, IOException {
// Install only once
- File jmeter = new File(new File(m_rootTmpDir, "jmeter-2.4"),
"jakarta-jmeter-2.4");
+ File jmeter = new File(new File(m_context.rootTmpDir, "jmeter-2.4"),
"jakarta-jmeter-2.4");
if (!jmeter.exists()) {
// Copy the jmeter.zip from this jar to the temp dir
URL url =
getClass().getClassLoader().getResources("jakarta-jmeter-2.4.zip").nextElement();
InputStream is = null;
try {
is = url.openStream();
- File jmeterFile = new File(m_rootTmpDir, "jmeter-2.4.zip");
+ File jmeterFile = new File(m_context.rootTmpDir,
"jmeter-2.4.zip");
IOUtils.copy(is, new FileOutputStream(jmeterFile));
// And unzip
- File target = new File(m_rootTmpDir, "jmeter-2.4");
+ File target = new File(m_context.rootTmpDir, "jmeter-2.4");
Utils.unzip(jmeterFile, target);
return new File(target, "jakarta-jmeter-2.4");
} finally {
@@ -147,6 +142,9 @@
write = true;
}
} else if (isNewFile) {
+ if (line.indexOf("<testResults ") != -1) {
+ line = line.replace(">", " amdatuversion=\"" +
m_context.currentContext.label + "\">");
+ }
write = true;
}
if (write) {
Added:
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/TestContext.java
==============================================================================
--- (empty file)
+++
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/TestContext.java
Wed Mar 23 15:20:16 2011
@@ -0,0 +1,24 @@
+package org.amdatu.test.performance.runtest;
+
+import java.io.File;
+
+public class TestContext {
+ public String label;
+ public String binFile;
+
+ public TestContext(File zipFile) {
+ binFile = zipFile.getAbsolutePath();
+ label = getVersionFromZipName(zipFile.getName());
+ }
+
+ public TestContext(String name) {
+ label = name;
+ }
+
+ private static String getVersionFromZipName(String zipName) {
+ // Syntax ...amdatu-release-[version]-bin.zip
+ String version = zipName.substring(0, zipName.indexOf("-bin.zip"));
+ version = version.substring(version.indexOf("amdatu-release-") +
"amdatu-release-".length());
+ return version;
+ }
+}
Added: trunk/etc/performancetest/src/main/resources/report_template.html
==============================================================================
--- (empty file)
+++ trunk/etc/performancetest/src/main/resources/report_template.html Wed Mar
23 15:20:16 2011
@@ -0,0 +1,185 @@
+<html>
+ <head>
+ <style type="text/css">
+ body {
+ background:#19233E;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 14px;
+ color: #ffffff;
+ }
+
+ h1, h2, h3 {
+ margin: 0;
+ font-weight: normal;
+ color: #ffffff;
+ }
+
+ table {
+ border-top: 1px solid #24130F;
+ border-bottom: 1px solid #24130F;
+ border-left: 1px solid #24130F;
+ border-right: 1px solid #24130F;
+ background: #323C52;
+ }
+
+ td.good {
+ background: #325552;
+ }
+
+ td.reallygood {
+ background: #007F0E;
+ }
+
+ td.bad {
+ background: #5D3C52;
+ }
+
+ td.reallybad {
+ background: #FF0000;
+ }
+
+ table.results th {
+ width:120px;
+ }
+
+ table.results td {
+ text-align:center;
+ }
+
+ th {
+ border-right: 1px solid #24130F;
+ background: #323C62;
+ }
+
+ td {
+ border-top: 1px solid #24130F;
+ border-right: 1px solid #24130F;
+ }
+
+ h1 {
+ font-size: 32px;
+ }
+
+ h2 {
+ font-size: 24px;
+ }
+
+ h3 {
+ font-size: 18px;
+ }
+
+ a {
+ color:#FFFFFF;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Performance test results</h1>
+ <h2>${x_versus_y}</h2>
+ <hr/>
+
+ <input type="button" id="button" value="Show definitions"
onClick="showDefinitions()"/>
+
+ <script language="javascript">
+ var show = false;
+ function showDefinitions() {
+ if (show) {
+ var button =
document.getElementById("definitions");
+ button.style.visibility = "hidden";
+ button.style.display = "none";
+ document.getElementById("button").value
= "Show definitions";
+ show = false;
+ } else {
+ var button =
document.getElementById("definitions");
+ button.style.visibility = "visible";
+ button.style.display = "block";
+ document.getElementById("button").value
= "Hide definitions";
+ show = true;
+ }
+ }
+ </script>
+
+ <div id="definitions" style="visibility: hidden;display:none">
+ <h3>Definitions</h3>
+ <table>
+ <tr>
+ <td>m</td>
+ <td>The amount of samples drawn from X
and Y to define new samples M<sub>x</sub>(m) and M<sub>y</sub>(m)</td>
+ </tr>
+ <tr>
+ <td>n</td>
+ <td>The amount of samples
Z<sub>1</sub>(m),...,Z<sub>n</sub>, where Z<sub>i</sub>(m) := M<sub>x</sub>(m)
- M<sub>y</sub>(m)</td>
+ </tr>
+ <tr>
+ <td>M<sub>x</sub></td>
+ <td>The average of the samples
M<sub>x</sub>(m)(1),...,M<sub>x</sub>(m)(n)</td>
+ </tr>
+ <tr>
+ <td>M<sub>y</sub></td>
+ <td>The average of the samples
M<sub>y</sub(m)(1),...,M<sub>y</sub(m)(n)</td>
+ </tr>
+ <tr>
+ <td>M<sub>z</sub></td>
+ <td>The average of the samples
Z<sub>1</sub(m),...,Z<sub>n</sub(m)</td></tr>
+ <tr>
+ <td>S<sub>z</sub></td>
+ <td>The standard deviation of the
samples Z<sub>1</sub(m),...,Z<sub>n</sub(m)</td></tr>
+ <tr>
+ <td>t<sub>z</sub></td>
+ <td>The value of T, calculated from the
samples
Z<sub>1</sub(m),...,Z<sub>n</sub(m)Z<sub>1</sub(m),...,Z<sub>n</sub(m)</td>
+ </tr>
+ <tr>
+ <td>Pt<sub>z</sub></td>
+ <td>The probability of the calculated
value of t<sub>z</sub>, assuming that µ<sub>z</sub> equals 0</td>
+ </tr>
+ <tr>
+ <td>D<sub>z</sub></td>
+ <td>The value of µ<sub>z</sub>
for which the probability of the calculated value of t<sub>z</sub> equals the
power of the test</td>
+ </tr>
+ <tr>
+ <td>Δ<sub>z</sub></td>
+
<td>D<sub>z</sub>/M<sub>z</sub></td></tr>
+ <tr>
+ <td>H<sub>0</sub></td>
+ <td>Whether the null hypothesis
µ<sub>x</sub>=µ<sub>y</sub> is accepted or rejected, w.r.t. the
defined power</td>
+ </tr>
+ <tr>
+ <td>H<sub>0</sub> power</td>
+ <td>The power of the null hypothesis
test</td>
+ </tr>
+ <tr>
+ <td>Success rate X</td>
+ <td>Success rate of samples drawn from
X as indicated by JMeter, must be 100%</td>
+ </tr>
+ <tr>
+ <td>Success rate X</td>
+ <td>Success rate of samples drawn from
Y as indicated by JMeter, must be 100%</td>
+ </tr>
+ </table>
+
+ <h3>Legend</h3>
+ <p>
+ <table>
+ <tr>
+ <td class="good">good</td>
+ </tr>
+ <tr>
+ <td class="reallygood">really
good</td>
+ </tr>
+ <tr>
+ <td class="bad">bad</td>
+ </tr>
+ <tr>
+ <td class="reallybad">really
bad</td>
+ </tr>
+ </table>
+ </p>
+ </div>
+ <hr>
+
+ <h2>${results_header}</h2>
+ <p>
+ ${results}
+ </p>
+ </body>
+</html>
_______________________________________________
Amdatu-commits mailing list
[email protected]
http://lists.amdatu.org/mailman/listinfo/amdatu-commits