lib/jmeter_perf.rb in jmeter_perf-0.0.6 vs lib/jmeter_perf.rb in jmeter_perf-0.0.7
- old
+ new
@@ -21,24 +21,159 @@
Dir.glob(File.join(lib, "jmeter_perf/plugins/*.rb")).each do |file|
require_relative file
end
-require_relative "jmeter_perf/dsl"
-
+# JmeterPerf module for handling performance testing with JMeter.
+# This module provides methods to define and execute JMeter test plans.
module JmeterPerf
- def self.test(params = {}, &)
- JmeterPerf.dsl_eval(JmeterPerf::ExtendedDSL.new(params), &)
- end
+ # Evaluates the test plan with the given parameters and block.
+ # @see https://github.com/jlurena/jmeter_perf/wiki/1.-DSL-Documentation DSL Documentation
+ # @param [Hash] params Parameters for the test plan (default: `{}`).
+ # @yield The block to define the test plan steps.
+ # @return [JmeterPerf::ExtendedDSL] The DSL instance with the configured test plan.
+ def self.test(params = {}, &block)
+ dsl = JmeterPerf::ExtendedDSL.new(params)
- def self.dsl_eval(dsl, &block)
block_context = eval("self", block.binding, __FILE__, __LINE__)
proxy_context = JmeterPerf::FallbackContextProxy.new(dsl, block_context)
begin
- block_context.instance_variables.each { |ivar| proxy_context.instance_variable_set(ivar, block_context.instance_variable_get(ivar)) }
+ block_context.instance_variables.each do |ivar|
+ proxy_context.instance_variable_set(ivar, block_context.instance_variable_get(ivar))
+ end
proxy_context.instance_eval(&block)
ensure
- block_context.instance_variables.each { |ivar| block_context.instance_variable_set(ivar, proxy_context.instance_variable_get(ivar)) }
+ block_context.instance_variables.each do |ivar|
+ block_context.instance_variable_set(ivar, proxy_context.instance_variable_get(ivar))
+ end
end
dsl
+ end
+
+ # DSL class for defining JMeter test plans.
+ # Provides methods to generate, save, and run JMeter tests.
+ class ExtendedDSL < DSL
+ include Parser
+ attr_accessor :root
+
+ # Initializes an ExtendedDSL object with the provided parameters.
+ # @param [Hash] params Parameters for the test plan (default: `{}`).
+ def initialize(params = {})
+ @root = Nokogiri::XML(<<-EOF.strip_heredoc)
+ <?xml version="1.0" encoding="UTF-8"?>
+ <jmeterTestPlan version="1.2" properties="3.1" jmeter="3.1" ruby-jmeter="3.0">
+ <hashTree>
+ </hashTree>
+ </jmeterTestPlan>
+ EOF
+ node = JmeterPerf::TestPlan.new(params)
+ @current_node = @root.at_xpath("//jmeterTestPlan/hashTree")
+ @current_node = attach_to_last(node)
+ end
+
+ # Saves the test plan as a JMX file.
+ #
+ # @param [String] out_jmx The path for the output JMX file (default: `"ruby-jmeter.jmx"`).
+ def jmx(out_jmx: "ruby-jmeter.jmx")
+ File.write(out_jmx, doc.to_xml(indent: 2))
+ logger.info "JMX saved to: #{out_jmx}"
+ end
+
+ # Runs the test plan with the specified configuration.
+ #
+ # @param [String] name The name of the test run (default: `"ruby-jmeter"`).
+ # @param [String] jmeter_path Path to the JMeter executable (default: `"jmeter"`).
+ # @param [String] out_jmx The filename for the output JMX file (default: `"ruby-jmeter.jmx"`).
+ # @param [String] out_jtl The filename for the output JTL file (default: `"jmeter.jtl"`).
+ # @param [String] out_jmeter_log The filename for the JMeter log file (default: `"jmeter.log"`).
+ # @param [String] out_cmd_log The filename for the command log file (default: `"jmeter-cmd.log"`).
+ # @return [JmeterPerf::Report::Summary] The summary report of the test run.
+ # @raise [RuntimeError] If the test execution fails.
+ def run(
+ name: "ruby-jmeter",
+ jmeter_path: "jmeter",
+ out_jmx: "ruby-jmeter.jmx",
+ out_jtl: "jmeter.jtl",
+ out_jmeter_log: "jmeter.log",
+ out_cmd_log: "jmeter-cmd.log"
+ )
+ jmx(out_jmx:)
+ logger.warn "Executing #{out_jmx} test plan locally ..."
+
+ cmd = <<~CMD.strip
+ #{jmeter_path} -n -t #{out_jmx} -j #{out_jmeter_log} -l #{out_jtl}
+ CMD
+
+ summary = JmeterPerf::Report::Summary.new(out_jtl, name)
+ jtl_process_thread = summary.stream_jtl_async
+
+ File.open(out_cmd_log, "w") do |f|
+ pid = Process.spawn(cmd, out: f, err: [:child, :out])
+ Process.waitpid(pid)
+ end
+
+ summary.finish! # Notify jtl collection that JTL cmd finished
+ jtl_process_thread.join # Join main thread and wait for it to finish
+
+ unless $?.exitstatus.zero?
+ logger.error("Failed to run #{cmd}. See #{out_cmd_log} and #{out_jmeter_log} for details.")
+ raise "Exit status: #{$?.exitstatus}"
+ end
+
+ summary.summarize_data!
+ logger.info "[Test Plan Execution Completed Successfully] JTL saved to: #{out_jtl}\n"
+ summary
+ rescue
+ summary.finish!
+ raise
+ end
+
+ private
+
+ # Creates a new hash tree node for the JMeter test plan.
+ #
+ # @return [Nokogiri::XML::Node] A new hash tree node.
+ def hash_tree
+ Nokogiri::XML::Node.new("hashTree", @root)
+ end
+
+ # Attaches a node as the last child of the current node.
+ #
+ # @param [Object] node The node to attach.
+ # @return [Object] The hash tree for the attached node.
+ def attach_to_last(node)
+ ht = hash_tree
+ last_node = @current_node
+ last_node << node.doc.children << ht
+ ht
+ end
+
+ # Attaches a node and evaluates the block within its context.
+ #
+ # @param [Object] node The node to attach.
+ # @yield The block to be executed in the node's context.
+ def attach_node(node, &block)
+ ht = attach_to_last(node)
+ previous = @current_node
+ @current_node = ht
+ instance_exec(&block) if block
+ @current_node = previous
+ end
+
+ # Returns the Nokogiri XML document.
+ #
+ # @return [Nokogiri::XML::Document] The XML document for the JMeter test plan.
+ def doc
+ Nokogiri::XML(@root.to_s, &:noblanks)
+ end
+
+ # Logger instance to log messages to the standard output.
+ #
+ # @return [Logger] The logger instance, initialized to DEBUG level.
+ def logger
+ return @logger if @logger
+ @logger = Logger.new($stdout)
+ @logger.level = Logger::DEBUG
+ @logger
+ end
end
end