1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
# frozen_string_literal: true
require "test_prof/rspec_stamp"
require "test_prof/logging"
module TestProf
# RSpecDissect tracks how much time do you spend in `before` hooks
# and memoization helpers (i.e. `let`) in your tests.
module RSpecDissect
module ExampleInstrumentation # :nodoc:
def run_before_example(*)
RSpecDissect.track(:before) { super }
end
end
module MemoizedInstrumentation # :nodoc:
def fetch_or_store(id, *)
res = nil
Thread.current[:_rspec_dissect_let_depth] ||= 0
Thread.current[:_rspec_dissect_let_depth] += 1
begin
res = if Thread.current[:_rspec_dissect_let_depth] == 1
RSpecDissect.track(:let, id) { super }
else
super
end
ensure
Thread.current[:_rspec_dissect_let_depth] -= 1
end
res
end
end
# RSpecDisect configuration
class Configuration
MODES = %w[all let before].freeze
attr_accessor :top_count, :let_stats_enabled,
:let_top_count
alias let_stats_enabled? let_stats_enabled
attr_reader :mode
def initialize
@let_stats_enabled = true
@let_top_count = (ENV["RD_PROF_LET_TOP"] || 3).to_i
@top_count = (ENV["RD_PROF_TOP"] || 5).to_i
@stamp = ENV["RD_PROF_STAMP"]
@mode = ENV["RD_PROF"] == "1" ? "all" : ENV["RD_PROF"]
unless MODES.include?(mode)
raise "Unknown RSpecDissect mode: #{mode};" \
"available modes: #{MODES.join(", ")}"
end
RSpecStamp.config.tags = @stamp if stamp?
end
def let?
mode == "all" || mode == "let"
end
def before?
mode == "all" || mode == "before"
end
def stamp?
!@stamp.nil?
end
end
METRICS = %w[before let].freeze
class << self
include Logging
def config
@config ||= Configuration.new
end
def configure
yield config
end
def init
RSpec::Core::Example.prepend(ExampleInstrumentation)
RSpec::Core::MemoizedHelpers::ThreadsafeMemoized.prepend(MemoizedInstrumentation)
RSpec::Core::MemoizedHelpers::NonThreadSafeMemoized.prepend(MemoizedInstrumentation)
@data = {}
METRICS.each do |type|
@data["total_#{type}"] = 0.0
end
reset!
log :info, "RSpecDissect enabled"
end
def track(type, meta = nil)
start = TestProf.now
res = yield
delta = (TestProf.now - start)
type = type.to_s
@data[type][:time] += delta
@data[type][:meta] << meta unless meta.nil?
@data["total_#{type}"] += delta
res
end
def reset!
METRICS.each do |type|
@data[type.to_s] = {time: 0.0, meta: []}
end
end
# Whether we are able to track `let` usage
def memoization_available?
defined?(::RSpec::Core::MemoizedHelpers::ThreadsafeMemoized)
end
def time_for(key)
@data[key.to_s][:time]
end
def meta_for(key)
@data[key.to_s][:meta]
end
def total_time_for(key)
@data["total_#{key}"]
end
end
end
end
require "test_prof/rspec_dissect/collectors/let"
require "test_prof/rspec_dissect/collectors/before"
require "test_prof/rspec_dissect/rspec" if TestProf.rspec?
TestProf.activate("RD_PROF") do
TestProf::RSpecDissect.init
end
|