knapsack_pro 1.20.2 → 1.21.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/README.md +24 -12
  4. data/lib/knapsack_pro.rb +6 -0
  5. data/lib/knapsack_pro/adapters/base_adapter.rb +16 -0
  6. data/lib/knapsack_pro/adapters/rspec_adapter.rb +11 -9
  7. data/lib/knapsack_pro/allocator.rb +7 -5
  8. data/lib/knapsack_pro/allocator_builder.rb +2 -1
  9. data/lib/knapsack_pro/base_allocator_builder.rb +41 -10
  10. data/lib/knapsack_pro/build_distribution_fetcher.rb +57 -0
  11. data/lib/knapsack_pro/client/api/v1/build_distributions.rb +13 -0
  12. data/lib/knapsack_pro/client/connection.rb +30 -12
  13. data/lib/knapsack_pro/config/env.rb +4 -0
  14. data/lib/knapsack_pro/queue_allocator.rb +7 -5
  15. data/lib/knapsack_pro/queue_allocator_builder.rb +2 -1
  16. data/lib/knapsack_pro/runners/queue/rspec_runner.rb +7 -0
  17. data/lib/knapsack_pro/runners/rspec_runner.rb +5 -2
  18. data/lib/knapsack_pro/slow_test_file_determiner.rb +28 -0
  19. data/lib/knapsack_pro/slow_test_file_finder.rb +27 -0
  20. data/lib/knapsack_pro/test_case_detectors/rspec_test_example_detector.rb +18 -2
  21. data/lib/knapsack_pro/test_case_mergers/base_merger.rb +29 -0
  22. data/lib/knapsack_pro/test_case_mergers/rspec_merger.rb +34 -0
  23. data/lib/knapsack_pro/test_file_finder.rb +43 -5
  24. data/lib/knapsack_pro/test_files_with_test_cases_composer.rb +22 -0
  25. data/lib/knapsack_pro/version.rb +1 -1
  26. data/spec/knapsack_pro/adapters/base_adapter_spec.rb +55 -0
  27. data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +61 -25
  28. data/spec/knapsack_pro/allocator_builder_spec.rb +7 -3
  29. data/spec/knapsack_pro/allocator_spec.rb +7 -5
  30. data/spec/knapsack_pro/base_allocator_builder_spec.rb +79 -27
  31. data/spec/knapsack_pro/build_distribution_fetcher_spec.rb +89 -0
  32. data/spec/knapsack_pro/client/api/v1/build_distributions_spec.rb +31 -0
  33. data/spec/knapsack_pro/client/connection_spec.rb +165 -103
  34. data/spec/knapsack_pro/config/env_spec.rb +14 -0
  35. data/spec/knapsack_pro/queue_allocator_builder_spec.rb +7 -3
  36. data/spec/knapsack_pro/queue_allocator_spec.rb +7 -5
  37. data/spec/knapsack_pro/runners/rspec_runner_spec.rb +4 -4
  38. data/spec/knapsack_pro/slow_test_file_determiner_spec.rb +74 -0
  39. data/spec/knapsack_pro/slow_test_file_finder_spec.rb +43 -0
  40. data/spec/knapsack_pro/test_case_detectors/rspec_test_example_detector_spec.rb +81 -35
  41. data/spec/knapsack_pro/test_case_mergers/base_merger_spec.rb +27 -0
  42. data/spec/knapsack_pro/test_case_mergers/rspec_merger_spec.rb +59 -0
  43. data/spec/knapsack_pro/test_file_finder_spec.rb +105 -29
  44. data/spec/knapsack_pro/test_files_with_test_cases_composer_spec.rb +41 -0
  45. metadata +27 -10
@@ -1,7 +1,8 @@
1
1
  module KnapsackPro
2
2
  class QueueAllocator
3
3
  def initialize(args)
4
- @test_files = args.fetch(:test_files)
4
+ @fast_and_slow_test_files_to_run = args.fetch(:fast_and_slow_test_files_to_run)
5
+ @fallback_mode_test_files = args.fetch(:fallback_mode_test_files)
5
6
  @ci_node_total = args.fetch(:ci_node_total)
6
7
  @ci_node_index = args.fetch(:ci_node_index)
7
8
  @ci_node_build_id = args.fetch(:ci_node_build_id)
@@ -36,14 +37,15 @@ module KnapsackPro
36
37
 
37
38
  private
38
39
 
39
- attr_reader :test_files,
40
+ attr_reader :fast_and_slow_test_files_to_run,
41
+ :fallback_mode_test_files,
40
42
  :ci_node_total,
41
43
  :ci_node_index,
42
44
  :ci_node_build_id,
43
45
  :repository_adapter
44
46
 
45
47
  def encrypted_test_files
46
- KnapsackPro::Crypto::Encryptor.call(test_files)
48
+ KnapsackPro::Crypto::Encryptor.call(fast_and_slow_test_files_to_run)
47
49
  end
48
50
 
49
51
  def encrypted_branch
@@ -63,12 +65,12 @@ module KnapsackPro
63
65
  end
64
66
 
65
67
  def prepare_test_files(response)
66
- decrypted_test_files = KnapsackPro::Crypto::Decryptor.call(test_files, response['test_files'])
68
+ decrypted_test_files = KnapsackPro::Crypto::Decryptor.call(fast_and_slow_test_files_to_run, response['test_files'])
67
69
  KnapsackPro::TestFilePresenter.paths(decrypted_test_files)
68
70
  end
69
71
 
70
72
  def fallback_test_files(executed_test_files)
71
- test_flat_distributor = KnapsackPro::TestFlatDistributor.new(test_files, ci_node_total)
73
+ test_flat_distributor = KnapsackPro::TestFlatDistributor.new(fallback_mode_test_files, ci_node_total)
72
74
  test_files_for_node_index = test_flat_distributor.test_files_for_node(ci_node_index)
73
75
  KnapsackPro::TestFilePresenter.paths(test_files_for_node_index) - executed_test_files
74
76
  end
@@ -2,7 +2,8 @@ module KnapsackPro
2
2
  class QueueAllocatorBuilder < BaseAllocatorBuilder
3
3
  def allocator
4
4
  KnapsackPro::QueueAllocator.new(
5
- test_files: test_files,
5
+ fast_and_slow_test_files_to_run: fast_and_slow_test_files_to_run,
6
+ fallback_mode_test_files: fallback_mode_test_files,
6
7
  ci_node_total: env.ci_node_total,
7
8
  ci_node_index: env.ci_node_index,
8
9
  ci_node_build_id: env.ci_node_build_id,
@@ -134,6 +134,13 @@ module KnapsackPro
134
134
  RSpec.world.example_groups.clear
135
135
  RSpec.configuration.start_time = ::RSpec::Core::Time.now
136
136
 
137
+ if KnapsackPro::Config::Env.rspec_split_by_test_examples?
138
+ # Reset example group counts to ensure scoped example ids in metadata
139
+ # have correct index (not increased by each subsequent run).
140
+ # Solves this problem: https://github.com/rspec/rspec-core/issues/2721
141
+ RSpec.world.instance_variable_set(:@example_group_counts_by_spec_file, Hash.new(0))
142
+ end
143
+
137
144
  # skip reset filters for old RSpec versions
138
145
  if RSpec.configuration.respond_to?(:reset_filters)
139
146
  RSpec.configuration.reset_filters
@@ -16,8 +16,11 @@ module KnapsackPro
16
16
  end
17
17
 
18
18
  RSpec::Core::RakeTask.new(task_name) do |t|
19
- t.rspec_opts = "#{args} --default-path #{runner.test_dir}"
20
- t.pattern = runner.test_file_paths
19
+ # we cannot pass runner.test_file_paths array to t.pattern
20
+ # because pattern does not accept test example path like spec/a_spec.rb[1:2]
21
+ # instead we pass test files and test example paths to t.rspec_opts
22
+ t.pattern = []
23
+ t.rspec_opts = "#{args} --default-path #{runner.test_dir} #{runner.stringify_test_file_paths}"
21
24
  end
22
25
  Rake::Task[task_name].invoke
23
26
  end
@@ -0,0 +1,28 @@
1
+ module KnapsackPro
2
+ class SlowTestFileDeterminer
3
+ TIME_THRESHOLD_PER_CI_NODE = 0.7 # 70%
4
+ REPORT_DIR = 'tmp/knapsack_pro/slow_test_file_determiner'
5
+ REPORT_PATH = "#{REPORT_DIR}/slow_test_files.json"
6
+
7
+ # test_files: { 'path' => 'a_spec.rb', 'time_execution' => 0.0 }
8
+ # time_execution: of build distribution (total time of CI build run)
9
+ def self.call(test_files, time_execution)
10
+ time_threshold = (time_execution / KnapsackPro::Config::Env.ci_node_total) * TIME_THRESHOLD_PER_CI_NODE
11
+
12
+ test_files.select do |test_file|
13
+ test_file.fetch('time_execution') >= time_threshold
14
+ end
15
+ end
16
+
17
+ def self.save_to_json_report(test_files)
18
+ FileUtils.mkdir_p(REPORT_DIR)
19
+ File.write(REPORT_PATH, test_files.to_json)
20
+ end
21
+
22
+ def self.read_from_json_report
23
+ raise 'Report with slow test files was not generated yet. If you have enabled split by test cases https://github.com/KnapsackPro/knapsack_pro-ruby#split-test-files-by-test-cases and you see this error it means that your tests accidentally cleaned up tmp/knapsack_pro directory. Please do not remove this directory during tests runtime!' unless File.exists?(REPORT_PATH)
24
+ slow_test_files_json_report = File.read(REPORT_PATH)
25
+ JSON.parse(slow_test_files_json_report)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ module KnapsackPro
2
+ class SlowTestFileFinder
3
+ # Get recorded test files from API.
4
+ # Find slow tests among them that are still present on the disk.
5
+ # Save slow test files in json file on the disk.
6
+ # Returns slow test files.
7
+ def self.call(adapter_class)
8
+ if KnapsackPro::Config::Env.test_files_encrypted?
9
+ raise 'Split by test cases is not possible when you have enabled test file names encryption ( https://github.com/KnapsackPro/knapsack_pro-ruby#test-file-names-encryption ). You need to disable encryption with KNAPSACK_PRO_TEST_FILES_ENCRYPTED=false in order to use split by test cases https://github.com/KnapsackPro/knapsack_pro-ruby#split-test-files-by-test-cases'
10
+ end
11
+
12
+ # get list of recorded test files for last CI Build
13
+ build_distribution_entity = KnapsackPro::BuildDistributionFetcher.call
14
+ test_files_from_api = build_distribution_entity.test_files
15
+
16
+ merged_test_files_from_api = KnapsackPro::TestCaseMergers::BaseMerger.call(adapter_class, test_files_from_api)
17
+
18
+ test_files_existing_on_disk = KnapsackPro::TestFileFinder.select_test_files_that_can_be_run(adapter_class, merged_test_files_from_api)
19
+
20
+ slow_test_files = KnapsackPro::SlowTestFileDeterminer.call(test_files_existing_on_disk, build_distribution_entity.time_execution)
21
+
22
+ KnapsackPro::SlowTestFileDeterminer.save_to_json_report(slow_test_files)
23
+
24
+ slow_test_files
25
+ end
26
+ end
27
+ end
@@ -18,13 +18,19 @@ module KnapsackPro
18
18
  ensure_report_dir_exists
19
19
  remove_old_json_report
20
20
 
21
- test_file_paths = KnapsackPro::TestFileFinder.call(test_file_pattern)
21
+ test_file_entities = slow_test_files
22
+
23
+ if test_file_entities.empty?
24
+ no_examples_json = { examples: [] }.to_json
25
+ File.write(REPORT_PATH, no_examples_json)
26
+ return
27
+ end
22
28
 
23
29
  cli_args = cli_format + [
24
30
  '--dry-run',
25
31
  '--out', REPORT_PATH,
26
32
  '--default-path', test_dir,
27
- ] + test_file_paths.map { |t| t.fetch('path') }
33
+ ] + KnapsackPro::TestFilePresenter.paths(test_file_entities)
28
34
  options = RSpec::Core::ConfigurationOptions.new(cli_args)
29
35
  exit_code = RSpec::Core::Runner.new(options).run($stderr, $stdout)
30
36
  if exit_code != 0
@@ -43,6 +49,16 @@ module KnapsackPro
43
49
  .map { |path_with_example_id| test_file_hash_for(path_with_example_id) }
44
50
  end
45
51
 
52
+ def slow_test_files
53
+ if KnapsackPro::Config::Env.slow_test_file_pattern
54
+ KnapsackPro::TestFileFinder.slow_test_files_by_pattern(adapter_class)
55
+ else
56
+ # read slow test files from JSON file on disk that was generated
57
+ # by lib/knapsack_pro/base_allocator_builder.rb
58
+ KnapsackPro::SlowTestFileDeterminer.read_from_json_report
59
+ end
60
+ end
61
+
46
62
  private
47
63
 
48
64
  def adapter_class
@@ -0,0 +1,29 @@
1
+ module KnapsackPro
2
+ module TestCaseMergers
3
+ class BaseMerger
4
+ # values must be string to avoid circular dependency problem during loading files
5
+ ADAPTER_TO_MERGER_MAP = {
6
+ KnapsackPro::Adapters::RSpecAdapter => 'KnapsackPro::TestCaseMergers::RSpecMerger',
7
+ }
8
+
9
+ def self.call(adapter_class, test_files)
10
+ merger_class =
11
+ ADAPTER_TO_MERGER_MAP[adapter_class] ||
12
+ raise("Test case merger does not exist for adapter_class: #{adapter_class}")
13
+ Kernel.const_get(merger_class).new(test_files).call
14
+ end
15
+
16
+ def initialize(test_files)
17
+ @test_files = test_files
18
+ end
19
+
20
+ def call
21
+ raise NotImplementedError
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :test_files
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,34 @@
1
+ module KnapsackPro
2
+ module TestCaseMergers
3
+ class RSpecMerger < BaseMerger
4
+ def call
5
+ merged_test_files_hash = {}
6
+ test_files.each do |test_file|
7
+ test_file_path = extract_test_file_path(test_file.fetch('path'))
8
+
9
+ # must be float (default type for time execution from API)
10
+ merged_test_files_hash[test_file_path] ||= 0.0
11
+ merged_test_files_hash[test_file_path] += test_file.fetch('time_execution')
12
+ end
13
+
14
+ merged_test_files = []
15
+ merged_test_files_hash.each do |path, time_execution|
16
+ merged_test_files << {
17
+ 'path' => path,
18
+ 'time_execution' => time_execution
19
+ }
20
+ end
21
+ merged_test_files
22
+ end
23
+
24
+ private
25
+
26
+ # path - can be:
27
+ # test file path: spec/a_spec.rb
28
+ # or test example path: spec/a_spec.rb[1:1]
29
+ def extract_test_file_path(path)
30
+ path.gsub(/\.rb\[.+\]$/, '.rb')
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,11 +1,49 @@
1
1
  module KnapsackPro
2
2
  class TestFileFinder
3
- def self.call(test_file_pattern)
4
- new(test_file_pattern).call
3
+ def self.call(test_file_pattern, test_file_list_enabled: true)
4
+ new(test_file_pattern, test_file_list_enabled).call
5
5
  end
6
6
 
7
- def initialize(test_file_pattern)
7
+ # finds slow test files on disk based on ENV patterns
8
+ # returns example: [{ 'path' => 'a_spec.rb' }]
9
+ def self.slow_test_files_by_pattern(adapter_class)
10
+ raise 'KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN is not defined' unless KnapsackPro::Config::Env.slow_test_file_pattern
11
+
12
+ test_file_pattern = KnapsackPro::TestFilePattern.call(adapter_class)
13
+ test_file_entities = call(test_file_pattern)
14
+
15
+ slow_test_file_entities = call(KnapsackPro::Config::Env.slow_test_file_pattern, test_file_list_enabled: false)
16
+
17
+ # slow test files (KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN)
18
+ # should be subset of test file pattern (KNAPSACK_PRO_TEST_FILE_PATTERN)
19
+ slow_test_file_entities & test_file_entities
20
+ end
21
+
22
+ # Args:
23
+ # test_file_entities_to_run - it can be list of slow test files that you want to run
24
+ # Return:
25
+ # subset of test_file_entities_to_run that are present on disk and it is subset of tests matching pattern KNAPSACK_PRO_TEST_FILE_PATTERN
26
+ # Thanks to that we can select only slow test files that are within list of test file pattern we want to run tests for
27
+ def self.select_test_files_that_can_be_run(adapter_class, test_file_entities_to_run)
28
+ test_file_pattern = KnapsackPro::TestFilePattern.call(adapter_class)
29
+ test_file_entities = call(test_file_pattern)
30
+
31
+ test_file_paths_existing_on_disk = KnapsackPro::TestFilePresenter.paths(test_file_entities)
32
+
33
+ selected_test_files = []
34
+
35
+ test_file_entities_to_run.each do |test_file_entity|
36
+ if test_file_paths_existing_on_disk.include?(test_file_entity.fetch('path'))
37
+ selected_test_files << test_file_entity
38
+ end
39
+ end
40
+
41
+ selected_test_files
42
+ end
43
+
44
+ def initialize(test_file_pattern, test_file_list_enabled)
8
45
  @test_file_pattern = test_file_pattern
46
+ @test_file_list_enabled = test_file_list_enabled
9
47
  end
10
48
 
11
49
  def call
@@ -18,10 +56,10 @@ module KnapsackPro
18
56
 
19
57
  private
20
58
 
21
- attr_reader :test_file_pattern
59
+ attr_reader :test_file_pattern, :test_file_list_enabled
22
60
 
23
61
  def test_files
24
- if KnapsackPro::Config::Env.test_file_list
62
+ if test_file_list_enabled && KnapsackPro::Config::Env.test_file_list
25
63
  return KnapsackPro::Config::Env.test_file_list.split(',').map(&:strip)
26
64
  end
27
65
 
@@ -0,0 +1,22 @@
1
+ module KnapsackPro
2
+ class TestFilesWithTestCasesComposer
3
+ # Args:
4
+ # All 3 arguments have structure: [{ 'path' => 'spec/a_spec.rb', 'time_execution' => 0.0 }]
5
+ # time_execution is not always present (but it's not relevant here)
6
+ #
7
+ # test_files - list of test files that you want to run tests for
8
+ # slow_test_files - list of slow test files that should be split by test cases
9
+ # test_file_cases - list of paths to test cases (test examples) inside of all slow test files (slow_test_files)
10
+ # Return:
11
+ # Test files and test cases paths (it excludes test files that should be split by test cases (test examples))
12
+ def self.call(test_files, slow_test_files, test_file_cases)
13
+ slow_test_file_paths = KnapsackPro::TestFilePresenter.paths(slow_test_files)
14
+
15
+ test_files_without_slow_test_files = test_files.reject do |test_file|
16
+ slow_test_file_paths.include?(test_file.fetch('path'))
17
+ end
18
+
19
+ test_files_without_slow_test_files + test_file_cases
20
+ end
21
+ end
22
+ end
@@ -1,3 +1,3 @@
1
1
  module KnapsackPro
2
- VERSION = '1.20.2'
2
+ VERSION = '1.21.0'
3
3
  end
@@ -3,6 +3,61 @@ describe KnapsackPro::Adapters::BaseAdapter do
3
3
  expect(described_class::TEST_DIR_PATTERN).to eq 'test/**{,/*/**}/*_test.rb'
4
4
  end
5
5
 
6
+ shared_examples '.slow_test_file? method' do
7
+ context 'when test_file_path is in slow test file paths' do
8
+ # add ./ before path to ensure KnapsackPro::TestFileCleaner.clean will clean it
9
+ let(:test_file_path) { './spec/models/user_spec.rb' }
10
+
11
+ it do
12
+ expect(subject).to be true
13
+ end
14
+ end
15
+
16
+ context 'when test_file_path is not in slow test file paths' do
17
+ let(:test_file_path) { './spec/models/article_spec.rb' }
18
+
19
+ it do
20
+ expect(subject).to be false
21
+ end
22
+ end
23
+ end
24
+
25
+ describe '.slow_test_file?' do
26
+ let(:adapter_class) { double }
27
+ let(:slow_test_files) do
28
+ [
29
+ { 'path' => 'spec/models/user_spec.rb' },
30
+ { 'path' => 'spec/controllers/users_spec.rb' },
31
+ ]
32
+ end
33
+
34
+ subject { described_class.slow_test_file?(adapter_class, test_file_path) }
35
+
36
+ before do
37
+ # reset class variable
38
+ described_class.instance_variable_set(:@slow_test_file_paths, nil)
39
+ end
40
+
41
+ context 'when slow test file pattern is present' do
42
+ before do
43
+ stub_const('ENV', {
44
+ 'KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN' => '{spec/models/*_spec.rb}',
45
+ })
46
+ expect(KnapsackPro::TestFileFinder).to receive(:slow_test_files_by_pattern).with(adapter_class).and_return(slow_test_files)
47
+ end
48
+
49
+ it_behaves_like '.slow_test_file? method'
50
+ end
51
+
52
+ context 'when slow test file pattern is not present' do
53
+ before do
54
+ expect(KnapsackPro::SlowTestFileDeterminer).to receive(:read_from_json_report).and_return(slow_test_files)
55
+ end
56
+
57
+ it_behaves_like '.slow_test_file? method'
58
+ end
59
+ end
60
+
6
61
  describe '.bind' do
7
62
  let(:adapter) { instance_double(described_class) }
8
63
 
@@ -71,22 +71,21 @@ describe KnapsackPro::Adapters::RSpecAdapter do
71
71
  let(:tracker) { instance_double(KnapsackPro::Tracker) }
72
72
  let(:logger) { instance_double(Logger) }
73
73
  let(:global_time) { 'Global time: 01m 05s' }
74
+ let(:test_path) { 'spec/a_spec.rb' }
75
+ let(:example) { double }
76
+ let(:example_group) { double }
77
+ let(:current_example) do
78
+ OpenStruct.new(metadata: {
79
+ example_group: example_group
80
+ })
81
+ end
74
82
 
75
83
  context 'when rspec split by test examples is disabled' do
76
- let(:test_path) { 'spec/a_spec.rb' }
77
- let(:example) { double }
78
- let(:example_group) { double }
79
- let(:current_example) do
80
- OpenStruct.new(metadata: {
81
- example_group: example_group
82
- })
83
- end
84
-
85
84
  before do
86
85
  expect(KnapsackPro::Config::Env).to receive(:rspec_split_by_test_examples?).and_return(false)
87
86
  end
88
87
 
89
- it do
88
+ it 'records time for current test path' do
90
89
  expect(config).to receive(:around).with(:each).and_yield(example)
91
90
  expect(config).to receive(:after).with(:suite).and_yield
92
91
  expect(::RSpec).to receive(:configure).and_yield(config)
@@ -112,32 +111,69 @@ describe KnapsackPro::Adapters::RSpecAdapter do
112
111
 
113
112
  context 'when rspec split by test examples is enabled' do
114
113
  let(:test_example_path) { 'spec/a_spec.rb[1:1]' }
115
- let(:example) { double }
116
114
 
117
115
  before do
118
116
  expect(KnapsackPro::Config::Env).to receive(:rspec_split_by_test_examples?).and_return(true)
119
117
  end
120
118
 
121
- it do
122
- expect(config).to receive(:around).with(:each).and_yield(example)
123
- expect(config).to receive(:after).with(:suite).and_yield
124
- expect(::RSpec).to receive(:configure).and_yield(config)
119
+ context 'when current test_path is a slow test file' do
120
+ before do
121
+ expect(described_class).to receive(:slow_test_file?).with(described_class, test_path).and_return(true)
122
+ end
125
123
 
126
- expect(example).to receive(:id).and_return(test_example_path)
124
+ it 'records time for example.id' do
125
+ expect(example).to receive(:id).and_return(test_example_path)
127
126
 
128
- allow(KnapsackPro).to receive(:tracker).and_return(tracker)
129
- expect(tracker).to receive(:current_test_path=).with(test_example_path)
130
- expect(tracker).to receive(:start_timer)
127
+ expect(config).to receive(:around).with(:each).and_yield(example)
128
+ expect(config).to receive(:after).with(:suite).and_yield
129
+ expect(::RSpec).to receive(:configure).and_yield(config)
131
130
 
132
- expect(example).to receive(:run)
131
+ expect(::RSpec).to receive(:current_example).twice.and_return(current_example)
132
+ expect(described_class).to receive(:test_path).with(example_group).and_return(test_path)
133
133
 
134
- expect(tracker).to receive(:stop_timer)
134
+ allow(KnapsackPro).to receive(:tracker).and_return(tracker)
135
+ expect(tracker).to receive(:current_test_path=).with(test_example_path)
136
+ expect(tracker).to receive(:start_timer)
135
137
 
136
- expect(KnapsackPro::Presenter).to receive(:global_time).and_return(global_time)
137
- expect(KnapsackPro).to receive(:logger).and_return(logger)
138
- expect(logger).to receive(:debug).with(global_time)
138
+ expect(example).to receive(:run)
139
139
 
140
- subject.bind_time_tracker
140
+ expect(tracker).to receive(:stop_timer)
141
+
142
+ expect(KnapsackPro::Presenter).to receive(:global_time).and_return(global_time)
143
+ expect(KnapsackPro).to receive(:logger).and_return(logger)
144
+ expect(logger).to receive(:debug).with(global_time)
145
+
146
+ subject.bind_time_tracker
147
+ end
148
+ end
149
+
150
+ context 'when current test_path is not a slow test file' do
151
+ before do
152
+ expect(described_class).to receive(:slow_test_file?).with(described_class, test_path).and_return(false)
153
+ end
154
+
155
+ it 'records time for current test path' do
156
+ expect(config).to receive(:around).with(:each).and_yield(example)
157
+ expect(config).to receive(:after).with(:suite).and_yield
158
+ expect(::RSpec).to receive(:configure).and_yield(config)
159
+
160
+ expect(::RSpec).to receive(:current_example).twice.and_return(current_example)
161
+ expect(described_class).to receive(:test_path).with(example_group).and_return(test_path)
162
+
163
+ allow(KnapsackPro).to receive(:tracker).and_return(tracker)
164
+ expect(tracker).to receive(:current_test_path=).with(test_path)
165
+ expect(tracker).to receive(:start_timer)
166
+
167
+ expect(example).to receive(:run)
168
+
169
+ expect(tracker).to receive(:stop_timer)
170
+
171
+ expect(KnapsackPro::Presenter).to receive(:global_time).and_return(global_time)
172
+ expect(KnapsackPro).to receive(:logger).and_return(logger)
173
+ expect(logger).to receive(:debug).with(global_time)
174
+
175
+ subject.bind_time_tracker
176
+ end
141
177
  end
142
178
  end
143
179
  end