summaryrefslogtreecommitdiff
path: root/spec/ruby/library/socket
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/library/socket')
-rw-r--r--spec/ruby/library/socket/addrinfo/afamily_spec.rb35
-rw-r--r--spec/ruby/library/socket/addrinfo/bind_spec.rb28
-rw-r--r--spec/ruby/library/socket/addrinfo/canonname_spec.rb27
-rw-r--r--spec/ruby/library/socket/addrinfo/connect_from_spec.rb75
-rw-r--r--spec/ruby/library/socket/addrinfo/connect_spec.rb35
-rw-r--r--spec/ruby/library/socket/addrinfo/connect_to_spec.rb75
-rw-r--r--spec/ruby/library/socket/addrinfo/family_addrinfo_spec.rb113
-rw-r--r--spec/ruby/library/socket/addrinfo/foreach_spec.rb9
-rw-r--r--spec/ruby/library/socket/addrinfo/getaddrinfo_spec.rb87
-rw-r--r--spec/ruby/library/socket/addrinfo/getnameinfo_spec.rb40
-rw-r--r--spec/ruby/library/socket/addrinfo/initialize_spec.rb589
-rw-r--r--spec/ruby/library/socket/addrinfo/inspect_sockaddr_spec.rb48
-rw-r--r--spec/ruby/library/socket/addrinfo/inspect_spec.rb63
-rw-r--r--spec/ruby/library/socket/addrinfo/ip_address_spec.rb64
-rw-r--r--spec/ruby/library/socket/addrinfo/ip_port_spec.rb33
-rw-r--r--spec/ruby/library/socket/addrinfo/ip_spec.rb62
-rw-r--r--spec/ruby/library/socket/addrinfo/ip_unpack_spec.rb33
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv4_loopback_spec.rb41
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv4_multicast_spec.rb27
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv4_private_spec.rb45
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv4_spec.rb33
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_linklocal_spec.rb23
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_loopback_spec.rb43
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_mc_global_spec.rb20
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_mc_linklocal_spec.rb19
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_mc_nodelocal_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_mc_orglocal_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_mc_sitelocal_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_multicast_spec.rb46
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_sitelocal_spec.rb23
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_spec.rb33
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_to_ipv4_spec.rb71
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_unique_local_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_unspecified_spec.rb15
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_v4compat_spec.rb20
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_v4mapped_spec.rb20
-rw-r--r--spec/ruby/library/socket/addrinfo/listen_spec.rb34
-rw-r--r--spec/ruby/library/socket/addrinfo/marshal_dump_spec.rb80
-rw-r--r--spec/ruby/library/socket/addrinfo/marshal_load_spec.rb33
-rw-r--r--spec/ruby/library/socket/addrinfo/pfamily_spec.rb41
-rw-r--r--spec/ruby/library/socket/addrinfo/protocol_spec.rb22
-rw-r--r--spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb47
-rw-r--r--spec/ruby/library/socket/addrinfo/socktype_spec.rb21
-rw-r--r--spec/ruby/library/socket/addrinfo/tcp_spec.rb34
-rw-r--r--spec/ruby/library/socket/addrinfo/to_s_spec.rb6
-rw-r--r--spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb6
-rw-r--r--spec/ruby/library/socket/addrinfo/udp_spec.rb34
-rw-r--r--spec/ruby/library/socket/addrinfo/unix_path_spec.rb35
-rw-r--r--spec/ruby/library/socket/addrinfo/unix_spec.rb69
-rw-r--r--spec/ruby/library/socket/ancillarydata/cmsg_is_spec.rb33
-rw-r--r--spec/ruby/library/socket/ancillarydata/data_spec.rb9
-rw-r--r--spec/ruby/library/socket/ancillarydata/family_spec.rb9
-rw-r--r--spec/ruby/library/socket/ancillarydata/initialize_spec.rb284
-rw-r--r--spec/ruby/library/socket/ancillarydata/int_spec.rb43
-rw-r--r--spec/ruby/library/socket/ancillarydata/ip_pktinfo_spec.rb145
-rw-r--r--spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_addr_spec.rb11
-rw-r--r--spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_ifindex_spec.rb11
-rw-r--r--spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_spec.rb89
-rw-r--r--spec/ruby/library/socket/ancillarydata/level_spec.rb9
-rw-r--r--spec/ruby/library/socket/ancillarydata/type_spec.rb9
-rw-r--r--spec/ruby/library/socket/ancillarydata/unix_rights_spec.rb61
-rw-r--r--spec/ruby/library/socket/basicsocket/close_read_spec.rb43
-rw-r--r--spec/ruby/library/socket/basicsocket/close_write_spec.rb48
-rw-r--r--spec/ruby/library/socket/basicsocket/connect_address_spec.rb152
-rw-r--r--spec/ruby/library/socket/basicsocket/do_not_reverse_lookup_spec.rb103
-rw-r--r--spec/ruby/library/socket/basicsocket/for_fd_spec.rb38
-rw-r--r--spec/ruby/library/socket/basicsocket/getpeereid_spec.rb36
-rw-r--r--spec/ruby/library/socket/basicsocket/getpeername_spec.rb25
-rw-r--r--spec/ruby/library/socket/basicsocket/getsockname_spec.rb28
-rw-r--r--spec/ruby/library/socket/basicsocket/getsockopt_spec.rb188
-rw-r--r--spec/ruby/library/socket/basicsocket/ioctl_spec.rb42
-rw-r--r--spec/ruby/library/socket/basicsocket/local_address_spec.rb10
-rw-r--r--spec/ruby/library/socket/basicsocket/read_nonblock_spec.rb74
-rw-r--r--spec/ruby/library/socket/basicsocket/read_spec.rb47
-rw-r--r--spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb172
-rw-r--r--spec/ruby/library/socket/basicsocket/recv_spec.rb250
-rw-r--r--spec/ruby/library/socket/basicsocket/recvmsg_nonblock_spec.rb300
-rw-r--r--spec/ruby/library/socket/basicsocket/recvmsg_spec.rb281
-rw-r--r--spec/ruby/library/socket/basicsocket/remote_address_spec.rb10
-rw-r--r--spec/ruby/library/socket/basicsocket/send_spec.rb220
-rw-r--r--spec/ruby/library/socket/basicsocket/sendmsg_nonblock_spec.rb118
-rw-r--r--spec/ruby/library/socket/basicsocket/sendmsg_spec.rb111
-rw-r--r--spec/ruby/library/socket/basicsocket/setsockopt_spec.rb334
-rw-r--r--spec/ruby/library/socket/basicsocket/shutdown_spec.rb155
-rw-r--r--spec/ruby/library/socket/basicsocket/write_nonblock_spec.rb43
-rw-r--r--spec/ruby/library/socket/constants/constants_spec.rb108
-rw-r--r--spec/ruby/library/socket/fixtures/classes.rb166
-rw-r--r--spec/ruby/library/socket/fixtures/send_io.txt1
-rw-r--r--spec/ruby/library/socket/ipsocket/addr_spec.rb105
-rw-r--r--spec/ruby/library/socket/ipsocket/getaddress_spec.rb28
-rw-r--r--spec/ruby/library/socket/ipsocket/peeraddr_spec.rb117
-rw-r--r--spec/ruby/library/socket/ipsocket/recvfrom_spec.rb205
-rw-r--r--spec/ruby/library/socket/option/bool_spec.rb27
-rw-r--r--spec/ruby/library/socket/option/initialize_spec.rb83
-rw-r--r--spec/ruby/library/socket/option/inspect_spec.rb19
-rw-r--r--spec/ruby/library/socket/option/int_spec.rb43
-rw-r--r--spec/ruby/library/socket/option/linger_spec.rb76
-rw-r--r--spec/ruby/library/socket/option/new_spec.rb35
-rw-r--r--spec/ruby/library/socket/shared/address.rb259
-rw-r--r--spec/ruby/library/socket/shared/pack_sockaddr.rb92
-rw-r--r--spec/ruby/library/socket/shared/partially_closable_sockets.rb13
-rw-r--r--spec/ruby/library/socket/shared/socketpair.rb138
-rw-r--r--spec/ruby/library/socket/socket/accept_loop_spec.rb84
-rw-r--r--spec/ruby/library/socket/socket/accept_nonblock_spec.rb141
-rw-r--r--spec/ruby/library/socket/socket/accept_spec.rb121
-rw-r--r--spec/ruby/library/socket/socket/bind_spec.rb150
-rw-r--r--spec/ruby/library/socket/socket/connect_nonblock_spec.rb147
-rw-r--r--spec/ruby/library/socket/socket/connect_spec.rb78
-rw-r--r--spec/ruby/library/socket/socket/for_fd_spec.rb30
-rw-r--r--spec/ruby/library/socket/socket/getaddrinfo_spec.rb391
-rw-r--r--spec/ruby/library/socket/socket/gethostbyaddr_spec.rb121
-rw-r--r--spec/ruby/library/socket/socket/gethostbyname_spec.rb135
-rw-r--r--spec/ruby/library/socket/socket/gethostname_spec.rb18
-rw-r--r--spec/ruby/library/socket/socket/getifaddrs_spec.rb117
-rw-r--r--spec/ruby/library/socket/socket/getnameinfo_spec.rb165
-rw-r--r--spec/ruby/library/socket/socket/getservbyname_spec.rb32
-rw-r--r--spec/ruby/library/socket/socket/getservbyport_spec.rb23
-rw-r--r--spec/ruby/library/socket/socket/initialize_spec.rb87
-rw-r--r--spec/ruby/library/socket/socket/ip_address_list_spec.rb50
-rw-r--r--spec/ruby/library/socket/socket/ipv6only_bang_spec.rb20
-rw-r--r--spec/ruby/library/socket/socket/listen_spec.rb66
-rw-r--r--spec/ruby/library/socket/socket/local_address_spec.rb43
-rw-r--r--spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb7
-rw-r--r--spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb7
-rw-r--r--spec/ruby/library/socket/socket/pair_spec.rb7
-rw-r--r--spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb219
-rw-r--r--spec/ruby/library/socket/socket/recvfrom_spec.rb179
-rw-r--r--spec/ruby/library/socket/socket/remote_address_spec.rb54
-rw-r--r--spec/ruby/library/socket/socket/sockaddr_in_spec.rb7
-rw-r--r--spec/ruby/library/socket/socket/sockaddr_un_spec.rb7
-rw-r--r--spec/ruby/library/socket/socket/socket_spec.rb38
-rw-r--r--spec/ruby/library/socket/socket/socketpair_spec.rb7
-rw-r--r--spec/ruby/library/socket/socket/sysaccept_spec.rb91
-rw-r--r--spec/ruby/library/socket/socket/tcp_server_loop_spec.rb54
-rw-r--r--spec/ruby/library/socket/socket/tcp_server_sockets_spec.rb39
-rw-r--r--spec/ruby/library/socket/socket/tcp_spec.rb70
-rw-r--r--spec/ruby/library/socket/socket/udp_server_loop_on_spec.rb47
-rw-r--r--spec/ruby/library/socket/socket/udp_server_loop_spec.rb59
-rw-r--r--spec/ruby/library/socket/socket/udp_server_recv_spec.rb35
-rw-r--r--spec/ruby/library/socket/socket/udp_server_sockets_spec.rb39
-rw-r--r--spec/ruby/library/socket/socket/unix_server_loop_spec.rb56
-rw-r--r--spec/ruby/library/socket/socket/unix_server_socket_spec.rb46
-rw-r--r--spec/ruby/library/socket/socket/unix_spec.rb43
-rw-r--r--spec/ruby/library/socket/socket/unpack_sockaddr_in_spec.rb44
-rw-r--r--spec/ruby/library/socket/socket/unpack_sockaddr_un_spec.rb24
-rw-r--r--spec/ruby/library/socket/spec_helper.rb12
-rw-r--r--spec/ruby/library/socket/tcpserver/accept_nonblock_spec.rb85
-rw-r--r--spec/ruby/library/socket/tcpserver/accept_spec.rb132
-rw-r--r--spec/ruby/library/socket/tcpserver/gets_spec.rb16
-rw-r--r--spec/ruby/library/socket/tcpserver/initialize_spec.rb101
-rw-r--r--spec/ruby/library/socket/tcpserver/listen_spec.rb22
-rw-r--r--spec/ruby/library/socket/tcpserver/new_spec.rb137
-rw-r--r--spec/ruby/library/socket/tcpserver/sysaccept_spec.rb66
-rw-r--r--spec/ruby/library/socket/tcpsocket/gethostbyname_spec.rb119
-rw-r--r--spec/ruby/library/socket/tcpsocket/initialize_spec.rb100
-rw-r--r--spec/ruby/library/socket/tcpsocket/local_address_spec.rb73
-rw-r--r--spec/ruby/library/socket/tcpsocket/open_spec.rb6
-rw-r--r--spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb21
-rw-r--r--spec/ruby/library/socket/tcpsocket/recv_nonblock_spec.rb48
-rw-r--r--spec/ruby/library/socket/tcpsocket/recv_spec.rb28
-rw-r--r--spec/ruby/library/socket/tcpsocket/remote_address_spec.rb72
-rw-r--r--spec/ruby/library/socket/tcpsocket/setsockopt_spec.rb45
-rw-r--r--spec/ruby/library/socket/tcpsocket/shared/new.rb102
-rw-r--r--spec/ruby/library/socket/udpsocket/bind_spec.rb83
-rw-r--r--spec/ruby/library/socket/udpsocket/connect_spec.rb35
-rw-r--r--spec/ruby/library/socket/udpsocket/initialize_spec.rb53
-rw-r--r--spec/ruby/library/socket/udpsocket/inspect_spec.rb17
-rw-r--r--spec/ruby/library/socket/udpsocket/local_address_spec.rb80
-rw-r--r--spec/ruby/library/socket/udpsocket/new_spec.rb40
-rw-r--r--spec/ruby/library/socket/udpsocket/open_spec.rb13
-rw-r--r--spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb111
-rw-r--r--spec/ruby/library/socket/udpsocket/remote_address_spec.rb79
-rw-r--r--spec/ruby/library/socket/udpsocket/send_spec.rb154
-rw-r--r--spec/ruby/library/socket/udpsocket/write_spec.rb21
-rw-r--r--spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb87
-rw-r--r--spec/ruby/library/socket/unixserver/accept_spec.rb126
-rw-r--r--spec/ruby/library/socket/unixserver/for_fd_spec.rb21
-rw-r--r--spec/ruby/library/socket/unixserver/initialize_spec.rb26
-rw-r--r--spec/ruby/library/socket/unixserver/listen_spec.rb19
-rw-r--r--spec/ruby/library/socket/unixserver/new_spec.rb12
-rw-r--r--spec/ruby/library/socket/unixserver/open_spec.rb24
-rw-r--r--spec/ruby/library/socket/unixserver/shared/new.rb20
-rw-r--r--spec/ruby/library/socket/unixserver/sysaccept_spec.rb50
-rw-r--r--spec/ruby/library/socket/unixsocket/addr_spec.rb33
-rw-r--r--spec/ruby/library/socket/unixsocket/initialize_spec.rb56
-rw-r--r--spec/ruby/library/socket/unixsocket/inspect_spec.rb15
-rw-r--r--spec/ruby/library/socket/unixsocket/local_address_spec.rb92
-rw-r--r--spec/ruby/library/socket/unixsocket/new_spec.rb12
-rw-r--r--spec/ruby/library/socket/unixsocket/open_spec.rb26
-rw-r--r--spec/ruby/library/socket/unixsocket/pair_spec.rb18
-rw-r--r--spec/ruby/library/socket/unixsocket/partially_closable_spec.rb21
-rw-r--r--spec/ruby/library/socket/unixsocket/path_spec.rb24
-rw-r--r--spec/ruby/library/socket/unixsocket/peeraddr_spec.rb26
-rw-r--r--spec/ruby/library/socket/unixsocket/recv_io_spec.rb84
-rw-r--r--spec/ruby/library/socket/unixsocket/recvfrom_spec.rb174
-rw-r--r--spec/ruby/library/socket/unixsocket/remote_address_spec.rb43
-rw-r--r--spec/ruby/library/socket/unixsocket/send_io_spec.rb55
-rw-r--r--spec/ruby/library/socket/unixsocket/shared/new.rb22
-rw-r--r--spec/ruby/library/socket/unixsocket/shared/pair.rb47
-rw-r--r--spec/ruby/library/socket/unixsocket/socketpair_spec.rb18
200 files changed, 13705 insertions, 0 deletions
diff --git a/spec/ruby/library/socket/addrinfo/afamily_spec.rb b/spec/ruby/library/socket/addrinfo/afamily_spec.rb
new file mode 100644
index 0000000000..5d075be057
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/afamily_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#afamily" do
+ describe "for an ipv4 socket" do
+
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "returns Socket::AF_INET" do
+ @addrinfo.afamily.should == Socket::AF_INET
+ end
+
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns Socket::AF_INET6" do
+ @addrinfo.afamily.should == Socket::AF_INET6
+ end
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns Socket::AF_UNIX" do
+ @addrinfo.afamily.should == Socket::AF_UNIX
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/bind_spec.rb b/spec/ruby/library/socket/addrinfo/bind_spec.rb
new file mode 100644
index 0000000000..6f78890a4d
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/bind_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Addrinfo#bind" do
+
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 0)
+ end
+
+ after :each do
+ @socket.close unless @socket.closed?
+ end
+
+ it "returns a bound socket when no block is given" do
+ @socket = @addrinfo.bind
+ @socket.should be_kind_of(Socket)
+ @socket.closed?.should be_false
+ end
+
+ it "yields the socket if a block is given" do
+ @addrinfo.bind do |sock|
+ @socket = sock
+ sock.should be_kind_of(Socket)
+ end
+ @socket.closed?.should be_true
+ end
+
+end
diff --git a/spec/ruby/library/socket/addrinfo/canonname_spec.rb b/spec/ruby/library/socket/addrinfo/canonname_spec.rb
new file mode 100644
index 0000000000..a1cc8b3980
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/canonname_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Addrinfo#canonname" do
+
+ before :each do
+ @addrinfos = Addrinfo.getaddrinfo("localhost", 80, :INET, :STREAM, nil, Socket::AI_CANONNAME)
+ end
+
+ it "returns the canonical name for a host" do
+ canonname = @addrinfos.map { |a| a.canonname }.find { |name| name and name.include?("localhost") }
+ if canonname
+ canonname.should include("localhost")
+ else
+ canonname.should == nil
+ end
+ end
+
+ describe 'when the canonical name is not available' do
+ it 'returns nil' do
+ addr = Addrinfo.new(Socket.sockaddr_in(0, '127.0.0.1'))
+
+ addr.canonname.should be_nil
+ end
+ end
+
+end
diff --git a/spec/ruby/library/socket/addrinfo/connect_from_spec.rb b/spec/ruby/library/socket/addrinfo/connect_from_spec.rb
new file mode 100644
index 0000000000..55fce2e159
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/connect_from_spec.rb
@@ -0,0 +1,75 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo#connect_from' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ @addr = Addrinfo.tcp(ip_address, @port)
+ end
+
+ after do
+ @socket.close if @socket
+ @server.close
+ end
+
+ describe 'using separate arguments' do
+ it 'returns a Socket when no block is given' do
+ @socket = @addr.connect_from(ip_address, 0)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket when a block is given' do
+ @addr.connect_from(ip_address, 0) do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'treats the last argument as a set of options if it is a Hash' do
+ @socket = @addr.connect_from(ip_address, 0, timeout: 2)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'binds the socket to the local address' do
+ @socket = @addr.connect_from(ip_address, 0)
+
+ @socket.local_address.ip_address.should == ip_address
+
+ @socket.local_address.ip_port.should > 0
+ @socket.local_address.ip_port.should_not == @port
+ end
+ end
+
+ describe 'using an Addrinfo as the 1st argument' do
+ before do
+ @from_addr = Addrinfo.tcp(ip_address, 0)
+ end
+
+ it 'returns a Socket when no block is given' do
+ @socket = @addr.connect_from(@from_addr)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket when a block is given' do
+ @addr.connect_from(@from_addr) do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'treats the last argument as a set of options if it is a Hash' do
+ @socket = @addr.connect_from(@from_addr, timeout: 2)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'binds the socket to the local address' do
+ @socket = @addr.connect_from(@from_addr)
+
+ @socket.local_address.ip_address.should == ip_address
+
+ @socket.local_address.ip_port.should > 0
+ @socket.local_address.ip_port.should_not == @port
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/connect_spec.rb b/spec/ruby/library/socket/addrinfo/connect_spec.rb
new file mode 100644
index 0000000000..1c2dc609ca
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/connect_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo#connect' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @socket.close if @socket
+ @server.close
+ end
+
+ it 'returns a Socket when no block is given' do
+ addr = Addrinfo.tcp(ip_address, @port)
+ @socket = addr.connect
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields a Socket when a block is given' do
+ addr = Addrinfo.tcp(ip_address, @port)
+ addr.connect do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'accepts a Hash of options' do
+ addr = Addrinfo.tcp(ip_address, @port)
+ @socket = addr.connect(timeout: 2)
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/connect_to_spec.rb b/spec/ruby/library/socket/addrinfo/connect_to_spec.rb
new file mode 100644
index 0000000000..69666da19b
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/connect_to_spec.rb
@@ -0,0 +1,75 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo#connect_to' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ @addr = Addrinfo.tcp(ip_address, 0)
+ end
+
+ after do
+ @socket.close if @socket
+ @server.close
+ end
+
+ describe 'using separate arguments' do
+ it 'returns a Socket when no block is given' do
+ @socket = @addr.connect_to(ip_address, @port)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket when a block is given' do
+ @addr.connect_to(ip_address, @port) do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'treats the last argument as a set of options if it is a Hash' do
+ @socket = @addr.connect_to(ip_address, @port, timeout: 2)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'binds the Addrinfo to the local address' do
+ @socket = @addr.connect_to(ip_address, @port)
+
+ @socket.local_address.ip_address.should == ip_address
+
+ @socket.local_address.ip_port.should > 0
+ @socket.local_address.ip_port.should_not == @port
+ end
+ end
+
+ describe 'using an Addrinfo as the 1st argument' do
+ before do
+ @to_addr = Addrinfo.tcp(ip_address, @port)
+ end
+
+ it 'returns a Socket when no block is given' do
+ @socket = @addr.connect_to(@to_addr)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket when a block is given' do
+ @addr.connect_to(@to_addr) do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'treats the last argument as a set of options if it is a Hash' do
+ @socket = @addr.connect_to(@to_addr, timeout: 2)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'binds the socket to the local address' do
+ @socket = @addr.connect_to(@to_addr)
+
+ @socket.local_address.ip_address.should == ip_address
+
+ @socket.local_address.ip_port.should > 0
+ @socket.local_address.ip_port.should_not == @port
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/family_addrinfo_spec.rb b/spec/ruby/library/socket/addrinfo/family_addrinfo_spec.rb
new file mode 100644
index 0000000000..3c2f9f73d8
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/family_addrinfo_spec.rb
@@ -0,0 +1,113 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#family_addrinfo' do
+ it 'raises ArgumentError if no arguments are given' do
+ addr = Addrinfo.tcp('127.0.0.1', 0)
+
+ -> { addr.family_addrinfo }.should raise_error(ArgumentError)
+ end
+
+ describe 'using multiple arguments' do
+ describe 'with an IP Addrinfo' do
+ before do
+ @source = Addrinfo.tcp('127.0.0.1', 0)
+ end
+
+ it 'raises ArgumentError if only 1 argument is given' do
+ -> { @source.family_addrinfo('127.0.0.1') }.should raise_error(ArgumentError)
+ end
+
+ it 'raises ArgumentError if more than 2 arguments are given' do
+ -> { @source.family_addrinfo('127.0.0.1', 0, 666) }.should raise_error(ArgumentError)
+ end
+
+ it 'returns an Addrinfo when a host and port are given' do
+ addr = @source.family_addrinfo('127.0.0.1', 0)
+
+ addr.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @source.family_addrinfo('127.0.0.1', 0)
+ end
+
+ it 'uses the same address family as the source Addrinfo' do
+ @addr.afamily.should == @source.afamily
+ end
+
+ it 'uses the same protocol family as the source Addrinfo' do
+ @addr.pfamily.should == @source.pfamily
+ end
+
+ it 'uses the same socket type as the source Addrinfo' do
+ @addr.socktype.should == @source.socktype
+ end
+
+ it 'uses the same protocol as the source Addrinfo' do
+ @addr.protocol.should == @source.protocol
+ end
+ end
+ end
+
+ describe 'with a UNIX Addrinfo' do
+ before do
+ @source = Addrinfo.unix('cats')
+ end
+
+ it 'raises ArgumentError if more than 1 argument is given' do
+ -> { @source.family_addrinfo('foo', 'bar') }.should raise_error(ArgumentError)
+ end
+
+ it 'returns an Addrinfo when a UNIX socket path is given' do
+ addr = @source.family_addrinfo('dogs')
+
+ addr.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @source.family_addrinfo('dogs')
+ end
+
+ it 'uses AF_UNIX as the address family' do
+ @addr.afamily.should == Socket::AF_UNIX
+ end
+
+ it 'uses PF_UNIX as the protocol family' do
+ @addr.pfamily.should == Socket::PF_UNIX
+ end
+
+ it 'uses the given socket path' do
+ @addr.unix_path.should == 'dogs'
+ end
+ end
+ end
+ end
+
+ describe 'using an Addrinfo as the 1st argument' do
+ before do
+ @source = Addrinfo.tcp('127.0.0.1', 0)
+ end
+
+ it 'returns the input Addrinfo' do
+ input = Addrinfo.tcp('127.0.0.2', 0)
+ @source.family_addrinfo(input).should == input
+ end
+
+ it 'raises ArgumentError if more than 1 argument is given' do
+ input = Addrinfo.tcp('127.0.0.2', 0)
+ -> { @source.family_addrinfo(input, 666) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the protocol families don't match" do
+ input = Addrinfo.tcp('::1', 0)
+ -> { @source.family_addrinfo(input) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the socket types don't match" do
+ input = Addrinfo.udp('127.0.0.1', 0)
+ -> { @source.family_addrinfo(input) }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/foreach_spec.rb b/spec/ruby/library/socket/addrinfo/foreach_spec.rb
new file mode 100644
index 0000000000..6ec8fab905
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/foreach_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo.foreach' do
+ it 'yields Addrinfo instances to the supplied block' do
+ Addrinfo.foreach('127.0.0.1', 80) do |addr|
+ addr.should be_an_instance_of(Addrinfo)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/getaddrinfo_spec.rb b/spec/ruby/library/socket/addrinfo/getaddrinfo_spec.rb
new file mode 100644
index 0000000000..e05fe9967a
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/getaddrinfo_spec.rb
@@ -0,0 +1,87 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo.getaddrinfo' do
+ it 'returns an Array of Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('127.0.0.1', 80)
+
+ array.should be_an_instance_of(Array)
+ array[0].should be_an_instance_of(Addrinfo)
+ end
+
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ it 'sets the IP address of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo(ip_address, 80)
+
+ array[0].ip_address.should == ip_address
+ end
+
+ it 'sets the port of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo(ip_address, 80)
+
+ array[0].ip_port.should == 80
+ end
+
+ it 'sets the address family of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo(ip_address, 80)
+
+ array[0].afamily.should == family
+ end
+
+ it 'sets the protocol family of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo(ip_address, 80)
+
+ array[0].pfamily.should == family
+ end
+ end
+
+ guard -> { SocketSpecs.ipv6_available? } do
+ it 'sets a custom protocol family of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('::1', 80, Socket::PF_INET6)
+
+ array[0].pfamily.should == Socket::PF_INET6
+ end
+
+ it 'sets a corresponding address family based on a custom protocol family' do
+ array = Addrinfo.getaddrinfo('::1', 80, Socket::PF_INET6)
+
+ array[0].afamily.should == Socket::AF_INET6
+ end
+ end
+
+ platform_is_not :windows do
+ it 'sets the default socket type of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('127.0.0.1', 80)
+ possible = [Socket::SOCK_STREAM, Socket::SOCK_DGRAM]
+
+ possible.should include(array[0].socktype)
+ end
+ end
+
+ it 'sets a custom socket type of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('127.0.0.1', 80, nil, Socket::SOCK_DGRAM)
+
+ array[0].socktype.should == Socket::SOCK_DGRAM
+ end
+
+ platform_is_not :windows do
+ it 'sets the default socket protocol of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('127.0.0.1', 80)
+ possible = [Socket::IPPROTO_TCP, Socket::IPPROTO_UDP]
+
+ possible.should include(array[0].protocol)
+ end
+ end
+
+ it 'sets a custom socket protocol of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('127.0.0.1', 80, nil, nil, Socket::IPPROTO_UDP)
+
+ array[0].protocol.should == Socket::IPPROTO_UDP
+ end
+
+ it 'sets the canonical name when AI_CANONNAME is given as a flag' do
+ array = Addrinfo.getaddrinfo('localhost', 80, nil, nil, nil, Socket::AI_CANONNAME)
+
+ array[0].canonname.should be_an_instance_of(String)
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/getnameinfo_spec.rb b/spec/ruby/library/socket/addrinfo/getnameinfo_spec.rb
new file mode 100644
index 0000000000..43b5a2000a
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/getnameinfo_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo#getnameinfo' do
+ describe 'using an IP Addrinfo' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @addr = Addrinfo.tcp(ip_address, 21)
+ end
+
+ it 'returns the node and service names' do
+ host, service = @addr.getnameinfo
+ service.should == 'ftp'
+ end
+
+ it 'accepts flags as an Integer as the first argument' do
+ host, service = @addr.getnameinfo(Socket::NI_NUMERICSERV)
+ service.should == '21'
+ end
+ end
+ end
+
+ platform_is :linux do
+ platform_is_not :android do
+ describe 'using a UNIX Addrinfo' do
+ before do
+ @addr = Addrinfo.unix('cats')
+ @host = Socket.gethostname
+ end
+
+ it 'returns the hostname and UNIX socket path' do
+ host, path = @addr.getnameinfo
+
+ host.should == @host
+ path.should == 'cats'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/initialize_spec.rb b/spec/ruby/library/socket/addrinfo/initialize_spec.rb
new file mode 100644
index 0000000000..1f16531aaa
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/initialize_spec.rb
@@ -0,0 +1,589 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#initialize" do
+
+ describe "with a sockaddr string" do
+
+ describe "without a family" do
+ before :each do
+ @addrinfo = Addrinfo.new(Socket.sockaddr_in("smtp", "2001:DB8::1"))
+ end
+
+ it "stores the ip address from the sockaddr" do
+ @addrinfo.ip_address.should == "2001:db8::1"
+ end
+
+ it "stores the port number from the sockaddr" do
+ @addrinfo.ip_port.should == 25
+ end
+
+ it "returns the UNSPEC pfamily" do
+ @addrinfo.pfamily.should == Socket::PF_UNSPEC
+ end
+
+ it 'returns AF_INET as the default address family' do
+ addr = Addrinfo.new(Socket.sockaddr_in(80, '127.0.0.1'))
+
+ addr.afamily.should == Socket::AF_INET
+ end
+
+ it "returns the INET6 afamily" do
+ @addrinfo.afamily.should == Socket::AF_INET6
+ end
+
+ it "returns the 0 socket type" do
+ @addrinfo.socktype.should == 0
+ end
+
+ it "returns the 0 protocol" do
+ @addrinfo.protocol.should == 0
+ end
+ end
+
+ describe "with a family given" do
+ before :each do
+ @addrinfo = Addrinfo.new(Socket.sockaddr_in("smtp", "2001:DB8::1"), Socket::PF_INET6)
+ end
+
+ it "stores the ip address from the sockaddr" do
+ @addrinfo.ip_address.should == "2001:db8::1"
+ end
+
+ it "stores the port number from the sockaddr" do
+ @addrinfo.ip_port.should == 25
+ end
+
+ it "returns the INET6 pfamily" do
+ @addrinfo.pfamily.should == Socket::PF_INET6
+ end
+
+ it "returns the INET6 afamily" do
+ @addrinfo.afamily.should == Socket::AF_INET6
+ end
+
+ it "returns the 0 socket type" do
+ @addrinfo.socktype.should == 0
+ end
+
+ it "returns the 0 protocol" do
+ @addrinfo.protocol.should == 0
+ end
+ end
+
+ describe "with a family and socket type" do
+ before :each do
+ @addrinfo = Addrinfo.new(Socket.sockaddr_in("smtp", "2001:DB8::1"), Socket::PF_INET6, Socket::SOCK_STREAM)
+ end
+
+ it "stores the ip address from the sockaddr" do
+ @addrinfo.ip_address.should == "2001:db8::1"
+ end
+
+ it "stores the port number from the sockaddr" do
+ @addrinfo.ip_port.should == 25
+ end
+
+ it "returns the INET6 pfamily" do
+ @addrinfo.pfamily.should == Socket::PF_INET6
+ end
+
+ it "returns the INET6 afamily" do
+ @addrinfo.afamily.should == Socket::AF_INET6
+ end
+
+ it "returns the specified socket type" do
+ @addrinfo.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it "returns the 0 protocol" do
+ @addrinfo.protocol.should == 0
+ end
+ end
+
+ describe "with a family, socket type and protocol" do
+ before :each do
+ @addrinfo = Addrinfo.new(Socket.sockaddr_in("smtp", "2001:DB8::1"), Socket::PF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP)
+ end
+
+ it "stores the ip address from the sockaddr" do
+ @addrinfo.ip_address.should == "2001:db8::1"
+ end
+
+ it "stores the port number from the sockaddr" do
+ @addrinfo.ip_port.should == 25
+ end
+
+ it "returns the INET6 pfamily" do
+ @addrinfo.pfamily.should == Socket::PF_INET6
+ end
+
+ it "returns the INET6 afamily" do
+ @addrinfo.afamily.should == Socket::AF_INET6
+ end
+
+ it "returns the specified socket type" do
+ @addrinfo.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it "returns the specified protocol" do
+ @addrinfo.protocol.should == Socket::IPPROTO_TCP
+ end
+ end
+
+ end
+
+ describe "with a sockaddr array" do
+
+ describe "without a family" do
+ before :each do
+ @addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"])
+ end
+
+ it "stores the ip address from the sockaddr" do
+ @addrinfo.ip_address.should == "127.0.0.1"
+ end
+
+ it "stores the port number from the sockaddr" do
+ @addrinfo.ip_port.should == 46102
+ end
+
+ it "returns the INET pfamily" do
+ @addrinfo.pfamily.should == Socket::PF_INET
+ end
+
+ it "returns the INET afamily" do
+ @addrinfo.afamily.should == Socket::AF_INET
+ end
+
+ it "returns the 0 socket type" do
+ @addrinfo.socktype.should == 0
+ end
+
+ it "returns the 0 protocol" do
+ @addrinfo.protocol.should == 0
+ end
+ end
+
+ describe 'with a valid IP address' do
+ # Uses AF_INET6 since AF_INET is the default, making it a better test
+ # that Addrinfo actually sets the family correctly.
+ before do
+ @sockaddr = ['AF_INET6', 80, 'hostname', '::1']
+ end
+
+ it 'returns an Addrinfo with the correct IP' do
+ addr = Addrinfo.new(@sockaddr)
+
+ addr.ip_address.should == '::1'
+ end
+
+ it 'returns an Addrinfo with the correct address family' do
+ addr = Addrinfo.new(@sockaddr)
+
+ addr.afamily.should == Socket::AF_INET6
+ end
+
+ it 'returns an Addrinfo with the correct protocol family' do
+ addr = Addrinfo.new(@sockaddr)
+
+ addr.pfamily.should == Socket::PF_INET6
+ end
+
+ it 'returns an Addrinfo with the correct port' do
+ addr = Addrinfo.new(@sockaddr)
+
+ addr.ip_port.should == 80
+ end
+ end
+
+ describe 'with an invalid IP address' do
+ it 'raises SocketError' do
+ block = -> { Addrinfo.new(['AF_INET6', 80, 'hostname', '127.0.0.1']) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+
+ describe "with a family given" do
+ before :each do
+ @addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"], Socket::PF_INET)
+ end
+
+ it "stores the ip address from the sockaddr" do
+ @addrinfo.ip_address.should == "127.0.0.1"
+ end
+
+ it "stores the port number from the sockaddr" do
+ @addrinfo.ip_port.should == 46102
+ end
+
+ it "returns the INET pfamily" do
+ @addrinfo.pfamily.should == Socket::PF_INET
+ end
+
+ it "returns the INET afamily" do
+ @addrinfo.afamily.should == Socket::AF_INET
+ end
+
+ it "returns the 0 socket type" do
+ @addrinfo.socktype.should == 0
+ end
+
+ it "returns the 0 protocol" do
+ @addrinfo.protocol.should == 0
+ end
+ end
+
+ describe "with a family and socket type" do
+ before :each do
+ @addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"], Socket::PF_INET, Socket::SOCK_STREAM)
+ end
+
+ it "stores the ip address from the sockaddr" do
+ @addrinfo.ip_address.should == "127.0.0.1"
+ end
+
+ it "stores the port number from the sockaddr" do
+ @addrinfo.ip_port.should == 46102
+ end
+
+ it "returns the INET pfamily" do
+ @addrinfo.pfamily.should == Socket::PF_INET
+ end
+
+ it "returns the INET afamily" do
+ @addrinfo.afamily.should == Socket::AF_INET
+ end
+
+ it "returns the 0 socket type" do
+ @addrinfo.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it "returns the 0 protocol" do
+ @addrinfo.protocol.should == 0
+ end
+
+ [:SOCK_STREAM, :SOCK_DGRAM, :SOCK_RAW].each do |type|
+ it "overwrites the socket type #{type}" do
+ sockaddr = ['AF_INET', 80, 'hostname', '127.0.0.1']
+
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(sockaddr, nil, value)
+
+ addr.socktype.should == value
+ end
+ end
+
+ platform_is_not :android do
+ with_feature :sock_packet do
+ [:SOCK_SEQPACKET].each do |type|
+ it "overwrites the socket type #{type}" do
+ sockaddr = ['AF_INET', 80, 'hostname', '127.0.0.1']
+
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(sockaddr, nil, value)
+
+ addr.socktype.should == value
+ end
+ end
+ end
+ end
+
+ it "raises SocketError when using SOCK_RDM" do
+ sockaddr = ['AF_INET', 80, 'hostname', '127.0.0.1']
+ value = Socket::SOCK_RDM
+ block = -> { Addrinfo.new(sockaddr, nil, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+
+ describe "with a family, socket type and protocol" do
+ before :each do
+ @addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"], Socket::PF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP)
+ end
+
+ it "stores the ip address from the sockaddr" do
+ @addrinfo.ip_address.should == "127.0.0.1"
+ end
+
+ it "stores the port number from the sockaddr" do
+ @addrinfo.ip_port.should == 46102
+ end
+
+ it "returns the INET pfamily" do
+ @addrinfo.pfamily.should == Socket::PF_INET
+ end
+
+ it "returns the INET afamily" do
+ @addrinfo.afamily.should == Socket::AF_INET
+ end
+
+ it "returns the 0 socket type" do
+ @addrinfo.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it "returns the specified protocol" do
+ @addrinfo.protocol.should == Socket::IPPROTO_TCP
+ end
+ end
+ end
+
+ describe 'using an Array with extra arguments' do
+ describe 'with the AF_INET6 address family and an explicit protocol family' do
+ before do
+ @sockaddr = ['AF_INET6', 80, 'hostname', '127.0.0.1']
+ end
+
+ it "raises SocketError when using any Socket constant except AF_INET(6)/PF_INET(6)" do
+ Socket.constants.grep(/(^AF_|^PF_)(?!INET)/).each do |constant|
+ value = Socket.const_get(constant)
+ -> {
+ Addrinfo.new(@sockaddr, value)
+ }.should raise_error(SocketError)
+ end
+ end
+ end
+
+ describe 'with the AF_INET address family and an explicit socket protocol' do
+ before do
+ @sockaddr = ['AF_INET', 80, 'hostname', '127.0.0.1']
+ end
+
+ describe 'and no socket type is given' do
+ valid = [:IPPROTO_IP, :IPPROTO_UDP, :IPPROTO_HOPOPTS]
+
+ valid.each do |type|
+ it "overwrites the protocol when using #{type}" do
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(@sockaddr, nil, nil, value)
+
+ addr.protocol.should == value
+ end
+ end
+
+ platform_is_not :windows, :aix do
+ (Socket.constants.grep(/^IPPROTO/) - valid).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = -> { Addrinfo.new(@sockaddr, nil, nil, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+
+ describe 'and the socket type is set to SOCK_DGRAM' do
+ before do
+ @socktype = Socket::SOCK_DGRAM
+ end
+
+ valid = [:IPPROTO_IP, :IPPROTO_UDP, :IPPROTO_HOPOPTS]
+
+ valid.each do |type|
+ it "overwrites the protocol when using #{type}" do
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(@sockaddr, nil, @socktype, value)
+
+ addr.protocol.should == value
+ end
+ end
+
+ platform_is_not :windows, :aix do
+ (Socket.constants.grep(/^IPPROTO/) - valid).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = -> { Addrinfo.new(@sockaddr, nil, @socktype, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+
+ with_feature :sock_packet do
+ describe 'and the socket type is set to SOCK_PACKET' do
+ before do
+ @socktype = Socket::SOCK_PACKET
+ end
+
+ Socket.constants.grep(/^IPPROTO/).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = -> { Addrinfo.new(@sockaddr, nil, @socktype, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+
+ describe 'and the socket type is set to SOCK_RAW' do
+ before do
+ @socktype = Socket::SOCK_RAW
+ end
+
+ Socket.constants.grep(/^IPPROTO/).each do |type|
+ it "overwrites the protocol when using #{type}" do
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(@sockaddr, nil, @socktype, value)
+
+ addr.protocol.should == value
+ end
+ end
+ end
+
+ describe 'and the socket type is set to SOCK_RDM' do
+ before do
+ @socktype = Socket::SOCK_RDM
+ end
+
+ Socket.constants.grep(/^IPPROTO/).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = -> { Addrinfo.new(@sockaddr, nil, @socktype, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+
+ platform_is :linux do
+ platform_is_not :android do
+ describe 'and the socket type is set to SOCK_SEQPACKET' do
+ before do
+ @socktype = Socket::SOCK_SEQPACKET
+ end
+
+ valid = [:IPPROTO_IP, :IPPROTO_HOPOPTS]
+
+ valid.each do |type|
+ it "overwrites the protocol when using #{type}" do
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(@sockaddr, nil, @socktype, value)
+
+ addr.protocol.should == value
+ end
+ end
+
+ (Socket.constants.grep(/^IPPROTO/) - valid).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = -> { Addrinfo.new(@sockaddr, nil, @socktype, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+ end
+
+ describe 'and the socket type is set to SOCK_STREAM' do
+ before do
+ @socktype = Socket::SOCK_STREAM
+ end
+
+ valid = [:IPPROTO_IP, :IPPROTO_TCP, :IPPROTO_HOPOPTS]
+
+ valid.each do |type|
+ it "overwrites the protocol when using #{type}" do
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(@sockaddr, nil, @socktype, value)
+
+ addr.protocol.should == value
+ end
+ end
+
+ platform_is_not :windows, :aix do
+ (Socket.constants.grep(/^IPPROTO/) - valid).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = -> { Addrinfo.new(@sockaddr, nil, @socktype, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ describe 'with Symbols' do
+ before do
+ @sockaddr = Socket.sockaddr_in(80, '127.0.0.1')
+ end
+
+ it 'returns an Addrinfo with :PF_INET family' do
+ addr = Addrinfo.new(@sockaddr, :PF_INET)
+
+ addr.pfamily.should == Socket::PF_INET
+ end
+
+ it 'returns an Addrinfo with :INET family' do
+ addr = Addrinfo.new(@sockaddr, :INET)
+
+ addr.pfamily.should == Socket::PF_INET
+ end
+
+ it 'returns an Addrinfo with :SOCK_STREAM as the socket type' do
+ addr = Addrinfo.new(@sockaddr, nil, :SOCK_STREAM)
+
+ addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'returns an Addrinfo with :STREAM as the socket type' do
+ addr = Addrinfo.new(@sockaddr, nil, :STREAM)
+
+ addr.socktype.should == Socket::SOCK_STREAM
+ end
+ end
+
+ describe 'with Strings' do
+ before do
+ @sockaddr = Socket.sockaddr_in(80, '127.0.0.1')
+ end
+
+ it 'returns an Addrinfo with "PF_INET" family' do
+ addr = Addrinfo.new(@sockaddr, 'PF_INET')
+
+ addr.pfamily.should == Socket::PF_INET
+ end
+
+ it 'returns an Addrinfo with "INET" family' do
+ addr = Addrinfo.new(@sockaddr, 'INET')
+
+ addr.pfamily.should == Socket::PF_INET
+ end
+
+ it 'returns an Addrinfo with "SOCK_STREAM" as the socket type' do
+ addr = Addrinfo.new(@sockaddr, nil, 'SOCK_STREAM')
+
+ addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'returns an Addrinfo with "STREAM" as the socket type' do
+ addr = Addrinfo.new(@sockaddr, nil, 'STREAM')
+
+ addr.socktype.should == Socket::SOCK_STREAM
+ end
+ end
+
+ describe 'using separate arguments for a Unix socket' do
+ before do
+ @sockaddr = Socket.pack_sockaddr_un('socket')
+ end
+
+ it 'returns an Addrinfo with the correct unix path' do
+ Addrinfo.new(@sockaddr).unix_path.should == 'socket'
+ end
+
+ it 'returns an Addrinfo with the correct protocol family' do
+ Addrinfo.new(@sockaddr).pfamily.should == Socket::PF_UNSPEC
+ end
+
+ it 'returns an Addrinfo with the correct address family' do
+ Addrinfo.new(@sockaddr).afamily.should == Socket::AF_UNIX
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/inspect_sockaddr_spec.rb b/spec/ruby/library/socket/addrinfo/inspect_sockaddr_spec.rb
new file mode 100644
index 0000000000..6b18c79469
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/inspect_sockaddr_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../spec_helper'
+
+
+describe 'Addrinfo#inspect_sockaddr' do
+ describe 'using an IPv4 address' do
+ it 'returns a String containing the IP address and port number' do
+ addr = Addrinfo.tcp('127.0.0.1', 80)
+
+ addr.inspect_sockaddr.should == '127.0.0.1:80'
+ end
+
+ it 'returns a String containing just the IP address when no port is given' do
+ addr = Addrinfo.tcp('127.0.0.1', 0)
+
+ addr.inspect_sockaddr.should == '127.0.0.1'
+ end
+ end
+
+ describe 'using an IPv6 address' do
+ before :each do
+ @ip = '2001:0db8:85a3:0000:0000:8a2e:0370:7334'
+ end
+
+ it 'returns a String containing the IP address and port number' do
+ Addrinfo.tcp('::1', 80).inspect_sockaddr.should == '[::1]:80'
+ Addrinfo.tcp(@ip, 80).inspect_sockaddr.should == '[2001:db8:85a3::8a2e:370:7334]:80'
+ end
+
+ it 'returns a String containing just the IP address when no port is given' do
+ Addrinfo.tcp('::1', 0).inspect_sockaddr.should == '::1'
+ Addrinfo.tcp(@ip, 0).inspect_sockaddr.should == '2001:db8:85a3::8a2e:370:7334'
+ end
+ end
+
+ describe 'using a UNIX path' do
+ it 'returns a String containing the UNIX path' do
+ addr = Addrinfo.unix('/foo/bar')
+
+ addr.inspect_sockaddr.should == '/foo/bar'
+ end
+
+ it 'returns a String containing the UNIX path when using a relative path' do
+ addr = Addrinfo.unix('foo')
+
+ addr.inspect_sockaddr.should == 'UNIX foo'
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/inspect_spec.rb b/spec/ruby/library/socket/addrinfo/inspect_spec.rb
new file mode 100644
index 0000000000..1442af6162
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/inspect_spec.rb
@@ -0,0 +1,63 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#inspect' do
+ describe 'using an IPv4 Addrinfo' do
+ it 'returns a String when using a TCP Addrinfo' do
+ addr = Addrinfo.tcp('127.0.0.1', 80)
+
+ addr.inspect.should == '#<Addrinfo: 127.0.0.1:80 TCP>'
+ end
+
+ it 'returns a String when using an UDP Addrinfo' do
+ addr = Addrinfo.udp('127.0.0.1', 80)
+
+ addr.inspect.should == '#<Addrinfo: 127.0.0.1:80 UDP>'
+ end
+
+ it 'returns a String when using an Addrinfo without a port' do
+ addr = Addrinfo.ip('127.0.0.1')
+
+ addr.inspect.should == '#<Addrinfo: 127.0.0.1>'
+ end
+ end
+
+ describe 'using an IPv6 Addrinfo' do
+ it 'returns a String when using a TCP Addrinfo' do
+ addr = Addrinfo.tcp('::1', 80)
+
+ addr.inspect.should == '#<Addrinfo: [::1]:80 TCP>'
+ end
+
+ it 'returns a String when using an UDP Addrinfo' do
+ addr = Addrinfo.udp('::1', 80)
+
+ addr.inspect.should == '#<Addrinfo: [::1]:80 UDP>'
+ end
+
+ it 'returns a String when using an Addrinfo without a port' do
+ addr = Addrinfo.ip('::1')
+
+ addr.inspect.should == '#<Addrinfo: ::1>'
+ end
+ end
+
+ describe 'using a UNIX Addrinfo' do
+ it 'returns a String' do
+ addr = Addrinfo.unix('/foo')
+
+ addr.inspect.should == '#<Addrinfo: /foo SOCK_STREAM>'
+ end
+
+ it 'returns a String when using a relative UNIX path' do
+ addr = Addrinfo.unix('foo')
+
+ addr.inspect.should == '#<Addrinfo: UNIX foo SOCK_STREAM>'
+ end
+
+ it 'returns a String when using a DGRAM socket' do
+ addr = Addrinfo.unix('/foo', Socket::SOCK_DGRAM)
+
+ addr.inspect.should == '#<Addrinfo: /foo SOCK_DGRAM>'
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ip_address_spec.rb b/spec/ruby/library/socket/addrinfo/ip_address_spec.rb
new file mode 100644
index 0000000000..193432e861
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ip_address_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ip_address" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "returns the ip address" do
+ @addrinfo.ip_address.should == "127.0.0.1"
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns the ip address" do
+ @addrinfo.ip_address.should == "::1"
+ end
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "raises an exception" do
+ -> { @addrinfo.ip_address }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'with an Array as the socket address' do
+ it 'returns the IP as a String' do
+ sockaddr = ['AF_INET', 80, 'localhost', '127.0.0.1']
+ addr = Addrinfo.new(sockaddr)
+
+ addr.ip_address.should == '127.0.0.1'
+ end
+ end
+
+ describe 'without an IP address' do
+ before do
+ @ips = ['127.0.0.1', '0.0.0.0', '::1']
+ end
+
+ # Both these cases seem to return different values at times on MRI. Since
+ # this is network dependent we can't rely on an exact IP being returned.
+ it 'returns the local IP address when using an empty String as the IP' do
+ sockaddr = Socket.sockaddr_in(80, '')
+ addr = Addrinfo.new(sockaddr)
+
+ @ips.include?(addr.ip_address).should == true
+ end
+
+ it 'returns the local IP address when using nil as the IP' do
+ sockaddr = Socket.sockaddr_in(80, nil)
+ addr = Addrinfo.new(sockaddr)
+
+ @ips.include?(addr.ip_address).should == true
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ip_port_spec.rb b/spec/ruby/library/socket/addrinfo/ip_port_spec.rb
new file mode 100644
index 0000000000..f10ce35143
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ip_port_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ip_port" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "returns the port" do
+ @addrinfo.ip_port.should == 80
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns the port" do
+ @addrinfo.ip_port.should == 80
+ end
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "raises an exception" do
+ -> { @addrinfo.ip_port }.should raise_error(SocketError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ip_spec.rb b/spec/ruby/library/socket/addrinfo/ip_spec.rb
new file mode 100644
index 0000000000..09b9341605
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ip_spec.rb
@@ -0,0 +1,62 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Addrinfo#ip?" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "returns true" do
+ @addrinfo.ip?.should be_true
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns true" do
+ @addrinfo.ip?.should be_true
+ end
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns false" do
+ @addrinfo.ip?.should be_false
+ end
+ end
+end
+
+describe 'Addrinfo.ip' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ it 'returns an Addrinfo instance' do
+ Addrinfo.ip(ip_address).should be_an_instance_of(Addrinfo)
+ end
+
+ it 'sets the IP address' do
+ Addrinfo.ip(ip_address).ip_address.should == ip_address
+ end
+
+ it 'sets the port to 0' do
+ Addrinfo.ip(ip_address).ip_port.should == 0
+ end
+
+ it 'sets the address family' do
+ Addrinfo.ip(ip_address).afamily.should == family
+ end
+
+ it 'sets the protocol family' do
+ Addrinfo.ip(ip_address).pfamily.should == family
+ end
+
+ it 'sets the socket type to 0' do
+ Addrinfo.ip(ip_address).socktype.should == 0
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ip_unpack_spec.rb b/spec/ruby/library/socket/addrinfo/ip_unpack_spec.rb
new file mode 100644
index 0000000000..58260c4557
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ip_unpack_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ip_unpack" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "returns the ip address and port pair" do
+ @addrinfo.ip_unpack.should == ["127.0.0.1", 80]
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns the ip address and port pair" do
+ @addrinfo.ip_unpack.should == ["::1", 80]
+ end
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "raises an exception" do
+ -> { @addrinfo.ip_unpack }.should raise_error(SocketError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv4_loopback_spec.rb b/spec/ruby/library/socket/addrinfo/ipv4_loopback_spec.rb
new file mode 100644
index 0000000000..3a584d4f52
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv4_loopback_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ipv4_loopback?" do
+ describe "for an ipv4 socket" do
+ it "returns true for the loopback address" do
+ Addrinfo.ip('127.0.0.1').should.ipv4_loopback?
+ Addrinfo.ip('127.0.0.2').should.ipv4_loopback?
+ Addrinfo.ip('127.255.0.1').should.ipv4_loopback?
+ Addrinfo.ip('127.255.255.255').should.ipv4_loopback?
+ end
+
+ it "returns false for another address" do
+ Addrinfo.ip('255.255.255.0').ipv4_loopback?.should be_false
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @loopback = Addrinfo.tcp("::1", 80)
+ @other = Addrinfo.tcp("::", 80)
+ end
+
+ it "returns false for the loopback address" do
+ @loopback.ipv4_loopback?.should be_false
+ end
+
+ it "returns false for another address" do
+ @other.ipv4_loopback?.should be_false
+ end
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns false" do
+ @addrinfo.ipv4_loopback?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv4_multicast_spec.rb b/spec/ruby/library/socket/addrinfo/ipv4_multicast_spec.rb
new file mode 100644
index 0000000000..e4b4cfcc84
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv4_multicast_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ipv4_multicast?" do
+ it 'returns true for a multicast address' do
+ Addrinfo.ip('224.0.0.0').should.ipv4_multicast?
+ Addrinfo.ip('224.0.0.9').should.ipv4_multicast?
+ Addrinfo.ip('239.255.255.250').should.ipv4_multicast?
+ end
+
+ it 'returns false for a regular address' do
+ Addrinfo.ip('8.8.8.8').should_not.ipv4_multicast?
+ end
+
+ it 'returns false for an IPv6 address' do
+ Addrinfo.ip('::1').should_not.ipv4_multicast?
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns false" do
+ @addrinfo.ipv4_multicast?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv4_private_spec.rb b/spec/ruby/library/socket/addrinfo/ipv4_private_spec.rb
new file mode 100644
index 0000000000..97218b5ba3
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv4_private_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ipv4_private?" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @private = Addrinfo.tcp("10.0.0.1", 80)
+ @other = Addrinfo.tcp("0.0.0.0", 80)
+ end
+
+ it "returns true for a private address" do
+ Addrinfo.ip('10.0.0.0').should.ipv4_private?
+ Addrinfo.ip('10.0.0.5').should.ipv4_private?
+
+ Addrinfo.ip('172.16.0.0').should.ipv4_private?
+ Addrinfo.ip('172.16.0.5').should.ipv4_private?
+
+ Addrinfo.ip('192.168.0.0').should.ipv4_private?
+ Addrinfo.ip('192.168.0.5').should.ipv4_private?
+ end
+
+ it "returns false for a public address" do
+ @other.ipv4_private?.should be_false
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @other = Addrinfo.tcp("::", 80)
+ end
+
+ it "returns false" do
+ @other.ipv4_private?.should be_false
+ end
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns false" do
+ @addrinfo.ipv4_private?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv4_spec.rb b/spec/ruby/library/socket/addrinfo/ipv4_spec.rb
new file mode 100644
index 0000000000..61f7759b10
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv4_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ipv4?" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("10.0.0.1", 80)
+ end
+
+ it "returns true" do
+ @addrinfo.ipv4?.should be_true
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns false" do
+ @addrinfo.ipv4?.should be_false
+ end
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns false" do
+ @addrinfo.ipv4?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_linklocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_linklocal_spec.rb
new file mode 100644
index 0000000000..bfef396381
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_linklocal_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+guard -> { SocketSpecs.ipv6_available? } do
+ describe 'Addrinfo#ipv6_linklocal?' do
+ platform_is_not :aix do
+ it 'returns true for a link-local address' do
+ Addrinfo.ip('fe80::').should.ipv6_linklocal?
+ Addrinfo.ip('fe81::').should.ipv6_linklocal?
+ Addrinfo.ip('fe8f::').should.ipv6_linklocal?
+ Addrinfo.ip('fe80::1').should.ipv6_linklocal?
+ end
+ end
+
+ it 'returns false for a regular address' do
+ Addrinfo.ip('::1').should_not.ipv6_linklocal?
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').should_not.ipv6_linklocal?
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_loopback_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_loopback_spec.rb
new file mode 100644
index 0000000000..ffc75185ea
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_loopback_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ipv6_loopback?" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @loopback = Addrinfo.tcp("127.0.0.1", 80)
+ @other = Addrinfo.tcp("0.0.0.0", 80)
+ end
+
+ it "returns false for the loopback address" do
+ @loopback.ipv6_loopback?.should be_false
+ end
+
+ it "returns false for another address" do
+ @other.ipv6_loopback?.should be_false
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @loopback = Addrinfo.tcp("::1", 80)
+ @other = Addrinfo.tcp("::", 80)
+ end
+
+ it "returns true for the loopback address" do
+ @loopback.ipv6_loopback?.should be_true
+ end
+
+ it "returns false for another address" do
+ @other.ipv6_loopback?.should be_false
+ end
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns false" do
+ @addrinfo.ipv6_loopback?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_mc_global_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_mc_global_spec.rb
new file mode 100644
index 0000000000..01fa0992ba
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_mc_global_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_mc_global?' do
+ it 'returns true for a multi-cast address in the global scope' do
+ Addrinfo.ip('ff1e::').should.ipv6_mc_global?
+ Addrinfo.ip('fffe::').should.ipv6_mc_global?
+ Addrinfo.ip('ff0e::').should.ipv6_mc_global?
+ Addrinfo.ip('ff1e::1').should.ipv6_mc_global?
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').should_not.ipv6_mc_global?
+ Addrinfo.ip('ff1a::').should_not.ipv6_mc_global?
+ Addrinfo.ip('ff1f::1').should_not.ipv6_mc_global?
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').should_not.ipv6_mc_global?
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_mc_linklocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_mc_linklocal_spec.rb
new file mode 100644
index 0000000000..a1298919eb
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_mc_linklocal_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_mc_linklocal?' do
+ it 'returns true for a multi-cast link-local address' do
+ Addrinfo.ip('ff12::').should.ipv6_mc_linklocal?
+ Addrinfo.ip('ff02::').should.ipv6_mc_linklocal?
+ Addrinfo.ip('fff2::').should.ipv6_mc_linklocal?
+ Addrinfo.ip('ff12::1').should.ipv6_mc_linklocal?
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').should_not.ipv6_mc_linklocal?
+ Addrinfo.ip('fff1::').should_not.ipv6_mc_linklocal?
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').should_not.ipv6_mc_linklocal?
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_mc_nodelocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_mc_nodelocal_spec.rb
new file mode 100644
index 0000000000..0aee952d88
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_mc_nodelocal_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_mc_nodelocal?' do
+ it 'returns true for a multi-cast node-local address' do
+ Addrinfo.ip('ff11::').should.ipv6_mc_nodelocal?
+ Addrinfo.ip('ff01::').should.ipv6_mc_nodelocal?
+ Addrinfo.ip('fff1::').should.ipv6_mc_nodelocal?
+ Addrinfo.ip('ff11::1').should.ipv6_mc_nodelocal?
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').should_not.ipv6_mc_nodelocal?
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').should_not.ipv6_mc_nodelocal?
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_mc_orglocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_mc_orglocal_spec.rb
new file mode 100644
index 0000000000..2977a98d30
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_mc_orglocal_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_mc_orglocal?' do
+ it 'returns true for a multi-cast org-local address' do
+ Addrinfo.ip('ff18::').should.ipv6_mc_orglocal?
+ Addrinfo.ip('ff08::').should.ipv6_mc_orglocal?
+ Addrinfo.ip('fff8::').should.ipv6_mc_orglocal?
+ Addrinfo.ip('ff18::1').should.ipv6_mc_orglocal?
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').should_not.ipv6_mc_orglocal?
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').should_not.ipv6_mc_orglocal?
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_mc_sitelocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_mc_sitelocal_spec.rb
new file mode 100644
index 0000000000..58e5976a40
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_mc_sitelocal_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_mc_sitelocal?' do
+ it 'returns true for a multi-cast site-local address' do
+ Addrinfo.ip('ff15::').should.ipv6_mc_sitelocal?
+ Addrinfo.ip('ff05::').should.ipv6_mc_sitelocal?
+ Addrinfo.ip('fff5::').should.ipv6_mc_sitelocal?
+ Addrinfo.ip('ff15::1').should.ipv6_mc_sitelocal?
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').should_not.ipv6_mc_sitelocal?
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').should_not.ipv6_mc_sitelocal?
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_multicast_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_multicast_spec.rb
new file mode 100644
index 0000000000..99d4e8cf4d
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_multicast_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ipv6_multicast?" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @multicast = Addrinfo.tcp("224.0.0.1", 80)
+ @other = Addrinfo.tcp("0.0.0.0", 80)
+ end
+
+ it "returns true for a multicast address" do
+ @multicast.ipv6_multicast?.should be_false
+ end
+
+ it "returns false for another address" do
+ @other.ipv6_multicast?.should be_false
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ it "returns true for a multicast address" do
+ Addrinfo.ip('ff00::').should.ipv6_multicast?
+ Addrinfo.ip('ff00::1').should.ipv6_multicast?
+ Addrinfo.ip('ff08::1').should.ipv6_multicast?
+ Addrinfo.ip('fff8::1').should.ipv6_multicast?
+
+ Addrinfo.ip('ff02::').should.ipv6_multicast?
+ Addrinfo.ip('ff02::1').should.ipv6_multicast?
+ Addrinfo.ip('ff0f::').should.ipv6_multicast?
+ end
+
+ it "returns false for another address" do
+ Addrinfo.ip('::1').should_not.ipv6_multicast?
+ Addrinfo.ip('fe80::').should_not.ipv6_multicast?
+ end
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns false" do
+ @addrinfo.ipv6_multicast?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_sitelocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_sitelocal_spec.rb
new file mode 100644
index 0000000000..9158eb5809
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_sitelocal_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+guard -> { SocketSpecs.ipv6_available? } do
+ describe 'Addrinfo#ipv6_sitelocal?' do
+ platform_is_not :aix do
+ it 'returns true for a site-local address' do
+ Addrinfo.ip('feef::').should.ipv6_sitelocal?
+ Addrinfo.ip('fee0::').should.ipv6_sitelocal?
+ Addrinfo.ip('fee2::').should.ipv6_sitelocal?
+ Addrinfo.ip('feef::1').should.ipv6_sitelocal?
+ end
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').should_not.ipv6_sitelocal?
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').should_not.ipv6_sitelocal?
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_spec.rb
new file mode 100644
index 0000000000..436d5e930b
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#ipv6?" do
+ describe "for an ipv4 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("10.0.0.1", 80)
+ end
+
+ it "returns true" do
+ @addrinfo.ipv6?.should be_false
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns false" do
+ @addrinfo.ipv6?.should be_true
+ end
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns false" do
+ @addrinfo.ipv6?.should be_false
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_to_ipv4_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_to_ipv4_spec.rb
new file mode 100644
index 0000000000..29050bec20
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_to_ipv4_spec.rb
@@ -0,0 +1,71 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+guard -> { SocketSpecs.ipv6_available? } do
+ describe 'Addrinfo#ipv6_to_ipv4' do
+ it 'returns an Addrinfo for ::192.168.1.1' do
+ addr = Addrinfo.ip('::192.168.1.1').ipv6_to_ipv4
+
+ addr.should be_an_instance_of(Addrinfo)
+
+ addr.afamily.should == Socket::AF_INET
+ addr.ip_address.should == '192.168.1.1'
+ end
+
+ platform_is_not :aix do
+ it 'returns an Addrinfo for ::0.0.1.1' do
+ addr = Addrinfo.ip('::0.0.1.1').ipv6_to_ipv4
+
+ addr.should be_an_instance_of(Addrinfo)
+
+ addr.afamily.should == Socket::AF_INET
+ addr.ip_address.should == '0.0.1.1'
+ end
+
+ it 'returns an Addrinfo for ::0.0.1.0' do
+ addr = Addrinfo.ip('::0.0.1.0').ipv6_to_ipv4
+
+ addr.should be_an_instance_of(Addrinfo)
+
+ addr.afamily.should == Socket::AF_INET
+ addr.ip_address.should == '0.0.1.0'
+ end
+
+ it 'returns an Addrinfo for ::0.1.0.0' do
+ addr = Addrinfo.ip('::0.1.0.0').ipv6_to_ipv4
+
+ addr.should be_an_instance_of(Addrinfo)
+
+ addr.afamily.should == Socket::AF_INET
+ addr.ip_address.should == '0.1.0.0'
+ end
+ end
+
+ it 'returns an Addrinfo for ::ffff:192.168.1.1' do
+ addr = Addrinfo.ip('::ffff:192.168.1.1').ipv6_to_ipv4
+
+ addr.should be_an_instance_of(Addrinfo)
+
+ addr.afamily.should == Socket::AF_INET
+ addr.ip_address.should == '192.168.1.1'
+ end
+
+ it 'returns nil for ::0.0.0.1' do
+ Addrinfo.ip('::0.0.0.1').ipv6_to_ipv4.should be_nil
+ end
+
+ it 'returns nil for a pure IPv6 Addrinfo' do
+ Addrinfo.ip('::1').ipv6_to_ipv4.should be_nil
+ end
+
+ it 'returns nil for an IPv4 Addrinfo' do
+ Addrinfo.ip('192.168.1.1').ipv6_to_ipv4.should be_nil
+ end
+
+ describe 'for a unix socket' do
+ it 'returns nil for a UNIX Addrinfo' do
+ Addrinfo.unix('foo').ipv6_to_ipv4.should be_nil
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_unique_local_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_unique_local_spec.rb
new file mode 100644
index 0000000000..22f0fa3b75
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_unique_local_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_unique_local?' do
+ it 'returns true for an unique local IPv6 address' do
+ Addrinfo.ip('fc00::').should.ipv6_unique_local?
+ Addrinfo.ip('fd00::').should.ipv6_unique_local?
+ Addrinfo.ip('fcff::').should.ipv6_unique_local?
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').should_not.ipv6_unique_local?
+ Addrinfo.ip('fe00::').should_not.ipv6_unique_local?
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').should_not.ipv6_unique_local?
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_unspecified_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_unspecified_spec.rb
new file mode 100644
index 0000000000..d63979ceda
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_unspecified_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_unspecified?' do
+ it 'returns true for an unspecified IPv6 address' do
+ Addrinfo.ip('::').should.ipv6_unspecified?
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').should_not.ipv6_unspecified?
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').should_not.ipv6_unspecified?
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_v4compat_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_v4compat_spec.rb
new file mode 100644
index 0000000000..21ca85af99
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_v4compat_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_v4compat?' do
+ it 'returns true for an IPv4 compatible address' do
+ Addrinfo.ip('::127.0.0.1').should.ipv6_v4compat?
+ Addrinfo.ip('::192.168.1.1').should.ipv6_v4compat?
+ end
+
+ it 'returns false for an IPv4 mapped address' do
+ Addrinfo.ip('::ffff:192.168.1.1').should_not.ipv6_v4compat?
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').should_not.ipv6_v4compat?
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').should_not.ipv6_v4compat?
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_v4mapped_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_v4mapped_spec.rb
new file mode 100644
index 0000000000..7dac0e75db
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_v4mapped_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_v4mapped?' do
+ it 'returns true for an IPv4 compatible address' do
+ Addrinfo.ip('::ffff:192.168.1.1').should.ipv6_v4mapped?
+ end
+
+ it 'returns false for an IPv4 compatible address' do
+ Addrinfo.ip('::192.168.1.1').should_not.ipv6_v4mapped?
+ Addrinfo.ip('::127.0.0.1').should_not.ipv6_v4mapped?
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').should_not.ipv6_v4mapped?
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').should_not.ipv6_v4mapped?
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/listen_spec.rb b/spec/ruby/library/socket/addrinfo/listen_spec.rb
new file mode 100644
index 0000000000..931093f732
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/listen_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#listen' do
+ before do
+ @addr = Addrinfo.tcp('127.0.0.1', 0)
+ @socket = nil
+ end
+
+ after do
+ @socket.close if @socket
+ end
+
+ it 'returns a Socket when no block is given' do
+ @socket = @addr.listen
+
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket if a block is given' do
+ @addr.listen do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes the socket if a block is given' do
+ socket = nil
+
+ @addr.listen do |sock|
+ socket = sock
+ end
+
+ socket.should.closed?
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/marshal_dump_spec.rb b/spec/ruby/library/socket/addrinfo/marshal_dump_spec.rb
new file mode 100644
index 0000000000..e2c3497f7f
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/marshal_dump_spec.rb
@@ -0,0 +1,80 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#marshal_dump' do
+ describe 'using an IP Addrinfo' do
+ before do
+ @addr = Addrinfo.getaddrinfo('localhost', 80, :INET, :STREAM,
+ Socket::IPPROTO_TCP, Socket::AI_CANONNAME)[0]
+ end
+
+ it 'returns an Array' do
+ @addr.marshal_dump.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @addr.marshal_dump
+ end
+
+ it 'includes the address family as the 1st value' do
+ @array[0].should == 'AF_INET'
+ end
+
+ it 'includes the IP address as the 2nd value' do
+ @array[1].should == [@addr.ip_address, @addr.ip_port.to_s]
+ end
+
+ it 'includes the protocol family as the 3rd value' do
+ @array[2].should == 'PF_INET'
+ end
+
+ it 'includes the socket type as the 4th value' do
+ @array[3].should == 'SOCK_STREAM'
+ end
+
+ it 'includes the protocol as the 5th value' do
+ @array[4].should == 'IPPROTO_TCP'
+ end
+
+ it 'includes the canonical name as the 6th value' do
+ @array[5].should == @addr.canonname
+ end
+ end
+ end
+
+ describe 'using a UNIX Addrinfo' do
+ before do
+ @addr = Addrinfo.unix('foo')
+ end
+
+ it 'returns an Array' do
+ @addr.marshal_dump.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @addr.marshal_dump
+ end
+
+ it 'includes the address family as the 1st value' do
+ @array[0].should == 'AF_UNIX'
+ end
+
+ it 'includes the UNIX path as the 2nd value' do
+ @array[1].should == @addr.unix_path
+ end
+
+ it 'includes the protocol family as the 3rd value' do
+ @array[2].should == 'PF_UNIX'
+ end
+
+ it 'includes the socket type as the 4th value' do
+ @array[3].should == 'SOCK_STREAM'
+ end
+
+ it 'includes the protocol as the 5th value' do
+ @array[4].should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/marshal_load_spec.rb b/spec/ruby/library/socket/addrinfo/marshal_load_spec.rb
new file mode 100644
index 0000000000..02cef90115
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/marshal_load_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#marshal_load' do
+ describe 'using an IP address' do
+ it 'returns a new Addrinfo' do
+ source = Addrinfo.getaddrinfo('localhost', 80, :INET, :STREAM,
+ Socket::IPPROTO_TCP, Socket::AI_CANONNAME)[0]
+
+ addr = Marshal.load(Marshal.dump(source))
+
+ addr.afamily.should == source.afamily
+ addr.pfamily.should == source.pfamily
+ addr.socktype.should == source.socktype
+ addr.protocol.should == source.protocol
+ addr.ip_address.should == source.ip_address
+ addr.ip_port.should == source.ip_port
+ addr.canonname.should == source.canonname
+ end
+ end
+
+ describe 'using a UNIX socket' do
+ it 'returns a new Addrinfo' do
+ source = Addrinfo.unix('foo')
+ addr = Marshal.load(Marshal.dump(source))
+
+ addr.afamily.should == source.afamily
+ addr.pfamily.should == source.pfamily
+ addr.socktype.should == source.socktype
+ addr.protocol.should == source.protocol
+ addr.unix_path.should == source.unix_path
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/pfamily_spec.rb b/spec/ruby/library/socket/addrinfo/pfamily_spec.rb
new file mode 100644
index 0000000000..da530b7fdc
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/pfamily_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#pfamily" do
+ it 'returns PF_UNSPEC as the default socket family' do
+ sockaddr = Socket.pack_sockaddr_in(80, 'localhost')
+
+ Addrinfo.new(sockaddr).pfamily.should == Socket::PF_UNSPEC
+ end
+
+ describe "for an ipv4 socket" do
+
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "returns Socket::PF_INET" do
+ @addrinfo.pfamily.should == Socket::PF_INET
+ end
+
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns Socket::PF_INET6" do
+ @addrinfo.pfamily.should == Socket::PF_INET6
+ end
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns Socket::PF_UNIX" do
+ @addrinfo.pfamily.should == Socket::PF_UNIX
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/protocol_spec.rb b/spec/ruby/library/socket/addrinfo/protocol_spec.rb
new file mode 100644
index 0000000000..f6ffc9acf9
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/protocol_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#protocol" do
+ it 'returns 0 by default' do
+ Addrinfo.ip('127.0.0.1').protocol.should == 0
+ end
+
+ it 'returns a custom protocol when given' do
+ Addrinfo.tcp('127.0.0.1', 80).protocol.should == Socket::IPPROTO_TCP
+ Addrinfo.tcp('::1', 80).protocol.should == Socket::IPPROTO_TCP
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns 0" do
+ @addrinfo.protocol.should == 0
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb b/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb
new file mode 100644
index 0000000000..70d6bfbbfe
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb
@@ -0,0 +1,47 @@
+describe :socket_addrinfo_to_sockaddr, shared: true do
+ describe "for an ipv4 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "returns a sockaddr packed structure" do
+ @addrinfo.send(@method).should == Socket.sockaddr_in(80, '127.0.0.1')
+ end
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns a sockaddr packed structure" do
+ @addrinfo.send(@method).should == Socket.sockaddr_in(80, '::1')
+ end
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns a sockaddr packed structure" do
+ @addrinfo.send(@method).should == Socket.sockaddr_un('/tmp/sock')
+ end
+ end
+
+ describe 'using a Addrinfo with just an IP address' do
+ it 'returns a String' do
+ addr = Addrinfo.ip('127.0.0.1')
+
+ addr.send(@method).should == Socket.sockaddr_in(0, '127.0.0.1')
+ end
+ end
+
+ describe 'using a Addrinfo without an IP and port' do
+ it 'returns a String' do
+ addr = Addrinfo.new(['AF_INET', 0, '', ''])
+
+ addr.send(@method).should == Socket.sockaddr_in(0, '')
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/socktype_spec.rb b/spec/ruby/library/socket/addrinfo/socktype_spec.rb
new file mode 100644
index 0000000000..e5f02cd759
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/socktype_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#socktype" do
+ it 'returns 0 by default' do
+ Addrinfo.ip('127.0.0.1').socktype.should == 0
+ end
+
+ it 'returns the socket type when given' do
+ Addrinfo.tcp('127.0.0.1', 80).socktype.should == Socket::SOCK_STREAM
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns Socket::SOCK_STREAM" do
+ @addrinfo.socktype.should == Socket::SOCK_STREAM
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/tcp_spec.rb b/spec/ruby/library/socket/addrinfo/tcp_spec.rb
new file mode 100644
index 0000000000..c74c9c21c2
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/tcp_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo.tcp' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ it 'returns an Addrinfo instance' do
+ Addrinfo.tcp(ip_address, 80).should be_an_instance_of(Addrinfo)
+ end
+
+ it 'sets the IP address' do
+ Addrinfo.tcp(ip_address, 80).ip_address.should == ip_address
+ end
+
+ it 'sets the port' do
+ Addrinfo.tcp(ip_address, 80).ip_port.should == 80
+ end
+
+ it 'sets the address family' do
+ Addrinfo.tcp(ip_address, 80).afamily.should == family
+ end
+
+ it 'sets the protocol family' do
+ Addrinfo.tcp(ip_address, 80).pfamily.should == family
+ end
+
+ it 'sets the socket type' do
+ Addrinfo.tcp(ip_address, 80).socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'sets the socket protocol' do
+ Addrinfo.tcp(ip_address, 80).protocol.should == Socket::IPPROTO_TCP
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/to_s_spec.rb b/spec/ruby/library/socket/addrinfo/to_s_spec.rb
new file mode 100644
index 0000000000..ddf994e051
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/to_s_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../spec_helper'
+require_relative 'shared/to_sockaddr'
+
+describe "Addrinfo#to_s" do
+ it_behaves_like :socket_addrinfo_to_sockaddr, :to_s
+end
diff --git a/spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb b/spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb
new file mode 100644
index 0000000000..b9f75454bd
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../spec_helper'
+require_relative 'shared/to_sockaddr'
+
+describe "Addrinfo#to_sockaddr" do
+ it_behaves_like :socket_addrinfo_to_sockaddr, :to_sockaddr
+end
diff --git a/spec/ruby/library/socket/addrinfo/udp_spec.rb b/spec/ruby/library/socket/addrinfo/udp_spec.rb
new file mode 100644
index 0000000000..ac02e76ef5
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/udp_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo.udp' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ it 'returns an Addrinfo instance' do
+ Addrinfo.udp(ip_address, 80).should be_an_instance_of(Addrinfo)
+ end
+
+ it 'sets the IP address' do
+ Addrinfo.udp(ip_address, 80).ip_address.should == ip_address
+ end
+
+ it 'sets the port' do
+ Addrinfo.udp(ip_address, 80).ip_port.should == 80
+ end
+
+ it 'sets the address family' do
+ Addrinfo.udp(ip_address, 80).afamily.should == family
+ end
+
+ it 'sets the protocol family' do
+ Addrinfo.udp(ip_address, 80).pfamily.should == family
+ end
+
+ it 'sets the socket type' do
+ Addrinfo.udp(ip_address, 80).socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'sets the socket protocol' do
+ Addrinfo.udp(ip_address, 80).protocol.should == Socket::IPPROTO_UDP
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/unix_path_spec.rb b/spec/ruby/library/socket/addrinfo/unix_path_spec.rb
new file mode 100644
index 0000000000..2a9076a354
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/unix_path_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+
+describe "Addrinfo#unix_path" do
+ describe "for an ipv4 socket" do
+
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "raises an exception" do
+ -> { @addrinfo.unix_path }.should raise_error(SocketError)
+ end
+
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "raises an exception" do
+ -> { @addrinfo.unix_path }.should raise_error(SocketError)
+ end
+ end
+
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns the socket path" do
+ @addrinfo.unix_path.should == "/tmp/sock"
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/unix_spec.rb b/spec/ruby/library/socket/addrinfo/unix_spec.rb
new file mode 100644
index 0000000000..7597533a76
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/unix_spec.rb
@@ -0,0 +1,69 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo.unix' do
+ it 'returns an Addrinfo instance' do
+ Addrinfo.unix('socket').should be_an_instance_of(Addrinfo)
+ end
+
+ it 'sets the IP address' do
+ Addrinfo.unix('socket').unix_path.should == 'socket'
+ end
+
+ it 'sets the address family' do
+ Addrinfo.unix('socket').afamily.should == Socket::AF_UNIX
+ end
+
+ it 'sets the protocol family' do
+ Addrinfo.unix('socket').pfamily.should == Socket::PF_UNIX
+ end
+
+ it 'sets the socket type' do
+ Addrinfo.unix('socket').socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'sets a custom socket type' do
+ addr = Addrinfo.unix('socket', Socket::SOCK_DGRAM)
+
+ addr.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'sets the socket protocol to 0' do
+ Addrinfo.unix('socket').protocol.should == 0
+ end
+end
+
+describe "Addrinfo#unix?" do
+ describe "for an ipv4 socket" do
+
+ before :each do
+ @addrinfo = Addrinfo.tcp("127.0.0.1", 80)
+ end
+
+ it "returns false" do
+ @addrinfo.unix?.should be_false
+ end
+
+ end
+
+ describe "for an ipv6 socket" do
+ before :each do
+ @addrinfo = Addrinfo.tcp("::1", 80)
+ end
+
+ it "returns false" do
+ @addrinfo.unix?.should be_false
+ end
+ end
+
+ platform_is_not :windows do
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
+
+ it "returns true" do
+ @addrinfo.unix?.should be_true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/cmsg_is_spec.rb b/spec/ruby/library/socket/ancillarydata/cmsg_is_spec.rb
new file mode 100644
index 0000000000..c54ee29825
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/cmsg_is_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#cmsg_is?' do
+ describe 'using :INET, :IP, :TTL as the family, level, and type' do
+ before do
+ @data = Socket::AncillaryData.new(:INET, :IP, :TTL, '')
+ end
+
+ it 'returns true when comparing with IPPROTO_IP and IP_TTL' do
+ @data.cmsg_is?(Socket::IPPROTO_IP, Socket::IP_TTL).should == true
+ end
+
+ it 'returns true when comparing with :IP and :TTL' do
+ @data.cmsg_is?(:IP, :TTL).should == true
+ end
+
+ with_feature :pktinfo do
+ it 'returns false when comparing with :IP and :PKTINFO' do
+ @data.cmsg_is?(:IP, :PKTINFO).should == false
+ end
+ end
+
+ it 'returns false when comparing with :SOCKET and :RIGHTS' do
+ @data.cmsg_is?(:SOCKET, :RIGHTS).should == false
+ end
+
+ it 'raises SocketError when comparing with :IPV6 and :RIGHTS' do
+ -> { @data.cmsg_is?(:IPV6, :RIGHTS) }.should raise_error(SocketError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/data_spec.rb b/spec/ruby/library/socket/ancillarydata/data_spec.rb
new file mode 100644
index 0000000000..5a1a446dd5
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/data_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#data' do
+ it 'returns the data as a String' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, 'ugh').data.should == 'ugh'
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/family_spec.rb b/spec/ruby/library/socket/ancillarydata/family_spec.rb
new file mode 100644
index 0000000000..975f0d2538
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/family_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#family' do
+ it 'returns the family as an Integer' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, '').family.should == Socket::AF_INET
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/initialize_spec.rb b/spec/ruby/library/socket/ancillarydata/initialize_spec.rb
new file mode 100644
index 0000000000..eca45599d7
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/initialize_spec.rb
@@ -0,0 +1,284 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#initialize' do
+ describe 'using Integers for the family, level, and type' do
+ before do
+ @data = Socket::AncillaryData
+ .new(Socket::AF_INET, Socket::IPPROTO_IP, Socket::IP_RECVTTL, 'ugh')
+ end
+
+ it 'sets the address family' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the message level' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the message type' do
+ @data.type.should == Socket::IP_RECVTTL
+ end
+
+ it 'sets the data' do
+ @data.data.should == 'ugh'
+ end
+ end
+
+ describe 'using Symbols for the family, level, and type' do
+ before do
+ @data = Socket::AncillaryData.new(:INET, :IPPROTO_IP, :RECVTTL, 'ugh')
+ end
+
+ it 'sets the address family' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the message level' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the message type' do
+ @data.type.should == Socket::IP_RECVTTL
+ end
+
+ it 'sets the data' do
+ @data.data.should == 'ugh'
+ end
+ end
+
+ describe 'using Strings for the family, level, and type' do
+ before do
+ @data = Socket::AncillaryData.new('INET', 'IPPROTO_IP', 'RECVTTL', 'ugh')
+ end
+
+ it 'sets the address family' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the message level' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the message type' do
+ @data.type.should == Socket::IP_RECVTTL
+ end
+
+ it 'sets the data' do
+ @data.data.should == 'ugh'
+ end
+ end
+
+ describe 'using custom objects with a to_str method for the family, level, and type' do
+ before do
+ fmock = mock(:family)
+ lmock = mock(:level)
+ tmock = mock(:type)
+ dmock = mock(:data)
+
+ fmock.stub!(:to_str).and_return('INET')
+ lmock.stub!(:to_str).and_return('IP')
+ tmock.stub!(:to_str).and_return('RECVTTL')
+ dmock.stub!(:to_str).and_return('ugh')
+
+ @data = Socket::AncillaryData.new(fmock, lmock, tmock, dmock)
+ end
+
+ it 'sets the address family' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the message level' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the message type' do
+ @data.type.should == Socket::IP_RECVTTL
+ end
+
+ it 'sets the data' do
+ @data.data.should == 'ugh'
+ end
+ end
+
+ describe 'using :AF_INET as the family and :SOCKET as the level' do
+ it 'sets the type to SCM_RIGHTS when using :RIGHTS as the type argument' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, '').type.should == Socket::SCM_RIGHTS
+ end
+
+ platform_is_not :aix do
+ it 'sets the type to SCM_TIMESTAMP when using :TIMESTAMP as the type argument' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :TIMESTAMP, '').type.should == Socket::SCM_TIMESTAMP
+ end
+ end
+
+ it 'raises TypeError when using a numeric string as the type argument' do
+ -> {
+ Socket::AncillaryData.new(:INET, :IGMP, Socket::SCM_RIGHTS.to_s, '')
+ }.should raise_error(TypeError)
+ end
+
+ it 'raises SocketError when using :RECVTTL as the type argument' do
+ -> {
+ Socket::AncillaryData.new(:INET, :SOCKET, :RECVTTL, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :MOO as the type argument' do
+ -> {
+ Socket::AncillaryData.new(:INET, :SOCKET, :MOO, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :IP_RECVTTL as the type argument' do
+ -> {
+ Socket::AncillaryData.new(:INET, :SOCKET, :IP_RECVTTL, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_INET as the family and :SOCKET as the level' do
+ it 'sets the type to SCM_RIGHTS when using :RIGHTS as the type argument' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, '').type.should == Socket::SCM_RIGHTS
+ end
+ end
+
+ describe 'using :AF_INET as the family and :IP as the level' do
+ it 'sets the type to IP_RECVTTL when using :RECVTTL as the type argument' do
+ Socket::AncillaryData.new(:INET, :IP, :RECVTTL, '').type.should == Socket::IP_RECVTTL
+ end
+
+ with_feature :ip_mtu do
+ it 'sets the type to IP_MTU when using :MTU as the type argument' do
+ Socket::AncillaryData.new(:INET, :IP, :MTU, '').type.should == Socket::IP_MTU
+ end
+ end
+
+ it 'raises SocketError when using :RIGHTS as the type argument' do
+ -> {
+ Socket::AncillaryData.new(:INET, :IP, :RIGHTS, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :MOO as the type argument' do
+ -> {
+ Socket::AncillaryData.new(:INET, :IP, :MOO, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_INET as the family and :IPV6 as the level' do
+ it 'sets the type to IPV6_CHECKSUM when using :CHECKSUM as the type argument' do
+ Socket::AncillaryData.new(:INET, :IPV6, :CHECKSUM, '').type.should == Socket::IPV6_CHECKSUM
+ end
+
+ with_feature :ipv6_nexthop do
+ it 'sets the type to IPV6_NEXTHOP when using :NEXTHOP as the type argument' do
+ Socket::AncillaryData.new(:INET, :IPV6, :NEXTHOP, '').type.should == Socket::IPV6_NEXTHOP
+ end
+ end
+
+ it 'raises SocketError when using :RIGHTS as the type argument' do
+ -> {
+ Socket::AncillaryData.new(:INET, :IPV6, :RIGHTS, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :MOO as the type argument' do
+ -> {
+ Socket::AncillaryData.new(:INET, :IPV6, :MOO, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_INET as the family and :TCP as the level' do
+ with_feature :tcp_cork do
+ it 'sets the type to TCP_CORK when using :CORK as the type argument' do
+ Socket::AncillaryData.new(:INET, :TCP, :CORK, '').type.should == Socket::TCP_CORK
+ end
+ end
+
+ with_feature :tcp_info do
+ it 'sets the type to TCP_INFO when using :INFO as the type argument' do
+ Socket::AncillaryData.new(:INET, :TCP, :INFO, '').type.should == Socket::TCP_INFO
+ end
+ end
+
+ it 'raises SocketError when using :RIGHTS as the type argument' do
+ -> {
+ Socket::AncillaryData.new(:INET, :TCP, :RIGHTS, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :MOO as the type argument' do
+ -> {
+ Socket::AncillaryData.new(:INET, :TCP, :MOO, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_INET as the family and :UDP as the level' do
+ with_feature :udp_cork do
+ it 'sets the type to UDP_CORK when using :CORK as the type argument' do
+ Socket::AncillaryData.new(:INET, :UDP, :CORK, '').type.should == Socket::UDP_CORK
+ end
+ end
+
+ it 'raises SocketError when using :RIGHTS as the type argument' do
+ -> {
+ Socket::AncillaryData.new(:INET, :UDP, :RIGHTS, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :MOO as the type argument' do
+ -> {
+ Socket::AncillaryData.new(:INET, :UDP, :MOO, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_UNIX as the family and :SOCKET as the level' do
+ it 'sets the type to SCM_RIGHTS when using :RIGHTS as the type argument' do
+ Socket::AncillaryData.new(:UNIX, :SOCKET, :RIGHTS, '').type.should == Socket::SCM_RIGHTS
+ end
+
+ it 'raises SocketError when using :CORK sa the type argument' do
+ -> {
+ Socket::AncillaryData.new(:UNIX, :SOCKET, :CORK, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_UNIX as the family and :IP as the level' do
+ it 'raises SocketError' do
+ -> {
+ Socket::AncillaryData.new(:UNIX, :IP, :RECVTTL, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_UNIX as the family and :IPV6 as the level' do
+ it 'raises SocketError' do
+ -> {
+ Socket::AncillaryData.new(:UNIX, :IPV6, :NEXTHOP, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_UNIX as the family and :TCP as the level' do
+ it 'raises SocketError' do
+ -> {
+ Socket::AncillaryData.new(:UNIX, :TCP, :CORK, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_UNIX as the family and :UDP as the level' do
+ it 'raises SocketError' do
+ -> {
+ Socket::AncillaryData.new(:UNIX, :UDP, :CORK, '')
+ }.should raise_error(SocketError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/int_spec.rb b/spec/ruby/library/socket/ancillarydata/int_spec.rb
new file mode 100644
index 0000000000..fe41a30a1a
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/int_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData.int' do
+ before do
+ @data = Socket::AncillaryData.int(:INET, :SOCKET, :RIGHTS, 4)
+ end
+
+ it 'returns a Socket::AncillaryData' do
+ @data.should be_an_instance_of(Socket::AncillaryData)
+ end
+
+ it 'sets the family to AF_INET' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the level SOL_SOCKET' do
+ @data.level.should == Socket::SOL_SOCKET
+ end
+
+ it 'sets the type SCM_RIGHTS' do
+ @data.type.should == Socket::SCM_RIGHTS
+ end
+
+ it 'sets the data to a packed String' do
+ @data.data.should == [4].pack('I')
+ end
+ end
+
+ describe 'Socket::AncillaryData#int' do
+ it 'returns the data as an Integer' do
+ data = Socket::AncillaryData.int(:UNIX, :SOCKET, :RIGHTS, 4)
+
+ data.int.should == 4
+ end
+
+ it 'raises when the data is not an Integer' do
+ data = Socket::AncillaryData.new(:UNIX, :SOCKET, :RIGHTS, 'ugh')
+
+ -> { data.int }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/ip_pktinfo_spec.rb b/spec/ruby/library/socket/ancillarydata/ip_pktinfo_spec.rb
new file mode 100644
index 0000000000..84910a038a
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/ip_pktinfo_spec.rb
@@ -0,0 +1,145 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data, :pktinfo do
+ describe 'Socket::AncillaryData.ip_pktinfo' do
+ describe 'with a source address and index' do
+ before do
+ @data = Socket::AncillaryData.ip_pktinfo(Addrinfo.ip('127.0.0.1'), 4)
+ end
+
+ it 'returns a Socket::AncillaryData' do
+ @data.should be_an_instance_of(Socket::AncillaryData)
+ end
+
+ it 'sets the family to AF_INET' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the level to IPPROTO_IP' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the type to IP_PKTINFO' do
+ @data.type.should == Socket::IP_PKTINFO
+ end
+ end
+
+ describe 'with a source address, index, and destination address' do
+ before do
+ source = Addrinfo.ip('127.0.0.1')
+ dest = Addrinfo.ip('127.0.0.5')
+ @data = Socket::AncillaryData.ip_pktinfo(source, 4, dest)
+ end
+
+ it 'returns a Socket::AncillaryData' do
+ @data.should be_an_instance_of(Socket::AncillaryData)
+ end
+
+ it 'sets the family to AF_INET' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the level to IPPROTO_IP' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the type to IP_PKTINFO' do
+ @data.type.should == Socket::IP_PKTINFO
+ end
+ end
+ end
+
+ describe 'Socket::AncillaryData#ip_pktinfo' do
+ describe 'using an Addrinfo without a port number' do
+ before do
+ @source = Addrinfo.ip('127.0.0.1')
+ @dest = Addrinfo.ip('127.0.0.5')
+ @data = Socket::AncillaryData.ip_pktinfo(@source, 4, @dest)
+ end
+
+ it 'returns an Array' do
+ @data.ip_pktinfo.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @info = @data.ip_pktinfo
+ end
+
+ it 'stores an Addrinfo at index 0' do
+ @info[0].should be_an_instance_of(Addrinfo)
+ end
+
+ it 'stores the ifindex at index 1' do
+ @info[1].should be_kind_of(Integer)
+ end
+
+ it 'stores an Addrinfo at index 2' do
+ @info[2].should be_an_instance_of(Addrinfo)
+ end
+ end
+
+ describe 'the source Addrinfo' do
+ before do
+ @addr = @data.ip_pktinfo[0]
+ end
+
+ it 'uses the correct IP address' do
+ @addr.ip_address.should == '127.0.0.1'
+ end
+
+ it 'is not the same object as the input Addrinfo' do
+ @addr.should_not equal @source
+ end
+ end
+
+ describe 'the ifindex' do
+ it 'is an Integer' do
+ @data.ip_pktinfo[1].should == 4
+ end
+ end
+
+ describe 'the destination Addrinfo' do
+ before do
+ @addr = @data.ip_pktinfo[2]
+ end
+
+ it 'uses the correct IP address' do
+ @addr.ip_address.should == '127.0.0.5'
+ end
+
+ it 'is not the same object as the input Addrinfo' do
+ @addr.should_not equal @dest
+ end
+ end
+ end
+
+ describe 'using an Addrinfo with a port number' do
+ before do
+ @source = Addrinfo.tcp('127.0.0.1', 80)
+ @dest = Addrinfo.tcp('127.0.0.5', 85)
+ @data = Socket::AncillaryData.ip_pktinfo(@source, 4, @dest)
+ end
+
+ describe 'the source Addrinfo' do
+ before do
+ @addr = @data.ip_pktinfo[0]
+ end
+
+ it 'does not contain a port number' do
+ @addr.ip_port.should == 0
+ end
+ end
+
+ describe 'the destination Addrinfo' do
+ before do
+ @addr = @data.ip_pktinfo[2]
+ end
+
+ it 'does not contain a port number' do
+ @addr.ip_port.should == 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_addr_spec.rb b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_addr_spec.rb
new file mode 100644
index 0000000000..f70fe27d6a
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_addr_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data, :ipv6_pktinfo do
+ describe 'Socket::AncillaryData#ipv6_pktinfo_addr' do
+ it 'returns an Addrinfo' do
+ data = Socket::AncillaryData.ipv6_pktinfo(Addrinfo.ip('::1'), 4)
+
+ data.ipv6_pktinfo_addr.should be_an_instance_of(Addrinfo)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_ifindex_spec.rb b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_ifindex_spec.rb
new file mode 100644
index 0000000000..bda37eec98
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_ifindex_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data, :ipv6_pktinfo do
+ describe 'Socket::AncillaryData#ipv6_pktinfo_ifindex' do
+ it 'returns an Addrinfo' do
+ data = Socket::AncillaryData.ipv6_pktinfo(Addrinfo.ip('::1'), 4)
+
+ data.ipv6_pktinfo_ifindex.should == 4
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_spec.rb b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_spec.rb
new file mode 100644
index 0000000000..0fffc720dc
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_spec.rb
@@ -0,0 +1,89 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data, :ipv6_pktinfo do
+ describe 'Socket::AncillaryData.ipv6_pktinfo' do
+ before do
+ @data = Socket::AncillaryData.ipv6_pktinfo(Addrinfo.ip('::1'), 4)
+ end
+
+ it 'returns a Socket::AncillaryData' do
+ @data.should be_an_instance_of(Socket::AncillaryData)
+ end
+
+ it 'sets the family to AF_INET' do
+ @data.family.should == Socket::AF_INET6
+ end
+
+ it 'sets the level to IPPROTO_IP' do
+ @data.level.should == Socket::IPPROTO_IPV6
+ end
+
+ it 'sets the type to IP_PKTINFO' do
+ @data.type.should == Socket::IPV6_PKTINFO
+ end
+ end
+
+ describe 'Socket::AncillaryData#ipv6_pktinfo' do
+ describe 'using an Addrinfo without a port number' do
+ before do
+ @source = Addrinfo.ip('::1')
+ @data = Socket::AncillaryData.ipv6_pktinfo(@source, 4)
+ end
+
+ it 'returns an Array' do
+ @data.ipv6_pktinfo.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @info = @data.ipv6_pktinfo
+ end
+
+ it 'stores an Addrinfo at index 0' do
+ @info[0].should be_an_instance_of(Addrinfo)
+ end
+
+ it 'stores the ifindex at index 1' do
+ @info[1].should be_kind_of(Integer)
+ end
+ end
+
+ describe 'the source Addrinfo' do
+ before do
+ @addr = @data.ipv6_pktinfo[0]
+ end
+
+ it 'uses the correct IP address' do
+ @addr.ip_address.should == '::1'
+ end
+
+ it 'is not the same object as the input Addrinfo' do
+ @addr.should_not equal @source
+ end
+ end
+
+ describe 'the ifindex' do
+ it 'is an Integer' do
+ @data.ipv6_pktinfo[1].should == 4
+ end
+ end
+ end
+
+ describe 'using an Addrinfo with a port number' do
+ before do
+ @source = Addrinfo.tcp('::1', 80)
+ @data = Socket::AncillaryData.ipv6_pktinfo(@source, 4)
+ end
+
+ describe 'the source Addrinfo' do
+ before do
+ @addr = @data.ipv6_pktinfo[0]
+ end
+
+ it 'does not contain a port number' do
+ @addr.ip_port.should == 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/level_spec.rb b/spec/ruby/library/socket/ancillarydata/level_spec.rb
new file mode 100644
index 0000000000..a2ff216f9d
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/level_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#level' do
+ it 'returns the level as an Integer' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, '').level.should == Socket::SOL_SOCKET
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/type_spec.rb b/spec/ruby/library/socket/ancillarydata/type_spec.rb
new file mode 100644
index 0000000000..972beeeca0
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/type_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#type' do
+ it 'returns the type as an Integer' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, '').type.should == Socket::SCM_RIGHTS
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/unix_rights_spec.rb b/spec/ruby/library/socket/ancillarydata/unix_rights_spec.rb
new file mode 100644
index 0000000000..95052fd91c
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/unix_rights_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData.unix_rights' do
+ describe 'using a list of IO objects' do
+ before do
+ @data = Socket::AncillaryData.unix_rights(STDOUT, STDERR)
+ end
+
+ it 'sets the family to AF_UNIX' do
+ @data.family.should == Socket::AF_UNIX
+ end
+
+ it 'sets the level to SOL_SOCKET' do
+ @data.level.should == Socket::SOL_SOCKET
+ end
+
+ it 'sets the type to SCM_RIGHTS' do
+ @data.type.should == Socket::SCM_RIGHTS
+ end
+
+ it 'sets the data to a String containing the file descriptors' do
+ @data.data.unpack('I*').should == [STDOUT.fileno, STDERR.fileno]
+ end
+ end
+
+ describe 'using non IO objects' do
+ it 'raises TypeError' do
+ -> { Socket::AncillaryData.unix_rights(10) }.should raise_error(TypeError)
+ end
+ end
+ end
+
+ describe 'Socket::AncillaryData#unix_rights' do
+ it 'returns the data as an Array of IO objects' do
+ data = Socket::AncillaryData.unix_rights(STDOUT, STDERR)
+
+ data.unix_rights.should == [STDOUT, STDERR]
+ end
+
+ it 'returns nil when the data is not a list of file descriptors' do
+ data = Socket::AncillaryData.new(:UNIX, :SOCKET, :RIGHTS, '')
+
+ data.unix_rights.should be_nil
+ end
+
+ it 'raises TypeError when the level is not SOL_SOCKET' do
+ data = Socket::AncillaryData.new(:INET, :IP, :RECVTTL, '')
+
+ -> { data.unix_rights }.should raise_error(TypeError)
+ end
+
+ platform_is_not :aix do
+ it 'raises TypeError when the type is not SCM_RIGHTS' do
+ data = Socket::AncillaryData.new(:INET, :SOCKET, :TIMESTAMP, '')
+
+ -> { data.unix_rights }.should raise_error(TypeError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/close_read_spec.rb b/spec/ruby/library/socket/basicsocket/close_read_spec.rb
new file mode 100644
index 0000000000..f317b34955
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/close_read_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::BasicSocket#close_read" do
+ before :each do
+ @server = TCPServer.new(0)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ end
+
+ it "closes the reading end of the socket" do
+ @server.close_read
+ -> { @server.read }.should raise_error(IOError)
+ end
+
+ it 'does not raise when called on a socket already closed for reading' do
+ @server.close_read
+ @server.close_read
+ -> { @server.read }.should raise_error(IOError)
+ end
+
+ it 'does not fully close the socket' do
+ @server.close_read
+ @server.closed?.should be_false
+ end
+
+ it "fully closes the socket if it was already closed for writing" do
+ @server.close_write
+ @server.close_read
+ @server.closed?.should be_true
+ end
+
+ it 'raises IOError when called on a fully closed socket' do
+ @server.close
+ -> { @server.close_read }.should raise_error(IOError)
+ end
+
+ it "returns nil" do
+ @server.close_read.should be_nil
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/close_write_spec.rb b/spec/ruby/library/socket/basicsocket/close_write_spec.rb
new file mode 100644
index 0000000000..232cfbb7c6
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/close_write_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::BasicSocket#close_write" do
+ before :each do
+ @server = TCPServer.new(0)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ end
+
+ it "closes the writing end of the socket" do
+ @server.close_write
+ -> { @server.write("foo") }.should raise_error(IOError)
+ end
+
+ it 'does not raise when called on a socket already closed for writing' do
+ @server.close_write
+ @server.close_write
+ -> { @server.write("foo") }.should raise_error(IOError)
+ end
+
+ it 'does not fully close the socket' do
+ @server.close_write
+ @server.closed?.should be_false
+ end
+
+ it "does not prevent reading" do
+ @server.close_write
+ @server.read(0).should == ""
+ end
+
+ it "fully closes the socket if it was already closed for reading" do
+ @server.close_read
+ @server.close_write
+ @server.closed?.should be_true
+ end
+
+ it 'raises IOError when called on a fully closed socket' do
+ @server.close
+ -> { @server.close_write }.should raise_error(IOError)
+ end
+
+ it "returns nil" do
+ @server.close_write.should be_nil
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/connect_address_spec.rb b/spec/ruby/library/socket/basicsocket/connect_address_spec.rb
new file mode 100644
index 0000000000..2e318fcb85
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/connect_address_spec.rb
@@ -0,0 +1,152 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket#connect_address' do
+ describe 'using an unbound socket' do
+ after do
+ @sock.close
+ end
+
+ it 'raises SocketError' do
+ @sock = Socket.new(:INET, :STREAM)
+
+ -> { @sock.connect_address }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using a socket bound to 0.0.0.0' do
+ before do
+ @sock = Socket.new(:INET, :STREAM)
+ @sock.bind(Socket.sockaddr_in(0, '0.0.0.0'))
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.connect_address.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'uses 127.0.0.1 as the IP address' do
+ @sock.connect_address.ip_address.should == '127.0.0.1'
+ end
+
+ it 'uses the correct port number' do
+ @sock.connect_address.ip_port.should > 0
+ end
+
+ it 'uses AF_INET as the address family' do
+ @sock.connect_address.afamily.should == Socket::AF_INET
+ end
+
+ it 'uses PF_INET as the address family' do
+ @sock.connect_address.pfamily.should == Socket::PF_INET
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.connect_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.connect_address.protocol.should == 0
+ end
+ end
+
+ guard -> { SocketSpecs.ipv6_available? } do
+ describe 'using a socket bound to ::' do
+ before do
+ @sock = Socket.new(:INET6, :STREAM)
+ @sock.bind(Socket.sockaddr_in(0, '::'))
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.connect_address.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'uses ::1 as the IP address' do
+ @sock.connect_address.ip_address.should == '::1'
+ end
+
+ it 'uses the correct port number' do
+ @sock.connect_address.ip_port.should > 0
+ end
+
+ it 'uses AF_INET6 as the address family' do
+ @sock.connect_address.afamily.should == Socket::AF_INET6
+ end
+
+ it 'uses PF_INET6 as the address family' do
+ @sock.connect_address.pfamily.should == Socket::PF_INET6
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.connect_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.connect_address.protocol.should == 0
+ end
+ end
+ end
+
+ platform_is_not :aix do
+ describe 'using an unbound UNIX socket' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @server.close
+ rm_r(@path)
+ end
+
+ it 'raises SocketError' do
+ -> { @client.connect_address }.should raise_error(SocketError)
+ end
+ end
+ end
+
+ describe 'using a bound UNIX socket' do
+ before do
+ @path = SocketSpecs.socket_path
+ @sock = UNIXServer.new(@path)
+ end
+
+ after do
+ @sock.close
+ rm_r(@path)
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.connect_address.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'uses the correct socket path' do
+ @sock.connect_address.unix_path.should == @path
+ end
+
+ it 'uses AF_UNIX as the address family' do
+ @sock.connect_address.afamily.should == Socket::AF_UNIX
+ end
+
+ it 'uses PF_UNIX as the protocol family' do
+ @sock.connect_address.pfamily.should == Socket::PF_UNIX
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.connect_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.connect_address.protocol.should == 0
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/do_not_reverse_lookup_spec.rb b/spec/ruby/library/socket/basicsocket/do_not_reverse_lookup_spec.rb
new file mode 100644
index 0000000000..a8800a8493
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/do_not_reverse_lookup_spec.rb
@@ -0,0 +1,103 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "BasicSocket.do_not_reverse_lookup" do
+ before :each do
+ @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup
+ @server = TCPServer.new('127.0.0.1', 0)
+ @port = @server.addr[1]
+ @socket = TCPSocket.new('127.0.0.1', @port)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ @socket.close unless @socket.closed?
+ BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup
+ end
+
+ it "defaults to true" do
+ BasicSocket.do_not_reverse_lookup.should be_true
+ end
+
+ it "causes 'peeraddr' to avoid name lookups" do
+ @socket.do_not_reverse_lookup = true
+ BasicSocket.do_not_reverse_lookup = true
+ @socket.peeraddr.should == ["AF_INET", @port, "127.0.0.1", "127.0.0.1"]
+ end
+
+ it "looks for hostnames when set to false" do
+ @socket.do_not_reverse_lookup = false
+ BasicSocket.do_not_reverse_lookup = false
+ @socket.peeraddr[2].should == SocketSpecs.hostname
+ end
+
+ it "looks for numeric addresses when set to true" do
+ @socket.do_not_reverse_lookup = true
+ BasicSocket.do_not_reverse_lookup = true
+ @socket.peeraddr[2].should == "127.0.0.1"
+ end
+end
+
+describe :socket_do_not_reverse_lookup, shared: true do
+ it "inherits from BasicSocket.do_not_reverse_lookup when the socket is created" do
+ @socket = @method.call
+ reverse = BasicSocket.do_not_reverse_lookup
+ @socket.do_not_reverse_lookup.should == reverse
+
+ BasicSocket.do_not_reverse_lookup = !reverse
+ @socket.do_not_reverse_lookup.should == reverse
+ end
+
+ it "is true when BasicSocket.do_not_reverse_lookup is true" do
+ BasicSocket.do_not_reverse_lookup = true
+ @socket = @method.call
+ @socket.do_not_reverse_lookup.should == true
+ end
+
+ it "is false when BasicSocket.do_not_reverse_lookup is false" do
+ BasicSocket.do_not_reverse_lookup = false
+ @socket = @method.call
+ @socket.do_not_reverse_lookup.should == false
+ end
+
+ it "can be changed with #do_not_reverse_lookup=" do
+ @socket = @method.call
+ reverse = @socket.do_not_reverse_lookup
+ @socket.do_not_reverse_lookup = !reverse
+ @socket.do_not_reverse_lookup.should == !reverse
+ end
+end
+
+describe "BasicSocket#do_not_reverse_lookup" do
+ before :each do
+ @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup
+ @server = TCPServer.new('127.0.0.1', 0)
+ @port = @server.addr[1]
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ @socket.close if @socket && !@socket.closed?
+ BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup
+ end
+
+ describe "for an TCPSocket.new socket" do
+ it_behaves_like :socket_do_not_reverse_lookup, -> {
+ TCPSocket.new('127.0.0.1', @port)
+ }
+ end
+
+ describe "for an TCPServer#accept socket" do
+ before :each do
+ @client = TCPSocket.new('127.0.0.1', @port)
+ end
+
+ after :each do
+ @client.close if @client && !@client.closed?
+ end
+
+ it_behaves_like :socket_do_not_reverse_lookup, -> {
+ @server.accept
+ }
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/for_fd_spec.rb b/spec/ruby/library/socket/basicsocket/for_fd_spec.rb
new file mode 100644
index 0000000000..9c9e6a8b55
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/for_fd_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "BasicSocket.for_fd" do
+ before :each do
+ @server = TCPServer.new(0)
+ @s2 = nil
+ end
+
+ after :each do
+ @socket1.close if @socket1
+ @server.close if @server
+ end
+
+ it "return a Socket instance wrapped around the descriptor" do
+ @s2 = TCPServer.for_fd(@server.fileno)
+ @s2.autoclose = false
+ @s2.should be_kind_of(TCPServer)
+ @s2.fileno.should == @server.fileno
+ end
+
+ it 'returns a new socket for a file descriptor' do
+ @socket1 = Socket.new(:INET, :DGRAM)
+ socket2 = Socket.for_fd(@socket1.fileno)
+ socket2.autoclose = false
+
+ socket2.should be_an_instance_of(Socket)
+ socket2.fileno.should == @socket1.fileno
+ end
+
+ it 'sets the socket into binary mode' do
+ @socket1 = Socket.new(:INET, :DGRAM)
+ socket2 = Socket.for_fd(@socket1.fileno)
+ socket2.autoclose = false
+
+ socket2.binmode?.should be_true
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/getpeereid_spec.rb b/spec/ruby/library/socket/basicsocket/getpeereid_spec.rb
new file mode 100644
index 0000000000..2e03cd3684
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/getpeereid_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'BasicSocket#getpeereid' do
+ platform_is_not :windows do
+ describe 'using a UNIXSocket' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @server.close
+
+ rm_r(@path)
+ end
+
+ it 'returns an Array with the user and group ID' do
+ @client.getpeereid.should == [Process.euid, Process.egid]
+ end
+ end
+ end
+
+ describe 'using an IPSocket' do
+ after do
+ @sock.close
+ end
+
+ it 'raises NoMethodError' do
+ @sock = TCPServer.new('127.0.0.1', 0)
+ -> { @sock.getpeereid }.should raise_error(NoMethodError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/getpeername_spec.rb b/spec/ruby/library/socket/basicsocket/getpeername_spec.rb
new file mode 100644
index 0000000000..0b93f02eef
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/getpeername_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::BasicSocket#getpeername" do
+
+ before :each do
+ @server = TCPServer.new("127.0.0.1", 0)
+ @port = @server.addr[1]
+ @client = TCPSocket.new("127.0.0.1", @port)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ @client.close unless @client.closed?
+ end
+
+ it "returns the sockaddr of the other end of the connection" do
+ server_sockaddr = Socket.pack_sockaddr_in(@port, "127.0.0.1")
+ @client.getpeername.should == server_sockaddr
+ end
+
+ it 'raises Errno::ENOTCONN for a disconnected socket' do
+ -> { @server.getpeername }.should raise_error(Errno::ENOTCONN)
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/getsockname_spec.rb b/spec/ruby/library/socket/basicsocket/getsockname_spec.rb
new file mode 100644
index 0000000000..b33db088b6
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/getsockname_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::BasicSocket#getsockname" do
+ after :each do
+ @socket.closed?.should be_false
+ @socket.close
+ end
+
+ it "returns the sockaddr associated with the socket" do
+ @socket = TCPServer.new("127.0.0.1", 0)
+ sockaddr = Socket.unpack_sockaddr_in(@socket.getsockname)
+ sockaddr.should == [@socket.addr[1], "127.0.0.1"]
+ end
+
+ it "works on sockets listening in ipaddr_any" do
+ @socket = TCPServer.new(0)
+ sockaddr = Socket.unpack_sockaddr_in(@socket.getsockname)
+ ["::", "0.0.0.0", "::ffff:0.0.0.0"].include?(sockaddr[1]).should be_true
+ sockaddr[0].should == @socket.addr[1]
+ end
+
+ it 'returns a default socket address for a disconnected socket' do
+ @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ sockaddr = Socket.unpack_sockaddr_in(@socket.getsockname)
+ sockaddr.should == [0, "0.0.0.0"]
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/getsockopt_spec.rb b/spec/ruby/library/socket/basicsocket/getsockopt_spec.rb
new file mode 100644
index 0000000000..ce65d6c92b
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/getsockopt_spec.rb
@@ -0,0 +1,188 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "BasicSocket#getsockopt" do
+ before :each do
+ @sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ end
+
+ after :each do
+ @sock.closed?.should be_false
+ @sock.close
+ end
+
+ platform_is_not :aix do
+ # A known bug in AIX. getsockopt(2) does not properly set
+ # the fifth argument for SO_TYPE, SO_OOBINLINE, SO_BROADCAST, etc.
+
+ it "gets a socket option Socket::SO_TYPE" do
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE).to_s
+ n.should == [Socket::SOCK_STREAM].pack("i")
+ end
+
+ it "gets a socket option Socket::SO_OOBINLINE" do
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should == [0].pack("i")
+ end
+ end
+
+ it "gets a socket option Socket::SO_LINGER" do
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER).to_s
+ if (n.size == 8) # linger struct on some platforms, not just a value
+ n.should == [0, 0].pack("ii")
+ else
+ n.should == [0].pack("i")
+ end
+ end
+
+ it "gets a socket option Socket::SO_SNDBUF" do
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s
+ n.unpack('i')[0].should > 0
+ end
+
+ it "raises a SystemCallError with an invalid socket option" do
+ -> { @sock.getsockopt Socket::SOL_SOCKET, -1 }.should raise_error(Errno::ENOPROTOOPT)
+ end
+
+ it 'returns a Socket::Option using a constant' do
+ opt = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE)
+
+ opt.should be_an_instance_of(Socket::Option)
+ end
+
+ it 'returns a Socket::Option for a boolean option' do
+ opt = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR)
+
+ opt.bool.should == false
+ end
+
+ it 'returns a Socket::Option for a numeric option' do
+ opt = @sock.getsockopt(Socket::IPPROTO_IP, Socket::IP_TTL)
+
+ opt.int.should be_kind_of(Integer)
+ end
+
+ it 'returns a Socket::Option for a struct option' do
+ opt = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER)
+
+ opt.linger.should == [false, 0]
+ end
+
+ it 'raises Errno::ENOPROTOOPT when requesting an invalid option' do
+ -> { @sock.getsockopt(Socket::SOL_SOCKET, -1) }.should raise_error(Errno::ENOPROTOOPT)
+ end
+
+ describe 'using Symbols as arguments' do
+ it 'returns a Socket::Option for arguments :SOCKET and :TYPE' do
+ opt = @sock.getsockopt(:SOCKET, :TYPE)
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_TYPE
+ end
+
+ it 'returns a Socket::Option for arguments :IP and :TTL' do
+ opt = @sock.getsockopt(:IP, :TTL)
+
+ opt.level.should == Socket::IPPROTO_IP
+ opt.optname.should == Socket::IP_TTL
+ end
+
+ it 'returns a Socket::Option for arguments :SOCKET and :REUSEADDR' do
+ opt = @sock.getsockopt(:SOCKET, :REUSEADDR)
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_REUSEADDR
+ end
+
+ it 'returns a Socket::Option for arguments :SOCKET and :LINGER' do
+ opt = @sock.getsockopt(:SOCKET, :LINGER)
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_LINGER
+ end
+
+ with_feature :udp_cork do
+ it 'returns a Socket::Option for arguments :UDP and :CORK' do
+ sock = Socket.new(:INET, :DGRAM)
+ begin
+ opt = sock.getsockopt(:UDP, :CORK)
+
+ opt.level.should == Socket::IPPROTO_UDP
+ opt.optname.should == Socket::UDP_CORK
+ ensure
+ sock.close
+ end
+ end
+ end
+ end
+
+ describe 'using Strings as arguments' do
+ it 'returns a Socket::Option for arguments "SOCKET" and "TYPE"' do
+ opt = @sock.getsockopt("SOCKET", "TYPE")
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_TYPE
+ end
+
+ it 'returns a Socket::Option for arguments "IP" and "TTL"' do
+ opt = @sock.getsockopt("IP", "TTL")
+
+ opt.level.should == Socket::IPPROTO_IP
+ opt.optname.should == Socket::IP_TTL
+ end
+
+ it 'returns a Socket::Option for arguments "SOCKET" and "REUSEADDR"' do
+ opt = @sock.getsockopt("SOCKET", "REUSEADDR")
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_REUSEADDR
+ end
+
+ it 'returns a Socket::Option for arguments "SOCKET" and "LINGER"' do
+ opt = @sock.getsockopt("SOCKET", "LINGER")
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_LINGER
+ end
+
+ with_feature :udp_cork do
+ it 'returns a Socket::Option for arguments "UDP" and "CORK"' do
+ sock = Socket.new("INET", "DGRAM")
+ begin
+ opt = sock.getsockopt("UDP", "CORK")
+
+ opt.level.should == Socket::IPPROTO_UDP
+ opt.optname.should == Socket::UDP_CORK
+ ensure
+ sock.close
+ end
+ end
+ end
+ end
+
+ describe 'using a String based option' do
+ it 'allows unpacking of a boolean option' do
+ opt = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR).to_s
+
+ opt.unpack('i').should == [0]
+ end
+
+ it 'allows unpacking of a numeric option' do
+ opt = @sock.getsockopt(Socket::IPPROTO_IP, Socket::IP_TTL).to_s
+ array = opt.unpack('i')
+
+ array[0].should be_kind_of(Integer)
+ array[0].should > 0
+ end
+
+ it 'allows unpacking of a struct option' do
+ opt = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER).to_s
+
+ if opt.bytesize == 8
+ opt.unpack('ii').should == [0, 0]
+ else
+ opt.unpack('i').should == [0]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/ioctl_spec.rb b/spec/ruby/library/socket/basicsocket/ioctl_spec.rb
new file mode 100644
index 0000000000..615d92bea8
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/ioctl_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../spec_helper'
+
+describe "Socket::BasicSocket#ioctl" do
+ platform_is :linux do
+ it "passes data from and to a String correctly" do
+ s = Socket.new Socket::AF_INET, Socket::SOCK_DGRAM, 0
+ # /usr/include/net/if.h, structure ifreq
+ # The structure is 32 bytes on x86, 40 bytes on x86_64
+ if_name = ['lo'].pack('a16')
+ buffer = if_name + 'z' * 24
+ # SIOCGIFADDR in /usr/include/bits/ioctls.h
+ s.ioctl 0x8915, buffer
+ s.close
+
+ # Interface name should remain unchanged.
+ buffer[0, 16].should == if_name
+ # lo should have an IPv4 address of 127.0.0.1
+ buffer[16, 2].unpack('S!').first.should == Socket::AF_INET
+ buffer[20, 4].should == "\x7f\0\0\x01"
+ end
+ end
+
+ platform_is :freebsd do
+ it "passes data from and to a String correctly" do
+ s = Socket.new Socket::AF_INET, Socket::SOCK_DGRAM, 0
+ # /usr/include/net/if.h, structure ifreq
+ # The structure is 32 bytes on x86, 40 bytes on x86_64
+ if_name = ['lo0'].pack('a16')
+ buffer = if_name + 'z' * 24
+ # SIOCGIFADDR in /usr/include/bits/ioctls.h
+ s.ioctl 0xc0206921, buffer
+ s.close
+
+ # Interface name should remain unchanged.
+ buffer[0, 16].should == if_name
+ # lo should have an IPv4 address of 127.0.0.1
+ buffer[16, 1].unpack('C').first.should == 16
+ buffer[17, 1].unpack('C').first.should == Socket::AF_INET
+ buffer[20, 4].should == "\x7f\0\0\x01"
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/local_address_spec.rb b/spec/ruby/library/socket/basicsocket/local_address_spec.rb
new file mode 100644
index 0000000000..0bd60a44cd
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/local_address_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../spec_helper'
+require_relative '../shared/address'
+
+describe 'BasicSocket#local_address' do
+ it_behaves_like :socket_local_remote_address, :local_address, -> socket {
+ a2 = BasicSocket.for_fd(socket.fileno)
+ a2.autoclose = false
+ a2.local_address
+ }
+end
diff --git a/spec/ruby/library/socket/basicsocket/read_nonblock_spec.rb b/spec/ruby/library/socket/basicsocket/read_nonblock_spec.rb
new file mode 100644
index 0000000000..ea5e65da5c
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/read_nonblock_spec.rb
@@ -0,0 +1,74 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "BasicSocket#read_nonblock" do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before :each do
+ @r = Socket.new(family, :DGRAM)
+ @w = Socket.new(family, :DGRAM)
+
+ @r.bind(Socket.pack_sockaddr_in(0, ip_address))
+ @w.send("aaa", 0, @r.getsockname)
+ end
+
+ after :each do
+ @r.close unless @r.closed?
+ @w.close unless @w.closed?
+ end
+
+ it "receives data after it's ready" do
+ IO.select([@r], nil, nil, 2)
+ @r.read_nonblock(5).should == "aaa"
+ end
+
+ platform_is_not :windows do
+ it 'returned data is binary encoded regardless of the external encoding' do
+ IO.select([@r], nil, nil, 2)
+ @r.read_nonblock(1).encoding.should == Encoding::BINARY
+
+ @w.send("bbb", 0, @r.getsockname)
+ @r.set_encoding(Encoding::ISO_8859_1)
+ IO.select([@r], nil, nil, 2)
+ buffer = @r.read_nonblock(3)
+ buffer.should == "bbb"
+ buffer.encoding.should == Encoding::BINARY
+ end
+ end
+
+ it 'replaces the content of the provided buffer without changing its encoding' do
+ buffer = "initial data".dup.force_encoding(Encoding::UTF_8)
+
+ IO.select([@r], nil, nil, 2)
+ @r.read_nonblock(3, buffer)
+ buffer.should == "aaa"
+ buffer.encoding.should == Encoding::UTF_8
+
+ @w.send("bbb", 0, @r.getsockname)
+ @r.set_encoding(Encoding::ISO_8859_1)
+ IO.select([@r], nil, nil, 2)
+ @r.read_nonblock(3, buffer)
+ buffer.should == "bbb"
+ buffer.encoding.should == Encoding::UTF_8
+ end
+
+ platform_is :linux do
+ it 'does not set the IO in nonblock mode' do
+ require 'io/nonblock'
+ @r.nonblock = false
+ IO.select([@r], nil, nil, 2)
+ @r.read_nonblock(3).should == "aaa"
+ @r.should_not.nonblock?
+ end
+ end
+
+ platform_is_not :linux, :windows do
+ it 'sets the IO in nonblock mode' do
+ require 'io/nonblock'
+ @r.nonblock = false
+ IO.select([@r], nil, nil, 2)
+ @r.read_nonblock(3).should == "aaa"
+ @r.should.nonblock?
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/read_spec.rb b/spec/ruby/library/socket/basicsocket/read_spec.rb
new file mode 100644
index 0000000000..ba9de7d5cf
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/read_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "BasicSocket#read" do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before :each do
+ @r = Socket.new(family, :DGRAM)
+ @w = Socket.new(family, :DGRAM)
+
+ @r.bind(Socket.pack_sockaddr_in(0, ip_address))
+ @w.send("aaa", 0, @r.getsockname)
+ end
+
+ after :each do
+ @r.close unless @r.closed?
+ @w.close unless @w.closed?
+ end
+
+ it "receives data after it's ready" do
+ @r.read(3).should == "aaa"
+ end
+
+ it 'returned data is binary encoded regardless of the external encoding' do
+ @r.read(3).encoding.should == Encoding::BINARY
+
+ @w.send("bbb", 0, @r.getsockname)
+ @r.set_encoding(Encoding::UTF_8)
+ buffer = @r.read(3)
+ buffer.should == "bbb"
+ buffer.encoding.should == Encoding::BINARY
+ end
+
+ it 'replaces the content of the provided buffer without changing its encoding' do
+ buffer = "initial data".dup.force_encoding(Encoding::UTF_8)
+
+ @r.read(3, buffer)
+ buffer.should == "aaa"
+ buffer.encoding.should == Encoding::UTF_8
+
+ @w.send("bbb", 0, @r.getsockname)
+ @r.set_encoding(Encoding::ISO_8859_1)
+ @r.read(3, buffer)
+ buffer.should == "bbb"
+ buffer.encoding.should == Encoding::UTF_8
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb b/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb
new file mode 100644
index 0000000000..f2a6682f12
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb
@@ -0,0 +1,172 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::BasicSocket#recv_nonblock" do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before :each do
+ @s1 = Socket.new(family, :DGRAM)
+ @s2 = Socket.new(family, :DGRAM)
+ end
+
+ after :each do
+ @s1.close unless @s1.closed?
+ @s2.close unless @s2.closed?
+ end
+
+ platform_is_not :windows do
+ describe 'using an unbound socket' do
+ it 'raises an exception extending IO::WaitReadable' do
+ -> { @s1.recv_nonblock(1) }.should raise_error(IO::WaitReadable)
+ end
+ end
+ end
+
+ it "raises an exception extending IO::WaitReadable if there's no data available" do
+ @s1.bind(Socket.pack_sockaddr_in(0, ip_address))
+ -> {
+ @s1.recv_nonblock(5)
+ }.should raise_error(IO::WaitReadable) { |e|
+ platform_is_not :windows do
+ e.should be_kind_of(Errno::EAGAIN)
+ end
+ platform_is :windows do
+ e.should be_kind_of(Errno::EWOULDBLOCK)
+ end
+ }
+ end
+
+ it "returns :wait_readable with exception: false" do
+ @s1.bind(Socket.pack_sockaddr_in(0, ip_address))
+ @s1.recv_nonblock(5, exception: false).should == :wait_readable
+ end
+
+ it "receives data after it's ready" do
+ @s1.bind(Socket.pack_sockaddr_in(0, ip_address))
+ @s2.send("aaa", 0, @s1.getsockname)
+ IO.select([@s1], nil, nil, 2)
+ @s1.recv_nonblock(5).should == "aaa"
+ end
+
+ it "allows an output buffer as third argument" do
+ @s1.bind(Socket.pack_sockaddr_in(0, ip_address))
+ @s2.send("data", 0, @s1.getsockname)
+ IO.select([@s1], nil, nil, 2)
+
+ buffer = +"foo"
+ @s1.recv_nonblock(5, 0, buffer).should.equal?(buffer)
+ buffer.should == "data"
+ end
+
+ it "preserves the encoding of the given buffer" do
+ @s1.bind(Socket.pack_sockaddr_in(0, ip_address))
+ @s2.send("data", 0, @s1.getsockname)
+ IO.select([@s1], nil, nil, 2)
+
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ @s1.recv_nonblock(5, 0, buffer)
+ buffer.encoding.should == Encoding::ISO_8859_1
+ end
+
+ it "does not block if there's no data available" do
+ @s1.bind(Socket.pack_sockaddr_in(0, ip_address))
+ @s2.send("a", 0, @s1.getsockname)
+ IO.select([@s1], nil, nil, 2)
+ @s1.recv_nonblock(1).should == "a"
+ -> {
+ @s1.recv_nonblock(5)
+ }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a connected but not bound socket' do
+ before do
+ @server = Socket.new(family, :STREAM)
+ end
+
+ after do
+ @server.close
+ end
+
+ it "raises Errno::ENOTCONN" do
+ -> { @server.recv_nonblock(1) }.should raise_error { |e|
+ [Errno::ENOTCONN, Errno::EINVAL].should.include?(e.class)
+ }
+ -> { @server.recv_nonblock(1, exception: false) }.should raise_error { |e|
+ [Errno::ENOTCONN, Errno::EINVAL].should.include?(e.class)
+ }
+ end
+ end
+ end
+end
+
+describe "Socket::BasicSocket#recv_nonblock" do
+ context "when recvfrom(2) returns 0 (if no messages are available to be received and the peer has performed an orderly shutdown)" do
+ describe "stream socket" do
+ before :each do
+ @server = TCPServer.new('127.0.0.1', 0)
+ @port = @server.addr[1]
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ end
+
+ ruby_version_is ""..."3.3" do
+ it "returns an empty String on a closed stream socket" do
+ ready = false
+
+ t = Thread.new do
+ client = @server.accept
+
+ Thread.pass while !ready
+ begin
+ client.recv_nonblock(10)
+ rescue IO::EAGAINWaitReadable
+ retry
+ end
+ ensure
+ client.close if client
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.close
+ ready = true
+
+ t.value.should == ""
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "returns nil on a closed stream socket" do
+ ready = false
+
+ t = Thread.new do
+ client = @server.accept
+
+ Thread.pass while !ready
+ begin
+ client.recv_nonblock(10)
+ rescue IO::EAGAINWaitReadable
+ retry
+ end
+ ensure
+ client.close if client
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.close
+ ready = true
+
+ t.value.should be_nil
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/recv_spec.rb b/spec/ruby/library/socket/basicsocket/recv_spec.rb
new file mode 100644
index 0000000000..a51920f52a
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/recv_spec.rb
@@ -0,0 +1,250 @@
+# encoding: binary
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "BasicSocket#recv" do
+
+ before :each do
+ @server = TCPServer.new('127.0.0.1', 0)
+ @port = @server.addr[1]
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ ScratchPad.clear
+ end
+
+ it "receives a specified number of bytes of a message from another socket" do
+ t = Thread.new do
+ client = @server.accept
+ ScratchPad.record client.recv(10)
+ client.recv(1) # this recv is important
+ client.close
+ end
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.send('hello', 0)
+ socket.close
+
+ t.join
+ ScratchPad.recorded.should == 'hello'
+ end
+
+ it "accepts flags to specify unusual receiving behaviour" do
+ t = Thread.new do
+ client = @server.accept
+
+ # in-band data (TCP), doesn't receive the flag.
+ ScratchPad.record client.recv(10)
+
+ # this recv is important (TODO: explain)
+ client.recv(10)
+ client.close
+ end
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.send('helloU', Socket::MSG_OOB)
+ socket.shutdown(1)
+ t.join
+ socket.close
+ ScratchPad.recorded.should == 'hello'
+ end
+
+ it "gets lines delimited with a custom separator" do
+ t = Thread.new do
+ client = @server.accept
+ ScratchPad.record client.gets("\377")
+
+ # this call is important (TODO: explain)
+ client.gets(nil)
+ client.close
+ end
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.write("firstline\377secondline\377")
+ socket.close
+
+ t.join
+ ScratchPad.recorded.should == "firstline\377"
+ end
+
+ it "allows an output buffer as third argument" do
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.write("data")
+
+ client = @server.accept
+ buffer = +"foo"
+ begin
+ client.recv(4, 0, buffer).should.equal?(buffer)
+ ensure
+ client.close
+ end
+ buffer.should == "data"
+
+ socket.close
+ end
+
+ it "preserves the encoding of the given buffer" do
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.write("data")
+
+ client = @server.accept
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ begin
+ client.recv(4, 0, buffer)
+ ensure
+ client.close
+ end
+ buffer.encoding.should == Encoding::ISO_8859_1
+
+ socket.close
+ end
+end
+
+describe 'BasicSocket#recv' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :DGRAM)
+ @client = Socket.new(family, :DGRAM)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'using an unbound socket' do
+ it 'blocks the caller' do
+ -> { @server.recv(4) }.should block_caller
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ describe 'without any data available' do
+ it 'blocks the caller' do
+ -> { @server.recv(4) }.should block_caller
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'reads the given amount of bytes' do
+ @client.write('hello')
+
+ @server.recv(2).should == 'he'
+ end
+
+ it 'reads the given amount of bytes when it exceeds the data size' do
+ @client.write('he')
+
+ @server.recv(6).should == 'he'
+ end
+
+ it 'blocks the caller when called twice without new data being available' do
+ @client.write('hello')
+
+ @server.recv(2).should == 'he'
+
+ -> { @server.recv(4) }.should block_caller
+ end
+
+ it 'takes a peek at the data when using the MSG_PEEK flag' do
+ @client.write('hello')
+
+ @server.recv(2, Socket::MSG_PEEK).should == 'he'
+ @server.recv(2).should == 'he'
+ end
+ end
+ end
+ end
+end
+
+describe "BasicSocket#recv" do
+ context "when recvfrom(2) returns 0 (if no messages are available to be received and the peer has performed an orderly shutdown)" do
+ describe "stream socket" do
+ before :each do
+ @server = TCPServer.new('127.0.0.1', 0)
+ @port = @server.addr[1]
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ end
+
+ ruby_version_is ""..."3.3" do
+ it "returns an empty String on a closed stream socket" do
+ t = Thread.new do
+ client = @server.accept
+ client.recv(10)
+ ensure
+ client.close if client
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.close
+
+ t.value.should == ""
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "returns nil on a closed stream socket" do
+ t = Thread.new do
+ client = @server.accept
+ client.recv(10)
+ ensure
+ client.close if client
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.close
+
+ t.value.should be_nil
+ end
+ end
+ end
+
+ describe "datagram socket" do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before :each do
+ @server = UDPSocket.new(family)
+ @client = UDPSocket.new(family)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ @client.close unless @client.closed?
+ end
+
+ it "returns empty String" do
+ @server.bind(ip_address, 0)
+ addr = @server.connect_address
+ @client.connect(addr.ip_address, addr.ip_port)
+
+ @client.send('', 0)
+
+ @server.recv(1).should == ""
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/recvmsg_nonblock_spec.rb b/spec/ruby/library/socket/basicsocket/recvmsg_nonblock_spec.rb
new file mode 100644
index 0000000000..b5fdd7c93b
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/recvmsg_nonblock_spec.rb
@@ -0,0 +1,300 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'BasicSocket#recvmsg_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a disconnected socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ describe 'using an unbound socket' do
+ it 'raises an exception extending IO::WaitReadable' do
+ -> { @server.recvmsg_nonblock }.should raise_error(IO::WaitReadable)
+ end
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ describe 'without any data available' do
+ it 'raises an exception extending IO::WaitReadable' do
+ -> { @server.recvmsg_nonblock }.should raise_error(IO::WaitReadable)
+ end
+
+ it 'returns :wait_readable with exception: false' do
+ @server.recvmsg_nonblock(exception: false).should == :wait_readable
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.connect(@server.getsockname)
+
+ @client.write('hello')
+
+ IO.select([@server], nil, nil, 5)
+ end
+
+ it 'returns an Array containing the data, an Addrinfo and the flags' do
+ @server.recvmsg_nonblock.should be_an_instance_of(Array)
+ end
+
+ describe 'without a maximum message length' do
+ it 'reads all the available data' do
+ @server.recvmsg_nonblock[0].should == 'hello'
+ end
+ end
+
+ describe 'with a maximum message length' do
+ platform_is_not :windows do
+ it 'reads up to the maximum amount of bytes' do
+ @server.recvmsg_nonblock(2)[0].should == 'he'
+ end
+ end
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @server.recvmsg_nonblock
+ end
+
+ it 'stores the message at index 0' do
+ @array[0].should == 'hello'
+ end
+
+ it 'stores an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+
+ platform_is_not :windows do
+ it 'stores the flags at index 2' do
+ @array[2].should be_kind_of(Integer)
+ end
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @array[1]
+ end
+
+ it 'uses the IP address of the client' do
+ @addr.ip_address.should == @client.local_address.ip_address
+ end
+
+ it 'uses the correct address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses the correct protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses the correct socket type' do
+ @addr.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses the port number of the client' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'using a connected but not bound socket' do
+ before do
+ @server = Socket.new(family, :STREAM)
+ end
+
+ after do
+ @server.close
+ end
+
+ it "raises Errno::ENOTCONN" do
+ -> { @server.recvmsg_nonblock }.should raise_error(Errno::ENOTCONN)
+ -> { @server.recvmsg_nonblock(exception: false) }.should raise_error(Errno::ENOTCONN)
+ end
+ end
+
+ describe 'using a connected socket' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without any data available' do
+ it 'raises IO::WaitReadable' do
+ -> {
+ socket, _ = @server.accept
+ begin
+ socket.recvmsg_nonblock
+ ensure
+ socket.close
+ end
+ }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+
+ @socket, _ = @server.accept
+ IO.select([@socket])
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'returns an Array containing the data, an Addrinfo and the flags' do
+ @socket.recvmsg_nonblock.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @socket.recvmsg_nonblock
+ end
+
+ it 'stores the message at index 0' do
+ @array[0].should == 'hello'
+ end
+
+ it 'stores an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+
+ it 'stores the flags at index 2' do
+ @array[2].should be_kind_of(Integer)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @array[1]
+ end
+
+ it 'raises when receiving the ip_address message' do
+ -> { @addr.ip_address }.should raise_error(SocketError)
+ end
+
+ it 'uses the correct address family' do
+ @addr.afamily.should == Socket::AF_UNSPEC
+ end
+
+ it 'uses 0 for the protocol family' do
+ @addr.pfamily.should == 0
+ end
+
+ it 'uses the correct socket type' do
+ @addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'raises when receiving the ip_port message' do
+ -> { @addr.ip_port }.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+describe 'BasicSocket#recvmsg_nonblock' do
+ context "when recvfrom(2) returns 0 (if no messages are available to be received and the peer has performed an orderly shutdown)" do
+ describe "stream socket" do
+ before :each do
+ @server = TCPServer.new('127.0.0.1', 0)
+ @port = @server.addr[1]
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ end
+
+ ruby_version_is ""..."3.3" do
+ platform_is_not :windows do # #recvmsg_nonblock() raises 'Errno::EINVAL: Invalid argument - recvmsg(2)'
+ it "returns an empty String as received data on a closed stream socket" do
+ ready = false
+
+ t = Thread.new do
+ client = @server.accept
+
+ Thread.pass while !ready
+ begin
+ client.recvmsg_nonblock(10)
+ rescue IO::EAGAINWaitReadable
+ retry
+ end
+ ensure
+ client.close if client
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.close
+ ready = true
+
+ t.value.should.is_a? Array
+ t.value[0].should == ""
+ end
+ end
+ end
+
+ ruby_version_is "3.3" do
+ platform_is_not :windows do
+ it "returns nil on a closed stream socket" do
+ ready = false
+
+ t = Thread.new do
+ client = @server.accept
+
+ Thread.pass while !ready
+ begin
+ client.recvmsg_nonblock(10)
+ rescue IO::EAGAINWaitReadable
+ retry
+ end
+ ensure
+ client.close if client
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.close
+ ready = true
+
+ t.value.should be_nil
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/recvmsg_spec.rb b/spec/ruby/library/socket/basicsocket/recvmsg_spec.rb
new file mode 100644
index 0000000000..04ba1d74c7
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/recvmsg_spec.rb
@@ -0,0 +1,281 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'BasicSocket#recvmsg' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a disconnected socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ describe 'using an unbound socket' do
+ it 'blocks the caller' do
+ -> { @server.recvmsg }.should block_caller
+ end
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ describe 'without any data available' do
+ it 'blocks the caller' do
+ -> { @server.recvmsg }.should block_caller
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.connect(@server.getsockname)
+
+ @client.write('hello')
+ end
+
+ it 'returns an Array containing the data, an Addrinfo and the flags' do
+ @server.recvmsg.should be_an_instance_of(Array)
+ end
+
+ describe 'without a maximum message length' do
+ it 'reads all the available data' do
+ @server.recvmsg[0].should == 'hello'
+ end
+ end
+
+ describe 'with a maximum message length' do
+ it 'reads up to the maximum amount of bytes' do
+ @server.recvmsg(2)[0].should == 'he'
+ end
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @server.recvmsg
+ end
+
+ it 'stores the message at index 0' do
+ @array[0].should == 'hello'
+ end
+
+ it 'stores an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+
+ platform_is_not :windows do
+ it 'stores the flags at index 2' do
+ @array[2].should be_kind_of(Integer)
+ end
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @array[1]
+ end
+
+ it 'uses the IP address of the client' do
+ @addr.ip_address.should == @client.local_address.ip_address
+ end
+
+ it 'uses the correct address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses the correct protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses the correct socket type' do
+ @addr.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses the port number of the client' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'using a connected socket' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without any data available' do
+ it 'blocks the caller' do
+ socket, _ = @server.accept
+ begin
+ -> { socket.recvmsg }.should block_caller
+ ensure
+ socket.close
+ end
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ @socket, _ = @server.accept
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'returns an Array containing the data, an Addrinfo and the flags' do
+ @socket.recvmsg.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @socket.recvmsg
+ end
+
+ it 'stores the message at index 0' do
+ @array[0].should == 'hello'
+ end
+
+ it 'stores an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+
+ it 'stores the flags at index 2' do
+ @array[2].should be_kind_of(Integer)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @array[1]
+ end
+
+ it 'raises when receiving the ip_address message' do
+ -> { @addr.ip_address }.should raise_error(SocketError)
+ end
+
+ it 'uses the correct address family' do
+ @addr.afamily.should == Socket::AF_UNSPEC
+ end
+
+ it 'returns 0 for the protocol family' do
+ @addr.pfamily.should == 0
+ end
+
+ it 'uses the correct socket type' do
+ @addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'raises when receiving the ip_port message' do
+ -> { @addr.ip_port }.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+describe 'BasicSocket#recvmsg' do
+ context "when recvfrom(2) returns 0 (if no messages are available to be received and the peer has performed an orderly shutdown)" do
+ describe "stream socket" do
+ before :each do
+ @server = TCPServer.new('127.0.0.1', 0)
+ @port = @server.addr[1]
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ end
+
+ ruby_version_is ""..."3.3" do
+ platform_is_not :windows do
+ it "returns an empty String as received data on a closed stream socket" do
+ t = Thread.new do
+ client = @server.accept
+ client.recvmsg(10)
+ ensure
+ client.close if client
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.close
+
+ t.value.should.is_a? Array
+ t.value[0].should == ""
+ end
+ end
+ end
+
+ ruby_version_is "3.3" do
+ platform_is_not :windows do
+ it "returns nil on a closed stream socket" do
+ t = Thread.new do
+ client = @server.accept
+ client.recvmsg(10)
+ ensure
+ client.close if client
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.close
+
+ t.value.should be_nil
+ end
+ end
+ end
+ end
+
+ describe "datagram socket" do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before :each do
+ @server = UDPSocket.new(family)
+ @client = UDPSocket.new(family)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ @client.close unless @client.closed?
+ end
+
+ it "returns an empty String as received data" do
+ @server.bind(ip_address, 0)
+ addr = @server.connect_address
+ @client.connect(addr.ip_address, addr.ip_port)
+
+ @client.send('', 0)
+ message = @server.recvmsg(1)
+
+ message.should.is_a? Array
+ message[0].should == ""
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/remote_address_spec.rb b/spec/ruby/library/socket/basicsocket/remote_address_spec.rb
new file mode 100644
index 0000000000..439bf31592
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/remote_address_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../spec_helper'
+require_relative '../shared/address'
+
+describe 'BasicSocket#remote_address' do
+ it_behaves_like :socket_local_remote_address, :remote_address, -> socket {
+ a2 = BasicSocket.for_fd(socket.fileno)
+ a2.autoclose = false
+ a2.remote_address
+ }
+end
diff --git a/spec/ruby/library/socket/basicsocket/send_spec.rb b/spec/ruby/library/socket/basicsocket/send_spec.rb
new file mode 100644
index 0000000000..25ba3f5655
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/send_spec.rb
@@ -0,0 +1,220 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "BasicSocket#send" do
+ before :each do
+ @server = TCPServer.new('127.0.0.1', 0)
+ @port = @server.addr[1]
+ @socket = TCPSocket.new('127.0.0.1', @port)
+ end
+
+ after :each do
+ @server.closed?.should be_false
+ @socket.closed?.should be_false
+
+ @server.close
+ @socket.close
+ end
+
+ it "sends a message to another socket and returns the number of bytes sent" do
+ data = +""
+ t = Thread.new do
+ client = @server.accept
+ loop do
+ got = client.recv(5)
+ break if got.nil? || got.empty?
+ data << got
+ end
+ client.close
+ end
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ @socket.send('hello', 0).should == 5
+ @socket.shutdown(1) # indicate, that we are done sending
+ @socket.recv(10)
+
+ t.join
+ data.should == 'hello'
+ end
+
+ platform_is_not :windows do
+ it "accepts flags to specify unusual sending behaviour" do
+ data = nil
+ peek_data = nil
+ t = Thread.new do
+ client = @server.accept
+ peek_data = client.recv(6, Socket::MSG_PEEK)
+ data = client.recv(6)
+ client.recv(10) # this recv is important
+ client.close
+ end
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ @socket.send('helloU', Socket::MSG_PEEK | Socket::MSG_OOB).should == 6
+ @socket.shutdown # indicate, that we are done sending
+
+ t.join
+ peek_data.should == "hello"
+ data.should == 'hello'
+ end
+ end
+
+ it "accepts a sockaddr as recipient address" do
+ data = +""
+ t = Thread.new do
+ client = @server.accept
+ loop do
+ got = client.recv(5)
+ break if got.nil? || got.empty?
+ data << got
+ end
+ client.close
+ end
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ sockaddr = Socket.pack_sockaddr_in(@port, "127.0.0.1")
+ @socket.send('hello', 0, sockaddr).should == 5
+ @socket.shutdown # indicate, that we are done sending
+
+ t.join
+ data.should == 'hello'
+ end
+end
+
+describe 'BasicSocket#send' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a disconnected socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'with an object implementing #to_str' do
+ it 'returns the amount of sent bytes' do
+ data = mock('message')
+ data.should_receive(:to_str).and_return('hello')
+ @client.send(data, 0, @server.getsockname).should == 5
+ end
+ end
+
+ describe 'without a destination address' do
+ it "raises #{SocketSpecs.dest_addr_req_error}" do
+ -> { @client.send('hello', 0) }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+
+ describe 'with a destination address as a String' do
+ it 'returns the amount of sent bytes' do
+ @client.send('hello', 0, @server.getsockname).should == 5
+ end
+
+ it 'does not persist the connection after writing to the socket' do
+ @client.send('hello', 0, @server.getsockname)
+
+ -> { @client.send('hello', 0) }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+
+ describe 'with a destination address as an Addrinfo' do
+ it 'returns the amount of sent bytes' do
+ @client.send('hello', 0, @server.connect_address).should == 5
+ end
+ end
+ end
+
+ describe 'using a connected UDP socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without a destination address argument' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'returns the amount of bytes written' do
+ @client.send('hello', 0).should == 5
+ end
+ end
+
+ describe 'with a destination address argument' do
+ before do
+ @alt_server = Socket.new(family, :DGRAM)
+
+ @alt_server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @alt_server.close
+ end
+
+ it 'sends the message to the given address instead' do
+ @client.send('hello', 0, @alt_server.getsockname).should == 5
+
+ -> { @server.recv(5) }.should block_caller
+
+ @alt_server.recv(5).should == 'hello'
+ end
+
+ it 'does not persist the alternative connection after writing to the socket' do
+ @client.send('hello', 0, @alt_server.getsockname)
+
+ @client.connect(@server.getsockname)
+ @client.send('world', 0)
+
+ @server.recv(5).should == 'world'
+ end
+ end
+ end
+
+ platform_is_not :darwin, :windows do
+ describe 'using a connected TCP socket' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'using the MSG_OOB flag' do
+ it 'sends an out-of-band message' do
+ socket, _ = @server.accept
+ socket.setsockopt(:SOCKET, :OOBINLINE, true)
+ @client.send('a', Socket::MSG_OOB).should == 1
+ begin
+ socket.recv(10).should == 'a'
+ ensure
+ socket.close
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/sendmsg_nonblock_spec.rb b/spec/ruby/library/socket/basicsocket/sendmsg_nonblock_spec.rb
new file mode 100644
index 0000000000..7acfc659bd
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/sendmsg_nonblock_spec.rb
@@ -0,0 +1,118 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'BasicSocket#sendmsg_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a disconnected socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without a destination address' do
+ it "raises #{SocketSpecs.dest_addr_req_error}" do
+ -> {
+ @client.sendmsg_nonblock('hello')
+ }.should raise_error(SocketSpecs.dest_addr_req_error)
+ -> {
+ @client.sendmsg_nonblock('hello', exception: false)
+ }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+
+ describe 'with a destination address as a String' do
+ it 'returns the amount of sent bytes' do
+ @client.sendmsg_nonblock('hello', 0, @server.getsockname).should == 5
+ end
+ end
+
+ describe 'with a destination address as an Addrinfo' do
+ it 'returns the amount of sent bytes' do
+ @client.sendmsg_nonblock('hello', 0, @server.connect_address).should == 5
+ end
+ end
+ end
+
+ describe 'using a connected UDP socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without a destination address argument' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'returns the amount of bytes written' do
+ @client.sendmsg_nonblock('hello').should == 5
+ end
+ end
+
+ describe 'with a destination address argument' do
+ before do
+ @alt_server = Socket.new(family, :DGRAM)
+ @alt_server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @alt_server.close
+ end
+
+ it 'sends the message to the given address instead' do
+ @client.sendmsg_nonblock('hello', 0, @alt_server.getsockname).should == 5
+ -> { @server.recv(5) }.should block_caller
+ @alt_server.recv(5).should == 'hello'
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'using a connected TCP socket' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'raises IO::WaitWritable when the underlying buffer is full' do
+ -> {
+ 10.times { @client.sendmsg_nonblock('hello' * 1_000_000) }
+ }.should raise_error(IO::WaitWritable)
+ end
+
+ it 'returns :wait_writable when the underlying buffer is full with exception: false' do
+ ret = nil
+ 10.times {
+ ret = @client.sendmsg_nonblock('hello' * 1_000_000, exception: false)
+ break unless ret.is_a?(Integer)
+ }
+ ret.should == :wait_writable
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/sendmsg_spec.rb b/spec/ruby/library/socket/basicsocket/sendmsg_spec.rb
new file mode 100644
index 0000000000..7ff336c0b7
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/sendmsg_spec.rb
@@ -0,0 +1,111 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'BasicSocket#sendmsg' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a disconnected socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ describe 'without a destination address' do
+ it "raises #{SocketSpecs.dest_addr_req_error}" do
+ -> { @client.sendmsg('hello') }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+ end
+
+ describe 'with a destination address as a String' do
+ it 'returns the amount of sent bytes' do
+ @client.sendmsg('hello', 0, @server.getsockname).should == 5
+ end
+ end
+
+ describe 'with a destination address as an Addrinfo' do
+ it 'returns the amount of sent bytes' do
+ @client.sendmsg('hello', 0, @server.connect_address).should == 5
+ end
+ end
+ end
+
+ describe 'using a connected UDP socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without a destination address argument' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'returns the amount of bytes written' do
+ @client.sendmsg('hello').should == 5
+ end
+ end
+
+ describe 'with a destination address argument' do
+ before do
+ @alt_server = Socket.new(family, :DGRAM)
+
+ @alt_server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @alt_server.close
+ end
+
+ it 'sends the message to the given address instead' do
+ @client.sendmsg('hello', 0, @alt_server.getsockname).should == 5
+
+ -> { @server.recv(5) }.should block_caller
+
+ @alt_server.recv(5).should == 'hello'
+ end
+ end
+ end
+
+ platform_is_not :windows do # spurious
+ describe 'using a connected TCP socket' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'blocks when the underlying buffer is full' do
+ # Buffer sizes may differ per platform, so sadly this is the only
+ # reliable way of testing blocking behaviour.
+ -> do
+ 10.times { @client.sendmsg('hello' * 1_000_000) }
+ end.should block_caller
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/setsockopt_spec.rb b/spec/ruby/library/socket/basicsocket/setsockopt_spec.rb
new file mode 100644
index 0000000000..f686e67326
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/setsockopt_spec.rb
@@ -0,0 +1,334 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "BasicSocket#setsockopt" do
+
+ before :each do
+ @sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ end
+
+ after :each do
+ @sock.close unless @sock.closed?
+ end
+
+ it "sets the socket linger to 0" do
+ linger = [0, 0].pack("ii")
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, linger).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER).to_s
+
+ if (n.size == 8) # linger struct on some platforms, not just a value
+ n.should == [0, 0].pack("ii")
+ else
+ n.should == [0].pack("i")
+ end
+ end
+
+ it "sets the socket linger to some positive value" do
+ linger = [64, 64].pack("ii")
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, linger).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER).to_s
+ if (n.size == 8) # linger struct on some platforms, not just a value
+ a = n.unpack('ii')
+ a[0].should_not == 0
+ a[1].should == 64
+ else
+ n.should == [64].pack("i")
+ end
+ end
+
+ platform_is_not :windows do
+ it "raises EINVAL if passed wrong linger value" do
+ -> do
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, 0)
+ end.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ platform_is_not :aix do
+ # A known bug in AIX. getsockopt(2) does not properly set
+ # the fifth argument for SO_TYPE, SO_OOBINLINE, SO_BROADCAST, etc.
+
+ it "sets the socket option Socket::SO_OOBINLINE" do
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, true).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should_not == [0].pack("i")
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, false).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should == [0].pack("i")
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 1).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should_not == [0].pack("i")
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 0).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should == [0].pack("i")
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 2).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should_not == [0].pack("i")
+
+ platform_is_not :windows do
+ -> {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "")
+ }.should raise_error(SystemCallError)
+ end
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "blah").should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should_not == [0].pack("i")
+
+ platform_is_not :windows do
+ -> {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "0")
+ }.should raise_error(SystemCallError)
+ end
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "\x00\x00\x00\x00").should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should == [0].pack("i")
+
+ platform_is_not :windows do
+ -> {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "1")
+ }.should raise_error(SystemCallError)
+ end
+
+ platform_is_not :windows do
+ -> {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "\x00\x00\x00")
+ }.should raise_error(SystemCallError)
+ end
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, [1].pack('i')).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should_not == [0].pack("i")
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, [0].pack('i')).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should == [0].pack("i")
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, [1000].pack('i')).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
+ n.should_not == [0].pack("i")
+ end
+ end
+
+ it "sets the socket option Socket::SO_SNDBUF" do
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, 4000).should == 0
+ sndbuf = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s
+ # might not always be possible to set to exact size
+ sndbuf.unpack('i')[0].should >= 4000
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, true).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s
+ n.unpack('i')[0].should >= 1
+
+ -> {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, nil).should == 0
+ }.should raise_error(TypeError)
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, 1).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s
+ n.unpack('i')[0].should >= 1
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, 2).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s
+ n.unpack('i')[0].should >= 2
+
+ -> {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "")
+ }.should raise_error(SystemCallError)
+
+ -> {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "bla")
+ }.should raise_error(SystemCallError)
+
+ -> {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "0")
+ }.should raise_error(SystemCallError)
+
+ -> {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "1")
+ }.should raise_error(SystemCallError)
+
+ -> {
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "\x00\x00\x00")
+ }.should raise_error(SystemCallError)
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "\x00\x00\x01\x00").should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s
+ n.unpack('i')[0].should >= "\x00\x00\x01\x00".unpack('i')[0]
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, [4000].pack('i')).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s
+ n.unpack('i')[0].should >= 4000
+
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, [1000].pack('i')).should == 0
+ n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).to_s
+ n.unpack('i')[0].should >= 1000
+ end
+
+ platform_is_not :aix do
+ describe 'accepts Socket::Option as argument' do
+ it 'boolean' do
+ option = Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true)
+ @sock.setsockopt(option).should == 0
+ @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE).bool.should == true
+ end
+
+ it 'int' do
+ option = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1)
+ @sock.setsockopt(option).should == 0
+ @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE).bool.should == true
+ end
+ end
+ end
+
+ platform_is :aix do
+ describe 'accepts Socket::Option as argument' do
+ it 'boolean' do
+ option = Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true)
+ @sock.setsockopt(option).should == 0
+ end
+
+ it 'int' do
+ option = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1)
+ @sock.setsockopt(option).should == 0
+ end
+ end
+ end
+
+ describe 'accepts Socket::Option as argument' do
+ it 'linger' do
+ option = Socket::Option.linger(true, 10)
+ @sock.setsockopt(option).should == 0
+ onoff, seconds = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER).linger
+ seconds.should == 10
+ # Both results can be produced depending on the OS and value of Socket::SO_LINGER
+ [true, Socket::SO_LINGER].should include(onoff)
+ end
+ end
+end
+
+describe 'BasicSocket#setsockopt' do
+ describe 'using a STREAM socket' do
+ before do
+ @socket = Socket.new(:INET, :STREAM)
+ end
+
+ after do
+ @socket.close
+ end
+
+ describe 'using separate arguments with Symbols' do
+ it 'raises TypeError when the first argument is nil' do
+ -> { @socket.setsockopt(nil, :REUSEADDR, true) }.should raise_error(TypeError)
+ end
+
+ it 'sets a boolean option' do
+ @socket.setsockopt(:SOCKET, :REUSEADDR, true).should == 0
+ @socket.getsockopt(:SOCKET, :REUSEADDR).bool.should == true
+ end
+
+ it 'sets an integer option' do
+ @socket.setsockopt(:IP, :TTL, 255).should == 0
+ @socket.getsockopt(:IP, :TTL).int.should == 255
+ end
+
+ guard -> { SocketSpecs.ipv6_available? } do
+ it 'sets an IPv6 boolean option' do
+ socket = Socket.new(:INET6, :STREAM)
+ begin
+ socket.setsockopt(:IPV6, :V6ONLY, true).should == 0
+ socket.getsockopt(:IPV6, :V6ONLY).bool.should == true
+ ensure
+ socket.close
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ it 'raises Errno::EINVAL when setting an invalid option value' do
+ -> { @socket.setsockopt(:SOCKET, :OOBINLINE, 'bla') }.should raise_error(Errno::EINVAL)
+ end
+ end
+ end
+
+ describe 'using separate arguments with Symbols' do
+ it 'sets a boolean option' do
+ @socket.setsockopt('SOCKET', 'REUSEADDR', true).should == 0
+ @socket.getsockopt(:SOCKET, :REUSEADDR).bool.should == true
+ end
+
+ it 'sets an integer option' do
+ @socket.setsockopt('IP', 'TTL', 255).should == 0
+ @socket.getsockopt(:IP, :TTL).int.should == 255
+ end
+ end
+
+ describe 'using separate arguments with constants' do
+ it 'sets a boolean option' do
+ @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true).should == 0
+ @socket.getsockopt(:SOCKET, :REUSEADDR).bool.should == true
+ end
+
+ it 'sets an integer option' do
+ @socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_TTL, 255).should == 0
+ @socket.getsockopt(:IP, :TTL).int.should == 255
+ end
+ end
+
+ describe 'using separate arguments with custom objects' do
+ it 'sets a boolean option' do
+ level = mock(:level)
+ name = mock(:name)
+
+ level.stub!(:to_str).and_return('SOCKET')
+ name.stub!(:to_str).and_return('REUSEADDR')
+
+ @socket.setsockopt(level, name, true).should == 0
+ end
+ end
+
+ describe 'using a Socket::Option as the first argument' do
+ it 'sets a boolean option' do
+ @socket.setsockopt(Socket::Option.bool(:INET, :SOCKET, :REUSEADDR, true)).should == 0
+ @socket.getsockopt(:SOCKET, :REUSEADDR).bool.should == true
+ end
+
+ it 'sets an integer option' do
+ @socket.setsockopt(Socket::Option.int(:INET, :IP, :TTL, 255)).should == 0
+ @socket.getsockopt(:IP, :TTL).int.should == 255
+ end
+
+ it 'raises ArgumentError when passing 2 arguments' do
+ option = Socket::Option.bool(:INET, :SOCKET, :REUSEADDR, true)
+ -> { @socket.setsockopt(option, :REUSEADDR) }.should raise_error(ArgumentError)
+ end
+
+ it 'raises TypeError when passing 3 arguments' do
+ option = Socket::Option.bool(:INET, :SOCKET, :REUSEADDR, true)
+ -> { @socket.setsockopt(option, :REUSEADDR, true) }.should raise_error(TypeError)
+ end
+ end
+ end
+
+ describe 'using a UNIX socket' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close
+ rm_r @path
+ end
+
+ it 'sets a boolean option' do
+ @server.setsockopt(:SOCKET, :REUSEADDR, true)
+ @server.getsockopt(:SOCKET, :REUSEADDR).bool.should == true
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/shutdown_spec.rb b/spec/ruby/library/socket/basicsocket/shutdown_spec.rb
new file mode 100644
index 0000000000..c78b32de38
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/shutdown_spec.rb
@@ -0,0 +1,155 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+platform_is_not :windows do # hangs
+ describe "Socket::BasicSocket#shutdown" do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM)
+ @client = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'using an Integer' do
+ it 'shuts down a socket for reading' do
+ @client.shutdown(Socket::SHUT_RD)
+
+ @client.recv(1).to_s.should be_empty
+ end
+
+ it 'shuts down a socket for writing' do
+ @client.shutdown(Socket::SHUT_WR)
+
+ -> { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'shuts down a socket for reading and writing' do
+ @client.shutdown(Socket::SHUT_RDWR)
+
+ @client.recv(1).to_s.should be_empty
+
+ -> { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'raises ArgumentError when using an invalid option' do
+ -> { @server.shutdown(666) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe 'using a Symbol' do
+ it 'shuts down a socket for reading using :RD' do
+ @client.shutdown(:RD)
+
+ @client.recv(1).to_s.should be_empty
+ end
+
+ it 'shuts down a socket for reading using :SHUT_RD' do
+ @client.shutdown(:SHUT_RD)
+
+ @client.recv(1).to_s.should be_empty
+ end
+
+ it 'shuts down a socket for writing using :WR' do
+ @client.shutdown(:WR)
+
+ -> { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'shuts down a socket for writing using :SHUT_WR' do
+ @client.shutdown(:SHUT_WR)
+
+ -> { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'shuts down a socket for reading and writing' do
+ @client.shutdown(:RDWR)
+
+ @client.recv(1).to_s.should be_empty
+
+ -> { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'raises ArgumentError when using an invalid option' do
+ -> { @server.shutdown(:Nope) }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using a String' do
+ it 'shuts down a socket for reading using "RD"' do
+ @client.shutdown('RD')
+
+ @client.recv(1).to_s.should be_empty
+ end
+
+ it 'shuts down a socket for reading using "SHUT_RD"' do
+ @client.shutdown('SHUT_RD')
+
+ @client.recv(1).to_s.should be_empty
+ end
+
+ it 'shuts down a socket for writing using "WR"' do
+ @client.shutdown('WR')
+
+ -> { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'shuts down a socket for writing using "SHUT_WR"' do
+ @client.shutdown('SHUT_WR')
+
+ -> { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'raises ArgumentError when using an invalid option' do
+ -> { @server.shutdown('Nope') }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using an object that responds to #to_str' do
+ before do
+ @dummy = mock(:dummy)
+ end
+
+ it 'shuts down a socket for reading using "RD"' do
+ @dummy.stub!(:to_str).and_return('RD')
+
+ @client.shutdown(@dummy)
+
+ @client.recv(1).to_s.should be_empty
+ end
+
+ it 'shuts down a socket for reading using "SHUT_RD"' do
+ @dummy.stub!(:to_str).and_return('SHUT_RD')
+
+ @client.shutdown(@dummy)
+
+ @client.recv(1).to_s.should be_empty
+ end
+
+ it 'shuts down a socket for reading and writing' do
+ @dummy.stub!(:to_str).and_return('RDWR')
+
+ @client.shutdown(@dummy)
+
+ @client.recv(1).to_s.should be_empty
+
+ -> { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+ end
+
+ describe 'using an object that does not respond to #to_str' do
+ it 'raises TypeError' do
+ -> { @server.shutdown(mock(:dummy)) }.should raise_error(TypeError)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/write_nonblock_spec.rb b/spec/ruby/library/socket/basicsocket/write_nonblock_spec.rb
new file mode 100644
index 0000000000..523e732959
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/write_nonblock_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "BasicSocket#write_nonblock" do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before :each do
+ @r = Socket.new(family, :DGRAM)
+ @w = Socket.new(family, :DGRAM)
+
+ @r.bind(Socket.pack_sockaddr_in(0, ip_address))
+ @w.connect(@r.getsockname)
+ end
+
+ after :each do
+ @r.close unless @r.closed?
+ @w.close unless @w.closed?
+ end
+
+ it "sends data" do
+ @w.write_nonblock("aaa").should == 3
+ IO.select([@r], nil, nil, 2)
+ @r.recv_nonblock(5).should == "aaa"
+ end
+
+ platform_is :linux do
+ it 'does not set the IO in nonblock mode' do
+ require 'io/nonblock'
+ @w.nonblock = false
+ @w.write_nonblock("aaa").should == 3
+ @w.should_not.nonblock?
+ end
+ end
+
+ platform_is_not :linux, :windows do
+ it 'sets the IO in nonblock mode' do
+ require 'io/nonblock'
+ @w.nonblock = false
+ @w.write_nonblock("aaa").should == 3
+ @w.should.nonblock?
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/constants/constants_spec.rb b/spec/ruby/library/socket/constants/constants_spec.rb
new file mode 100644
index 0000000000..b9a9d42725
--- /dev/null
+++ b/spec/ruby/library/socket/constants/constants_spec.rb
@@ -0,0 +1,108 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::Constants" do
+ it "defines socket types" do
+ consts = ["SOCK_DGRAM", "SOCK_RAW", "SOCK_RDM", "SOCK_SEQPACKET", "SOCK_STREAM"]
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+
+ it "defines protocol families" do
+ consts = ["PF_INET6", "PF_INET", "PF_UNIX", "PF_UNSPEC"]
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+
+ platform_is_not :aix do
+ it "defines PF_IPX protocol" do
+ Socket::Constants.should have_constant("PF_IPX")
+ end
+ end
+
+ it "defines address families" do
+ consts = ["AF_INET6", "AF_INET", "AF_UNIX", "AF_UNSPEC"]
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+
+ platform_is_not :aix do
+ it "defines AF_IPX address" do
+ Socket::Constants.should have_constant("AF_IPX")
+ end
+ end
+
+ it "defines send/receive options" do
+ consts = ["MSG_DONTROUTE", "MSG_OOB", "MSG_PEEK"]
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+
+ it "defines socket level options" do
+ consts = ["SOL_SOCKET"]
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+
+ it "defines socket options" do
+ consts = ["SO_BROADCAST", "SO_DEBUG", "SO_DONTROUTE", "SO_ERROR", "SO_KEEPALIVE", "SO_LINGER",
+ "SO_OOBINLINE", "SO_RCVBUF", "SO_REUSEADDR", "SO_SNDBUF", "SO_TYPE"]
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+
+ it "defines multicast options" do
+ consts = ["IP_ADD_MEMBERSHIP",
+ "IP_MULTICAST_LOOP", "IP_MULTICAST_TTL"]
+ platform_is_not :windows do
+ consts += ["IP_DEFAULT_MULTICAST_LOOP", "IP_DEFAULT_MULTICAST_TTL"]
+ end
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+
+ platform_is_not :windows, :aix, :android do
+ it "defines multicast options" do
+ consts = ["IP_MAX_MEMBERSHIPS"]
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+ end
+
+ it "defines TCP options" do
+ consts = ["TCP_NODELAY"]
+ platform_is_not :windows do
+ consts << "TCP_MAXSEG"
+ end
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+
+ platform_is_not :windows do
+ it 'defines SCM options' do
+ Socket::Constants.should have_constant('SCM_RIGHTS')
+ end
+
+ it 'defines error options' do
+ consts = ["EAI_ADDRFAMILY", "EAI_NODATA"]
+
+ # FreeBSD (11.1, at least) obsoletes EAI_ADDRFAMILY and EAI_NODATA
+ platform_is :freebsd do
+ consts = %w(EAI_MEMORY)
+ end
+
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/fixtures/classes.rb b/spec/ruby/library/socket/fixtures/classes.rb
new file mode 100644
index 0000000000..786629d2ef
--- /dev/null
+++ b/spec/ruby/library/socket/fixtures/classes.rb
@@ -0,0 +1,166 @@
+require 'socket'
+
+module SocketSpecs
+ # helper to get the hostname associated to 127.0.0.1 or the given ip
+ def self.hostname(ip = "127.0.0.1")
+ # Calculate each time, without caching, since the result might
+ # depend on things like do_not_reverse_lookup mode, which is
+ # changing from test to test
+ Socket.getaddrinfo(ip, nil)[0][2]
+ end
+
+ def self.hostname_reverse_lookup(ip = "127.0.0.1")
+ Socket.getaddrinfo(ip, nil, 0, 0, 0, 0, true)[0][2]
+ end
+
+ def self.addr(which=:ipv4)
+ case which
+ when :ipv4
+ host = "127.0.0.1"
+ when :ipv6
+ host = "::1"
+ end
+ Socket.getaddrinfo(host, nil)[0][3]
+ end
+
+ def self.reserved_unused_port
+ # https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
+ 0
+ end
+
+ def self.sockaddr_in(port, host)
+ Socket::SockAddr_In.new(Socket.sockaddr_in(port, host))
+ end
+
+ def self.socket_path
+ path = tmp("unix.sock", false)
+ # Check for too long unix socket path (max 104 bytes on macOS)
+ # Note that Linux accepts not null-terminated paths but the man page advises against it.
+ if path.bytesize > 104
+ # rm_r in spec/mspec/lib/mspec/helpers/fs.rb fails against
+ # "/tmp/unix_server_spec.socket"
+ skip "too long unix socket path: #{path}"
+ end
+ rm_socket(path)
+ path
+ end
+
+ def self.rm_socket(path)
+ File.delete(path) if File.exist?(path)
+ end
+
+ def self.ipv6_available?
+ @ipv6_available ||= begin
+ server = TCPServer.new('::1', 0)
+ rescue Errno::EAFNOSUPPORT, Errno::EADDRNOTAVAIL, SocketError
+ :no
+ else
+ server.close
+ :yes
+ end
+ @ipv6_available == :yes
+ end
+
+ def self.each_ip_protocol
+ describe 'using IPv4' do
+ yield Socket::AF_INET, '127.0.0.1', 'AF_INET'
+ end
+
+ guard -> { SocketSpecs.ipv6_available? } do
+ describe 'using IPv6' do
+ yield Socket::AF_INET6, '::1', 'AF_INET6'
+ end
+ end
+ end
+
+ def self.loop_with_timeout(timeout = TIME_TOLERANCE)
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+
+ while yield == :retry
+ if Process.clock_gettime(Process::CLOCK_MONOTONIC) - start >= timeout
+ raise RuntimeError, "Did not succeed within #{timeout} seconds"
+ end
+ end
+ end
+
+ def self.dest_addr_req_error
+ error = Errno::EDESTADDRREQ
+ platform_is :windows do
+ error = Errno::ENOTCONN
+ end
+ error
+ end
+
+ # TCPServer echo server accepting one connection
+ class SpecTCPServer
+ attr_reader :hostname, :port
+
+ def initialize
+ @hostname = SocketSpecs.hostname
+ @server = TCPServer.new @hostname, 0
+ @port = @server.addr[1]
+
+ log "SpecTCPServer starting on #{@hostname}:#{@port}"
+
+ @thread = Thread.new do
+ socket = @server.accept
+ log "SpecTCPServer accepted connection: #{socket}"
+ service socket
+ end
+ end
+
+ def service(socket)
+ begin
+ data = socket.recv(1024)
+
+ return if data.nil? || data.empty?
+ log "SpecTCPServer received: #{data.inspect}"
+
+ return if data == "QUIT"
+
+ socket.send data, 0
+ ensure
+ socket.close
+ end
+ end
+
+ def shutdown
+ log "SpecTCPServer shutting down"
+ @thread.join
+ @server.close
+ end
+
+ def log(message)
+ @logger.puts message if @logger
+ end
+ end
+
+ # We need to find a free port for Socket.tcp_server_loop and Socket.udp_server_loop,
+ # and the only reliable way to do that is to pass 0 as the port, but then we need to
+ # find out which one was chosen and the API doesn't let us find what it is. So we
+ # intercept one of the public API methods called by these methods.
+ class ServerLoopPortFinder < Socket
+ def self.tcp_server_sockets(*args)
+ super(*args) { |sockets|
+ @port = sockets.first.local_address.ip_port
+ yield(sockets)
+ }
+ end
+
+ def self.udp_server_sockets(*args, &block)
+ super(*args) { |sockets|
+ @port = sockets.first.local_address.ip_port
+ yield(sockets)
+ }
+ end
+
+ def self.cleanup
+ @port = nil
+ end
+
+ def self.port
+ sleep 0.001 until @port
+ @port
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/fixtures/send_io.txt b/spec/ruby/library/socket/fixtures/send_io.txt
new file mode 100644
index 0000000000..eaaa1eb3ec
--- /dev/null
+++ b/spec/ruby/library/socket/fixtures/send_io.txt
@@ -0,0 +1 @@
+This data is magic.
diff --git a/spec/ruby/library/socket/ipsocket/addr_spec.rb b/spec/ruby/library/socket/ipsocket/addr_spec.rb
new file mode 100644
index 0000000000..199eb85ab7
--- /dev/null
+++ b/spec/ruby/library/socket/ipsocket/addr_spec.rb
@@ -0,0 +1,105 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::IPSocket#addr" do
+ before :each do
+ @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup
+ @socket = TCPServer.new("127.0.0.1", 0)
+ end
+
+ after :each do
+ @socket.close unless @socket.closed?
+ BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup
+ end
+
+ it "returns an array with the socket's information" do
+ @socket.do_not_reverse_lookup = false
+ BasicSocket.do_not_reverse_lookup = false
+ addrinfo = @socket.addr
+ addrinfo[0].should == "AF_INET"
+ addrinfo[1].should be_kind_of(Integer)
+ addrinfo[2].should == SocketSpecs.hostname
+ addrinfo[3].should == "127.0.0.1"
+ end
+
+ it "returns an address in the array if do_not_reverse_lookup is true" do
+ @socket.do_not_reverse_lookup = true
+ BasicSocket.do_not_reverse_lookup = true
+ addrinfo = @socket.addr
+ addrinfo[0].should == "AF_INET"
+ addrinfo[1].should be_kind_of(Integer)
+ addrinfo[2].should == "127.0.0.1"
+ addrinfo[3].should == "127.0.0.1"
+ end
+
+ it "returns an address in the array if passed false" do
+ addrinfo = @socket.addr(false)
+ addrinfo[0].should == "AF_INET"
+ addrinfo[1].should be_kind_of(Integer)
+ addrinfo[2].should == "127.0.0.1"
+ addrinfo[3].should == "127.0.0.1"
+ end
+end
+
+describe 'Socket::IPSocket#addr' do
+ SocketSpecs.each_ip_protocol do |family, ip_address, family_name|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'without reverse lookups' do
+ before do
+ @hostname = Socket.getaddrinfo(ip_address, nil)[0][2]
+ end
+
+ it 'returns an Array containing address information' do
+ @server.addr.should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'with reverse lookups' do
+ before do
+ @hostname = Socket.getaddrinfo(ip_address, nil, nil, 0, 0, 0, true)[0][2]
+ end
+
+ describe 'using true as the argument' do
+ it 'returns an Array containing address information' do
+ @server.addr(true).should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'using :hostname as the argument' do
+ it 'returns an Array containing address information' do
+ @server.addr(:hostname).should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'using :cats as the argument' do
+ it 'raises ArgumentError' do
+ -> { @server.addr(:cats) }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe 'with do_not_reverse_lookup disabled on socket level' do
+ before do
+ @server.do_not_reverse_lookup = false
+
+ @hostname = Socket.getaddrinfo(ip_address, nil, nil, 0, 0, 0, true)[0][2]
+ end
+
+ after do
+ @server.do_not_reverse_lookup = true
+ end
+
+ it 'returns an Array containing address information' do
+ @server.addr.should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ipsocket/getaddress_spec.rb b/spec/ruby/library/socket/ipsocket/getaddress_spec.rb
new file mode 100644
index 0000000000..329f8267d3
--- /dev/null
+++ b/spec/ruby/library/socket/ipsocket/getaddress_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::IPSocket#getaddress" do
+ it "returns the IP address of hostname" do
+ addr_local = IPSocket.getaddress(SocketSpecs.hostname)
+ ["127.0.0.1", "::1"].include?(addr_local).should == true
+ end
+
+ it "returns the IP address when passed an IP" do
+ IPSocket.getaddress("127.0.0.1").should == "127.0.0.1"
+ IPSocket.getaddress("0.0.0.0").should == "0.0.0.0"
+ IPSocket.getaddress('::1').should == '::1'
+ end
+
+ it 'returns IPv4 compatible IPv6 addresses' do
+ IPSocket.getaddress('::ffff:192.168.1.1').should == '::ffff:192.168.1.1'
+ end
+
+ # There is no way to make this fail-proof on all machines, because
+ # DNS servers like opendns return A records for ANY host, including
+ # traditionally invalidly named ones.
+ it "raises an error on unknown hostnames" do
+ -> {
+ IPSocket.getaddress("rubyspecdoesntexist.ruby-lang.org")
+ }.should raise_error(SocketError)
+ end
+end
diff --git a/spec/ruby/library/socket/ipsocket/peeraddr_spec.rb b/spec/ruby/library/socket/ipsocket/peeraddr_spec.rb
new file mode 100644
index 0000000000..702650940b
--- /dev/null
+++ b/spec/ruby/library/socket/ipsocket/peeraddr_spec.rb
@@ -0,0 +1,117 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::IPSocket#peeraddr" do
+ before :each do
+ @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup
+ @server = TCPServer.new("127.0.0.1", 0)
+ @port = @server.addr[1]
+ @client = TCPSocket.new("127.0.0.1", @port)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ @client.close unless @client.closed?
+ BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup
+ end
+
+ it "raises error if socket is not connected" do
+ -> {
+ @server.peeraddr
+ }.should raise_error(Errno::ENOTCONN)
+ end
+
+ it "returns an array of information on the peer" do
+ @client.do_not_reverse_lookup = false
+ BasicSocket.do_not_reverse_lookup = false
+ addrinfo = @client.peeraddr
+ addrinfo[0].should == "AF_INET"
+ addrinfo[1].should == @port
+ addrinfo[2].should == SocketSpecs.hostname
+ addrinfo[3].should == "127.0.0.1"
+ end
+
+ it "returns an IP instead of hostname if do_not_reverse_lookup is true" do
+ @client.do_not_reverse_lookup = true
+ BasicSocket.do_not_reverse_lookup = true
+ addrinfo = @client.peeraddr
+ addrinfo[0].should == "AF_INET"
+ addrinfo[1].should == @port
+ addrinfo[2].should == "127.0.0.1"
+ addrinfo[3].should == "127.0.0.1"
+ end
+
+ it "returns an IP instead of hostname if passed false" do
+ addrinfo = @client.peeraddr(false)
+ addrinfo[0].should == "AF_INET"
+ addrinfo[1].should == @port
+ addrinfo[2].should == "127.0.0.1"
+ addrinfo[3].should == "127.0.0.1"
+ end
+end
+
+describe 'Socket::IPSocket#peeraddr' do
+ SocketSpecs.each_ip_protocol do |family, ip_address, family_name|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ @client = TCPSocket.new(ip_address, @port)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without reverse lookups' do
+ before do
+ @hostname = Socket.getaddrinfo(ip_address, nil)[0][2]
+ end
+
+ it 'returns an Array containing address information' do
+ @client.peeraddr.should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'with reverse lookups' do
+ before do
+ @hostname = Socket.getaddrinfo(ip_address, nil, nil, 0, 0, 0, true)[0][2]
+ end
+
+ describe 'using true as the argument' do
+ it 'returns an Array containing address information' do
+ @client.peeraddr(true).should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'using :hostname as the argument' do
+ it 'returns an Array containing address information' do
+ @client.peeraddr(:hostname).should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'using :cats as the argument' do
+ it 'raises ArgumentError' do
+ -> { @client.peeraddr(:cats) }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe 'with do_not_reverse_lookup disabled on socket level' do
+ before do
+ @client.do_not_reverse_lookup = false
+
+ @hostname = Socket.getaddrinfo(ip_address, nil, nil, 0, 0, 0, true)[0][2]
+ @port = @client.local_address.ip_port
+ end
+
+ after do
+ @client.do_not_reverse_lookup = true
+ end
+
+ it 'returns an Array containing address information' do
+ @client.addr.should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ipsocket/recvfrom_spec.rb b/spec/ruby/library/socket/ipsocket/recvfrom_spec.rb
new file mode 100644
index 0000000000..b58903df23
--- /dev/null
+++ b/spec/ruby/library/socket/ipsocket/recvfrom_spec.rb
@@ -0,0 +1,205 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::IPSocket#recvfrom" do
+ before :each do
+ @server = TCPServer.new("127.0.0.1", 0)
+ @port = @server.addr[1]
+ @client = TCPSocket.new("127.0.0.1", @port)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ @client.close unless @client.closed?
+ end
+
+ it "reads data from the connection" do
+ data = nil
+ t = Thread.new do
+ client = @server.accept
+ begin
+ data = client.recvfrom(6)
+ ensure
+ client.close
+ end
+ end
+
+ @client.send('hello', 0)
+ @client.shutdown rescue nil
+ # shutdown may raise Errno::ENOTCONN when sent data is pending.
+ t.join
+
+ data.first.should == 'hello'
+ end
+
+ it "reads up to len bytes" do
+ data = nil
+ t = Thread.new do
+ client = @server.accept
+ begin
+ data = client.recvfrom(3)
+ ensure
+ client.close
+ end
+ end
+
+ @client.send('hello', 0)
+ @client.shutdown rescue nil
+ t.join
+
+ data.first.should == 'hel'
+ end
+
+ it "returns an array with the data and connection info" do
+ data = nil
+ t = Thread.new do
+ client = @server.accept
+ data = client.recvfrom(3)
+ client.close
+ end
+
+ @client.send('hello', 0)
+ @client.shutdown rescue nil
+ t.join
+
+ data.size.should == 2
+ data.first.should == "hel"
+ # This does not apply to every platform, dependent on recvfrom(2)
+ # data.last.should == nil
+ end
+end
+
+describe "Socket::IPSocket#recvfrom" do
+ context "when recvfrom(2) returns 0 (if no messages are available to be received and the peer has performed an orderly shutdown)" do
+ describe "stream socket" do
+ before :each do
+ @server = TCPServer.new("127.0.0.1", 0)
+ port = @server.addr[1]
+ @client = TCPSocket.new("127.0.0.1", port)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ @client.close unless @client.closed?
+ end
+
+ ruby_version_is ""..."3.3" do
+ it "returns an empty String as received data on a closed stream socket" do
+ t = Thread.new do
+ client = @server.accept
+ message = client.recvfrom(10)
+ message
+ ensure
+ client.close if client
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ @client.close
+
+ t.value.should.is_a? Array
+ t.value[0].should == ""
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "returns nil on a closed stream socket" do
+ t = Thread.new do
+ client = @server.accept
+ message = client.recvfrom(10)
+ message
+ ensure
+ client.close if client
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ @client.close
+
+ t.value.should be_nil
+ end
+ end
+ end
+
+ describe "datagram socket" do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before :each do
+ @server = UDPSocket.new(family)
+ @client = UDPSocket.new(family)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ @client.close unless @client.closed?
+ end
+
+ it "returns an empty String as received data" do
+ @server.bind(ip_address, 0)
+ addr = @server.connect_address
+ @client.connect(addr.ip_address, addr.ip_port)
+
+ @client.send('', 0)
+ message = @server.recvfrom(1)
+
+ message.should.is_a? Array
+ message[0].should == ""
+ end
+ end
+ end
+ end
+end
+
+describe 'Socket::IPSocket#recvfrom' do
+ SocketSpecs.each_ip_protocol do |family, ip_address, family_name|
+ before do
+ @server = UDPSocket.new(family)
+ @client = UDPSocket.new(family)
+
+ @server.bind(ip_address, 0)
+ @client.connect(ip_address, @server.connect_address.ip_port)
+
+ @hostname = Socket.getaddrinfo(ip_address, nil)[0][2]
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns an Array containing up to N bytes and address information' do
+ @client.write('hello')
+
+ port = @client.local_address.ip_port
+ ret = @server.recvfrom(2)
+
+ ret.should == ['he', [family_name, port, @hostname, ip_address]]
+ end
+
+ it 'allows specifying of flags when receiving data' do
+ @client.write('hello')
+
+ @server.recvfrom(2, Socket::MSG_PEEK)[0].should == 'he'
+
+ @server.recvfrom(2)[0].should == 'he'
+ end
+
+ describe 'using reverse lookups' do
+ before do
+ @server.do_not_reverse_lookup = false
+
+ @hostname = Socket.getaddrinfo(ip_address, nil, 0, 0, 0, 0, true)[0][2]
+ end
+
+ it 'includes the hostname in the address Array' do
+ @client.write('hello')
+
+ port = @client.local_address.ip_port
+ ret = @server.recvfrom(2)
+
+ ret.should == ['he', [family_name, port, @hostname, ip_address]]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/option/bool_spec.rb b/spec/ruby/library/socket/option/bool_spec.rb
new file mode 100644
index 0000000000..144a78043d
--- /dev/null
+++ b/spec/ruby/library/socket/option/bool_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::Option.bool" do
+ it "creates a new Socket::Option" do
+ so = Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true)
+ so.should be_an_instance_of(Socket::Option)
+ so.family.should == Socket::AF_INET
+ so.level.should == Socket::SOL_SOCKET
+ so.optname.should == Socket::SO_KEEPALIVE
+ so.data.should == [1].pack('i')
+ end
+end
+
+describe "Socket::Option#bool" do
+ it "returns boolean value" do
+ Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true).bool.should == true
+ Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, false).bool.should == false
+ end
+
+ platform_is_not :windows do
+ it 'raises TypeError when called on a non boolean option' do
+ opt = Socket::Option.linger(1, 4)
+ -> { opt.bool }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/option/initialize_spec.rb b/spec/ruby/library/socket/option/initialize_spec.rb
new file mode 100644
index 0000000000..8071ad7ef0
--- /dev/null
+++ b/spec/ruby/library/socket/option/initialize_spec.rb
@@ -0,0 +1,83 @@
+require_relative '../spec_helper'
+
+describe 'Socket::Option#initialize' do
+ before do
+ @bool = [0].pack('i')
+ end
+
+ describe 'using Integers' do
+ it 'returns a Socket::Option' do
+ opt = Socket::Option
+ .new(Socket::AF_INET, Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, @bool)
+
+ opt.should be_an_instance_of(Socket::Option)
+
+ opt.family.should == Socket::AF_INET
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_KEEPALIVE
+ opt.data.should == @bool
+ end
+ end
+
+ describe 'using Symbols' do
+ it 'returns a Socket::Option' do
+ opt = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, @bool)
+
+ opt.should be_an_instance_of(Socket::Option)
+
+ opt.family.should == Socket::AF_INET
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_KEEPALIVE
+ opt.data.should == @bool
+ end
+
+ it 'raises when using an invalid address family' do
+ -> {
+ Socket::Option.new(:INET2, :SOCKET, :KEEPALIVE, @bool)
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises when using an invalid level' do
+ -> {
+ Socket::Option.new(:INET, :CATS, :KEEPALIVE, @bool)
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises when using an invalid option name' do
+ -> {
+ Socket::Option.new(:INET, :SOCKET, :CATS, @bool)
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using Strings' do
+ it 'returns a Socket::Option' do
+ opt = Socket::Option.new('INET', 'SOCKET', 'KEEPALIVE', @bool)
+
+ opt.should be_an_instance_of(Socket::Option)
+
+ opt.family.should == Socket::AF_INET
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_KEEPALIVE
+ opt.data.should == @bool
+ end
+
+ it 'raises when using an invalid address family' do
+ -> {
+ Socket::Option.new('INET2', 'SOCKET', 'KEEPALIVE', @bool)
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises when using an invalid level' do
+ -> {
+ Socket::Option.new('INET', 'CATS', 'KEEPALIVE', @bool)
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises when using an invalid option name' do
+ -> {
+ Socket::Option.new('INET', 'SOCKET', 'CATS', @bool)
+ }.should raise_error(SocketError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/option/inspect_spec.rb b/spec/ruby/library/socket/option/inspect_spec.rb
new file mode 100644
index 0000000000..ebea940d2f
--- /dev/null
+++ b/spec/ruby/library/socket/option/inspect_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+
+describe 'Socket::Option#inspect' do
+ it 'correctly returns SO_LINGER value' do
+ value = Socket::Option.linger(nil, 0).inspect
+ value.should == '#<Socket::Option: UNSPEC SOCKET LINGER off 0sec>'
+
+ value = Socket::Option.linger(false, 30).inspect
+ value.should == '#<Socket::Option: UNSPEC SOCKET LINGER off 30sec>'
+
+ value = Socket::Option.linger(true, 0).inspect
+ value.should == '#<Socket::Option: UNSPEC SOCKET LINGER on 0sec>'
+
+ value = Socket::Option.linger(true, 30).inspect
+ value.should == '#<Socket::Option: UNSPEC SOCKET LINGER on 30sec>'
+ end
+end
diff --git a/spec/ruby/library/socket/option/int_spec.rb b/spec/ruby/library/socket/option/int_spec.rb
new file mode 100644
index 0000000000..8c69ef6cbd
--- /dev/null
+++ b/spec/ruby/library/socket/option/int_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::Option.int" do
+ it "creates a new Socket::Option" do
+ so = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 5)
+ so.should be_an_instance_of(Socket::Option)
+ so.family.should == Socket::Constants::AF_INET
+ so.level.should == Socket::Constants::SOL_SOCKET
+ so.optname.should == Socket::Constants::SO_KEEPALIVE
+ so.data.should == [5].pack('i')
+ end
+
+ it 'returns a Socket::Option' do
+ opt = Socket::Option.int(:INET, :IP, :TTL, 4)
+
+ opt.should be_an_instance_of(Socket::Option)
+
+ opt.family.should == Socket::AF_INET
+ opt.level.should == Socket::IPPROTO_IP
+ opt.optname.should == Socket::IP_TTL
+ opt.data.should == [4].pack('i')
+ end
+end
+
+describe "Socket::Option#int" do
+ it "returns int value" do
+ so = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 17)
+ so.int.should == 17
+
+ so = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 32765)
+ so.int.should == 32765
+
+ Socket::Option.int(:INET, :IP, :TTL, 4).int.should == 4
+ end
+
+ platform_is_not :windows do
+ it 'raises TypeError when called on a non integer option' do
+ opt = Socket::Option.linger(1, 4)
+ -> { opt.int }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/option/linger_spec.rb b/spec/ruby/library/socket/option/linger_spec.rb
new file mode 100644
index 0000000000..ee987db85b
--- /dev/null
+++ b/spec/ruby/library/socket/option/linger_spec.rb
@@ -0,0 +1,76 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+option_pack = 'i*'
+platform_is :windows do
+ option_pack = 's*'
+end
+
+describe "Socket::Option.linger" do
+ it "creates a new Socket::Option for SO_LINGER" do
+ so = Socket::Option.linger(1, 10)
+ so.should be_an_instance_of(Socket::Option)
+
+ so.family.should == Socket::Constants::AF_UNSPEC
+ so.level.should == Socket::Constants::SOL_SOCKET
+ so.optname.should == Socket::Constants::SO_LINGER
+
+ so.data.should == [1, 10].pack(option_pack)
+ end
+
+ it "accepts boolean as onoff argument" do
+ so = Socket::Option.linger(false, 0)
+ so.data.should == [0, 0].pack(option_pack)
+
+ so = Socket::Option.linger(true, 1)
+ so.data.should == [1, 1].pack(option_pack)
+ end
+end
+
+describe "Socket::Option#linger" do
+ it "returns linger option" do
+ so = Socket::Option.linger(0, 5)
+ ary = so.linger
+ ary[0].should be_false
+ ary[1].should == 5
+
+ so = Socket::Option.linger(false, 4)
+ ary = so.linger
+ ary[0].should be_false
+ ary[1].should == 4
+
+ so = Socket::Option.linger(1, 10)
+ ary = so.linger
+ ary[0].should be_true
+ ary[1].should == 10
+
+ so = Socket::Option.linger(true, 9)
+ ary = so.linger
+ ary[0].should be_true
+ ary[1].should == 9
+ end
+
+ it "raises TypeError if not a SO_LINGER" do
+ so = Socket::Option.int(:AF_UNSPEC, :SOL_SOCKET, :KEEPALIVE, 1)
+ -> { so.linger }.should raise_error(TypeError)
+ end
+
+ it 'raises TypeError when called on a non SOL_SOCKET/SO_LINGER option' do
+ opt = Socket::Option.int(:INET, :IP, :TTL, 4)
+
+ -> { opt.linger }.should raise_error(TypeError)
+ end
+
+ platform_is_not :windows do
+ it "raises TypeError if option has not good size" do
+ so = Socket::Option.int(:AF_UNSPEC, :SOL_SOCKET, :LINGER, 1)
+ -> { so.linger }.should raise_error(TypeError)
+ end
+ end
+
+ it 'raises TypeError when called on a non linger option' do
+ opt = Socket::Option.new(:INET, :SOCKET, :LINGER, '')
+
+ -> { opt.linger }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/socket/option/new_spec.rb b/spec/ruby/library/socket/option/new_spec.rb
new file mode 100644
index 0000000000..a9e6f09097
--- /dev/null
+++ b/spec/ruby/library/socket/option/new_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::Option.new" do
+ it "should accept integers" do
+ so = Socket::Option.new(Socket::AF_INET, Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, [0].pack('i'))
+ so.family.should == Socket::AF_INET
+ so.level.should == Socket::SOL_SOCKET
+ so.optname.should == Socket::SO_KEEPALIVE
+ end
+
+ it "should accept symbols" do
+ so = Socket::Option.new(:AF_INET, :SOL_SOCKET, :SO_KEEPALIVE, [0].pack('i'))
+ so.family.should == Socket::AF_INET
+ so.level.should == Socket::SOL_SOCKET
+ so.optname.should == Socket::SO_KEEPALIVE
+
+ so = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [0].pack('i'))
+ so.family.should == Socket::AF_INET
+ so.level.should == Socket::SOL_SOCKET
+ so.optname.should == Socket::SO_KEEPALIVE
+ end
+
+ it "should raise error on unknown family" do
+ -> { Socket::Option.new(:INET4, :SOCKET, :KEEPALIVE, [0].pack('i')) }.should raise_error(SocketError)
+ end
+
+ it "should raise error on unknown level" do
+ -> { Socket::Option.new(:INET, :ROCKET, :KEEPALIVE, [0].pack('i')) }.should raise_error(SocketError)
+ end
+
+ it "should raise error on unknown option name" do
+ -> { Socket::Option.new(:INET, :SOCKET, :ALIVE, [0].pack('i')) }.should raise_error(SocketError)
+ end
+end
diff --git a/spec/ruby/library/socket/shared/address.rb b/spec/ruby/library/socket/shared/address.rb
new file mode 100644
index 0000000000..49ba17c400
--- /dev/null
+++ b/spec/ruby/library/socket/shared/address.rb
@@ -0,0 +1,259 @@
+require_relative '../fixtures/classes'
+
+describe :socket_local_remote_address, shared: true do
+ describe 'using TCPSocket' do
+ before :each do
+ @s = TCPServer.new('127.0.0.1', 0)
+ @a = TCPSocket.new('127.0.0.1', @s.addr[1])
+ @b = @s.accept
+ @addr = @object.call(@a)
+ end
+
+ after :each do
+ [@b, @a, @s].each(&:close)
+ end
+
+ it 'uses AF_INET as the address family' do
+ @addr.afamily.should == Socket::AF_INET
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @addr.pfamily.should == Socket::PF_INET
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses the correct IP address' do
+ @addr.ip_address.should == '127.0.0.1'
+ end
+
+ it 'uses the correct port' do
+ if @method == :local_address
+ @addr.ip_port.should != @s.addr[1]
+ else
+ @addr.ip_port.should == @s.addr[1]
+ end
+ end
+
+ it 'equals address of peer socket' do
+ if @method == :local_address
+ @addr.to_s.should == @b.remote_address.to_s
+ else
+ @addr.to_s.should == @b.local_address.to_s
+ end
+ end
+
+ it 'returns an Addrinfo' do
+ @addr.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'uses 0 as the protocol' do
+ @addr.protocol.should == 0
+ end
+
+ it 'can be used to connect to the server' do
+ skip if @method == :local_address
+ b = @addr.connect
+ begin
+ b.remote_address.to_s.should == @addr.to_s
+ ensure
+ b.close
+ end
+ end
+ end
+
+ guard -> { SocketSpecs.ipv6_available? } do
+ describe 'using IPv6' do
+ before :each do
+ @s = TCPServer.new('::1', 0)
+ @a = TCPSocket.new('::1', @s.addr[1])
+ @b = @s.accept
+ @addr = @object.call(@a)
+ end
+
+ after :each do
+ [@b, @a, @s].each(&:close)
+ end
+
+ it 'uses AF_INET6 as the address family' do
+ @addr.afamily.should == Socket::AF_INET6
+ end
+
+ it 'uses PF_INET6 as the protocol family' do
+ @addr.pfamily.should == Socket::PF_INET6
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses the correct IP address' do
+ @addr.ip_address.should == '::1'
+ end
+
+ it 'uses the correct port' do
+ if @method == :local_address
+ @addr.ip_port.should != @s.addr[1]
+ else
+ @addr.ip_port.should == @s.addr[1]
+ end
+ end
+
+ it 'equals address of peer socket' do
+ if @method == :local_address
+ @addr.to_s.should == @b.remote_address.to_s
+ else
+ @addr.to_s.should == @b.local_address.to_s
+ end
+ end
+
+ it 'returns an Addrinfo' do
+ @addr.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'uses 0 as the protocol' do
+ @addr.protocol.should == 0
+ end
+
+ it 'can be used to connect to the server' do
+ skip if @method == :local_address
+ b = @addr.connect
+ begin
+ b.remote_address.to_s.should == @addr.to_s
+ ensure
+ b.close
+ end
+ end
+ end
+ end
+
+ describe 'using UNIXSocket' do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @s = UNIXServer.new(@path)
+ @a = UNIXSocket.new(@path)
+ @b = @s.accept
+ @addr = @object.call(@a)
+ end
+
+ after :each do
+ [@b, @a, @s].each(&:close)
+ rm_r(@path)
+ end
+
+ it 'uses AF_UNIX as the address family' do
+ @addr.afamily.should == Socket::AF_UNIX
+ end
+
+ it 'uses PF_UNIX as the protocol family' do
+ @addr.pfamily.should == Socket::PF_UNIX
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses the correct socket path' do
+ if @method == :local_address
+ @addr.unix_path.should == ""
+ else
+ @addr.unix_path.should == @path
+ end
+ end
+
+ platform_is_not :windows do
+ it 'equals address of peer socket' do
+ if @method == :local_address
+ @addr.to_s.should == @b.remote_address.to_s
+ else
+ @addr.to_s.should == @b.local_address.to_s
+ end
+ end
+ end
+
+ guard -> { platform_is :windows and ruby_bug "#21702", ""..."4.2" } do
+ it 'equals address of peer socket' do
+ if @method == :local_address
+ @addr.to_s.should == @b.remote_address.to_s
+ else
+ @addr.to_s.should == @b.local_address.to_s
+ end
+ end
+ end
+
+ it 'returns an Addrinfo' do
+ @addr.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'uses 0 as the protocol' do
+ @addr.protocol.should == 0
+ end
+
+ it 'can be used to connect to the server' do
+ skip if @method == :local_address
+ b = @addr.connect
+ begin
+ b.remote_address.to_s.should == @addr.to_s
+ ensure
+ b.close
+ end
+ end
+ end
+
+ describe 'using UDPSocket' do
+ before :each do
+ @s = UDPSocket.new
+ @s.bind("127.0.0.1", 0)
+ @a = UDPSocket.new
+ @a.connect("127.0.0.1", @s.addr[1])
+ @addr = @object.call(@a)
+ end
+
+ after :each do
+ [@a, @s].each(&:close)
+ end
+
+ it 'uses the correct address family' do
+ @addr.afamily.should == Socket::AF_INET
+ end
+
+ it 'uses the correct protocol family' do
+ @addr.pfamily.should == Socket::PF_INET
+ end
+
+ it 'uses SOCK_DGRAM as the socket type' do
+ @addr.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses the correct IP address' do
+ @addr.ip_address.should == '127.0.0.1'
+ end
+
+ it 'uses the correct port' do
+ if @method == :local_address
+ @addr.ip_port.should != @s.addr[1]
+ else
+ @addr.ip_port.should == @s.addr[1]
+ end
+ end
+
+ it 'returns an Addrinfo' do
+ @addr.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'uses 0 as the protocol' do
+ @addr.protocol.should == 0
+ end
+
+ it 'can be used to connect to the peer' do
+ b = @addr.connect
+ begin
+ b.remote_address.to_s.should == @addr.to_s
+ ensure
+ b.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/shared/pack_sockaddr.rb b/spec/ruby/library/socket/shared/pack_sockaddr.rb
new file mode 100644
index 0000000000..4bfcf4edb9
--- /dev/null
+++ b/spec/ruby/library/socket/shared/pack_sockaddr.rb
@@ -0,0 +1,92 @@
+# coding: utf-8
+describe :socket_pack_sockaddr_in, shared: true do
+ it "packs and unpacks" do
+ sockaddr_in = Socket.public_send(@method, 0, nil)
+ port, addr = Socket.unpack_sockaddr_in(sockaddr_in)
+ ["127.0.0.1", "::1"].include?(addr).should == true
+ port.should == 0
+
+ sockaddr_in = Socket.public_send(@method, 0, '')
+ Socket.unpack_sockaddr_in(sockaddr_in).should == [0, '0.0.0.0']
+
+ sockaddr_in = Socket.public_send(@method, 80, '127.0.0.1')
+ Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '127.0.0.1']
+
+ sockaddr_in = Socket.public_send(@method, '80', '127.0.0.1')
+ Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '127.0.0.1']
+
+ sockaddr_in = Socket.public_send(@method, nil, '127.0.0.1')
+ Socket.unpack_sockaddr_in(sockaddr_in).should == [0, '127.0.0.1']
+
+ sockaddr_in = Socket.public_send(@method, 80, Socket::INADDR_ANY)
+ Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '0.0.0.0']
+ end
+
+ it 'resolves the service name to a port' do
+ sockaddr_in = Socket.public_send(@method, 'http', '127.0.0.1')
+ Socket.unpack_sockaddr_in(sockaddr_in).should == [80, '127.0.0.1']
+ end
+
+ describe 'using an IPv4 address' do
+ it 'returns a String of 16 bytes' do
+ str = Socket.public_send(@method, 80, '127.0.0.1')
+
+ str.should be_an_instance_of(String)
+ str.bytesize.should == 16
+ end
+ end
+
+ describe 'using an IPv6 address' do
+ it 'returns a String of 28 bytes' do
+ str = Socket.public_send(@method, 80, '::1')
+
+ str.should be_an_instance_of(String)
+ str.bytesize.should == 28
+ end
+ end
+end
+
+describe :socket_pack_sockaddr_un, shared: true do
+ it 'should be idempotent' do
+ bytes = Socket.public_send(@method, '/tmp/foo').bytes
+ bytes[2..9].should == [47, 116, 109, 112, 47, 102, 111, 111]
+ bytes[10..-1].all?(&:zero?).should == true
+ end
+
+ it "packs and unpacks" do
+ sockaddr_un = Socket.public_send(@method, '/tmp/s')
+ Socket.unpack_sockaddr_un(sockaddr_un).should == '/tmp/s'
+ end
+
+ it "handles correctly paths with multibyte chars" do
+ sockaddr_un = Socket.public_send(@method, '/home/вася/sock')
+ path = Socket.unpack_sockaddr_un(sockaddr_un).encode('UTF-8', 'UTF-8')
+ path.should == '/home/вася/sock'
+ end
+
+ platform_is :linux do
+ it 'returns a String of 110 bytes' do
+ str = Socket.public_send(@method, '/tmp/test.sock')
+
+ str.should be_an_instance_of(String)
+ str.bytesize.should == 110
+ end
+ end
+
+ platform_is :bsd do
+ it 'returns a String of 106 bytes' do
+ str = Socket.public_send(@method, '/tmp/test.sock')
+
+ str.should be_an_instance_of(String)
+ str.bytesize.should == 106
+ end
+ end
+
+ platform_is_not :aix do
+ it "raises ArgumentError for paths that are too long" do
+ # AIX doesn't raise error
+ long_path = 'a' * 110
+ -> { Socket.public_send(@method, long_path) }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/shared/partially_closable_sockets.rb b/spec/ruby/library/socket/shared/partially_closable_sockets.rb
new file mode 100644
index 0000000000..b1c2ebabe1
--- /dev/null
+++ b/spec/ruby/library/socket/shared/partially_closable_sockets.rb
@@ -0,0 +1,13 @@
+describe :partially_closable_sockets, shared: true do
+ it "if the write end is closed then the other side can read past EOF without blocking" do
+ @s1.write("foo")
+ @s1.close_write
+ @s2.read("foo".size + 1).should == "foo"
+ end
+
+ it "closing the write end ensures that the other side can read until EOF" do
+ @s1.write("hello world")
+ @s1.close_write
+ @s2.read.should == "hello world"
+ end
+end
diff --git a/spec/ruby/library/socket/shared/socketpair.rb b/spec/ruby/library/socket/shared/socketpair.rb
new file mode 100644
index 0000000000..25146cfff6
--- /dev/null
+++ b/spec/ruby/library/socket/shared/socketpair.rb
@@ -0,0 +1,138 @@
+describe :socket_socketpair, shared: true do
+ platform_is_not :windows do
+ it "ensures the returned sockets are connected" do
+ s1, s2 = Socket.public_send(@method, Socket::AF_UNIX, 1, 0)
+ s1.puts("test")
+ s2.gets.should == "test\n"
+ s1.close
+ s2.close
+ end
+
+ it "responses with array of two sockets" do
+ begin
+ s1, s2 = Socket.public_send(@method, :UNIX, :STREAM)
+
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ ensure
+ s1.close
+ s2.close
+ end
+ end
+
+ describe 'using an Integer as the 1st and 2nd argument' do
+ it 'returns two Socket objects' do
+ s1, s2 = Socket.public_send(@method, Socket::AF_UNIX, Socket::SOCK_STREAM)
+
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ s1.close
+ s2.close
+ end
+ end
+
+ describe 'using a Symbol as the 1st and 2nd argument' do
+ it 'returns two Socket objects' do
+ s1, s2 = Socket.public_send(@method, :UNIX, :STREAM)
+
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ s1.close
+ s2.close
+ end
+
+ it 'raises SocketError for an unknown address family' do
+ -> { Socket.public_send(@method, :CATS, :STREAM) }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError for an unknown socket type' do
+ -> { Socket.public_send(@method, :UNIX, :CATS) }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using a String as the 1st and 2nd argument' do
+ it 'returns two Socket objects' do
+ s1, s2 = Socket.public_send(@method, 'UNIX', 'STREAM')
+
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ s1.close
+ s2.close
+ end
+
+ it 'raises SocketError for an unknown address family' do
+ -> { Socket.public_send(@method, 'CATS', 'STREAM') }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError for an unknown socket type' do
+ -> { Socket.public_send(@method, 'UNIX', 'CATS') }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using an object that responds to #to_str as the 1st and 2nd argument' do
+ it 'returns two Socket objects' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return('UNIX')
+ type.stub!(:to_str).and_return('STREAM')
+
+ s1, s2 = Socket.public_send(@method, family, type)
+
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ s1.close
+ s2.close
+ end
+
+ it 'raises TypeError when #to_str does not return a String' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return(Socket::AF_UNIX)
+ type.stub!(:to_str).and_return(Socket::SOCK_STREAM)
+
+ -> { Socket.public_send(@method, family, type) }.should raise_error(TypeError)
+ end
+
+ it 'raises SocketError for an unknown address family' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return('CATS')
+ type.stub!(:to_str).and_return('STREAM')
+
+ -> { Socket.public_send(@method, family, type) }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError for an unknown socket type' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return('UNIX')
+ type.stub!(:to_str).and_return('CATS')
+
+ -> { Socket.public_send(@method, family, type) }.should raise_error(SocketError)
+ end
+ end
+
+ it 'accepts a custom protocol as an Integer as the 3rd argument' do
+ s1, s2 = Socket.public_send(@method, :UNIX, :STREAM, Socket::IPPROTO_IP)
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ s1.close
+ s2.close
+ end
+
+ it 'connects the returned Socket objects' do
+ s1, s2 = Socket.public_send(@method, :UNIX, :STREAM)
+ begin
+ s1.write('hello')
+ s2.recv(5).should == 'hello'
+ ensure
+ s1.close
+ s2.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/accept_loop_spec.rb b/spec/ruby/library/socket/socket/accept_loop_spec.rb
new file mode 100644
index 0000000000..78e8c3fa4a
--- /dev/null
+++ b/spec/ruby/library/socket/socket/accept_loop_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../spec_helper'
+
+describe 'Socket.accept_loop' do
+ before do
+ @server = Socket.new(:INET, :STREAM)
+ @client = Socket.new(:INET, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, '127.0.0.1'))
+ @server.listen(1)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'using an Array of Sockets' do
+ describe 'without any available connections' do
+ # FIXME windows randomly hangs here forever
+ # https://ci.appveyor.com/project/ruby/ruby/builds/20817932/job/dor2ipny7ru4erpa
+ platform_is_not :windows do
+ it 'blocks the caller' do
+ -> { Socket.accept_loop([@server]) }.should block_caller
+ end
+ end
+ end
+
+ describe 'with available connections' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'yields a Socket and an Addrinfo' do
+ conn = nil
+ addr = nil
+
+ Socket.accept_loop([@server]) do |connection, address|
+ conn = connection
+ addr = address
+ break
+ end
+
+ begin
+ conn.should be_an_instance_of(Socket)
+ addr.should be_an_instance_of(Addrinfo)
+ ensure
+ conn.close
+ end
+ end
+ end
+ end
+
+ describe 'using separate Socket arguments' do
+ describe 'without any available connections' do
+ it 'blocks the caller' do
+ -> { Socket.accept_loop(@server) }.should block_caller
+ end
+ end
+
+ describe 'with available connections' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'yields a Socket and an Addrinfo' do
+ conn = nil
+ addr = nil
+
+ Socket.accept_loop(@server) do |connection, address|
+ conn = connection
+ addr = address
+ break
+ end
+
+ begin
+ conn.should be_an_instance_of(Socket)
+ addr.should be_an_instance_of(Addrinfo)
+ ensure
+ conn.close
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/accept_nonblock_spec.rb b/spec/ruby/library/socket/socket/accept_nonblock_spec.rb
new file mode 100644
index 0000000000..011622988c
--- /dev/null
+++ b/spec/ruby/library/socket/socket/accept_nonblock_spec.rb
@@ -0,0 +1,141 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket#accept_nonblock" do
+ before :each do
+ @hostname = "127.0.0.1"
+ @addr = Socket.sockaddr_in(0, @hostname)
+ @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ @socket.bind(@addr)
+ @socket.listen(1)
+ end
+
+ after :each do
+ @socket.close
+ end
+
+ it "raises IO::WaitReadable if the connection is not accepted yet" do
+ -> {
+ @socket.accept_nonblock
+ }.should raise_error(IO::WaitReadable) { |e|
+ platform_is_not :windows do
+ e.should be_kind_of(Errno::EAGAIN)
+ end
+ platform_is :windows do
+ e.should be_kind_of(Errno::EWOULDBLOCK)
+ end
+ }
+ end
+
+ it 'returns :wait_readable in exceptionless mode' do
+ @socket.accept_nonblock(exception: false).should == :wait_readable
+ end
+end
+
+describe 'Socket#accept_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM, 0)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+ end
+
+ after do
+ @server.close unless @server.closed?
+ end
+
+ describe 'using an unbound socket' do
+ it 'raises Errno::EINVAL' do
+ -> { @server.accept_nonblock }.should raise_error(Errno::EINVAL)
+ -> { @server.accept_nonblock(exception: false) }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ describe "using a bound socket that's not listening" do
+ before do
+ @server.bind(@sockaddr)
+ end
+
+ it 'raises Errno::EINVAL' do
+ -> { @server.accept_nonblock }.should raise_error(Errno::EINVAL)
+ -> { @server.accept_nonblock(exception: false) }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ describe 'using a closed socket' do
+ it 'raises IOError' do
+ @server.close
+
+ -> { @server.accept_nonblock }.should raise_error(IOError)
+ -> { @server.accept_nonblock(exception: false) }.should raise_error(IOError)
+ end
+ end
+
+ describe "using a bound socket that's listening" do
+ before do
+ @server.bind(@sockaddr)
+ @server.listen(1)
+ end
+
+ describe 'without a connected client' do
+ it 'raises IO::WaitReadable' do
+ -> { @server.accept_nonblock }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'with a connected client' do
+ before do
+ addr = Socket.sockaddr_in(@server.local_address.ip_port, ip_address)
+ @client = Socket.new(family, :STREAM, 0)
+
+ @client.connect(addr)
+ end
+
+ after do
+ @socket.close if @socket
+ @client.close
+ end
+
+ it 'returns an Array containing a Socket and an Addrinfo' do
+ IO.select([@server])
+ @socket, addrinfo = @server.accept_nonblock
+
+ @socket.should be_an_instance_of(Socket)
+ addrinfo.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ IO.select([@server])
+ @socket, @addr = @server.accept_nonblock
+ end
+
+ it 'uses AF_INET as the address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0 as the protocol' do
+ @addr.protocol.should == 0
+ end
+
+ it 'uses the same IP address as the client Socket' do
+ @addr.ip_address.should == @client.local_address.ip_address
+ end
+
+ it 'uses the same port as the client Socket' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/accept_spec.rb b/spec/ruby/library/socket/socket/accept_spec.rb
new file mode 100644
index 0000000000..417f996c55
--- /dev/null
+++ b/spec/ruby/library/socket/socket/accept_spec.rb
@@ -0,0 +1,121 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket#accept' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM, 0)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+ end
+
+ after do
+ @server.close unless @server.closed?
+ end
+
+ platform_is :linux do # hangs on other platforms
+ describe 'using an unbound socket' do
+ it 'raises Errno::EINVAL' do
+ -> { @server.accept }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ describe "using a bound socket that's not listening" do
+ before do
+ @server.bind(@sockaddr)
+ end
+
+ it 'raises Errno::EINVAL' do
+ -> { @server.accept }.should raise_error(Errno::EINVAL)
+ end
+ end
+ end
+
+ describe 'using a closed socket' do
+ it 'raises IOError' do
+ @server.close
+
+ -> { @server.accept }.should raise_error(IOError)
+ end
+ end
+
+ describe "using a bound socket that's listening" do
+ before do
+ @server.bind(@sockaddr)
+ @server.listen(1)
+
+ server_ip = @server.local_address.ip_port
+ @server_addr = Socket.sockaddr_in(server_ip, ip_address)
+ end
+
+ describe 'without a connected client' do
+ it 'blocks the caller until a connection is available' do
+ client = Socket.new(family, :STREAM, 0)
+ thread = Thread.new do
+ @server.accept
+ end
+
+ client.connect(@server_addr)
+
+ value = thread.value
+ begin
+ value.should be_an_instance_of(Array)
+ ensure
+ client.close
+ value[0].close
+ end
+ end
+ end
+
+ describe 'with a connected client' do
+ before do
+ addr = Socket.sockaddr_in(@server.local_address.ip_port, ip_address)
+ @client = Socket.new(family, :STREAM, 0)
+
+ @client.connect(addr)
+ end
+
+ after do
+ @socket.close if @socket
+ @client.close
+ end
+
+ it 'returns an Array containing a Socket and an Addrinfo' do
+ @socket, addrinfo = @server.accept
+
+ @socket.should be_an_instance_of(Socket)
+ addrinfo.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @socket, @addr = @server.accept
+ end
+
+ it 'uses AF_INET as the address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0 as the protocol' do
+ @addr.protocol.should == 0
+ end
+
+ it 'uses the same IP address as the client Socket' do
+ @addr.ip_address.should == @client.local_address.ip_address
+ end
+
+ it 'uses the same port as the client Socket' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/bind_spec.rb b/spec/ruby/library/socket/socket/bind_spec.rb
new file mode 100644
index 0000000000..e76336eafa
--- /dev/null
+++ b/spec/ruby/library/socket/socket/bind_spec.rb
@@ -0,0 +1,150 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket#bind on SOCK_DGRAM socket" do
+ before :each do
+ @sock = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
+ @sockaddr = Socket.pack_sockaddr_in(0, "127.0.0.1")
+ end
+
+ after :each do
+ @sock.closed?.should be_false
+ @sock.close
+ end
+
+ it "binds to a port" do
+ -> { @sock.bind(@sockaddr) }.should_not raise_error
+ end
+
+ it "returns 0 if successful" do
+ @sock.bind(@sockaddr).should == 0
+ end
+
+ it "raises Errno::EINVAL when already bound" do
+ @sock.bind(@sockaddr)
+
+ -> { @sock.bind(@sockaddr) }.should raise_error(Errno::EINVAL)
+ end
+
+ it "raises Errno::EADDRNOTAVAIL when the specified sockaddr is not available from the local machine" do
+ sockaddr1 = Socket.pack_sockaddr_in(0, "4.3.2.1")
+ -> { @sock.bind(sockaddr1) }.should raise_error(Errno::EADDRNOTAVAIL)
+ end
+
+ platform_is_not :windows, :cygwin do
+ as_user do
+ break if File.read('/proc/sys/net/ipv4/ip_unprivileged_port_start').to_i <= 1 rescue nil
+ it "raises Errno::EACCES when the current user does not have permission to bind" do
+ sockaddr1 = Socket.pack_sockaddr_in(1, "127.0.0.1")
+ -> { @sock.bind(sockaddr1) }.should raise_error(Errno::EACCES)
+ end
+ end
+ end
+end
+
+describe "Socket#bind on SOCK_STREAM socket" do
+ before :each do
+ @sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
+ @sockaddr = Socket.pack_sockaddr_in(0, "127.0.0.1")
+ end
+
+ after :each do
+ @sock.closed?.should be_false
+ @sock.close
+ end
+
+ it "binds to a port" do
+ -> { @sock.bind(@sockaddr) }.should_not raise_error
+ end
+
+ it "returns 0 if successful" do
+ @sock.bind(@sockaddr).should == 0
+ end
+
+ it "raises Errno::EINVAL when already bound" do
+ @sock.bind(@sockaddr)
+
+ -> { @sock.bind(@sockaddr) }.should raise_error(Errno::EINVAL)
+ end
+
+ it "raises Errno::EADDRNOTAVAIL when the specified sockaddr is not available from the local machine" do
+ sockaddr1 = Socket.pack_sockaddr_in(0, "4.3.2.1")
+ -> { @sock.bind(sockaddr1) }.should raise_error(Errno::EADDRNOTAVAIL)
+ end
+
+ platform_is_not :windows, :cygwin do
+ as_user do
+ break if File.read('/proc/sys/net/ipv4/ip_unprivileged_port_start').to_i <= 1 rescue nil
+ it "raises Errno::EACCES when the current user does not have permission to bind" do
+ sockaddr1 = Socket.pack_sockaddr_in(1, "127.0.0.1")
+ -> { @sock.bind(sockaddr1) }.should raise_error(Errno::EACCES)
+ end
+ end
+ end
+end
+
+describe 'Socket#bind' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a packed socket address' do
+ before do
+ @socket = Socket.new(family, :DGRAM)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'returns 0 when successfully bound' do
+ @socket.bind(@sockaddr).should == 0
+ end
+
+ it 'raises Errno::EINVAL when binding to an already bound port' do
+ @socket.bind(@sockaddr)
+
+ -> { @socket.bind(@sockaddr) }.should raise_error(Errno::EINVAL)
+ end
+
+ it 'raises Errno::EADDRNOTAVAIL when the specified sockaddr is not available' do
+ ip = family == Socket::AF_INET ? '4.3.2.1' : '::2'
+ sockaddr1 = Socket.sockaddr_in(0, ip)
+
+ -> { @socket.bind(sockaddr1) }.should raise_error(Errno::EADDRNOTAVAIL)
+ end
+
+ platform_is_not :windows do
+ as_user do
+ break if File.read('/proc/sys/net/ipv4/ip_unprivileged_port_start').to_i <= 1 rescue nil
+
+ it 'raises Errno::EACCES when the user is not allowed to bind to the port' do
+ sockaddr1 = Socket.pack_sockaddr_in(1, ip_address)
+
+ -> { @socket.bind(sockaddr1) }.should raise_error(Errno::EACCES)
+ end
+ end
+ end
+ end
+
+ describe 'using an Addrinfo' do
+ before do
+ @addr = Addrinfo.udp(ip_address, 0)
+ @socket = Socket.new(@addr.afamily, @addr.socktype)
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'binds to an Addrinfo' do
+ @socket.bind(@addr).should == 0
+ @socket.local_address.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'uses a new Addrinfo for the local address' do
+ @socket.bind(@addr)
+ @socket.local_address.should_not == @addr
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/connect_nonblock_spec.rb b/spec/ruby/library/socket/socket/connect_nonblock_spec.rb
new file mode 100644
index 0000000000..359b8719fb
--- /dev/null
+++ b/spec/ruby/library/socket/socket/connect_nonblock_spec.rb
@@ -0,0 +1,147 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket#connect_nonblock" do
+ before :each do
+ @hostname = "127.0.0.1"
+ @server = TCPServer.new(@hostname, 0) # started, but no accept
+ @addr = Socket.sockaddr_in(@server.addr[1], @hostname)
+ @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ @thread = nil
+ end
+
+ after :each do
+ @socket.close
+ @server.close
+ @thread.join if @thread
+ end
+
+ it "connects the socket to the remote side" do
+ port = nil
+ accept = false
+ @thread = Thread.new do
+ server = TCPServer.new(@hostname, 0)
+ port = server.addr[1]
+ Thread.pass until accept
+ conn = server.accept
+ conn << "hello!"
+ conn.close
+ server.close
+ end
+
+ Thread.pass until port
+
+ addr = Socket.sockaddr_in(port, @hostname)
+ begin
+ @socket.connect_nonblock(addr)
+ rescue Errno::EINPROGRESS
+ end
+
+ accept = true
+ IO.select nil, [@socket]
+
+ begin
+ @socket.connect_nonblock(addr)
+ rescue Errno::EISCONN
+ # Not all OS's use this errno, so we trap and ignore it
+ end
+
+ @socket.read(6).should == "hello!"
+ end
+
+ platform_is_not :freebsd, :aix do
+ it "raises Errno::EINPROGRESS when the connect would block" do
+ -> do
+ @socket.connect_nonblock(@addr)
+ end.should raise_error(Errno::EINPROGRESS)
+ end
+
+ it "raises Errno::EINPROGRESS with IO::WaitWritable mixed in when the connect would block" do
+ -> do
+ @socket.connect_nonblock(@addr)
+ end.should raise_error(IO::WaitWritable)
+ end
+
+ it "returns :wait_writable in exceptionless mode when the connect would block" do
+ @socket.connect_nonblock(@addr, exception: false).should == :wait_writable
+ end
+ end
+end
+
+describe 'Socket#connect_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a DGRAM socket' do
+ before do
+ @server = Socket.new(family, :DGRAM)
+ @client = Socket.new(family, :DGRAM)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+
+ @server.bind(@sockaddr)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns 0 when successfully connected using a String' do
+ @client.connect_nonblock(@server.getsockname).should == 0
+ end
+
+ it 'returns 0 when successfully connected using an Addrinfo' do
+ @client.connect_nonblock(@server.connect_address).should == 0
+ end
+
+ it 'raises TypeError when passed an Integer' do
+ -> { @client.connect_nonblock(666) }.should raise_error(TypeError)
+ end
+ end
+
+ describe 'using a STREAM socket' do
+ before do
+ @server = Socket.new(family, :STREAM)
+ @client = Socket.new(family, :STREAM)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ it 'raises Errno::EISCONN when already connected' do
+ @server.listen(1)
+ @client.connect(@server.connect_address).should == 0
+
+ -> {
+ @client.connect_nonblock(@server.connect_address)
+
+ # A second call needed if non-blocking sockets become default
+ # XXX honestly I don't expect any real code to care about this spec
+ # as it's too implementation-dependent and checking for connect()
+ # errors is futile anyways because of TOCTOU
+ @client.connect_nonblock(@server.connect_address)
+ }.should raise_error(Errno::EISCONN)
+ end
+
+ it 'returns 0 when already connected in exceptionless mode' do
+ @server.listen(1)
+ @client.connect(@server.connect_address).should == 0
+
+ @client.connect_nonblock(@server.connect_address, exception: false).should == 0
+ end
+ end
+
+ platform_is_not :freebsd do
+ it 'raises IO:EINPROGRESSWaitWritable when the connection would block' do
+ @server.bind(@sockaddr)
+
+ -> {
+ @client.connect_nonblock(@server.connect_address)
+ }.should raise_error(IO::EINPROGRESSWaitWritable)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/connect_spec.rb b/spec/ruby/library/socket/socket/connect_spec.rb
new file mode 100644
index 0000000000..130379ce2b
--- /dev/null
+++ b/spec/ruby/library/socket/socket/connect_spec.rb
@@ -0,0 +1,78 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket#connect' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM)
+ @client = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns 0 when connected successfully using a String' do
+ @server.listen(1)
+
+ @client.connect(@server.getsockname).should == 0
+ end
+
+ it 'returns 0 when connected successfully using an Addrinfo' do
+ @server.listen(1)
+
+ @client.connect(@server.connect_address).should == 0
+ end
+
+ it 'raises Errno::EISCONN when already connected' do
+ @server.listen(1)
+
+ @client.connect(@server.getsockname).should == 0
+
+ -> {
+ @client.connect(@server.getsockname)
+
+ # A second call needed if non-blocking sockets become default
+ # XXX honestly I don't expect any real code to care about this spec
+ # as it's too implementation-dependent and checking for connect()
+ # errors is futile anyways because of TOCTOU
+ @client.connect(@server.getsockname)
+ }.should raise_error(Errno::EISCONN)
+ end
+
+ platform_is_not :darwin do
+ it 'raises Errno::ECONNREFUSED or Errno::ETIMEDOUT when the connection failed' do
+ begin
+ @client.connect(@server.getsockname)
+ rescue => e
+ [Errno::ECONNREFUSED, Errno::ETIMEDOUT].include?(e.class).should == true
+ end
+ end
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "fails with timeout" do
+ # TEST-NET-1 IP address are reserved for documentation and example purposes.
+ address = Socket.pack_sockaddr_in(1, "192.0.2.1")
+
+ client = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
+ client.timeout = 0
+
+ -> {
+ begin
+ client.connect(address)
+ rescue Errno::ECONNREFUSED
+ skip "Outgoing packets may be filtered"
+ rescue Errno::ENETUNREACH
+ skip "Off line"
+ end
+ }.should raise_error(IO::TimeoutError)
+ ensure
+ client.close
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/for_fd_spec.rb b/spec/ruby/library/socket/socket/for_fd_spec.rb
new file mode 100644
index 0000000000..e89228d436
--- /dev/null
+++ b/spec/ruby/library/socket/socket/for_fd_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket.for_fd" do
+ before :each do
+ @server = TCPServer.new("127.0.0.1", 0)
+ @port = @server.addr[1]
+ @client = TCPSocket.open("127.0.0.1", @port)
+ end
+
+ after :each do
+ @socket.close
+ @client.close
+ @host.close
+ @server.close
+ end
+
+ it "creates a new Socket that aliases the existing Socket's file descriptor" do
+ @socket = Socket.for_fd(@client.fileno)
+ @socket.autoclose = false
+ @socket.fileno.should == @client.fileno
+
+ @socket.send("foo", 0)
+ @client.send("bar", 0)
+
+ @host = @server.accept
+ @host.read(3).should == "foo"
+ @host.read(3).should == "bar"
+ end
+end
diff --git a/spec/ruby/library/socket/socket/getaddrinfo_spec.rb b/spec/ruby/library/socket/socket/getaddrinfo_spec.rb
new file mode 100644
index 0000000000..6576af52ee
--- /dev/null
+++ b/spec/ruby/library/socket/socket/getaddrinfo_spec.rb
@@ -0,0 +1,391 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket.getaddrinfo" do
+ before :each do
+ @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup
+ BasicSocket.do_not_reverse_lookup = true
+ end
+
+ after :each do
+ BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup
+ end
+
+ platform_is_not :windows do
+ it "gets the address information" do
+ expected = []
+ # The check for AP_INET6's class is needed because ipaddr.rb adds
+ # fake AP_INET6 even in case when IPv6 is not really supported.
+ # Without such check, this test might fail when ipaddr was required
+ # by some other specs.
+ if (Socket.constants.include? 'AF_INET6') &&
+ (Socket::AF_INET6.class != Object) then
+ expected.concat [
+ ['AF_INET6', 9, SocketSpecs.hostname, '::1', Socket::AF_INET6,
+ Socket::SOCK_DGRAM, Socket::IPPROTO_UDP],
+ ['AF_INET6', 9, SocketSpecs.hostname, '::1', Socket::AF_INET6,
+ Socket::SOCK_STREAM, Socket::IPPROTO_TCP],
+ ['AF_INET6', 9, SocketSpecs.hostname, 'fe80::1%lo0', Socket::AF_INET6,
+ Socket::SOCK_DGRAM, Socket::IPPROTO_UDP],
+ ['AF_INET6', 9, SocketSpecs.hostname, 'fe80::1%lo0', Socket::AF_INET6,
+ Socket::SOCK_STREAM, Socket::IPPROTO_TCP],
+ ]
+ end
+
+ expected.concat [
+ ['AF_INET', 9, SocketSpecs.hostname, '127.0.0.1', Socket::AF_INET,
+ Socket::SOCK_DGRAM, Socket::IPPROTO_UDP],
+ ['AF_INET', 9, SocketSpecs.hostname, '127.0.0.1', Socket::AF_INET,
+ Socket::SOCK_STREAM, Socket::IPPROTO_TCP],
+ ]
+
+ addrinfo = Socket.getaddrinfo SocketSpecs.hostname, 'discard'
+ addrinfo.each do |a|
+ case a.last
+ when Socket::IPPROTO_UDP, Socket::IPPROTO_TCP
+ expected.should include(a)
+ else
+ # don't check this. It's some weird protocol we don't know about
+ # so we can't spec it.
+ end
+ end
+ end
+
+ # #getaddrinfo will return a INADDR_ANY address (0.0.0.0 or "::")
+ # if it's a passive socket. In the case of non-passive
+ # sockets (AI_PASSIVE not set) it should return the loopback
+ # address (127.0.0.1 or "::1").
+
+ it "accepts empty addresses for IPv4 passive sockets" do
+ res = Socket.getaddrinfo(nil, "discard",
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ Socket::IPPROTO_TCP,
+ Socket::AI_PASSIVE)
+
+ expected = [["AF_INET", 9, "0.0.0.0", "0.0.0.0", Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP]]
+ res.should == expected
+ end
+
+ it "accepts empty addresses for IPv4 non-passive sockets" do
+ res = Socket.getaddrinfo(nil, "discard",
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ Socket::IPPROTO_TCP,
+ 0)
+
+ expected = [["AF_INET", 9, "127.0.0.1", "127.0.0.1", Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP]]
+ res.should == expected
+ end
+
+
+ it "accepts empty addresses for IPv6 passive sockets" do
+ res = Socket.getaddrinfo(nil, "discard",
+ Socket::AF_INET6,
+ Socket::SOCK_STREAM,
+ Socket::IPPROTO_TCP,
+ Socket::AI_PASSIVE)
+
+ expected = [
+ ["AF_INET6", 9, "::", "::", Socket::AF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP],
+ ["AF_INET6", 9, "0:0:0:0:0:0:0:0", "0:0:0:0:0:0:0:0", Socket::AF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP]
+ ]
+ res.each { |a| expected.should include(a) }
+ end
+
+ it "accepts empty addresses for IPv6 non-passive sockets" do
+ res = Socket.getaddrinfo(nil, "discard",
+ Socket::AF_INET6,
+ Socket::SOCK_STREAM,
+ Socket::IPPROTO_TCP,
+ 0)
+
+ expected = [
+ ["AF_INET6", 9, "::1", "::1", Socket::AF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP],
+ ["AF_INET6", 9, "0:0:0:0:0:0:0:1", "0:0:0:0:0:0:0:1", Socket::AF_INET6, Socket::SOCK_STREAM, Socket::IPPROTO_TCP]
+ ]
+ res.each { |a| expected.should include(a) }
+ end
+
+ ruby_version_is ""..."3.3" do
+ it "raises SocketError when fails to resolve address" do
+ -> {
+ Socket.getaddrinfo("www.kame.net", 80, "AF_UNIX")
+ }.should raise_error(SocketError)
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raises ResolutionError when fails to resolve address" do
+ -> {
+ Socket.getaddrinfo("www.kame.net", 80, "AF_UNIX")
+ }.should raise_error(Socket::ResolutionError) { |e|
+ [Socket::EAI_FAMILY, Socket::EAI_FAIL].should.include?(e.error_code)
+ }
+ end
+ end
+ end
+end
+
+describe 'Socket.getaddrinfo' do
+ describe 'without global reverse lookups' do
+ it 'returns an Array' do
+ Socket.getaddrinfo(nil, 'ftp').should be_an_instance_of(Array)
+ end
+
+ it 'accepts an Integer as the address family' do
+ array = Socket.getaddrinfo(nil, 'ftp', Socket::AF_INET)[0]
+
+ array[0].should == 'AF_INET'
+ array[1].should == 21
+ array[2].should == '127.0.0.1'
+ array[3].should == '127.0.0.1'
+ array[4].should == Socket::AF_INET
+ array[5].should be_kind_of(Integer)
+ array[6].should be_kind_of(Integer)
+ end
+
+ it 'accepts an Integer as the address family using IPv6' do
+ array = Socket.getaddrinfo(nil, 'ftp', Socket::AF_INET6)[0]
+
+ array[0].should == 'AF_INET6'
+ array[1].should == 21
+ array[2].should == '::1'
+ array[3].should == '::1'
+ array[4].should == Socket::AF_INET6
+ array[5].should be_kind_of(Integer)
+ array[6].should be_kind_of(Integer)
+ end
+
+ it 'accepts a Symbol as the address family' do
+ array = Socket.getaddrinfo(nil, 'ftp', :INET)[0]
+
+ array[0].should == 'AF_INET'
+ array[1].should == 21
+ array[2].should == '127.0.0.1'
+ array[3].should == '127.0.0.1'
+ array[4].should == Socket::AF_INET
+ array[5].should be_kind_of(Integer)
+ array[6].should be_kind_of(Integer)
+ end
+
+ it 'accepts a Symbol as the address family using IPv6' do
+ array = Socket.getaddrinfo(nil, 'ftp', :INET6)[0]
+
+ array[0].should == 'AF_INET6'
+ array[1].should == 21
+ array[2].should == '::1'
+ array[3].should == '::1'
+ array[4].should == Socket::AF_INET6
+ array[5].should be_kind_of(Integer)
+ array[6].should be_kind_of(Integer)
+ end
+
+ it 'accepts a String as the address family' do
+ array = Socket.getaddrinfo(nil, 'ftp', 'INET')[0]
+
+ array[0].should == 'AF_INET'
+ array[1].should == 21
+ array[2].should == '127.0.0.1'
+ array[3].should == '127.0.0.1'
+ array[4].should == Socket::AF_INET
+ array[5].should be_kind_of(Integer)
+ array[6].should be_kind_of(Integer)
+ end
+
+ it 'accepts a String as the address family using IPv6' do
+ array = Socket.getaddrinfo(nil, 'ftp', 'INET6')[0]
+
+ array[0].should == 'AF_INET6'
+ array[1].should == 21
+ array[2].should == '::1'
+ array[3].should == '::1'
+ array[4].should == Socket::AF_INET6
+ array[5].should be_kind_of(Integer)
+ array[6].should be_kind_of(Integer)
+ end
+
+ it 'accepts an object responding to #to_str as the host' do
+ dummy = mock(:dummy)
+
+ dummy.stub!(:to_str).and_return('127.0.0.1')
+
+ array = Socket.getaddrinfo(dummy, 'ftp')[0]
+
+ array[0].should == 'AF_INET'
+ array[1].should == 21
+ array[2].should == '127.0.0.1'
+ array[3].should == '127.0.0.1'
+ array[4].should == Socket::AF_INET
+ array[5].should be_kind_of(Integer)
+ array[6].should be_kind_of(Integer)
+ end
+
+ it 'accepts an object responding to #to_str as the address family' do
+ dummy = mock(:dummy)
+
+ dummy.stub!(:to_str).and_return('INET')
+
+ array = Socket.getaddrinfo(nil, 'ftp', dummy)[0]
+
+ array[0].should == 'AF_INET'
+ array[1].should == 21
+ array[2].should == '127.0.0.1'
+ array[3].should == '127.0.0.1'
+ array[4].should == Socket::AF_INET
+ array[5].should be_kind_of(Integer)
+ array[6].should be_kind_of(Integer)
+ end
+
+ it 'accepts an Integer as the socket type' do
+ *array, proto = Socket.getaddrinfo(nil, 'ftp', :INET, Socket::SOCK_STREAM)[0]
+ array.should == [
+ 'AF_INET',
+ 21,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ ]
+ [0, Socket::IPPROTO_TCP].should include(proto)
+ end
+
+ it 'accepts a Symbol as the socket type' do
+ *array, proto = Socket.getaddrinfo(nil, 'ftp', :INET, :STREAM)[0]
+ array.should == [
+ 'AF_INET',
+ 21,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ ]
+ [0, Socket::IPPROTO_TCP].should include(proto)
+ end
+
+ it 'accepts a String as the socket type' do
+ *array, proto = Socket.getaddrinfo(nil, 'ftp', :INET, 'STREAM')[0]
+ array.should == [
+ 'AF_INET',
+ 21,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ ]
+ [0, Socket::IPPROTO_TCP].should include(proto)
+ end
+
+ it 'accepts an object responding to #to_str as the socket type' do
+ dummy = mock(:dummy)
+
+ dummy.stub!(:to_str).and_return('STREAM')
+
+ *array, proto = Socket.getaddrinfo(nil, 'ftp', :INET, dummy)[0]
+ array.should == [
+ 'AF_INET',
+ 21,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ ]
+ [0, Socket::IPPROTO_TCP].should include(proto)
+ end
+
+ platform_is_not :windows do
+ it 'accepts an Integer as the protocol family' do
+ *array, proto = Socket.getaddrinfo(nil, 'discard', :INET, :DGRAM, Socket::IPPROTO_UDP)[0]
+ array.should == [
+ 'AF_INET',
+ 9,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_DGRAM,
+ ]
+ [0, Socket::IPPROTO_UDP].should include(proto)
+ end
+ end
+
+ it 'accepts an Integer as the flags' do
+ *array, proto = Socket.getaddrinfo(nil, 'ftp', :INET, :STREAM,
+ Socket::IPPROTO_TCP, Socket::AI_PASSIVE)[0]
+ array.should == [
+ 'AF_INET',
+ 21,
+ '0.0.0.0',
+ '0.0.0.0',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ ]
+ [0, Socket::IPPROTO_TCP].should include(proto)
+ end
+
+ it 'performs a reverse lookup when the reverse_lookup argument is true' do
+ addr = Socket.getaddrinfo(nil, 'ftp', :INET, :STREAM,
+ Socket::IPPROTO_TCP, 0, true)[0]
+
+ addr[0].should == 'AF_INET'
+ addr[1].should == 21
+
+ addr[2].should be_an_instance_of(String)
+ addr[2].should_not == addr[3]
+
+ addr[3].should == '127.0.0.1'
+ end
+
+ it 'performs a reverse lookup when the reverse_lookup argument is :hostname' do
+ addr = Socket.getaddrinfo(nil, 'ftp', :INET, :STREAM,
+ Socket::IPPROTO_TCP, 0, :hostname)[0]
+
+ addr[0].should == 'AF_INET'
+ addr[1].should == 21
+
+ addr[2].should be_an_instance_of(String)
+ addr[2].should_not == addr[3]
+
+ addr[3].should == '127.0.0.1'
+ end
+
+ it 'performs a reverse lookup when the reverse_lookup argument is :numeric' do
+ *array, proto = Socket.getaddrinfo(nil, 'ftp', :INET, :STREAM,
+ Socket::IPPROTO_TCP, 0, :numeric)[0]
+ array.should == [
+ 'AF_INET',
+ 21,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ ]
+ [0, Socket::IPPROTO_TCP].should include(proto)
+ end
+ end
+
+ describe 'with global reverse lookups' do
+ before do
+ @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup
+ BasicSocket.do_not_reverse_lookup = false
+ end
+
+ after do
+ BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup
+ end
+
+ it 'returns an address honoring the global lookup option' do
+ addr = Socket.getaddrinfo(nil, 'ftp', :INET)[0]
+
+ addr[0].should == 'AF_INET'
+ addr[1].should == 21
+
+ # We don't have control over this value and there's no way to test this
+ # without relying on Socket.getaddrinfo()'s own behaviour (meaning this
+ # test would faily any way of the method was not implemented correctly).
+ addr[2].should be_an_instance_of(String)
+ addr[2].should_not == addr[3]
+
+ addr[3].should == '127.0.0.1'
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/gethostbyaddr_spec.rb b/spec/ruby/library/socket/socket/gethostbyaddr_spec.rb
new file mode 100644
index 0000000000..5d936046f5
--- /dev/null
+++ b/spec/ruby/library/socket/socket/gethostbyaddr_spec.rb
@@ -0,0 +1,121 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require 'ipaddr'
+
+describe 'Socket.gethostbyaddr' do
+ describe 'using an IPv4 address' do
+ before do
+ @addr = IPAddr.new('127.0.0.1').hton
+ end
+
+ describe 'without an explicit address family' do
+ it 'returns an Array' do
+ suppress_warning { Socket.gethostbyaddr(@addr) }.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = suppress_warning { Socket.gethostbyaddr(@addr) }
+ end
+
+ it 'includes the hostname as the first value' do
+ @array[0].should == SocketSpecs.hostname_reverse_lookup
+ end
+
+ it 'includes the aliases as the 2nd value' do
+ @array[1].should be_an_instance_of(Array)
+
+ @array[1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @array[2].should == Socket::AF_INET
+ end
+
+ it 'includes all address strings as the remaining values' do
+ @array[3].should == @addr
+
+ @array[4..-1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+ end
+ end
+
+ describe 'with an explicit address family' do
+ it 'returns an Array when using an Integer as the address family' do
+ suppress_warning { Socket.gethostbyaddr(@addr, Socket::AF_INET) }.should be_an_instance_of(Array)
+ end
+
+ it 'returns an Array when using a Symbol as the address family' do
+ suppress_warning { Socket.gethostbyaddr(@addr, :INET) }.should be_an_instance_of(Array)
+ end
+
+ it 'raises SocketError when the address is not supported by the family' do
+ -> { suppress_warning { Socket.gethostbyaddr(@addr, :INET6) } }.should raise_error(SocketError)
+ end
+ end
+ end
+
+ guard -> { SocketSpecs.ipv6_available? && platform_is_not(:aix) } do
+ describe 'using an IPv6 address' do
+ before do
+ @addr = IPAddr.new('::1').hton
+ end
+
+ describe 'without an explicit address family' do
+ it 'returns an Array' do
+ suppress_warning { Socket.gethostbyaddr(@addr) }.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = suppress_warning { Socket.gethostbyaddr(@addr) }
+ end
+
+ it 'includes the hostname as the first value' do
+ @array[0].should == SocketSpecs.hostname_reverse_lookup("::1")
+ end
+
+ it 'includes the aliases as the 2nd value' do
+ @array[1].should be_an_instance_of(Array)
+
+ @array[1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @array[2].should == Socket::AF_INET6
+ end
+
+ it 'includes all address strings as the remaining values' do
+ @array[3].should be_an_instance_of(String)
+
+ @array[4..-1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+ end
+ end
+
+ describe 'with an explicit address family' do
+ it 'returns an Array when using an Integer as the address family' do
+ suppress_warning { Socket.gethostbyaddr(@addr, Socket::AF_INET6) }.should be_an_instance_of(Array)
+ end
+
+ it 'returns an Array when using a Symbol as the address family' do
+ suppress_warning { Socket.gethostbyaddr(@addr, :INET6) }.should be_an_instance_of(Array)
+ end
+
+ platform_is_not :windows, :wsl do
+ it 'raises SocketError when the address is not supported by the family' do
+ -> { suppress_warning { Socket.gethostbyaddr(@addr, :INET) } }.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/gethostbyname_spec.rb b/spec/ruby/library/socket/socket/gethostbyname_spec.rb
new file mode 100644
index 0000000000..618ef85387
--- /dev/null
+++ b/spec/ruby/library/socket/socket/gethostbyname_spec.rb
@@ -0,0 +1,135 @@
+# encoding: binary
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket.gethostbyname" do
+ it "returns broadcast address info for '<broadcast>'" do
+ addr = suppress_warning { Socket.gethostbyname('<broadcast>') }
+ addr.should == ["255.255.255.255", [], 2, "\xFF\xFF\xFF\xFF"]
+ end
+
+ it "returns broadcast address info for '<any>'" do
+ addr = suppress_warning { Socket.gethostbyname('<any>') }
+ addr.should == ["0.0.0.0", [], 2, "\x00\x00\x00\x00"]
+ end
+end
+
+describe 'Socket.gethostbyname' do
+ it 'returns an Array' do
+ suppress_warning { Socket.gethostbyname('127.0.0.1') }.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = suppress_warning { Socket.gethostbyname('127.0.0.1') }
+ end
+
+ it 'includes the hostname as the first value' do
+ @array[0].should == '127.0.0.1'
+ end
+
+ it 'includes the aliases as the 2nd value' do
+ @array[1].should be_an_instance_of(Array)
+
+ @array[1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+
+ it 'includes the address type as the 3rd value' do
+ possible = [Socket::AF_INET, Socket::AF_INET6]
+
+ possible.include?(@array[2]).should == true
+ end
+
+ it 'includes the address strings as the remaining values' do
+ @array[3].should be_an_instance_of(String)
+
+ @array[4..-1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+ end
+
+ describe 'using <broadcast> as the input address' do
+ describe 'the returned Array' do
+ before do
+ @addr = suppress_warning { Socket.gethostbyname('<broadcast>') }
+ end
+
+ it 'includes the broadcast address as the first value' do
+ @addr[0].should == '255.255.255.255'
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @addr[2].should == Socket::AF_INET
+ end
+
+ it 'includes the address string as the 4th value' do
+ @addr[3].should == [255, 255, 255, 255].pack('C4')
+ end
+ end
+ end
+
+ describe 'using <any> as the input address' do
+ describe 'the returned Array' do
+ before do
+ @addr = suppress_warning { Socket.gethostbyname('<any>') }
+ end
+
+ it 'includes the wildcard address as the first value' do
+ @addr[0].should == '0.0.0.0'
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @addr[2].should == Socket::AF_INET
+ end
+
+ it 'includes the address string as the 4th value' do
+ @addr[3].should == [0, 0, 0, 0].pack('C4')
+ end
+ end
+ end
+
+ describe 'using an IPv4 address' do
+ describe 'the returned Array' do
+ before do
+ @addr = suppress_warning { Socket.gethostbyname('127.0.0.1') }
+ end
+
+ it 'includes the IP address as the first value' do
+ @addr[0].should == '127.0.0.1'
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @addr[2].should == Socket::AF_INET
+ end
+
+ it 'includes the address string as the 4th value' do
+ @addr[3].should == [127, 0, 0, 1].pack('C4')
+ end
+ end
+ end
+
+ guard -> { SocketSpecs.ipv6_available? } do
+ describe 'using an IPv6 address' do
+ describe 'the returned Array' do
+ before do
+ @addr = suppress_warning { Socket.gethostbyname('::1') }
+ end
+
+ it 'includes the IP address as the first value' do
+ @addr[0].should == '::1'
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @addr[2].should == Socket::AF_INET6
+ end
+
+ it 'includes the address string as the 4th value' do
+ @addr[3].should == [0, 0, 0, 0, 0, 0, 0, 1].pack('n8')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/gethostname_spec.rb b/spec/ruby/library/socket/socket/gethostname_spec.rb
new file mode 100644
index 0000000000..dfca7cf5cd
--- /dev/null
+++ b/spec/ruby/library/socket/socket/gethostname_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket.gethostname" do
+ def system_hostname
+ if platform_is_not :windows
+ # `uname -n` is the most portable way to get the hostname, as it is a POSIX standard:
+ `uname -n`.strip
+ else
+ # Windows does not have uname, so we use hostname instead:
+ `hostname`.strip
+ end
+ end
+
+ it "returns the host name" do
+ Socket.gethostname.should == system_hostname
+ end
+end
diff --git a/spec/ruby/library/socket/socket/getifaddrs_spec.rb b/spec/ruby/library/socket/socket/getifaddrs_spec.rb
new file mode 100644
index 0000000000..839854ea27
--- /dev/null
+++ b/spec/ruby/library/socket/socket/getifaddrs_spec.rb
@@ -0,0 +1,117 @@
+require_relative '../spec_helper'
+
+platform_is_not :aix do
+describe 'Socket.getifaddrs' do
+ before do
+ @ifaddrs = Socket.getifaddrs
+ end
+
+ it 'returns an Array' do
+ @ifaddrs.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ it 'should not be empty' do
+ @ifaddrs.should_not be_empty
+ end
+
+ it 'contains instances of Socket::Ifaddr' do
+ @ifaddrs.each do |ifaddr|
+ ifaddr.should be_an_instance_of(Socket::Ifaddr)
+ end
+ end
+ end
+
+ describe 'each returned Socket::Ifaddr' do
+ it 'has an interface index' do
+ @ifaddrs.each do |ifaddr|
+ ifaddr.ifindex.should be_kind_of(Integer)
+ end
+ end
+
+ it 'has an interface name' do
+ @ifaddrs.each do |ifaddr|
+ ifaddr.name.should be_an_instance_of(String)
+ end
+ end
+
+ it 'has a set of flags' do
+ @ifaddrs.each do |ifaddr|
+ ifaddr.flags.should be_kind_of(Integer)
+ end
+ end
+ end
+
+ describe 'the Socket::Ifaddr address' do
+ before do
+ @addrs = @ifaddrs.map(&:addr).compact
+ end
+
+ it 'is an Addrinfo' do
+ @addrs.all? do |addr|
+ addr.should be_an_instance_of(Addrinfo)
+ true
+ end.should be_true
+ end
+
+ it 'has an address family' do
+ @addrs.all? do |addr|
+ addr.afamily.should be_kind_of(Integer)
+ addr.afamily.should_not == Socket::AF_UNSPEC
+ true
+ end.should be_true
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'the Socket::Ifaddr broadcast address' do
+ before do
+ @addrs = @ifaddrs.map(&:broadaddr).compact
+ end
+
+ it 'is an Addrinfo' do
+ @addrs.all? do |addr|
+ addr.should be_an_instance_of(Addrinfo)
+ true
+ end.should be_true
+ end
+
+ it 'has an address family' do
+ @addrs.all? do |addr|
+ addr.afamily.should be_kind_of(Integer)
+ addr.afamily.should_not == Socket::AF_UNSPEC
+ true
+ end.should be_true
+ end
+ end
+
+ describe 'the Socket::Ifaddr netmask address' do
+ before do
+ @addrs = @ifaddrs.map(&:netmask).compact.select(&:ip?)
+ end
+
+ it 'is an Addrinfo' do
+ @addrs.all? do |addr|
+ addr.should be_an_instance_of(Addrinfo)
+ true
+ end.should be_true
+ end
+
+ it 'has an address family' do
+ @addrs.all? do |addr|
+ addr.afamily.should be_kind_of(Integer)
+ addr.afamily.should_not == Socket::AF_UNSPEC
+ true
+ end.should be_true
+ end
+
+ it 'has an IP address' do
+ @addrs.all? do |addr|
+ addr.ip_address.should be_an_instance_of(String)
+ true
+ end.should be_true
+ end
+ end
+ end
+end
+end
diff --git a/spec/ruby/library/socket/socket/getnameinfo_spec.rb b/spec/ruby/library/socket/socket/getnameinfo_spec.rb
new file mode 100644
index 0000000000..af4a10c9c2
--- /dev/null
+++ b/spec/ruby/library/socket/socket/getnameinfo_spec.rb
@@ -0,0 +1,165 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket.getnameinfo" do
+ before :each do
+ @reverse_lookup = BasicSocket.do_not_reverse_lookup
+ BasicSocket.do_not_reverse_lookup = true
+ end
+
+ after :each do
+ BasicSocket.do_not_reverse_lookup = @reverse_lookup
+ end
+
+ it "gets the name information and don't resolve it" do
+ sockaddr = Socket.sockaddr_in 3333, '127.0.0.1'
+ name_info = Socket.getnameinfo(sockaddr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV)
+ name_info.should == ['127.0.0.1', "3333"]
+ end
+
+ def should_be_valid_dns_name(name)
+ # http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address
+ # ftp://ftp.rfc-editor.org/in-notes/rfc3696.txt
+ # http://domainkeys.sourceforge.net/underscore.html
+ valid_dns = /^(([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9\-_]*[a-zA-Z0-9_])\.)*([A-Za-z_]|[A-Za-z_][A-Za-z0-9\-_]*[A-Za-z0-9_])\.?$/
+ name.should =~ valid_dns
+ end
+
+ it "gets the name information and resolve the host" do
+ sockaddr = Socket.sockaddr_in 3333, '127.0.0.1'
+ name_info = Socket.getnameinfo(sockaddr, Socket::NI_NUMERICSERV)
+ should_be_valid_dns_name(name_info[0])
+ name_info[1].should == 3333.to_s
+ end
+
+ it "gets the name information and resolves the service" do
+ sockaddr = Socket.sockaddr_in 9, '127.0.0.1'
+ name_info = Socket.getnameinfo(sockaddr)
+ name_info.size.should == 2
+ should_be_valid_dns_name(name_info[0])
+ # see http://www.iana.org/assignments/port-numbers
+ name_info[1].should == 'discard'
+ end
+
+ it "gets a 3-element array and doesn't resolve hostname" do
+ name_info = Socket.getnameinfo(["AF_INET", 3333, '127.0.0.1'], Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV)
+ name_info.should == ['127.0.0.1', "3333"]
+ end
+
+ it "gets a 3-element array and resolves the service" do
+ name_info = Socket.getnameinfo ["AF_INET", 9, '127.0.0.1']
+ name_info[1].should == 'discard'
+ end
+
+ it "gets a 4-element array and doesn't resolve hostname" do
+ name_info = Socket.getnameinfo(["AF_INET", 3333, 'foo', '127.0.0.1'], Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV)
+ name_info.should == ['127.0.0.1', "3333"]
+ end
+
+ it "gets a 4-element array and resolves the service" do
+ name_info = Socket.getnameinfo ["AF_INET", 9, 'foo', '127.0.0.1']
+ name_info[1].should == 'discard'
+ end
+
+ ruby_version_is ""..."3.3" do
+ it "raises SocketError when fails to resolve address" do
+ -> {
+ Socket.getnameinfo(["AF_UNIX", 80, "0.0.0.0"])
+ }.should raise_error(SocketError)
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raises ResolutionError when fails to resolve address" do
+ -> {
+ Socket.getnameinfo(["AF_UNIX", 80, "0.0.0.0"])
+ }.should raise_error(Socket::ResolutionError) { |e|
+ [Socket::EAI_FAMILY, Socket::EAI_FAIL].should.include?(e.error_code)
+ }
+ end
+ end
+end
+
+describe 'Socket.getnameinfo' do
+ describe 'using a String as the first argument' do
+ before do
+ @addr = Socket.sockaddr_in(21, '127.0.0.1')
+ end
+
+ it 'raises SocketError or TypeError when using an invalid String' do
+ -> { Socket.getnameinfo('cats') }.should raise_error(Exception) { |e|
+ (e.is_a?(SocketError) || e.is_a?(TypeError)).should == true
+ }
+ end
+
+ describe 'without custom flags' do
+ it 'returns an Array containing the hostname and service name' do
+ Socket.getnameinfo(@addr).should == [SocketSpecs.hostname_reverse_lookup, 'ftp']
+ end
+ end
+
+ describe 'using NI_NUMERICHOST as the flag' do
+ it 'returns an Array containing the numeric hostname and service name' do
+ array = Socket.getnameinfo(@addr, Socket::NI_NUMERICHOST)
+
+ %w{127.0.0.1 ::1}.include?(array[0]).should == true
+
+ array[1].should == 'ftp'
+ end
+ end
+ end
+
+ SocketSpecs.each_ip_protocol do |family, ip_address, family_name|
+ before do
+ @hostname = SocketSpecs.hostname_reverse_lookup(ip_address)
+ end
+
+ describe 'using a 3 element Array as the first argument' do
+ before do
+ @addr = [family_name, 21, @hostname]
+ end
+
+ it 'raises ArgumentError when using an invalid Array' do
+ -> { Socket.getnameinfo([family_name]) }.should raise_error(ArgumentError)
+ end
+
+ platform_is_not :windows do
+ describe 'using NI_NUMERICHOST as the flag' do
+ it 'returns an Array containing the numeric hostname and service name' do
+ Socket.getnameinfo(@addr, Socket::NI_NUMERICHOST).should == [ip_address, 'ftp']
+ end
+ end
+ end
+ end
+
+ describe 'using a 4 element Array as the first argument' do
+ before do
+ @addr = [family_name, 21, ip_address, ip_address]
+ end
+
+ describe 'without custom flags' do
+ it 'returns an Array containing the hostname and service name' do
+ array = Socket.getnameinfo(@addr)
+ array.should be_an_instance_of(Array)
+ array[0].should == @hostname
+ array[1].should == 'ftp'
+ end
+
+ it 'uses the 3rd value as the hostname if the 4th is not present' do
+ addr = [family_name, 21, ip_address, nil]
+
+ array = Socket.getnameinfo(addr)
+ array.should be_an_instance_of(Array)
+ array[0].should == @hostname
+ array[1].should == 'ftp'
+ end
+ end
+
+ describe 'using NI_NUMERICHOST as the flag' do
+ it 'returns an Array containing the numeric hostname and service name' do
+ Socket.getnameinfo(@addr, Socket::NI_NUMERICHOST).should == [ip_address, 'ftp']
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/getservbyname_spec.rb b/spec/ruby/library/socket/socket/getservbyname_spec.rb
new file mode 100644
index 0000000000..d361e619f2
--- /dev/null
+++ b/spec/ruby/library/socket/socket/getservbyname_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket#getservbyname" do
+ it "returns the port for service 'discard'" do
+ Socket.getservbyname('discard').should == 9
+ end
+
+ it "returns the port for service 'discard' with protocol 'tcp'" do
+ Socket.getservbyname('discard', 'tcp').should == 9
+ end
+
+ it 'returns the port for service "ftp"' do
+ Socket.getservbyname('ftp').should == 21
+ end
+
+ it 'returns the port for service "ftp" with protocol "tcp"' do
+ Socket.getservbyname('ftp', 'tcp').should == 21
+ end
+
+ it "returns the port for service 'domain' with protocol 'udp'" do
+ Socket.getservbyname('domain', 'udp').should == 53
+ end
+
+ it "returns the port for service 'daytime'" do
+ Socket.getservbyname('daytime').should == 13
+ end
+
+ it "raises a SocketError when the service or port is invalid" do
+ -> { Socket.getservbyname('invalid') }.should raise_error(SocketError)
+ end
+end
diff --git a/spec/ruby/library/socket/socket/getservbyport_spec.rb b/spec/ruby/library/socket/socket/getservbyport_spec.rb
new file mode 100644
index 0000000000..563c592b54
--- /dev/null
+++ b/spec/ruby/library/socket/socket/getservbyport_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../spec_helper'
+
+describe 'Socket.getservbyport' do
+ platform_is_not :windows do
+ it 'returns the service name as a String' do
+ Socket.getservbyport(514).should == 'shell'
+ end
+ end
+
+ platform_is :windows do
+ it 'returns the service name as a String' do
+ Socket.getservbyport(514).should == 'cmd'
+ end
+ end
+
+ it 'returns the service name when using a custom protocol name' do
+ Socket.getservbyport(514, 'udp').should == 'syslog'
+ end
+
+ it 'raises SocketError for an unknown port number' do
+ -> { Socket.getservbyport(0) }.should raise_error(SocketError)
+ end
+end
diff --git a/spec/ruby/library/socket/socket/initialize_spec.rb b/spec/ruby/library/socket/socket/initialize_spec.rb
new file mode 100644
index 0000000000..f8337bcaa5
--- /dev/null
+++ b/spec/ruby/library/socket/socket/initialize_spec.rb
@@ -0,0 +1,87 @@
+require_relative '../spec_helper'
+
+describe 'Socket#initialize' do
+ before do
+ @socket = nil
+ end
+
+ after do
+ @socket.close if @socket
+ end
+
+ describe 'using an Integer as the 1st and 2nd arguments' do
+ it 'returns a Socket' do
+ @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'using Symbols as the 1st and 2nd arguments' do
+ it 'returns a Socket' do
+ @socket = Socket.new(:INET, :STREAM)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'using Strings as the 1st and 2nd arguments' do
+ it 'returns a Socket' do
+ @socket = Socket.new('INET', 'STREAM')
+
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'using objects that respond to #to_str' do
+ it 'returns a Socket' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return('AF_INET')
+ type.stub!(:to_str).and_return('STREAM')
+
+ @socket = Socket.new(family, type)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'raises TypeError when the #to_str method does not return a String' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return(Socket::AF_INET)
+ type.stub!(:to_str).and_return(Socket::SOCK_STREAM)
+
+ -> { Socket.new(family, type) }.should raise_error(TypeError)
+ end
+ end
+
+ describe 'using a custom protocol' do
+ it 'returns a Socket when using an Integer' do
+ @socket = Socket.new(:INET, :STREAM, Socket::IPPROTO_TCP)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'raises TypeError when using a Symbol' do
+ -> { Socket.new(:INET, :STREAM, :TCP) }.should raise_error(TypeError)
+ end
+ end
+
+ it 'sets the do_not_reverse_lookup option' do
+ @socket = Socket.new(:INET, :STREAM)
+
+ @socket.do_not_reverse_lookup.should == Socket.do_not_reverse_lookup
+ end
+
+ it "sets basic IO accessors" do
+ @socket = Socket.new(:INET, :STREAM)
+ @socket.lineno.should == 0
+ end
+
+ it "sets the socket to binary mode" do
+ @socket = Socket.new(:INET, :STREAM)
+ @socket.binmode?.should be_true
+ end
+end
diff --git a/spec/ruby/library/socket/socket/ip_address_list_spec.rb b/spec/ruby/library/socket/socket/ip_address_list_spec.rb
new file mode 100644
index 0000000000..f97c2d7f85
--- /dev/null
+++ b/spec/ruby/library/socket/socket/ip_address_list_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../spec_helper'
+
+describe 'Socket.ip_address_list' do
+ it 'returns an Array' do
+ Socket.ip_address_list.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = Socket.ip_address_list
+ end
+
+ it 'is not empty' do
+ @array.should_not be_empty
+ end
+
+ it 'contains Addrinfo objects' do
+ @array.each do |klass|
+ klass.should be_an_instance_of(Addrinfo)
+ end
+ end
+ end
+
+ describe 'each returned Addrinfo' do
+ before do
+ @array = Socket.ip_address_list
+ end
+
+ it 'has a non-empty IP address' do
+ @array.each do |addr|
+ addr.ip_address.should be_an_instance_of(String)
+ addr.ip_address.should_not be_empty
+ end
+ end
+
+ it 'has an address family' do
+ families = [Socket::AF_INET, Socket::AF_INET6]
+
+ @array.each do |addr|
+ families.include?(addr.afamily).should == true
+ end
+ end
+
+ it 'uses 0 as the port number' do
+ @array.each do |addr|
+ addr.ip_port.should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/ipv6only_bang_spec.rb b/spec/ruby/library/socket/socket/ipv6only_bang_spec.rb
new file mode 100644
index 0000000000..4f429c089e
--- /dev/null
+++ b/spec/ruby/library/socket/socket/ipv6only_bang_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+guard -> { SocketSpecs.ipv6_available? } do
+ describe 'Socket#ipv6only!' do
+ before do
+ @socket = Socket.new(:INET6, :DGRAM)
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'enables IPv6 only mode' do
+ @socket.ipv6only!
+
+ @socket.getsockopt(:IPV6, :V6ONLY).bool.should == true
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/listen_spec.rb b/spec/ruby/library/socket/socket/listen_spec.rb
new file mode 100644
index 0000000000..4d2aedab19
--- /dev/null
+++ b/spec/ruby/library/socket/socket/listen_spec.rb
@@ -0,0 +1,66 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket#listen" do
+ before :each do
+ @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ end
+
+ after :each do
+ @socket.closed?.should be_false
+ @socket.close
+ end
+
+ it "verifies we can listen for incoming connections" do
+ sockaddr = Socket.pack_sockaddr_in(0, "127.0.0.1")
+ @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
+ @socket.bind(sockaddr)
+ @socket.listen(1).should == 0
+ end
+end
+
+describe 'Socket#listen' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a DGRAM socket' do
+ before do
+ @server = Socket.new(family, :DGRAM)
+ @client = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'raises Errno::EOPNOTSUPP or Errno::EACCES' do
+ -> { @server.listen(1) }.should raise_error { |e|
+ [Errno::EOPNOTSUPP, Errno::EACCES].should.include?(e.class)
+ }
+ end
+ end
+
+ describe 'using a STREAM socket' do
+ before do
+ @server = Socket.new(family, :STREAM)
+ @client = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns 0' do
+ @server.listen(1).should == 0
+ end
+
+ it "raises when the given argument can't be coerced to an Integer" do
+ -> { @server.listen('cats') }.should raise_error(TypeError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/local_address_spec.rb b/spec/ruby/library/socket/socket/local_address_spec.rb
new file mode 100644
index 0000000000..3687f93a0c
--- /dev/null
+++ b/spec/ruby/library/socket/socket/local_address_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+
+describe 'Socket#local_address' do
+ before do
+ @sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP)
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.local_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_INET as the address family' do
+ @sock.local_address.afamily.should == Socket::AF_INET
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @sock.local_address.pfamily.should == Socket::PF_INET
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.local_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0.0.0.0 as the IP address' do
+ @sock.local_address.ip_address.should == '0.0.0.0'
+ end
+
+ platform_is_not :windows do
+ it 'uses 0 as the port' do
+ @sock.local_address.ip_port.should == 0
+ end
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.local_address.protocol.should == 0
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb b/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb
new file mode 100644
index 0000000000..ef2a2d4ba9
--- /dev/null
+++ b/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/pack_sockaddr'
+
+describe "Socket.pack_sockaddr_in" do
+ it_behaves_like :socket_pack_sockaddr_in, :pack_sockaddr_in
+end
diff --git a/spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb b/spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb
new file mode 100644
index 0000000000..1ee0bc6157
--- /dev/null
+++ b/spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/pack_sockaddr'
+
+describe "Socket#pack_sockaddr_un" do
+ it_behaves_like :socket_pack_sockaddr_un, :pack_sockaddr_un
+end
diff --git a/spec/ruby/library/socket/socket/pair_spec.rb b/spec/ruby/library/socket/socket/pair_spec.rb
new file mode 100644
index 0000000000..8dd470a95e
--- /dev/null
+++ b/spec/ruby/library/socket/socket/pair_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/socketpair'
+
+describe "Socket.pair" do
+ it_behaves_like :socket_socketpair, :pair
+end
diff --git a/spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb b/spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb
new file mode 100644
index 0000000000..01b42bcc52
--- /dev/null
+++ b/spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb
@@ -0,0 +1,219 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket#recvfrom_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :DGRAM)
+ @client = Socket.new(family, :DGRAM)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ describe 'using an unbound socket' do
+ it 'raises IO::WaitReadable' do
+ -> { @server.recvfrom_nonblock(1) }.should raise_error(IO::WaitReadable)
+ end
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @client.connect(@server.getsockname)
+ end
+
+ describe 'without any data available' do
+ it 'raises IO::WaitReadable' do
+ -> { @server.recvfrom_nonblock(1) }.should raise_error(IO::WaitReadable)
+ end
+
+ it 'returns :wait_readable with exception: false' do
+ @server.recvfrom_nonblock(1, exception: false).should == :wait_readable
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ end
+
+ platform_is_not :windows do
+ it 'returns an Array containing the data and an Addrinfo' do
+ IO.select([@server])
+ ret = @server.recvfrom_nonblock(1)
+
+ ret.should be_an_instance_of(Array)
+ ret.length.should == 2
+ end
+ end
+
+ it "allows an output buffer as third argument" do
+ @client.write('hello')
+
+ IO.select([@server])
+ buffer = +''
+ message, = @server.recvfrom_nonblock(5, 0, buffer)
+
+ message.should.equal?(buffer)
+ buffer.should == 'hello'
+ end
+
+ it "preserves the encoding of the given buffer" do
+ @client.write('hello')
+
+ IO.select([@server])
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ @server.recvfrom_nonblock(5, 0, buffer)
+
+ buffer.encoding.should == Encoding::ISO_8859_1
+ end
+
+ describe 'the returned data' do
+ it 'is the same as the sent data' do
+ 5.times do
+ @client.write('hello')
+
+ IO.select([@server])
+ msg, _ = @server.recvfrom_nonblock(5)
+
+ msg.should == 'hello'
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'the returned Array' do
+ before do
+ IO.select([@server])
+ @array = @server.recvfrom_nonblock(1)
+ end
+
+ it 'contains the data at index 0' do
+ @array[0].should == 'h'
+ end
+
+ it 'contains an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ IO.select([@server])
+ @addr = @server.recvfrom_nonblock(1)[1]
+ end
+
+ it 'uses AF_INET as the address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses SOCK_DGRAM as the socket type' do
+ @addr.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses 0 as the protocol' do
+ @addr.protocol.should == 0
+ end
+
+ it 'uses the IP address of the client' do
+ @addr.ip_address.should == ip_address
+ end
+
+ it 'uses the port of the client' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+describe 'Socket#recvfrom_nonblock' do
+ context "when recvfrom(2) returns 0 (if no messages are available to be received and the peer has performed an orderly shutdown)" do
+ describe "stream socket" do
+ before :each do
+ @server = Socket.new Socket::AF_INET, :STREAM, 0
+ @sockaddr = Socket.sockaddr_in(0, "127.0.0.1")
+ @server.bind(@sockaddr)
+ @server.listen(1)
+
+ server_ip = @server.local_address.ip_port
+ @server_addr = Socket.sockaddr_in(server_ip, "127.0.0.1")
+
+ @client = Socket.new(Socket::AF_INET, :STREAM, 0)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ @client.close unless @client.closed?
+ end
+
+ ruby_version_is ""..."3.3" do
+ it "returns an empty String as received data on a closed stream socket" do
+ ready = false
+
+ t = Thread.new do
+ client, _ = @server.accept
+
+ Thread.pass while !ready
+ begin
+ client.recvfrom_nonblock(10)
+ rescue IO::EAGAINWaitReadable
+ retry
+ end
+ ensure
+ client.close if client
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ @client.connect(@server_addr)
+ @client.close
+ ready = true
+
+ t.value.should.is_a? Array
+ t.value[0].should == ""
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "returns nil on a closed stream socket" do
+ ready = false
+
+ t = Thread.new do
+ client, _ = @server.accept
+
+ Thread.pass while !ready
+ begin
+ client.recvfrom_nonblock(10)
+ rescue IO::EAGAINWaitReadable
+ retry
+ end
+ ensure
+ client.close if client
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ @client.connect(@server_addr)
+ @client.close
+ ready = true
+
+ t.value.should be_nil
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/recvfrom_spec.rb b/spec/ruby/library/socket/socket/recvfrom_spec.rb
new file mode 100644
index 0000000000..6ba39ffcaf
--- /dev/null
+++ b/spec/ruby/library/socket/socket/recvfrom_spec.rb
@@ -0,0 +1,179 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket#recvfrom' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :DGRAM)
+ @client = Socket.new(family, :DGRAM)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'using an unbound socket' do
+ it 'blocks the caller' do
+ -> { @server.recvfrom(1) }.should block_caller
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @client.connect(@server.getsockname)
+ end
+
+ describe 'without any data available' do
+ it 'blocks the caller' do
+ -> { @server.recvfrom(1) }.should block_caller
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ end
+
+ it 'returns an Array containing the data and an Addrinfo' do
+ ret = @server.recvfrom(1)
+
+ ret.should be_an_instance_of(Array)
+ ret.length.should == 2
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @server.recvfrom(1)
+ end
+
+ it 'contains the data at index 0' do
+ @array[0].should == 'h'
+ end
+
+ it 'contains an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @server.recvfrom(1)[1]
+ end
+
+ it 'uses AF_INET as the address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses SOCK_DGRAM as the socket type' do
+ @addr.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses 0 as the protocol' do
+ @addr.protocol.should == 0
+ end
+
+ it 'uses the IP address of the client' do
+ @addr.ip_address.should == ip_address
+ end
+
+ it 'uses the port of the client' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+end
+
+describe 'Socket#recvfrom' do
+ context "when recvfrom(2) returns 0 (if no messages are available to be received and the peer has performed an orderly shutdown)" do
+ describe "stream socket" do
+ before :each do
+ @server = Socket.new Socket::AF_INET, :STREAM, 0
+ sockaddr = Socket.sockaddr_in(0, "127.0.0.1")
+ @server.bind(sockaddr)
+ @server.listen(1)
+
+ server_ip = @server.local_address.ip_port
+ @server_addr = Socket.sockaddr_in(server_ip, "127.0.0.1")
+
+ @client = Socket.new(Socket::AF_INET, :STREAM, 0)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ @client.close unless @client.closed?
+ end
+
+ ruby_version_is ""..."3.3" do
+ it "returns an empty String as received data on a closed stream socket" do
+ t = Thread.new do
+ client, _ = @server.accept
+ client.recvfrom(10)
+ ensure
+ client.close if client
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ @client.connect(@server_addr)
+ @client.close
+
+ t.value.should.is_a? Array
+ t.value[0].should == ""
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "returns nil on a closed stream socket" do
+ t = Thread.new do
+ client, _ = @server.accept
+ client.recvfrom(10)
+ ensure
+ client.close if client
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.status.should_not be_nil
+
+ @client.connect(@server_addr)
+ @client.close
+
+ t.value.should be_nil
+ end
+ end
+ end
+
+ describe "datagram socket" do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before :each do
+ @server = Socket.new(family, :DGRAM)
+ @client = Socket.new(family, :DGRAM)
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ @client.close unless @client.closed?
+ end
+
+ it "returns an empty String as received data" do
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @client.connect(@server.getsockname)
+
+ @client.send('', 0)
+ message = @server.recvfrom(1)
+
+ message.should.is_a? Array
+ message[0].should == ""
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/remote_address_spec.rb b/spec/ruby/library/socket/socket/remote_address_spec.rb
new file mode 100644
index 0000000000..24d60d7f58
--- /dev/null
+++ b/spec/ruby/library/socket/socket/remote_address_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket#remote_address' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @host = @server.local_address.ip_address
+ @port = @server.local_address.ip_port
+ @client = Socket.new(family, :STREAM, Socket::IPPROTO_TCP)
+
+ @client.connect(Socket.sockaddr_in(@port, @host))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns an Addrinfo' do
+ @client.remote_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_INET as the address family' do
+ @client.remote_address.afamily.should == family
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @client.remote_address.pfamily.should == family
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @client.remote_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses the correct IP address' do
+ @client.remote_address.ip_address.should == @host
+ end
+
+ it 'uses the correct port' do
+ @client.remote_address.ip_port.should == @port
+ end
+
+ it 'uses 0 as the protocol' do
+ @client.remote_address.protocol.should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/sockaddr_in_spec.rb b/spec/ruby/library/socket/socket/sockaddr_in_spec.rb
new file mode 100644
index 0000000000..8ee956ac26
--- /dev/null
+++ b/spec/ruby/library/socket/socket/sockaddr_in_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/pack_sockaddr'
+
+describe "Socket#sockaddr_in" do
+ it_behaves_like :socket_pack_sockaddr_in, :sockaddr_in
+end
diff --git a/spec/ruby/library/socket/socket/sockaddr_un_spec.rb b/spec/ruby/library/socket/socket/sockaddr_un_spec.rb
new file mode 100644
index 0000000000..8922ff4d6d
--- /dev/null
+++ b/spec/ruby/library/socket/socket/sockaddr_un_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/pack_sockaddr'
+
+describe "Socket#sockaddr_un" do
+ it_behaves_like :socket_pack_sockaddr_un, :sockaddr_un
+end
diff --git a/spec/ruby/library/socket/socket/socket_spec.rb b/spec/ruby/library/socket/socket/socket_spec.rb
new file mode 100644
index 0000000000..5a3d6733e0
--- /dev/null
+++ b/spec/ruby/library/socket/socket/socket_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket" do
+ it "inherits from BasicSocket and IO" do
+ Socket.superclass.should == BasicSocket
+ BasicSocket.superclass.should == IO
+ end
+end
+
+describe "The socket class hierarchy" do
+ it "has an IPSocket in parallel to Socket" do
+ Socket.ancestors.include?(IPSocket).should == false
+ IPSocket.ancestors.include?(Socket).should == false
+ IPSocket.superclass.should == BasicSocket
+ end
+
+ it "has TCPSocket and UDPSocket subclasses of IPSocket" do
+ TCPSocket.superclass.should == IPSocket
+ UDPSocket.superclass.should == IPSocket
+ end
+
+ platform_is_not :windows do
+ it "has a UNIXSocket in parallel to Socket" do
+ Socket.ancestors.include?(UNIXSocket).should == false
+ UNIXSocket.ancestors.include?(Socket).should == false
+ UNIXSocket.superclass.should == BasicSocket
+ end
+ end
+end
+
+platform_is_not :windows do
+ describe "Server class hierarchy" do
+ it "contains UNIXServer" do
+ UNIXServer.superclass.should == UNIXSocket
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/socketpair_spec.rb b/spec/ruby/library/socket/socket/socketpair_spec.rb
new file mode 100644
index 0000000000..551c376d49
--- /dev/null
+++ b/spec/ruby/library/socket/socket/socketpair_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/socketpair'
+
+describe "Socket.socketpair" do
+ it_behaves_like :socket_socketpair, :socketpair
+end
diff --git a/spec/ruby/library/socket/socket/sysaccept_spec.rb b/spec/ruby/library/socket/socket/sysaccept_spec.rb
new file mode 100644
index 0000000000..92ac21124e
--- /dev/null
+++ b/spec/ruby/library/socket/socket/sysaccept_spec.rb
@@ -0,0 +1,91 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket#sysaccept' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+ end
+
+ after do
+ @server.close
+ end
+
+ platform_is :linux do # hangs on other platforms
+ describe 'using an unbound socket' do
+ it 'raises Errno::EINVAL' do
+ -> { @server.sysaccept }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ describe "using a bound socket that's not listening" do
+ before do
+ @server.bind(@sockaddr)
+ end
+
+ it 'raises Errno::EINVAL' do
+ -> { @server.sysaccept }.should raise_error(Errno::EINVAL)
+ end
+ end
+ end
+
+ describe "using a bound socket that's listening" do
+ before do
+ @server.bind(@sockaddr)
+ @server.listen(1)
+
+ server_ip = @server.local_address.ip_port
+ @server_addr = Socket.sockaddr_in(server_ip, ip_address)
+ end
+
+ after do
+ Socket.for_fd(@fd).close if @fd
+ end
+
+ describe 'without a connected client' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ end
+
+ after do
+ @client.close
+ end
+
+ it 'blocks the caller until a connection is available' do
+ thread = Thread.new do
+ @fd, _ = @server.sysaccept
+ end
+
+ @client.connect(@server_addr)
+
+ thread.value.should be_an_instance_of(Array)
+ end
+ end
+
+ describe 'with a connected client' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ end
+
+ it 'returns an Array containing an Integer and an Addrinfo' do
+ @fd, addrinfo = @server.sysaccept
+
+ @fd.should be_kind_of(Integer)
+ addrinfo.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'returns a new file descriptor' do
+ @fd, _ = @server.sysaccept
+
+ @fd.should_not == @client.fileno
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/tcp_server_loop_spec.rb b/spec/ruby/library/socket/socket/tcp_server_loop_spec.rb
new file mode 100644
index 0000000000..a46c6df5c6
--- /dev/null
+++ b/spec/ruby/library/socket/socket/tcp_server_loop_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket.tcp_server_loop' do
+ describe 'when no connections are available' do
+ it 'blocks the caller' do
+ -> { Socket.tcp_server_loop('127.0.0.1', 0) }.should block_caller
+ end
+ end
+
+ describe 'when a connection is available' do
+ before do
+ @client = Socket.new(:INET, :STREAM)
+ SocketSpecs::ServerLoopPortFinder.cleanup
+ end
+
+ after do
+ @sock.close if @sock
+ @client.close
+ end
+
+ it 'yields a Socket and an Addrinfo' do
+ @sock, addr = nil
+
+ thread = Thread.new do
+ SocketSpecs::ServerLoopPortFinder.tcp_server_loop('127.0.0.1', 0) do |socket, addrinfo|
+ @sock = socket
+ addr = addrinfo
+
+ break
+ end
+ end
+
+ port = SocketSpecs::ServerLoopPortFinder.port
+
+ SocketSpecs.loop_with_timeout do
+ begin
+ @client.connect(Socket.sockaddr_in(port, '127.0.0.1'))
+ rescue SystemCallError
+ sleep 0.01
+ :retry
+ end
+ end
+
+ # At this point the connection has been set up but the thread may not yet
+ # have returned, thus we'll need to wait a little longer for it to
+ # complete.
+ thread.join
+
+ @sock.should be_an_instance_of(Socket)
+ addr.should be_an_instance_of(Addrinfo)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/tcp_server_sockets_spec.rb b/spec/ruby/library/socket/socket/tcp_server_sockets_spec.rb
new file mode 100644
index 0000000000..bd496d3015
--- /dev/null
+++ b/spec/ruby/library/socket/socket/tcp_server_sockets_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../spec_helper'
+
+describe 'Socket.tcp_server_sockets' do
+ describe 'without a block' do
+ before do
+ @sockets = nil
+ end
+
+ after do
+ @sockets.each(&:close)
+ end
+
+ it 'returns an Array of Socket objects' do
+ @sockets = Socket.tcp_server_sockets(0)
+
+ @sockets.should be_an_instance_of(Array)
+ @sockets[0].should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'with a block' do
+ it 'yields the sockets to the supplied block' do
+ Socket.tcp_server_sockets(0) do |sockets|
+ sockets.should be_an_instance_of(Array)
+ sockets[0].should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes all sockets after the block returns' do
+ sockets = nil
+
+ Socket.tcp_server_sockets(0) { |socks| sockets = socks }
+
+ sockets.each do |socket|
+ socket.should.closed?
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/tcp_spec.rb b/spec/ruby/library/socket/socket/tcp_spec.rb
new file mode 100644
index 0000000000..faf020b1ea
--- /dev/null
+++ b/spec/ruby/library/socket/socket/tcp_spec.rb
@@ -0,0 +1,70 @@
+require_relative '../spec_helper'
+
+describe 'Socket.tcp' do
+ before do
+ @server = Socket.new(:INET, :STREAM)
+ @client = nil
+
+ @server.bind(Socket.sockaddr_in(0, '127.0.0.1'))
+ @server.listen(1)
+
+ @host = @server.connect_address.ip_address
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @client.close if @client && !@client.closed?
+ @client = nil
+
+ @server.close
+ end
+
+ it 'returns a Socket when no block is given' do
+ @client = Socket.tcp(@host, @port)
+
+ @client.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket when a block is given' do
+ Socket.tcp(@host, @port) do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes the Socket automatically when a block is given' do
+ Socket.tcp(@host, @port) do |socket|
+ @socket = socket
+ end
+
+ @socket.should.closed?
+ end
+
+ it 'binds to a local address and port when specified' do
+ @client = Socket.tcp(@host, @port, @host, 0)
+
+ @client.local_address.ip_address.should == @host
+
+ @client.local_address.ip_port.should > 0
+ @client.local_address.ip_port.should_not == @port
+ end
+
+ it 'raises ArgumentError when 6 arguments are provided' do
+ -> {
+ Socket.tcp(@host, @port, @host, 0, {:connect_timeout => 1}, 10)
+ }.should raise_error(ArgumentError)
+ end
+
+ it 'connects to the server' do
+ @client = Socket.tcp(@host, @port)
+
+ @client.write('hello')
+
+ connection, _ = @server.accept
+
+ begin
+ connection.recv(5).should == 'hello'
+ ensure
+ connection.close
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/udp_server_loop_on_spec.rb b/spec/ruby/library/socket/socket/udp_server_loop_on_spec.rb
new file mode 100644
index 0000000000..cb8c5c5587
--- /dev/null
+++ b/spec/ruby/library/socket/socket/udp_server_loop_on_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../spec_helper'
+
+describe 'Socket.udp_server_loop_on' do
+ before do
+ @server = Socket.new(:INET, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, '127.0.0.1'))
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'when no connections are available' do
+ it 'blocks the caller' do
+ -> { Socket.udp_server_loop_on([@server]) }.should block_caller
+ end
+ end
+
+ describe 'when a connection is available' do
+ before do
+ @client = Socket.new(:INET, :DGRAM)
+ end
+
+ after do
+ @client.close
+ end
+
+ it 'yields the message and a Socket::UDPSource' do
+ msg = nil
+ src = nil
+
+ @client.connect(@server.getsockname)
+ @client.write('hello')
+
+ Socket.udp_server_loop_on([@server]) do |message, source|
+ msg = message
+ src = source
+
+ break
+ end
+
+ msg.should == 'hello'
+ src.should be_an_instance_of(Socket::UDPSource)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/udp_server_loop_spec.rb b/spec/ruby/library/socket/socket/udp_server_loop_spec.rb
new file mode 100644
index 0000000000..cd22ea56cf
--- /dev/null
+++ b/spec/ruby/library/socket/socket/udp_server_loop_spec.rb
@@ -0,0 +1,59 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket.udp_server_loop' do
+ describe 'when no connections are available' do
+ it 'blocks the caller' do
+ -> { Socket.udp_server_loop('127.0.0.1', 0) }.should block_caller
+ end
+ end
+
+ describe 'when a connection is available' do
+ before do
+ @client = Socket.new(:INET, :DGRAM)
+ SocketSpecs::ServerLoopPortFinder.cleanup
+ end
+
+ after do
+ @client.close
+ end
+
+ it 'yields the message and a Socket::UDPSource' do
+ msg, src = nil
+
+ thread = Thread.new do
+ SocketSpecs::ServerLoopPortFinder.udp_server_loop('127.0.0.1', 0) do |message, source|
+ msg = message
+ src = source
+
+ break
+ end
+ end
+
+ port = SocketSpecs::ServerLoopPortFinder.port
+
+ # Because this will return even if the server is up and running (it's UDP
+ # after all) we'll have to write and wait until "msg" is set.
+ @client.connect(Socket.sockaddr_in(port, '127.0.0.1'))
+
+ SocketSpecs.loop_with_timeout do
+ begin
+ @client.write('hello')
+ rescue SystemCallError
+ sleep 0.01
+ :retry
+ else
+ unless msg
+ sleep 0.001
+ :retry
+ end
+ end
+ end
+
+ thread.join
+
+ msg.should == 'hello'
+ src.should be_an_instance_of(Socket::UDPSource)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/udp_server_recv_spec.rb b/spec/ruby/library/socket/socket/udp_server_recv_spec.rb
new file mode 100644
index 0000000000..47ed74bc03
--- /dev/null
+++ b/spec/ruby/library/socket/socket/udp_server_recv_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+
+describe 'Socket.udp_server_recv' do
+ before do
+ @server = Socket.new(:INET, :DGRAM)
+ @client = Socket.new(:INET, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, '127.0.0.1'))
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'yields the message and a Socket::UDPSource' do
+ msg = :unset
+ src = :unset
+
+ @client.write('hello')
+
+ readable, _, _ = IO.select([@server])
+ readable.size.should == 1
+
+ Socket.udp_server_recv(readable) do |message, source|
+ msg = message
+ src = source
+ break
+ end
+
+ msg.should == 'hello'
+ src.should be_an_instance_of(Socket::UDPSource)
+ end
+end
diff --git a/spec/ruby/library/socket/socket/udp_server_sockets_spec.rb b/spec/ruby/library/socket/socket/udp_server_sockets_spec.rb
new file mode 100644
index 0000000000..f8be672612
--- /dev/null
+++ b/spec/ruby/library/socket/socket/udp_server_sockets_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../spec_helper'
+
+describe 'Socket.udp_server_sockets' do
+ describe 'without a block' do
+ before do
+ @sockets = nil
+ end
+
+ after do
+ @sockets.each(&:close)
+ end
+
+ it 'returns an Array of Socket objects' do
+ @sockets = Socket.udp_server_sockets(0)
+
+ @sockets.should be_an_instance_of(Array)
+ @sockets[0].should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'with a block' do
+ it 'yields the sockets to the supplied block' do
+ Socket.udp_server_sockets(0) do |sockets|
+ sockets.should be_an_instance_of(Array)
+ sockets[0].should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes all sockets after the block returns' do
+ sockets = nil
+
+ Socket.udp_server_sockets(0) { |socks| sockets = socks }
+
+ sockets.each do |socket|
+ socket.should.closed?
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/unix_server_loop_spec.rb b/spec/ruby/library/socket/socket/unix_server_loop_spec.rb
new file mode 100644
index 0000000000..6192bc8bf6
--- /dev/null
+++ b/spec/ruby/library/socket/socket/unix_server_loop_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket.unix_server_loop' do
+ before do
+ @path = SocketSpecs.socket_path
+ end
+
+ after do
+ rm_r(@path) if File.file?(@path)
+ end
+
+ describe 'when no connections are available' do
+ it 'blocks the caller' do
+ -> { Socket.unix_server_loop(@path) }.should block_caller
+ end
+ end
+
+ describe 'when a connection is available' do
+ before do
+ @client = nil
+ end
+
+ after do
+ @sock.close if @sock
+ @client.close if @client
+ end
+
+ it 'yields a Socket and an Addrinfo' do
+ @sock, addr = nil
+
+ thread = Thread.new do
+ Socket.unix_server_loop(@path) do |socket, addrinfo|
+ @sock = socket
+ addr = addrinfo
+
+ break
+ end
+ end
+
+ SocketSpecs.loop_with_timeout do
+ begin
+ @client = Socket.unix(@path)
+ rescue SystemCallError
+ sleep 0.01
+ :retry
+ end
+ end
+
+ thread.join
+
+ @sock.should be_an_instance_of(Socket)
+ addr.should be_an_instance_of(Addrinfo)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/unix_server_socket_spec.rb b/spec/ruby/library/socket/socket/unix_server_socket_spec.rb
new file mode 100644
index 0000000000..34c3b96d07
--- /dev/null
+++ b/spec/ruby/library/socket/socket/unix_server_socket_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket.unix_server_socket' do
+ before do
+ @path = SocketSpecs.socket_path
+ end
+
+ after do
+ rm_r(@path)
+ end
+
+ describe 'when no block is given' do
+ before do
+ @socket = nil
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'returns a Socket' do
+ @socket = Socket.unix_server_socket(@path)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'when a block is given' do
+ it 'yields a Socket' do
+ Socket.unix_server_socket(@path) do |sock|
+ sock.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes the Socket when the block returns' do
+ socket = nil
+
+ Socket.unix_server_socket(@path) do |sock|
+ socket = sock
+ end
+
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/unix_spec.rb b/spec/ruby/library/socket/socket/unix_spec.rb
new file mode 100644
index 0000000000..2a5d77f96f
--- /dev/null
+++ b/spec/ruby/library/socket/socket/unix_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket.unix' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @socket = nil
+ end
+
+ after do
+ @server.close
+ @socket.close if @socket
+
+ rm_r(@path)
+ end
+
+ describe 'when no block is given' do
+ it 'returns a Socket' do
+ @socket = Socket.unix(@path)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'when a block is given' do
+ it 'yields a Socket' do
+ Socket.unix(@path) do |sock|
+ sock.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes the Socket when the block returns' do
+ socket = nil
+
+ Socket.unix(@path) do |sock|
+ socket = sock
+ end
+
+ socket.should.closed?
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/unpack_sockaddr_in_spec.rb b/spec/ruby/library/socket/socket/unpack_sockaddr_in_spec.rb
new file mode 100644
index 0000000000..935b5cb543
--- /dev/null
+++ b/spec/ruby/library/socket/socket/unpack_sockaddr_in_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket.unpack_sockaddr_in" do
+ it "decodes the host name and port number of a packed sockaddr_in" do
+ sockaddr = Socket.sockaddr_in 3333, '127.0.0.1'
+ Socket.unpack_sockaddr_in(sockaddr).should == [3333, '127.0.0.1']
+ end
+
+ it "gets the hostname and port number from a passed Addrinfo" do
+ addrinfo = Addrinfo.tcp('127.0.0.1', 3333)
+ Socket.unpack_sockaddr_in(addrinfo).should == [3333, '127.0.0.1']
+ end
+
+ describe 'using an IPv4 address' do
+ it 'returns an Array containing the port and IP address' do
+ port = 80
+ ip = '127.0.0.1'
+ addr = Socket.pack_sockaddr_in(port, ip)
+
+ Socket.unpack_sockaddr_in(addr).should == [port, ip]
+ end
+ end
+
+ describe 'using an IPv6 address' do
+ it 'returns an Array containing the port and IP address' do
+ port = 80
+ ip = '::1'
+ addr = Socket.pack_sockaddr_in(port, ip)
+
+ Socket.unpack_sockaddr_in(addr).should == [port, ip]
+ end
+ end
+
+ it "raises an ArgumentError when the sin_family is not AF_INET" do
+ sockaddr = Socket.sockaddr_un '/tmp/x'
+ -> { Socket.unpack_sockaddr_in sockaddr }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed addrinfo is not AF_INET/AF_INET6" do
+ addrinfo = Addrinfo.unix('/tmp/sock')
+ -> { Socket.unpack_sockaddr_in(addrinfo) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/socket/socket/unpack_sockaddr_un_spec.rb b/spec/ruby/library/socket/socket/unpack_sockaddr_un_spec.rb
new file mode 100644
index 0000000000..6e0f11de3d
--- /dev/null
+++ b/spec/ruby/library/socket/socket/unpack_sockaddr_un_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket.unpack_sockaddr_un' do
+ it 'decodes sockaddr to unix path' do
+ sockaddr = Socket.sockaddr_un('/tmp/sock')
+ Socket.unpack_sockaddr_un(sockaddr).should == '/tmp/sock'
+ end
+
+ it 'returns unix path from a passed Addrinfo' do
+ addrinfo = Addrinfo.unix('/tmp/sock')
+ Socket.unpack_sockaddr_un(addrinfo).should == '/tmp/sock'
+ end
+
+ it 'raises an ArgumentError when the sa_family is not AF_UNIX' do
+ sockaddr = Socket.sockaddr_in(0, '127.0.0.1')
+ -> { Socket.unpack_sockaddr_un(sockaddr) }.should raise_error(ArgumentError)
+ end
+
+ it 'raises an ArgumentError when passed addrinfo is not AF_UNIX' do
+ addrinfo = Addrinfo.tcp('127.0.0.1', 0)
+ -> { Socket.unpack_sockaddr_un(addrinfo) }.should raise_error(ArgumentError)
+ end
+end
diff --git a/spec/ruby/library/socket/spec_helper.rb b/spec/ruby/library/socket/spec_helper.rb
new file mode 100644
index 0000000000..b33663e02d
--- /dev/null
+++ b/spec/ruby/library/socket/spec_helper.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require 'socket'
+
+MSpec.enable_feature :sock_packet if Socket.const_defined?(:SOCK_PACKET)
+MSpec.enable_feature :udp_cork if Socket.const_defined?(:UDP_CORK)
+MSpec.enable_feature :tcp_cork if Socket.const_defined?(:TCP_CORK)
+MSpec.enable_feature :pktinfo if Socket.const_defined?(:IP_PKTINFO)
+MSpec.enable_feature :ipv6_pktinfo if Socket.const_defined?(:IPV6_PKTINFO)
+MSpec.enable_feature :ip_mtu if Socket.const_defined?(:IP_MTU)
+MSpec.enable_feature :ipv6_nexthop if Socket.const_defined?(:IPV6_NEXTHOP)
+MSpec.enable_feature :tcp_info if Socket.const_defined?(:TCP_INFO)
+MSpec.enable_feature :ancillary_data if Socket.const_defined?(:AncillaryData)
diff --git a/spec/ruby/library/socket/tcpserver/accept_nonblock_spec.rb b/spec/ruby/library/socket/tcpserver/accept_nonblock_spec.rb
new file mode 100644
index 0000000000..91f6a327f0
--- /dev/null
+++ b/spec/ruby/library/socket/tcpserver/accept_nonblock_spec.rb
@@ -0,0 +1,85 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "Socket::TCPServer.accept_nonblock" do
+ before :each do
+ @server = TCPServer.new("127.0.0.1", 0)
+ @port = @server.addr[1]
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ end
+
+ it "accepts non blocking connections" do
+ @server.listen(5)
+ -> {
+ @server.accept_nonblock
+ }.should raise_error(IO::WaitReadable)
+
+ c = TCPSocket.new("127.0.0.1", @port)
+ IO.select([@server])
+ s = @server.accept_nonblock
+
+ port, address = Socket.unpack_sockaddr_in(s.getsockname)
+
+ port.should == @port
+ address.should == "127.0.0.1"
+ s.should be_kind_of(TCPSocket)
+
+ c.close
+ s.close
+ end
+
+ it "raises an IOError if the socket is closed" do
+ @server.close
+ -> { @server.accept }.should raise_error(IOError)
+ end
+
+ describe 'without a connected client' do
+ it 'raises error' do
+ -> { @server.accept_nonblock }.should raise_error(IO::WaitReadable)
+ end
+
+ it 'returns :wait_readable in exceptionless mode' do
+ @server.accept_nonblock(exception: false).should == :wait_readable
+ end
+ end
+end
+
+describe 'TCPServer#accept_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'without a connected client' do
+ it 'raises IO::WaitReadable' do
+ -> { @server.accept_nonblock }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ platform_is_not :windows do # spurious
+ describe 'with a connected client' do
+ before do
+ @client = TCPSocket.new(ip_address, @server.connect_address.ip_port)
+ end
+
+ after do
+ @socket.close if @socket
+ @client.close
+ end
+
+ it 'returns a TCPSocket' do
+ IO.select([@server])
+ @socket = @server.accept_nonblock
+ @socket.should be_an_instance_of(TCPSocket)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpserver/accept_spec.rb b/spec/ruby/library/socket/tcpserver/accept_spec.rb
new file mode 100644
index 0000000000..d8892cd5f0
--- /dev/null
+++ b/spec/ruby/library/socket/tcpserver/accept_spec.rb
@@ -0,0 +1,132 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "TCPServer#accept" do
+ before :each do
+ @server = TCPServer.new("127.0.0.1", 0)
+ @port = @server.addr[1]
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ end
+
+ it "accepts a connection and returns a TCPSocket" do
+ data = nil
+ t = Thread.new do
+ client = @server.accept
+ client.should be_kind_of(TCPSocket)
+ data = client.read(5)
+ client << "goodbye"
+ client.close
+ end
+ Thread.pass while t.status and t.status != "sleep"
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.write('hello')
+ socket.shutdown(1) # we are done with sending
+ socket.read.should == 'goodbye'
+ t.join
+ data.should == 'hello'
+ socket.close
+ end
+
+ it "can be interrupted by Thread#kill" do
+ t = Thread.new { @server.accept }
+
+ Thread.pass while t.status and t.status != "sleep"
+
+ # kill thread, ensure it dies in a reasonable amount of time
+ t.kill
+ a = 0
+ while t.alive? and a < 5000
+ sleep 0.001
+ a += 1
+ end
+ a.should < 5000
+ end
+
+ it "can be interrupted by Thread#raise" do
+ t = Thread.new {
+ -> {
+ @server.accept
+ }.should raise_error(Exception, "interrupted")
+ }
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.raise Exception, "interrupted"
+ t.join
+ end
+
+ it "is automatically retried when interrupted by SIGVTALRM" do
+ t = Thread.new do
+ client = @server.accept
+ value = client.read(2)
+ client.close
+ value
+ end
+
+ Thread.pass while t.status and t.status != "sleep"
+ # Thread#backtrace uses SIGVTALRM on TruffleRuby and potentially other implementations.
+ # Sending a signal to a thread is not possible with Ruby APIs.
+ t.backtrace.join("\n").should =~ /in [`'](?:TCPServer#)?accept'/
+
+ socket = TCPSocket.new('127.0.0.1', @port)
+ socket.write("OK")
+ socket.close
+
+ t.value.should == "OK"
+ end
+
+ it "raises an IOError if the socket is closed" do
+ @server.close
+ -> { @server.accept }.should raise_error(IOError)
+ end
+end
+
+describe 'TCPServer#accept' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'without a connected client' do
+ it 'blocks the caller' do
+ -> { @server.accept }.should block_caller
+ end
+ end
+
+ describe 'with a connected client' do
+ before do
+ @client = TCPSocket.new(ip_address, @server.connect_address.ip_port)
+ end
+
+ after do
+ @socket.close if @socket
+ @client.close
+ end
+
+ it 'returns a TCPSocket' do
+ @socket = @server.accept
+ @socket.should be_an_instance_of(TCPSocket)
+ end
+
+ platform_is_not :windows do
+ it "returns a TCPSocket which is set to nonblocking" do
+ require 'io/nonblock'
+ @socket = @server.accept
+ @socket.should.nonblock?
+ end
+ end
+
+ it "returns a TCPSocket which is set to close on exec" do
+ @socket = @server.accept
+ @socket.should.close_on_exec?
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpserver/gets_spec.rb b/spec/ruby/library/socket/tcpserver/gets_spec.rb
new file mode 100644
index 0000000000..417976d737
--- /dev/null
+++ b/spec/ruby/library/socket/tcpserver/gets_spec.rb
@@ -0,0 +1,16 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "TCPServer#gets" do
+ before :each do
+ @server = TCPServer.new(SocketSpecs.hostname, 0)
+ end
+
+ after :each do
+ @server.close
+ end
+
+ it "raises Errno::ENOTCONN on gets" do
+ -> { @server.gets }.should raise_error(Errno::ENOTCONN)
+ end
+end
diff --git a/spec/ruby/library/socket/tcpserver/initialize_spec.rb b/spec/ruby/library/socket/tcpserver/initialize_spec.rb
new file mode 100644
index 0000000000..4ddd1f465f
--- /dev/null
+++ b/spec/ruby/library/socket/tcpserver/initialize_spec.rb
@@ -0,0 +1,101 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'TCPServer#initialize' do
+ describe 'with a single Integer argument' do
+ before do
+ @server = TCPServer.new(0)
+ end
+
+ after do
+ @server.close
+ end
+
+ it 'sets the port to the given argument' do
+ @server.local_address.ip_port.should be_kind_of(Integer)
+ @server.local_address.ip_port.should > 0
+ end
+
+ platform_is_not :windows do
+ it 'sets the hostname to 0.0.0.0 or ::' do
+ a = @server.local_address
+ a.ip_address.should == (a.ipv6? ? '::' : '0.0.0.0')
+ end
+ end
+
+ it "sets the socket to binmode" do
+ @server.binmode?.should be_true
+ end
+ end
+
+ describe 'with a single String argument containing a numeric value' do
+ before do
+ @server = TCPServer.new('0')
+ end
+
+ after do
+ @server.close
+ end
+
+ it 'sets the port to the given argument' do
+ @server.local_address.ip_port.should be_kind_of(Integer)
+ @server.local_address.ip_port.should > 0
+ end
+
+ platform_is_not :windows do
+ it 'sets the hostname to 0.0.0.0 or ::' do
+ a = @server.local_address
+ a.ip_address.should == (a.ipv6? ? '::' : '0.0.0.0')
+ end
+ end
+ end
+
+ describe 'with a single String argument containing a non numeric value' do
+ it 'raises SocketError' do
+ -> { TCPServer.new('cats') }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'with a String and an Integer' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ end
+
+ after do
+ @server.close
+ end
+
+ it 'sets the port to the given port argument' do
+ @server.local_address.ip_port.should be_kind_of(Integer)
+ @server.local_address.ip_port.should > 0
+ end
+
+ it 'sets the hostname to the given host argument' do
+ @server.local_address.ip_address.should == ip_address
+ end
+ end
+ end
+
+ describe 'with a String and a custom object' do
+ before do
+ dummy = mock(:dummy)
+ dummy.stub!(:to_str).and_return('0')
+
+ @server = TCPServer.new('127.0.0.1', dummy)
+ end
+
+ after do
+ @server.close
+ end
+
+ it 'sets the port to the given port argument' do
+ @server.local_address.ip_port.should be_kind_of(Integer)
+ @server.local_address.ip_port.should > 0
+ end
+
+ it 'sets the hostname to the given host argument' do
+ @server.local_address.ip_address.should == '127.0.0.1'
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpserver/listen_spec.rb b/spec/ruby/library/socket/tcpserver/listen_spec.rb
new file mode 100644
index 0000000000..c877fdced6
--- /dev/null
+++ b/spec/ruby/library/socket/tcpserver/listen_spec.rb
@@ -0,0 +1,22 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'TCPServer#listen' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ end
+
+ after do
+ @server.close
+ end
+
+ it 'returns 0' do
+ @server.listen(1).should == 0
+ end
+
+ it "raises when the given argument can't be coerced to an Integer" do
+ -> { @server.listen('cats') }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpserver/new_spec.rb b/spec/ruby/library/socket/tcpserver/new_spec.rb
new file mode 100644
index 0000000000..dd1ba676bd
--- /dev/null
+++ b/spec/ruby/library/socket/tcpserver/new_spec.rb
@@ -0,0 +1,137 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "TCPServer.new" do
+ after :each do
+ @server.close if @server && !@server.closed?
+ end
+
+ it "binds to a host and a port" do
+ @server = TCPServer.new('127.0.0.1', 0)
+ addr = @server.addr
+ addr[0].should == 'AF_INET'
+ addr[1].should be_kind_of(Integer)
+ # on some platforms (Mac), MRI
+ # returns comma at the end.
+ addr[2].should =~ /^#{SocketSpecs.hostname}\b/
+ addr[3].should == '127.0.0.1'
+ end
+
+ it "binds to localhost and a port with either IPv4 or IPv6" do
+ @server = TCPServer.new(SocketSpecs.hostname, 0)
+ addr = @server.addr
+ addr[1].should be_kind_of(Integer)
+ if addr[0] == 'AF_INET'
+ addr[2].should =~ /^#{SocketSpecs.hostname}\b/
+ addr[3].should == '127.0.0.1'
+ else
+ addr[2].should =~ /^#{SocketSpecs.hostname('::1')}\b/
+ addr[3].should == '::1'
+ end
+ end
+
+ it "binds to INADDR_ANY if the hostname is empty" do
+ @server = TCPServer.new('', 0)
+ addr = @server.addr
+ addr[0].should == 'AF_INET'
+ addr[1].should be_kind_of(Integer)
+ addr[2].should == '0.0.0.0'
+ addr[3].should == '0.0.0.0'
+ end
+
+ it "binds to INADDR_ANY if the hostname is empty and the port is a string" do
+ @server = TCPServer.new('', '0')
+ addr = @server.addr
+ addr[0].should == 'AF_INET'
+ addr[1].should be_kind_of(Integer)
+ addr[2].should == '0.0.0.0'
+ addr[3].should == '0.0.0.0'
+ end
+
+ it "binds to a port if the port is explicitly nil" do
+ @server = TCPServer.new('', nil)
+ addr = @server.addr
+ addr[0].should == 'AF_INET'
+ addr[1].should be_kind_of(Integer)
+ addr[2].should == '0.0.0.0'
+ addr[3].should == '0.0.0.0'
+ end
+
+ it "binds to a port if the port is an empty string" do
+ @server = TCPServer.new('', '')
+ addr = @server.addr
+ addr[0].should == 'AF_INET'
+ addr[1].should be_kind_of(Integer)
+ addr[2].should == '0.0.0.0'
+ addr[3].should == '0.0.0.0'
+ end
+
+ it "coerces port to string, then determines port from that number or service name" do
+ -> { TCPServer.new(SocketSpecs.hostname, Object.new) }.should raise_error(TypeError)
+
+ port = Object.new
+ port.should_receive(:to_str).and_return("0")
+
+ @server = TCPServer.new(SocketSpecs.hostname, port)
+ addr = @server.addr
+ addr[1].should be_kind_of(Integer)
+
+ # TODO: This should also accept strings like 'https', but I don't know how to
+ # pick such a service port that will be able to reliably bind...
+ end
+
+ it "has a single argument form and treats it as a port number" do
+ @server = TCPServer.new(0)
+ addr = @server.addr
+ addr[1].should be_kind_of(Integer)
+ end
+
+ it "coerces port to a string when it is the only argument" do
+ -> { TCPServer.new(Object.new) }.should raise_error(TypeError)
+
+ port = Object.new
+ port.should_receive(:to_str).and_return("0")
+
+ @server = TCPServer.new(port)
+ addr = @server.addr
+ addr[1].should be_kind_of(Integer)
+ end
+
+ it "does not use the given block and warns to use TCPServer::open" do
+ -> {
+ @server = TCPServer.new(0) { raise }
+ }.should complain(/warning: TCPServer::new\(\) does not take block; use TCPServer::open\(\) instead/)
+ end
+
+ it "raises Errno::EADDRNOTAVAIL when the address is unknown" do
+ -> { TCPServer.new("1.2.3.4", 0) }.should raise_error(Errno::EADDRNOTAVAIL)
+ end
+
+ # There is no way to make this fail-proof on all machines, because
+ # DNS servers like opendns return A records for ANY host, including
+ # traditionally invalidly named ones.
+ quarantine! do
+ it "raises a SocketError when the host is unknown" do
+ -> {
+ TCPServer.new("--notavalidname", 0)
+ }.should raise_error(SocketError)
+ end
+ end
+
+ it "raises Errno::EADDRINUSE when address is already in use" do
+ @server = TCPServer.new('127.0.0.1', 0)
+ -> {
+ @server = TCPServer.new('127.0.0.1', @server.addr[1])
+ }.should raise_error(Errno::EADDRINUSE)
+ end
+
+ platform_is_not :windows, :aix do
+ # A known bug in AIX. getsockopt(2) does not properly set
+ # the fifth argument for SO_REUSEADDR.
+ it "sets SO_REUSEADDR on the resulting server" do
+ @server = TCPServer.new('127.0.0.1', 0)
+ @server.getsockopt(:SOCKET, :REUSEADDR).data.should_not == "\x00\x00\x00\x00"
+ @server.getsockopt(:SOCKET, :REUSEADDR).int.should_not == 0
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpserver/sysaccept_spec.rb b/spec/ruby/library/socket/tcpserver/sysaccept_spec.rb
new file mode 100644
index 0000000000..bd7d33faf4
--- /dev/null
+++ b/spec/ruby/library/socket/tcpserver/sysaccept_spec.rb
@@ -0,0 +1,66 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "TCPServer#sysaccept" do
+ before :each do
+ @server = TCPServer.new(SocketSpecs.hostname, 0)
+ @port = @server.addr[1]
+ end
+
+ after :each do
+ @server.close unless @server.closed?
+ end
+
+ it 'blocks if no connections' do
+ -> { @server.sysaccept }.should block_caller
+ end
+
+ it 'returns file descriptor of an accepted connection' do
+ begin
+ sock = TCPSocket.new(SocketSpecs.hostname, @port)
+
+ fd = @server.sysaccept
+
+ fd.should be_kind_of(Integer)
+ ensure
+ sock.close if sock && !sock.closed?
+ IO.for_fd(fd).close if fd
+ end
+ end
+end
+
+describe 'TCPServer#sysaccept' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'without a connected client' do
+ it 'blocks the caller' do
+ -> { @server.sysaccept }.should block_caller
+ end
+ end
+
+ describe 'with a connected client' do
+ before do
+ @client = TCPSocket.new(ip_address, @server.connect_address.ip_port)
+ end
+
+ after do
+ Socket.for_fd(@fd).close if @fd
+ @client.close
+ end
+
+ it 'returns a new file descriptor as an Integer' do
+ @fd = @server.sysaccept
+
+ @fd.should be_kind_of(Integer)
+ @fd.should_not == @client.fileno
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/gethostbyname_spec.rb b/spec/ruby/library/socket/tcpsocket/gethostbyname_spec.rb
new file mode 100644
index 0000000000..5a2c704f35
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/gethostbyname_spec.rb
@@ -0,0 +1,119 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+# TODO: verify these for windows
+describe "TCPSocket.gethostbyname" do
+ before :each do
+ suppress_warning do
+ @host_info = TCPSocket.gethostbyname(SocketSpecs.hostname)
+ end
+ end
+
+ it "returns an array elements of information on the hostname" do
+ @host_info.should be_kind_of(Array)
+ end
+
+ platform_is_not :windows do
+ it "returns the canonical name as first value" do
+ @host_info[0].should == SocketSpecs.hostname
+ end
+
+ it "returns the address type as the third value" do
+ address_type = @host_info[2]
+ [Socket::AF_INET, Socket::AF_INET6].include?(address_type).should be_true
+ end
+
+ it "returns the IP address as the fourth value" do
+ ip = @host_info[3]
+ ["127.0.0.1", "::1"].include?(ip).should be_true
+ end
+ end
+
+ platform_is :windows do
+ quarantine! do # name lookup seems not working on Windows CI
+ it "returns the canonical name as first value" do
+ host = "#{ENV['COMPUTERNAME'].downcase}"
+ host << ".#{ENV['USERDNSDOMAIN'].downcase}" if ENV['USERDNSDOMAIN']
+ @host_info[0].should == host
+ end
+ end
+
+ it "returns the address type as the third value" do
+ @host_info[2].should == Socket::AF_INET
+ end
+
+ it "returns the IP address as the fourth value" do
+ @host_info[3].should == "127.0.0.1"
+ end
+ end
+
+ it "returns any aliases to the address as second value" do
+ @host_info[1].should be_kind_of(Array)
+ end
+end
+
+describe 'TCPSocket.gethostbyname' do
+ it 'returns an Array' do
+ suppress_warning do
+ TCPSocket.gethostbyname('127.0.0.1').should be_an_instance_of(Array)
+ end
+ end
+
+ describe 'using a hostname' do
+ describe 'the returned Array' do
+ before do
+ suppress_warning do
+ @array = TCPSocket.gethostbyname('127.0.0.1')
+ end
+ end
+
+ it 'includes the canonical name as the 1st value' do
+ @array[0].should == '127.0.0.1'
+ end
+
+ it 'includes an array of alternative hostnames as the 2nd value' do
+ @array[1].should be_an_instance_of(Array)
+ end
+
+ it 'includes the address family as the 3rd value' do
+ @array[2].should be_kind_of(Integer)
+ end
+
+ it 'includes the IP addresses as all the remaining values' do
+ ips = %w{::1 127.0.0.1}
+
+ ips.include?(@array[3]).should == true
+
+ # Not all machines might have both IPv4 and IPv6 set up, so this value is
+ # optional.
+ ips.include?(@array[4]).should == true if @array[4]
+ end
+ end
+ end
+
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'the returned Array' do
+ before do
+ suppress_warning do
+ @array = TCPSocket.gethostbyname(ip_address)
+ end
+ end
+
+ it 'includes the IP address as the 1st value' do
+ @array[0].should == ip_address
+ end
+
+ it 'includes an empty list of aliases as the 2nd value' do
+ @array[1].should == []
+ end
+
+ it 'includes the address family as the 3rd value' do
+ @array[2].should == family
+ end
+
+ it 'includes the IP address as the 4th value' do
+ @array[3].should == ip_address
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/initialize_spec.rb b/spec/ruby/library/socket/tcpsocket/initialize_spec.rb
new file mode 100644
index 0000000000..d7feb9751b
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/initialize_spec.rb
@@ -0,0 +1,100 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/new'
+
+describe 'TCPSocket#initialize' do
+ it_behaves_like :tcpsocket_new, :new
+
+ describe "with a running server" do
+ before :each do
+ @server = SocketSpecs::SpecTCPServer.new
+ @hostname = @server.hostname
+ end
+
+ after :each do
+ if @socket
+ @socket.write "QUIT"
+ @socket.close
+ end
+ @server.shutdown
+ end
+
+ it "does not use the given block and warns to use TCPSocket::open" do
+ -> {
+ @socket = TCPSocket.new(@hostname, @server.port, nil) { raise }
+ }.should complain(/warning: TCPSocket::new\(\) does not take block; use TCPSocket::open\(\) instead/)
+ end
+ end
+end
+
+describe 'TCPSocket#initialize' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'when no server is listening on the given address' do
+ it 'raises Errno::ECONNREFUSED' do
+ -> { TCPSocket.new(ip_address, 666) }.should raise_error(Errno::ECONNREFUSED)
+ end
+ end
+
+ describe 'when a server is listening on the given address' do
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @client.close if @client
+ @server.close
+ end
+
+ it 'returns a TCPSocket when using an Integer as the port' do
+ @client = TCPSocket.new(ip_address, @port)
+ @client.should be_an_instance_of(TCPSocket)
+ end
+
+ it 'returns a TCPSocket when using a String as the port' do
+ @client = TCPSocket.new(ip_address, @port.to_s)
+ @client.should be_an_instance_of(TCPSocket)
+ end
+
+ it 'raises SocketError when the port number is a non numeric String' do
+ -> { TCPSocket.new(ip_address, 'cats') }.should raise_error(SocketError)
+ end
+
+ it 'set the socket to binmode' do
+ @client = TCPSocket.new(ip_address, @port)
+ @client.binmode?.should be_true
+ end
+
+ it 'connects to the right address' do
+ @client = TCPSocket.new(ip_address, @port)
+
+ @client.remote_address.ip_address.should == @server.local_address.ip_address
+ @client.remote_address.ip_port.should == @server.local_address.ip_port
+ end
+
+ platform_is_not :windows do
+ it "creates a socket which is set to nonblocking" do
+ require 'io/nonblock'
+ @client = TCPSocket.new(ip_address, @port)
+ @client.should.nonblock?
+ end
+ end
+
+ it "creates a socket which is set to close on exec" do
+ @client = TCPSocket.new(ip_address, @port)
+ @client.should.close_on_exec?
+ end
+
+ describe 'using a local address and service' do
+ it 'binds the client socket to the local address and service' do
+ @client = TCPSocket.new(ip_address, @port, ip_address, 0)
+
+ @client.local_address.ip_address.should == ip_address
+
+ @client.local_address.ip_port.should > 0
+ @client.local_address.ip_port.should_not == @port
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/local_address_spec.rb b/spec/ruby/library/socket/tcpsocket/local_address_spec.rb
new file mode 100644
index 0000000000..ce66d5ff8f
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/local_address_spec.rb
@@ -0,0 +1,73 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'TCPSocket#local_address' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @host = @server.connect_address.ip_address
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'using an explicit hostname' do
+ before do
+ @sock = TCPSocket.new(@host, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.local_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_INET as the address family' do
+ @sock.local_address.afamily.should == family
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @sock.local_address.pfamily.should == family
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.local_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses the correct IP address' do
+ @sock.local_address.ip_address.should == @host
+ end
+
+ it 'uses a randomly assigned local port' do
+ @sock.local_address.ip_port.should > 0
+ @sock.local_address.ip_port.should_not == @port
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.local_address.protocol.should == 0
+ end
+ end
+ end
+
+ describe 'using an implicit hostname' do
+ before do
+ @sock = TCPSocket.new(nil, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct IP address' do
+ @sock.local_address.ip_address.should == @host
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/open_spec.rb b/spec/ruby/library/socket/tcpsocket/open_spec.rb
new file mode 100644
index 0000000000..0c0b579064
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/open_spec.rb
@@ -0,0 +1,6 @@
+require_relative "../../../spec_helper"
+require_relative 'shared/new'
+
+describe "TCPSocket.open" do
+ it_behaves_like :tcpsocket_new, :open
+end
diff --git a/spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb b/spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb
new file mode 100644
index 0000000000..d365ecd335
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/partially_closable_sockets'
+
+describe "TCPSocket partial closability" do
+
+ before :each do
+ @server = TCPServer.new("127.0.0.1", 0)
+ @s1 = TCPSocket.new("127.0.0.1", @server.addr[1])
+ @s2 = @server.accept
+ end
+
+ after :each do
+ @server.close
+ @s1.close
+ @s2.close
+ end
+
+ it_should_behave_like :partially_closable_sockets
+
+end
diff --git a/spec/ruby/library/socket/tcpsocket/recv_nonblock_spec.rb b/spec/ruby/library/socket/tcpsocket/recv_nonblock_spec.rb
new file mode 100644
index 0000000000..6ce5a41b58
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/recv_nonblock_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "TCPSocket#recv_nonblock" do
+ before :each do
+ @server = SocketSpecs::SpecTCPServer.new
+ @hostname = @server.hostname
+ end
+
+ after :each do
+ if @socket
+ @socket.write "QUIT"
+ @socket.close
+ end
+ @server.shutdown
+ end
+
+ it "returns a String read from the socket" do
+ @socket = TCPSocket.new @hostname, @server.port
+ @socket.write "TCPSocket#recv_nonblock"
+
+ # Wait for the server to echo. This spec is testing the return
+ # value, not the non-blocking behavior.
+ #
+ # TODO: Figure out a good way to test non-blocking.
+ IO.select([@socket])
+ @socket.recv_nonblock(50).should == "TCPSocket#recv_nonblock"
+ end
+
+ it 'writes the read to a buffer from the socket' do
+ @socket = TCPSocket.new @hostname, @server.port
+ @socket.write "TCPSocket#recv_nonblock"
+
+ # Wait for the server to echo. This spec is testing the return
+ # value, not the non-blocking behavior.
+ #
+ # TODO: Figure out a good way to test non-blocking.
+ IO.select([@socket])
+ buffer = "".b
+ @socket.recv_nonblock(50, 0, buffer)
+ buffer.should == 'TCPSocket#recv_nonblock'
+ end
+
+ it 'returns :wait_readable in exceptionless mode' do
+ @socket = TCPSocket.new @hostname, @server.port
+ @socket.recv_nonblock(50, exception: false).should == :wait_readable
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/recv_spec.rb b/spec/ruby/library/socket/tcpsocket/recv_spec.rb
new file mode 100644
index 0000000000..f380db670d
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/recv_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'TCPSocket#recv' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @client = TCPSocket.new(ip_address, @server.connect_address.ip_port)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns the message data' do
+ @client.write('hello')
+
+ socket = @server.accept
+
+ begin
+ socket.recv(5).should == 'hello'
+ ensure
+ socket.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/remote_address_spec.rb b/spec/ruby/library/socket/tcpsocket/remote_address_spec.rb
new file mode 100644
index 0000000000..eb9dabc075
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/remote_address_spec.rb
@@ -0,0 +1,72 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'TCPSocket#remote_address' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @host = @server.connect_address.ip_address
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'using an explicit hostname' do
+ before do
+ @sock = TCPSocket.new(@host, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.remote_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_INET as the address family' do
+ @sock.remote_address.afamily.should == family
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @sock.remote_address.pfamily.should == family
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.remote_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses the correct IP address' do
+ @sock.remote_address.ip_address.should == @host
+ end
+
+ it 'uses the correct port' do
+ @sock.remote_address.ip_port.should == @port
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.remote_address.protocol.should == 0
+ end
+ end
+ end
+
+ describe 'using an implicit hostname' do
+ before do
+ @sock = TCPSocket.new(nil, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct IP address' do
+ @sock.remote_address.ip_address.should == @host
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/setsockopt_spec.rb b/spec/ruby/library/socket/tcpsocket/setsockopt_spec.rb
new file mode 100644
index 0000000000..8b728b7522
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/setsockopt_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "TCPSocket#setsockopt" do
+ before :each do
+ @server = SocketSpecs::SpecTCPServer.new
+ @hostname = @server.hostname
+ @sock = TCPSocket.new @hostname, @server.port
+ end
+
+ after :each do
+ @sock.close unless @sock.closed?
+ @server.shutdown
+ end
+
+ describe "using constants" do
+ it "sets the TCP nodelay to 1" do
+ @sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1).should == 0
+ end
+ end
+
+ describe "using symbols" do
+ it "sets the TCP nodelay to 1" do
+ @sock.setsockopt(:IPPROTO_TCP, :TCP_NODELAY, 1).should == 0
+ end
+
+ context "without prefix" do
+ it "sets the TCP nodelay to 1" do
+ @sock.setsockopt(:TCP, :NODELAY, 1).should == 0
+ end
+ end
+ end
+
+ describe "using strings" do
+ it "sets the TCP nodelay to 1" do
+ @sock.setsockopt('IPPROTO_TCP', 'TCP_NODELAY', 1).should == 0
+ end
+
+ context "without prefix" do
+ it "sets the TCP nodelay to 1" do
+ @sock.setsockopt('TCP', 'NODELAY', 1).should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/shared/new.rb b/spec/ruby/library/socket/tcpsocket/shared/new.rb
new file mode 100644
index 0000000000..0e405253c8
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/shared/new.rb
@@ -0,0 +1,102 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/classes'
+
+describe :tcpsocket_new, shared: true do
+ it "requires a hostname and a port as arguments" do
+ -> { TCPSocket.send(@method) }.should raise_error(ArgumentError)
+ end
+
+ it "refuses the connection when there is no server to connect to" do
+ -> do
+ TCPSocket.send(@method, SocketSpecs.hostname, SocketSpecs.reserved_unused_port)
+ end.should raise_error(SystemCallError) {|e|
+ [Errno::ECONNREFUSED, Errno::EADDRNOTAVAIL].should include(e.class)
+ }
+ end
+
+ it 'raises IO::TimeoutError with :connect_timeout when no server is listening on the given address' do
+ -> {
+ TCPSocket.send(@method, "192.0.2.1", 80, connect_timeout: 0)
+ }.should raise_error(IO::TimeoutError)
+ rescue Errno::ENETUNREACH
+ # In the case all network interfaces down.
+ # raise_error cannot deal with multiple expected exceptions
+ end
+
+ describe "with a running server" do
+ before :each do
+ @server = SocketSpecs::SpecTCPServer.new
+ @hostname = @server.hostname
+ end
+
+ after :each do
+ if @socket
+ @socket.write "QUIT"
+ @socket.close
+ end
+ @server.shutdown
+ end
+
+ it "silently ignores 'nil' as the third parameter" do
+ @socket = TCPSocket.send(@method, @hostname, @server.port, nil)
+ @socket.should be_an_instance_of(TCPSocket)
+ end
+
+ it "connects to a listening server with host and port" do
+ @socket = TCPSocket.send(@method, @hostname, @server.port)
+ @socket.should be_an_instance_of(TCPSocket)
+ end
+
+ it "connects to a server when passed local_host argument" do
+ @socket = TCPSocket.send(@method, @hostname, @server.port, @hostname)
+ @socket.should be_an_instance_of(TCPSocket)
+ end
+
+ it "connects to a server when passed local_host and local_port arguments" do
+ retries = 0
+ max_retries = 3
+
+ begin
+ retries += 1
+ server = TCPServer.new(SocketSpecs.hostname, 0)
+ begin
+ available_port = server.addr[1]
+ ensure
+ server.close
+ end
+ @socket = TCPSocket.send(@method, @hostname, @server.port,
+ @hostname, available_port)
+ rescue Errno::EADDRINUSE
+ raise if retries >= max_retries
+ retry
+ end
+ @socket.should be_an_instance_of(TCPSocket)
+ end
+
+ it "has an address once it has connected to a listening server" do
+ @socket = TCPSocket.send(@method, @hostname, @server.port)
+ @socket.should be_an_instance_of(TCPSocket)
+
+ # TODO: Figure out how to abstract this. You can get AF_INET
+ # from 'Socket.getaddrinfo(hostname, nil)[0][3]' but socket.addr
+ # will return AF_INET6. At least this check will weed out clearly
+ # erroneous values.
+ @socket.addr[0].should =~ /^AF_INET6?/
+
+ case @socket.addr[0]
+ when 'AF_INET'
+ @socket.addr[3].should == SocketSpecs.addr(:ipv4)
+ when 'AF_INET6'
+ @socket.addr[3].should == SocketSpecs.addr(:ipv6)
+ end
+
+ @socket.addr[1].should be_kind_of(Integer)
+ @socket.addr[2].should =~ /^#{@hostname}/
+ end
+
+ it "connects to a server when passed connect_timeout argument" do
+ @socket = TCPSocket.send(@method, @hostname, @server.port, connect_timeout: 1)
+ @socket.should be_an_instance_of(TCPSocket)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/bind_spec.rb b/spec/ruby/library/socket/udpsocket/bind_spec.rb
new file mode 100644
index 0000000000..08b386e941
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/bind_spec.rb
@@ -0,0 +1,83 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UDPSocket#bind" do
+ before :each do
+ @socket = UDPSocket.new
+ end
+
+ after :each do
+ @socket.close unless @socket.closed?
+ end
+
+ it "binds the socket to a port" do
+ @socket.bind(SocketSpecs.hostname, 0)
+ @socket.addr[1].should be_kind_of(Integer)
+ end
+
+ it "raises Errno::EINVAL when already bound" do
+ @socket.bind(SocketSpecs.hostname, 0)
+
+ -> {
+ @socket.bind(SocketSpecs.hostname, @socket.addr[1])
+ }.should raise_error(Errno::EINVAL)
+ end
+
+ it "receives a hostname and a port" do
+ @socket.bind(SocketSpecs.hostname, 0)
+
+ port, host = Socket.unpack_sockaddr_in(@socket.getsockname)
+
+ host.should == "127.0.0.1"
+ port.should == @socket.addr[1]
+ end
+
+ it "binds to INADDR_ANY if the hostname is empty" do
+ @socket.bind("", 0).should == 0
+ port, host = Socket.unpack_sockaddr_in(@socket.getsockname)
+ host.should == "0.0.0.0"
+ port.should == @socket.addr[1]
+ end
+end
+
+describe 'UDPSocket#bind' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @socket = UDPSocket.new(family)
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'binds to an address and port' do
+ @socket.bind(ip_address, 0).should == 0
+
+ @socket.local_address.ip_address.should == ip_address
+ @socket.local_address.ip_port.should > 0
+ end
+
+ it 'binds to an address and port using String arguments' do
+ @socket.bind(ip_address, '0').should == 0
+
+ @socket.local_address.ip_address.should == ip_address
+ @socket.local_address.ip_port.should > 0
+ end
+
+ it 'can receive data after being bound to an address' do
+ @socket.bind(ip_address, 0)
+
+ addr = @socket.connect_address
+ client = UDPSocket.new(family)
+
+ client.connect(addr.ip_address, addr.ip_port)
+ client.write('hello')
+
+ begin
+ @socket.recv(6).should == 'hello'
+ ensure
+ client.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/connect_spec.rb b/spec/ruby/library/socket/udpsocket/connect_spec.rb
new file mode 100644
index 0000000000..d92bdeb981
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/connect_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UDPSocket#connect' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @socket = UDPSocket.new(family)
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'connects to an address even when it is not used' do
+ @socket.connect(ip_address, 9996).should == 0
+ end
+
+ it 'can send data after connecting' do
+ receiver = UDPSocket.new(family)
+
+ receiver.bind(ip_address, 0)
+
+ addr = receiver.connect_address
+
+ @socket.connect(addr.ip_address, addr.ip_port)
+ @socket.write('hello')
+
+ begin
+ receiver.recv(6).should == 'hello'
+ ensure
+ receiver.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/initialize_spec.rb b/spec/ruby/library/socket/udpsocket/initialize_spec.rb
new file mode 100644
index 0000000000..ecf0043c10
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/initialize_spec.rb
@@ -0,0 +1,53 @@
+require_relative '../spec_helper'
+
+describe 'UDPSocket#initialize' do
+ after do
+ @socket.close if @socket
+ end
+
+ it 'initializes a new UDPSocket' do
+ @socket = UDPSocket.new
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'initializes a new UDPSocket using an Integer' do
+ @socket = UDPSocket.new(Socket::AF_INET)
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'initializes a new UDPSocket using a Symbol' do
+ @socket = UDPSocket.new(:INET)
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'initializes a new UDPSocket using a String' do
+ @socket = UDPSocket.new('INET')
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'sets the socket to binmode' do
+ @socket = UDPSocket.new(:INET)
+ @socket.binmode?.should be_true
+ end
+
+ platform_is_not :windows do
+ it 'sets the socket to nonblock' do
+ require 'io/nonblock'
+ @socket = UDPSocket.new(:INET)
+ @socket.should.nonblock?
+ end
+ end
+
+ it 'sets the socket to close on exec' do
+ @socket = UDPSocket.new(:INET)
+ @socket.should.close_on_exec?
+ end
+
+ it 'raises Errno::EAFNOSUPPORT or Errno::EPROTONOSUPPORT when given an invalid address family' do
+ -> {
+ UDPSocket.new(666)
+ }.should raise_error(SystemCallError) { |e|
+ [Errno::EAFNOSUPPORT, Errno::EPROTONOSUPPORT].should include(e.class)
+ }
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/inspect_spec.rb b/spec/ruby/library/socket/udpsocket/inspect_spec.rb
new file mode 100644
index 0000000000..e212120b14
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/inspect_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../spec_helper'
+
+describe 'UDPSocket#inspect' do
+ before do
+ @socket = UDPSocket.new
+ @socket.bind('127.0.0.1', 0)
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'returns a String with the fd, family, address and port' do
+ port = @socket.addr[1]
+ @socket.inspect.should == "#<UDPSocket:fd #{@socket.fileno}, AF_INET, 127.0.0.1, #{port}>"
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/local_address_spec.rb b/spec/ruby/library/socket/udpsocket/local_address_spec.rb
new file mode 100644
index 0000000000..92e4cc10c7
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/local_address_spec.rb
@@ -0,0 +1,80 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UDPSocket#local_address' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :DGRAM, Socket::IPPROTO_UDP)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+
+ @host = @server.connect_address.ip_address
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'using an explicit hostname' do
+ before do
+ @sock = UDPSocket.new(family)
+
+ @sock.connect(@host, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.local_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct address family' do
+ @sock.local_address.afamily.should == family
+ end
+
+ it 'uses the correct protocol family' do
+ @sock.local_address.pfamily.should == family
+ end
+
+ it 'uses SOCK_DGRAM as the socket type' do
+ @sock.local_address.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses the correct IP address' do
+ @sock.local_address.ip_address.should == @host
+ end
+
+ it 'uses a randomly assigned local port' do
+ @sock.local_address.ip_port.should > 0
+ @sock.local_address.ip_port.should_not == @port
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.local_address.protocol.should == 0
+ end
+ end
+ end
+
+ describe 'using an implicit hostname' do
+ before do
+ @sock = UDPSocket.new(family)
+
+ @sock.connect(nil, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct IP address' do
+ @sock.local_address.ip_address.should == @host
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/new_spec.rb b/spec/ruby/library/socket/udpsocket/new_spec.rb
new file mode 100644
index 0000000000..79bfcb624d
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/new_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UDPSocket.new' do
+ after :each do
+ @socket.close if @socket && !@socket.closed?
+ end
+
+ it 'without arguments' do
+ @socket = UDPSocket.new
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'using Integer argument' do
+ @socket = UDPSocket.new(Socket::AF_INET)
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'using Symbol argument' do
+ @socket = UDPSocket.new(:INET)
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'using String argument' do
+ @socket = UDPSocket.new('INET')
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it "does not use the given block and warns to use UDPSocket::open" do
+ -> {
+ @socket = UDPSocket.new { raise }
+ }.should complain(/warning: UDPSocket::new\(\) does not take block; use UDPSocket::open\(\) instead/)
+ end
+
+ it 'raises Errno::EAFNOSUPPORT or Errno::EPROTONOSUPPORT if unsupported family passed' do
+ -> { UDPSocket.new(-1) }.should raise_error(SystemCallError) { |e|
+ [Errno::EAFNOSUPPORT, Errno::EPROTONOSUPPORT].should include(e.class)
+ }
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/open_spec.rb b/spec/ruby/library/socket/udpsocket/open_spec.rb
new file mode 100644
index 0000000000..e4dbb2ee2a
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/open_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UDPSocket.open" do
+ after :each do
+ @socket.close if @socket && !@socket.closed?
+ end
+
+ it "allows calls to open without arguments" do
+ @socket = UDPSocket.open
+ @socket.should be_kind_of(UDPSocket)
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb b/spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb
new file mode 100644
index 0000000000..b804099589
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb
@@ -0,0 +1,111 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UDPSocket#recvfrom_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address, family_name|
+ before do
+ @server = UDPSocket.new(family)
+ @client = UDPSocket.new(family)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ describe 'using an unbound socket' do
+ it 'raises IO::WaitReadable' do
+ -> { @server.recvfrom_nonblock(1) }.should raise_error(IO::WaitReadable)
+ end
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(ip_address, 0)
+
+ addr = @server.connect_address
+
+ @client.connect(addr.ip_address, addr.ip_port)
+ end
+
+ describe 'without any data available' do
+ it 'raises IO::WaitReadable' do
+ -> { @server.recvfrom_nonblock(1) }.should raise_error(IO::WaitReadable)
+ end
+
+ it 'returns :wait_readable with exception: false' do
+ @server.recvfrom_nonblock(1, exception: false).should == :wait_readable
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ end
+
+ it 'returns an Array containing the data and an Array' do
+ IO.select([@server])
+ @server.recvfrom_nonblock(1).should be_an_instance_of(Array)
+ end
+
+ it 'writes the data to the buffer when one is present' do
+ buffer = "".b
+ IO.select([@server])
+ @server.recvfrom_nonblock(1, 0, buffer)
+ buffer.should == 'h'
+ end
+
+ it "preserves the encoding of the given buffer" do
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ IO.select([@server])
+ message, = @server.recvfrom_nonblock(1, 0, buffer)
+
+ message.should.equal?(buffer)
+ buffer.encoding.should == Encoding::ISO_8859_1
+ end
+
+ describe 'the returned Array' do
+ before do
+ IO.select([@server])
+ @array = @server.recvfrom_nonblock(1)
+ end
+
+ it 'contains the data at index 0' do
+ @array[0].should == 'h'
+ end
+
+ it 'contains an Array at index 1' do
+ @array[1].should be_an_instance_of(Array)
+ end
+ end
+
+ describe 'the returned address Array' do
+ before do
+ IO.select([@server])
+ @addr = @server.recvfrom_nonblock(1)[1]
+ end
+
+ it 'uses the correct address family' do
+ @addr[0].should == family_name
+ end
+
+ it 'uses the port of the client' do
+ @addr[1].should == @client.local_address.ip_port
+ end
+
+ it 'uses the hostname of the client' do
+ @addr[2].should == ip_address
+ end
+
+ it 'uses the IP address of the client' do
+ @addr[3].should == ip_address
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/remote_address_spec.rb b/spec/ruby/library/socket/udpsocket/remote_address_spec.rb
new file mode 100644
index 0000000000..94889ce560
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/remote_address_spec.rb
@@ -0,0 +1,79 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UDPSocket#remote_address' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :DGRAM, Socket::IPPROTO_UDP)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+
+ @host = @server.connect_address.ip_address
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'using an explicit hostname' do
+ before do
+ @sock = UDPSocket.new(family)
+
+ @sock.connect(@host, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.remote_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct address family' do
+ @sock.remote_address.afamily.should == family
+ end
+
+ it 'uses the correct protocol family' do
+ @sock.remote_address.pfamily.should == family
+ end
+
+ it 'uses SOCK_DGRAM as the socket type' do
+ @sock.remote_address.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses the correct IP address' do
+ @sock.remote_address.ip_address.should == @host
+ end
+
+ it 'uses the correct port' do
+ @sock.remote_address.ip_port.should == @port
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.remote_address.protocol.should == 0
+ end
+ end
+ end
+
+ describe 'using an implicit hostname' do
+ before do
+ @sock = UDPSocket.new(family)
+
+ @sock.connect(nil, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct IP address' do
+ @sock.remote_address.ip_address.should == @host
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/send_spec.rb b/spec/ruby/library/socket/udpsocket/send_spec.rb
new file mode 100644
index 0000000000..6dd5f67bea
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/send_spec.rb
@@ -0,0 +1,154 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UDPSocket#send" do
+ before :each do
+ @port = nil
+ @server_thread = Thread.new do
+ @server = UDPSocket.open
+ begin
+ @server.bind(nil, 0)
+ @port = @server.addr[1]
+ begin
+ @msg = @server.recvfrom_nonblock(64)
+ rescue IO::WaitReadable
+ IO.select([@server])
+ retry
+ end
+ ensure
+ @server.close if !@server.closed?
+ end
+ end
+ Thread.pass while @server_thread.status and !@port
+ end
+
+ after :each do
+ @server_thread.join
+ end
+
+ it "sends data in ad hoc mode" do
+ @socket = UDPSocket.open
+ @socket.send("ad hoc", 0, SocketSpecs.hostname, @port)
+ @socket.close
+ @server_thread.join
+
+ @msg[0].should == "ad hoc"
+ @msg[1][0].should == "AF_INET"
+ @msg[1][1].should be_kind_of(Integer)
+ @msg[1][3].should == "127.0.0.1"
+ end
+
+ it "sends data in ad hoc mode (with port given as a String)" do
+ @socket = UDPSocket.open
+ @socket.send("ad hoc", 0, SocketSpecs.hostname, @port.to_s)
+ @socket.close
+ @server_thread.join
+
+ @msg[0].should == "ad hoc"
+ @msg[1][0].should == "AF_INET"
+ @msg[1][1].should be_kind_of(Integer)
+ @msg[1][3].should == "127.0.0.1"
+ end
+
+ it "sends data in connection mode" do
+ @socket = UDPSocket.open
+ @socket.connect(SocketSpecs.hostname, @port)
+ @socket.send("connection-based", 0)
+ @socket.close
+ @server_thread.join
+
+ @msg[0].should == "connection-based"
+ @msg[1][0].should == "AF_INET"
+ @msg[1][1].should be_kind_of(Integer)
+ @msg[1][3].should == "127.0.0.1"
+ end
+
+ it "raises EMSGSIZE if data is too big" do
+ @socket = UDPSocket.open
+ begin
+ -> do
+ @socket.send('1' * 100_000, 0, SocketSpecs.hostname, @port.to_s)
+ end.should raise_error(Errno::EMSGSIZE)
+ ensure
+ @socket.send("ad hoc", 0, SocketSpecs.hostname, @port)
+ @socket.close
+ @server_thread.join
+ end
+ end
+end
+
+describe 'UDPSocket#send' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = UDPSocket.new(family)
+ @client = UDPSocket.new(family)
+
+ @server.bind(ip_address, 0)
+
+ @addr = @server.connect_address
+ end
+
+ after do
+ @server.close
+ @client.close
+ end
+
+ describe 'using a disconnected socket' do
+ describe 'without a destination address' do
+ it "raises #{SocketSpecs.dest_addr_req_error}" do
+ -> { @client.send('hello', 0) }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+
+ describe 'with a destination address as separate arguments' do
+ it 'returns the amount of sent bytes' do
+ @client.send('hello', 0, @addr.ip_address, @addr.ip_port).should == 5
+ end
+
+ it 'does not persist the connection after sending data' do
+ @client.send('hello', 0, @addr.ip_address, @addr.ip_port)
+
+ -> { @client.send('hello', 0) }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+
+ describe 'with a destination address as a single String argument' do
+ it 'returns the amount of sent bytes' do
+ @client.send('hello', 0, @server.getsockname).should == 5
+ end
+ end
+ end
+
+ describe 'using a connected socket' do
+ describe 'without an explicit destination address' do
+ before do
+ @client.connect(@addr.ip_address, @addr.ip_port)
+ end
+
+ it 'returns the amount of bytes written' do
+ @client.send('hello', 0).should == 5
+ end
+ end
+
+ describe 'with an explicit destination address' do
+ before do
+ @alt_server = UDPSocket.new(family)
+
+ @alt_server.bind(ip_address, 0)
+ end
+
+ after do
+ @alt_server.close
+ end
+
+ it 'sends the data to the given address instead' do
+ @client.send('hello', 0, @alt_server.getsockname).should == 5
+
+ -> { @server.recv(5) }.should block_caller
+
+ @alt_server.recv(5).should == 'hello'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/write_spec.rb b/spec/ruby/library/socket/udpsocket/write_spec.rb
new file mode 100644
index 0000000000..c971f29b62
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/write_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UDPSocket#write" do
+ it "raises EMSGSIZE if msg is too long" do
+ begin
+ host = SocketSpecs.hostname
+ s1 = UDPSocket.new
+ s1.bind(host, 0)
+ s2 = UDPSocket.new
+ s2.connect(host, s1.addr[1])
+
+ -> do
+ s2.write('1' * 100_000)
+ end.should raise_error(Errno::EMSGSIZE)
+ ensure
+ s1.close if s1 && !s1.closed?
+ s2.close if s2 && !s2.closed?
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb b/spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb
new file mode 100644
index 0000000000..f67941b296
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb
@@ -0,0 +1,87 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UNIXServer#accept_nonblock" do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ @client = UNIXSocket.open(@path)
+
+ @socket = @server.accept_nonblock
+ @client.send("foobar", 0)
+ end
+
+ after :each do
+ @socket.close
+ @client.close
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "accepts a connection in a non-blocking way" do
+ data = @socket.recvfrom(6).first
+ data.should == "foobar"
+ end
+
+ it "returns a UNIXSocket" do
+ @socket.should be_kind_of(UNIXSocket)
+ end
+
+ it 'returns :wait_readable in exceptionless mode' do
+ @server.accept_nonblock(exception: false).should == :wait_readable
+ end
+end
+
+describe 'UNIXServer#accept_nonblock' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close
+ rm_r(@path)
+ end
+
+ describe 'without a client' do
+ it 'raises IO::WaitReadable' do
+ -> { @server.accept_nonblock }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ describe 'with a client' do
+ before do
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @socket.close if @socket
+ end
+
+ describe 'without any data' do
+ it 'returns a UNIXSocket' do
+ @socket = @server.accept_nonblock
+ @socket.should be_an_instance_of(UNIXSocket)
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ end
+
+ it 'returns a UNIXSocket' do
+ @socket = @server.accept_nonblock
+ @socket.should be_an_instance_of(UNIXSocket)
+ end
+
+ describe 'the returned UNIXSocket' do
+ it 'can read the data written' do
+ @socket = @server.accept_nonblock
+ @socket.recv(5).should == 'hello'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/accept_spec.rb b/spec/ruby/library/socket/unixserver/accept_spec.rb
new file mode 100644
index 0000000000..cc2c922b6f
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/accept_spec.rb
@@ -0,0 +1,126 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UNIXServer#accept" do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ end
+
+ after :each do
+ @server.close if @server
+ SocketSpecs.rm_socket @path
+ end
+
+ it "accepts what is written by the client" do
+ client = UNIXSocket.open(@path)
+
+ client.send('hello', 0)
+
+ sock = @server.accept
+ begin
+ data, info = sock.recvfrom(5)
+
+ data.should == 'hello'
+ info.should_not be_empty
+ ensure
+ sock.close
+ client.close
+ end
+ end
+
+ it "can be interrupted by Thread#kill" do
+ t = Thread.new {
+ @server.accept
+ }
+ Thread.pass while t.status and t.status != "sleep"
+
+ # kill thread, ensure it dies in a reasonable amount of time
+ t.kill
+ a = 0
+ while t.alive? and a < 5000
+ sleep 0.001
+ a += 1
+ end
+ a.should < 5000
+ end
+
+ it "can be interrupted by Thread#raise" do
+ t = Thread.new {
+ -> {
+ @server.accept
+ }.should raise_error(Exception, "interrupted")
+ }
+
+ Thread.pass while t.status and t.status != "sleep"
+ t.raise Exception, "interrupted"
+ t.join
+ end
+end
+
+describe 'UNIXServer#accept' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close
+ rm_r(@path)
+ end
+
+ describe 'without a client' do
+ it 'blocks the calling thread' do
+ -> { @server.accept }.should block_caller
+ end
+ end
+
+ describe 'with a client' do
+ before do
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @socket.close if @socket
+ end
+
+ describe 'without any data' do
+ it 'returns a UNIXSocket' do
+ @socket = @server.accept
+ @socket.should be_an_instance_of(UNIXSocket)
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ end
+
+ it 'returns a UNIXSocket' do
+ @socket = @server.accept
+ @socket.should be_an_instance_of(UNIXSocket)
+ end
+
+ describe 'the returned UNIXSocket' do
+ it 'can read the data written' do
+ @socket = @server.accept
+ @socket.recv(5).should == 'hello'
+ end
+
+ platform_is_not :windows do
+ it "is set to nonblocking" do
+ require 'io/nonblock'
+ @socket = @server.accept
+ @socket.should.nonblock?
+ end
+ end
+
+ it "is set to close on exec" do
+ @socket = @server.accept
+ @socket.should.close_on_exec?
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/for_fd_spec.rb b/spec/ruby/library/socket/unixserver/for_fd_spec.rb
new file mode 100644
index 0000000000..be1c2df4d7
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/for_fd_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UNIXServer.for_fd" do
+ before :each do
+ @unix_path = SocketSpecs.socket_path
+ @unix = UNIXServer.new(@unix_path)
+ end
+
+ after :each do
+ @unix.close if @unix
+ SocketSpecs.rm_socket @unix_path
+ end
+
+ it "can calculate the path" do
+ b = UNIXServer.for_fd(@unix.fileno)
+ b.autoclose = false
+
+ b.path.should == @unix_path
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/initialize_spec.rb b/spec/ruby/library/socket/unixserver/initialize_spec.rb
new file mode 100644
index 0000000000..3728a307b0
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/initialize_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UNIXServer#initialize' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close if @server
+ rm_r @path
+ end
+
+ it 'returns a new UNIXServer' do
+ @server.should be_an_instance_of(UNIXServer)
+ end
+
+ it 'sets the socket to binmode' do
+ @server.binmode?.should be_true
+ end
+
+ it 'raises Errno::EADDRINUSE when the socket is already in use' do
+ -> { UNIXServer.new(@path) }.should raise_error(Errno::EADDRINUSE)
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/listen_spec.rb b/spec/ruby/library/socket/unixserver/listen_spec.rb
new file mode 100644
index 0000000000..7938d648c4
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/listen_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UNIXServer#listen' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close
+
+ rm_r(@path)
+ end
+
+ it 'returns 0' do
+ @server.listen(1).should == 0
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/new_spec.rb b/spec/ruby/library/socket/unixserver/new_spec.rb
new file mode 100644
index 0000000000..7d0c7bf76e
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/new_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../spec_helper'
+require_relative 'shared/new'
+
+describe "UNIXServer.new" do
+ it_behaves_like :unixserver_new, :new
+
+ it "does not use the given block and warns to use UNIXServer::open" do
+ -> {
+ @server = UNIXServer.new(@path) { raise }
+ }.should complain(/warning: UNIXServer::new\(\) does not take block; use UNIXServer::open\(\) instead/)
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/open_spec.rb b/spec/ruby/library/socket/unixserver/open_spec.rb
new file mode 100644
index 0000000000..c49df802d0
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/open_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/new'
+
+describe "UNIXServer.open" do
+ it_behaves_like :unixserver_new, :open
+
+ before :each do
+ @path = SocketSpecs.socket_path
+ end
+
+ after :each do
+ @server.close if @server
+ @server = nil
+ SocketSpecs.rm_socket @path
+ end
+
+ it "yields the new UNIXServer object to the block, if given" do
+ UNIXServer.open(@path) do |unix|
+ unix.path.should == @path
+ unix.addr.should == ["AF_UNIX", @path]
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/shared/new.rb b/spec/ruby/library/socket/unixserver/shared/new.rb
new file mode 100644
index 0000000000..b537f2a871
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/shared/new.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/classes'
+
+describe :unixserver_new, shared: true do
+ before :each do
+ @path = SocketSpecs.socket_path
+ end
+
+ after :each do
+ @server.close if @server
+ @server = nil
+ SocketSpecs.rm_socket @path
+ end
+
+ it "creates a new UNIXServer" do
+ @server = UNIXServer.send(@method, @path)
+ @server.path.should == @path
+ @server.addr.should == ["AF_UNIX", @path]
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/sysaccept_spec.rb b/spec/ruby/library/socket/unixserver/sysaccept_spec.rb
new file mode 100644
index 0000000000..c4a4ecc824
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/sysaccept_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UNIXServer#sysaccept' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close
+
+ rm_r(@path)
+ end
+
+ describe 'without a client' do
+ it 'blocks the calling thread' do
+ -> { @server.sysaccept }.should block_caller
+ end
+ end
+
+ describe 'with a client' do
+ before do
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ Socket.for_fd(@fd).close if @fd
+ @client.close
+ end
+
+ describe 'without any data' do
+ it 'returns an Integer' do
+ @fd = @server.sysaccept
+ @fd.should be_kind_of(Integer)
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ end
+
+ it 'returns an Integer' do
+ @fd = @server.sysaccept
+ @fd.should be_kind_of(Integer)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/addr_spec.rb b/spec/ruby/library/socket/unixsocket/addr_spec.rb
new file mode 100644
index 0000000000..1afe9b12dc
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/addr_spec.rb
@@ -0,0 +1,33 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UNIXSocket#addr" do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ @client = UNIXSocket.open(@path)
+ end
+
+ after :each do
+ @client.close
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "returns an array" do
+ @client.addr.should be_kind_of(Array)
+ end
+
+ it "returns the address family of this socket in an array" do
+ @client.addr[0].should == "AF_UNIX"
+ @server.addr[0].should == "AF_UNIX"
+ end
+
+ it "returns the path of the socket in an array if it's a server" do
+ @server.addr[1].should == @path
+ end
+
+ it "returns an empty string for path if it's a client" do
+ @client.addr[1].should == ""
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/initialize_spec.rb b/spec/ruby/library/socket/unixsocket/initialize_spec.rb
new file mode 100644
index 0000000000..5dccfcc745
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/initialize_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UNIXSocket#initialize' do
+ describe 'using a non existing path' do
+ platform_is_not :windows do
+ it 'raises Errno::ENOENT' do
+ -> { UNIXSocket.new(SocketSpecs.socket_path) }.should raise_error(Errno::ENOENT)
+ end
+ end
+
+ platform_is :windows do
+ # Why, Windows, why?
+ it 'raises Errno::ECONNREFUSED' do
+ -> { UNIXSocket.new(SocketSpecs.socket_path) }.should raise_error(Errno::ECONNREFUSED)
+ end
+ end
+ end
+
+ describe 'using an existing socket path' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @socket = UNIXSocket.new(@path)
+ end
+
+ after do
+ @socket.close
+ @server.close
+ rm_r(@path)
+ end
+
+ it 'returns a new UNIXSocket' do
+ @socket.should be_an_instance_of(UNIXSocket)
+ end
+
+ it 'sets the socket path to an empty String' do
+ @socket.path.should == ''
+ end
+
+ it 'sets the socket to binmode' do
+ @socket.binmode?.should be_true
+ end
+
+ platform_is_not :windows do
+ it 'sets the socket to nonblock' do
+ require 'io/nonblock'
+ @socket.should.nonblock?
+ end
+ end
+
+ it 'sets the socket to close on exec' do
+ @socket.should.close_on_exec?
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/inspect_spec.rb b/spec/ruby/library/socket/unixsocket/inspect_spec.rb
new file mode 100644
index 0000000000..77bb521069
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/inspect_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UNIXSocket#inspect" do
+ it "returns sockets fd for unnamed sockets" do
+ begin
+ s1, s2 = UNIXSocket.socketpair
+ s1.inspect.should == "#<UNIXSocket:fd #{s1.fileno}>"
+ s2.inspect.should == "#<UNIXSocket:fd #{s2.fileno}>"
+ ensure
+ s1.close
+ s2.close
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/local_address_spec.rb b/spec/ruby/library/socket/unixsocket/local_address_spec.rb
new file mode 100644
index 0000000000..0fdec38293
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/local_address_spec.rb
@@ -0,0 +1,92 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UNIXSocket#local_address' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @server.close
+
+ rm_r(@path)
+ end
+
+ it 'returns an Addrinfo' do
+ @client.local_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ platform_is_not :aix do
+ it 'uses AF_UNIX as the address family' do
+ @client.local_address.afamily.should == Socket::AF_UNIX
+ end
+
+ it 'uses PF_UNIX as the protocol family' do
+ @client.local_address.pfamily.should == Socket::PF_UNIX
+ end
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @client.local_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ platform_is_not :aix do
+ it 'uses an empty socket path' do
+ @client.local_address.unix_path.should == ''
+ end
+ end
+
+ it 'uses 0 as the protocol' do
+ @client.local_address.protocol.should == 0
+ end
+ end
+end
+
+describe 'UNIXSocket#local_address with a UNIX socket pair' do
+ before :each do
+ @sock, @sock2 = Socket.pair(Socket::AF_UNIX, Socket::SOCK_STREAM)
+ end
+
+ after :each do
+ @sock.close
+ @sock2.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.local_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_UNIX as the address family' do
+ @sock.local_address.afamily.should == Socket::AF_UNIX
+ end
+
+ it 'uses PF_UNIX as the protocol family' do
+ @sock.local_address.pfamily.should == Socket::PF_UNIX
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.local_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'raises SocketError for #ip_address' do
+ -> {
+ @sock.local_address.ip_address
+ }.should raise_error(SocketError, "need IPv4 or IPv6 address")
+ end
+
+ it 'raises SocketError for #ip_port' do
+ -> {
+ @sock.local_address.ip_port
+ }.should raise_error(SocketError, "need IPv4 or IPv6 address")
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.local_address.protocol.should == 0
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/new_spec.rb b/spec/ruby/library/socket/unixsocket/new_spec.rb
new file mode 100644
index 0000000000..fea2c1e2b7
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/new_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../spec_helper'
+require_relative 'shared/new'
+
+describe "UNIXSocket.new" do
+ it_behaves_like :unixsocket_new, :new
+
+ it "does not use the given block and warns to use UNIXSocket::open" do
+ -> {
+ @client = UNIXSocket.new(@path) { raise }
+ }.should complain(/warning: UNIXSocket::new\(\) does not take block; use UNIXSocket::open\(\) instead/)
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/open_spec.rb b/spec/ruby/library/socket/unixsocket/open_spec.rb
new file mode 100644
index 0000000000..b5e8c6c23a
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/open_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative 'shared/new'
+
+describe "UNIXSocket.open" do
+ it_behaves_like :unixsocket_new, :open
+end
+
+describe "UNIXSocket.open" do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ end
+
+ after :each do
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "opens a unix socket on the specified file and yields it to the block" do
+ UNIXSocket.open(@path) do |client|
+ client.addr[0].should == "AF_UNIX"
+ client.should_not.closed?
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/pair_spec.rb b/spec/ruby/library/socket/unixsocket/pair_spec.rb
new file mode 100644
index 0000000000..9690142668
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/pair_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/partially_closable_sockets'
+require_relative 'shared/pair'
+
+describe "UNIXSocket.pair" do
+ it_should_behave_like :unixsocket_pair
+ it_should_behave_like :partially_closable_sockets
+
+ before :each do
+ @s1, @s2 = UNIXSocket.pair
+ end
+
+ after :each do
+ @s1.close
+ @s2.close
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/partially_closable_spec.rb b/spec/ruby/library/socket/unixsocket/partially_closable_spec.rb
new file mode 100644
index 0000000000..108a6c3063
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/partially_closable_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/partially_closable_sockets'
+
+describe "UNIXSocket partial closability" do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ @s1 = UNIXSocket.new(@path)
+ @s2 = @server.accept
+ end
+
+ after :each do
+ @server.close
+ @s1.close
+ @s2.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it_should_behave_like :partially_closable_sockets
+end
diff --git a/spec/ruby/library/socket/unixsocket/path_spec.rb b/spec/ruby/library/socket/unixsocket/path_spec.rb
new file mode 100644
index 0000000000..ffe7e4bea2
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/path_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UNIXSocket#path" do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ @client = UNIXSocket.open(@path)
+ end
+
+ after :each do
+ @client.close
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "returns the path of the socket if it's a server" do
+ @server.path.should == @path
+ end
+
+ it "returns an empty string for path if it's a client" do
+ @client.path.should == ""
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/peeraddr_spec.rb b/spec/ruby/library/socket/unixsocket/peeraddr_spec.rb
new file mode 100644
index 0000000000..10cab13b42
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/peeraddr_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UNIXSocket#peeraddr" do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ @client = UNIXSocket.open(@path)
+ end
+
+ after :each do
+ @client.close
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "returns the address family and path of the server end of the connection" do
+ @client.peeraddr.should == ["AF_UNIX", @path]
+ end
+
+ it "raises an error in server sockets" do
+ -> {
+ @server.peeraddr
+ }.should raise_error(Errno::ENOTCONN)
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/recv_io_spec.rb b/spec/ruby/library/socket/unixsocket/recv_io_spec.rb
new file mode 100644
index 0000000000..f0b408f309
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/recv_io_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+platform_is_not :windows do
+ describe "UNIXSocket#recv_io" do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ @client = UNIXSocket.open(@path)
+
+ @send_io_path = File.expand_path('../../fixtures/send_io.txt', __FILE__)
+ @file = File.open(@send_io_path)
+ end
+
+ after :each do
+ @io.close if @io
+ @socket.close if @socket
+
+ @file.close
+ @client.close
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "reads an IO object across the socket" do
+ @client.send_io(@file)
+
+ @socket = @server.accept
+ @io = @socket.recv_io
+
+ @io.read.should == File.read(@send_io_path)
+ end
+
+ it "takes an optional class to use" do
+ @client.send_io(@file)
+
+ @socket = @server.accept
+ @io = @socket.recv_io(File)
+
+ @io.should be_an_instance_of(File)
+ end
+ end
+
+ describe 'UNIXSocket#recv_io' do
+ before do
+ @file = File.open('/dev/null', 'w')
+ @client, @server = UNIXSocket.socketpair
+ end
+
+ after do
+ @client.close
+ @server.close
+ @io.close if @io
+ @file.close
+ end
+
+ describe 'without a custom class' do
+ it 'returns an IO' do
+ @client.send_io(@file)
+
+ @io = @server.recv_io
+ @io.should be_an_instance_of(IO)
+ end
+ end
+
+ describe 'with a custom class' do
+ it 'returns an instance of the custom class' do
+ @client.send_io(@file)
+
+ @io = @server.recv_io(File)
+ @io.should be_an_instance_of(File)
+ end
+ end
+
+ describe 'with a custom mode' do
+ it 'opens the IO using the given mode' do
+ @client.send_io(@file)
+
+ @io = @server.recv_io(File, File::WRONLY)
+ @io.should be_an_instance_of(File)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb b/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb
new file mode 100644
index 0000000000..9ae3777961
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb
@@ -0,0 +1,174 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe "UNIXSocket#recvfrom" do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ @client = UNIXSocket.open(@path)
+ end
+
+ after :each do
+ @client.close
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "receives len bytes from sock, returning an array containing sent data as first element" do
+ @client.send("foobar", 0)
+ sock = @server.accept
+ sock.recvfrom(6).first.should == "foobar"
+ sock.close
+ end
+
+ context "when called on a server's socket" do
+ platform_is_not :windows do
+ it "returns an array containing basic information on the client as second element" do
+ @client.send("foobar", 0)
+ sock = @server.accept
+ data = sock.recvfrom(6)
+ data.last.should == ["AF_UNIX", ""]
+ sock.close
+ end
+ end
+
+ guard -> { platform_is :windows and ruby_bug "#21702", ""..."4.2" } do
+ it "returns an array containing basic information on the client as second element" do
+ @client.send("foobar", 0)
+ sock = @server.accept
+ data = sock.recvfrom(6)
+ data.last.should == ["AF_UNIX", ""]
+ sock.close
+ end
+ end
+ end
+
+ context "when called on a client's socket" do
+ platform_is :linux do
+ it "returns an array containing server's address as second element" do
+ @client.send("", 0)
+ sock = @server.accept
+ sock.send("barfoo", 0)
+ @client.recvfrom(6).last.should == ["AF_UNIX", @server.local_address.unix_path]
+ sock.close
+ end
+ end
+
+ guard -> { platform_is :windows and ruby_bug "#21702", ""..."4.2" } do
+ it "returns an array containing server's address as second element" do
+ @client.send("", 0)
+ sock = @server.accept
+ sock.send("barfoo", 0)
+ # This may not be correct, depends on what underlying recvfrom actually returns.
+ @client.recvfrom(6).last.should == ["AF_UNIX", @server.local_address.unix_path]
+ sock.close
+ end
+ end
+
+ platform_is :darwin do
+ it "returns an array containing basic information on the server as second element" do
+ @client.send("", 0)
+ sock = @server.accept
+ sock.send("barfoo", 0)
+ @client.recvfrom(6).last.should == ["AF_UNIX", ""]
+ sock.close
+ end
+ end
+ end
+
+ it "allows an output buffer as third argument" do
+ buffer = +''
+
+ @client.send("foobar", 0)
+ sock = @server.accept
+ message, = sock.recvfrom(6, 0, buffer)
+ sock.close
+
+ message.should.equal?(buffer)
+ buffer.should == "foobar"
+ end
+
+ it "preserves the encoding of the given buffer" do
+ buffer = ''.encode(Encoding::ISO_8859_1)
+
+ @client.send("foobar", 0)
+ sock = @server.accept
+ sock.recvfrom(6, 0, buffer)
+ sock.close
+
+ buffer.encoding.should == Encoding::ISO_8859_1
+ end
+
+ platform_is_not :windows do
+ it "uses different message options" do
+ @client.send("foobar", Socket::MSG_PEEK)
+ sock = @server.accept
+ peek_data = sock.recvfrom(6, Socket::MSG_PEEK) # Does not retrieve the message
+ real_data = sock.recvfrom(6)
+
+ real_data.should == peek_data
+ peek_data.should == ["foobar", ["AF_UNIX", ""]]
+ sock.close
+ end
+ end
+end
+
+describe 'UNIXSocket#recvfrom' do
+ describe 'using a socket pair' do
+ before do
+ @client, @server = UNIXSocket.socketpair
+ @client.write('hello')
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ it 'returns an Array containing the data and address information' do
+ @server.recvfrom(5).should == ['hello', ['AF_UNIX', '']]
+ end
+ end
+
+ guard -> { platform_is :windows and ruby_bug "#21702", ""..."4.2" } do
+ it 'returns an Array containing the data and address information' do
+ @server.recvfrom(5).should == ['hello', ['AF_UNIX', '']]
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ # These specs are taken from the rdoc examples on UNIXSocket#recvfrom.
+ describe 'using a UNIX socket constructed using UNIXSocket.for_fd' do
+ before do
+ @path1 = SocketSpecs.socket_path
+ @path2 = SocketSpecs.socket_path.chop + '2'
+ rm_r(@path2)
+
+ @client_raw = Socket.new(:UNIX, :DGRAM)
+ @client_raw.bind(Socket.sockaddr_un(@path1))
+
+ @server_raw = Socket.new(:UNIX, :DGRAM)
+ @server_raw.bind(Socket.sockaddr_un(@path2))
+
+ @socket = UNIXSocket.for_fd(@server_raw.fileno)
+ @socket.autoclose = false
+ end
+
+ after do
+ @client_raw.close
+ @server_raw.close # also closes @socket
+
+ rm_r @path1
+ rm_r @path2
+ end
+
+ it 'returns an Array containing the data and address information' do
+ @client_raw.send('hello', 0, Socket.sockaddr_un(@path2))
+
+ @socket.recvfrom(5).should == ['hello', ['AF_UNIX', @path1]]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/remote_address_spec.rb b/spec/ruby/library/socket/unixsocket/remote_address_spec.rb
new file mode 100644
index 0000000000..84bdff0a6a
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/remote_address_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UNIXSocket#remote_address' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @server.close
+
+ rm_r(@path)
+ end
+
+ it 'returns an Addrinfo' do
+ @client.remote_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_UNIX as the address family' do
+ @client.remote_address.afamily.should == Socket::AF_UNIX
+ end
+
+ it 'uses PF_UNIX as the protocol family' do
+ @client.remote_address.pfamily.should == Socket::PF_UNIX
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @client.remote_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses the correct socket path' do
+ @client.remote_address.unix_path.should == @path
+ end
+
+ it 'uses 0 as the protocol' do
+ @client.remote_address.protocol.should == 0
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/send_io_spec.rb b/spec/ruby/library/socket/unixsocket/send_io_spec.rb
new file mode 100644
index 0000000000..52186ec3cf
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/send_io_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+platform_is_not :windows do
+ describe "UNIXSocket#send_io" do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ @client = UNIXSocket.open(@path)
+
+ @send_io_path = File.expand_path('../../fixtures/send_io.txt', __FILE__)
+ @file = File.open(@send_io_path)
+ end
+
+ after :each do
+ @io.close if @io
+ @socket.close if @socket
+
+ @file.close
+ @client.close
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "sends the fd for an IO object across the socket" do
+ @client.send_io(@file)
+
+ @socket = @server.accept
+ @io = @socket.recv_io
+
+ @io.read.should == File.read(@send_io_path)
+ end
+ end
+
+ describe 'UNIXSocket#send_io' do
+ before do
+ @file = File.open('/dev/null', 'w')
+ @client, @server = UNIXSocket.socketpair
+ end
+
+ after do
+ @client.close
+ @server.close
+ @io.close if @io
+ @file.close
+ end
+
+ it 'sends an IO object' do
+ @client.send_io(@file)
+
+ @io = @server.recv_io
+ @io.should be_an_instance_of(IO)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/shared/new.rb b/spec/ruby/library/socket/unixsocket/shared/new.rb
new file mode 100644
index 0000000000..f075b03c5e
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/shared/new.rb
@@ -0,0 +1,22 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/classes'
+
+describe :unixsocket_new, shared: true do
+ before :each do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.open(@path)
+ end
+
+ after :each do
+ @client.close if @client
+ @server.close
+ SocketSpecs.rm_socket @path
+ end
+
+ it "opens a unix socket on the specified file" do
+ @client = UNIXSocket.send(@method, @path)
+
+ @client.addr[0].should == "AF_UNIX"
+ @client.should_not.closed?
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/shared/pair.rb b/spec/ruby/library/socket/unixsocket/shared/pair.rb
new file mode 100644
index 0000000000..d68ee466bf
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/shared/pair.rb
@@ -0,0 +1,47 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/classes'
+
+describe :unixsocket_pair, shared: true do
+ it "returns two UNIXSockets" do
+ @s1.should be_an_instance_of(UNIXSocket)
+ @s2.should be_an_instance_of(UNIXSocket)
+ end
+
+ it "returns a pair of connected sockets" do
+ @s1.puts "foo"
+ @s2.gets.should == "foo\n"
+ end
+
+ platform_is_not :windows do
+ it "sets the socket paths to empty Strings" do
+ @s1.path.should == ""
+ @s2.path.should == ""
+ end
+
+ it "sets the socket addresses to empty Strings" do
+ @s1.addr.should == ["AF_UNIX", ""]
+ @s2.addr.should == ["AF_UNIX", ""]
+ end
+
+ it "sets the socket peer addresses to empty Strings" do
+ @s1.peeraddr.should == ["AF_UNIX", ""]
+ @s2.peeraddr.should == ["AF_UNIX", ""]
+ end
+ end
+
+ platform_is :windows do
+ it "emulates unnamed sockets with a temporary file with a path" do
+ @s1.addr.should == ["AF_UNIX", @s1.path]
+ @s2.peeraddr.should == ["AF_UNIX", @s1.path]
+ end
+
+ it "sets the peer address of first socket to an empty string" do
+ @s1.peeraddr.should == ["AF_UNIX", ""]
+ end
+
+ it "sets the address and path of second socket to an empty string" do
+ @s2.addr.should == ["AF_UNIX", ""]
+ @s2.path.should == ""
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/socketpair_spec.rb b/spec/ruby/library/socket/unixsocket/socketpair_spec.rb
new file mode 100644
index 0000000000..c61fc00be4
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/socketpair_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+require_relative '../shared/partially_closable_sockets'
+require_relative 'shared/pair'
+
+describe "UNIXSocket.socketpair" do
+ it_should_behave_like :unixsocket_pair
+ it_should_behave_like :partially_closable_sockets
+
+ before :each do
+ @s1, @s2 = UNIXSocket.socketpair
+ end
+
+ after :each do
+ @s1.close
+ @s2.close
+ end
+end