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
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Crystalball
2
2
 
3
- Crystalball is a Ruby library which implements [Regression Test Selection mechanism](https://tenderlovemaking.com/2015/02/13/predicting-test-failues.html) originally published by Aaron Patterson. Its main purpose is to select a subset of your test suite which should be run to ensure your changes didn't break anything.
3
+ Crystalball is a Ruby library which implements [Regression Test Selection mechanism](https://tenderlovemaking.com/2015/02/13/predicting-test-failues.html) originally published by Aaron Patterson.
4
+ Its main purpose is to select a minimal subset of your test suite which should be run to ensure your changes didn't break anything.
4
5
 
5
6
  [![Build Status](https://travis-ci.org/toptal/crystalball.svg?branch=master)](https://travis-ci.org/toptal/crystalball)
6
7
  [![Maintainability](https://api.codeclimate.com/v1/badges/c8bfc25a43a1a2ecf964/maintainability)](https://codeclimate.com/github/toptal/crystalball/maintainability)
@@ -26,171 +27,25 @@ Or install it yourself as:
26
27
 
27
28
  ## Usage
28
29
 
29
- 1. Start MapGenerator in your `spec_helper` before you loaded any file of your app. E.g.
30
- ```ruby
31
- Crystalball::MapGenerator.start! do |config|
32
- config.register Crystalball::MapGenerator::CoverageStrategy.new
33
- end
34
- ```
35
- 1. Run your test suite on clean branch with green build. This step will generate file `execution_map.yml` in your project root
36
- 1. Make some changes to your app code
37
- 1. Run `bundle exec crystalball` to build a prediction and run RSpec with it. Check out [RSpec runner section](#rspec-runner) for customization details.
30
+ Please see our [official documentation](https://toptal.github.io/crystalball/).
38
31
 
39
- Keep in mind that as your target branch (usually master) code changes your execution maps will become outdated,
40
- so you need to regenerate execution maps regularly.
32
+ ### Versioning
41
33
 
42
- ## Map Generator
43
-
44
- There are different map generator strategies that can (and should) be used together for better predictions. Each one has its own benefits and drawbacks, so they should be configured to best fit your needs.
45
-
46
- ### CoverageStrategy
47
-
48
- Uses coverage information to detect which files are covered by the given spec (i.e. the files that, if changed, may potentially break the spec);
49
- To customize the way the execution detection works, pass an object that responds to #detect and returns the paths to the strategy initialization:
50
-
51
- ```ruby
52
- # ...
53
- config.register Crystalball::MapGenerator::CoverageStrategy.new(MyDetector)
54
- ```
55
-
56
- By default, the execution detector is a `Crystalball::MapGenerator::CoverageStrategy::ExecutionDetector`, which filters out the paths outside the root and converts absolute paths to relative.
57
-
58
- ### AllocatedObjectsStrategy
59
-
60
- Looks for the files in which the objects allocated during the spec execution are defined. It is considerably slower than `CoverageStrategy`.
61
- To use this strategy, use the convenient method `.build` which takes two optional keyword arguments: `only`, used to define the classes or modules to have their descendants tracked (defaults to `[]`); and `root`, which is the path where the detection will take place (defaults to `Dir.pwd`).
62
- Here's an example that tracks allocation of `ActiveRecord::Base` objects:
63
-
64
- ```ruby
65
- # ...
66
- config.register Crystalball::MapGenerator::AllocatedObjectsStrategy.build(only: ['ActiveRecord::Base'])
67
- ```
68
-
69
- That method is fine for most uses, but if you need to further customize the behavior of the strategy, you can directly instantiate the class.
70
-
71
- ```ruby
72
- # ...
73
- config.register Crystalball::MapGenerator::AllocatedObjectsStrategy
74
- .new(execution_detector: MyCustomDetector, object_tracker: MyCustomTracker)
75
- ```
76
-
77
- The initialization takes two keyword arguments: `execution_detector` and `object_tracker`.
78
- `execution_detector` must be an object that responds to `#detect` receiving a list of objects and returning the paths affected by said objects. `object_tracker` is something that responds to `#used_classes_during` which yields to the caller and returns the array of classes of objects allocated during the execution of the block.
79
-
80
- ### DescribedClassStrategy
81
-
82
- This strategy will take each example that has a `described_class` (i.e. examples inside `describe` blocks of classes and not strings) and add the paths where the described class and its ancestors are defined to the case map of the example;
83
-
84
- To use it, add to your `Crystalball::MapGenerator.start!` block:
85
-
86
- ```ruby
87
- # ...
88
- config.register Crystalball::MapGenerator::DescribedClassStrategy.new
89
- ```
90
-
91
- As with `AllocatedObjectsStrategy`, you can pass a custom execution detector (an object that responds to `#detect` and returns the paths) to the initialization:
92
-
93
- ```ruby
94
- # ...
95
- config.register Crystalball::MapGenerator::DescribedClassStrategy.new(MyDetector)
96
- ```
97
-
98
- ### Rails specific strategies
99
-
100
- To use Rails specific strategies you must first `require 'crystalball/rails'`.
101
-
102
- #### ActionViewStrategy
103
-
104
- This strategy patches `ActionView::Template#compile!` to map the examples to affected views. Use it as follows:
105
-
106
- ```ruby
107
- # ...
108
- config.register Crystalball::MapGenerator::ActionViewStrategy.new
109
- ```
110
-
111
- #### I18nStrategy
112
-
113
- Patches I18n to have access to the path where the locales are defined, so that those paths can be added to the case map.
114
- To use it, add to your config:
115
-
116
- ```ruby
117
- # ...
118
- config.register Crystalball::MapGenerator::I18nStrategy.new
119
- ```
120
-
121
- ### Custom strategies
122
-
123
- You can create your own strategy and use it with the map generator. Any object that responds to `#call(case_map, example)` (where `case_map` is a `Crystalball::CaseMap` and `example` a `RSpec::Core::Example`) and augmenting its list of affected files using `case_map.push(*paths_to_files)`.
124
- Check out the [implementation](https://github.com/toptal/crystalball/tree/master/lib/crystalball/map_generator) of the default strategies for details.
125
-
126
- 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.
127
-
128
- ## Predictor
129
-
130
- The predictor can also be customized with different strategies:
131
-
132
- ### AssociatedSpecs
133
-
134
- Needs to be configured with rules for detecting which specs should be on the prediction.
135
- `predictor.use Crystalball::Predictor::AssociatedSpecs.new(from: %r{models/(.*).rb}, to: "./spec/models/%s_spec.rb")`
136
- will add `./spec/models/foo_spec.rb` to prediction when `models/foo.rb` changes.
137
- This strategy does not depend on a previoulsy generated case map.
138
-
139
- ### ModifiedExecutionPaths
140
-
141
- Checks the case map and the diff to see which specs are affected by the new or modified files.
142
-
143
- ### ModifiedSpecs
144
-
145
- As the name implies, checks for modified specs. The scope can be modified by passing a regex as argument, which defaults to `%r{spec/.*_spec\.rb\z}`.
146
- This strategy does not depend on a previously generated case map.
147
-
148
- ### Custom strategies
149
-
150
- As with the map generator you may define custom strategies for prediction. It must be an object that responds to `#call(diff, case_map)` (where `diff` is a `Crystalball::SourceDiff` and `case_map` is a `Crystalball::CaseMap`) and returns an array of paths.
151
-
152
- Check out [default strategies implementation](https://github.com/toptal/crystalball/tree/master/lib/crystalball/predictor) for details.
153
-
154
- ## Under the hood
155
-
156
- TODO: Write good description for anyone who wants to customize behavior
157
-
158
- ## Spring integration
159
-
160
- It's very easy to integrate Crystalball with [Spring](https://github.com/rails/spring). Check out [spring-commands-crystalball](https://github.com/pluff/spring-commands-crystalball) for details.
161
-
162
- ## Plans
163
-
164
- 1. RSpec parallel integration
165
- 1. Map size optimization
166
- 1. Different strategies for execution map
167
- 1. Different strategies for failure predictor
168
- 1. Integration for git hook
169
-
170
- ## RSpec Runner
171
-
172
- There is a custom RSpec runner you can use in your development with `bundle exec crystalball` command. It builds a prediction and runs it.
173
-
174
- ### Runner Configuration
175
-
176
- #### Config file
177
-
178
- Create a YAML file for the runner. Default locations are `./crystalball.yml` and `./config/crystalball.yml`. You can override config path with `CRYSTALBALL_CONFIG` env variable.
179
- Please check an [example of a config file](https://github.com/toptal/crystalball/blob/master/spec/fixtures/crystalball.yml) for available options
180
-
181
- #### Environment variables
182
-
183
- `CRYSTALBALL_CONFIG=path/to/crystalball.yml` if you want to override default path to config file.
184
- `CRYSTALBALL_SKIP_MAP_CHECK=true` if you want to skip maps expiration period check.
185
- `CRYSTALBALL_SKIP_EXAMPLES_LIMIT=true` if you want to skip examples limit check.
34
+ We use [semantic versioning](https://semver.org/) for our [releases](https://github.com/toptal/crystalball/releases).
186
35
 
187
36
  ## Development
188
37
 
189
38
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
190
39
 
191
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
40
+ To install this gem onto your local machine, run `bundle exec rake install`.
192
41
 
193
42
  ## Contributing
194
43
 
195
- Bug reports and pull requests are welcome on GitHub at https://github.com/toptal/crystalball. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
44
+ Bug reports and pull requests are welcome on GitHub at https://github.com/toptal/crystalball.
45
+ This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
46
+
47
+ ## License
48
+
49
+ Crystalball is released under the [MIT License](https://opensource.org/licenses/MIT).
50
+
196
51
 
data/crystalball.gemspec CHANGED
@@ -7,7 +7,7 @@ require 'crystalball/version'
7
7
  Gem::Specification.new do |spec|
8
8
  spec.name = "crystalball"
9
9
  spec.version = Crystalball::VERSION
10
- spec.authors = ["Pavel Shutsin"]
10
+ spec.authors = ["Pavel Shutsin", "Evgenii Pecherkin", "Jaimerson Araujo"]
11
11
  spec.email = ["publicshady@gmail.com"]
12
12
 
13
13
  spec.summary = 'A library for RSpec regression test selection'
@@ -35,14 +35,18 @@ Gem::Specification.new do |spec|
35
35
  spec.required_ruby_version = '> 2.3.0'
36
36
 
37
37
  spec.add_development_dependency 'actionview'
38
+ spec.add_development_dependency 'activerecord'
38
39
  spec.add_development_dependency "bundler", "~> 1.14"
40
+ spec.add_development_dependency 'factory_bot'
39
41
  spec.add_development_dependency 'i18n'
42
+ spec.add_development_dependency 'parser'
40
43
  spec.add_development_dependency 'pry'
41
44
  spec.add_development_dependency 'pry-byebug'
42
45
  spec.add_development_dependency "rake", "~> 10.0"
43
46
  spec.add_development_dependency "rspec", "~> 3.0"
44
- spec.add_development_dependency 'rubocop'
47
+ spec.add_development_dependency 'rubocop', ">= 0.56"
45
48
  spec.add_development_dependency 'rubocop-rspec'
46
49
  spec.add_development_dependency 'simplecov'
50
+ spec.add_development_dependency 'sqlite3'
47
51
  spec.add_development_dependency 'yard'
48
52
  end
Binary file
data/docs/img/logo.png ADDED
Binary file
data/docs/index.md ADDED
@@ -0,0 +1,44 @@
1
+ # Crystalball
2
+
3
+ Crystalball is a Ruby library which implements [Regression Test Selection mechanism](https://tenderlovemaking.com/2015/02/13/predicting-test-failues.html) originally published by Aaron Patterson.
4
+ Its main purpose is to select a minimal subset of your test suite which should be run to ensure your changes didn't break anything.
5
+
6
+ ## Installation
7
+
8
+ Please check our [installation instructions](https://github.com/toptal/crystalball#installation).
9
+
10
+ ## Basic Usage
11
+
12
+ 1. Start MapGenerator in your `spec_helper` before you loaded any file of your app. E.g.
13
+
14
+ if ENV['CRYSTALBALL'] == 'true' do
15
+ Crystalball::MapGenerator.start! do |config|
16
+ config.register Crystalball::MapGenerator::CoverageStrategy.new
17
+ end
18
+ end
19
+
20
+ 1. Run your test suite with Crystaball enabled on clean master branch with green build. `CRYSTALBALL=true bundle exec rspec .` This step will generate file `tmp/crystalball_data.yml` in your project root. This file contains useful profiling data for Crystalball.
21
+ 1. Make some changes to your app code
22
+ 1. Run `bundle exec crystalball` to build a prediction and run RSpec with it. Check out [RSpec runner section](runner.md) for customization details.
23
+
24
+ Keep in mind that as your target branch (usually master) code changes your execution maps will become outdated,
25
+ so you need to regenerate execution maps regularly.
26
+
27
+ ## Advanced Usage
28
+
29
+ Crystalball workflow can be divided into 2 parts.
30
+ 1. Full build profiling where Crystalball gathers some data about your RSpec suite for later use in predictions. This is where map generators do their job.
31
+ 2. Actual predicting where Crystalball uses profiling info from step above and tries to get best prediction possible. This is where predictors do their job.
32
+
33
+ Both of these steps can be heavily customized and enchanted based on your project specifics and your needs.
34
+
35
+ You might want to check:
36
+
37
+ * [map generators docs](map_generators.md) for details related to suite profiling.
38
+ * [predictors docs](predictors.md) for details related to actual prediction.
39
+ * [runner docs](runner.md) for runner configuration details.
40
+
41
+
42
+ ## Spring integration
43
+
44
+ It's very easy to integrate Crystalball with [Spring](https://github.com/rails/spring). Check out [spring-commands-crystalball](https://github.com/pluff/spring-commands-crystalball) for details.
@@ -0,0 +1,149 @@
1
+ # Map generators
2
+
3
+ ## Execution Map Generator
4
+
5
+ There are different map generator strategies that can (and should) be used together for better predictions. Each one has its own benefits and drawbacks, so they should be configured to best fit your needs.
6
+
7
+ ### Custom map file name
8
+
9
+ You can customize resulting map filename with `map_storage_path` value. E.g.
10
+ ```ruby
11
+ Crystalball::MapGenerator.start! do |config|
12
+ #...
13
+ config.map_storage_path = "execution_map_#{ENV['TEST_ENV_NUMBER'].to_i}.yml"
14
+ end
15
+ ```
16
+
17
+ ### CoverageStrategy
18
+
19
+ Uses coverage information to detect which files are covered by the given spec (i.e. the files that, if changed, may potentially break the spec);
20
+ To customize the way the execution detection works, pass an object that responds to #detect and returns the paths to the strategy initialization:
21
+
22
+ ```ruby
23
+ Crystalball::MapGenerator.start! do |config|
24
+ #...
25
+ config.register Crystalball::MapGenerator::CoverageStrategy.new(my_detector)
26
+ end
27
+ ```
28
+
29
+ By default, the execution detector is a `Crystalball::MapGenerator::CoverageStrategy::ExecutionDetector`, which filters out the paths outside of the project root and converts absolute paths to relative.
30
+
31
+ ### AllocatedObjectsStrategy
32
+
33
+ Looks for the files in which the objects allocated during the spec execution are defined. It is considerably slower than `CoverageStrategy`.
34
+ To use this strategy, use the convenient method `.build` which takes two optional keyword arguments: `only`, used to define the classes or modules to have their descendants tracked (defaults to `[]`); and `root`, which is the path where the detection will take place (defaults to `Dir.pwd`).
35
+ Here's an example that tracks allocation of `ActiveRecord::Base` objects:
36
+
37
+ ```ruby
38
+ Crystalball::MapGenerator.start! do |config|
39
+ #...
40
+ config.register Crystalball::MapGenerator::AllocatedObjectsStrategy.build(only: ['ActiveRecord::Base'])
41
+ end
42
+ ```
43
+
44
+ That method is fine for most uses, but if you need to further customize the behavior of the strategy, you can directly instantiate the class.
45
+
46
+ ```ruby
47
+ Crystalball::MapGenerator.start! do |config|
48
+ #...
49
+ config.register Crystalball::MapGenerator::AllocatedObjectsStrategy
50
+ .new(execution_detector: my_detector, object_tracker: my_tracker)
51
+ end
52
+ ```
53
+
54
+ The initialization takes two keyword arguments: `execution_detector` and `object_tracker`.
55
+ `execution_detector` must be an object that responds to `#detect` receiving a list of objects and returning the paths affected by said objects. `object_tracker` is something that responds to `#used_classes_during` which yields to the caller and returns the array of classes of objects allocated during the execution of the block.
56
+
57
+ ### DescribedClassStrategy
58
+
59
+ This strategy will take each example that has a `described_class` (i.e. examples inside `describe` blocks of classes and not strings) and add the paths where the described class and its ancestors are defined to the example group map of the example;
60
+
61
+ To use it, add to your `Crystalball::MapGenerator.start!` block:
62
+
63
+ ```ruby
64
+ Crystalball::MapGenerator.start! do |config|
65
+ #...
66
+ config.register Crystalball::MapGenerator::DescribedClassStrategy.new
67
+ end
68
+ ```
69
+
70
+ As with `AllocatedObjectsStrategy`, you can pass a custom execution detector (an object that responds to `#detect` and returns the paths) to the initialization:
71
+
72
+ ```ruby
73
+ Crystalball::MapGenerator.start! do |config|
74
+ #...
75
+ config.register Crystalball::MapGenerator::DescribedClassStrategy.new(my_detector)
76
+ end
77
+ ```
78
+
79
+ ### ParserStrategy
80
+
81
+ The `ParserStrategy`, as the name suggests parses the files in order to detect which files are affected by an example.
82
+ It works by first parsing all (`.rb`) files that match the given pattern under the configured root directory (defaults to current directory) to collect the constants definition paths.
83
+ Then, when each example is executed, the used files of the current example group map are parsed to check for method calls to those constants. For that reason, `ParserStrategy` **only works when used with other strategies and is placed at the end of the strategies list**.
84
+
85
+ To use it, add the `parser` gem to your `Gemfile` and:
86
+
87
+ ```ruby
88
+ require 'crystalball/map_generator/parser_strategy'
89
+ Crystalball::MapGenerator.start! do |config|
90
+ #...
91
+ config.register Crystalball::MapGenerator::ParserStrategy.new(pattern: /\A(app)|(lib)/)
92
+ end
93
+ ```
94
+
95
+ ### ActionViewStrategy
96
+
97
+ To use Rails specific strategies you must first `require 'crystalball/rails'`.
98
+ This strategy patches `ActionView::Template#compile!` to map the examples to affected views. Use it as follows:
99
+
100
+ ```ruby
101
+ Crystalball::MapGenerator.start! do |config|
102
+ #...
103
+ config.register Crystalball::MapGenerator::ActionViewStrategy.new
104
+ end
105
+ ```
106
+
107
+ ### I18nStrategy
108
+
109
+ To use Rails specific strategies you must first `require 'crystalball/rails'`.
110
+ Patches I18n to have access to the path where the locales are defined, so that those paths can be added to the example group map.
111
+ To use it, add to your config:
112
+
113
+ ```ruby
114
+ Crystalball::MapGenerator.start! do |config|
115
+ #...
116
+ config.register Crystalball::MapGenerator::I18nStrategy.new
117
+ end
118
+ ```
119
+
120
+ ### FactoryBotStrategy
121
+
122
+ Tracks which factories were used during the example and add files with corresponding definitions to the example group map.
123
+ To use it, add to your config:
124
+ ```ruby
125
+ Crystalball::MapGenerator.start! do |config|
126
+ #...
127
+ config.register Crystalball::MapGenerator::FactoryBotStrategy.new
128
+ end
129
+ ```
130
+
131
+ ### Custom strategies
132
+
133
+ You can create your own strategy and use it with the map generator. Any object that responds to `#call(example_group_map, example)` (where `example_group_map` is a `Crystalball::ExampleGroupMap` and `example` a `RSpec::Core::Example`) and augmenting its list of used files using `example_group_map.push(*paths_to_files)`.
134
+ Check out the [implementation](https://github.com/toptal/crystalball/tree/master/lib/crystalball/map_generator) of the default strategies for examples.
135
+
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
+
138
+ ## TablesMapGenerator
139
+
140
+ 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`.
141
+ Use `Crystalball::Rails::TablesMapGenerator.start!` to start it.
142
+
143
+ By default TablesMapGenerator will generate `tables_map.yml` file. You can customize this behavior by setting `map_storage_path` variable:
144
+ ```ruby
145
+ Crystalball::TablesMapGenerator.start! do |config|
146
+ #...
147
+ config.map_storage_path = 'my_custom_tables_map_name.yml'
148
+ end
149
+ ```
@@ -0,0 +1,75 @@
1
+ # Predictors
2
+
3
+ ## Basic usage
4
+
5
+ [By default](https://github.com/toptal/crystalball/blob/master/lib/crystalball/rspec/runner/configuration.rb) Crystalball uses `Crystalball::RSpec::StandardPredictionBuilder` which uses
6
+ just two strategies for prediction [ModifiedExecutionPaths](#ModifiedExecutionPaths) and [ModifiedSpecs](#ModifiedSpecs).
7
+ This is more than enough for first time use.
8
+
9
+ ## Advanced usage
10
+
11
+ If you want to squeeze out the most out of Crystalball you might want to add additional available prediction strategies or even add your own.
12
+ To do that you should create a class inherited from `Crystalball::RSpec::PredictionBuilder` and overload `predictor` method with your custom predictor setup similar
13
+ to what we have in [StandardPredictionBuilder](https://github.com/toptal/crystalball/blob/master/lib/crystalball/rspec/standard_prediction_builder.rb)
14
+
15
+ E.g.
16
+ ```ruby
17
+ class MyPredictionBuilder < Crystalball::RSpec::PredictionBuilder
18
+ def predictor
19
+ super do |p|
20
+ p.use Crystalball::Predictor::ModifiedSpecs.new
21
+ p.use Crystalball::Predictor::ModifiedExecutionPaths.new
22
+ p.use Crystalball::Predictor::ModifiedSupportSpecs.new
23
+ end
24
+ end
25
+ end
26
+
27
+ ```
28
+ creates a predictor with additional `ModifiedSupportSpecs` strategy enabled.
29
+
30
+ You also must let our runner know about your new prediction builder by adding it to configuration. Please see [runner configuration](runner.md) page for details.
31
+
32
+ ### Strategies
33
+
34
+ #### AssociatedSpecs
35
+
36
+ Needs to be configured with rules for detecting which specs should be on the prediction.
37
+ ```ruby
38
+ predictor.use Crystalball::Predictor::AssociatedSpecs.new(
39
+ from: %r{models/(.*).rb},
40
+ to: "./spec/models/%s_spec.rb"
41
+ )
42
+ ```
43
+ will add `./spec/models/foo_spec.rb` to prediction when `models/foo.rb` changes.
44
+ This strategy does not depend on a previously generated example group map.
45
+
46
+ #### ModifiedExecutionPaths
47
+
48
+ Checks the example group map and the diff to see which specs are affected by the new or modified files.
49
+
50
+ #### ModifiedSpecs
51
+
52
+ As the name implies, checks for modified specs. The scope can be modified by passing a regex as argument, which defaults to `%r{spec/.*_spec\.rb\z}`.
53
+ This strategy does not depend on a previously generated example group map.
54
+
55
+ #### ModifiedSupportSpecs
56
+
57
+ Checks for modified support files used in specs and predicts full spec file. The scope can be modified by passing a regex as argument, which defaults to `%r{spec/support/.*\.rb\z}`.
58
+ Mostly usable for shared_contexts and shared_examples.
59
+
60
+ #### ModifiedSchema
61
+
62
+ Checks for modified db schema in rails application. You need to specify a path to a file with tables map generated by `TablesMapGenerator`. It checks schema diff to see which models are affected by modified tables and which specs are affected by this models.
63
+ ```ruby
64
+ predictor.use Crystalball::Rails::Predictor::ModifiedSchema.new(
65
+ tables_map_path: './tables_map.yml'
66
+ )
67
+ ```
68
+
69
+ _**Note**_: You may meet a warning like "WARNING: there are no model files for changed table ...". Usually, such tables are leftovers or a relation table for `has-and-belongs-to-many` associations. For the first case - nothing to worry about. For the second case - it means you want to change the default relation table.
70
+
71
+ ### Custom strategies
72
+
73
+ As with the map generator you may define custom strategies for prediction. It must be an object that responds to `#call(diff, example_group_map)` (where `diff` is a `Crystalball::SourceDiff` and `example_group_map` is a `Crystalball::ExampleGroupMap`) and returns an array of paths.
74
+
75
+ Check out [default strategies implementation](https://github.com/toptal/crystalball/tree/master/lib/crystalball/predictor) for examples.