net-ping 2.1.0-universal-linux
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 +7 -0
- data/CHANGES +28 -0
- data/CHANGES.out_of_date +354 -0
- data/CLAUDE.md +89 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +53 -0
- data/MANIFEST +25 -0
- data/README.md +65 -0
- data/Rakefile +183 -0
- data/doc/ping.txt +246 -0
- data/docs/superpowers/plans/2026-07-25-platform-specific-gems.md +317 -0
- data/docs/superpowers/specs/2026-07-25-platform-specific-gems-design.md +71 -0
- data/examples/example_pingexternal.rb +16 -0
- data/examples/example_pinghttp.rb +22 -0
- data/examples/example_pingtcp.rb +16 -0
- data/examples/example_pingudp.rb +12 -0
- data/lib/net/ping/external.rb +185 -0
- data/lib/net/ping/http.rb +189 -0
- data/lib/net/ping/icmp.rb +188 -0
- data/lib/net/ping/ping.rb +99 -0
- data/lib/net/ping/tcp.rb +110 -0
- data/lib/net/ping/udp.rb +117 -0
- data/lib/net/ping/version.rb +6 -0
- data/lib/net/ping/wmi.rb +118 -0
- data/lib/net/ping.rb +24 -0
- data/net-ping-universal-linux.gemspec +4 -0
- data/net-ping-universal-mingw-ucrt.gemspec +7 -0
- data/net-ping.gemspec +36 -0
- data/test/test_net_ping.rb +44 -0
- data/test/test_net_ping_external.rb +232 -0
- data/test/test_net_ping_gem_packaging.rb +333 -0
- data/test/test_net_ping_http.rb +264 -0
- data/test/test_net_ping_icmp.rb +199 -0
- data/test/test_net_ping_tcp.rb +106 -0
- data/test/test_net_ping_udp.rb +117 -0
- data/test/test_net_ping_wmi.rb +81 -0
- metadata +159 -0
data/lib/net/ping/wmi.rb
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'ping')
|
|
2
|
+
require 'win32ole'
|
|
3
|
+
|
|
4
|
+
# The Net module serves as a namespace only.
|
|
5
|
+
#
|
|
6
|
+
module Net
|
|
7
|
+
|
|
8
|
+
# The Ping::WMI class encapsulates the Win32_PingStatus WMI class for
|
|
9
|
+
# MS Windows.
|
|
10
|
+
#
|
|
11
|
+
class Ping::WMI < Ping
|
|
12
|
+
|
|
13
|
+
PingStatus = Struct.new(
|
|
14
|
+
'PingStatus',
|
|
15
|
+
:address,
|
|
16
|
+
:buffer_size,
|
|
17
|
+
:no_fragmentation,
|
|
18
|
+
:primary_address_resolution_status,
|
|
19
|
+
:protocol_address,
|
|
20
|
+
:protocol_address_resolved,
|
|
21
|
+
:record_route,
|
|
22
|
+
:reply_inconsistency,
|
|
23
|
+
:reply_size,
|
|
24
|
+
:resolve_address_names,
|
|
25
|
+
:response_time,
|
|
26
|
+
:response_time_to_live,
|
|
27
|
+
:route_record,
|
|
28
|
+
:route_record_resolved,
|
|
29
|
+
:source_route,
|
|
30
|
+
:source_route_type,
|
|
31
|
+
:status_code,
|
|
32
|
+
:timeout,
|
|
33
|
+
:timestamp_record,
|
|
34
|
+
:timestamp_record_address,
|
|
35
|
+
:timestamp_record_address_resolved,
|
|
36
|
+
:timestamp_route,
|
|
37
|
+
:time_to_live,
|
|
38
|
+
:type_of_service
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Unlike the ping method for other Ping subclasses, this version returns
|
|
42
|
+
# a PingStatus struct which contains various bits of information about
|
|
43
|
+
# the results of the ping itself, such as response time.
|
|
44
|
+
#
|
|
45
|
+
# In addition, this version allows you to pass certain options that are
|
|
46
|
+
# then passed on to the underlying WQL query. See the MSDN documentation
|
|
47
|
+
# on Win32_PingStatus for details.
|
|
48
|
+
#
|
|
49
|
+
# Examples:
|
|
50
|
+
#
|
|
51
|
+
# # Ping with no options
|
|
52
|
+
# Ping::WMI.ping('www.perl.com')
|
|
53
|
+
#
|
|
54
|
+
# # Ping with options
|
|
55
|
+
# Ping::WMI.ping('www.perl.com', :BufferSize => 64, :NoFragmentation => true)
|
|
56
|
+
#--
|
|
57
|
+
# The PingStatus struct is a wrapper for the Win32_PingStatus WMI class.
|
|
58
|
+
#
|
|
59
|
+
def ping(host = @host, options = {})
|
|
60
|
+
super(host)
|
|
61
|
+
|
|
62
|
+
lhost = Socket.gethostname
|
|
63
|
+
|
|
64
|
+
cs = "winmgmts:{impersonationLevel=impersonate}!//#{lhost}/root/cimv2"
|
|
65
|
+
wmi = WIN32OLE.connect(cs)
|
|
66
|
+
|
|
67
|
+
query = "select * from win32_pingstatus where address = '#{host}'"
|
|
68
|
+
|
|
69
|
+
unless options.empty?
|
|
70
|
+
options.each{ |key, value|
|
|
71
|
+
if value.is_a?(String)
|
|
72
|
+
query << " and #{key} = '#{value}'"
|
|
73
|
+
else
|
|
74
|
+
query << " and #{key} = #{value}"
|
|
75
|
+
end
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
status = Struct::PingStatus.new
|
|
80
|
+
|
|
81
|
+
wmi.execquery(query).each{ |obj|
|
|
82
|
+
status.address = obj.Address
|
|
83
|
+
status.buffer_size = obj.BufferSize
|
|
84
|
+
status.no_fragmentation = obj.NoFragmentation
|
|
85
|
+
status.primary_address_resolution_status = obj.PrimaryAddressResolutionStatus
|
|
86
|
+
status.protocol_address = obj.ProtocolAddress
|
|
87
|
+
status.protocol_address_resolved = obj.ProtocolAddressResolved
|
|
88
|
+
status.record_route = obj.RecordRoute
|
|
89
|
+
status.reply_inconsistency = obj.ReplyInconsistency
|
|
90
|
+
status.reply_size = obj.ReplySize
|
|
91
|
+
status.resolve_address_names = obj.ResolveAddressNames
|
|
92
|
+
status.response_time = obj.ResponseTime
|
|
93
|
+
status.response_time_to_live = obj.ResponseTimeToLive
|
|
94
|
+
status.route_record = obj.RouteRecord
|
|
95
|
+
status.route_record_resolved = obj.RouteRecordResolved
|
|
96
|
+
status.source_route = obj.SourceRoute
|
|
97
|
+
status.source_route_type = obj.SourceRouteType
|
|
98
|
+
status.status_code = obj.StatusCode
|
|
99
|
+
status.timeout = obj.Timeout
|
|
100
|
+
status.timestamp_record = obj.TimeStampRecord
|
|
101
|
+
status.timestamp_record_address = obj.TimeStampRecordAddress
|
|
102
|
+
status.timestamp_record_address_resolved = obj.TimeStampRecordAddressResolved
|
|
103
|
+
status.timestamp_route = obj.TimeStampRoute
|
|
104
|
+
status.time_to_live = obj.TimeToLive
|
|
105
|
+
status.type_of_service = obj.TypeOfService
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
status.freeze # Read-only data
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Unlike Net::Ping::WMI#ping, this method returns true or false to
|
|
112
|
+
# indicate whether or not the ping was successful.
|
|
113
|
+
#
|
|
114
|
+
def ping?(host = @host, options = {})
|
|
115
|
+
ping(host, options).status_code == 0
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
data/lib/net/ping.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# By doing a "require 'net/ping'" you are requiring every subclass. If you
|
|
2
|
+
# want to require a specific ping type only, do "require 'net/ping/tcp'",
|
|
3
|
+
# for example.
|
|
4
|
+
#
|
|
5
|
+
require 'rbconfig'
|
|
6
|
+
|
|
7
|
+
require_relative 'ping/tcp'
|
|
8
|
+
require_relative 'ping/udp'
|
|
9
|
+
require_relative 'ping/icmp'
|
|
10
|
+
require_relative 'ping/external'
|
|
11
|
+
require_relative 'ping/http'
|
|
12
|
+
|
|
13
|
+
RbConfig = Config unless Object.const_defined?(:RbConfig)
|
|
14
|
+
|
|
15
|
+
begin
|
|
16
|
+
`busybox`
|
|
17
|
+
RbConfig::CONFIG['busybox'] = true
|
|
18
|
+
rescue Errno::ENOENT
|
|
19
|
+
RbConfig::CONFIG['busybox'] = false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if File::ALT_SEPARATOR
|
|
23
|
+
require_relative 'ping/wmi'
|
|
24
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
spec = Gem::Specification.load('net-ping.gemspec').dup
|
|
2
|
+
spec.platform = Gem::Platform.new(['universal', 'mingw-ucrt'])
|
|
3
|
+
spec.add_dependency('win32-security', '>= 0.2.0')
|
|
4
|
+
|
|
5
|
+
# No longer a default gem as of Ruby 4.0. Used by the WMI ping class.
|
|
6
|
+
spec.add_dependency('win32ole', '>= 1.8.8')
|
|
7
|
+
spec
|
data/net-ping.gemspec
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require_relative "lib/net/ping/version"
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |spec|
|
|
4
|
+
spec.name = 'net-ping'
|
|
5
|
+
spec.version = Net::Ping::VERSION
|
|
6
|
+
spec.license = 'Artistic 2.0'
|
|
7
|
+
spec.author = 'Chris Chernesky'
|
|
8
|
+
spec.email = 'chris.netping@tinderglow.com'
|
|
9
|
+
spec.homepage = 'https://github.com/chernesk/net-ping'
|
|
10
|
+
spec.summary = 'A ping interface for Ruby.'
|
|
11
|
+
spec.test_file = 'test/test_net_ping.rb'
|
|
12
|
+
spec.files = Dir['**/*'].reject { |f| f.include?('git') || File.extname(f) == '.gem' }
|
|
13
|
+
spec.metadata = {
|
|
14
|
+
'bug_tracker_uri' => "#{spec.homepage}/issues",
|
|
15
|
+
'changelog_uri' => "#{spec.homepage}/blob/master/CHANGES",
|
|
16
|
+
'documentation_uri' => "https://www.rubydoc.info/gems/#{spec.name}",
|
|
17
|
+
'homepage_uri' => spec.homepage,
|
|
18
|
+
'source_code_uri' => spec.homepage
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
spec.extra_rdoc_files = ['README.md', 'CHANGES', 'doc/ping.txt']
|
|
22
|
+
|
|
23
|
+
# The TCP Ping class requires this for non-blocking sockets.
|
|
24
|
+
spec.required_ruby_version = ">= 1.9.3"
|
|
25
|
+
|
|
26
|
+
spec.add_development_dependency('test-unit', '>= 0')
|
|
27
|
+
spec.add_development_dependency('webmock', '>= 0')
|
|
28
|
+
spec.add_development_dependency('rake', '>= 0')
|
|
29
|
+
spec.add_development_dependency('pry-byebug', '>= 0')
|
|
30
|
+
|
|
31
|
+
spec.description = <<-EOF
|
|
32
|
+
The net-ping library provides a ping interface for Ruby. It includes
|
|
33
|
+
separate TCP, HTTP, LDAP, ICMP, UDP, WMI (for Windows) and external ping
|
|
34
|
+
classes.
|
|
35
|
+
EOF
|
|
36
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
######################################################################
|
|
2
|
+
# test_net_ping.rb
|
|
3
|
+
#
|
|
4
|
+
# Test suite for all the Ping subclasses. Note that the Ping::ICMP
|
|
5
|
+
# class test won't be run unless this is run as a privileged process.
|
|
6
|
+
######################################################################
|
|
7
|
+
require 'test_net_ping_external'
|
|
8
|
+
require 'test_net_ping_gem_packaging'
|
|
9
|
+
require 'test_net_ping_http'
|
|
10
|
+
require 'test_net_ping_tcp'
|
|
11
|
+
require 'test_net_ping_udp'
|
|
12
|
+
|
|
13
|
+
if File::ALT_SEPARATOR
|
|
14
|
+
require 'win32/security'
|
|
15
|
+
|
|
16
|
+
if Win32::Security.elevated_security?
|
|
17
|
+
require 'test_net_ping_icmp'
|
|
18
|
+
end
|
|
19
|
+
else
|
|
20
|
+
# If cap2 is availble, check if we are root, or our process has net_raw,
|
|
21
|
+
# then include ICMP tests
|
|
22
|
+
begin
|
|
23
|
+
require 'cap2'
|
|
24
|
+
current_process = Cap2.process
|
|
25
|
+
if Process.euid == 0 \
|
|
26
|
+
|| current_process.permitted?(:net_raw) \
|
|
27
|
+
&& current_process.enabled?(:net_raw)
|
|
28
|
+
require 'test_net_ping_icmp'
|
|
29
|
+
end
|
|
30
|
+
rescue LoadError
|
|
31
|
+
# If we don't have cap2, include ICMP tests if we are root
|
|
32
|
+
require 'test_net_ping_icmp' if Process.euid == 0
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
if File::ALT_SEPARATOR
|
|
37
|
+
require 'test_net_ping_wmi'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class TC_Net_Ping < Test::Unit::TestCase
|
|
41
|
+
def test_net_ping_version
|
|
42
|
+
assert_equal(Gem::Specification.load('net-ping.gemspec').version.to_s, Net::Ping::VERSION)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#########################################################################
|
|
2
|
+
# test_net_ping_external.rb
|
|
3
|
+
#
|
|
4
|
+
# Test case for the Net::PingExternal class. Run this via the 'test' or
|
|
5
|
+
# 'test:external' rake task.
|
|
6
|
+
#
|
|
7
|
+
# WARNING: I've noticed that test failures will occur if you're using
|
|
8
|
+
# OpenDNS. This is apparently caused by them messing with upstream
|
|
9
|
+
# replies for advertising purposes.
|
|
10
|
+
#########################################################################
|
|
11
|
+
require 'test-unit'
|
|
12
|
+
require 'net/ping/external'
|
|
13
|
+
require 'mkmf'
|
|
14
|
+
require 'stringio'
|
|
15
|
+
|
|
16
|
+
class TC_Net_Ping_External < Test::Unit::TestCase
|
|
17
|
+
def setup
|
|
18
|
+
@host = 'localhost'
|
|
19
|
+
@bogus = 'foo.bar.baz'
|
|
20
|
+
@pe = Net::Ping::External.new(@host)
|
|
21
|
+
@bad = Net::Ping::External.new(@bogus)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
test "ping basic functionality" do
|
|
25
|
+
assert_respond_to(@pe, :ping)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
test "ping with no arguments" do
|
|
29
|
+
assert_nothing_raised{ @pe.ping }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
test "ping accepts a hostname" do
|
|
33
|
+
assert_nothing_raised{ @pe.ping(@host) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
test "ping returns a boolean" do
|
|
37
|
+
assert_boolean(@pe.ping)
|
|
38
|
+
assert_boolean(@bad.ping)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
test "ping? alias" do
|
|
42
|
+
assert_respond_to(@pe, :ping?)
|
|
43
|
+
assert_alias_method(@pe, :ping?, :ping)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
test "pingecho alias" do
|
|
47
|
+
assert_nothing_raised{ @pe.pingecho }
|
|
48
|
+
assert_alias_method(@pe, :pingecho, :ping)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
test "pinging a good host returns true" do
|
|
52
|
+
omit_if(find_executable0('ping').nil?)
|
|
53
|
+
assert_true(@pe.ping?)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
test "pinging a bogus host returns false" do
|
|
57
|
+
assert_false(@bad.ping?)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
test "duration basic functionality" do
|
|
61
|
+
omit_if(find_executable0('ping').nil?)
|
|
62
|
+
assert_nothing_raised{ @pe.ping }
|
|
63
|
+
assert_respond_to(@pe, :duration)
|
|
64
|
+
assert_kind_of(Float, @pe.duration)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
test "duration is unset if a bad ping follows a good ping" do
|
|
68
|
+
omit_if(find_executable0('ping').nil?)
|
|
69
|
+
assert_nothing_raised{ @pe.ping }
|
|
70
|
+
assert_not_nil(@pe.duration)
|
|
71
|
+
assert_false(@pe.ping?(@bogus))
|
|
72
|
+
assert_nil(@pe.duration)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
test "host getter basic functionality" do
|
|
76
|
+
assert_respond_to(@pe, :host)
|
|
77
|
+
assert_equal('localhost', @pe.host)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
test "host setter basic functionality" do
|
|
81
|
+
assert_respond_to(@pe, :host=)
|
|
82
|
+
assert_nothing_raised{ @pe.host = @bad }
|
|
83
|
+
assert_equal(@bad, @pe.host)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
test "port getter basic functionality" do
|
|
87
|
+
assert_respond_to(@pe, :port)
|
|
88
|
+
assert_equal(7, @pe.port)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
test "port setter basic functionality" do
|
|
92
|
+
assert_respond_to(@pe, :port=)
|
|
93
|
+
assert_nothing_raised{ @pe.port = 90 }
|
|
94
|
+
assert_equal(90, @pe.port)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
test "timeout getter basic functionality" do
|
|
98
|
+
assert_respond_to(@pe, :timeout)
|
|
99
|
+
assert_equal(5, @pe.timeout)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
test "timeout setter basic functionality" do
|
|
103
|
+
assert_respond_to(@pe, :timeout=)
|
|
104
|
+
assert_nothing_raised{ @pe.timeout = 30 }
|
|
105
|
+
assert_equal(30, @pe.timeout)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
test "exception method basic functionality" do
|
|
109
|
+
assert_respond_to(@pe, :exception)
|
|
110
|
+
assert_nil(@pe.exception)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
test "pinging a bogus host stores exception data" do
|
|
114
|
+
assert_nothing_raised{ @bad.ping? }
|
|
115
|
+
assert_not_nil(@bad.exception)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
test "pinging a good host results in no exception data" do
|
|
119
|
+
omit_if(find_executable0('ping').nil?)
|
|
120
|
+
assert_nothing_raised{ @pe.ping }
|
|
121
|
+
assert_nil(@pe.exception)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
test "warning basic functionality" do
|
|
125
|
+
assert_respond_to(@pe, :warning)
|
|
126
|
+
assert_nil(@pe.warning)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
test "timing out causes expected result" do
|
|
130
|
+
ext = Net::Ping::External.new('foo.bar.baz', nil, 1)
|
|
131
|
+
start = Time.now
|
|
132
|
+
assert_false(ext.ping?)
|
|
133
|
+
elapsed = Time.now - start
|
|
134
|
+
assert_true(elapsed < 2.5, "Actual elapsed: #{elapsed}")
|
|
135
|
+
assert_not_nil(ext.exception)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
test "pinging an unreachable host on the same subnet returns false" do
|
|
139
|
+
@bad = Net::Ping::External.new('192.168.0.99')
|
|
140
|
+
assert_false(@bad.ping?)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
test "ping6 uses timeout for FreeBSD" do
|
|
144
|
+
command = nil
|
|
145
|
+
original_os = RbConfig::CONFIG['host_os']
|
|
146
|
+
original_popen3 = Open3.method(:popen3)
|
|
147
|
+
|
|
148
|
+
RbConfig::CONFIG['host_os'] = 'freebsd'
|
|
149
|
+
Open3.define_singleton_method(:popen3) do |*args, &block|
|
|
150
|
+
command = args
|
|
151
|
+
stdin = StringIO.new
|
|
152
|
+
stdout = StringIO.new
|
|
153
|
+
stderr = StringIO.new
|
|
154
|
+
status = Struct.new(:exitstatus).new(0)
|
|
155
|
+
thread = Struct.new(:value).new(status)
|
|
156
|
+
block.call(stdin, stdout, stderr, thread)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
@pe.ping6('example.test', 2, 3, 4)
|
|
160
|
+
|
|
161
|
+
assert_equal(['ping6', '-c', '2', '-x', '4', 'example.test'], command)
|
|
162
|
+
ensure
|
|
163
|
+
RbConfig::CONFIG['host_os'] = original_os
|
|
164
|
+
Open3.define_singleton_method(:popen3, original_popen3)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
test "ping6 uses interval for macOS" do
|
|
168
|
+
command = nil
|
|
169
|
+
original_os = RbConfig::CONFIG['host_os']
|
|
170
|
+
original_popen3 = Open3.method(:popen3)
|
|
171
|
+
|
|
172
|
+
RbConfig::CONFIG['host_os'] = 'darwin'
|
|
173
|
+
Open3.define_singleton_method(:popen3) do |*args, &block|
|
|
174
|
+
command = args
|
|
175
|
+
stdin = StringIO.new
|
|
176
|
+
stdout = StringIO.new
|
|
177
|
+
stderr = StringIO.new
|
|
178
|
+
status = Struct.new(:exitstatus).new(0)
|
|
179
|
+
thread = Struct.new(:value).new(status)
|
|
180
|
+
block.call(stdin, stdout, stderr, thread)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
@pe.ping6('example.test', 2, 3, 4)
|
|
184
|
+
|
|
185
|
+
assert_equal(['ping6', '-c', '2', '-i', '3', 'example.test'], command)
|
|
186
|
+
ensure
|
|
187
|
+
RbConfig::CONFIG['host_os'] = original_os
|
|
188
|
+
Open3.define_singleton_method(:popen3, original_popen3)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
test "ping6 succeeds without an exception when stderr is empty" do
|
|
192
|
+
stdin = StringIO.new
|
|
193
|
+
stdout = StringIO.new('64 bytes from ::1')
|
|
194
|
+
stderr = StringIO.new
|
|
195
|
+
status = Struct.new(:exitstatus).new(0)
|
|
196
|
+
thread = Struct.new(:value).new(status)
|
|
197
|
+
popen3 = Open3.method(:popen3)
|
|
198
|
+
nil_match = NilClass.instance_method(:=~) if NilClass.instance_methods(false).include?(:=~)
|
|
199
|
+
false_match = FalseClass.instance_method(:=~) if FalseClass.instance_methods(false).include?(:=~)
|
|
200
|
+
|
|
201
|
+
Open3.singleton_class.send(:define_method, :popen3) do |*args, &block|
|
|
202
|
+
block.call(stdin, stdout, stderr, thread)
|
|
203
|
+
end
|
|
204
|
+
[NilClass, FalseClass].each do |klass|
|
|
205
|
+
klass.send(:define_method, :=~) do |*args|
|
|
206
|
+
raise NoMethodError, "undefined method '=~' for #{self.inspect}"
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
assert_true(@pe.ping6('::1'))
|
|
211
|
+
assert_nil(@pe.exception)
|
|
212
|
+
ensure
|
|
213
|
+
Open3.singleton_class.send(:define_method, :popen3, popen3) if popen3
|
|
214
|
+
if nil_match
|
|
215
|
+
NilClass.send(:define_method, :=~, nil_match)
|
|
216
|
+
else
|
|
217
|
+
NilClass.send(:remove_method, :=~)
|
|
218
|
+
end
|
|
219
|
+
if false_match
|
|
220
|
+
FalseClass.send(:define_method, :=~, false_match)
|
|
221
|
+
else
|
|
222
|
+
FalseClass.send(:remove_method, :=~)
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def teardown
|
|
227
|
+
@host = nil
|
|
228
|
+
@bogus = nil
|
|
229
|
+
@pe = nil
|
|
230
|
+
@bad = nil
|
|
231
|
+
end
|
|
232
|
+
end
|