rspec_log_formatter 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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