From 76e24ee5f7c37eac366d59edd5b714d4f4b6c3e1 Mon Sep 17 00:00:00 2001 From: Joaquin Araujo Date: Tue, 1 Oct 2024 03:59:37 +0100 Subject: [PATCH] Release v3.0 # Auto Correlation Recorder Plugin v3.0 Release Note We're excited to bring you the latest enhancements and features in the Auto Correlation Recorder Plugin v3.0 Here's what's new: ## Siebel removed from core The following components were removed from the plugin - Siebel Array JMeter Function - Siebel Counter Correlation Replacement - Siebel Row Correlation Extractor - Siebel Row Id Correlation Replacement - Siebel Row Params Correlation Replacement The main reason this release is major one is due to the lack of backward compatibility. However, not all are bad news. Now it's possible to access to a better Siebel correlation support by joining the [Blazemeter community](https://www.blazemeter.com/). It's possible to use the new template and components directly from the plugin! ## Other Improvements - Fix issue which reseted the JMeter templates.xml - Allow to display templates description that contain custom rules despite of jar installation - Dynamic class loader for custom extractors and replacements - Custom extensions are now saved in lib/ext to benefit from JMeter class loading system - Fix several issues related to apply suggestions based on a template --- .../public/examples/CustomContext.java | 1 - .../src/contributing/assets/classDiagram.puml | 78 -- docs/src/guide/README.md | 2 +- .../siebel-row-correlation-extractor.png | Bin 26198 -> 0 bytes docs/src/guide/before-recording.md | 47 +- docs/src/guide/correlation-rules.md | 45 +- docs/src/guide/custom-extensions/README.md | 1 - .../siebel_extension_explanations.md | 112 -- docs/src/guide/templates/readme.md | 1 + pom.xml | 5 +- releases.json | 14 + .../correlation/CorrelationProxyControl.java | 7 + .../CorrelationProxyControlGui.java | 9 +- .../correlation/core/ClassFinderUtils.java | 66 + .../correlation/core/CorrelationEngine.java | 38 +- .../core/analysis/AnalysisReporter.java | 4 +- .../RegexCorrelationReplacement.java | 60 +- .../CorrelationTemplatesRegistryHandler.java | 3 + ...ionTemplatesRepositoriesConfiguration.java | 3 + ...nTemplatesRepositoriesRegistryHandler.java | 2 + .../core/templates/LocalConfiguration.java | 151 ++- .../core/templates/SiebelTemplateRemoval.java | 87 ++ .../repository/pluggable/LocalRepository.java | 40 +- .../gui/CorrelationComponentsRegistry.java | 110 +- .../gui/CustomExtensionsDialog.java | 1 + .../correlation/gui/RulesContainer.java | 32 +- .../gui/TestPlanTemplatesRepository.java | 2 +- .../templates/RepositoriesConfigFrame.java | 4 + .../gui/templates/TemplatesManagerFrame.java | 8 + .../siebel/SiebelArrayFunction.java | 80 -- .../correlation/siebel/SiebelContext.java | 190 --- .../SiebelCounterCorrelationReplacement.java | 98 -- .../siebel/SiebelRowCorrelationExtractor.java | 185 --- .../SiebelRowIdCorrelationReplacement.java | 76 -- ...SiebelRowParamsCorrelationReplacement.java | 114 -- .../SiebelCounterCorrelationReplacement.html | 32 - .../SiebelRowCorrelationExtractor.html | 51 - .../SiebelRowIdCorrelationReplacement.html | 31 - ...SiebelRowParamsCorrelationReplacement.html | 32 - .../CorrelationProxyControlTest.java | 2 - .../core/CorrelationEngineTest.java | 22 +- .../core/analysis/AnalysisReporterIT.java | 2 - .../suggestions/SuggestionGeneratorIT.java | 8 +- ...emplatesRepositoriesConfigurationTest.java | 12 +- .../templates/LocalConfigurationTest.java | 962 ++++++------- ...LocalCorrelationTemplatesRegistryTest.java | 88 +- ...tionTemplatesRepositoriesRegistryTest.java | 29 +- .../core/templates/ProtocolTest.java | 7 +- .../templates/SiebelTemplateRemovalTest.java | 111 ++ .../core/templates/TemplateTest.java | 1189 ++++++++--------- .../core/templates/TemplateUtils.java | 47 + .../TemplateVersionKeyDeserializerTest.java | 13 +- .../custom/extension/CustomContext.java | 48 + .../extension/CustomCorrelationExtractor.java | 108 ++ .../CustomCorrelationReplacement.java | 22 + .../CorrelationComponentsRegistryTest.java | 101 +- .../gui/CustomExtensionsDialogIT.java | 68 +- .../correlation/gui/RulesContainerIT.java | 6 +- .../gui/TestPlanTemplatesRepositoryTest.java | 19 - .../gui/common/RulePartTypeTest.java | 15 +- .../siebel/SiebelArrayFunctionTest.java | 140 -- .../correlation/siebel/SiebelContextTest.java | 77 -- ...ebelCounterCorrelationReplacementTest.java | 156 --- .../SiebelRowCorrelationExtractorTest.java | 129 -- ...SiebelRowIdCorrelationReplacementTest.java | 79 -- ...elRowParamsCorrelationReplacementTest.java | 118 -- .../resources/Siebel-CRM-1.0-template.json | 45 - .../resources/Siebel-CRM-2.0-template.json | 49 - .../CustomCorrelationExtractor.html | 5 + .../CustomCorrelationReplacement.html | 11 + .../base-repository.json | 4 +- .../test-1.0-template.json | 33 + .../test-1.1-template.json | 21 + .../wordpress-1.0-template.json | 0 ...son => delete-wordpress-1.0-template.json} | 16 +- src/test/resources/initialResponse.txt | 1 - src/test/resources/invalidResponseData.txt | 1 - src/test/resources/requestToReplaceArray.txt | 1 - src/test/resources/responseToExtractArray.txt | 1 - src/test/resources/siebel-1.0-template.json | 185 --- .../resources/starArrayPostProcessor.groovy | 84 -- .../serialization-test-1.0-template.json | 46 + .../serialization-test-2.0-template.json | 50 + .../templates/siebel-template-removal.json} | 0 84 files changed, 2190 insertions(+), 3763 deletions(-) delete mode 100644 docs/src/guide/assets/siebel-row-correlation-extractor.png delete mode 100644 docs/src/guide/custom-extensions/siebel_extension_explanations.md create mode 100644 src/main/java/com/blazemeter/jmeter/correlation/core/ClassFinderUtils.java create mode 100644 src/main/java/com/blazemeter/jmeter/correlation/core/templates/SiebelTemplateRemoval.java delete mode 100644 src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelArrayFunction.java delete mode 100644 src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelContext.java delete mode 100644 src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelCounterCorrelationReplacement.java delete mode 100644 src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowCorrelationExtractor.java delete mode 100644 src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowIdCorrelationReplacement.java delete mode 100644 src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowParamsCorrelationReplacement.java delete mode 100644 src/main/resources/correlation-descriptions/SiebelCounterCorrelationReplacement.html delete mode 100644 src/main/resources/correlation-descriptions/SiebelRowCorrelationExtractor.html delete mode 100644 src/main/resources/correlation-descriptions/SiebelRowIdCorrelationReplacement.html delete mode 100644 src/main/resources/correlation-descriptions/SiebelRowParamsCorrelationReplacement.html create mode 100644 src/test/java/com/blazemeter/jmeter/correlation/core/templates/SiebelTemplateRemovalTest.java create mode 100644 src/test/java/com/blazemeter/jmeter/correlation/core/templates/TemplateUtils.java create mode 100644 src/test/java/com/blazemeter/jmeter/correlation/custom/extension/CustomContext.java create mode 100644 src/test/java/com/blazemeter/jmeter/correlation/custom/extension/CustomCorrelationExtractor.java create mode 100644 src/test/java/com/blazemeter/jmeter/correlation/custom/extension/CustomCorrelationReplacement.java delete mode 100644 src/test/java/com/blazemeter/jmeter/correlation/siebel/SiebelArrayFunctionTest.java delete mode 100644 src/test/java/com/blazemeter/jmeter/correlation/siebel/SiebelContextTest.java delete mode 100644 src/test/java/com/blazemeter/jmeter/correlation/siebel/SiebelCounterCorrelationReplacementTest.java delete mode 100644 src/test/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowCorrelationExtractorTest.java delete mode 100644 src/test/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowIdCorrelationReplacementTest.java delete mode 100644 src/test/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowParamsCorrelationReplacementTest.java delete mode 100644 src/test/resources/Siebel-CRM-1.0-template.json delete mode 100644 src/test/resources/Siebel-CRM-2.0-template.json create mode 100644 src/test/resources/correlation-descriptions/CustomCorrelationExtractor.html create mode 100644 src/test/resources/correlation-descriptions/CustomCorrelationReplacement.html rename src/test/resources/{ => correlation-templates}/base-repository.json (88%) create mode 100644 src/test/resources/correlation-templates/test-1.0-template.json create mode 100644 src/test/resources/correlation-templates/test-1.1-template.json rename src/test/resources/{ => correlation-templates}/wordpress-1.0-template.json (100%) rename src/test/resources/{siebel-1.1-template.json => delete-wordpress-1.0-template.json} (65%) delete mode 100644 src/test/resources/initialResponse.txt delete mode 100644 src/test/resources/invalidResponseData.txt delete mode 100644 src/test/resources/requestToReplaceArray.txt delete mode 100644 src/test/resources/responseToExtractArray.txt delete mode 100644 src/test/resources/siebel-1.0-template.json delete mode 100644 src/test/resources/starArrayPostProcessor.groovy create mode 100644 src/test/resources/templates/serialization-test-1.0-template.json create mode 100644 src/test/resources/templates/serialization-test-2.0-template.json rename src/{main/resources/correlation-templates/siebel-1.0-template.json => test/resources/templates/siebel-template-removal.json} (100%) diff --git a/docs/src/.vuepress/public/examples/CustomContext.java b/docs/src/.vuepress/public/examples/CustomContext.java index a1d92e6..699b30f 100644 --- a/docs/src/.vuepress/public/examples/CustomContext.java +++ b/docs/src/.vuepress/public/examples/CustomContext.java @@ -36,7 +36,6 @@ public void reset() { * This method is always called when the {@link CorrelationEngine} is processing the responses * from the server. * - * Look for the implementation on {@link ../src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelContext} */ @Override public void update(SampleResult sampleResult) { diff --git a/docs/src/contributing/assets/classDiagram.puml b/docs/src/contributing/assets/classDiagram.puml index b4592a4..fe6b557 100644 --- a/docs/src/contributing/assets/classDiagram.puml +++ b/docs/src/contributing/assets/classDiagram.puml @@ -308,80 +308,11 @@ title __Correlations Recorder's Class Diagram__\n class com.blazemeter.jmeter.correlation.gui.TestPlanTemplatesRepository { - rootFolder : String + TestPlanTemplatesRepository() - + addSiebelCorrelationTemplate() - + addSiebelTemplate() + setRootFolder() } } - namespace siebel { - class com.blazemeter.jmeter.correlation.siebel.SiebelArrayFunction { - {static} - DESC : List - {static} - KEY : String - } - - class com.blazemeter.jmeter.correlation.siebel.SiebelContext { - - bcis : Map - - counter : Integer - - paramRowFields : Map - - prefixId : int - - rowVars : Map - + update() - } - - class com.blazemeter.jmeter.correlation.siebel.SiebelContext.BCI { - - fields : Map - - addField() - - getField() - } - class com.blazemeter.jmeter.correlation.siebel.SiebelContext.Field { - {static} - TELEPHONE_TYPE : int - - id : int - - position : int - - type : int - + getIgnoredCharsRegex() - + getPosition() - - Field() - } - class com.blazemeter.jmeter.correlation.siebel.SiebelCounterCorrelationReplacement { - + SiebelCounterCorrelationReplacement() - + getSupportedContext() - + process() - + toString() - + update() - + updateTestElem() - - createPreProcessor() - } - class com.blazemeter.jmeter.correlation.siebel.SiebelRowCorrelationExtractor { - + SiebelRowCorrelationExtractor() - + getParams() - + getParamsDefinition() - + getSupportedContext() - + process() - + update() - + updateTestElem() - - buildArrayParserPostProcessor() - } - class com.blazemeter.jmeter.correlation.siebel.SiebelRowIdCorrelationReplacement { - + getSupportedContext() - + update() - + updateTestElem() - # replaceWithRegex() - } - class com.blazemeter.jmeter.correlation.siebel.SiebelRowParamsCorrelationReplacement { - - rowVarPrefix : String - + SiebelRowParamsCorrelationReplacement() - + getParams() - + getParamsDefinition() - + getSupportedContext() - + process() - + setParams() - + update() - + updateTestElem() - # replaceWithRegex() - } - } } @@ -463,15 +394,6 @@ title __Correlations Recorder's Class Diagram__\n com.blazemeter.jmeter.correlation.gui.RulesContainer o-- com.blazemeter.jmeter.correlation.gui.ComponentContainer : componentContainer com.blazemeter.jmeter.correlation.gui.RulesContainer o-- com.blazemeter.jmeter.correlation.core.CorrelationComponentsRegistry : componentsRepository - com.blazemeter.jmeter.correlation.siebel.SiebelArrayFunction -up-|> org.apache.jmeter.functions.AbstractFunction - com.blazemeter.jmeter.correlation.siebel.SiebelContext .up.|> com.blazemeter.jmeter.correlation.core.CorrelationContext - com.blazemeter.jmeter.correlation.siebel.SiebelContext +-down- com.blazemeter.jmeter.correlation.siebel.SiebelContext.BCI - com.blazemeter.jmeter.correlation.siebel.SiebelContext +-down- com.blazemeter.jmeter.correlation.siebel.SiebelContext.Field - com.blazemeter.jmeter.correlation.siebel.SiebelCounterCorrelationReplacement -up-|> com.blazemeter.jmeter.correlation.core.replacements.RegexCorrelationReplacement - com.blazemeter.jmeter.correlation.siebel.SiebelRowCorrelationExtractor -up-|> com.blazemeter.jmeter.correlation.core.extractors.RegexCorrelationExtractor - com.blazemeter.jmeter.correlation.siebel.SiebelRowIdCorrelationReplacement -up-|> com.blazemeter.jmeter.correlation.core.replacements.RegexCorrelationReplacement - com.blazemeter.jmeter.correlation.siebel.SiebelRowParamsCorrelationReplacement -up-|> com.blazemeter.jmeter.correlation.core.replacements.RegexCorrelationReplacement - com.blazemeter.jmeter.correlation.CorrelationProxyControl .up.|> com.blazemeter.jmeter.correlation.core.templates.CorrelationTemplatesRegistryHandler com.blazemeter.jmeter.correlation.CorrelationProxyControl .up.|> com.blazemeter.jmeter.correlation.core.templates.CorrelationTemplatesRepositoriesRegistryHandler com.blazemeter.jmeter.correlation.CorrelationProxyControl -up-|> org.apache.jmeter.protocol.http.proxy.ProxyControl diff --git a/docs/src/guide/README.md b/docs/src/guide/README.md index eda9c93..47181eb 100644 --- a/docs/src/guide/README.md +++ b/docs/src/guide/README.md @@ -16,7 +16,7 @@ easier to maintain. ## Key Features - Automated correlation detection and suggestions for faster and more accurate test script creation -- Preloaded SIEBEL templates for convenience and faster test script creation +- Access to SIEBEL templates for convenience and faster test script creation - Auto install, download and update repositories for a convenient and hassle-free team collaboration - Easy customization options for efficient test script creation and increased productivity - Customizable correlations to match your specific testing needs and requirements diff --git a/docs/src/guide/assets/siebel-row-correlation-extractor.png b/docs/src/guide/assets/siebel-row-correlation-extractor.png deleted file mode 100644 index 62e3143fcf581e2e00db35a6bededd45af03a2f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26198 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 z 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 ayL|{PJ5y*Qv_?6epwc+6t5U3l3HlE_ngZ(p diff --git a/docs/src/guide/before-recording.md b/docs/src/guide/before-recording.md index 0a4adaa..9c63b1d 100644 --- a/docs/src/guide/before-recording.md +++ b/docs/src/guide/before-recording.md @@ -268,39 +268,6 @@ The IETF group in charge of creating Internet standards in February 2024 complet You can consult the RFC documentation in case you require more details about JSONPath https://datatracker.ietf.org/doc/rfc9535/ -**SiebelRow** - -This Correlation Extractor comes in the already installed Siebel's Template. To know more about how to load and save Correlation Rules Templates, please refer to the [Saving and Loading Rules](#saving-and-loading-rules) section, for further details about it. - -![Siebel Row Correlation Extractor](./assets/siebel-row-correlation-extractor.png) - -The Siebel Row Correlation Extractor works in a similar way like the previously mentioned RegEx, with the main differences that: - -- This applies a "Siebel Star Array strings" parsing function over the matched regex and store the parsed values as variables. -- Regarding the Parameters expected, Siebel Row Correlation Extractor uses all but the _Match Number_, since it parses every match it founds. -- Last, but not least, if the Regex its matched, a JSR223PostProcessor will be added to the sampler. - -_For a better understanding, lets do an example_ - -Let's say that the String we want to extract and, therefore, apply this extracting and parsing function, its - -`8\*testUser12\*testPassword6\*VRId-0` - -The plugin will search for the number before an occurrence of "\*", uses that value as the length of the number of characters to store, and then repeats if there is another occurrence of "\*". - -If the Reference Variable is set to _VAR_, the split strings returned will be set in variables names like ${VAR_1}, ${VAR_2}, ${VAR_3} etc. and, the count of variables is returned as ${VAR_n}. - -The stored values for that string, at the end, will be: - -- VAR_n=3 -- VAR_1=testUser -- VAR_2=testPassword -- VAR_3=VRId-0 - -### Star Array Correlation - -When the server returns variables using a star array, the plugin will parse the array and generate a new variable for each of the parameters, using the specified prefix name. - ## Variable Generation The creation of JMeter variables for later usage is one of the pillars of this plugin. Therefore, this section is specially dedicated to understand and review all the possible scenarios. @@ -428,7 +395,7 @@ Now that we have a basic idea of a replacement, lets explain the most complex an ## List of Correlation Replacements -All the Correlation Replacements mentioned below comes either installed by default in the plugin, or as a part of the preloaded Siebel Template. To know more about how to load and save Correlation Rules Templates, please refer to the [Saving and Loading Rules](#saving-and-loading-rules) section, for further details about it. +All the Correlation Replacements mentioned below are installed by default in the plugin. To know more about how to load and save Correlation Rules Templates, please refer to the [Saving and Loading Rules](#saving-and-loading-rules) section, for further details about it. In case none of the following Replacements fits the desired behavior your application requires, to correlate the dynamic variables that it might have, feel free to read the [Customizing your one extensions](CUSTOM_EXTENSIONS.md) section that we have prepared for you. @@ -447,15 +414,3 @@ Similarly to the _JSON Correlation Extractor_, this one also receives a JSONPath For examples of using JSONPath expressions, refer to the _JSON Correlation Extractor_ documentation. The logic behind **Replacement string** and **Ignore value** is the same as _Regex Correlation Replacement_. Go to the documentation related to **Variable Replacement** for usage examples. - -**Siebel Counter** - -This Correlation Replacement replaced the matched regex with a counter that holds the value of each time it has matched on the moment the replacement occurs. - -**Siebel Row Id** - -This replacement adds _\_rowId_ to the Reference Variable name before each replacement, and search the value of the regex on rows of the Siebel Context. After this, it behaves like a regular RegEx Replacement using the Siebel Context - -**SiebelRowParams** - -Similarly to the rest of the Correlation that involves Regex, this Replacement will receive a Regular Expression as param, its going to search for the first occurrence of the first group of (), inside the Siebel Context values, extracted previously by the SiebelRow's CorrelationExtractor, and replace the respective value following the formula `RefVar` + \_ + `RowNumber`. diff --git a/docs/src/guide/correlation-rules.md b/docs/src/guide/correlation-rules.md index 82d2b07..c13ddb4 100644 --- a/docs/src/guide/correlation-rules.md +++ b/docs/src/guide/correlation-rules.md @@ -239,38 +239,6 @@ The IETF group in charge of creating Internet standards in February 2024 complet You can consult the RFC documentation in case you require more details about JSONPath https://datatracker.ietf.org/doc/rfc9535/ -**SiebelRow** - -This Correlation Extractor comes in the already installed Siebel's Template. To know more about how to load and save Correlation Rules Templates, please refer to the [Saving and Loading Rules](#saving-and-loading-rules) section, for further details about it. - -![Siebel Row Correlation Extractor](./assets/siebel-row-correlation-extractor.png) - -The Siebel Row Correlation Extractor works in a similar way like the previously mentioned RegEx, with the main differences that: - -- This applies a "Siebel Star Array strings" parsing function over the matched regex and store the parsed values as variables. -- Regarding the Parameters expected, Siebel Row Correlation Extractor uses all but the _Match Number_, since it parses every match it founds. -- Last, but not least, if the Regex its matched, a JSR223PostProcessor will be added to the sampler. - -_For a better understanding, lets do an example_ - -Let's say that the String we want to extract and, therefore, apply this extracting and parsing function, its - -`8\*testUser12\*testPassword6\*VRId-0` - -The plugin will search for the number before an occurrence of "\*", uses that value as the length of the number of characters to store, and then repeats if there is another occurrence of "\*". - -If the Reference Variable is set to _VAR_, the split strings returned will be set in variables names like ${VAR_1}, ${VAR_2}, ${VAR_3} etc. and, the count of variables is returned as ${VAR_n}. - -The stored values for that string, at the end, will be: - -- VAR_n=3 -- VAR_1=testUser -- VAR_2=testPassword -- VAR_3=VRId-0 - -### Star Array Correlation - -When the server returns variables using a star array, the plugin will parse the array and generate a new variable for each of the parameters, using the specified prefix name. ## Variable Generation @@ -399,7 +367,7 @@ Now that we have a basic idea of a replacement, lets explain the most complex an ## List of Correlation Replacements -All the Correlation Replacements mentioned below comes either installed by default in the plugin, or as a part of the preloaded Siebel Template. To know more about how to load and save Correlation Rules Templates, please refer to the [Saving and Loading Rules](#saving-and-loading-rules) section, for further details about it. +All the Correlation Replacements mentioned below are installed by default in the plugin. To know more about how to load and save Correlation Rules Templates, please refer to the [Saving and Loading Rules](#saving-and-loading-rules) section, for further details about it. In case none of the following Replacements fits the desired behavior your application requires, to correlate the dynamic variables that it might have, feel free to read the [Customizing your one extensions](custom-extensions) section that we have prepared for you. @@ -419,14 +387,3 @@ For examples of using JSONPath expressions, refer to the _JSON Correlation Extra The logic behind **Replacement string** and **Ignore value** is the same as _Regex Correlation Replacement_. Go to the documentation related to **Variable Replacement** for usage examples. -**Siebel Counter** - -This Correlation Replacement replaced the matched regex with a counter that holds the value of each time it has matched on the moment the replacement occurs. - -**Siebel Row Id** - -This replacement adds _\_rowId_ to the Reference Variable name before each replacement, and search the value of the regex on rows of the Siebel Context. After this, it behaves like a regular RegEx Replacement using the Siebel Context - -**SiebelRowParams** - -Similarly to the rest of the Correlation that involves Regex, this Replacement will receive a Regular Expression as param, its going to search for the first occurrence of the first group of (), inside the Siebel Context values, extracted previously by the SiebelRow's CorrelationExtractor, and replace the respective value following the formula `RefVar` + \_ + `RowNumber`. diff --git a/docs/src/guide/custom-extensions/README.md b/docs/src/guide/custom-extensions/README.md index 7f127d2..77c54af 100644 --- a/docs/src/guide/custom-extensions/README.md +++ b/docs/src/guide/custom-extensions/README.md @@ -188,6 +188,5 @@ This is important, in most of the cases, to properly show the name of the Extens Review these links for a further understanding of correlating concepts and examples: -* [Siebel's Custom Extension explained](siebel_extension_explanations.md): an explanation of Siebel CRM’s Custom Extension. * [Extensions and useful methods in the Flow](the_flow_explanation.md): detailed explanation of how correlation works. * [Examples](https://github.com/Blazemeter/CorrelationRecorder/tree/master/examples): basic structure for a Correlation Extension. diff --git a/docs/src/guide/custom-extensions/siebel_extension_explanations.md b/docs/src/guide/custom-extensions/siebel_extension_explanations.md deleted file mode 100644 index 38e5577..0000000 --- a/docs/src/guide/custom-extensions/siebel_extension_explanations.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -sidebar : auto ---- - -

Correlations Recorder Plugin for JMeter

- -# How Siebel CRM Extensions works - -## Basic Concepts - -Remember to review [The Flow](the_flow_explanation.md) to know when and where the methods and Context classes are being used. - -The following section will give an explanation of the problem and the necessity of a Siebel CRM Extensions. - -##Some Context - -While the user is moving from one point of the page to another, some data is sent under the hood (embedded inside JS scripts, before the HTML or inside it). That information is used by Siebel CRM to keep track of where the user is coming. - -Those values don’t come in a format that will allow you to correlate them easily because they may vary on position, on the way they are formed, even by the separator and the values represented in them. - -Because of this, the default core tooling that comes with the Auto Correlation Recorder Plugin needed to be customized, allowing the users: - -* To stop using complex scripts at the start of recording to correlate those values -* To ease the customization of the Correlation Rules when using this protocol -Reduce the boilerplate to test each one of their scenarios. - -In the following sections, we will talk on how the Auto Correlation Recorder handles the customization of the default set of Correlation Extractors and Correlation Replacements (Correlation Components, for short) in order to tackle the complexity of correlating dynamic data inside a Siebel CRM environment. - -## Structure of the extension - -### Siebel Correlation Context -As explained before, the Siebel CRM handles dynamic values inside their pages, some of them could be stored in the HTML tags and displayed to the user, and others could appear using more complex logic like Start Arrays and so on. - -Regardless of the way that the server provides the data, the plugin needs to find a way to extract the information, parse and share it between the Correlation Components, during recording process, so they can be properly correlated. - -That's why the [SiebelContext](https://github.com/Blazemeter/CorrelationRecorder/blob/master/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelContext.java) is necessary. This class implements [CorrelationContext](https://github.com/Blazemeter/CorrelationRecorder/blob/master/src/main/java/com/blazemeter/jmeter/correlation/core/CorrelationContext.java)'s methods: - -1. `void update(SampleResult sampleResult)` -2. `void reset()` - -Which are important for correlating dynamic variables during the recording and testing phases. - -#### Update - -This handles the process of updating the values stored in the Siebel Correlation Context after a response is received. This might be important if your application needs to know, for example: - -1. The source of the action that took the user to a certain page -2. The number of times a certain type of users go to one specific page -3. Or any particularly sensitive information that is needed to be encrypted and sent to another part of the app - -This method not only contains the logic to extract, and share that information between the Correlation Components but also validates if any update is needed. - -::: warning -As mentioned in the [Special Considerations](the_flow_explanation.md#special-considerations), the availability of the updated information will differ from Correlation Replacements to Correlation Extractors. -::: - -### Reset -As mentioned in [proxy's methods](the_flow_explanation.md#context-reset), this method is an important part of the proxy's responsibilities, since it triggers the reset of the contexts for every Correlation Rule when a Recording starts. - -This method will contain all the logic required to clean the variables inside a Correlation Context, allowing to commence with a blank slate. - -## Siebel Extensions - -Now that the concepts of the Siebel CRM Environment are clear, and you have a glance at the importance of shared variables and how the whole process takes place, let’s talk about each one of the Extensions: let’s begin explaining rule parts. - -### Siebel Correlation Components - -#### Siebel Correlation Extractors - -One of the many structures that Siebel CRM uses to store information inside a page is called Star Array. It represents the values as Strings and their lengths in numbers, in decimal or hexadecimal, separated by “*” or “_” respectively. - -The logic to extract and store those values is handled by the [Siebel Row Correlation Extractor](https://github.com/Blazemeter/CorrelationRecorder/blob/master/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowCorrelationExtractor.java). - -When a Star Array is found, this Correlation Extractor creates a [JSR223 PostProcessor](https://jmeter.apache.org/usermanual/component_reference.html#JSR223_PostProcessor) in the `buildArrayParserPostProcessor` containing the logic for: - -The extraction of the appearances for the values found. -The storage of those variables, during the replay, to be used in future correlations. - -Is important to mention that this method also stores the values in the Siebel Correlation Context during recording. This is key for correlating its appearances in future requests. - -The logic is overwritten in the `process( … )` method, which is one of the APIs from its father, the [RegexCorrelationExtractor](https://github.com/Blazemeter/CorrelationRecorder/blob/master/src/main/java/com/blazemeter/jmeter/correlation/core/extractors/RegexCorrelationExtractor.java). - -For this Correlation Extension, a JSR223 PostProcessor component was used, because of the dynamic nature of the Siebel CRM’s Star Array. If the Array would always have the same number of values, or their length remained the same, other components like [Regular Expression Extractor](https://jmeter.apache.org/usermanual/component_reference.html#Regular_Expression_Extractor) could also do the trick. Use the Components that fit your particular scenario. - -#### Siebel Correlation Replacements - -All the values obtained from the Correlation Extractors and the ones in the updated Siebel Context will be replaced wherever they appear in requests by the Siebel Correlation Replacements. - -The Siebel CRM Extension contains following Replacements: - -* [Siebel Row Params Correlation Replacement](https://github.com/Blazemeter/CorrelationRecorder/blob/master/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowParamsCorrelationReplacement.java) -* [Siebel Row Id Correlation Replacement](https://github.com/Blazemeter/CorrelationRecorder/blob/master/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowIdCorrelationReplacement.java) -* [Siebel Counter Correlation Replacement](https://github.com/Blazemeter/CorrelationRecorder/blob/master/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelCounterCorrelationReplacement.java) - -As mentioned on the [Special Considerations](the_flow_explanation.md#special-considerations), a value that was updated on the Siebel Context from a response, won’t be available for the Correlation Replacements until the next request is processed. - -Once a value is matched by any Correlation Replacement, its appearance in the arguments will be replaced by the Reference Variable surrounded by ${} (eg: with a Reference Variable "RV", the replaced value will be ${RV}). - -One can implement the replacement functionality by replacing directly the value in an argument, or by using other JMeter's Components, like Pre/Post Processors for more complex logic. Taking for example the [Siebel Counter Correlation Replacement](https://github.com/Blazemeter/CorrelationRecorder/blob/master/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelCounterCorrelationReplacement.java): - -The method `public void process(HTTPSamplerBase sampler, List children, SampleResult result, JMeterVariables vars)` is overwritten from [RegexCorrelationReplacement](https://github.com/Blazemeter/CorrelationRecorder/blob/master/src/main/java/com/blazemeter/jmeter/correlation/core/replacements/RegexCorrelationReplacement.java) because the Siebel Counter Correlation Replacement doesn't take the "Counter" value from a response, previously extracted, instead, it calculates the difference of the SWEC variable from previous stored value, and updates it accordingly on a previously stored value of the SWEC variable. This logic allows doing the replacement even if the previous steps are disabled, since this logic is not bounded to fixed values that might be affected by their presence/absence. - -You can start using the Templates provided in the following section, refactor the ones that the Plugin has for Siebel or, make of your own. - -# Templates - -Remember to take a look at [Make your own Custom Extension](..) section for detailed info on the Correlation Components. The following examples can be used as a base: - -* [Basic Correlation Extractor](https://github.com/Blazemeter/CorrelationRecorder/blob/master/examples/CustomCorrelationExtractor.java) -* [Basic Correlation Context](https://github.com/Blazemeter/CorrelationRecorder/blob/master/examples/CustomContext.java) - - diff --git a/docs/src/guide/templates/readme.md b/docs/src/guide/templates/readme.md index 4c685ea..c73a503 100644 --- a/docs/src/guide/templates/readme.md +++ b/docs/src/guide/templates/readme.md @@ -1,3 +1,4 @@ +@@ -1,140 +0,0 @@ --- next: /guide/templates/create.md --- diff --git a/pom.xml b/pom.xml index 00d84e4..f3a26ed 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.blazemeter jmeter-bzm-correlation-recorder jar - 2.6 + 3.0 Auto Correlation Recorder Auto Correlation Recorder as JMeter Plugin https://github.com/Blazemeter/CorrelationRecorder @@ -269,8 +269,7 @@ maven-failsafe-plugin - - + **/RegressionIT.java diff --git a/releases.json b/releases.json index 5cf424d..1190e9c 100644 --- a/releases.json +++ b/releases.json @@ -26,5 +26,19 @@ "json>=20190722", "maven-artifact>=3.8.4" ] + }, + { + "version": "3.0", + "what_is_new": "Siebel components removed and many bug fixes!", + "dependencies": [ + "jackson-dataformat-xml>=2.10.3", + "jackson-annotations>=2.13.3", + "jackson-databind>=2.10.3", + "jackson-core>=2.13.3", + "jackson-module-jaxb-annotations>=2.10.3", + "jmeter-bzm-commons>=0.2.1", + "json>=20190722", + "maven-artifact>=3.8.4" + ] } ] diff --git a/src/main/java/com/blazemeter/jmeter/correlation/CorrelationProxyControl.java b/src/main/java/com/blazemeter/jmeter/correlation/CorrelationProxyControl.java index 8b4a21a..015071b 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/CorrelationProxyControl.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/CorrelationProxyControl.java @@ -167,6 +167,9 @@ private static Field getProxyControlField(String fieldName) { } private static String getTemplateDirectoryPath() { + // TODO: This not is the best way to manage this + // because the instance of the template directory can be used on different places + // review this in a universal way and not as CorrelationProxyControl property return JMeterUtils.getPropDefault(TEMPLATE_PATH, JMeterUtils.getJMeterHome()); } @@ -946,4 +949,8 @@ private boolean isRedirectDisablingConfigured() { String property = JMeterUtils.getProperty(CORRELATION_PROXY_REDIRECT_DISABLING_NAME); return "false".equals(property); } + + public void setTemplatesIgnoreErrors(boolean ignoreErrors) { + templateRepositoryConfig.setTemplatesIgnoreErrors(ignoreErrors); + } } diff --git a/src/main/java/com/blazemeter/jmeter/correlation/CorrelationProxyControlGui.java b/src/main/java/com/blazemeter/jmeter/correlation/CorrelationProxyControlGui.java index 3568529..fd0ef47 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/CorrelationProxyControlGui.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/CorrelationProxyControlGui.java @@ -58,9 +58,9 @@ public class CorrelationProxyControlGui extends ProxyControlGui } public CorrelationProxyControlGui() { - JTabbedPane siebelPane = findTabbedPane(); + JTabbedPane correlationPane = findTabbedPane(); rulesContainer = new RulesContainer(this, () -> modifyTestElement(model)); - Objects.requireNonNull(siebelPane).add("Correlation", rulesContainer); + Objects.requireNonNull(correlationPane).add("Correlation", rulesContainer); add(new BlazemeterLabsLogo(), BorderLayout.SOUTH); wizard = new CorrelationWizard(); @@ -83,6 +83,11 @@ public CorrelationProxyControlGui(CorrelationProxyControl model, RulesContainer this.rulesContainer = container; } + @Override + public void setTemplatesIgnoreErrors(boolean ignoreErrors) { + model.setTemplatesIgnoreErrors(ignoreErrors); + } + public void actionPerformed(ActionEvent action) { String command = action.getActionCommand(); if ("start".equals(command)) { diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/ClassFinderUtils.java b/src/main/java/com/blazemeter/jmeter/correlation/core/ClassFinderUtils.java new file mode 100644 index 0000000..4b61a0b --- /dev/null +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/ClassFinderUtils.java @@ -0,0 +1,66 @@ +package com.blazemeter.jmeter.correlation.core; + +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.apache.jmeter.util.JMeterUtils; +import org.apache.jorphan.reflect.ClassFinder; + +public class ClassFinderUtils { + + private static String inJUnitTest = null; + private static String basePath; + + static { + try { + // Quick and fast way to retrieve $JmeterHome/lib/ext + basePath = new File(URLDecoder.decode(ClassFinderUtils.class + .getProtectionDomain().getCodeSource() + .getLocation().getFile(), "UTF-8")) + .getAbsolutePath(); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + private static boolean isJUnitTest() { + if (inJUnitTest == null) { + for (StackTraceElement element : Thread.currentThread().getStackTrace()) { + if (element.getClassName().startsWith("org.junit.")) { + inJUnitTest = String.valueOf(true); + break; + } + } + if (inJUnitTest == null) { + inJUnitTest = String.valueOf(false); + } + } + return Boolean.parseBoolean(inJUnitTest); + } + + public static List findClassesThatExtendOnLibExt(Class refClass) throws IOException { + List findPaths = new ArrayList<>(); + + if (isJUnitTest()) { + // Ad the jar of ACR to the find path + findPaths.add(basePath); + } + // Add the /lib/ext and others "extension" folder to the search + findPaths.addAll(Arrays.asList(JMeterUtils.getSearchPaths())); + return findClassesThatExtend(refClass, findPaths); + } + + public static List findClassesThatExtend(Class refClass, List findPaths) + throws IOException { + String[] findPathsArr = new String[findPaths.size()]; + findPathsArr = findPaths.toArray(findPathsArr); + return ClassFinder.findClassesThatExtend( + findPathsArr, + new Class[]{refClass}); + } + +} diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/CorrelationEngine.java b/src/main/java/com/blazemeter/jmeter/correlation/core/CorrelationEngine.java index fbb688c..897f257 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/CorrelationEngine.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/CorrelationEngine.java @@ -3,7 +3,6 @@ import com.blazemeter.jmeter.correlation.gui.CorrelationComponentsRegistry; import com.helger.commons.annotation.VisibleForTesting; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.Optional; import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase; @@ -30,7 +29,7 @@ public CorrelationEngine() { } public synchronized void setCorrelationRules(List groups, - CorrelationComponentsRegistry registry) { + CorrelationComponentsRegistry registry) { rules.clear(); groups.stream() .filter(RulesGroup::isEnable) @@ -43,7 +42,7 @@ public synchronized void setCorrelationRules(List groups, } private void updateCorrelationContext(CorrelationRulePartTestElement rulePartTestElement, - CorrelationComponentsRegistry registry) { + CorrelationComponentsRegistry registry) { if (rulePartTestElement != null && rulePartTestElement.getSupportedContext() != null) { rulePartTestElement .setContext(getSupportedContext(rulePartTestElement.getSupportedContext(), registry)); @@ -72,8 +71,8 @@ public void reset() { } public synchronized void process(HTTPSamplerBase sampler, List children, - SampleResult result, - String responseFilter) { + SampleResult result, + String responseFilter) { if (result != null && !result.isSuccessful()) { String originalComment = sampler.getComment(); @@ -88,11 +87,17 @@ public synchronized void process(HTTPSamplerBase sampler, List chil JMeterContextService.getContext().setVariables(vars); // Using for instead of streams to avoid ConcurrentModificationException - Iterator replacementRulesIterator = rules.iterator(); - while (replacementRulesIterator.hasNext()) { - CorrelationRule rule = replacementRulesIterator.next(); + for (CorrelationRule rule : rules) { if (rule.isEnabled() && rule.getCorrelationReplacement() != null) { - rule.getCorrelationReplacement().process(sampler, children, result, vars); + try { + rule.getCorrelationReplacement().process(sampler, children, result, vars); + } catch (RuntimeException e) { + LOG.warn("Error applying the following replacement {} in the request {}", rule, + sampler.getName(), e); + LOG.debug("Request URL: {}\nRequest Headers:\n{}\nRequest Body:\n{}\n", + result.getUrlAsString(), result.getRequestHeaders(), + result.getDataEncodingWithDefault()); + } } } @@ -100,11 +105,18 @@ public synchronized void process(HTTPSamplerBase sampler, List chil if (isContentTypeAllowed(result, responseFilter)) { // Using for instead of streams to avoid ConcurrentModificationException - Iterator extractorRulesIterator = rules.iterator(); - while (extractorRulesIterator.hasNext()) { - CorrelationRule rule = extractorRulesIterator.next(); + for (CorrelationRule rule : rules) { if (rule.isEnabled() && rule.getCorrelationExtractor() != null) { - rule.getCorrelationExtractor().process(sampler, children, result, vars); + try { + rule.getCorrelationExtractor().process(sampler, children, result, vars); + + } catch (RuntimeException e) { + LOG.warn("Error applying the following extractor {} in the request {}", rule, + sampler.getName(), e); + LOG.debug("Response URL: {}\nResponse Headers:\n{}\nResponse Body:\n{}\n", + result.getUrlAsString(), result.getResponseHeaders(), + result.getResponseDataAsString()); + } } } } diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/analysis/AnalysisReporter.java b/src/main/java/com/blazemeter/jmeter/correlation/core/analysis/AnalysisReporter.java index 8cc87af..b87c426 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/analysis/AnalysisReporter.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/analysis/AnalysisReporter.java @@ -155,7 +155,7 @@ public static List generateCorrelationSuggestions() { for (ReportEntry entry : report.entries) { ExtractionSuggestion extraction = new ExtractionSuggestion(extractor, entry.getSampler()); extraction.setValue(entry.value); - extraction.setName(entry.variableName); + extraction.setName(paramName); extraction.setSource(entry.location); suggestion.setOriginalValue(entry.value); suggestion.addExtractionSuggestion(extraction); @@ -167,7 +167,7 @@ public static List generateCorrelationSuggestions() { = new ReplacementSuggestion(replacement, entry.getSampler()); replacementSuggestion.setValue(entry.value); - replacementSuggestion.setName(entry.variableName); + replacementSuggestion.setName(paramName); replacementSuggestion.setSource(entry.location); suggestion.setOriginalValue(entry.value); suggestion.addReplacementSuggestion(replacementSuggestion); diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/replacements/RegexCorrelationReplacement.java b/src/main/java/com/blazemeter/jmeter/correlation/core/replacements/RegexCorrelationReplacement.java index b99ae2d..edf981b 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/replacements/RegexCorrelationReplacement.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/replacements/RegexCorrelationReplacement.java @@ -113,18 +113,17 @@ public void updateTestElem(CorrelationRuleTestElement testElem) { * from the father's class {@link CorrelationReplacement}'s by applying short circuit evaluation * for better performance. * - * @param sampler recorded sampler containing the information of the request + * @param sampler recorded sampler containing the information of the request * @param children list of children added to the sampler (if the value matches the one associated - * to the Reference Variable, components could be added to help the correlation - * process) - * @param result containing information about the request and associated response from server - * @param vars stored variables shared between requests during recording + * to the Reference Variable, components could be added to help the correlation process) + * @param result containing information about the request and associated response from server + * @param vars stored variables shared between requests during recording * @see Short-circuit * evaluation */ @Override public void process(HTTPSamplerBase sampler, List children, SampleResult result, - JMeterVariables vars) { + JMeterVariables vars) { if (regex.isEmpty()) { return; } @@ -141,7 +140,7 @@ public void process(HTTPSamplerBase sampler, List children, SampleR * for
variableName=VAR_1
the replacement would be
${VAR_1}
* * @param input property's string to check and replace - * @param vars stored variables shared between request during the recording + * @param vars stored variables shared between request during the recording * @return the resultant input after been processed */ @Override @@ -158,18 +157,18 @@ public String replaceString(String input, JMeterVariables vars) { * Handles the method used to evaluate all the matched values by the Regular Expression. * *

Establish the condition that, if a value is matched with the Regular expression, it should - * also be equals to the value stored in the JMeterVariables with the variable name. Not all - * the values matched with the regex needs to be correlated. Overwrite it when the condition - * wants to be changed. + * also be equals to the value stored in the JMeterVariables with the variable name. Not all the + * values matched with the regex needs to be correlated. Overwrite it when the condition wants to + * be changed. * - * @param input property's string to check and replace - * @param regex regular expression used to do the evaluation + * @param input property's string to check and replace + * @param regex regular expression used to do the evaluation * @param variableName name of the variable name associated to this Correlation Replacement - * @param vars stored variables shared between requests during recording + * @param vars stored variables shared between requests during recording * @return the resultant input after been processed */ protected String replaceWithRegex(String input, String regex, - String variableName, JMeterVariables vars) + String variableName, JMeterVariables vars) throws MalformedPatternException { // Skip empty inputs if (input == null || input.isEmpty() || regex == null || regex.isEmpty() || variableName == null @@ -200,7 +199,7 @@ protected String replaceWithRegex(String input, String regex, int varNr = 0; while (varNr <= context.getVariableCount(variableName) && !hasMatch) { /* varNr could be 0 if non MultiValuedExtractor is used - so this code is to support when yo use MultiValuedReplacement with + so this code is to support when yo use MultiValuedReplacement with SingleValuedExtractor */ String varName = varNr == 0 ? variableName : variableName + "#" + varNr; String varMatchesCount = vars.get(varName + "_matchNr"); @@ -215,7 +214,8 @@ protected String replaceWithRegex(String input, String regex, currentVariableName = varName; } else if (ignoreValue && !replacementString.isEmpty()) { replaceExpression = replacementString; - /* This case does not care if the value is 'matching'. Because ignore value is + currentVariableName = super.variableName; + /* This case does not care if the value is 'matching'. Because ignore value is activated, therefore we need to step out of loop by setting hasMatch.*/ hasMatch = true; } else if (computeStringReplacement(varName) @@ -270,7 +270,7 @@ protected String replaceWithRegex(String input, String regex, } for (Pair valueReplaced : valuesReplaced) { - analysis(valueReplaced.getLeft(), valueReplaced.getRight()); + analysis(valueReplaced.getLeft(), this.variableName); } if (!AnalysisReporter.canCorrelate()) { return input; @@ -280,8 +280,8 @@ protected String replaceWithRegex(String input, String regex, } private StringBuilder replaceMatch(StringBuilder result, PatternMatcherInput patternMatcherInput, - MatchResult match, int beginOffset, char[] inputBuffer, - String expression) { + MatchResult match, int beginOffset, char[] inputBuffer, + String expression) { return result.append(inputBuffer, beginOffset, match.beginOffset(1) - beginOffset) .append(expression) .append(inputBuffer, match.endOffset(1), @@ -295,15 +295,15 @@ private StringBuilder replaceMatch(StringBuilder result, PatternMatcherInput pat *

Receives a matching condition that evaluates the String input. If the condition is met, * every appearance of the matched value will be replaced by ${expression}. * - * @param input property's string to check and replace - * @param regex regular expression used to do the evaluation - * @param expression expression that will replace the matched value(s) + * @param input property's string to check and replace + * @param regex regular expression used to do the evaluation + * @param expression expression that will replace the matched value(s) * @param matchCondition predicate that will serve for evaluating whether or not the matched value - * should be replaced by the expression + * should be replaced by the expression * @return the resultant input after been processed */ protected String replaceWithRegexAndPredicate(String input, String regex, String expression, - Predicate matchCondition) + Predicate matchCondition) throws MalformedPatternException { PatternMatcher matcher = JMeterUtils.getMatcher(); Pattern pattern = new Perl5Compiler().compile(regex); @@ -314,8 +314,14 @@ protected String replaceWithRegexAndPredicate(String input, String regex, String while (matcher.contains(patternMatcherInput, pattern)) { MatchResult match = matcher.getMatch(); if (matchCondition.test(match.group(1))) { - replaceMatch(result, patternMatcherInput, match, beginOffset, inputBuffer, - FUNCTION_REF_PREFIX + expression + FUNCTION_REF_SUFFIX); + analysis(match.group(1), this.variableName); + if (AnalysisReporter.canCorrelate()) { + replaceMatch(result, patternMatcherInput, match, beginOffset, inputBuffer, + FUNCTION_REF_PREFIX + expression + FUNCTION_REF_SUFFIX); + } else { + result.append(inputBuffer, beginOffset, + patternMatcherInput.getMatchEndOffset() - beginOffset); + } } else { result.append(inputBuffer, beginOffset, patternMatcherInput.getMatchEndOffset() - beginOffset); @@ -333,7 +339,7 @@ public void update(CorrelationRuleTestElement testElem) { ignoreValue = testElem.getPropertyAsBoolean(REPLACEMENT_IGNORE_VALUE_PROPERTY_NAME); } - private void analysis(String literalMatched, String currentVariableName) { + protected void analysis(String literalMatched, String currentVariableName) { AnalysisReporter.report(this, literalMatched, currentSampler, currentVariableName); } diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/templates/CorrelationTemplatesRegistryHandler.java b/src/main/java/com/blazemeter/jmeter/correlation/core/templates/CorrelationTemplatesRegistryHandler.java index 262bb7b..7cfc8f4 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/templates/CorrelationTemplatesRegistryHandler.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/templates/CorrelationTemplatesRegistryHandler.java @@ -15,6 +15,9 @@ public interface CorrelationTemplatesRegistryHandler { boolean isLocalTemplateVersionSaved(String templateId, String templateVersion); boolean isValidDependencyURL(String url, String name, String version); + + void setTemplatesIgnoreErrors(boolean ignoreErrors); + } diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/templates/CorrelationTemplatesRepositoriesConfiguration.java b/src/main/java/com/blazemeter/jmeter/correlation/core/templates/CorrelationTemplatesRepositoriesConfiguration.java index 62304be..3f68551 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/templates/CorrelationTemplatesRepositoriesConfiguration.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/templates/CorrelationTemplatesRepositoriesConfiguration.java @@ -120,4 +120,7 @@ public LocalConfiguration getLocalConfig() { return localConfig; } + public void setTemplatesIgnoreErrors(boolean ignoreErrors) { + localConfig.setTemplateLogParseError(!ignoreErrors); + } } diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/templates/CorrelationTemplatesRepositoriesRegistryHandler.java b/src/main/java/com/blazemeter/jmeter/correlation/core/templates/CorrelationTemplatesRepositoriesRegistryHandler.java index 969f450..2a12ef2 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/templates/CorrelationTemplatesRepositoriesRegistryHandler.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/templates/CorrelationTemplatesRepositoriesRegistryHandler.java @@ -51,4 +51,6 @@ void uninstallTemplate(String repositoryName, String id, String version) boolean refreshRepositories(String localConfigurationRoute, Consumer setProgressConsumer, Consumer setStatusConsumer); + + void setTemplatesIgnoreErrors(boolean b); } diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/templates/LocalConfiguration.java b/src/main/java/com/blazemeter/jmeter/correlation/core/templates/LocalConfiguration.java index 4932703..e46581f 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/templates/LocalConfiguration.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/templates/LocalConfiguration.java @@ -7,6 +7,7 @@ import static com.blazemeter.jmeter.correlation.core.templates.repository.RepositoryUtils.isURL; import static com.blazemeter.jmeter.correlation.core.templates.repository.RepositoryUtils.removeRepositoryNameFromFile; +import com.blazemeter.jmeter.correlation.core.ClassFinderUtils; import com.blazemeter.jmeter.correlation.core.templates.repository.PluggableRepository; import com.blazemeter.jmeter.correlation.core.templates.repository.RepositoryManager; import com.blazemeter.jmeter.correlation.core.templates.repository.RepositoryUtils; @@ -33,7 +34,6 @@ import java.lang.reflect.Modifier; import java.net.HttpURLConnection; import java.net.URL; -import java.net.URLDecoder; import java.nio.file.Paths; import java.util.AbstractMap; import java.util.ArrayList; @@ -53,16 +53,15 @@ import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FileUtils; import org.apache.jmeter.util.JMeterUtils; -import org.apache.jorphan.reflect.ClassFinder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LocalConfiguration { + public static final String CORRELATIONS_TEMPLATE_INSTALLATION_FOLDER = "/correlation-templates/"; public static final String INSTALL = "install"; public static final String UNINSTALL = "uninstall"; private static final String LOCAL_CONFIGURATION_FILE_NAME = "repositories"; - private static final String SIEBEL_CORRELATION_TEMPLATE = "siebel-1.0-template.json"; private static final String JAR_FILE_SUFFIX = ".jar"; private static final String TEMPLATES_FOLDER_PATH = "/templates/"; private static final String LOCAL_REPOSITORIES_MANAGERS_FILE_NAME = "managers"; @@ -85,6 +84,7 @@ public class LocalConfiguration { private transient ObjectWriter writer; private List repositories = new ArrayList<>(); + private boolean templateLogParseError; //Constructor added to avoid issues with the serialization public LocalConfiguration() { @@ -114,7 +114,7 @@ public LocalConfiguration(String jmeterRootFolder) { public static void installDefaultFiles(String rootFolder) { LOG.info("Installing default files"); installCorrelationRecorderTemplateTestPlan(rootFolder); - installSiebelCorrelationTemplate(rootFolder); + SiebelTemplateRemoval.delete(rootFolder); addBlazeMeterProperties(rootFolder); } @@ -130,16 +130,6 @@ private static void installCorrelationRecorderTemplateTestPlan(String rootFolder LOG.info("bzm - Correlation Recorder Test Plan Template installed"); } - private static void installSiebelCorrelationTemplate(String rootFolder) { - TestPlanTemplatesRepository templateRepository = new TestPlanTemplatesRepository(Paths - .get(rootFolder, - LocalConfiguration.CORRELATIONS_TEMPLATE_INSTALLATION_FOLDER) - .toAbsolutePath().toString() + File.separator); - templateRepository.addCorrelationTemplate(SIEBEL_CORRELATION_TEMPLATE, - LocalConfiguration.CORRELATIONS_TEMPLATE_INSTALLATION_FOLDER); - LOG.info("Siebel Correlation's Template installed"); - } - private static void addBlazeMeterProperties(String rootFolder) { File propertiesFile = new File(Paths.get(rootFolder, "correlation.properties") .toAbsolutePath().toString()); @@ -213,30 +203,8 @@ public RepositoryManager findAndInstanceRepositoryClass(String strClassName) return null; } - private boolean isJUnitTest() { - for (StackTraceElement element : Thread.currentThread().getStackTrace()) { - if (element.getClassName().startsWith("org.junit.")) { - return true; - } - } - return false; - } - private List findPluggableRepositoriesClasses() throws IOException { - List findPaths = new ArrayList<>(); - findPaths.addAll(Arrays.asList(JMeterUtils.getSearchPaths())); - // WA for no correct jmeter env initialization like junit tests - if (isJUnitTest()) { - findPaths.add(new File(URLDecoder.decode(this.getClass() - .getProtectionDomain().getCodeSource() - .getLocation().getFile(), "UTF-8")) - .getAbsolutePath()); - } - String[] findPathsArr = new String[findPaths.size()]; - findPathsArr = findPaths.toArray(findPathsArr); - return ClassFinder.findClassesThatExtend( - findPathsArr, - new Class[] {PluggableRepository.class}); + return ClassFinderUtils.findClassesThatExtendOnLibExt(PluggableRepository.class); } private void registerPreInstalledRepositories() throws IOException { @@ -370,6 +338,7 @@ private void setJsonConfigurations() { mapper.setFilterProvider(filters); mapper.writerWithDefaultPrettyPrinter().with(filters); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + setTemplateLogParseError(true); writer = mapper.writerWithDefaultPrettyPrinter().with(filters); } @@ -382,20 +351,57 @@ private void loadLocalConfigurationFile() { LOG.error("There was an error trying to read the local configuration file. ", e); } } else { - LOG.info("No local configuration file was found."); + LOG.warn("No local configuration file was found."); } } public T readValue(File source, Class valueType) throws IOException { - return mapper.readValue(source, valueType); + // By default, set to fail on invalid subtype + mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, true); + try { + return mapper.readValue(source, valueType); + } catch (Exception e) { + // On case on error, try to load the template ignoring subtype errors + if (templateLogParseError) { + LOG.warn("Error loading template file", e); + } + // Set to don't fail on invalid subtype + mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false); + try { + return mapper.readValue(source, valueType); + } catch (Exception ex) { + // On this case, it's a critical error + LOG.error("Error loading template file", ex); + throw new IOException(ex); // Wrap any type of exception into an IOException + } + } } public T readValue(String source, Class valueType) throws IOException { - return mapper.readValue(source, valueType); + // By default, set to fail on invalid subtype + mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, true); + try { + return mapper.readValue(source, valueType); + } catch (Exception e) { + // On case on error, try to load the template ignoring subtype errors + if (templateLogParseError) { + LOG.warn("Error loading template file", e); + } + // Set to don't fail on invalid subtype + mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false); + try { + return mapper.readValue(source, valueType); + } catch (Exception ex) { + // On this case, it's a critical error + LOG.error("Error loading template file", ex); + throw new IOException(ex); // Wrap any type of exception into an IOException + } + } } public Template readTemplateFromPath(String templatePath) throws IOException { - return readValue(new File(templatePath), Template.class); + Template tmpl = readValue(new File(templatePath), Template.class); + return tmpl; } public Template readTemplateFromString(String templateContent) throws IOException { @@ -409,8 +415,29 @@ public TemplateProperties readTemplatePropertiesFromPath(String templateProperti public Map readTemplatesReferences(File source) throws IOException { - return mapper.readValue(source, new TypeReference>() { - }); + // By default, set to fail on invalid subtype + mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, true); + try { + return mapper.readValue(source, + new TypeReference>() { + }); + } catch (Exception e) { + // On case on error, try to load the template ignoring subtype errors + if (templateLogParseError) { + LOG.warn("Error loading template file", e); + } + // Set to don't fail on invalid subtype + mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false); + try { + return mapper.readValue(source, + new TypeReference>() { + }); + } catch (Exception ex) { + // On this case, it's a critical error + LOG.error("Error loading template file", ex); + throw new IOException(ex); // Wrap any type of exception into an IOException + } + } } public void writeValue(File resultFile, Object value) throws IOException { @@ -469,7 +496,7 @@ private Optional findRepositoryById } public void manageTemplate(String action, String repositoryName, String templateId, - String templateVersion) throws ConfigurationException { + String templateVersion) throws ConfigurationException { Optional repository = findRepositoryById( repositoryName); @@ -548,7 +575,7 @@ public List findConflictingDependencies(List fileName.toLowerCase().contains(dependency.getName().toLowerCase()) || fileName.contains(possibleJarFileName.replace(JAR_FILE_SUFFIX, ""))); @@ -579,6 +606,10 @@ public List findConflictingDependencies(List dependencies) { public void downloadDependencies(List dependencies) throws IOException { - String dependenciesFolderPath = rootFolder + "/lib/"; + Map dependenciesJars = getDependenciesJars(dependencies); + for (Map.Entry dependencyJar : dependenciesJars.entrySet()) { + saveFileFromURL(dependencyJar.getKey(), dependencyJar.getValue()); + } + } + + public Map getDependenciesJars(List dependencies) { + String dependenciesFolderPath = getDependenciesFolderPath(); + Map dependenciesJars = new HashMap<>(); for (CorrelationTemplateDependency dependency : dependencies) { - saveFileFromURL(dependency.getUrl(), - dependenciesFolderPath + dependency.getName() + "-" + dependency.getVersion() - + JAR_FILE_SUFFIX); + String jarPath = + dependenciesFolderPath + dependency.getName() + "-" + dependency.getVersion() + + JAR_FILE_SUFFIX; + dependenciesJars.put(dependency.getUrl(), jarPath); } + return dependenciesJars; } private void saveFileFromURL(String fileURL, String fileFullPath) throws IOException { @@ -657,7 +698,7 @@ public List checkRepositoryURL(String repositoryId, String repositoryURL } public RepositoryManager getRepositoryManagerFromFolderOrUrl(String repositoryId, - String repositoryURL) { + String repositoryURL) { RepositoryManager manager; if (isURL(repositoryURL)) { manager = new RemoteUrlRepository(); @@ -713,7 +754,7 @@ public File getRepositoryFile(String repositoryName) { } public String getTemplateFilePath(String repositoryName, String templateId, - String templateVersion) { + String templateVersion) { return Paths.get(getRepositoryFolderPath(repositoryName), getTemplateFilename(templateId, templateVersion)) .toAbsolutePath() @@ -871,9 +912,13 @@ public void uninstallTemplate(String repositoryName, String templateId, String t manageTemplate(LocalConfiguration.UNINSTALL, repositoryName, templateId, templateVersion); } + void setTemplateLogParseError(boolean fail) { + templateLogParseError = fail; + } + public boolean refreshRepositories(String configurationRoute, - Consumer setProgressConsumer, - Consumer setStatusConsumer) { + Consumer setProgressConsumer, + Consumer setStatusConsumer) { int progress = 0; boolean isUpToDate = true; setProgressConsumer.accept(progress); diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/templates/SiebelTemplateRemoval.java b/src/main/java/com/blazemeter/jmeter/correlation/core/templates/SiebelTemplateRemoval.java new file mode 100644 index 0000000..8fa79d4 --- /dev/null +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/templates/SiebelTemplateRemoval.java @@ -0,0 +1,87 @@ +package com.blazemeter.jmeter.correlation.core.templates; + +import com.blazemeter.jmeter.correlation.gui.TestPlanTemplatesRepository; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import org.apache.commons.codec.digest.DigestUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class was added in version 3.0. The sole propose of this class is to encapsulate the logic + * that deletes the siebel template and testplan from an upgraded version of ACR. Since the + * creation, the deprecation was added in order to warn deletion once considered everyone is + * migrated to newer versions of ACR. It's possible to see code duplication or raw manipulation of + * files. The idea is to add this removal modifying the less. Encapsulating everything here without + * dependency, makes the deletion of this class easier in the future. + */ +@Deprecated +public class SiebelTemplateRemoval { + + private static final String SIEBEL_TESTPLAN_NAME = "siebel-template.jmx"; + private static final String SIEBEL_TEMPLATE_NAME = "siebel-1.0-template.json"; + private static final String CORRELATION_TEMPLATES_PATH = "/correlation-templates/"; + private static final String SIEBEL_TEMPLATE_MD5_VALUE = "e1de737f84e2081b26b51d04acecd71a"; + private static final Logger LOG = LoggerFactory.getLogger(SiebelTemplateRemoval.class); + + public static void delete(String rootFolder) { + deleteTestPlan(rootFolder); + try { + deleteTemplate(rootFolder); + } catch (IOException e) { + LOG.debug("Failed to delete old siebel template", e); + } + } + + private static void deleteTestPlan(String rootFolder) { + TestPlanTemplatesRepository templateRepository = new TestPlanTemplatesRepository( + Paths.get(rootFolder, "/templates/").toAbsolutePath() + + File.separator); + templateRepository.removeDeprecatedTemplate(SIEBEL_TESTPLAN_NAME); + } + + private static void deleteTemplate(String rootFolder) throws IOException { + String acrTemplatePath = rootFolder + CORRELATION_TEMPLATES_PATH; + if (!isSiebelTemplateAdded(acrTemplatePath)) { + return; + } + deleteJsonTemplate(acrTemplatePath); + removeSiebelTemplateFromLocalRepository(acrTemplatePath); + } + + private static boolean isSiebelTemplateAdded(String acrTemplatePath) + throws IOException { + File siebelTemplate = new File( + Paths.get(acrTemplatePath, SIEBEL_TEMPLATE_NAME).toAbsolutePath().toString()); + return siebelTemplate.exists() && DigestUtils + .md5Hex(Files.newInputStream(Paths.get(siebelTemplate.getPath()))) + .equals(SIEBEL_TEMPLATE_MD5_VALUE); + } + + private static void removeSiebelTemplateFromLocalRepository(String acrTemplatePath) + throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + File localRepository = new File(acrTemplatePath + "local-repository.json"); + JsonNode rootNode = objectMapper.readTree(localRepository); + ObjectNode rootObjectNode = (ObjectNode) rootNode; + rootObjectNode.remove("siebel"); + objectMapper.writerWithDefaultPrettyPrinter().writeValue(localRepository, rootNode); + } + + private static void deleteJsonTemplate(String acrTemplatePath) { + File siebelTemplate = new File(acrTemplatePath, SIEBEL_TEMPLATE_NAME); + if (siebelTemplate.delete()) { + LOG.info("Siebel template was deleted successfully"); + return; + } + LOG.error("Siebel template couldn't be deleted. Which could lead into several issues on newer " + + "versions of ACR. Change correlation-templates folder and files inside permissions. " + + "Restart Jmeter"); + } + +} diff --git a/src/main/java/com/blazemeter/jmeter/correlation/core/templates/repository/pluggable/LocalRepository.java b/src/main/java/com/blazemeter/jmeter/correlation/core/templates/repository/pluggable/LocalRepository.java index bea9953..d444462 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/core/templates/repository/pluggable/LocalRepository.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/core/templates/repository/pluggable/LocalRepository.java @@ -2,11 +2,9 @@ import static com.blazemeter.jmeter.correlation.core.templates.RepositoryGeneralConst.LOCAL_REPOSITORY_NAME; -import com.blazemeter.jmeter.correlation.core.templates.ConfigurationException; import com.blazemeter.jmeter.correlation.core.templates.CorrelationTemplateVersions; import com.blazemeter.jmeter.correlation.core.templates.CorrelationTemplatesRepository; import com.blazemeter.jmeter.correlation.core.templates.LocalConfiguration; -import com.blazemeter.jmeter.correlation.core.templates.RepositoryGeneralConst; import com.blazemeter.jmeter.correlation.core.templates.Template; import com.blazemeter.jmeter.correlation.core.templates.TemplateVersion; import com.blazemeter.jmeter.correlation.core.templates.repository.FileRepository; @@ -21,8 +19,7 @@ import org.slf4j.LoggerFactory; public class LocalRepository extends FileRepository implements PluggableRepository { - private static final String SIEBEL_TEMPLATE_NAME = "siebel"; - private static final String DEFAULT_SIEBEL_TEMPLATE_VERSION = "1.0"; + private static final Logger LOG = LoggerFactory.getLogger(LocalConfiguration.class); @Override @@ -41,20 +38,8 @@ public void setup() { try { if (!localRepositoryFile.exists() && localRepositoryFile.createNewFile()) { + initEmptyLocalRepository(localRepositoryFile); LOG.info("Created the local repository file {}", localRepositoryFile); - this.getConfig() - .writeValue(localRepositoryFile, new HashMap() { - { - CorrelationTemplateVersions value = - new CorrelationTemplateVersions(DEFAULT_SIEBEL_TEMPLATE_VERSION); - value.setRepositoryDisplayName(LOCAL_REPOSITORY_NAME); - put(SIEBEL_TEMPLATE_NAME, value); - } - }); - LOG.info("Saved local repository file"); - if (!isSiebelInstalled()) { - installSiebel(); - } } } catch (IOException e) { throw new RuntimeException(e); @@ -62,6 +47,11 @@ public void setup() { } + private void initEmptyLocalRepository(File localRepositoryFile) throws IOException { + Map empty = new HashMap<>(); + this.getConfig().writeValue(localRepositoryFile, empty); + } + @Override public boolean autoLoad() { return true; @@ -112,20 +102,4 @@ public void setDisplayName(String displayName) { // Left empty intentionally for development purposes } - private boolean isSiebelInstalled() { - return this.getConfig().isInstalled(getName(), SIEBEL_TEMPLATE_NAME); - } - - private void installSiebel() { - if (!isSiebelInstalled()) { - try { - this.getConfig().installTemplate(RepositoryGeneralConst.LOCAL_REPOSITORY_NAME, - SIEBEL_TEMPLATE_NAME, DEFAULT_SIEBEL_TEMPLATE_VERSION); - } catch (ConfigurationException e) { - LOG.error("Error installing Siebel template", e); - } - } - this.getConfig().saveLocalConfiguration(); - } - } diff --git a/src/main/java/com/blazemeter/jmeter/correlation/gui/CorrelationComponentsRegistry.java b/src/main/java/com/blazemeter/jmeter/correlation/gui/CorrelationComponentsRegistry.java index 51e795c..f4abd9f 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/gui/CorrelationComponentsRegistry.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/gui/CorrelationComponentsRegistry.java @@ -2,18 +2,16 @@ import static java.lang.Class.forName; +import com.blazemeter.jmeter.correlation.core.ClassFinderUtils; import com.blazemeter.jmeter.correlation.core.CorrelationContext; import com.blazemeter.jmeter.correlation.core.CorrelationRulePartTestElement; import com.blazemeter.jmeter.correlation.core.DescriptionContent; import com.blazemeter.jmeter.correlation.core.InvalidRulePartElementException; import com.blazemeter.jmeter.correlation.core.ParameterDefinition; import com.blazemeter.jmeter.correlation.core.extractors.CorrelationExtractor; -import com.blazemeter.jmeter.correlation.core.extractors.JsonCorrelationExtractor; -import com.blazemeter.jmeter.correlation.core.extractors.RegexCorrelationExtractor; +import com.blazemeter.jmeter.correlation.core.extractors.XmlCorrelationExtractor; import com.blazemeter.jmeter.correlation.core.replacements.CorrelationReplacement; import com.blazemeter.jmeter.correlation.core.replacements.FunctionCorrelationReplacement; -import com.blazemeter.jmeter.correlation.core.replacements.JsonCorrelationReplacement; -import com.blazemeter.jmeter.correlation.core.replacements.RegexCorrelationReplacement; import com.google.common.annotations.VisibleForTesting; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -22,10 +20,10 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; -import org.apache.jmeter.util.JMeterUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,28 +42,77 @@ public class CorrelationComponentsRegistry { private static CorrelationComponentsRegistry instance; private final Set> customExtractors = new HashSet<>(); private final Set> customReplacements = new HashSet<>(); - private final List> defaultExtractors = - Arrays.asList(RegexCorrelationExtractor.class, JsonCorrelationExtractor.class); - private final List> defaultReplacements = - Arrays.asList(RegexCorrelationReplacement.class, - JsonCorrelationReplacement.class); - private final List deprecatedComponents = Collections.singletonList( - FunctionCorrelationReplacement.class.getCanonicalName()); + private List> defaultExtractors = Arrays.asList(); + private List> defaultReplacements = Arrays.asList(); + private final List deprecatedComponents = + Arrays.asList(FunctionCorrelationReplacement.class.getCanonicalName(), + XmlCorrelationExtractor.class.getCanonicalName()); // TODO: XML Temporarily excluded @VisibleForTesting protected Function, List> classFinderFunction = (clazz) -> { try { - return JMeterUtils.findClassesThatExtend(clazz); + return ClassFinderUtils.findClassesThatExtendOnLibExt(clazz); } catch (IOException e) { LOG.error("There was an error trying to search for the classes that extends {}", clazz, e); return Collections.emptyList(); } }; + private List> filteredAvailableExtensions; + private CorrelationComponentsRegistry() { + loadClasses(); + } + + private Map>> mapCoreComponents(Class refClass) { + try { + List> components = stringClassToClass( + ClassFinderUtils.findClassesThatExtendOnLibExt(refClass)); + String core = "com.blazemeter.jmeter.correlation.core"; + return components.stream() + .collect(Collectors.partitioningBy( + s -> s.getCanonicalName().startsWith(core) && + !deprecatedComponents.contains(s.getCanonicalName()))); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void loadClasses() { + + // Only once + if (defaultExtractors.size() != 0) { + return; + } + + // Load the classes related with Extractor and Replacement available in the class loader + Map>> availableExtractorsMap = + mapCoreComponents(CorrelationExtractor.class); + defaultExtractors = availableExtractorsMap.get(true); + customExtractors.clear(); + customExtractors.addAll(availableExtractorsMap.get(false)); + + Map>> availableReplacementsMap = + mapCoreComponents(CorrelationReplacement.class); + defaultReplacements = availableReplacementsMap.get(true); + customReplacements.clear(); + customReplacements.addAll(availableReplacementsMap.get(false)); } + private List> stringClassToClass(List classNames) { + return classNames.stream() + .map(className -> { + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + return null; // Temporary null + } + }) + .filter(clazz -> clazz != null) // Null filtered + .collect(Collectors.toList()); + } + public static synchronized CorrelationComponentsRegistry getInstance() { if (instance == null) { instance = new CorrelationComponentsRegistry(); @@ -73,6 +120,11 @@ public static synchronized CorrelationComponentsRegistry getInstance() { return instance; } + public static synchronized CorrelationComponentsRegistry getNewInstance() { + instance = new CorrelationComponentsRegistry(); + return instance; + } + public Set> getAllCustomExtensionClasses() { Set> extensions = new HashSet<>(customExtractors); extensions.addAll(customReplacements); @@ -133,7 +185,7 @@ private Object getClassInstance(Class correlationPartClass) { return correlationPartClass.getConstructor().newInstance(); } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { - LOG.warn("Couldn't build the correlation type '{}'.", correlationPartClass, e); + LOG.error("Couldn't build the correlation type '{}'.", correlationPartClass, e); return null; } } @@ -143,13 +195,13 @@ public CorrelationContext getContext(Class context return contextType.getConstructor().newInstance(); } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { - LOG.warn("Couldn't build the correlation type='{}'.", contextType, e); + LOG.error("Couldn't build the correlation type='{}'.", contextType, e); return null; } } public String updateActiveComponents(String components, - List missingSelectedExtensions) { + List missingSelectedExtensions) { reset(); //Deprecated components shouldn't appear nor in ComboBoxes nor in ComponentsContainer List validComponents = removeDeprecatedComponents(components); @@ -292,18 +344,22 @@ private boolean isDeprecated(Class ite //Returns Extensions that are Custom, not installed and not deprecated public List> getAvailableExtensions() { - List availableExtensions = classFinderFunction - .apply(CorrelationRulePartTestElement.class); - List> filteredAvailable = availableExtensions.stream() - .filter(ext -> !deprecatedComponents.contains(ext) - && defaultExtractors.stream().noneMatch(e -> e.getCanonicalName().equals(ext)) - && defaultReplacements.stream().noneMatch(r -> r.getCanonicalName().equals(ext)) - && customExtractors.stream().noneMatch(e -> e.getCanonicalName().equals(ext)) - && customReplacements.stream().noneMatch(r -> r.getCanonicalName().equals(ext))) - .map(this::buildRulePartFromClassName) - .collect(Collectors.toList()); + // Find all classes related to CorrelationRulePartTestElement only once + if (filteredAvailableExtensions == null) { + List allAvailableExtensions = classFinderFunction + .apply(CorrelationRulePartTestElement.class); + + filteredAvailableExtensions = allAvailableExtensions.stream() + .filter(ext -> !deprecatedComponents.contains(ext) + && defaultExtractors.stream().noneMatch(e -> e.getCanonicalName().equals(ext)) + && defaultReplacements.stream().noneMatch(r -> r.getCanonicalName().equals(ext)) + && customExtractors.stream().noneMatch(e -> e.getCanonicalName().equals(ext)) + && customReplacements.stream().noneMatch(r -> r.getCanonicalName().equals(ext))) + .map(this::buildRulePartFromClassName) + .collect(Collectors.toList()); + } - return filteredAvailable; + return filteredAvailableExtensions; } public String getCorrelationComponents() { diff --git a/src/main/java/com/blazemeter/jmeter/correlation/gui/CustomExtensionsDialog.java b/src/main/java/com/blazemeter/jmeter/correlation/gui/CustomExtensionsDialog.java index 927ee4c..b2b0b32 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/gui/CustomExtensionsDialog.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/gui/CustomExtensionsDialog.java @@ -235,6 +235,7 @@ public void buildExtensions(Set> availableModel = new DefaultListModel<>(); registry.getAvailableExtensions().stream() + .filter(c -> c != null) // Exclude components with errors .filter(c -> type == RulePartType.fromComponent(c)) .forEach(e -> availableModel.addElement(new ExtensionItem(e, false))); diff --git a/src/main/java/com/blazemeter/jmeter/correlation/gui/RulesContainer.java b/src/main/java/com/blazemeter/jmeter/correlation/gui/RulesContainer.java index 14d962b..6427715 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/gui/RulesContainer.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/gui/RulesContainer.java @@ -5,8 +5,6 @@ import com.blazemeter.jmeter.correlation.core.CorrelationRule; import com.blazemeter.jmeter.correlation.core.RulesGroup; import com.blazemeter.jmeter.correlation.core.automatic.CorrelationHistory; -import com.blazemeter.jmeter.correlation.core.extractors.RegexCorrelationExtractor; -import com.blazemeter.jmeter.correlation.core.replacements.RegexCorrelationReplacement; import com.blazemeter.jmeter.correlation.core.templates.CorrelationTemplatesRegistryHandler; import com.blazemeter.jmeter.correlation.core.templates.CorrelationTemplatesRepositoriesRegistryHandler; import com.blazemeter.jmeter.correlation.core.templates.Template; @@ -23,18 +21,15 @@ import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.util.ArrayList; -import java.util.LinkedHashSet; import java.util.List; import java.util.function.Consumer; import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JPanel; import javax.swing.SwingWorker; -import org.apache.http.entity.ContentType; import org.apache.jmeter.util.JMeterUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,7 +60,6 @@ public class RulesContainer extends JPanel implements ActionListener { private TemplatesManagerFrame loadFrame; private CorrelationHistoryFrame historyFrame; private CorrelationHistory history; - private boolean isSiebelTestPlan; private JCheckBox enableCorrelation; private Runnable onWizardDisplay; private Runnable onSuggestionsDisplay; @@ -263,22 +257,14 @@ public List getRulesGroups() { public void configure(CorrelationProxyControl model) { clearLocalConfiguration(); updateCustomExtensions(buildCorrelationComponents(model)); - setResponseFilter(isSiebelTestPlan ? ContentType.TEXT_HTML.getMimeType() - : model.getResponseFilter()); + setResponseFilter(model.getResponseFilter()); if (model.getGroups() != null && !model.getGroups().isEmpty()) { groupsContainer.configure(model.getGroups()); } } private String buildCorrelationComponents(CorrelationProxyControl correlationProxyControl) { - String correlationComponents = correlationProxyControl.getCorrelationComponents(); - if (correlationComponents.isEmpty()) { - //if empty could mean that comes from CRM-Siebel. - correlationComponents = getSiebelCRMComponents( - correlationProxyControl.getCorrelationRulesTestElement()); - isSiebelTestPlan = !correlationComponents.isEmpty(); - } - return correlationComponents; + return correlationProxyControl.getCorrelationComponents(); } private BufferedImage getSnapshotBufferedImage() { @@ -306,19 +292,6 @@ private void updateCustomExtensions(String correlationComponents) { .updateActiveComponents(correlationComponents, new ArrayList<>()); } - private String getSiebelCRMComponents(CorrelationRulesTestElement correlationRulesTestElement) { - if (correlationRulesTestElement == null) { - return ""; - } - return correlationRulesTestElement.getRules().stream() - .flatMap(r -> Stream.>of(r.getExtractorClass(), r.getReplacementClass())) - .filter(c -> c != null && c != RegexCorrelationExtractor.class - && c != RegexCorrelationReplacement.class) - .collect(Collectors.toCollection(LinkedHashSet::new)).stream() - .map(Class::getCanonicalName) - .collect(Collectors.joining(",\n")); - } - public String getResponseFilter() { return responseFilterPanel.getResponseFilter(); } @@ -336,7 +309,6 @@ public String toString() { return "RulesContainer{" + "loadedTemplates=" + loadedTemplates + ", groupsContainer=" + groupsContainer + - ", isSiebelTestPlan=" + isSiebelTestPlan + '}'; } diff --git a/src/main/java/com/blazemeter/jmeter/correlation/gui/TestPlanTemplatesRepository.java b/src/main/java/com/blazemeter/jmeter/correlation/gui/TestPlanTemplatesRepository.java index 9025632..6e8f3cd 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/gui/TestPlanTemplatesRepository.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/gui/TestPlanTemplatesRepository.java @@ -71,7 +71,7 @@ private void copyTemplateFile(String fileName, String sourcePath) { } } - private void removeDeprecatedTemplate( + public void removeDeprecatedTemplate( @SuppressWarnings("SameParameterValue") String templateName) { File oldTemplate = new File(Paths.get(rootFolder, templateName).toAbsolutePath().toString()); diff --git a/src/main/java/com/blazemeter/jmeter/correlation/gui/templates/RepositoriesConfigFrame.java b/src/main/java/com/blazemeter/jmeter/correlation/gui/templates/RepositoriesConfigFrame.java index dcfed3d..443cefd 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/gui/templates/RepositoriesConfigFrame.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/gui/templates/RepositoriesConfigFrame.java @@ -180,6 +180,8 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole @VisibleForTesting void updateRepositoriesTable() { + repositoryHandler.setTemplatesIgnoreErrors(true); + GuiUtils.cancelEditing(repositoryTable); List listCorrelationRepositories = repositoryHandler .getCorrelationRepositories(); @@ -202,6 +204,8 @@ void updateRepositoriesTable() { }); repositoryTable.clearSelection(); repositoryTable.repaint(); + + repositoryHandler.setTemplatesIgnoreErrors(false); } private JPanel makeButtonPanel() { diff --git a/src/main/java/com/blazemeter/jmeter/correlation/gui/templates/TemplatesManagerFrame.java b/src/main/java/com/blazemeter/jmeter/correlation/gui/templates/TemplatesManagerFrame.java index b1395fd..03c1917 100644 --- a/src/main/java/com/blazemeter/jmeter/correlation/gui/templates/TemplatesManagerFrame.java +++ b/src/main/java/com/blazemeter/jmeter/correlation/gui/templates/TemplatesManagerFrame.java @@ -312,6 +312,8 @@ private void toggleVersionButtons(boolean installed) { private void updateTemplatesList(boolean useLocal) { Map rawTemplates = new HashMap<>(); + repositoriesRegistryHandler.setTemplatesIgnoreErrors(true); + List repositories = repositoriesRegistryHandler.getCorrelationRepositories(); @@ -343,6 +345,8 @@ private void updateTemplatesList(boolean useLocal) { installedTemplates.setModel(installedModel); availableTemplates.setModel(availableModel); + + repositoriesRegistryHandler.setTemplatesIgnoreErrors(false); } private DefaultListModel unifyTemplates( @@ -584,6 +588,8 @@ public void showFrame() { * after closing this frame, and open it again. * */ + repositoriesRegistryHandler.setTemplatesIgnoreErrors(true); + Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); // calculate the new location of the window int x = (dim.width - this.getSize().width) / 2; @@ -597,6 +603,8 @@ public void showFrame() { } else { updateTemplatesList(true); } + + repositoriesRegistryHandler.setTemplatesIgnoreErrors(false); } private boolean needsRefresh(DefaultListModel listModel) { diff --git a/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelArrayFunction.java b/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelArrayFunction.java deleted file mode 100644 index 191b839..0000000 --- a/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelArrayFunction.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.blazemeter.jmeter.correlation.siebel; - -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; -import org.apache.jmeter.engine.util.CompoundVariable; -import org.apache.jmeter.functions.AbstractFunction; -import org.apache.jmeter.functions.InvalidVariableException; -import org.apache.jmeter.samplers.SampleResult; -import org.apache.jmeter.samplers.Sampler; -import org.apache.jmeter.threads.JMeterVariables; -import org.apache.jmeter.util.JMeterUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SiebelArrayFunction extends AbstractFunction { - - private static final Logger LOG = LoggerFactory.getLogger(SiebelArrayFunction.class); - private static final List DESC = new LinkedList<>(); - private static final String KEY = "__splitStarArray"; // $NON-NLS-1$ - - static { - DESC.add(JMeterUtils.getResString("split_function_string")); //$NON-NLS-1$ - DESC.add(JMeterUtils.getResString("function_name_param")); //$NON-NLS-1$ - } - - private Object[] values; - - public static void split(String stringToSplit, String varNamePrefix, JMeterVariables vars) { - int i = 0; - int variableIndex = 0; - try { - while (i < stringToSplit.length()) { - variableIndex++; - int indexOfStar = stringToSplit.indexOf("*", i); - int indexOfStarPlusOne = indexOfStar + 1; - int length = Integer.parseInt(stringToSplit.substring(i, indexOfStar)); - String varName = varNamePrefix + "_" + variableIndex; - String varValue = stringToSplit.substring(indexOfStarPlusOne, - indexOfStarPlusOne + length); - vars.put(varName, varValue); - if (LOG.isDebugEnabled()) { - LOG.debug("${{}} = {}", varName, varValue); - } - i = indexOfStarPlusOne + length; - } - vars.put(varNamePrefix + "_n", String.valueOf(variableIndex)); - } catch (StringIndexOutOfBoundsException e) { - throw new IllegalArgumentException("Given string does not comply star array format."); - } catch (NullPointerException e) { - throw new IllegalArgumentException("Input string cannot be null."); - } - } - - @Override - public List getArgumentDesc() { - return DESC; - } - - @Override - public String execute(SampleResult previousResult, Sampler currentSampler) { - JMeterVariables vars = getVariables(); - String stringToSplit = ((CompoundVariable) values[0]).execute(); - String varNamePrefix = ((CompoundVariable) values[1]).execute().trim(); - split(stringToSplit, varNamePrefix, vars); - return stringToSplit; - } - - @Override - public String getReferenceKey() { - return KEY; - } - - @Override - public void setParameters(Collection parameters) - throws InvalidVariableException { - checkParameterCount(parameters, 2); - values = parameters.toArray(); - } -} diff --git a/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelContext.java b/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelContext.java deleted file mode 100644 index b3b3e4f..0000000 --- a/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelContext.java +++ /dev/null @@ -1,190 +0,0 @@ -package com.blazemeter.jmeter.correlation.siebel; - -import com.blazemeter.jmeter.correlation.core.BaseCorrelationContext; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import org.apache.jmeter.samplers.SampleResult; - -/** - * Context needed in order to keep all the Siebel CRM variables updated. All the Correlation - * Extractors and Replacements for Siebel Extension support this {@link BaseCorrelationContext} - */ -public class SiebelContext extends BaseCorrelationContext { - - private final Map bcis = new HashMap<>(); - private final Map paramRowFields = new HashMap<>(); - private final Map rowVars = new HashMap<>(); - private int prefixId = 0; - private Integer counter; - - public Map getParamRowFields() { - return this.paramRowFields; - } - - /** - * Obtains the information needed to update BCIs, RowVars and RowFields from the response. All - * Contexts are updated in the method process on the {@link - * com.blazemeter.jmeter.correlation.core.CorrelationEngine} - * - * @param sampleResult the result from the request obtained from the server - */ - @Override - public void update(SampleResult sampleResult) { - - String responseAsString = sampleResult.getResponseDataAsString(); - if (responseAsString.startsWith("@0")) { - String delimiter = Pattern.quote(responseAsString.substring(2, 3)); - String[] parts = responseAsString.split(delimiter); - parsePage("", parts, 2, null); - } - } - - /** - * Reset all the variables for this context. Will be executed when the - * startProxy() is called, on - * {@link com.blazemeter.jmeter.correlation.CorrelationProxyControl} - */ - @Override - public void reset() { - bcis.clear(); - paramRowFields.clear(); - prefixId = 0; - counter = null; - } - - private int parsePage(String parentPath, String[] parts, int index, BCI bci) { - int attrCount = Integer.parseInt(parts[index++]); - int childCount = Integer.parseInt(parts[index++]); - String path = parentPath + "/" + parts[index++]; - int type = Integer.parseInt(parts[index++]); - if (type == 3) { - String typeId = parts[index++]; - bci = new BCI(); - this.bcis.put(typeId, bci); - } - Map attrs = new HashMap<>(); - for (int i = 0; i < attrCount; i++) { - String key = parts[index++]; - String val = parts[index++]; - attrs.put(key, val); - } - String bciAttr = Optional.ofNullable(attrs.get("bci")).orElse(attrs.get("bc")); - bci = Optional.ofNullable(bciAttr).map(this.bcis::get).orElse(bci); - - if (path.endsWith("/bci/fl/f")) { - bci.addField(new Field(attrs)); - } else if (path.endsWith("/cl/c") || path.endsWith("/col/co")) { - String fn = attrs.get("fn"); - String sp = attrs.get("sp"); - if (fn != null && sp != null) { - Field field = bci.getField(Integer.parseInt(fn)); - if (field != null) { - paramRowFields.put(sp, field); - } - } - } - for (int i = 0; i < childCount; i++) { - index = parsePage(path, parts, index, bci); - } - return index; - } - - public int getNextRowPrefixId() { - return prefixId++; - } - - public void addRowVar(String rowId, String varNamePrefix) { - rowVars.put(rowId, varNamePrefix); - } - - public Map getRowVars() { - return rowVars; - } - - public Integer getCounter() { - return counter; - } - - public void setCounter(Integer counter) { - this.counter = counter; - } - - @Override - public String toString() { - return "SiebelContext{" + "bcis=" + (bcis.isEmpty() ? "{}" - : bcis.keySet().stream() - .map(key -> key + "=" + bcis.get(key)) - .collect(Collectors.joining(", ", "{", "}"))) + - ", paramRowFields=" + (paramRowFields.isEmpty() ? "{}" - : paramRowFields.keySet().stream() - .map(key -> key + "=" + paramRowFields.get(key)) - .collect(Collectors.joining(", ", "{", "}"))) + - ", prefixId=" + prefixId + - ", rowVars=" + (rowVars.isEmpty() ? "{}" - : rowVars.keySet().stream() - .map(key -> key + "=" + rowVars.get(key)) - .collect(Collectors.joining(",", "{", "}"))) + - ", counter=" + counter + - '}'; - } - - private static class BCI { - - private final Map fields = new HashMap<>(); - - private void addField(Field field) { - field.position = fields.size(); - fields.put(field.id, field); - } - - private Field getField(int id) { - return fields.get(id); - } - - @Override - public String toString() { - return "BCI{" + - "fields=" + (fields.isEmpty() ? "{}" : fields.keySet().stream() - .map(key -> key + "=" + fields.get(key)) - .collect(Collectors.joining(", ", "{", "}"))) + - '}'; - } - } - - public static class Field { - - private final int telephoneType = 155; - private final int id; - private final int type; - private int position; - - private Field(Map attrs) { - id = Integer.parseInt(attrs.get("n")); - type = Integer.parseInt(attrs.get("t")); - } - - public int getPosition() { - return position; - } - - public String getIgnoredCharsRegex() { - if (type == telephoneType) { - return "[\\()\\- ]"; - } else { - return ""; - } - } - - @Override - public String toString() { - return "Field{" + - "id=" + id + - ", type=" + type + - ", position=" + position + - '}'; - } - } -} diff --git a/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelCounterCorrelationReplacement.java b/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelCounterCorrelationReplacement.java deleted file mode 100644 index d057953..0000000 --- a/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelCounterCorrelationReplacement.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.blazemeter.jmeter.correlation.siebel; - -import com.blazemeter.jmeter.correlation.core.CorrelationContext; -import com.blazemeter.jmeter.correlation.core.ParameterDefinition; -import com.blazemeter.jmeter.correlation.core.ParameterDefinition.TextParameterDefinition; -import com.blazemeter.jmeter.correlation.core.RegexMatcher; -import com.blazemeter.jmeter.correlation.core.replacements.RegexCorrelationReplacement; -import java.util.Collections; -import java.util.List; -import java.util.UUID; -import org.apache.jmeter.modifiers.JSR223PreProcessor; -import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase; -import org.apache.jmeter.samplers.SampleResult; -import org.apache.jmeter.testbeans.gui.TestBeanGUI; -import org.apache.jmeter.testelement.TestElement; -import org.apache.jmeter.threads.JMeterVariables; - -/** - * Siebel CRM Correlation Extension that calculates the difference of the SWEC variable, based on - * previous values, to correlate it. - */ -public class SiebelCounterCorrelationReplacement extends - RegexCorrelationReplacement { - - public SiebelCounterCorrelationReplacement() { - - } - - public SiebelCounterCorrelationReplacement(String regex) { - super(regex); - } - - @Override - public String getDisplayName() { - return "Siebel Counter"; - } - - @Override - public List getParamsDefinition() { - return Collections.singletonList( - new TextParameterDefinition(REPLACEMENT_REGEX_PROPERTY_NAME, - REPLACEMENT_REGEX_PROPERTY_DESCRIPTION, REGEX_DEFAULT_VALUE)); - } - - @Override - public void process(HTTPSamplerBase sampler, List children, SampleResult result, - JMeterVariables vars) { - if (regex.isEmpty()) { - return; - } - String match = new RegexMatcher(regex, 1).findMatch(result.getSamplerData(), 1); - if (match != null) { - int count = Integer.parseInt(match); - Integer counter = context.getCounter(); - if (counter == null || counter != count) { - children.add(createPreProcessor(counter, count)); - vars.put(getVariableName(), String.valueOf(count)); - } - super.process(sampler, children, result, vars); - } - } - - private JSR223PreProcessor createPreProcessor(Integer counter, int count) { - String variableName = getVariableName(); - JSR223PreProcessor jSR223PreProcessor = new JSR223PreProcessor(); - jSR223PreProcessor.setProperty(JSR223PreProcessor.GUI_CLASS, TestBeanGUI.class.getName()); - jSR223PreProcessor.setName(String.format("Calculate %s", variableName)); - StringBuilder script = new StringBuilder(); - if (counter == null) { - script.append(String.format("int %s_var = %d;\n", variableName, count)); - } else if (counter < count) { - script.append(String - .format("int %1$s_var = Integer.valueOf(vars.get(\"%1$s\")) + %2$d;\n", variableName, - count - counter)); - } else if (counter > count) { - script.append(String - .format("int %1$s_var = Integer.valueOf(vars.get(\"%1$s\")) - %2$d;\n", variableName, - counter - count)); - } - context.setCounter(count); - script.append(String.format("vars.put(\"%1$s\", String.valueOf(%1$s_var));\n", variableName)); - jSR223PreProcessor.setProperty("script", script.toString()); - jSR223PreProcessor.setProperty("cacheKey", UUID.randomUUID().toString()); - return jSR223PreProcessor; - } - - @Override - public String toString() { - return "SiebelCounterCorrelationReplacement{" + - "paramValues=" + getParams() + - '}'; - } - - @Override - public Class getSupportedContext() { - return SiebelContext.class; - } -} diff --git a/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowCorrelationExtractor.java b/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowCorrelationExtractor.java deleted file mode 100644 index 90335f5..0000000 --- a/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowCorrelationExtractor.java +++ /dev/null @@ -1,185 +0,0 @@ -package com.blazemeter.jmeter.correlation.siebel; - -import com.blazemeter.jmeter.correlation.core.CorrelationContext; -import com.blazemeter.jmeter.correlation.core.ParameterDefinition; -import com.blazemeter.jmeter.correlation.core.ParameterDefinition.ComboParameterDefinition; -import com.blazemeter.jmeter.correlation.core.ParameterDefinition.TextParameterDefinition; -import com.blazemeter.jmeter.correlation.core.RegexMatcher; -import com.blazemeter.jmeter.correlation.core.extractors.RegexCorrelationExtractor; -import com.blazemeter.jmeter.correlation.core.extractors.ResultField; -import com.blazemeter.jmeter.correlation.gui.CorrelationRuleTestElement; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; -import org.apache.jmeter.extractor.JSR223PostProcessor; -import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase; -import org.apache.jmeter.samplers.SampleResult; -import org.apache.jmeter.testbeans.gui.TestBeanGUI; -import org.apache.jmeter.testelement.TestElement; -import org.apache.jmeter.threads.JMeterVariables; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Correlation Extractor that gets the value of the Siebel CRM Context in every response and - * extracts its rows. Applies parsing Array function with the regex, adding post processors for each - * matching string. - */ -public class SiebelRowCorrelationExtractor extends RegexCorrelationExtractor { - - private static final Logger LOG = LoggerFactory.getLogger(SiebelRowCorrelationExtractor.class); - private static final ResultField DEFAULT_TARGET = ResultField.BODY; - - // Constructor added in order to satisfy json conversion - public SiebelRowCorrelationExtractor() { - } - - public SiebelRowCorrelationExtractor(String regex) { - super(regex, -1, DEFAULT_TARGET); - } - - public SiebelRowCorrelationExtractor(String regex, int group) { - super(regex, group, -1, DEFAULT_TARGET); - } - - /** - * Constructor that receives only strings to create the Correlation Extractor. Needed to create - * from the values in the UI. - * - * @param regex Regular Expression used to extract the value - * @param groupNumber Group Number from where the information will be extracted in the regex - * @param targetName Target Field where the extraction will be applied - */ - public SiebelRowCorrelationExtractor(String regex, String groupNumber, String targetName) { - super(regex, Integer.parseInt(groupNumber), -1, - ResultField.valueOf(targetName)); - } - - //Added to support backward compatibility with beta version - @Deprecated - public SiebelRowCorrelationExtractor(String regex, String groupNumber, String targetName, - String matchNumber) { - super(regex, Integer.parseInt(groupNumber), Integer.parseInt(matchNumber), - ResultField.valueOf(targetName)); - } - - @Override - public String getDisplayName() { - return "Siebel Row"; - } - - @Override - public void process(HTTPSamplerBase sampler, List children, SampleResult result, - JMeterVariables vars) { - super.process(sampler, children, result, vars); - vars.remove(variableName); - JSR223PostProcessor jsr223PostProcessor = buildArrayParserPostProcessor(result, vars); - if (jsr223PostProcessor != null) { - children.add(jsr223PostProcessor); - } - } - - private JSR223PostProcessor buildArrayParserPostProcessor(SampleResult result, - JMeterVariables vars) { - StringBuilder script = new StringBuilder(); - JSR223PostProcessor jSR223PostProcessor = new JSR223PostProcessor(); - jSR223PostProcessor.setProperty(JSR223PostProcessor.GUI_CLASS, TestBeanGUI.class.getName()); - jSR223PostProcessor.setName("Parse Array Values"); - script.append("import com.blazemeter.jmeter.correlation.siebel.SiebelArrayFunction;\n\n"); - script.append("String stringToSplit = \"\";\n"); - script.append("String rowId = \"\";"); - int matchNumber = 1; - for (String match : new RegexMatcher(regex, groupNr) - .findMatches(target.getField(result))) { - if (match == null) { - continue; - } - try { - String varNamePrefix = variableName + context.getNextRowPrefixId(); - SiebelArrayFunction.split(match, varNamePrefix, vars); - int numberOfVariables = Integer.parseInt(vars.get(varNamePrefix + "_n")); - script - .append(String.format("\n\n// Parsing Star Array parameter(s) using match number %1$d\n" - + "stringToSplit = vars.get(\"%2$s_%1$d\");\n" - + "if (stringToSplit != null) {\n" - + "\tSiebelArrayFunction.split(stringToSplit, \"%3$s\", vars);\n" - + "\trowIdValue = vars.get(\"%3$s_%4$d\");\n" - + "\tvars.put(\"%3$s_rowId\", rowIdValue);" - + "\n}", matchNumber, variableName, varNamePrefix, - numberOfVariables)); - String rowId = vars.get(varNamePrefix + "_" + numberOfVariables); - context.addRowVar(rowId, varNamePrefix); - vars.put(varNamePrefix + "_rowId", rowId); - matchNumber++; - } catch (IllegalArgumentException e) { - LOG.warn(e.getMessage()); - } - } - jSR223PostProcessor.setProperty("script", script.toString()); - jSR223PostProcessor.setProperty("cacheKey", UUID.randomUUID().toString()); - return matchNumber > 1 ? jSR223PostProcessor : null; - } - - @Override - public String toString() { - return "SiebelRowCorrelationExtractor{" + - "paramValues=" + getParams() + - '}'; - } - - public List getParamsDefinition() { - return Arrays.asList(new TextParameterDefinition(EXTRACTOR_REGEX_NAME, - EXTRACTOR_REGEX_DESCRIPTION, REGEX_DEFAULT_VALUE), - new TextParameterDefinition(GROUP_NUMBER_NAME, GROUP_NUMBER_DESCRIPTION, - String.valueOf(1), true), - new ComboParameterDefinition(TARGET_FIELD_NAME, TARGET_FIELD_DESCRIPTION, - ResultField.BODY.name(), ResultField.getNamesToCodesMapping(), true)); - } - - @Override - public List getParams() { - return Arrays - .asList(regex, Integer.toString(groupNr), target.name()); - } - - @Override - public void setParams(List params) { - regex = !params.isEmpty() ? params.get(0) : REGEX_DEFAULT_VALUE; - setGroupNr(params.size() > 1 ? parseInteger(params.get(1), DEFAULT_MATCH_GROUP_NAME, - DEFAULT_MATCH_GROUP) : DEFAULT_MATCH_GROUP); - //This means it applies to all the matches - matchNr = -1; - int targetParamIndex = 3; - target = - params.size() > targetParamIndex && !params.get(targetParamIndex).isEmpty() ? ResultField - .valueOf(params.get(targetParamIndex)) : DEFAULT_TARGET_VALUE; - } - - @Override - public void updateTestElem(CorrelationRuleTestElement testElem) { - super.updateTestElem(testElem); - testElem.setExtractorClass(getClass()); - } - - public void setRegex(String regex) { - this.regex = regex; - } - - @Override - public Class getSupportedContext() { - return SiebelContext.class; - } - - @Override - public boolean equals(Object o) { - if (o.getClass() != getClass()) { - return false; - } - return super.equals(o); - } - - @Override - public int hashCode() { - return super.hashCode(); - } -} diff --git a/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowIdCorrelationReplacement.java b/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowIdCorrelationReplacement.java deleted file mode 100644 index 676d02c..0000000 --- a/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowIdCorrelationReplacement.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.blazemeter.jmeter.correlation.siebel; - -import com.blazemeter.jmeter.correlation.core.CorrelationContext; -import com.blazemeter.jmeter.correlation.core.ParameterDefinition; -import com.blazemeter.jmeter.correlation.core.ParameterDefinition.TextParameterDefinition; -import com.blazemeter.jmeter.correlation.core.replacements.RegexCorrelationReplacement; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import org.apache.jmeter.threads.JMeterVariables; -import org.apache.oro.text.regex.MalformedPatternException; - -/** - * Handles the replacement of Row Ids on Siebel CRM Correlations. - */ -public class SiebelRowIdCorrelationReplacement extends RegexCorrelationReplacement { - - // Constructor added in order to satisfy json conversion - public SiebelRowIdCorrelationReplacement() { - } - - public SiebelRowIdCorrelationReplacement(String regex) { - super(regex); - } - - @Override - public String getDisplayName() { - return "Siebel Row Id"; - } - - @Override - public List getParamsDefinition() { - return Collections.singletonList( - new TextParameterDefinition(REPLACEMENT_REGEX_PROPERTY_NAME, - REPLACEMENT_REGEX_PROPERTY_DESCRIPTION, REGEX_DEFAULT_VALUE)); - } - - /** - * Handles the replacement of the row ids. - * - *

Receives an input and applies the replacement based in the values stored in the {@link - * SiebelContext}, followed by "_rowId". Works together with the {@link - * com.blazemeter.jmeter.correlation.siebel.SiebelRowCorrelationExtractor}. - * - *

The method is overwritten because is necessary to alter the way inputs are made but not - * how the Regular Expression is handled by the father class - * {@link com.blazemeter.jmeter.correlation.core.replacements.RegexCorrelationReplacement}. - * - * @param input the input against the condition will test and replacements will be applied - * @param regex the regular expression used to eval the input - * @param variableName the variable name ignored in this case - * @param vars the stored values during the recording - * @return the resultant input. Will be the same if it doesn't meet the condition - */ - @Override - protected String replaceWithRegex(String input, String regex, String variableName, - JMeterVariables vars) - throws MalformedPatternException { - for (Map.Entry rowVar : context.getRowVars().entrySet()) { - input = super.replaceWithRegex(input, regex, rowVar.getValue() + "_rowId", vars); - } - return input; - } - - @Override - public String toString() { - return "SiebelRowIdCorrelationReplacement{" + - "paramValues='" + getParams() + '\'' + - '}'; - } - - @Override - public Class getSupportedContext() { - return SiebelContext.class; - } -} diff --git a/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowParamsCorrelationReplacement.java b/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowParamsCorrelationReplacement.java deleted file mode 100644 index 35c4132..0000000 --- a/src/main/java/com/blazemeter/jmeter/correlation/siebel/SiebelRowParamsCorrelationReplacement.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.blazemeter.jmeter.correlation.siebel; - -import com.blazemeter.jmeter.correlation.core.CorrelationContext; -import com.blazemeter.jmeter.correlation.core.ParameterDefinition; -import com.blazemeter.jmeter.correlation.core.ParameterDefinition.TextParameterDefinition; -import com.blazemeter.jmeter.correlation.core.RegexMatcher; -import com.blazemeter.jmeter.correlation.core.replacements.RegexCorrelationReplacement; -import com.blazemeter.jmeter.correlation.siebel.SiebelContext.Field; -import com.fasterxml.jackson.annotation.JsonIgnore; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.function.Predicate; -import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase; -import org.apache.jmeter.samplers.SampleResult; -import org.apache.jmeter.testelement.TestElement; -import org.apache.jmeter.threads.JMeterVariables; -import org.apache.oro.text.regex.MalformedPatternException; -import org.apache.oro.text.regex.Perl5Compiler; - -/** - * Siebel CRM Custom Extension that handles the replacements of the Parameters based on the - * existence of the values in the Siebel Correlation Context. - * - * @see com.blazemeter.jmeter.correlation.siebel.SiebelContext - */ -public class SiebelRowParamsCorrelationReplacement extends - RegexCorrelationReplacement { - - private static final String DEFAULT_ROW_VAR_PREFIX = "SWERowId"; - @JsonIgnore - private String rowVarPrefix; - - // Constructor added in order to satisfy json conversion - public SiebelRowParamsCorrelationReplacement() { - rowVarPrefix = DEFAULT_ROW_VAR_PREFIX; - } - - public SiebelRowParamsCorrelationReplacement(String regex) { - super(regex); - rowVarPrefix = DEFAULT_ROW_VAR_PREFIX; - } - - @Override - public String getDisplayName() { - return "Siebel Row Params"; - } - - @Override - public List getParams() { - return Collections.singletonList(regex); - } - - @Override - public List getParamsDefinition() { - return Collections.singletonList(new TextParameterDefinition(REPLACEMENT_REGEX_PROPERTY_NAME, - REPLACEMENT_REGEX_PROPERTY_DESCRIPTION, REGEX_DEFAULT_VALUE)); - } - - @Override - public void process(HTTPSamplerBase sampler, List children, SampleResult result, - JMeterVariables vars) { - String rowId = new RegexMatcher(regex, 1).findMatch(result.getSamplerData(), 1); - rowVarPrefix = context.getRowVars().get(rowId); - super.process(sampler, children, result, vars); - } - - /** - * Handles the replacement of the row fields parameters - * - *

Receives an input and apply replacement for all known Siebel CRM row fields parameters (the - * ones following the s_\d+\d+\d+_\d+ pattern)'. The rowId comes from the ones stored into the - * {@link SiebelContext}. - * - *

Works together with the - * {@link com.blazemeter.jmeter.correlation.siebel.SiebelRowCorrelationExtractor} - * - * @param input the input against the condition will test and replacements will be applied - * @param regex the regular expression used to eval the input - * @param variableName the variable name ignored in this case - * @param vars the stored values during the recording - * @return the resultant input. Will be the same if it doesn't meet the condition - */ - @Override - protected String replaceWithRegex(String input, String regex, String variableName, - JMeterVariables vars) - throws MalformedPatternException { - for (Map.Entry entry : context.getParamRowFields().entrySet()) { - /* - we remove _\d+$ from param names and then add same regex, since when navigating rows the last - index is the position of the row and is dynamic. - */ - String paramRegex = - Perl5Compiler.quotemeta(entry.getKey().replaceAll("_\\d+$", "")) + "_\\d+=(.*)"; - String varName = rowVarPrefix + "_" + (entry.getValue().getPosition() + 1); - String varValue = vars.get(varName); - Predicate matchCondition = match -> varValue != null && varValue - .equals(match.replaceAll(entry.getValue().getIgnoredCharsRegex(), "")); - input = replaceWithRegexAndPredicate(input, paramRegex, varName, matchCondition); - } - return input; - } - - @Override - public String toString() { - return "SiebelRowParamsCorrelationReplacement{" + - "paramValues=" + getParams() + '}'; - } - - @Override - public Class getSupportedContext() { - return SiebelContext.class; - } -} diff --git a/src/main/resources/correlation-descriptions/SiebelCounterCorrelationReplacement.html b/src/main/resources/correlation-descriptions/SiebelCounterCorrelationReplacement.html deleted file mode 100644 index 4ebd04c..0000000 --- a/src/main/resources/correlation-descriptions/SiebelCounterCorrelationReplacement.html +++ /dev/null @@ -1,32 +0,0 @@ - -Siebel Counter Correlation Replacement -

Replaces the matched regex with a counter that holds the value of each time it has matched on the moment the - replacement occurs.

-
- - - - - - - - - - - - - - - -
FieldDescriptionDefault Value
RegExAllows to get the value of the SWECount variable in order to extract itparam="(.+?)"
- -

For more information about Replacements and how to use them, check the Documentation at List - of Correlation Replacements

- diff --git a/src/main/resources/correlation-descriptions/SiebelRowCorrelationExtractor.html b/src/main/resources/correlation-descriptions/SiebelRowCorrelationExtractor.html deleted file mode 100644 index 4faead7..0000000 --- a/src/main/resources/correlation-descriptions/SiebelRowCorrelationExtractor.html +++ /dev/null @@ -1,51 +0,0 @@ - - -Siebel Row Correlation Extractor -

The This Correlation Extractor applies a "Siebel Star Array strings" parsing function over the matched regex and - store the parsed values as variables. It doesn't have any match number, since it applied to every match it - founds

-
-
    -
  • RegEx: Regular Expression used to do the matching. Default value: param="(.+?)"
  • -
  • The Group Number: determines the group from where the information will be taken, based on the RegEx. Default - value: 1 -
  • -
  • The Target: determines where the RegEx will be applied. Default value: Response's Body
  • -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
FieldDescriptionDefault Value
RegExRegular Expression used to do the matchingparam="(.+?)"
Group Numberdetermines the group from where the information will be taken, based on the RegEx1
Targetdetermines where the RegEx will be appliedResponse's Body
- -

For more information about Extractors and how to use them, check the Documentation at List - Correlation Extractors

diff --git a/src/main/resources/correlation-descriptions/SiebelRowIdCorrelationReplacement.html b/src/main/resources/correlation-descriptions/SiebelRowIdCorrelationReplacement.html deleted file mode 100644 index 003e121..0000000 --- a/src/main/resources/correlation-descriptions/SiebelRowIdCorrelationReplacement.html +++ /dev/null @@ -1,31 +0,0 @@ - -Siebel Row Id Correlation Replacement -

Replaces the row ids based in the values stored in the SiebelContext, followed by "_rowId".

-
- - - - - - - - - - - - - - - -
FieldDescriptionDefault Value
RegExRegular Expression used to do the matchingparam="(.+?)"
- -

For more information about Replacements and how to use them, check the Documentation at List - of Correlation Replacements

- diff --git a/src/main/resources/correlation-descriptions/SiebelRowParamsCorrelationReplacement.html b/src/main/resources/correlation-descriptions/SiebelRowParamsCorrelationReplacement.html deleted file mode 100644 index cba3123..0000000 --- a/src/main/resources/correlation-descriptions/SiebelRowParamsCorrelationReplacement.html +++ /dev/null @@ -1,32 +0,0 @@ - -Siebel Row Params Correlation Replacement -

Replaces all known Siebel CRM row fields parameters (the ones following the s_\d+\d+\d+_\d+ pattern). Works together - with the Siebel Row Correlation Extractor

-
- - - - - - - - - - - - - - - -
FieldDescriptionDefault Value
RegExRegular Expression used to do the matchingparam="(.+?)"
- -

For more information about Replacements and how to use them, check the Documentation at List - of Correlation Replacements

- diff --git a/src/test/java/com/blazemeter/jmeter/correlation/CorrelationProxyControlTest.java b/src/test/java/com/blazemeter/jmeter/correlation/CorrelationProxyControlTest.java index 6d156c2..43f990e 100644 --- a/src/test/java/com/blazemeter/jmeter/correlation/CorrelationProxyControlTest.java +++ b/src/test/java/com/blazemeter/jmeter/correlation/CorrelationProxyControlTest.java @@ -17,7 +17,6 @@ import com.blazemeter.jmeter.correlation.core.templates.LocalConfiguration; import com.blazemeter.jmeter.correlation.core.templates.Template; import com.blazemeter.jmeter.correlation.core.templates.Template.Builder; -import com.blazemeter.jmeter.correlation.siebel.SiebelRowParamsCorrelationReplacement; import java.io.IOException; import java.util.Arrays; import java.util.Collections; @@ -198,7 +197,6 @@ private void assertModelInfo(String expectedComponents, List expecte private void setInitialValues(CorrelationProxyControl model) { model.setResponseFilter("Filter 0"); - model.setCorrelationComponents(SiebelRowParamsCorrelationReplacement.class.getName()); RegexCorrelationExtractor regexExtractor = new RegexCorrelationExtractor<>(); regexExtractor.setParams(Collections.singletonList("=(1)")); diff --git a/src/test/java/com/blazemeter/jmeter/correlation/core/CorrelationEngineTest.java b/src/test/java/com/blazemeter/jmeter/correlation/core/CorrelationEngineTest.java index 621b86b..91e1f07 100644 --- a/src/test/java/com/blazemeter/jmeter/correlation/core/CorrelationEngineTest.java +++ b/src/test/java/com/blazemeter/jmeter/correlation/core/CorrelationEngineTest.java @@ -7,9 +7,9 @@ import com.blazemeter.jmeter.correlation.core.extractors.RegexCorrelationExtractor; import com.blazemeter.jmeter.correlation.core.extractors.ResultField; import com.blazemeter.jmeter.correlation.core.replacements.RegexCorrelationReplacement; +import com.blazemeter.jmeter.correlation.custom.extension.CustomContext; +import com.blazemeter.jmeter.correlation.custom.extension.CustomCorrelationReplacement; import com.blazemeter.jmeter.correlation.gui.CorrelationComponentsRegistry; -import com.blazemeter.jmeter.correlation.siebel.SiebelContext; -import com.blazemeter.jmeter.correlation.siebel.SiebelRowIdCorrelationReplacement; import java.io.IOException; import java.net.URL; import java.util.ArrayList; @@ -64,7 +64,7 @@ private static HTTPSampler createSampler() { public void prepare() { engine = new CorrelationEngine(); engine.setEnabled(true); - when(registry.getContext(SiebelContext.class)).thenReturn(new SiebelContext()); + when(registry.getContext(CustomContext.class)).thenReturn(new CustomContext()); when(registry.getContext(BaseCorrelationContext.class)) .thenReturn(new BaseCorrelationContext()); } @@ -72,7 +72,7 @@ public void prepare() { @Test public void shouldUpdateContextWhenSetCorrelationRules() { engine.setCorrelationRules(createGroupWithRules( - Arrays.asList(buildRuleWithEnable(true), buildRuleWithSiebelReplacement( + Arrays.asList(buildRuleWithEnable(true), buildRuleWithCustomReplacement( "variable2"))), registry); assertThat(engine.getCorrelationRules().stream() @@ -96,10 +96,10 @@ private CorrelationRule buildRuleWithEnable(boolean enable) { return correlationRule; } - private CorrelationRule buildRuleWithSiebelReplacement(String referenceName) { + private CorrelationRule buildRuleWithCustomReplacement(String referenceName) { return new CorrelationRule(referenceName, new RegexCorrelationExtractor<>(REGEX, "1", "1", ResultField.BODY.name(), "false"), - createSiebelRowIdReplacement()); + new CustomCorrelationReplacement()); } private RegexCorrelationExtractor createRegexExtractor(String regex) { @@ -108,21 +108,13 @@ private RegexCorrelationExtractor createRegexExtractor(String regex) { return regexExtractorWithoutRegex; } - private SiebelRowIdCorrelationReplacement createSiebelRowIdReplacement() { - SiebelRowIdCorrelationReplacement siebelRowIdReplacement = - new SiebelRowIdCorrelationReplacement(); - siebelRowIdReplacement.setParams(Collections.singletonList("")); - return siebelRowIdReplacement; - } - @Test public void shouldResetContextWhenReset() throws IOException { engine.setCorrelationRules(createGroupWithRules(Collections - .singletonList(buildRuleWithSiebelReplacement("var"))), registry); + .singletonList(buildRuleWithCustomReplacement("var"))), registry); engine.updateContexts(buildSampleResult()); String updatedContext = getContextsToString(); engine.reset(); - assertThat(getContextsToString()).isNotEqualTo(updatedContext); } diff --git a/src/test/java/com/blazemeter/jmeter/correlation/core/analysis/AnalysisReporterIT.java b/src/test/java/com/blazemeter/jmeter/correlation/core/analysis/AnalysisReporterIT.java index bf5b66b..7c89e5d 100644 --- a/src/test/java/com/blazemeter/jmeter/correlation/core/analysis/AnalysisReporterIT.java +++ b/src/test/java/com/blazemeter/jmeter/correlation/core/analysis/AnalysisReporterIT.java @@ -16,7 +16,6 @@ import com.blazemeter.jmeter.correlation.core.replacements.RegexCorrelationReplacement; import com.blazemeter.jmeter.correlation.gui.CorrelationComponentsRegistry; import com.blazemeter.jmeter.correlation.gui.automatic.CorrelationSuggestionsPanel; -import com.blazemeter.jmeter.correlation.siebel.SiebelContext; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; @@ -58,7 +57,6 @@ public void setup() throws IOException { engine = new CorrelationEngine(); panel = new CorrelationSuggestionsPanel(null); frame = showInFrame(panel); - when(registry.getContext(SiebelContext.class)).thenReturn(new SiebelContext()); when(registry.getContext(BaseCorrelationContext.class)) .thenReturn(new BaseCorrelationContext()); AnalysisReporter.startCollecting(); diff --git a/src/test/java/com/blazemeter/jmeter/correlation/core/suggestions/SuggestionGeneratorIT.java b/src/test/java/com/blazemeter/jmeter/correlation/core/suggestions/SuggestionGeneratorIT.java index 1d7b024..67ed7d2 100644 --- a/src/test/java/com/blazemeter/jmeter/correlation/core/suggestions/SuggestionGeneratorIT.java +++ b/src/test/java/com/blazemeter/jmeter/correlation/core/suggestions/SuggestionGeneratorIT.java @@ -37,7 +37,7 @@ public class SuggestionGeneratorIT { public void setUp() throws IOException { JMeterTestUtils.setupUpdatedJMeter(); analysisContext = new AnalysisContext(); - analysisContext.setRegistry(CorrelationComponentsRegistry.getInstance()); + analysisContext.setRegistry(CorrelationComponentsRegistry.getNewInstance()); analysisMethod = new AnalysisMethod(); suggestionGenerator = new SuggestionGenerator(analysisMethod); } @@ -141,7 +141,7 @@ public void shouldReturnListWhenGenerateSuggestionsWithValidContext() { if (didntLoadRecordingTraceToContextSuccessfully()) { return; } - analysisContext.setRegistry(CorrelationComponentsRegistry.getInstance()); + analysisContext.setRegistry(CorrelationComponentsRegistry.getNewInstance()); List suggestions = suggestionGenerator.generateSuggestions(analysisContext); softly.assertThat(suggestions).isNotEmpty(); @@ -174,7 +174,7 @@ public void shouldReturnListOfRulesWhenParseToRulesWhenApplySuggestions() { if (didntLoadRecordingTraceToContextSuccessfully()) { return; } - analysisContext.setRegistry(CorrelationComponentsRegistry.getInstance()); + analysisContext.setRegistry(CorrelationComponentsRegistry.getNewInstance()); List suggestions = suggestionGenerator.generateSuggestions(analysisContext); softly.assertThat(suggestions).isNotEmpty(); @@ -198,7 +198,7 @@ public void shouldSuccessfullyModifyTestPlanWhen() { if (didntLoadRecordingTraceToContextSuccessfully()) { return; } - analysisContext.setRegistry(CorrelationComponentsRegistry.getInstance()); + analysisContext.setRegistry(CorrelationComponentsRegistry.getNewInstance()); suggestionGenerator.applySuggestions(suggestionGenerator.generateSuggestions(analysisContext)); } } diff --git a/src/test/java/com/blazemeter/jmeter/correlation/core/templates/CorrelationTemplatesRepositoriesConfigurationTest.java b/src/test/java/com/blazemeter/jmeter/correlation/core/templates/CorrelationTemplatesRepositoriesConfigurationTest.java index 67bb4c0..338a410 100644 --- a/src/test/java/com/blazemeter/jmeter/correlation/core/templates/CorrelationTemplatesRepositoriesConfigurationTest.java +++ b/src/test/java/com/blazemeter/jmeter/correlation/core/templates/CorrelationTemplatesRepositoriesConfigurationTest.java @@ -1,5 +1,6 @@ package com.blazemeter.jmeter.correlation.core.templates; +import com.blazemeter.jmeter.correlation.JMeterTestUtils; import com.blazemeter.jmeter.correlation.core.templates.repository.RepositoryManager; import com.blazemeter.jmeter.correlation.core.templates.repository.TemplateProperties; import com.blazemeter.jmeter.correlation.core.templates.repository.pluggable.RemoteUrlRepository; @@ -28,7 +29,6 @@ public class CorrelationTemplatesRepositoriesConfigurationTest extends WiredBase private static final String TEST_REPO_NAME = "test"; private static final String FIRST_TEMPLATE_NAME = "first"; private static final String SECOND_TEMPLATE_NAME = "second"; - private static final String SIEBEL_TEMPLATE_NAME = "siebel"; private static final String TEMPLATE_VERSION_1_1 = "1.1"; private static final String TEMPLATE_VERSION_1_0 = "1.0"; private static final String TEMPLATE_DESCRIPTION = "Description1"; @@ -37,6 +37,7 @@ public class CorrelationTemplatesRepositoriesConfigurationTest extends WiredBase @Before public void setUp() throws IOException { + JMeterTestUtils.setupUpdatedJMeter(); String path = folder.getRoot().getPath(); LocalConfiguration.installDefaultFiles(path); localConfiguration = new LocalConfiguration(path, true); @@ -105,8 +106,10 @@ private RemoteUrlRepository createRemoteRepository() { } @Test - public void shouldReturnCorrelationRepositoriesWhenGetCorrelationRepositories() throws IOException { + public void shouldReturnCorrelationRepositoriesWhenGetCorrelationRepositories() + throws IOException, ConfigurationException { installTestRepo(); + TemplateUtils.installTestTemplate(folder.getRoot().getPath(), localConfiguration); List repositories = correlationTempRepoConfig.getCorrelationRepositories(); assertTrue(repositories.stream().anyMatch(r -> r.getName().equals(TEST_REPO_NAME))); assertTrue(repositories.stream().anyMatch(r -> r.getName().equals(LOCAL_REPO_NAME))); @@ -161,11 +164,6 @@ public void shouldReturnRepositoryUrlWhenGetRepositoryURL() throws IOException { assertEquals(getBaseURL() + TEST_REPOSITORY_URL, repoURL); } - @Test - public void shouldReturnTrueWhenIisLocalTemplateVersionSavedWithSavedVersion() { - assertTrue(correlationTempRepoConfig.isLocalTemplateVersionSaved(SIEBEL_TEMPLATE_NAME, TEMPLATE_VERSION_1_0)); - } - @Test public void shouldReturnCorrelationTemplatesAndPropertiesWhenGetCorrelationTemplatesAndPropertiesByRepositoryNameWithUseLocal() throws IOException { installTestRepo(); diff --git a/src/test/java/com/blazemeter/jmeter/correlation/core/templates/LocalConfigurationTest.java b/src/test/java/com/blazemeter/jmeter/correlation/core/templates/LocalConfigurationTest.java index 233e6b5..44aafee 100644 --- a/src/test/java/com/blazemeter/jmeter/correlation/core/templates/LocalConfigurationTest.java +++ b/src/test/java/com/blazemeter/jmeter/correlation/core/templates/LocalConfigurationTest.java @@ -1,24 +1,24 @@ package com.blazemeter.jmeter.correlation.core.templates; import static com.blazemeter.jmeter.correlation.TestUtils.getFileContent; +import static com.blazemeter.jmeter.correlation.core.templates.TemplateUtils.TEST_TEMPLATE_DESCRIPTION; +import static com.blazemeter.jmeter.correlation.core.templates.TemplateUtils.TEST_TEMPLATE_NAME; +import static com.blazemeter.jmeter.correlation.core.templates.TemplateUtils.TEST_TEMPLATE_VERSION; import static com.github.tomakehurst.wiremock.client.WireMock.configureFor; -import static com.github.tomakehurst.wiremock.client.WireMock.get; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.when; +import com.blazemeter.jmeter.correlation.JMeterTestUtils; import com.blazemeter.jmeter.correlation.TestUtils; import com.blazemeter.jmeter.correlation.core.templates.repository.RemoteRepository; import com.blazemeter.jmeter.correlation.core.templates.repository.RepositoryManager; import com.blazemeter.jmeter.correlation.core.templates.repository.TemplateProperties; import com.blazemeter.jmeter.correlation.core.templates.repository.pluggable.RemoteUrlRepository; -import com.github.tomakehurst.wiremock.client.MappingBuilder; - import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; @@ -32,7 +32,7 @@ import java.util.Objects; import java.util.function.Consumer; import java.util.stream.Collectors; - +import org.apache.jmeter.util.JMeterUtils; import org.assertj.core.api.JUnitSoftAssertions; import org.junit.After; import org.junit.Assert; @@ -46,456 +46,510 @@ @RunWith(MockitoJUnitRunner.class) public class LocalConfigurationTest extends WiredBaseTest { - private static final String VERSION_ONE = "1.0"; - private static final String DUMMY_FILE_VERSION_ONE = "dummy-1.0.jar"; - private static final String SIEBEL_REPO_NAME = "siebel"; - private static final String FIRST_REPO_NAME = "first"; - private static final String TEST_REPO_NAME = "test"; - private static final String SECOND_REPO_NAME = "second"; - private static final String firstRepositoryURL = "localhost/firstRepository.json"; - private static final String secondRepositoryURL = "localhost/secondRepository.json"; - private static final String DEFAULT_ULR = "http://localhost.com/1"; - private static final String JAR_SUFFIX = ".jar"; - private static final String firstTemplateID = "first"; - private static final String firstTemplateVersion = "1.0"; - private static final String firstTemplateDescription = "Description1"; - private static final String REPOSITORY_ID = "R1"; - private static final String SIEBEL_ID = "siebel"; - private static final String SIEBEL_DESCRIPTION = "

This is the first version of the " + "SIEBEL CRM correlation recorder rules

" + "

It contains 16 of the most used Correlations for the platform.

"; - private static final String SIEBEL_VERSION = "1.0"; - - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - public JUnitSoftAssertions softly = new JUnitSoftAssertions(); - @Mock - private CorrelationTemplateDependency firstDependency; - @Mock - private CorrelationTemplatesRepository localRepository; - @Mock - private CorrelationTemplatesRepository firstRepository; - @Mock - private CorrelationTemplatesRepository secondRepository; - @Mock - private CorrelationTemplatesRepositoryConfiguration repositoryWithInstalledTemplates; - @Mock - private CorrelationTemplatesRepositoryConfiguration localRepositoryConfiguration; - @Mock - private CorrelationTemplatesRepository expectedSiebelRepository; - @Mock - private CorrelationTemplatesRepository expectedCentralRepository; - private LocalConfiguration configuration; - private String dependenciesFolder; - private static final String TEMPLATE_FILE_SUFFIX = "template.json"; - - private static final String REPOSITORY_FILE_SUFFIX = "repository.json"; - - private static final String LOCAL_REPOSITORY_NAME = "local"; - private static final String LOCAL_REPOSITORY_DISPLAY_NAME = "Local"; - - private static final String CENTRAL_REPOSITORY_NAME = "central"; - private static final String CENTRAL_REPOSITORY_DISPLAY_NAME = "GitHub's Central"; - - private static final String SIEBEL_TEMPLATE_REFERENCE_NAME = "siebel"; - private static final String WORDPRESS_TEMPLATE_REFERENCE_NAME = "wordpress"; - private static final String TEMPLATE_VERSION_TWO = "1.1"; - private static final String TEMPLATE_VERSION_ONE = "1.0"; - - private static final String TEMPLATE_VERSION_THREE = "0.1-alpha"; - - private static final String CORRELATION_TEMPLATES_REPOSITORY_NAME = "CorrelationTemplatesRepository"; - - private static final String SIEBEL_TEMPLATE_VERSION_TWO_NAME = SIEBEL_TEMPLATE_REFERENCE_NAME + "-" + TEMPLATE_VERSION_TWO + "-" + TEMPLATE_FILE_SUFFIX; - private static final String SIEBEL_TEMPLATE_VERSION_ONE_NAME = SIEBEL_TEMPLATE_REFERENCE_NAME + "-" + TEMPLATE_VERSION_ONE + "-" + TEMPLATE_FILE_SUFFIX; - - private static final String WORDPRESS_TEMPLATE_VERSION_ONE_NAME = WORDPRESS_TEMPLATE_REFERENCE_NAME + "-" + TEMPLATE_VERSION_ONE + "-" + TEMPLATE_FILE_SUFFIX; - - @Before - public void setUp() throws IOException { - String path = folder.getRoot().getPath(); - LocalConfiguration.installDefaultFiles(path); - configuration = new LocalConfiguration(path, true); - configuration.setupRepositoryManagers(); - when(firstRepository.getName()).thenReturn(FIRST_REPO_NAME); - when(secondRepository.getName()).thenReturn(SECOND_REPO_NAME); - when(localRepository.getName()).thenReturn(LOCAL_REPOSITORY_NAME); - configuration.addRepository(firstRepository.getName(), firstRepositoryURL); - dependenciesFolder = folder.getRoot().getAbsolutePath() + "/lib/"; - prepareExpectedLocalRepository(); - } - - @After - public void tearDown() { - stopWiredMock(); - } - - private void prepareExpectedLocalRepository() { - when(expectedSiebelRepository.getValues()).thenReturn(CORRELATION_TEMPLATES_REPOSITORY_NAME + "{name='" + LOCAL_REPOSITORY_NAME + "', displayName='" + LOCAL_REPOSITORY_DISPLAY_NAME + "', templatesVersions={" + SIEBEL_TEMPLATE_REFERENCE_NAME + "=CorrelationTemplateVersions {versions=[" + TEMPLATE_VERSION_ONE + "]}}}"); - - when(expectedCentralRepository.getValues()).thenReturn(CORRELATION_TEMPLATES_REPOSITORY_NAME + "{name='" + CENTRAL_REPOSITORY_NAME + "', displayName='" + CENTRAL_REPOSITORY_DISPLAY_NAME + "', templatesVersions={" + WORDPRESS_TEMPLATE_REFERENCE_NAME + "=CorrelationTemplateVersions {versions=[" + TEMPLATE_VERSION_THREE + "]}}}"); - - } - - public void configureWireMockServer() { - wireMockServer.start(); - configureFor("localhost", wireMockServer.port()); - } - - @Test - public void shouldReturnRepositoryNameWhenAdded() { - assertEquals(Arrays.asList(CENTRAL_REPOSITORY_NAME, localRepository.getName(), firstRepository.getName()), configuration.getRepositoriesNames()); - } - - @Test - public void shouldReturnNullWhenGetRepositoryWithANonExistingRepo() { - RepositoryManager repositoryManager = configuration.getRepositoryManager("fourth"); - assertNull(repositoryManager); - } - @Test - public void shouldNotAddRepositoryWhenRepositoryAlreadyExists() { - configuration.addRepository(firstRepository.getName(), firstRepositoryURL); - assertEquals(Arrays.asList(CENTRAL_REPOSITORY_NAME, localRepository.getName(), firstRepository.getName()), configuration.getRepositoriesNames()); - } - - @Test - public void shouldDeleteRepositoryWhenDeleteAddedRepository() { - configuration.removeRepository(firstRepository.getName()); - assertEquals(Arrays.asList(CENTRAL_REPOSITORY_NAME, localRepository.getName()), configuration.getRepositoriesNames()); - } - - @Test - public void shouldNotDeleteRepositoryWhenDeleteRepositoryNotAdded() { - configuration.removeRepository(secondRepository.getName()); - assertEquals(Arrays.asList(CENTRAL_REPOSITORY_NAME, localRepository.getName(), firstRepository.getName()), configuration.getRepositoriesNames()); - } - - @Test - public void shouldReturnOnlyRepositoriesWithInstalledTemplateWhenGetRepositoriesWithInstalledTemplates() throws ConfigurationException { - - prepareExpectedRepositoryWithInstalledTemplates(); - - configuration.addRepository(secondRepository.getName(), secondRepositoryURL); - configuration.manageTemplate(LocalConfiguration.INSTALL, firstRepository.getName(), firstTemplateID, firstTemplateVersion); - - List expected = Arrays.asList(localRepositoryConfiguration, repositoryWithInstalledTemplates); - - List actual = configuration.getRepositoriesWithInstalledTemplates(); - - //We are taking the second since the first it's the default Siebel - assertTrue(expected.size() == actual.size() && actual.get(1).equals(expected.get(1))); - } - - private void prepareExpectedRepositoryWithInstalledTemplates() { - when(repositoryWithInstalledTemplates.getName()).thenReturn(FIRST_REPO_NAME); - when(repositoryWithInstalledTemplates.getUrl()).thenReturn(firstRepositoryURL); - when(repositoryWithInstalledTemplates.getInstalledTemplates()).thenReturn(new HashMap() {{ - put(firstTemplateID, firstTemplateVersion); + private static final String VERSION_ONE = "1.0"; + private static final String DUMMY_FILE_VERSION_ONE = "dummy-1.0.jar"; + private static final String FIRST_REPO_NAME = "first"; + private static final String TEST_REPO_NAME = TEST_TEMPLATE_NAME; + private static final String SECOND_REPO_NAME = "second"; + private static final String firstRepositoryURL = "localhost/firstRepository.json"; + private static final String secondRepositoryURL = "localhost/secondRepository.json"; + private static final String DEFAULT_ULR = "http://localhost.com/1"; + private static final String JAR_SUFFIX = ".jar"; + private static final String firstTemplateID = "first"; + private static final String firstTemplateVersion = "1.0"; + private static final String firstTemplateDescription = "Description1"; + private static final String REPOSITORY_ID = "R1"; + + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + public JUnitSoftAssertions softly = new JUnitSoftAssertions(); + @Mock + private CorrelationTemplateDependency firstDependency; + @Mock + private CorrelationTemplatesRepository localRepository; + @Mock + private CorrelationTemplatesRepository firstRepository; + @Mock + private CorrelationTemplatesRepository secondRepository; + @Mock + private CorrelationTemplatesRepositoryConfiguration repositoryWithInstalledTemplates; + @Mock + private CorrelationTemplatesRepositoryConfiguration localRepositoryConfiguration; + @Mock + private CorrelationTemplatesRepository expectedCentralRepository; + @Mock + private CorrelationTemplatesRepository expectedLocalRepository; + private LocalConfiguration configuration; + private String dependenciesFolder; + private static final String LOCAL_REPOSITORY_NAME = "local"; + private static final String CENTRAL_REPOSITORY_NAME = "central"; + private static final String CENTRAL_REPOSITORY_DISPLAY_NAME = "GitHub's Central"; + private static final String WORDPRESS_TEMPLATE_REFERENCE_NAME = "wordpress"; + private static final String TEMPLATE_VERSION_THREE = "0.1-alpha"; + + private static final String CORRELATION_TEMPLATES_REPOSITORY_NAME = + "CorrelationTemplatesRepository"; + + @Before + public void setUp() throws IOException { + JMeterTestUtils.setupUpdatedJMeter(); + String path = folder.getRoot().getPath(); + LocalConfiguration.installDefaultFiles(path); + configuration = new LocalConfiguration(path, true); + configuration.setupRepositoryManagers(); + when(firstRepository.getName()).thenReturn(FIRST_REPO_NAME); + when(secondRepository.getName()).thenReturn(SECOND_REPO_NAME); + when(localRepository.getName()).thenReturn(LOCAL_REPOSITORY_NAME); + configuration.addRepository(firstRepository.getName(), firstRepositoryURL); + dependenciesFolder = JMeterUtils.getJMeterHome() + "/lib/ext/"; + prepareExpectedCentralRepository(); + mockExpectedLocalRepository(); + } + + + @After + public void tearDown() { + stopWiredMock(); + } + + private void prepareExpectedCentralRepository() { + when(expectedCentralRepository.getValues()).thenReturn( + CORRELATION_TEMPLATES_REPOSITORY_NAME + "{name='" + CENTRAL_REPOSITORY_NAME + + "', displayName='" + CENTRAL_REPOSITORY_DISPLAY_NAME + "', templatesVersions={" + + WORDPRESS_TEMPLATE_REFERENCE_NAME + "=CorrelationTemplateVersions {versions=[" + + TEMPLATE_VERSION_THREE + "]}}}"); + + } + + private void mockExpectedLocalRepository() { + when(expectedLocalRepository.getValues()).thenReturn( + CORRELATION_TEMPLATES_REPOSITORY_NAME + "{name='" + "local" + + "', displayName='" + "Local" + "', templatesVersions={}}"); + } + + public void configureWireMockServer() { + wireMockServer.start(); + configureFor("localhost", wireMockServer.port()); + } + + @Test + public void shouldReturnRepositoryNameWhenAdded() { + assertEquals(Arrays.asList(CENTRAL_REPOSITORY_NAME, localRepository.getName(), + firstRepository.getName()), configuration.getRepositoriesNames()); + } + + @Test + public void shouldReturnNullWhenGetRepositoryWithANonExistingRepo() { + RepositoryManager repositoryManager = configuration.getRepositoryManager("fourth"); + assertNull(repositoryManager); + } + + @Test + public void shouldNotAddRepositoryWhenRepositoryAlreadyExists() { + configuration.addRepository(firstRepository.getName(), firstRepositoryURL); + assertEquals(Arrays.asList(CENTRAL_REPOSITORY_NAME, localRepository.getName(), + firstRepository.getName()), configuration.getRepositoriesNames()); + } + + @Test + public void shouldDeleteRepositoryWhenDeleteAddedRepository() { + configuration.removeRepository(firstRepository.getName()); + assertEquals(Arrays.asList(CENTRAL_REPOSITORY_NAME, localRepository.getName()), + configuration.getRepositoriesNames()); + } + + @Test + public void shouldNotDeleteRepositoryWhenDeleteRepositoryNotAdded() { + configuration.removeRepository(secondRepository.getName()); + assertEquals(Arrays.asList(CENTRAL_REPOSITORY_NAME, localRepository.getName(), + firstRepository.getName()), configuration.getRepositoriesNames()); + } + + @Test + public void shouldReturnOnlyRepositoriesWithInstalledTemplateWhenGetRepositoriesWithInstalledTemplates() + throws ConfigurationException, IOException { + installTestTemplate(); + prepareExpectedRepositoryWithInstalledTemplates(); + + configuration.addRepository(secondRepository.getName(), secondRepositoryURL); + configuration.manageTemplate(LocalConfiguration.INSTALL, firstRepository.getName(), + firstTemplateID, firstTemplateVersion); + + List expected = Arrays.asList( + localRepositoryConfiguration, repositoryWithInstalledTemplates); + + List actual = + configuration.getRepositoriesWithInstalledTemplates(); + + //We are taking the second since the first it's the local repository with the Custom template + // added + assertTrue(expected.size() == actual.size() && actual.get(1).equals(expected.get(1))); + } + + private void installTestTemplate() throws IOException, ConfigurationException { + TemplateUtils.installTestTemplate(folder.getRoot().toPath().toAbsolutePath().toString(), + configuration); + } + + private void prepareExpectedRepositoryWithInstalledTemplates() { + when(repositoryWithInstalledTemplates.getName()).thenReturn(FIRST_REPO_NAME); + when(repositoryWithInstalledTemplates.getUrl()).thenReturn(firstRepositoryURL); + when(repositoryWithInstalledTemplates.getInstalledTemplates()).thenReturn( + new HashMap() {{ + put(firstTemplateID, firstTemplateVersion); }}); - } - - @Test - public void shouldReturnTrueWhenIsInstalledWithInstalledTemplate() throws ConfigurationException { - configuration.manageTemplate(LocalConfiguration.INSTALL, firstRepository.getName(), firstTemplateID, firstTemplateVersion); - assertTrue(configuration.isInstalled(firstRepository.getName(), firstTemplateID, firstTemplateVersion)); - } - - @Test - public void shouldReturnFalseWhenIsInstalledWithNotInstalledTemplate() { - assertFalse(configuration.isInstalled(firstRepository.getName(), firstTemplateID, firstTemplateVersion)); - } - - @Test(expected = ConfigurationException.class) - public void shouldThrowAnExceptionWhenManageTemplateWithNotAddedRepo() throws ConfigurationException { - configuration.manageTemplate(LocalConfiguration.INSTALL, secondRepository.getName(), firstTemplateID, firstTemplateVersion); - } - - @Test - public void shouldUninstallTemplateWhenManageTemplateWithEmptyAction() throws ConfigurationException { - configuration.manageTemplate(LocalConfiguration.INSTALL, firstRepository.getName(), firstTemplateID, firstTemplateVersion); - - configuration.manageTemplate("", firstRepository.getName(), firstTemplateID, firstTemplateVersion); - assertFalse(configuration.isInstalled(firstRepository.getName(), firstTemplateID, firstTemplateVersion)); - } - - @Test - public void shouldInstallTemplateWhenInstallTemplate() throws ConfigurationException, IOException { - prepareRepoFiles(); - configuration.installTemplate(TEST_REPO_NAME, firstTemplateID, firstTemplateVersion); - assertTrue(configuration.isInstalled(TEST_REPO_NAME, firstTemplateID, firstTemplateVersion)); - } - - private void prepareRepoFiles() throws IOException { - removeCentralRepo(); - startWiredMock(WIRED_HOST); - RemoteUrlRepository testRepo = createRemoteRepository(); - mockRequestsToRepoFiles(); - addAndInstallRepos(testRepo); - } - - private void addAndInstallRepos(RemoteRepository testRepo) { - configuration.addRepositoryManager(testRepo); - configuration.setupRepositoryManagers(); - } - - private void removeCentralRepo() { - configuration.getRepositoriesManagers().remove(CENTRAL_REPOSITORY_NAME); - configuration.removeRepository(CENTRAL_REPOSITORY_NAME); - } - - private RemoteUrlRepository createRemoteRepository() { - RemoteUrlRepository testRepo = new RemoteUrlRepository(); - testRepo.setName(TEST_REPO_NAME); - testRepo.setEndPoint(getBaseURL() + TEST_REPOSITORY_URL); - testRepo.setConfig(configuration); - testRepo.init(); - return testRepo; - } - - @Test - public void shouldUninstallTemplateWhenUninstallTemplate() throws ConfigurationException { - configuration.manageTemplate(LocalConfiguration.INSTALL, firstRepository.getName(), firstTemplateID, firstTemplateVersion); - configuration.uninstallTemplate(firstRepository.getName(), firstTemplateID, firstTemplateVersion); - assertFalse(configuration.isInstalled(firstRepository.getName(), firstTemplateID, firstTemplateVersion)); - } - - @Test - public void shouldReturnListWithRepositoriesWhenGetRepositoriesNames() { - configuration.addRepository(secondRepository.getName(), secondRepositoryURL); - assertEquals(Arrays.asList(CENTRAL_REPOSITORY_NAME, localRepository.getName(), FIRST_REPO_NAME, SECOND_REPO_NAME), configuration.getRepositoriesNames()); - } - - @Test - public void shouldReturnURLWhenGetRepositoryURL() { - assertEquals(firstRepositoryURL, configuration.getRepositoryURL(firstRepository.getName())); - } - - @Test - public void shouldReturnTrueWhenRefreshRepositoryWithUpToDataRemoteRepoAndLocal() throws IOException, ConfigurationException { - prepareRepoFiles(); - List progressList = new ArrayList<>(); - Consumer progressConsumer = progressList::add; - - List statusList = new ArrayList<>(); - Consumer statusConsumer = statusList::add; - Assert.assertTrue(configuration.refreshRepositories("", progressConsumer, statusConsumer)); - - assertEquals(Arrays.asList(0, 25, 50, 75, 100, 100), progressList); - assertEquals(Arrays.asList("Syncing 'test' repository.", "Syncing 'local' repository.", "Updating 'Local' repository.", "Updating 'http://localhost:" + getWiredPort() + TEST_REPOSITORY_URL + "' repository.", "Update finished"), statusList); - } - - @Test - public void shouldReturnInstalledTemplatesWhenGetInstalledTemplates() { - List