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