oneshot_coverage 0.2.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f17a50188404018a1e6a33f0ab5c71ddddabe4ac2686d3089bd6fb4b4e690d5
4
- data.tar.gz: c4a47f69c6fd77ab84a12b698fb1a725c4719a4451d3afdb95d40fbd99b35e39
3
+ metadata.gz: 293537ff9297d5f5aa840aee43fcc4f80e01f6f386117211887be7f643b3c311
4
+ data.tar.gz: 05d17ad02efabffa178b7ca1f414bdfe131b3f4a28c62d272359f4bcf3f8e4dc
5
5
  SHA512:
6
- metadata.gz: 0c09df9d74e6e6975e41933653b6c7904097e54eccaf50d24a18b5dd3f047d18eee8c8bd503c59bfc1bc1bccebd8381e0375b4359882c79b9ef95fd54bca84c9
7
- data.tar.gz: bcb475a7d9ef58378738c0bd93126ba3b5ba1edb183272bb8f624f492b16b03616d84faace863c05b212f24c1d1bf2b9631135496283675d47a25b786e777448
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
@@ -41,6 +41,7 @@ Temporary Items
41
41
  /test/tmp/
42
42
  /test/version_tmp/
43
43
  /tmp/
44
+ Gemfile.lock
44
45
 
45
46
  # Used by dotenv library to load environment variables.
46
47
  # .env
data/CHANGELOG ADDED
@@ -0,0 +1,4 @@
1
+ ## 0.4.0
2
+
3
+ - Provide options for checking bundle_path (#7)
4
+ - Fix error when empty log file is exist (#4)
data/Gemfile CHANGED
@@ -4,3 +4,8 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in oneshot_coverage.gemspec
6
6
  gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+ gem "bundler", "> 1.17"
10
+
11
+ gem "minitest", "~> 5.0"
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 onetshot mode](https://bugs.ruby-lang.org/issues/15022),
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 automatically.
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
- max_emit_at_once: nil, # Flush all when it set to `nil`, which is default
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 SampleFluentLogger
53
- def initialize
54
- @logger = Fluent::Logger::FluentLogger.new('tag_prefix')
54
+ class FileLogger
55
+ def initialize(log_path)
56
+ @log_path = log_path
55
57
  end
56
58
 
57
- def post(path:, md5_hash:, lineno:)
58
- @logger.post(nil, path: path, md5_hash: md5_hash, lineno: lineno)
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
- task :default => :spec
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]
@@ -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:, max_emit_at_once:)
25
+ def initialize(target_path:, logger:, emit_term: nil, cover_bundle_path: false)
24
26
  @target_path = target_path
25
27
  @logger = logger
26
- @buffer = []
27
- @max_emit_at_once = max_emit_at_once
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
- Coverage.result(clear: true, stop: false).
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
- flat_map { |k, v| transform(k, v) }.
37
- each { |row| @buffer << row }
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
- @buffer.shift(emit_at_once).each do |row|
40
- # Retry when fail to post
41
- unless @logger.post(row)
42
- @buffer << row
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 transform(filepath, value)
55
- rel_path = filepath[@target_path.size..]
56
- md5_hash =
57
- if md5_hash_cache.key?(filepath)
58
- md5_hash_cache[filepath]
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 emit_at_once
77
- @max_emit_at_once || @buffer.size
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, max_emit_at_once: nil)
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: target_path_by_pathname.cleanpath.to_s + "/",
116
+ target_path: Pathname.new(target_path).cleanpath.to_s + "/",
105
117
  logger: logger,
106
- max_emit_at_once: max_emit_at_once
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
@@ -3,7 +3,6 @@ module OneshotCoverage
3
3
  class NullLogger
4
4
  def post(_row)
5
5
  # Do nothing
6
- true
7
6
  end
8
7
  end
9
8
  end
@@ -1,11 +1,12 @@
1
1
  module OneshotCoverage
2
2
  module Logger
3
3
  class StdoutLogger
4
- def post(path:, md5_hash:, lineno:)
5
- $stdout.puts(
6
- "[OneshotCoverage] logged path: #{path}, md5_hash: #{md5_hash}, lineno: #{lineno}"
7
- )
8
- true
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
@@ -1,3 +1,3 @@
1
1
  module OneshotCoverage
2
- VERSION = "0.2.1"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -36,7 +36,4 @@ Gem::Specification.new do |spec|
36
36
 
37
37
  # Release ruby version lock temperary
38
38
  # spec.required_ruby_version = '>= 2.6'
39
-
40
- spec.add_development_dependency "bundler", "~> 1.17"
41
- spec.add_development_dependency "rake", "~> 10.0"
42
39
  end
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.2.1
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: 2019-03-22 00:00:00.000000000 Z
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.0.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