diff options
author | eregon <eregon@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-05-07 12:04:49 +0000 |
---|---|---|
committer | eregon <eregon@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-05-07 12:04:49 +0000 |
commit | 95e8c48dd3348503a8c7db5d0498894a1b676395 (patch) | |
tree | 9eef7f720314ebaff56845a74e203770e62284e4 /spec/rubyspec/library/socket | |
parent | ed7d803500de38186c74bce94d233e85ef51e503 (diff) | |
download | ruby-95e8c48dd3348503a8c7db5d0498894a1b676395.tar.gz |
Add in-tree mspec and ruby/spec
* For easier modifications of ruby/spec by MRI developers.
* .gitignore: track changes under spec.
* spec/mspec, spec/rubyspec: add in-tree mspec and ruby/spec.
These files can therefore be updated like any other file in MRI.
Instructions are provided in spec/README.
[Feature #13156] [ruby-core:79246]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58595 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'spec/rubyspec/library/socket')
120 files changed, 4324 insertions, 0 deletions
diff --git a/spec/rubyspec/library/socket/addrinfo/afamily_spec.rb b/spec/rubyspec/library/socket/addrinfo/afamily_spec.rb new file mode 100644 index 0000000000..1845ab5e04 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/afamily_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +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 + + platform_is_not :windows do + 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 +end diff --git a/spec/rubyspec/library/socket/addrinfo/bind_spec.rb b/spec/rubyspec/library/socket/addrinfo/bind_spec.rb new file mode 100644 index 0000000000..4afa3a0211 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/bind_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require 'socket' + +describe "Addrinfo#bind" do + + before :each do + @addrinfo = Addrinfo.tcp("127.0.0.1", SocketSpecs.port) + 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/rubyspec/library/socket/addrinfo/canonname_spec.rb b/spec/rubyspec/library/socket/addrinfo/canonname_spec.rb new file mode 100644 index 0000000000..15dfe86467 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/canonname_spec.rb @@ -0,0 +1,19 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require 'socket' + +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 +end diff --git a/spec/rubyspec/library/socket/addrinfo/initialize_spec.rb b/spec/rubyspec/library/socket/addrinfo/initialize_spec.rb new file mode 100644 index 0000000000..254539f95e --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/initialize_spec.rb @@ -0,0 +1,253 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +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 Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_UNSPEC + 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 Socket::UNSPEC 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 Socket::UNSPEC 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 == 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 Socket::UNSPEC 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 Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET + end + + it "returns the INET6 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 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 Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET + end + + it "returns the INET6 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 Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET + end + + it "returns the INET6 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 + 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 Socket::UNSPEC pfamily" do + @addrinfo.pfamily.should == Socket::PF_INET + end + + it "returns the INET6 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 + +end diff --git a/spec/rubyspec/library/socket/addrinfo/inspect_sockaddr_spec.rb b/spec/rubyspec/library/socket/addrinfo/inspect_sockaddr_spec.rb new file mode 100644 index 0000000000..c7d69db760 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/inspect_sockaddr_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +require 'socket' + +describe 'Addrinfo#inspect_sockaddr' do + it 'IPv4' do + Addrinfo.tcp('127.0.0.1', 80).inspect_sockaddr.should == '127.0.0.1:80' + Addrinfo.tcp('127.0.0.1', 0).inspect_sockaddr.should == '127.0.0.1' + end + + it 'IPv6' do + Addrinfo.tcp('::1', 80).inspect_sockaddr.should == '[::1]:80' + Addrinfo.tcp('::1', 0).inspect_sockaddr.should == '::1' + ip = '2001:0db8:85a3:0000:0000:8a2e:0370:7334' + Addrinfo.tcp(ip, 80).inspect_sockaddr.should == '[2001:db8:85a3::8a2e:370:7334]:80' + Addrinfo.tcp(ip, 0).inspect_sockaddr.should == '2001:db8:85a3::8a2e:370:7334' + end + + platform_is_not :windows do + it 'UNIX' do + Addrinfo.unix('/tmp/sock').inspect_sockaddr.should == '/tmp/sock' + Addrinfo.unix('rel').inspect_sockaddr.should == 'UNIX rel' + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ip_address_spec.rb b/spec/rubyspec/library/socket/addrinfo/ip_address_spec.rb new file mode 100644 index 0000000000..f82cef0812 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ip_address_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +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 + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "raises an exception" do + lambda { @addrinfo.ip_address }.should raise_error(SocketError) + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ip_port_spec.rb b/spec/rubyspec/library/socket/addrinfo/ip_port_spec.rb new file mode 100644 index 0000000000..e437b88ca1 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ip_port_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +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 + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "raises an exception" do + lambda { @addrinfo.ip_port }.should raise_error(SocketError) + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ip_spec.rb b/spec/rubyspec/library/socket/addrinfo/ip_spec.rb new file mode 100644 index 0000000000..2e4b613ae5 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ip_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +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 + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "returns Socket::AF_INET6" do + @addrinfo.ip?.should be_false + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ip_unpack_spec.rb b/spec/rubyspec/library/socket/addrinfo/ip_unpack_spec.rb new file mode 100644 index 0000000000..2b4a9372cc --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ip_unpack_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +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 + + platform_is_not :windows do + describe "for a unix socket" do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "raises an exception" do + lambda { @addrinfo.ip_unpack }.should raise_error(SocketError) + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ipv4_loopback_spec.rb b/spec/rubyspec/library/socket/addrinfo/ipv4_loopback_spec.rb new file mode 100644 index 0000000000..457bd7cebf --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ipv4_loopback_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#ipv4_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 true for the loopback address" do + @loopback.ipv4_loopback?.should be_true + end + + it "returns false for another address" do + @other.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 + + platform_is_not :windows do + 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 +end diff --git a/spec/rubyspec/library/socket/addrinfo/ipv4_multicast_spec.rb b/spec/rubyspec/library/socket/addrinfo/ipv4_multicast_spec.rb new file mode 100644 index 0000000000..01f6a6ebf7 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ipv4_multicast_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#ipv4_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 the loopback address" do + @multicast.ipv4_multicast?.should be_true + end + + it "returns false for another address" do + @other.ipv4_multicast?.should be_false + end + end + + describe "for an ipv6 socket" do + before :each do + @multicast = Addrinfo.tcp("ff02::1", 80) + @other = Addrinfo.tcp("::", 80) + end + + it "returns false for the loopback address" do + @multicast.ipv4_multicast?.should be_false + end + + it "returns false for another address" do + @other.ipv4_multicast?.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 false" do + @addrinfo.ipv4_multicast?.should be_false + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ipv4_private_spec.rb b/spec/rubyspec/library/socket/addrinfo/ipv4_private_spec.rb new file mode 100644 index 0000000000..cf8bd8c1aa --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ipv4_private_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +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 + @private.ipv4_private?.should be_true + 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 + + platform_is_not :windows do + 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 +end diff --git a/spec/rubyspec/library/socket/addrinfo/ipv4_spec.rb b/spec/rubyspec/library/socket/addrinfo/ipv4_spec.rb new file mode 100644 index 0000000000..3d4560532e --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ipv4_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +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 + + platform_is_not :windows do + 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 +end diff --git a/spec/rubyspec/library/socket/addrinfo/ipv6_loopback_spec.rb b/spec/rubyspec/library/socket/addrinfo/ipv6_loopback_spec.rb new file mode 100644 index 0000000000..b0060378e6 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ipv6_loopback_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +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 true 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 false 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 + + platform_is_not :windows do + 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 +end diff --git a/spec/rubyspec/library/socket/addrinfo/ipv6_multicast_spec.rb b/spec/rubyspec/library/socket/addrinfo/ipv6_multicast_spec.rb new file mode 100644 index 0000000000..d8b3a96ebb --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ipv6_multicast_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +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 the loopback 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 + before :each do + @multicast = Addrinfo.tcp("ff02::1", 80) + @other = Addrinfo.tcp("::", 80) + end + + it "returns false for the loopback address" do + @multicast.ipv6_multicast?.should be_true + end + + it "returns false for another address" do + @other.ipv6_multicast?.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 false" do + @addrinfo.ipv6_multicast?.should be_false + end + end + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/ipv6_spec.rb b/spec/rubyspec/library/socket/addrinfo/ipv6_spec.rb new file mode 100644 index 0000000000..b66bc0d70b --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/ipv6_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +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 + + platform_is_not :windows do + 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 +end diff --git a/spec/rubyspec/library/socket/addrinfo/pfamily_spec.rb b/spec/rubyspec/library/socket/addrinfo/pfamily_spec.rb new file mode 100644 index 0000000000..d37ed73e1e --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/pfamily_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#pfamily" do + 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 + + platform_is_not :windows do + 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 +end diff --git a/spec/rubyspec/library/socket/addrinfo/protocol_spec.rb b/spec/rubyspec/library/socket/addrinfo/protocol_spec.rb new file mode 100644 index 0000000000..4ff11dc017 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/protocol_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#protocol" do + describe "for an ipv4 socket" do + + before :each do + @addrinfo = Addrinfo.tcp("127.0.0.1", 80) + end + + it "returns Socket::IPPROTO_TCP" do + @addrinfo.protocol.should == Socket::IPPROTO_TCP + end + + end + + describe "for an ipv6 socket" do + before :each do + @addrinfo = Addrinfo.tcp("::1", 80) + end + + it "returns Socket::IPPROTO_TCP" do + @addrinfo.protocol.should == Socket::IPPROTO_TCP + end + end + + platform_is_not :windows do + 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 +end diff --git a/spec/rubyspec/library/socket/addrinfo/shared/to_sockaddr.rb b/spec/rubyspec/library/socket/addrinfo/shared/to_sockaddr.rb new file mode 100644 index 0000000000..86819a31b0 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/shared/to_sockaddr.rb @@ -0,0 +1,35 @@ +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 be_kind_of(String) + 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 be_kind_of(String) + end + end + + platform_is_not :windows do + 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 be_kind_of(String) + end + end + end + +end diff --git a/spec/rubyspec/library/socket/addrinfo/socktype_spec.rb b/spec/rubyspec/library/socket/addrinfo/socktype_spec.rb new file mode 100644 index 0000000000..e1c8c0f3f5 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/socktype_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo#socktype" do + describe "for an ipv4 socket" do + + before :each do + @addrinfo = Addrinfo.tcp("127.0.0.1", 80) + end + + it "returns Socket::SOCK_STREAM" do + @addrinfo.socktype.should == Socket::SOCK_STREAM + end + + end + + describe "for an ipv6 socket" do + before :each do + @addrinfo = Addrinfo.tcp("::1", 80) + end + + it "returns Socket::SOCK_STREAM" do + @addrinfo.socktype.should == Socket::SOCK_STREAM + end + end + + platform_is_not :windows do + 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 +end diff --git a/spec/rubyspec/library/socket/addrinfo/tcp_spec.rb b/spec/rubyspec/library/socket/addrinfo/tcp_spec.rb new file mode 100644 index 0000000000..b5c18cefea --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/tcp_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo.tcp" do + + before :each do + @addrinfo = Addrinfo.tcp("localhost", "smtp") + end + + it "creates a addrinfo for a tcp socket" do + ["::1", "127.0.0.1"].should include(@addrinfo.ip_address) + [Socket::PF_INET, Socket::PF_INET6].should include(@addrinfo.pfamily) + @addrinfo.ip_port.should == 25 + @addrinfo.socktype.should == Socket::SOCK_STREAM + platform_is_not :solaris do + @addrinfo.protocol.should == Socket::IPPROTO_TCP + end + end + +end diff --git a/spec/rubyspec/library/socket/addrinfo/to_s_spec.rb b/spec/rubyspec/library/socket/addrinfo/to_s_spec.rb new file mode 100644 index 0000000000..7205bdc823 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/to_s_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_sockaddr', __FILE__) +require 'socket' + +describe "Addrinfo#to_s" do + it_behaves_like(:socket_addrinfo_to_sockaddr, :to_s) +end diff --git a/spec/rubyspec/library/socket/addrinfo/to_sockaddr_spec.rb b/spec/rubyspec/library/socket/addrinfo/to_sockaddr_spec.rb new file mode 100644 index 0000000000..f3f926c2b6 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/to_sockaddr_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/to_sockaddr', __FILE__) +require 'socket' + +describe "Addrinfo#to_sockaddr" do + it_behaves_like(:socket_addrinfo_to_sockaddr, :to_sockaddr) +end diff --git a/spec/rubyspec/library/socket/addrinfo/udp_spec.rb b/spec/rubyspec/library/socket/addrinfo/udp_spec.rb new file mode 100644 index 0000000000..712d730e05 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/udp_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo.udp" do + + before :each do + @addrinfo = Addrinfo.udp("localhost", "daytime") + end + + it "creates a addrinfo for a tcp socket" do + ["::1", "127.0.0.1"].should include(@addrinfo.ip_address) + [Socket::PF_INET, Socket::PF_INET6].should include(@addrinfo.pfamily) + @addrinfo.ip_port.should == 13 + @addrinfo.socktype.should == Socket::SOCK_DGRAM + platform_is_not :solaris do + @addrinfo.protocol.should == Socket::IPPROTO_UDP + end + end + +end diff --git a/spec/rubyspec/library/socket/addrinfo/unix_path_spec.rb b/spec/rubyspec/library/socket/addrinfo/unix_path_spec.rb new file mode 100644 index 0000000000..3f7e03dd7b --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/unix_path_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +platform_is_not :windows do + 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 + lambda { @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 + lambda { @addrinfo.unix_path }.should raise_error(SocketError) + end + end + + platform_is_not :windows do + 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 + end +end diff --git a/spec/rubyspec/library/socket/addrinfo/unix_spec.rb b/spec/rubyspec/library/socket/addrinfo/unix_spec.rb new file mode 100644 index 0000000000..00eedc96e7 --- /dev/null +++ b/spec/rubyspec/library/socket/addrinfo/unix_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +describe "Addrinfo.unix" do + + platform_is_not :windows do + before :each do + @addrinfo = Addrinfo.unix("/tmp/sock") + end + + it "creates a addrinfo for a unix socket" do + @addrinfo.pfamily.should == Socket::PF_UNIX + @addrinfo.socktype.should == Socket::SOCK_STREAM + @addrinfo.protocol.should == 0 + @addrinfo.unix_path.should == "/tmp/sock" + end + 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/rubyspec/library/socket/basicsocket/close_read_spec.rb b/spec/rubyspec/library/socket/basicsocket/close_read_spec.rb new file mode 100644 index 0000000000..d0b73c88ad --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/close_read_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::BasicSocket#close_read" do + before :each do + @server = TCPServer.new(SocketSpecs.port) + end + + after :each do + @server.close unless @server.closed? + end + + it "closes the reading end of the socket" do + @server.close_read + lambda { @server.read }.should raise_error(IOError) + end + + it "it works on sockets with closed ends" do + @server.close_read + lambda { @server.close_read }.should_not raise_error(Exception) + lambda { @server.read }.should raise_error(IOError) + end + + it "does not 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 on closed socket" do + @server.close + lambda { @server.close_read }.should raise_error(IOError) + end + + it "returns nil" do + @server.close_read.should be_nil + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/close_write_spec.rb b/spec/rubyspec/library/socket/basicsocket/close_write_spec.rb new file mode 100644 index 0000000000..7cba1caa8f --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/close_write_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::BasicSocket#close_write" do + before :each do + @server = TCPServer.new(SocketSpecs.port) + end + + after :each do + @server.close unless @server.closed? + end + + it "closes the writing end of the socket" do + @server.close_write + lambda { @server.write("foo") }.should raise_error(IOError) + end + + it "works on sockets with closed write ends" do + @server.close_write + lambda { @server.close_write }.should_not raise_error(Exception) + lambda { @server.write("foo") }.should raise_error(IOError) + end + + it "does not 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 on closed socket" do + @server.close + lambda { @server.close_write }.should raise_error(IOError) + end + + it "returns nil" do + @server.close_write.should be_nil + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/do_not_reverse_lookup_spec.rb b/spec/rubyspec/library/socket/basicsocket/do_not_reverse_lookup_spec.rb new file mode 100644 index 0000000000..0875c1eca1 --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/do_not_reverse_lookup_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +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', SocketSpecs.port) + @socket = TCPSocket.new('127.0.0.1', SocketSpecs.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", SocketSpecs.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 diff --git a/spec/rubyspec/library/socket/basicsocket/for_fd_spec.rb b/spec/rubyspec/library/socket/basicsocket/for_fd_spec.rb new file mode 100644 index 0000000000..5ad1ea84d0 --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/for_fd_spec.rb @@ -0,0 +1,21 @@ + +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "BasicSocket#for_fd" do + before :each do + @server = TCPServer.new(SocketSpecs.port) + @s2 = nil + end + + after :each do + @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 +end diff --git a/spec/rubyspec/library/socket/basicsocket/getpeername_spec.rb b/spec/rubyspec/library/socket/basicsocket/getpeername_spec.rb new file mode 100644 index 0000000000..1edfbcd3ec --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/getpeername_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::BasicSocket#getpeername" do + + before :each do + @server = TCPServer.new("127.0.0.1", SocketSpecs.port) + @client = TCPSocket.new("127.0.0.1", SocketSpecs.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(SocketSpecs.port, "127.0.0.1") + @client.getpeername.should == server_sockaddr + end + + # Catch general exceptions to prevent NotImplementedError + it "raises an error if socket's not connected" do + lambda { @server.getpeername }.should raise_error(Exception) + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/getsockname_spec.rb b/spec/rubyspec/library/socket/basicsocket/getsockname_spec.rb new file mode 100644 index 0000000000..6d30286733 --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/getsockname_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::BasicSocket#getsockname" do + after :each do + @socket.closed?.should be_false + @socket.close + end + + it "returns the sockaddr associacted with the socket" do + @socket = TCPServer.new("127.0.0.1", SocketSpecs.port) + sockaddr = Socket.unpack_sockaddr_in(@socket.getsockname) + sockaddr.should == [SocketSpecs.port, "127.0.0.1"] + end + + it "works on sockets listening in ipaddr_any" do + @socket = TCPServer.new(SocketSpecs.port) + 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 == SocketSpecs.port + end + + it "returns empty sockaddr for unbinded sockets" 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/rubyspec/library/socket/basicsocket/getsockopt_spec.rb b/spec/rubyspec/library/socket/basicsocket/getsockopt_spec.rb new file mode 100644 index 0000000000..dc4fffa5c1 --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/getsockopt_spec.rb @@ -0,0 +1,46 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +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 + lambda { @sock.getsockopt Socket::SOL_SOCKET, -1 }.should raise_error(Errno::ENOPROTOOPT) + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/ioctl_spec.rb b/spec/rubyspec/library/socket/basicsocket/ioctl_spec.rb new file mode 100644 index 0000000000..9a7f535317 --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/ioctl_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require 'socket' + +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/rubyspec/library/socket/basicsocket/recv_nonblock_spec.rb b/spec/rubyspec/library/socket/basicsocket/recv_nonblock_spec.rb new file mode 100644 index 0000000000..2c948eaa2f --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/recv_nonblock_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../shared/recv_nonblock', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::BasicSocket#recv_nonblock" do + it_behaves_like :socket_recv_nonblock, :recv_nonblock +end diff --git a/spec/rubyspec/library/socket/basicsocket/recv_spec.rb b/spec/rubyspec/library/socket/basicsocket/recv_spec.rb new file mode 100644 index 0000000000..9f3227dc65 --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/recv_spec.rb @@ -0,0 +1,93 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "BasicSocket#recv" do + + before :each do + @server = TCPServer.new('127.0.0.1', SocketSpecs.port) + end + + after :each do + @server.closed?.should be_false + @server.close + 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', SocketSpecs.port) + socket.send('hello', 0) + socket.close + + t.join + ScratchPad.recorded.should == 'hello' + end + + platform_is_not :solaris do + 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', SocketSpecs.port) + socket.send('helloU', Socket::MSG_OOB) + socket.shutdown(1) + t.join + socket.close + ScratchPad.recorded.should == 'hello' + end + 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', SocketSpecs.port) + socket.write("firstline\377secondline\377") + socket.close + + t.join + ScratchPad.recorded.should == "firstline\377" + end + + ruby_version_is "2.3" do + it "allows an output buffer as third argument" do + socket = TCPSocket.new('127.0.0.1', SocketSpecs.port) + socket.write("data") + + client = @server.accept + buf = "foo" + client.recv(4, 0, buf) + client.close + buf.should == "data" + + socket.close + end + end +end diff --git a/spec/rubyspec/library/socket/basicsocket/send_spec.rb b/spec/rubyspec/library/socket/basicsocket/send_spec.rb new file mode 100644 index 0000000000..7822f4696b --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/send_spec.rb @@ -0,0 +1,84 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "BasicSocket#send" do + before :each do + @server = TCPServer.new('127.0.0.1', SocketSpecs.port) + @socket = TCPSocket.new('127.0.0.1', SocketSpecs.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.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 :solaris, :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.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(SocketSpecs.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 diff --git a/spec/rubyspec/library/socket/basicsocket/setsockopt_spec.rb b/spec/rubyspec/library/socket/basicsocket/setsockopt_spec.rb new file mode 100644 index 0000000000..523a22d957 --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/setsockopt_spec.rb @@ -0,0 +1,213 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +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 + lambda 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 + lambda { + @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 + lambda { + @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 + lambda { + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "1") + }.should raise_error(SystemCallError) + end + + platform_is_not :windows do + lambda { + @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 + + lambda { + @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 + + lambda { + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "") + }.should raise_error(SystemCallError) + + lambda { + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "bla") + }.should raise_error(SystemCallError) + + lambda { + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "0") + }.should raise_error(SystemCallError) + + lambda { + @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, "1") + }.should raise_error(SystemCallError) + + lambda { + @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 diff --git a/spec/rubyspec/library/socket/basicsocket/shutdown_spec.rb b/spec/rubyspec/library/socket/basicsocket/shutdown_spec.rb new file mode 100644 index 0000000000..c874f08697 --- /dev/null +++ b/spec/rubyspec/library/socket/basicsocket/shutdown_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::BasicSocket#shutdown" do + +end diff --git a/spec/rubyspec/library/socket/constants/constants_spec.rb b/spec/rubyspec/library/socket/constants/constants_spec.rb new file mode 100644 index 0000000000..9b8a0e55b3 --- /dev/null +++ b/spec/rubyspec/library/socket/constants/constants_spec.rb @@ -0,0 +1,90 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +include Socket::Constants + +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 :solaris, :windows, :aix 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 +end diff --git a/spec/rubyspec/library/socket/fixtures/classes.rb b/spec/rubyspec/library/socket/fixtures/classes.rb new file mode 100644 index 0000000000..ddb2396731 --- /dev/null +++ b/spec/rubyspec/library/socket/fixtures/classes.rb @@ -0,0 +1,106 @@ +require 'socket' + +module SocketSpecs + # helper to get the hostname associated to 127.0.0.1 + def self.hostname + # 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("127.0.0.1", nil)[0][2] + end + + def self.hostnamev6 + Socket.getaddrinfo("::1", nil)[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.find_available_port + begin + s = TCPServer.open(0) + port = s.addr[1] + s.close + port + rescue + 43191 + end + end + + def self.port + @@_port ||= find_available_port + end + + def self.str_port + port.to_s + end + + def self.local_port + find_available_port + end + + def self.sockaddr_in(port, host) + Socket::SockAddr_In.new(Socket.sockaddr_in(port, host)) + end + + def self.socket_path + tmp("unix_server_spec.socket", false) + end + + # TCPServer echo server accepting one connection + class SpecTCPServer + attr_accessor :hostname, :port, :logger + + def initialize(host=nil, port=nil, logger=nil) + @hostname = host || SocketSpecs.hostname + @port = port || SocketSpecs.port + @logger = logger + + start + end + + def start + log "SpecTCPServer starting on #{@hostname}:#{@port}" + @server = TCPServer.new @hostname, @port + + @thread = Thread.new do + socket = @server.accept + log "SpecTCPServer accepted connection: #{socket}" + service socket + end + self + end + + def service(socket) + begin + data = socket.recv(1024) + + return if 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 +end diff --git a/spec/rubyspec/library/socket/fixtures/send_io.txt b/spec/rubyspec/library/socket/fixtures/send_io.txt new file mode 100644 index 0000000000..eaaa1eb3ec --- /dev/null +++ b/spec/rubyspec/library/socket/fixtures/send_io.txt @@ -0,0 +1 @@ +This data is magic. diff --git a/spec/rubyspec/library/socket/ipsocket/addr_spec.rb b/spec/rubyspec/library/socket/ipsocket/addr_spec.rb new file mode 100644 index 0000000000..2ac4c3d413 --- /dev/null +++ b/spec/rubyspec/library/socket/ipsocket/addr_spec.rb @@ -0,0 +1,42 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::IPSocket#addr" do + before :each do + @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup + @socket = TCPServer.new("127.0.0.1", SocketSpecs.port) + 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 == SocketSpecs.port + 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 == SocketSpecs.port + 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 == SocketSpecs.port + addrinfo[2].should == "127.0.0.1" + addrinfo[3].should == "127.0.0.1" + end +end diff --git a/spec/rubyspec/library/socket/ipsocket/getaddress_spec.rb b/spec/rubyspec/library/socket/ipsocket/getaddress_spec.rb new file mode 100644 index 0000000000..c574c7d267 --- /dev/null +++ b/spec/rubyspec/library/socket/ipsocket/getaddress_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +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" + 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 an error on unknown hostnames" do + lambda { + IPSocket.getaddress("rubyspecdoesntexist.fallingsnow.net") + }.should raise_error(SocketError) + end + end + +end diff --git a/spec/rubyspec/library/socket/ipsocket/peeraddr_spec.rb b/spec/rubyspec/library/socket/ipsocket/peeraddr_spec.rb new file mode 100644 index 0000000000..01b697bd27 --- /dev/null +++ b/spec/rubyspec/library/socket/ipsocket/peeraddr_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::IPSocket#peeraddr" do + before :each do + @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup + @server = TCPServer.new("127.0.0.1", SocketSpecs.port) + @client = TCPSocket.new("127.0.0.1", SocketSpecs.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 + lambda { @server.peeraddr }.should raise_error + 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 == SocketSpecs.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 == SocketSpecs.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 == SocketSpecs.port + addrinfo[2].should == "127.0.0.1" + addrinfo[3].should == "127.0.0.1" + end +end diff --git a/spec/rubyspec/library/socket/ipsocket/recvfrom_spec.rb b/spec/rubyspec/library/socket/ipsocket/recvfrom_spec.rb new file mode 100644 index 0000000000..565a1795e9 --- /dev/null +++ b/spec/rubyspec/library/socket/ipsocket/recvfrom_spec.rb @@ -0,0 +1,65 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::IPSocket#recvfrom" do + + before :each do + @server = TCPServer.new("127.0.0.1", SocketSpecs.port) + @client = TCPSocket.new("127.0.0.1", SocketSpecs.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 + data = client.recvfrom(6) + client.close + 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 + data = client.recvfrom(3) + client.close + 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, dependant on recvfrom(2) + # data.last.should == nil + end + +end diff --git a/spec/rubyspec/library/socket/option/bool_spec.rb b/spec/rubyspec/library/socket/option/bool_spec.rb new file mode 100644 index 0000000000..74c832a0ad --- /dev/null +++ b/spec/rubyspec/library/socket/option/bool_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +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 + + it "raises TypeError if option has not good size" do + so = Socket::Option.new(:UNSPEC, :SOCKET, :SO_LINGER, [0, 0].pack('i*')) + lambda { so.bool }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/socket/option/inspect_spec.rb b/spec/rubyspec/library/socket/option/inspect_spec.rb new file mode 100644 index 0000000000..df72f227a9 --- /dev/null +++ b/spec/rubyspec/library/socket/option/inspect_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +require 'socket' + +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/rubyspec/library/socket/option/int_spec.rb b/spec/rubyspec/library/socket/option/int_spec.rb new file mode 100644 index 0000000000..f926ff7968 --- /dev/null +++ b/spec/rubyspec/library/socket/option/int_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +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 +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 + end + + it "raises TypeError if option has not good size" do + so = Socket::Option.new(:UNSPEC, :SOCKET, :SO_LINGER, [0, 0].pack('i*')) + lambda { so.int }.should raise_error(TypeError) + end +end diff --git a/spec/rubyspec/library/socket/option/linger_spec.rb b/spec/rubyspec/library/socket/option/linger_spec.rb new file mode 100644 index 0000000000..687d421af3 --- /dev/null +++ b/spec/rubyspec/library/socket/option/linger_spec.rb @@ -0,0 +1,62 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +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) + lambda { so.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) + lambda { so.linger }.should raise_error(TypeError) + end + end +end diff --git a/spec/rubyspec/library/socket/option/new_spec.rb b/spec/rubyspec/library/socket/option/new_spec.rb new file mode 100644 index 0000000000..4f2d0c5386 --- /dev/null +++ b/spec/rubyspec/library/socket/option/new_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +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 + lambda { Socket::Option.new(:INET4, :SOCKET, :KEEPALIVE, [0].pack('i')) }.should raise_error(SocketError) + end + + it "should raise error on unknown level" do + lambda { Socket::Option.new(:INET, :ROCKET, :KEEPALIVE, [0].pack('i')) }.should raise_error(SocketError) + end + + it "should raise error on unknown option name" do + lambda { Socket::Option.new(:INET, :SOCKET, :ALIVE, [0].pack('i')) }.should raise_error(SocketError) + end +end diff --git a/spec/rubyspec/library/socket/shared/pack_sockaddr.rb b/spec/rubyspec/library/socket/shared/pack_sockaddr.rb new file mode 100644 index 0000000000..4ffa02a8d8 --- /dev/null +++ b/spec/rubyspec/library/socket/shared/pack_sockaddr.rb @@ -0,0 +1,50 @@ +# 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'] + end +end + +describe :socket_pack_sockaddr_un, shared: true do + platform_is_not :windows 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 + end + + platform_is_not :windows, :aix do + it "raises if path length exceeds max size" do + # AIX doesn't raise error + long_path = Array.new(512, 0).join + lambda { Socket.public_send(@method, long_path) }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/library/socket/shared/partially_closable_sockets.rb b/spec/rubyspec/library/socket/shared/partially_closable_sockets.rb new file mode 100644 index 0000000000..1309f15f85 --- /dev/null +++ b/spec/rubyspec/library/socket/shared/partially_closable_sockets.rb @@ -0,0 +1,13 @@ +describe "partially closable sockets", shared: true do + specify "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 + + specify "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/rubyspec/library/socket/shared/recv_nonblock.rb b/spec/rubyspec/library/socket/shared/recv_nonblock.rb new file mode 100644 index 0000000000..4a63b16024 --- /dev/null +++ b/spec/rubyspec/library/socket/shared/recv_nonblock.rb @@ -0,0 +1,54 @@ +describe :socket_recv_nonblock, shared: true do + before :each do + @s1 = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0) + @s2 = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0) + end + + after :each do + @s1.close unless @s1.closed? + @s2.close unless @s2.closed? + end + + it "raises an exception extending IO::WaitReadable if there's no data available" do + @s1.bind(Socket.pack_sockaddr_in(SocketSpecs.port, "127.0.0.1")) + lambda { + @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 "receives data after it's ready" do + @s1.bind(Socket.pack_sockaddr_in(SocketSpecs.port, "127.0.0.1")) + @s2.send("aaa", 0, @s1.getsockname) + IO.select([@s1], nil, nil, 2) + @s1.recv_nonblock(5).should == "aaa" + end + + ruby_version_is "2.3" do + it "allows an output buffer as third argument" do + @s1.bind(Socket.pack_sockaddr_in(SocketSpecs.port, "127.0.0.1")) + @s2.send("data", 0, @s1.getsockname) + IO.select([@s1], nil, nil, 2) + + buf = "foo" + @s1.recv_nonblock(5, 0, buf) + buf.should == "data" + end + end + + it "does not block if there's no data available" do + @s1.bind(Socket.pack_sockaddr_in(SocketSpecs.port, "127.0.0.1")) + @s2.send("a", 0, @s1.getsockname) + IO.select([@s1], nil, nil, 2) + @s1.recv_nonblock(1).should == "a" + lambda { + @s1.recv_nonblock(5) + }.should raise_error(IO::WaitReadable) + end +end diff --git a/spec/rubyspec/library/socket/shared/socketpair.rb b/spec/rubyspec/library/socket/shared/socketpair.rb new file mode 100644 index 0000000000..03ee0e1a52 --- /dev/null +++ b/spec/rubyspec/library/socket/shared/socketpair.rb @@ -0,0 +1,23 @@ +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 + end +end diff --git a/spec/rubyspec/library/socket/socket/accept_nonblock_spec.rb b/spec/rubyspec/library/socket/socket/accept_nonblock_spec.rb new file mode 100644 index 0000000000..be0bbf5f03 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/accept_nonblock_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +require 'socket' + +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 + lambda { + @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 + + ruby_version_is '2.3' do + it 'returns :wait_readable in exceptionless mode' do + @socket.accept_nonblock(exception: false).should == :wait_readable + end + end +end diff --git a/spec/rubyspec/library/socket/socket/accept_spec.rb b/spec/rubyspec/library/socket/socket/accept_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/accept_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/socket/bind_spec.rb b/spec/rubyspec/library/socket/socket/bind_spec.rb new file mode 100644 index 0000000000..57dff3bd6d --- /dev/null +++ b/spec/rubyspec/library/socket/socket/bind_spec.rb @@ -0,0 +1,81 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +include Socket::Constants + +describe "Socket#bind on SOCK_DGRAM socket" do + before :each do + @sock = Socket.new(AF_INET, SOCK_DGRAM, 0); + @sockaddr = Socket.pack_sockaddr_in(SocketSpecs.port, "127.0.0.1"); + end + + after :each do + @sock.closed?.should be_false + @sock.close + end + + it "binds to a port" do + lambda { @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 binding to an already bound port" do + @sock.bind(@sockaddr); + + lambda { @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(SocketSpecs.port, "4.3.2.1"); + lambda { @sock.bind(sockaddr1); }.should raise_error(Errno::EADDRNOTAVAIL) + end + + platform_is_not :windows, :cygwin do + 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"); + lambda { @sock.bind(sockaddr1); }.should raise_error(Errno::EACCES) + end + end +end + +describe "Socket#bind on SOCK_STREAM socket" do + before :each do + @sock = Socket.new(AF_INET, SOCK_STREAM, 0); + @sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, true) + @sockaddr = Socket.pack_sockaddr_in(SocketSpecs.port, "127.0.0.1"); + end + + after :each do + @sock.closed?.should be_false + @sock.close + end + + it "binds to a port" do + lambda { @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 binding to an already bound port" do + @sock.bind(@sockaddr); + + lambda { @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(SocketSpecs.port, "4.3.2.1"); + lambda { @sock.bind(sockaddr1); }.should raise_error(Errno::EADDRNOTAVAIL) + end + + platform_is_not :windows, :cygwin do + 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"); + lambda { @sock.bind(sockaddr1); }.should raise_error(Errno::EACCES) + end + end +end diff --git a/spec/rubyspec/library/socket/socket/connect_nonblock_spec.rb b/spec/rubyspec/library/socket/socket/connect_nonblock_spec.rb new file mode 100644 index 0000000000..77c2340688 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/connect_nonblock_spec.rb @@ -0,0 +1,67 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +require 'socket' + +describe "Socket#connect_nonblock" do + before :each do + @hostname = "127.0.0.1" + @addr = Socket.sockaddr_in(SocketSpecs.port, @hostname) + @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) + @thread = nil + end + + after :each do + @socket.close + @thread.join if @thread + end + + it "connects the socket to the remote side" do + ready = false + @thread = Thread.new do + server = TCPServer.new(@hostname, SocketSpecs.port) + ready = true + conn = server.accept + conn << "hello!" + conn.close + server.close + end + + Thread.pass while (@thread.status and @thread.status != 'sleep') or !ready + + begin + @socket.connect_nonblock(@addr) + rescue Errno::EINPROGRESS + end + + 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, :solaris, :aix do + it "raises Errno::EINPROGRESS when the connect would block" do + lambda 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 + lambda do + @socket.connect_nonblock(@addr) + end.should raise_error(IO::WaitWritable) + end + + ruby_version_is "2.3" do + 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 +end diff --git a/spec/rubyspec/library/socket/socket/connect_spec.rb b/spec/rubyspec/library/socket/socket/connect_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/connect_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/socket/for_fd_spec.rb b/spec/rubyspec/library/socket/socket/for_fd_spec.rb new file mode 100644 index 0000000000..7f3cfcceb4 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/for_fd_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require 'socket' + +describe "Socket.for_fd" do + before :each do + @server = TCPServer.new("127.0.0.1", SocketSpecs.port) + @client = TCPSocket.open("127.0.0.1", SocketSpecs.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/rubyspec/library/socket/socket/getaddrinfo_spec.rb b/spec/rubyspec/library/socket/socket/getaddrinfo_spec.rb new file mode 100644 index 0000000000..fa8112c010 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/getaddrinfo_spec.rb @@ -0,0 +1,112 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +require 'socket' + +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 :solaris, :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 + end +end diff --git a/spec/rubyspec/library/socket/socket/gethostbyaddr_spec.rb b/spec/rubyspec/library/socket/socket/gethostbyaddr_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/gethostbyaddr_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/socket/gethostbyname_spec.rb b/spec/rubyspec/library/socket/socket/gethostbyname_spec.rb new file mode 100644 index 0000000000..a93c9ffb98 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/gethostbyname_spec.rb @@ -0,0 +1,17 @@ +# -*- encoding: binary -*- +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +require 'socket' + +describe "Socket#gethostbyname" do + it "returns broadcast address info for '<broadcast>'" do + addr = Socket.gethostbyname('<broadcast>'); + addr.should == ["255.255.255.255", [], 2, "\377\377\377\377"] + end + + it "returns broadcast address info for '<any>'" do + addr = Socket.gethostbyname('<any>'); + addr.should == ["0.0.0.0", [], 2, "\000\000\000\000"] + end +end diff --git a/spec/rubyspec/library/socket/socket/gethostname_spec.rb b/spec/rubyspec/library/socket/socket/gethostname_spec.rb new file mode 100644 index 0000000000..c61e6b3eb4 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/gethostname_spec.rb @@ -0,0 +1,8 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket.gethostname" do + it "returns the host name" do + Socket.gethostname.should == `hostname`.strip + end +end diff --git a/spec/rubyspec/library/socket/socket/getnameinfo_spec.rb b/spec/rubyspec/library/socket/socket/getnameinfo_spec.rb new file mode 100644 index 0000000000..2b0ea4a723 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/getnameinfo_spec.rb @@ -0,0 +1,66 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +require 'socket' + +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 SocketSpecs.port, '127.0.0.1' + name_info = Socket.getnameinfo(sockaddr, Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV) + name_info.should == ['127.0.0.1', "#{SocketSpecs.port}"] + 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 SocketSpecs.port, '127.0.0.1' + name_info = Socket.getnameinfo(sockaddr, Socket::NI_NUMERICSERV) + should_be_valid_dns_name(name_info[0]) + name_info[1].should == SocketSpecs.port.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", SocketSpecs.port, '127.0.0.1'], Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV) + name_info.should == ['127.0.0.1', "#{SocketSpecs.port}"] + 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", SocketSpecs.port, 'foo', '127.0.0.1'], Socket::NI_NUMERICHOST | Socket::NI_NUMERICSERV) + name_info.should == ['127.0.0.1', "#{SocketSpecs.port}"] + 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 + +end diff --git a/spec/rubyspec/library/socket/socket/getservbyname_spec.rb b/spec/rubyspec/library/socket/socket/getservbyname_spec.rb new file mode 100644 index 0000000000..a48b5753b4 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/getservbyname_spec.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +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 '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 + lambda { Socket.getservbyname('invalid') }.should raise_error(SocketError) + end +end diff --git a/spec/rubyspec/library/socket/socket/listen_spec.rb b/spec/rubyspec/library/socket/socket/listen_spec.rb new file mode 100644 index 0000000000..ebc97954fb --- /dev/null +++ b/spec/rubyspec/library/socket/socket/listen_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +include Socket::Constants + +describe "Socket#listen" do + before :each do + @socket = Socket.new(AF_INET, 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(SocketSpecs.port, "127.0.0.1") + @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true) + @socket.bind(sockaddr) + @socket.listen(1).should == 0 + end +end diff --git a/spec/rubyspec/library/socket/socket/new_spec.rb b/spec/rubyspec/library/socket/socket/new_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/new_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/socket/pack_sockaddr_in_spec.rb b/spec/rubyspec/library/socket/socket/pack_sockaddr_in_spec.rb new file mode 100644 index 0000000000..8c95b948dc --- /dev/null +++ b/spec/rubyspec/library/socket/socket/pack_sockaddr_in_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/pack_sockaddr', __FILE__) + +describe "Socket#pack_sockaddr_in" do + it_behaves_like :socket_pack_sockaddr_in, :pack_sockaddr_in +end diff --git a/spec/rubyspec/library/socket/socket/pack_sockaddr_un_spec.rb b/spec/rubyspec/library/socket/socket/pack_sockaddr_un_spec.rb new file mode 100644 index 0000000000..aacb6d54dc --- /dev/null +++ b/spec/rubyspec/library/socket/socket/pack_sockaddr_un_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/pack_sockaddr', __FILE__) + +describe "Socket#pack_sockaddr_un" do + it_behaves_like :socket_pack_sockaddr_un, :pack_sockaddr_un +end diff --git a/spec/rubyspec/library/socket/socket/pair_spec.rb b/spec/rubyspec/library/socket/socket/pair_spec.rb new file mode 100644 index 0000000000..663ca3f183 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/pair_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/socketpair', __FILE__) + +describe "Socket#pair" do + it_behaves_like :socket_socketpair, :pair +end diff --git a/spec/rubyspec/library/socket/socket/recvfrom_nonblock_spec.rb b/spec/rubyspec/library/socket/socket/recvfrom_nonblock_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/recvfrom_nonblock_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/socket/recvfrom_spec.rb b/spec/rubyspec/library/socket/socket/recvfrom_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/recvfrom_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/socket/sockaddr_in_spec.rb b/spec/rubyspec/library/socket/socket/sockaddr_in_spec.rb new file mode 100644 index 0000000000..59e0318fda --- /dev/null +++ b/spec/rubyspec/library/socket/socket/sockaddr_in_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/pack_sockaddr', __FILE__) + +describe "Socket#sockaddr_in" do + it_behaves_like :socket_pack_sockaddr_in, :sockaddr_in +end diff --git a/spec/rubyspec/library/socket/socket/sockaddr_un_spec.rb b/spec/rubyspec/library/socket/socket/sockaddr_un_spec.rb new file mode 100644 index 0000000000..fa233587d9 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/sockaddr_un_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/pack_sockaddr', __FILE__) + +describe "Socket#sockaddr_un" do + it_behaves_like :socket_pack_sockaddr_un, :sockaddr_un +end diff --git a/spec/rubyspec/library/socket/socket/socket_spec.rb b/spec/rubyspec/library/socket/socket/socket_spec.rb new file mode 100644 index 0000000000..dbaed17af4 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/socket_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +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/rubyspec/library/socket/socket/socketpair_spec.rb b/spec/rubyspec/library/socket/socket/socketpair_spec.rb new file mode 100644 index 0000000000..80b07170a6 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/socketpair_spec.rb @@ -0,0 +1,7 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/socketpair', __FILE__) + +describe "Socket#socketpair" do + it_behaves_like :socket_socketpair, :socketpair +end diff --git a/spec/rubyspec/library/socket/socket/sysaccept_spec.rb b/spec/rubyspec/library/socket/socket/sysaccept_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/sysaccept_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/socket/unpack_sockaddr_in_spec.rb b/spec/rubyspec/library/socket/socket/unpack_sockaddr_in_spec.rb new file mode 100644 index 0000000000..2df3b69a6d --- /dev/null +++ b/spec/rubyspec/library/socket/socket/unpack_sockaddr_in_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require 'socket' + +describe "Socket.unpack_sockaddr_in" do + + it "decodes the host name and port number of a packed sockaddr_in" do + sockaddr = Socket.sockaddr_in SocketSpecs.port, '127.0.0.1' + Socket.unpack_sockaddr_in(sockaddr).should == [SocketSpecs.port, '127.0.0.1'] + end + + it "gets the hostname and port number from a passed Addrinfo" do + addrinfo = Addrinfo.tcp('127.0.0.1', SocketSpecs.port) + Socket.unpack_sockaddr_in(addrinfo).should == [SocketSpecs.port, '127.0.0.1'] + end + + platform_is_not :windows do + it "raises an ArgumentError when the sin_family is not AF_INET" do + sockaddr = Socket.sockaddr_un '/tmp/x' + lambda { 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') + lambda { Socket.unpack_sockaddr_in(addrinfo) }.should raise_error(ArgumentError) + end + end + +end diff --git a/spec/rubyspec/library/socket/socket/unpack_sockaddr_un_spec.rb b/spec/rubyspec/library/socket/socket/unpack_sockaddr_un_spec.rb new file mode 100644 index 0000000000..4d558c89c4 --- /dev/null +++ b/spec/rubyspec/library/socket/socket/unpack_sockaddr_un_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe 'Socket.unpack_sockaddr_un' do + platform_is_not :windows 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 sin_family is not AF_UNIX' do + sockaddr = Socket.sockaddr_in(SocketSpecs.port, '127.0.0.1') + lambda { 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', SocketSpecs.port) + lambda { Socket.unpack_sockaddr_un(addrinfo) }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/rubyspec/library/socket/tcpserver/accept_nonblock_spec.rb b/spec/rubyspec/library/socket/tcpserver/accept_nonblock_spec.rb new file mode 100644 index 0000000000..d0f2673af3 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpserver/accept_nonblock_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "Socket::TCPServer.accept_nonblock" do + before :each do + @server = TCPServer.new("127.0.0.1", SocketSpecs.port) + end + + after :each do + @server.close unless @server.closed? + end + + it "accepts non blocking connections" do + @server.listen(5) + lambda { + @server.accept_nonblock + }.should raise_error(IO::WaitReadable) + + c = TCPSocket.new("127.0.0.1", SocketSpecs.port) + sleep 0.1 + s = @server.accept_nonblock + + port, address = Socket.unpack_sockaddr_in(s.getsockname) + + port.should == SocketSpecs.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 + lambda { @server.accept }.should raise_error(IOError) + end + + describe 'without a connected client' do + it 'raises error' do + lambda { @server.accept_nonblock }.should raise_error(IO::WaitReadable) + end + + ruby_version_is '2.3' do + it 'returns :wait_readable in exceptionless mode' do + @server.accept_nonblock(exception: false).should == :wait_readable + end + end + end +end diff --git a/spec/rubyspec/library/socket/tcpserver/accept_spec.rb b/spec/rubyspec/library/socket/tcpserver/accept_spec.rb new file mode 100644 index 0000000000..cf1fbbd873 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpserver/accept_spec.rb @@ -0,0 +1,66 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + + +describe "TCPServer#accept" do + before :each do + @server = TCPServer.new("127.0.0.1", SocketSpecs.port) + 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', SocketSpecs.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 = 1 + while a < 2000 + break unless t.alive? + Thread.pass + sleep 0.2 + a += 1 + end + a.should < 2000 + end + + it "can be interrupted by Thread#raise" do + t = Thread.new { @server.accept } + + Thread.pass while t.status and t.status != "sleep" + + # raise in thread, ensure the raise happens + ex = Exception.new + t.raise ex + lambda { t.join }.should raise_error(Exception) + end + + it "raises an IOError if the socket is closed" do + @server.close + lambda { @server.accept }.should raise_error(IOError) + end +end diff --git a/spec/rubyspec/library/socket/tcpserver/gets_spec.rb b/spec/rubyspec/library/socket/tcpserver/gets_spec.rb new file mode 100644 index 0000000000..2f8e699a53 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpserver/gets_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "TCPServer#gets" do + before :each do + @server = TCPServer.new(SocketSpecs.hostname, SocketSpecs.port) + end + + after :each do + @server.close + end + + it "raises Errno::ENOTCONN on gets" do + lambda { @server.gets }.should raise_error(Errno::ENOTCONN) + end +end diff --git a/spec/rubyspec/library/socket/tcpserver/listen_spec.rb b/spec/rubyspec/library/socket/tcpserver/listen_spec.rb new file mode 100644 index 0000000000..5e9b9c1090 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpserver/listen_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +require 'socket' + +describe 'TCPServer#listen' do + before :each do + @server = TCPServer.new(SocketSpecs.hostname, SocketSpecs.port) + end + + after :each do + @server.close unless @server.closed? + end + + it 'returns 0' do + @server.listen(10).should == 0 + end +end diff --git a/spec/rubyspec/library/socket/tcpserver/new_spec.rb b/spec/rubyspec/library/socket/tcpserver/new_spec.rb new file mode 100644 index 0000000000..4b7f53a397 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpserver/new_spec.rb @@ -0,0 +1,97 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +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', SocketSpecs.port) + addr = @server.addr + addr[0].should == 'AF_INET' + addr[1].should be_kind_of(Fixnum) + # 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, SocketSpecs.port) + addr = @server.addr + if addr[0] == 'AF_INET' + addr[1].should == SocketSpecs.port + addr[2].should =~ /^#{SocketSpecs.hostname}\b/ + addr[3].should == '127.0.0.1' + else + addr[1].should == SocketSpecs.port + addr[2].should =~ /^#{SocketSpecs.hostnamev6}\b/ + addr[3].should == '::1' + end + end + + it "binds to INADDR_ANY if the hostname is empty" do + @server = TCPServer.new('', SocketSpecs.port) + addr = @server.addr + addr[0].should == 'AF_INET' + addr[1].should == SocketSpecs.port + 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('', SocketSpecs.port.to_s) + addr = @server.addr + addr[0].should == 'AF_INET' + addr[1].should == SocketSpecs.port + 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 + t = Object.new + lambda { TCPServer.new(SocketSpecs.hostname, t) }.should raise_error(TypeError) + + def t.to_str; SocketSpecs.port.to_s; end + + @server = TCPServer.new(SocketSpecs.hostname, t) + addr = @server.addr + addr[1].should == SocketSpecs.port + + # 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 "raises Errno::EADDRNOTAVAIL when the adress is unknown" do + lambda { TCPServer.new("1.2.3.4", 4000) }.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 + lambda { + TCPServer.new("--notavalidname", 4000) + }.should raise_error(SocketError) + end + end + + it "raises Errno::EADDRINUSE when address is already in use" do + lambda { + @server = TCPServer.new('127.0.0.1', SocketSpecs.port) + @server = TCPServer.new('127.0.0.1', SocketSpecs.port) + }.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', SocketSpecs.port) + @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/rubyspec/library/socket/tcpserver/output_spec.rb b/spec/rubyspec/library/socket/tcpserver/output_spec.rb new file mode 100644 index 0000000000..b193bb83ea --- /dev/null +++ b/spec/rubyspec/library/socket/tcpserver/output_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "TCPServer#<<" do + after :each do + @server.close if @server + @socket.close if @socket + end +end diff --git a/spec/rubyspec/library/socket/tcpserver/readpartial_spec.rb b/spec/rubyspec/library/socket/tcpserver/readpartial_spec.rb new file mode 100644 index 0000000000..35d07a0309 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpserver/readpartial_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "TCPServer#readpartial" do + after :each do + @server.close if @server + @socket.close if @socket + end +end diff --git a/spec/rubyspec/library/socket/tcpserver/sysaccept_spec.rb b/spec/rubyspec/library/socket/tcpserver/sysaccept_spec.rb new file mode 100644 index 0000000000..1cd1c1c719 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpserver/sysaccept_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +require 'socket' + +describe "TCPServer#sysaccept" do + before :each do + @server = TCPServer.new(SocketSpecs.hostname, SocketSpecs.port) + end + + after :each do + @server.close unless @server.closed? + end + + it 'blocks if no connections' do + lambda { @server.sysaccept }.should block_caller + end + + it 'returns file descriptor of an accepted connection' do + begin + sock = TCPSocket.new(SocketSpecs.hostname, SocketSpecs.port) + + fd = @server.sysaccept + + fd.should be_an_instance_of(Fixnum) + ensure + sock.close if sock && !sock.closed? + IO.for_fd(fd).close if fd + end + end +end diff --git a/spec/rubyspec/library/socket/tcpsocket/gethostbyname_spec.rb b/spec/rubyspec/library/socket/tcpsocket/gethostbyname_spec.rb new file mode 100644 index 0000000000..11838aca27 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpsocket/gethostbyname_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +# TODO: verify these for windows +describe "TCPSocket#gethostbyname" do + before :each do + @host_info = TCPSocket.gethostbyname(SocketSpecs.hostname) + 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 diff --git a/spec/rubyspec/library/socket/tcpsocket/new_spec.rb b/spec/rubyspec/library/socket/tcpsocket/new_spec.rb new file mode 100644 index 0000000000..279576272b --- /dev/null +++ b/spec/rubyspec/library/socket/tcpsocket/new_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../shared/new', __FILE__) + +describe "TCPSocket.new" do + it_behaves_like :tcpsocket_new, :new +end diff --git a/spec/rubyspec/library/socket/tcpsocket/open_spec.rb b/spec/rubyspec/library/socket/tcpsocket/open_spec.rb new file mode 100644 index 0000000000..fb4cc4629a --- /dev/null +++ b/spec/rubyspec/library/socket/tcpsocket/open_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../shared/new', __FILE__) + +describe "TCPSocket.open" do + it_behaves_like :tcpsocket_new, :open +end diff --git a/spec/rubyspec/library/socket/tcpsocket/partially_closable_spec.rb b/spec/rubyspec/library/socket/tcpsocket/partially_closable_spec.rb new file mode 100644 index 0000000000..f38b251090 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpsocket/partially_closable_spec.rb @@ -0,0 +1,22 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/partially_closable_sockets', __FILE__) + +describe "TCPSocket partial closability" do + + before :each do + port = SocketSpecs.find_available_port + @server = TCPServer.new("127.0.0.1", port) + @s1 = TCPSocket.new("127.0.0.1", port) + @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/rubyspec/library/socket/tcpsocket/recv_nonblock_spec.rb b/spec/rubyspec/library/socket/tcpsocket/recv_nonblock_spec.rb new file mode 100644 index 0000000000..1d89aa866c --- /dev/null +++ b/spec/rubyspec/library/socket/tcpsocket/recv_nonblock_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +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, SocketSpecs.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 + + ruby_version_is '2.3' do + it 'returns :wait_readable in exceptionless mode' do + @socket = TCPSocket.new @hostname, SocketSpecs.port + @socket.recv_nonblock(50, exception: false).should == :wait_readable + end + end +end diff --git a/spec/rubyspec/library/socket/tcpsocket/setsockopt_spec.rb b/spec/rubyspec/library/socket/tcpsocket/setsockopt_spec.rb new file mode 100644 index 0000000000..e4d3da10d1 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpsocket/setsockopt_spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "TCPSocket#setsockopt" do + before :each do + @server = SocketSpecs::SpecTCPServer.new + @hostname = @server.hostname + end + + before :each do + @sock = TCPSocket.new @hostname, SocketSpecs.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
\ No newline at end of file diff --git a/spec/rubyspec/library/socket/tcpsocket/shared/new.rb b/spec/rubyspec/library/socket/tcpsocket/shared/new.rb new file mode 100644 index 0000000000..b6f557fc18 --- /dev/null +++ b/spec/rubyspec/library/socket/tcpsocket/shared/new.rb @@ -0,0 +1,71 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/classes', __FILE__) + +describe :tcpsocket_new, shared: true do + it "requires a hostname and a port as arguments" do + lambda { TCPSocket.send(@method) }.should raise_error(ArgumentError) + end + + it "refuses the connection when there is no server to connect to" do + lambda do + TCPSocket.send(@method, SocketSpecs.hostname, SocketSpecs.local_port) + end.should raise_error(Errno::ECONNREFUSED) + 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, SocketSpecs.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, SocketSpecs.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, SocketSpecs.port, @hostname) + @socket.should be_an_instance_of(TCPSocket) + end + + it "connects to a server when passed local_host and local_port arguments" do + @socket = TCPSocket.send(@method, @hostname, SocketSpecs.port, + @hostname, SocketSpecs.local_port) + @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, SocketSpecs.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(Fixnum) + @socket.addr[2].should =~ /^#{@hostname}/ + end + end +end diff --git a/spec/rubyspec/library/socket/udpsocket/bind_spec.rb b/spec/rubyspec/library/socket/udpsocket/bind_spec.rb new file mode 100644 index 0000000000..067baa2472 --- /dev/null +++ b/spec/rubyspec/library/socket/udpsocket/bind_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +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, SocketSpecs.port) + + lambda { @socket.bind(SocketSpecs.hostname, SocketSpecs.port) }.should raise_error + end + + it "receives a hostname and a port" do + @socket.bind(SocketSpecs.hostname, SocketSpecs.port) + + port, host = Socket.unpack_sockaddr_in(@socket.getsockname) + + host.should == "127.0.0.1" + port.should == SocketSpecs.port + end + + it "binds to INADDR_ANY if the hostname is empty" do + @socket.bind("", SocketSpecs.port) + port, host = Socket.unpack_sockaddr_in(@socket.getsockname) + host.should == "0.0.0.0" + port.should == SocketSpecs.port + end +end diff --git a/spec/rubyspec/library/socket/udpsocket/connect_spec.rb b/spec/rubyspec/library/socket/udpsocket/connect_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/udpsocket/connect_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/udpsocket/new_spec.rb b/spec/rubyspec/library/socket/udpsocket/new_spec.rb new file mode 100644 index 0000000000..5a2e4e1f31 --- /dev/null +++ b/spec/rubyspec/library/socket/udpsocket/new_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +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 Fixnum 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 'raises Errno::EAFNOSUPPORT if unsupported family passed' do + lambda { UDPSocket.new(-1) }.should raise_error(Errno::EAFNOSUPPORT) + end +end diff --git a/spec/rubyspec/library/socket/udpsocket/open_spec.rb b/spec/rubyspec/library/socket/udpsocket/open_spec.rb new file mode 100644 index 0000000000..188f879ed1 --- /dev/null +++ b/spec/rubyspec/library/socket/udpsocket/open_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +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/rubyspec/library/socket/udpsocket/recvfrom_nonblock_spec.rb b/spec/rubyspec/library/socket/udpsocket/recvfrom_nonblock_spec.rb new file mode 100644 index 0000000000..fcd29e1257 --- /dev/null +++ b/spec/rubyspec/library/socket/udpsocket/recvfrom_nonblock_spec.rb @@ -0,0 +1,2 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) diff --git a/spec/rubyspec/library/socket/udpsocket/send_spec.rb b/spec/rubyspec/library/socket/udpsocket/send_spec.rb new file mode 100644 index 0000000000..ad0e6a7f2f --- /dev/null +++ b/spec/rubyspec/library/socket/udpsocket/send_spec.rb @@ -0,0 +1,58 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UDPSocket.send" do + before :each do + @ready = false + @server_thread = Thread.new do + @server = UDPSocket.open + @server.bind(nil, SocketSpecs.port) + @ready = true + begin + @msg = @server.recvfrom_nonblock(64) + rescue IO::WaitReadable + IO.select([@server]) + retry + end + @server.close + end + Thread.pass while @server_thread.status and !@ready + end + + it "sends data in ad hoc mode" do + @socket = UDPSocket.open + @socket.send("ad hoc", 0, SocketSpecs.hostname,SocketSpecs.port) + @socket.close + @server_thread.join + + @msg[0].should == "ad hoc" + @msg[1][0].should == "AF_INET" + @msg[1][1].should be_kind_of(Fixnum) + @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,SocketSpecs.str_port) + @socket.close + @server_thread.join + + @msg[0].should == "ad hoc" + @msg[1][0].should == "AF_INET" + @msg[1][1].should be_kind_of(Fixnum) + @msg[1][3].should == "127.0.0.1" + end + + it "sends data in connection mode" do + @socket = UDPSocket.open + @socket.connect(SocketSpecs.hostname,SocketSpecs.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(Fixnum) + @msg[1][3].should == "127.0.0.1" + end +end diff --git a/spec/rubyspec/library/socket/unixserver/accept_nonblock_spec.rb b/spec/rubyspec/library/socket/unixserver/accept_nonblock_spec.rb new file mode 100644 index 0000000000..bad9139eea --- /dev/null +++ b/spec/rubyspec/library/socket/unixserver/accept_nonblock_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UNIXServer#accept_nonblock" do + + platform_is_not :windows do + before :each do + @path = SocketSpecs.socket_path + rm_r @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 + rm_r @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 + + ruby_version_is '2.3' do + it 'returns :wait_readable in exceptionless mode' do + @server.accept_nonblock(exception: false).should == :wait_readable + end + end + end +end diff --git a/spec/rubyspec/library/socket/unixserver/accept_spec.rb b/spec/rubyspec/library/socket/unixserver/accept_spec.rb new file mode 100644 index 0000000000..6ce1f2c6db --- /dev/null +++ b/spec/rubyspec/library/socket/unixserver/accept_spec.rb @@ -0,0 +1,66 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is_not :windows do + describe "UNIXServer#accept" do + before :each do + @path = SocketSpecs.socket_path + rm_r @path + end + + after :each do + rm_r @path + end + + it "accepts what is written by the client" do + server = UNIXServer.open(SocketSpecs.socket_path) + client = UNIXSocket.open(SocketSpecs.socket_path) + + client.send('hello', 0) + + sock = server.accept + data, info = sock.recvfrom(5) + + data.should == 'hello' + info.should_not be_empty + + server.close + client.close + sock.close + end + + it "can be interrupted by Thread#kill" do + server = UNIXServer.new(@path) + 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 = 1 + while a < 2000 + break unless t.alive? + Thread.pass + sleep 0.2 + a += 1 + end + a.should < 2000 + server.close + end + + it "can be interrupted by Thread#raise" do + server = UNIXServer.new(@path) + t = Thread.new { + server.accept + } + Thread.pass while t.status and t.status != "sleep" + + # raise in thread, ensure the raise happens + ex = Exception.new + t.raise ex + lambda { t.join }.should raise_error(Exception) + server.close + end + end +end diff --git a/spec/rubyspec/library/socket/unixserver/for_fd_spec.rb b/spec/rubyspec/library/socket/unixserver/for_fd_spec.rb new file mode 100644 index 0000000000..bf8aa41d40 --- /dev/null +++ b/spec/rubyspec/library/socket/unixserver/for_fd_spec.rb @@ -0,0 +1,23 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +platform_is_not :windows do + describe "UNIXServer#for_fd" do + before :each do + @unix_path = tmp("unix_socket") + @unix = UNIXServer.new(@unix_path) + end + + after :each do + @unix.close if @unix + rm_r @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 +end diff --git a/spec/rubyspec/library/socket/unixserver/new_spec.rb b/spec/rubyspec/library/socket/unixserver/new_spec.rb new file mode 100644 index 0000000000..d34aa0ca03 --- /dev/null +++ b/spec/rubyspec/library/socket/unixserver/new_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/new', __FILE__) + +describe "UNIXServer.new" do + it_behaves_like :unixserver_new, :new +end diff --git a/spec/rubyspec/library/socket/unixserver/open_spec.rb b/spec/rubyspec/library/socket/unixserver/open_spec.rb new file mode 100644 index 0000000000..4401d9dda8 --- /dev/null +++ b/spec/rubyspec/library/socket/unixserver/open_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/new', __FILE__) + +describe "UNIXServer.open" do + it_behaves_like :unixserver_new, :open + + platform_is_not :windows do + before :each do + @path = tmp("unixserver_spec") + rm_r @path + end + + after :each do + @server.close if @server + @server = nil + rm_r @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 +end diff --git a/spec/rubyspec/library/socket/unixserver/shared/new.rb b/spec/rubyspec/library/socket/unixserver/shared/new.rb new file mode 100644 index 0000000000..9b0798b828 --- /dev/null +++ b/spec/rubyspec/library/socket/unixserver/shared/new.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/classes', __FILE__) +require 'tempfile' + +describe :unixserver_new, shared: true do + platform_is_not :windows do + before :each do + @path = tmp("unixserver_spec") + rm_r @path + end + + after :each do + @server.close if @server + @server = nil + rm_r @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 +end diff --git a/spec/rubyspec/library/socket/unixsocket/addr_spec.rb b/spec/rubyspec/library/socket/unixsocket/addr_spec.rb new file mode 100644 index 0000000000..893a910e92 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/addr_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UNIXSocket#addr" do + + platform_is_not :windows do + before :each do + @path = SocketSpecs.socket_path + rm_r @path + + @server = UNIXServer.open(@path) + @client = UNIXSocket.open(@path) + end + + after :each do + @client.close + @server.close + rm_r @path + end + + it "returns the address family of this socket in an array" do + @client.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 + + it "returns an array" do + @client.addr.should be_kind_of(Array) + end + end + +end diff --git a/spec/rubyspec/library/socket/unixsocket/inspect_spec.rb b/spec/rubyspec/library/socket/unixsocket/inspect_spec.rb new file mode 100644 index 0000000000..8ea25ec1e9 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/inspect_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UNIXSocket#inspect" do + platform_is_not :windows 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 +end diff --git a/spec/rubyspec/library/socket/unixsocket/new_spec.rb b/spec/rubyspec/library/socket/unixsocket/new_spec.rb new file mode 100644 index 0000000000..7db8613b96 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/new_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/new', __FILE__) + +describe "UNIXSocket.new" do + it_behaves_like :unixsocket_new, :new +end diff --git a/spec/rubyspec/library/socket/unixsocket/open_spec.rb b/spec/rubyspec/library/socket/unixsocket/open_spec.rb new file mode 100644 index 0000000000..eb8ffbaf22 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/open_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../shared/new', __FILE__) + +describe "UNIXSocket.open" do + it_behaves_like :unixsocket_new, :open +end + +describe "UNIXSocket.open" do + platform_is_not :windows do + before :each do + @path = SocketSpecs.socket_path + @server = UNIXServer.open(@path) + end + + after :each do + @server.close + rm_r @path + end + + it "opens a unix socket on the specified file and yields it to the block" do + UNIXSocket.send(@method, @path) do |client| + client.addr[0].should == "AF_UNIX" + client.closed?.should == false + end + end + end +end diff --git a/spec/rubyspec/library/socket/unixsocket/pair_spec.rb b/spec/rubyspec/library/socket/unixsocket/pair_spec.rb new file mode 100644 index 0000000000..5cd75e2906 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/pair_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/partially_closable_sockets', __FILE__) + +describe "UNIXSocket#pair" do + platform_is_not :windows do + + it_should_behave_like "partially closable sockets" + + before :each do + @s1, @s2 = UNIXSocket.pair + end + + after :each do + @s1.close + @s2.close + end + + it "returns a pair of connected sockets" do + @s1.puts "foo" + @s2.gets.should == "foo\n" + end + + it "returns sockets with no name" do + @s1.path.should == @s2.path + @s1.path.should == "" + end + + it "returns sockets with no address" do + @s1.addr.should == ["AF_UNIX", ""] + @s2.addr.should == ["AF_UNIX", ""] + end + + it "returns sockets with no peeraddr" do + @s1.peeraddr.should == ["AF_UNIX", ""] + @s2.peeraddr.should == ["AF_UNIX", ""] + end + end +end diff --git a/spec/rubyspec/library/socket/unixsocket/partially_closable_spec.rb b/spec/rubyspec/library/socket/unixsocket/partially_closable_spec.rb new file mode 100644 index 0000000000..1123a23541 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/partially_closable_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) +require File.expand_path('../../shared/partially_closable_sockets', __FILE__) + +platform_is_not :windows do + describe "UNIXSocket partial closability" do + + before :each do + @path = SocketSpecs.socket_path + rm_r @path + @server = UNIXServer.open(@path) + @s1 = UNIXSocket.new(@path) + @s2 = @server.accept + end + + after :each do + @server.close + @s1.close + @s2.close + rm_r @path + end + + it_should_behave_like "partially closable sockets" + + end +end diff --git a/spec/rubyspec/library/socket/unixsocket/path_spec.rb b/spec/rubyspec/library/socket/unixsocket/path_spec.rb new file mode 100644 index 0000000000..a9186854da --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/path_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UNIXSocket#path" do + + platform_is_not :windows do + before :each do + @path = SocketSpecs.socket_path + rm_r @path + + @server = UNIXServer.open(@path) + @client = UNIXSocket.open(@path) + end + + after :each do + @client.close + @server.close + rm_r @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 + +end diff --git a/spec/rubyspec/library/socket/unixsocket/peeraddr_spec.rb b/spec/rubyspec/library/socket/unixsocket/peeraddr_spec.rb new file mode 100644 index 0000000000..cd224540ef --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/peeraddr_spec.rb @@ -0,0 +1,30 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UNIXSocket#peeraddr" do + + platform_is_not :windows do + before :each do + @path = SocketSpecs.socket_path + rm_r @path + + @server = UNIXServer.open(@path) + @client = UNIXSocket.open(@path) + end + + after :each do + @client.close + @server.close + rm_r @path + end + + it "returns the address familly 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 + lambda { @server.peeraddr }.should raise_error + end + end + +end diff --git a/spec/rubyspec/library/socket/unixsocket/recv_io_spec.rb b/spec/rubyspec/library/socket/unixsocket/recv_io_spec.rb new file mode 100644 index 0000000000..9fc4470572 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/recv_io_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UNIXSocket#recv_io" do + + platform_is_not :windows 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 + rm_r @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_kind_of(File) + end + end +end diff --git a/spec/rubyspec/library/socket/unixsocket/recvfrom_spec.rb b/spec/rubyspec/library/socket/unixsocket/recvfrom_spec.rb new file mode 100644 index 0000000000..7ac002607c --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/recvfrom_spec.rb @@ -0,0 +1,49 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UNIXSocket#recvfrom" do + + platform_is_not :windows do + before :each do + @path = SocketSpecs.socket_path + rm_r @path + + @server = UNIXServer.open(@path) + @client = UNIXSocket.open(@path) + end + + after :each do + @client.close + @server.close + rm_r @path + end + + it "receives len bytes from sock" do + @client.send("foobar", 0) + sock = @server.accept + sock.recvfrom(6).first.should == "foobar" + sock.close + end + + it "returns an array with data and information on the sender" do + @client.send("foobar", 0) + sock = @server.accept + data = sock.recvfrom(6) + data.first.should == "foobar" + data.last.should == ["AF_UNIX", ""] + sock.close + end + + 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 diff --git a/spec/rubyspec/library/socket/unixsocket/send_io_spec.rb b/spec/rubyspec/library/socket/unixsocket/send_io_spec.rb new file mode 100644 index 0000000000..fb6ce9ba17 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/send_io_spec.rb @@ -0,0 +1,35 @@ +require File.expand_path('../../../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/classes', __FILE__) + +describe "UNIXSocket#send_io" do + + platform_is_not :windows 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 + rm_r @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 +end diff --git a/spec/rubyspec/library/socket/unixsocket/shared/new.rb b/spec/rubyspec/library/socket/unixsocket/shared/new.rb new file mode 100644 index 0000000000..9d8fb809d2 --- /dev/null +++ b/spec/rubyspec/library/socket/unixsocket/shared/new.rb @@ -0,0 +1,24 @@ +require File.expand_path('../../../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/classes', __FILE__) + +describe :unixsocket_new, shared: true do + platform_is_not :windows do + before :each do + @path = SocketSpecs.socket_path + @server = UNIXServer.open(@path) + end + + after :each do + @client.close if @client + @server.close + rm_r @path + end + + it "opens a unix socket on the specified file" do + @client = UNIXSocket.send(@method, @path) + + @client.addr[0].should == "AF_UNIX" + @client.closed?.should == false + end + end +end |