oneshot_coverage 0.2.1 → 0.4.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/.github/workflows/main.yml +19 -0
- data/.gitignore +1 -0
- data/CHANGELOG +4 -0
- data/Gemfile +5 -0
- data/README.md +31 -11
- data/Rakefile +11 -1
- data/lib/oneshot_coverage.rb +54 -41
- data/lib/oneshot_coverage/logger/file_logger.rb +35 -0
- data/lib/oneshot_coverage/logger/null_logger.rb +0 -1
- data/lib/oneshot_coverage/logger/stdout_logger.rb +6 -5
- data/lib/oneshot_coverage/version.rb +1 -1
- data/oneshot_coverage.gemspec +0 -3
- metadata +10 -36
- data/Gemfile.lock +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 293537ff9297d5f5aa840aee43fcc4f80e01f6f386117211887be7f643b3c311
|
4
|
+
data.tar.gz: 05d17ad02efabffa178b7ca1f414bdfe131b3f4a28c62d272359f4bcf3f8e4dc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 555c67956799e0fd4284360938c64d41c3aea1b829306ef2e5d0a7308134694ccaa3250e591dd953ab8e816f4f62dcb53e49ddc120777ce92239ea1f47f4369a
|
7
|
+
data.tar.gz: fe81e8fc2256a7b1792eb13719f6958a1a67198ed4082cf1f2821cf7f19f3f5f17875620b7aa6446ced775b0956806e38823faa599655f787e5f47c5ba724f35
|
@@ -0,0 +1,19 @@
|
|
1
|
+
name: Ruby
|
2
|
+
|
3
|
+
on: [push,pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
strategy:
|
9
|
+
matrix:
|
10
|
+
ruby: [ '2.6', '2.7', '3.0' ]
|
11
|
+
name: Test on Ruby ${{ matrix.ruby }}
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v2
|
14
|
+
- uses: ruby/setup-ruby@v1
|
15
|
+
with:
|
16
|
+
ruby-version: ${{ matrix.ruby }}
|
17
|
+
bundler-cache: true
|
18
|
+
- name: Execute test
|
19
|
+
run: bundle exec rake
|
data/.gitignore
CHANGED
data/CHANGELOG
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# OneshotCoverage
|
2
2
|
|
3
|
-
This gem may not be very useful when you want to use [Coverage
|
3
|
+
This gem may not be very useful when you want to use [Coverage oneshot mode](https://bugs.ruby-lang.org/issues/15022),
|
4
4
|
however, It could be good example to study how to implement by yourself.
|
5
5
|
|
6
6
|
This gem provides simple tools to use oneshot mode easier. It gives you:
|
@@ -9,7 +9,7 @@ This gem provides simple tools to use oneshot mode easier. It gives you:
|
|
9
9
|
- Pluggable logger interface
|
10
10
|
|
11
11
|
Please notice that it records code executions under the target path(usually, project base path).
|
12
|
-
If you have bundle gem path under target path, It will be ignored
|
12
|
+
If you have bundle gem path under target path, It will be ignored by default.
|
13
13
|
|
14
14
|
## Installation
|
15
15
|
|
@@ -35,7 +35,8 @@ Or install it yourself as:
|
|
35
35
|
OneshotCoverage.configure(
|
36
36
|
target_path: '/base/project/path',
|
37
37
|
logger: OneshotCoverage::Logger::NullLogger.new,
|
38
|
-
|
38
|
+
emit_term: nil, # emit per `emit_term` seconds. It tries to emit per request when `nil`.
|
39
|
+
cover_bundle_path: false, # record bundle gem path. Default value is false.
|
39
40
|
)
|
40
41
|
OneshotCoverage.start
|
41
42
|
```
|
@@ -44,25 +45,44 @@ As default, OneshotCoverage supports 2 logger.
|
|
44
45
|
|
45
46
|
- OneshotCoverage::Logger::NullLogger (default)
|
46
47
|
- OneshotCoverage::Logger::StdoutLogger
|
48
|
+
- OneshotCoverage::Logger::FileLogger
|
47
49
|
|
48
50
|
Only required interface is `#post` instance method, so you could implement
|
49
51
|
by yourself easily.
|
50
52
|
|
51
53
|
```ruby
|
52
|
-
class
|
53
|
-
def initialize
|
54
|
-
@
|
54
|
+
class FileLogger
|
55
|
+
def initialize(log_path)
|
56
|
+
@log_path = log_path
|
55
57
|
end
|
56
58
|
|
57
|
-
|
58
|
-
|
59
|
+
# new_logs: Struct.new(:path, :md5_hash, :lines)
|
60
|
+
def post(new_logs)
|
61
|
+
current_coverage = fetch
|
62
|
+
|
63
|
+
new_logs.each do |new_log|
|
64
|
+
key = "#{new_log.path}-#{new_log.md5_hash}"
|
65
|
+
|
66
|
+
logged_lines = current_coverage.fetch(key, [])
|
67
|
+
current_coverage[key] = logged_lines | new_log.lines
|
68
|
+
end
|
69
|
+
save(current_coverage)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def fetch
|
75
|
+
JSON.load(File.read(@log_path))
|
76
|
+
rescue Errno::ENOENT
|
77
|
+
{}
|
78
|
+
end
|
79
|
+
|
80
|
+
def save(data)
|
81
|
+
File.write(@log_path, JSON.dump(data))
|
59
82
|
end
|
60
83
|
end
|
61
84
|
```
|
62
85
|
|
63
|
-
Please note that it will retry to send data if `#post` returns falsy value.
|
64
|
-
Hence, make sure to return `true` if you don't want to retry.
|
65
|
-
|
66
86
|
### Emit logs
|
67
87
|
|
68
88
|
#### With rack application
|
data/Rakefile
CHANGED
@@ -1,2 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "bundler/gem_tasks"
|
2
|
-
|
4
|
+
require "rake/testtask"
|
5
|
+
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.libs << "lib"
|
9
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
10
|
+
end
|
11
|
+
|
12
|
+
task default: %i[test]
|
data/lib/oneshot_coverage.rb
CHANGED
@@ -19,53 +19,67 @@ module OneshotCoverage
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
OneshotLog = Struct.new(:path, :md5_hash, :lines)
|
23
|
+
|
22
24
|
class Reporter
|
23
|
-
def initialize(target_path:, logger:,
|
25
|
+
def initialize(target_path:, logger:, emit_term: nil, cover_bundle_path: false)
|
24
26
|
@target_path = target_path
|
25
27
|
@logger = logger
|
26
|
-
@
|
27
|
-
@
|
28
|
+
@emit_term = emit_term
|
29
|
+
if @emit_term
|
30
|
+
@next_emit_time = Time.now.to_i + rand(@emit_term)
|
31
|
+
end
|
32
|
+
|
28
33
|
if defined?(Bundler)
|
29
34
|
@bundler_path = Bundler.bundle_path.to_s
|
35
|
+
@cover_bundle_path = cover_bundle_path
|
30
36
|
end
|
31
37
|
end
|
32
38
|
|
33
|
-
def emit
|
34
|
-
|
39
|
+
def emit(force_emit)
|
40
|
+
if !force_emit
|
41
|
+
if !time_to_emit?
|
42
|
+
return
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
logs =
|
47
|
+
Coverage.result(clear: true, stop: false).
|
35
48
|
select { |k, v| is_target?(k, v) }.
|
36
|
-
|
37
|
-
|
49
|
+
map do |filepath, v|
|
50
|
+
OneshotLog.new(relative_path(filepath), md5_hash_for(filepath), v[:oneshot_lines])
|
51
|
+
end
|
52
|
+
|
53
|
+
if logs.size > 0
|
54
|
+
@logger.post(logs)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
38
59
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
60
|
+
def time_to_emit?
|
61
|
+
if @emit_term
|
62
|
+
if @next_emit_time > Time.now.to_i
|
63
|
+
return false # Do not emit until next_emit_time
|
64
|
+
else
|
65
|
+
@next_emit_time += @emit_term
|
43
66
|
end
|
44
67
|
end
|
68
|
+
true
|
45
69
|
end
|
46
70
|
|
47
71
|
def is_target?(filepath, value)
|
48
72
|
return false if value[:oneshot_lines].empty?
|
73
|
+
return @cover_bundle_path if @bundler_path && filepath.start_with?(@bundler_path)
|
49
74
|
return false if !filepath.start_with?(@target_path)
|
50
|
-
return false if @bundler_path && filepath.start_with?(@bundler_path)
|
51
75
|
true
|
52
76
|
end
|
53
77
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
else
|
60
|
-
md5_hash_cache[filepath] = Digest::MD5.file(filepath).hexdigest
|
61
|
-
end
|
62
|
-
|
63
|
-
value[:oneshot_lines].map do |line|
|
64
|
-
{
|
65
|
-
path: rel_path,
|
66
|
-
md5_hash: md5_hash,
|
67
|
-
lineno: line
|
68
|
-
}
|
78
|
+
def relative_path(filepath)
|
79
|
+
if filepath.include?(@target_path)
|
80
|
+
filepath[@target_path.size..-1]
|
81
|
+
else
|
82
|
+
filepath
|
69
83
|
end
|
70
84
|
end
|
71
85
|
|
@@ -73,8 +87,12 @@ module OneshotCoverage
|
|
73
87
|
@md5_hash_cache ||= {}
|
74
88
|
end
|
75
89
|
|
76
|
-
def
|
77
|
-
|
90
|
+
def md5_hash_for(filepath)
|
91
|
+
if md5_hash_cache.key? filepath
|
92
|
+
md5_hash_cache[filepath]
|
93
|
+
else
|
94
|
+
md5_hash_cache[filepath] = Digest::MD5.file(filepath).hexdigest
|
95
|
+
end
|
78
96
|
end
|
79
97
|
end
|
80
98
|
|
@@ -85,25 +103,20 @@ module OneshotCoverage
|
|
85
103
|
|
86
104
|
# To handle execution with exit immediatly
|
87
105
|
at_exit do
|
88
|
-
OneshotCoverage.emit
|
106
|
+
OneshotCoverage.emit(force_emit: true)
|
89
107
|
end
|
90
108
|
end
|
91
109
|
|
92
|
-
def emit
|
93
|
-
@reporter&.emit
|
110
|
+
def emit(force_emit: false)
|
111
|
+
@reporter&.emit(force_emit)
|
94
112
|
end
|
95
113
|
|
96
|
-
def configure(target_path:, logger: OneshotCoverage::Logger::NullLogger.new,
|
97
|
-
target_path_by_pathname =
|
98
|
-
if target_path.is_a? Pathname
|
99
|
-
target_path
|
100
|
-
else
|
101
|
-
Pathname.new(target_path)
|
102
|
-
end
|
114
|
+
def configure(target_path:, logger: OneshotCoverage::Logger::NullLogger.new, emit_term: nil, cover_bundle_path: false)
|
103
115
|
@reporter = OneshotCoverage::Reporter.new(
|
104
|
-
target_path:
|
116
|
+
target_path: Pathname.new(target_path).cleanpath.to_s + "/",
|
105
117
|
logger: logger,
|
106
|
-
|
118
|
+
emit_term: emit_term,
|
119
|
+
cover_bundle_path: cover_bundle_path
|
107
120
|
)
|
108
121
|
end
|
109
122
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module OneshotCoverage
|
4
|
+
module Logger
|
5
|
+
class FileLogger
|
6
|
+
def initialize(log_path)
|
7
|
+
@log_path = log_path
|
8
|
+
end
|
9
|
+
|
10
|
+
def post(new_logs)
|
11
|
+
current_coverage = fetch
|
12
|
+
|
13
|
+
new_logs.each do |new_log|
|
14
|
+
key = "#{new_log.path}-#{new_log.md5_hash}"
|
15
|
+
|
16
|
+
logged_lines = current_coverage.fetch(key, [])
|
17
|
+
current_coverage[key] = logged_lines | new_log.lines
|
18
|
+
end
|
19
|
+
save(current_coverage)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def fetch
|
25
|
+
JSON.load(File.read(@log_path)) || {}
|
26
|
+
rescue Errno::ENOENT
|
27
|
+
{}
|
28
|
+
end
|
29
|
+
|
30
|
+
def save(data)
|
31
|
+
File.write(@log_path, JSON.dump(data))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
module OneshotCoverage
|
2
2
|
module Logger
|
3
3
|
class StdoutLogger
|
4
|
-
def post(
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
def post(logs)
|
5
|
+
logs.each do |log|
|
6
|
+
$stdout.puts(
|
7
|
+
"[OneshotCoverage] logged path: #{log.path}, md5_hash: #{log.md5_hash}, executed_lines: #{log.lines}"
|
8
|
+
)
|
9
|
+
end
|
9
10
|
end
|
10
11
|
end
|
11
12
|
end
|
data/oneshot_coverage.gemspec
CHANGED
metadata
CHANGED
@@ -1,43 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oneshot_coverage
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shia
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.17'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.17'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '10.0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '10.0'
|
11
|
+
date: 2021-07-05 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
41
13
|
description: OneshotCoverage will help you to measure oneshot coverage on production
|
42
14
|
email:
|
43
15
|
- rise.shia@gmail.com
|
@@ -45,16 +17,18 @@ executables: []
|
|
45
17
|
extensions: []
|
46
18
|
extra_rdoc_files: []
|
47
19
|
files:
|
20
|
+
- ".github/workflows/main.yml"
|
48
21
|
- ".gitignore"
|
22
|
+
- CHANGELOG
|
49
23
|
- CODE_OF_CONDUCT.md
|
50
24
|
- Gemfile
|
51
|
-
- Gemfile.lock
|
52
25
|
- LICENSE.txt
|
53
26
|
- README.md
|
54
27
|
- Rakefile
|
55
28
|
- bin/console
|
56
29
|
- bin/setup
|
57
30
|
- lib/oneshot_coverage.rb
|
31
|
+
- lib/oneshot_coverage/logger/file_logger.rb
|
58
32
|
- lib/oneshot_coverage/logger/null_logger.rb
|
59
33
|
- lib/oneshot_coverage/logger/stdout_logger.rb
|
60
34
|
- lib/oneshot_coverage/railtie.rb
|
@@ -67,7 +41,7 @@ metadata:
|
|
67
41
|
homepage_uri: https://github.com/riseshia/oneshot_coverage
|
68
42
|
source_code_uri: https://github.com/riseshia/oneshot_coverage
|
69
43
|
changelog_uri: https://github.com/riseshia/oneshot_coverage
|
70
|
-
post_install_message:
|
44
|
+
post_install_message:
|
71
45
|
rdoc_options: []
|
72
46
|
require_paths:
|
73
47
|
- lib
|
@@ -82,8 +56,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
82
56
|
- !ruby/object:Gem::Version
|
83
57
|
version: '0'
|
84
58
|
requirements: []
|
85
|
-
rubygems_version: 3.
|
86
|
-
signing_key:
|
59
|
+
rubygems_version: 3.2.3
|
60
|
+
signing_key:
|
87
61
|
specification_version: 4
|
88
62
|
summary: Simple toolbox for oneshot coverage
|
89
63
|
test_files: []
|
data/Gemfile.lock
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
oneshot_coverage (0.1.0)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: https://rubygems.org/
|
8
|
-
specs:
|
9
|
-
rake (10.5.0)
|
10
|
-
|
11
|
-
PLATFORMS
|
12
|
-
ruby
|
13
|
-
|
14
|
-
DEPENDENCIES
|
15
|
-
bundler (~> 1.17)
|
16
|
-
oneshot_coverage!
|
17
|
-
rake (~> 10.0)
|
18
|
-
|
19
|
-
BUNDLED WITH
|
20
|
-
1.17.2
|