deep-cover-core 0.6.4 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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