cover_rage 1.1.0 → 1.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.
- checksums.yaml +4 -4
- data/lib/cover_rage/record.rb +8 -1
- data/lib/cover_rage/recorder.rb +14 -5
- data/lib/cover_rage/reporters/html_reporter/index.html.erb +15 -4
- data/lib/cover_rage/stores/pstore.rb +23 -8
- data/lib/cover_rage/stores/redis.rb +15 -12
- data/lib/cover_rage/stores/sqlite.rb +30 -38
- metadata +11 -52
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5621019f54428ba46ed131c128c14da48bb2e9fd2bc10e283a3c0c8f8642b2c0
|
|
4
|
+
data.tar.gz: a1a8912ce53c591c178923f790c6eba60651fb22834489513b89368a785dfea2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 10fafa62c5ddc3c37b1be895d7875c116c44343ea2bed5b18ff3571003958c6867ef40c2945ec70f4747a84760dc786e9ef5fc6c14865e770f7283a05a6004a0
|
|
7
|
+
data.tar.gz: 5d60032466d783830936617366c326df471fde464428fbabf00b0e8e390b1f9b1c79d9c384ea15f5d80448a1f6edd4d3ec5e369b273dbc659e19f6ba053da3d9
|
data/lib/cover_rage/record.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module CoverRage
|
|
4
|
-
Record = Data.define(:path, :revision, :source, :execution_count) do
|
|
4
|
+
Record = Data.define(:path, :revision, :source, :execution_count, :last_executed_at) do
|
|
5
5
|
def self.merge(existing, current)
|
|
6
6
|
records_to_save = []
|
|
7
7
|
current.each do |record|
|
|
@@ -20,6 +20,13 @@ module CoverRage
|
|
|
20
20
|
with(
|
|
21
21
|
execution_count: execution_count.map.with_index do |item, index|
|
|
22
22
|
item.nil? ? nil : item + other.execution_count[index]
|
|
23
|
+
end,
|
|
24
|
+
last_executed_at: last_executed_at.map.with_index do |item, index|
|
|
25
|
+
if item.nil? && other.last_executed_at[index].nil? then nil
|
|
26
|
+
elsif item.nil? then other.last_executed_at[index]
|
|
27
|
+
elsif other.last_executed_at[index].nil? then item
|
|
28
|
+
else [item.to_i, other.last_executed_at[index].to_i].max
|
|
29
|
+
end
|
|
23
30
|
end
|
|
24
31
|
)
|
|
25
32
|
end
|
data/lib/cover_rage/recorder.rb
CHANGED
|
@@ -26,7 +26,7 @@ module CoverRage
|
|
|
26
26
|
interval = Config.interval
|
|
27
27
|
jitter = 0.15
|
|
28
28
|
loop do
|
|
29
|
-
sleep(interval + rand * interval * jitter)
|
|
29
|
+
sleep(interval + (rand * interval * jitter))
|
|
30
30
|
save(Coverage.result(stop: false, clear: true))
|
|
31
31
|
end
|
|
32
32
|
end
|
|
@@ -42,14 +42,23 @@ module CoverRage
|
|
|
42
42
|
relative_path = filepath.delete_prefix(@path_prefix)
|
|
43
43
|
revision, source = read_file_with_revision(filepath)
|
|
44
44
|
|
|
45
|
+
now = Time.now.to_i
|
|
46
|
+
last_executed_at = execution_count.map { |c| c&.positive? ? now : nil }
|
|
47
|
+
|
|
45
48
|
records << Record.new(
|
|
46
49
|
path: relative_path,
|
|
47
|
-
revision
|
|
48
|
-
source
|
|
49
|
-
execution_count
|
|
50
|
+
revision:,
|
|
51
|
+
source:,
|
|
52
|
+
execution_count:,
|
|
53
|
+
last_executed_at:
|
|
50
54
|
)
|
|
51
55
|
end
|
|
52
|
-
|
|
56
|
+
return unless records.any?
|
|
57
|
+
|
|
58
|
+
@store.transaction do
|
|
59
|
+
records_to_save = Record.merge(@store.list, records)
|
|
60
|
+
@store.update(records_to_save)
|
|
61
|
+
end
|
|
53
62
|
end
|
|
54
63
|
|
|
55
64
|
private
|
|
@@ -27,11 +27,18 @@
|
|
|
27
27
|
.number {
|
|
28
28
|
display: inline-block;
|
|
29
29
|
text-align: right;
|
|
30
|
-
margin-right: 0.5em;
|
|
31
30
|
background-color: lightgray;
|
|
32
31
|
padding: 0 0.5em 0 1.5em;
|
|
33
32
|
}
|
|
34
33
|
|
|
34
|
+
.timestamp {
|
|
35
|
+
display: inline-block;
|
|
36
|
+
text-align: right;
|
|
37
|
+
margin-right: 0.5em;
|
|
38
|
+
background-color: lightgray;
|
|
39
|
+
padding: 0 0.5em 0 0.5em;
|
|
40
|
+
}
|
|
41
|
+
|
|
35
42
|
.nav {
|
|
36
43
|
display: flex;
|
|
37
44
|
list-style: none;
|
|
@@ -123,9 +130,13 @@
|
|
|
123
130
|
if (typeof value === "number")
|
|
124
131
|
color = value > 0 ? "green" : "red";
|
|
125
132
|
const number = typeof value === "number" ? value : "-";
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
133
|
+
const posix_time = record.last_executed_at[index];
|
|
134
|
+
const iso8601_time = typeof posix_time === "number" ? new Date(posix_time * 1000).toISOString().slice(0, 10) : "-";
|
|
135
|
+
return `<span class="line ${color}"><span class="number">${
|
|
136
|
+
number.toString().padStart(digit_width, " ")
|
|
137
|
+
}</span><time class="timestamp">${
|
|
138
|
+
iso8601_time.padStart(10, " ")
|
|
139
|
+
}</time>${line}</span>`;
|
|
129
140
|
})
|
|
130
141
|
.join("\n");
|
|
131
142
|
}
|
|
@@ -10,27 +10,42 @@ module CoverRage
|
|
|
10
10
|
@store = PStore.new(path, true)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def
|
|
13
|
+
def transaction
|
|
14
14
|
@store.transaction do
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
@transaction = true
|
|
16
|
+
yield
|
|
17
|
+
ensure
|
|
18
|
+
@transaction = false
|
|
18
19
|
end
|
|
19
20
|
end
|
|
20
21
|
|
|
21
|
-
def
|
|
22
|
-
@
|
|
22
|
+
def update(records)
|
|
23
|
+
if @transaction
|
|
24
|
+
records.each { @store[_1.path] = _1 }
|
|
25
|
+
else
|
|
26
|
+
@store.transaction do
|
|
27
|
+
records.each { @store[_1.path] = _1 }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
23
30
|
end
|
|
24
31
|
|
|
25
32
|
def list
|
|
26
|
-
@
|
|
33
|
+
if @transaction
|
|
27
34
|
@store.keys.map { @store[_1] }
|
|
35
|
+
else
|
|
36
|
+
@store.transaction do
|
|
37
|
+
@store.keys.map { @store[_1] }
|
|
38
|
+
end
|
|
28
39
|
end
|
|
29
40
|
end
|
|
30
41
|
|
|
31
42
|
def clear
|
|
32
|
-
@
|
|
43
|
+
if @transaction
|
|
33
44
|
@store.keys.each { @store.delete(_1) }
|
|
45
|
+
else
|
|
46
|
+
@store.transaction do
|
|
47
|
+
@store.keys.each { @store.delete(_1) }
|
|
48
|
+
end
|
|
34
49
|
end
|
|
35
50
|
end
|
|
36
51
|
end
|
|
@@ -12,30 +12,33 @@ module CoverRage
|
|
|
12
12
|
def initialize(url)
|
|
13
13
|
@redis =
|
|
14
14
|
if url.start_with?('rediss')
|
|
15
|
-
::Redis.new(url
|
|
15
|
+
::Redis.new(url:, ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE })
|
|
16
16
|
else
|
|
17
|
-
::Redis.new(url:
|
|
17
|
+
::Redis.new(url:)
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
def
|
|
21
|
+
def transaction(&)
|
|
22
22
|
loop do
|
|
23
23
|
break if @redis.watch(KEY) do
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
@redis.multi do |multi|
|
|
25
|
+
Thread.current[:redis_multi] = multi
|
|
26
|
+
yield
|
|
27
|
+
ensure
|
|
28
|
+
Thread.current[:redis_multi] = nil
|
|
28
29
|
end
|
|
29
|
-
@redis.multi { _1.hset(KEY, *arguments) }
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
def update(records)
|
|
35
|
+
arguments = []
|
|
36
|
+
records.each do |record|
|
|
37
|
+
arguments.push(record.path, JSON.dump(record.to_h))
|
|
38
|
+
end
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
client = Thread.current[:redis_multi] || @redis
|
|
41
|
+
client.hset(KEY, *arguments)
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
def list
|
|
@@ -8,13 +8,17 @@ module CoverRage
|
|
|
8
8
|
module Stores
|
|
9
9
|
class Sqlite
|
|
10
10
|
def initialize(path)
|
|
11
|
+
@mutex = Mutex.new
|
|
12
|
+
@path = path
|
|
11
13
|
@db = SQLite3::Database.new(path)
|
|
14
|
+
@db.busy_handler { true }
|
|
12
15
|
@db.execute <<-SQL
|
|
13
16
|
create table if not exists records (
|
|
14
17
|
path text primary key not null,
|
|
15
18
|
revision blob not null,
|
|
16
19
|
source text not null,
|
|
17
|
-
execution_count text not null
|
|
20
|
+
execution_count text not null,
|
|
21
|
+
last_executed_at text not null
|
|
18
22
|
)
|
|
19
23
|
SQL
|
|
20
24
|
process_ext = Module.new
|
|
@@ -23,58 +27,46 @@ module CoverRage
|
|
|
23
27
|
store.instance_variable_get(:@db).close
|
|
24
28
|
pid = super()
|
|
25
29
|
store.instance_variable_set(:@db, SQLite3::Database.new(path))
|
|
30
|
+
store.instance_variable_get(:@db).busy_handler { true }
|
|
26
31
|
pid
|
|
27
32
|
end
|
|
28
33
|
end
|
|
29
34
|
Process.singleton_class.prepend(process_ext)
|
|
30
35
|
end
|
|
31
36
|
|
|
32
|
-
def
|
|
33
|
-
@
|
|
34
|
-
|
|
35
|
-
@db.execute(
|
|
36
|
-
"insert or replace into records (path, revision, source, execution_count) values #{
|
|
37
|
-
(['(?,?,?,?)'] * records_to_save.length).join(',')
|
|
38
|
-
}",
|
|
39
|
-
records_to_save.each_with_object([]) do |record, memo|
|
|
40
|
-
memo.push(
|
|
41
|
-
record.path,
|
|
42
|
-
record.revision,
|
|
43
|
-
record.source,
|
|
44
|
-
JSON.dump(record.execution_count)
|
|
45
|
-
)
|
|
46
|
-
end
|
|
47
|
-
)
|
|
37
|
+
def transaction(&)
|
|
38
|
+
@mutex.synchronize do
|
|
39
|
+
@db.transaction(:exclusive, &)
|
|
48
40
|
end
|
|
49
|
-
rescue SQLite3::BusyException
|
|
50
|
-
retry
|
|
51
41
|
end
|
|
52
42
|
|
|
53
|
-
def
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
43
|
+
def update(records)
|
|
44
|
+
@db.execute(
|
|
45
|
+
"insert or replace into records (path, revision, source, execution_count, last_executed_at) values #{
|
|
46
|
+
(['(?,?,?,?,?)'] * records.length).join(',')
|
|
47
|
+
}",
|
|
48
|
+
records.each_with_object([]) do |record, memo|
|
|
49
|
+
memo.push(
|
|
50
|
+
record.path,
|
|
51
|
+
record.revision,
|
|
52
|
+
record.source,
|
|
53
|
+
JSON.dump(record.execution_count),
|
|
54
|
+
JSON.dump(record.last_executed_at)
|
|
55
|
+
)
|
|
56
|
+
end
|
|
66
57
|
)
|
|
67
58
|
end
|
|
68
59
|
|
|
69
60
|
def list
|
|
70
61
|
@db
|
|
71
|
-
.execute('select path, revision, source, execution_count from records')
|
|
72
|
-
.map do |(path, revision, source, execution_count)|
|
|
62
|
+
.execute('select path, revision, source, execution_count, last_executed_at from records')
|
|
63
|
+
.map do |(path, revision, source, execution_count, last_executed_at)|
|
|
73
64
|
Record.new(
|
|
74
|
-
path
|
|
75
|
-
revision
|
|
76
|
-
source
|
|
77
|
-
execution_count: JSON.parse(execution_count)
|
|
65
|
+
path:,
|
|
66
|
+
revision:,
|
|
67
|
+
source:,
|
|
68
|
+
execution_count: JSON.parse(execution_count),
|
|
69
|
+
last_executed_at: JSON.parse(last_executed_at)
|
|
78
70
|
)
|
|
79
71
|
end
|
|
80
72
|
end
|
metadata
CHANGED
|
@@ -1,70 +1,28 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cover_rage
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Weihang Jian
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
|
-
name:
|
|
13
|
+
name: pstore
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
|
-
- - "
|
|
16
|
+
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '
|
|
19
|
-
type: :
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
|
-
- - "
|
|
23
|
+
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '
|
|
26
|
-
- !ruby/object:Gem::Dependency
|
|
27
|
-
name: rake
|
|
28
|
-
requirement: !ruby/object:Gem::Requirement
|
|
29
|
-
requirements:
|
|
30
|
-
- - "~>"
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: '13.0'
|
|
33
|
-
type: :development
|
|
34
|
-
prerelease: false
|
|
35
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - "~>"
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '13.0'
|
|
40
|
-
- !ruby/object:Gem::Dependency
|
|
41
|
-
name: redis
|
|
42
|
-
requirement: !ruby/object:Gem::Requirement
|
|
43
|
-
requirements:
|
|
44
|
-
- - "~>"
|
|
45
|
-
- !ruby/object:Gem::Version
|
|
46
|
-
version: '5.3'
|
|
47
|
-
type: :development
|
|
48
|
-
prerelease: false
|
|
49
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
-
requirements:
|
|
51
|
-
- - "~>"
|
|
52
|
-
- !ruby/object:Gem::Version
|
|
53
|
-
version: '5.3'
|
|
54
|
-
- !ruby/object:Gem::Dependency
|
|
55
|
-
name: sqlite3
|
|
56
|
-
requirement: !ruby/object:Gem::Requirement
|
|
57
|
-
requirements:
|
|
58
|
-
- - "~>"
|
|
59
|
-
- !ruby/object:Gem::Version
|
|
60
|
-
version: '2.5'
|
|
61
|
-
type: :development
|
|
62
|
-
prerelease: false
|
|
63
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
-
requirements:
|
|
65
|
-
- - "~>"
|
|
66
|
-
- !ruby/object:Gem::Version
|
|
67
|
-
version: '2.5'
|
|
25
|
+
version: '0'
|
|
68
26
|
description: |
|
|
69
27
|
cover_rage is a Ruby code coverage tool designed to be simple and easy to use. It can be used not only for test coverage but also in production services to identify unused code.
|
|
70
28
|
|
|
@@ -95,7 +53,8 @@ files:
|
|
|
95
53
|
homepage: https://github.com/tonytonyjan/cover_rage
|
|
96
54
|
licenses:
|
|
97
55
|
- MIT
|
|
98
|
-
metadata:
|
|
56
|
+
metadata:
|
|
57
|
+
rubygems_mfa_required: 'true'
|
|
99
58
|
rdoc_options: []
|
|
100
59
|
require_paths:
|
|
101
60
|
- lib
|
|
@@ -110,7 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
110
69
|
- !ruby/object:Gem::Version
|
|
111
70
|
version: '0'
|
|
112
71
|
requirements: []
|
|
113
|
-
rubygems_version:
|
|
72
|
+
rubygems_version: 4.0.4
|
|
114
73
|
specification_version: 4
|
|
115
74
|
summary: cover_rage is a Ruby code coverage tool designed to be simple and easy to
|
|
116
75
|
use. It can be used not only for test coverage but also in production services to
|