deep-cover-core 0.6.4 → 0.7.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/deep_cover_core.gemspec +1 -0
  3. data/lib/deep_cover.rb +3 -20
  4. data/lib/deep_cover/auto_run.rb +10 -35
  5. data/lib/deep_cover/autoload_tracker.rb +9 -5
  6. data/lib/deep_cover/base.rb +85 -12
  7. data/lib/deep_cover/basics.rb +15 -3
  8. data/lib/deep_cover/config.rb +36 -2
  9. data/lib/deep_cover/config_setter.rb +1 -1
  10. data/lib/deep_cover/core_ext/exec_callbacks.rb +15 -9
  11. data/lib/deep_cover/core_ext/instruction_sequence_load_iseq.rb +1 -1
  12. data/lib/deep_cover/coverage.rb +51 -41
  13. data/lib/deep_cover/covered_code.rb +54 -17
  14. data/lib/deep_cover/custom_requirer.rb +10 -10
  15. data/lib/deep_cover/global_variables.rb +32 -0
  16. data/lib/deep_cover/load.rb +25 -9
  17. data/lib/deep_cover/node/base.rb +6 -6
  18. data/lib/deep_cover/node/begin.rb +1 -2
  19. data/lib/deep_cover/node/block.rb +5 -1
  20. data/lib/deep_cover/node/case.rb +1 -1
  21. data/lib/deep_cover/node/empty_body.rb +1 -5
  22. data/lib/deep_cover/node/if.rb +3 -4
  23. data/lib/deep_cover/node/loops.rb +1 -1
  24. data/lib/deep_cover/node/mixin/flow_accounting.rb +1 -8
  25. data/lib/deep_cover/node/mixin/has_child.rb +3 -12
  26. data/lib/deep_cover/node/mixin/has_child_handler.rb +2 -5
  27. data/lib/deep_cover/node/mixin/has_tracker.rb +3 -7
  28. data/lib/deep_cover/node/module.rb +20 -23
  29. data/lib/deep_cover/node/root.rb +1 -1
  30. data/lib/deep_cover/node/send.rb +4 -5
  31. data/lib/deep_cover/node/short_circuit.rb +1 -1
  32. data/lib/deep_cover/persistence.rb +100 -0
  33. data/lib/deep_cover/problem_with_diagnostic.rb +2 -2
  34. data/lib/deep_cover/setup/clone_mode_entry_template.rb +66 -0
  35. data/lib/deep_cover/setup/deep_cover_auto_run.rb +21 -0
  36. data/lib/deep_cover/setup/deep_cover_config.rb +19 -0
  37. data/lib/deep_cover/setup/deep_cover_without_config.rb +15 -0
  38. data/lib/deep_cover/tools.rb +0 -2
  39. data/lib/deep_cover/tools/after_tests.rb +33 -0
  40. data/lib/deep_cover/tools/looks_like_rails_project.rb +13 -0
  41. data/lib/deep_cover/tools/our_coverage.rb +1 -1
  42. data/lib/deep_cover/tools/scan_match_datas.rb +1 -1
  43. data/lib/deep_cover/version.rb +4 -2
  44. metadata +24 -7
  45. data/lib/deep_cover/coverage/persistence.rb +0 -84
  46. data/lib/deep_cover/tracker_bucket.rb +0 -50
  47. data/lib/deep_cover/tracker_hits_per_path.rb +0 -35
  48. data/lib/deep_cover/tracker_storage.rb +0 -76
  49. data/lib/deep_cover/tracker_storage_per_path.rb +0 -34
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is called from `require 'deep-cover'`
4
+ #
5
+ # Based on the DEEP_COVER environment variable, we automatically start the
6
+ # covering. This way, users just need to require a file and the CLI will
7
+ # be able to automatically do the coverage.
8
+
9
+ if %w[exec 1 t true].include?(ENV['DEEP_COVER'])
10
+ # If we spawn more processes, then we don't want them clearing the trackers or doing reports.
11
+ # We only want them to gather.
12
+ ENV['DEEP_COVER'] = 'gather'
13
+ DeepCover.start
14
+ DeepCover.delete_trackers
15
+ require_relative '../auto_run'
16
+ DeepCover::AutoRun.run!('.').report!(**DeepCover.config)
17
+ elsif ENV['DEEP_COVER'] == 'gather'
18
+ DeepCover.start
19
+ require_relative '../auto_run'
20
+ DeepCover::AutoRun.run!('.')
21
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is called from `require 'deep-cover'` and from the CLI, it may have changed work directory
4
+ #
5
+ # This initializes DeepCover's configuration from a configuration file
6
+ # and from an environment variable, if present.
7
+ # Then, we set the enrionment variable to the current configuration so that
8
+ # child process are running with the same options.
9
+
10
+ require './.deep_cover.rb' if File.exist?('./.deep_cover.rb')
11
+
12
+ if ENV['DEEP_COVER_OPTIONS']
13
+ DeepCover.config.load_hash_for_serialize(YAML.load(ENV['DEEP_COVER_OPTIONS']))
14
+ end
15
+
16
+ # Any sub process should use the same config as this one
17
+ # Just leaving DEEP_COVER_OPTIONS as is means only options passed to this process will propagate,
18
+ # but we want, at the very least, that every sub-process use the same cache_directory.
19
+ ENV['DEEP_COVER_OPTIONS'] = YAML.dump(DeepCover.config.to_hash_for_serialize)
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is called from `require 'deep-cover'` and from the CLI
4
+ #
5
+ # This setups the DeepCover environment code-wise.
6
+
7
+ module DeepCover
8
+ require_relative '../load'
9
+
10
+ load_absolute_basics
11
+
12
+ extend Base
13
+ extend ConfigSetter
14
+ end
15
+ DeepCover::GLOBAL_BINDING = binding
@@ -6,8 +6,6 @@ module DeepCover
6
6
 
7
7
  require_relative 'tools/require_relative_dir'
8
8
  extend Tools::RequireRelativeDir
9
- require_relative 'tools/silence_warnings'
10
- extend Tools::SilenceWarnings
11
9
  require_relative_dir 'tools'
12
10
 
13
11
  # The functions defined in the submodules of Tools can be accessed
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is used by projects cloned with clone mode. As such, special care must be taken to
4
+ # be compatible with any projects.
5
+ # THERE MUST NOT BE ANY USE/REQUIRE OF DEPENDENCIES OF DeepCover HERE
6
+ # See deep-cover/core_gem/lib/deep_cover/setup/clone_mode_entry_template.rb for explanation of
7
+ # clone mode and of this top_level_module stuff.
8
+ top_level_module = Thread.current['_deep_cover_top_level_module'] || Object # rubocop:disable Lint/UselessAssignment
9
+
10
+ module top_level_module::DeepCover # rubocop:disable Naming/ClassAndModuleCamelCase
11
+ module Tools
12
+ module AfterTests
13
+ extend self
14
+
15
+ def after_tests
16
+ use_at_exit = true
17
+ if defined?(::Minitest)
18
+ use_at_exit = false
19
+ ::Minitest.after_run { yield }
20
+ end
21
+ if defined?(::Rspec)
22
+ use_at_exit = false
23
+ ::RSpec.configure do |config|
24
+ config.after(:suite) { yield }
25
+ end
26
+ end
27
+ if use_at_exit
28
+ at_exit { yield }
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepCover
4
+ module Tools::LooksLikeRailsProject
5
+ extend self
6
+ def looks_like_rails_project?(path)
7
+ path = File.expand_path(path)
8
+ %w(app config/application.rb config/environments db/migrate lib).all? do |q_path|
9
+ File.exist?("#{path}/#{q_path}")
10
+ end
11
+ end
12
+ end
13
+ end
@@ -5,7 +5,7 @@ module DeepCover
5
5
  def our_coverage(source, filename, lineno, **options)
6
6
  covered_code = CoveredCode.new(source: source, path: filename, lineno: lineno)
7
7
  Tools.execute_sample(covered_code)
8
- covered_code.line_coverage(options)[(lineno - 1)..-1]
8
+ covered_code.line_coverage(**options)[(lineno - 1)..-1]
9
9
  end
10
10
  end
11
11
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeepCover
4
- module Tools::RequireRelativeDir
4
+ module Tools::ScanMatchDatas
5
5
  # Like String#scan, but return the MatchData object instead
6
6
  def scan_match_datas(source, matcher)
7
7
  source.to_enum(:scan, matcher).map { Regexp.last_match }
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module DeepCover
4
- VERSION = '0.6.4'
3
+ top_level_module = Thread.current['_deep_cover_top_level_module'] || Object # rubocop:disable Lint/UselessAssignment
4
+
5
+ module top_level_module::DeepCover # rubocop:disable Naming/ClassAndModuleCamelCase
6
+ VERSION = '0.7.0'
5
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deep-cover-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.4
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc-André Lafortune
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2018-08-23 00:00:00.000000000 Z
12
+ date: 2018-11-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: parser
@@ -53,6 +53,20 @@ dependencies:
53
53
  - - ">="
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: term-ansicolor
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
56
70
  - !ruby/object:Gem::Dependency
57
71
  name: terminal-table
58
72
  requirement: !ruby/object:Gem::Requirement
@@ -194,10 +208,10 @@ files:
194
208
  - lib/deep_cover/core_ext/require_overrides.rb
195
209
  - lib/deep_cover/coverage.rb
196
210
  - lib/deep_cover/coverage/analysis.rb
197
- - lib/deep_cover/coverage/persistence.rb
198
211
  - lib/deep_cover/covered_code.rb
199
212
  - lib/deep_cover/custom_requirer.rb
200
213
  - lib/deep_cover/flag_comment_associator.rb
214
+ - lib/deep_cover/global_variables.rb
201
215
  - lib/deep_cover/load.rb
202
216
  - lib/deep_cover/memoize.rb
203
217
  - lib/deep_cover/module_override.rb
@@ -238,6 +252,7 @@ files:
238
252
  - lib/deep_cover/node/splat.rb
239
253
  - lib/deep_cover/node/variables.rb
240
254
  - lib/deep_cover/parser_ext/range.rb
255
+ - lib/deep_cover/persistence.rb
241
256
  - lib/deep_cover/problem_with_diagnostic.rb
242
257
  - lib/deep_cover/reporter.rb
243
258
  - lib/deep_cover/reporter/base.rb
@@ -261,7 +276,12 @@ files:
261
276
  - lib/deep_cover/reporter/istanbul.rb
262
277
  - lib/deep_cover/reporter/text.rb
263
278
  - lib/deep_cover/reporter/tree/util.rb
279
+ - lib/deep_cover/setup/clone_mode_entry_template.rb
280
+ - lib/deep_cover/setup/deep_cover_auto_run.rb
281
+ - lib/deep_cover/setup/deep_cover_config.rb
282
+ - lib/deep_cover/setup/deep_cover_without_config.rb
264
283
  - lib/deep_cover/tools.rb
284
+ - lib/deep_cover/tools/after_tests.rb
265
285
  - lib/deep_cover/tools/blank.rb
266
286
  - lib/deep_cover/tools/builtin_coverage.rb
267
287
  - lib/deep_cover/tools/camelize.rb
@@ -272,6 +292,7 @@ files:
272
292
  - lib/deep_cover/tools/format_char_cover.rb
273
293
  - lib/deep_cover/tools/format_generated_code.rb
274
294
  - lib/deep_cover/tools/indent_string.rb
295
+ - lib/deep_cover/tools/looks_like_rails_project.rb
275
296
  - lib/deep_cover/tools/merge.rb
276
297
  - lib/deep_cover/tools/number_lines.rb
277
298
  - lib/deep_cover/tools/our_coverage.rb
@@ -283,10 +304,6 @@ files:
283
304
  - lib/deep_cover/tools/slice.rb
284
305
  - lib/deep_cover/tools/strip_heredoc.rb
285
306
  - lib/deep_cover/tools/truncate_backtrace.rb
286
- - lib/deep_cover/tracker_bucket.rb
287
- - lib/deep_cover/tracker_hits_per_path.rb
288
- - lib/deep_cover/tracker_storage.rb
289
- - lib/deep_cover/tracker_storage_per_path.rb
290
307
  - lib/deep_cover/version.rb
291
308
  homepage: https://github.com/deep-cover/deep-cover
292
309
  licenses:
@@ -1,84 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- require 'securerandom'
5
- class Coverage
6
- class Persistence
7
- BASENAME = 'coverage.dc'
8
- TRACKER_TEMPLATE = 'trackers%{unique}.dct'
9
-
10
- attr_reader :dir_path
11
- def initialize(dest_path, dirname)
12
- @dir_path = Pathname(dest_path).join(dirname).expand_path
13
- end
14
-
15
- def load(with_trackers: true)
16
- saved?
17
- cov = load_coverage
18
- cov.tracker_storage_per_path.tracker_hits_per_path = load_trackers if with_trackers
19
- cov
20
- end
21
-
22
- def save(coverage)
23
- create_if_needed
24
- delete_trackers
25
- save_coverage(coverage)
26
- end
27
-
28
- def save_trackers(tracker_hits_per_path)
29
- saved?
30
- basename = format(TRACKER_TEMPLATE, unique: SecureRandom.urlsafe_base64)
31
- dir_path.join(basename).binwrite(Marshal.dump(
32
- version: DeepCover::VERSION,
33
- tracker_hits_per_path: tracker_hits_per_path,
34
- ))
35
- end
36
-
37
- def saved?
38
- raise "Can't find folder '#{dir_path}'" unless dir_path.exist?
39
- self
40
- end
41
-
42
- private
43
-
44
- def create_if_needed
45
- dir_path.mkpath
46
- end
47
-
48
- def save_coverage(coverage)
49
- dir_path.join(BASENAME).binwrite(Marshal.dump(
50
- version: DeepCover::VERSION,
51
- coverage: coverage,
52
- ))
53
- end
54
-
55
- # rubocop:disable Security/MarshalLoad
56
- def load_coverage
57
- Marshal.load(dir_path.join(BASENAME).binread).tap do |version:, coverage:|
58
- raise "dump version mismatch: #{version}, currently #{DeepCover::VERSION}" unless version == DeepCover::VERSION
59
- return coverage
60
- end
61
- end
62
-
63
- # returns a TrackerHitsPerPath
64
- def load_trackers
65
- tracker_files.map do |full_path|
66
- Marshal.load(full_path.binread).yield_self do |version:, tracker_hits_per_path:|
67
- raise "dump version mismatch: #{version}, currently #{DeepCover::VERSION}" unless version == DeepCover::VERSION
68
- tracker_hits_per_path
69
- end
70
- end.inject(:merge!)
71
- end
72
- # rubocop:enable Security/MarshalLoad
73
-
74
- def tracker_files
75
- basename = format(TRACKER_TEMPLATE, unique: '*')
76
- Pathname.glob(dir_path.join(basename))
77
- end
78
-
79
- def delete_trackers
80
- tracker_files.each(&:delete)
81
- end
82
- end
83
- end
84
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- bootstrap
5
-
6
- require_relative 'tracker_storage'
7
-
8
- # A holder for TrackerStorages, using some `global_name`.
9
- class TrackerBucket
10
- @@index = {}
11
-
12
- def self.[](global_name)
13
- raise ArgumentError, "'#{global_name}' is not a valid global name" unless global_name.start_with? '$'
14
- @@index[global_name] ||= new(global_name)
15
- end
16
-
17
- def setup_source
18
- "#{source} ||= {}"
19
- end
20
-
21
- def source
22
- @global_name
23
- end
24
-
25
- class << self
26
- alias_method :_load, :[]
27
- private :_load, :new
28
- end
29
-
30
- def inspect
31
- %{#<DeepCover::TrackerBucket "#{@global_name}">}
32
- end
33
-
34
- def create_storage(index = nil)
35
- index ||= @global.size
36
- TrackerStorage.new(bucket: self, array: @global[index] ||= [], index: index)
37
- end
38
-
39
- private
40
-
41
- def initialize(global_name)
42
- @global_name = global_name
43
- @global = eval(setup_source) # rubocop:disable Security/Eval
44
- end
45
-
46
- def _dump(_level)
47
- @global_name
48
- end
49
- end
50
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- bootstrap
5
-
6
- # Should be seen as a hash like {path => tracker_hits, ...},
7
- # where tracker_hits is simply an array of integers returned from
8
- # TrackerStorage#tracker_hits.
9
- # Make it easier to separate some concerns, as well as marshalling.
10
- #
11
- class TrackerHitsPerPath
12
- extend Forwardable
13
- def_delegators :@index, :each, :each_key, :map, :transform_values, :to_h, :to_hash
14
-
15
- def initialize(index = {})
16
- @index = index
17
- end
18
-
19
- def [](val)
20
- @index[val] ||= []
21
- end
22
-
23
- def merge!(tracker_hits_per_path)
24
- @index.merge!(tracker_hits_per_path) { |_h, actual, to_merge| merge_tracker_hits(actual, to_merge) }
25
- self
26
- end
27
-
28
- private def merge_tracker_hits(hits, to_merge)
29
- unless hits.size == to_merge.size
30
- raise "Attempting to merge trackers of different sizes: #{hits.size} vs #{to_merge.size}"
31
- end
32
- hits.map!.with_index { |val, i| val + to_merge[i] }
33
- end
34
- end
35
- end
@@ -1,76 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- bootstrap
5
-
6
- # List of allocated trackers from a bucket.
7
- # Should be thought of as a simple array of integers with
8
- # a limited interface.
9
- class TrackerBucket
10
- class TrackerStorage
11
- extend Forwardable
12
- def_delegators :@array, :[], :size, :each, :map, :fetch
13
-
14
- attr_reader :bucket
15
-
16
- def initialize(bucket:, array:, index:)
17
- @bucket = bucket
18
- @array = array
19
- @index = index
20
- @allocated = 0
21
- end
22
-
23
- # Returns a range of tracker ids
24
- def allocate_trackers(nb_needed)
25
- prev = @allocated
26
- @allocated += nb_needed
27
- missing = @allocated - @array.size
28
- @array.concat(Array.new(missing, 0)) if missing > 0
29
- prev...@allocated
30
- end
31
-
32
- def setup_source
33
- "(#{bucket.setup_source})[#{@index}]||=Array.new(#{size},0)"
34
- end
35
-
36
- def tracker_source(tracker_id)
37
- "#{bucket.source}[#{@index}][#{tracker_id}]+=1"
38
- end
39
-
40
- def tracker_hits
41
- @array.dup.freeze
42
- end
43
-
44
- def tracker_hits=(new_hits)
45
- if new_hits.size != @array.size
46
- warn 'Replacing tracker hits with array of different size'
47
- end
48
- @array.replace(new_hits)
49
- end
50
-
51
- private
52
-
53
- def dump
54
- {bucket: @bucket, index: @index, size: @array.size}
55
- end
56
-
57
- def _dump(_level)
58
- Marshal.dump(dump)
59
- end
60
-
61
- class << self
62
- private def load(bucket:, index:, size:)
63
- storage = bucket.create_storage(index)
64
- storage.allocate_trackers(size - storage.size)
65
- storage
66
- end
67
-
68
- private def _load(data)
69
- load(Marshal.load(data)) # rubocop:disable Security/MarshalLoad
70
- end
71
- end
72
- end
73
-
74
- private_constant :TrackerStorage
75
- end
76
- end