test-prof 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/lib/{test_prof → rubocop/test_prof}/cops/rspec/aggregate_examples/matchers_with_side_effects.rb +1 -1
  4. data/lib/{test_prof → rubocop/test_prof}/cops/rspec/aggregate_examples/node_matchers.rb +1 -1
  5. data/lib/{test_prof → rubocop/test_prof}/cops/rspec/aggregate_examples.rb +5 -5
  6. data/lib/{test_prof/cops → rubocop/test_prof}/plugin.rb +2 -2
  7. data/lib/rubocop/test_prof.rb +9 -0
  8. data/lib/test-prof.rb +5 -0
  9. data/lib/test_prof/event_prof/custom_events/sidekiq_inline.rb +1 -1
  10. data/lib/test_prof/event_prof/custom_events/sidekiq_jobs.rb +1 -1
  11. data/lib/test_prof/memory_prof/printer.rb +31 -0
  12. data/lib/test_prof/memory_prof/rspec.rb +2 -0
  13. data/lib/test_prof/memory_prof/tracker.rb +11 -1
  14. data/lib/test_prof/memory_prof.rb +8 -3
  15. data/lib/test_prof/recipes/rspec/let_it_be.rb +1 -1
  16. data/lib/test_prof/rspec_dissect/collector.rb +58 -0
  17. data/lib/test_prof/rspec_dissect/rspec.rb +13 -21
  18. data/lib/test_prof/rspec_dissect.rb +45 -51
  19. data/lib/test_prof/rspec_stamp/parser/prism.rb +75 -0
  20. data/lib/test_prof/rspec_stamp/parser/ripper.rb +128 -0
  21. data/lib/test_prof/rspec_stamp/parser.rb +8 -120
  22. data/lib/test_prof/rspec_stamp.rb +1 -0
  23. data/lib/test_prof/rubocop.rb +10 -9
  24. data/lib/test_prof/tps_prof/profiler.rb +52 -15
  25. data/lib/test_prof/tps_prof/reporter/text.rb +27 -7
  26. data/lib/test_prof/tps_prof/rspec.rb +25 -2
  27. data/lib/test_prof/tps_prof.rb +69 -4
  28. data/lib/test_prof/version.rb +1 -1
  29. metadata +14 -13
  30. data/lib/test_prof/rspec_dissect/collectors/base.rb +0 -70
  31. data/lib/test_prof/rspec_dissect/collectors/before.rb +0 -19
  32. data/lib/test_prof/rspec_dissect/collectors/let.rb +0 -39
  33. /data/lib/{test_prof → rubocop/test_prof}/cops/rspec/aggregate_examples/its.rb +0 -0
  34. /data/lib/{test_prof → rubocop/test_prof}/cops/rspec/aggregate_examples/line_range_helpers.rb +0 -0
  35. /data/lib/{test_prof → rubocop/test_prof}/cops/rspec/aggregate_examples/metadata_helpers.rb +0 -0
  36. /data/lib/{test_prof → rubocop/test_prof}/cops/rspec/language.rb +0 -0
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ripper"
4
+
5
+ module TestProf
6
+ module RSpecStamp
7
+ module Parser
8
+ class Ripper
9
+ def parse(code)
10
+ sexp = ::Ripper.sexp(code)
11
+ return unless sexp
12
+
13
+ # sexp has the following format:
14
+ # [:program,
15
+ # [
16
+ # [
17
+ # :command,
18
+ # [:@ident, "it", [1, 0]],
19
+ # [:args_add_block, [ ... ]]
20
+ # ]
21
+ # ]
22
+ # ]
23
+ #
24
+ # or
25
+ #
26
+ # [:program,
27
+ # [
28
+ # [
29
+ # :vcall,
30
+ # [:@ident, "it", [1, 0]]
31
+ # ]
32
+ # ]
33
+ # ]
34
+ res = Result.new
35
+
36
+ fcall = sexp[1][0][1]
37
+ args_block = sexp[1][0][2]
38
+
39
+ if fcall.first == :fcall
40
+ fcall = fcall[1]
41
+ elsif fcall.first == :var_ref
42
+ res.fname = [parse_const(fcall), sexp[1][0][3][1]].join(".")
43
+ args_block = sexp[1][0][4]
44
+ end
45
+
46
+ res.fname ||= fcall[1]
47
+
48
+ return res if args_block.nil?
49
+
50
+ args_block = args_block[1] if args_block.first == :arg_paren
51
+
52
+ args = args_block[1]
53
+
54
+ if args.first.first == :string_literal
55
+ res.desc = parse_literal(args.shift)
56
+ elsif args.first.first == :var_ref || args.first.first == :const_path_ref
57
+ res.desc_const = parse_const(args.shift)
58
+ end
59
+
60
+ parse_arg(res, args.shift) until args.empty?
61
+
62
+ res
63
+ end
64
+
65
+ private
66
+
67
+ def parse_arg(res, arg)
68
+ if arg.first == :symbol_literal
69
+ res.add_tag parse_literal(arg)
70
+ elsif arg.first == :bare_assoc_hash
71
+ parse_hash(res, arg[1])
72
+ end
73
+ end
74
+
75
+ def parse_hash(res, hash_arg)
76
+ hash_arg.each do |(_, label, val)|
77
+ res.add_htag label[1][0..-2].to_sym, parse_value(val)
78
+ end
79
+ end
80
+
81
+ # Expr of the form:
82
+ # bool - [:var_ref, [:@kw, "true", [1, 24]]]
83
+ # string - [:string_literal, [:string_content, [...]]]
84
+ # int - [:@int, "3", [1, 52]]]]
85
+ def parse_value(expr)
86
+ case expr.first
87
+ when :var_ref
88
+ expr[1][1] == "true"
89
+ when :@int
90
+ expr[1].to_i
91
+ when :@float
92
+ expr[1].to_f
93
+ else
94
+ parse_literal(expr)
95
+ end
96
+ end
97
+
98
+ # Expr of the form:
99
+ # [:string_literal, [:string_content, [:@tstring_content, "is", [1, 4]]]]
100
+ def parse_literal(expr)
101
+ val = expr[1][1][1]
102
+ val = val.to_sym if expr[0] == :symbol_literal ||
103
+ expr[0] == :assoc_new
104
+ val
105
+ end
106
+
107
+ # Expr of the form:
108
+ # [:var_ref, [:@const, "User", [1, 9]]]
109
+ #
110
+ # or
111
+ #
112
+ # [:const_path_ref, [:const_path_ref, [:var_ref,
113
+ # [:@const, "User", [1, 17]]],
114
+ # [:@const, "Guest", [1, 23]]],
115
+ # [:@const, "Collection", [1, 30]]
116
+ def parse_const(expr)
117
+ if expr.first == :var_ref
118
+ expr[1][1]
119
+ elsif expr.first == :@const
120
+ expr[1]
121
+ elsif expr.first == :const_path_ref
122
+ expr[1..].map(&method(:parse_const)).join("::")
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "ripper"
4
-
5
3
  module TestProf
6
4
  module RSpecStamp
7
5
  # Parse examples headers
@@ -27,126 +25,16 @@ module TestProf
27
25
  end
28
26
  end
29
27
 
30
- class << self
31
- def parse(code)
32
- sexp = Ripper.sexp(code)
33
- return unless sexp
34
-
35
- # sexp has the following format:
36
- # [:program,
37
- # [
38
- # [
39
- # :command,
40
- # [:@ident, "it", [1, 0]],
41
- # [:args_add_block, [ ... ]]
42
- # ]
43
- # ]
44
- # ]
45
- #
46
- # or
47
- #
48
- # [:program,
49
- # [
50
- # [
51
- # :vcall,
52
- # [:@ident, "it", [1, 0]]
53
- # ]
54
- # ]
55
- # ]
56
- res = Result.new
57
-
58
- fcall = sexp[1][0][1]
59
- args_block = sexp[1][0][2]
60
-
61
- if fcall.first == :fcall
62
- fcall = fcall[1]
63
- elsif fcall.first == :var_ref
64
- res.fname = [parse_const(fcall), sexp[1][0][3][1]].join(".")
65
- args_block = sexp[1][0][4]
66
- end
67
-
68
- res.fname ||= fcall[1]
69
-
70
- return res if args_block.nil?
71
-
72
- args_block = args_block[1] if args_block.first == :arg_paren
73
-
74
- args = args_block[1]
75
-
76
- if args.first.first == :string_literal
77
- res.desc = parse_literal(args.shift)
78
- elsif args.first.first == :var_ref || args.first.first == :const_path_ref
79
- res.desc_const = parse_const(args.shift)
80
- end
81
-
82
- parse_arg(res, args.shift) until args.empty?
83
-
84
- res
28
+ instance =
29
+ begin
30
+ require_relative "parser/prism"
31
+ self::Prism.new
32
+ rescue LoadError
33
+ require_relative "parser/ripper"
34
+ self::Ripper.new
85
35
  end
86
- # rubocop: enable Metrics/CyclomaticComplexity
87
- # rubocop: enable Metrics/PerceivedComplexity
88
36
 
89
- private
90
-
91
- def parse_arg(res, arg)
92
- if arg.first == :symbol_literal
93
- res.add_tag parse_literal(arg)
94
- elsif arg.first == :bare_assoc_hash
95
- parse_hash(res, arg[1])
96
- end
97
- end
98
-
99
- def parse_hash(res, hash_arg)
100
- hash_arg.each do |(_, label, val)|
101
- res.add_htag label[1][0..-2].to_sym, parse_value(val)
102
- end
103
- end
104
-
105
- # Expr of the form:
106
- # bool - [:var_ref, [:@kw, "true", [1, 24]]]
107
- # string - [:string_literal, [:string_content, [...]]]
108
- # int - [:@int, "3", [1, 52]]]]
109
- def parse_value(expr)
110
- case expr.first
111
- when :var_ref
112
- expr[1][1] == "true"
113
- when :@int
114
- expr[1].to_i
115
- when :@float
116
- expr[1].to_f
117
- else
118
- parse_literal(expr)
119
- end
120
- end
121
-
122
- # Expr of the form:
123
- # [:string_literal, [:string_content, [:@tstring_content, "is", [1, 4]]]]
124
- def parse_literal(expr)
125
- val = expr[1][1][1]
126
- val = val.to_sym if expr[0] == :symbol_literal ||
127
- expr[0] == :assoc_new
128
- val
129
- end
130
-
131
- # Expr of the form:
132
- # [:var_ref, [:@const, "User", [1, 9]]]
133
- #
134
- # or
135
- #
136
- # [:const_path_ref, [:const_path_ref, [:var_ref,
137
- # [:@const, "User", [1, 17]]],
138
- # [:@const, "Guest", [1, 23]]],
139
- # [:@const, "Collection", [1, 30]]
140
- def parse_const(expr)
141
- if expr.first == :var_ref
142
- expr[1][1]
143
- elsif expr.first == :@const
144
- expr[1]
145
- elsif expr.first == :const_path_ref
146
- expr[1..].map(&method(:parse_const)).join("::")
147
- end
148
- end
149
- end
37
+ define_singleton_method(:parse) { |code| instance.parse(code) }
150
38
  end
151
39
  end
152
40
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "test_prof/core"
3
4
  require "test_prof/logging"
4
5
  require "test_prof/rspec_stamp/parser"
5
6
 
@@ -1,13 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "test_prof/utils"
4
- supported = TestProf::Utils.verify_gem_version("rubocop", at_least: "0.51.0")
5
- unless supported
6
- warn "TestProf cops require RuboCop >= 0.51.0 to run."
7
- return
8
- end
3
+ warn <<~MSG
4
+ !!!
9
5
 
10
- require "rubocop"
6
+ Please, update your .rubocop.yml configuration to load TestProf plugin as follows (and fix the error below):
11
7
 
12
- require "test_prof/cops/plugin"
13
- require "test_prof/cops/rspec/aggregate_examples"
8
+ plugins:
9
+ - test-prof
10
+
11
+ !!!
12
+
13
+
14
+ MSG
@@ -1,18 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "test_prof/utils/sized_ordered_set"
4
+ require "forwardable"
4
5
 
5
6
  module TestProf
6
7
  module TPSProf
7
8
  class Profiler
8
- attr_reader :top_count, :groups, :total_count, :total_time, :threshold
9
+ extend Forwardable
9
10
 
10
- def initialize(top_count, threshold: 10)
11
- @threshold = threshold
12
- @top_count = top_count
11
+ attr_reader :top_count, :groups, :total_count, :total_time,
12
+ :config
13
+
14
+ def_delegators :@config, :min_examples_count, :min_group_time, :min_target_tps,
15
+ :mode, :max_examples_count, :max_group_time, :min_tps
16
+
17
+ def initialize(top_count, config)
18
+ @config = config
19
+ # In strict mode, we use the sorted set to keep track of offenders
20
+ # to show in the end
21
+ @top_count = (config.mode == :strict) ? 100 : top_count
13
22
  @total_count = 0
14
23
  @total_time = 0.0
15
- @groups = Utils::SizedOrderedSet.new(top_count, sort_by: :tps)
24
+ @groups = Utils::SizedOrderedSet.new(top_count, sort_by: :penalty)
16
25
  end
17
26
 
18
27
  def group_started(id)
@@ -22,20 +31,48 @@ module TestProf
22
31
  @group_started_at = TestProf.now
23
32
  end
24
33
 
25
- def group_finished(id)
26
- return unless @examples_count >= threshold
34
+ def group_finished(group)
35
+ return unless @examples_count >= min_examples_count
36
+
37
+ total_time = TestProf.now - @group_started_at
38
+ shared_setup_time = total_time - @examples_time
39
+
40
+ return unless total_time >= min_group_time
27
41
 
28
- # Context-time
29
- group_time = (TestProf.now - @group_started_at) - @examples_time
30
- run_time = @examples_time + group_time
42
+ tps = (@examples_count / total_time).round(2)
31
43
 
32
- groups << {
33
- id: id,
34
- run_time: run_time,
35
- group_time: group_time,
44
+ return unless tps < min_target_tps
45
+
46
+ # How much time did we waste compared to the target TPS
47
+ penalty = @examples_count * ((1.0 / tps) - (1.0 / min_target_tps))
48
+
49
+ item = {
50
+ id: group,
51
+ total_time: total_time,
52
+ shared_setup_time: shared_setup_time,
36
53
  count: @examples_count,
37
- tps: -(@examples_count / run_time).round(2)
54
+ tps: tps,
55
+ penalty: penalty
38
56
  }
57
+
58
+ if mode == :strict
59
+ location = group.metadata[:location]
60
+
61
+ if TPSProf.handle_group_strictly(
62
+ GroupInfo.new(
63
+ group: group,
64
+ location: location,
65
+ examples_count: @examples_count,
66
+ total_time: total_time,
67
+ tps: tps,
68
+ penalty: penalty
69
+ )
70
+ )
71
+ groups << item
72
+ end
73
+ else
74
+ groups << item
75
+ end
39
76
  end
40
77
 
41
78
  def example_started(id)
@@ -23,25 +23,45 @@ module TestProf
23
23
  <<~MSG
24
24
  Total TPS (tests per second): #{total_tps}
25
25
 
26
- Top #{profiler.top_count} slowest suites by TPS (tests per second) (min examples per group: #{profiler.threshold}):
27
-
28
26
  MSG
29
27
 
28
+ if profiler.mode == :strict
29
+ return if groups.empty?
30
+
31
+ msgs << if groups.size < profiler.top_count
32
+ <<~MSG
33
+ Suites violating TPS limits:
34
+
35
+ MSG
36
+ else
37
+ <<~MSG
38
+ Top #{profiler.top_count} suites violating TPS limits:
39
+
40
+ MSG
41
+ end
42
+ else
43
+ msgs <<
44
+ <<~MSG
45
+ Top #{profiler.top_count} slowest suites by TPS (tests per second):
46
+
47
+ MSG
48
+ end
49
+
30
50
  groups.each do |group|
31
51
  description = group[:id].top_level_description
32
52
  location = group[:id].metadata[:location]
33
- time = group[:run_time]
34
- group_time = group[:group_time]
53
+ time = group[:total_time]
54
+ setup_time = group[:shared_setup_time]
35
55
  count = group[:count]
36
- tps = -group[:tps]
56
+ tps = group[:tps]
37
57
 
38
58
  msgs <<
39
59
  <<~GROUP
40
- #{description.truncate} (#{location}) – #{tps} TPS (#{time.duration} / #{count}), group time: #{group_time.duration}
60
+ #{description.truncate} (#{location}) – #{tps} TPS (#{time.duration} / #{count}, shared setup time: #{setup_time.duration})
41
61
  GROUP
42
62
  end
43
63
 
44
- log :info, msgs.join
64
+ log((profiler.mode == :strict) ? :error : :info, msgs.join)
45
65
  end
46
66
  end
47
67
  end
@@ -15,27 +15,50 @@ module TestProf
15
15
  attr_reader :reporter, :profiler
16
16
 
17
17
  def initialize
18
- @profiler = Profiler.new(TPSProf.config.top_count, threshold: TPSProf.config.threshold)
18
+ @profiler = Profiler.new(TPSProf.config.top_count, TPSProf.config)
19
19
  @reporter = TPSProf.config.reporter
20
20
 
21
- log :info, "TPSProf enabled (top-#{TPSProf.config.top_count})"
21
+ conf = TPSProf.config
22
+ if conf.mode == :strict
23
+ config_parts = []
24
+
25
+ if conf.custom_strict_handler
26
+ config_parts << "custom handler"
27
+ else
28
+ config_parts << "max examples: #{conf.max_examples_count}" if conf.max_examples_count
29
+ config_parts << "max group time: #{conf.max_group_time}" if conf.max_group_time
30
+ config_parts << "min tps: #{conf.min_tps}" if conf.min_tps
31
+ end
32
+
33
+ log :info, "TPSProf strict enabled (#{config_parts.join(", ")})"
34
+ else
35
+ log :info, "TPSProf enabled (top-#{TPSProf.config.top_count})"
36
+ end
22
37
  end
23
38
 
24
39
  def example_group_started(notification)
25
40
  return unless notification.group.top_level?
41
+ return if notification.group.metadata[:tps_prof] == :ignore
42
+
26
43
  profiler.group_started notification.group
27
44
  end
28
45
 
29
46
  def example_group_finished(notification)
30
47
  return unless notification.group.top_level?
48
+ return if notification.group.metadata[:tps_prof] == :ignore
49
+
31
50
  profiler.group_finished notification.group
32
51
  end
33
52
 
34
53
  def example_started(notification)
54
+ return if notification.example.metadata[:tps_prof] == :ignore
55
+
35
56
  profiler.example_started notification.example
36
57
  end
37
58
 
38
59
  def example_finished(notification)
60
+ return if notification.example.metadata[:tps_prof] == :ignore
61
+
39
62
  profiler.example_finished notification.example
40
63
  end
41
64
 
@@ -8,25 +8,81 @@ module TestProf
8
8
  #
9
9
  # Example:
10
10
  #
11
+ # # Show top-10 groups with the worst TPS
11
12
  # TPS_PROF=10 rspec ...
12
13
  #
14
+ # # Report (as errors) groups with lower TPS
15
+ # TPS_PROF_MIN_TPS=10 TPS_PROF=strict rspec ...
13
16
  module TPSProf
17
+ class Error < StandardError; end
18
+
19
+ class GroupInfo < Struct.new(:group, :location, :examples_count, :total_time, :tps, :penalty, keyword_init: true)
20
+ end
21
+
14
22
  class Configuration
15
- attr_accessor :top_count, :threshold, :reporter
23
+ attr_accessor :top_count, :reporter, :mode
24
+
25
+ # Thresholds
26
+ attr_accessor(
27
+ :min_examples_count, # Ignore groups with fewer examples
28
+ :min_group_time, # Ignore groups with less total time (in seconds)
29
+ :min_target_tps, # Ignore groups with higher TPS
30
+ :max_examples_count, # Report groups with more examples in strict mode
31
+ :max_group_time, # Report groups with more total time in strict mode
32
+ :min_tps # Report groups with lower TPS in strict mode
33
+ )
34
+
35
+ attr_reader :custom_strict_handler
16
36
 
17
37
  def initialize
18
- @top_count = ENV["TPS_PROF"].to_i
19
- @top_count = 10 if @top_count == 1
20
- @threshold = ENV.fetch("TPS_PROF_MIN", 10).to_i
38
+ @mode = (ENV["TPS_PROF_MODE"] || ((ENV["TPS_PROF"] == "strict") ? :strict : :profile)).to_sym
39
+ @top_count = ENV["TPS_PROF_COUNT"]&.to_i || ((ENV["TPS_PROF"].to_i > 1) ? ENV["TPS_PROF"].to_i : nil) || 10
40
+
41
+ @min_examples_count = ENV.fetch("TPS_PROF_MIN_EXAMPLES", 10).to_i
42
+ @min_group_time = ENV.fetch("TPS_PROF_MIN_TIME", 5).to_i
43
+ @min_target_tps = ENV.fetch("TPS_PROF_TARGET_TPS", 30).to_i
44
+
45
+ @max_examples_count = ENV["TPS_PROF_MAX_EXAMPLES"]&.to_i
46
+ @max_group_time = ENV["TPS_PROF_MAX_TIME"]&.to_i
47
+ @min_tps = ENV["TPS_PROF_MIN_TPS"]&.to_i
48
+
21
49
  @reporter = resolve_reporter(ENV["TPS_PROF_FORMAT"])
22
50
  end
23
51
 
52
+ def strict_handler
53
+ @strict_handler ||= method(:default_strict_handler)
54
+ end
55
+
56
+ def strict_handler=(val)
57
+ @strict_handler = val
58
+ @custom_strict_handler = true
59
+ end
60
+
24
61
  private
25
62
 
26
63
  def resolve_reporter(format)
27
64
  # TODO: support other formats
28
65
  TPSProf::Reporter::Text.new
29
66
  end
67
+
68
+ def default_strict_handler(group_info)
69
+ error_msg = nil
70
+ location = group_info.location
71
+
72
+ if max_examples_count && group_info.examples_count > max_examples_count
73
+ error_msg ||= "Group #{location} has too many examples: #{group_info.examples_count} > #{max_examples_count}"
74
+ end
75
+
76
+ if max_group_time && group_info.total_time > max_group_time
77
+ error_msg ||= "Group #{location} has too long total time: #{group_info.total_time} > #{max_group_time}"
78
+ end
79
+
80
+ if min_tps && group_info.tps < min_tps
81
+ error_msg ||= "Group #{location} has too low TPS: #{group_info.tps} < #{min_tps}"
82
+ end
83
+
84
+ raise error_msg if error_msg
85
+ end
30
86
  end
31
87
 
32
88
  class << self
@@ -37,6 +93,15 @@ module TestProf
37
93
  def configure
38
94
  yield config
39
95
  end
96
+
97
+ def handle_group_strictly(group_info)
98
+ reporter = ::RSpec.configuration.reporter
99
+ config.strict_handler.call(group_info)
100
+ false
101
+ rescue => err
102
+ reporter.notify_non_example_exception(Error.new(err.message), "")
103
+ true
104
+ end
40
105
  end
41
106
  end
42
107
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TestProf
4
- VERSION = "1.5.0"
4
+ VERSION = "1.6.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: test-prof
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-12-04 00:00:00.000000000 Z
11
+ date: 2026-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -123,6 +123,15 @@ files:
123
123
  - lib/minitest/base_reporter.rb
124
124
  - lib/minitest/event_prof_formatter.rb
125
125
  - lib/minitest/test_prof_plugin.rb
126
+ - lib/rubocop/test_prof.rb
127
+ - lib/rubocop/test_prof/cops/rspec/aggregate_examples.rb
128
+ - lib/rubocop/test_prof/cops/rspec/aggregate_examples/its.rb
129
+ - lib/rubocop/test_prof/cops/rspec/aggregate_examples/line_range_helpers.rb
130
+ - lib/rubocop/test_prof/cops/rspec/aggregate_examples/matchers_with_side_effects.rb
131
+ - lib/rubocop/test_prof/cops/rspec/aggregate_examples/metadata_helpers.rb
132
+ - lib/rubocop/test_prof/cops/rspec/aggregate_examples/node_matchers.rb
133
+ - lib/rubocop/test_prof/cops/rspec/language.rb
134
+ - lib/rubocop/test_prof/plugin.rb
126
135
  - lib/test-prof.rb
127
136
  - lib/test_prof.rb
128
137
  - lib/test_prof/any_fixture.rb
@@ -135,14 +144,6 @@ files:
135
144
  - lib/test_prof/before_all.rb
136
145
  - lib/test_prof/before_all/adapters/active_record.rb
137
146
  - lib/test_prof/before_all/isolator.rb
138
- - lib/test_prof/cops/plugin.rb
139
- - lib/test_prof/cops/rspec/aggregate_examples.rb
140
- - lib/test_prof/cops/rspec/aggregate_examples/its.rb
141
- - lib/test_prof/cops/rspec/aggregate_examples/line_range_helpers.rb
142
- - lib/test_prof/cops/rspec/aggregate_examples/matchers_with_side_effects.rb
143
- - lib/test_prof/cops/rspec/aggregate_examples/metadata_helpers.rb
144
- - lib/test_prof/cops/rspec/aggregate_examples/node_matchers.rb
145
- - lib/test_prof/cops/rspec/language.rb
146
147
  - lib/test_prof/core.rb
147
148
  - lib/test_prof/event_prof.rb
148
149
  - lib/test_prof/event_prof/custom_events.rb
@@ -200,12 +201,12 @@ files:
200
201
  - lib/test_prof/recipes/rspec/let_it_be.rb
201
202
  - lib/test_prof/recipes/rspec/sample.rb
202
203
  - lib/test_prof/rspec_dissect.rb
203
- - lib/test_prof/rspec_dissect/collectors/base.rb
204
- - lib/test_prof/rspec_dissect/collectors/before.rb
205
- - lib/test_prof/rspec_dissect/collectors/let.rb
204
+ - lib/test_prof/rspec_dissect/collector.rb
206
205
  - lib/test_prof/rspec_dissect/rspec.rb
207
206
  - lib/test_prof/rspec_stamp.rb
208
207
  - lib/test_prof/rspec_stamp/parser.rb
208
+ - lib/test_prof/rspec_stamp/parser/prism.rb
209
+ - lib/test_prof/rspec_stamp/parser/ripper.rb
209
210
  - lib/test_prof/rspec_stamp/rspec.rb
210
211
  - lib/test_prof/rubocop.rb
211
212
  - lib/test_prof/ruby_prof.rb