test-prof 0.3.0.beta → 0.3.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1c450b597564326b40288fbee6f6ceb27f6d9ec8
4
- data.tar.gz: '080d7077ea0713b74a223a421c4a9af6dfc6d8f7'
3
+ metadata.gz: 6af9c62cf692b558711fe96e40cff6e82fb49b99
4
+ data.tar.gz: e308236c1ed5c5c9abc81e6a7a672adfcbc49dbf
5
5
  SHA512:
6
- metadata.gz: fcbfd05e6c5889d302c309f55a6285e0764838111dcb60af31f0b8ce20cd65619281dc3433c3be35cc9de748a65a3b63ccc58a6bb3506ecc8d490d8652b554e9
7
- data.tar.gz: 0e5c02a21fe5ebeeb9b457abbb230158a6289fa66e5e694778f7acb0ce0293f50e9ab9c2f97c60b8d9fc78b9738b39e88fa6ed18f6b22f613f198ce2d19bd404
6
+ metadata.gz: 493dfc669dec10234282bc6c24c22a4da701537f30f920f295a98993fd029abd47f5fd419e27779711d2f9266c4011778bf61fafb9da8894bfb20b3d18e728d9
7
+ data.tar.gz: 57f0c57b1320e4f2fa50249e0f88da00fb6091b32faef4f16ef57c65deb784ba203b9bab10d54e5ebc0c279a79de02539f69ea3af2bc429b492060c29ad2d253
data/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@
4
4
 
5
5
  Features:
6
6
 
7
+ - [#17](https://github.com/palkan/test-prof/pull/17) Combine RSpecStamp with EventProf and RSpecDissect. ([@palkan][])
8
+
9
+ It is possible now to automatically mark _slow_ examples and groups with custom tags. For example:
10
+
11
+ ```sh
12
+ EVENT_PROF="sql.active_record" EVENT_PROF_STAMP="slow:sql" rspec ...
13
+ ```
14
+
15
+ After running the command above the top 5 slowest example groups would be marked with `slow: :sql` tag.
16
+
7
17
  - [#14](https://github.com/palkan/test-prof/pull/14) RSpecDissect profiler. ([@palkan][])
8
18
 
9
19
  RSpecDissect tracks how much time do you spend in `before` hooks
@@ -13,6 +23,10 @@ and memoization helpers (i.e. `let`) in your tests.
13
23
 
14
24
  Just like `let`, but persist the result for the whole group (i.e. `let` + `before_all`).
15
25
 
26
+ ## 0.2.5
27
+
28
+ - [#16](https://github.com/palkan/test-prof/pull/16) Support Ruby >= 2.2.0 (was >= 2.3.0). ([@palkan][])
29
+
16
30
  ## 0.2.4
17
31
 
18
32
  - EventProf: Fix regression bug with examples profiling. ([@palkan][])
data/README.md CHANGED
@@ -24,6 +24,12 @@ Of course, we have some [solutions](#tips-and-tricks) for common performance iss
24
24
 
25
25
  See [Table of Contents](#table-of-contents) for more.
26
26
 
27
+ Supported Ruby versions:
28
+
29
+ - Ruby (MRI) >= 2.2.0
30
+
31
+ - JRuby >= 9.1.0.0
32
+
27
33
  <a href="https://evilmartians.com/">
28
34
  <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
29
35
 
data/guides/event_prof.md CHANGED
@@ -66,6 +66,16 @@ EVENT_PROF_RANK=count EVENT_PROF='instantiation.active_record' be rspec
66
66
 
67
67
  See [event_prof.rb](https://github.com/palkan/test-prof/tree/master/lib/test_prof/event_prof.rb) for all available configuration options and their usage.
68
68
 
69
+ ## Using with RSpecStamp
70
+
71
+ EventProf can be used with [RSpec Stamp](https://github.com/palkan/test-prof/tree/master/guides/rspec_stamp.md) to automatically mark _slow_ examples with custom tags. For example:
72
+
73
+ ```sh
74
+ EVENT_PROF="sql.active_record" EVENT_PROF_STAMP="slow:sql" rspec ...
75
+ ```
76
+
77
+ After running the command above the slowest example groups (and examples if configured) would be marked with the `slow: :sql` tag.
78
+
69
79
  ## Custom Instrumentation
70
80
 
71
81
  To use EventProf with your instrumentation engine just complete the two following steps:
@@ -47,3 +47,12 @@ You can also specify the number of top slow groups through `RD_TOP` variable:
47
47
  RD=1 RD_TOP=10 rspec ...
48
48
  ```
49
49
 
50
+ ## Using with RSpecStamp
51
+
52
+ RSpecDissect can be used with [RSpec Stamp](https://github.com/palkan/test-prof/tree/master/guides/rspec_stamp.md) to automatically mark _slow_ examples with custom tags. For example:
53
+
54
+ ```sh
55
+ RD=1 RD_STAMP="slow" rspec ...
56
+ ```
57
+
58
+ After running the command above the slowest example groups would be marked with the `:slow` tag.
@@ -39,7 +39,7 @@ module RuboCop
39
39
 
40
40
  def on_block(node)
41
41
  method, _args, body = *node
42
- return unless body&.begin_type?
42
+ return unless body && body.begin_type?
43
43
 
44
44
  _receiver, method_name, _object = *method
45
45
  return unless GROUP_BLOCKS.include?(method_name)
@@ -109,7 +109,7 @@ module RuboCop
109
109
  end
110
110
 
111
111
  def oneliner?(node)
112
- node&.block_type? &&
112
+ node && node.block_type? &&
113
113
  (node.source.lines.size == 1) &&
114
114
  example_node?(node)
115
115
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "test_prof/rspec_stamp"
3
4
  require "test_prof/event_prof/instrumentations/active_support"
4
5
  require "test_prof/utils/sized_ordered_set"
5
6
 
@@ -39,6 +40,13 @@ module TestProf
39
40
  @top_count = (ENV['EVENT_PROF_TOP'] || 5).to_i
40
41
  @per_example = ENV['EVENT_PROF_EXAMPLES'] == '1'
41
42
  @rank_by = (ENV['EVENT_PROF_RANK'] || :time).to_sym
43
+ @stamp = ENV['EVENT_PROF_STAMP']
44
+
45
+ RSpecStamp.config.tags = @stamp if stamp?
46
+ end
47
+
48
+ def stamp?
49
+ !@stamp.nil?
42
50
  end
43
51
 
44
52
  def per_example?
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "test_prof/ext/string_strip_heredoc"
4
+
5
+ using TestProf::StringStripHeredoc
6
+
3
7
  module TestProf::EventProf::CustomEvents
4
8
  module FactoryCreate # :nodoc: all
5
9
  module RunnerPatch
@@ -42,7 +46,7 @@ end
42
46
  TestProf.activate('EVENT_PROF', 'factory.create') do
43
47
  if TestProf.require(
44
48
  'factory_girl',
45
- <<~MSG
49
+ <<-MSG.strip_heredoc
46
50
  Failed to load FactoryGirl.
47
51
 
48
52
  Make sure that "factory_girl" gem is in your Gemfile.
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "test_prof/ext/string_strip_heredoc"
4
+
5
+ using TestProf::StringStripHeredoc
6
+
3
7
  module TestProf::EventProf::CustomEvents
4
8
  module SidekiqInline # :nodoc: all
5
9
  module ClientPatch
@@ -39,7 +43,7 @@ end
39
43
  TestProf.activate('EVENT_PROF', 'sidekiq.inline') do
40
44
  if TestProf.require(
41
45
  'sidekiq/testing',
42
- <<~MSG
46
+ <<-MSG.strip_heredoc
43
47
  Failed to load Sidekiq.
44
48
 
45
49
  Make sure that "sidekiq" gem is in your Gemfile.
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "test_prof/ext/string_strip_heredoc"
4
+
5
+ using TestProf::StringStripHeredoc
6
+
3
7
  module TestProf::EventProf::CustomEvents
4
8
  module SidekiqJobs # :nodoc: all
5
9
  module ClientPatch
@@ -26,7 +30,7 @@ end
26
30
  TestProf.activate('EVENT_PROF', 'sidekiq.jobs') do
27
31
  if TestProf.require(
28
32
  'sidekiq/testing',
29
- <<~MSG
33
+ <<-MSG.strip_heredoc
30
34
  Failed to load Sidekiq.
31
35
 
32
36
  Make sure that "sidekiq" gem is in your Gemfile.
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "test_prof/ext/float_duration"
4
4
  require "test_prof/ext/string_truncate"
5
+ require "test_prof/ext/string_strip_heredoc"
5
6
 
6
7
  module TestProf
7
8
  module EventProf
@@ -9,6 +10,7 @@ module TestProf
9
10
  include Logging
10
11
  using FloatDuration
11
12
  using StringTruncate
13
+ using StringStripHeredoc
12
14
 
13
15
  NOTIFICATIONS = %i[
14
16
  example_group_started
@@ -45,7 +47,7 @@ module TestProf
45
47
  msgs = []
46
48
 
47
49
  msgs <<
48
- <<~MSG
50
+ <<-MSG.strip_heredoc
49
51
  EventProf results for #{@profiler.event}
50
52
 
51
53
  Total time: #{@profiler.total_time.duration}
@@ -60,7 +62,7 @@ module TestProf
60
62
  location = group[:id].metadata[:location]
61
63
 
62
64
  msgs <<
63
- <<~GROUP
65
+ <<-GROUP.strip_heredoc
64
66
  #{description.truncate} (#{location}) – #{group[:time].duration} (#{group[:count]} / #{group[:examples]})
65
67
  GROUP
66
68
  end
@@ -72,13 +74,48 @@ module TestProf
72
74
  description = example[:id].description
73
75
  location = example[:id].metadata[:location]
74
76
  msgs <<
75
- <<~GROUP
77
+ <<-GROUP.strip_heredoc
76
78
  #{description.truncate} (#{location}) – #{example[:time].duration} (#{example[:count]})
77
79
  GROUP
78
80
  end
79
81
  end
80
82
 
81
83
  log :info, msgs.join
84
+
85
+ stamp! if EventProf.config.stamp?
86
+ end
87
+
88
+ def stamp!
89
+ result = @profiler.results
90
+
91
+ stamper = RSpecStamp::Stamper.new
92
+
93
+ examples = Hash.new { |h, k| h[k] = [] }
94
+
95
+ (result[:groups].to_a + result.fetch(:examples, []).to_a)
96
+ .map { |obj| obj[:id].metadata[:location] }.each do |location|
97
+ file, line = location.split(":")
98
+ examples[file] << line.to_i
99
+ end
100
+
101
+ examples.each do |file, lines|
102
+ stamper.stamp_file(file, lines.uniq)
103
+ end
104
+
105
+ msgs = []
106
+
107
+ msgs <<
108
+ <<-MSG.strip_heredoc
109
+ RSpec Stamp results
110
+
111
+ Total patches: #{stamper.total}
112
+ Total files: #{examples.keys.size}
113
+
114
+ Failed patches: #{stamper.failed}
115
+ Ignored files: #{stamper.ignored}
116
+ MSG
117
+
118
+ log :info, msgs.join
82
119
  end
83
120
  end
84
121
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TestProf
4
+ # Ruby 2.3 #bsearch_index method (for usage with older Rubies)
5
+ # Straighforward and non-optimal implementation,
6
+ # just for compatiblity
7
+ module ArrayBSearchIndex
8
+ refine Array do
9
+ def bsearch_index(&block)
10
+ el = bsearch(&block)
11
+ index(el)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TestProf
4
+ # Add #strip_heredoc method to use instead of
5
+ # squiggly docs (to support older Rubies)
6
+ module StringStripHeredoc
7
+ refine String do
8
+ def strip_heredoc
9
+ min = scan(/^[ \t]*(?=\S)/).min
10
+ indent = min ? min.size : 0
11
+ gsub(/^[ \t]{#{indent}}/, '')
12
+ end
13
+ end
14
+ end
15
+ end
@@ -16,7 +16,7 @@ module TestProf
16
16
  end
17
17
 
18
18
  def bad?
19
- count.positive? && queries_count.zero?
19
+ count > 0 && queries_count.zero?
20
20
  end
21
21
  end
22
22
 
@@ -111,7 +111,7 @@ module TestProf
111
111
  end
112
112
 
113
113
  def within_factory?
114
- @depth.positive?
114
+ @depth > 0
115
115
  end
116
116
 
117
117
  def ignore?
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "test_prof/ext/float_duration"
4
+ require "test_prof/ext/string_strip_heredoc"
4
5
 
5
6
  module TestProf
6
7
  module FactoryDoctor
7
8
  class RSpecListener # :nodoc:
8
9
  include Logging
9
10
  using FloatDuration
11
+ using StringStripHeredoc
10
12
 
11
- SUCCESS_MESSAGE = 'FactoryDoctor says: "Looks good to me!"'
13
+ SUCCESS_MESSAGE = 'FactoryDoctor says: "Looks good to me!"'.freeze
12
14
 
13
15
  NOTIFICATIONS = %i[
14
16
  example_started
@@ -49,7 +51,7 @@ module TestProf
49
51
  msgs = []
50
52
 
51
53
  msgs <<
52
- <<~MSG
54
+ <<-MSG.strip_heredoc
53
55
  FactoryDoctor report
54
56
 
55
57
  Total (potentially) bad examples: #{@count}
@@ -1,16 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "test_prof/ext/string_strip_heredoc"
4
+
3
5
  module TestProf::FactoryProf
4
6
  module Printers
5
7
  module Simple # :nodoc: all
6
8
  class << self
7
9
  include TestProf::Logging
10
+ using TestProf::StringStripHeredoc
8
11
 
9
12
  def dump(result)
10
13
  msgs = []
11
14
 
12
15
  msgs <<
13
- <<~MSG
16
+ <<-MSG.strip_heredoc
14
17
  Factories usage
15
18
 
16
19
  total top-level name
@@ -12,7 +12,7 @@ RSpec.shared_context "any_fixture:clean", with_clean_fixture: true do
12
12
 
13
13
  def open_transaction?
14
14
  pool = ActiveRecord::Base.connection_pool
15
- pool.active_connection? && pool.connection.open_transactions.positive?
15
+ pool.active_connection? && pool.connection.open_transactions > 0
16
16
  end
17
17
  end
18
18
 
@@ -22,7 +22,7 @@ module TestProf
22
22
  # Use uniq prefix for instance variables to avoid collisions
23
23
  # We want to use the power of Ruby's unicode support)
24
24
  # And we love cats!)
25
- PREFIX = "@😸"
25
+ PREFIX = RUBY_ENGINE == 'jruby' ? "@__jruby_is_not_cat_friendly__".freeze : "@😸".freeze
26
26
 
27
27
  def let_it_be(identifier, **options, &block)
28
28
  initializer = proc do
@@ -41,7 +41,13 @@ module TestProf
41
41
  def define_let_it_be_methods(identifier, reload: false, refind: false)
42
42
  let_accessor = -> { instance_variable_get(:"#{PREFIX}#{identifier}") }
43
43
 
44
- let_accessor = -> { instance_variable_get(:"#{PREFIX}#{identifier}")&.reload } if reload
44
+ if reload
45
+ let_accessor = lambda do
46
+ record = instance_variable_get(:"#{PREFIX}#{identifier}")
47
+ next unless record.is_a?(::ActiveRecord::Base)
48
+ record.reload
49
+ end
50
+ end
45
51
 
46
52
  if refind
47
53
  let_accessor = lambda do
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "test_prof/rspec_stamp"
3
4
  require "test_prof/logging"
4
5
 
5
6
  module TestProf
@@ -36,6 +37,13 @@ module TestProf
36
37
 
37
38
  def initialize
38
39
  @top_count = (ENV['RD_TOP'] || 5).to_i
40
+ @stamp = ENV['RD_STAMP']
41
+
42
+ RSpecStamp.config.tags = @stamp if stamp?
43
+ end
44
+
45
+ def stamp?
46
+ !@stamp.nil?
39
47
  end
40
48
  end
41
49
 
@@ -3,6 +3,7 @@
3
3
  require "test_prof/ext/float_duration"
4
4
  require "test_prof/ext/string_truncate"
5
5
  require "test_prof/utils/sized_ordered_set"
6
+ require "test_prof/ext/string_strip_heredoc"
6
7
 
7
8
  module TestProf
8
9
  module RSpecDissect
@@ -10,6 +11,7 @@ module TestProf
10
11
  include Logging
11
12
  using FloatDuration
12
13
  using StringTruncate
14
+ using StringStripHeredoc
13
15
 
14
16
  NOTIFICATIONS = %i[
15
17
  example_finished
@@ -58,7 +60,7 @@ module TestProf
58
60
  msgs = []
59
61
 
60
62
  msgs <<
61
- <<~MSG
63
+ <<-MSG.strip_heredoc
62
64
  RSpecDissect report
63
65
 
64
66
  Total time: #{@total_examples_time.duration}
@@ -68,32 +70,65 @@ module TestProf
68
70
  MSG
69
71
 
70
72
  msgs <<
71
- <<~MSG
73
+ <<-MSG.strip_heredoc
72
74
  Top #{top_count} slowest suites (by `before(:each)` time):
73
75
 
74
76
  MSG
75
77
 
76
78
  @before_results.each do |group|
77
79
  msgs <<
78
- <<~GROUP
80
+ <<-GROUP.strip_heredoc
79
81
  #{group[:desc].truncate} (#{group[:loc]}) – #{group[:before].duration} of #{group[:total].duration} (#{group[:count]})
80
82
  GROUP
81
83
  end
82
84
 
83
85
  msgs <<
84
- <<~MSG
86
+ <<-MSG.strip_heredoc
85
87
  Top #{top_count} slowest suites (by `let` time):
86
88
 
87
89
  MSG
88
90
 
89
91
  @memo_results.each do |group|
90
92
  msgs <<
91
- <<~GROUP
93
+ <<-GROUP.strip_heredoc
92
94
  #{group[:desc].truncate} (#{group[:loc]}) – #{group[:memo].duration} of #{group[:total].duration} (#{group[:count]})
93
95
  GROUP
94
96
  end
95
97
 
96
98
  log :info, msgs.join
99
+
100
+ stamp! if RSpecDissect.config.stamp?
101
+ end
102
+
103
+ def stamp!
104
+ stamper = RSpecStamp::Stamper.new
105
+
106
+ examples = Hash.new { |h, k| h[k] = [] }
107
+
108
+ (@before_results.to_a + @memo_results.to_a)
109
+ .map { |obj| obj[:loc] }.each do |location|
110
+ file, line = location.split(":")
111
+ examples[file] << line.to_i
112
+ end
113
+
114
+ examples.each do |file, lines|
115
+ stamper.stamp_file(file, lines.uniq)
116
+ end
117
+
118
+ msgs = []
119
+
120
+ msgs <<
121
+ <<-MSG.strip_heredoc
122
+ RSpec Stamp results
123
+
124
+ Total patches: #{stamper.total}
125
+ Total files: #{examples.keys.size}
126
+
127
+ Failed patches: #{stamper.failed}
128
+ Ignored files: #{stamper.ignored}
129
+ MSG
130
+
131
+ log :info, msgs.join
97
132
  end
98
133
 
99
134
  private
@@ -44,6 +44,49 @@ module TestProf
44
44
  end
45
45
  end
46
46
 
47
+ # Stamper collects statistics about applying tags
48
+ # to examples.
49
+ class Stamper
50
+ include TestProf::Logging
51
+
52
+ attr_reader :total, :failed, :ignored
53
+
54
+ def initialize
55
+ @total = 0
56
+ @failed = 0
57
+ @ignored = 0
58
+ end
59
+
60
+ def stamp_file(file, lines)
61
+ @total += lines.size
62
+ return if ignored?(file)
63
+
64
+ log :info, "(dry-run) Patching #{file}" if dry_run?
65
+
66
+ code = File.readlines(file)
67
+
68
+ @failed += RSpecStamp.apply_tags(code, lines, RSpecStamp.config.tags)
69
+
70
+ File.write(file, code.join) unless dry_run?
71
+ end
72
+
73
+ private
74
+
75
+ def ignored?(file)
76
+ ignored = RSpecStamp.config.ignore_files.find do |pattern|
77
+ file =~ pattern
78
+ end
79
+
80
+ return unless ignored
81
+ log :warn, "Ignore stamping file: #{file}"
82
+ @ignored += 1
83
+ end
84
+
85
+ def dry_run?
86
+ RSpecStamp.config.dry_run?
87
+ end
88
+ end
89
+
47
90
  class << self
48
91
  include TestProf::Logging
49
92
 
@@ -84,7 +127,7 @@ module TestProf
84
127
  parsed = Parser.parse(code)
85
128
  return false unless parsed
86
129
 
87
- parsed.desc ||= 'works'
130
+ desc = parsed.desc_const || quote(parsed.desc || 'works')
88
131
 
89
132
  tags.each do |t|
90
133
  if t.is_a?(Hash)
@@ -112,7 +155,7 @@ module TestProf
112
155
  end
113
156
 
114
157
  replacement = "\\1#{parsed.fname}#{need_parens ? '(' : ' '}"\
115
- "#{[quote(parsed.desc), tags_str, htags_str].compact.join(', ')}"\
158
+ "#{[desc, tags_str, htags_str].compact.join(', ')}"\
116
159
  "#{need_parens ? ') ' : ' '}\\3"
117
160
 
118
161
  if config.dry_run?
@@ -2,15 +2,13 @@
2
2
 
3
3
  require "ripper"
4
4
 
5
- # rubocop: disable Metrics/CyclomaticComplexity
6
-
7
5
  module TestProf
8
6
  module RSpecStamp
9
7
  # Parse examples headers
10
8
  module Parser
11
9
  # Contains the result of parsing
12
10
  class Result
13
- attr_accessor :fname, :desc
11
+ attr_accessor :fname, :desc, :desc_const
14
12
  attr_reader :tags, :htags
15
13
 
16
14
  def add_tag(v)
@@ -24,12 +22,14 @@ module TestProf
24
22
  end
25
23
 
26
24
  def remove_tag(tag)
27
- @tags&.delete(tag)
28
- @htags&.delete_if { |(k, _v)| k == tag }
25
+ @tags.delete(tag) if @tags
26
+ @htags.delete_if { |(k, _v)| k == tag } if @htags
29
27
  end
30
28
  end
31
29
 
32
30
  class << self
31
+ # rubocop: disable Metrics/CyclomaticComplexity
32
+ # rubocop: disable Metrics/PerceivedComplexity
33
33
  def parse(code)
34
34
  sexp = Ripper.sexp(code)
35
35
  return unless sexp
@@ -58,11 +58,17 @@ module TestProf
58
58
  res = Result.new
59
59
 
60
60
  fcall = sexp[1][0][1]
61
- fcall = fcall[1] if fcall.first == :fcall
62
- res.fname = fcall[1]
63
-
64
61
  args_block = sexp[1][0][2]
65
62
 
63
+ if fcall.first == :fcall
64
+ fcall = fcall[1]
65
+ elsif fcall.first == :var_ref
66
+ res.fname = [parse_const(fcall), sexp[1][0][3][1]].join(".")
67
+ args_block = sexp[1][0][4]
68
+ end
69
+
70
+ res.fname ||= fcall[1]
71
+
66
72
  return res if args_block.nil?
67
73
 
68
74
  args_block = args_block[1] if args_block.first == :arg_paren
@@ -71,12 +77,16 @@ module TestProf
71
77
 
72
78
  if args.first.first == :string_literal
73
79
  res.desc = parse_literal(args.shift)
80
+ elsif args.first.first == :var_ref || args.first.first == :const_path_ref
81
+ res.desc_const = parse_const(args.shift)
74
82
  end
75
83
 
76
84
  parse_arg(res, args.shift) until args.empty?
77
85
 
78
86
  res
79
87
  end
88
+ # rubocop: enable Metrics/CyclomaticComplexity
89
+ # rubocop: enable Metrics/PerceivedComplexity
80
90
 
81
91
  private
82
92
 
@@ -102,6 +112,25 @@ module TestProf
102
112
  expr[0] == :assoc_new
103
113
  val
104
114
  end
115
+
116
+ # Expr of the form:
117
+ # [:var_ref, [:@const, "User", [1, 9]]]
118
+ #
119
+ # or
120
+ #
121
+ # [:const_path_ref, [:const_path_ref, [:var_ref,
122
+ # [:@const, "User", [1, 17]]],
123
+ # [:@const, "Guest", [1, 23]]],
124
+ # [:@const, "Collection", [1, 30]]
125
+ def parse_const(expr)
126
+ if expr.first == :var_ref
127
+ expr[1][1]
128
+ elsif expr.first == :@const
129
+ expr[1]
130
+ elsif expr.first == :const_path_ref
131
+ expr[1..-1].map(&method(:parse_const)).join("::")
132
+ end
133
+ end
105
134
  end
106
135
  end
107
136
  end
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "test_prof/ext/string_strip_heredoc"
4
+
3
5
  module TestProf
4
6
  module RSpecStamp
5
7
  class RSpecListener # :nodoc:
6
8
  include Logging
9
+ using StringStripHeredoc
7
10
 
8
11
  NOTIFICATIONS = %i[
9
12
  example_failed
@@ -27,54 +30,27 @@ module TestProf
27
30
  end
28
31
 
29
32
  def stamp!
33
+ stamper = Stamper.new
34
+
30
35
  @examples.each do |file, lines|
31
- stamp_file(file, lines.uniq)
36
+ stamper.stamp_file(file, lines.uniq)
32
37
  end
33
38
 
34
39
  msgs = []
35
40
 
36
41
  msgs <<
37
- <<~MSG
42
+ <<-MSG.strip_heredoc
38
43
  RSpec Stamp results
39
44
 
40
- Total patches: #{@total}
45
+ Total patches: #{stamper.total}
41
46
  Total files: #{@examples.keys.size}
42
47
 
43
- Failed patches: #{@failed}
44
- Ignored files: #{@ignored}
48
+ Failed patches: #{stamper.failed}
49
+ Ignored files: #{stamper.ignored}
45
50
  MSG
46
51
 
47
52
  log :info, msgs.join
48
53
  end
49
-
50
- private
51
-
52
- def stamp_file(file, lines)
53
- @total += lines.size
54
- return if ignored?(file)
55
-
56
- log :info, "(dry-run) Patching #{file}" if dry_run?
57
-
58
- code = File.readlines(file)
59
-
60
- @failed += RSpecStamp.apply_tags(code, lines, RSpecStamp.config.tags)
61
-
62
- File.write(file, code.join) unless dry_run?
63
- end
64
-
65
- def ignored?(file)
66
- ignored = RSpecStamp.config.ignore_files.find do |pattern|
67
- file =~ pattern
68
- end
69
-
70
- return unless ignored
71
- log :warn, "Ignore stamping file: #{file}"
72
- @ignored += 1
73
- end
74
-
75
- def dry_run?
76
- RSpecStamp.config.dry_run?
77
- end
78
54
  end
79
55
  end
80
56
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "test_prof/ext/string_strip_heredoc"
4
+
3
5
  module TestProf
4
6
  # RubyProf wrapper.
5
7
  #
@@ -119,6 +121,7 @@ module TestProf
119
121
 
120
122
  class << self
121
123
  include Logging
124
+ using StringStripHeredoc
122
125
 
123
126
  def config
124
127
  @config ||= Configuration.new
@@ -172,7 +175,7 @@ module TestProf
172
175
  ENV["RUBY_PROF_MEASURE_MODE"] = config.mode.to_s
173
176
  @initialized = TestProf.require(
174
177
  'ruby-prof',
175
- <<~MSG
178
+ <<-MSG.strip_heredoc
176
179
  Please, install 'ruby-prof' first:
177
180
  # Gemfile
178
181
  gem 'ruby-prof', '>= 0.16.0', require: false
@@ -184,7 +187,7 @@ module TestProf
184
187
  if Utils.verify_gem_version('ruby-prof', at_least: '0.16.0')
185
188
  true
186
189
  else
187
- log :error, <<~MGS
190
+ log :error, <<-MGS.strip_heredoc
188
191
  Please, upgrade 'ruby-prof' to version >= 0.16.0.
189
192
  MGS
190
193
  false
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "test_prof/ext/string_strip_heredoc"
4
+
3
5
  module TestProf
4
6
  # StackProf wrapper.
5
7
  #
@@ -32,6 +34,7 @@ module TestProf
32
34
 
33
35
  class << self
34
36
  include Logging
37
+ using StringStripHeredoc
35
38
 
36
39
  def config
37
40
  @config ||= Configuration.new
@@ -88,7 +91,7 @@ module TestProf
88
91
 
89
92
  html_path = path.gsub(/\.dump$/, '.html')
90
93
 
91
- log :info, <<~MSG
94
+ log :info, <<-MSG.strip_heredoc
92
95
  Run the following command to generate a flame graph report:
93
96
 
94
97
  stackprof --flamegraph #{path} > #{html_path} && stackprof --flamegraph-viewer=#{html_path}
@@ -111,7 +114,7 @@ module TestProf
111
114
  return @initialized if instance_variable_defined?(:@initialized)
112
115
  @initialized = TestProf.require(
113
116
  'stackprof',
114
- <<~MSG
117
+ <<-MSG.strip_heredoc
115
118
  Please, install 'stackprof' first:
116
119
  # Gemfile
117
120
  gem 'stackprof', '>= 0.2.9', require: false
@@ -123,9 +126,9 @@ module TestProf
123
126
  if Utils.verify_gem_version('stackprof', at_least: '0.2.9')
124
127
  true
125
128
  else
126
- log :error, <<~MGS
129
+ log :error, <<-MSG.strip_heredoc
127
130
  Please, upgrade 'stackprof' to version >= 0.2.9.
128
- MGS
131
+ MSG
129
132
  false
130
133
  end
131
134
  end
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "test_prof/ext/float_duration"
4
+ require "test_prof/ext/string_strip_heredoc"
4
5
 
5
6
  module TestProf
6
7
  module TagProf
7
8
  class RSpecListener # :nodoc:
8
9
  include Logging
9
10
  using FloatDuration
11
+ using StringStripHeredoc
10
12
 
11
13
  NOTIFICATIONS = %i[
12
14
  example_started
@@ -37,7 +39,7 @@ module TestProf
37
39
  msgs = []
38
40
 
39
41
  msgs <<
40
- <<~MSG
42
+ <<-MSG.strip_heredoc
41
43
  TagProf report for #{@tag}
42
44
  MSG
43
45
 
@@ -4,6 +4,11 @@ module TestProf
4
4
  module Utils
5
5
  # Ordered set with capacity
6
6
  class SizedOrderedSet
7
+ unless [].respond_to?(:bsearch_index)
8
+ require "test_prof/ext/array_bsearch_index"
9
+ using ArrayBSearchIndex
10
+ end
11
+
7
12
  include Enumerable
8
13
 
9
14
  def initialize(max_size, sort_by: nil)
@@ -49,7 +54,7 @@ module TestProf
49
54
  end
50
55
 
51
56
  def to_a
52
- data
57
+ data.dup
53
58
  end
54
59
 
55
60
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TestProf
4
- VERSION = "0.3.0.beta"
4
+ VERSION = "0.3.0.beta2".freeze
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: 0.3.0.beta
4
+ version: 0.3.0.beta2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-29 00:00:00.000000000 Z
11
+ date: 2017-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,20 +80,6 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0.49'
83
- - !ruby/object:Gem::Dependency
84
- name: pry-byebug
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
83
  description: "\n Ruby applications tests profiling tools.\n\n Contains tools
98
84
  to anylyze factories usage, integrate with Ruby profilers,\n profile your examples
99
85
  using ActiveSupport notifications (if any) and\n statically analyze your code
@@ -142,7 +128,9 @@ files:
142
128
  - lib/test_prof/event_prof/instrumentations/active_support.rb
143
129
  - lib/test_prof/event_prof/minitest.rb
144
130
  - lib/test_prof/event_prof/rspec.rb
131
+ - lib/test_prof/ext/array_bsearch_index.rb
145
132
  - lib/test_prof/ext/float_duration.rb
133
+ - lib/test_prof/ext/string_strip_heredoc.rb
146
134
  - lib/test_prof/ext/string_truncate.rb
147
135
  - lib/test_prof/factory_default.rb
148
136
  - lib/test_prof/factory_default/factory_girl_patch.rb
@@ -188,7 +176,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
188
176
  requirements:
189
177
  - - ">="
190
178
  - !ruby/object:Gem::Version
191
- version: 2.3.0
179
+ version: 2.2.0
192
180
  required_rubygems_version: !ruby/object:Gem::Requirement
193
181
  requirements:
194
182
  - - ">"
@@ -196,7 +184,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
196
184
  version: 1.3.1
197
185
  requirements: []
198
186
  rubyforge_project:
199
- rubygems_version: 2.6.11
187
+ rubygems_version: 2.6.13
200
188
  signing_key:
201
189
  specification_version: 4
202
190
  summary: Ruby applications tests profiling tools