crystalball 0.5.0 → 0.6.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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/.rubocop.yml +4 -0
  4. data/.travis.yml +1 -1
  5. data/CHANGELOG.md +18 -0
  6. data/LICENSE +22 -674
  7. data/README.md +13 -158
  8. data/crystalball.gemspec +6 -2
  9. data/docs/img/favicon.ico +0 -0
  10. data/docs/img/logo.png +0 -0
  11. data/docs/index.md +44 -0
  12. data/docs/map_generators.md +149 -0
  13. data/docs/predictors.md +75 -0
  14. data/docs/runner.md +24 -0
  15. data/lib/crystalball.rb +8 -3
  16. data/lib/crystalball/active_record.rb +4 -0
  17. data/lib/crystalball/example_group_map.rb +19 -0
  18. data/lib/crystalball/execution_map.rb +17 -16
  19. data/lib/crystalball/extensions/git.rb +4 -0
  20. data/lib/crystalball/extensions/git/base.rb +14 -0
  21. data/lib/crystalball/extensions/git/lib.rb +18 -0
  22. data/lib/crystalball/factory_bot.rb +3 -0
  23. data/lib/crystalball/git_repo.rb +2 -7
  24. data/lib/crystalball/logging.rb +51 -0
  25. data/lib/crystalball/map_generator.rb +5 -7
  26. data/lib/crystalball/map_generator/allocated_objects_strategy.rb +6 -5
  27. data/lib/crystalball/map_generator/allocated_objects_strategy/object_tracker.rb +1 -0
  28. data/lib/crystalball/map_generator/base_strategy.rb +5 -4
  29. data/lib/crystalball/map_generator/configuration.rb +1 -1
  30. data/lib/crystalball/map_generator/coverage_strategy.rb +6 -5
  31. data/lib/crystalball/map_generator/described_class_strategy.rb +5 -5
  32. data/lib/crystalball/map_generator/factory_bot_strategy.rb +59 -0
  33. data/lib/crystalball/map_generator/factory_bot_strategy/dsl_patch.rb +40 -0
  34. data/lib/crystalball/map_generator/factory_bot_strategy/dsl_patch/factory_path_fetcher.rb +30 -0
  35. data/lib/crystalball/map_generator/factory_bot_strategy/factory_gem_loader.rb +27 -0
  36. data/lib/crystalball/map_generator/factory_bot_strategy/factory_runner_patch.rb +25 -0
  37. data/lib/crystalball/map_generator/parser_strategy.rb +60 -0
  38. data/lib/crystalball/map_generator/parser_strategy/processor.rb +129 -0
  39. data/lib/crystalball/map_generator/strategies_collection.rb +9 -9
  40. data/lib/crystalball/map_storage/yaml_storage.rb +7 -6
  41. data/lib/crystalball/prediction.rb +12 -11
  42. data/lib/crystalball/predictor.rb +7 -5
  43. data/lib/crystalball/predictor/associated_specs.rb +9 -4
  44. data/lib/crystalball/predictor/helpers/affected_example_groups_detector.rb +20 -0
  45. data/lib/crystalball/predictor/helpers/path_formatter.rb +18 -0
  46. data/lib/crystalball/predictor/modified_execution_paths.rb +11 -5
  47. data/lib/crystalball/predictor/modified_specs.rb +8 -2
  48. data/lib/crystalball/predictor/modified_support_specs.rb +39 -0
  49. data/lib/crystalball/predictor/strategy.rb +16 -0
  50. data/lib/crystalball/predictor_evaluator.rb +1 -1
  51. data/lib/crystalball/rails.rb +1 -0
  52. data/lib/crystalball/rails/helpers/base_schema_parser.rb +51 -0
  53. data/lib/crystalball/rails/helpers/schema_definition_parser.rb +36 -0
  54. data/lib/crystalball/rails/helpers/schema_definition_parser/active_record.rb +21 -0
  55. data/lib/crystalball/rails/helpers/schema_definition_parser/table_content_parser.rb +27 -0
  56. data/lib/crystalball/rails/map_generator/action_view_strategy.rb +5 -5
  57. data/lib/crystalball/rails/map_generator/action_view_strategy/patch.rb +1 -1
  58. data/lib/crystalball/rails/map_generator/i18n_strategy.rb +5 -5
  59. data/lib/crystalball/rails/map_generator/i18n_strategy/simple_patch.rb +1 -0
  60. data/lib/crystalball/rails/predictor/modified_schema.rb +81 -0
  61. data/lib/crystalball/rails/tables_map.rb +53 -0
  62. data/lib/crystalball/rails/tables_map_generator.rb +84 -0
  63. data/lib/crystalball/rails/tables_map_generator/configuration.rb +39 -0
  64. data/lib/crystalball/rspec/filtering.rb +52 -0
  65. data/lib/crystalball/rspec/prediction_builder.rb +21 -11
  66. data/lib/crystalball/rspec/prediction_pruning.rb +56 -0
  67. data/lib/crystalball/rspec/prediction_pruning/examples_pruner.rb +70 -0
  68. data/lib/crystalball/rspec/runner.rb +39 -27
  69. data/lib/crystalball/rspec/runner/configuration.rb +24 -14
  70. data/lib/crystalball/rspec/standard_prediction_builder.rb +17 -0
  71. data/lib/crystalball/source_diff.rb +12 -2
  72. data/lib/crystalball/source_diff/file_diff.rb +1 -1
  73. data/lib/crystalball/source_diff/formatting_checker.rb +50 -0
  74. data/lib/crystalball/version.rb +1 -1
  75. data/mkdocs.yml +23 -0
  76. metadata +102 -7
  77. data/lib/crystalball/case_map.rb +0 -19
  78. data/lib/crystalball/simple_predictor.rb +0 -18
@@ -1,28 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'crystalball/simple_predictor'
3
+ require 'crystalball/rspec/standard_prediction_builder'
4
4
 
5
5
  module Crystalball
6
6
  module RSpec
7
7
  class Runner
8
8
  # Class for storing local runner configuration
9
9
  class Configuration
10
- def initialize(config = {})
10
+ def initialize(config = {}) # rubocop:disable Metrics/MethodLength
11
11
  @values = {
12
- 'map_path' => 'tmp/execution_maps',
12
+ 'execution_map_path' => 'tmp/crystalball_data.yml',
13
13
  'map_expiration_period' => 86_400,
14
14
  'repo_path' => Dir.pwd,
15
- 'predictor_class_name' => 'Crystalball::SimplePredictor',
16
15
  'requires' => [],
17
16
  'diff_from' => 'HEAD',
18
17
  'diff_to' => nil,
19
- 'runner_class_name' => 'Crystalball::RSpec::Runner'
18
+ 'runner_class_name' => 'Crystalball::RSpec::Runner',
19
+ 'prediction_builder_class_name' => 'Crystalball::RSpec::StandardPredictionBuilder',
20
+ 'log_level' => :info,
21
+ 'log_file' => 'log/crystalball.log'
20
22
  }.merge(config)
21
23
  end
22
24
 
23
25
  def to_h
24
26
  dynamic_values = {}
25
- (private_methods - Object.private_instance_methods - %i[run_requires values]).each do |method|
27
+ (private_methods - Object.private_instance_methods - %i[run_requires values raw_value]).each do |method|
26
28
  dynamic_values[method.to_s] = send(method)
27
29
  end
28
30
 
@@ -30,16 +32,20 @@ module Crystalball
30
32
  end
31
33
 
32
34
  def [](key)
33
- respond_to?(key, true) ? send(key) : values[key]
35
+ respond_to?(key, true) ? send(key) : raw_value(key)
34
36
  end
35
37
 
36
38
  private
37
39
 
38
- def predictor_class
39
- @predictor_class ||= begin
40
+ def raw_value(key)
41
+ ENV.fetch("CRYSTALBALL_#{key.to_s.upcase}", values[key])
42
+ end
43
+
44
+ def prediction_builder_class
45
+ @prediction_builder_class ||= begin
40
46
  run_requires
41
47
 
42
- Object.const_get(self['predictor_class_name'])
48
+ Object.const_get(self['prediction_builder_class_name'])
43
49
  end
44
50
  end
45
51
 
@@ -51,18 +57,22 @@ module Crystalball
51
57
  end
52
58
  end
53
59
 
54
- def map_path
55
- @map_path ||= Pathname.new(values['map_path'])
60
+ def execution_map_path
61
+ @execution_map_path ||= Pathname.new(raw_value('execution_map_path'))
56
62
  end
57
63
 
58
64
  def repo_path
59
- @repo_path ||= Pathname.new(values['repo_path'])
65
+ @repo_path ||= Pathname.new(raw_value('repo_path'))
66
+ end
67
+
68
+ def log_file
69
+ @log_file ||= Pathname.new(raw_value('log_file'))
60
70
  end
61
71
 
62
72
  attr_reader :values
63
73
 
64
74
  def run_requires
65
- self['requires'].each { |f| require f }
75
+ Array(self['requires']).each { |f| require f }
66
76
  end
67
77
  end
68
78
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crystalball
4
+ module RSpec
5
+ # Simple version of predictor
6
+ class StandardPredictionBuilder < PredictionBuilder
7
+ private
8
+
9
+ def predictor
10
+ super do |p|
11
+ p.use Crystalball::Predictor::ModifiedExecutionPaths.new
12
+ p.use Crystalball::Predictor::ModifiedSpecs.new
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'crystalball/source_diff/file_diff'
4
+ require 'crystalball/source_diff/formatting_checker'
4
5
 
5
6
  module Crystalball
6
7
  # Wrapper class representing Git source diff for given repo
@@ -8,7 +9,8 @@ module Crystalball
8
9
  include Enumerable
9
10
  extend Forwardable
10
11
 
11
- delegate %i[stats size lines from to] => :git_diff
12
+ delegate %i[stats lines from to] => :git_diff
13
+ alias size count
12
14
 
13
15
  # @param [Git::Diff] git_diff raw diff made by ruby-git gem
14
16
  def initialize(git_diff)
@@ -25,13 +27,21 @@ module Crystalball
25
27
  changeset.none?
26
28
  end
27
29
 
30
+ # @return [Git::Base]
31
+ def repository
32
+ @repository ||= git_diff.instance_variable_get(:@base)
33
+ end
34
+
28
35
  private
29
36
 
30
37
  attr_reader :git_diff
31
38
 
32
39
  # TODO: Include untracked to changeset
33
40
  def changeset
34
- @changeset ||= git_diff.map { |file_diff| FileDiff.new(file_diff) }
41
+ @changeset ||= git_diff.map do |diff_file|
42
+ file_diff = FileDiff.new(diff_file)
43
+ file_diff unless FormattingChecker.pure_formatting?(file_diff)
44
+ end.compact
35
45
  end
36
46
  end
37
47
  end
@@ -10,7 +10,7 @@ module Crystalball
10
10
  end
11
11
 
12
12
  def moved?
13
- git_diff.patch =~ /rename from.*\nrename to/
13
+ !(git_diff.patch =~ /rename from.*\nrename to/).nil?
14
14
  end
15
15
 
16
16
  def modified?
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crystalball
4
+ class SourceDiff
5
+ # Determinates if file_diff's patch contains changes for whitespaces or comments only
6
+ module FormattingChecker
7
+ class << self
8
+ # Returns `true` if file_diff's patch contains changes for whitespaces or comments only
9
+ #
10
+ # @param [Crystalball::SourceDiff::FileDiff] file_diff
11
+ # @return [Boolean]
12
+ def pure_formatting?(file_diff)
13
+ return false unless stripable_file?(file_diff.path) && file_diff.modified?
14
+
15
+ patch = file_diff.patch.to_s.lines
16
+
17
+ return true if patch.empty?
18
+
19
+ added = collect_patch(patch, '+')
20
+ removed = collect_patch(patch, '-')
21
+
22
+ trim_patch(added) == trim_patch(removed)
23
+ end
24
+
25
+ private
26
+
27
+ STRIPABLE_FILES = %w[.rb .erb].freeze # TODO: move to config
28
+
29
+ def stripable_file?(file_name)
30
+ STRIPABLE_FILES.include?(Pathname(file_name).extname)
31
+ end
32
+
33
+ def collect_patch(patch, sign)
34
+ patch.each.with_object([]) do |line, result|
35
+ next if line.start_with?('+++', '---', '@@') # Skip meta of a patch
36
+
37
+ result << line[1..-1] if line.start_with?(sign, ' ')
38
+ end
39
+ end
40
+
41
+ def trim_patch(patch)
42
+ patch.map do |line|
43
+ line = line.gsub(/\s/, '')
44
+ line.start_with?('#') || line.empty? ? nil : line
45
+ end.compact
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Crystalball
4
- VERSION = '0.5.0'
4
+ VERSION = '0.6.0'
5
5
  end
data/mkdocs.yml ADDED
@@ -0,0 +1,23 @@
1
+ site_name: Crystalball Manual
2
+ repo_url: https://github.com/toptal/crystalball
3
+ edit_uri: edit/master/docs/
4
+ #google_analytics: ['-', 'auto']
5
+ theme:
6
+ name: material
7
+ logo: img/logo.png
8
+ favicon: img/favicon.ico
9
+ palette:
10
+ primary: indigo
11
+ accent: pink
12
+ markdown_extensions:
13
+ - admonition
14
+ - codehilite:
15
+ guess_lang: false
16
+ - toc:
17
+ permalink: true
18
+ copyright: 'Copyright &copy; 2013 - 2018 Toptal'
19
+ pages:
20
+ - Home: index.md
21
+ - "Map Generators": map_generators.md
22
+ - "Predictors": predictors.md
23
+ - "Runner": runner.md
metadata CHANGED
@@ -1,14 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crystalball
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pavel Shutsin
8
+ - Evgenii Pecherkin
9
+ - Jaimerson Araujo
8
10
  autorequire:
9
11
  bindir: bin
10
12
  cert_chain: []
11
- date: 2018-04-13 00:00:00.000000000 Z
13
+ date: 2018-10-04 00:00:00.000000000 Z
12
14
  dependencies:
13
15
  - !ruby/object:Gem::Dependency
14
16
  name: git
@@ -38,6 +40,20 @@ dependencies:
38
40
  - - ">="
39
41
  - !ruby/object:Gem::Version
40
42
  version: '0'
43
+ - !ruby/object:Gem::Dependency
44
+ name: activerecord
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ type: :development
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
41
57
  - !ruby/object:Gem::Dependency
42
58
  name: bundler
43
59
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +68,20 @@ dependencies:
52
68
  - - "~>"
53
69
  - !ruby/object:Gem::Version
54
70
  version: '1.14'
71
+ - !ruby/object:Gem::Dependency
72
+ name: factory_bot
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
55
85
  - !ruby/object:Gem::Dependency
56
86
  name: i18n
57
87
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +96,20 @@ dependencies:
66
96
  - - ">="
67
97
  - !ruby/object:Gem::Version
68
98
  version: '0'
99
+ - !ruby/object:Gem::Dependency
100
+ name: parser
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
69
113
  - !ruby/object:Gem::Dependency
70
114
  name: pry
71
115
  requirement: !ruby/object:Gem::Requirement
@@ -128,14 +172,14 @@ dependencies:
128
172
  requirements:
129
173
  - - ">="
130
174
  - !ruby/object:Gem::Version
131
- version: '0'
175
+ version: '0.56'
132
176
  type: :development
133
177
  prerelease: false
134
178
  version_requirements: !ruby/object:Gem::Requirement
135
179
  requirements:
136
180
  - - ">="
137
181
  - !ruby/object:Gem::Version
138
- version: '0'
182
+ version: '0.56'
139
183
  - !ruby/object:Gem::Dependency
140
184
  name: rubocop-rspec
141
185
  requirement: !ruby/object:Gem::Requirement
@@ -164,6 +208,20 @@ dependencies:
164
208
  - - ">="
165
209
  - !ruby/object:Gem::Version
166
210
  version: '0'
211
+ - !ruby/object:Gem::Dependency
212
+ name: sqlite3
213
+ requirement: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - ">="
216
+ - !ruby/object:Gem::Version
217
+ version: '0'
218
+ type: :development
219
+ prerelease: false
220
+ version_requirements: !ruby/object:Gem::Requirement
221
+ requirements:
222
+ - - ">="
223
+ - !ruby/object:Gem::Version
224
+ version: '0'
167
225
  - !ruby/object:Gem::Dependency
168
226
  name: yard
169
227
  requirement: !ruby/object:Gem::Requirement
@@ -191,6 +249,7 @@ files:
191
249
  - ".rspec"
192
250
  - ".rubocop.yml"
193
251
  - ".travis.yml"
252
+ - CHANGELOG.md
194
253
  - CODE_OF_CONDUCT.md
195
254
  - Gemfile
196
255
  - LICENSE
@@ -200,10 +259,22 @@ files:
200
259
  - bin/crystalball
201
260
  - bin/setup
202
261
  - crystalball.gemspec
262
+ - docs/img/favicon.ico
263
+ - docs/img/logo.png
264
+ - docs/index.md
265
+ - docs/map_generators.md
266
+ - docs/predictors.md
267
+ - docs/runner.md
203
268
  - lib/crystalball.rb
204
- - lib/crystalball/case_map.rb
269
+ - lib/crystalball/active_record.rb
270
+ - lib/crystalball/example_group_map.rb
205
271
  - lib/crystalball/execution_map.rb
272
+ - lib/crystalball/extensions/git.rb
273
+ - lib/crystalball/extensions/git/base.rb
274
+ - lib/crystalball/extensions/git/lib.rb
275
+ - lib/crystalball/factory_bot.rb
206
276
  - lib/crystalball/git_repo.rb
277
+ - lib/crystalball/logging.rb
207
278
  - lib/crystalball/map_generator.rb
208
279
  - lib/crystalball/map_generator/allocated_objects_strategy.rb
209
280
  - lib/crystalball/map_generator/allocated_objects_strategy/object_tracker.rb
@@ -212,30 +283,54 @@ files:
212
283
  - lib/crystalball/map_generator/coverage_strategy.rb
213
284
  - lib/crystalball/map_generator/coverage_strategy/execution_detector.rb
214
285
  - lib/crystalball/map_generator/described_class_strategy.rb
286
+ - lib/crystalball/map_generator/factory_bot_strategy.rb
287
+ - lib/crystalball/map_generator/factory_bot_strategy/dsl_patch.rb
288
+ - lib/crystalball/map_generator/factory_bot_strategy/dsl_patch/factory_path_fetcher.rb
289
+ - lib/crystalball/map_generator/factory_bot_strategy/factory_gem_loader.rb
290
+ - lib/crystalball/map_generator/factory_bot_strategy/factory_runner_patch.rb
215
291
  - lib/crystalball/map_generator/helpers/path_filter.rb
216
292
  - lib/crystalball/map_generator/object_sources_detector.rb
217
293
  - lib/crystalball/map_generator/object_sources_detector/definition_tracer.rb
218
294
  - lib/crystalball/map_generator/object_sources_detector/hierarchy_fetcher.rb
295
+ - lib/crystalball/map_generator/parser_strategy.rb
296
+ - lib/crystalball/map_generator/parser_strategy/processor.rb
219
297
  - lib/crystalball/map_generator/strategies_collection.rb
220
298
  - lib/crystalball/map_storage/yaml_storage.rb
221
299
  - lib/crystalball/prediction.rb
222
300
  - lib/crystalball/predictor.rb
223
301
  - lib/crystalball/predictor/associated_specs.rb
302
+ - lib/crystalball/predictor/helpers/affected_example_groups_detector.rb
303
+ - lib/crystalball/predictor/helpers/path_formatter.rb
224
304
  - lib/crystalball/predictor/modified_execution_paths.rb
225
305
  - lib/crystalball/predictor/modified_specs.rb
306
+ - lib/crystalball/predictor/modified_support_specs.rb
307
+ - lib/crystalball/predictor/strategy.rb
226
308
  - lib/crystalball/predictor_evaluator.rb
227
309
  - lib/crystalball/rails.rb
310
+ - lib/crystalball/rails/helpers/base_schema_parser.rb
311
+ - lib/crystalball/rails/helpers/schema_definition_parser.rb
312
+ - lib/crystalball/rails/helpers/schema_definition_parser/active_record.rb
313
+ - lib/crystalball/rails/helpers/schema_definition_parser/table_content_parser.rb
228
314
  - lib/crystalball/rails/map_generator/action_view_strategy.rb
229
315
  - lib/crystalball/rails/map_generator/action_view_strategy/patch.rb
230
316
  - lib/crystalball/rails/map_generator/i18n_strategy.rb
231
317
  - lib/crystalball/rails/map_generator/i18n_strategy/simple_patch.rb
318
+ - lib/crystalball/rails/predictor/modified_schema.rb
319
+ - lib/crystalball/rails/tables_map.rb
320
+ - lib/crystalball/rails/tables_map_generator.rb
321
+ - lib/crystalball/rails/tables_map_generator/configuration.rb
322
+ - lib/crystalball/rspec/filtering.rb
232
323
  - lib/crystalball/rspec/prediction_builder.rb
324
+ - lib/crystalball/rspec/prediction_pruning.rb
325
+ - lib/crystalball/rspec/prediction_pruning/examples_pruner.rb
233
326
  - lib/crystalball/rspec/runner.rb
234
327
  - lib/crystalball/rspec/runner/configuration.rb
235
- - lib/crystalball/simple_predictor.rb
328
+ - lib/crystalball/rspec/standard_prediction_builder.rb
236
329
  - lib/crystalball/source_diff.rb
237
330
  - lib/crystalball/source_diff/file_diff.rb
331
+ - lib/crystalball/source_diff/formatting_checker.rb
238
332
  - lib/crystalball/version.rb
333
+ - mkdocs.yml
239
334
  homepage: https://github.com/toptal/crystalball
240
335
  licenses: []
241
336
  metadata:
@@ -256,7 +351,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
256
351
  version: '0'
257
352
  requirements: []
258
353
  rubyforge_project:
259
- rubygems_version: 2.6.14
354
+ rubygems_version: 2.6.14.1
260
355
  signing_key:
261
356
  specification_version: 4
262
357
  summary: A library for RSpec regression test selection