You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jmeter.apache.org by pm...@apache.org on 2017/01/18 20:22:24 UTC
svn commit: r1779376 - in /jmeter/trunk:
src/components/org/apache/jmeter/extractor/
src/components/org/apache/jmeter/extractor/gui/
src/core/org/apache/jmeter/util/ test/src/org/apache/jmeter/extractor/
xdocs/ xdocs/usermanual/
Author: pmouawad
Date: Wed Jan 18 20:22:24 2017
New Revision: 1779376
URL: http://svn.apache.org/viewvc?rev=1779376&view=rev
Log:
Bug 60602 - XPath Extractor : Add Match No. to allow extraction randomly, by index or all matches
Bugzilla Id: 60602
Modified:
jmeter/trunk/src/components/org/apache/jmeter/extractor/XPathExtractor.java
jmeter/trunk/src/components/org/apache/jmeter/extractor/gui/XPathExtractorGui.java
jmeter/trunk/src/core/org/apache/jmeter/util/XPathUtil.java
jmeter/trunk/test/src/org/apache/jmeter/extractor/TestXPathExtractor.java
jmeter/trunk/xdocs/changes.xml
jmeter/trunk/xdocs/usermanual/component_reference.xml
Modified: jmeter/trunk/src/components/org/apache/jmeter/extractor/XPathExtractor.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/extractor/XPathExtractor.java?rev=1779376&r1=1779375&r2=1779376&view=diff
==============================================================================
--- jmeter/trunk/src/components/org/apache/jmeter/extractor/XPathExtractor.java (original)
+++ jmeter/trunk/src/components/org/apache/jmeter/extractor/XPathExtractor.java Wed Jan 18 20:22:24 2017
@@ -32,6 +32,7 @@ import org.apache.jmeter.processor.PostP
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.testelement.AbstractScopedTestElement;
import org.apache.jmeter.testelement.property.BooleanProperty;
+import org.apache.jmeter.testelement.property.IntegerProperty;
import org.apache.jmeter.threads.JMeterContext;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jmeter.util.TidyException;
@@ -43,7 +44,6 @@ import org.apache.log.Logger;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
-//@see org.apache.jmeter.extractor.TestXPathExtractor for unit tests
/**
* Extracts text from (X)HTML response using XPath query language
@@ -60,20 +60,18 @@ import org.xml.sax.SAXException;
* <dt>//head/text()</dt>
* <dd>extracts the text content for head node.</dd>
* </dl>
- */
- /* This file is inspired by RegexExtractor.
- * author <a href="mailto:hpaluch@gitus.cz">Henryk Paluch</a>
- * of <a href="http://www.gitus.com">Gitus a.s.</a>
- *
- * See Bugzilla: 37183
+ see org.apache.jmeter.extractor.TestXPathExtractor for unit tests
*/
public class XPathExtractor extends AbstractScopedTestElement implements
PostProcessor, Serializable {
private static final Logger log = LoggingManager.getLoggerForClass();
- private static final long serialVersionUID = 240L;
+ private static final long serialVersionUID = 241L;
+
+ private static final int DEFAULT_VALUE = -1;
+ public static final String DEFAULT_VALUE_AS_STRING = Integer.toString(DEFAULT_VALUE);
- private static final String MATCH_NR = "matchNr"; // $NON-NLS-1$
+ private static final String REF_MATCH_NR = "matchNr"; // $NON-NLS-1$
//+ JMX file attributes
private static final String XPATH_QUERY = "XPathExtractor.xpathQuery"; // $NON-NLS-1$
@@ -88,6 +86,7 @@ public class XPathExtractor extends Abst
private static final String WHITESPACE = "XPathExtractor.whitespace"; // $NON-NLS-1$
private static final String VALIDATE = "XPathExtractor.validate"; // $NON-NLS-1$
private static final String FRAGMENT = "XPathExtractor.fragment"; // $NON-NLS-1$
+ private static final String MATCH_NUMBER = "XPathExtractor.matchNumber"; // $NON-NLS-1$
//- JMX file attributes
@@ -114,7 +113,7 @@ public class XPathExtractor extends Abst
JMeterVariables vars = context.getVariables();
String refName = getRefName();
vars.put(refName, getDefaultValue());
- final String matchNR = concat(refName,MATCH_NR);
+ final String matchNR = concat(refName,REF_MATCH_NR);
int prevCount=0; // number of previous matches
try {
prevCount=Integer.parseInt(vars.get(matchNR));
@@ -124,6 +123,7 @@ public class XPathExtractor extends Abst
vars.put(matchNR, "0"); // In case parse fails // $NON-NLS-1$
vars.remove(concat(refName,"1")); // In case parse fails // $NON-NLS-1$
+ int matchNumber = getMatchNumber();
List<String> matches = new ArrayList<>();
try{
if (isScopeVariable()){
@@ -131,7 +131,7 @@ public class XPathExtractor extends Abst
if(inputString != null) {
if(inputString.length()>0) {
Document d = parseResponse(inputString);
- getValuesForXPath(d,getXPathQuery(),matches);
+ getValuesForXPath(d,getXPathQuery(), matches, matchNumber);
}
} else {
log.warn("No variable '"+getVariableName()+"' found to process by XPathExtractor '"+getName()+"', skipping processing");
@@ -140,7 +140,7 @@ public class XPathExtractor extends Abst
List<SampleResult> samples = getSampleList(previousResult);
for (SampleResult res : samples) {
Document d = parseResponse(res.getResponseDataAsString());
- getValuesForXPath(d,getXPathQuery(),matches);
+ getValuesForXPath(d,getXPathQuery(), matches, matchNumber);
}
}
final int matchCount = matches.size();
@@ -308,12 +308,13 @@ public class XPathExtractor extends Abst
* @param d the document
* @param query the query to execute
* @param matchStrings list of matched strings (may include nulls)
+ * @param matchNumber int Match Number
*
* @throws TransformerException
*/
- private void getValuesForXPath(Document d,String query, List<String> matchStrings)
+ private void getValuesForXPath(Document d,String query, List<String> matchStrings, int matchNumber)
throws TransformerException {
- XPathUtil.putValuesForXPathInList(d, query, matchStrings, getFragment());
+ XPathUtil.putValuesForXPathInList(d, query, matchStrings, getFragment(), matchNumber);
}
public void setWhitespace(boolean selected) {
@@ -339,4 +340,44 @@ public class XPathExtractor extends Abst
public boolean isDownloadDTDs() {
return getPropertyAsBoolean(DOWNLOAD_DTDS, false);
}
+
+ /**
+ * Set which Match to use. This can be any positive number, indicating the
+ * exact match to use, or <code>0</code>, which is interpreted as meaning random.
+ *
+ * @param matchNumber The number of the match to be used
+ */
+ public void setMatchNumber(int matchNumber) {
+ setProperty(new IntegerProperty(MATCH_NUMBER, matchNumber));
+ }
+
+ /**
+ * Set which Match to use. This can be any positive number, indicating the
+ * exact match to use, or <code>0</code>, which is interpreted as meaning random.
+ *
+ * @param matchNumber The number of the match to be used
+ */
+ public void setMatchNumber(String matchNumber) {
+ setProperty(MATCH_NUMBER, matchNumber);
+ }
+
+ /**
+ * Return which Match to use. This can be any positive number, indicating the
+ * exact match to use, or <code>0</code>, which is interpreted as meaning random.
+ *
+ * @return matchNumber The number of the match to be used
+ */
+ public int getMatchNumber() {
+ return getPropertyAsInt(MATCH_NUMBER, DEFAULT_VALUE);
+ }
+
+ /**
+ * Return which Match to use. This can be any positive number, indicating the
+ * exact match to use, or <code>0</code>, which is interpreted as meaning random.
+ *
+ * @return matchNumber The number of the match to be used
+ */
+ public String getMatchNumberAsString() {
+ return getPropertyAsString(MATCH_NUMBER, DEFAULT_VALUE_AS_STRING);
+ }
}
Modified: jmeter/trunk/src/components/org/apache/jmeter/extractor/gui/XPathExtractorGui.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/extractor/gui/XPathExtractorGui.java?rev=1779376&r1=1779375&r2=1779376&view=diff
==============================================================================
--- jmeter/trunk/src/components/org/apache/jmeter/extractor/gui/XPathExtractorGui.java (original)
+++ jmeter/trunk/src/components/org/apache/jmeter/extractor/gui/XPathExtractorGui.java Wed Jan 18 20:22:24 2017
@@ -37,9 +37,6 @@ import org.apache.jorphan.gui.JLabeledTe
/**
* GUI for XPathExtractor class.
*/
- /* This file is inspired by RegexExtractor.
- * See Bugzilla: 37183
- */
public class XPathExtractorGui extends AbstractPostProcessorGui {
private static final long serialVersionUID = 240L;
@@ -50,6 +47,9 @@ public class XPathExtractorGui extends A
private final JLabeledTextField xpathQueryField =
new JLabeledTextField(JMeterUtils.getResString("xpath_extractor_query"));//$NON-NLS-1$
+ private final JLabeledTextField matchNumberField =
+ new JLabeledTextField(JMeterUtils.getResString("match_num_field"));//$NON-NLS-1$
+
private final JLabeledTextField refNameField =
new JLabeledTextField(JMeterUtils.getResString("ref_name_field"));//$NON-NLS-1$
@@ -77,6 +77,7 @@ public class XPathExtractorGui extends A
xpathQueryField.setText(xpe.getXPathQuery());
defaultField.setText(xpe.getDefaultValue());
refNameField.setText(xpe.getRefName());
+ matchNumberField.setText(xpe.getMatchNumberAsString());
getFragment.setSelected(xpe.getFragment());
xml.configure(xpe);
}
@@ -97,6 +98,7 @@ public class XPathExtractorGui extends A
saveScopeSettings(xpath);
xpath.setDefaultValue(defaultField.getText());
xpath.setRefName(refNameField.getText());
+ xpath.setMatchNumber(matchNumberField.getText());
xpath.setXPathQuery(xpathQueryField.getText());
xpath.setFragment(getFragment.isSelected());
xml.modifyTestElement(xpath);
@@ -113,6 +115,7 @@ public class XPathExtractorGui extends A
xpathQueryField.setText(""); // $NON-NLS-1$
defaultField.setText(""); // $NON-NLS-1$
refNameField.setText(""); // $NON-NLS-1$
+ matchNumberField.setText(XPathExtractor.DEFAULT_VALUE_AS_STRING); // $NON-NLS-1$
xml.setDefaultValues();
}
@@ -140,6 +143,8 @@ public class XPathExtractorGui extends A
resetContraints(gbc);
addField(panel, xpathQueryField, gbc);
resetContraints(gbc);
+ addField(panel, matchNumberField, gbc);
+ resetContraints(gbc);
gbc.weighty = 1;
addField(panel, defaultField, gbc);
return panel;
Modified: jmeter/trunk/src/core/org/apache/jmeter/util/XPathUtil.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/util/XPathUtil.java?rev=1779376&r1=1779375&r2=1779376&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/util/XPathUtil.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/util/XPathUtil.java Wed Jan 18 20:22:24 2017
@@ -309,12 +309,34 @@ public class XPathUtil {
public static void putValuesForXPathInList(Document document,
String xPathQuery,
List<String> matchStrings, boolean fragment) throws TransformerException {
+ putValuesForXPathInList(document, xPathQuery, matchStrings, fragment, -1);
+ }
+
+
+ /**
+ * Put in matchStrings results of evaluation
+ * @param document XML document
+ * @param xPathQuery XPath Query
+ * @param matchStrings List of strings that will be filled
+ * @param fragment return fragment
+ * @param matchNumber match number
+ * @throws TransformerException when the internally used xpath engine fails
+ */
+ public static void putValuesForXPathInList(Document document,
+ String xPathQuery,
+ List<String> matchStrings, boolean fragment,
+ int matchNumber) throws TransformerException {
String val = null;
XObject xObject = XPathAPI.eval(document, xPathQuery, getPrefixResolver(document));
final int objectType = xObject.getType();
if (objectType == XObject.CLASS_NODESET) {
NodeList matches = xObject.nodelist();
int length = matches.getLength();
+ int indexToMatch = matchNumber;
+ if(matchNumber == 0 && length>0) {
+ indexToMatch = JMeterUtils.getRandomInt(length)+1;
+ }
+ boolean storeAllValues = matchNumber < 0;
for (int i = 0 ; i < length; i++) {
Node match = matches.item(i);
if ( match instanceof Element){
@@ -332,7 +354,9 @@ public class XPathUtil {
} else {
val = match.getNodeValue();
}
- matchStrings.add(val);
+ if(storeAllValues || indexToMatch == (i+1)) {
+ matchStrings.add(val);
+ }
}
} else if (objectType == XObject.CLASS_NULL
|| objectType == XObject.CLASS_UNKNOWN
Modified: jmeter/trunk/test/src/org/apache/jmeter/extractor/TestXPathExtractor.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/test/src/org/apache/jmeter/extractor/TestXPathExtractor.java?rev=1779376&r1=1779375&r2=1779376&view=diff
==============================================================================
--- jmeter/trunk/test/src/org/apache/jmeter/extractor/TestXPathExtractor.java (original)
+++ jmeter/trunk/test/src/org/apache/jmeter/extractor/TestXPathExtractor.java Wed Jan 18 20:22:24 2017
@@ -23,10 +23,13 @@ import static org.junit.Assert.assertEqu
import static org.junit.Assert.assertNull;
import java.io.UnsupportedEncodingException;
+
+import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.threads.JMeterContext;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.threads.JMeterVariables;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -107,6 +110,29 @@ public class TestXPathExtractor {
assertEquals("two", vars.get(VAL_NAME+"_2"));
assertNull(vars.get(VAL_NAME+"_3"));
+ // Test match 1
+ extractor.setXPathQuery("/book/page");
+ extractor.setMatchNumber(1);
+ extractor.process();
+ assertEquals("one", vars.get(VAL_NAME));
+ assertEquals("1", vars.get(VAL_NAME_NR));
+ assertEquals("one", vars.get(VAL_NAME+"_1"));
+ assertNull(vars.get(VAL_NAME+"_2"));
+ assertNull(vars.get(VAL_NAME+"_3"));
+
+ // Test match Random
+ extractor.setXPathQuery("/book/page");
+ extractor.setMatchNumber(0);
+ extractor.process();
+ assertEquals("1", vars.get(VAL_NAME_NR));
+ Assert.assertTrue(StringUtils.isNoneEmpty(vars.get(VAL_NAME)));
+ Assert.assertTrue(StringUtils.isNoneEmpty(vars.get(VAL_NAME+"_1")));
+ assertNull(vars.get(VAL_NAME+"_2"));
+ assertNull(vars.get(VAL_NAME+"_3"));
+
+ // Put back default value
+ extractor.setMatchNumber(-1);
+
extractor.setXPathQuery("/book/page[2]");
extractor.process();
assertEquals("two", vars.get(VAL_NAME));
@@ -140,6 +166,25 @@ public class TestXPathExtractor {
extractor.process();
assertEquals("Default", vars.get(VAL_NAME));
+ // No text all matches
+ extractor.setXPathQuery("//a");
+ extractor.process();
+ extractor.setMatchNumber(-1);
+ assertEquals("Default", vars.get(VAL_NAME));
+
+ // No text match second
+ extractor.setXPathQuery("//a");
+ extractor.process();
+ extractor.setMatchNumber(2);
+ assertEquals("Default", vars.get(VAL_NAME));
+
+ // No text match random
+ extractor.setXPathQuery("//a");
+ extractor.process();
+ extractor.setMatchNumber(0);
+ assertEquals("Default", vars.get(VAL_NAME));
+
+ extractor.setMatchNumber(-1);
// Test fragment
extractor.setXPathQuery("/book/page[2]");
extractor.setFragment(true);
Modified: jmeter/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1779376&r1=1779375&r2=1779376&view=diff
==============================================================================
--- jmeter/trunk/xdocs/changes.xml [utf-8] (original)
+++ jmeter/trunk/xdocs/changes.xml [utf-8] Wed Jan 18 20:22:24 2017
@@ -134,6 +134,7 @@ JMeter now requires Java 8. Ensure you u
<li><bug>60154</bug>User Parameters GUI: allow rows to be moved up & down in the list. Contributed by Murdecai777 (https://github.com/Murdecai777).</li>
<li><bug>60507</bug>Added '<code>Or</code>' Function into ResponseAssertion. Based on a contribution from \u5ffb\u9686 (298015902 at qq.com)</li>
<li><bug>58943</bug>Create a Better Think Time experience. Contributed by Ubik Load Pack (support at ubikloadpack.com)</li>
+ <li><bug>60602</bug>XPath Extractor : Add Match No. to allow extraction randomly, by index or all matches</li>
</ul>
<h3>Functions</h3>
Modified: jmeter/trunk/xdocs/usermanual/component_reference.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/usermanual/component_reference.xml?rev=1779376&r1=1779375&r2=1779376&view=diff
==============================================================================
--- jmeter/trunk/xdocs/usermanual/component_reference.xml (original)
+++ jmeter/trunk/xdocs/usermanual/component_reference.xml Wed Jan 18 20:22:24 2017
@@ -5605,7 +5605,7 @@ generate the template string, and store
<property name="Template" required="Yes">The template used to create a string from the matches found. This is an arbitrary string
with special elements to refer to groups within the regular expression. The syntax to refer to a group is: '<code>$1$</code>' to refer to
group <code>1</code>, '<code>$2$</code>' to refer to group <code>2</code>, etc. <code>$0$</code> refers to whatever the entire expression matches.</property>
- <property name="Match No." required="Yes">Indicates which match to use. The regular expression may match multiple times.
+ <property name="Match No. (0 for Random)" required="Yes">Indicates which match to use. The regular expression may match multiple times.
<ul>
<li>Use a value of zero to indicate JMeter should choose a match at random.</li>
<li>A positive number N means to select the n<sup>th</sup> match.</li>
@@ -5719,7 +5719,7 @@ extracting the node as text or attribute
If empty this is the equivalent of <a href="http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#text%28%29">Element#text()</a> function for JSoup if not value is set for attribute.
<figure width="825" height="275" image="css_extractor_noattr.png">CSS Extractor with no attribute set</figure>
</property>
- <property name="Match No." required="Yes">Indicates which match to use. The CSS/JQuery selector may match multiple times.
+ <property name="Match No. (0 for Random)" required="Yes">Indicates which match to use. The CSS/JQuery selector may match multiple times.
<ul>
<li>Use a value of zero to indicate JMeter should choose a match at random.</li>
<li>A positive number <code>N</code> means to select the n<sup>th</sup> match.</li>
@@ -5815,6 +5815,13 @@ extracting the node as text or attribute
</property>
<property name="Reference Name" required="Yes">The name of the JMeter variable in which to store the result.</property>
<property name="XPath Query" required="Yes">Element query in XPath language. Can return more than one match.</property>
+ <property name="Match No. (0 for Random)" required="No">If the XPath Path query leads to many results, you can choose which one(s) to extract as Variables:
+ <ul>
+ <li><code>0</code> : means random</li>
+ <li><code>-1</code> means extract all results (default value), they will be named as <code><em><variable name></em>_N</code> (where <code>N</code> goes from 1 to Number of results)</li>
+ <li><code>X</code> : means extract the X<sup>th</sup> result. If this X<sup>th</sup> is greater than number of matches, then nothing is returned. Default value will be used</li>
+ </ul>
+ </property>
<property name="Default Value" required="">Default value returned when no match found.
It is also returned if the node has no value and the fragment option is not selected.</property>
</properties>
@@ -6051,7 +6058,7 @@ It will allow you to extract in a very e
<property name="Variable Names" required="Yes">Semi-colon separated names of variables that will contain the results of JSON-PATH expressions (must match number of JSON-PATH expressions)</property>
<property name="JSON Path Expressions" required="Yes">Semi-colon separated JSON-PATH expressions (must match number of variables)</property>
<property name="Default Values" required="No">Semi-colon separated default values if JSON-PATH expressions do not return any result(must match number of variables)</property>
- <property name="Match Numbers" required="No">If the JSON Path query leads to many results, you can choose which one(s) to extract as Variables:
+ <property name="Match No. (0 for Random)" required="No">If the JSON Path query leads to many results, you can choose which one(s) to extract as Variables:
<ul>
<li><code>0</code> : means random (Default Value)</li>
<li><code>-1</code> means extract all results, they will be named as <code><em><variable name></em>_N</code> (where <code>N</code> goes from 1 to Number of results)</li>