knapsack_pro 2.12.0 → 2.17.0

Sign up to get free protection for your applications and to get access to all the features.
data/knapsack_pro.gemspec CHANGED
@@ -29,14 +29,13 @@ Gem::Specification.new do |spec|
29
29
 
30
30
  spec.add_development_dependency 'bundler', '>= 1.6'
31
31
  spec.add_development_dependency 'rspec', '~> 3.0', '>= 2.10.0'
32
- spec.add_development_dependency 'rspec-its', '~> 1.2'
32
+ spec.add_development_dependency 'rspec-its', '~> 1.3'
33
33
  spec.add_development_dependency 'cucumber', '>= 0'
34
34
  spec.add_development_dependency 'spinach', '>= 0.8'
35
35
  spec.add_development_dependency 'minitest', '>= 5.0.0'
36
36
  spec.add_development_dependency 'test-unit', '>= 3.0.0'
37
- spec.add_development_dependency 'codeclimate-test-reporter', '~> 0'
38
37
  spec.add_development_dependency 'pry', '~> 0'
39
- spec.add_development_dependency 'vcr', '~> 2.9'
40
- spec.add_development_dependency 'webmock', '~> 1.21'
41
- spec.add_development_dependency 'timecop', '>= 0.1.0'
38
+ spec.add_development_dependency 'vcr', '>= 6.0'
39
+ spec.add_development_dependency 'webmock', '>= 3.13'
40
+ spec.add_development_dependency 'timecop', '>= 0.9.4'
42
41
  end
@@ -21,7 +21,15 @@ module KnapsackPro
21
21
 
22
22
  def bind_time_tracker
23
23
  ::RSpec.configure do |config|
24
+ config.prepend_before(:context) do
25
+ KnapsackPro.tracker.start_timer
26
+ end
27
+
24
28
  config.around(:each) do |example|
29
+ # stop timer to update time for a previously run test example
30
+ # this way we count time spend in runtime for the previous test example after around(:each) is already done
31
+ KnapsackPro.tracker.stop_timer
32
+
25
33
  current_example_group =
26
34
  if ::RSpec.respond_to?(:current_example)
27
35
  ::RSpec.current_example.metadata[:example_group]
@@ -38,10 +46,12 @@ module KnapsackPro
38
46
  current_test_path
39
47
  end
40
48
 
41
- KnapsackPro.tracker.start_timer
42
-
43
49
  example.run
50
+ end
44
51
 
52
+ config.append_after(:context) do
53
+ # after(:context) hook is run one time only, after all of the examples in a group
54
+ # stop timer to count time for the very last executed test example
45
55
  KnapsackPro.tracker.stop_timer
46
56
  end
47
57
 
@@ -26,7 +26,7 @@ module KnapsackPro
26
26
  KnapsackPro.logger.error(message)
27
27
  raise message
28
28
  else
29
- KnapsackPro.logger.warn("Fallback mode started. We could not connect with Knapsack Pro API. Your tests will be executed based on directory names. Read more about fallback mode at https://github.com/KnapsackPro/knapsack_pro-ruby#what-happens-when-knapsack-pro-api-is-not-availablenot-reachable-temporarily")
29
+ KnapsackPro.logger.warn("Fallback mode started. We could not connect with Knapsack Pro API. Your tests will be executed based on directory names. Read more about fallback mode at https://knapsackpro.com/faq/question/what-happens-when-knapsack-pro-api-is-not-availablenot-reachable-temporarily")
30
30
  fallback_test_files
31
31
  end
32
32
  end
@@ -23,8 +23,7 @@ module KnapsackPro
23
23
  end
24
24
 
25
25
  def project_dir
26
- project_repo_name = ENV['CIRCLE_PROJECT_REPONAME']
27
- "/home/ubuntu/#{project_repo_name}" if project_repo_name
26
+ ENV['CIRCLE_WORKING_DIRECTORY']
28
27
  end
29
28
  end
30
29
  end
@@ -39,7 +39,7 @@ module KnapsackPro
39
39
  raise message
40
40
  else
41
41
  @fallback_activated = true
42
- KnapsackPro.logger.warn("Fallback mode started. We could not connect with Knapsack Pro API. Your tests will be executed based on directory names. If other CI nodes were able to connect with Knapsack Pro API then you may notice that some of the test files will be executed twice across CI nodes. The most important thing is to guarantee each of test files is run at least once! Read more about fallback mode at https://github.com/KnapsackPro/knapsack_pro-ruby#what-happens-when-knapsack-pro-api-is-not-availablenot-reachable-temporarily")
42
+ KnapsackPro.logger.warn("Fallback mode started. We could not connect with Knapsack Pro API. Your tests will be executed based on directory names. If other CI nodes were able to connect with Knapsack Pro API then you may notice that some of the test files will be executed twice across CI nodes. The most important thing is to guarantee each of test files is run at least once! Read more about fallback mode at https://knapsackpro.com/faq/question/what-happens-when-knapsack-pro-api-is-not-availablenot-reachable-temporarily")
43
43
  fallback_test_files(executed_test_files)
44
44
  end
45
45
  end
@@ -17,7 +17,8 @@ module KnapsackPro
17
17
  private
18
18
 
19
19
  def working_dir
20
- KnapsackPro::Config::Env.project_dir
20
+ dir = KnapsackPro::Config::Env.project_dir
21
+ File.expand_path(dir)
21
22
  end
22
23
  end
23
24
  end
@@ -35,6 +35,10 @@ module KnapsackPro
35
35
 
36
36
  attr_reader :allocator_builder,
37
37
  :allocator
38
+
39
+ def self.child_status
40
+ $?
41
+ end
38
42
  end
39
43
  end
40
44
  end
@@ -29,6 +29,10 @@ module KnapsackPro
29
29
 
30
30
  attr_reader :allocator_builder,
31
31
  :allocator
32
+
33
+ def self.child_status
34
+ $?
35
+ end
32
36
  end
33
37
  end
34
38
  end
@@ -95,16 +95,14 @@ module KnapsackPro
95
95
  # which is defined in lib/knapsack_pro/adapters/cucumber_adapter.rb
96
96
  ENV['KNAPSACK_PRO_BEFORE_QUEUE_HOOK_CALLED'] = 'true'
97
97
 
98
- process_status = $?
99
-
100
- unless process_status.exited?
98
+ unless child_status.exited?
101
99
  raise "Cucumber process execution failed. It's likely that your CI server has exceeded"\
102
100
  " its available memory. Please try changing CI config or retrying the CI build.\n"\
103
101
  "Failed command: #{cmd}\n"\
104
- "Process status: #{process_status.inspect}"
102
+ "Process status: #{child_status.inspect}"
105
103
  end
106
104
 
107
- process_status.exitstatus
105
+ child_status.exitstatus
108
106
  end
109
107
  end
110
108
  end
@@ -14,12 +14,18 @@ module KnapsackPro
14
14
  runner = new(KnapsackPro::Adapters::RSpecAdapter)
15
15
 
16
16
  cli_args = (args || '').split
17
- # if user didn't provide the format then use explicitly default progress formatter
18
- # in order to avoid KnapsackPro::Formatters::RSpecQueueSummaryFormatter being the only default formatter
19
- if !cli_args.any? { |arg| arg.start_with?('-f') || arg.start_with?('--format')}
20
- cli_args += ['--format', 'progress']
17
+
18
+ if KnapsackPro::Config::Env.rspec_split_by_test_examples? && has_tag_option?(cli_args)
19
+ error_message = 'It is not allowed to use the RSpec tag option together with the RSpec split by test examples feature. Please see: https://knapsackpro.com/faq/question/how-to-split-slow-rspec-test-files-by-test-examples-by-individual-it#warning-dont-use-rspec-tag-option'
20
+ KnapsackPro.logger.error(error_message)
21
+ raise error_message
21
22
  end
23
+
24
+ # when format option is not defined by user then use progress formatter to show tests execution progress
25
+ cli_args += ['--format', 'progress'] unless has_format_option?(cli_args)
26
+
22
27
  cli_args += [
28
+ # shows summary of all tests executed in Queue Mode at the very end
23
29
  '--format', KnapsackPro::Formatters::RSpecQueueSummaryFormatter.to_s,
24
30
  '--default-path', runner.test_dir,
25
31
  ]
@@ -148,6 +154,19 @@ module KnapsackPro
148
154
  ::RSpec.configuration.reset_filters
149
155
  end
150
156
  end
157
+
158
+ def self.has_tag_option?(cli_args)
159
+ # use start_with? because user can define tag option in a few ways:
160
+ # -t mytag
161
+ # -tmytag
162
+ # --tag mytag
163
+ # --tag=mytag
164
+ cli_args.any? { |arg| arg.start_with?('-t') || arg.start_with?('--tag') }
165
+ end
166
+
167
+ def self.has_format_option?(cli_args)
168
+ cli_args.any? { |arg| arg.start_with?('-f') || arg.start_with?('--format') }
169
+ end
151
170
  end
152
171
  end
153
172
  end
@@ -13,7 +13,7 @@ module KnapsackPro
13
13
  cmd = %Q[KNAPSACK_PRO_RECORDING_ENABLED=true KNAPSACK_PRO_TEST_SUITE_TOKEN=#{ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN']} bundle exec spinach #{args} --features_path #{runner.test_dir} -- #{runner.stringify_test_file_paths}]
14
14
 
15
15
  Kernel.system(cmd)
16
- Kernel.exit($?.exitstatus) unless $?.exitstatus.zero?
16
+ Kernel.exit(child_status.exitstatus) unless child_status.exitstatus.zero?
17
17
  end
18
18
  end
19
19
  end
@@ -11,21 +11,32 @@ module KnapsackPro
11
11
  if runner.test_files_to_execute_exist?
12
12
  adapter_class.verify_bind_method_called
13
13
 
14
- require 'test/unit'
15
-
16
14
  cli_args =
17
15
  (args || '').split +
18
16
  runner.test_file_paths.map do |f|
19
17
  File.expand_path(f)
20
18
  end
21
19
 
22
- exit ::Test::Unit::AutoRunner.run(
20
+ exit test_unit_autorunner_run(
23
21
  true,
24
22
  runner.test_dir,
25
23
  cli_args
26
24
  )
27
25
  end
28
26
  end
27
+
28
+ private
29
+
30
+ # https://www.rubydoc.info/github/test-unit/test-unit/Test/Unit/AutoRunner#run-class_method
31
+ def self.test_unit_autorunner_run(force_standalone, default_dir, argv)
32
+ require 'test/unit'
33
+
34
+ ::Test::Unit::AutoRunner.run(
35
+ force_standalone,
36
+ default_dir,
37
+ argv
38
+ )
39
+ end
29
40
  end
30
41
  end
31
42
  end
@@ -19,13 +19,22 @@ module KnapsackPro
19
19
  end
20
20
 
21
21
  def start_timer
22
+ @start_time ||= now_without_mock_time.to_f
23
+ end
24
+
25
+ def reset_timer
22
26
  @start_time = now_without_mock_time.to_f
23
27
  end
24
28
 
25
29
  def stop_timer
26
30
  execution_time = @start_time ? now_without_mock_time.to_f - @start_time : 0.0
27
- update_global_time(execution_time)
28
- update_test_file_time(execution_time)
31
+
32
+ if @current_test_path
33
+ update_global_time(execution_time)
34
+ update_test_file_time(execution_time)
35
+ reset_timer
36
+ end
37
+
29
38
  execution_time
30
39
  end
31
40
 
@@ -63,7 +72,7 @@ module KnapsackPro
63
72
  def set_defaults
64
73
  @global_time = 0
65
74
  @test_files_with_time = {}
66
- @test_path = nil
75
+ @current_test_path = nil
67
76
  end
68
77
 
69
78
  def update_global_time(execution_time)
@@ -1,3 +1,3 @@
1
1
  module KnapsackPro
2
- VERSION = '2.12.0'
2
+ VERSION = '2.17.0'
3
3
  end
@@ -86,20 +86,26 @@ describe KnapsackPro::Adapters::RSpecAdapter do
86
86
  end
87
87
 
88
88
  it 'records time for current test path' do
89
+ expect(config).to receive(:prepend_before).with(:context).and_yield
90
+
91
+ allow(KnapsackPro).to receive(:tracker).and_return(tracker)
92
+ expect(tracker).to receive(:start_timer).ordered
93
+
89
94
  expect(config).to receive(:around).with(:each).and_yield(example)
95
+ expect(config).to receive(:append_after).with(:context).and_yield
90
96
  expect(config).to receive(:after).with(:suite).and_yield
91
97
  expect(::RSpec).to receive(:configure).and_yield(config)
92
98
 
99
+ expect(tracker).to receive(:stop_timer).ordered
100
+
93
101
  expect(::RSpec).to receive(:current_example).twice.and_return(current_example)
94
102
  expect(described_class).to receive(:test_path).with(example_group).and_return(test_path)
95
103
 
96
- allow(KnapsackPro).to receive(:tracker).and_return(tracker)
97
- expect(tracker).to receive(:current_test_path=).with(test_path)
98
- expect(tracker).to receive(:start_timer)
104
+ expect(tracker).to receive(:current_test_path=).with(test_path).ordered
99
105
 
100
106
  expect(example).to receive(:run)
101
107
 
102
- expect(tracker).to receive(:stop_timer)
108
+ expect(tracker).to receive(:stop_timer).ordered
103
109
 
104
110
  expect(KnapsackPro::Presenter).to receive(:global_time).and_return(global_time)
105
111
  expect(KnapsackPro).to receive(:logger).and_return(logger)
@@ -124,20 +130,26 @@ describe KnapsackPro::Adapters::RSpecAdapter do
124
130
  it 'records time for example.id' do
125
131
  expect(example).to receive(:id).and_return(test_example_path)
126
132
 
133
+ expect(config).to receive(:prepend_before).with(:context).and_yield
134
+
135
+ allow(KnapsackPro).to receive(:tracker).and_return(tracker)
136
+ expect(tracker).to receive(:start_timer).ordered
137
+
127
138
  expect(config).to receive(:around).with(:each).and_yield(example)
139
+ expect(config).to receive(:append_after).with(:context).and_yield
128
140
  expect(config).to receive(:after).with(:suite).and_yield
129
141
  expect(::RSpec).to receive(:configure).and_yield(config)
130
142
 
143
+ expect(tracker).to receive(:stop_timer).ordered
144
+
131
145
  expect(::RSpec).to receive(:current_example).twice.and_return(current_example)
132
146
  expect(described_class).to receive(:test_path).with(example_group).and_return(test_path)
133
147
 
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)
148
+ expect(tracker).to receive(:current_test_path=).with(test_example_path).ordered
137
149
 
138
150
  expect(example).to receive(:run)
139
151
 
140
- expect(tracker).to receive(:stop_timer)
152
+ expect(tracker).to receive(:stop_timer).ordered
141
153
 
142
154
  expect(KnapsackPro::Presenter).to receive(:global_time).and_return(global_time)
143
155
  expect(KnapsackPro).to receive(:logger).and_return(logger)
@@ -153,20 +165,26 @@ describe KnapsackPro::Adapters::RSpecAdapter do
153
165
  end
154
166
 
155
167
  it 'records time for current test path' do
168
+ expect(config).to receive(:prepend_before).with(:context).and_yield
169
+
170
+ allow(KnapsackPro).to receive(:tracker).and_return(tracker)
171
+ expect(tracker).to receive(:start_timer).ordered
172
+
156
173
  expect(config).to receive(:around).with(:each).and_yield(example)
174
+ expect(config).to receive(:append_after).with(:context).and_yield
157
175
  expect(config).to receive(:after).with(:suite).and_yield
158
176
  expect(::RSpec).to receive(:configure).and_yield(config)
159
177
 
160
178
  expect(::RSpec).to receive(:current_example).twice.and_return(current_example)
161
179
  expect(described_class).to receive(:test_path).with(example_group).and_return(test_path)
162
180
 
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)
181
+ expect(tracker).to receive(:stop_timer).ordered
182
+
183
+ expect(tracker).to receive(:current_test_path=).with(test_path).ordered
166
184
 
167
185
  expect(example).to receive(:run)
168
186
 
169
- expect(tracker).to receive(:stop_timer)
187
+ expect(tracker).to receive(:stop_timer).ordered
170
188
 
171
189
  expect(KnapsackPro::Presenter).to receive(:global_time).and_return(global_time)
172
190
  expect(KnapsackPro).to receive(:logger).and_return(logger)
@@ -1,4 +1,11 @@
1
- require 'test/unit/testcase'
1
+ # fake class to make tests pass and to avoid require 'test/unit/testcase' to not break RSpec
2
+ # https://www.rubydoc.info/gems/test-unit/3.4.1/Test/Unit/TestSuite
3
+ module Test
4
+ module Unit
5
+ class TestSuite
6
+ end
7
+ end
8
+ end
2
9
 
3
10
  describe KnapsackPro::Adapters::TestUnitAdapter do
4
11
  it do
@@ -75,9 +75,9 @@ describe KnapsackPro::Config::CI::Circle do
75
75
  describe '#project_dir' do
76
76
  subject { described_class.new.project_dir }
77
77
 
78
- context 'when environment exists' do
79
- let(:env) { { 'CIRCLE_PROJECT_REPONAME' => 'knapsack_pro-ruby' } }
80
- it { should eql '/home/ubuntu/knapsack_pro-ruby' }
78
+ context 'when CIRCLE_WORKING_DIRECTORY environment variable exists' do
79
+ let(:env) { { 'CIRCLE_WORKING_DIRECTORY' => '~/knapsack_pro-ruby' } }
80
+ it { should eql '~/knapsack_pro-ruby' }
81
81
  end
82
82
 
83
83
  context "when environment doesn't exist" do
@@ -15,20 +15,20 @@ describe KnapsackPro::RepositoryAdapters::GitAdapter do
15
15
 
16
16
  it { should_not be_nil }
17
17
  its(:size) { should eq 40 }
18
- it { should eq circle_sha1 } if ENV['CIRCLE_SHA1']
18
+ it { should eq circle_sha1 } if ENV['CIRCLECI']
19
19
  end
20
20
 
21
21
  describe '#branch' do
22
22
  subject { described_class.new.branch }
23
23
 
24
24
  it { should_not be_nil }
25
- it { should eq circle_branch } if ENV['CIRCLE_BRANCH']
25
+ it { should eq circle_branch } if ENV['CIRCLECI']
26
26
  end
27
27
 
28
28
  describe '#branches' do
29
29
  subject { described_class.new.branches }
30
30
 
31
31
  it { expect(subject.include?('master')).to be true }
32
- it { expect(subject.include?(circle_branch)).to be true } if ENV['CIRCLE_BRANCH']
32
+ it { expect(subject.include?(circle_branch)).to be true } if ENV['CIRCLECI']
33
33
  end
34
34
  end
@@ -100,6 +100,7 @@ describe KnapsackPro::Runners::Queue::CucumberRunner do
100
100
 
101
101
  context 'when test files exist' do
102
102
  let(:test_file_paths) { ['features/a.feature', 'features/b.feature'] }
103
+ let(:child_status) { double }
103
104
 
104
105
  before do
105
106
  subset_queue_id = 'fake-subset-queue-id'
@@ -117,8 +118,9 @@ describe KnapsackPro::Runners::Queue::CucumberRunner do
117
118
 
118
119
  expect(ENV).to receive(:[]=).with('KNAPSACK_PRO_BEFORE_QUEUE_HOOK_CALLED', 'true')
119
120
 
120
- expect($?).to receive(:exited?).and_return(process_exited)
121
- allow($?).to receive(:exitstatus).and_return(exitstatus)
121
+ allow(described_class).to receive(:child_status).and_return(child_status)
122
+ expect(child_status).to receive(:exited?).and_return(process_exited)
123
+ allow(child_status).to receive(:exitstatus).and_return(exitstatus)
122
124
  end
123
125
 
124
126
  context 'when system process finished its work (exited)' do
@@ -30,7 +30,7 @@ describe KnapsackPro::Runners::Queue::RSpecRunner do
30
30
  end
31
31
 
32
32
  context 'when args provided' do
33
- context 'when format param is not provided' do
33
+ context 'when format option is not provided' do
34
34
  let(:args) { '--example-arg example-value' }
35
35
 
36
36
  it 'uses default formatter progress' do
@@ -55,10 +55,10 @@ describe KnapsackPro::Runners::Queue::RSpecRunner do
55
55
  end
56
56
  end
57
57
 
58
- context 'when format param is provided as --format' do
58
+ context 'when format option is provided as --format' do
59
59
  let(:args) { '--format documentation' }
60
60
 
61
- it 'uses provided format param instead of default formatter progress' do
61
+ it 'uses provided format option instead of default formatter progress' do
62
62
  expected_exitstatus = 0
63
63
  expected_accumulator = {
64
64
  status: :completed,
@@ -80,10 +80,10 @@ describe KnapsackPro::Runners::Queue::RSpecRunner do
80
80
  end
81
81
  end
82
82
 
83
- context 'when format param is provided as -f' do
83
+ context 'when format option is provided as -f' do
84
84
  let(:args) { '-f d' }
85
85
 
86
- it 'uses provided format param instead of default formatter progress' do
86
+ it 'uses provided format option instead of default formatter progress' do
87
87
  expected_exitstatus = 0
88
88
  expected_accumulator = {
89
89
  status: :completed,
@@ -105,10 +105,10 @@ describe KnapsackPro::Runners::Queue::RSpecRunner do
105
105
  end
106
106
  end
107
107
 
108
- context 'when format param is provided without a delimiter' do
108
+ context 'when format option is provided without a delimiter' do
109
109
  let(:args) { '-fMyCustomFormatter' }
110
110
 
111
- it 'uses provided format param instead of default formatter progress' do
111
+ it 'uses provided format option instead of default formatter progress' do
112
112
  expected_exitstatus = 0
113
113
  expected_accumulator = {
114
114
  status: :completed,
@@ -129,6 +129,36 @@ describe KnapsackPro::Runners::Queue::RSpecRunner do
129
129
  subject
130
130
  end
131
131
  end
132
+
133
+ context 'when RSpec split by test examples feature is enabled' do
134
+ before do
135
+ expect(KnapsackPro::Config::Env).to receive(:rspec_split_by_test_examples?).and_return(true)
136
+ end
137
+
138
+ context 'when tag option is provided as --tag' do
139
+ let(:args) { '--tag example-value' }
140
+
141
+ it do
142
+ expect { subject }.to raise_error(/It is not allowed to use the RSpec tag option together with the RSpec split by test examples feature/)
143
+ end
144
+ end
145
+
146
+ context 'when tag option is provided as -t' do
147
+ let(:args) { '-t example-value' }
148
+
149
+ it do
150
+ expect { subject }.to raise_error(/It is not allowed to use the RSpec tag option together with the RSpec split by test examples feature/)
151
+ end
152
+ end
153
+
154
+ context 'when tag option is provided without delimiter' do
155
+ let(:args) { '-texample-value' }
156
+
157
+ it do
158
+ expect { subject }.to raise_error(/It is not allowed to use the RSpec tag option together with the RSpec split by test examples feature/)
159
+ end
160
+ end
161
+ end
132
162
  end
133
163
 
134
164
  context 'when args not provided' do