jmeter icon indicating copy to clipboard operation
jmeter copied to clipboard

Custom JMeter Functions plugin is not detected if jmx file contains any of its functions

Open asfimport opened this issue 4 years ago • 4 comments

eR@SeR (Bug 65116): Hello,

If jmx file contains any of the Custom JMeter Functions like "doubleSum", "if", "base64Encode" etc. but the plugin is not previously installed by JMeter plugins manager, an error pop up message is not shown like for other missing plugins. If JMeter plugins manager is put in lib/ext folder, and when jmx file is opened which contains one or more of its functions, it doesn't detect that plugin should be installed and therefore functions are not evaluated when test is run. Check jmx example in the attachment.

Expected:

Pop up error message is shown that Custom JMeter functions are detected in jmx file. If JMeter plugins manager is in lib/ext folder, suggest the installation of Custom JMeter functions.

Or

All Custom JMeter functions can be merged to be part of JMeter core functions. I vote for the latter.

Custom JMeter functions link https://jmeter-plugins.org/wiki/Functions/

Jmeter 5.5 ff9866a Microsoft Windows 10 Enterprise 64-bit java version "14.0.1" 2020-04-14

Created attachment customJMeterFunctions.jmx: customJMeterFunctions.jmx

customJMeterFunctions.jmx
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.4.1-SNAPSHOT f551e6f">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
      <stringProp name="TestPlan.comments"></stringProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <boolProp name="TestPlan.tearDown_on_shutdown">true</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>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
    </TestPlan>
    <hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="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">1</stringProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">1</stringProp>
        <stringProp name="ThreadGroup.ramp_time">1</stringProp>
        <boolProp name="ThreadGroup.scheduler">false</boolProp>
        <stringProp name="ThreadGroup.duration"></stringProp>
        <stringProp name="ThreadGroup.delay"></stringProp>
        <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
      </ThreadGroup>
      <hashTree>
        <ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
          <boolProp name="ResultCollector.error_logging">false</boolProp>
          <objProp>
            <name>saveConfig</name>
            <value class="SampleSaveConfiguration">
              <time>true</time>
              <latency>true</latency>
              <timestamp>true</timestamp>
              <success>true</success>
              <label>true</label>
              <code>true</code>
              <message>true</message>
              <threadName>true</threadName>
              <dataType>true</dataType>
              <encoding>false</encoding>
              <assertions>true</assertions>
              <subresults>true</subresults>
              <responseData>false</responseData>
              <samplerData>false</samplerData>
              <xml>false</xml>
              <fieldNames>true</fieldNames>
              <responseHeaders>false</responseHeaders>
              <requestHeaders>false</requestHeaders>
              <responseDataOnError>false</responseDataOnError>
              <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
              <assertionsResultsToSave>0</assertionsResultsToSave>
              <bytes>true</bytes>
              <sentBytes>true</sentBytes>
              <url>true</url>
              <threadCounts>true</threadCounts>
              <idleTime>true</idleTime>
              <connectTime>true</connectTime>
            </value>
          </objProp>
          <stringProp name="filename"></stringProp>
        </ResultCollector>
        <hashTree/>
        <JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="JSR223 Sampler ${__if(1,1,true,false,)}" enabled="true">
          <stringProp name="cacheKey">true</stringProp>
          <stringProp name="filename"></stringProp>
          <stringProp name="parameters"></stringProp>
          <stringProp name="script">true</stringProp>
          <stringProp name="scriptLanguage">groovy</stringProp>
        </JSR223Sampler>
        <hashTree/>
        <JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="JSR223 Sampler ${__doubleSum(2.22,3.33,doubleSum)}" enabled="true">
          <stringProp name="cacheKey">true</stringProp>
          <stringProp name="filename"></stringProp>
          <stringProp name="parameters"></stringProp>
          <stringProp name="script">true</stringProp>
          <stringProp name="scriptLanguage">groovy</stringProp>
        </JSR223Sampler>
        <hashTree/>
        <JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="JSR223 Sampler ${__MD5(test)}" enabled="true">
          <stringProp name="cacheKey">true</stringProp>
          <stringProp name="filename"></stringProp>
          <stringProp name="parameters"></stringProp>
          <stringProp name="script">true</stringProp>
          <stringProp name="scriptLanguage">groovy</stringProp>
        </JSR223Sampler>
        <hashTree/>
        <JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="JSR223 Sampler ${__base64Encode(test string)}" enabled="true">
          <stringProp name="cacheKey">true</stringProp>
          <stringProp name="filename"></stringProp>
          <stringProp name="parameters"></stringProp>
          <stringProp name="script">true</stringProp>
          <stringProp name="scriptLanguage">groovy</stringProp>
        </JSR223Sampler>
        <hashTree/>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

Version: Nightly Severity: normal OS: All

asfimport avatar Feb 01 '21 00:02 asfimport

@FSchumacher (migrated from Bugzilla): This issue addresses a two points simultaneously.

a) It is about merging functionality from external plug-ins into JMeter b) It is about the way JMeter handles non-existing functions

On a)

Plug-ins are invented to ease the burden on the core JMeter team. We are only a few people and are happy, that others can and do provide extensions via plug-ins. Inclusion of such functionality should be done after careful inspection (best via devs mailing list).

On b)

I think, that this can't be changed easily. The way JMeter currently works, is that it tries to find a function by the name inside the ${...} constructs. JMeter doesn't know or care, where the function was defined (plug-in or built-in). So, we can't tell a user function X should be provided by plug-in Y.

At the moment the best we could do, would be to display a warning, that a function was not found.

For such a warning, we would have to decide, where to display it - log message or display a dialog?

Maybe we could discuss better ways to hint at the source of messages on the mailing list (devs?).

I think it might be nice to have an option to include custom functions (as JSR-223 snippets?) inside test plans, but on a first glance, this doesn't look easy.

asfimport avatar Feb 06 '21 10:02 asfimport

eR@SeR (migrated from Bugzilla): Hi,

a) It is about merging functionality from external plug-ins into JMeter We can discuss it on dev list, as you suggested. Thorough testing should be done for sure.

At the moment the best we could do, would be to display a warning, that a function was not found. For such a warning, we would have to decide, where to display it - log message or display a dialog? As I said, for other plugins we have pop-up error message shown. So, yes both log message and dialog display should be done. Example images for the Dummy sampler are in the attachment. You can follow the same pattern as shown in the images.

I think, that this can't be changed easily. The way JMeter currently works, is that it tries to find a function by the name inside the ${...} constructs. JMeter doesn't know or care, where the function was defined (plug-in or built-in). So, we can't tell a user function X should be provided by plug-in Y. Yes, that's right. That is the job of jmeter-plugins-manager-1.6.jar file, I guess. IDK how it works, since it detects properly which jar file to download if the plugin is not installed. If pop-up error message is implemented, probably the change should be done on JMeter plugins side as well - to point to the corresponding jar file download. The most important thing here is that JMeter notifies user that functions, that are not part of the core are detected and that the loaded jmx file won't work as expected.

Created attachment pop up messages.rar: pop up messages

asfimport avatar Feb 06 '21 23:02 asfimport

@rollno748 (migrated from Bugzilla): Created attachment 37730.jmx: A simple workaround

37730.jmx
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.4.2">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
      <stringProp name="TestPlan.comments"></stringProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <boolProp name="TestPlan.tearDown_on_shutdown">true</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>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
    </TestPlan>
    <hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="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">1</stringProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">1</stringProp>
        <stringProp name="ThreadGroup.ramp_time">1</stringProp>
        <boolProp name="ThreadGroup.scheduler">false</boolProp>
        <stringProp name="ThreadGroup.duration"></stringProp>
        <stringProp name="ThreadGroup.delay"></stringProp>
        <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
      </ThreadGroup>
      <hashTree>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP Request" enabled="true">
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
            <collectionProp name="Arguments.arguments">
              <elementProp name="q" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.value">KB4474419</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
                <boolProp name="HTTPArgument.use_equals">true</boolProp>
                <stringProp name="Argument.name">q</stringProp>
              </elementProp>
            </collectionProp>
          </elementProp>
          <stringProp name="HTTPSampler.domain">www.catalog.update.microsoft.com</stringProp>
          <stringProp name="HTTPSampler.port"></stringProp>
          <stringProp name="HTTPSampler.protocol"></stringProp>
          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
          <stringProp name="HTTPSampler.path">/Search.aspx</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>
          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
        </HTTPSamplerProxy>
        <hashTree>
          <HtmlExtractor guiclass="HtmlExtractorGui" testclass="HtmlExtractor" testname="CSS Selector Extractor" enabled="true">
            <stringProp name="HtmlExtractor.refname">title</stringProp>
            <stringProp name="HtmlExtractor.expr">td.resultsbottomBorder a</stringProp>
            <stringProp name="HtmlExtractor.attribute"></stringProp>
            <stringProp name="HtmlExtractor.default"></stringProp>
            <boolProp name="HtmlExtractor.default_empty_value">false</boolProp>
            <stringProp name="HtmlExtractor.match_number">-1</stringProp>
            <stringProp name="HtmlExtractor.extractor_impl"></stringProp>
          </HtmlExtractor>
          <hashTree/>
          <BoundaryExtractor guiclass="BoundaryExtractorGui" testclass="BoundaryExtractor" testname="Boundary Extractor" enabled="true">
            <stringProp name="BoundaryExtractor.useHeaders">false</stringProp>
            <stringProp name="BoundaryExtractor.refname">id</stringProp>
            <stringProp name="BoundaryExtractor.lboundary">&lt;a id=&apos;</stringProp>
            <stringProp name="BoundaryExtractor.rboundary">_link</stringProp>
            <stringProp name="BoundaryExtractor.default"></stringProp>
            <boolProp name="BoundaryExtractor.default_empty_value">false</boolProp>
            <stringProp name="BoundaryExtractor.match_number">-1</stringProp>
          </BoundaryExtractor>
          <hashTree/>
        </hashTree>
        <ForeachController guiclass="ForeachControlPanel" testclass="ForeachController" testname="ForEach Controller" enabled="true">
          <stringProp name="ForeachController.inputVal">title</stringProp>
          <stringProp name="ForeachController.returnVal">CurrentTitle</stringProp>
          <boolProp name="ForeachController.useSeparator">true</boolProp>
          <stringProp name="ForeachController.startIndex">0</stringProp>
          <stringProp name="ForeachController.endIndex">${title_matchNr}</stringProp>
        </ForeachController>
        <hashTree>
          <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="${CurrentTitle},${CurrentId}" 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">www.qq.com</stringProp>
            <stringProp name="HTTPSampler.port"></stringProp>
            <stringProp name="HTTPSampler.protocol"></stringProp>
            <stringProp name="HTTPSampler.contentEncoding"></stringProp>
            <stringProp name="HTTPSampler.path"></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>
            <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
            <stringProp name="HTTPSampler.connect_timeout"></stringProp>
            <stringProp name="HTTPSampler.response_timeout"></stringProp>
          </HTTPSamplerProxy>
          <hashTree>
            <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="JSR223 PreProcessor" enabled="true">
              <stringProp name="cacheKey">false</stringProp>
              <stringProp name="filename"></stringProp>
              <stringProp name="parameters">${__jm__ForEach Controller__idx}</stringProp>
              <stringProp name="script">def counter = Integer.parseInt(args[0]) + 1;
def currTitle = &quot;title_&quot;+ counter;
def currId = &quot;id_&quot;+ counter;

vars.put(&quot;CurrentTitle&quot;, vars.get(currTitle))
vars.put(&quot;CurrentId&quot;, vars.get(currId))
</stringProp>
              <stringProp name="scriptLanguage">groovy</stringProp>
            </JSR223PreProcessor>
            <hashTree/>
          </hashTree>
        </hashTree>
      </hashTree>
      <ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
        <boolProp name="ResultCollector.error_logging">false</boolProp>
        <objProp>
          <name>saveConfig</name>
          <value class="SampleSaveConfiguration">
            <time>true</time>
            <latency>true</latency>
            <timestamp>true</timestamp>
            <success>true</success>
            <label>true</label>
            <code>true</code>
            <message>true</message>
            <threadName>true</threadName>
            <dataType>true</dataType>
            <encoding>false</encoding>
            <assertions>true</assertions>
            <subresults>true</subresults>
            <responseData>false</responseData>
            <samplerData>false</samplerData>
            <xml>false</xml>
            <fieldNames>true</fieldNames>
            <responseHeaders>false</responseHeaders>
            <requestHeaders>false</requestHeaders>
            <responseDataOnError>false</responseDataOnError>
            <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
            <assertionsResultsToSave>0</assertionsResultsToSave>
            <bytes>true</bytes>
            <sentBytes>true</sentBytes>
            <url>true</url>
            <threadCounts>true</threadCounts>
            <idleTime>true</idleTime>
            <connectTime>true</connectTime>
          </value>
        </objProp>
        <stringProp name="filename"></stringProp>
      </ResultCollector>
      <hashTree/>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

asfimport avatar May 09 '22 22:05 asfimport

@rollno748 (migrated from Bugzilla): Needed info form the user

asfimport avatar May 09 '22 22:05 asfimport