test-prof 1.2.2 → 1.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -2
- data/README.md +1 -1
- data/lib/minitest/test_prof_plugin.rb +10 -0
- data/lib/test_prof/any_fixture/dump/sqlite.rb +1 -1
- data/lib/test_prof/any_fixture.rb +1 -1
- data/lib/test_prof/before_all/adapters/active_record.rb +3 -1
- data/lib/test_prof/before_all.rb +1 -1
- data/lib/test_prof/core.rb +167 -0
- data/lib/test_prof/factory_all_stub.rb +1 -1
- data/lib/test_prof/factory_default.rb +1 -1
- data/lib/test_prof/memory_prof/minitest.rb +56 -0
- data/lib/test_prof/memory_prof/printer/number_to_human.rb +44 -0
- data/lib/test_prof/memory_prof/printer.rb +93 -0
- data/lib/test_prof/memory_prof/rspec.rb +76 -0
- data/lib/test_prof/memory_prof/tracker/linked_list.rb +119 -0
- data/lib/test_prof/memory_prof/tracker/rss_tool.rb +87 -0
- data/lib/test_prof/memory_prof/tracker.rb +84 -0
- data/lib/test_prof/memory_prof.rb +78 -0
- data/lib/test_prof/recipes/logging.rb +1 -1
- data/lib/test_prof/recipes/minitest/sample.rb +2 -2
- data/lib/test_prof/recipes/rspec/let_it_be.rb +1 -1
- data/lib/test_prof/ruby_prof.rb +19 -3
- data/lib/test_prof/stack_prof/rspec.rb +2 -1
- data/lib/test_prof/stack_prof.rb +3 -3
- data/lib/test_prof/utils/sized_ordered_set.rb +4 -0
- data/lib/test_prof/vernier/rspec.rb +59 -0
- data/lib/test_prof/vernier.rb +152 -0
- data/lib/test_prof/version.rb +1 -1
- data/lib/test_prof.rb +3 -165
- metadata +14 -3
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TestProf
|
4
|
+
# Vernier wrapper.
|
5
|
+
#
|
6
|
+
# Has 2 modes: global and per-example.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# # To activate global profiling you can use env variable
|
11
|
+
# TEST_VERNIER=1 rspec ...
|
12
|
+
#
|
13
|
+
# To profile a specific examples add :vernier tag to it:
|
14
|
+
#
|
15
|
+
# it "is doing heavy stuff", :vernier do
|
16
|
+
# ...
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
module Vernier
|
20
|
+
# Vernier configuration
|
21
|
+
class Configuration
|
22
|
+
attr_accessor :mode, :target, :interval
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@mode = ENV.fetch("TEST_VERNIER_MODE", :wall).to_sym
|
26
|
+
@target = (ENV["TEST_VERNIER"] == "boot") ? :boot : :suite
|
27
|
+
|
28
|
+
sample_interval = ENV["TEST_VERNIER_INTERVAL"].to_i
|
29
|
+
@interval = (sample_interval > 0) ? sample_interval : nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def boot?
|
33
|
+
target == :boot
|
34
|
+
end
|
35
|
+
|
36
|
+
def suite?
|
37
|
+
target == :suite
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class << self
|
42
|
+
include Logging
|
43
|
+
|
44
|
+
def config
|
45
|
+
@config ||= Configuration.new
|
46
|
+
end
|
47
|
+
|
48
|
+
def configure
|
49
|
+
yield config
|
50
|
+
end
|
51
|
+
|
52
|
+
attr_reader :default_collector
|
53
|
+
|
54
|
+
# Run Vernier and automatically dump
|
55
|
+
# a report when the process exits or when the application is booted.
|
56
|
+
def run
|
57
|
+
collector = profile
|
58
|
+
return unless collector
|
59
|
+
|
60
|
+
@locked = true
|
61
|
+
@default_collector = collector
|
62
|
+
|
63
|
+
log :info, "Vernier enabled globally: " \
|
64
|
+
"mode – #{config.mode}, target – #{config.target}"
|
65
|
+
|
66
|
+
at_exit { dump(collector, "total") } if config.suite?
|
67
|
+
end
|
68
|
+
|
69
|
+
def profile(name = nil)
|
70
|
+
if locked?
|
71
|
+
log :warn, <<~MSG
|
72
|
+
Vernier has been already activated.
|
73
|
+
|
74
|
+
Make sure you do not have the TEST_VERNIER environmental variable set somewhere.
|
75
|
+
MSG
|
76
|
+
|
77
|
+
return false
|
78
|
+
end
|
79
|
+
|
80
|
+
return false unless init_vernier
|
81
|
+
|
82
|
+
options = {}
|
83
|
+
|
84
|
+
options[:interval] = config.interval if config.interval
|
85
|
+
|
86
|
+
if block_given?
|
87
|
+
options[:mode] = config.mode
|
88
|
+
options[:out] = build_path(name)
|
89
|
+
::Vernier.trace(**options) { yield }
|
90
|
+
else
|
91
|
+
collector = ::Vernier::Collector.new(config.mode, **options)
|
92
|
+
collector.start
|
93
|
+
|
94
|
+
collector
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def dump(collector, name)
|
99
|
+
result = collector.stop
|
100
|
+
|
101
|
+
path = build_path(name)
|
102
|
+
|
103
|
+
File.write(path, ::Vernier::Output::Firefox.new(result).output)
|
104
|
+
|
105
|
+
log :info, "Vernier report generated: #{path}"
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def build_path(name)
|
111
|
+
TestProf.artifact_path(
|
112
|
+
"vernier-report-#{config.mode}-#{name}.json"
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
def locked?
|
117
|
+
@locked == true
|
118
|
+
end
|
119
|
+
|
120
|
+
def init_vernier
|
121
|
+
return @initialized if instance_variable_defined?(:@initialized)
|
122
|
+
@locked = false
|
123
|
+
@initialized = TestProf.require(
|
124
|
+
"vernier",
|
125
|
+
<<~MSG
|
126
|
+
Please, install 'vernier' first:
|
127
|
+
# Gemfile
|
128
|
+
gem 'vernier', '>= 0.3.0', require: false
|
129
|
+
MSG
|
130
|
+
) { check_vernier_version }
|
131
|
+
end
|
132
|
+
|
133
|
+
def check_vernier_version
|
134
|
+
if Utils.verify_gem_version("vernier", at_least: "0.3.0")
|
135
|
+
true
|
136
|
+
else
|
137
|
+
log :error, <<~MSG
|
138
|
+
Please, upgrade 'vernier' to version >= 0.3.0.
|
139
|
+
MSG
|
140
|
+
false
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
require "test_prof/vernier/rspec" if TestProf.rspec?
|
148
|
+
|
149
|
+
# Hook to run Vernier globally
|
150
|
+
TestProf.activate("TEST_VERNIER") do
|
151
|
+
TestProf::Vernier.run
|
152
|
+
end
|
data/lib/test_prof/version.rb
CHANGED
data/lib/test_prof.rb
CHANGED
@@ -1,177 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "fileutils"
|
4
|
-
require "logger"
|
5
|
-
|
6
3
|
require "test_prof/version"
|
7
|
-
require "test_prof/
|
8
|
-
require "test_prof/utils"
|
9
|
-
|
10
|
-
# Ruby applications tests profiling tools.
|
11
|
-
#
|
12
|
-
# Contains tools to analyze factories usage, integrate with Ruby profilers,
|
13
|
-
# profile your examples using ActiveSupport notifications (if any) and
|
14
|
-
# statically analyze your code with custom RuboCop cops.
|
15
|
-
#
|
16
|
-
# Example usage:
|
17
|
-
#
|
18
|
-
# require 'test_prof'
|
19
|
-
#
|
20
|
-
# # Activate a tool by providing environment variable, e.g.
|
21
|
-
# TEST_RUBY_PROF=1 rspec ...
|
22
|
-
#
|
23
|
-
# # or manually in your code
|
24
|
-
# TestProf::RubyProf.run
|
25
|
-
#
|
26
|
-
# See other modules for more examples.
|
27
|
-
module TestProf
|
28
|
-
class << self
|
29
|
-
include Logging
|
30
|
-
|
31
|
-
def config
|
32
|
-
@config ||= Configuration.new
|
33
|
-
end
|
34
|
-
|
35
|
-
def configure
|
36
|
-
yield config
|
37
|
-
end
|
38
|
-
|
39
|
-
# Returns true if we're inside RSpec
|
40
|
-
def rspec?
|
41
|
-
defined?(RSpec::Core)
|
42
|
-
end
|
43
|
-
|
44
|
-
# Returns true if we're inside Minitest
|
45
|
-
def minitest?
|
46
|
-
defined?(Minitest)
|
47
|
-
end
|
48
|
-
|
49
|
-
# Returns true if Spring is used and not disabled
|
50
|
-
def spring?
|
51
|
-
# See https://github.com/rails/spring/blob/577cf01f232bb6dbd0ade7df2df2ac209697e741/lib/spring/binstub.rb
|
52
|
-
disabled = ENV["DISABLE_SPRING"]
|
53
|
-
defined?(::Spring::Application) && (disabled.nil? || disabled.empty? || disabled == "0")
|
54
|
-
end
|
55
|
-
|
56
|
-
# Returns the current process time
|
57
|
-
def now
|
58
|
-
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
59
|
-
end
|
60
|
-
|
61
|
-
# Require gem and shows a custom
|
62
|
-
# message if it fails to load
|
63
|
-
def require(gem_name, msg = nil)
|
64
|
-
Kernel.require gem_name
|
65
|
-
block_given? ? yield : true
|
66
|
-
rescue LoadError
|
67
|
-
log(:error, msg) if msg
|
68
|
-
false
|
69
|
-
end
|
70
|
-
|
71
|
-
# Run block only if provided env var is present and
|
72
|
-
# equal to the provided value (if any).
|
73
|
-
# Contains workaround for applications using Spring.
|
74
|
-
def activate(env_var, val = nil)
|
75
|
-
if spring?
|
76
|
-
notify_spring_detected
|
77
|
-
::Spring.after_fork do
|
78
|
-
activate!(env_var, val) do
|
79
|
-
notify_spring_activate env_var
|
80
|
-
yield
|
81
|
-
end
|
82
|
-
end
|
83
|
-
else
|
84
|
-
activate!(env_var, val) { yield }
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# Return absolute path to asset
|
89
|
-
def asset_path(filename)
|
90
|
-
::File.expand_path(filename, ::File.join(::File.dirname(__FILE__), "..", "assets"))
|
91
|
-
end
|
92
|
-
|
93
|
-
# Return a path to store artifact
|
94
|
-
def artifact_path(filename)
|
95
|
-
create_artifact_dir
|
96
|
-
|
97
|
-
with_timestamps(
|
98
|
-
::File.join(
|
99
|
-
config.output_dir,
|
100
|
-
with_report_suffix(
|
101
|
-
filename
|
102
|
-
)
|
103
|
-
)
|
104
|
-
)
|
105
|
-
end
|
106
|
-
|
107
|
-
def create_artifact_dir
|
108
|
-
FileUtils.mkdir_p(config.output_dir)[0]
|
109
|
-
end
|
110
|
-
|
111
|
-
private
|
112
|
-
|
113
|
-
def activate!(env_var, val)
|
114
|
-
yield if ENV[env_var] && (val.nil? || val === ENV[env_var])
|
115
|
-
end
|
116
|
-
|
117
|
-
def with_timestamps(path)
|
118
|
-
return path unless config.timestamps?
|
119
|
-
timestamps = "-#{now.to_i}"
|
120
|
-
"#{path.sub(/\.\w+$/, "")}#{timestamps}#{::File.extname(path)}"
|
121
|
-
end
|
122
|
-
|
123
|
-
def with_report_suffix(path)
|
124
|
-
return path if config.report_suffix.nil?
|
125
|
-
|
126
|
-
"#{path.sub(/\.\w+$/, "")}-#{config.report_suffix}#{::File.extname(path)}"
|
127
|
-
end
|
128
|
-
|
129
|
-
def notify_spring_detected
|
130
|
-
return if instance_variable_defined?(:@spring_notified)
|
131
|
-
log :info, "Spring detected"
|
132
|
-
@spring_notified = true
|
133
|
-
end
|
134
|
-
|
135
|
-
def notify_spring_activate(env_var)
|
136
|
-
log :info, "Activating #{env_var} with `Spring.after_fork`"
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
# TestProf configuration
|
141
|
-
class Configuration
|
142
|
-
attr_accessor :output, # IO to write logs
|
143
|
-
:color, # Whether to colorize output or not
|
144
|
-
:output_dir, # Directory to store artifacts
|
145
|
-
:timestamps, # Whether to use timestamped names for artifacts,
|
146
|
-
:report_suffix # Custom suffix for reports/artifacts
|
147
|
-
|
148
|
-
def initialize
|
149
|
-
@output = $stdout
|
150
|
-
@color = true
|
151
|
-
@output_dir = "tmp/test_prof"
|
152
|
-
@timestamps = false
|
153
|
-
@report_suffix = ENV["TEST_PROF_REPORT"]
|
154
|
-
end
|
155
|
-
|
156
|
-
def color?
|
157
|
-
color == true && output.is_a?(IO) && output.tty?
|
158
|
-
end
|
159
|
-
|
160
|
-
def timestamps?
|
161
|
-
timestamps == true
|
162
|
-
end
|
163
|
-
|
164
|
-
def logger
|
165
|
-
@logger ||= Logger.new(output, formatter: Logging::Formatter.new)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
4
|
+
require "test_prof/core"
|
169
5
|
|
170
6
|
require "test_prof/ruby_prof"
|
171
7
|
require "test_prof/stack_prof"
|
8
|
+
require "test_prof/vernier"
|
172
9
|
require "test_prof/event_prof"
|
173
10
|
require "test_prof/factory_doctor"
|
174
11
|
require "test_prof/factory_prof"
|
12
|
+
require "test_prof/memory_prof"
|
175
13
|
require "test_prof/rspec_stamp"
|
176
14
|
require "test_prof/tag_prof"
|
177
15
|
require "test_prof/rspec_dissect" if TestProf.rspec?
|
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.
|
4
|
+
version: 1.3.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: 2023-
|
11
|
+
date: 2023-11-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -143,6 +143,7 @@ files:
|
|
143
143
|
- lib/test_prof/cops/rspec/aggregate_examples/metadata_helpers.rb
|
144
144
|
- lib/test_prof/cops/rspec/aggregate_examples/node_matchers.rb
|
145
145
|
- lib/test_prof/cops/rspec/language.rb
|
146
|
+
- lib/test_prof/core.rb
|
146
147
|
- lib/test_prof/event_prof.rb
|
147
148
|
- lib/test_prof/event_prof/custom_events.rb
|
148
149
|
- lib/test_prof/event_prof/custom_events/factory_create.rb
|
@@ -179,6 +180,14 @@ files:
|
|
179
180
|
- lib/test_prof/factory_prof/printers/nate_heckler.rb
|
180
181
|
- lib/test_prof/factory_prof/printers/simple.rb
|
181
182
|
- lib/test_prof/logging.rb
|
183
|
+
- lib/test_prof/memory_prof.rb
|
184
|
+
- lib/test_prof/memory_prof/minitest.rb
|
185
|
+
- lib/test_prof/memory_prof/printer.rb
|
186
|
+
- lib/test_prof/memory_prof/printer/number_to_human.rb
|
187
|
+
- lib/test_prof/memory_prof/rspec.rb
|
188
|
+
- lib/test_prof/memory_prof/tracker.rb
|
189
|
+
- lib/test_prof/memory_prof/tracker/linked_list.rb
|
190
|
+
- lib/test_prof/memory_prof/tracker/rss_tool.rb
|
182
191
|
- lib/test_prof/recipes/logging.rb
|
183
192
|
- lib/test_prof/recipes/minitest/before_all.rb
|
184
193
|
- lib/test_prof/recipes/minitest/sample.rb
|
@@ -211,6 +220,8 @@ files:
|
|
211
220
|
- lib/test_prof/utils/html_builder.rb
|
212
221
|
- lib/test_prof/utils/rspec.rb
|
213
222
|
- lib/test_prof/utils/sized_ordered_set.rb
|
223
|
+
- lib/test_prof/vernier.rb
|
224
|
+
- lib/test_prof/vernier/rspec.rb
|
214
225
|
- lib/test_prof/version.rb
|
215
226
|
homepage: http://github.com/test-prof/test-prof
|
216
227
|
licenses:
|
@@ -237,7 +248,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
237
248
|
- !ruby/object:Gem::Version
|
238
249
|
version: '0'
|
239
250
|
requirements: []
|
240
|
-
rubygems_version: 3.4.
|
251
|
+
rubygems_version: 3.4.20
|
241
252
|
signing_key:
|
242
253
|
specification_version: 4
|
243
254
|
summary: Ruby applications tests profiling tools
|