crystalball 0.6.0 → 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.
- checksums.yaml +5 -5
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +2 -0
- data/crystalball.gemspec +1 -2
- data/docs/index.md +1 -1
- data/docs/map_generators.md +12 -0
- data/lib/crystalball.rb +1 -0
- data/lib/crystalball/map_compactor.rb +44 -0
- data/lib/crystalball/map_compactor/example_context.rb +29 -0
- data/lib/crystalball/map_compactor/example_groups_data_compactor.rb +66 -0
- data/lib/crystalball/map_generator.rb +6 -1
- data/lib/crystalball/map_generator/configuration.rb +6 -2
- data/lib/crystalball/map_storage/yaml_storage.rb +1 -1
- data/lib/crystalball/source_diff.rb +1 -0
- data/lib/crystalball/version.rb +1 -1
- metadata +10 -22
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 51dd1b54ddce6da0c48bf4e7c0af2618179f882523e31c3b1c670b17638c6c7f
|
|
4
|
+
data.tar.gz: b82ae8f52e5000a846c8798e4c88905a1f62d0a1aed6ff81f9483d68a91d2581
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 575147f129b34cf224ecf749eb85da0fe36944caa7b2ce9ef60bf606cdb3449f6f38866dfd0d1ba5b1d786f25167885af5700d940aa451e4224a5eb666801462
|
|
7
|
+
data.tar.gz: fb0d841b9af7eb7d6c0889520f1297ebed58786e2729580ffcdc5d152c7f1ad6bdc1988a7cb8c45900a05c5ca151e46a12823d17d92f10ed8520781c4114cd15
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/crystalball.gemspec
CHANGED
|
@@ -36,7 +36,6 @@ Gem::Specification.new do |spec|
|
|
|
36
36
|
|
|
37
37
|
spec.add_development_dependency 'actionview'
|
|
38
38
|
spec.add_development_dependency 'activerecord'
|
|
39
|
-
spec.add_development_dependency "bundler", "~> 1.14"
|
|
40
39
|
spec.add_development_dependency 'factory_bot'
|
|
41
40
|
spec.add_development_dependency 'i18n'
|
|
42
41
|
spec.add_development_dependency 'parser'
|
|
@@ -47,6 +46,6 @@ Gem::Specification.new do |spec|
|
|
|
47
46
|
spec.add_development_dependency 'rubocop', ">= 0.56"
|
|
48
47
|
spec.add_development_dependency 'rubocop-rspec'
|
|
49
48
|
spec.add_development_dependency 'simplecov'
|
|
50
|
-
spec.add_development_dependency 'sqlite3'
|
|
49
|
+
spec.add_development_dependency 'sqlite3', "~> 1.3.13"
|
|
51
50
|
spec.add_development_dependency 'yard'
|
|
52
51
|
end
|
data/docs/index.md
CHANGED
|
@@ -11,7 +11,7 @@ Please check our [installation instructions](https://github.com/toptal/crystalba
|
|
|
11
11
|
|
|
12
12
|
1. Start MapGenerator in your `spec_helper` before you loaded any file of your app. E.g.
|
|
13
13
|
|
|
14
|
-
if ENV['CRYSTALBALL'] == 'true'
|
|
14
|
+
if ENV['CRYSTALBALL'] == 'true'
|
|
15
15
|
Crystalball::MapGenerator.start! do |config|
|
|
16
16
|
config.register Crystalball::MapGenerator::CoverageStrategy.new
|
|
17
17
|
end
|
data/docs/map_generators.md
CHANGED
|
@@ -135,6 +135,16 @@ Check out the [implementation](https://github.com/toptal/crystalball/tree/master
|
|
|
135
135
|
|
|
136
136
|
Keep in mind that all the strategies configured for the map generator will run for each example of your test suite, so it may slow down the generation process considerably.
|
|
137
137
|
|
|
138
|
+
### Debugging
|
|
139
|
+
|
|
140
|
+
By default MapGenerator generates compact map. In case you need plain and easily readable map add to your config:
|
|
141
|
+
```ruby
|
|
142
|
+
Crystalball::MapGenerator.start! do |config|
|
|
143
|
+
#...
|
|
144
|
+
config.compact_map = false
|
|
145
|
+
end
|
|
146
|
+
```
|
|
147
|
+
|
|
138
148
|
## TablesMapGenerator
|
|
139
149
|
|
|
140
150
|
TablesMapGenerator is a separate map generator for Rails applications. It collects information about tables-to-models mapping and stores it in a file. The file is used by `Crystalball::Rails::Predictor::ModifiedSchema`.
|
|
@@ -147,3 +157,5 @@ Crystalball::TablesMapGenerator.start! do |config|
|
|
|
147
157
|
config.map_storage_path = 'my_custom_tables_map_name.yml'
|
|
148
158
|
end
|
|
149
159
|
```
|
|
160
|
+
|
|
161
|
+
|
data/lib/crystalball.rb
CHANGED
|
@@ -19,6 +19,7 @@ require 'crystalball/map_generator/coverage_strategy'
|
|
|
19
19
|
require 'crystalball/map_generator/allocated_objects_strategy'
|
|
20
20
|
require 'crystalball/map_generator/described_class_strategy'
|
|
21
21
|
require 'crystalball/map_storage/yaml_storage'
|
|
22
|
+
require 'crystalball/map_compactor'
|
|
22
23
|
require 'crystalball/version'
|
|
23
24
|
|
|
24
25
|
# Main module for the library
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ostruct'
|
|
4
|
+
|
|
5
|
+
require 'crystalball/example_group_map'
|
|
6
|
+
require 'crystalball/execution_map'
|
|
7
|
+
require 'crystalball/map_compactor/example_groups_data_compactor'
|
|
8
|
+
|
|
9
|
+
module Crystalball
|
|
10
|
+
# a module for compacting execution map by moving out repeated used files to upper contexts records.
|
|
11
|
+
module MapCompactor
|
|
12
|
+
class << self
|
|
13
|
+
# @param [Crystalball::ExecutionMap] map execution map to be compacted
|
|
14
|
+
# @return [Crystalball::ExecutionMap] compact map
|
|
15
|
+
def compact_map!(map)
|
|
16
|
+
new_map = Crystalball::ExecutionMap.new(metadata: map.metadata.to_h)
|
|
17
|
+
|
|
18
|
+
compact_examples!(map.example_groups).each do |context, used_files|
|
|
19
|
+
new_map << ExampleGroupMap.new(OpenStruct.new(id: context, file_path: example_filename(context)), used_files)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
new_map
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def compact_examples!(example_groups)
|
|
26
|
+
result = {}
|
|
27
|
+
example_groups.group_by { |k, _v| example_filename(k) }.each do |filename, examples|
|
|
28
|
+
compact_data = ExampleGroupsDataCompactor.compact!(examples.to_h)
|
|
29
|
+
|
|
30
|
+
compact_data.each do |context_address, used_files|
|
|
31
|
+
result["#{filename}[#{context_address}]"] = used_files unless used_files.empty?
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
result
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def example_filename(example_id)
|
|
40
|
+
example_id.split('[').first
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Crystalball
|
|
4
|
+
module MapCompactor
|
|
5
|
+
# Class representing RSpec context data
|
|
6
|
+
class ExampleContext
|
|
7
|
+
attr_reader :address
|
|
8
|
+
|
|
9
|
+
def initialize(address)
|
|
10
|
+
@address = address
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def parent
|
|
14
|
+
@parent ||= begin
|
|
15
|
+
parent_uid = address.split(':')[0..-2].join(':')
|
|
16
|
+
parent_uid.empty? ? nil : self.class.new(parent_uid)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def include?(example_id)
|
|
21
|
+
example_id =~ /\[#{address}[\:\]]/
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def depth
|
|
25
|
+
@depth ||= address.split(':').size
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'crystalball/map_compactor/example_context'
|
|
4
|
+
|
|
5
|
+
module Crystalball
|
|
6
|
+
module MapCompactor
|
|
7
|
+
# Class representing example groups data compacting logic for a single file
|
|
8
|
+
class ExampleGroupsDataCompactor
|
|
9
|
+
# @param [Hash] plain_data a hash of examples and used files
|
|
10
|
+
def self.compact!(plain_data)
|
|
11
|
+
new(plain_data).compact!
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def compact!
|
|
15
|
+
contexts = extract_contexts(plain_data.keys).sort_by(&:depth)
|
|
16
|
+
|
|
17
|
+
contexts.each do |context|
|
|
18
|
+
compact_data[context.address] = compact_context!(context)
|
|
19
|
+
end
|
|
20
|
+
compact_data
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
attr_reader :compact_data, :plain_data
|
|
26
|
+
|
|
27
|
+
def initialize(plain_data)
|
|
28
|
+
@plain_data = plain_data
|
|
29
|
+
@compact_data = {}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def compact_context!(context) # rubocop:disable Metrics/MethodLength
|
|
33
|
+
result = nil
|
|
34
|
+
plain_data.each do |example_uid, used_files|
|
|
35
|
+
next unless context.include?(example_uid)
|
|
36
|
+
|
|
37
|
+
if result.nil?
|
|
38
|
+
result = used_files
|
|
39
|
+
result -= deep_used_files(context.parent) if context.parent
|
|
40
|
+
else
|
|
41
|
+
result &= used_files
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
result
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def deep_used_files(context)
|
|
48
|
+
result = compact_data[context.address]
|
|
49
|
+
result += deep_used_files(context.parent) if context.parent
|
|
50
|
+
result
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def extract_contexts(example_uids)
|
|
54
|
+
result = []
|
|
55
|
+
example_uids.each do |example_uid|
|
|
56
|
+
context_numbers = /\[(.*)\]/.match(example_uid)[1].split(':')
|
|
57
|
+
until context_numbers.empty?
|
|
58
|
+
result << ExampleContext.new(context_numbers.join(':'))
|
|
59
|
+
context_numbers.pop
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
result.compact.uniq(&:address)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -52,7 +52,11 @@ module Crystalball
|
|
|
52
52
|
return unless started
|
|
53
53
|
|
|
54
54
|
strategies.each(&:before_finalize)
|
|
55
|
-
|
|
55
|
+
|
|
56
|
+
return unless map.size.positive?
|
|
57
|
+
|
|
58
|
+
example_groups = (configuration.compact_map? ? MapCompactor.compact_map!(map) : map).example_groups
|
|
59
|
+
map_storage.dump(example_groups)
|
|
56
60
|
end
|
|
57
61
|
|
|
58
62
|
def map
|
|
@@ -70,6 +74,7 @@ module Crystalball
|
|
|
70
74
|
end
|
|
71
75
|
|
|
72
76
|
def check_dump_threshold
|
|
77
|
+
return if configuration.compact_map
|
|
73
78
|
return unless dump_threshold.positive? && map.size >= dump_threshold
|
|
74
79
|
|
|
75
80
|
map_storage.dump(map.example_groups)
|
|
@@ -9,13 +9,17 @@ module Crystalball
|
|
|
9
9
|
class Configuration
|
|
10
10
|
attr_writer :map_storage
|
|
11
11
|
attr_writer :map_class
|
|
12
|
-
attr_accessor :commit
|
|
13
|
-
attr_accessor :version
|
|
12
|
+
attr_accessor :commit, :version, :compact_map
|
|
14
13
|
|
|
15
14
|
attr_reader :strategies
|
|
16
15
|
|
|
17
16
|
def initialize
|
|
18
17
|
@strategies = StrategiesCollection.new
|
|
18
|
+
@compact_map = true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def compact_map?
|
|
22
|
+
!!@compact_map
|
|
19
23
|
end
|
|
20
24
|
|
|
21
25
|
def map_class
|
|
@@ -21,7 +21,7 @@ module Crystalball
|
|
|
21
21
|
|
|
22
22
|
guard_metadata_consistency(meta)
|
|
23
23
|
|
|
24
|
-
Object.const_get(meta.first[:type]).new(metadata: meta.first, example_groups: example_groups.inject(&:merge!))
|
|
24
|
+
Object.const_get(meta.first[:type]).new(metadata: meta.first, example_groups: example_groups.compact.inject(&:merge!))
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
private
|
data/lib/crystalball/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: crystalball
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Pavel Shutsin
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date:
|
|
13
|
+
date: 2019-07-11 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: git
|
|
@@ -54,20 +54,6 @@ dependencies:
|
|
|
54
54
|
- - ">="
|
|
55
55
|
- !ruby/object:Gem::Version
|
|
56
56
|
version: '0'
|
|
57
|
-
- !ruby/object:Gem::Dependency
|
|
58
|
-
name: bundler
|
|
59
|
-
requirement: !ruby/object:Gem::Requirement
|
|
60
|
-
requirements:
|
|
61
|
-
- - "~>"
|
|
62
|
-
- !ruby/object:Gem::Version
|
|
63
|
-
version: '1.14'
|
|
64
|
-
type: :development
|
|
65
|
-
prerelease: false
|
|
66
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
67
|
-
requirements:
|
|
68
|
-
- - "~>"
|
|
69
|
-
- !ruby/object:Gem::Version
|
|
70
|
-
version: '1.14'
|
|
71
57
|
- !ruby/object:Gem::Dependency
|
|
72
58
|
name: factory_bot
|
|
73
59
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -212,16 +198,16 @@ dependencies:
|
|
|
212
198
|
name: sqlite3
|
|
213
199
|
requirement: !ruby/object:Gem::Requirement
|
|
214
200
|
requirements:
|
|
215
|
-
- - "
|
|
201
|
+
- - "~>"
|
|
216
202
|
- !ruby/object:Gem::Version
|
|
217
|
-
version:
|
|
203
|
+
version: 1.3.13
|
|
218
204
|
type: :development
|
|
219
205
|
prerelease: false
|
|
220
206
|
version_requirements: !ruby/object:Gem::Requirement
|
|
221
207
|
requirements:
|
|
222
|
-
- - "
|
|
208
|
+
- - "~>"
|
|
223
209
|
- !ruby/object:Gem::Version
|
|
224
|
-
version:
|
|
210
|
+
version: 1.3.13
|
|
225
211
|
- !ruby/object:Gem::Dependency
|
|
226
212
|
name: yard
|
|
227
213
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -275,6 +261,9 @@ files:
|
|
|
275
261
|
- lib/crystalball/factory_bot.rb
|
|
276
262
|
- lib/crystalball/git_repo.rb
|
|
277
263
|
- lib/crystalball/logging.rb
|
|
264
|
+
- lib/crystalball/map_compactor.rb
|
|
265
|
+
- lib/crystalball/map_compactor/example_context.rb
|
|
266
|
+
- lib/crystalball/map_compactor/example_groups_data_compactor.rb
|
|
278
267
|
- lib/crystalball/map_generator.rb
|
|
279
268
|
- lib/crystalball/map_generator/allocated_objects_strategy.rb
|
|
280
269
|
- lib/crystalball/map_generator/allocated_objects_strategy/object_tracker.rb
|
|
@@ -350,8 +339,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
350
339
|
- !ruby/object:Gem::Version
|
|
351
340
|
version: '0'
|
|
352
341
|
requirements: []
|
|
353
|
-
|
|
354
|
-
rubygems_version: 2.6.14.1
|
|
342
|
+
rubygems_version: 3.0.3
|
|
355
343
|
signing_key:
|
|
356
344
|
specification_version: 4
|
|
357
345
|
summary: A library for RSpec regression test selection
|