parallel_cucumber 0.2.22 → 0.2.23

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4d312aab611528ffc08d935c5871ba120f18e27364de8ab2bd5ea42183e4ce46
4
- data.tar.gz: 9c5f960b08d8b2f03fc937387bb735ca447791b49381c803f9d34811eb35ee9e
3
+ metadata.gz: 9bb333397a44aa5a42a0ab3fd0b2ff757727eb9fc63bb01960ad1a6d92f7d40e
4
+ data.tar.gz: 7615b9ececcb23e8e8ce863e1bc77a021a28f14cfadd710bbc9e5a7b7d68c329
5
5
  SHA512:
6
- metadata.gz: c79e25152a7f1f991b8dbc407904b8421e0eca3cc8fc45cf2217d5f0e3d39fff716310b38f77380b0d91c46bc3819002a37b735dcb8bcd0c894628827bdf4a8c
7
- data.tar.gz: f4a846cf322989b8c60d95b23e3b744408c73729aecb8f12919479c21c636f35868060aaa7afb20e1d2b54472df06f022f22529ac97f874cae8d0741c804518a
6
+ metadata.gz: 969b2a5e2dafad3fd558ba5e0cfc9ea0cbb7f2510c8ecb3d968668490b9b86319c123447989998f3e4370a45153e8b13c1eabe6060457955d264dda81e3c5b74
7
+ data.tar.gz: 2065449df4c9997df9f9e19bbbedebe8b64679d307fe91b4e4ce4106e82b8270a51e13d8706976a3caaa1cb2b85f8aacf9eee178e7b31cc91f6fa059db2e135d
@@ -15,8 +15,13 @@ module ParallelCucumber
15
15
  end
16
16
 
17
17
  ONE_SECOND = 1
18
+ STACKTRACE_COLLECTION_TIMEOUT = 10
18
19
 
19
- def exec_command(env, desc, script, logger, log_decoration = {}, timeout: 30, capture: false, return_script_error: false) # rubocop:disable Metrics/ParameterLists, Metrics/LineLength
20
+ # rubocop:disable Metrics/ParameterLists, Metrics/LineLength
21
+ def exec_command(env, desc, script, logger, log_decoration = {},
22
+ timeout: 30, capture: false, return_script_error: false,
23
+ return_on_timeout: false, collect_stacktrace: false
24
+ )
20
25
  block_name = ''
21
26
  if log_decoration['worker_block']
22
27
  if log_decoration['start'] || log_decoration['end']
@@ -28,42 +33,48 @@ module ParallelCucumber
28
33
  full_script = "#{script} 2>&1"
29
34
  env_string = env.map { |k, v| "#{k}=#{v}" }.sort.join(' ')
30
35
  logger << "== Running command `#{full_script}` at #{Time.now}\n== with environment variables: #{env_string}\n"
31
- pstat = nil
36
+ wait_thread = nil
32
37
  pout = nil
33
38
  capture &&= [''] # Pass by reference
34
39
  exception = nil
40
+ command_pid = nil
35
41
 
36
42
  begin
37
43
  completed = begin
38
- pin, pout, pstat = Open3.popen2e(env, full_script)
39
- logger << "Command has pid #{pstat[:pid]}\n"
44
+ pin, pout, wait_thread = Open3.popen2e(env, full_script)
45
+ command_pid = wait_thread[:pid].to_s
46
+ logger << "Command has pid #{command_pid}\n"
40
47
  pin.close
41
48
  out_reader = Thread.new do
42
- output_reader(pout, pstat, logger, capture)
49
+ output_reader(pout, wait_thread, logger, capture)
43
50
  end
44
51
 
45
52
  unless out_reader.join(timeout)
46
53
  raise TimedOutError
47
54
  end
48
55
 
49
- graceful_process_shutdown(out_reader, pstat, pout, logger)
56
+ graceful_process_shutdown(out_reader, wait_thread, pout, logger)
50
57
 
51
- pstat.value # reap already-terminated child.
52
- "Command completed #{pstat.value} at #{Time.now}"
58
+ wait_thread.value # reap already-terminated child.
59
+ "Command completed #{wait_thread.value} at #{Time.now}"
53
60
  end
54
61
 
55
62
  logger << "#{completed}\n"
56
63
 
57
- raise "Script returned #{pstat.value.exitstatus}" unless pstat.value.success? || return_script_error
64
+ raise "Script returned #{wait_thread.value.exitstatus}" unless wait_thread.value.success? || return_script_error
58
65
 
59
66
  capture_or_empty = capture ? capture.first : '' # Even '' is truthy
60
- return pstat.value.success? ? capture_or_empty : nil
67
+ return wait_thread.value.success? ? capture_or_empty : nil
61
68
  rescue TimedOutError => e
62
- force_kill_process_with_tree(out_reader, pstat, pout, full_script, logger, timeout)
69
+ process_tree = Helper::Processes.ps_tree
70
+ send_usr1_to_process_with_tree(command_pid, full_script, logger, process_tree) if collect_stacktrace
71
+ force_kill_process_with_tree(out_reader, wait_thread, pout, full_script, logger, timeout, process_tree, command_pid)
72
+
73
+ return capture.first if return_on_timeout
63
74
 
64
75
  exception = e
65
76
  rescue => e
66
- logger.debug("Exception #{pstat ? pstat[:pid] : "pstat=#{pstat}=nil"}")
77
+ logger.debug("Exception #{wait_thread ? wait_thread[:pid] : "wait_thread=#{wait_thread}=nil"}")
67
78
  trace = e.backtrace.join("\n\t").sub("\n\t", ": #{$ERROR_INFO}#{e.class ? " (#{e.class})" : ''}\n\t")
68
79
  logger.error("Threw for #{full_script}, caused #{trace}")
69
80
 
@@ -75,6 +86,7 @@ module ParallelCucumber
75
86
 
76
87
  raise exception
77
88
  end
89
+ # rubocop:enable Metrics/ParameterLists, Metrics/LineLength
78
90
 
79
91
  def log_until_incomplete_line(logger, out_string)
80
92
  loop do
@@ -87,13 +99,13 @@ module ParallelCucumber
87
99
 
88
100
  private
89
101
 
90
- def output_reader(pout, pstat, logger, capture)
102
+ def output_reader(pout, wait_thread, logger, capture)
91
103
  out_string = ''
92
104
 
93
105
  loop do
94
106
  io_select = IO.select([pout], [], [], ONE_SECOND)
95
- unless io_select || pstat.alive?
96
- logger << "\n== Terminating because io_select=#{io_select} when pstat.alive?=#{pstat.alive?}\n"
107
+ unless io_select || wait_thread.alive?
108
+ logger << "\n== Terminating because io_select=#{io_select} when wait_thread.alive?=#{wait_thread.alive?}\n"
97
109
  break
98
110
  end
99
111
  next unless io_select
@@ -103,44 +115,45 @@ module ParallelCucumber
103
115
  out_string = log_until_incomplete_line(logger, out_string + partial)
104
116
  end
105
117
  rescue EOFError
106
- logger << "\n== EOF is normal exit, #{pstat.inspect}\n"
118
+ logger << "\n== EOF is normal exit, #{wait_thread.inspect}\n"
107
119
  rescue => e
108
120
  logger << "\n== Exception in out_reader due to #{e.inspect} #{e.backtrace}\n"
109
121
  ensure
110
122
  logger << out_string
111
123
  logger << ["\n== Left out_reader at #{Time.now}; ",
112
- "pipe=#{pstat.status}+#{pstat.status ? '≤no value≥' : pstat.value}\n"].join
124
+ "pipe=#{wait_thread.status}+#{wait_thread.status ? '≤no value≥' : wait_thread.value}\n"].join
113
125
  end
114
126
 
115
- def graceful_process_shutdown(out_reader, pstat, pout, logger)
116
- out_reader.value # Should terminate with pstat
127
+ def graceful_process_shutdown(out_reader, wait_thread, pout, logger)
128
+ out_reader.value # Should terminate with wait_thread
117
129
  pout.close
118
- if pstat.status
119
- logger << "== Thread #{pstat.inspect} is not dead"
130
+ if wait_thread.status
131
+ logger << "== Thread #{wait_thread.inspect} is not dead"
120
132
 
121
- if pstat.join(3)
122
- logger << "== Thread #{pstat.inspect} joined late"
133
+ if wait_thread.join(3)
134
+ logger << "== Thread #{wait_thread.inspect} joined late"
123
135
  else
124
- pstat.terminate # Just in case
125
- logger << "== Thread #{pstat.inspect} terminated"
136
+ wait_thread.terminate # Just in case
137
+ logger << "== Thread #{wait_thread.inspect} terminated"
126
138
  end # Make an effort to reap
127
139
  end
128
140
 
129
- pstat.value # reap already-terminated child.
130
- "Command completed #{pstat.value} at #{Time.now}"
141
+ wait_thread.value # reap already-terminated child.
142
+ "Command completed #{wait_thread.value} at #{Time.now}"
131
143
  end
132
144
 
133
- def force_kill_process_with_tree(out_reader, pstat, pout, full_script, logger, timeout) # rubocop:disable Metrics/ParameterLists, Metrics/LineLength
145
+ def send_usr1_to_process_with_tree(command_pid, full_script, logger, tree)
146
+ return if Helper::Processes.ms_windows?
147
+
148
+ logger << "Timeout, so trying SIGUSR1 to trigger watchdog stacktrace #{command_pid}=#{full_script}"
149
+ Helper::Processes.kill_tree('SIGUSR1', command_pid, logger, tree)
150
+ sleep(STACKTRACE_COLLECTION_TIMEOUT) # Wait enough time for child processes to act on SIGUSR1
151
+ end
152
+
153
+ def force_kill_process_with_tree(out_reader, wait_thread, pout, full_script, logger, timeout, tree, pid) # rubocop:disable Metrics/ParameterLists, Metrics/LineLength
134
154
  out_reader.exit
135
- tree = Helper::Processes.ps_tree
136
- pid = pstat[:pid].to_s
137
- unless Helper::Processes.ms_windows?
138
- logger << "Timeout, so trying SIGUSR1 to trigger watchdog stacktrace #{pstat[:pid]}=#{full_script}"
139
- Helper::Processes.kill_tree('SIGUSR1', pid, logger, tree)
140
- sleep 2
141
- end
142
155
 
143
- logger << "Timeout, so trying SIGINT at #{pstat[:pid]}=#{full_script}"
156
+ logger << "Timeout, so trying SIGINT at #{wait_thread[:pid]}=#{full_script}"
144
157
 
145
158
  log_copy = Thread.new do
146
159
  pout.each_line { |l| logger << l }
@@ -167,7 +180,7 @@ module ParallelCucumber
167
180
  end
168
181
 
169
182
  logger << "About to reap root #{pid}"
170
- pstat.value # reap root - everything else should be reaped by init.
183
+ wait_thread.value # reap root - everything else should be reaped by init.
171
184
  logger << "Reaped root #{pid}"
172
185
  end
173
186
  end
@@ -35,7 +35,7 @@ module ParallelCucumber
35
35
  else
36
36
  descendants(root, logger, tree, old_tree, 'kill') do |pid, node|
37
37
  begin
38
- logger.warn "Killing #{node}"
38
+ logger.warn "Sending signal #{sig} to #{node}"
39
39
  Process.kill(sig, pid.to_i)
40
40
  rescue Errno::ESRCH
41
41
  nil # It's gone already? Hurrah!
@@ -44,7 +44,7 @@ module ParallelCucumber
44
44
  end
45
45
  # Let's kill pid unconditionally: descendants will go astray once reparented.
46
46
  begin
47
- logger.warn "Killing #{root} just in case"
47
+ logger.warn "Sending signal #{sig} to root process #{root} just in case"
48
48
  Process.kill(sig, root.to_i)
49
49
  rescue Errno::ESRCH
50
50
  nil # It's gone already? Hurrah!
@@ -1,3 +1,3 @@
1
1
  module ParallelCucumber
2
- VERSION = '0.2.22'.freeze
2
+ VERSION = '0.2.23'.freeze
3
3
  end
@@ -201,7 +201,8 @@ module ParallelCucumber
201
201
  begin
202
202
  ParallelCucumber::Helper::Command.exec_command(
203
203
  batch_env, 'batch', mapped_batch_cmd, @logger, @log_decoration,
204
- timeout: @batch_timeout, return_script_error: true
204
+ timeout: @batch_timeout, capture: true, return_script_error: true,
205
+ return_on_timeout: true, collect_stacktrace: true
205
206
  )
206
207
  rescue => e
207
208
  @logger << "ERROR #{e} #{e.backtrace.first(5)}"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parallel_cucumber
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.22
4
+ version: 0.2.23
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Bayandin
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-23 00:00:00.000000000 Z
11
+ date: 2020-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cucumber
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - '='
74
74
  - !ruby/object:Gem::Version
75
- version: 0.59.2
75
+ version: 0.73.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - '='
81
81
  - !ruby/object:Gem::Version
82
- version: 0.59.2
82
+ version: 0.73.0
83
83
  description: Our own parallel cucumber with queue and workers
84
84
  email: a.bayandin@gmail.com
85
85
  executables:
@@ -112,7 +112,7 @@ homepage: https://github.com/badoo/parallel_cucumber
112
112
  licenses:
113
113
  - MIT
114
114
  metadata: {}
115
- post_install_message:
115
+ post_install_message:
116
116
  rdoc_options: []
117
117
  require_paths:
118
118
  - lib
@@ -127,8 +127,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
127
  - !ruby/object:Gem::Version
128
128
  version: '0'
129
129
  requirements: []
130
- rubygems_version: 3.0.3
131
- signing_key:
130
+ rubygems_version: 3.0.8
131
+ signing_key:
132
132
  specification_version: 4
133
133
  summary: Run cucumber in parallel
134
134
  test_files: []