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.
- checksums.yaml +4 -4
- data/.rubocop.yml +29 -10
- data/.travis.yml +16 -8
- data/CODE_OF_CONDUCT.md +76 -0
- data/Gemfile +3 -1
- data/Gemfile.rails4 +1 -0
- data/README.md +108 -59
- data/Rakefile +19 -3
- data/changes.md +85 -10
- data/coverband.gemspec +3 -5
- data/lib/coverband/adapters/base.rb +64 -45
- data/lib/coverband/adapters/file_store.rb +15 -9
- data/lib/coverband/adapters/hash_redis_store.rb +190 -0
- data/lib/coverband/adapters/redis_store.rb +35 -11
- data/lib/coverband/at_exit.rb +2 -11
- data/lib/coverband/collectors/coverage.rb +35 -41
- data/lib/coverband/collectors/delta.rb +34 -28
- data/lib/coverband/collectors/view_tracker.rb +157 -0
- data/lib/coverband/configuration.rb +115 -33
- data/lib/coverband/integrations/background.rb +12 -4
- data/lib/coverband/integrations/{middleware.rb → background_middleware.rb} +2 -6
- data/lib/coverband/integrations/bundler.rb +8 -0
- data/lib/coverband/integrations/report_middleware.rb +15 -0
- data/lib/coverband/integrations/resque.rb +3 -5
- data/lib/coverband/reporters/base.rb +40 -16
- data/lib/coverband/reporters/console_report.rb +1 -0
- data/lib/coverband/reporters/html_report.rb +31 -27
- data/lib/coverband/reporters/web.rb +63 -18
- data/lib/coverband/utils/absolute_file_converter.rb +47 -0
- data/lib/coverband/utils/file_hasher.rb +16 -0
- data/lib/coverband/utils/file_list.rb +16 -5
- data/lib/coverband/utils/html_formatter.rb +45 -8
- data/lib/coverband/utils/lines_classifier.rb +5 -0
- data/lib/coverband/utils/railtie.rb +28 -10
- data/lib/coverband/utils/relative_file_converter.rb +41 -0
- data/lib/coverband/utils/result.rb +6 -39
- data/lib/coverband/utils/results.rb +51 -0
- data/lib/coverband/utils/s3_report.rb +2 -3
- data/lib/coverband/utils/source_file.rb +34 -8
- data/lib/coverband/utils/tasks.rb +10 -10
- data/lib/coverband/version.rb +5 -1
- data/lib/coverband.rb +49 -27
- data/lua/install.sh +15 -0
- data/lua/lib/persist-coverage.lua +28 -0
- data/lua/test/bootstrap.lua +5 -0
- data/lua/test/harness.lua +19 -0
- data/lua/test/redis-call.lua +55 -0
- data/lua/test/test-persist-coverage.lua +132 -0
- data/public/application.css +14 -6
- data/public/application.js +27 -0
- data/test/benchmarks/benchmark.rake +167 -73
- data/test/benchmarks/init_rails.rake +10 -0
- data/test/coverband/adapters/base_test.rb +73 -42
- data/test/coverband/adapters/file_store_test.rb +48 -37
- data/test/coverband/adapters/hash_redis_store_test.rb +195 -0
- data/test/coverband/adapters/redis_store_test.rb +102 -59
- data/test/coverband/at_exit_test.rb +0 -2
- data/test/coverband/collectors/coverage_test.rb +71 -13
- data/test/coverband/collectors/delta_test.rb +36 -6
- data/test/coverband/collectors/view_tracker_test.rb +113 -0
- data/test/coverband/configuration_test.rb +47 -7
- data/test/coverband/coverband_test.rb +14 -2
- data/test/coverband/integrations/background_middleware_test.rb +44 -0
- data/test/coverband/integrations/background_test.rb +11 -3
- data/test/coverband/integrations/report_middleware_test.rb +44 -0
- data/test/coverband/integrations/resque_worker_test.rb +5 -6
- data/test/coverband/integrations/test_resque_job.rb +3 -1
- data/test/coverband/reporters/base_test.rb +5 -81
- data/test/coverband/reporters/console_test.rb +3 -10
- data/test/coverband/reporters/html_test.rb +71 -21
- data/test/coverband/reporters/web_test.rb +0 -10
- data/test/coverband/utils/absolute_file_converter_test.rb +56 -0
- data/test/coverband/utils/file_groups_test.rb +11 -5
- data/test/coverband/utils/file_hasher_test.rb +29 -0
- data/test/coverband/utils/file_list_test.rb +5 -5
- data/test/coverband/utils/html_formatter_test.rb +41 -0
- data/test/coverband/utils/relative_file_converter_test.rb +39 -0
- data/test/coverband/utils/result_test.rb +6 -47
- data/test/coverband/utils/results_test.rb +54 -0
- data/test/coverband/utils/s3_report_test.rb +2 -0
- data/test/coverband/utils/source_file_test.rb +54 -0
- data/test/dog.rb.erb +12 -0
- data/test/forked/rails_full_stack_test.rb +106 -0
- data/test/forked/rails_rake_full_stack_test.rb +58 -0
- data/test/integration/full_stack_test.rb +15 -16
- data/test/rails4_dummy/Rakefile +6 -0
- data/test/rails4_dummy/config/application.rb +8 -9
- data/test/rails4_dummy/config/coverband.rb +3 -11
- data/test/rails4_dummy/config/coverband_missing_redis.rb +3 -0
- data/test/rails5_dummy/Rakefile +6 -0
- data/test/rails5_dummy/config/application.rb +6 -10
- data/test/rails5_dummy/config/coverband.rb +7 -3
- data/test/rails5_dummy/config/coverband_missing_redis.rb +15 -0
- data/test/rails_test_helper.rb +22 -5
- data/test/test_helper.rb +52 -11
- data/test/unique_files.rb +17 -9
- data/views/data.erb +1 -0
- data/views/file_list.erb +13 -12
- data/views/gem_list.erb +10 -1
- data/views/layout.erb +6 -18
- data/views/nav.erb +36 -0
- data/views/source_file.erb +20 -4
- data/views/source_file_loader.erb +1 -1
- data/views/view_tracker.erb +52 -0
- metadata +71 -12
- data/lib/coverband/utils/file_path_helper.rb +0 -57
- data/test/coverband/integrations/middleware_test.rb +0 -96
- 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)
|
data/public/application.css
CHANGED
|
@@ -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;
|
data/public/application.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
150
|
-
|
|
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(
|
|
173
|
-
|
|
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('
|
|
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
|
-
|
|
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: '
|
|
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
|
-
|
|
242
|
-
|
|
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
|
|
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: [
|
|
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: [
|
|
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
|