parallel_cucumber 0.2.22 → 0.2.23
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.
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: []
|