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