test-prof 0.4.0 → 0.4.1
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 +4 -8
- data/guides/any_fixture.md +1 -1
- data/guides/before_all.md +2 -2
- data/guides/event_prof.md +2 -2
- data/guides/factory_default.md +4 -4
- data/guides/factory_doctor.md +48 -3
- data/guides/factory_prof.md +5 -5
- data/guides/let_it_be.md +3 -2
- data/guides/rspec_dissect.md +3 -1
- data/guides/rspec_stamp.md +4 -5
- data/guides/rubocop.md +4 -4
- data/guides/ruby_prof.md +1 -1
- data/guides/stack_prof.md +3 -3
- data/lib/minitest/base_reporter.rb +57 -0
- data/lib/{test_prof/event_prof/formatters/minitest.rb → minitest/event_prof_formatter.rb} +6 -6
- data/lib/minitest/fd_ignorable.rb +13 -0
- data/lib/minitest/test_prof_plugin.rb +43 -0
- data/lib/test_prof.rb +3 -3
- data/lib/test_prof/event_prof/minitest.rb +51 -69
- data/lib/test_prof/event_prof/rspec.rb +8 -1
- data/lib/test_prof/factory_doctor.rb +1 -5
- data/lib/test_prof/factory_doctor/minitest.rb +84 -1
- data/lib/test_prof/factory_doctor/rspec.rb +10 -1
- data/lib/test_prof/factory_prof/factory_builders/fabrication.rb +3 -3
- data/lib/test_prof/factory_prof/factory_builders/factory_girl.rb +3 -2
- data/lib/test_prof/factory_prof/printers/flamegraph.rb +1 -0
- data/lib/test_prof/factory_prof/printers/simple.rb +1 -0
- data/lib/test_prof/logging.rb +1 -0
- data/lib/test_prof/recipes/rspec/let_it_be.rb +11 -0
- data/lib/test_prof/recipes/rspec/sample.rb +2 -2
- data/lib/test_prof/rspec_dissect.rb +10 -2
- data/lib/test_prof/rspec_dissect/rspec.rb +27 -11
- data/lib/test_prof/tag_prof/rspec.rb +6 -3
- data/lib/test_prof/version.rb +1 -1
- metadata +7 -5
- data/lib/minitest/event_prof_plugin.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6efa575090e0c535e962071ed5b24a0d5f9f360b
|
4
|
+
data.tar.gz: c60d25f31f5450a7b926845711527531ac595c3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4d148c53dd4807ede82768a7e779d56bfcfb04a8ac9ed6f8eb3aa260fbbe753e64dd79a9c021093442fcfb981d9e2a846c2450482c6ffb84c68d05ef336c620
|
7
|
+
data.tar.gz: 5aa0611fe30a98061f0d9a97bf7a09066614a3663e845e99cc510697f29d847c581183b75d89e9ea0cfa92ea93048249780fe77fdd6d4e87b25eacc9b29576fe
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# Change log
|
2
2
|
|
3
|
+
## 0.4.1
|
4
|
+
|
5
|
+
- [#44](https://github.com/palkan/test-prof/pull/44) Suppor older versions of RSpec. ([@palkan][])
|
6
|
+
|
7
|
+
Support RSpec 3.1.0+ in general.
|
8
|
+
|
9
|
+
`let_it_be` supports only RSpec 3.3.0+.
|
10
|
+
|
11
|
+
RSpecDissect `let` tracking supports only RSpec 3.3.0+.
|
12
|
+
|
13
|
+
- [#38](https://github.com/palkan/test-prof/pull/38) Factory Doctor Minitest integration. ([@IDolgirev][])
|
14
|
+
|
15
|
+
It is possible now to use Factory Doctor with Minitest
|
16
|
+
|
3
17
|
## 0.4.0
|
4
18
|
|
5
19
|
### Features:
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
TestProf is a collection of different tools to analyze your test suite performance.
|
6
6
|
|
7
|
-
Why does test suite performance matter? First of all, testing is a part of a developer's feedback loop (see @searls [talk](https://
|
7
|
+
Why does test suite performance matter? First of all, testing is a part of a developer's feedback loop (see [@searls](https://github.com/searls) [talk](https://vimeo.com/145917204)) and, secondly, it is a part of a deployment cycle.
|
8
8
|
|
9
9
|
Simply speaking, slow tests waste your time making you less productive.
|
10
10
|
|
@@ -16,7 +16,7 @@ TestProf toolbox aims to help you identify bottlenecks in your test suite. It co
|
|
16
16
|
|
17
17
|
- ActiveSupport-backed profilers
|
18
18
|
|
19
|
-
-
|
19
|
+
- RuboCop cops
|
20
20
|
|
21
21
|
- etc.
|
22
22
|
|
@@ -73,7 +73,7 @@ Checkout our guides for each specific tool:
|
|
73
73
|
|
74
74
|
- [RSpecDissect Profiler](https://github.com/palkan/test-prof/tree/master/guides/rspec_dissect.md)
|
75
75
|
|
76
|
-
- [
|
76
|
+
- [RuboCop cops](https://github.com/palkan/test-prof/tree/master/guides/rubocop.md)
|
77
77
|
|
78
78
|
## Tips and Tricks (or _Recipes_)
|
79
79
|
|
@@ -110,13 +110,9 @@ end
|
|
110
110
|
|
111
111
|
## What's next?
|
112
112
|
|
113
|
-
Or TODO list:
|
114
113
|
|
115
|
-
|
114
|
+
Have an idea? [Propose](https://github.com/palkan/test-prof/issues/new) a feature request!
|
116
115
|
|
117
|
-
- Improve FactoryDoctor
|
118
|
-
|
119
|
-
- Add more Rubocop cops (e.g. `CreateListLimit`)
|
120
116
|
|
121
117
|
## License
|
122
118
|
|
data/guides/any_fixture.md
CHANGED
@@ -112,4 +112,4 @@ include_context "article"
|
|
112
112
|
include_context "author"
|
113
113
|
```
|
114
114
|
|
115
|
-
Now we have the following affected tables list: `[
|
115
|
+
Now we have the following affected tables list: `['articles', 'authors']`. At the end of the suite, the 'authors' table is cleaned first which leads to a foreign-key violation error.
|
data/guides/before_all.md
CHANGED
@@ -34,7 +34,7 @@ describe BeatleWeightedSearchQuery do
|
|
34
34
|
end
|
35
35
|
```
|
36
36
|
|
37
|
-
But then you have to deal with database cleaning, which can be
|
37
|
+
But then you have to deal with database cleaning, which can be either tricky or slow.
|
38
38
|
|
39
39
|
There is a better option: we can wrap the whole example group into a transaction.
|
40
40
|
And that's how `before_all` works:
|
@@ -95,4 +95,4 @@ end
|
|
95
95
|
# Note, that @user.reload may not be enough,
|
96
96
|
# 'cause it doesn't reset associations
|
97
97
|
let(:user) { User.find(@user.id) }
|
98
|
-
```
|
98
|
+
```
|
data/guides/event_prof.md
CHANGED
@@ -34,7 +34,7 @@ Currently, EventProf supports only ActiveSupport::Notifications
|
|
34
34
|
|
35
35
|
To activate EventProf with:
|
36
36
|
|
37
|
-
###
|
37
|
+
### RSpec
|
38
38
|
|
39
39
|
Use `EVENT_PROF` environment variable set to event name:
|
40
40
|
|
@@ -95,7 +95,7 @@ end
|
|
95
95
|
|
96
96
|
Or provide the `EVENT_PROF_EXAMPLES=1` env variable.
|
97
97
|
|
98
|
-
Another useful configuration parameter – `rank_by`. It's responsible for sorting stats –
|
98
|
+
Another useful configuration parameter – `rank_by`. It's responsible for sorting stats –
|
99
99
|
either by the time spent in the event or by the number of occurrences:
|
100
100
|
|
101
101
|
```sh
|
data/guides/factory_default.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# FactoryDefault
|
2
2
|
|
3
|
-
_Factory Default_ aims to help you cope with _factory cascades_ (see [FactoryProf](https://github.com/palkan/test-prof/tree/master/guides/factory_prof.md)) by
|
3
|
+
_Factory Default_ aims to help you cope with _factory cascades_ (see [FactoryProf](https://github.com/palkan/test-prof/tree/master/guides/factory_prof.md)) by reusing associated records.
|
4
4
|
|
5
5
|
It can be very useful when you're working on a typical SaaS application (or other hierarchical data).
|
6
6
|
|
@@ -51,7 +51,7 @@ Typical workaround:
|
|
51
51
|
describe "PATCH #update" do
|
52
52
|
let(:account) { create(:account) }
|
53
53
|
let(:project) { create(:project, account: account) }
|
54
|
-
let(:task
|
54
|
+
let(:task) { create(:task, project: project, account: account) }
|
55
55
|
|
56
56
|
it "works" do
|
57
57
|
patch :update, id: task.id, task: { completed: 't' }
|
@@ -68,9 +68,9 @@ Here is how we can deal with it using FactoryDefault:
|
|
68
68
|
describe "PATCH #update" do
|
69
69
|
let(:account) { create_default(:account) }
|
70
70
|
let(:project) { create_default(:project) }
|
71
|
-
let(:task
|
71
|
+
let(:task) { create(:task) }
|
72
72
|
|
73
|
-
# and if need more projects, users, tasks with the same parent record,
|
73
|
+
# and if we need more projects, users, tasks with the same parent record,
|
74
74
|
# we just write
|
75
75
|
let(:another_project) { create(:project) } # uses the same account
|
76
76
|
let(:another_task) { create(:task) } # uses the same account and the first project
|
data/guides/factory_doctor.md
CHANGED
@@ -37,12 +37,12 @@ User (./spec/models/user_spec.rb:3)
|
|
37
37
|
validates email (./spec/user_spec.rb:8) – 2 records created, 00:00.514
|
38
38
|
```
|
39
39
|
|
40
|
-
**NOTE**: have you noticed the "potentially" word? Unfortunately, FactoryDoctor is not a
|
40
|
+
**NOTE**: have you noticed the "potentially" word? Unfortunately, FactoryDoctor is not a
|
41
41
|
magician (it's still learning) and sometimes it produces false negatives and false positives too.
|
42
42
|
|
43
43
|
Please, submit an [issue](https://github.com/palkan/test-prof/issues) if you found a case which makes FactoryDoctor fail.
|
44
44
|
|
45
|
-
You can also tell FactoryDoctor to ignore specific examples/groups. Just add `:fd_ignore` tag to it:
|
45
|
+
You can also tell FactoryDoctor to ignore specific examples/groups. Just add the `:fd_ignore` tag to it:
|
46
46
|
|
47
47
|
```ruby
|
48
48
|
# won't be reported as offense
|
@@ -55,7 +55,9 @@ end
|
|
55
55
|
|
56
56
|
## Instructions
|
57
57
|
|
58
|
-
Currently, FactoryDoctor works only with FactoryGirl
|
58
|
+
Currently, FactoryDoctor works only with FactoryGirl.
|
59
|
+
|
60
|
+
## RSpec
|
59
61
|
|
60
62
|
To activate FactoryDoctor use `FDOC` environment variable:
|
61
63
|
|
@@ -72,3 +74,46 @@ FDOC=1 FDOC_STAMP="fdoc:consider" rspec ...
|
|
72
74
|
```
|
73
75
|
|
74
76
|
After running the command above all _potentially_ bad examples would be marked with the `fdoc: :consider` tag.
|
77
|
+
|
78
|
+
## Minitest
|
79
|
+
|
80
|
+
To activate FactoryDoctor use `FDOC` environment variable:
|
81
|
+
|
82
|
+
```sh
|
83
|
+
FDOC=1 ruby ...
|
84
|
+
```
|
85
|
+
|
86
|
+
or use CLI option as shown below:
|
87
|
+
|
88
|
+
```sh
|
89
|
+
ruby ... --factory-doctor
|
90
|
+
```
|
91
|
+
|
92
|
+
The same option to force Factory Doctor to ignore specific examples is also available for Minitest.
|
93
|
+
Just use `fd_ignore` inside your example:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
# won't be reported as offense
|
97
|
+
it "is ignored" do
|
98
|
+
fd_ignore
|
99
|
+
|
100
|
+
@user.name = ''
|
101
|
+
refute @user.valid?
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
## Using with Minitest::Reporters
|
106
|
+
|
107
|
+
If you're using `Minitest::Reporters` in your project you have to explicitly declare it
|
108
|
+
in your test helper file:
|
109
|
+
|
110
|
+
```sh
|
111
|
+
require 'minitest/reporters'
|
112
|
+
Minitest::Reporters.use! [YOUR_FAVORITE_REPORTERS]
|
113
|
+
```
|
114
|
+
#### NOTICE
|
115
|
+
When you have `minitest-reporters` installed as a gem but not declared in your `Gemfile`
|
116
|
+
make sure to always prepend your test run command with `bundle exec` (but we sure that you always do it).
|
117
|
+
Otherwise, you'll get an error caused by Minitest plugin system, which scans all the entries in the
|
118
|
+
`$LOAD_PATH` for any `minitest/*_plugin.rb`, thus initialization of `minitest-reporters` plugin which is
|
119
|
+
available in that case doesn't happens correctly.
|
data/guides/factory_prof.md
CHANGED
@@ -16,7 +16,7 @@ Example output:
|
|
16
16
|
|
17
17
|
It shows both the total number of the factory runs and the number of _top-level_ runs, i.e. not during another factory invocation (e.g. when using associations.)
|
18
18
|
|
19
|
-
**NOTE**: FactoryProf only tracks the database-persisted factories. In case of
|
19
|
+
**NOTE**: FactoryProf only tracks the database-persisted factories. In case of FactoryGirl these are the factories
|
20
20
|
provided by using `create` strategy. In case of Fabrication - objects that created using `create` method.
|
21
21
|
|
22
22
|
## Instructions
|
@@ -29,20 +29,20 @@ To activate FactoryProf use `FPROF` environment variable:
|
|
29
29
|
# Simple profiler
|
30
30
|
FPROF=1 rspec
|
31
31
|
|
32
|
-
# or
|
32
|
+
# or
|
33
33
|
FPROF=1 bundle exec rake test
|
34
34
|
```
|
35
35
|
|
36
36
|
## Factory Flamegraph
|
37
37
|
|
38
|
-
The most useful feature of FactoryProf is the _FactoryFlame_ report. That's the special
|
38
|
+
The most useful feature of FactoryProf is the _FactoryFlame_ report. That's the special interpretation of Brendan Gregg's [flame graphs](http://www.brendangregg.com/flamegraphs.html) which allows you to identify _factory cascades_.
|
39
39
|
|
40
40
|
To generate FactoryFlame report set `FPROF` environment variable to `flamegraph`:
|
41
41
|
|
42
42
|
```sh
|
43
43
|
FPROF=flamegraph rspec
|
44
44
|
|
45
|
-
# or
|
45
|
+
# or
|
46
46
|
FPROF=flamegraph bundle exec rake test
|
47
47
|
```
|
48
48
|
|
@@ -52,7 +52,7 @@ That's how a report looks like:
|
|
52
52
|
|
53
53
|
How to read this?
|
54
54
|
|
55
|
-
Every column represents a _factory stack_ or _cascade_, that is a sequence of recursive `#create` method calls. Consider
|
55
|
+
Every column represents a _factory stack_ or _cascade_, that is a sequence of recursive `#create` method calls. Consider an example:
|
56
56
|
|
57
57
|
```ruby
|
58
58
|
factory :comment do
|
data/guides/let_it_be.md
CHANGED
@@ -11,7 +11,7 @@ describe BeatleWeightedSearchQuery do
|
|
11
11
|
let!(:george) { create(:beatle, name: 'George') }
|
12
12
|
let!(:john) { create(:beatle, name: 'John') }
|
13
13
|
|
14
|
-
specify { expect(subject.call('
|
14
|
+
specify { expect(subject.call('john')).to contain_exactly(john) }
|
15
15
|
|
16
16
|
# and more examples here
|
17
17
|
end
|
@@ -45,7 +45,7 @@ describe BeatleWeightedSearchQuery do
|
|
45
45
|
let_it_be(:george) { create(:beatle, name: 'George') }
|
46
46
|
let_it_be(:john) { create(:beatle, name: 'John') }
|
47
47
|
|
48
|
-
specify { expect(subject.call('
|
48
|
+
specify { expect(subject.call('john')).to contain_exactly(john) }
|
49
49
|
|
50
50
|
# and more examples here
|
51
51
|
end
|
@@ -53,6 +53,7 @@ end
|
|
53
53
|
|
54
54
|
That's it! Just replace `let!` with `let_it_be`. That's equal to the `before_all` approach but requires less refactoring.
|
55
55
|
|
56
|
+
**NOTE**: requires RSpec >= 3.2.0.
|
56
57
|
|
57
58
|
## Instructions
|
58
59
|
|
data/guides/rspec_dissect.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# RSpecDissect
|
2
2
|
|
3
|
-
Do you know how much time you spend in `before` hooks? Or memoization helpers such as `let`? Usually, the most of the whole test suite time.
|
3
|
+
Do you know how much time you spend in `before` hooks? Or in memoization helpers such as `let`? Usually, the most of the whole test suite time.
|
4
4
|
|
5
5
|
_RSpecDissect_ provides this kind of information and also shows you the worst example groups. The main purpose of RSpecDissect is to identify these slow groups and refactor them using [`before_all`](https://github.com/palkan/test-prof/tree/master/guides/before_all.md) or [`let_it_be`](https://github.com/palkan/test-prof/tree/master/guides/let_it_be.md) recipes.
|
6
6
|
|
@@ -31,6 +31,8 @@ BookedSlotsController (./spec/controllers/booked_slots_controller_spec.rb:3) –
|
|
31
31
|
AvailableSlotsController (./spec/controllers/available_slots_controller_spec.rb:3) – 00:18.481 of 00:23.366 (85)
|
32
32
|
```
|
33
33
|
|
34
|
+
**NOTE**: Tracking `let` time only supported in RSpec >= 3.3.0.
|
35
|
+
|
34
36
|
## Instructions
|
35
37
|
|
36
38
|
RSpecDissect can only be used with RSpec (which is clear from the name).
|
data/guides/rspec_stamp.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
#
|
1
|
+
# RSpecStamp
|
2
2
|
|
3
|
-
|
3
|
+
RSpecStamp is a tool to automatically _tag_ failed examples with custom tags.
|
4
4
|
|
5
5
|
It _literally_ adds tags to your examples (i.e. rewrites them).
|
6
6
|
|
7
|
-
The main purpose of
|
7
|
+
The main purpose of RSpecStamp is to make testing codebase refactoring easy. Changing global configuration may cause a lot of failures. You can patch failing spec by adding a shared context. And here comes RSpecStamp.
|
8
8
|
|
9
9
|
## Example Use Case: Sidekiq Inline
|
10
10
|
|
@@ -38,7 +38,7 @@ The output of the command above contains information about the _stamping_ proces
|
|
38
38
|
|
39
39
|
Now all (or almost all) failing specs are tagged with `sidekiq: :inline`. Run the whole suite again and check it there are any failures left.
|
40
40
|
|
41
|
-
There is also a
|
41
|
+
There is also a `dry-run` mode (activated by `RSTAMP_DRY_RUN=1` env variable) which prints out patches instead of re-writing files.
|
42
42
|
|
43
43
|
## Configuration
|
44
44
|
|
@@ -50,4 +50,3 @@ TestProf::RSpecStamp.configure do |config|
|
|
50
50
|
config.ignore_files << %r{spec/my_directory}
|
51
51
|
end
|
52
52
|
```
|
53
|
-
|
data/guides/rubocop.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
# Custom
|
1
|
+
# Custom RuboCop Cops
|
2
2
|
|
3
|
-
TestProf comes with the [
|
3
|
+
TestProf comes with the [RuboCop](https://github.com/bbatsov/rubocop) cops that help you write more performant tests.
|
4
4
|
|
5
5
|
To enable them:
|
6
6
|
|
7
|
-
- Require `test_prof/rubocop` in your
|
7
|
+
- Require `test_prof/rubocop` in your RuboCop configuration:
|
8
8
|
|
9
9
|
```yml
|
10
10
|
# .rubocop.yml
|
@@ -21,7 +21,7 @@ RSpec/AggregateFailures:
|
|
21
21
|
- 'spec/**/*.rb'
|
22
22
|
```
|
23
23
|
|
24
|
-
##
|
24
|
+
## RSpec/AggregateFailures
|
25
25
|
|
26
26
|
This cop encourages you to use one of the greatest features of the recent RSpec – aggregating failures within an example.
|
27
27
|
|
data/guides/ruby_prof.md
CHANGED
@@ -13,7 +13,7 @@ group :development, :test do
|
|
13
13
|
end
|
14
14
|
```
|
15
15
|
|
16
|
-
RubyProf profiler has two modes:
|
16
|
+
RubyProf profiler has two modes: `global` and `per-example`.
|
17
17
|
|
18
18
|
You can activate the global profiling using the environment variable `TEST_RUBY_PROF`:
|
19
19
|
|
data/guides/stack_prof.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
## Instructions
|
6
6
|
|
7
|
-
Install
|
7
|
+
Install `stackprof` gem (>= 0.2.9):
|
8
8
|
|
9
9
|
```ruby
|
10
10
|
# Gemfile
|
@@ -13,9 +13,9 @@ group :development, :test do
|
|
13
13
|
end
|
14
14
|
```
|
15
15
|
|
16
|
-
StackProf profiler has 2 modes:
|
16
|
+
StackProf profiler has 2 modes: `global` and `per-example`.
|
17
17
|
|
18
|
-
You can activate
|
18
|
+
You can activate global profiling using the environment variable `TEST_STACK_PROF`:
|
19
19
|
|
20
20
|
```sh
|
21
21
|
TEST_STACK_PROF=1 bundle exec rake test
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest'
|
4
|
+
require 'test_prof/logging'
|
5
|
+
|
6
|
+
module Minitest
|
7
|
+
module TestProf
|
8
|
+
class BaseReporter < AbstractReporter # :nodoc:
|
9
|
+
include ::TestProf::Logging
|
10
|
+
|
11
|
+
attr_accessor :io
|
12
|
+
|
13
|
+
def initialize(io = $stdout, _options = {})
|
14
|
+
@io = io
|
15
|
+
inject_to_minitest_reporters if defined? Minitest::Reporters
|
16
|
+
end
|
17
|
+
|
18
|
+
def start; end
|
19
|
+
|
20
|
+
def prerecord(group, example); end
|
21
|
+
|
22
|
+
def before_test(test); end
|
23
|
+
|
24
|
+
def record(*); end
|
25
|
+
|
26
|
+
def after_test(test); end
|
27
|
+
|
28
|
+
def report; end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def location(group, example = nil)
|
33
|
+
if group.is_a? Class
|
34
|
+
suite = group.public_instance_methods.select { |mtd| mtd.to_s.match /^test_/ }
|
35
|
+
name = suite.find { |mtd| mtd.to_s == example }
|
36
|
+
group.instance_method(name).source_location
|
37
|
+
else
|
38
|
+
suite = group.methods.select { |mtd| mtd.to_s.match /^test_/ }
|
39
|
+
name = suite.find { |mtd| mtd.to_s == group.name }
|
40
|
+
group.method(name).source_location
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def location_with_line_number(group, example = nil)
|
45
|
+
File.expand_path(location(group, example).join(':')).gsub(Dir.getwd, '.')
|
46
|
+
end
|
47
|
+
|
48
|
+
def location_without_line_number(group, example = nil)
|
49
|
+
File.expand_path(location(group, example).first).gsub(Dir.getwd, '.')
|
50
|
+
end
|
51
|
+
|
52
|
+
def inject_to_minitest_reporters
|
53
|
+
Minitest::Reporters.reporters << self if Minitest::Reporters.reporters
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -4,12 +4,12 @@ require "test_prof/ext/float_duration"
|
|
4
4
|
require "test_prof/ext/string_truncate"
|
5
5
|
require "test_prof/ext/string_strip_heredoc"
|
6
6
|
|
7
|
-
module
|
8
|
-
module
|
9
|
-
class
|
10
|
-
using FloatDuration
|
11
|
-
using StringTruncate
|
12
|
-
using StringStripHeredoc
|
7
|
+
module Minitest
|
8
|
+
module TestProf
|
9
|
+
class EventProfFormatter # :nodoc:
|
10
|
+
using ::TestProf::FloatDuration
|
11
|
+
using ::TestProf::StringTruncate
|
12
|
+
using ::TestProf::StringStripHeredoc
|
13
13
|
|
14
14
|
def initialize(profiler)
|
15
15
|
@profiler = profiler
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_prof/event_prof/minitest'
|
4
|
+
require 'test_prof/factory_doctor/minitest'
|
5
|
+
|
6
|
+
module Minitest # :nodoc:
|
7
|
+
module TestProf # :nodoc:
|
8
|
+
def self.configure_options(options = {})
|
9
|
+
options.tap do |opts|
|
10
|
+
opts[:event] = ENV['EVENT_PROF'] if ENV['EVENT_PROF']
|
11
|
+
opts[:rank_by] = ENV['EVENT_PROF_RANK'].to_sym if ENV['EVENT_PROF_RANK']
|
12
|
+
opts[:top_count] = ENV['EVENT_PROF_TOP'].to_i if ENV['EVENT_PROF_TOP']
|
13
|
+
opts[:per_example] = true if ENV['EVENT_PROF_EXAMPLES']
|
14
|
+
opts[:fdoc] = true if ENV['FDOC']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.plugin_test_prof_options(opts, options)
|
20
|
+
opts.on "--event-prof=EVENT", "Collects metrics for specified EVENT" do |event|
|
21
|
+
options[:event] = event
|
22
|
+
end
|
23
|
+
opts.on "--event-prof-rank-by=RANK_BY", "Defines RANK_BY parameter for results" do |rank|
|
24
|
+
options[:rank_by] = rank.to_sym
|
25
|
+
end
|
26
|
+
opts.on "--event-prof-top-count=N", "Limits results with N groups/examples" do |count|
|
27
|
+
options[:top_count] = count.to_i
|
28
|
+
end
|
29
|
+
opts.on "--event-prof-per-example", TrueClass, "Includes examples metrics to results" do |flag|
|
30
|
+
options[:per_example] = flag
|
31
|
+
end
|
32
|
+
opts.on "--factory-doctor", TrueClass, 'Enable Factory Doctor for your examples' do |flag|
|
33
|
+
options[:fdoc] = flag
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.plugin_test_prof_init(options)
|
38
|
+
options = TestProf.configure_options(options)
|
39
|
+
|
40
|
+
reporter << TestProf::EventProfReporter.new(options[:io], options) if options[:event]
|
41
|
+
reporter << TestProf::FactoryDoctorReporter.new(options[:io], options) if options[:fdoc]
|
42
|
+
end
|
43
|
+
end
|
data/lib/test_prof.rb
CHANGED
@@ -9,7 +9,7 @@ require "test_prof/utils"
|
|
9
9
|
#
|
10
10
|
# Contains tools to anylyze factories usage, integrate with Ruby profilers,
|
11
11
|
# profile your examples using ActiveSupport notifications (if any) and
|
12
|
-
# statically analyze your code with custom
|
12
|
+
# statically analyze your code with custom RuboCop cops.
|
13
13
|
#
|
14
14
|
# Example usage:
|
15
15
|
#
|
@@ -47,11 +47,11 @@ module TestProf
|
|
47
47
|
|
48
48
|
# Require gem and shows a custom
|
49
49
|
# message if it fails to load
|
50
|
-
def require(gem_name, msg)
|
50
|
+
def require(gem_name, msg = nil)
|
51
51
|
Kernel.require gem_name
|
52
52
|
block_given? ? yield : true
|
53
53
|
rescue LoadError
|
54
|
-
log
|
54
|
+
log(:error, msg) if msg
|
55
55
|
false
|
56
56
|
end
|
57
57
|
|
@@ -1,90 +1,72 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
require '
|
3
|
+
require 'minitest/base_reporter'
|
4
|
+
require 'minitest/event_prof_formatter'
|
5
5
|
|
6
6
|
module Minitest
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@current_group = nil
|
17
|
-
@current_example = nil
|
18
|
-
inject_to_minitest_reporters if defined? Minitest::Reporters
|
19
|
-
end
|
20
|
-
|
21
|
-
def start; end
|
22
|
-
|
23
|
-
def prerecord(group, example)
|
24
|
-
change_current_group(group, example) unless @current_group
|
25
|
-
track_current_example(group, example)
|
26
|
-
end
|
27
|
-
|
28
|
-
def before_test(test)
|
29
|
-
prerecord(test.class, test.name)
|
30
|
-
end
|
31
|
-
|
32
|
-
def record(*)
|
33
|
-
@profiler.example_finished(@current_example)
|
34
|
-
end
|
7
|
+
module TestProf
|
8
|
+
class EventProfReporter < BaseReporter # :nodoc:
|
9
|
+
def initialize(io = $stdout, options = {})
|
10
|
+
super
|
11
|
+
@profiler = configure_profiler(options)
|
12
|
+
@formatter = EventProfFormatter.new(@profiler)
|
13
|
+
@current_group = nil
|
14
|
+
@current_example = nil
|
15
|
+
end
|
35
16
|
|
36
|
-
|
17
|
+
def prerecord(group, example)
|
18
|
+
change_current_group(group, example) unless @current_group
|
19
|
+
track_current_example(group, example)
|
20
|
+
end
|
37
21
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
puts "\n"
|
42
|
-
log :info, result
|
43
|
-
end
|
22
|
+
def before_test(test)
|
23
|
+
prerecord(test.class, test.name)
|
24
|
+
end
|
44
25
|
|
45
|
-
|
26
|
+
def record(*)
|
27
|
+
@profiler.example_finished(@current_example)
|
28
|
+
end
|
46
29
|
|
47
|
-
|
48
|
-
unless @current_group[:name] == group.name
|
30
|
+
def report
|
49
31
|
@profiler.group_finished(@current_group)
|
50
|
-
|
32
|
+
result = @formatter.prepare_results
|
33
|
+
log :info, result
|
51
34
|
end
|
52
35
|
|
53
|
-
|
54
|
-
name: example.gsub(/^test_(?:\d+_)?/, ''),
|
55
|
-
location: File.expand_path(location(group, example).join(':')).gsub(Dir.getwd, '.')
|
56
|
-
}
|
36
|
+
private
|
57
37
|
|
58
|
-
|
59
|
-
|
38
|
+
def track_current_example(group, example)
|
39
|
+
unless @current_group[:name] == group.name
|
40
|
+
@profiler.group_finished(@current_group)
|
41
|
+
change_current_group(group, example)
|
42
|
+
end
|
60
43
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
}
|
44
|
+
@current_example = {
|
45
|
+
name: example.gsub(/^test_(?:\d+_)?/, ''),
|
46
|
+
location: location_with_line_number(group, example)
|
47
|
+
}
|
66
48
|
|
67
|
-
|
68
|
-
|
49
|
+
@profiler.example_started(@current_example)
|
50
|
+
end
|
69
51
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
52
|
+
def change_current_group(group, example)
|
53
|
+
@current_group = {
|
54
|
+
name: group.name,
|
55
|
+
location: location_without_line_number(group, example)
|
56
|
+
}
|
75
57
|
|
76
|
-
|
77
|
-
TestProf::EventProf.configure do |config|
|
78
|
-
config.event = options[:event]
|
79
|
-
config.rank_by = options[:rank_by] if options[:rank_by]
|
80
|
-
config.top_count = options[:top_count] if options[:top_count]
|
81
|
-
config.per_example = options[:per_example] if options[:per_example]
|
58
|
+
@profiler.group_started(@current_group)
|
82
59
|
end
|
83
|
-
TestProf::EventProf.build
|
84
|
-
end
|
85
60
|
|
86
|
-
|
87
|
-
|
61
|
+
def configure_profiler(options)
|
62
|
+
::TestProf::EventProf.configure do |config|
|
63
|
+
config.event = options[:event]
|
64
|
+
config.rank_by = options[:rank_by] if options[:rank_by]
|
65
|
+
config.top_count = options[:top_count] if options[:top_count]
|
66
|
+
config.per_example = options[:per_example] if options[:per_example]
|
67
|
+
end
|
68
|
+
::TestProf::EventProf.build
|
69
|
+
end
|
88
70
|
end
|
89
71
|
end
|
90
72
|
end
|
@@ -16,7 +16,9 @@ module TestProf
|
|
16
16
|
example_group_started
|
17
17
|
example_group_finished
|
18
18
|
example_started
|
19
|
-
|
19
|
+
example_failed
|
20
|
+
example_passed
|
21
|
+
example_pending
|
20
22
|
].freeze
|
21
23
|
|
22
24
|
def initialize
|
@@ -41,6 +43,11 @@ module TestProf
|
|
41
43
|
@profiler.example_finished notification.example
|
42
44
|
end
|
43
45
|
|
46
|
+
# NOTE: RSpec < 3.4.0 doesn't have example_finished event
|
47
|
+
alias example_passed example_finished
|
48
|
+
alias example_failed example_finished
|
49
|
+
alias example_pending example_finished
|
50
|
+
|
44
51
|
def print
|
45
52
|
result = @profiler.results
|
46
53
|
|
@@ -134,8 +134,4 @@ module TestProf
|
|
134
134
|
end
|
135
135
|
|
136
136
|
require "test_prof/factory_doctor/rspec" if defined?(RSpec::Core)
|
137
|
-
require "test_prof/factory_doctor/minitest" if defined?(Minitest
|
138
|
-
|
139
|
-
TestProf.activate('FDOC') do
|
140
|
-
TestProf::FactoryDoctor.init
|
141
|
-
end
|
137
|
+
require "test_prof/factory_doctor/minitest" if defined?(Minitest)
|
@@ -1,3 +1,86 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'minitest/base_reporter'
|
4
|
+
require 'minitest/fd_ignorable'
|
5
|
+
require 'test_prof/factory_doctor'
|
6
|
+
require 'test_prof/ext/float_duration'
|
7
|
+
require 'test_prof/ext/string_strip_heredoc'
|
8
|
+
|
9
|
+
module Minitest
|
10
|
+
module TestProf
|
11
|
+
class FactoryDoctorReporter < BaseReporter # :nodoc:
|
12
|
+
using ::TestProf::FloatDuration
|
13
|
+
using ::TestProf::StringStripHeredoc
|
14
|
+
|
15
|
+
SUCCESS_MESSAGE = 'FactoryDoctor says: "Looks good to me!"'.freeze
|
16
|
+
|
17
|
+
def initialize(io = $stdout, options = {})
|
18
|
+
super
|
19
|
+
::TestProf::FactoryDoctor.init
|
20
|
+
@count = 0
|
21
|
+
@time = 0.0
|
22
|
+
@example_groups = Hash.new { |h, k| h[k] = [] }
|
23
|
+
end
|
24
|
+
|
25
|
+
def prerecord(_group, _example)
|
26
|
+
::TestProf::FactoryDoctor.start
|
27
|
+
end
|
28
|
+
|
29
|
+
def record(example)
|
30
|
+
::TestProf::FactoryDoctor.stop
|
31
|
+
return if example.skipped? || example.fd_ignore?
|
32
|
+
|
33
|
+
result = ::TestProf::FactoryDoctor.result
|
34
|
+
return unless result.bad?
|
35
|
+
|
36
|
+
group = {
|
37
|
+
description: example.class.name,
|
38
|
+
location: location_without_line_number(example)
|
39
|
+
}
|
40
|
+
|
41
|
+
@example_groups[group] << {
|
42
|
+
description: example.name.gsub(/^test_(?:\d+_)?/, ''),
|
43
|
+
location: location_with_line_number(example),
|
44
|
+
factories: result.count,
|
45
|
+
time: result.time
|
46
|
+
}
|
47
|
+
|
48
|
+
@count += 1
|
49
|
+
@time += result.time
|
50
|
+
end
|
51
|
+
|
52
|
+
def report
|
53
|
+
return log(:info, SUCCESS_MESSAGE) if @example_groups.empty?
|
54
|
+
|
55
|
+
msgs = []
|
56
|
+
|
57
|
+
msgs <<
|
58
|
+
<<-MSG.strip_heredoc
|
59
|
+
FactoryDoctor report
|
60
|
+
|
61
|
+
Total (potentially) bad examples: #{@count}
|
62
|
+
Total wasted time: #{@time.duration}
|
63
|
+
|
64
|
+
MSG
|
65
|
+
|
66
|
+
@example_groups.each do |group, examples|
|
67
|
+
msgs << "#{group[:description]} (#{group[:location]})\n"
|
68
|
+
examples.each do |ex|
|
69
|
+
msgs << " #{ex[:description]} (#{ex[:location]}) "\
|
70
|
+
"– #{pluralize_records(ex[:factories])} created, "\
|
71
|
+
"#{ex[:time].duration}\n"
|
72
|
+
end
|
73
|
+
msgs << "\n"
|
74
|
+
end
|
75
|
+
|
76
|
+
log :info, msgs.join
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def pluralize_records(count)
|
82
|
+
count == 1 ? '1 record' : "#{count} records"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -14,7 +14,9 @@ module TestProf
|
|
14
14
|
|
15
15
|
NOTIFICATIONS = %i[
|
16
16
|
example_started
|
17
|
-
|
17
|
+
example_passed
|
18
|
+
example_failed
|
19
|
+
example_pending
|
18
20
|
].freeze
|
19
21
|
|
20
22
|
def initialize
|
@@ -45,6 +47,11 @@ module TestProf
|
|
45
47
|
@time += result.time
|
46
48
|
end
|
47
49
|
|
50
|
+
# NOTE: RSpec < 3.4.0 doesn't have example_finished event
|
51
|
+
alias example_passed example_finished
|
52
|
+
alias example_failed example_finished
|
53
|
+
alias example_pending example_finished
|
54
|
+
|
48
55
|
def print
|
49
56
|
return log(:info, SUCCESS_MESSAGE) if @example_groups.empty?
|
50
57
|
|
@@ -118,6 +125,8 @@ end
|
|
118
125
|
|
119
126
|
# Register FactoryDoctor listener
|
120
127
|
TestProf.activate('FDOC') do
|
128
|
+
TestProf::FactoryDoctor.init
|
129
|
+
|
121
130
|
RSpec.configure do |config|
|
122
131
|
listener = TestProf::FactoryDoctor::RSpecListener.new
|
123
132
|
|
@@ -10,9 +10,9 @@ module TestProf
|
|
10
10
|
class Fabrication
|
11
11
|
# Monkey-patch Fabrication
|
12
12
|
def self.patch
|
13
|
-
TestProf.require 'fabrication'
|
14
|
-
|
15
|
-
|
13
|
+
TestProf.require 'fabrication' do
|
14
|
+
::Fabricate.singleton_class.prepend(FabricationPatch)
|
15
|
+
end
|
16
16
|
end
|
17
17
|
|
18
18
|
def self.track(factory, &block)
|
@@ -10,8 +10,9 @@ module TestProf
|
|
10
10
|
class FactoryGirl
|
11
11
|
# Monkey-patch FactoryGirl
|
12
12
|
def self.patch
|
13
|
-
|
14
|
-
|
13
|
+
TestProf.require 'factory_girl' do
|
14
|
+
::FactoryGirl::FactoryRunner.prepend(FactoryGirlPatch)
|
15
|
+
end
|
15
16
|
end
|
16
17
|
|
17
18
|
def self.track(strategy, factory, &block)
|
data/lib/test_prof/logging.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "test_prof"
|
3
4
|
require_relative "./before_all"
|
4
5
|
|
5
6
|
module TestProf
|
@@ -13,6 +14,11 @@ module TestProf
|
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
17
|
+
# Only works with RSpec 3.2.0
|
18
|
+
def supported?
|
19
|
+
TestProf::Utils.verify_gem_version('rspec', at_least: '3.2.0')
|
20
|
+
end
|
21
|
+
|
16
22
|
private
|
17
23
|
|
18
24
|
def modules
|
@@ -25,6 +31,11 @@ module TestProf
|
|
25
31
|
PREFIX = RUBY_ENGINE == 'jruby' ? "@__jruby_is_not_cat_friendly__".freeze : "@😸".freeze
|
26
32
|
|
27
33
|
def let_it_be(identifier, **options, &block)
|
34
|
+
unless LetItBe.supported?
|
35
|
+
TestProf.log :warn, "let_it_be requires RSpec >= 3.2.0. Fallback to let!"
|
36
|
+
return let!(identifier, &block)
|
37
|
+
end
|
38
|
+
|
28
39
|
initializer = proc do
|
29
40
|
instance_variable_set(:"#{PREFIX}#{identifier}", instance_exec(&block))
|
30
41
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module TestProf
|
4
4
|
# Add ability to run only a specified number of example groups (randomly selected)
|
5
|
-
module
|
5
|
+
module RSpecSample
|
6
6
|
def ordered_example_groups
|
7
7
|
@example_groups = @example_groups.sample(ENV['SAMPLE'].to_i) unless ENV['SAMPLE'].nil?
|
8
8
|
super
|
@@ -10,4 +10,4 @@ module TestProf
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
RSpec::Core::World.prepend(TestProf::
|
13
|
+
RSpec::Core::World.prepend(TestProf::RSpecSample)
|
@@ -62,8 +62,11 @@ module TestProf
|
|
62
62
|
|
63
63
|
def init
|
64
64
|
RSpec::Core::Example.prepend(ExampleInstrumentation)
|
65
|
-
|
66
|
-
|
65
|
+
|
66
|
+
if memoization_available?
|
67
|
+
RSpec::Core::MemoizedHelpers::ThreadsafeMemoized.prepend(MemoizedInstrumentation)
|
68
|
+
RSpec::Core::MemoizedHelpers::NonThreadSafeMemoized.prepend(MemoizedInstrumentation)
|
69
|
+
end
|
67
70
|
|
68
71
|
@data = {}
|
69
72
|
|
@@ -92,6 +95,11 @@ module TestProf
|
|
92
95
|
end
|
93
96
|
end
|
94
97
|
|
98
|
+
# Whether we are able to track `let` usage
|
99
|
+
def memoization_available?
|
100
|
+
defined?(::RSpec::Core::MemoizedHelpers::ThreadsafeMemoized)
|
101
|
+
end
|
102
|
+
|
95
103
|
METRICS.each do |type|
|
96
104
|
define_method("#{type}_time") do
|
97
105
|
@data[type.to_s]
|
@@ -7,6 +7,7 @@ require "test_prof/ext/string_strip_heredoc"
|
|
7
7
|
|
8
8
|
module TestProf
|
9
9
|
module RSpecDissect
|
10
|
+
# rubocop:disable Metrics/ClassLength
|
10
11
|
class Listener # :nodoc:
|
11
12
|
include Logging
|
12
13
|
using FloatDuration
|
@@ -16,6 +17,9 @@ module TestProf
|
|
16
17
|
NOTIFICATIONS = %i[
|
17
18
|
example_finished
|
18
19
|
example_group_finished
|
20
|
+
example_passed
|
21
|
+
example_failed
|
22
|
+
example_pending
|
19
23
|
].freeze
|
20
24
|
|
21
25
|
def initialize
|
@@ -35,6 +39,11 @@ module TestProf
|
|
35
39
|
@examples_time += notification.example.execution_result.run_time
|
36
40
|
end
|
37
41
|
|
42
|
+
# NOTE: RSpec < 3.4.0 doesn't have example_finished event
|
43
|
+
alias example_passed example_finished
|
44
|
+
alias example_failed example_finished
|
45
|
+
alias example_pending example_finished
|
46
|
+
|
38
47
|
def example_group_finished(notification)
|
39
48
|
return unless notification.group.top_level?
|
40
49
|
|
@@ -65,10 +74,15 @@ module TestProf
|
|
65
74
|
|
66
75
|
Total time: #{@total_examples_time.duration}
|
67
76
|
Total `before(:each)` time: #{RSpecDissect.total_before_time.duration}
|
68
|
-
Total `let` time: #{RSpecDissect.total_memo_time.duration}
|
69
|
-
|
70
77
|
MSG
|
71
78
|
|
79
|
+
msgs <<
|
80
|
+
if RSpecDissect.memoization_available?
|
81
|
+
"Total `let` time: #{RSpecDissect.total_memo_time.duration}\n\n"
|
82
|
+
else
|
83
|
+
"Total `let` time: NOT SUPPORTED (requires RSpec >= 3.3.0)\n\n"
|
84
|
+
end
|
85
|
+
|
72
86
|
msgs <<
|
73
87
|
<<-MSG.strip_heredoc
|
74
88
|
Top #{top_count} slowest suites (by `before(:each)` time):
|
@@ -82,18 +96,20 @@ module TestProf
|
|
82
96
|
GROUP
|
83
97
|
end
|
84
98
|
|
85
|
-
|
86
|
-
|
99
|
+
if RSpecDissect.memoization_available?
|
100
|
+
msgs <<
|
101
|
+
<<-MSG.strip_heredoc
|
87
102
|
|
88
|
-
|
103
|
+
Top #{top_count} slowest suites (by `let` time):
|
89
104
|
|
90
|
-
|
105
|
+
MSG
|
91
106
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
107
|
+
@memo_results.each do |group|
|
108
|
+
msgs <<
|
109
|
+
<<-GROUP.strip_heredoc
|
110
|
+
#{group[:desc].truncate} (#{group[:loc]}) – #{group[:memo].duration} of #{group[:total].duration} (#{group[:count]})
|
111
|
+
GROUP
|
112
|
+
end
|
97
113
|
end
|
98
114
|
|
99
115
|
log :info, msgs.join
|
@@ -12,7 +12,8 @@ module TestProf
|
|
12
12
|
|
13
13
|
NOTIFICATIONS = %i[
|
14
14
|
example_started
|
15
|
-
|
15
|
+
example_failed
|
16
|
+
example_passed
|
16
17
|
].freeze
|
17
18
|
|
18
19
|
def initialize
|
@@ -27,14 +28,16 @@ module TestProf
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def example_finished(notification)
|
30
|
-
return if notification.example.pending?
|
31
|
-
|
32
31
|
tag = notification.example.metadata.fetch(@tag, :__unknown__)
|
33
32
|
|
34
33
|
@tags[tag][:count] += 1
|
35
34
|
@tags[tag][:time] += (TestProf.now - @ts)
|
36
35
|
end
|
37
36
|
|
37
|
+
# NOTE: RSpec < 3.4.0 doesn't have example_finished event
|
38
|
+
alias example_passed example_finished
|
39
|
+
alias example_failed example_finished
|
40
|
+
|
38
41
|
def print
|
39
42
|
msgs = []
|
40
43
|
|
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.4.
|
4
|
+
version: 0.4.1
|
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-10-
|
11
|
+
date: 2017-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -83,7 +83,7 @@ dependencies:
|
|
83
83
|
description: "\n Ruby applications tests profiling tools.\n\n Contains tools
|
84
84
|
to analyze factories usage, integrate with Ruby profilers,\n profile your examples
|
85
85
|
using ActiveSupport notifications (if any) and\n statically analyze your code
|
86
|
-
with custom
|
86
|
+
with custom RuboCop cops.\n "
|
87
87
|
email:
|
88
88
|
- dementiev.vm@gmail.com
|
89
89
|
executables: []
|
@@ -116,7 +116,10 @@ files:
|
|
116
116
|
- guides/stack_prof.md
|
117
117
|
- guides/tag_prof.md
|
118
118
|
- guides/tests_sampling.md
|
119
|
-
- lib/minitest/
|
119
|
+
- lib/minitest/base_reporter.rb
|
120
|
+
- lib/minitest/event_prof_formatter.rb
|
121
|
+
- lib/minitest/fd_ignorable.rb
|
122
|
+
- lib/minitest/test_prof_plugin.rb
|
120
123
|
- lib/test-prof.rb
|
121
124
|
- lib/test_prof.rb
|
122
125
|
- lib/test_prof/any_fixture.rb
|
@@ -126,7 +129,6 @@ files:
|
|
126
129
|
- lib/test_prof/event_prof/custom_events/factory_create.rb
|
127
130
|
- lib/test_prof/event_prof/custom_events/sidekiq_inline.rb
|
128
131
|
- lib/test_prof/event_prof/custom_events/sidekiq_jobs.rb
|
129
|
-
- lib/test_prof/event_prof/formatters/minitest.rb
|
130
132
|
- lib/test_prof/event_prof/instrumentations/active_support.rb
|
131
133
|
- lib/test_prof/event_prof/minitest.rb
|
132
134
|
- lib/test_prof/event_prof/rspec.rb
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'test_prof/event_prof/minitest'
|
4
|
-
|
5
|
-
module Minitest # :nodoc:
|
6
|
-
def self.plugin_event_prof_options(opts, options)
|
7
|
-
opts.on "--event-prof=EVENT", "Collects metrics for specified EVENT" do |event|
|
8
|
-
options[:event] = event
|
9
|
-
end
|
10
|
-
opts.on "--event-prof-rank-by=RANK_BY", "Defines RANK_BY parameter for results" do |rank|
|
11
|
-
options[:rank_by] = rank.to_sym
|
12
|
-
end
|
13
|
-
opts.on "--event-prof-top-count=N", "Limits results with N groups/examples" do |count|
|
14
|
-
options[:top_count] = count.to_i
|
15
|
-
end
|
16
|
-
opts.on "--event-prof-per-example", TrueClass, "Includes examples metrics to results" do |flag|
|
17
|
-
options[:per_example] = flag
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.plugin_event_prof_init(options)
|
22
|
-
options[:event] = ENV['EVENT_PROF'] if ENV['EVENT_PROF']
|
23
|
-
options[:rank_by] = ENV['EVENT_PROF_RANK'].to_sym if ENV['EVENT_PROF_RANK']
|
24
|
-
options[:top_count] = ENV['EVENT_PROF_TOP'].to_i if ENV['EVENT_PROF_TOP']
|
25
|
-
options[:per_example] = true if ENV['EVENT_PROF_EXAMPLES']
|
26
|
-
|
27
|
-
return unless options[:event]
|
28
|
-
reporter << EventProfReporter.new(options[:io], options)
|
29
|
-
end
|
30
|
-
end
|