def convert(self, group, target, load, concurrency): """ Convert a thread group to ThreadGroup/ConcurrencyThreadGroup for applying of load """ msg = "Converting %s (%s) to %s and apply load parameters" self.log.debug(msg, group.gtype, group.get_testname(), target) on_error = group.get_on_error() if target == ThreadGroup.__name__: new_group_element = JMX.get_thread_group( concurrency=concurrency, rampup=load.ramp_up, hold=load.hold, iterations=load.iterations, testname=group.get_testname(), on_error=on_error) elif target == ConcurrencyThreadGroup.__name__: new_group_element = JMX.get_concurrency_thread_group( concurrency=concurrency, rampup=load.ramp_up, hold=load.hold, steps=load.steps, testname=group.get_testname(), on_error=on_error) else: self.log.warning('Unsupported preferred thread group: %s', target) return group.element.getparent().replace(group.element, new_group_element)
def test_regexp_subject(self): res = JMX._get_extractor('test_name', 'baddy', 'regexp', 1, 1, 'error') self.assertEqual("body", res.find(".//stringProp[@name='RegexExtractor.useHeaders']").text) res = JMX._get_extractor('test_name', 'headers', 'regexp', 1, 1, 'error') self.assertEqual("true", res.find(".//stringProp[@name='RegexExtractor.useHeaders']").text) res = JMX._get_extractor('test_name', 'http-code', 'regexp', 1, 1, 'error') self.assertEqual("code", res.find(".//stringProp[@name='RegexExtractor.useHeaders']").text) self.assertIsNone(res.find(".//stringProp[@name='Sample.scope']"))
def test_json_body_app_str(self): obj = JMeterExecutor() obj.engine = EngineEmul() obj.execution.merge({ "scenario": { "requests": [{ "url": "http://blazedemo.com", "headers": {"Content-Type": "application/json"}, "body": "{\"store_id\": \"${store_id}\", \"display_name\": \"${display_name}\"}" }]}}) obj.prepare() jmx = JMX(obj.original_jmx) selector = 'elementProp[name="HTTPsampler.Arguments"]>collectionProp' selector += '>elementProp>stringProp[name="Argument.value"]' self.assertNotEqual(jmx.get(selector)[0].text.find('store_id'), -1)
def __gen_authorization(scenario): """ Generates HTTP Authorization Manager """ elements = [] authorizations = scenario.get("authorization") if authorizations: clear_flag = False if isinstance(authorizations, dict): if "clear" in authorizations or "list" in authorizations: # full form clear_flag = authorizations.get("clear", False) authorizations = authorizations.get("list", []) else: authorizations = [authorizations] # short form if not isinstance(authorizations, list): raise TaurusConfigError("Wrong authorization format: %s" % authorizations) auth_manager = JMX.get_auth_manager(authorizations, clear_flag) elements.append(auth_manager) elements.append(etree.Element("hashTree")) return elements
def __gen_datasources(self, scenario): sources = scenario.get("data-sources") if not sources: return [] if not isinstance(sources, list): raise TaurusConfigError("data-sources '%s' is not a list" % sources) elements = [] for idx, source in enumerate(sources): source = ensure_is_dict(sources, idx, "path") source_path = source["path"] delimiter = source.get("delimiter") if has_variable_pattern(source_path): msg = "Path to CSV contains JMeter variable/function, can't check for file existence: %s" self.log.warning(msg, source_path) if not delimiter: delimiter = ',' self.log.warning("Can't detect CSV dialect, default delimiter will be '%s'", delimiter) else: modified_path = self.executor.engine.find_file(source_path) if not os.path.isfile(modified_path): raise TaurusConfigError("data-sources path not found: %s" % modified_path) if not delimiter: delimiter = self.__guess_delimiter(modified_path) source_path = get_full_path(modified_path) config = JMX._get_csv_config(source_path, delimiter, source.get("quoted", False), source.get("loop", True), source.get("variable-names", "")) elements.append(config) elements.append(etree.Element("hashTree")) return elements
def configure(self, jmx_file=None, load=None, settings=None, has_ctg=None): executor = MockJMeterExecutor(load, settings, has_ctg) executor.engine.config.merge({Provisioning.PROV: 'local'}) executor.install_required_tools() self.obj = LoadSettingsProcessor(executor) if jmx_file: self.jmx = JMX(jmx_file)
def compile_request(self, request): """ :type request: HierarchicHTTPRequest :return: """ sampler = children = None protocol_name = request.priority_option('protocol', default=self.default_protocol) if protocol_name in self.protocol_handlers: protocol = self.protocol_handlers[protocol_name] sampler, children = protocol.get_sampler_pair(request) if sampler is None: self.log.warning("Problematic request: %s", request.config) raise TaurusInternalException("Unable to handle request, please review missing options") children.extend(self._get_timer(request)) self.__add_assertions(children, request) timeout = ProtocolHandler.safe_time(request.priority_option('timeout')) if timeout is not None: children.append(JMX._get_dur_assertion(timeout)) children.append(etree.Element("hashTree")) self.__add_extractors(children, request) self.__add_jsr_elements(children, request) return [sampler, children]
def __add_regexp_ext(self, children, req): extractors = req.config.get("extract-regexp") for varname in extractors: cfg = ensure_is_dict(extractors, varname, "regexp") extractor = JMX._get_extractor(varname, cfg.get('subject', 'body'), cfg['regexp'], cfg.get('template', 1), cfg.get('match-no', 1), cfg.get('default', 'NOT_FOUND')) children.append(extractor) children.append(etree.Element("hashTree"))
def test_jmx_2tg(self): obj = JMeterExecutor() obj.engine = EngineEmul() obj.engine.config[Provisioning.PROV] = 'test' obj.execution.merge({ "concurrency": 1051, "ramp-up": 15, "iterations": 100, "scenario": {"script": __dir__() + "/../jmx/two_tg.jmx"} }) obj.prepare() jmx = JMX(obj.modified_jmx) selector = 'jmeterTestPlan>hashTree>hashTree>ThreadGroup' selector += '>stringProp[name=ThreadGroup\.num_threads]' thr = jmx.get(selector) self.assertEquals('420', thr[0].text) self.assertEquals('631', thr[1].text)
def compile_include_scenario_block(self, block): elements = [] controller = JMX._get_simple_controller(block.scenario_name) children = etree.Element("hashTree") scenario = self.executor.get_scenario(name=block.scenario_name) for element in self.compile_scenario(scenario): children.append(element) elements.extend([controller, children]) return elements
def compile_transaction_block(self, block): elements = [] controller = JMX._get_transaction_controller(block.name, block.priority_option('force-parent-sample', True)) children = etree.Element("hashTree") for compiled in self.compile_requests(block.requests): for element in compiled: children.append(element) elements.extend([controller, children]) return elements
def compile_http_request(self, request): """ :type request: HierarchicHTTPRequest :return: """ timeout = request.priority_option('timeout') if timeout is not None: timeout = self.smart_time(timeout) content_type = self._get_merged_ci_headers(request, 'content-type') if content_type == 'application/json' and isinstance(request.body, (dict, list)): body = json.dumps(request.body) else: body = request.body use_random_host_ip = request.priority_option('random-source-ip', default=False) host_ips = get_host_ips(filter_loopbacks=True) if use_random_host_ip else [] http = JMX._get_http_request(request.url, request.label, request.method, timeout, body, request.priority_option('keepalive', default=True), request.upload_files, request.content_encoding, request.priority_option('follow-redirects', default=True), use_random_host_ip, host_ips) children = etree.Element("hashTree") if request.headers: children.append(JMX._get_header_mgr(request.headers)) children.append(etree.Element("hashTree")) self.__add_think_time(children, request) self.__add_assertions(children, request) if timeout is not None: children.append(JMX._get_dur_assertion(timeout)) children.append(etree.Element("hashTree")) self.__add_extractors(children, request) self.__add_jsr_elements(children, request) return [http, children]
def _get_timer(req): think_time = req.get_think_time(full=True) if not think_time: return [] if not isinstance(think_time, list): # constant return JMX.get_constant_timer(delay=ProtocolHandler.safe_time(think_time)) mean = ProtocolHandler.safe_time(think_time[1]) dev = ProtocolHandler.safe_time(think_time[2]) if think_time[0] == "uniform": return JMX.get_uniform_timer(maximum=dev * 2, offset=mean - dev) elif think_time[0] == "gaussian": return JMX.get_gaussian_timer(dev=dev, offset=mean) elif think_time[0] == "poisson": return JMX.get_poisson_timer(lam=mean - dev, delay=dev) else: raise TaurusConfigError("Wrong timer type: %s" % think_time[0])
def __add_xpath_ext(self, children, req): xpath_extractors = req.config.get("extract-xpath") for varname in xpath_extractors: cfg = ensure_is_dict(xpath_extractors, varname, "xpath") children.append(JMX._get_xpath_extractor(varname, cfg['xpath'], cfg.get('default', 'NOT_FOUND'), cfg.get('validate-xml', False), cfg.get('ignore-whitespace', True), cfg.get('use-tolerant-parser', False))) children.append(etree.Element("hashTree"))
def __add_json_ext(self, children, req): jextractors = req.config.get("extract-jsonpath") for varname in jextractors: cfg = ensure_is_dict(jextractors, varname, "jsonpath") if LooseVersion(str(self.executor.settings.get("version"))) < LooseVersion("3.0"): extractor = JMX._get_json_extractor(varname, cfg["jsonpath"], cfg.get("default", "NOT_FOUND"), cfg.get("from-variable", None)) else: extractor = JMX._get_internal_json_extractor(varname, cfg["jsonpath"], cfg.get("default", "NOT_FOUND"), cfg.get("scope", None), cfg.get("from-variable", None), cfg.get("match-no", "0"), cfg.get("concat", False)) children.append(extractor) children.append(etree.Element("hashTree"))
def compile_while_block(self, block): elements = [] controller = JMX._get_while_controller(block.condition) children = etree.Element("hashTree") for compiled in self.compile_requests(block.requests): for element in compiled: children.append(element) elements.extend([controller, children]) return elements
def compile_if_block(self, block): elements = [] # TODO: pass jmeter IfController options if_controller = JMX._get_if_controller(block.condition) then_children = etree.Element("hashTree") for compiled in self.compile_requests(block.then_clause): for element in compiled: then_children.append(element) elements.extend([if_controller, then_children]) if block.else_clause: inverted_condition = "!(" + block.condition + ")" else_controller = JMX._get_if_controller(inverted_condition) else_children = etree.Element("hashTree") for compiled in self.compile_requests(block.else_clause): for element in compiled: else_children.append(element) elements.extend([else_controller, else_children]) return elements
def test_jmx_modification_unicode(self): obj = JMeterExecutor() obj.engine = EngineEmul() cfg_selector = ('Home Page>HTTPsampler.Arguments>Arguments.arguments' '>param>Argument.value') obj.execution.merge({ "scenario": { "script": __dir__() + "/../jmx/dummy_plan.jmx", "modifications": { "set-prop": { cfg_selector: u"✓", } } } }) selector = ("[testname='Home Page']>[name='HTTPsampler.Arguments']" ">[name='Arguments.arguments']>[name='param']>[name='Argument.value']") obj.prepare() jmx = JMX(obj.modified_jmx) self.assertEqual(jmx.get(selector)[0].text, u"✓")
def compile_set_variables_block(self, block): # pause current thread for 0s test_action = JMX._get_action_block(action_index=1, target_index=0, duration_ms=0) children = etree.Element("hashTree") fmt = "vars.put('%s', %r);" block.config["jsr223"] = [{ "language": "groovy", "execute": "before", "script-text": "\n".join(fmt % (var, expr) for var, expr in iteritems(block.mapping)) }] self.__add_jsr_elements(children, block) return [test_action, children]
def __gen_keystore_config(self, scenario): elements = [] keystore_config = scenario.get(self.FIELD_KEYSTORE_CONFIG) if keystore_config: variable_name = keystore_config["variable-name"] start_index = keystore_config["start-index"] end_index = keystore_config["end-index"] preload = keystore_config["preload"] config = JMX.get_keystore_config_elements(variable_name, start_index, end_index, preload) elements.append(config) elements.append(etree.Element("hashTree")) return elements
def __add_assertions(children, req): assertions = req.config.get("assert", []) for idx, assertion in enumerate(assertions): assertion = ensure_is_dict(assertions, idx, "contains") if not isinstance(assertion['contains'], list): assertion['contains'] = [assertion['contains']] children.append(JMX._get_resp_assertion(assertion.get("subject", Scenario.FIELD_BODY), assertion['contains'], assertion.get('regexp', True), assertion.get('not', False), assertion.get('assume-success', False))) children.append(etree.Element("hashTree")) jpath_assertions = req.config.get("assert-jsonpath", []) for idx, assertion in enumerate(jpath_assertions): assertion = ensure_is_dict(jpath_assertions, idx, "jsonpath") exc = TaurusConfigError('JSON Path not found in assertion: %s' % assertion) component = JMX._get_json_path_assertion(assertion.get('jsonpath', exc), assertion.get('expected-value', ''), assertion.get('validate', False), assertion.get('expect-null', False), assertion.get('invert', False), assertion.get('regexp', True)) children.append(component) children.append(etree.Element("hashTree")) xpath_assertions = req.config.get("assert-xpath", []) for idx, assertion in enumerate(xpath_assertions): assertion = ensure_is_dict(xpath_assertions, idx, "xpath") exc = TaurusConfigError('XPath not found in assertion: %s' % assertion) component = JMX._get_xpath_assertion(assertion.get('xpath', exc), assertion.get('validate-xml', False), assertion.get('ignore-whitespace', True), assertion.get('use-tolerant-parser', False), assertion.get('invert', False)) children.append(component) children.append(etree.Element("hashTree"))
def __add_boundary_ext(self, children, req): extractors = req.config.get("extract-boundary") for varname, cfg in iteritems(extractors): subj = cfg.get('subject', 'body') left = cfg.get('left', TaurusConfigError("Left boundary is missing for boundary extractor %s" % varname)) right = cfg.get('right', TaurusConfigError("Right boundary is missing for boundary extractor %s" % varname)) match_no = cfg.get('match-no', 1) defvalue = cfg.get('default', 'NOT_FOUND') scope = cfg.get("scope", None) from_var = cfg.get("from-variable", None) extractor = JMX._get_boundary_extractor(varname, subj, left, right, match_no, defvalue, scope, from_var) children.append(extractor) children.append(etree.Element("hashTree"))
def compile_foreach_block(self, block): """ :type block: ForEachBlock """ elements = [] controller = JMX._get_foreach_controller(block.input_var, block.loop_var) children = etree.Element("hashTree") for compiled in self.compile_requests(block.requests): for element in compiled: children.append(element) elements.extend([controller, children]) return elements
def __add_jsr_elements(children, req): """ :type children: etree.Element :type req: Request """ jsrs = req.config.get("jsr223", []) if not isinstance(jsrs, list): jsrs = [jsrs] for idx, _ in enumerate(jsrs): jsr = ensure_is_dict(jsrs, idx, default_key='script-text') lang = jsr.get("language", "groovy") script_file = jsr.get("script-file", None) script_text = jsr.get("script-text", None) if not script_file and not script_text: raise TaurusConfigError("jsr223 element must specify one of 'script-file' or 'script-text'") parameters = jsr.get("parameters", "") execute = jsr.get("execute", "after") children.append(JMX._get_jsr223_element(lang, script_file, parameters, execute, script_text)) children.append(etree.Element("hashTree"))
def __add_boundary_ext(self, children, req): extractors = req.config.get("extract-boundary") for varname, cfg in iteritems(extractors): subj = cfg.get('subject', 'body') left = cfg.get( 'left', TaurusConfigError( "Left boundary is missing for boundary extractor %s" % varname)) right = cfg.get( 'right', TaurusConfigError( "Right boundary is missing for boundary extractor %s" % varname)) match_no = cfg.get('match-no', 1) defvalue = cfg.get('default', 'NOT_FOUND') extractor = JMX._get_boundary_extractor(varname, subj, left, right, match_no, defvalue) children.append(extractor) children.append(etree.Element("hashTree"))
def __generate(self): """ Generate the test plan """ thread_group = JMX.get_thread_group(testname=self.executor.label) thread_group_ht = etree.Element("hashTree", type="tg") # NOTE: set realistic dns-cache and JVM prop by default? self.request_compiler = RequestCompiler(self) for element in self.compile_scenario(self.scenario): thread_group_ht.append(element) results_tree = self._get_results_tree() results_tree_ht = etree.Element("hashTree") self.append(self.TEST_PLAN_SEL, thread_group) self.append(self.TEST_PLAN_SEL, thread_group_ht) self.append(self.TEST_PLAN_SEL, results_tree) self.append(self.TEST_PLAN_SEL, results_tree_ht)
def compile_action_block(self, block): """ :type block: ActionBlock :return: """ actions = { 'stop': 0, 'pause': 1, 'stop-now': 2, 'continue': 3, } targets = {'current-thread': 0, 'all-threads': 2} action = actions[block.action] target = targets[block.target] duration = 0 if block.duration is not None: duration = int(block.duration * 1000) test_action = JMX._get_action_block(action, target, duration) children = etree.Element("hashTree") self.__add_jsr_elements(children, block) return [test_action, children]
def __gen_datasources(self, scenario): sources = scenario.get("data-sources", []) if not sources: return [] if not isinstance(sources, list): raise TaurusConfigError("data-sources '%s' is not a list" % sources) elements = [] for idx, source in enumerate(sources): source = ensure_is_dict(sources, idx, "path") source_path = source["path"] jmeter_var_pattern = re.compile("\${.+\}") delimiter = source.get('delimiter', None) if jmeter_var_pattern.search(source_path): self.log.warning( "Path to CSV contains JMeter variable/function, can't check for file existence: %s", source_path) if not delimiter: delimiter = ',' self.log.warning( "Can't detect CSV dialect, default delimiter will be '%s'", delimiter) else: modified_path = self.executor.engine.find_file(source_path) if not os.path.isfile(modified_path): raise TaurusConfigError("data-sources path not found: %s" % modified_path) if not delimiter: delimiter = self.__guess_delimiter(modified_path) source_path = get_full_path(modified_path) config = JMX._get_csv_config(source_path, delimiter, source.get("quoted", False), source.get("loop", True), source.get("variable-names", "")) elements.append(config) elements.append(etree.Element("hashTree")) return elements
def __add_jsr_elements(children, req): """ :type children: etree.Element :type req: Request """ jsrs = req.config.get("jsr223", []) if not isinstance(jsrs, list): jsrs = [jsrs] for idx, _ in enumerate(jsrs): jsr = ensure_is_dict(jsrs, idx, default_key='script-text') lang = jsr.get("language", "groovy") script_file = jsr.get("script-file", None) script_text = jsr.get("script-text", None) if not script_file and not script_text: raise TaurusConfigError("jsr223 element must specify one of 'script-file' or 'script-text'") parameters = jsr.get("parameters", "") execute = jsr.get("execute", "after") cache_key = str(jsr.get("compile-cache", True)).lower() children.append(JMX._get_jsr223_element(lang, script_file, parameters, execute, script_text, cache_key)) children.append(etree.Element("hashTree"))
def __gen_data_sources(self, scenario): elements = [] for source in scenario.get_data_sources(): source_path = source["path"] delimiter = source.get("delimiter") if has_variable_pattern(source_path): msg = "Path to CSV contains JMeter variable/function, can't check for file existence: %s" self.log.warning(msg, source_path) if not delimiter: delimiter = ',' self.log.warning("Can't detect CSV dialect, default delimiter will be '%s'", delimiter) else: source_path = self.executor.engine.find_file(source_path) if not os.path.isfile(source_path): raise TaurusConfigError("data-sources path not found: %s" % source_path) if not delimiter: delimiter = guess_delimiter(source_path) config = JMX._get_csv_config(source_path, delimiter, source.get("quoted", False), source.get("loop", True), source.get("variable-names", "")) elements.append(config) elements.append(etree.Element("hashTree")) return elements
def __add_think_time(self, children, req): think_time = req.priority_option('think-time') if think_time is not None: children.append( JMX._get_constant_timer(self.smart_time(think_time))) children.append(etree.Element("hashTree"))
def configure(self, jmx_file=None, load=None, settings=None, has_ctg=None): executor = MockJMeterExecutor(load, settings, has_ctg) executor.engine.config.merge({Provisioning.PROV: 'local'}) self.obj = LoadSettingsProcessor(executor) if jmx_file: self.jmx = JMX(jmx_file)
def compile_set_variables_block(block): set_var_action = JMX.get_set_var_action(block.mapping) hashtree = etree.Element("hashTree") return [set_var_action, hashtree]
def test_jmx_unicode_checkmark(self): obj = JMX() res = obj._get_http_request("url", "label", "method", 0, {"param": u"✓"}, True) prop = res.find(".//stringProp[@name='Argument.value']") self.assertNotEqual("BINARY", prop.text) self.assertEqual(u"✓", prop.text)
class TestLoadSettingsProcessor(BZTestCase): def configure(self, jmx_file=None, load=None, settings=None, has_ctg=None): executor = MockJMeterExecutor(settings, has_ctg) executor.engine = EngineEmul() executor.configure(load=load) executor.engine.config.merge({Provisioning.PROV: 'local'}) executor.install_required_tools() self.obj = LoadSettingsProcessor(executor) if jmx_file: self.jmx = JMX(jmx_file) def get_groupset(self, testname=None): groupset = [] for group in self.obj.tg_handler.groups(self.jmx): # 'testname == None' means 'get all groups' if not testname or (testname and group.element.attrib['testname'] == testname): groupset.append(group) return groupset def test_keep_original(self): self.configure(jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx') self.assertEqual(LoadSettingsProcessor.TG, self.obj.tg) # because no duration self.sniff_log(self.obj.log) self.obj.modify(self.jmx) msg = "No iterations/concurrency/duration found, thread group modification is skipped" self.assertIn(msg, self.log_recorder.debug_buff.getvalue()) groupset = self.get_groupset() groups = [group.gtype for group in groupset] self.assertEqual(5, len(set(groups))) # no one group was modified self.assertEqual("", self.log_recorder.warn_buff.getvalue()) res_values = {} for group in groupset: res_values[group.get_testname()] = { 'conc': group.get_concurrency(), 'rate': group.get_rate(), 'duration': group.get_duration(), 'iterations': group.get_iterations()} self.assertEqual(res_values, {'TG.01': {'conc': 2, 'duration': 3, 'iterations': 100, 'rate': 1}, 'CTG.02': {'conc': 3, 'duration': 100, 'iterations': 10, 'rate': 1}, 'STG.03': {'conc': 4, 'duration': None, 'iterations': None, 'rate': 1}, 'UTG.04': {'conc': 1, 'duration': None, 'iterations': None, 'rate': 1}, 'ATG.05': {'conc': 1, 'duration': 480, 'iterations': 33, 'rate': 2}}) def test_TG_cs(self): """ ThreadGroup: concurrency, steps """ self.configure(load={'concurrency': 76, 'steps': 5}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx') self.assertEqual(LoadSettingsProcessor.TG, self.obj.tg) # because no duration self.sniff_log(self.obj.log) self.obj.modify(self.jmx) msg = 'UltimateThreadGroup: getting of concurrency is impossible (not implemented)' self.assertIn(msg, self.log_recorder.debug_buff.getvalue()) msg = "Had to add 1 more threads to maintain thread group proportion" self.assertIn(msg, self.log_recorder.warn_buff.getvalue()) msg = "Stepping ramp-up isn't supported for regular ThreadGroup" self.assertIn(msg, self.log_recorder.warn_buff.getvalue()) res_values = {} for group in self.get_groupset(): self.assertEqual('ThreadGroup', group.gtype) self.assertEqual("false", group.element.find(".//*[@name='LoopController.continue_forever']").text) self.assertEqual("-1", group.element.find(".//*[@name='LoopController.loops']").text) # no loop limit res_values[group.get_testname()] = { 'conc': group.get_concurrency(), 'on_error': group.get_on_error(), 'delay': group.get_scheduler_delay()} self.assertEqual(res_values, {'TG.01': {'conc': 14, 'on_error': 'startnextloop', 'delay': '33'}, 'CTG.02': {'conc': 21, 'on_error': 'stopthread', 'delay': None}, 'STG.03': {'conc': 28, 'on_error': 'stoptest', 'delay': None}, 'UTG.04': {'conc': 7, 'on_error': 'stoptestnow', 'delay': None}, 'ATG.05': {'conc': 7, 'on_error': 'continue', 'delay': None}}) def test_CTG_crs(self): """ ConcurrencyThreadGroup: concurrency, ramp-up, steps """ self.configure(load={'concurrency': 71, 'ramp-up': 103, 'steps': 5, "throughput": 52}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx') self.assertEqual(LoadSettingsProcessor.CTG, self.obj.tg) self.sniff_log(self.obj.log) self.obj.modify(self.jmx) msg = 'UltimateThreadGroup: getting of concurrency is impossible (not implemented)' self.assertIn(msg, self.log_recorder.debug_buff.getvalue()) msg = "1 threads left undistributed due to thread group proportion" self.assertIn(msg, self.log_recorder.warn_buff.getvalue()) res_values = {} for group in self.get_groupset(): self.assertEqual(group.gtype, "ConcurrencyThreadGroup") self.assertEqual("5", group.element.find(".//*[@name='Steps']").text) self.assertEqual("103", group.element.find(".//*[@name='RampUp']").text) self.assertEqual("S", group.element.find(".//*[@name='Unit']").text) self.assertIn(group.element.find(".//*[@name='Hold']").text, ("", "0")) res_values[group.get_testname()] = {'conc': group.get_concurrency(), 'on_error': group.get_on_error()} self.assertEqual(res_values, {'TG.01': {'conc': 13, 'on_error': 'startnextloop'}, 'CTG.02': {'conc': 19, 'on_error': 'stopthread'}, 'STG.03': {'conc': 26, 'on_error': 'stoptest'}, 'UTG.04': {'conc': 6, 'on_error': 'stoptestnow'}, 'ATG.05': {'conc': 6, 'on_error': 'continue'}}) self.assertListEqual(self._get_tst_schedule(), [['10.4', '10.4', '20'], ['20.8', '20.8', '21'], ['31.2', '31.2', '20'], ['41.6', '41.6', '21'], ['52.0', '52.0', '21']]) def test_CTG_null_iterations(self): """ ConcurrencyThreadGroup: concurrency, ramp-up, steps """ self.configure(load={'hold-for': 103}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/null-iterations.jmx') self.assertEqual(LoadSettingsProcessor.CTG, self.obj.tg) self.sniff_log(self.obj.log) self.obj.modify(self.jmx) msg = "Parsing iterations 'None' in group 'ConcurrencyThreadGroup' failed" self.assertNotIn(msg, self.log_recorder.warn_buff.getvalue()) res_values = {} for group in self.get_groupset(): self.assertEqual(group.gtype, "ConcurrencyThreadGroup") self.assertEqual("", group.element.find(".//*[@name='Iterations']").text) self.assertIn(group.element.find(".//*[@name='Hold']").text, ("103",)) res_values[group.get_testname()] = {'conc': group.get_concurrency(), 'on_error': group.get_on_error()} self.assertEqual({'CTG.02': {'conc': 3, 'on_error': 'stopthread'}}, res_values, ) def test_CTG_prop_rs(self): """ ConcurrencyThreadGroup: properties in ramp-up, steps """ self.configure(load={'ramp-up': '${__P(r)}', 'steps': '${__P(s)}'}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx') self.assertEqual(LoadSettingsProcessor.CTG, self.obj.tg) self.obj.modify(self.jmx) res_values = {} for group in self.get_groupset(): self.assertEqual(group.gtype, "ConcurrencyThreadGroup") self.assertEqual("${__P(s)}", group.element.find(".//*[@name='Steps']").text) self.assertEqual("${__P(r)}", group.element.find(".//*[@name='RampUp']").text) self.assertIn(group.element.find(".//*[@name='Hold']").text, ("", "0")) res_values[group.get_testname()] = group.get_concurrency() self.assertEqual(res_values, {'TG.01': 2, 'CTG.02': 3, 'STG.03': 4, 'UTG.04': 1, 'ATG.05': 1}) def test_CTG_prop_trh(self): """ ConcurrencyThreadGroup: properties in throughput, ramp-up, hold-for """ self.configure(load={'ramp-up': '${__P(r)}', 'throughput': '${__P(t)}', 'hold-for': '${__P(h)}'}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx') self.assertEqual(LoadSettingsProcessor.CTG, self.obj.tg) self.obj.modify(self.jmx) self.assertListEqual(self._get_tst_schedule(), [["1.0", "${__P(t)}", "${__P(r)}"], ["${__P(t)}", "${__P(t)}", "${__P(h)}"]], ) def _get_tst_schedule(self): records = [] shaper_elements = self.jmx.get("kg\.apc\.jmeter\.timers\.VariableThroughputTimer") self.assertEqual(1, len(shaper_elements)) shaper_collection = shaper_elements[0].find(".//collectionProp[@name='load_profile']") coll_elements = shaper_collection.findall(".//collectionProp") for expected in coll_elements: item = [] strings0 = expected.findall(".//stringProp") for rec in strings0: item.append(rec.text) records.append(item) return records def test_TG_prop_cih(self): """ ThreadGroup: properties in concurrency, hold-for, iterations """ self.configure(load={'concurrency': '${__P(c)}', 'hold-for': '${__P(h)}', 'iterations': '${__P(i)}'}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx') self.assertEqual(LoadSettingsProcessor.TG, self.obj.tg) self.obj.modify(self.jmx) for group in self.get_groupset(): self.assertEqual(group.gtype, "ThreadGroup") self.assertEqual("${__P(c)}", group.element.find(".//*[@name='ThreadGroup.num_threads']").text) self.assertEqual("${__P(i)}", group.element.find(".//*[@name='LoopController.loops']").text) self.assertEqual("${__P(h)}", group.element.find(".//*[@name='ThreadGroup.duration']").text) def test_TG_prop_rh(self): """ ThreadGroup: properties in ramp-up, hold-for """ self.configure(load={'ramp-up': '${__P(r)}', 'hold-for': '${__P(h)}'}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx', has_ctg=False) self.assertEqual(LoadSettingsProcessor.TG, self.obj.tg) self.obj.modify(self.jmx) for group in self.get_groupset(): self.assertEqual(group.gtype, "ThreadGroup") delay = group.element.find(".//boolProp[@name='ThreadGroup.delayedStart']") self.assertIsNone(delay) self.assertEqual("-1", group.element.find(".//*[@name='LoopController.loops']").text) self.assertEqual("${__P(r)}", group.element.find(".//*[@name='ThreadGroup.ramp_time']").text) self.assertEqual("${__intSum(${__P(r)},${__P(h)})}", group.element.find(".//*[@name='ThreadGroup.duration']").text) def test_CTG_h(self): """ ConcurrencyThreadGroup: hold-for """ self.configure(load={'hold-for': 70.5}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx') self.assertEqual(LoadSettingsProcessor.CTG, self.obj.tg) self.obj.modify(self.jmx) res_values = {} for group in self.get_groupset(): self.assertEqual("70", group.element.find(".//*[@name='Hold']").text) res_values[group.get_testname()] = {'conc': group.get_concurrency(), 'iterations': group.get_iterations()} self.assertEqual(res_values, { 'TG.01': {'conc': 2, 'iterations': None}, 'CTG.02': {'conc': 3, 'iterations': 10}, 'STG.03': {'conc': 4, 'iterations': None}, 'UTG.04': {'conc': 1, 'iterations': None}, 'ATG.05': {'conc': 1, 'iterations': None}}) def test_TG_ci(self): """ ThreadGroup: concurrency, iterations """ self.configure(load={'concurrency': 1, 'iterations': 7}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx') self.assertEqual(LoadSettingsProcessor.TG, self.obj.tg) self.obj.modify(self.jmx) for group in self.get_groupset(): self.assertEqual(1, group.get_concurrency()) self.assertEqual("false", group.element.find(".//*[@name='ThreadGroup.scheduler']").text) self.assertEqual("7", group.element.find(".//*[@name='LoopController.loops']").text) def test_TG_hr(self): """ ThreadGroup: hold-for, ramp-up, no plugin """ self.configure(load={'ramp-up': 10, 'hold-for': 20}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx', has_ctg=False) self.assertEqual(LoadSettingsProcessor.TG, self.obj.tg) self.obj.modify(self.jmx) res_values = {} for group in self.get_groupset(): self.assertEqual("true", group.element.find(".//*[@name='ThreadGroup.scheduler']").text) self.assertEqual("true", group.element.find(".//*[@name='ThreadGroup.scheduler']").text) self.assertEqual(str(10 + 20), group.element.find(".//*[@name='ThreadGroup.duration']").text) self.assertEqual("-1", group.element.find(".//*[@name='LoopController.loops']").text) res_values[group.get_testname()] = group.get_concurrency() self.assertEqual(res_values, {'TG.01': 2, 'CTG.02': 3, 'STG.03': 4, 'UTG.04': 1, 'ATG.05': 1})
def test_no_port(self): obj = JMX() res = obj._get_http_request("http://hostname", "label", "method", 0, {}, True) self.assertEqual("", res.find(".//stringProp[@name='HTTPSampler.path']").text) self.assertEqual("hostname", res.find(".//stringProp[@name='HTTPSampler.domain']").text) self.assertEqual("", res.find(".//stringProp[@name='HTTPSampler.port']").text)
def test_variable_hostname(self): obj = JMX() res = obj._get_http_request("http://${hostName}:${Port}/${Path}", "label", "method", 0, {}, True) self.assertEqual("/${Path}", res.find(".//stringProp[@name='HTTPSampler.path']").text) self.assertEqual("${hostName}", res.find(".//stringProp[@name='HTTPSampler.domain']").text) self.assertEqual("${Port}", res.find(".//stringProp[@name='HTTPSampler.port']").text)
def test_int_udv(self): res = JMX() data = {"varname2": "1", "varname": 1, 2: 3} res.add_user_def_vars_elements(data)
class TestLoadSettingsProcessor(BZTestCase): def configure(self, jmx_file=None, load=None, settings=None, has_ctg=None): executor = MockJMeterExecutor(load, settings, has_ctg) executor.engine.config.merge({Provisioning.PROV: 'local'}) self.obj = LoadSettingsProcessor(executor) if jmx_file: self.jmx = JMX(jmx_file) def get_groupset(self, testname=None): groupset = [] for group in self.obj.tg_handler.groups(self.jmx): # 'testname == None' means 'get all groups' if not testname or (testname and group.element.attrib['testname'] == testname): groupset.append(group) return groupset def test_keep_original(self): self.configure(jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx') self.assertEqual(LoadSettingsProcessor.TG, self.obj.tg) # because no duration self.sniff_log(self.obj.log) self.obj.modify(self.jmx) msg = "No iterations/concurrency/duration found, thread group modification is skipped" self.assertIn(msg, self.log_recorder.debug_buff.getvalue()) groupset = self.get_groupset() groups = [group.gtype for group in groupset] self.assertEqual(4, len(set(groups))) # no one group was modified self.assertEqual("", self.log_recorder.warn_buff.getvalue()) def test_TG_cs(self): """ ThreadGroup: concurrency, steps """ self.configure(load={'concurrency': 69, 'steps': 5}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx') self.assertEqual(LoadSettingsProcessor.TG, self.obj.tg) # because no duration self.sniff_log(self.obj.log) self.obj.modify(self.jmx) msg = 'Getting of concurrency for UltimateThreadGroup not implemented' self.assertIn(msg, self.log_recorder.warn_buff.getvalue()) msg = "Had to add 1 more threads to maintain thread group proportion" self.assertIn(msg, self.log_recorder.warn_buff.getvalue()) msg = "Stepping ramp-up isn't supported for regular ThreadGroup" self.assertIn(msg, self.log_recorder.warn_buff.getvalue()) res_values = {} for group in self.get_groupset(): self.assertEqual('ThreadGroup', group.gtype) self.assertEqual("false", group.element.find(".//*[@name='LoopController.continue_forever']").text) self.assertEqual("-1", group.element.find(".//*[@name='LoopController.loops']").text) # no loop limit res_values[group.get_testname()] = {'conc': group.get_concurrency(), 'on_error': group.get_on_error()} self.assertEqual(res_values, {'TG.01': {'conc': 14, 'on_error': 'startnextloop'}, 'CTG.02': {'conc': 21, 'on_error': 'stopthread'}, 'STG.03': {'conc': 28, 'on_error': 'stoptest'}, 'UTG.04': {'conc': 7, 'on_error': 'stoptestnow'}}) def test_CTG_crs(self): """ ConcurrencyThreadGroup: concurrency, ramp-up, steps """ self.configure(load={'concurrency': 71, 'ramp-up': 100, 'steps': 5}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx') self.assertEqual(LoadSettingsProcessor.CTG, self.obj.tg) self.sniff_log(self.obj.log) self.obj.modify(self.jmx) msg = 'Getting of concurrency for UltimateThreadGroup not implemented' self.assertIn(msg, self.log_recorder.warn_buff.getvalue()) msg = "1 threads left undistributed due to thread group proportion" self.assertIn(msg, self.log_recorder.warn_buff.getvalue()) res_values = {} for group in self.get_groupset(): self.assertEqual(group.gtype, "ConcurrencyThreadGroup") self.assertEqual("5", group.element.find(".//*[@name='Steps']").text) self.assertEqual("100", group.element.find(".//*[@name='RampUp']").text) self.assertEqual("S", group.element.find(".//*[@name='Unit']").text) self.assertIn(group.element.find(".//*[@name='Hold']").text, ("", "0")) res_values[group.get_testname()] = {'conc': group.get_concurrency(), 'on_error': group.get_on_error()} self.assertEqual(res_values, {'TG.01': {'conc': 14, 'on_error': 'startnextloop'}, 'CTG.02': {'conc': 21, 'on_error': 'stopthread'}, 'STG.03': {'conc': 28, 'on_error': 'stoptest'}, 'UTG.04': {'conc': 7, 'on_error': 'stoptestnow'}}) def test_CTG_prop_rs(self): """ ConcurrencyThreadGroup: properties in ramp-up, steps """ self.configure(load={'ramp-up': '${__P(r)}', 'steps': '${__P(s)}'}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx') self.assertEqual(LoadSettingsProcessor.CTG, self.obj.tg) self.obj.modify(self.jmx) res_values = {} for group in self.get_groupset(): self.assertEqual(group.gtype, "ConcurrencyThreadGroup") self.assertEqual("${__P(s)}", group.element.find(".//*[@name='Steps']").text) self.assertEqual("${__P(r)}", group.element.find(".//*[@name='RampUp']").text) self.assertIn(group.element.find(".//*[@name='Hold']").text, ("", "0")) res_values[group.get_testname()] = group.get_concurrency() self.assertEqual(res_values, {'TG.01': 2, 'CTG.02': 3, 'STG.03': 4, 'UTG.04': 1}) def test_CTG_prop_trh(self): """ ConcurrencyThreadGroup: properties in throughput, ramp-up, hold-for """ self.configure(load={'ramp-up': '${__P(r)}', 'throughput': '${__P(t)}', 'hold-for': '${__P(h)}'}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx') self.assertEqual(LoadSettingsProcessor.CTG, self.obj.tg) self.obj.modify(self.jmx) shaper_elements = self.jmx.get("kg\.apc\.jmeter\.timers\.VariableThroughputTimer") self.assertEqual(1, len(shaper_elements)) shaper_collection = shaper_elements[0].find(".//collectionProp[@name='load_profile']") coll_elements = shaper_collection.findall(".//collectionProp") self.assertEqual(2, len(coll_elements)) strings0 = coll_elements[0].findall(".//stringProp") self.assertEqual("1", strings0[0].text) self.assertEqual("${__P(t)}", strings0[1].text) self.assertEqual("${__P(r)}", strings0[2].text) strings1 = coll_elements[1].findall(".//stringProp") self.assertEqual("${__P(t)}", strings1[0].text) self.assertEqual("${__P(t)}", strings1[1].text) self.assertEqual("${__P(h)}", strings1[2].text) def test_TG_prop_cih(self): """ ThreadGroup: properties in concurrency, hold-for, iterations """ self.configure(load={'concurrency': '${__P(c)}', 'hold-for': '${__P(h)}', 'iterations': '${__P(i)}'}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx') self.assertEqual(LoadSettingsProcessor.TG, self.obj.tg) self.obj.modify(self.jmx) for group in self.get_groupset(): self.assertEqual(group.gtype, "ThreadGroup") self.assertEqual("${__P(c)}", group.element.find(".//*[@name='ThreadGroup.num_threads']").text) self.assertEqual("${__P(i)}", group.element.find(".//*[@name='LoopController.loops']").text) self.assertEqual("${__P(h)}", group.element.find(".//*[@name='ThreadGroup.duration']").text) def test_TG_prop_rh(self): """ ThreadGroup: properties in ramp-up, hold-for """ self.configure(load={'ramp-up': '${__P(r)}', 'hold-for': '${__P(h)}'}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx', has_ctg=False) self.assertEqual(LoadSettingsProcessor.TG, self.obj.tg) self.obj.modify(self.jmx) for group in self.get_groupset(): self.assertEqual(group.gtype, "ThreadGroup") self.assertEqual("-1", group.element.find(".//*[@name='LoopController.loops']").text) self.assertEqual("${__P(r)}", group.element.find(".//*[@name='ThreadGroup.ramp_time']").text) self.assertEqual("${__intSum(${__P(r)},${__P(h)})}", group.element.find(".//*[@name='ThreadGroup.duration']").text) def test_CTG_h(self): """ ConcurrencyThreadGroup: hold-for """ self.configure(load={'hold-for': 70.5}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx') self.assertEqual(LoadSettingsProcessor.CTG, self.obj.tg) self.obj.modify(self.jmx) res_values = {} for group in self.get_groupset(): self.assertEqual("70", group.element.find(".//*[@name='Hold']").text) res_values[group.get_testname()] = group.get_concurrency() self.assertEqual(res_values, {'TG.01': 2, 'CTG.02': 3, 'STG.03': 4, 'UTG.04': 1}) def test_TG_ci(self): """ ThreadGroup: concurrency, iterations """ self.configure(load={'concurrency': 1, 'iterations': 7}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx') self.assertEqual(LoadSettingsProcessor.TG, self.obj.tg) self.obj.modify(self.jmx) for group in self.get_groupset(): self.assertEqual(1, group.get_concurrency()) self.assertEqual("false", group.element.find(".//*[@name='ThreadGroup.scheduler']").text) self.assertEqual("7", group.element.find(".//*[@name='LoopController.loops']").text) def test_TG_hr(self): """ ThreadGroup: hold-for, ramp-up, no plugin """ self.configure(load={'ramp-up': 10, 'hold-for': 20}, jmx_file=RESOURCES_DIR + 'jmeter/jmx/threadgroups.jmx', has_ctg=False) self.assertEqual(LoadSettingsProcessor.TG, self.obj.tg) self.obj.modify(self.jmx) res_values = {} for group in self.get_groupset(): self.assertEqual("true", group.element.find(".//*[@name='ThreadGroup.scheduler']").text) self.assertEqual("true", group.element.find(".//*[@name='ThreadGroup.scheduler']").text) self.assertEqual(str(10 + 20), group.element.find(".//*[@name='ThreadGroup.duration']").text) self.assertEqual("-1", group.element.find(".//*[@name='LoopController.loops']").text) res_values[group.get_testname()] = group.get_concurrency() self.assertEqual(res_values, {'TG.01': 2, 'CTG.02': 3, 'STG.03': 4, 'UTG.04': 1})
def test_param_url(self): obj = JMX() res = obj._get_http_request(url="https://example_url.net/Xhtml;jsessionid=XXXX:XXXX?JacadaApplicationName=XXXX", label="label", method="method", timeout=0, body={}, keepalive=True) self.assertEqual("/Xhtml;jsessionid=XXXX:XXXX?JacadaApplicationName=XXXX", res.find(".//stringProp[@name='HTTPSampler.path']").text)
def __add_think_time(self, children, req): think_time = req.priority_option('think-time') if think_time is not None: children.append(JMX._get_constant_timer(self.smart_time(think_time))) children.append(etree.Element("hashTree"))
def test_source_ips_multiple(self): obj = JMX() res = obj._get_http_request("/", "label", "method", 0, {}, True, use_random_host_ip=True, host_ips=["192.168.1.1", "192.168.1.2"]) self.assertEqual("${__chooseRandom(192.168.1.1,192.168.1.2,randomAddr)}", res.find(".//stringProp[@name='HTTPSampler.ipSource']").text)