rubocop-rspec-guide 0.2.2 → 0.4.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/.rubocop.yml +6 -6
- data/.yardopts +9 -0
- data/CHANGELOG.md +86 -0
- data/CONTRIBUTING.md +358 -0
- data/INTEGRATION_TESTING.md +324 -0
- data/README.md +443 -16
- data/Rakefile +49 -0
- data/benchmark/README.md +349 -0
- data/benchmark/baseline_v0.3.1.txt +67 -0
- data/benchmark/baseline_v0.4.0.txt +167 -0
- data/benchmark/benchmark_helper.rb +92 -0
- data/benchmark/compare_versions.rb +136 -0
- data/benchmark/cops_benchmark.rb +428 -0
- data/benchmark/cops_performance.rb +109 -0
- data/benchmark/quick_comparison.rb +58 -0
- data/benchmark/quick_invariant_bench.rb +52 -0
- data/benchmark/rspec_base_integration.rb +86 -0
- data/benchmark/save_baseline.rb +18 -0
- data/benchmark/scalability_benchmark.rb +181 -0
- data/config/default.yml +43 -2
- data/config/obsoletion.yml +6 -0
- data/lib/rubocop/cop/factory_bot_guide/dynamic_attribute_evaluation.rb +193 -0
- data/lib/rubocop/cop/factory_bot_guide/dynamic_attributes_for_time_and_random.rb +10 -106
- data/lib/rubocop/cop/rspec_guide/characteristics_and_contexts.rb +13 -78
- data/lib/rubocop/cop/rspec_guide/context_setup.rb +81 -30
- data/lib/rubocop/cop/rspec_guide/duplicate_before_hooks.rb +89 -22
- data/lib/rubocop/cop/rspec_guide/duplicate_let_values.rb +89 -22
- data/lib/rubocop/cop/rspec_guide/happy_path_first.rb +52 -21
- data/lib/rubocop/cop/rspec_guide/invariant_examples.rb +60 -19
- data/lib/rubocop/cop/rspec_guide/minimum_behavioral_coverage.rb +165 -0
- data/lib/rubocop/rspec/guide/inject.rb +26 -0
- data/lib/rubocop/rspec/guide/plugin.rb +45 -0
- data/lib/rubocop/rspec/guide/version.rb +1 -1
- data/lib/rubocop-rspec-guide.rb +4 -0
- metadata +49 -1
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Benchmark to compare performance before and after RSpec::Base integration
|
|
5
|
+
# This shows the impact of using rubocop-rspec Language API
|
|
6
|
+
|
|
7
|
+
require "benchmark"
|
|
8
|
+
require "rubocop"
|
|
9
|
+
|
|
10
|
+
SAMPLE_CODE = <<~RUBY
|
|
11
|
+
describe 'UserService' do
|
|
12
|
+
context 'when user is admin' do
|
|
13
|
+
let(:user) { create(:user, :admin) }
|
|
14
|
+
let(:service) { UserService.new(user) }
|
|
15
|
+
|
|
16
|
+
before { setup_admin_permissions }
|
|
17
|
+
|
|
18
|
+
it 'has admin access' do
|
|
19
|
+
expect(service.admin?).to be true
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
context 'when user is regular' do
|
|
24
|
+
let(:user) { create(:user) }
|
|
25
|
+
let(:service) { UserService.new(user) }
|
|
26
|
+
|
|
27
|
+
before { setup_admin_permissions }
|
|
28
|
+
|
|
29
|
+
it 'does not have admin access' do
|
|
30
|
+
expect(service.admin?).to be false
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
RUBY
|
|
35
|
+
|
|
36
|
+
puts "=" * 80
|
|
37
|
+
puts "Performance Comparison: Before vs After RSpec::Base Integration"
|
|
38
|
+
puts "=" * 80
|
|
39
|
+
puts
|
|
40
|
+
|
|
41
|
+
source = RuboCop::ProcessedSource.new(SAMPLE_CODE, RUBY_VERSION.to_f)
|
|
42
|
+
|
|
43
|
+
config = RuboCop::Config.new(
|
|
44
|
+
{
|
|
45
|
+
"RSpec" => {
|
|
46
|
+
"Enabled" => true,
|
|
47
|
+
"Language" => {
|
|
48
|
+
"ExampleGroups" => {
|
|
49
|
+
"Regular" => %w[describe context feature example_group],
|
|
50
|
+
"Skipped" => %w[xdescribe xcontext xfeature],
|
|
51
|
+
"Focused" => %w[fdescribe fcontext ffeature]
|
|
52
|
+
},
|
|
53
|
+
"Examples" => {
|
|
54
|
+
"Regular" => %w[it specify example scenario its],
|
|
55
|
+
"Focused" => %w[fit fspecify fexample fscenario focus],
|
|
56
|
+
"Skipped" => %w[xit xspecify xexample xscenario skip],
|
|
57
|
+
"Pending" => %w[pending]
|
|
58
|
+
},
|
|
59
|
+
"Helpers" => %w[let let! let_it_be let_it_be!],
|
|
60
|
+
"Hooks" => %w[prepend_before before append_before around prepend_after after append_after],
|
|
61
|
+
"Subjects" => %w[subject subject!]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Test individual matcher performance
|
|
68
|
+
iterations = 10_000
|
|
69
|
+
|
|
70
|
+
puts "Testing node matcher performance (#{iterations} iterations):"
|
|
71
|
+
puts "-" * 80
|
|
72
|
+
|
|
73
|
+
require_relative "../lib/rubocop-rspec-guide"
|
|
74
|
+
|
|
75
|
+
# Initialize RSpec Language config
|
|
76
|
+
RuboCop::RSpec::Language.config = config["RSpec"]["Language"]
|
|
77
|
+
|
|
78
|
+
Benchmark.bm(45) do |x|
|
|
79
|
+
# Test let? matcher
|
|
80
|
+
cop = RuboCop::Cop::RSpecGuide::DuplicateLetValues.new(config)
|
|
81
|
+
let_nodes = []
|
|
82
|
+
|
|
83
|
+
source.ast.each_node(:block) do |node|
|
|
84
|
+
let_nodes << node if cop.let?(node)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
x.report("let? matcher (finds #{let_nodes.size} nodes)") do
|
|
88
|
+
iterations.times do
|
|
89
|
+
source.ast.each_node(:block) do |node|
|
|
90
|
+
cop.let?(node)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Test example_group? matcher
|
|
96
|
+
cop2 = RuboCop::Cop::RSpecGuide::MinimumBehavioralCoverage.new(config)
|
|
97
|
+
group_nodes = []
|
|
98
|
+
|
|
99
|
+
source.ast.each_node(:block) do |node|
|
|
100
|
+
group_nodes << node if cop2.example_group?(node)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
x.report("example_group? matcher (finds #{group_nodes.size} nodes)") do
|
|
104
|
+
iterations.times do
|
|
105
|
+
source.ast.each_node(:block) do |node|
|
|
106
|
+
cop2.example_group?(node)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Test hook? matcher
|
|
112
|
+
cop3 = RuboCop::Cop::RSpecGuide::DuplicateBeforeHooks.new(config)
|
|
113
|
+
hook_nodes = []
|
|
114
|
+
|
|
115
|
+
source.ast.each_node(:block) do |node|
|
|
116
|
+
hook_nodes << node if cop3.hook?(node)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
x.report("hook? matcher (finds #{hook_nodes.size} nodes)") do
|
|
120
|
+
iterations.times do
|
|
121
|
+
source.ast.each_node(:block) do |node|
|
|
122
|
+
cop3.hook?(node)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
puts
|
|
129
|
+
puts "=" * 80
|
|
130
|
+
puts "Summary:"
|
|
131
|
+
puts "=" * 80
|
|
132
|
+
puts "✅ Current implementation uses RuboCop::Cop::RSpec::Base"
|
|
133
|
+
puts "✅ Leverages well-tested rubocop-rspec Language API"
|
|
134
|
+
puts "✅ Supports let_it_be and let_it_be! out of the box"
|
|
135
|
+
puts "✅ Consistent with rubocop-rspec ecosystem"
|
|
136
|
+
puts "=" * 80
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "benchmark_helper"
|
|
5
|
+
|
|
6
|
+
# Benchmark all RSpecGuide cops
|
|
7
|
+
puts "=" * 80
|
|
8
|
+
puts "RuboCop RSpec Guide - Cops Performance Benchmark"
|
|
9
|
+
puts "=" * 80
|
|
10
|
+
puts ""
|
|
11
|
+
|
|
12
|
+
# Configure benchmark timing based on environment
|
|
13
|
+
# FULL_BENCHMARK=1 - Full mode: longer warmup/measurement for accuracy (~5 minutes)
|
|
14
|
+
# Default - Quick mode: shorter warmup/measurement for fast feedback (~1 minute)
|
|
15
|
+
if ENV["FULL_BENCHMARK"]
|
|
16
|
+
WARMUP_TIME = 2
|
|
17
|
+
MEASUREMENT_TIME = 5
|
|
18
|
+
puts "Mode: FULL (accurate measurements, ~5 minutes)"
|
|
19
|
+
else
|
|
20
|
+
WARMUP_TIME = 1
|
|
21
|
+
MEASUREMENT_TIME = 2
|
|
22
|
+
puts "Mode: QUICK (fast feedback, ~1 minute)"
|
|
23
|
+
puts "Tip: Use FULL_BENCHMARK=1 for more accurate measurements"
|
|
24
|
+
end
|
|
25
|
+
puts ""
|
|
26
|
+
|
|
27
|
+
# Benchmark MinimumBehavioralCoverage
|
|
28
|
+
Benchmark.ips do |x|
|
|
29
|
+
x.config(time: MEASUREMENT_TIME, warmup: WARMUP_TIME)
|
|
30
|
+
|
|
31
|
+
source_with_violation = <<~RUBY
|
|
32
|
+
RSpec.describe User do
|
|
33
|
+
context "when user is valid" do
|
|
34
|
+
it "saves successfully" do
|
|
35
|
+
expect(user.save).to be true
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
RUBY
|
|
40
|
+
|
|
41
|
+
source_without_violation = <<~RUBY
|
|
42
|
+
RSpec.describe User do
|
|
43
|
+
context "when user is valid" do
|
|
44
|
+
it "saves successfully" do
|
|
45
|
+
expect(user.save).to be true
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
context "when user is invalid" do
|
|
50
|
+
it "does not save" do
|
|
51
|
+
expect(user.save).to be false
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
RUBY
|
|
56
|
+
|
|
57
|
+
x.report("MinimumBehavioralCoverage (with violation)") do
|
|
58
|
+
BenchmarkHelper.run_cop(
|
|
59
|
+
RuboCop::Cop::RSpecGuide::MinimumBehavioralCoverage,
|
|
60
|
+
source_with_violation
|
|
61
|
+
)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
x.report("MinimumBehavioralCoverage (without violation)") do
|
|
65
|
+
BenchmarkHelper.run_cop(
|
|
66
|
+
RuboCop::Cop::RSpecGuide::MinimumBehavioralCoverage,
|
|
67
|
+
source_without_violation
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
x.compare!
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
puts "\n"
|
|
75
|
+
|
|
76
|
+
# Benchmark HappyPathFirst
|
|
77
|
+
Benchmark.ips do |x|
|
|
78
|
+
x.config(time: MEASUREMENT_TIME, warmup: WARMUP_TIME)
|
|
79
|
+
|
|
80
|
+
source_with_violation = <<~RUBY
|
|
81
|
+
RSpec.describe User do
|
|
82
|
+
context "when email is invalid" do
|
|
83
|
+
it "returns error" do
|
|
84
|
+
expect(user.valid?).to be false
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
context "when email is valid" do
|
|
89
|
+
it "is valid" do
|
|
90
|
+
expect(user.valid?).to be true
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
RUBY
|
|
95
|
+
|
|
96
|
+
source_without_violation = <<~RUBY
|
|
97
|
+
RSpec.describe User do
|
|
98
|
+
context "when email is valid" do
|
|
99
|
+
it "is valid" do
|
|
100
|
+
expect(user.valid?).to be true
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
context "when email is invalid" do
|
|
105
|
+
it "returns error" do
|
|
106
|
+
expect(user.valid?).to be false
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
RUBY
|
|
111
|
+
|
|
112
|
+
x.report("HappyPathFirst (with violation)") do
|
|
113
|
+
BenchmarkHelper.run_cop(
|
|
114
|
+
RuboCop::Cop::RSpecGuide::HappyPathFirst,
|
|
115
|
+
source_with_violation
|
|
116
|
+
)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
x.report("HappyPathFirst (without violation)") do
|
|
120
|
+
BenchmarkHelper.run_cop(
|
|
121
|
+
RuboCop::Cop::RSpecGuide::HappyPathFirst,
|
|
122
|
+
source_without_violation
|
|
123
|
+
)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
x.compare!
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
puts "\n"
|
|
130
|
+
|
|
131
|
+
# Benchmark ContextSetup
|
|
132
|
+
Benchmark.ips do |x|
|
|
133
|
+
x.config(time: MEASUREMENT_TIME, warmup: WARMUP_TIME)
|
|
134
|
+
|
|
135
|
+
source_with_violation = <<~RUBY
|
|
136
|
+
RSpec.describe User do
|
|
137
|
+
context "when user is admin" do
|
|
138
|
+
it "has admin privileges" do
|
|
139
|
+
user = create(:user, :admin)
|
|
140
|
+
expect(user.admin?).to be true
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
RUBY
|
|
145
|
+
|
|
146
|
+
source_without_violation = <<~RUBY
|
|
147
|
+
RSpec.describe User do
|
|
148
|
+
context "when user is admin" do
|
|
149
|
+
let(:user) { create(:user, :admin) }
|
|
150
|
+
|
|
151
|
+
it "has admin privileges" do
|
|
152
|
+
expect(user.admin?).to be true
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
RUBY
|
|
157
|
+
|
|
158
|
+
x.report("ContextSetup (with violation)") do
|
|
159
|
+
BenchmarkHelper.run_cop(
|
|
160
|
+
RuboCop::Cop::RSpecGuide::ContextSetup,
|
|
161
|
+
source_with_violation
|
|
162
|
+
)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
x.report("ContextSetup (without violation)") do
|
|
166
|
+
BenchmarkHelper.run_cop(
|
|
167
|
+
RuboCop::Cop::RSpecGuide::ContextSetup,
|
|
168
|
+
source_without_violation
|
|
169
|
+
)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
x.compare!
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
puts "\n"
|
|
176
|
+
|
|
177
|
+
# Benchmark DuplicateLetValues
|
|
178
|
+
Benchmark.ips do |x|
|
|
179
|
+
x.config(time: MEASUREMENT_TIME, warmup: WARMUP_TIME)
|
|
180
|
+
|
|
181
|
+
source_with_violation = <<~RUBY
|
|
182
|
+
RSpec.describe Calculator do
|
|
183
|
+
context "with positive numbers" do
|
|
184
|
+
let(:value) { 42 }
|
|
185
|
+
|
|
186
|
+
it "works" do
|
|
187
|
+
expect(value).to eq(42)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
context "with negative numbers" do
|
|
192
|
+
let(:value) { 42 }
|
|
193
|
+
|
|
194
|
+
it "works differently" do
|
|
195
|
+
expect(value).to eq(42)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
RUBY
|
|
200
|
+
|
|
201
|
+
source_without_violation = <<~RUBY
|
|
202
|
+
RSpec.describe Calculator do
|
|
203
|
+
let(:default_value) { 42 }
|
|
204
|
+
|
|
205
|
+
context "with positive numbers" do
|
|
206
|
+
let(:value) { default_value }
|
|
207
|
+
|
|
208
|
+
it "works" do
|
|
209
|
+
expect(value).to eq(42)
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
context "with negative numbers" do
|
|
214
|
+
let(:value) { -default_value }
|
|
215
|
+
|
|
216
|
+
it "works differently" do
|
|
217
|
+
expect(value).to eq(-42)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
RUBY
|
|
222
|
+
|
|
223
|
+
x.report("DuplicateLetValues (with violation)") do
|
|
224
|
+
BenchmarkHelper.run_cop(
|
|
225
|
+
RuboCop::Cop::RSpecGuide::DuplicateLetValues,
|
|
226
|
+
source_with_violation
|
|
227
|
+
)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
x.report("DuplicateLetValues (without violation)") do
|
|
231
|
+
BenchmarkHelper.run_cop(
|
|
232
|
+
RuboCop::Cop::RSpecGuide::DuplicateLetValues,
|
|
233
|
+
source_without_violation
|
|
234
|
+
)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
x.compare!
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
puts "\n"
|
|
241
|
+
|
|
242
|
+
# Benchmark DuplicateBeforeHooks
|
|
243
|
+
Benchmark.ips do |x|
|
|
244
|
+
x.config(time: MEASUREMENT_TIME, warmup: WARMUP_TIME)
|
|
245
|
+
|
|
246
|
+
source_with_violation = <<~RUBY
|
|
247
|
+
RSpec.describe Service do
|
|
248
|
+
context "with feature enabled" do
|
|
249
|
+
before do
|
|
250
|
+
setup_database
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
it "works" do
|
|
254
|
+
expect(service.call).to be_success
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
context "with feature disabled" do
|
|
259
|
+
before do
|
|
260
|
+
setup_database
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
it "still works" do
|
|
264
|
+
expect(service.call).to be_success
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
RUBY
|
|
269
|
+
|
|
270
|
+
source_without_violation = <<~RUBY
|
|
271
|
+
RSpec.describe Service do
|
|
272
|
+
before do
|
|
273
|
+
setup_database
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
context "with feature enabled" do
|
|
277
|
+
it "works" do
|
|
278
|
+
expect(service.call).to be_success
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
context "with feature disabled" do
|
|
283
|
+
it "still works" do
|
|
284
|
+
expect(service.call).to be_success
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
RUBY
|
|
289
|
+
|
|
290
|
+
x.report("DuplicateBeforeHooks (with violation)") do
|
|
291
|
+
BenchmarkHelper.run_cop(
|
|
292
|
+
RuboCop::Cop::RSpecGuide::DuplicateBeforeHooks,
|
|
293
|
+
source_with_violation
|
|
294
|
+
)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
x.report("DuplicateBeforeHooks (without violation)") do
|
|
298
|
+
BenchmarkHelper.run_cop(
|
|
299
|
+
RuboCop::Cop::RSpecGuide::DuplicateBeforeHooks,
|
|
300
|
+
source_without_violation
|
|
301
|
+
)
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
x.compare!
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
puts "\n"
|
|
308
|
+
|
|
309
|
+
# Benchmark InvariantExamples
|
|
310
|
+
Benchmark.ips do |x|
|
|
311
|
+
x.config(time: MEASUREMENT_TIME, warmup: WARMUP_TIME)
|
|
312
|
+
|
|
313
|
+
source_with_violation = <<~RUBY
|
|
314
|
+
RSpec.describe Calculator do
|
|
315
|
+
context "with integers" do
|
|
316
|
+
it "adds numbers" do
|
|
317
|
+
expect(calculator.add(1, 2)).to eq(3)
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
it "returns numeric result" do
|
|
321
|
+
expect(calculator.add(1, 2)).to be_a(Numeric)
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
context "with floats" do
|
|
326
|
+
it "adds numbers" do
|
|
327
|
+
expect(calculator.add(1.5, 2.5)).to eq(4.0)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
it "returns numeric result" do
|
|
331
|
+
expect(calculator.add(1.5, 2.5)).to be_a(Numeric)
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
RUBY
|
|
336
|
+
|
|
337
|
+
source_without_violation = <<~RUBY
|
|
338
|
+
RSpec.describe Calculator do
|
|
339
|
+
shared_examples "returns numeric result" do
|
|
340
|
+
it "returns numeric result" do
|
|
341
|
+
expect(result).to be_a(Numeric)
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
context "with integers" do
|
|
346
|
+
let(:result) { calculator.add(1, 2) }
|
|
347
|
+
|
|
348
|
+
it "adds numbers" do
|
|
349
|
+
expect(result).to eq(3)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
include_examples "returns numeric result"
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
context "with floats" do
|
|
356
|
+
let(:result) { calculator.add(1.5, 2.5) }
|
|
357
|
+
|
|
358
|
+
it "adds numbers" do
|
|
359
|
+
expect(result).to eq(4.0)
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
include_examples "returns numeric result"
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
RUBY
|
|
366
|
+
|
|
367
|
+
x.report("InvariantExamples (with violation)") do
|
|
368
|
+
BenchmarkHelper.run_cop(
|
|
369
|
+
RuboCop::Cop::RSpecGuide::InvariantExamples,
|
|
370
|
+
source_with_violation
|
|
371
|
+
)
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
x.report("InvariantExamples (without violation)") do
|
|
375
|
+
BenchmarkHelper.run_cop(
|
|
376
|
+
RuboCop::Cop::RSpecGuide::InvariantExamples,
|
|
377
|
+
source_without_violation
|
|
378
|
+
)
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
x.compare!
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
puts "\n"
|
|
385
|
+
|
|
386
|
+
# Benchmark DynamicAttributeEvaluation
|
|
387
|
+
Benchmark.ips do |x|
|
|
388
|
+
x.config(time: MEASUREMENT_TIME, warmup: WARMUP_TIME)
|
|
389
|
+
|
|
390
|
+
source_with_violation = <<~RUBY
|
|
391
|
+
FactoryBot.define do
|
|
392
|
+
factory :user do
|
|
393
|
+
created_at { Time.current }
|
|
394
|
+
random_number { rand(1..100) }
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
RUBY
|
|
398
|
+
|
|
399
|
+
source_without_violation = <<~RUBY
|
|
400
|
+
FactoryBot.define do
|
|
401
|
+
factory :user do
|
|
402
|
+
created_at { -> { Time.current } }
|
|
403
|
+
random_number { -> { rand(1..100) } }
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
RUBY
|
|
407
|
+
|
|
408
|
+
x.report("DynamicAttributeEvaluation (with violation)") do
|
|
409
|
+
BenchmarkHelper.run_cop(
|
|
410
|
+
RuboCop::Cop::FactoryBotGuide::DynamicAttributeEvaluation,
|
|
411
|
+
source_with_violation
|
|
412
|
+
)
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
x.report("DynamicAttributeEvaluation (without violation)") do
|
|
416
|
+
BenchmarkHelper.run_cop(
|
|
417
|
+
RuboCop::Cop::FactoryBotGuide::DynamicAttributeEvaluation,
|
|
418
|
+
source_without_violation
|
|
419
|
+
)
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
x.compare!
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
puts "\n"
|
|
426
|
+
puts "=" * 80
|
|
427
|
+
puts "Benchmark completed!"
|
|
428
|
+
puts "=" * 80
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "benchmark"
|
|
5
|
+
require "rubocop"
|
|
6
|
+
require_relative "../lib/rubocop-rspec-guide"
|
|
7
|
+
|
|
8
|
+
# Sample RSpec code for benchmarking
|
|
9
|
+
SAMPLE_CODE = <<~RUBY
|
|
10
|
+
describe 'UserService' do
|
|
11
|
+
context 'when user is admin' do
|
|
12
|
+
let(:user) { create(:user, :admin) }
|
|
13
|
+
let(:service) { UserService.new(user) }
|
|
14
|
+
|
|
15
|
+
before { setup_admin_permissions }
|
|
16
|
+
|
|
17
|
+
it 'has admin access' do
|
|
18
|
+
expect(service.admin?).to be true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'can modify settings' do
|
|
22
|
+
expect(service.can_modify_settings?).to be true
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
context 'when user is regular' do
|
|
27
|
+
let(:user) { create(:user) }
|
|
28
|
+
let(:service) { UserService.new(user) }
|
|
29
|
+
|
|
30
|
+
before { setup_admin_permissions }
|
|
31
|
+
|
|
32
|
+
it 'does not have admin access' do
|
|
33
|
+
expect(service.admin?).to be false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'cannot modify settings' do
|
|
37
|
+
expect(service.can_modify_settings?).to be false
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
RUBY
|
|
42
|
+
|
|
43
|
+
puts "=" * 80
|
|
44
|
+
puts "RuboCop RSpec Cops Performance Benchmark"
|
|
45
|
+
puts "=" * 80
|
|
46
|
+
puts "Sample code: #{SAMPLE_CODE.lines.count} lines"
|
|
47
|
+
puts "Ruby version: #{RUBY_VERSION}"
|
|
48
|
+
puts "RuboCop version: #{RuboCop::Version.version}"
|
|
49
|
+
puts "=" * 80
|
|
50
|
+
puts
|
|
51
|
+
|
|
52
|
+
# Parse the code once
|
|
53
|
+
source = RuboCop::ProcessedSource.new(SAMPLE_CODE, RUBY_VERSION.to_f)
|
|
54
|
+
|
|
55
|
+
# Create config
|
|
56
|
+
config = RuboCop::Config.new(
|
|
57
|
+
{
|
|
58
|
+
"RSpec" => {
|
|
59
|
+
"Enabled" => true,
|
|
60
|
+
"Language" => {
|
|
61
|
+
"ExampleGroups" => {
|
|
62
|
+
"Regular" => %w[describe context]
|
|
63
|
+
},
|
|
64
|
+
"Examples" => {
|
|
65
|
+
"Regular" => %w[it]
|
|
66
|
+
},
|
|
67
|
+
"Helpers" => %w[let let!],
|
|
68
|
+
"Hooks" => %w[before after],
|
|
69
|
+
"Subjects" => %w[subject]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
cops = [
|
|
76
|
+
RuboCop::Cop::RSpecGuide::MinimumBehavioralCoverage,
|
|
77
|
+
RuboCop::Cop::RSpecGuide::HappyPathFirst,
|
|
78
|
+
RuboCop::Cop::RSpecGuide::ContextSetup,
|
|
79
|
+
RuboCop::Cop::RSpecGuide::DuplicateLetValues,
|
|
80
|
+
RuboCop::Cop::RSpecGuide::DuplicateBeforeHooks,
|
|
81
|
+
RuboCop::Cop::RSpecGuide::InvariantExamples
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
iterations = 1000
|
|
85
|
+
|
|
86
|
+
Benchmark.bm(40) do |x|
|
|
87
|
+
cops.each do |cop_class|
|
|
88
|
+
x.report(cop_class.badge.to_s) do
|
|
89
|
+
iterations.times do
|
|
90
|
+
cop = cop_class.new(config)
|
|
91
|
+
commissioner = RuboCop::Cop::Commissioner.new([cop], [], raise_error: false)
|
|
92
|
+
commissioner.investigate(source)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
x.report("All 6 cops together") do
|
|
98
|
+
iterations.times do
|
|
99
|
+
cop_instances = cops.map { |c| c.new(config) }
|
|
100
|
+
commissioner = RuboCop::Cop::Commissioner.new(cop_instances, [], raise_error: false)
|
|
101
|
+
commissioner.investigate(source)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
puts
|
|
107
|
+
puts "=" * 80
|
|
108
|
+
puts "Benchmark complete! (#{iterations} iterations per cop)"
|
|
109
|
+
puts "=" * 80
|