Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FIXED JENKINS-26988]
At some point in the release history, JMeter switched from XML to CSV as
a default for data persistency. The performance plugin should determine
if a JTL file is either XML or CSV, and parse the data accordingly. See
https://issues.jenkins-ci.org/browse/JENKINS-16627 for more details.
  • Loading branch information
guusdk committed Feb 17, 2015
1 parent 234f9da commit 7e1559f
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 7 deletions.
Expand Up @@ -85,7 +85,7 @@ public Collection<PerformanceReport> parse(AbstractBuild<?, ?> build, Collection
* @throws Throwable
* On any exception.
*/
abstract PerformanceReport parse(File reportFile) throws Throwable;
abstract PerformanceReport parse(File reportFile) throws Exception;

/**
* Returns a PerformanceReport instance for the provided report file, based on
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/hudson/plugins/performance/IagoParser.java
Expand Up @@ -79,7 +79,7 @@ protected String getStatsDateFormat()
}

@Override
PerformanceReport parse(File reportFile) throws Throwable
PerformanceReport parse(File reportFile) throws Exception
{
final PerformanceReport report = new PerformanceReport();
report.setReportFileName(reportFile.getName());
Expand Down
Expand Up @@ -110,7 +110,7 @@ public String getDefaultPattern() {
}

@Override
PerformanceReport parse(File reportFile) throws Throwable {
PerformanceReport parse(File reportFile) throws Exception {
final PerformanceReport report = new PerformanceReport();
report.setReportFileName(reportFile.getName());

Expand Down
56 changes: 55 additions & 1 deletion src/main/java/hudson/plugins/performance/JMeterParser.java
Expand Up @@ -2,7 +2,10 @@

import hudson.Extension;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Date;

import javax.xml.parsers.SAXParserFactory;
Expand Down Expand Up @@ -37,7 +40,48 @@ public String getDefaultGlobPattern() {
return "**/*.jtl";
}

PerformanceReport parse(File reportFile) throws Throwable {
PerformanceReport parse(File reportFile) throws Exception
{
// JMeter stores either CSV or XML in .JTL files.
final boolean isXml = isXmlFile(reportFile);

if (isXml) {
return parseXml(reportFile);
} else {
return parseCsv(reportFile);
}
}

/**
* Utility method that checks if the provided file has XML content.
*
* This implementation looks for the first non-empty file. If an XML prolog appears there, this method returns <code>true</code>, otherwise <code>false</code> is returned.
*
* @param file File from which the content is to e analyzed. Cannot be null.
* @return <code>true</code> if the file content has been determined to be XML, otherwise <code>false</code>.
*/
public static boolean isXmlFile(File file) throws IOException {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(file));
String firstLine;
while ((firstLine = reader.readLine()) != null ) {
if (firstLine.trim().length() == 0) continue; // skip empty lines.
return firstLine != null && firstLine.toLowerCase().trim().startsWith("<?xml ");
}
return false;
} finally {
if (reader != null) {
reader.close();
}
}
}

/**
* A delegate for {@link #parse(File)} that can process XML data.
*/
PerformanceReport parseXml(File reportFile) throws Exception
{
final SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(false);
Expand Down Expand Up @@ -139,4 +183,14 @@ public void endElement(String uri, String localName, String qName) {

return report;
}

/**
* A delegate for {@link #parse(File)} that can process CSV data.
*/
PerformanceReport parseCsv(File reportFile) throws Exception {
// TODO The arguments in this constructor should be configurable.
final JMeterCsvParser delegate = new JMeterCsvParser(this.glob, "timestamp,elapsed,URL,responseCode,success", ",", false);
return delegate.parse(reportFile);
}

}
2 changes: 1 addition & 1 deletion src/main/java/hudson/plugins/performance/JUnitParser.java
Expand Up @@ -39,7 +39,7 @@ public String getDefaultGlobPattern() {
}

@Override
PerformanceReport parse(File reportFile) throws Throwable {
PerformanceReport parse(File reportFile) throws Exception {

final SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(false);
Expand Down
Expand Up @@ -47,7 +47,7 @@ public String getDefaultDatePattern() {
}

@Override
PerformanceReport parse(File reportFile) throws Throwable
PerformanceReport parse(File reportFile) throws Exception
{
final PerformanceReport report = new PerformanceReport();
report.setReportFileName(reportFile.getName());
Expand Down
Expand Up @@ -70,7 +70,7 @@ public String getDefaultGlobPattern() {
}

@Override
PerformanceReport parse(File reportFile) throws Throwable
PerformanceReport parse(File reportFile) throws Exception
{
final PerformanceReport r = new PerformanceReport();
r.setReportFileName(reportFile.getName());
Expand Down
146 changes: 146 additions & 0 deletions src/test/java/hudson/plugins/performance/JMeterParserTest.java
@@ -0,0 +1,146 @@
package hudson.plugins.performance;

import java.io.File;

import org.junit.Test;

import static org.junit.Assert.*;

/**
* This class contains basic tests that verify the parsing behavior of
* {@link JMeterParser}.
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class JMeterParserTest
{
/**
* Verifies that {@link JMeterParser#isXmlFile(File)} correctly identifies an
* XML file.
*/
@Test
public void testIsXml() throws Exception
{
// Setup fixture.
final File xmlFile = new File( getClass().getResource("/JMeterResults.jtl").toURI() );

// Execute system under test.
final boolean result = JMeterParser.isXmlFile(xmlFile);

// Verify results.
assertTrue(result);
}

/**
* Verifies that {@link JMeterParser#isXmlFile(File)} returns false when the
* provided data is CSV.
*/
@Test
public void testIsCsv() throws Exception
{
// Setup fixture.
final File csvFile = new File( getClass().getResource("/JENKINS-16627_CSV_instead_of_XML.jtl").toURI() );

// Execute system under test.
final boolean result = JMeterParser.isXmlFile(csvFile);

// Verify results.
assertFalse(result);
}

/**
* Verifies that {@link JMeterParser#isXmlFile(File)} returns false when the
* provided data is CSV.
*/
@Test
public void testIsEmpty() throws Exception
{
// Setup fixture.
final File emptyFile = new File( getClass().getResource("/emptyfile.jtl").toURI() );

// Execute system under test.
final boolean result = JMeterParser.isXmlFile(emptyFile);

// Verify results.
assertFalse(result);
}

/**
* Verifies that {@link JMeterParser#isXmlFile(File)} returns true when the
* XML data is preceded by whitespace.
*/
@Test
public void testIsWhitespaceXml() throws Exception
{
// Setup fixture.
final File xml = new File( getClass().getResource("/whitespace-followed-by-xml.jtl").toURI() );

// Execute system under test.
final boolean result = JMeterParser.isXmlFile(xml);

// Verify results.
assertTrue(result);
}

/**
* JMeter can generate JTL files that contain XML data. This test verifies
* that such a file can be parsed by {@link JMeterParser#parse(File)} without
* incident.
*
* Note that this tests verifies that the file can be parsed. It does not
* verify the correctness of the parsed data.
*/
@Test
public void testParseXmlJtlFile() throws Exception
{
// Setup fixture.
final JMeterParser parser = new JMeterParser(null);
final File reportFile = new File( getClass().getResource("/JMeterResults.jtl").toURI() );

// Execute system under test.
final PerformanceReport result = parser.parse(reportFile);

// Verify results.
assertNotNull(result);
assertEquals("The source file contains eight samples. These should all have been added to the performance report.",
8, result.size());
}

/**
* JMeter can generate JTL files that contain XML data. This test verifies
* that such a file can be parsed by {@link JMeterParser#parse(File)} without
* incident.
*
* Note that this tests verifies that the file can be parsed. It does not
* verify the correctness of the parsed data.
*/
@Test
public void testParseCsvJtlFile() throws Exception
{
// Setup fixture.
final JMeterParser parser = new JMeterParser(null);
final File reportFile = new File( getClass().getResource("/JENKINS-16627_CSV_instead_of_XML.jtl").toURI() );

// Execute system under test.
final PerformanceReport result = parser.parse(reportFile);

// Verify results.
assertNotNull(result);
assertEquals("The source file contains three samples. These should all have been added to the performance report.",
3, result.size());
}


/*
@Test
public void parseXmlTest() throws Exception
{
// Setup fixture.
// Execute system under test.
// Verify results.
}
*/
}
3 changes: 3 additions & 0 deletions src/test/resources/JENKINS-16627_CSV_instead_of_XML.jtl
@@ -0,0 +1,3 @@
1393227741256,1425,GET /ordermgmt/login - Login Page,200,OK,Thread Group 1-1,text,true,3478,1016
1393227742815,59,GET /ordermgmt/inventory,200,OK,Thread Group 1-1,text,true,3100,38
1393227743132,2204,GET /ordermgmt/pre-logout,200,OK,Thread Group 1-1,text,true,19714,25
Empty file.
11 changes: 11 additions & 0 deletions src/test/resources/whitespace-followed-by-xml.jtl
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<testResults version="1.2">
<httpSample t="14720" lt="9770" ts="1296846793179" s="true" lb="Home" rc="200" rm="OK" tn="Sesiones de usuario 1-2" dt="text" by="771065"/>
<httpSample t="15902" lt="10954" ts="1296846792004" s="true" lb="Home" rc="200" rm="OK" tn="Sesiones de usuario 1-1" dt="text" by="771065"/>
<httpSample t="278" lt="148" ts="1296846847952" s="true" lb="Workgroup" rc="200" rm="OK" tn="Sesiones de usuario 1-2" dt="text" by="744705"/>
<httpSample t="1017" lt="694" ts="1296846847222" s="true" lb="Workgroup" rc="200" rm="OK" tn="Sesiones de usuario 1-1" dt="text" by="744705"/>
<httpSample t="598" lt="321" ts="1296846947037" s="true" lb="Home" rc="200" rm="OK" tn="Sesiones de usuario 1-1" dt="text" by="771149"/>
<httpSample t="501" lt="298" ts="1296846947144" s="true" lb="Home" rc="200" rm="OK" tn="Sesiones de usuario 1-2" dt="text" by="771149"/>
<httpSample t="63" lt="3" ts="1296846968923" s="true" lb="Workgroup" rc="200" rm="OK" tn="Sesiones de usuario 1-1" dt="text" by="744705"/>
<httpSample t="58" lt="2" ts="1296846969096" s="true" lb="Workgroup" rc="200" rm="OK" tn="Sesiones de usuario 1-2" dt="text" by="744705"/>
</testResults>

0 comments on commit 7e1559f

Please sign in to comment.