jmeter_perf 1.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +19 -0
- data/.standard.yml +4 -0
- data/CHANGELOG.md +18 -0
- data/DSL.md +235 -0
- data/README.md +24 -0
- data/Rakefile +12 -0
- data/example/Gemfile +39 -0
- data/example/Gemfile.lock +232 -0
- data/example/README.md +3 -0
- data/example/Rakefile +6 -0
- data/example/app/controllers/application_controller.rb +2 -0
- data/example/app/controllers/test_controller.rb +15 -0
- data/example/bin/bundle +109 -0
- data/example/bin/docker-entrypoint +8 -0
- data/example/bin/rails +4 -0
- data/example/bin/rake +4 -0
- data/example/bin/setup +33 -0
- data/example/config/application.rb +44 -0
- data/example/config/boot.rb +3 -0
- data/example/config/credentials.yml.enc +1 -0
- data/example/config/database.yml +25 -0
- data/example/config/environment.rb +5 -0
- data/example/config/environments/development.rb +64 -0
- data/example/config/environments/production.rb +82 -0
- data/example/config/environments/test.rb +61 -0
- data/example/config/initializers/cors.rb +16 -0
- data/example/config/initializers/filter_parameter_logging.rb +8 -0
- data/example/config/initializers/inflections.rb +16 -0
- data/example/config/locales/en.yml +31 -0
- data/example/config/puma.rb +35 -0
- data/example/config/routes.rb +5 -0
- data/example/config.ru +7 -0
- data/example/fast.log +49 -0
- data/example/jmeter.log +28 -0
- data/example/lib/tasks/test.rake +40 -0
- data/example/log/.keep +0 -0
- data/example/public/robots.txt +1 -0
- data/example/random.log +49 -0
- data/example/slow.log +49 -0
- data/example/vendor/.keep +0 -0
- data/lib/Rakefile +4 -0
- data/lib/jmeter_perf/dsl/access_log_sampler.rb +38 -0
- data/lib/jmeter_perf/dsl/aggregate_graph.rb +61 -0
- data/lib/jmeter_perf/dsl/aggregate_report.rb +61 -0
- data/lib/jmeter_perf/dsl/ajp13_sampler.rb +47 -0
- data/lib/jmeter_perf/dsl/assertion_results.rb +61 -0
- data/lib/jmeter_perf/dsl/bean_shell_assertion.rb +34 -0
- data/lib/jmeter_perf/dsl/bean_shell_listener.rb +34 -0
- data/lib/jmeter_perf/dsl/bean_shell_postprocessor.rb +34 -0
- data/lib/jmeter_perf/dsl/bean_shell_preprocessor.rb +34 -0
- data/lib/jmeter_perf/dsl/bean_shell_sampler.rb +34 -0
- data/lib/jmeter_perf/dsl/bean_shell_timer.rb +34 -0
- data/lib/jmeter_perf/dsl/bsf_assertion.rb +34 -0
- data/lib/jmeter_perf/dsl/bsf_listener.rb +34 -0
- data/lib/jmeter_perf/dsl/bsf_postprocessor.rb +34 -0
- data/lib/jmeter_perf/dsl/bsf_preprocessor.rb +34 -0
- data/lib/jmeter_perf/dsl/bsf_sampler.rb +34 -0
- data/lib/jmeter_perf/dsl/bsf_timer.rb +34 -0
- data/lib/jmeter_perf/dsl/compare_assertion.rb +33 -0
- data/lib/jmeter_perf/dsl/comparison_assertion_visualizer.rb +61 -0
- data/lib/jmeter_perf/dsl/constant_throughput_timer.rb +32 -0
- data/lib/jmeter_perf/dsl/constant_timer.rb +31 -0
- data/lib/jmeter_perf/dsl/counter.rb +37 -0
- data/lib/jmeter_perf/dsl/css_jquery_extractor.rb +37 -0
- data/lib/jmeter_perf/dsl/csv_data_set_config.rb +39 -0
- data/lib/jmeter_perf/dsl/debug_postprocessor.rb +34 -0
- data/lib/jmeter_perf/dsl/debug_sampler.rb +33 -0
- data/lib/jmeter_perf/dsl/distribution_graphalpha.rb +61 -0
- data/lib/jmeter_perf/dsl/duration_assertion.rb +31 -0
- data/lib/jmeter_perf/dsl/for_each_controller.rb +33 -0
- data/lib/jmeter_perf/dsl/ftp_request.rb +40 -0
- data/lib/jmeter_perf/dsl/ftp_request_defaults.rb +38 -0
- data/lib/jmeter_perf/dsl/gaussian_random_timer.rb +32 -0
- data/lib/jmeter_perf/dsl/generate_summary_results.rb +29 -0
- data/lib/jmeter_perf/dsl/graph_results.rb +61 -0
- data/lib/jmeter_perf/dsl/html_assertion.rb +36 -0
- data/lib/jmeter_perf/dsl/html_link_parser.rb +29 -0
- data/lib/jmeter_perf/dsl/html_parameter_mask.rb +38 -0
- data/lib/jmeter_perf/dsl/http_authorization_manager.rb +39 -0
- data/lib/jmeter_perf/dsl/http_cache_manager.rb +32 -0
- data/lib/jmeter_perf/dsl/http_cookie_manager.rb +34 -0
- data/lib/jmeter_perf/dsl/http_header_manager.rb +36 -0
- data/lib/jmeter_perf/dsl/http_request.rb +47 -0
- data/lib/jmeter_perf/dsl/http_request_defaults.rb +53 -0
- data/lib/jmeter_perf/dsl/http_url_rewriting_modifier.rb +36 -0
- data/lib/jmeter_perf/dsl/if_controller.rb +33 -0
- data/lib/jmeter_perf/dsl/include_controller.rb +31 -0
- data/lib/jmeter_perf/dsl/j_unit_request.rb +43 -0
- data/lib/jmeter_perf/dsl/java_request.rb +75 -0
- data/lib/jmeter_perf/dsl/java_request_defaults.rb +75 -0
- data/lib/jmeter_perf/dsl/jdbc_connection_configuration.rb +43 -0
- data/lib/jmeter_perf/dsl/jdbc_postprocessor.rb +39 -0
- data/lib/jmeter_perf/dsl/jdbc_preprocessor.rb +39 -0
- data/lib/jmeter_perf/dsl/jdbc_request.rb +39 -0
- data/lib/jmeter_perf/dsl/jms_pointto_point.rb +47 -0
- data/lib/jmeter_perf/dsl/jms_publisher.rb +49 -0
- data/lib/jmeter_perf/dsl/jms_subscriber.rb +41 -0
- data/lib/jmeter_perf/dsl/json_path_postprocessor.rb +33 -0
- data/lib/jmeter_perf/dsl/jsr223_assertion.rb +35 -0
- data/lib/jmeter_perf/dsl/jsr223_listener.rb +35 -0
- data/lib/jmeter_perf/dsl/jsr223_postprocessor.rb +35 -0
- data/lib/jmeter_perf/dsl/jsr223_preprocessor.rb +35 -0
- data/lib/jmeter_perf/dsl/jsr223_sampler.rb +35 -0
- data/lib/jmeter_perf/dsl/jsr223_timer.rb +35 -0
- data/lib/jmeter_perf/dsl/keystore_configuration.rb +34 -0
- data/lib/jmeter_perf/dsl/ldap_extended_request.rb +48 -0
- data/lib/jmeter_perf/dsl/ldap_extended_request_defaults.rb +48 -0
- data/lib/jmeter_perf/dsl/ldap_request.rb +41 -0
- data/lib/jmeter_perf/dsl/ldap_request_defaults.rb +45 -0
- data/lib/jmeter_perf/dsl/login_config_element.rb +32 -0
- data/lib/jmeter_perf/dsl/loop_controller.rb +32 -0
- data/lib/jmeter_perf/dsl/mail_reader_sampler.rb +43 -0
- data/lib/jmeter_perf/dsl/mailer_visualizer.rb +70 -0
- data/lib/jmeter_perf/dsl/md5_hex_assertion.rb +31 -0
- data/lib/jmeter_perf/dsl/module_controller.rb +31 -0
- data/lib/jmeter_perf/dsl/monitor_results.rb +61 -0
- data/lib/jmeter_perf/dsl/once_only_controller.rb +29 -0
- data/lib/jmeter_perf/dsl/os_process_sampler.rb +40 -0
- data/lib/jmeter_perf/dsl/poisson_random_timer.rb +32 -0
- data/lib/jmeter_perf/dsl/random_controller.rb +31 -0
- data/lib/jmeter_perf/dsl/random_order_controller.rb +29 -0
- data/lib/jmeter_perf/dsl/random_variable.rb +36 -0
- data/lib/jmeter_perf/dsl/recording_controller.rb +29 -0
- data/lib/jmeter_perf/dsl/reg_ex_user_parameters.rb +33 -0
- data/lib/jmeter_perf/dsl/regular_expression_extractor.rb +38 -0
- data/lib/jmeter_perf/dsl/response_assertion.rb +37 -0
- data/lib/jmeter_perf/dsl/response_time_graph.rb +61 -0
- data/lib/jmeter_perf/dsl/result_status_action_handler.rb +31 -0
- data/lib/jmeter_perf/dsl/runtime_controller.rb +31 -0
- data/lib/jmeter_perf/dsl/save_responses_to_a_file.rb +35 -0
- data/lib/jmeter_perf/dsl/simple_config_element.rb +29 -0
- data/lib/jmeter_perf/dsl/simple_controller.rb +29 -0
- data/lib/jmeter_perf/dsl/simple_data_writer.rb +61 -0
- data/lib/jmeter_perf/dsl/smime_assertion.rb +41 -0
- data/lib/jmeter_perf/dsl/smtp_sampler.rb +57 -0
- data/lib/jmeter_perf/dsl/soap_xml_rpc_request.rb +39 -0
- data/lib/jmeter_perf/dsl/spline_visualizer.rb +61 -0
- data/lib/jmeter_perf/dsl/summary_report.rb +61 -0
- data/lib/jmeter_perf/dsl/switch_controller.rb +31 -0
- data/lib/jmeter_perf/dsl/synchronizing_timer.rb +32 -0
- data/lib/jmeter_perf/dsl/tcp_sampler.rb +39 -0
- data/lib/jmeter_perf/dsl/tcp_sampler_config.rb +37 -0
- data/lib/jmeter_perf/dsl/test_action.rb +33 -0
- data/lib/jmeter_perf/dsl/test_fragment.rb +29 -0
- data/lib/jmeter_perf/dsl/test_plan.rb +37 -0
- data/lib/jmeter_perf/dsl/thread_group.rb +43 -0
- data/lib/jmeter_perf/dsl/throughput_controller.rb +38 -0
- data/lib/jmeter_perf/dsl/transaction_controller.rb +32 -0
- data/lib/jmeter_perf/dsl/uniform_random_timer.rb +32 -0
- data/lib/jmeter_perf/dsl/user_defined_variables.rb +39 -0
- data/lib/jmeter_perf/dsl/user_parameters.rb +36 -0
- data/lib/jmeter_perf/dsl/view_results_in_table.rb +61 -0
- data/lib/jmeter_perf/dsl/view_results_tree.rb +61 -0
- data/lib/jmeter_perf/dsl/while_controller.rb +31 -0
- data/lib/jmeter_perf/dsl/x_path_assertion.rb +37 -0
- data/lib/jmeter_perf/dsl/x_path_extractor.rb +37 -0
- data/lib/jmeter_perf/dsl/xml_assertion.rb +29 -0
- data/lib/jmeter_perf/dsl/xml_schema_assertion.rb +31 -0
- data/lib/jmeter_perf/extend/assertions/response_assertion.rb +38 -0
- data/lib/jmeter_perf/extend/config_elements/header_manager.rb +13 -0
- data/lib/jmeter_perf/extend/config_elements/http_cache_manager.rb +12 -0
- data/lib/jmeter_perf/extend/config_elements/http_cookie_manager.rb +39 -0
- data/lib/jmeter_perf/extend/config_elements/http_request_defaults.rb +55 -0
- data/lib/jmeter_perf/extend/config_elements/user_defined_variables.rb +13 -0
- data/lib/jmeter_perf/extend/config_elements/user_parameters.rb +31 -0
- data/lib/jmeter_perf/extend/controllers/foreach_controller.rb +31 -0
- data/lib/jmeter_perf/extend/controllers/loop_controller.rb +11 -0
- data/lib/jmeter_perf/extend/controllers/module_controller.rb +26 -0
- data/lib/jmeter_perf/extend/controllers/throughput_controller.rb +15 -0
- data/lib/jmeter_perf/extend/controllers/transaction_controller.rb +14 -0
- data/lib/jmeter_perf/extend/misc/exists.rb +13 -0
- data/lib/jmeter_perf/extend/misc/rsync.rb +24 -0
- data/lib/jmeter_perf/extend/misc/uuid.rb +12 -0
- data/lib/jmeter_perf/extend/misc/with_helpers.rb +27 -0
- data/lib/jmeter_perf/extend/plugins/jmeter_plugins.rb +124 -0
- data/lib/jmeter_perf/extend/processors/extract.rb +27 -0
- data/lib/jmeter_perf/extend/processors/regular_expression_extractor.rb +27 -0
- data/lib/jmeter_perf/extend/samplers/http_request.rb +66 -0
- data/lib/jmeter_perf/extend/samplers/jms_pointtopoint.rb +23 -0
- data/lib/jmeter_perf/extend/samplers/soapxmlrpc_request.rb +10 -0
- data/lib/jmeter_perf/extend/threads/thread_group.rb +19 -0
- data/lib/jmeter_perf/extend/timers/constant_throughput_timer.rb +11 -0
- data/lib/jmeter_perf/extend/timers/random_timer.rb +14 -0
- data/lib/jmeter_perf/helpers/dsl_generator.rb +157 -0
- data/lib/jmeter_perf/helpers/fallback_content_proxy.rb +96 -0
- data/lib/jmeter_perf/helpers/helper.rb +63 -0
- data/lib/jmeter_perf/helpers/parser.rb +143 -0
- data/lib/jmeter_perf/helpers/running_statistics.rb +62 -0
- data/lib/jmeter_perf/helpers/string.rb +60 -0
- data/lib/jmeter_perf/helpers/user-agents.rb +42 -0
- data/lib/jmeter_perf/plugins/active_threads_over_time.rb +59 -0
- data/lib/jmeter_perf/plugins/composite_graph.rb +77 -0
- data/lib/jmeter_perf/plugins/console_status_logger.rb +19 -0
- data/lib/jmeter_perf/plugins/dummy_sampler.rb +30 -0
- data/lib/jmeter_perf/plugins/jmx_collector.rb +74 -0
- data/lib/jmeter_perf/plugins/json_path_assertion.rb +23 -0
- data/lib/jmeter_perf/plugins/json_path_extractor.rb +22 -0
- data/lib/jmeter_perf/plugins/latencies_over_time.rb +53 -0
- data/lib/jmeter_perf/plugins/loadosophia_uploader.rb +66 -0
- data/lib/jmeter_perf/plugins/perfmon_collector.rb +87 -0
- data/lib/jmeter_perf/plugins/redis_data_set.rb +43 -0
- data/lib/jmeter_perf/plugins/response_codes_per_second.rb +53 -0
- data/lib/jmeter_perf/plugins/response_times_distribution.rb +53 -0
- data/lib/jmeter_perf/plugins/response_times_over_time.rb +53 -0
- data/lib/jmeter_perf/plugins/response_times_percentiles.rb +54 -0
- data/lib/jmeter_perf/plugins/stepping_thread_group.rb +34 -0
- data/lib/jmeter_perf/plugins/transactions_per_second.rb +53 -0
- data/lib/jmeter_perf/plugins/ultimate_thread_group.rb +28 -0
- data/lib/jmeter_perf/plugins/variable_throughput_timer.rb +35 -0
- data/lib/jmeter_perf/report/comparator.rb +258 -0
- data/lib/jmeter_perf/report/summary.rb +268 -0
- data/lib/jmeter_perf/tasks/dsl.rake +19 -0
- data/lib/jmeter_perf/version.rb +5 -0
- data/lib/jmeter_perf/views/report_template.html.erb +114 -0
- data/lib/jmeter_perf.rb +183 -0
- data/lib/specifications/idl.xml +1494 -0
- data/sig/jmeter_perf.rbs +195 -0
- data/sorbet/config +5 -0
- data/sorbet/rbi/annotations/.gitattributes +1 -0
- data/sorbet/rbi/annotations/rainbow.rbi +269 -0
- data/sorbet/rbi/gems/.gitattributes +1 -0
- data/sorbet/rbi/gems/ast@2.4.2.rbi +585 -0
- data/sorbet/rbi/gems/bump@0.10.0.rbi +169 -0
- data/sorbet/rbi/gems/byebug@11.1.3.rbi +3607 -0
- data/sorbet/rbi/gems/coderay@1.1.3.rbi +3427 -0
- data/sorbet/rbi/gems/diff-lcs@1.5.1.rbi +1131 -0
- data/sorbet/rbi/gems/docile@1.4.1.rbi +377 -0
- data/sorbet/rbi/gems/erubi@1.13.0.rbi +150 -0
- data/sorbet/rbi/gems/json@2.7.2.rbi +1562 -0
- data/sorbet/rbi/gems/language_server-protocol@3.17.0.3.rbi +14238 -0
- data/sorbet/rbi/gems/lint_roller@1.1.0.rbi +240 -0
- data/sorbet/rbi/gems/method_source@1.1.0.rbi +304 -0
- data/sorbet/rbi/gems/netrc@0.11.0.rbi +159 -0
- data/sorbet/rbi/gems/nokogiri@1.16.7.rbi +7311 -0
- data/sorbet/rbi/gems/parallel@1.26.3.rbi +291 -0
- data/sorbet/rbi/gems/parser@3.3.5.0.rbi +5519 -0
- data/sorbet/rbi/gems/prism@1.2.0.rbi +39085 -0
- data/sorbet/rbi/gems/pry-byebug@3.10.1.rbi +1151 -0
- data/sorbet/rbi/gems/pry@0.14.2.rbi +10076 -0
- data/sorbet/rbi/gems/racc@1.8.1.rbi +162 -0
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +403 -0
- data/sorbet/rbi/gems/rake@13.2.1.rbi +3074 -0
- data/sorbet/rbi/gems/rbi@0.2.1.rbi +4535 -0
- data/sorbet/rbi/gems/rbtree3@0.7.1.rbi +9 -0
- data/sorbet/rbi/gems/regexp_parser@2.9.2.rbi +3772 -0
- data/sorbet/rbi/gems/rexml@3.3.8.rbi +4858 -0
- data/sorbet/rbi/gems/rspec-core@3.13.1.rbi +11132 -0
- data/sorbet/rbi/gems/rspec-expectations@3.13.3.rbi +8183 -0
- data/sorbet/rbi/gems/rspec-mocks@3.13.1.rbi +5341 -0
- data/sorbet/rbi/gems/rspec-support@3.13.1.rbi +1630 -0
- data/sorbet/rbi/gems/rspec@3.13.0.rbi +83 -0
- data/sorbet/rbi/gems/rubocop-ast@1.32.3.rbi +7054 -0
- data/sorbet/rbi/gems/rubocop-performance@1.21.1.rbi +9 -0
- data/sorbet/rbi/gems/rubocop@1.65.1.rbi +58182 -0
- data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1318 -0
- data/sorbet/rbi/gems/simplecov-html@0.13.1.rbi +225 -0
- data/sorbet/rbi/gems/simplecov@0.22.0.rbi +2149 -0
- data/sorbet/rbi/gems/simplecov_json_formatter@0.1.4.rbi +9 -0
- data/sorbet/rbi/gems/spoom@1.5.0.rbi +4932 -0
- data/sorbet/rbi/gems/standard-custom@1.0.2.rbi +9 -0
- data/sorbet/rbi/gems/standard-performance@1.4.0.rbi +9 -0
- data/sorbet/rbi/gems/standard@1.40.0.rbi +926 -0
- data/sorbet/rbi/gems/tapioca@0.16.3.rbi +3596 -0
- data/sorbet/rbi/gems/tdigest@0.2.1.rbi +170 -0
- data/sorbet/rbi/gems/thor@1.3.2.rbi +4378 -0
- data/sorbet/rbi/gems/unicode-display_width@2.6.0.rbi +66 -0
- data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +435 -0
- data/sorbet/rbi/gems/yard@0.9.37.rbi +18379 -0
- data/sorbet/rbi/todo.rbi +31 -0
- data/sorbet/tapioca/config.yml +13 -0
- data/sorbet/tapioca/require.rb +15 -0
- data/tasks/dsl.rake +22 -0
- metadata +355 -0
@@ -0,0 +1,268 @@
|
|
1
|
+
require "csv"
|
2
|
+
require "timeout"
|
3
|
+
require_relative "../helpers/running_statistics"
|
4
|
+
module JmeterPerf
|
5
|
+
module Report
|
6
|
+
# Summary provides a statistical analysis of performance test results by processing
|
7
|
+
# JMeter JTL files. It calculates metrics such as average response time, error percentage,
|
8
|
+
# min/max response times, and percentiles, helping to understand the distribution of
|
9
|
+
# response times across requests.
|
10
|
+
# @note This class uses a TDigest data structure to keep statistics "close enough". Accuracy is not guaranteed.
|
11
|
+
#
|
12
|
+
|
13
|
+
# @!attribute [rw] avg
|
14
|
+
# @return [Float] the average response time
|
15
|
+
# @!attribute [rw] error_percentage
|
16
|
+
# @return [Float] the error percentage across all requests
|
17
|
+
# @!attribute [rw] max
|
18
|
+
# @return [Integer] the maximum response time encountered
|
19
|
+
# @!attribute [rw] min
|
20
|
+
# @return [Integer] the minimum response time encountered
|
21
|
+
# @!attribute [rw] p10
|
22
|
+
# @return [Float] the 10th percentile of response times
|
23
|
+
# @!attribute [rw] p50
|
24
|
+
# @return [Float] the median (50th percentile) of response times
|
25
|
+
# @!attribute [rw] p95
|
26
|
+
# @return [Float] the 95th percentile of response times
|
27
|
+
# @!attribute [rw] requests_per_minute
|
28
|
+
# @return [Float] the requests per minute rate
|
29
|
+
# @!attribute [rw] response_codes
|
30
|
+
# @return [Hash<String, Integer>] a hash of response codes with their respective counts
|
31
|
+
# @!attribute [rw] standard_deviation
|
32
|
+
# @return [Float] the standard deviation of response times
|
33
|
+
# @!attribute [rw] total_bytes
|
34
|
+
# @return [Integer] the total number of bytes received
|
35
|
+
# @!attribute [rw] total_elapsed_time
|
36
|
+
# @return [Integer] the total elapsed time in milliseconds
|
37
|
+
# @!attribute [rw] total_errors
|
38
|
+
# @return [Integer] the total number of errors encountered
|
39
|
+
# @!attribute [rw] total_latency
|
40
|
+
# @return [Integer] the total latency time across all requests
|
41
|
+
# @!attribute [rw] total_requests
|
42
|
+
# @return [Integer] the total number of requests processed
|
43
|
+
# @!attribute [rw] total_sent_bytes
|
44
|
+
# @return [Integer] the total number of bytes sent
|
45
|
+
# @!attribute [rw] csv_error_lines
|
46
|
+
# @return [Array<Integer>] the line numbers of where CSV errors were encountered
|
47
|
+
# @!attribute [rw] total_run_time
|
48
|
+
# @return [Integer] the total run time in seconds
|
49
|
+
# @!attribute [rw] name
|
50
|
+
# @return [String] the name of the summary, derived from the file path if not provided
|
51
|
+
# @!attribute [rw] response_codes
|
52
|
+
# @return [Hash<String, Integer>] a hash of response codes with their respective counts
|
53
|
+
# @!attribute [rw] csv_error_lines
|
54
|
+
# @return [Array<Integer>] the line numbers of where CSV errors were encountered
|
55
|
+
class Summary
|
56
|
+
# JTL file headers used for parsing CSV rows
|
57
|
+
JTL_HEADER = %i[
|
58
|
+
timeStamp
|
59
|
+
elapsed
|
60
|
+
label
|
61
|
+
responseCode
|
62
|
+
responseMessage
|
63
|
+
threadName
|
64
|
+
dataType
|
65
|
+
success
|
66
|
+
failureMessage
|
67
|
+
bytes
|
68
|
+
sentBytes
|
69
|
+
grpThreads
|
70
|
+
allThreads
|
71
|
+
URL
|
72
|
+
Latency
|
73
|
+
IdleTime
|
74
|
+
Connect
|
75
|
+
]
|
76
|
+
|
77
|
+
# @return [Hash<String, Symbol>] a mapping of CSV headers to their corresponding attribute symbols.
|
78
|
+
CSV_HEADER_MAPPINGS = {
|
79
|
+
"Name" => :name,
|
80
|
+
"Average Response Time" => :avg,
|
81
|
+
"Error Percentage" => :error_percentage,
|
82
|
+
"Max Response Time" => :max,
|
83
|
+
"Min Response Time" => :min,
|
84
|
+
"10th Percentile" => :p10,
|
85
|
+
"Median (50th Percentile)" => :p50,
|
86
|
+
"95th Percentile" => :p95,
|
87
|
+
"Requests Per Minute" => :requests_per_minute,
|
88
|
+
"Standard Deviation" => :standard_deviation,
|
89
|
+
"Total Run Time" => :total_run_time,
|
90
|
+
"Total Bytes" => :total_bytes,
|
91
|
+
"Total Elapsed Time" => :total_elapsed_time,
|
92
|
+
"Total Errors" => :total_errors,
|
93
|
+
"Total Latency" => :total_latency,
|
94
|
+
"Total Requests" => :total_requests,
|
95
|
+
"Total Sent Bytes" => :total_sent_bytes
|
96
|
+
# "Response Code 200" => :response_codes["200"],
|
97
|
+
# "Response Code 500" => :response_codes["500"] etc.
|
98
|
+
}
|
99
|
+
|
100
|
+
attr_accessor(*CSV_HEADER_MAPPINGS.values)
|
101
|
+
# Response codes have multiple keys, so we need to handle them separately
|
102
|
+
attr_accessor :response_codes
|
103
|
+
# CSV Error Lines are an array of integers that get delimited by ":" when written to the CSV
|
104
|
+
attr_accessor :csv_error_lines
|
105
|
+
alias_method :rpm, :requests_per_minute
|
106
|
+
alias_method :std, :standard_deviation
|
107
|
+
alias_method :median, :p50
|
108
|
+
|
109
|
+
# Reads a generated CSV report and sets all appropriate attributes.
|
110
|
+
#
|
111
|
+
# @param csv_path [String] the file path of the CSV report to read
|
112
|
+
# @return [Summary] a new Summary instance with the parsed data
|
113
|
+
def self.read(csv_path)
|
114
|
+
summary = new(file_path: csv_path)
|
115
|
+
CSV.foreach(csv_path, headers: true) do |row|
|
116
|
+
metric = row["Metric"]
|
117
|
+
value = row["Value"]
|
118
|
+
|
119
|
+
if metric == "Name"
|
120
|
+
summary.name = value
|
121
|
+
elsif metric.start_with?("Response Code")
|
122
|
+
code = metric.split.last
|
123
|
+
summary.response_codes[code] = value.to_i
|
124
|
+
elsif metric == "CSV Errors"
|
125
|
+
summary.csv_error_lines = value.split(":").map(&:to_i)
|
126
|
+
elsif CSV_HEADER_MAPPINGS.key?(metric)
|
127
|
+
summary.public_send(:"#{CSV_HEADER_MAPPINGS[metric]}=", value.include?(".") ? value.to_f : value.to_i)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
summary
|
131
|
+
end
|
132
|
+
|
133
|
+
# Initializes a new Summary instance for analyzing performance data.
|
134
|
+
#
|
135
|
+
# @param file_path [String] the file path of the performance file to summarize. Either a JTL or CSV file.
|
136
|
+
# @param name [String, nil] an optional name for the summary, derived from the file path if not provided (default: nil)
|
137
|
+
# @param jtl_read_timeout [Integer] the maximum number of seconds to wait for a line read (default: 3)
|
138
|
+
def initialize(file_path:, name: nil, jtl_read_timeout: 3)
|
139
|
+
@name = name || file_path.to_s.tr("/", "_")
|
140
|
+
@jtl_read_timeout = jtl_read_timeout
|
141
|
+
@finished = false
|
142
|
+
@running_statistics_helper = JmeterPerf::Helpers::RunningStatistisc.new
|
143
|
+
|
144
|
+
@max = 0
|
145
|
+
@min = 1_000_000
|
146
|
+
@response_codes = Hash.new { |h, k| h[k.to_s] = 0 }
|
147
|
+
@total_bytes = 0
|
148
|
+
@total_elapsed_time = 0
|
149
|
+
@total_errors = 0
|
150
|
+
@total_latency = 0
|
151
|
+
@total_requests = 0
|
152
|
+
@total_sent_bytes = 0
|
153
|
+
@csv_error_lines = []
|
154
|
+
|
155
|
+
@file_path = file_path
|
156
|
+
|
157
|
+
@start_time = nil
|
158
|
+
@end_time = nil
|
159
|
+
end
|
160
|
+
|
161
|
+
# Marks the summary as finished, allowing any pending asynchronous operations to complete.
|
162
|
+
#
|
163
|
+
# @return [void]
|
164
|
+
def finish!
|
165
|
+
@finished = true
|
166
|
+
@processing_jtl_thread.join if @processing_jtl_thread&.alive?
|
167
|
+
end
|
168
|
+
|
169
|
+
# Generates a CSV report with the given output file.
|
170
|
+
#
|
171
|
+
# The CSV report includes the following:
|
172
|
+
# - A header row with "Metric" and "Value".
|
173
|
+
# - Rows for each metric and its corresponding value from `CSV_HEADER_MAPPINGS`.
|
174
|
+
# - Rows for each response code and its count from `@response_codes`.
|
175
|
+
# - A row for CSV errors, concatenated with ":".
|
176
|
+
#
|
177
|
+
# @param output_file [String] The path to the output CSV file.
|
178
|
+
# @return [void]
|
179
|
+
def write_csv(output_file)
|
180
|
+
CSV.open(output_file, "wb") do |csv|
|
181
|
+
csv << ["Metric", "Value"]
|
182
|
+
CSV_HEADER_MAPPINGS.each do |metric, value|
|
183
|
+
csv << [metric, public_send(value)]
|
184
|
+
end
|
185
|
+
@response_codes.each do |code, count|
|
186
|
+
csv << ["Response Code #{code}", count]
|
187
|
+
end
|
188
|
+
|
189
|
+
csv << ["CSV Errors", @csv_error_lines.join(":")]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Starts streaming and processing JTL file content asynchronously.
|
194
|
+
#
|
195
|
+
# @return [Thread] a thread that handles the asynchronous file streaming and parsing
|
196
|
+
def stream_jtl_async
|
197
|
+
@processing_jtl_thread = Thread.new do
|
198
|
+
sleep 0.1 until File.exist?(@file_path) # Wait for the file to be created
|
199
|
+
|
200
|
+
File.open(@file_path, "r") do |file|
|
201
|
+
file.seek(0, IO::SEEK_END)
|
202
|
+
until @finished && file.eof?
|
203
|
+
line = file.gets
|
204
|
+
next unless line # Skip if no line was read
|
205
|
+
|
206
|
+
# Process only if the line is complete (ends with a newline)
|
207
|
+
read_until_complete_line(file, line)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Summarizes the collected data by calculating statistical metrics and error rates.
|
214
|
+
#
|
215
|
+
# @return [void]
|
216
|
+
def summarize_data!
|
217
|
+
@p10, @p50, @p95 = @running_statistics_helper.get_percentiles(0.1, 0.5, 0.95)
|
218
|
+
@error_percentage = (@total_errors.to_f / @total_requests) * 100
|
219
|
+
@avg = @running_statistics_helper.avg
|
220
|
+
@total_run_time = ((@end_time - @start_time) / 1000).to_f # Convert milliseconds to seconds
|
221
|
+
@requests_per_minute = @total_run_time.zero? ? 0 : (@total_requests / @total_run_time) * 60.0
|
222
|
+
@standard_deviation = @running_statistics_helper.std
|
223
|
+
end
|
224
|
+
|
225
|
+
private
|
226
|
+
|
227
|
+
def read_until_complete_line(file, line)
|
228
|
+
return if file.lineno == 1 # Skip the header row
|
229
|
+
Timeout.timeout(@jtl_read_timeout) do
|
230
|
+
until line.end_with?("\n")
|
231
|
+
sleep 0.1
|
232
|
+
line += file.gets.to_s
|
233
|
+
end
|
234
|
+
end
|
235
|
+
parse_csv_row(line)
|
236
|
+
rescue Timeout::Error
|
237
|
+
puts "Timeout waiting for line to complete: #{line}"
|
238
|
+
raise
|
239
|
+
rescue CSV::MalformedCSVError
|
240
|
+
@csv_error_lines << file.lineno
|
241
|
+
end
|
242
|
+
|
243
|
+
def parse_csv_row(line)
|
244
|
+
CSV.parse(line, headers: JTL_HEADER, liberal_parsing: true).each do |row|
|
245
|
+
line_item = row.to_hash
|
246
|
+
elapsed = line_item.fetch(:elapsed).to_i
|
247
|
+
timestamp = line_item.fetch(:timeStamp).to_i
|
248
|
+
|
249
|
+
# Update start and end times
|
250
|
+
@start_time = timestamp if @start_time.nil? || timestamp < @start_time
|
251
|
+
@end_time = timestamp + elapsed if @end_time.nil? || (timestamp + elapsed) > @end_time
|
252
|
+
|
253
|
+
# Continue with processing the row as before...
|
254
|
+
@running_statistics_helper.add_number(elapsed)
|
255
|
+
@total_requests += 1
|
256
|
+
@total_elapsed_time += elapsed
|
257
|
+
@response_codes[line_item.fetch(:responseCode)] += 1
|
258
|
+
@total_errors += (line_item.fetch(:success) == "true") ? 0 : 1
|
259
|
+
@total_bytes += line_item.fetch(:bytes, 0).to_i
|
260
|
+
@total_sent_bytes += line_item.fetch(:sentBytes, 0).to_i
|
261
|
+
@total_latency += line_item.fetch(:Latency).to_i
|
262
|
+
@min = [@min, elapsed].min
|
263
|
+
@max = [@max, elapsed].max
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
namespace :jmeter_perf do
|
2
|
+
desc "Generate JmeterPerf::DSL methods"
|
3
|
+
task :dsl_generate, [:dsl_dir, :idl_xml_path] do |t, args|
|
4
|
+
require_relative "../helpers/dsl_generator"
|
5
|
+
puts "Generating DSL methods..."
|
6
|
+
|
7
|
+
dsl_dir = Pathname(args[:dsl_dir]).expand_path
|
8
|
+
idl_xml_path = Pathname(args[:idl_xml_path]).expand_path
|
9
|
+
|
10
|
+
generator = JmeterPerf::Helpers::DSLGenerator.new(
|
11
|
+
dsl_dir: dsl_dir,
|
12
|
+
idl_xml_path: idl_xml_path
|
13
|
+
)
|
14
|
+
|
15
|
+
generator.generate
|
16
|
+
|
17
|
+
puts "Finished generating DSL methods."
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Comparison Report</title>
|
5
|
+
<style>
|
6
|
+
body {
|
7
|
+
font-family: Arial, sans-serif;
|
8
|
+
}
|
9
|
+
.summary-box {
|
10
|
+
margin-bottom: 20px;
|
11
|
+
padding: 10px;
|
12
|
+
border: 1px solid #ccc;
|
13
|
+
border-radius: 5px;
|
14
|
+
background-color: #f9f9f9;
|
15
|
+
}
|
16
|
+
table {
|
17
|
+
width: 100%;
|
18
|
+
border-collapse: collapse;
|
19
|
+
table-layout: fixed;
|
20
|
+
}
|
21
|
+
td {
|
22
|
+
border: 1px solid black;
|
23
|
+
padding: 8px;
|
24
|
+
text-align: center;
|
25
|
+
word-wrap: break-word;
|
26
|
+
}
|
27
|
+
.header-row td {
|
28
|
+
background-color: #f2f2f2;
|
29
|
+
font-weight: bold;
|
30
|
+
}
|
31
|
+
.bad {
|
32
|
+
background-color: #ffcccc;
|
33
|
+
}
|
34
|
+
.tooltip {
|
35
|
+
position: relative;
|
36
|
+
}
|
37
|
+
.tooltip .tooltiptext {
|
38
|
+
visibility: hidden;
|
39
|
+
width: 150px;
|
40
|
+
background-color: black;
|
41
|
+
color: #fff;
|
42
|
+
text-align: center;
|
43
|
+
border-radius: 6px;
|
44
|
+
padding: 5px;
|
45
|
+
position: absolute;
|
46
|
+
z-index: 1;
|
47
|
+
bottom: 100%;
|
48
|
+
left: 50%;
|
49
|
+
margin-left: -75px;
|
50
|
+
opacity: 0;
|
51
|
+
transition: opacity 0.3s;
|
52
|
+
}
|
53
|
+
.tooltip:hover .tooltiptext {
|
54
|
+
visibility: visible;
|
55
|
+
opacity: 1;
|
56
|
+
}
|
57
|
+
</style>
|
58
|
+
</head>
|
59
|
+
<body>
|
60
|
+
<% if @comparator.name %>
|
61
|
+
<h2>Comparison Report for <span style="text-decoration: underline;"><%= @comparator.name %></span></h2>
|
62
|
+
<% end %>
|
63
|
+
<div class="summary-box" id="summary-box">
|
64
|
+
<b>Report Generated:</b> <%= Time.now.strftime("%Y-%m-%d %H:%M:%S") %><br>
|
65
|
+
<b>Cohen's D:</b> <%= @comparator.cohens_d %><br>
|
66
|
+
<b>T Statistics:</b> <%= @comparator.t_statistic %><br>
|
67
|
+
<b>Summary:</b> <%= @comparator.human_rating %> in performance
|
68
|
+
</div>
|
69
|
+
<table>
|
70
|
+
<tr class="header-row">
|
71
|
+
<% ['Label', 'Total Requests', 'Total Elapsed Time', 'RPM', 'Errors', 'Error %', 'Min', 'Max', 'Avg', 'SD', 'P10', 'P50', 'P95'].each do |header| %>
|
72
|
+
<td class="tooltip"><%= header %>
|
73
|
+
<span class="tooltiptext"><%= "Description for #{header}" %></span>
|
74
|
+
</td>
|
75
|
+
<% end %>
|
76
|
+
</tr>
|
77
|
+
<% @reports.each_with_index do |report, index| %>
|
78
|
+
<tr>
|
79
|
+
<td><%= index == 0 ? "Base Metric" : "Test Metric" %></td>
|
80
|
+
<td><%= report.total_requests %></td>
|
81
|
+
<td><%= report.total_elapsed_time %></td>
|
82
|
+
<td><%= sprintf('%.2f', report.rpm) %></td>
|
83
|
+
<td class="<%= 'bad' if report.total_errors > 0 %>"><%= report.total_errors %></td>
|
84
|
+
<td class="<%= 'bad' if report.error_percentage > 0.0 %>"><%= sprintf('%.2f', report.error_percentage) %></td>
|
85
|
+
<td><%= report.min %></td>
|
86
|
+
<td><%= report.max %></td>
|
87
|
+
<td><%= sprintf('%.2f', report.avg) %></td>
|
88
|
+
<td><%= sprintf('%.2f', report.std) %></td>
|
89
|
+
<td><%= report.p10 %></td>
|
90
|
+
<td><%= report.p50 %></td>
|
91
|
+
<td><%= report.p95 %></td>
|
92
|
+
</tr>
|
93
|
+
<% end %>
|
94
|
+
</table>
|
95
|
+
<script type="module">
|
96
|
+
import ColorScale from "https://cdn.skypack.dev/color-scales";
|
97
|
+
document.addEventListener('DOMContentLoaded', function () {
|
98
|
+
const summaryBox = document.getElementById('summary-box');
|
99
|
+
const cohenValue = <%= @comparator.cohens_d %>;
|
100
|
+
let color;
|
101
|
+
|
102
|
+
if (cohenValue === 0) {
|
103
|
+
color = 'lightgray'; // Neutral color for zero
|
104
|
+
} else {
|
105
|
+
const colorScale = new ColorScale(-3, 2, ['#FF0000', '#FFFF00', '#008000']);
|
106
|
+
color = colorScale.getColor(cohenValue).toRGBString()
|
107
|
+
}
|
108
|
+
|
109
|
+
summaryBox.style.backgroundColor = color;
|
110
|
+
});
|
111
|
+
|
112
|
+
</script>
|
113
|
+
</body>
|
114
|
+
</html>
|
data/lib/jmeter_perf.rb
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "jmeter_perf/version"
|
4
|
+
lib = File.dirname(File.absolute_path(__FILE__))
|
5
|
+
|
6
|
+
Dir.glob(File.join(lib, "jmeter_perf/report/*.rb")).each do |file|
|
7
|
+
require_relative file
|
8
|
+
end
|
9
|
+
|
10
|
+
Dir.glob(File.join(lib, "jmeter_perf/helpers/*.rb")).each do |file|
|
11
|
+
require_relative file
|
12
|
+
end
|
13
|
+
|
14
|
+
Dir.glob(File.join(lib, "jmeter_perf/dsl/*.rb")).each do |file|
|
15
|
+
require_relative file
|
16
|
+
end
|
17
|
+
|
18
|
+
Dir.glob(File.join(lib, "jmeter_perf/extend/**/*.rb")).each do |file|
|
19
|
+
require_relative file
|
20
|
+
end
|
21
|
+
|
22
|
+
Dir.glob(File.join(lib, "jmeter_perf/plugins/*.rb")).each do |file|
|
23
|
+
require_relative file
|
24
|
+
end
|
25
|
+
|
26
|
+
# JmeterPerf module for handling performance testing with JMeter.
|
27
|
+
# This module provides methods to define and execute JMeter test plans.
|
28
|
+
module JmeterPerf
|
29
|
+
# Evaluates the test plan with the given parameters and block.
|
30
|
+
# @see https://github.com/jlurena/jmeter_perf/wiki/1.-DSL-Documentation DSL Documentation
|
31
|
+
# @param params [Hash] Parameters for the test plan (default: `{}`).
|
32
|
+
# @yield The block to define the test plan steps.
|
33
|
+
# @return [JmeterPerf::ExtendedDSL] The DSL instance with the configured test plan.
|
34
|
+
def self.test(params = {}, &block)
|
35
|
+
dsl = JmeterPerf::ExtendedDSL.new(params)
|
36
|
+
|
37
|
+
block_context = eval("self", block.binding, __FILE__, __LINE__)
|
38
|
+
proxy_context = JmeterPerf::Helpers::FallbackContextProxy.new(dsl, block_context)
|
39
|
+
begin
|
40
|
+
block_context.instance_variables.each do |ivar|
|
41
|
+
proxy_context.instance_variable_set(ivar, block_context.instance_variable_get(ivar))
|
42
|
+
end
|
43
|
+
proxy_context.instance_eval(&block)
|
44
|
+
ensure
|
45
|
+
block_context.instance_variables.each do |ivar|
|
46
|
+
block_context.instance_variable_set(ivar, proxy_context.instance_variable_get(ivar))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
dsl
|
50
|
+
end
|
51
|
+
|
52
|
+
# DSL class for defining JMeter test plans.
|
53
|
+
# Provides methods to generate, save, and run JMeter tests.
|
54
|
+
class ExtendedDSL < DSL
|
55
|
+
include JmeterPerf::Helpers::Parser
|
56
|
+
attr_accessor :root
|
57
|
+
|
58
|
+
# Initializes an ExtendedDSL object with the provided parameters.
|
59
|
+
# @param params [Hash] Parameters for the test plan (default: `{}`).
|
60
|
+
def initialize(params = {})
|
61
|
+
@root = Nokogiri::XML(JmeterPerf::Helpers::String.strip_heredoc(
|
62
|
+
<<-EOF
|
63
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
64
|
+
<jmeterTestPlan version="1.2" properties="3.1" jmeter="3.1" ruby-jmeter="3.0">
|
65
|
+
<hashTree>
|
66
|
+
</hashTree>
|
67
|
+
</jmeterTestPlan>
|
68
|
+
EOF
|
69
|
+
))
|
70
|
+
node = JmeterPerf::DSL::TestPlan.new(params)
|
71
|
+
@current_node = @root.at_xpath("//jmeterTestPlan/hashTree")
|
72
|
+
@current_node = attach_to_last(node)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Saves the test plan as a JMX file.
|
76
|
+
#
|
77
|
+
# @param out_jmx [String] The path for the output JMX file (default: `"ruby-jmeter.jmx"`).
|
78
|
+
def jmx(out_jmx: "ruby-jmeter.jmx")
|
79
|
+
File.write(out_jmx, doc.to_xml(indent: 2))
|
80
|
+
logger.info "JMX saved to: #{out_jmx}"
|
81
|
+
end
|
82
|
+
|
83
|
+
# Runs the test plan with the specified configuration.
|
84
|
+
#
|
85
|
+
# @param name [String] The name of the test run (default: `"ruby-jmeter"`).
|
86
|
+
# @param jmeter_path [String] Path to the JMeter executable (default: `"jmeter"`).
|
87
|
+
# @param out_jmx [String] The filename for the output JMX file (default: `"ruby-jmeter.jmx"`).
|
88
|
+
# @param out_jtl [String] The filename for the output JTL file (default: `"jmeter.jtl"`).
|
89
|
+
# @param out_jmeter_log [String] The filename for the JMeter log file (default: `"jmeter.log"`).
|
90
|
+
# @param out_cmd_log [String] The filename for the command log file (default: `"jmeter-cmd.log"`).
|
91
|
+
# @param jtl_read_timeout [Integer] The maximum number of seconds to wait for a line read (default: `3`).
|
92
|
+
# @return [JmeterPerf::Report::Summary] The summary report of the test run.
|
93
|
+
# @raise [RuntimeError] If the test execution fails.
|
94
|
+
def run(
|
95
|
+
name: "ruby-jmeter",
|
96
|
+
jmeter_path: "jmeter",
|
97
|
+
out_jmx: "ruby-jmeter.jmx",
|
98
|
+
out_jtl: "jmeter.jtl",
|
99
|
+
out_jmeter_log: "jmeter.log",
|
100
|
+
out_cmd_log: "jmeter-cmd.log",
|
101
|
+
jtl_read_timeout: 3
|
102
|
+
)
|
103
|
+
jmx(out_jmx:)
|
104
|
+
logger.warn "Executing #{out_jmx} test plan locally ..."
|
105
|
+
|
106
|
+
cmd = <<~CMD.strip
|
107
|
+
#{jmeter_path} -n -t #{out_jmx} -j #{out_jmeter_log} -l #{out_jtl}
|
108
|
+
CMD
|
109
|
+
|
110
|
+
summary = JmeterPerf::Report::Summary.new(file_path: out_jtl, name:, jtl_read_timeout:)
|
111
|
+
jtl_process_thread = summary.stream_jtl_async
|
112
|
+
|
113
|
+
File.open(out_cmd_log, "w") do |f|
|
114
|
+
pid = Process.spawn(cmd, out: f, err: [:child, :out])
|
115
|
+
Process.waitpid(pid)
|
116
|
+
end
|
117
|
+
|
118
|
+
summary.finish! # Notify jtl collection that JTL cmd finished
|
119
|
+
jtl_process_thread.join # Join main thread and wait for it to finish
|
120
|
+
|
121
|
+
unless $?.exitstatus.zero?
|
122
|
+
logger.error("Failed to run #{cmd}. See #{out_cmd_log} and #{out_jmeter_log} for details.")
|
123
|
+
raise "Exit status: #{$?.exitstatus}"
|
124
|
+
end
|
125
|
+
|
126
|
+
summary.summarize_data!
|
127
|
+
logger.info "[Test Plan Execution Completed Successfully] JTL saved to: #{out_jtl}\n"
|
128
|
+
summary
|
129
|
+
rescue
|
130
|
+
summary.finish!
|
131
|
+
raise
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
# Creates a new hash tree node for the JMeter test plan.
|
137
|
+
#
|
138
|
+
# @return [Nokogiri::XML::Node] A new hash tree node.
|
139
|
+
def hash_tree
|
140
|
+
Nokogiri::XML::Node.new("hashTree", @root)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Attaches a node as the last child of the current node.
|
144
|
+
#
|
145
|
+
# @param node [Object] The node to attach.
|
146
|
+
# @return [Object] The hash tree for the attached node.
|
147
|
+
def attach_to_last(node)
|
148
|
+
ht = hash_tree
|
149
|
+
last_node = @current_node
|
150
|
+
last_node << node.doc.children << ht
|
151
|
+
ht
|
152
|
+
end
|
153
|
+
|
154
|
+
# Attaches a node and evaluates the block within its context.
|
155
|
+
#
|
156
|
+
# @param node [Object] The node to attach.
|
157
|
+
# @yield The block to be executed in the node's context.
|
158
|
+
def attach_node(node, &block)
|
159
|
+
ht = attach_to_last(node)
|
160
|
+
previous = @current_node
|
161
|
+
@current_node = ht
|
162
|
+
instance_exec(&block) if block
|
163
|
+
@current_node = previous
|
164
|
+
end
|
165
|
+
|
166
|
+
# Returns the Nokogiri XML document.
|
167
|
+
#
|
168
|
+
# @return [Nokogiri::XML::Document] The XML document for the JMeter test plan.
|
169
|
+
def doc
|
170
|
+
Nokogiri::XML(@root.to_s, &:noblanks)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Logger instance to log messages to the standard output.
|
174
|
+
#
|
175
|
+
# @return [Logger] The logger instance, initialized to DEBUG level.
|
176
|
+
def logger
|
177
|
+
return @logger if @logger
|
178
|
+
@logger = Logger.new($stdout)
|
179
|
+
@logger.level = Logger::DEBUG
|
180
|
+
@logger
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|