rspec_log_formatter 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +5 -13
  2. data/.gitignore +1 -1
  3. data/.ruby-version +1 -1
  4. data/README.md +53 -7
  5. data/bin/rspec_log_formatter +30 -0
  6. data/dummy/.gitignore +16 -0
  7. data/dummy/.rspec +1 -0
  8. data/dummy/Gemfile +47 -0
  9. data/dummy/README.rdoc +28 -0
  10. data/dummy/Rakefile +6 -0
  11. data/dummy/app/assets/images/.keep +0 -0
  12. data/dummy/app/assets/javascripts/application.js +16 -0
  13. data/dummy/app/assets/stylesheets/application.css +13 -0
  14. data/dummy/app/controllers/application_controller.rb +5 -0
  15. data/dummy/app/controllers/concerns/.keep +0 -0
  16. data/dummy/app/helpers/application_helper.rb +2 -0
  17. data/dummy/app/mailers/.keep +0 -0
  18. data/dummy/app/models/.keep +0 -0
  19. data/dummy/app/models/concerns/.keep +0 -0
  20. data/dummy/app/views/layouts/application.html.erb +14 -0
  21. data/dummy/bin/bundle +3 -0
  22. data/dummy/bin/rails +4 -0
  23. data/dummy/bin/rake +4 -0
  24. data/dummy/config/application.rb +23 -0
  25. data/dummy/config/boot.rb +4 -0
  26. data/dummy/config/database.yml +25 -0
  27. data/dummy/config/environment.rb +5 -0
  28. data/dummy/config/environments/development.rb +29 -0
  29. data/dummy/config/environments/production.rb +80 -0
  30. data/dummy/config/environments/test.rb +36 -0
  31. data/dummy/config/initializers/backtrace_silencers.rb +7 -0
  32. data/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  33. data/dummy/config/initializers/inflections.rb +16 -0
  34. data/dummy/config/initializers/mime_types.rb +5 -0
  35. data/dummy/config/initializers/secret_token.rb +12 -0
  36. data/dummy/config/initializers/session_store.rb +3 -0
  37. data/dummy/config/initializers/wrap_parameters.rb +14 -0
  38. data/dummy/config/locales/en.yml +23 -0
  39. data/dummy/config/routes.rb +56 -0
  40. data/dummy/config.ru +4 -0
  41. data/dummy/db/schema.rb +16 -0
  42. data/dummy/db/seeds.rb +7 -0
  43. data/dummy/lib/assets/.keep +0 -0
  44. data/dummy/lib/tasks/.keep +0 -0
  45. data/dummy/log/.keep +0 -0
  46. data/dummy/public/404.html +58 -0
  47. data/dummy/public/422.html +58 -0
  48. data/dummy/public/500.html +57 -0
  49. data/dummy/public/favicon.ico +0 -0
  50. data/dummy/public/robots.txt +5 -0
  51. data/dummy/spec/dummy_spec.rb +7 -0
  52. data/dummy/spec/spec_helper.rb +46 -0
  53. data/dummy/test/controllers/.keep +0 -0
  54. data/dummy/test/fixtures/.keep +0 -0
  55. data/dummy/test/helpers/.keep +0 -0
  56. data/dummy/test/integration/.keep +0 -0
  57. data/dummy/test/mailers/.keep +0 -0
  58. data/dummy/test/models/.keep +0 -0
  59. data/dummy/test/test_helper.rb +15 -0
  60. data/dummy/vendor/assets/javascripts/.keep +0 -0
  61. data/dummy/vendor/assets/stylesheets/.keep +0 -0
  62. data/lib/rspec_log_formatter/analysis/analyzer.rb +15 -50
  63. data/lib/rspec_log_formatter/analysis/pretty_printer.rb +10 -6
  64. data/lib/rspec_log_formatter/analysis/result.rb +8 -4
  65. data/lib/rspec_log_formatter/analysis/score.rb +42 -6
  66. data/lib/rspec_log_formatter/analyzer_formatter.rb +18 -6
  67. data/lib/rspec_log_formatter/formatter.rb +19 -12
  68. data/lib/rspec_log_formatter/history_manager.rb +46 -0
  69. data/lib/rspec_log_formatter/javascripts/chartkick.js +678 -0
  70. data/lib/rspec_log_formatter/performance_analyzer.rb +40 -0
  71. data/lib/rspec_log_formatter/templates/charts.html.erb +11 -0
  72. data/lib/rspec_log_formatter/version.rb +1 -1
  73. data/lib/rspec_log_formatter.rb +2 -1
  74. data/rspec_log_formatter.gemspec +1 -0
  75. data/spec/fixtures/test_slowing_down_over_time.history +7 -0
  76. data/spec/fixtures/varying_flakiness.history +9 -9
  77. data/spec/lib/rspec_log_analyzer/analysis/analyzer_spec.rb +25 -21
  78. data/spec/lib/rspec_log_analyzer/analysis/pretty_printer_spec.rb +10 -8
  79. data/spec/lib/rspec_log_analyzer/analyzer_formatter_spec.rb +3 -3
  80. data/spec/lib/rspec_log_analyzer/formatter_spec.rb +16 -18
  81. data/spec/lib/rspec_log_analyzer/history_manager_spec.rb +16 -0
  82. data/spec/lib/rspec_log_analyzer/performance_analyzer_spec.rb +16 -0
  83. data/specs.sh +4 -0
  84. metadata +95 -12
@@ -0,0 +1,56 @@
1
+ Dummy::Application.routes.draw do
2
+ # The priority is based upon order of creation: first created -> highest priority.
3
+ # See how all your routes lay out with "rake routes".
4
+
5
+ # You can have the root of your site routed with "root"
6
+ # root 'welcome#index'
7
+
8
+ # Example of regular route:
9
+ # get 'products/:id' => 'catalog#view'
10
+
11
+ # Example of named route that can be invoked with purchase_url(id: product.id)
12
+ # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
13
+
14
+ # Example resource route (maps HTTP verbs to controller actions automatically):
15
+ # resources :products
16
+
17
+ # Example resource route with options:
18
+ # resources :products do
19
+ # member do
20
+ # get 'short'
21
+ # post 'toggle'
22
+ # end
23
+ #
24
+ # collection do
25
+ # get 'sold'
26
+ # end
27
+ # end
28
+
29
+ # Example resource route with sub-resources:
30
+ # resources :products do
31
+ # resources :comments, :sales
32
+ # resource :seller
33
+ # end
34
+
35
+ # Example resource route with more complex sub-resources:
36
+ # resources :products do
37
+ # resources :comments
38
+ # resources :sales do
39
+ # get 'recent', on: :collection
40
+ # end
41
+ # end
42
+
43
+ # Example resource route with concerns:
44
+ # concern :toggleable do
45
+ # post 'toggle'
46
+ # end
47
+ # resources :posts, concerns: :toggleable
48
+ # resources :photos, concerns: :toggleable
49
+
50
+ # Example resource route within a namespace:
51
+ # namespace :admin do
52
+ # # Directs /admin/products/* to Admin::ProductsController
53
+ # # (app/controllers/admin/products_controller.rb)
54
+ # resources :products
55
+ # end
56
+ end
data/dummy/config.ru ADDED
@@ -0,0 +1,4 @@
1
+ # This file is used by Rack-based servers to start the application.
2
+
3
+ require ::File.expand_path('../config/environment', __FILE__)
4
+ run Rails.application
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+ # This file is auto-generated from the current state of the database. Instead
3
+ # of editing this file, please use the migrations feature of Active Record to
4
+ # incrementally modify your database, and then regenerate this schema definition.
5
+ #
6
+ # Note that this schema.rb definition is the authoritative source for your
7
+ # database schema. If you need to create the application database on another
8
+ # system, you should be using db:schema:load, not running all the migrations
9
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
11
+ #
12
+ # It's strongly recommended that you check this file into your version control system.
13
+
14
+ ActiveRecord::Schema.define(version: 0) do
15
+
16
+ end
data/dummy/db/seeds.rb ADDED
@@ -0,0 +1,7 @@
1
+ # This file should contain all the record creation needed to seed the database with its default values.
2
+ # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3
+ #
4
+ # Examples:
5
+ #
6
+ # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
7
+ # Mayor.create(name: 'Emanuel', city: cities.first)
File without changes
File without changes
data/dummy/log/.keep ADDED
File without changes
@@ -0,0 +1,58 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <style>
6
+ body {
7
+ background-color: #EFEFEF;
8
+ color: #2E2F30;
9
+ text-align: center;
10
+ font-family: arial, sans-serif;
11
+ }
12
+
13
+ div.dialog {
14
+ width: 25em;
15
+ margin: 4em auto 0 auto;
16
+ border: 1px solid #CCC;
17
+ border-right-color: #999;
18
+ border-left-color: #999;
19
+ border-bottom-color: #BBB;
20
+ border-top: #B00100 solid 4px;
21
+ border-top-left-radius: 9px;
22
+ border-top-right-radius: 9px;
23
+ background-color: white;
24
+ padding: 7px 4em 0 4em;
25
+ }
26
+
27
+ h1 {
28
+ font-size: 100%;
29
+ color: #730E15;
30
+ line-height: 1.5em;
31
+ }
32
+
33
+ body > p {
34
+ width: 33em;
35
+ margin: 0 auto 1em;
36
+ padding: 1em 0;
37
+ background-color: #F7F7F7;
38
+ border: 1px solid #CCC;
39
+ border-right-color: #999;
40
+ border-bottom-color: #999;
41
+ border-bottom-left-radius: 4px;
42
+ border-bottom-right-radius: 4px;
43
+ border-top-color: #DADADA;
44
+ color: #666;
45
+ box-shadow:0 3px 8px rgba(50, 50, 50, 0.17);
46
+ }
47
+ </style>
48
+ </head>
49
+
50
+ <body>
51
+ <!-- This file lives in public/404.html -->
52
+ <div class="dialog">
53
+ <h1>The page you were looking for doesn't exist.</h1>
54
+ <p>You may have mistyped the address or the page may have moved.</p>
55
+ </div>
56
+ <p>If you are the application owner check the logs for more information.</p>
57
+ </body>
58
+ </html>
@@ -0,0 +1,58 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <style>
6
+ body {
7
+ background-color: #EFEFEF;
8
+ color: #2E2F30;
9
+ text-align: center;
10
+ font-family: arial, sans-serif;
11
+ }
12
+
13
+ div.dialog {
14
+ width: 25em;
15
+ margin: 4em auto 0 auto;
16
+ border: 1px solid #CCC;
17
+ border-right-color: #999;
18
+ border-left-color: #999;
19
+ border-bottom-color: #BBB;
20
+ border-top: #B00100 solid 4px;
21
+ border-top-left-radius: 9px;
22
+ border-top-right-radius: 9px;
23
+ background-color: white;
24
+ padding: 7px 4em 0 4em;
25
+ }
26
+
27
+ h1 {
28
+ font-size: 100%;
29
+ color: #730E15;
30
+ line-height: 1.5em;
31
+ }
32
+
33
+ body > p {
34
+ width: 33em;
35
+ margin: 0 auto 1em;
36
+ padding: 1em 0;
37
+ background-color: #F7F7F7;
38
+ border: 1px solid #CCC;
39
+ border-right-color: #999;
40
+ border-bottom-color: #999;
41
+ border-bottom-left-radius: 4px;
42
+ border-bottom-right-radius: 4px;
43
+ border-top-color: #DADADA;
44
+ color: #666;
45
+ box-shadow:0 3px 8px rgba(50, 50, 50, 0.17);
46
+ }
47
+ </style>
48
+ </head>
49
+
50
+ <body>
51
+ <!-- This file lives in public/422.html -->
52
+ <div class="dialog">
53
+ <h1>The change you wanted was rejected.</h1>
54
+ <p>Maybe you tried to change something you didn't have access to.</p>
55
+ </div>
56
+ <p>If you are the application owner check the logs for more information.</p>
57
+ </body>
58
+ </html>
@@ -0,0 +1,57 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <style>
6
+ body {
7
+ background-color: #EFEFEF;
8
+ color: #2E2F30;
9
+ text-align: center;
10
+ font-family: arial, sans-serif;
11
+ }
12
+
13
+ div.dialog {
14
+ width: 25em;
15
+ margin: 4em auto 0 auto;
16
+ border: 1px solid #CCC;
17
+ border-right-color: #999;
18
+ border-left-color: #999;
19
+ border-bottom-color: #BBB;
20
+ border-top: #B00100 solid 4px;
21
+ border-top-left-radius: 9px;
22
+ border-top-right-radius: 9px;
23
+ background-color: white;
24
+ padding: 7px 4em 0 4em;
25
+ }
26
+
27
+ h1 {
28
+ font-size: 100%;
29
+ color: #730E15;
30
+ line-height: 1.5em;
31
+ }
32
+
33
+ body > p {
34
+ width: 33em;
35
+ margin: 0 auto 1em;
36
+ padding: 1em 0;
37
+ background-color: #F7F7F7;
38
+ border: 1px solid #CCC;
39
+ border-right-color: #999;
40
+ border-bottom-color: #999;
41
+ border-bottom-left-radius: 4px;
42
+ border-bottom-right-radius: 4px;
43
+ border-top-color: #DADADA;
44
+ color: #666;
45
+ box-shadow:0 3px 8px rgba(50, 50, 50, 0.17);
46
+ }
47
+ </style>
48
+ </head>
49
+
50
+ <body>
51
+ <!-- This file lives in public/500.html -->
52
+ <div class="dialog">
53
+ <h1>We're sorry, but something went wrong.</h1>
54
+ </div>
55
+ <p>If you are the application owner check the logs for more information.</p>
56
+ </body>
57
+ </html>
File without changes
@@ -0,0 +1,5 @@
1
+ # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
2
+ #
3
+ # To ban all spiders from the entire site uncomment the next two lines:
4
+ # User-agent: *
5
+ # Disallow: /
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Math" do
4
+ it "works" do
5
+ 1.should == 1
6
+ end
7
+ end
@@ -0,0 +1,46 @@
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ ENV["RAILS_ENV"] ||= 'test'
3
+ require File.expand_path("../../config/environment", __FILE__)
4
+ require 'rspec/rails'
5
+ require 'rspec/autorun'
6
+
7
+ # Requires supporting ruby files with custom matchers and macros, etc,
8
+ # in spec/support/ and its subdirectories.
9
+ Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
10
+ # Checks for pending migrations before tests are run.
11
+ # If you are not using ActiveRecord, you can remove this line.
12
+ ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
13
+
14
+ RSpec.configure do |config|
15
+ # ## Mock Framework
16
+ #
17
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
18
+ #
19
+ # config.mock_with :mocha
20
+ # config.mock_with :flexmock
21
+ # config.mock_with :rr
22
+
23
+
24
+ config.formatters << RspecLogFormatter::Formatter::Factory.new.build
25
+ config.add_formatter(:progress)
26
+ config.formatters << RspecLogFormatter::AnalyzerFormatter::Factory.new.build($stdout)
27
+
28
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
29
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
30
+
31
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
32
+ # examples within a transaction, remove the following line or assign false
33
+ # instead of true.
34
+ config.use_transactional_fixtures = true
35
+
36
+ # If true, the base class of anonymous controllers will be inferred
37
+ # automatically. This will be the default behavior in future versions of
38
+ # rspec-rails.
39
+ config.infer_base_class_for_anonymous_controllers = false
40
+
41
+ # Run specs in random order to surface order dependencies. If you find an
42
+ # order dependency and want to debug it, you can fix the order by providing
43
+ # the seed, which is printed after each run.
44
+ # --seed 1234
45
+ config.order = "random"
46
+ end
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,15 @@
1
+ ENV["RAILS_ENV"] ||= "test"
2
+ require File.expand_path('../../config/environment', __FILE__)
3
+ require 'rails/test_help'
4
+
5
+ class ActiveSupport::TestCase
6
+ ActiveRecord::Migration.check_pending!
7
+
8
+ # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
9
+ #
10
+ # Note: You'll currently still have to declare fixtures explicitly in integration tests
11
+ # -- they do not yet inherit this setting
12
+ fixtures :all
13
+
14
+ # Add more helper methods to be used by all tests here...
15
+ end
File without changes
File without changes
@@ -1,22 +1,29 @@
1
- require 'csv'
2
1
 
3
2
  module RspecLogFormatter
4
3
  module Analysis
5
4
  class Analyzer
6
- def analyze(filepath, options = {})
7
- window = options[:last_builds]
8
5
 
9
- build_numbers, results = result_numbers(filepath, options = {})
6
+ def initialize(history_provider, options={})
7
+ @builds_to_analyze = options[:builds_to_analyze]
8
+ @max_reruns = options[:max_reruns]
9
+ @limit_history = options[:limit_history]
10
+ @history_provider = history_provider
11
+ end
12
+
13
+ def analyze
14
+ build_numbers = @history_provider.builds
15
+ results = @history_provider.results.reject do |res|
16
+ @builds_to_analyze && !build_numbers.last(@builds_to_analyze).include?(res.build_number.to_i)
17
+ end
10
18
 
11
19
  scores = []
12
20
  results.group_by(&:description).each do |description, results|
13
- score = Score.new(description)
21
+ score = Score.new(description, max_reruns: @max_reruns)
14
22
 
15
23
  results.group_by(&:build_number).each do |build_number, results|
16
- next if (window && !build_numbers.last(window).include?(build_number))
17
24
  next if results.all?(&:failure?) #not flaky
18
25
 
19
-
26
+ results.each{|r| score.absorb(r) }
20
27
  score.runs += results.count
21
28
  score.failures += results.count(&:failure?)
22
29
  score.failure_messages += results.select(&:failure?).map { |r| "#{r.klass}\n #{r.message}" }
@@ -24,50 +31,8 @@ module RspecLogFormatter
24
31
  scores << score if score.runs > 0
25
32
  end
26
33
 
27
- scores.sort.map(&:as_hash)
34
+ scores.select(&:flaky?).sort.map(&:as_hash)
28
35
  end
29
-
30
- def truncate(filepath, opts = {})
31
- builds = opts.fetch(:keep_builds)
32
- build_numbers, results = result_numbers(filepath, options = {})
33
- sio = StringIO.new
34
-
35
- File.open(filepath, 'r').each_line do |line|
36
- result = parse_line(line)
37
- next unless build_numbers.last(builds).include? result.build_number
38
- sio.puts line
39
- end
40
-
41
- sio.rewind
42
- File.open(filepath, 'w') do |f|
43
- f.write sio.read
44
- end
45
- end
46
-
47
- private
48
-
49
- def parse_line(line)
50
- Result.new(*CSV.parse_line(line, col_sep: "\t").first(7))
51
- end
52
-
53
- def each_result(filepath, &block)
54
- File.open(filepath, 'r').each_line do |line|
55
- result = parse_line(line)
56
- block.call(result)
57
- end
58
- end
59
-
60
- def result_numbers(filepath, options = {})
61
- build_numbers = []
62
- results = []
63
- each_result(filepath) do |result|
64
- build_numbers << result.build_number
65
- results << result
66
- end
67
- [build_numbers.uniq.sort, results]
68
- end
69
-
70
36
  end
71
-
72
37
  end
73
38
  end
@@ -7,20 +7,24 @@ module RspecLogFormatter
7
7
 
8
8
  def to_s
9
9
  results = @results.reject do |result|
10
- result[:percent] == 0.0
10
+ result[:fraction] == 0.0
11
11
  end.first(10)
12
12
 
13
13
  header = if results.empty?
14
- "None of the specs were flaky"
15
- else
16
- "Top #{results.size} flakiest examples\n"
17
- end
14
+ "None of the specs were flaky"
15
+ else
16
+ "Top #{results.size} flakiest examples\n"
17
+ end
18
18
  header + results.each_with_index.map do |result, i|
19
- title = " #{i+1}) #{result[:description]} -- #{result[:percent]}%"
19
+ title = " #{i+1}) #{result[:description]} -- #{(100.0*result[:fraction]).to_i}%#{cost_segment(result)}"
20
20
  failure_messages = result[:failure_messages].map { |fm| " * #{fm}" }.join("\n")
21
21
  title + "\n" + failure_messages
22
22
  end.join("\n")
23
23
  end
24
+
25
+ def cost_segment(result)
26
+ " (cost: #{result[:cost].to_i}s)" if result[:cost]
27
+ end
24
28
  end
25
29
  end
26
30
  end
@@ -1,17 +1,21 @@
1
+ require 'time'
2
+
1
3
  module RspecLogFormatter
2
4
  module Analysis
3
5
  class Result
4
- def initialize(build_number, time, outcome, description, spec_path, message=nil, klass=nil)
5
- @build_number = build_number
6
+ def initialize(build_number, time, outcome, description, spec_path, message=nil, klass=nil,duration=nil)
7
+ @time = Time.parse(time)
8
+ @build_number = (build_number || -1).to_i
6
9
  @description = description
7
10
  @outcome = outcome
8
11
  @spec_path = spec_path
9
12
  @message = message
10
13
  @klass = klass
14
+ @duration = duration.to_f
11
15
  end
12
16
 
13
- attr_accessor :build_number, :description
14
- attr_reader :message, :klass
17
+ attr_accessor :build_number, :description, :duration
18
+ attr_reader :message, :klass, :time
15
19
 
16
20
  def failure?
17
21
  @outcome == "failed"
@@ -1,29 +1,65 @@
1
1
  module RspecLogFormatter
2
2
  module Analysis
3
3
  class Score
4
- def initialize(desc)
4
+ def initialize(desc, opts={})
5
5
  @description = desc
6
6
  @runs = 0
7
7
  @failures = 0
8
8
  @failure_messages = []
9
+ @last_fail_time = Time.at(0)
10
+ @last_pass_time = Time.at(0)
11
+ @max_reruns = opts[:max_reruns]
9
12
  end
10
13
 
11
- def percent
12
- 100 * @failures.to_f/@runs
14
+ def fraction
15
+ @failures.to_f/@runs
16
+ end
17
+
18
+ def cost
19
+ sum = 0.0
20
+ 0.upto(max_reruns) do |i|
21
+ sum += (fraction**i)*(1.0-fraction)*(i*@fail_duration + @pass_duration)
22
+ end
23
+ sum + (fraction**(max_reruns+1.0))*@fail_duration
13
24
  end
14
25
 
15
26
  def <=>(other)
16
- other.percent <=> percent
27
+ if max_reruns
28
+ other.cost <=> cost
29
+ else
30
+ other.fraction <=> fraction
31
+ end
32
+ end
33
+
34
+ def flaky?
35
+ fraction > 0.0
36
+ end
37
+
38
+ def absorb(result)
39
+ if result.failure? && result.time > @last_fail_time
40
+ @last_fail_time = result.time
41
+ @fail_duration = result.duration
42
+ elsif result.success? && result.time > @last_pass_time
43
+ @last_pass_time = result.time
44
+ @pass_duration = result.duration
45
+ end
17
46
  end
18
47
 
19
48
  def as_hash
20
- {
49
+ h = {
21
50
  description: @description,
22
- percent: percent,
51
+ fraction: fraction,
23
52
  failure_messages: failure_messages,
24
53
  }
54
+ if max_reruns
55
+ h.merge!({cost: cost})
56
+ end
57
+ h
25
58
  end
26
59
  attr_accessor :runs, :failures, :failure_messages
60
+
61
+ private
62
+ attr_reader :max_reruns
27
63
  end
28
64
  end
29
65
  end
@@ -2,19 +2,31 @@ require "csv"
2
2
  require "rspec/core/formatters/base_formatter"
3
3
 
4
4
  module RspecLogFormatter
5
- class AnalyzerFormatter < RSpec::Core::Formatters::BaseFormatter
5
+ class AnalyzerFormatter
6
6
  FILENAME = "rspec.history"
7
7
 
8
- def initialize(*args)
9
- super
8
+ class Factory
9
+ def initialize(options={})
10
+ @options = options
11
+ end
12
+
13
+ def build(output)
14
+ RspecLogFormatter::AnalyzerFormatter.new(output, {
15
+ builds_to_analyze: nil,
16
+ max_reruns: nil
17
+ }.merge(@options))
18
+ end
10
19
  end
11
20
 
21
+ def initialize(output, options={})
22
+ @output = output
23
+ @options = options
24
+ end
12
25
 
13
26
  def dump_summary(_,_,_,_)
14
- output.puts RspecLogFormatter::Analysis::PrettyPrinter.new(
15
- RspecLogFormatter::Analysis::Analyzer.new.analyze(FILENAME)
27
+ @output.puts RspecLogFormatter::Analysis::PrettyPrinter.new(
28
+ RspecLogFormatter::Analysis::Analyzer.new(HistoryManager.new(FILENAME), @options).analyze
16
29
  )
17
30
  end
18
-
19
31
  end
20
32
  end
@@ -2,20 +2,27 @@ require "csv"
2
2
  require "rspec/core/formatters/base_formatter"
3
3
 
4
4
  module RspecLogFormatter
5
- class Formatter < RSpec::Core::Formatters::BaseFormatter
6
- FILENAME = "rspec.history"
5
+ class Formatter
7
6
 
8
- class Maker
9
- def new(_output)
10
- RspecLogFormatter::Formatter.new(clock, opts)
7
+ class Factory
8
+ def initialize(options={})
9
+ @options = options
10
+ end
11
+
12
+ def build
13
+ RspecLogFormatter::Formatter.new({
14
+ clock: Time,
15
+ build_number: ENV["BUILD_NUMBER"],
16
+ limit_history: nil
17
+ }.merge(@options))
11
18
  end
12
19
  end
13
- Factory = Maker.new
14
20
 
15
- def initialize(clock=nil, opts={})
16
- @clock = clock || Time
17
- @build_number = opts[:build_number] || ENV["BUILD_NUMBER"]
18
- @keep_builds = opts[:keep_builds]
21
+ def initialize(opts={})
22
+ @clock = opts[:clock]
23
+ @build_number = opts[:build_number]
24
+ @limit_history = opts[:limit_history]
25
+ @history_manager = RspecLogFormatter::HistoryManager.new(FILENAME)
19
26
  end
20
27
 
21
28
  def example_started(example)
@@ -31,8 +38,8 @@ module RspecLogFormatter
31
38
  end
32
39
 
33
40
  def dump_summary(_,_,_,_)
34
- return unless @keep_builds
35
- RspecLogFormatter::Analysis::Analyzer.new.truncate(FILENAME, keep_builds: @keep_builds)
41
+ return unless @limit_history
42
+ @history_manager.truncate(@limit_history)
36
43
  end
37
44
 
38
45
  private