test-prof 0.7.2 → 0.7.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8c7d0976d2d56b7989f91253c752561a10d01ce12ba20b6a4fca377ea424c69a
4
- data.tar.gz: a41df011c54a846f949238cc89ab9a021f5d86281f1f18566bcb5963782397e9
3
+ metadata.gz: 6b330384292e5a4bcb8429d4e2d1e215a320692e526b53cdddfb8714b43538b6
4
+ data.tar.gz: 0a22281b9a7acc4dde3552be07a143420bd63648a380f6b2e5cd446cdcc36b54
5
5
  SHA512:
6
- metadata.gz: da313236dd4e1ab8078bac1267835b85303ba81129a5d469db198fd9fc65fae1f4decc0b0ad894878810bda3460feb4a84b76db637c4b1d1580457d48ea5e994
7
- data.tar.gz: ee2e2c89b3eef006a17362d18987bdc31f5269804814c890ccb2597517f0a67cee1ab1c80c6a05b6268c87930d6c56fc7f624d0427cf6764e2d11956efb16cce
6
+ metadata.gz: d4d008f6cfe0055fff132d6c31f440bbd9974e0294480498d76a57b1f39a49ea51a1145632d901de54bc3a18f6e4d6c317e565ddfca090ad4d905ec9ec7e7e5a
7
+ data.tar.gz: 001a18ff90644936cacff9e5a00858671e070ffe4b11a10921f4401ad29fb12b4de2d190fc92c7fbc3bb137e42a2eaa2a55945bf1b7c1fd88bcb87c2ded00f13
data/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.7.3 (2018-11-07)
6
+
7
+ - Add a header with the general information on factories usage [#99](https://github.com/palkan/test-prof/issues/99) ([@szemek][])
8
+
9
+ - Improve test sampling.([@mkldon][])
10
+
11
+ ```bash
12
+ SAMPLE=10 rake test # runs 10 random test examples
13
+ SAMPLE_GROUPS=10 rake test # runs 10 random example groups
14
+ ```
15
+
16
+ - Extend Event Prof formatter to include the absolute run time and the percentage of the event tim [#100](https://github.com/palkan/test-prof/issues/100) ([@dmagro][])
17
+
5
18
  ## 0.7.2 (2018-10-08)
6
19
 
7
20
  - Add `RSpec/AggregateFailures` support for non-regular 'its' examples. ([@broels][])
@@ -368,3 +381,6 @@ Ensure output dir exists in `#artifact_path` method.
368
381
  [@desoleary]: https://github.com/desoleary
369
382
  [@rabotyaga]: https://github.com/rabotyaga
370
383
  [@Vasfed]: https://github.com/Vasfed
384
+ [@szemek]: https://github.com/szemek
385
+ [@mkldon]: https://github.com/mkldon
386
+ [@dmagro]: https://github.com/dmagro
@@ -26,11 +26,13 @@ module Minitest
26
26
  private
27
27
 
28
28
  def total_results(profiler)
29
+ time_percentage = time_percentage(profiler.total_time, profiler.absolute_run_time)
30
+
29
31
  @results <<
30
32
  <<~MSG
31
33
  EventProf results for #{profiler.event}
32
34
 
33
- Total time: #{profiler.total_time.duration}
35
+ Total time: #{profiler.total_time.duration} of #{profiler.absolute_run_time.duration} (#{time_percentage}%)
34
36
  Total events: #{profiler.total_count}
35
37
 
36
38
  Top #{profiler.top_count} slowest suites (by #{profiler.rank_by}):
@@ -39,31 +41,47 @@ module Minitest
39
41
  end
40
42
 
41
43
  def by_groups(profiler)
42
- profiler.results[:groups].each do |group|
44
+ result = profiler.results
45
+ groups = result[:groups]
46
+
47
+ groups.each do |group|
43
48
  description = group[:id][:name]
44
49
  location = group[:id][:location]
50
+ time = group[:time]
51
+ run_time = group[:run_time]
52
+ time_percentage = time_percentage(time, run_time)
45
53
 
46
54
  @results <<
47
55
  <<~GROUP
48
- #{description.truncate} (#{location}) – #{group[:time].duration} (#{group[:count]} / #{group[:examples]})
56
+ #{description.truncate} (#{location}) – #{time.duration} (#{group[:count]} / #{group[:examples]}) of #{run_time.duration} (#{time_percentage}%)
49
57
  GROUP
50
58
  end
51
59
  end
52
60
 
53
61
  def by_examples(profiler)
54
- return unless profiler.results[:examples]
62
+ result = profiler.results
63
+ examples = result[:examples]
64
+
65
+ return unless examples
55
66
  @results << "\nTop #{profiler.top_count} slowest tests (by #{profiler.rank_by}):\n\n"
56
67
 
57
- profiler.results[:examples].each do |example|
68
+ examples.each do |example|
58
69
  description = example[:id][:name]
59
70
  location = example[:id][:location]
71
+ time = example[:time]
72
+ run_time = example[:run_time]
73
+ time_percentage = time_percentage(time, run_time)
60
74
 
61
75
  @results <<
62
76
  <<~GROUP
63
- #{description.truncate} (#{location}) – #{example[:time].duration} (#{example[:count]})
77
+ #{description.truncate} (#{location}) – #{time.duration} (#{example[:count]}) of #{run_time.duration} (#{time_percentage}%)
64
78
  GROUP
65
79
  end
66
80
  end
81
+
82
+ def time_percentage(time, total_time)
83
+ (time / total_time * 100).round(2)
84
+ end
67
85
  end
68
86
  end
69
87
  end
@@ -5,7 +5,7 @@ require "test_prof/ext/float_duration"
5
5
  module TestProf
6
6
  # Make DB fixtures from blocks.
7
7
  module AnyFixture
8
- INSERT_RXP = /^INSERT INTO ([\S]+)/
8
+ INSERT_RXP = /^INSERT INTO ([\S]+)/.freeze
9
9
 
10
10
  using FloatDuration
11
11
 
@@ -4,7 +4,7 @@ module TestProf
4
4
  module EventProf
5
5
  class Profiler # :nodoc:
6
6
  attr_reader :event, :total_count, :total_time, :rank_by, :top_count, :per_example,
7
- :time, :count, :example_time, :example_count
7
+ :time, :count, :example_time, :example_count, :absolute_run_time
8
8
 
9
9
  alias per_example? per_example
10
10
 
@@ -26,6 +26,7 @@ module TestProf
26
26
 
27
27
  @total_count = 0
28
28
  @total_time = 0.0
29
+ @absolute_run_time = 0.0
29
30
  end
30
31
 
31
32
  def track(time)
@@ -48,7 +49,16 @@ module TestProf
48
49
  end
49
50
 
50
51
  def group_finished(id)
51
- data = { id: id, time: @time, count: @count, examples: @total_examples }
52
+ group_run_time = take_time(@group_ts)
53
+ @absolute_run_time += group_run_time
54
+
55
+ data = {
56
+ id: id,
57
+ time: @time,
58
+ run_time: group_run_time,
59
+ count: @count,
60
+ examples: @total_examples
61
+ }
52
62
 
53
63
  @groups << data unless data[rank_by].zero?
54
64
 
@@ -65,19 +75,31 @@ module TestProf
65
75
  @total_examples += 1
66
76
  return unless per_example?
67
77
 
68
- data = { id: id, time: @example_time, count: @example_count }
78
+ example_run_time = take_time(@example_ts)
79
+ data = {
80
+ id: id,
81
+ time: @example_time,
82
+ run_time: example_run_time,
83
+ count: @example_count
84
+ }
85
+
69
86
  @examples << data unless data[rank_by].zero?
70
87
  @current_example = nil
71
88
  end
72
89
 
73
90
  def results
74
- {
91
+ results = {
75
92
  groups: @groups.to_a
76
93
  }.tap do |data|
77
94
  next unless per_example?
78
95
 
79
96
  data[:examples] = @examples.to_a
80
97
  end
98
+ results
99
+ end
100
+
101
+ def take_time(start_ts)
102
+ TestProf.now - start_ts
81
103
  end
82
104
 
83
105
  private
@@ -86,11 +108,13 @@ module TestProf
86
108
  @time = 0.0
87
109
  @count = 0
88
110
  @total_examples = 0
111
+ @group_ts = TestProf.now
89
112
  end
90
113
 
91
114
  def reset_example!
92
115
  @example_count = 0
93
116
  @example_time = 0.0
117
+ @example_ts = TestProf.now
94
118
  end
95
119
  end
96
120
 
@@ -54,6 +54,7 @@ module TestProf
54
54
 
55
55
  def report(profiler)
56
56
  result = profiler.results
57
+ time_percentage = time_percentage(profiler.total_time, profiler.absolute_run_time)
57
58
 
58
59
  msgs = []
59
60
 
@@ -61,7 +62,7 @@ module TestProf
61
62
  <<~MSG
62
63
  EventProf results for #{profiler.event}
63
64
 
64
- Total time: #{profiler.total_time.duration}
65
+ Total time: #{profiler.total_time.duration} of #{profiler.absolute_run_time.duration} (#{time_percentage}%)
65
66
  Total events: #{profiler.total_count}
66
67
 
67
68
  Top #{profiler.top_count} slowest suites (by #{profiler.rank_by}):
@@ -71,10 +72,13 @@ module TestProf
71
72
  result[:groups].each do |group|
72
73
  description = group[:id].top_level_description
73
74
  location = group[:id].metadata[:location]
75
+ time = group[:time]
76
+ run_time = group[:run_time]
77
+ time_percentage = time_percentage(time, run_time)
74
78
 
75
79
  msgs <<
76
80
  <<~GROUP
77
- #{description.truncate} (#{location}) – #{group[:time].duration} (#{group[:count]} / #{group[:examples]})
81
+ #{description.truncate} (#{location}) – #{time.duration} (#{group[:count]} / #{group[:examples]}) of #{run_time.duration} (#{time_percentage}%)
78
82
  GROUP
79
83
  end
80
84
 
@@ -84,9 +88,13 @@ module TestProf
84
88
  result[:examples].each do |example|
85
89
  description = example[:id].description
86
90
  location = example[:id].metadata[:location]
91
+ time = example[:time]
92
+ run_time = example[:run_time]
93
+ time_percentage = time_percentage(time, run_time)
94
+
87
95
  msgs <<
88
96
  <<~GROUP
89
- #{description.truncate} (#{location}) – #{example[:time].duration} (#{example[:count]})
97
+ #{description.truncate} (#{location}) – #{time.duration} (#{example[:count]}) of #{run_time.duration} (#{time_percentage}%)
90
98
  GROUP
91
99
  end
92
100
  end
@@ -128,6 +136,10 @@ module TestProf
128
136
 
129
137
  log :info, msgs.join
130
138
  end
139
+
140
+ def time_percentage(time, total_time)
141
+ (time / total_time * 100).round(2)
142
+ end
131
143
  end
132
144
  end
133
145
  end
@@ -35,7 +35,7 @@ module TestProf
35
35
  \AROLLBACK|
36
36
  \ARELEASE|
37
37
  \ASAVEPOINT
38
- )}xi
38
+ )}xi.freeze
39
39
 
40
40
  class << self
41
41
  include TestProf::Logging
@@ -10,10 +10,18 @@ module TestProf::FactoryProf
10
10
  return log(:info, "No factories detected") if result.raw_stats == {}
11
11
  msgs = []
12
12
 
13
+ total = result.stats.sum { |stat| stat[:total] }
14
+ total_top_level = result.stats.sum { |stat| stat[:top_level] }
15
+ total_uniq_factories = result.stats.map { |stat| stat[:name] }.uniq.count
16
+
13
17
  msgs <<
14
18
  <<~MSG
15
19
  Factories usage
16
20
 
21
+ Total: #{total}
22
+ Total top-level: #{total_top_level}
23
+ Total uniq factories: #{total_uniq_factories}
24
+
17
25
  total top-level name
18
26
  MSG
19
27
 
@@ -10,16 +10,39 @@ module TestProf
10
10
  Minitest::Spec
11
11
  ].freeze
12
12
 
13
- def run(*)
14
- unless ENV['SAMPLE'].nil?
15
- sample_size = ENV['SAMPLE'].to_i
13
+ class << self
14
+ def suites
16
15
  # Make sure that sample contains only _real_ suites
17
- runnables = Minitest::Runnable.runnables
18
- .sample(sample_size + CORE_RUNNABLES.size)
19
- .reject { |suite| CORE_RUNNABLES.include?(suite) }
20
- .take(sample_size)
16
+ Minitest::Runnable.runnables
17
+ .reject { |suite| CORE_RUNNABLES.include?(suite) }
18
+ end
19
+
20
+ def sample_groups(sample_size)
21
+ saved_suites = suites
21
22
  Minitest::Runnable.reset
22
- runnables.each { |r| Minitest::Runnable.runnables << r }
23
+ saved_suites.sample(sample_size).each { |r| Minitest::Runnable.runnables << r }
24
+ end
25
+
26
+ def sample_examples(sample_size)
27
+ all_examples = suites.flat_map do |runnable|
28
+ runnable.runnable_methods.map { |method| [runnable, method] }
29
+ end
30
+ sample = all_examples.sample(sample_size)
31
+ # Filter examples by overriding #runnable_methods for all suites
32
+ suites.each do |runnable|
33
+ runnable.define_singleton_method(:runnable_methods) do
34
+ super() & sample.select { |ex| ex.first.equal?(runnable) }.map(&:last)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ # Overrides Minitest.run
41
+ def run(*)
42
+ if ENV['SAMPLE']
43
+ MinitestSample.sample_examples(ENV['SAMPLE'].to_i)
44
+ elsif ENV['SAMPLE_GROUPS']
45
+ MinitestSample.sample_groups(ENV['SAMPLE_GROUPS'].to_i)
23
46
  end
24
47
  super
25
48
  end
@@ -1,13 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TestProf
4
- # Add ability to run only a specified number of example groups (randomly selected)
5
- module RSpecSample
6
- def ordered_example_groups
7
- @example_groups = @example_groups.sample(ENV['SAMPLE'].to_i) unless ENV['SAMPLE'].nil?
8
- super
4
+ # Instance variable writer for RSpec::Core::World
5
+ module RSpecWorldSamplePatch
6
+ def filtered_examples=(val)
7
+ @filtered_examples = val
9
8
  end
10
9
  end
11
10
  end
12
11
 
13
- RSpec::Core::World.prepend(TestProf::RSpecSample)
12
+ if ENV["SAMPLE"]
13
+ RSpec::Core::World.include(TestProf::RSpecWorldSamplePatch)
14
+
15
+ RSpec.configure do |config|
16
+ config.before(:suite) do
17
+ filtered_examples = RSpec.world.filtered_examples.values.flatten
18
+ sample = filtered_examples.sample(ENV["SAMPLE"].to_i)
19
+ RSpec.world.filtered_examples = Hash.new do |hash, group|
20
+ hash[group] = group.examples & sample
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ if ENV["SAMPLE_GROUPS"]
27
+ RSpec::Core::World.include(TestProf::RSpecWorldSamplePatch)
28
+
29
+ RSpec.configure do |config|
30
+ config.before(:suite) do
31
+ filtered_groups = RSpec.world.filtered_examples.reject do |_group, examples|
32
+ examples.empty?
33
+ end.keys
34
+ sample = filtered_groups.sample(ENV["SAMPLE_GROUPS"].to_i)
35
+ RSpec.world.filtered_examples = Hash.new do |hash, group|
36
+ hash[group] = sample.include?(group) ? group.examples : []
37
+ end
38
+ end
39
+ end
40
+ end
@@ -6,7 +6,7 @@ require "test_prof/rspec_stamp/parser"
6
6
  module TestProf
7
7
  # Mark RSpec examples with provided tags
8
8
  module RSpecStamp
9
- EXAMPLE_RXP = /(\s*)(\w+\s*(?:.*)\s*)(do|{)/
9
+ EXAMPLE_RXP = /(\s*)(\w+\s*(?:.*)\s*)(do|{)/.freeze
10
10
 
11
11
  # RSpecStamp configuration
12
12
  class Configuration
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TestProf
4
- VERSION = "0.7.2"
4
+ VERSION = "0.7.3"
5
5
  end
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.7.2
4
+ version: 0.7.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-08 00:00:00.000000000 Z
11
+ date: 2018-11-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.59.0
75
+ version: 0.60.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 0.59.0
82
+ version: 0.60.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rubocop-md
85
85
  requirement: !ruby/object:Gem::Requirement