test-prof 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|