Skip to content

Instantly share code, notes, and snippets.

@arika
Last active November 29, 2018 03:40
Show Gist options
  • Save arika/f4fac246e717da8dc17abf6cd5da45d4 to your computer and use it in GitHub Desktop.
Save arika/f4fac246e717da8dc17abf6cd5da45d4 to your computer and use it in GitHub Desktop.
Test::Unitのtestやsetup/cleanup/teardownのプロファイルをとる(Rails編)
# test/test_helper.rbに書く
module TestProfiling
module TestCase
def run(result)
data = {
name: method_name,
owner_name: self.class.name,
source_location: method(method_name).source_location,
passed: passed?,
}
ActiveSupport::Notifications.instrument("test_unit.run_test_case", data) do
with_benchmark(data) { super }
end
end
private
def run_fixture_callback(method_name, options, &block)
method = method(method_name)
data = {
name: method_name,
owner_name: method.owner.name,
source_location: method.source_location,
}
ActiveSupport::Notifications.instrument("test_unit.run_fixture_callback", data) do
with_benchmark(data) { super }
end
end
def run_test
data = {
name: @method_name,
owner_name: self.class.name,
source_location: method(@method_name).source_location,
}
ActiveSupport::Notifications.instrument("test_unit.run_test", data) do
with_benchmark(data) { super }
end
end
def with_benchmark(data)
ret = nil
data[:elapsed_time] = Benchmark.realtime do
ret = yield
end
ret
end
end
::Test::Unit::TestCase.prepend TestCase
module TestSuite
def run(result, &progress_block)
data = {
test_suite: name,
passed: passed?,
}
ActiveSupport::Notifications.instrument("test_unit.run_test_suite", data) do
super
end
end
end
::Test::Unit::TestSuite.prepend TestSuite
end
max_result_count = 15
test_unit_data = {
fixture: {},
test: {},
}
test_root_regexp = %r{\A#{Regexp.quote(Rails.root.to_s)}/test/}
ActiveSupport::Notifications.subscribe(/\Atest_unit.run_(?:fixture_callback|test)\z/) do |name, _started, _finished, _unique_id, data|
source_location = data[:source_location]
source_file, = source_location
next unless test_root_regexp.match?(source_file)
next unless data[:elapsed_time]
if name == "test_unit.run_fixture_callback"
type = :fixture
key = [data[:owner_name], *source_location]
else
type = :test
key = [data[:owner_name], data[:name]]
end
test_unit_data[type][key] ||= {
count: 0,
elapsed_time: 0.0,
name: data[:name],
owner_name: data[:owner_name],
source_location: source_location,
}
test_unit_data[type][key][:count] += 1
test_unit_data[type][key][:elapsed_time] += data[:elapsed_time]
end
Test::Unit.at_exit do
fixture_callback_name = lambda do |source_file, lineno, name|
if /_\d+\z/ =~ name
"#{Regexp.last_match.pre_match}(#{source_file.sub(test_root_regexp, '')}:#{lineno})"
else
name
end
end
results = {
test_by_owner: test_unit_data[:test].each_with_object({}) do |((owner_name, name), data), h|
name = name.sub(/\Atest: /, "")
h["#{owner_name}\##{name}"] = data
end,
fixture_by_owner: test_unit_data[:fixture].each_with_object({}) do |((owner_name, source_file, lineno), data), h|
name_with_location = fixture_callback_name.call(source_file, lineno, data[:name])
h["#{owner_name}\##{name_with_location}"] = data
end,
fixture_by_source: test_unit_data[:fixture].each_with_object({}) do |((_, source_file, lineno), data), h|
name_with_location = fixture_callback_name.call(source_file, lineno, data[:name])
if h[name_with_location]
h[name_with_location][:count] += data[:count]
h[name_with_location][:elapsed_time] += data[:elapsed_time]
else
h[name_with_location] = data.dup
h[name_with_location].delete(:owner_name)
end
end,
}
results.each do |type, result|
print "#{type}::\n\n"
total_time = result.sum {|_, data| data[:elapsed_time] }
result.sort_by {|_, data| -data[:elapsed_time] }.each_with_index do |(key, data), idx|
break if idx == max_result_count
printf(
"%6.2fs %5.1f%% (%4d) %.2fs %s\n",
data[:elapsed_time],
data[:elapsed_time] * 100 / total_time,
data[:count],
data[:elapsed_time] / data[:count],
key,
)
end
print "\n\n"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment