coverband 4.2.1 → 4.2.2

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -9
  3. data/CODE_OF_CONDUCT.md +76 -0
  4. data/README.md +32 -28
  5. data/changes.md +31 -11
  6. data/coverband.gemspec +0 -5
  7. data/lib/coverband/adapters/base.rb +8 -13
  8. data/lib/coverband/adapters/file_store.rb +10 -4
  9. data/lib/coverband/adapters/hash_redis_store.rb +182 -0
  10. data/lib/coverband/adapters/redis_store.rb +15 -1
  11. data/lib/coverband/collectors/coverage.rb +5 -7
  12. data/lib/coverband/collectors/view_tracker.rb +124 -0
  13. data/lib/coverband/configuration.rb +21 -1
  14. data/lib/coverband/integrations/background.rb +2 -1
  15. data/lib/coverband/reporters/base.rb +4 -5
  16. data/lib/coverband/reporters/console_report.rb +1 -0
  17. data/lib/coverband/reporters/web.rb +6 -0
  18. data/lib/coverband/utils/absolute_file_converter.rb +47 -0
  19. data/lib/coverband/utils/file_hasher.rb +16 -0
  20. data/lib/coverband/utils/html_formatter.rb +12 -0
  21. data/lib/coverband/utils/railtie.rb +15 -3
  22. data/lib/coverband/utils/relative_file_converter.rb +41 -0
  23. data/lib/coverband/utils/s3_report.rb +2 -2
  24. data/lib/coverband/utils/source_file.rb +1 -3
  25. data/lib/coverband/utils/tasks.rb +1 -0
  26. data/lib/coverband/version.rb +5 -1
  27. data/lib/coverband.rb +7 -1
  28. data/public/application.css +6 -6
  29. data/test/benchmarks/benchmark.rake +25 -49
  30. data/test/benchmarks/init_rails.rake +10 -0
  31. data/test/coverband/adapters/hash_redis_store_test.rb +190 -0
  32. data/test/coverband/adapters/redis_store_test.rb +90 -88
  33. data/test/coverband/collectors/coverage_test.rb +6 -2
  34. data/test/coverband/collectors/view_tracker_test.rb +77 -0
  35. data/test/coverband/integrations/resque_worker_test.rb +2 -3
  36. data/test/coverband/reporters/base_test.rb +1 -78
  37. data/test/coverband/reporters/console_test.rb +1 -4
  38. data/test/coverband/reporters/html_test.rb +9 -9
  39. data/test/coverband/utils/absolute_file_converter_test.rb +56 -0
  40. data/test/coverband/utils/file_hasher_test.rb +29 -0
  41. data/test/coverband/utils/relative_file_converter_test.rb +39 -0
  42. data/test/forked/rails_rake_full_stack_test.rb +9 -1
  43. data/test/integration/full_stack_test.rb +1 -2
  44. data/test/test_helper.rb +6 -7
  45. data/test/unique_files.rb +1 -1
  46. data/views/layout.erb +1 -20
  47. data/views/nav.erb +35 -0
  48. data/views/view_tracker.erb +37 -0
  49. metadata +22 -31
  50. data/lib/coverband/utils/file_path_helper.rb +0 -68
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ module Coverband
6
+ module Collectors
7
+ ###
8
+ # This class tracks view file usage via ActiveSupport::Notifications
9
+ #
10
+ # This is a port of Flatfoot, a project I open sourced years ago, but am now rolling into Coverband
11
+ # https://github.com/livingsocial/flatfoot
12
+ #
13
+ # TODO: test and ensure slim, haml, and other support
14
+ ###
15
+ class ViewTracker
16
+ DEFAULT_TARGET = Dir.glob('app/views/**/*.html.erb').reject { |file| file.match(/(_mailer)/) }
17
+ attr_accessor :target, :logged_views, :views_to_record
18
+ attr_reader :logger, :roots, :store, :ignore_patterns
19
+
20
+ def initialize(options = {})
21
+ raise NotImplementedError, 'View Tracker requires Rails 4 or greater' unless self.class.supported_version?
22
+
23
+ @project_directory = File.expand_path(Coverband.configuration.root)
24
+ @ignore_patterns = Coverband.configuration.ignore
25
+ @store = options.fetch(:store) { Coverband.configuration.store }
26
+ @logger = options.fetch(:logger) { Coverband.configuration.logger }
27
+ @target = options.fetch(:target) { DEFAULT_TARGET }
28
+
29
+ @roots = options.fetch(:roots) { Coverband.configuration.all_root_patterns }
30
+ @roots = @roots.split(',') if @roots.is_a?(String)
31
+
32
+ @logged_views = []
33
+ @views_to_record = []
34
+ end
35
+
36
+ ###
37
+ # This method is called on every render call, so we try to reduce method calls
38
+ # and ensure high performance
39
+ ###
40
+ def track_views(_name, _start, _finish, _id, payload)
41
+ if (file = payload[:identifier])
42
+ if newly_seen_file?(file)
43
+ logged_views << file
44
+ views_to_record << file if track_file?(file)
45
+ end
46
+ end
47
+
48
+ ###
49
+ # Annoyingly while you get full path for templates
50
+ # notifications only pass part of the path for layouts dropping any format info
51
+ # such as .html.erb or .js.erb
52
+ # http://edgeguides.rubyonrails.org/active_support_instrumentation.html#render_partial-action_view
53
+ ###
54
+ return unless (layout_file = payload[:layout])
55
+ return unless newly_seen_file?(layout_file)
56
+
57
+ logged_views << layout_file
58
+ views_to_record << layout_file if track_file?(layout_file, layout: true)
59
+ end
60
+
61
+ def used_views
62
+ views = redis_store.smembers(tracker_key)
63
+ normalized_views = []
64
+ views.each do |view|
65
+ roots.each do |root|
66
+ view = view.gsub(/#{root}/, '')
67
+ end
68
+ normalized_views << view
69
+ end
70
+ normalized_views
71
+ end
72
+
73
+ def unused_views
74
+ recently_used_views = used_views
75
+ all_views = target.reject { |view| recently_used_views.include?(view) }
76
+ # since layouts don't include format we count them used if they match with ANY formats
77
+ all_views.reject { |view| view.match(/\/layouts\//) && recently_used_views.any? { |used_view| view.include?(used_view) } }
78
+ end
79
+
80
+ def reset_recordings
81
+ redis_store.del(tracker_key)
82
+ end
83
+
84
+ def self.supported_version?
85
+ defined?(Rails) && defined?(Rails::VERSION) && Rails::VERSION::STRING.split('.').first.to_i >= 4
86
+ end
87
+
88
+ def report_views_tracked
89
+ views_to_record.each do |file|
90
+ redis_store.sadd(tracker_key, file)
91
+ end
92
+ self.views_to_record = []
93
+ rescue StandardError => e
94
+ # we don't want to raise errors if Coverband can't reach redis.
95
+ # This is a nice to have not a bring the system down
96
+ logger&.error "Coverband: view_tracker failed to store, error #{e.class.name}"
97
+ end
98
+
99
+ protected
100
+
101
+ def newly_seen_file?(file)
102
+ return false if logged_views.include?(file)
103
+
104
+ true
105
+ end
106
+
107
+ def track_file?(file, options = {})
108
+ @ignore_patterns.none? do |pattern|
109
+ file.include?(pattern)
110
+ end && (file.start_with?(@project_directory) || options[:layout])
111
+ end
112
+
113
+ private
114
+
115
+ def redis_store
116
+ store.raw_store
117
+ end
118
+
119
+ def tracker_key
120
+ 'render_tracker'
121
+ end
122
+ end
123
+ end
124
+ end
@@ -8,7 +8,7 @@ module Coverband
8
8
  :background_reporting_enabled,
9
9
  :background_reporting_sleep_seconds, :test_env,
10
10
  :web_enable_clear, :gem_details, :web_debug, :report_on_exit,
11
- :simulate_oneshot_lines_coverage
11
+ :simulate_oneshot_lines_coverage, :track_views, :view_tracker
12
12
 
13
13
  attr_writer :logger, :s3_region, :s3_bucket, :s3_access_key_id, :s3_secret_access_key, :password
14
14
  attr_reader :track_gems, :ignore, :use_oneshot_lines_coverage
@@ -30,6 +30,10 @@ module Coverband
30
30
  # heroku asset compilation
31
31
  IGNORE_DEFAULTS = %w[vendor .erb$ .slim$ /tmp internal:prelude schema.rb]
32
32
 
33
+ # Add in missing files which were never loaded
34
+ # we need to know what all paths to check for unloaded files
35
+ TRACKED_DEFAULT_PATHS = %w[app lib config]
36
+
33
37
  def initialize
34
38
  reset
35
39
  end
@@ -38,6 +42,7 @@ module Coverband
38
42
  @root = Dir.pwd
39
43
  @root_paths = []
40
44
  @ignore = IGNORE_DEFAULTS.dup
45
+ @seach_paths = TRACKED_DEFAULT_PATHS.dup
41
46
  @additional_files = []
42
47
  @verbose = false
43
48
  @reporter = 'scov'
@@ -49,6 +54,7 @@ module Coverband
49
54
  @web_enable_clear = false
50
55
  @track_gems = false
51
56
  @gem_details = false
57
+ @track_views = false
52
58
  @groups = {}
53
59
  @web_debug = false
54
60
  @report_on_exit = true
@@ -106,6 +112,20 @@ module Coverband
106
112
  @store = store
107
113
  end
108
114
 
115
+ ###
116
+ # Search Paths
117
+ ###
118
+ def tracked_search_paths
119
+ "#{Coverband.configuration.current_root}/{#{@seach_paths.join(',')}}/**/*.{rb}"
120
+ end
121
+
122
+ ###
123
+ # Don't allow the to override defaults
124
+ ###
125
+ def search_paths=(path_array)
126
+ @seach_paths = (@seach_paths + path_array).uniq
127
+ end
128
+
109
129
  ###
110
130
  # Don't allow the ignore to override things like gem tracking
111
131
  ###
@@ -27,11 +27,12 @@ module Coverband
27
27
  @semaphore.synchronize do
28
28
  return if running?
29
29
 
30
- logger.debug('Coverband: Starting background reporting')
30
+ logger.debug('Coverband: Starting background reporting') if Coverband.configuration.verbose
31
31
  sleep_seconds = Coverband.configuration.background_reporting_sleep_seconds
32
32
  @thread = Thread.new do
33
33
  loop do
34
34
  Coverband.report_coverage
35
+ Coverband.configuration.view_tracker&.report_views_tracked
35
36
  if Coverband.configuration.verbose
36
37
  logger.debug("Coverband: background reporting coverage (#{Coverband.configuration.store.type}). Sleeping #{sleep_seconds}s")
37
38
  end
@@ -8,8 +8,6 @@ module Coverband
8
8
  ###
9
9
  class Base
10
10
  class << self
11
- include Coverband::Utils::FilePathHelper
12
-
13
11
  DATA_KEY = 'data'
14
12
 
15
13
  def report(store, _options = {})
@@ -17,6 +15,7 @@ module Coverband
17
15
  scov_style_report = get_current_scov_data_imp(store, all_roots)
18
16
 
19
17
  # These are extremelhy verbose but useful during coverband development, not generally for users
18
+ # Only avaiable by uncommenting this mode is never released
20
19
  # if Coverband.configuration.verbose
21
20
  # # msg = "report:\n #{scov_style_report.inspect}"
22
21
  # # Coverband.configuration.logger.debug msg
@@ -30,7 +29,7 @@ module Coverband
30
29
  ###
31
30
  def fix_reports(reports)
32
31
  # list all files, even if not tracked by Coverband (0% coverage)
33
- tracked_glob = "#{Coverband.configuration.current_root}/{app,lib,config}/**/*.{rb}"
32
+ tracked_glob = Coverband.configuration.tracked_search_paths
34
33
  filtered_report_files = {}
35
34
 
36
35
  reports.each_pair do |report_name, report_data|
@@ -50,13 +49,13 @@ module Coverband
50
49
  protected
51
50
 
52
51
  def fix_file_names(report_hash, roots)
53
- Coverband.configuration.logger.info "fixing root: #{roots.join(', ')}" if Coverband.configuration.verbose
52
+ Coverband.configuration.logger.debug "fixing root: #{roots.join(', ')}" if Coverband.configuration.verbose
54
53
 
55
54
  # normalize names across servers
56
55
  report_hash.each_with_object({}) do |(name, report), fixed_report|
57
56
  fixed_report[name] = {}
58
57
  report.each_pair do |key, vals|
59
- filename = relative_path_to_full(key, roots)
58
+ filename = Coverband::Utils::AbsoluteFileConverter.convert(key)
60
59
  fixed_report[name][filename] = if fixed_report[name].key?(filename) && fixed_report[name][filename][DATA_KEY] && vals[DATA_KEY]
61
60
  merged_data = merge_arrays(fixed_report[name][filename][DATA_KEY], vals[DATA_KEY])
62
61
  vals[DATA_KEY] = merged_data
@@ -10,6 +10,7 @@ module Coverband
10
10
  scov_style_report = super(store, options)
11
11
 
12
12
  scov_style_report.each_pair do |file, usage|
13
+ # TODO: In Coverband 5 deprecate none hash format
13
14
  if usage.is_a?(Hash)
14
15
  Coverband.configuration.logger.info "#{file}: #{usage['data']}"
15
16
  else
@@ -47,6 +47,8 @@ module Coverband
47
47
  @static.call(env)
48
48
  when %r{\/settings}
49
49
  [200, { 'Content-Type' => 'text/html' }, [settings]]
50
+ when %r{\/view_tracker}
51
+ [200, { 'Content-Type' => 'text/html' }, [view_tracker]]
50
52
  when %r{\/debug_data}
51
53
  [200, { 'Content-Type' => 'text/json' }, [debug_data]]
52
54
  when %r{\/load_file_details}
@@ -73,6 +75,10 @@ module Coverband
73
75
  Coverband::Utils::HTMLFormatter.new(nil, base_path: base_path).format_settings!
74
76
  end
75
77
 
78
+ def view_tracker
79
+ Coverband::Utils::HTMLFormatter.new(nil, base_path: base_path).format_view_tracker!
80
+ end
81
+
76
82
  def debug_data
77
83
  Coverband.configuration.store.get_coverage_report.to_json
78
84
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coverband
4
+ module Utils
5
+ class AbsoluteFileConverter
6
+ def initialize(roots)
7
+ @cache = {}
8
+ @roots = roots.map { |root| "#{File.expand_path(root)}/" }
9
+ end
10
+
11
+ def self.instance
12
+ @instance ||= new(Coverband.configuration.all_root_paths)
13
+ end
14
+
15
+ def self.reset
16
+ @instance = nil
17
+ end
18
+
19
+ def self.convert(relative_path)
20
+ instance.convert(relative_path)
21
+ end
22
+
23
+ def convert(relative_path)
24
+ @cache[relative_path] ||= begin
25
+ relative_filename = relative_path
26
+ local_filename = relative_filename
27
+ @roots.each do |root|
28
+ relative_filename = relative_filename.sub(/^#{root}/, './')
29
+ # once we have a relative path break out of the loop
30
+ break if relative_filename.start_with? './'
31
+ end
32
+ # the filename for our reports is expected to be a full path.
33
+ # roots.last should be roots << current_root}/
34
+ # a fully expanded path of config.root
35
+ # filename = filename.gsub('./', roots.last)
36
+ # above only works for app files
37
+ # we need to rethink some of this logic
38
+ # gems aren't at project root and can have multiple locations
39
+ local_root = @roots.find do |root|
40
+ File.exist?(relative_filename.gsub('./', root))
41
+ end
42
+ local_root ? relative_filename.gsub('./', local_root) : local_filename
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coverband
4
+ module Utils
5
+ class FileHasher
6
+ @cache = {}
7
+
8
+ def self.hash(file, path_converter: AbsoluteFileConverter.instance)
9
+ @cache[file] ||= begin
10
+ file = path_converter.convert(file)
11
+ Digest::MD5.file(file).hexdigest if File.exist?(file)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -33,6 +33,10 @@ module Coverband
33
33
  format_settings
34
34
  end
35
35
 
36
+ def format_view_tracker!
37
+ format_view_tracker
38
+ end
39
+
36
40
  def format_source_file!(filename)
37
41
  source_file = @coverage_result.file_from_path_with_type(filename)
38
42
 
@@ -49,6 +53,10 @@ module Coverband
49
53
  template('settings').result(binding)
50
54
  end
51
55
 
56
+ def format_view_tracker
57
+ template('view_tracker').result(binding)
58
+ end
59
+
52
60
  def format(result)
53
61
  Dir[File.join(File.dirname(__FILE__), '../../../public/*')].each do |path|
54
62
  FileUtils.cp_r(path, asset_output_path)
@@ -104,6 +112,10 @@ module Coverband
104
112
  button + '</form>'
105
113
  end
106
114
 
115
+ def display_nav(nav_options = {})
116
+ template('nav').result(binding)
117
+ end
118
+
107
119
  # Returns the html for the given source_file
108
120
  def formatted_source_file(result, source_file)
109
121
  template('source_file').result(binding)
@@ -5,9 +5,6 @@ module Coverband
5
5
  def eager_load!
6
6
  Coverband.eager_loading_coverage!
7
7
  super
8
- ensure
9
- Coverband.report_coverage
10
- Coverband.runtime_coverage!
11
8
  end
12
9
  end
13
10
  Rails::Engine.prepend(RailsEagerLoad)
@@ -15,6 +12,21 @@ module Coverband
15
12
  class Railtie < Rails::Railtie
16
13
  initializer 'coverband.configure' do |app|
17
14
  app.middleware.use Coverband::BackgroundMiddleware
15
+
16
+ if Coverband.configuration.track_views
17
+ CoverbandViewTracker = Coverband::Collectors::ViewTracker.new
18
+ Coverband.configuration.view_tracker = CoverbandViewTracker
19
+
20
+ ActiveSupport::Notifications.subscribe(/render_partial.action_view|render_template.action_view/) do |name, start, finish, id, payload|
21
+ CoverbandViewTracker.track_views(name, start, finish, id, payload) unless name.include?('!')
22
+ end
23
+ end
24
+ end
25
+
26
+ config.after_initialize do
27
+ Coverband.eager_loading_coverage!
28
+ Coverband.report_coverage
29
+ Coverband.runtime_coverage!
18
30
  end
19
31
 
20
32
  rake_tasks do
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coverband
4
+ module Utils
5
+ class RelativeFileConverter
6
+ def self.instance
7
+ @instance ||= new(Coverband.configuration.all_root_paths)
8
+ end
9
+
10
+ def self.reset
11
+ @instance = nil
12
+ end
13
+
14
+ def self.convert(file)
15
+ instance.convert(file)
16
+ end
17
+
18
+ def initialize(roots)
19
+ @cache = {}
20
+ @roots = normalize(roots)
21
+ end
22
+
23
+ def convert(file)
24
+ @cache[file] ||= begin
25
+ relative_file = file
26
+ @roots.each do |root|
27
+ relative_file = file.gsub(/^#{root}/, '.')
28
+ break relative_file if relative_file.start_with?('.')
29
+ end
30
+ relative_file
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def normalize(paths)
37
+ paths.map { |root| File.expand_path(root) }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -26,8 +26,8 @@ module Coverband
26
26
  begin
27
27
  require 'aws-sdk-s3'
28
28
  rescue StandardError
29
- err_msg = 'coverband requires aws-sdk in order use S3Report.'
30
- Coverband.configuration.logger.error err_msg
29
+ err_msg = 'Coverband requires aws-sdk in order use S3Report.'
30
+ Coverband.configuration.logger&.error err_msg
31
31
  return
32
32
  end
33
33
  end
@@ -10,8 +10,6 @@
10
10
  module Coverband
11
11
  module Utils
12
12
  class SourceFile
13
- include Coverband::Utils::FilePathHelper
14
-
15
13
  # TODO: Refactor Line into its own file
16
14
  # Representation of a single line in a source file including
17
15
  # this specific line's source code, line_number and code coverage,
@@ -246,7 +244,7 @@ module Coverband
246
244
  end
247
245
 
248
246
  def relative_path
249
- full_path_to_relative(filename)
247
+ RelativeFileConverter.convert(filename)
250
248
  end
251
249
 
252
250
  def gem?
@@ -15,6 +15,7 @@ namespace :coverband do
15
15
 
16
16
  desc 'report runtime Coverband code coverage'
17
17
  task :coverage_server do
18
+ Rake.application['environment'].invoke if Rake::Task.task_defined?('environment')
18
19
  Rack::Server.start app: Coverband::Reporters::Web.new, Port: ENV.fetch('COVERBAND_COVERAGE_PORT', 1022).to_i
19
20
  end
20
21
 
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ ###
4
+ # ensure we properly do release candidate versioning; https://github.com/danmayer/coverband/issues/288
5
+ # use format '4.2.1.rc.1' ~> 4.2.1.rc to prerelease versions like v4.2.1.rc.2 and v4.2.1.rc.3
6
+ ###
3
7
  module Coverband
4
- VERSION = '4.2.1'
8
+ VERSION = '4.2.2'
5
9
  end
data/lib/coverband.rb CHANGED
@@ -6,10 +6,13 @@ require 'redis'
6
6
  require 'coverband/version'
7
7
  require 'coverband/at_exit'
8
8
  require 'coverband/configuration'
9
- require 'coverband/utils/file_path_helper'
9
+ require 'coverband/utils/relative_file_converter'
10
+ require 'coverband/utils/absolute_file_converter'
10
11
  require 'coverband/adapters/base'
11
12
  require 'coverband/adapters/redis_store'
13
+ require 'coverband/adapters/hash_redis_store'
12
14
  require 'coverband/adapters/file_store'
15
+ require 'coverband/utils/file_hasher'
13
16
  require 'coverband/utils/s3_report'
14
17
  require 'coverband/utils/html_formatter'
15
18
  require 'coverband/utils/result'
@@ -20,6 +23,7 @@ require 'coverband/utils/file_groups'
20
23
  require 'coverband/utils/lines_classifier'
21
24
  require 'coverband/utils/results'
22
25
  require 'coverband/collectors/coverage'
26
+ require 'coverband/collectors/view_tracker'
23
27
  require 'coverband/reporters/base'
24
28
  require 'coverband/reporters/html_report'
25
29
  require 'coverband/reporters/console_report'
@@ -28,6 +32,8 @@ require 'coverband/integrations/background'
28
32
  require 'coverband/integrations/background_middleware'
29
33
  require 'coverband/integrations/rack_server_check'
30
34
 
35
+ Coverband::Adapters::RedisStore = Coverband::Adapters::HashRedisStore if ENV['COVERBAND_HASH_REDIS_STORE']
36
+
31
37
  module Coverband
32
38
  @@configured = false
33
39
  CONFIG_FILE = './config/coverband.rb'
@@ -634,15 +634,15 @@ abbr.timeago {
634
634
  float: right;
635
635
  color: #dddddd; }
636
636
 
637
- .group_tabs {
637
+ .group_tabs, .extra_tabs {
638
638
  list-style: none;
639
639
  float: left;
640
640
  margin: 0;
641
641
  padding: 0; }
642
- .group_tabs li {
642
+ .group_tabs li, .extra_tabs li {
643
643
  display: inline;
644
644
  float: left; }
645
- .group_tabs li a {
645
+ .group_tabs li a, .extra_tabs li a {
646
646
  font-family: Helvetica, Arial, sans-serif;
647
647
  display: block;
648
648
  float: left;
@@ -664,15 +664,15 @@ abbr.timeago {
664
664
  -moz-border-radius-topright: 2px;
665
665
  border-top-left-radius: 2px;
666
666
  border-top-right-radius: 2px; }
667
- .group_tabs li a:hover {
667
+ .group_tabs li a:hover, .extra_tabs li a:hover {
668
668
  background-color: #cccccc;
669
669
  background: -webkit-gradient(linear, 0 0, 0 bottom, from(#eeeeee), to(#aaaaaa));
670
670
  background: -moz-linear-gradient(#eeeeee, #aaaaaa);
671
671
  background: linear-gradient(#eeeeee, #aaaaaa); }
672
- .group_tabs li a:active {
672
+ .group_tabs li a:active, .extra_tabs li a:active {
673
673
  padding-top: 5px;
674
674
  padding-bottom: 3px; }
675
- .group_tabs li.active a {
675
+ .group_tabs li.active a, .extra_tabs li.active a {
676
676
  color: black;
677
677
  text-shadow: white 1px 1px 0px;
678
678
  background-color: #dddddd;
@@ -27,30 +27,11 @@ namespace :benchmarks do
27
27
  end
28
28
  end
29
29
 
30
- def classifier_dir
31
- File.join(File.dirname(__FILE__), 'classifier-reborn')
32
- end
33
-
34
- def clone_classifier
35
- system "git clone https://github.com/jekyll/classifier-reborn.git #{classifier_dir}" unless Dir.exist? classifier_dir
36
- end
37
-
38
30
  # desc 'setup standard benchmark'
39
31
  task :setup do
40
- clone_classifier
41
- $LOAD_PATH.unshift(File.join(classifier_dir, 'lib'))
42
32
  require 'benchmark'
43
33
  require 'benchmark/ips'
44
34
 
45
- # TODO: ok this is interesting and weird
46
- # basically the earlier I require coverage and
47
- # then require files the larger perf impact
48
- # this is somewhat expected but can lead to significant perf diffs
49
- # for example moving `require 'classifier-reborn'` below the coverage.start
50
- # results in 1.5x slower vs "difference falls within error"
51
- # moving from 5 second of time to 12 still shows slower based on when classifier is required
52
- # make sure to be plugged in while benchmarking ;) Otherwise you get very unreliable results
53
- require 'classifier-reborn'
54
35
  if ENV['COVERAGE'] || ENV['ONESHOT']
55
36
  require 'coverage'
56
37
  ::Coverage.start(oneshot_lines: !!ENV['ONESHOT'])
@@ -91,32 +72,7 @@ namespace :benchmarks do
91
72
  end
92
73
  end
93
74
 
94
- def bayes_classification
95
- b = ClassifierReborn::Bayes.new 'Interesting', 'Uninteresting'
96
- b.train_interesting 'here are some good words. I hope you love them'
97
- b.train_uninteresting 'here are some bad words, I hate you'
98
- b.classify 'I hate bad words and you' # returns 'Uninteresting'
99
- end
100
-
101
- def lsi_classification
102
- lsi = ClassifierReborn::LSI.new
103
- strings = [['This text deals with dogs. Dogs.', :dog],
104
- ['This text involves dogs too. Dogs! ', :dog],
105
- ['This text revolves around cats. Cats.', :cat],
106
- ['This text also involves cats. Cats!', :cat],
107
- ['This text involves birds. Birds.', :bird]]
108
- strings.each { |x| lsi.add_item x.first, x.last }
109
- lsi.search('dog', 3)
110
- lsi.find_related(strings[2], 2)
111
- lsi.classify 'This text is also about dogs!'
112
- end
113
-
114
75
  def work
115
- 5.times do
116
- bayes_classification
117
- lsi_classification
118
- end
119
-
120
76
  # simulate many calls to the same line
121
77
  10_000.times { Dog.new.bark }
122
78
  end
@@ -140,9 +96,11 @@ namespace :benchmarks do
140
96
  Coverband::Collectors::Coverage.instance.reset_instance
141
97
  end
142
98
 
99
+ LINES = 45
100
+ NON_NIL_LINES = 18
143
101
  def fake_line_numbers
144
- 24.times.each_with_object({}) do |line, line_hash|
145
- line_hash[(line + 1).to_s] = rand(5)
102
+ LINES.times.map do |line|
103
+ coverage = (line < NON_NIL_LINES) ? rand(5) : nil
146
104
  end
147
105
  end
148
106
 
@@ -165,8 +123,8 @@ namespace :benchmarks do
165
123
  ###
166
124
  # this is a hack because in the benchmark we don't have real files
167
125
  ###
168
- def store.file_hash(file)
169
- @file_hash_cache[file] || @file_hash_cache[file] = Digest::MD5.file(__FILE__).hexdigest
126
+ def store.file_hash(_file)
127
+ @file_hash ||= Digest::MD5.file(__FILE__).hexdigest
170
128
  end
171
129
 
172
130
  def store.full_path_to_relative(file)
@@ -183,7 +141,13 @@ namespace :benchmarks do
183
141
  5.times { store.save_report(report) }
184
142
  Benchmark.ips do |x|
185
143
  x.config(time: 15, warmup: 5)
186
- x.report('store_reports') { store.save_report(report) }
144
+ x.report('store_reports_all') { store.save_report(report) }
145
+ end
146
+ keys_subset = report.keys.first(100)
147
+ report_subset = report.select { |key, _value| keys_subset.include?(key) }
148
+ Benchmark.ips do |x|
149
+ x.config(time: 20, warmup: 5)
150
+ x.report('store_reports_subset') { store.save_report(report_subset) }
187
151
  end
188
152
  end
189
153
 
@@ -415,6 +379,18 @@ namespace :benchmarks do
415
379
  `open tmp/timeseries.jpg`
416
380
  end
417
381
 
382
+ desc 'benchmark initialization of rails'
383
+ task :init_rails do
384
+ require 'benchmark'
385
+ require 'benchmark/ips'
386
+ Benchmark.ips do |x|
387
+ x.config(time: 60, warmup: 0)
388
+ x.report('init_rails') do
389
+ system('bundle exec rake init_rails -f ./test/benchmarks/init_rails.rake')
390
+ end
391
+ end
392
+ end
393
+
418
394
  desc 'compare Coverband Ruby Coverage with Filestore with normal Ruby'
419
395
  task :compare_file do
420
396
  puts 'comparing Coverage loaded/not, this takes some time for output...'