coverband 4.2.0 → 4.2.4

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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +29 -10
  3. data/.travis.yml +16 -8
  4. data/CODE_OF_CONDUCT.md +76 -0
  5. data/Gemfile +3 -1
  6. data/Gemfile.rails4 +1 -0
  7. data/README.md +108 -59
  8. data/Rakefile +19 -3
  9. data/changes.md +85 -10
  10. data/coverband.gemspec +3 -5
  11. data/lib/coverband/adapters/base.rb +64 -45
  12. data/lib/coverband/adapters/file_store.rb +15 -9
  13. data/lib/coverband/adapters/hash_redis_store.rb +190 -0
  14. data/lib/coverband/adapters/redis_store.rb +35 -11
  15. data/lib/coverband/at_exit.rb +2 -11
  16. data/lib/coverband/collectors/coverage.rb +35 -41
  17. data/lib/coverband/collectors/delta.rb +34 -28
  18. data/lib/coverband/collectors/view_tracker.rb +157 -0
  19. data/lib/coverband/configuration.rb +115 -33
  20. data/lib/coverband/integrations/background.rb +12 -4
  21. data/lib/coverband/integrations/{middleware.rb → background_middleware.rb} +2 -6
  22. data/lib/coverband/integrations/bundler.rb +8 -0
  23. data/lib/coverband/integrations/report_middleware.rb +15 -0
  24. data/lib/coverband/integrations/resque.rb +3 -5
  25. data/lib/coverband/reporters/base.rb +40 -16
  26. data/lib/coverband/reporters/console_report.rb +1 -0
  27. data/lib/coverband/reporters/html_report.rb +31 -27
  28. data/lib/coverband/reporters/web.rb +63 -18
  29. data/lib/coverband/utils/absolute_file_converter.rb +47 -0
  30. data/lib/coverband/utils/file_hasher.rb +16 -0
  31. data/lib/coverband/utils/file_list.rb +16 -5
  32. data/lib/coverband/utils/html_formatter.rb +45 -8
  33. data/lib/coverband/utils/lines_classifier.rb +5 -0
  34. data/lib/coverband/utils/railtie.rb +28 -10
  35. data/lib/coverband/utils/relative_file_converter.rb +41 -0
  36. data/lib/coverband/utils/result.rb +6 -39
  37. data/lib/coverband/utils/results.rb +51 -0
  38. data/lib/coverband/utils/s3_report.rb +2 -3
  39. data/lib/coverband/utils/source_file.rb +34 -8
  40. data/lib/coverband/utils/tasks.rb +10 -10
  41. data/lib/coverband/version.rb +5 -1
  42. data/lib/coverband.rb +49 -27
  43. data/lua/install.sh +15 -0
  44. data/lua/lib/persist-coverage.lua +28 -0
  45. data/lua/test/bootstrap.lua +5 -0
  46. data/lua/test/harness.lua +19 -0
  47. data/lua/test/redis-call.lua +55 -0
  48. data/lua/test/test-persist-coverage.lua +132 -0
  49. data/public/application.css +14 -6
  50. data/public/application.js +27 -0
  51. data/test/benchmarks/benchmark.rake +167 -73
  52. data/test/benchmarks/init_rails.rake +10 -0
  53. data/test/coverband/adapters/base_test.rb +73 -42
  54. data/test/coverband/adapters/file_store_test.rb +48 -37
  55. data/test/coverband/adapters/hash_redis_store_test.rb +195 -0
  56. data/test/coverband/adapters/redis_store_test.rb +102 -59
  57. data/test/coverband/at_exit_test.rb +0 -2
  58. data/test/coverband/collectors/coverage_test.rb +71 -13
  59. data/test/coverband/collectors/delta_test.rb +36 -6
  60. data/test/coverband/collectors/view_tracker_test.rb +113 -0
  61. data/test/coverband/configuration_test.rb +47 -7
  62. data/test/coverband/coverband_test.rb +14 -2
  63. data/test/coverband/integrations/background_middleware_test.rb +44 -0
  64. data/test/coverband/integrations/background_test.rb +11 -3
  65. data/test/coverband/integrations/report_middleware_test.rb +44 -0
  66. data/test/coverband/integrations/resque_worker_test.rb +5 -6
  67. data/test/coverband/integrations/test_resque_job.rb +3 -1
  68. data/test/coverband/reporters/base_test.rb +5 -81
  69. data/test/coverband/reporters/console_test.rb +3 -10
  70. data/test/coverband/reporters/html_test.rb +71 -21
  71. data/test/coverband/reporters/web_test.rb +0 -10
  72. data/test/coverband/utils/absolute_file_converter_test.rb +56 -0
  73. data/test/coverband/utils/file_groups_test.rb +11 -5
  74. data/test/coverband/utils/file_hasher_test.rb +29 -0
  75. data/test/coverband/utils/file_list_test.rb +5 -5
  76. data/test/coverband/utils/html_formatter_test.rb +41 -0
  77. data/test/coverband/utils/relative_file_converter_test.rb +39 -0
  78. data/test/coverband/utils/result_test.rb +6 -47
  79. data/test/coverband/utils/results_test.rb +54 -0
  80. data/test/coverband/utils/s3_report_test.rb +2 -0
  81. data/test/coverband/utils/source_file_test.rb +54 -0
  82. data/test/dog.rb.erb +12 -0
  83. data/test/forked/rails_full_stack_test.rb +106 -0
  84. data/test/forked/rails_rake_full_stack_test.rb +58 -0
  85. data/test/integration/full_stack_test.rb +15 -16
  86. data/test/rails4_dummy/Rakefile +6 -0
  87. data/test/rails4_dummy/config/application.rb +8 -9
  88. data/test/rails4_dummy/config/coverband.rb +3 -11
  89. data/test/rails4_dummy/config/coverband_missing_redis.rb +3 -0
  90. data/test/rails5_dummy/Rakefile +6 -0
  91. data/test/rails5_dummy/config/application.rb +6 -10
  92. data/test/rails5_dummy/config/coverband.rb +7 -3
  93. data/test/rails5_dummy/config/coverband_missing_redis.rb +15 -0
  94. data/test/rails_test_helper.rb +22 -5
  95. data/test/test_helper.rb +52 -11
  96. data/test/unique_files.rb +17 -9
  97. data/views/data.erb +1 -0
  98. data/views/file_list.erb +13 -12
  99. data/views/gem_list.erb +10 -1
  100. data/views/layout.erb +6 -18
  101. data/views/nav.erb +36 -0
  102. data/views/source_file.erb +20 -4
  103. data/views/source_file_loader.erb +1 -1
  104. data/views/view_tracker.erb +52 -0
  105. metadata +71 -12
  106. data/lib/coverband/utils/file_path_helper.rb +0 -57
  107. data/test/coverband/integrations/middleware_test.rb +0 -96
  108. data/test/integration/rails_full_stack_test.rb +0 -95
@@ -0,0 +1,55 @@
1
+ redis = require 'redis'
2
+
3
+ -- If you have some different host/port change it here
4
+ local host = "127.0.0.1"
5
+ local port = 6379
6
+
7
+
8
+ client = redis.connect(host, port)
9
+
10
+ -- Workaround for absence of redis.call
11
+ redis.call = function(cmd, ...)
12
+ local arg={...}
13
+ local args_string = ''
14
+ for i,v in ipairs(arg) do
15
+ args_string = args_string .. ' ' .. v
16
+ end
17
+ cmd = string.lower(cmd)
18
+ print(cmd .. args_string)
19
+ --local result = assert(load('return client:'.. cmd ..'(...)'))(...)
20
+ local result = assert(loadstring('return client:'.. cmd ..'(...)'))(...)
21
+
22
+ -- The redis-lua library returns some values differently to how `redis.call` works inside redis.
23
+ -- this makes the responses look like those from the builtin redis
24
+ local response_lookup = {
25
+ type = function() return { ["ok"]= result } end,
26
+ sadd = function() return tonumber(result) end,
27
+ zrange = function()
28
+ if type(result) == "table" and type(result[1]) == "table" then
29
+ -- Deal with WITHSCORES...
30
+ local new_result = {}
31
+ for k,v in pairs(result) do
32
+ table.insert(new_result, v[1])
33
+ table.insert(new_result, v[2])
34
+ end
35
+ return new_result;
36
+ end
37
+
38
+ return result;
39
+ end,
40
+ hgetall = function()
41
+ local new_result = {}
42
+ for key, value in pairs(result) do
43
+ table.insert(new_result, key)
44
+ table.insert(new_result, value)
45
+ end
46
+ return new_result
47
+ end
48
+ }
49
+
50
+ if response_lookup[cmd] then
51
+ return response_lookup[cmd]()
52
+ end
53
+
54
+ return result;
55
+ end
@@ -0,0 +1,132 @@
1
+ local call_redis_script = require "./lua/test/harness";
2
+
3
+ describe("persist-coverage", function()
4
+ local function hgetall(hash_key)
5
+ local flat_map = redis.call('HGETALL', hash_key)
6
+ local result = {}
7
+ for i = 1, #flat_map, 2 do
8
+ result[flat_map[i]] = flat_map[i + 1]
9
+ end
10
+ return result
11
+ end
12
+
13
+ local function clean_redis()
14
+ redis.call('flushdb')
15
+ end
16
+
17
+ before_each(function()
18
+ clean_redis()
19
+ end)
20
+
21
+ after_each(function()
22
+ clean_redis()
23
+ end)
24
+
25
+ it("Adds data on multiple files", function()
26
+ local first_updated_at = "1569453853"
27
+ local last_updated_at = first_updated_at
28
+
29
+ local key = 'hash_key'
30
+ local json = cjson.encode({
31
+ ttl = nil,
32
+ files_data = {
33
+ {
34
+ hash_key = "coverband_hash_3_3.coverband_test.runtime../dog.rb.abcd",
35
+ meta = {
36
+ first_updated_at = first_updated_at,
37
+ last_updated_at = last_updated_at,
38
+ file = "./dog.rb",
39
+ file_hash = 'abcd',
40
+ file_length = 3
41
+ },
42
+ coverage = {
43
+ ['0'] = 0,
44
+ ['1'] = 1,
45
+ ['2'] = 2
46
+ }
47
+ },
48
+ {
49
+ hash_key = "coverband_hash_3_3.coverband_test.runtime../fish.rb.1234",
50
+ meta = {
51
+ first_updated_at = first_updated_at,
52
+ last_updated_at = last_updated_at,
53
+ file = "./fish.rb",
54
+ file_hash = '1234',
55
+ file_length = 3
56
+ },
57
+ coverage = {
58
+ ['0'] = 1,
59
+ ['1'] = 0,
60
+ ['2'] = 1
61
+ }
62
+ }
63
+ }
64
+ });
65
+ redis.call( 'set', key, json)
66
+
67
+ call_redis_script('persist-coverage.lua', { key }, {});
68
+ local results = hgetall("coverband_hash_3_3.coverband_test.runtime../dog.rb.abcd")
69
+ assert.are.same({
70
+ ["0"] = "0",
71
+ ["1"] = "1",
72
+ ["2"] = "2",
73
+ file = "./dog.rb",
74
+ file_hash = "abcd",
75
+ file_length = "3",
76
+ first_updated_at = first_updated_at ,
77
+ last_updated_at = last_updated_at
78
+ }, results)
79
+
80
+ results = hgetall("coverband_hash_3_3.coverband_test.runtime../fish.rb.1234")
81
+ assert.are.same({
82
+ ["0"] = "1",
83
+ ["1"] = "0",
84
+ ["2"] = "1",
85
+ file = "./fish.rb",
86
+ file_hash = "1234",
87
+ file_length = "3",
88
+ first_updated_at = first_updated_at ,
89
+ last_updated_at = last_updated_at
90
+ }, results)
91
+
92
+ assert.is_false(false, redis.call('exists', key))
93
+
94
+ last_updated_at = "1569453953"
95
+ json = cjson.encode({
96
+ ttl = nil,
97
+ files_data = {
98
+ {
99
+ hash_key="coverband_hash_3_3.coverband_test.runtime../dog.rb.abcd",
100
+ meta = {
101
+ first_updated_at=first_updated_at,
102
+ last_updated_at=last_updated_at,
103
+ file="./dog.rb",
104
+ file_hash='abcd',
105
+ file_length=3
106
+ },
107
+ coverage = {
108
+ ['0']= 1,
109
+ ['1']= 1,
110
+ ['2']= 1
111
+ }
112
+ }
113
+ }
114
+ })
115
+ redis.call( 'set', key, json )
116
+
117
+ call_redis_script('persist-coverage.lua', { key }, {} );
118
+ results = hgetall("coverband_hash_3_3.coverband_test.runtime../dog.rb.abcd")
119
+ assert.are.same({
120
+ ["0"] = "1",
121
+ ["1"] = "2",
122
+ ["2"] = "3",
123
+ file = "./dog.rb",
124
+ file_hash = "abcd",
125
+ file_length = "3",
126
+ first_updated_at = first_updated_at,
127
+ last_updated_at = last_updated_at
128
+ }, results)
129
+
130
+ assert.is_false(false, redis.call('exists', key))
131
+ end)
132
+ end)
@@ -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;
@@ -687,6 +687,10 @@ a.src_link {
687
687
  background: url("./magnify.png") no-repeat left 50%;
688
688
  padding-left: 18px; }
689
689
 
690
+ .red a.src_link {
691
+ color: #990000;
692
+ }
693
+
690
694
  tr, td {
691
695
  margin: 0;
692
696
  padding: 0; }
@@ -784,6 +788,10 @@ td {
784
788
  display: inline-block;
785
789
  }
786
790
 
791
+ .last_seen_at {
792
+ font-size: 8px;
793
+ }
794
+
787
795
  .notice {
788
796
  color: #856404;
789
797
  background-color: #fff3cd;
@@ -1,4 +1,14 @@
1
1
  $(document).ready(function() {
2
+ // remove the url params like notice=message so they don't stick around
3
+ window.history.replaceState(
4
+ "object or string",
5
+ "Coverband",
6
+ window.location.href.replace(/notice=.*/, "")
7
+ );
8
+ $(".notice")
9
+ .delay(3000)
10
+ .fadeOut("slow");
11
+
2
12
  $(".del").click(function() {
3
13
  if (!confirm("Do you want to delete")) {
4
14
  return false;
@@ -13,8 +23,25 @@ $(document).ready(function() {
13
23
  aoColumns: [
14
24
  null,
15
25
  { sType: "percent" },
26
+ { sType: "percent" },
27
+ null,
28
+ null,
29
+ null,
16
30
  null,
17
31
  null,
32
+ null
33
+ ]
34
+ });
35
+
36
+ // Configuration for fancy sortable tables for source file groups
37
+ $(".gem_list").dataTable({
38
+ aaSorting: [[1, "asc"]],
39
+ bPaginate: false,
40
+ bJQueryUI: true,
41
+ aoColumns: [
42
+ null,
43
+ { sType: "percent" },
44
+ { sType: "percent" },
18
45
  null,
19
46
  null,
20
47
  null,
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  namespace :benchmarks do
3
4
  # https://github.com/evanphx/benchmark-ips
4
5
  # Enable and start GC before each job run. Disable GC afterwards.
@@ -13,11 +14,9 @@ namespace :benchmarks do
13
14
  run_gc
14
15
  end
15
16
 
16
- def warmup_stats(*)
17
- end
17
+ def warmup_stats(*); end
18
18
 
19
- def add_report(*)
20
- end
19
+ def add_report(*); end
21
20
 
22
21
  private
23
22
 
@@ -28,37 +27,14 @@ namespace :benchmarks do
28
27
  end
29
28
  end
30
29
 
31
- def classifier_dir
32
- File.join(File.dirname(__FILE__), 'classifier-reborn')
33
- end
34
-
35
- def clone_classifier
36
- # rubocop:disable Style/IfUnlessModifier
37
- unless Dir.exist? classifier_dir
38
- system "git clone https://github.com/jekyll/classifier-reborn.git #{classifier_dir}"
39
- end
40
- # rubocop:enable Style/IfUnlessModifier
41
- end
42
-
43
30
  # desc 'setup standard benchmark'
44
31
  task :setup do
45
- clone_classifier
46
- $LOAD_PATH.unshift(File.join(classifier_dir, 'lib'))
47
32
  require 'benchmark'
48
33
  require 'benchmark/ips'
49
34
 
50
- # TODO: ok this is interesting and weird
51
- # basically the earlier I require coverage and
52
- # then require files the larger perf impact
53
- # this is somewhat expected but can lead to significant perf diffs
54
- # for example moving `require 'classifier-reborn'` below the coverage.start
55
- # results in 1.5x slower vs "difference falls within error"
56
- # moving from 5 second of time to 12 still shows slower based on when classifier is required
57
- # make sure to be plugged in while benchmarking ;) Otherwise you get very unreliable results
58
- require 'classifier-reborn'
59
- if ENV['COVERAGE']
35
+ if ENV['COVERAGE'] || ENV['ONESHOT']
60
36
  require 'coverage'
61
- ::Coverage.start
37
+ ::Coverage.start(oneshot_lines: !!ENV['ONESHOT'])
62
38
  end
63
39
  require 'redis'
64
40
  require 'coverband'
@@ -79,9 +55,10 @@ namespace :benchmarks do
79
55
  task :setup_redis do
80
56
  Coverband.configure do |config|
81
57
  config.root = Dir.pwd
82
- config.reporting_frequency = 100.0
83
58
  config.logger = $stdout
84
59
  config.store = benchmark_redis_store
60
+ config.use_oneshot_lines_coverage = true if ENV['ONESHOT']
61
+ config.simulate_oneshot_lines_coverage = true if ENV['SIMULATE_ONESHOT']
85
62
  end
86
63
  end
87
64
 
@@ -89,39 +66,13 @@ namespace :benchmarks do
89
66
  task :setup_file do
90
67
  Coverband.configure do |config|
91
68
  config.root = Dir.pwd
92
- config.reporting_frequency = 100.0
93
69
  config.logger = $stdout
94
70
  file_path = '/tmp/benchmark_store.json'
95
71
  config.store = Coverband::Adapters::FileStore.new(file_path)
96
72
  end
97
73
  end
98
74
 
99
- def bayes_classification
100
- b = ClassifierReborn::Bayes.new 'Interesting', 'Uninteresting'
101
- b.train_interesting 'here are some good words. I hope you love them'
102
- b.train_uninteresting 'here are some bad words, I hate you'
103
- b.classify 'I hate bad words and you' # returns 'Uninteresting'
104
- end
105
-
106
- def lsi_classification
107
- lsi = ClassifierReborn::LSI.new
108
- strings = [['This text deals with dogs. Dogs.', :dog],
109
- ['This text involves dogs too. Dogs! ', :dog],
110
- ['This text revolves around cats. Cats.', :cat],
111
- ['This text also involves cats. Cats!', :cat],
112
- ['This text involves birds. Birds.', :bird]]
113
- strings.each { |x| lsi.add_item x.first, x.last }
114
- lsi.search('dog', 3)
115
- lsi.find_related(strings[2], 2)
116
- lsi.classify 'This text is also about dogs!'
117
- end
118
-
119
75
  def work
120
- 5.times do
121
- bayes_classification
122
- lsi_classification
123
- end
124
-
125
76
  # simulate many calls to the same line
126
77
  10_000.times { Dog.new.bark }
127
78
  end
@@ -134,7 +85,7 @@ namespace :benchmarks do
134
85
  x.config(time: 12, warmup: 5, suite: suite)
135
86
  x.report 'coverband' do
136
87
  work
137
- Coverband.report_coverage(true)
88
+ Coverband.report_coverage
138
89
  end
139
90
  x.report 'no coverband' do
140
91
  work
@@ -145,9 +96,11 @@ namespace :benchmarks do
145
96
  Coverband::Collectors::Coverage.instance.reset_instance
146
97
  end
147
98
 
99
+ LINES = 45
100
+ NON_NIL_LINES = 18
148
101
  def fake_line_numbers
149
- 24.times.each_with_object({}) do |line, line_hash|
150
- line_hash[(line + 1).to_s] = rand(5)
102
+ LINES.times.map do |line|
103
+ coverage = (line < NON_NIL_LINES) ? rand(5) : nil
151
104
  end
152
105
  end
153
106
 
@@ -160,6 +113,7 @@ namespace :benchmarks do
160
113
  def adjust_report(report)
161
114
  report.keys.each do |file|
162
115
  next unless rand < 0.15
116
+
163
117
  report[file] = fake_line_numbers
164
118
  end
165
119
  report
@@ -169,12 +123,8 @@ namespace :benchmarks do
169
123
  ###
170
124
  # this is a hack because in the benchmark we don't have real files
171
125
  ###
172
- def store.file_hash(file)
173
- if @file_hash_cache[file]
174
- @file_hash_cache[file]
175
- else
176
- @file_hash_cache[file] = Digest::MD5.file(__FILE__).hexdigest
177
- end
126
+ def store.file_hash(_file)
127
+ @file_hash ||= Digest::MD5.file(__FILE__).hexdigest
178
128
  end
179
129
 
180
130
  def store.full_path_to_relative(file)
@@ -191,7 +141,13 @@ namespace :benchmarks do
191
141
  5.times { store.save_report(report) }
192
142
  Benchmark.ips do |x|
193
143
  x.config(time: 15, warmup: 5)
194
- 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) }
195
151
  end
196
152
  end
197
153
 
@@ -214,11 +170,54 @@ namespace :benchmarks do
214
170
  end.pretty_print
215
171
  data = $stdout.string
216
172
  $stdout = previous_out
217
- raise 'leaking memory!!!' unless data.match('Total retained: 0 bytes')
173
+ unless data.match('Total retained: 0 bytes')
174
+ puts data
175
+ raise 'leaking memory!!!'
176
+ end
177
+ ensure
178
+ $stdout = previous_out
179
+ end
180
+
181
+ def measure_memory_report_coverage
182
+ require 'memory_profiler'
183
+ report = fake_report
184
+ store = benchmark_redis_store
185
+ store.clear!
186
+ mock_files(store)
187
+
188
+ # warmup
189
+ 3.times { Coverband.report_coverage }
190
+
191
+ previous_out = $stdout
192
+ capture = StringIO.new
193
+ $stdout = capture
194
+
195
+ MemoryProfiler.report do
196
+ 10.times do
197
+ Coverband.report_coverage
198
+ ###
199
+ # Set to nil not {} as it is easier to verify that no memory is retained when nil gets released
200
+ # don't use Coverband::Collectors::Delta.reset which sets to {}
201
+ # we clear this as this one variable is expected to retain memory and is a false positive
202
+ ###
203
+ Coverband::Collectors::Delta.class_variable_set(:@@previous_coverage, nil)
204
+ end
205
+ end.pretty_print
206
+ data = $stdout.string
207
+ $stdout = previous_out
208
+ unless data.match('Total retained: 0 bytes')
209
+ puts data
210
+ raise 'leaking memory!!!'
211
+ end
218
212
  ensure
219
213
  $stdout = previous_out
220
214
  end
221
215
 
216
+ ###
217
+ # TODO: This currently fails, as it holds a string in redis adapter
218
+ # but really Coverband shouldn't be configured multiple times and the leak is small
219
+ # not including in test suite but we can try to figure it out and fix.
220
+ ###
222
221
  def measure_configure_memory
223
222
  require 'memory_profiler'
224
223
  # warmup
@@ -232,24 +231,79 @@ namespace :benchmarks do
232
231
  10.times do
233
232
  Coverband.configure do |config|
234
233
  redis_url = ENV['CACHE_REDIS_URL'] || ENV['REDIS_URL']
235
- config.store = Coverband::Adapters::RedisStore.new(Redis.new(url: redis_url), redis_namespace: 'coverband_data')
234
+ config.store = Coverband::Adapters::RedisStore.new(Redis.new(url: redis_url), redis_namespace: 'coverband_bench_data')
236
235
  end
237
236
  end
238
237
  end.pretty_print
239
238
  data = $stdout.string
240
239
  $stdout = previous_out
241
- puts data
242
- raise 'leaking memory!!!' unless data.match('Total retained: 0 bytes')
240
+ unless data.match('Total retained: 0 bytes')
241
+ puts data
242
+ raise 'leaking memory!!!'
243
+ end
243
244
  ensure
244
245
  $stdout = previous_out
245
246
  end
246
247
 
248
+ desc 'checks memory of collector'
249
+ task memory_check: [:setup] do
250
+ require 'pry-byebug'
251
+ require 'objspace'
252
+ puts 'memory load check'
253
+ puts(ObjectSpace.memsize_of_all / 2**20)
254
+ data = File.read('./tmp/debug_data.json')
255
+ # about 2mb
256
+ puts(ObjectSpace.memsize_of(data) / 2**20)
257
+
258
+ json_data = JSON.parse(data)
259
+ # this seems to just show the value of the pointer
260
+ # puts(ObjectSpace.memsize_of(json_data) / 2**20)
261
+ # implies json takes 10-12 mb
262
+ puts(ObjectSpace.memsize_of_all / 2**20)
263
+
264
+ json_data = nil
265
+ GC.start
266
+ json_data = JSON.parse(data)
267
+ # this seems to just show the value of the pointer
268
+ # puts(ObjectSpace.memsize_of(json_data) / 2**20)
269
+ # implies json takes 10-12 mb
270
+ puts(ObjectSpace.memsize_of_all / 2**20)
271
+
272
+ json_data = nil
273
+ GC.start
274
+ json_data = JSON.parse(data)
275
+ # this seems to just show the value of the pointer
276
+ # puts(ObjectSpace.memsize_of(json_data) / 2**20)
277
+ # implies json takes 10-12 mb
278
+ puts(ObjectSpace.memsize_of_all / 2**20)
279
+
280
+ json_data = nil
281
+ GC.start
282
+ json_data = JSON.parse(data)
283
+ # this seems to just show the value of the pointer
284
+ # puts(ObjectSpace.memsize_of(json_data) / 2**20)
285
+ # implies json takes 10-12 mb
286
+ puts(ObjectSpace.memsize_of_all / 2**20)
287
+
288
+ json_data = nil
289
+ GC.start
290
+ puts(ObjectSpace.memsize_of_all / 2**20)
291
+ debugger
292
+ puts 'done'
293
+ end
294
+
247
295
  desc 'runs memory reporting on Redis store'
248
296
  task memory_reporting: [:setup] do
249
297
  puts 'runs memory benchmarking to ensure we dont leak'
250
298
  measure_memory
251
299
  end
252
300
 
301
+ desc 'runs memory reporting on report_coverage'
302
+ task memory_reporting_report_coverage: [:setup] do
303
+ puts 'runs memory benchmarking on report_coverage to ensure we dont leak'
304
+ measure_memory_report_coverage
305
+ end
306
+
253
307
  desc 'runs memory reporting on configure'
254
308
  task memory_configure_reporting: [:setup] do
255
309
  puts 'runs memory benchmarking on configure to ensure we dont leak'
@@ -259,7 +313,12 @@ namespace :benchmarks do
259
313
  desc 'runs memory leak check via Rails tests'
260
314
  task memory_rails: [:setup] do
261
315
  puts 'runs memory rails test to ensure we dont leak'
262
- puts `COVERBAND_MEMORY_TEST=true bundle exec m test/unit/rails_full_stack_test.rb:22`
316
+ puts `COVERBAND_MEMORY_TEST=true bundle exec test/forked/rails_full_stack_test.rb`
317
+ end
318
+
319
+ desc 'runs memory leak checks'
320
+ task memory: %i[memory_reporting memory_reporting_report_coverage memory_rails] do
321
+ puts 'done'
263
322
  end
264
323
 
265
324
  desc 'runs benchmarks on reporting large sets of files to redis'
@@ -269,13 +328,36 @@ namespace :benchmarks do
269
328
  end
270
329
 
271
330
  # desc 'runs benchmarks on default redis setup'
272
- task run_redis: [:setup, :setup_redis] do
331
+ task run_redis: %i[setup setup_redis] do
273
332
  puts 'Coverband configured with default Redis store'
274
333
  run_work(true)
275
334
  end
276
335
 
336
+ def run_big
337
+ require 'memory_profiler'
338
+ require './test/unique_files'
339
+
340
+ 4000.times { |index| require_unique_file('dog.rb.erb', dog_number: index) }
341
+ # warmup
342
+ 3.times { Coverband.report_coverage }
343
+ dogs = 400.times.map { |index| Object.const_get("Dog#{index}") }
344
+ MemoryProfiler.report do
345
+ 10.times do
346
+ dogs.each(&:bark)
347
+ Coverband.report_coverage
348
+ end
349
+ end.pretty_print
350
+ end
351
+
352
+ task run_big: %i[setup setup_redis] do
353
+ # ensure we cleared from last run
354
+ benchmark_redis_store.clear!
355
+
356
+ run_big
357
+ end
358
+
277
359
  # desc 'runs benchmarks file store'
278
- task run_file: [:setup, :setup_file] do
360
+ task run_file: %i[setup setup_file] do
279
361
  puts 'Coverband configured with file store'
280
362
  run_work(true)
281
363
  end
@@ -297,6 +379,18 @@ namespace :benchmarks do
297
379
  `open tmp/timeseries.jpg`
298
380
  end
299
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
+
300
394
  desc 'compare Coverband Ruby Coverage with Filestore with normal Ruby'
301
395
  task :compare_file do
302
396
  puts 'comparing Coverage loaded/not, this takes some time for output...'
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails'
4
+ require 'coverband'
5
+
6
+ desc 'Initialize rails'
7
+ task 'init_rails' do
8
+ Coverband.configure("./test/rails#{Rails::VERSION::MAJOR}_dummy/config/coverband.rb")
9
+ require "./test/rails#{Rails::VERSION::MAJOR}_dummy/config/environment"
10
+ end