crystalball 0.5.0 → 0.6.0

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