unobtainium 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.rubocop.yml +6 -0
- data/Gemfile.lock +44 -0
- data/README.md +17 -14
- data/Rakefile +20 -4
- data/config/config.yml +14 -0
- data/docs/CONFIGURATION.md +250 -0
- data/{cuke/features → features}/step_definitions/steps.rb +0 -0
- data/{cuke/features → features}/support/env.rb +0 -0
- data/{cuke/features → features}/world.feature +0 -0
- data/lib/unobtainium/config.rb +31 -10
- data/lib/unobtainium/driver.rb +55 -36
- data/lib/unobtainium/drivers/appium.rb +16 -13
- data/lib/unobtainium/drivers/phantom.rb +138 -0
- data/lib/unobtainium/drivers/selenium.rb +6 -5
- data/lib/unobtainium/pathed_hash.rb +29 -7
- data/lib/unobtainium/recursive_merge.rb +15 -0
- data/lib/unobtainium/runtime.rb +33 -8
- data/lib/unobtainium/support/port_scanner.rb +160 -0
- data/lib/unobtainium/support/runner.rb +180 -0
- data/lib/unobtainium/{drivers/support → support}/util.rb +10 -3
- data/lib/unobtainium/version.rb +2 -1
- data/lib/unobtainium/world.rb +6 -3
- data/spec/port_scanner_spec.rb +131 -0
- data/spec/runner_spec.rb +66 -0
- data/spec/utility_spec.rb +31 -0
- data/unobtainium.gemspec +9 -1
- metadata +105 -12
- data/cuke/.gitignore +0 -2
- data/cuke/Gemfile +0 -13
- data/cuke/Gemfile.lock +0 -59
- data/cuke/README.md +0 -2
- data/cuke/config/config.yml +0 -7
@@ -7,9 +7,11 @@
|
|
7
7
|
# All rights reserved.
|
8
8
|
#
|
9
9
|
|
10
|
-
require_relative '
|
10
|
+
require_relative '../support/util'
|
11
11
|
|
12
12
|
module Unobtainium
|
13
|
+
# @api private
|
14
|
+
# Contains driver implementations
|
13
15
|
module Drivers
|
14
16
|
|
15
17
|
##
|
@@ -20,13 +22,12 @@ module Unobtainium
|
|
20
22
|
firefox: [:ff,],
|
21
23
|
internet_explorer: [:internetexplorer, :explorer, :ie,],
|
22
24
|
safari: [],
|
23
|
-
chrome: [],
|
24
|
-
chromium: [],
|
25
|
+
chrome: [:chromium],
|
25
26
|
remote: [],
|
26
27
|
}.freeze
|
27
28
|
|
28
29
|
class << self
|
29
|
-
include ::Unobtainium::
|
30
|
+
include ::Unobtainium::Support::Utility
|
30
31
|
|
31
32
|
##
|
32
33
|
# Return true if the given label matches this driver implementation,
|
@@ -47,7 +48,7 @@ module Unobtainium
|
|
47
48
|
|
48
49
|
##
|
49
50
|
# Selenium really wants symbol keys for the options
|
50
|
-
def
|
51
|
+
def resolve_options(label, options)
|
51
52
|
new_opts = {}
|
52
53
|
|
53
54
|
if not options.nil?
|
@@ -13,14 +13,25 @@ module Unobtainium
|
|
13
13
|
|
14
14
|
##
|
15
15
|
# The PathedHash class wraps Hash by offering pathed access on top of
|
16
|
-
# regular access, i.e. instead of h["first"]["second"] you can write
|
17
|
-
# h["first.second"]
|
16
|
+
# regular access, i.e. instead of `h["first"]["second"]` you can write
|
17
|
+
# `h["first.second"]`.
|
18
|
+
#
|
19
|
+
# The main benefit is much simpler code for accessing nested structured.
|
20
|
+
# For any given path, PathedHash will return nil from `[]` if *any* of
|
21
|
+
# the path components do not exist.
|
22
|
+
#
|
23
|
+
# Similarly, intermediate nodes will be created when you write a value
|
24
|
+
# for a path.
|
25
|
+
#
|
26
|
+
# PathedHash also includes RecursiveMerge.
|
18
27
|
class PathedHash
|
19
28
|
include RecursiveMerge
|
20
29
|
|
21
30
|
##
|
22
|
-
# Initializer
|
23
|
-
|
31
|
+
# Initializer. Accepts `nil`, hashes or pathed hashes.
|
32
|
+
#
|
33
|
+
# @param init [NilClass, Hash] initial values.
|
34
|
+
def initialize(init = nil)
|
24
35
|
if init.nil?
|
25
36
|
@data = {}
|
26
37
|
else
|
@@ -29,18 +40,23 @@ module Unobtainium
|
|
29
40
|
@separator = '.'
|
30
41
|
end
|
31
42
|
|
32
|
-
#
|
43
|
+
# @return [String] the separator is the character or pattern splitting paths.
|
33
44
|
attr_accessor :separator
|
34
45
|
|
46
|
+
# @api private
|
47
|
+
# Methods redefined to support pathed read access.
|
35
48
|
READ_METHODS = [
|
36
49
|
:[], :default, :delete, :fetch, :has_key?, :include?, :key?, :member?,
|
37
50
|
].freeze
|
51
|
+
|
52
|
+
# @api private
|
53
|
+
# Methods redefined to support pathed write access.
|
38
54
|
WRITE_METHODS = [
|
39
55
|
:[]=, :store,
|
40
56
|
].freeze
|
41
57
|
|
42
58
|
##
|
43
|
-
#
|
59
|
+
# @return [RegExp] the pattern to split paths at; based on `separator`
|
44
60
|
def split_pattern
|
45
61
|
/(?<!\\)#{Regexp.escape(@separator)}/
|
46
62
|
end
|
@@ -99,14 +115,18 @@ module Unobtainium
|
|
99
115
|
end
|
100
116
|
end
|
101
117
|
|
118
|
+
# @return [String] string representation
|
102
119
|
def to_s
|
103
120
|
@data.to_s
|
104
121
|
end
|
105
122
|
|
123
|
+
# @return [PathedHash] duplicate, as `.dup` usually works
|
106
124
|
def dup
|
107
125
|
PathedHash.new(@data.dup)
|
108
126
|
end
|
109
127
|
|
128
|
+
# In place merge, as it usually works for hashes.
|
129
|
+
# @return [PathedHash] self
|
110
130
|
def merge!(*args, &block)
|
111
131
|
# FIXME: we may need other methods like this. This is used by
|
112
132
|
# RecursiveMerge, so we know it's required.
|
@@ -114,7 +134,7 @@ module Unobtainium
|
|
114
134
|
end
|
115
135
|
|
116
136
|
##
|
117
|
-
# Map any missing method to the
|
137
|
+
# Map any missing method to the Hash implementation
|
118
138
|
def respond_to?(meth)
|
119
139
|
if not @data.nil? and @data.respond_to?(meth)
|
120
140
|
return true
|
@@ -122,6 +142,8 @@ module Unobtainium
|
|
122
142
|
return super
|
123
143
|
end
|
124
144
|
|
145
|
+
##
|
146
|
+
# Map any missing method to the Hash implementation
|
125
147
|
def method_missing(meth, *args, &block)
|
126
148
|
if not @data.nil? and @data.respond_to?(meth)
|
127
149
|
return @data.send(meth.to_s, *args, &block)
|
@@ -11,6 +11,18 @@ module Unobtainium
|
|
11
11
|
##
|
12
12
|
# Provides recursive merge functions for hashes. Used in PathedHash.
|
13
13
|
module RecursiveMerge
|
14
|
+
##
|
15
|
+
# Recursively merge `:other` into this Hash.
|
16
|
+
#
|
17
|
+
# This starts by merging the leaf-most Hash entries. Arrays are merged
|
18
|
+
# by addition.
|
19
|
+
#
|
20
|
+
# For everything that's neither Hash or Array, if the `:overwrite`
|
21
|
+
# parameter is true, the entry from `:other` is used. Otherwise the entry
|
22
|
+
# from `:self` is used.
|
23
|
+
#
|
24
|
+
# @param other [Hash] the hash to merge into `:self`
|
25
|
+
# @param overwrite [Boolean] see method description.
|
14
26
|
def recursive_merge!(other, overwrite = true)
|
15
27
|
if other.nil?
|
16
28
|
return self
|
@@ -33,6 +45,9 @@ module Unobtainium
|
|
33
45
|
merge!(other, &merger)
|
34
46
|
end
|
35
47
|
|
48
|
+
##
|
49
|
+
# Same as `dup.recursive_merge!`
|
50
|
+
# @param (see #recursive_merge!)
|
36
51
|
def recursive_merge(other, overwrite = true)
|
37
52
|
dup.recursive_merge!(other, overwrite)
|
38
53
|
end
|
data/lib/unobtainium/runtime.rb
CHANGED
@@ -34,13 +34,14 @@ module Unobtainium
|
|
34
34
|
end
|
35
35
|
|
36
36
|
##
|
37
|
-
#
|
37
|
+
# @return [Integer] number of objects stored in the object map
|
38
38
|
def length
|
39
39
|
return @objects.length
|
40
40
|
end
|
41
41
|
|
42
42
|
##
|
43
|
-
#
|
43
|
+
# @param name [String, Symbol] name or label for an object
|
44
|
+
# @return [Boolean] does an object with the given name exist?
|
44
45
|
def has?(name)
|
45
46
|
return @objects.key?(name)
|
46
47
|
end
|
@@ -51,8 +52,14 @@ module Unobtainium
|
|
51
52
|
# is stored.
|
52
53
|
#
|
53
54
|
# If a destructor is passed, it is used to destroy the *new* object only.
|
54
|
-
# If no destructor is passed and the object responds to a
|
55
|
-
# method is called.
|
55
|
+
# If no destructor is passed and the object responds to a `#destroy` method,
|
56
|
+
# that method is called.
|
57
|
+
#
|
58
|
+
# @param name [String, Symbol] name or label for the object to store
|
59
|
+
# @param object [Object] the object to store
|
60
|
+
# @param destructor [Func] a custom destructor accepting the object as its
|
61
|
+
# parameter.
|
62
|
+
# @return [Object] the stored object
|
56
63
|
def store(name, object, destructor = nil)
|
57
64
|
delete(name)
|
58
65
|
|
@@ -65,7 +72,13 @@ module Unobtainium
|
|
65
72
|
# Store the object returned by the block, if any. If no object is returned
|
66
73
|
# or no block is given, this function does nothing.
|
67
74
|
#
|
68
|
-
# Otherwise it works much like
|
75
|
+
# Otherwise it works much like `#store`.
|
76
|
+
#
|
77
|
+
# @param name [String, Symbol] name or label for the object to store
|
78
|
+
# @param destructor [Func] a custom destructor accepting the object as its
|
79
|
+
# parameter.
|
80
|
+
# @param block [Func] a block returning the created object.
|
81
|
+
# @return [Object] the stored object
|
69
82
|
def store_with(name, destructor = nil, &block)
|
70
83
|
object = nil
|
71
84
|
if not block.nil?
|
@@ -80,7 +93,8 @@ module Unobtainium
|
|
80
93
|
end
|
81
94
|
|
82
95
|
##
|
83
|
-
#
|
96
|
+
# (see #store)
|
97
|
+
# Like `#store`, but only stores the object if none exists for that key yet.
|
84
98
|
def store_if(name, object, destructor = nil)
|
85
99
|
if has?(name)
|
86
100
|
return self[name]
|
@@ -89,7 +103,8 @@ module Unobtainium
|
|
89
103
|
end
|
90
104
|
|
91
105
|
##
|
92
|
-
#
|
106
|
+
# (see #store_with)
|
107
|
+
# Like `#store_if`, but as a block version similar to `#store_with`.
|
93
108
|
def store_with_if(name, destructor = nil, &block)
|
94
109
|
if has?(name)
|
95
110
|
return self[name]
|
@@ -99,6 +114,8 @@ module Unobtainium
|
|
99
114
|
|
100
115
|
##
|
101
116
|
# Deletes (and destroys) any object found under the given name.
|
117
|
+
#
|
118
|
+
# @param name [String, Symbol] name or label for the object to store
|
102
119
|
def delete(name)
|
103
120
|
if not @objects.key?(name)
|
104
121
|
return
|
@@ -112,6 +129,11 @@ module Unobtainium
|
|
112
129
|
##
|
113
130
|
# Returns the object with the given name, or the default value if no such
|
114
131
|
# object exists.
|
132
|
+
#
|
133
|
+
# @param name [String, Symbol] name or label for the object to retrieve
|
134
|
+
# @param default [Object] default value to return if no object is found for
|
135
|
+
# name or label.
|
136
|
+
# @return [Object] the object matching the name/label, or the default value.
|
115
137
|
def fetch(name, default = nil)
|
116
138
|
return @objects.fetch(name)[0]
|
117
139
|
rescue KeyError
|
@@ -122,8 +144,11 @@ module Unobtainium
|
|
122
144
|
end
|
123
145
|
|
124
146
|
##
|
125
|
-
# Similar to
|
147
|
+
# Similar to `#fetch`, but always returns nil for an object that could not
|
126
148
|
# be found.
|
149
|
+
#
|
150
|
+
# @param name [String, Symbol] name or label for the object to retrieve
|
151
|
+
# @return [Object] the object matching the name/label, or the default value.
|
127
152
|
def [](name)
|
128
153
|
val = @objects[name]
|
129
154
|
if val.nil?
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
#
|
3
|
+
# unobtainium
|
4
|
+
# https://github.com/jfinkhaeuser/unobtainium
|
5
|
+
#
|
6
|
+
# Copyright (c) 2016 Jens Finkhaeuser and other unobtainium contributors.
|
7
|
+
# All rights reserved.
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'socket'
|
11
|
+
|
12
|
+
module Unobtainium
|
13
|
+
# @api private
|
14
|
+
# Contains support code
|
15
|
+
module Support
|
16
|
+
##
|
17
|
+
# A bit of metaprogramming hackery to make a constant with possible domains
|
18
|
+
# from Socket::Constants.
|
19
|
+
if not constants.include?('DOMAINS')
|
20
|
+
domains = []
|
21
|
+
Socket::Constants.constants.each do |name|
|
22
|
+
if name.to_s.start_with?("AF_")
|
23
|
+
domains << name.to_s.gsub(/^AF_/, '').to_sym
|
24
|
+
end
|
25
|
+
end
|
26
|
+
const_set('DOMAINS', domains.freeze)
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# A port scanner for finding a free port for running e.g. a selenium
|
31
|
+
# or appium server.
|
32
|
+
module PortScanner
|
33
|
+
##
|
34
|
+
# Returns true if the port is open on the host, false otherwise.
|
35
|
+
# @param host [String] host name or IP address
|
36
|
+
# @param port [Integer] port number (1..65535)
|
37
|
+
# @param domains [Array/Symbol] :INET, :INET6, etc. or an Array of
|
38
|
+
# these. Any from Socket::Constants::AF_* work. Defaults to
|
39
|
+
# [:INET, :INET6].
|
40
|
+
def port_open?(host, port, domains = [:INET, :INET6])
|
41
|
+
if port < 1 or port > 65535
|
42
|
+
raise ArgumentError, "Port must be in range 1..65535!"
|
43
|
+
end
|
44
|
+
|
45
|
+
test_domains = nil
|
46
|
+
if domains.is_a? Array
|
47
|
+
test_domains = domains.dup
|
48
|
+
else
|
49
|
+
test_domains = [domains]
|
50
|
+
end
|
51
|
+
|
52
|
+
test_domains.each do |domain|
|
53
|
+
if not DOMAINS.include?(domain)
|
54
|
+
raise ArgumentError, "Domains must be one of #{DOMAINS}, or an Array "\
|
55
|
+
"of them, but #{domain} isn't!"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Test a socket for each domain
|
60
|
+
addr = Socket.sockaddr_in(port, host)
|
61
|
+
|
62
|
+
test_domains.each do |domain|
|
63
|
+
begin
|
64
|
+
sock = Socket.new(domain, :STREAM)
|
65
|
+
return 0 == sock.connect(addr)
|
66
|
+
rescue Errno::EAFNOSUPPORT
|
67
|
+
next # try next domain
|
68
|
+
rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT
|
69
|
+
return false
|
70
|
+
ensure
|
71
|
+
if not sock.nil?
|
72
|
+
sock.close
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# Scan a mixture of ranges and arrays of ports for a given host.
|
80
|
+
# Return those that are open or closed, depending on the options
|
81
|
+
# given.
|
82
|
+
def scan(host, *args)
|
83
|
+
# Argument checks
|
84
|
+
if args.empty?
|
85
|
+
raise ArgumentError, "Need at least one port to scan!"
|
86
|
+
end
|
87
|
+
|
88
|
+
args.each do |item|
|
89
|
+
if not item.respond_to?(:each) and not item.respond_to?(:to_i)
|
90
|
+
raise ArgumentError, "The argument '#{item}' to #scan is not a "\
|
91
|
+
"Range, Array or convertible to Integer, aborting!"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# If the last argument is a Hash, treat it as options.
|
96
|
+
opts = {}
|
97
|
+
if args.last.is_a? Hash
|
98
|
+
opts = args.pop
|
99
|
+
end
|
100
|
+
opts = { for: :open, amount: :all }.merge(opts)
|
101
|
+
|
102
|
+
if not [:all, :first].include?(opts[:amount])
|
103
|
+
raise ArgumentError, ":amount must be one of :all, :first!"
|
104
|
+
end
|
105
|
+
if not [:open, :closed, :available].include?(opts[:for])
|
106
|
+
raise ArgumentError, ":for must beone of :open, :closed, :available!"
|
107
|
+
end
|
108
|
+
|
109
|
+
return run_scan(host, opts, *args)
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def run_scan(host, opts, *args)
|
115
|
+
results = []
|
116
|
+
|
117
|
+
# Iteratively scan all arguments
|
118
|
+
args.each do |item|
|
119
|
+
if item.respond_to?(:to_i)
|
120
|
+
item_i = item.to_i
|
121
|
+
if test_port(host, item_i, opts[:for])
|
122
|
+
results << item_i
|
123
|
+
if opts[:amount] == :first
|
124
|
+
return results
|
125
|
+
end
|
126
|
+
end
|
127
|
+
next
|
128
|
+
end
|
129
|
+
|
130
|
+
item.each do |port|
|
131
|
+
if not test_port(host, port, opts[:for])
|
132
|
+
next
|
133
|
+
end
|
134
|
+
|
135
|
+
results << port
|
136
|
+
if opts[:amount] == :first
|
137
|
+
return results
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
return results
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_port(host, port, test_for)
|
146
|
+
open = port_open?(host, port)
|
147
|
+
|
148
|
+
if open and :open == test_for
|
149
|
+
return true
|
150
|
+
end
|
151
|
+
|
152
|
+
if not open and [:closed, :available].include?(test_for)
|
153
|
+
return true
|
154
|
+
end
|
155
|
+
|
156
|
+
return false
|
157
|
+
end
|
158
|
+
end # module PortScanner
|
159
|
+
end # module Support
|
160
|
+
end # module Unobtainium
|
@@ -0,0 +1,180 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
#
|
3
|
+
# unobtainium
|
4
|
+
# https://github.com/jfinkhaeuser/unobtainium
|
5
|
+
#
|
6
|
+
# Copyright (c) 2016 Jens Finkhaeuser and other unobtainium contributors.
|
7
|
+
# All rights reserved.
|
8
|
+
#
|
9
|
+
require 'sys-proctable'
|
10
|
+
|
11
|
+
module Unobtainium
|
12
|
+
# @api private
|
13
|
+
# Contains support code
|
14
|
+
module Support
|
15
|
+
##
|
16
|
+
# Runs a shell command and detaches. Offers methods for managing the process
|
17
|
+
# lifetime. Implements #destroy, so you can use it with the Runtime class to
|
18
|
+
# kill the shell command upon script end.
|
19
|
+
class Runner
|
20
|
+
# @return [String] the ID passed to the constructor
|
21
|
+
attr_reader :id
|
22
|
+
|
23
|
+
# @return [Array] the command passed to the constructor
|
24
|
+
attr_reader :command
|
25
|
+
|
26
|
+
# @return [Integer] if the command is started, the pid of the process,
|
27
|
+
# or nil otherwise.
|
28
|
+
attr_reader :pid
|
29
|
+
|
30
|
+
# @return [IO] if the command is started, an IO object to read the
|
31
|
+
# commands output from, or nil otherwise.
|
32
|
+
attr_reader :stdout
|
33
|
+
|
34
|
+
# @return [IO] if the command is started, an IO object to read the
|
35
|
+
# commands error output from, or nil otherwise.
|
36
|
+
attr_reader :stderr
|
37
|
+
|
38
|
+
##
|
39
|
+
# Initialize with a shell command, but do not run yet.
|
40
|
+
#
|
41
|
+
# @param id [String] a unique ID for the command. This is to differentiate
|
42
|
+
# multiple similar commands from each other.
|
43
|
+
# @param command [Array] the remaining parameters are the command and its
|
44
|
+
# arguments.
|
45
|
+
def initialize(id, *command)
|
46
|
+
if command.empty?
|
47
|
+
raise ArgumentError, "Command may not be empty!"
|
48
|
+
end
|
49
|
+
|
50
|
+
@id = id
|
51
|
+
@command = command
|
52
|
+
@pid = nil
|
53
|
+
@stdout = nil
|
54
|
+
@stderr = nil
|
55
|
+
@wout = nil
|
56
|
+
@werr = nil
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# Start the command. Afterwards, #pid, #stdout, and #stderr should be
|
61
|
+
# non-nil.
|
62
|
+
# @return [Integer] the pid of the command process
|
63
|
+
def start
|
64
|
+
if not @pid.nil?
|
65
|
+
raise "Command already running!"
|
66
|
+
end
|
67
|
+
|
68
|
+
# Capture options; pipes for stdout and stderr
|
69
|
+
@stdout, @wout = IO.pipe
|
70
|
+
@stderr, @werr = IO.pipe
|
71
|
+
opts = {
|
72
|
+
out: @wout,
|
73
|
+
err: @werr,
|
74
|
+
}
|
75
|
+
|
76
|
+
@pid = spawn({}, *@command, opts)
|
77
|
+
return @pid
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Wait for the command to exit.
|
82
|
+
# @return [Process::Status] exit status of the command.
|
83
|
+
def wait
|
84
|
+
_, status = Process.wait2(@pid)
|
85
|
+
cleanup
|
86
|
+
return status
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Send the "KILL" signal to the command process and all its children
|
91
|
+
def kill
|
92
|
+
signal("KILL", scope: :all)
|
93
|
+
cleanup
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# Send the given signal to the process, and/or it's children.
|
98
|
+
# @param signal [String] the signal to send
|
99
|
+
# @param scope [Symbol] one of :self (the command process),
|
100
|
+
# :children (it's children *only) or :all (the process and its
|
101
|
+
# children.
|
102
|
+
def signal(signal, scope: :self)
|
103
|
+
if @pid.nil?
|
104
|
+
raise "No command is running!"
|
105
|
+
end
|
106
|
+
|
107
|
+
if not [:self, :children, :all].include?(scope)
|
108
|
+
raise ArgumentError, "The :scope argument must be one of :self, "\
|
109
|
+
":children or :all!"
|
110
|
+
end
|
111
|
+
|
112
|
+
# Figure out which pids to send the signal to. That is usually @pid,
|
113
|
+
# but possibly its children.
|
114
|
+
to_send = []
|
115
|
+
if [:self, :all].include?(scope)
|
116
|
+
to_send << @pid
|
117
|
+
end
|
118
|
+
|
119
|
+
if [:children, :all].include?(scope)
|
120
|
+
children = ::Sys::ProcTable.ps.select { |p| p.ppid == @pid }
|
121
|
+
to_send += children.collect(&:pid)
|
122
|
+
end
|
123
|
+
|
124
|
+
if to_send.empty?
|
125
|
+
raise "This should not happen. I have no pids to send a signal to!"
|
126
|
+
end
|
127
|
+
|
128
|
+
# Alright, send the signal!
|
129
|
+
to_send.each do |pid|
|
130
|
+
# rubocop:disable Lint/HandleExceptions
|
131
|
+
begin
|
132
|
+
Process.kill(signal, pid)
|
133
|
+
rescue
|
134
|
+
# If the kill didn't work, we don't really care.
|
135
|
+
end
|
136
|
+
# rubocop:enable Lint/HandleExceptions
|
137
|
+
end
|
138
|
+
|
139
|
+
# Clean up everything
|
140
|
+
cleanup(true)
|
141
|
+
end
|
142
|
+
|
143
|
+
##
|
144
|
+
# (see #kill)
|
145
|
+
# Use together with Runtime class to clean up any commands at exit.
|
146
|
+
def destroy
|
147
|
+
kill
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def cleanup(all = false)
|
153
|
+
@pid = nil
|
154
|
+
# rubocop:disable Style/GuardClause
|
155
|
+
if not @wout.nil?
|
156
|
+
@wout.close
|
157
|
+
@wout = nil
|
158
|
+
end
|
159
|
+
if not @werr.nil?
|
160
|
+
@werr.close
|
161
|
+
@werr = nil
|
162
|
+
end
|
163
|
+
|
164
|
+
if not all
|
165
|
+
return
|
166
|
+
end
|
167
|
+
|
168
|
+
if not @stdout.nil?
|
169
|
+
@stdout.close
|
170
|
+
@stdout = nil
|
171
|
+
end
|
172
|
+
if not @stderr.nil?
|
173
|
+
@stderr.close
|
174
|
+
@stderr = nil
|
175
|
+
end
|
176
|
+
# rubocop:enable Style/GuardClause
|
177
|
+
end
|
178
|
+
end # class Runner
|
179
|
+
end # module Support
|
180
|
+
end # module Unobtainium
|