knapsack_pro 6.0.4 → 7.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +80 -19
  3. data/.github/pull_request_template.md +22 -0
  4. data/.gitignore +4 -0
  5. data/CHANGELOG.md +95 -0
  6. data/Gemfile +9 -0
  7. data/README.md +7 -9
  8. data/knapsack_pro.gemspec +2 -1
  9. data/lib/knapsack_pro/adapters/base_adapter.rb +7 -2
  10. data/lib/knapsack_pro/adapters/cucumber_adapter.rb +1 -3
  11. data/lib/knapsack_pro/adapters/rspec_adapter.rb +24 -9
  12. data/lib/knapsack_pro/config/env.rb +1 -9
  13. data/lib/knapsack_pro/extensions/rspec_extension.rb +137 -0
  14. data/lib/knapsack_pro/formatters/time_tracker.rb +10 -26
  15. data/lib/knapsack_pro/formatters/time_tracker_fetcher.rb +6 -0
  16. data/lib/knapsack_pro/presenter.rb +1 -1
  17. data/lib/knapsack_pro/pure/queue/rspec_pure.rb +100 -0
  18. data/lib/knapsack_pro/runners/queue/base_runner.rb +6 -1
  19. data/lib/knapsack_pro/runners/queue/cucumber_runner.rb +6 -6
  20. data/lib/knapsack_pro/runners/queue/minitest_runner.rb +6 -6
  21. data/lib/knapsack_pro/runners/queue/rspec_runner.rb +127 -173
  22. data/lib/knapsack_pro/urls.rb +2 -0
  23. data/lib/knapsack_pro/version.rb +1 -1
  24. data/lib/knapsack_pro.rb +1 -0
  25. data/spec/integration/runners/queue/rspec_runner.rb +80 -0
  26. data/spec/integration/runners/queue/rspec_runner_spec.rb +2405 -0
  27. data/spec/knapsack_pro/adapters/base_adapter_spec.rb +17 -11
  28. data/spec/knapsack_pro/adapters/cucumber_adapter_spec.rb +2 -5
  29. data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +56 -24
  30. data/spec/knapsack_pro/config/env_spec.rb +1 -35
  31. data/spec/knapsack_pro/formatters/time_tracker_fetcher_spec.rb +30 -0
  32. data/spec/knapsack_pro/formatters/time_tracker_specs.rb +8 -37
  33. data/spec/knapsack_pro/hooks/queue_spec.rb +2 -2
  34. data/spec/knapsack_pro/presenter_spec.rb +1 -1
  35. data/spec/knapsack_pro/pure/queue/rspec_pure_spec.rb +248 -0
  36. data/spec/knapsack_pro/runners/queue/cucumber_runner_spec.rb +16 -16
  37. data/spec/knapsack_pro/runners/queue/minitest_runner_spec.rb +14 -14
  38. data/spec/knapsack_pro_spec.rb +3 -3
  39. metadata +19 -12
  40. data/lib/knapsack_pro/formatters/rspec_queue_profile_formatter_extension.rb +0 -58
  41. data/lib/knapsack_pro/formatters/rspec_queue_summary_formatter.rb +0 -145
  42. data/spec/knapsack_pro/runners/queue/rspec_runner_spec.rb +0 -536
@@ -9,24 +9,18 @@ module KnapsackPro
9
9
  :example_group_started,
10
10
  :example_started,
11
11
  :example_finished,
12
- :example_group_finished,
13
- :stop
12
+ :example_group_finished
14
13
 
15
14
  attr_reader :output # RSpec < v3.10.2
16
15
 
17
- # Called at the beginning of each batch,
18
- # but only the first instance of this class is used,
19
- # so don't rely on the initializer to reset values.
20
16
  def initialize(_output)
21
17
  @output = StringIO.new
22
18
  @time_each = nil
23
19
  @time_all = nil
24
20
  @before_all = 0.0
25
21
  @group = {}
26
- @batch = {}
27
- @queue = {}
22
+ @paths = {}
28
23
  @suite_started = now
29
- @batch_started = now
30
24
  end
31
25
 
32
26
  def example_group_started(notification)
@@ -47,21 +41,15 @@ module KnapsackPro
47
41
  def example_group_finished(notification)
48
42
  return unless top_level_group?(notification.group)
49
43
 
50
- add_hooks_time(@group, @before_all, now - @time_all)
51
- @batch = merge(@batch, @group)
44
+ after_all = @time_all.nil? ? 0.0 : now - @time_all
45
+ add_hooks_time(@group, @before_all, after_all)
52
46
  @before_all = 0.0
47
+ @paths = merge(@paths, @group)
53
48
  @group = {}
54
49
  end
55
50
 
56
- # Called at the end of each batch
57
- def stop(_notification)
58
- @queue = merge(@queue, @batch)
59
- @batch = {}
60
- @batch_started = now
61
- end
62
-
63
51
  def queue(scheduled_paths)
64
- recorded_paths = @queue.values.map do |example|
52
+ recorded_paths = @paths.values.map do |example|
65
53
  KnapsackPro::Adapters::RSpecAdapter.parse_file_path(example[:path])
66
54
  end
67
55
 
@@ -69,13 +57,13 @@ module KnapsackPro
69
57
  object[path] = { path: path, time_execution: 0.0 }
70
58
  end
71
59
 
72
- merge(@queue, missing).values.map do |example|
60
+ merge(@paths, missing).values.map do |example|
73
61
  example.transform_keys(&:to_s)
74
62
  end
75
63
  end
76
64
 
77
65
  def batch
78
- @batch.values.map do |example|
66
+ @paths.values.map do |example|
79
67
  example.transform_keys(&:to_s)
80
68
  end
81
69
  end
@@ -84,17 +72,13 @@ module KnapsackPro
84
72
  now - @suite_started
85
73
  end
86
74
 
87
- def batch_duration
88
- now - @batch_started
89
- end
90
-
91
75
  def unexecuted_test_files(scheduled_paths)
92
- pending_paths = (@queue.values + @batch.values)
76
+ pending_paths = @paths.values
93
77
  .filter { |example| example[:time_execution] == 0.0 }
94
78
  .map { |example| example[:path] }
95
79
 
96
80
  not_run_paths = scheduled_paths -
97
- (@queue.values + @batch.values)
81
+ @paths.values
98
82
  .map { |example| example[:path] }
99
83
 
100
84
  pending_paths + not_run_paths
@@ -9,6 +9,12 @@ module KnapsackPro
9
9
  .formatters
10
10
  .find { |f| f.class.to_s == "KnapsackPro::Formatters::TimeTracker" }
11
11
  end
12
+
13
+ def self.unexecuted_test_files(scheduled_paths)
14
+ time_tracker = call
15
+ return [] unless time_tracker
16
+ time_tracker.unexecuted_test_files(scheduled_paths)
17
+ end
12
18
  end
13
19
  end
14
20
  end
@@ -6,7 +6,7 @@ module KnapsackPro
6
6
  def global_time(time = nil)
7
7
  time = KnapsackPro.tracker.global_time if time.nil?
8
8
  global_time = pretty_seconds(time)
9
- "Global time execution for tests: #{global_time}"
9
+ "Global test execution duration: #{global_time}"
10
10
  end
11
11
 
12
12
  def pretty_seconds(seconds)
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KnapsackPro
4
+ module Pure
5
+ module Queue
6
+ class RSpecPure
7
+ FAILURE_EXIT_CODE = 1
8
+ FORMATTERS = [
9
+ 'KnapsackPro::Formatters::TimeTracker',
10
+ ]
11
+
12
+ def add_knapsack_pro_formatters_to(spec_opts)
13
+ return spec_opts unless spec_opts
14
+ return spec_opts if FORMATTERS.all? { |formatter| spec_opts.include?(formatter) }
15
+
16
+ FORMATTERS.each do |formatter|
17
+ next if spec_opts.include?(formatter)
18
+ spec_opts += " --format #{formatter}"
19
+ end
20
+
21
+ spec_opts
22
+ end
23
+
24
+ def error_exit_code(rspec_error_exit_code)
25
+ rspec_error_exit_code || FAILURE_EXIT_CODE
26
+ end
27
+
28
+ def args_with_seed_option_added_when_viable(order_option, seed, args)
29
+ return args if order_option && !order_option.include?('rand')
30
+ return args if order_option && order_option.to_s.split(':')[1]
31
+
32
+ return args unless seed.used?
33
+
34
+ args + ['--seed', seed.value]
35
+ end
36
+
37
+ def prepare_cli_args(args, has_format_option, has_require_rails_helper_option, rails_helper_exists, test_dir)
38
+ (args || '').split
39
+ .yield_self { args_with_at_least_one_formatter(_1, has_format_option) }
40
+ .yield_self { args_with_require_rails_helper_if_needed(_1, has_require_rails_helper_option, rails_helper_exists) }
41
+ .yield_self { args_with_default_options(_1, test_dir) }
42
+ end
43
+
44
+ def rspec_command(args, test_file_paths, scope)
45
+ messages = []
46
+ return messages if test_file_paths.empty?
47
+
48
+ case scope
49
+ when :batch_finished
50
+ messages << 'To retry the last batch of tests fetched from the Queue API, please run the following command on your machine:'
51
+ when :queue_finished
52
+ messages << 'To retry all the tests assigned to this CI node, please run the following command on your machine:'
53
+ end
54
+
55
+ stringified_cli_args = args.join(' ')
56
+ FORMATTERS.each do |formatter|
57
+ stringified_cli_args.sub!(" --format #{formatter}", '')
58
+ end
59
+
60
+ messages << "bundle exec rspec #{stringified_cli_args} " + KnapsackPro::TestFilePresenter.stringify_paths(test_file_paths)
61
+
62
+ messages
63
+ end
64
+
65
+ def exit_summary(unexecuted_test_files)
66
+ return if unexecuted_test_files.empty?
67
+
68
+ "Unexecuted tests on this CI node (including pending tests): #{unexecuted_test_files.join(' ')}"
69
+ end
70
+
71
+ private
72
+
73
+ def args_with_at_least_one_formatter(cli_args, has_format_option)
74
+ return cli_args if has_format_option
75
+
76
+ cli_args + ['--format', 'progress']
77
+ end
78
+
79
+ def args_with_require_rails_helper_if_needed(cli_args, has_require_rails_helper_option, rails_helper_exists)
80
+ return cli_args if has_require_rails_helper_option
81
+ return cli_args unless rails_helper_exists
82
+
83
+ cli_args + ['--require', 'rails_helper']
84
+ end
85
+
86
+ def args_with_default_options(cli_args, test_dir)
87
+ new_cli_args = cli_args + [
88
+ '--default-path', test_dir,
89
+ ]
90
+
91
+ FORMATTERS.each do |formatter|
92
+ new_cli_args += ['--format', formatter]
93
+ end
94
+
95
+ new_cli_args
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -4,6 +4,7 @@ module KnapsackPro
4
4
  module Runners
5
5
  module Queue
6
6
  class BaseRunner
7
+ TerminationError = Class.new(StandardError)
7
8
  TERMINATION_SIGNALS = %w(HUP INT TERM ABRT QUIT USR1 USR2)
8
9
 
9
10
  @@terminate_process = false
@@ -42,13 +43,17 @@ module KnapsackPro
42
43
  end
43
44
 
44
45
  def self.handle_signal!
45
- raise 'Knapsack Pro process was terminated!' if @@terminate_process
46
+ raise TerminationError.new('Knapsack Pro process was terminated!') if @@terminate_process
46
47
  end
47
48
 
48
49
  def self.set_terminate_process
49
50
  @@terminate_process = true
50
51
  end
51
52
 
53
+ def set_terminate_process
54
+ self.class.set_terminate_process
55
+ end
56
+
52
57
  def trap_signals
53
58
  TERMINATION_SIGNALS.each do |signal|
54
59
  Signal.trap(signal) {
@@ -21,7 +21,7 @@ module KnapsackPro
21
21
  can_initialize_queue: true,
22
22
  args: args,
23
23
  exitstatus: 0,
24
- all_test_file_paths: [],
24
+ node_test_file_paths: [],
25
25
  }
26
26
  while accumulator[:status] == :next
27
27
  handle_signal!
@@ -36,15 +36,15 @@ module KnapsackPro
36
36
  can_initialize_queue = accumulator.fetch(:can_initialize_queue)
37
37
  args = accumulator.fetch(:args)
38
38
  exitstatus = accumulator.fetch(:exitstatus)
39
- all_test_file_paths = accumulator.fetch(:all_test_file_paths)
39
+ node_test_file_paths = accumulator.fetch(:node_test_file_paths)
40
40
 
41
41
  test_file_paths = runner.test_file_paths(
42
42
  can_initialize_queue: can_initialize_queue,
43
- executed_test_files: all_test_file_paths
43
+ executed_test_files: node_test_file_paths
44
44
  )
45
45
 
46
46
  if test_file_paths.empty?
47
- unless all_test_file_paths.empty?
47
+ unless node_test_file_paths.empty?
48
48
  KnapsackPro::Adapters::CucumberAdapter.verify_bind_method_called
49
49
  end
50
50
 
@@ -65,7 +65,7 @@ module KnapsackPro
65
65
 
66
66
  KnapsackPro::Hooks::Queue.call_before_subset_queue
67
67
 
68
- all_test_file_paths += test_file_paths
68
+ node_test_file_paths += test_file_paths
69
69
 
70
70
  result_exitstatus = cucumber_run(runner, test_file_paths, args)
71
71
  exitstatus = result_exitstatus if result_exitstatus != 0
@@ -80,7 +80,7 @@ module KnapsackPro
80
80
  can_initialize_queue: false,
81
81
  args: args,
82
82
  exitstatus: exitstatus,
83
- all_test_file_paths: all_test_file_paths,
83
+ node_test_file_paths: node_test_file_paths,
84
84
  }
85
85
  end
86
86
  end
@@ -32,7 +32,7 @@ module KnapsackPro
32
32
  can_initialize_queue: true,
33
33
  args: cli_args,
34
34
  exitstatus: 0,
35
- all_test_file_paths: [],
35
+ node_test_file_paths: [],
36
36
  }
37
37
  while accumulator[:status] == :next
38
38
  handle_signal!
@@ -47,15 +47,15 @@ module KnapsackPro
47
47
  can_initialize_queue = accumulator.fetch(:can_initialize_queue)
48
48
  args = accumulator.fetch(:args)
49
49
  exitstatus = accumulator.fetch(:exitstatus)
50
- all_test_file_paths = accumulator.fetch(:all_test_file_paths)
50
+ node_test_file_paths = accumulator.fetch(:node_test_file_paths)
51
51
 
52
52
  test_file_paths = runner.test_file_paths(
53
53
  can_initialize_queue: can_initialize_queue,
54
- executed_test_files: all_test_file_paths
54
+ executed_test_files: node_test_file_paths
55
55
  )
56
56
 
57
57
  if test_file_paths.empty?
58
- unless all_test_file_paths.empty?
58
+ unless node_test_file_paths.empty?
59
59
  KnapsackPro::Adapters::MinitestAdapter.verify_bind_method_called
60
60
  end
61
61
 
@@ -76,7 +76,7 @@ module KnapsackPro
76
76
 
77
77
  KnapsackPro::Hooks::Queue.call_before_subset_queue
78
78
 
79
- all_test_file_paths += test_file_paths
79
+ node_test_file_paths += test_file_paths
80
80
 
81
81
  result = minitest_run(runner, test_file_paths, args)
82
82
  exitstatus = 1 unless result
@@ -91,7 +91,7 @@ module KnapsackPro
91
91
  can_initialize_queue: false,
92
92
  args: args,
93
93
  exitstatus: exitstatus,
94
- all_test_file_paths: all_test_file_paths,
94
+ node_test_file_paths: node_test_file_paths,
95
95
  }
96
96
  end
97
97
  end