diff --git a/utilities/load-testing/testResultMunger/_mergeTestResults.rb b/utilities/load-testing/testResultMunger/_mergeTestResults.rb
new file mode 100644
index 00000000..516cd836
--- /dev/null
+++ b/utilities/load-testing/testResultMunger/_mergeTestResults.rb
@@ -0,0 +1,31 @@
+#! /usr/bin/ruby
+
+require "#{File.dirname(__FILE__)}/subscripts/loadParms"
+require "#{File.dirname(__FILE__)}/subscripts/test_result_merger"
+
+properties = {}
+
+source_dir = "/home/jeb228/LoadTesting/tests/results/#{@version_name}"
+properties["source_directory"] = source_dir
+properties["target_directory"] = "/var/www/html/loadTesting/"
+properties["version_name"] = ""#{@version_name}"
+
+suggestions = []
+if File.directory?(source_dir)
+ Dir.chdir(source_dir) do |dir|
+ if File.file?("fileOrderSuggestions.txt")
+ File.open("fileOrderSuggestions.txt") do |f|
+ f.each_line() do |line|
+ suggestions.push(line.strip())
+ end
+ end
+ end
+ end
+end
+properties["file_order_suggestions"] = suggestions
+
+trm = TestResultMerger.new(properties)
+trm.merge
+
+puts "TestResultMerger was successful."
+
diff --git a/utilities/load-testing/testResultMunger/test_result_file.rb b/utilities/load-testing/testResultMunger/test_result_file.rb
new file mode 100644
index 00000000..1963eab8
--- /dev/null
+++ b/utilities/load-testing/testResultMunger/test_result_file.rb
@@ -0,0 +1,101 @@
+#! /usr/bin/ruby
+
+=begin
+--------------------------------------------------------------------------------
+
+Parse a file of JMeter test results (*./jtl), summarize the times for each test,
+and make the summaries easily available.
+
+--------------------------------------------------------------------------------
+=end
+
+require "rexml/document"
+
+include REXML
+
+# ------------------------------------------------------------------------------------
+# TestResultSummary class
+# ------------------------------------------------------------------------------------
+
+class TestResultSummary
+ attr_reader :label
+ attr_reader :how_many
+ attr_reader :failures
+ attr_reader :min_time
+ attr_reader :max_time
+ attr_reader :avg_time
+ def addResult(result_element)
+ @how_many += 1
+ @failures += 1 unless result_element.attributes["s"] == "true"
+
+ time = result_element.attributes["t"].to_i
+ @total_time += time
+ @min_time = [@min_time, time].min
+ @max_time = [@max_time, time].max
+
+ @avg_time = @total_time / how_many
+ end
+
+ def initialize(result_element)
+ @label = result_element.attributes["lb"]
+ @how_many = 0
+ @failures = 0
+ @min_time = 100000000
+ @max_time = 0
+ @total_time = 0
+
+ addResult(result_element)
+ end
+end
+
+# ------------------------------------------------------------------------------------
+# TestResultFile class
+# ------------------------------------------------------------------------------------
+
+class TestResultFile
+ attr_reader :filename
+ attr_reader :timestamp
+ attr_reader :summaries
+ attr_reader :version
+ def parse_result_file()
+ @summaries = {}
+ @version = "_"
+
+ file = File.new( @file_path )
+ doc = Document.new file
+ XPath.each(doc, "/testResults/httpSample") do | result |
+ test_label = result.attributes["lb"]
+ if @summaries[test_label] == nil
+ @summaries[test_label] = TestResultSummary.new(result)
+ else
+ @summaries[test_label].addResult(result)
+ end
+ end
+
+ XPath.each(doc, "version") do | version |
+ @version = version.attributes["name"]
+ end
+ end
+
+ def initialize(filename, source_directory)
+ raise("filename must not be nil") if filename == nil
+ raise("source_directory must not be nil") if source_directory == nil
+
+ @filename = filename
+ @source_directory = source_directory
+
+ if !File.directory?(@source_directory)
+ raise "Directory does not exist: '#{@source_directory}'."
+ end
+
+ @file_path = File.expand_path(filename + ".jtl", @source_directory)
+
+ if !File.file?(@file_path)
+ raise "File doesn't exist: '#{@file_path}'."
+ end
+
+ @timestamp = File.mtime(@file_path)
+
+ parse_result_file()
+ end
+end
diff --git a/utilities/load-testing/testResultMunger/test_result_marshaller.rb b/utilities/load-testing/testResultMunger/test_result_marshaller.rb
new file mode 100644
index 00000000..9ec304ce
--- /dev/null
+++ b/utilities/load-testing/testResultMunger/test_result_marshaller.rb
@@ -0,0 +1,160 @@
+#! /usr/bin/ruby
+
+=begin
+--------------------------------------------------------------------------------
+
+Parse a file of JMeter test results (*./jtl), summarize the times for each test,
+and make the summaries easily available.
+
+--------------------------------------------------------------------------------
+=end
+
+# ------------------------------------------------------------------------------------
+# TestResultMarshaller class
+# ------------------------------------------------------------------------------------
+
+class TestResultMarshaller
+ def marshall()
+ File.open(@output_filename, 'w') do | out |
+ write_html_header(out)
+ write_table_header(out)
+ write_table_lines(out)
+ write_table_footer(out)
+ write_html_footer(out)
+ end
+ end
+
+ def write_html_header(out)
+ out.puts <<"EOF"
+
+
+
+ Performance tests for #{@site_name}
+
+
+EOF
+ end
+
+ def write_table_header(out)
+ top_cells = ['  | ']
+ @test_results.each do | test |
+ top_cells.push("#{test.version} #{test.filename} #{test.timestamp.strftime('%Y-%m-%d %H:%M:%S')} | ")
+ end
+
+ bottom_cells = ['Test Name | ']
+ @test_results.each do | test |
+ bottom_cells.push('Iterations | ')
+ bottom_cells.push('time (min/max) | ')
+ bottom_cells.push("compare | ")
+ end
+
+ out.puts <<"EOF"
+
+
+ #{top_cells.join("\n ")}
+
+
+ #{bottom_cells.join("\n ")}
+
+EOF
+ end
+
+ def write_table_lines(out)
+ all_test_names().each do | test_name |
+ out.puts <<"EOF"
+
+ #{test_name} |
+ #{format_test_results(test_name)}
+
+EOF
+ end
+ end
+
+ def all_test_names
+ names = []
+ @test_results.each do | test |
+ names.concat(test.summaries.keys)
+ end
+ names.uniq.sort
+ end
+
+ def format_test_results(test_name)
+ results = []
+ @test_results.each do | test |
+ results.push(format_test_result(test_name, test))
+ end
+ results.join("\n ")
+ end
+
+ def format_test_result(test_name, test)
+ s = test.summaries[test_name]
+ if s
+ <<"EOF"
+ #{s.how_many} |
+
+
+
+ #{format_millis(s.avg_time)} |
+ #{format_millis(s.min_time)} |
+
+
+ #{format_millis(s.max_time)} |
+
+
+ |
+ #{performance_ratio(test_name, s.avg_time)} |
+EOF
+ else
+ <<"EOF"
+ |
+
+
+ |
+ |
+EOF
+ end
+ end
+
+ def format_millis(millis)
+ "%.3f" % [millis.to_f / 1000]
+ end
+
+ def performance_ratio(test_name, time)
+ return " " if @test_results.empty?
+ return " " unless @test_results[0].summaries.key?(test_name)
+
+ s = @test_results[0].summaries[test_name]
+ reference = s.avg_time
+ return " " if reference == 0
+
+ return "#{"%.0f" % [time * 100 / reference]}%"
+ end
+
+ def write_table_footer(out)
+ out.puts "
"
+ end
+
+ def write_html_footer(out)
+ out.puts <<"EOF"
+
+
+EOF
+ end
+
+ def initialize(target_directory, site_name, test_results)
+ @target_directory = target_directory
+ @site_name = site_name
+ @test_results = test_results
+
+ filename = "#{site_name}-merged_#{Time.now.strftime('%Y-%m-%d_%H-%M-%S')}"
+ @output_filename = File.expand_path(filename, @target_directory)
+ end
+end
diff --git a/utilities/load-testing/testResultMunger/test_result_merger.rb b/utilities/load-testing/testResultMunger/test_result_merger.rb
new file mode 100644
index 00000000..e406d8d8
--- /dev/null
+++ b/utilities/load-testing/testResultMunger/test_result_merger.rb
@@ -0,0 +1,95 @@
+#! /usr/bin/ruby
+
+=begin
+--------------------------------------------------------------------------------
+
+Look through a directory of test results files (*.jtl), and produce an HTML file
+that summarizes, merges, and compares the information.
+
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+=end
+$: << File.dirname(File.expand_path(__FILE__))
+require 'test_result_file'
+require 'test_result_marshaller'
+
+# ------------------------------------------------------------------------------------
+# TestResultMerger class
+# ------------------------------------------------------------------------------------
+
+class TestResultMerger
+ #
+ # Do we have any chance of succeeding with these properties?
+ #
+ def sanity_checks_on_properties()
+ raise("Properties must contain a value for 'source_directory'") if @source_directory == nil
+ raise("Properties must contain a value for 'target_directory'") if @target_directory == nil
+ raise("Properties must contain a value for 'site_name'") if @site_name == nil
+
+ if !File.directory?(@source_directory)
+ raise "Not a directory: '#{@source_directory}'."
+ end
+ if !File.directory?(@target_directory)
+ raise "Not a directory: '#{@target_directory}'."
+ end
+ end
+
+ def parse_files()
+ test_result_files = build_file_list()
+
+ @test_results = []
+ test_result_files.each() do | test_result_file |
+ puts "Parsing #{test_result_file}"
+ @test_results.push(TestResultFile.new(test_result_file, @source_directory))
+ end
+ end
+
+ def build_file_list
+ existing_files = []
+ Dir.foreach(@source_directory) do | filename |
+ next unless File.extname(filename) == ".jtl"
+ existing_files.push(File.basename(filename, ".jtl"))
+ end
+ puts "BOGUS existing files = [#{existing_files.join(', ')}]"
+
+ file_list = []
+ @file_order_suggestions.each() do | suggestion|
+ if existing_files.include?(suggestion)
+ file_list.push(suggestion)
+ existing_files.delete(suggestion)
+ end
+ end
+ file_list.concat(existing_files)
+ puts "BOGUS file list = [#{file_list.join(', ')}]"
+
+ return file_list
+ end
+
+ def marshall()
+ marshaller = TestResultMarshaller.new(@target_directory, @site_name, @test_results)
+ marshaller.marshall()
+ end
+
+ def initialize(properties)
+ @source_directory = properties['source_directory']
+ @target_directory = properties['target_directory']
+ @site_name = properties['site_name']
+ @file_order_suggestions = properties['file_order_suggestions']
+
+ puts "source_directory = #{@source_directory}"
+ puts "target_directory = #{@target_directory}"
+ puts "site_name = #{@site_name}"
+ if (@file_order_suggestions == nil)
+ puts "file_order_suggestions = nil"
+ else
+ puts "file_order_suggestions = [#{@file_order_suggestions.join(', ')}]"
+ end
+
+ sanity_checks_on_properties
+ end
+
+ def merge()
+ parse_files()
+ marshall()
+ end
+end