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.
- checksums.yaml +4 -4
- data/.gitignore +4 -1
- data/.rubocop.yml +4 -0
- data/.travis.yml +1 -1
- data/CHANGELOG.md +18 -0
- data/LICENSE +22 -674
- data/README.md +13 -158
- data/crystalball.gemspec +6 -2
- data/docs/img/favicon.ico +0 -0
- data/docs/img/logo.png +0 -0
- data/docs/index.md +44 -0
- data/docs/map_generators.md +149 -0
- data/docs/predictors.md +75 -0
- data/docs/runner.md +24 -0
- data/lib/crystalball.rb +8 -3
- data/lib/crystalball/active_record.rb +4 -0
- data/lib/crystalball/example_group_map.rb +19 -0
- data/lib/crystalball/execution_map.rb +17 -16
- data/lib/crystalball/extensions/git.rb +4 -0
- data/lib/crystalball/extensions/git/base.rb +14 -0
- data/lib/crystalball/extensions/git/lib.rb +18 -0
- data/lib/crystalball/factory_bot.rb +3 -0
- data/lib/crystalball/git_repo.rb +2 -7
- data/lib/crystalball/logging.rb +51 -0
- data/lib/crystalball/map_generator.rb +5 -7
- data/lib/crystalball/map_generator/allocated_objects_strategy.rb +6 -5
- data/lib/crystalball/map_generator/allocated_objects_strategy/object_tracker.rb +1 -0
- data/lib/crystalball/map_generator/base_strategy.rb +5 -4
- data/lib/crystalball/map_generator/configuration.rb +1 -1
- data/lib/crystalball/map_generator/coverage_strategy.rb +6 -5
- data/lib/crystalball/map_generator/described_class_strategy.rb +5 -5
- data/lib/crystalball/map_generator/factory_bot_strategy.rb +59 -0
- data/lib/crystalball/map_generator/factory_bot_strategy/dsl_patch.rb +40 -0
- data/lib/crystalball/map_generator/factory_bot_strategy/dsl_patch/factory_path_fetcher.rb +30 -0
- data/lib/crystalball/map_generator/factory_bot_strategy/factory_gem_loader.rb +27 -0
- data/lib/crystalball/map_generator/factory_bot_strategy/factory_runner_patch.rb +25 -0
- data/lib/crystalball/map_generator/parser_strategy.rb +60 -0
- data/lib/crystalball/map_generator/parser_strategy/processor.rb +129 -0
- data/lib/crystalball/map_generator/strategies_collection.rb +9 -9
- data/lib/crystalball/map_storage/yaml_storage.rb +7 -6
- data/lib/crystalball/prediction.rb +12 -11
- data/lib/crystalball/predictor.rb +7 -5
- data/lib/crystalball/predictor/associated_specs.rb +9 -4
- data/lib/crystalball/predictor/helpers/affected_example_groups_detector.rb +20 -0
- data/lib/crystalball/predictor/helpers/path_formatter.rb +18 -0
- data/lib/crystalball/predictor/modified_execution_paths.rb +11 -5
- data/lib/crystalball/predictor/modified_specs.rb +8 -2
- data/lib/crystalball/predictor/modified_support_specs.rb +39 -0
- data/lib/crystalball/predictor/strategy.rb +16 -0
- data/lib/crystalball/predictor_evaluator.rb +1 -1
- data/lib/crystalball/rails.rb +1 -0
- data/lib/crystalball/rails/helpers/base_schema_parser.rb +51 -0
- data/lib/crystalball/rails/helpers/schema_definition_parser.rb +36 -0
- data/lib/crystalball/rails/helpers/schema_definition_parser/active_record.rb +21 -0
- data/lib/crystalball/rails/helpers/schema_definition_parser/table_content_parser.rb +27 -0
- data/lib/crystalball/rails/map_generator/action_view_strategy.rb +5 -5
- data/lib/crystalball/rails/map_generator/action_view_strategy/patch.rb +1 -1
- data/lib/crystalball/rails/map_generator/i18n_strategy.rb +5 -5
- data/lib/crystalball/rails/map_generator/i18n_strategy/simple_patch.rb +1 -0
- data/lib/crystalball/rails/predictor/modified_schema.rb +81 -0
- data/lib/crystalball/rails/tables_map.rb +53 -0
- data/lib/crystalball/rails/tables_map_generator.rb +84 -0
- data/lib/crystalball/rails/tables_map_generator/configuration.rb +39 -0
- data/lib/crystalball/rspec/filtering.rb +52 -0
- data/lib/crystalball/rspec/prediction_builder.rb +21 -11
- data/lib/crystalball/rspec/prediction_pruning.rb +56 -0
- data/lib/crystalball/rspec/prediction_pruning/examples_pruner.rb +70 -0
- data/lib/crystalball/rspec/runner.rb +39 -27
- data/lib/crystalball/rspec/runner/configuration.rb +24 -14
- data/lib/crystalball/rspec/standard_prediction_builder.rb +17 -0
- data/lib/crystalball/source_diff.rb +12 -2
- data/lib/crystalball/source_diff/file_diff.rb +1 -1
- data/lib/crystalball/source_diff/formatting_checker.rb +50 -0
- data/lib/crystalball/version.rb +1 -1
- data/mkdocs.yml +23 -0
- metadata +102 -7
- data/lib/crystalball/case_map.rb +0 -19
- data/lib/crystalball/simple_predictor.rb +0 -18
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Crystalball
|
4
|
+
module Rails
|
5
|
+
# Storage for tables map
|
6
|
+
class TablesMap
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
# Simple data object for map metadata information
|
10
|
+
class Metadata
|
11
|
+
attr_reader :commit, :version
|
12
|
+
|
13
|
+
# @param [String] commit - SHA of commit
|
14
|
+
# @param [Numeric] version - map generator version number
|
15
|
+
def initialize(commit: nil, version: nil, **_)
|
16
|
+
@commit = commit
|
17
|
+
@version = version
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_h
|
21
|
+
{type: TablesMap.name, commit: commit, version: version}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :example_groups, :metadata
|
26
|
+
|
27
|
+
delegate %i[commit version] => :metadata
|
28
|
+
delegate %i[size [] []=] => :example_groups
|
29
|
+
|
30
|
+
# @param [Hash] metadata - add or override metadata of execution map
|
31
|
+
# @param [Hash] example_groups - initial list of tables
|
32
|
+
def initialize(metadata: {}, example_groups: {})
|
33
|
+
@metadata = Metadata.new(**metadata)
|
34
|
+
@example_groups = example_groups
|
35
|
+
end
|
36
|
+
|
37
|
+
# Remove all example_groups
|
38
|
+
def clear!
|
39
|
+
self.example_groups = {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def add(files:, for_table:)
|
43
|
+
example_groups[for_table] ||= []
|
44
|
+
example_groups[for_table] += files
|
45
|
+
example_groups[for_table].uniq!
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
attr_writer :example_groups, :metadata
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'crystalball/rails/tables_map'
|
4
|
+
require 'crystalball/rails/tables_map_generator/configuration'
|
5
|
+
|
6
|
+
module Crystalball
|
7
|
+
module Rails
|
8
|
+
# Class to generate tables to files map during RSpec build execution
|
9
|
+
class TablesMapGenerator
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
attr_reader :configuration
|
13
|
+
delegate %i[map_storage object_sources_detector] => :configuration
|
14
|
+
|
15
|
+
class << self
|
16
|
+
# Registers Crystalball handlers to generate execution map during specs execution
|
17
|
+
#
|
18
|
+
# @param [Proc] block to configure MapGenerator and Register strategies
|
19
|
+
def start!(&block)
|
20
|
+
generator = new(&block)
|
21
|
+
|
22
|
+
::RSpec.configure do |c|
|
23
|
+
c.before(:suite) { generator.start! }
|
24
|
+
c.after(:suite) { generator.finalize! }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@configuration = Configuration.new
|
31
|
+
@configuration.commit = repo.object('HEAD').sha if repo
|
32
|
+
yield @configuration if block_given?
|
33
|
+
object_sources_detector.after_register
|
34
|
+
end
|
35
|
+
|
36
|
+
# Prepares metadata for execution map
|
37
|
+
def start!
|
38
|
+
self.map = nil
|
39
|
+
map_storage.clear!
|
40
|
+
|
41
|
+
map_storage.dump(map.metadata.to_h)
|
42
|
+
|
43
|
+
self.started = true
|
44
|
+
end
|
45
|
+
|
46
|
+
# Finalizes and saves map
|
47
|
+
def finalize!
|
48
|
+
return unless started
|
49
|
+
|
50
|
+
collect_tables_info
|
51
|
+
|
52
|
+
object_sources_detector.before_finalize
|
53
|
+
map_storage.dump(map.example_groups) if map.size.positive?
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [Crystalball::Rails::TablesMap]
|
57
|
+
def map
|
58
|
+
@map ||= TablesMap.new(metadata: {commit: configuration.commit, version: configuration.version})
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def repo
|
64
|
+
@repo = GitRepo.open('.') unless defined?(@repo)
|
65
|
+
@repo
|
66
|
+
end
|
67
|
+
|
68
|
+
def collect_tables_info
|
69
|
+
ActiveRecord::Base.descendants.each do |descendant|
|
70
|
+
table_name = descendant.table_name
|
71
|
+
|
72
|
+
next if table_name.nil?
|
73
|
+
|
74
|
+
files = object_sources_detector.detect([descendant])
|
75
|
+
|
76
|
+
map.add(files: files, for_table: table_name)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
attr_writer :map
|
81
|
+
attr_accessor :started
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'crystalball/map_generator/object_sources_detector'
|
4
|
+
|
5
|
+
module Crystalball
|
6
|
+
module Rails
|
7
|
+
class TablesMapGenerator
|
8
|
+
# Configuration of tables map generator. Is can be accessed as a first argument inside
|
9
|
+
# `Crystalball::Rails::TablesMapGenerator.start! { |config| config } block.
|
10
|
+
class Configuration
|
11
|
+
attr_writer :map_storage
|
12
|
+
attr_accessor :commit
|
13
|
+
attr_accessor :version
|
14
|
+
attr_writer :root_path
|
15
|
+
attr_writer :object_sources_detector
|
16
|
+
|
17
|
+
def map_storage_path
|
18
|
+
@map_storage_path ||= Pathname('tables_map.yml')
|
19
|
+
end
|
20
|
+
|
21
|
+
def map_storage_path=(value)
|
22
|
+
@map_storage_path = Pathname(value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def map_storage
|
26
|
+
@map_storage ||= MapStorage::YAMLStorage.new(map_storage_path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def root_path
|
30
|
+
@root_path ||= Dir.pwd
|
31
|
+
end
|
32
|
+
|
33
|
+
def object_sources_detector
|
34
|
+
@object_sources_detector ||= ::Crystalball::MapGenerator::ObjectSourcesDetector.new(root_path: root_path)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Crystalball
|
4
|
+
module RSpec
|
5
|
+
# This class is meant to remove the example filtering options
|
6
|
+
# for example_groups when a prediction contains a file path and the same file
|
7
|
+
# example id.
|
8
|
+
#
|
9
|
+
# For example, if a prediction contains `./spec/foo_spec.rb[1:1] ./spec/foo_spec.rb`,
|
10
|
+
# only `./spec/foo_spec.rb[1:1]` would run, because of the way RSpec
|
11
|
+
# filters are designed.
|
12
|
+
#
|
13
|
+
# Therefore, we need to manually remove the filters from such example_groups.
|
14
|
+
class Filtering
|
15
|
+
# @param [RSpec::Core::Configuration] config
|
16
|
+
# @param [Array<String>] paths
|
17
|
+
def self.remove_unnecessary_filters(config, paths)
|
18
|
+
new(config).remove_unnecessary_filters(paths)
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(configuration)
|
22
|
+
@configuration = configuration
|
23
|
+
end
|
24
|
+
|
25
|
+
def remove_unnecessary_filters(files_or_directories)
|
26
|
+
directories, files = files_or_directories.partition { |f| File.directory?(f) }
|
27
|
+
remove_unecessary_filters_from_files(files)
|
28
|
+
remove_unecessary_filters_from_directories(directories)
|
29
|
+
end
|
30
|
+
|
31
|
+
def remove_unecessary_filters_from_directories(directories)
|
32
|
+
directories.each do |dir|
|
33
|
+
files = configuration.__send__(:gather_directories, dir)
|
34
|
+
remove_unecessary_filters_from_files(files)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def remove_unecessary_filters_from_files(files)
|
39
|
+
files.select { |f| ::RSpec::Core::Example.parse_id(f).last.nil? }.each do |file|
|
40
|
+
next remove_unecessary_filters(fd) if File.directory?(file)
|
41
|
+
|
42
|
+
path = ::RSpec::Core::Metadata.relative_path(File.expand_path(file))
|
43
|
+
configuration.filter_manager.inclusions[:ids]&.delete(path)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
attr_reader :configuration
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -14,29 +14,39 @@ module Crystalball
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def prediction
|
17
|
-
|
17
|
+
predictor.prediction
|
18
18
|
end
|
19
19
|
|
20
20
|
def expired_map?
|
21
|
-
|
21
|
+
expiration_period = config['map_expiration_period'].to_i
|
22
|
+
return false unless expiration_period.positive?
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
map_commit.date < Time.now - config['map_expiration_period']
|
24
|
+
execution_map.timestamp.to_i <= Time.now.to_i - config['map_expiration_period']
|
26
25
|
end
|
27
26
|
|
28
|
-
def
|
29
|
-
@
|
27
|
+
def execution_map
|
28
|
+
@execution_map ||= Crystalball::MapStorage::YAMLStorage.load(config['execution_map_path'])
|
30
29
|
end
|
31
30
|
|
32
|
-
private
|
33
|
-
|
34
31
|
def repo
|
35
32
|
@repo ||= Crystalball::GitRepo.open(config['repo_path'])
|
36
33
|
end
|
37
34
|
|
38
|
-
|
39
|
-
|
35
|
+
private
|
36
|
+
|
37
|
+
# This method should be overridden in ancestor. Example:
|
38
|
+
#
|
39
|
+
# def predictor
|
40
|
+
# super do |p|
|
41
|
+
# p.use Crystalball::Predictor::ModifiedExecutionPaths.new
|
42
|
+
# p.use Crystalball::Predictor::ModifiedSpecs.new
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
def predictor(&block)
|
47
|
+
raise NotImplementedError, 'Configure `prediction_builder_class_name` in `crystalball.yml` and override `predictor` method' unless block_given?
|
48
|
+
|
49
|
+
@predictor ||= Crystalball::Predictor.new(execution_map, repo, from: config['diff_from'], to: config['diff_to'], &block)
|
40
50
|
end
|
41
51
|
end
|
42
52
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'crystalball/rspec/prediction_pruning/examples_pruner'
|
4
|
+
|
5
|
+
module Crystalball
|
6
|
+
module RSpec
|
7
|
+
# Module contains logic related to examples_limit configuration option for our runner.
|
8
|
+
module PredictionPruning
|
9
|
+
def self.included(base)
|
10
|
+
base.extend ClassMethods
|
11
|
+
end
|
12
|
+
|
13
|
+
# Class methods for prediction pruning logic
|
14
|
+
module ClassMethods
|
15
|
+
def examples_limit
|
16
|
+
config['examples_limit'].to_i
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def prune_prediction_to_limit(prediction)
|
22
|
+
return prediction if !examples_limit.positive? || prediction.size <= examples_limit
|
23
|
+
|
24
|
+
Crystalball.log :warn, "Prediction size #{prediction.size} is over the limit (#{examples_limit})"
|
25
|
+
Crystalball.log :warn, "Prediction is pruned to fit the limit!"
|
26
|
+
|
27
|
+
# Actual examples size is not less than prediction size.
|
28
|
+
prediction.first(examples_limit)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def examples_limit
|
35
|
+
self.class.examples_limit
|
36
|
+
end
|
37
|
+
|
38
|
+
def reconfiguration_needed?
|
39
|
+
examples_limit.positive? && @world.example_count > examples_limit
|
40
|
+
end
|
41
|
+
|
42
|
+
def reconfigure_to_limit
|
43
|
+
pruner = ExamplesPruner.new(@world, to: examples_limit)
|
44
|
+
|
45
|
+
@options = ::RSpec::Core::ConfigurationOptions.new(pruner.pruned_set)
|
46
|
+
@world.reset
|
47
|
+
@world.filtered_examples.clear
|
48
|
+
@world.instance_variable_get(:@example_group_counts_by_spec_file).clear
|
49
|
+
@configuration.reset
|
50
|
+
@configuration.reset_filters
|
51
|
+
|
52
|
+
@options.configure(@configuration)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Crystalball
|
4
|
+
module RSpec
|
5
|
+
module PredictionPruning
|
6
|
+
# A class to prune given world example groups to fit the limit.
|
7
|
+
class ExamplesPruner
|
8
|
+
# Simple data object for holding context ids array with total examples size
|
9
|
+
class ContextIdsSet
|
10
|
+
attr_reader :ids, :size
|
11
|
+
alias to_a ids
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@size = 0
|
15
|
+
@ids = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(id, size = 1)
|
19
|
+
@size += size
|
20
|
+
@ids << id
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :world, :limit
|
25
|
+
|
26
|
+
# @param [RSpec::Core::World] rspec_world RSpec world instance
|
27
|
+
# @param [Integer] to upper bound limit for prediction.
|
28
|
+
def initialize(rspec_world, to:)
|
29
|
+
@world = rspec_world
|
30
|
+
@limit = to
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Array<String>] set of example and context ids to run
|
34
|
+
def pruned_set
|
35
|
+
resulting_set = ContextIdsSet.new
|
36
|
+
world.ordered_example_groups.each { |g| prune_to_limit(g, resulting_set) }
|
37
|
+
resulting_set.to_a
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def prune_to_limit(group, resulting_set)
|
43
|
+
return if resulting_set.size >= limit
|
44
|
+
|
45
|
+
group_size = world.example_count([group])
|
46
|
+
|
47
|
+
if resulting_set.size + group_size > limit
|
48
|
+
(group.descendants - [group]).each do |g|
|
49
|
+
prune_to_limit(g, resulting_set)
|
50
|
+
end
|
51
|
+
|
52
|
+
add_examples(group, resulting_set)
|
53
|
+
else
|
54
|
+
resulting_set.add(group.id, group_size)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_examples(group, resulting_set)
|
59
|
+
limit_diff = limit - resulting_set.size
|
60
|
+
|
61
|
+
return unless limit_diff.positive?
|
62
|
+
|
63
|
+
group.filtered_examples.first(limit_diff).each do |example|
|
64
|
+
resulting_set.add(example.id)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -2,17 +2,26 @@
|
|
2
2
|
|
3
3
|
require 'rspec/core'
|
4
4
|
require 'crystalball/rspec/prediction_builder'
|
5
|
+
require 'crystalball/rspec/filtering'
|
6
|
+
require 'crystalball/rspec/prediction_pruning'
|
5
7
|
|
6
8
|
module Crystalball
|
7
9
|
module RSpec
|
8
10
|
# Our custom RSpec runner to run predictions
|
9
11
|
class Runner < ::RSpec::Core::Runner
|
12
|
+
include PredictionPruning
|
13
|
+
|
10
14
|
class << self
|
11
15
|
def run(args, err = $stderr, out = $stdout)
|
12
16
|
return config['runner_class'].run(args, err, out) unless config['runner_class'] == self
|
13
17
|
|
14
|
-
|
15
|
-
|
18
|
+
Crystalball.log :info, "Crystalball starts to glow..."
|
19
|
+
prediction = build_prediction
|
20
|
+
|
21
|
+
Crystalball.log :debug, "Prediction: #{prediction.first(5).join(' ')}#{'...' if prediction.size > 5}"
|
22
|
+
Crystalball.log :info, "Starting RSpec."
|
23
|
+
|
24
|
+
super(args + prediction, err, out)
|
16
25
|
end
|
17
26
|
|
18
27
|
def reset!
|
@@ -21,11 +30,11 @@ module Crystalball
|
|
21
30
|
end
|
22
31
|
|
23
32
|
def prepare
|
24
|
-
config['runner_class'].
|
33
|
+
config['runner_class'].load_execution_map
|
25
34
|
end
|
26
35
|
|
27
36
|
def prediction_builder
|
28
|
-
@prediction_builder ||=
|
37
|
+
@prediction_builder ||= config['prediction_builder_class'].new(config)
|
29
38
|
end
|
30
39
|
|
31
40
|
def config
|
@@ -43,9 +52,9 @@ module Crystalball
|
|
43
52
|
|
44
53
|
protected
|
45
54
|
|
46
|
-
def
|
47
|
-
check_map
|
48
|
-
prediction_builder.
|
55
|
+
def load_execution_map
|
56
|
+
check_map
|
57
|
+
prediction_builder.execution_map
|
49
58
|
end
|
50
59
|
|
51
60
|
private
|
@@ -58,35 +67,38 @@ module Crystalball
|
|
58
67
|
file.exist? ? file : nil
|
59
68
|
end
|
60
69
|
|
61
|
-
def build_prediction
|
62
|
-
check_map
|
63
|
-
|
64
|
-
out.puts "Prediction: #{prediction.first(5).join(' ')}#{'...' if prediction.size > 5}"
|
65
|
-
out.puts "Starting RSpec."
|
66
|
-
prediction
|
70
|
+
def build_prediction
|
71
|
+
check_map
|
72
|
+
prune_prediction_to_limit(prediction_builder.prediction.sort_by(&:length))
|
67
73
|
end
|
68
74
|
|
69
|
-
def check_map
|
70
|
-
|
75
|
+
def check_map
|
76
|
+
Crystalball.log :warn, 'Maps are outdated!' if prediction_builder.expired_map?
|
71
77
|
end
|
72
78
|
end
|
73
79
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
end
|
80
|
+
def setup(err, out)
|
81
|
+
configure(err, out)
|
82
|
+
@configuration.load_spec_files
|
78
83
|
|
79
|
-
|
80
|
-
limit = self.class.config['examples_limit'].to_i
|
81
|
-
return if ENV['CRYSTALBALL_SKIP_EXAMPLES_LIMIT'] || !limit.positive?
|
84
|
+
Filtering.remove_unnecessary_filters(@configuration, @options.options[:files_or_directories_to_run])
|
82
85
|
|
83
|
-
|
86
|
+
if reconfiguration_needed?
|
87
|
+
Crystalball.log :warn, "Prediction examples size #{@world.example_count} is over the limit (#{examples_limit})"
|
88
|
+
Crystalball.log :warn, "Prediction is pruned to fit the limit!"
|
84
89
|
|
85
|
-
|
90
|
+
reconfigure_to_limit
|
91
|
+
@configuration.load_spec_files
|
92
|
+
end
|
93
|
+
|
94
|
+
@world.announce_filters
|
95
|
+
end
|
86
96
|
|
87
|
-
|
88
|
-
|
89
|
-
|
97
|
+
# Backward compatibility for RSpec < 3.7
|
98
|
+
def configure(err, out)
|
99
|
+
@configuration.error_stream = err
|
100
|
+
@configuration.output_stream = out if @configuration.output_stream == $stdout
|
101
|
+
@options.configure(@configuration)
|
90
102
|
end
|
91
103
|
end
|
92
104
|
end
|