test-prof 0.3.0.beta → 0.3.0.beta2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +6 -0
- data/guides/event_prof.md +10 -0
- data/guides/rspec_dissect.md +9 -0
- data/lib/test_prof/cops/rspec/aggregate_failures.rb +2 -2
- data/lib/test_prof/event_prof.rb +8 -0
- data/lib/test_prof/event_prof/custom_events/factory_create.rb +5 -1
- data/lib/test_prof/event_prof/custom_events/sidekiq_inline.rb +5 -1
- data/lib/test_prof/event_prof/custom_events/sidekiq_jobs.rb +5 -1
- data/lib/test_prof/event_prof/rspec.rb +40 -3
- data/lib/test_prof/ext/array_bsearch_index.rb +15 -0
- data/lib/test_prof/ext/string_strip_heredoc.rb +15 -0
- data/lib/test_prof/factory_doctor.rb +2 -2
- data/lib/test_prof/factory_doctor/rspec.rb +4 -2
- data/lib/test_prof/factory_prof/printers/simple.rb +4 -1
- data/lib/test_prof/recipes/rspec/any_fixture.rb +1 -1
- data/lib/test_prof/recipes/rspec/let_it_be.rb +8 -2
- data/lib/test_prof/rspec_dissect.rb +8 -0
- data/lib/test_prof/rspec_dissect/rspec.rb +40 -5
- data/lib/test_prof/rspec_stamp.rb +45 -2
- data/lib/test_prof/rspec_stamp/parser.rb +37 -8
- data/lib/test_prof/rspec_stamp/rspec.rb +10 -34
- data/lib/test_prof/ruby_prof.rb +5 -2
- data/lib/test_prof/stack_prof.rb +7 -4
- data/lib/test_prof/tag_prof/rspec.rb +3 -1
- data/lib/test_prof/utils/sized_ordered_set.rb +6 -1
- data/lib/test_prof/version.rb +1 -1
- metadata +6 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6af9c62cf692b558711fe96e40cff6e82fb49b99
|
4
|
+
data.tar.gz: e308236c1ed5c5c9abc81e6a7a672adfcbc49dbf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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:
|
data/guides/rspec_dissect.md
CHANGED
@@ -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
|
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
|
112
|
+
node && node.block_type? &&
|
113
113
|
(node.source.lines.size == 1) &&
|
114
114
|
example_node?(node)
|
115
115
|
end
|
data/lib/test_prof/event_prof.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
"#{[
|
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
|
28
|
-
@htags
|
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
|
-
|
42
|
+
<<-MSG.strip_heredoc
|
38
43
|
RSpec Stamp results
|
39
44
|
|
40
|
-
Total patches: #{
|
45
|
+
Total patches: #{stamper.total}
|
41
46
|
Total files: #{@examples.keys.size}
|
42
47
|
|
43
|
-
Failed patches: #{
|
44
|
-
Ignored files: #{
|
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
|
data/lib/test_prof/ruby_prof.rb
CHANGED
@@ -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
|
-
|
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,
|
190
|
+
log :error, <<-MGS.strip_heredoc
|
188
191
|
Please, upgrade 'ruby-prof' to version >= 0.16.0.
|
189
192
|
MGS
|
190
193
|
false
|
data/lib/test_prof/stack_prof.rb
CHANGED
@@ -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,
|
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
|
-
|
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,
|
129
|
+
log :error, <<-MSG.strip_heredoc
|
127
130
|
Please, upgrade 'stackprof' to version >= 0.2.9.
|
128
|
-
|
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
|
-
|
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
|
data/lib/test_prof/version.rb
CHANGED
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.
|
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-
|
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.
|
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.
|
187
|
+
rubygems_version: 2.6.13
|
200
188
|
signing_key:
|
201
189
|
specification_version: 4
|
202
190
|
summary: Ruby applications tests profiling tools
|