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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9bb333397a44aa5a42a0ab3fd0b2ff757727eb9fc63bb01960ad1a6d92f7d40e
|
4
|
+
data.tar.gz: 7615b9ececcb23e8e8ce863e1bc77a021a28f14cfadd710bbc9e5a7b7d68c329
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
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,
|
39
|
-
|
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,
|
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,
|
56
|
+
graceful_process_shutdown(out_reader, wait_thread, pout, logger)
|
50
57
|
|
51
|
-
|
52
|
-
"Command completed #{
|
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 #{
|
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
|
67
|
+
return wait_thread.value.success? ? capture_or_empty : nil
|
61
68
|
rescue TimedOutError => e
|
62
|
-
|
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 #{
|
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,
|
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 ||
|
96
|
-
logger << "\n== Terminating because io_select=#{io_select} when
|
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, #{
|
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=#{
|
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,
|
116
|
-
out_reader.value # Should terminate with
|
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
|
119
|
-
logger << "== Thread #{
|
130
|
+
if wait_thread.status
|
131
|
+
logger << "== Thread #{wait_thread.inspect} is not dead"
|
120
132
|
|
121
|
-
if
|
122
|
-
logger << "== Thread #{
|
133
|
+
if wait_thread.join(3)
|
134
|
+
logger << "== Thread #{wait_thread.inspect} joined late"
|
123
135
|
else
|
124
|
-
|
125
|
-
logger << "== Thread #{
|
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
|
-
|
130
|
-
"Command completed #{
|
141
|
+
wait_thread.value # reap already-terminated child.
|
142
|
+
"Command completed #{wait_thread.value} at #{Time.now}"
|
131
143
|
end
|
132
144
|
|
133
|
-
def
|
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 #{
|
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
|
-
|
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 "
|
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 "
|
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!
|
@@ -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.
|
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-
|
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.
|
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.
|
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.
|
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: []
|