summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/ruby/README.md7
-rw-r--r--spec/ruby/command_line/rubyopt_spec.rb2
-rw-r--r--spec/ruby/core/array/reject_spec.rb25
-rw-r--r--spec/ruby/core/binding/eval_spec.rb60
-rw-r--r--spec/ruby/core/binding/fixtures/classes.rb12
-rw-r--r--spec/ruby/core/binding/location_spec.rb46
-rw-r--r--spec/ruby/core/enumerable/sort_by_spec.rb7
-rw-r--r--spec/ruby/core/io/read_spec.rb4
-rw-r--r--spec/ruby/core/io/shared/write.rb4
-rw-r--r--spec/ruby/core/kernel/__dir___spec.rb7
-rw-r--r--spec/ruby/core/kernel/autoload_spec.rb11
-rw-r--r--spec/ruby/core/kernel/fixtures/autoload_c.rb5
-rw-r--r--spec/ruby/core/kernel/require_relative_spec.rb40
-rw-r--r--spec/ruby/core/kernel/require_spec.rb2
-rw-r--r--spec/ruby/core/kernel/shared/require.rb76
-rw-r--r--spec/ruby/core/module/autoload_spec.rb307
-rw-r--r--spec/ruby/core/module/const_get_spec.rb21
-rw-r--r--spec/ruby/core/module/fixtures/autoload_callback.rb2
-rw-r--r--spec/ruby/core/module/fixtures/autoload_during_autoload.rb7
-rw-r--r--spec/ruby/core/module/fixtures/autoload_during_require.rb4
-rw-r--r--spec/ruby/core/module/fixtures/autoload_exception.rb3
-rw-r--r--spec/ruby/core/module/fixtures/autoload_nested.rb8
-rw-r--r--spec/ruby/core/module/fixtures/autoload_o.rb1
-rw-r--r--spec/ruby/core/module/fixtures/autoload_raise.rb2
-rw-r--r--spec/ruby/core/module/fixtures/autoload_scope.rb8
-rw-r--r--spec/ruby/core/module/fixtures/constants_autoload.rb6
-rw-r--r--spec/ruby/core/module/fixtures/constants_autoload_a.rb2
-rw-r--r--spec/ruby/core/module/fixtures/constants_autoload_b.rb2
-rw-r--r--spec/ruby/core/module/fixtures/constants_autoload_c.rb3
-rw-r--r--spec/ruby/core/module/fixtures/constants_autoload_d.rb4
-rw-r--r--spec/ruby/core/module/initialize_copy_spec.rb8
-rw-r--r--spec/ruby/core/signal/fixtures/trap_all.rb8
-rw-r--r--spec/ruby/core/signal/trap_spec.rb43
-rw-r--r--spec/ruby/core/string/force_encoding_spec.rb20
-rw-r--r--spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb38
-rw-r--r--spec/ruby/core/thread/backtrace/location/fixtures/absolute_path.rb4
-rw-r--r--spec/ruby/core/time/localtime_spec.rb2
-rw-r--r--spec/ruby/fixtures/code/load_fixture_and__FILE__.rb1
-rw-r--r--spec/ruby/language/array_spec.rb7
-rw-r--r--spec/ruby/language/constants_spec.rb19
-rw-r--r--spec/ruby/language/fixtures/array.rb21
-rw-r--r--spec/ruby/language/optional_assignments_spec.rb22
-rw-r--r--spec/ruby/library/date/iso8601_spec.rb37
-rw-r--r--spec/ruby/library/date/parse_spec.rb5
-rw-r--r--spec/ruby/library/socket/addrinfo/afamily_spec.rb5
-rw-r--r--spec/ruby/library/socket/addrinfo/bind_spec.rb3
-rw-r--r--spec/ruby/library/socket/addrinfo/canonname_spec.rb12
-rw-r--r--spec/ruby/library/socket/addrinfo/connect_from_spec.rb75
-rw-r--r--spec/ruby/library/socket/addrinfo/connect_spec.rb35
-rw-r--r--spec/ruby/library/socket/addrinfo/connect_to_spec.rb75
-rw-r--r--spec/ruby/library/socket/addrinfo/family_addrinfo_spec.rb115
-rw-r--r--spec/ruby/library/socket/addrinfo/foreach_spec.rb9
-rw-r--r--spec/ruby/library/socket/addrinfo/getaddrinfo_spec.rb88
-rw-r--r--spec/ruby/library/socket/addrinfo/getnameinfo_spec.rb44
-rw-r--r--spec/ruby/library/socket/addrinfo/initialize_spec.rb340
-rw-r--r--spec/ruby/library/socket/addrinfo/inspect_sockaddr_spec.rb55
-rw-r--r--spec/ruby/library/socket/addrinfo/inspect_spec.rb65
-rw-r--r--spec/ruby/library/socket/addrinfo/ip_address_spec.rb55
-rw-r--r--spec/ruby/library/socket/addrinfo/ip_port_spec.rb5
-rw-r--r--spec/ruby/library/socket/addrinfo/ip_spec.rb36
-rw-r--r--spec/ruby/library/socket/addrinfo/ip_unpack_spec.rb5
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv4_loopback_spec.rb17
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv4_multicast_spec.rb39
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv4_private_spec.rb14
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv4_spec.rb5
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_linklocal_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_loopback_spec.rb9
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_mc_global_spec.rb20
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_mc_linklocal_spec.rb19
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_mc_nodelocal_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_mc_orglocal_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_mc_sitelocal_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_multicast_spec.rb26
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_sitelocal_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_spec.rb5
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_to_ipv4_spec.rb66
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_unique_local_spec.rb18
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_unspecified_spec.rb15
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_v4compat_spec.rb20
-rw-r--r--spec/ruby/library/socket/addrinfo/ipv6_v4mapped_spec.rb20
-rw-r--r--spec/ruby/library/socket/addrinfo/listen_spec.rb34
-rw-r--r--spec/ruby/library/socket/addrinfo/marshal_dump_spec.rb82
-rw-r--r--spec/ruby/library/socket/addrinfo/marshal_load_spec.rb35
-rw-r--r--spec/ruby/library/socket/addrinfo/pfamily_spec.rb11
-rw-r--r--spec/ruby/library/socket/addrinfo/protocol_spec.rb28
-rw-r--r--spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb24
-rw-r--r--spec/ruby/library/socket/addrinfo/socktype_spec.rb27
-rw-r--r--spec/ruby/library/socket/addrinfo/tcp_spec.rb42
-rw-r--r--spec/ruby/library/socket/addrinfo/to_s_spec.rb3
-rw-r--r--spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb3
-rw-r--r--spec/ruby/library/socket/addrinfo/udp_spec.rb42
-rw-r--r--spec/ruby/library/socket/addrinfo/unix_path_spec.rb19
-rw-r--r--spec/ruby/library/socket/addrinfo/unix_spec.rb39
-rw-r--r--spec/ruby/library/socket/ancillarydata/cmsg_is_spec.rb31
-rw-r--r--spec/ruby/library/socket/ancillarydata/data_spec.rb9
-rw-r--r--spec/ruby/library/socket/ancillarydata/family_spec.rb9
-rw-r--r--spec/ruby/library/socket/ancillarydata/initialize_spec.rb282
-rw-r--r--spec/ruby/library/socket/ancillarydata/int_spec.rb43
-rw-r--r--spec/ruby/library/socket/ancillarydata/ip_pktinfo_spec.rb145
-rw-r--r--spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_addr_spec.rb11
-rw-r--r--spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_ifindex_spec.rb11
-rw-r--r--spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_spec.rb89
-rw-r--r--spec/ruby/library/socket/ancillarydata/level_spec.rb9
-rw-r--r--spec/ruby/library/socket/ancillarydata/type_spec.rb9
-rw-r--r--spec/ruby/library/socket/ancillarydata/unix_rights_spec.rb59
-rw-r--r--spec/ruby/library/socket/basicsocket/close_read_spec.rb10
-rw-r--r--spec/ruby/library/socket/basicsocket/close_write_spec.rb10
-rw-r--r--spec/ruby/library/socket/basicsocket/connect_address_spec.rb150
-rw-r--r--spec/ruby/library/socket/basicsocket/do_not_reverse_lookup_spec.rb2
-rw-r--r--spec/ruby/library/socket/basicsocket/for_fd_spec.rb23
-rw-r--r--spec/ruby/library/socket/basicsocket/getpeereid_spec.rb36
-rw-r--r--spec/ruby/library/socket/basicsocket/getpeername_spec.rb7
-rw-r--r--spec/ruby/library/socket/basicsocket/getsockname_spec.rb4
-rw-r--r--spec/ruby/library/socket/basicsocket/getsockopt_spec.rb144
-rw-r--r--spec/ruby/library/socket/basicsocket/ioctl_spec.rb3
-rw-r--r--spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb64
-rw-r--r--spec/ruby/library/socket/basicsocket/recv_spec.rb67
-rw-r--r--spec/ruby/library/socket/basicsocket/recvmsg_nonblock_spec.rb204
-rw-r--r--spec/ruby/library/socket/basicsocket/recvmsg_spec.rb197
-rw-r--r--spec/ruby/library/socket/basicsocket/send_spec.rb131
-rw-r--r--spec/ruby/library/socket/basicsocket/sendmsg_nonblock_spec.rb104
-rw-r--r--spec/ruby/library/socket/basicsocket/sendmsg_spec.rb111
-rw-r--r--spec/ruby/library/socket/basicsocket/setsockopt_spec.rb123
-rw-r--r--spec/ruby/library/socket/basicsocket/shutdown_spec.rb153
-rw-r--r--spec/ruby/library/socket/constants/constants_spec.rb16
-rw-r--r--spec/ruby/library/socket/fixtures/classes.rb65
-rw-r--r--spec/ruby/library/socket/ipsocket/addr_spec.rb65
-rw-r--r--spec/ruby/library/socket/ipsocket/getaddress_spec.rb4
-rw-r--r--spec/ruby/library/socket/ipsocket/peeraddr_spec.rb68
-rw-r--r--spec/ruby/library/socket/ipsocket/recvfrom_spec.rb55
-rw-r--r--spec/ruby/library/socket/option/bool_spec.rb10
-rw-r--r--spec/ruby/library/socket/option/initialize_spec.rb83
-rw-r--r--spec/ruby/library/socket/option/inspect_spec.rb3
-rw-r--r--spec/ruby/library/socket/option/int_spec.rb23
-rw-r--r--spec/ruby/library/socket/option/linger_spec.rb16
-rw-r--r--spec/ruby/library/socket/option/new_spec.rb2
-rw-r--r--spec/ruby/library/socket/shared/pack_sockaddr.rb42
-rw-r--r--spec/ruby/library/socket/shared/recv_nonblock.rb52
-rw-r--r--spec/ruby/library/socket/shared/socketpair.rb115
-rw-r--r--spec/ruby/library/socket/socket/accept_loop_spec.rb80
-rw-r--r--spec/ruby/library/socket/socket/accept_nonblock_spec.rb107
-rw-r--r--spec/ruby/library/socket/socket/accept_spec.rb122
-rw-r--r--spec/ruby/library/socket/socket/bind_spec.rb71
-rw-r--r--spec/ruby/library/socket/socket/connect_nonblock_spec.rb56
-rw-r--r--spec/ruby/library/socket/socket/connect_spec.rb48
-rw-r--r--spec/ruby/library/socket/socket/for_fd_spec.rb3
-rw-r--r--spec/ruby/library/socket/socket/getaddrinfo_spec.rb279
-rw-r--r--spec/ruby/library/socket/socket/gethostbyaddr_spec.rb121
-rw-r--r--spec/ruby/library/socket/socket/gethostbyname_spec.rb124
-rw-r--r--spec/ruby/library/socket/socket/gethostname_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/getifaddrs_spec.rb108
-rw-r--r--spec/ruby/library/socket/socket/getnameinfo_spec.rb94
-rw-r--r--spec/ruby/library/socket/socket/getservbyname_spec.rb10
-rw-r--r--spec/ruby/library/socket/socket/getservbyport_spec.rb23
-rw-r--r--spec/ruby/library/socket/socket/initialize_spec.rb87
-rw-r--r--spec/ruby/library/socket/socket/ip_address_list_spec.rb50
-rw-r--r--spec/ruby/library/socket/socket/ipv6only_bang_spec.rb17
-rw-r--r--spec/ruby/library/socket/socket/listen_spec.rb50
-rw-r--r--spec/ruby/library/socket/socket/local_address_spec.rb43
-rw-r--r--spec/ruby/library/socket/socket/new_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/pair_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb110
-rw-r--r--spec/ruby/library/socket/socket/recvfrom_spec.rb92
-rw-r--r--spec/ruby/library/socket/socket/remote_address_spec.rb54
-rw-r--r--spec/ruby/library/socket/socket/sockaddr_in_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/sockaddr_un_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/socket_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/socketpair_spec.rb2
-rw-r--r--spec/ruby/library/socket/socket/sysaccept_spec.rb93
-rw-r--r--spec/ruby/library/socket/socket/tcp_server_loop_spec.rb47
-rw-r--r--spec/ruby/library/socket/socket/tcp_server_sockets_spec.rb39
-rw-r--r--spec/ruby/library/socket/socket/tcp_spec.rb70
-rw-r--r--spec/ruby/library/socket/socket/udp_server_loop_on_spec.rb47
-rw-r--r--spec/ruby/library/socket/socket/udp_server_loop_spec.rb47
-rw-r--r--spec/ruby/library/socket/socket/udp_server_recv_spec.rb32
-rw-r--r--spec/ruby/library/socket/socket/udp_server_sockets_spec.rb39
-rw-r--r--spec/ruby/library/socket/socket/unix_server_loop_spec.rb51
-rw-r--r--spec/ruby/library/socket/socket/unix_server_socket_spec.rb48
-rw-r--r--spec/ruby/library/socket/socket/unix_spec.rb45
-rw-r--r--spec/ruby/library/socket/socket/unpack_sockaddr_in_spec.rb27
-rw-r--r--spec/ruby/library/socket/socket/unpack_sockaddr_un_spec.rb6
-rw-r--r--spec/ruby/library/socket/spec_helper.rb16
-rw-r--r--spec/ruby/library/socket/tcpserver/accept_nonblock_spec.rb38
-rw-r--r--spec/ruby/library/socket/tcpserver/accept_spec.rb37
-rw-r--r--spec/ruby/library/socket/tcpserver/gets_spec.rb2
-rw-r--r--spec/ruby/library/socket/tcpserver/initialize_spec.rb99
-rw-r--r--spec/ruby/library/socket/tcpserver/listen_spec.rb26
-rw-r--r--spec/ruby/library/socket/tcpserver/new_spec.rb4
-rw-r--r--spec/ruby/library/socket/tcpserver/sysaccept_spec.rb40
-rw-r--r--spec/ruby/library/socket/tcpsocket/gethostbyname_spec.rb62
-rw-r--r--spec/ruby/library/socket/tcpsocket/initialize_spec.rb61
-rw-r--r--spec/ruby/library/socket/tcpsocket/local_address_spec.rb73
-rw-r--r--spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb2
-rw-r--r--spec/ruby/library/socket/tcpsocket/recv_nonblock_spec.rb2
-rw-r--r--spec/ruby/library/socket/tcpsocket/recv_spec.rb28
-rw-r--r--spec/ruby/library/socket/tcpsocket/remote_address_spec.rb72
-rw-r--r--spec/ruby/library/socket/tcpsocket/setsockopt_spec.rb2
-rw-r--r--spec/ruby/library/socket/tcpsocket/shared/new.rb2
-rw-r--r--spec/ruby/library/socket/udpsocket/bind_spec.rb49
-rw-r--r--spec/ruby/library/socket/udpsocket/connect_spec.rb35
-rw-r--r--spec/ruby/library/socket/udpsocket/initialize_spec.rb36
-rw-r--r--spec/ruby/library/socket/udpsocket/inspect_spec.rb25
-rw-r--r--spec/ruby/library/socket/udpsocket/local_address_spec.rb80
-rw-r--r--spec/ruby/library/socket/udpsocket/new_spec.rb2
-rw-r--r--spec/ruby/library/socket/udpsocket/open_spec.rb2
-rw-r--r--spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb88
-rw-r--r--spec/ruby/library/socket/udpsocket/remote_address_spec.rb79
-rw-r--r--spec/ruby/library/socket/udpsocket/send_spec.rb80
-rw-r--r--spec/ruby/library/socket/udpsocket/write_spec.rb2
-rw-r--r--spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb58
-rw-r--r--spec/ruby/library/socket/unixserver/accept_spec.rb58
-rw-r--r--spec/ruby/library/socket/unixserver/for_fd_spec.rb2
-rw-r--r--spec/ruby/library/socket/unixserver/initialize_spec.rb28
-rw-r--r--spec/ruby/library/socket/unixserver/listen_spec.rb21
-rw-r--r--spec/ruby/library/socket/unixserver/new_spec.rb2
-rw-r--r--spec/ruby/library/socket/unixserver/open_spec.rb3
-rw-r--r--spec/ruby/library/socket/unixserver/shared/new.rb3
-rw-r--r--spec/ruby/library/socket/unixserver/sysaccept_spec.rb52
-rw-r--r--spec/ruby/library/socket/unixsocket/addr_spec.rb12
-rw-r--r--spec/ruby/library/socket/unixsocket/initialize_spec.rb38
-rw-r--r--spec/ruby/library/socket/unixsocket/inspect_spec.rb2
-rw-r--r--spec/ruby/library/socket/unixsocket/local_address_spec.rb45
-rw-r--r--spec/ruby/library/socket/unixsocket/new_spec.rb2
-rw-r--r--spec/ruby/library/socket/unixsocket/open_spec.rb3
-rw-r--r--spec/ruby/library/socket/unixsocket/pair_spec.rb2
-rw-r--r--spec/ruby/library/socket/unixsocket/partially_closable_spec.rb2
-rw-r--r--spec/ruby/library/socket/unixsocket/path_spec.rb2
-rw-r--r--spec/ruby/library/socket/unixsocket/peeraddr_spec.rb2
-rw-r--r--spec/ruby/library/socket/unixsocket/recv_io_spec.rb47
-rw-r--r--spec/ruby/library/socket/unixsocket/recvfrom_spec.rb54
-rw-r--r--spec/ruby/library/socket/unixsocket/remote_address_spec.rb45
-rw-r--r--spec/ruby/library/socket/unixsocket/send_io_spec.rb25
-rw-r--r--spec/ruby/library/socket/unixsocket/shared/new.rb2
-rw-r--r--spec/ruby/library/socket/unixsocket/socketpair_spec.rb40
-rw-r--r--spec/ruby/library/weakref/allocate_spec.rb8
-rw-r--r--spec/ruby/library/weakref/new_spec.rb13
-rw-r--r--spec/ruby/library/zlib/deflate/deflate_spec.rb2
-rw-r--r--spec/ruby/library/zlib/deflate_spec.rb8
-rw-r--r--spec/ruby/library/zlib/inflate_spec.rb8
-rw-r--r--spec/ruby/optional/capi/encoding_spec.rb18
-rw-r--r--spec/ruby/optional/capi/ext/encoding_spec.c2
-rw-r--r--spec/ruby/optional/capi/ext/rubyspec.h6
-rw-r--r--spec/ruby/optional/capi/module_spec.rb24
-rw-r--r--spec/ruby/security/cve_2010_1330_spec.rb21
246 files changed, 9104 insertions, 544 deletions
diff --git a/spec/ruby/README.md b/spec/ruby/README.md
index d4a55ea9c1..f2e18d5800 100644
--- a/spec/ruby/README.md
+++ b/spec/ruby/README.md
@@ -92,6 +92,13 @@ In similar fashion, the following commands run the respective specs:
See [CONTRIBUTING.md](https://github.com/ruby/spec/blob/master/CONTRIBUTING.md).
+### Socket specs from rubysl-socket
+
+Most specs under `library/socket` were imported from [the rubysl-socket project](https://github.com/rubysl/rubysl-socket).
+The 3 copyright holders of rubysl-socket, Yorick Peterse, Chuck Remes and
+Brian Shirai, [agreed to relicense those specs](https://github.com/rubysl/rubysl-socket/issues/15)
+under the MIT license in ruby/spec.
+
### History and RubySpec
This project was originally born from [Rubinius](https://github.com/rubinius/rubinius) tests being converted to the spec style.
diff --git a/spec/ruby/command_line/rubyopt_spec.rb b/spec/ruby/command_line/rubyopt_spec.rb
index fb06f60e51..2db42f77ef 100644
--- a/spec/ruby/command_line/rubyopt_spec.rb
+++ b/spec/ruby/command_line/rubyopt_spec.rb
@@ -22,7 +22,7 @@ describe "Processing RUBYOPT" do
result.should =~ /value of \$DEBUG is true/
end
- unless CROSS_COMPILING
+ guard -> { not CROSS_COMPILING } do
it "prints the version number for '-v'" do
ENV["RUBYOPT"] = '-v'
ruby_exe("")[/\A.*/].should == RUBY_DESCRIPTION
diff --git a/spec/ruby/core/array/reject_spec.rb b/spec/ruby/core/array/reject_spec.rb
index 77835ef5cd..e6e5e851b6 100644
--- a/spec/ruby/core/array/reject_spec.rb
+++ b/spec/ruby/core/array/reject_spec.rb
@@ -111,6 +111,31 @@ describe "Array#reject!" do
lambda { ArraySpecs.empty_frozen_array.reject! {} }.should raise_error(frozen_error_class)
end
+ it "does not truncate the array is the block raises an exception" do
+ a = [1, 2, 3]
+ begin
+ a.reject! { raise StandardError, 'Oops' }
+ rescue
+ end
+
+ a.should == [1, 2, 3]
+ end
+
+ ruby_version_is "2.4" do
+ it "only removes elements for which the block returns true, keeping the element which raised an error." do
+ a = [1, 2, 3, 4]
+ begin
+ a.reject! do |x|
+ return true if x == 2
+ raise raise StandardError, 'Oops' if x == 3
+ end
+ rescue
+ end
+
+ a.should == [1, 3, 4]
+ end
+ end
+
it_behaves_like :enumeratorize, :reject!
it_behaves_like :enumeratorized_with_origin_size, :reject!, [1,2,3]
it_behaves_like :delete_if, :reject!
diff --git a/spec/ruby/core/binding/eval_spec.rb b/spec/ruby/core/binding/eval_spec.rb
index 9a397757e9..c0264192b8 100644
--- a/spec/ruby/core/binding/eval_spec.rb
+++ b/spec/ruby/core/binding/eval_spec.rb
@@ -23,6 +23,54 @@ describe "Binding#eval" do
bind2.local_variables.should == []
end
+ it "inherits __LINE__ from the enclosing scope" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ bind.eval("__LINE__").should == obj.get_line_of_binding
+ end
+
+ it "preserves __LINE__ across multiple calls to eval" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ bind.eval("__LINE__").should == obj.get_line_of_binding
+ bind.eval("__LINE__").should == obj.get_line_of_binding
+ end
+
+ it "increments __LINE__ on each line of a multiline eval" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ bind.eval("#foo\n__LINE__").should == obj.get_line_of_binding + 1
+ end
+
+ it "inherits __LINE__ from the enclosing scope even if the Binding is created with #send" do
+ obj = BindingSpecs::Demo.new(1)
+ bind, line = obj.get_binding_with_send_and_line
+ bind.eval("__LINE__").should == line
+ end
+
+ it "starts with a __LINE__ of 1 if a filename is passed" do
+ bind = BindingSpecs::Demo.new(1).get_binding
+ bind.eval("__LINE__", "(test)").should == 1
+ bind.eval("#foo\n__LINE__", "(test)").should == 2
+ end
+
+ it "starts with a __LINE__ from the third argument if passed" do
+ bind = BindingSpecs::Demo.new(1).get_binding
+ bind.eval("__LINE__", "(test)", 88).should == 88
+ bind.eval("#foo\n__LINE__", "(test)", 88).should == 89
+ end
+
+ it "inherits __FILE__ from the enclosing scope" do
+ obj = BindingSpecs::Demo.new(1)
+ bind = obj.get_binding
+ bind.eval("__FILE__").should == obj.get_file_of_binding
+ end
+
+ it "uses the __FILE__ that is passed in" do
+ bind = BindingSpecs::Demo.new(1).get_binding
+ bind.eval("__FILE__", "(test)").should == "(test)"
+ end
+
describe "with a file given" do
it "does not store the filename permanently" do
obj = BindingSpecs::Demo.new(1)
@@ -33,5 +81,15 @@ describe "Binding#eval" do
end
end
- it "needs to be reviewed for spec completeness"
+ it "with __method__ returns the method where the Binding was created" do
+ obj = BindingSpecs::Demo.new(1)
+ bind, meth = obj.get_binding_and_method
+ bind.eval("__method__").should == meth
+ end
+
+ it "with __method__ returns the method where the Binding was created, ignoring #send" do
+ obj = BindingSpecs::Demo.new(1)
+ bind, meth = obj.get_binding_with_send_and_method
+ bind.eval("__method__").should == meth
+ end
end
diff --git a/spec/ruby/core/binding/fixtures/classes.rb b/spec/ruby/core/binding/fixtures/classes.rb
index 05ca2479ba..43e32cacf6 100644
--- a/spec/ruby/core/binding/fixtures/classes.rb
+++ b/spec/ruby/core/binding/fixtures/classes.rb
@@ -25,6 +25,18 @@ module BindingSpecs
__FILE__
end
+ def get_binding_with_send_and_line
+ [send(:binding), __LINE__]
+ end
+
+ def get_binding_and_method
+ [binding, :get_binding_and_method]
+ end
+
+ def get_binding_with_send_and_method
+ [send(:binding), :get_binding_with_send_and_method]
+ end
+
def get_empty_binding
binding
end
diff --git a/spec/ruby/core/binding/location_spec.rb b/spec/ruby/core/binding/location_spec.rb
deleted file mode 100644
index 2ae0c5e9f1..0000000000
--- a/spec/ruby/core/binding/location_spec.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-require_relative '../../spec_helper'
-require_relative 'fixtures/classes'
-
-describe "Binding#eval" do
- it "inherits __LINE__ from the enclosing scope" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- bind.eval("__LINE__").should == obj.get_line_of_binding
- end
-
- it "preserves __LINE__ across multiple calls to eval" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- bind.eval("__LINE__").should == obj.get_line_of_binding
- bind.eval("__LINE__").should == obj.get_line_of_binding
- end
-
- it "increments __LINE__ on each line of a multiline eval" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- bind.eval("#foo\n__LINE__").should == obj.get_line_of_binding + 1
- end
-
- it "starts with a __LINE__ of 1 if a filename is passed" do
- bind = BindingSpecs::Demo.new(1).get_binding
- bind.eval("__LINE__", "(test)").should == 1
- bind.eval("#foo\n__LINE__", "(test)").should == 2
- end
-
- it "starts with a __LINE__ from the third argument if passed" do
- bind = BindingSpecs::Demo.new(1).get_binding
- bind.eval("__LINE__", "(test)", 88).should == 88
- bind.eval("#foo\n__LINE__", "(test)", 88).should == 89
- end
-
- it "inherits __FILE__ from the enclosing scope" do
- obj = BindingSpecs::Demo.new(1)
- bind = obj.get_binding
- bind.eval("__FILE__").should == obj.get_file_of_binding
- end
-
- it "uses the __FILE__ that is passed in" do
- bind = BindingSpecs::Demo.new(1).get_binding
- bind.eval("__FILE__", "(test)").should == "(test)"
- end
-end
diff --git a/spec/ruby/core/enumerable/sort_by_spec.rb b/spec/ruby/core/enumerable/sort_by_spec.rb
index 46e1135412..8fdd923fb4 100644
--- a/spec/ruby/core/enumerable/sort_by_spec.rb
+++ b/spec/ruby/core/enumerable/sort_by_spec.rb
@@ -32,5 +32,12 @@ describe "Enumerable#sort_by" do
b.sort_by{ |x| -x }.should == [3, 2, 1]
end
+ it "calls #each to iterate over the elements to be sorted" do
+ b = EnumerableSpecs::Numerous.new( 1, 2, 3 )
+ b.should_receive(:each).once.and_yield(1).and_yield(2).and_yield(3)
+ b.should_not_receive :map
+ b.sort_by { |x| -x }.should == [3, 2, 1]
+ end
+
it_behaves_like :enumerable_enumeratorized_with_origin_size, :sort_by
end
diff --git a/spec/ruby/core/io/read_spec.rb b/spec/ruby/core/io/read_spec.rb
index dffa79f10e..3bb581f430 100644
--- a/spec/ruby/core/io/read_spec.rb
+++ b/spec/ruby/core/io/read_spec.rb
@@ -27,6 +27,10 @@ describe "IO.read" do
IO.read(@fname, {}).should == @contents
end
+ it "accepts a length, and empty options Hash" do
+ IO.read(@fname, 3, {}).should == @contents[0, 3]
+ end
+
it "accepts a length, offset, and empty options Hash" do
IO.read(@fname, 3, 0, {}).should == @contents[0, 3]
end
diff --git a/spec/ruby/core/io/shared/write.rb b/spec/ruby/core/io/shared/write.rb
index bca96da81c..140eeb04ab 100644
--- a/spec/ruby/core/io/shared/write.rb
+++ b/spec/ruby/core/io/shared/write.rb
@@ -85,9 +85,9 @@ describe :io_write, shared: true do
@r.read.should == "foo"
end
- it "raises Errno::EPIPE if the read end is closed" do
+ it "raises Errno::EPIPE if the read end is closed and does not die from SIGPIPE" do
@r.close
- -> { @w.send(@method, "foo") }.should raise_error(Errno::EPIPE, "Broken pipe")
+ -> { @w.send(@method, "foo") }.should raise_error(Errno::EPIPE, /Broken pipe/)
end
end
end
diff --git a/spec/ruby/core/kernel/__dir___spec.rb b/spec/ruby/core/kernel/__dir___spec.rb
index 25df92a15d..3c34277277 100644
--- a/spec/ruby/core/kernel/__dir___spec.rb
+++ b/spec/ruby/core/kernel/__dir___spec.rb
@@ -5,6 +5,13 @@ describe "Kernel#__dir__" do
__dir__.should == File.realpath(File.dirname(__FILE__))
end
+ context "when used in eval with a given filename" do
+ it "returns File.dirname(filename)" do
+ eval("__dir__", nil, "foo.rb").should == "."
+ eval("__dir__", nil, "foo/bar.rb").should == "foo"
+ end
+ end
+
context "when used in eval with top level binding" do
it "returns the real name of the directory containing the currently-executing file" do
eval("__dir__", binding).should == File.realpath(File.dirname(__FILE__))
diff --git a/spec/ruby/core/kernel/autoload_spec.rb b/spec/ruby/core/kernel/autoload_spec.rb
index c2573ffac6..5fa8fa92b3 100644
--- a/spec/ruby/core/kernel/autoload_spec.rb
+++ b/spec/ruby/core/kernel/autoload_spec.rb
@@ -7,7 +7,7 @@ require_relative 'fixtures/classes'
autoload :KSAutoloadA, "autoload_a.rb"
autoload :KSAutoloadB, fixture(__FILE__, "autoload_b.rb")
-autoload :KSAutoloadC, fixture(__FILE__, "autoload_c.rb")
+autoload :KSAutoloadCallsRequire, "main_autoload_not_exist.rb"
def check_autoload(const)
autoload? const
@@ -42,10 +42,11 @@ describe "Kernel#autoload" do
KSAutoloadB.loaded.should == :ksautoload_b
end
- it "does not call Kernel.require or Kernel.load to load the file" do
- Kernel.should_not_receive(:require)
- Kernel.should_not_receive(:load)
- KSAutoloadC.loaded.should == :ksautoload_c
+ it "calls main.require(path) to load the file" do
+ main = TOPLEVEL_BINDING.eval("self")
+ main.should_receive(:require).with("main_autoload_not_exist.rb")
+ # The constant won't be defined since require is mocked to do nothing
+ -> { KSAutoloadCallsRequire }.should raise_error(NameError)
end
it "can autoload in instance_eval" do
diff --git a/spec/ruby/core/kernel/fixtures/autoload_c.rb b/spec/ruby/core/kernel/fixtures/autoload_c.rb
deleted file mode 100644
index 4569b23669..0000000000
--- a/spec/ruby/core/kernel/fixtures/autoload_c.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module KSAutoloadC
- def self.loaded
- :ksautoload_c
- end
-end
diff --git a/spec/ruby/core/kernel/require_relative_spec.rb b/spec/ruby/core/kernel/require_relative_spec.rb
index 4a2ef87357..a16e7164e6 100644
--- a/spec/ruby/core/kernel/require_relative_spec.rb
+++ b/spec/ruby/core/kernel/require_relative_spec.rb
@@ -199,6 +199,46 @@ describe "Kernel#require_relative with a relative path" do
$LOADED_FEATURES.should include(@abs_path)
end
+ platform_is_not :windows do
+ describe "with symlinks" do
+ before :each do
+ @symlink_to_code_dir = tmp("codesymlink")
+ File.symlink(CODE_LOADING_DIR, @symlink_to_code_dir)
+ @symlink_basename = File.basename(@symlink_to_code_dir)
+ @requiring_file = tmp("requiring")
+ end
+
+ after :each do
+ rm_r @symlink_to_code_dir, @requiring_file
+ end
+
+ it "does not canonicalize the path and stores a path with symlinks" do
+ symlink_path = "#{@symlink_basename}/load_fixture.rb"
+ absolute_path = "#{tmp("")}#{symlink_path}"
+ canonical_path = "#{CODE_LOADING_DIR}/load_fixture.rb"
+ touch(@requiring_file) { |f|
+ f.puts "require_relative #{symlink_path.inspect}"
+ }
+ load(@requiring_file)
+ ScratchPad.recorded.should == [:loaded]
+
+ features = $LOADED_FEATURES.select { |path| path.end_with?('load_fixture.rb') }
+ features.should include(absolute_path)
+ features.should_not include(canonical_path)
+ end
+
+ it "stores the same path that __FILE__ returns in the required file" do
+ symlink_path = "#{@symlink_basename}/load_fixture_and__FILE__.rb"
+ touch(@requiring_file) { |f|
+ f.puts "require_relative #{symlink_path.inspect}"
+ }
+ load(@requiring_file)
+ loaded_feature = $LOADED_FEATURES.last
+ ScratchPad.recorded.should == [loaded_feature]
+ end
+ end
+ end
+
it "does not store the path if the load fails" do
saved_loaded_features = $LOADED_FEATURES.dup
lambda { require_relative("#{@dir}/raise_fixture.rb") }.should raise_error(RuntimeError)
diff --git a/spec/ruby/core/kernel/require_spec.rb b/spec/ruby/core/kernel/require_spec.rb
index 362acf8003..dc3da4b7e6 100644
--- a/spec/ruby/core/kernel/require_spec.rb
+++ b/spec/ruby/core/kernel/require_spec.rb
@@ -17,7 +17,6 @@ describe "Kernel#require" do
end
it_behaves_like :kernel_require_basic, :require, CodeLoadingSpecs::Method.new
-
it_behaves_like :kernel_require, :require, CodeLoadingSpecs::Method.new
end
@@ -31,6 +30,5 @@ describe "Kernel.require" do
end
it_behaves_like :kernel_require_basic, :require, Kernel
-
it_behaves_like :kernel_require, :require, Kernel
end
diff --git a/spec/ruby/core/kernel/shared/require.rb b/spec/ruby/core/kernel/shared/require.rb
index 5e3f98f813..a81a68088a 100644
--- a/spec/ruby/core/kernel/shared/require.rb
+++ b/spec/ruby/core/kernel/shared/require.rb
@@ -305,6 +305,80 @@ describe :kernel_require, shared: true do
$LOADED_FEATURES.should include(@path)
end
+ platform_is_not :windows do
+ describe "with symlinks" do
+ before :each do
+ @symlink_to_code_dir = tmp("codesymlink")
+ File.symlink(CODE_LOADING_DIR, @symlink_to_code_dir)
+
+ $LOAD_PATH.delete(CODE_LOADING_DIR)
+ $LOAD_PATH.unshift(@symlink_to_code_dir)
+ end
+
+ after :each do
+ rm_r @symlink_to_code_dir
+ end
+
+ it "does not canonicalize the path and stores a path with symlinks" do
+ symlink_path = "#{@symlink_to_code_dir}/load_fixture.rb"
+ canonical_path = "#{CODE_LOADING_DIR}/load_fixture.rb"
+ @object.require(symlink_path).should be_true
+ ScratchPad.recorded.should == [:loaded]
+
+ features = $LOADED_FEATURES.select { |path| path.end_with?('load_fixture.rb') }
+ features.should include(symlink_path)
+ features.should_not include(canonical_path)
+ end
+
+ it "stores the same path that __FILE__ returns in the required file" do
+ symlink_path = "#{@symlink_to_code_dir}/load_fixture_and__FILE__.rb"
+ @object.require(symlink_path).should be_true
+ loaded_feature = $LOADED_FEATURES.last
+ ScratchPad.recorded.should == [loaded_feature]
+ end
+ end
+
+ describe "with symlinks in the required feature and $LOAD_PATH" do
+ before :each do
+ @dir = tmp("realdir")
+ mkdir_p @dir
+ @file = "#{@dir}/realfile.rb"
+ touch(@file) { |f| f.puts 'ScratchPad << __FILE__' }
+
+ @symlink_to_dir = tmp("symdir").freeze
+ File.symlink(@dir, @symlink_to_dir)
+ @symlink_to_file = "#{@dir}/symfile.rb"
+ File.symlink("realfile.rb", @symlink_to_file)
+ end
+
+ after :each do
+ rm_r @dir, @symlink_to_dir
+ end
+
+ ruby_version_is ""..."2.4.4" do
+ it "canonicalizes neither the entry in $LOAD_PATH nor the filename passed to #require" do
+ $LOAD_PATH.unshift(@symlink_to_dir)
+ @object.require("symfile").should be_true
+ loaded_feature = "#{@symlink_to_dir}/symfile.rb"
+ ScratchPad.recorded.should == [loaded_feature]
+ $".last.should == loaded_feature
+ $LOAD_PATH[0].should == @symlink_to_dir
+ end
+ end
+
+ ruby_version_is "2.4.4" do
+ it "canonicalizes the entry in $LOAD_PATH but not the filename passed to #require" do
+ $LOAD_PATH.unshift(@symlink_to_dir)
+ @object.require("symfile").should be_true
+ loaded_feature = "#{@dir}/symfile.rb"
+ ScratchPad.recorded.should == [loaded_feature]
+ $".last.should == loaded_feature
+ $LOAD_PATH[0].should == @symlink_to_dir
+ end
+ end
+ end
+ end
+
it "does not store the path if the load fails" do
$LOAD_PATH << CODE_LOADING_DIR
saved_loaded_features = $LOADED_FEATURES.dup
@@ -417,7 +491,7 @@ describe :kernel_require, shared: true do
$LOADED_FEATURES.should include(@path)
end
- it "canonicalizes non-unique absolute paths" do
+ it "expands absolute paths containing .." do
path = File.join CODE_LOADING_DIR, "..", "code", "load_fixture.rb"
@object.require(path).should be_true
$LOADED_FEATURES.should include(@path)
diff --git a/spec/ruby/core/module/autoload_spec.rb b/spec/ruby/core/module/autoload_spec.rb
index f355a5968d..89c061ff47 100644
--- a/spec/ruby/core/module/autoload_spec.rb
+++ b/spec/ruby/core/module/autoload_spec.rb
@@ -105,6 +105,14 @@ describe "Module#autoload" do
ModuleSpecs::Autoload::J.should == :autoload_j
end
+ it "calls main.require(path) to load the file" do
+ ModuleSpecs::Autoload.autoload :ModuleAutoloadCallsRequire, "module_autoload_not_exist.rb"
+ main = TOPLEVEL_BINDING.eval("self")
+ main.should_receive(:require).with("module_autoload_not_exist.rb")
+ # The constant won't be defined since require is mocked to do nothing
+ -> { ModuleSpecs::Autoload::ModuleAutoloadCallsRequire }.should raise_error(NameError)
+ end
+
it "does not load the file if the file is manually required" do
filename = fixture(__FILE__, "autoload_k.rb")
ModuleSpecs::Autoload.autoload :KHash, filename
@@ -158,28 +166,169 @@ describe "Module#autoload" do
ModuleSpecs::Autoload.use_ex1.should == :good
end
- it "does not load the file when referring to the constant in defined?" do
- module ModuleSpecs::Autoload::Q
- autoload :R, fixture(__FILE__, "autoload.rb")
- defined?(R).should == "constant"
+ describe "interacting with defined?" do
+ it "does not load the file when referring to the constant in defined?" do
+ module ModuleSpecs::Autoload::Dog
+ autoload :R, fixture(__FILE__, "autoload_exception.rb")
+ end
+
+ defined?(ModuleSpecs::Autoload::Dog::R).should == "constant"
+ ScratchPad.recorded.should be_nil
+
+ ModuleSpecs::Autoload::Dog.should have_constant(:R)
+ end
+
+ it "loads an autoloaded parent when referencing a nested constant" do
+ module ModuleSpecs::Autoload
+ autoload :GoodParent, fixture(__FILE__, "autoload_nested.rb")
+ end
+
+ defined?(ModuleSpecs::Autoload::GoodParent::Nested).should == 'constant'
+ ScratchPad.recorded.should == :loaded
+
+ ModuleSpecs::Autoload.send(:remove_const, :GoodParent)
+ end
+
+ it "returns nil when it fails to load an autoloaded parent when referencing a nested constant" do
+ module ModuleSpecs::Autoload
+ autoload :BadParent, fixture(__FILE__, "autoload_exception.rb")
+ end
+
+ defined?(ModuleSpecs::Autoload::BadParent::Nested).should be_nil
+ ScratchPad.recorded.should == :exception
+ end
+ end
+
+ describe "during the autoload before the constant is assigned" do
+ before :each do
+ @path = fixture(__FILE__, "autoload_during_autoload.rb")
+ ModuleSpecs::Autoload.autoload :DuringAutoload, @path
+ raise unless ModuleSpecs::Autoload.autoload?(:DuringAutoload) == @path
+ end
+
+ after :each do
+ ModuleSpecs::Autoload.send(:remove_const, :DuringAutoload)
+ end
+
+ def check_before_during_thread_after(&check)
+ before = check.call
+ to_autoload_thread, from_autoload_thread = Queue.new, Queue.new
+ ScratchPad.record -> {
+ from_autoload_thread.push check.call
+ to_autoload_thread.pop
+ }
+ t = Thread.new {
+ in_loading_thread = from_autoload_thread.pop
+ in_other_thread = check.call
+ to_autoload_thread.push :done
+ [in_loading_thread, in_other_thread]
+ }
+ in_loading_thread, in_other_thread = nil
+ begin
+ ModuleSpecs::Autoload::DuringAutoload
+ ensure
+ in_loading_thread, in_other_thread = t.value
+ end
+ after = check.call
+ [before, in_loading_thread, in_other_thread, after]
+ end
+
+ it "returns nil in autoload thread and 'constant' otherwise for defined?" do
+ results = check_before_during_thread_after {
+ defined?(ModuleSpecs::Autoload::DuringAutoload)
+ }
+ results.should == ['constant', nil, 'constant', 'constant']
+ end
+
+ it "keeps the constant in Module#constants" do
+ results = check_before_during_thread_after {
+ ModuleSpecs::Autoload.constants(false).include?(:DuringAutoload)
+ }
+ results.should == [true, true, true, true]
+ end
+
+ it "returns false in autoload thread and true otherwise for Module#const_defined?" do
+ results = check_before_during_thread_after {
+ ModuleSpecs::Autoload.const_defined?(:DuringAutoload, false)
+ }
+ results.should == [true, false, true, true]
+ end
+
+ it "returns nil in autoload thread and returns the path in other threads for Module#autoload?" do
+ results = check_before_during_thread_after {
+ ModuleSpecs::Autoload.autoload?(:DuringAutoload)
+ }
+ results.should == [@path, nil, @path, nil]
end
- ModuleSpecs::Autoload::Q.should have_constant(:R)
end
- it "does not remove the constant from the constant table if load fails" do
+ it "does not remove the constant from Module#constants if load fails and keeps it as an autoload" do
ModuleSpecs::Autoload.autoload :Fail, @non_existent
+
+ ModuleSpecs::Autoload.const_defined?(:Fail).should == true
ModuleSpecs::Autoload.should have_constant(:Fail)
+ ModuleSpecs::Autoload.autoload?(:Fail).should == @non_existent
lambda { ModuleSpecs::Autoload::Fail }.should raise_error(LoadError)
+
ModuleSpecs::Autoload.should have_constant(:Fail)
+ ModuleSpecs::Autoload.const_defined?(:Fail).should == true
+ ModuleSpecs::Autoload.autoload?(:Fail).should == @non_existent
+
+ lambda { ModuleSpecs::Autoload::Fail }.should raise_error(LoadError)
end
- it "does not remove the constant from the constant table if the loaded files does not define it" do
- ModuleSpecs::Autoload.autoload :O, fixture(__FILE__, "autoload_o.rb")
+ it "does not remove the constant from Module#constants if load raises a RuntimeError and keeps it as an autoload" do
+ path = fixture(__FILE__, "autoload_raise.rb")
+ ScratchPad.record []
+ ModuleSpecs::Autoload.autoload :Raise, path
+
+ ModuleSpecs::Autoload.const_defined?(:Raise).should == true
+ ModuleSpecs::Autoload.should have_constant(:Raise)
+ ModuleSpecs::Autoload.autoload?(:Raise).should == path
+
+ lambda { ModuleSpecs::Autoload::Raise }.should raise_error(RuntimeError)
+ ScratchPad.recorded.should == [:raise]
+
+ ModuleSpecs::Autoload.should have_constant(:Raise)
+ ModuleSpecs::Autoload.const_defined?(:Raise).should == true
+ ModuleSpecs::Autoload.autoload?(:Raise).should == path
+
+ lambda { ModuleSpecs::Autoload::Raise }.should raise_error(RuntimeError)
+ ScratchPad.recorded.should == [:raise, :raise]
+ end
+
+ it "does not remove the constant from Module#constants if the loaded file does not define it, but leaves it as 'undefined'" do
+ path = fixture(__FILE__, "autoload_o.rb")
+ ScratchPad.record []
+ ModuleSpecs::Autoload.autoload :O, path
+
+ ModuleSpecs::Autoload.const_defined?(:O).should == true
ModuleSpecs::Autoload.should have_constant(:O)
+ ModuleSpecs::Autoload.autoload?(:O).should == path
lambda { ModuleSpecs::Autoload::O }.should raise_error(NameError)
+
ModuleSpecs::Autoload.should have_constant(:O)
+ ModuleSpecs::Autoload.const_defined?(:O).should == false
+ ModuleSpecs::Autoload.autoload?(:O).should == nil
+ -> { ModuleSpecs::Autoload.const_get(:O) }.should raise_error(NameError)
+ end
+
+ it "does not try to load the file again if the loaded file did not define the constant" do
+ path = fixture(__FILE__, "autoload_o.rb")
+ ScratchPad.record []
+ ModuleSpecs::Autoload.autoload :NotDefinedByFile, path
+
+ -> { ModuleSpecs::Autoload::NotDefinedByFile }.should raise_error(NameError)
+ ScratchPad.recorded.should == [:loaded]
+ -> { ModuleSpecs::Autoload::NotDefinedByFile }.should raise_error(NameError)
+ ScratchPad.recorded.should == [:loaded]
+
+ Thread.new {
+ -> { ModuleSpecs::Autoload::NotDefinedByFile }.should raise_error(NameError)
+ }.join
+ ScratchPad.recorded.should == [:loaded]
end
it "returns 'constant' on referring the constant with defined?()" do
@@ -216,7 +365,6 @@ describe "Module#autoload" do
end
it "loads the file that defines subclass XX::YY < YY and YY is a top level constant" do
-
module ModuleSpecs::Autoload::XX
autoload :YY, fixture(__FILE__, "autoload_subclass.rb")
end
@@ -224,30 +372,130 @@ describe "Module#autoload" do
ModuleSpecs::Autoload::XX::YY.superclass.should == YY
end
+ describe "after autoloading searches for the constant like the original lookup" do
+ it "in lexical scopes if both declared and defined in parent" do
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ DeclaredAndDefinedInParent = :declared_and_defined_in_parent
+ }
+ autoload :DeclaredAndDefinedInParent, fixture(__FILE__, "autoload_callback.rb")
+ class LexicalScope
+ DeclaredAndDefinedInParent.should == :declared_and_defined_in_parent
- it "looks up the constant in the scope where it is referred" do
- module ModuleSpecs
- module Autoload
- autoload :QQ, fixture(__FILE__, "autoload_scope.rb")
- class PP
- QQ.new.should be_kind_of(ModuleSpecs::Autoload::PP::QQ)
+ # The constant is really in Autoload, not Autoload::LexicalScope
+ self.should_not have_constant(:DeclaredAndDefinedInParent)
+ -> { const_get(:DeclaredAndDefinedInParent) }.should raise_error(NameError)
end
+ DeclaredAndDefinedInParent.should == :declared_and_defined_in_parent
end
end
- end
- it "looks up the constant when in a meta class scope" do
- module ModuleSpecs
- module Autoload
- autoload :R, fixture(__FILE__, "autoload_r.rb")
+ it "in lexical scopes if declared in parent and defined in current" do
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ class LexicalScope
+ DeclaredInParentDefinedInCurrent = :declared_in_parent_defined_in_current
+ end
+ }
+ autoload :DeclaredInParentDefinedInCurrent, fixture(__FILE__, "autoload_callback.rb")
+
+ class LexicalScope
+ DeclaredInParentDefinedInCurrent.should == :declared_in_parent_defined_in_current
+ LexicalScope::DeclaredInParentDefinedInCurrent.should == :declared_in_parent_defined_in_current
+ end
+
+ # Basically, the parent autoload constant remains in a "undefined" state
+ self.autoload?(:DeclaredInParentDefinedInCurrent).should == nil
+ const_defined?(:DeclaredInParentDefinedInCurrent).should == false
+ self.should have_constant(:DeclaredInParentDefinedInCurrent)
+ -> { DeclaredInParentDefinedInCurrent }.should raise_error(NameError)
+
+ ModuleSpecs::Autoload::LexicalScope.send(:remove_const, :DeclaredInParentDefinedInCurrent)
+ end
+ end
+
+ it "and fails when finding the undefined autoload constant in the the current scope when declared in current and defined in parent" do
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ DeclaredInCurrentDefinedInParent = :declared_in_current_defined_in_parent
+ }
+
+ class LexicalScope
+ autoload :DeclaredInCurrentDefinedInParent, fixture(__FILE__, "autoload_callback.rb")
+ -> { DeclaredInCurrentDefinedInParent }.should raise_error(NameError)
+ # Basically, the autoload constant remains in a "undefined" state
+ self.autoload?(:DeclaredInCurrentDefinedInParent).should == nil
+ const_defined?(:DeclaredInCurrentDefinedInParent).should == false
+ self.should have_constant(:DeclaredInCurrentDefinedInParent)
+ -> { const_get(:DeclaredInCurrentDefinedInParent) }.should raise_error(NameError)
+ end
+
+ DeclaredInCurrentDefinedInParent.should == :declared_in_current_defined_in_parent
+ end
+ end
+
+ it "in the included modules" do
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ module DefinedInIncludedModule
+ Incl = :defined_in_included_module
+ end
+ include DefinedInIncludedModule
+ }
+ autoload :Incl, fixture(__FILE__, "autoload_callback.rb")
+ Incl.should == :defined_in_included_module
+ end
+ end
+
+ it "in the included modules of the superclass" do
+ module ModuleSpecs::Autoload
+ class LookupAfterAutoloadSuper
+ end
+ class LookupAfterAutoloadChild < LookupAfterAutoloadSuper
+ end
+
+ ScratchPad.record -> {
+ module DefinedInSuperclassIncludedModule
+ InclS = :defined_in_superclass_included_module
+ end
+ LookupAfterAutoloadSuper.include DefinedInSuperclassIncludedModule
+ }
+
+ class LookupAfterAutoloadChild
+ autoload :InclS, fixture(__FILE__, "autoload_callback.rb")
+ InclS.should == :defined_in_superclass_included_module
+ end
+ end
+ end
+
+ it "in the prepended modules" do
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ module DefinedInPrependedModule
+ Prep = :defined_in_prepended_module
+ end
+ include DefinedInPrependedModule
+ }
+ autoload :Prep, fixture(__FILE__, "autoload_callback.rb")
+ Prep.should == :defined_in_prepended_module
+ end
+ end
+
+ it "in a meta class scope" do
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ class MetaScope
+ end
+ }
+ autoload :MetaScope, fixture(__FILE__, "autoload_callback.rb")
class << self
def r
- R.new
+ MetaScope.new
end
end
end
+ ModuleSpecs::Autoload.r.should be_kind_of(ModuleSpecs::Autoload::MetaScope)
end
- ModuleSpecs::Autoload.r.should be_kind_of(ModuleSpecs::Autoload::R)
end
# [ruby-core:19127] [ruby-core:29941]
@@ -266,6 +514,21 @@ describe "Module#autoload" do
ModuleSpecs::Autoload::W.send(:remove_const, :Y)
end
+ it "does not call #require a second time and does not warn if already loading the same feature with #require" do
+ main = TOPLEVEL_BINDING.eval("self")
+ main.should_not_receive(:require)
+
+ module ModuleSpecs::Autoload
+ autoload :AutoloadDuringRequire, fixture(__FILE__, "autoload_during_require.rb")
+ end
+
+ -> {
+ $VERBOSE = true
+ Kernel.require fixture(__FILE__, "autoload_during_require.rb")
+ }.should_not complain
+ ModuleSpecs::Autoload::AutoloadDuringRequire.should be_kind_of(Class)
+ end
+
it "calls #to_path on non-string filenames" do
p = mock('path')
p.should_receive(:to_path).and_return @non_existent
diff --git a/spec/ruby/core/module/const_get_spec.rb b/spec/ruby/core/module/const_get_spec.rb
index 74fe94aaed..461b303d6d 100644
--- a/spec/ruby/core/module/const_get_spec.rb
+++ b/spec/ruby/core/module/const_get_spec.rb
@@ -1,5 +1,6 @@
require_relative '../../spec_helper'
require_relative '../../fixtures/constants'
+require_relative 'fixtures/constants_autoload'
describe "Module#const_get" do
it "accepts a String or Symbol name" do
@@ -95,6 +96,10 @@ describe "Module#const_get" do
ConstantSpecs.const_get("ClassA::CS_CONST10").should == :const10_10
end
+ it "raises a NameError if the name includes two successive scope separators" do
+ lambda { ConstantSpecs.const_get("ClassA::::CS_CONST10") }.should raise_error(NameError)
+ end
+
it "raises a NameError if only '::' is passed" do
lambda { ConstantSpecs.const_get("::") }.should raise_error(NameError)
end
@@ -111,6 +116,22 @@ describe "Module#const_get" do
ConstantSpecs.const_get(:CS_PRIVATE).should == :cs_private
end
+ it 'does autoload a constant' do
+ Object.const_get('CSAutoloadA').name.should == 'CSAutoloadA'
+ end
+
+ it 'does autoload a constant with a toplevel scope qualifier' do
+ Object.const_get('::CSAutoloadB').name.should == 'CSAutoloadB'
+ end
+
+ it 'does autoload a module and resolve a constant within' do
+ Object.const_get('CSAutoloadC::CONST').should == 7
+ end
+
+ it 'does autoload a non-toplevel module' do
+ Object.const_get('CSAutoloadD::InnerModule').name.should == 'CSAutoloadD::InnerModule'
+ end
+
describe "with statically assigned constants" do
it "searches the immediate class or module first" do
ConstantSpecs::ClassA.const_get(:CS_CONST10).should == :const10_10
diff --git a/spec/ruby/core/module/fixtures/autoload_callback.rb b/spec/ruby/core/module/fixtures/autoload_callback.rb
new file mode 100644
index 0000000000..51d53eb580
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_callback.rb
@@ -0,0 +1,2 @@
+block = ScratchPad.recorded
+block.call
diff --git a/spec/ruby/core/module/fixtures/autoload_during_autoload.rb b/spec/ruby/core/module/fixtures/autoload_during_autoload.rb
new file mode 100644
index 0000000000..5202bd8b23
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_during_autoload.rb
@@ -0,0 +1,7 @@
+block = ScratchPad.recorded
+ScratchPad.record(block.call)
+
+module ModuleSpecs::Autoload
+ class DuringAutoload
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_during_require.rb b/spec/ruby/core/module/fixtures/autoload_during_require.rb
new file mode 100644
index 0000000000..6fd81592e3
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_during_require.rb
@@ -0,0 +1,4 @@
+module ModuleSpecs::Autoload
+ class AutoloadDuringRequire
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_exception.rb b/spec/ruby/core/module/fixtures/autoload_exception.rb
new file mode 100644
index 0000000000..09acf9f537
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_exception.rb
@@ -0,0 +1,3 @@
+ScratchPad.record(:exception)
+
+raise 'intentional error to test failure conditions during autoloading'
diff --git a/spec/ruby/core/module/fixtures/autoload_nested.rb b/spec/ruby/core/module/fixtures/autoload_nested.rb
new file mode 100644
index 0000000000..073cec0dce
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_nested.rb
@@ -0,0 +1,8 @@
+module ModuleSpecs::Autoload
+ module GoodParent
+ class Nested
+ end
+ end
+end
+
+ScratchPad.record(:loaded)
diff --git a/spec/ruby/core/module/fixtures/autoload_o.rb b/spec/ruby/core/module/fixtures/autoload_o.rb
index 6d54ddaf12..7d88f969b2 100644
--- a/spec/ruby/core/module/fixtures/autoload_o.rb
+++ b/spec/ruby/core/module/fixtures/autoload_o.rb
@@ -1 +1,2 @@
# does not define ModuleSpecs::Autoload::O
+ScratchPad << :loaded
diff --git a/spec/ruby/core/module/fixtures/autoload_raise.rb b/spec/ruby/core/module/fixtures/autoload_raise.rb
new file mode 100644
index 0000000000..f6051e3ba2
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_raise.rb
@@ -0,0 +1,2 @@
+ScratchPad << :raise
+raise "exception during autoload"
diff --git a/spec/ruby/core/module/fixtures/autoload_scope.rb b/spec/ruby/core/module/fixtures/autoload_scope.rb
deleted file mode 100644
index 04193687b5..0000000000
--- a/spec/ruby/core/module/fixtures/autoload_scope.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-module ModuleSpecs
- module Autoload
- class PP
- class QQ
- end
- end
- end
-end
diff --git a/spec/ruby/core/module/fixtures/constants_autoload.rb b/spec/ruby/core/module/fixtures/constants_autoload.rb
new file mode 100644
index 0000000000..8e9aa8de0c
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/constants_autoload.rb
@@ -0,0 +1,6 @@
+autoload :CSAutoloadA, fixture(__FILE__, 'constants_autoload_a.rb')
+autoload :CSAutoloadB, fixture(__FILE__, 'constants_autoload_b.rb')
+autoload :CSAutoloadC, fixture(__FILE__, 'constants_autoload_c.rb')
+module CSAutoloadD
+ autoload :InnerModule, fixture(__FILE__, 'constants_autoload_d.rb')
+end
diff --git a/spec/ruby/core/module/fixtures/constants_autoload_a.rb b/spec/ruby/core/module/fixtures/constants_autoload_a.rb
new file mode 100644
index 0000000000..48d3b63681
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/constants_autoload_a.rb
@@ -0,0 +1,2 @@
+module CSAutoloadA
+end
diff --git a/spec/ruby/core/module/fixtures/constants_autoload_b.rb b/spec/ruby/core/module/fixtures/constants_autoload_b.rb
new file mode 100644
index 0000000000..29cd742d03
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/constants_autoload_b.rb
@@ -0,0 +1,2 @@
+module CSAutoloadB
+end
diff --git a/spec/ruby/core/module/fixtures/constants_autoload_c.rb b/spec/ruby/core/module/fixtures/constants_autoload_c.rb
new file mode 100644
index 0000000000..9d6a6bf4d7
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/constants_autoload_c.rb
@@ -0,0 +1,3 @@
+module CSAutoloadC
+ CONST = 7
+end
diff --git a/spec/ruby/core/module/fixtures/constants_autoload_d.rb b/spec/ruby/core/module/fixtures/constants_autoload_d.rb
new file mode 100644
index 0000000000..52d550bab0
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/constants_autoload_d.rb
@@ -0,0 +1,4 @@
+module CSAutoloadD
+ module InnerModule
+ end
+end
diff --git a/spec/ruby/core/module/initialize_copy_spec.rb b/spec/ruby/core/module/initialize_copy_spec.rb
index 412f1c511b..7ae48f85a9 100644
--- a/spec/ruby/core/module/initialize_copy_spec.rb
+++ b/spec/ruby/core/module/initialize_copy_spec.rb
@@ -7,4 +7,12 @@ describe "Module#initialize_copy" do
end
mod.dup.methods(false).should == [:hello]
end
+
+ # jruby/jruby#5245, https://bugs.ruby-lang.org/issues/3461
+ it "should produce a duped module with inspectable class methods" do
+ mod = Module.new
+ def mod.hello
+ end
+ mod.dup.method(:hello).inspect.should =~ /Module.*hello/
+ end
end
diff --git a/spec/ruby/core/signal/fixtures/trap_all.rb b/spec/ruby/core/signal/fixtures/trap_all.rb
new file mode 100644
index 0000000000..b2e85df247
--- /dev/null
+++ b/spec/ruby/core/signal/fixtures/trap_all.rb
@@ -0,0 +1,8 @@
+reserved_signals = ARGV
+
+(Signal.list.keys - reserved_signals).each do |signal|
+ Signal.trap(signal, -> {})
+ Signal.trap(signal, "DEFAULT")
+end
+
+puts "OK"
diff --git a/spec/ruby/core/signal/trap_spec.rb b/spec/ruby/core/signal/trap_spec.rb
index d621b6ae6d..66101ee5a0 100644
--- a/spec/ruby/core/signal/trap_spec.rb
+++ b/spec/ruby/core/signal/trap_spec.rb
@@ -115,6 +115,49 @@ platform_is_not :windows do
end
describe "Signal.trap" do
+ cannot_be_trapped = %w[KILL STOP] # See man 2 signal
+ reserved_signals = %w[VTALRM]
+
+ if PlatformGuard.implementation?(:ruby)
+ reserved_signals += %w[SEGV ILL FPE BUS]
+ end
+
+ if PlatformGuard.implementation?(:truffleruby)
+ if !TruffleRuby.native?
+ reserved_signals += %w[SEGV ILL FPE USR1 QUIT]
+ end
+ end
+
+ if PlatformGuard.implementation?(:jruby)
+ reserved_signals += %w[SEGV ILL FPE BUS USR1 QUIT]
+ end
+
+ cannot_be_trapped.each do |signal|
+ it "raises ArgumentError or Errno::EINVAL for SIG#{signal}" do
+ -> {
+ trap(signal, -> {})
+ }.should raise_error(StandardError) { |e|
+ [ArgumentError, Errno::EINVAL].should include(e.class)
+ e.message.should =~ /Invalid argument|Signal already used by VM or OS/
+ }
+ end
+ end
+
+ reserved_signals.each do |signal|
+ it "raises ArgumentError for reserved signal SIG#{signal}" do
+ -> {
+ trap(signal, -> {})
+ }.should raise_error(ArgumentError, /can't trap reserved signal|Signal already used by VM or OS/)
+ end
+ end
+
+ it "allows to register a handler for all known signals, except reserved signals" do
+ excluded = cannot_be_trapped + reserved_signals
+ out = ruby_exe(fixture(__FILE__, "trap_all.rb"), args: [*excluded, "2>&1"])
+ out.should == "OK\n"
+ $?.exitstatus.should == 0
+ end
+
it "returns SYSTEM_DEFAULT if passed DEFAULT and no handler was ever set" do
Signal.trap("PROF", "DEFAULT").should == "SYSTEM_DEFAULT"
end
diff --git a/spec/ruby/core/string/force_encoding_spec.rb b/spec/ruby/core/string/force_encoding_spec.rb
index c317c84c1f..06e04b8d95 100644
--- a/spec/ruby/core/string/force_encoding_spec.rb
+++ b/spec/ruby/core/string/force_encoding_spec.rb
@@ -6,6 +6,26 @@ with_feature :encoding do
"abc".force_encoding('shift_jis').encoding.should == Encoding::Shift_JIS
end
+ describe "with a special encoding name" do
+ before :each do
+ @original_encoding = Encoding.default_internal
+ end
+
+ after :each do
+ Encoding.default_internal = @original_encoding
+ end
+
+ it "accepts valid special encoding names" do
+ Encoding.default_internal = "US-ASCII"
+ "abc".force_encoding("internal").encoding.should == Encoding::US_ASCII
+ end
+
+ it "defaults to ASCII-8BIT if special encoding name is not set" do
+ Encoding.default_internal = nil
+ "abc".force_encoding("internal").encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+
it "accepts an Encoding instance" do
"abc".force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::Shift_JIS
end
diff --git a/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb b/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb
index aeae6b77c6..5bd97442d9 100644
--- a/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb
+++ b/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb
@@ -9,4 +9,42 @@ describe 'Thread::Backtrace::Location#absolute_path' do
it 'returns the absolute path of the call frame' do
@frame.absolute_path.should == File.realpath(__FILE__)
end
+
+ context "when used in eval with a given filename" do
+ it "returns filename" do
+ code = "caller_locations(0)[0].absolute_path"
+ eval(code, nil, "foo.rb").should == "foo.rb"
+ eval(code, nil, "foo/bar.rb").should == "foo/bar.rb"
+ end
+ end
+
+ platform_is_not :windows do
+ before :each do
+ @file = fixture(__FILE__, "absolute_path.rb")
+ @symlink = tmp("symlink.rb")
+ File.symlink(@file, @symlink)
+ ScratchPad.record []
+ end
+
+ after :each do
+ rm_r @symlink
+ end
+
+ it "returns a canonical path without symlinks, even when __FILE__ does not" do
+ realpath = File.realpath(@symlink)
+ realpath.should_not == @symlink
+
+ load @symlink
+ ScratchPad.recorded.should == [@symlink, realpath]
+ end
+
+ it "returns a canonical path without symlinks, even when __FILE__ is removed" do
+ realpath = File.realpath(@symlink)
+ realpath.should_not == @symlink
+
+ ScratchPad << -> { rm_r(@symlink) }
+ load @symlink
+ ScratchPad.recorded.should == [@symlink, realpath]
+ end
+ end
end
diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path.rb b/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path.rb
new file mode 100644
index 0000000000..875e97ffac
--- /dev/null
+++ b/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path.rb
@@ -0,0 +1,4 @@
+action = ScratchPad.recorded.pop
+ScratchPad << __FILE__
+action.call if action
+ScratchPad << caller_locations(0)[0].absolute_path
diff --git a/spec/ruby/core/time/localtime_spec.rb b/spec/ruby/core/time/localtime_spec.rb
index 5592150ca2..7942653c78 100644
--- a/spec/ruby/core/time/localtime_spec.rb
+++ b/spec/ruby/core/time/localtime_spec.rb
@@ -93,7 +93,7 @@ describe "Time#localtime" do
it "does nothing if already in a local time zone" do
time = with_timezone("America/New_York") do
- break Time.new(2005, 2, 27, 22, 50, 0)
+ Time.new(2005, 2, 27, 22, 50, 0)
end
zone = time.zone
diff --git a/spec/ruby/fixtures/code/load_fixture_and__FILE__.rb b/spec/ruby/fixtures/code/load_fixture_and__FILE__.rb
new file mode 100644
index 0000000000..27388c7d8d
--- /dev/null
+++ b/spec/ruby/fixtures/code/load_fixture_and__FILE__.rb
@@ -0,0 +1 @@
+ScratchPad << __FILE__
diff --git a/spec/ruby/language/array_spec.rb b/spec/ruby/language/array_spec.rb
index 2198d7a28b..2583cffbf7 100644
--- a/spec/ruby/language/array_spec.rb
+++ b/spec/ruby/language/array_spec.rb
@@ -36,6 +36,13 @@ describe "Array literals" do
[1, *nil, 3].should == [1, 3]
[*nil, *nil, *nil].should == []
end
+
+ it "evaluates each argument exactly once" do
+ se = ArraySpec::SideEffect.new
+ se.array_result(true)
+ se.array_result(false)
+ se.call_count.should == 4
+ end
end
describe "Bareword array literal" do
diff --git a/spec/ruby/language/constants_spec.rb b/spec/ruby/language/constants_spec.rb
index 49ff8844af..5b111f4e81 100644
--- a/spec/ruby/language/constants_spec.rb
+++ b/spec/ruby/language/constants_spec.rb
@@ -458,16 +458,17 @@ describe "Module#private_constant marked constants" do
lambda {mod::Foo}.should raise_error(NameError)
end
- it "sends #const_missing to the original class or module" do
- mod = Module.new
- mod.const_set :Foo, true
- mod.send :private_constant, :Foo
- def mod.const_missing(name)
- @const_missing_arg = name
- name == :Foo ? name : super
- end
+ ruby_version_is "2.6" do
+ it "sends #const_missing to the original class or module" do
+ mod = Module.new
+ mod.const_set :Foo, true
+ mod.send :private_constant, :Foo
+ def mod.const_missing(name)
+ name == :Foo ? name : super
+ end
- mod::Foo.should == :Foo
+ mod::Foo.should == :Foo
+ end
end
describe "in a module" do
diff --git a/spec/ruby/language/fixtures/array.rb b/spec/ruby/language/fixtures/array.rb
index 4d8ce74ed6..c1036575ff 100644
--- a/spec/ruby/language/fixtures/array.rb
+++ b/spec/ruby/language/fixtures/array.rb
@@ -8,4 +8,25 @@ module ArraySpec
[a, b, c, d]
end
end
+
+ class SideEffect
+ def initialize()
+ @call_count = 0
+ end
+
+ attr_reader :call_count
+
+ def array_result(a_number)
+ [result(a_number), result(a_number)]
+ end
+
+ def result(a_number)
+ @call_count += 1
+ if a_number
+ 1
+ else
+ :thing
+ end
+ end
+ end
end
diff --git a/spec/ruby/language/optional_assignments_spec.rb b/spec/ruby/language/optional_assignments_spec.rb
index a83c0da272..91b580e084 100644
--- a/spec/ruby/language/optional_assignments_spec.rb
+++ b/spec/ruby/language/optional_assignments_spec.rb
@@ -185,6 +185,19 @@ describe 'Optional variable assignments' do
describe 'using a #[]' do
before do
@a = {}
+ klass = Class.new do
+ def [](k)
+ @hash ||= {}
+ @hash[k]
+ end
+
+ def []=(k, v)
+ @hash ||= {}
+ @hash[k] = v
+ 7
+ end
+ end
+ @b = klass.new
end
it 'leaves new variable unassigned' do
@@ -226,6 +239,15 @@ describe 'Optional variable assignments' do
@a[:k].should == 20
end
+
+ it 'returns the assigned value, not the result of the []= method with ||=' do
+ (@b[:k] ||= 12).should == 12
+ end
+
+ it 'returns the assigned value, not the result of the []= method with +=' do
+ @b[:k] = 17
+ (@b[:k] += 12).should == 29
+ end
end
end
diff --git a/spec/ruby/library/date/iso8601_spec.rb b/spec/ruby/library/date/iso8601_spec.rb
new file mode 100644
index 0000000000..41f055e648
--- /dev/null
+++ b/spec/ruby/library/date/iso8601_spec.rb
@@ -0,0 +1,37 @@
+require_relative '../../spec_helper'
+require 'date'
+
+describe "Date.iso8601" do
+ it "parses YYYY-MM-DD into a Date object" do
+ d = Date.iso8601("2018-01-01")
+ d.should == Date.civil(2018, 1, 1)
+ end
+
+ it "parses YYYYMMDD into a Date object" do
+ d = Date.iso8601("20180715")
+ d.should == Date.civil(2018, 7, 15)
+ end
+
+ it "parses a negative Date" do
+ d = Date.iso8601("-4712-01-01")
+ d.should == Date.civil(-4712, 1, 1)
+ end
+
+ it "parses a Symbol into a Date object" do
+ d = Date.iso8601(:'2015-10-15')
+ d.should == Date.civil(2015, 10, 15)
+ end
+
+ it "parses a StringSubclass into a Date object" do
+ d = Date.iso8601(Class.new(String).new("-4712-01-01"))
+ d.should == Date.civil(-4712, 1, 1)
+ end
+
+ it "raises an ArgumentError when passed a Symbol without a valid Date" do
+ lambda { Date.iso8601(:test) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError when passed an Object" do
+ lambda { Date.iso8601(Object.new) }.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/library/date/parse_spec.rb b/spec/ruby/library/date/parse_spec.rb
index 0c21e9aeba..09e072ba3e 100644
--- a/spec/ruby/library/date/parse_spec.rb
+++ b/spec/ruby/library/date/parse_spec.rb
@@ -64,6 +64,11 @@ describe "Date#parse" do
d = Date.parse("19101101")
d.should == Date.civil(1910, 11, 1)
end
+
+ it "raises a TypeError trying to parse non-String-like object" do
+ lambda { Date.parse(1) }.should raise_error(TypeError)
+ lambda { Date.parse(:invalid) }.should raise_error(TypeError)
+ end
end
describe "Date#parse with '.' separator" do
diff --git a/spec/ruby/library/socket/addrinfo/afamily_spec.rb b/spec/ruby/library/socket/addrinfo/afamily_spec.rb
index 5e61c8f99c..7229dab9de 100644
--- a/spec/ruby/library/socket/addrinfo/afamily_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/afamily_spec.rb
@@ -1,5 +1,4 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
describe "Addrinfo#afamily" do
describe "for an ipv4 socket" do
@@ -24,7 +23,7 @@ describe "Addrinfo#afamily" do
end
end
- platform_is_not :windows do
+ with_feature :unix_socket do
describe "for a unix socket" do
before :each do
@addrinfo = Addrinfo.unix("/tmp/sock")
diff --git a/spec/ruby/library/socket/addrinfo/bind_spec.rb b/spec/ruby/library/socket/addrinfo/bind_spec.rb
index 8a20ab54d4..6f78890a4d 100644
--- a/spec/ruby/library/socket/addrinfo/bind_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/bind_spec.rb
@@ -1,6 +1,5 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-require 'socket'
describe "Addrinfo#bind" do
diff --git a/spec/ruby/library/socket/addrinfo/canonname_spec.rb b/spec/ruby/library/socket/addrinfo/canonname_spec.rb
index 6105d905d6..a1cc8b3980 100644
--- a/spec/ruby/library/socket/addrinfo/canonname_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/canonname_spec.rb
@@ -1,6 +1,5 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-require 'socket'
describe "Addrinfo#canonname" do
@@ -16,4 +15,13 @@ describe "Addrinfo#canonname" do
canonname.should == nil
end
end
+
+ describe 'when the canonical name is not available' do
+ it 'returns nil' do
+ addr = Addrinfo.new(Socket.sockaddr_in(0, '127.0.0.1'))
+
+ addr.canonname.should be_nil
+ end
+ end
+
end
diff --git a/spec/ruby/library/socket/addrinfo/connect_from_spec.rb b/spec/ruby/library/socket/addrinfo/connect_from_spec.rb
new file mode 100644
index 0000000000..55fce2e159
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/connect_from_spec.rb
@@ -0,0 +1,75 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo#connect_from' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ @addr = Addrinfo.tcp(ip_address, @port)
+ end
+
+ after do
+ @socket.close if @socket
+ @server.close
+ end
+
+ describe 'using separate arguments' do
+ it 'returns a Socket when no block is given' do
+ @socket = @addr.connect_from(ip_address, 0)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket when a block is given' do
+ @addr.connect_from(ip_address, 0) do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'treats the last argument as a set of options if it is a Hash' do
+ @socket = @addr.connect_from(ip_address, 0, timeout: 2)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'binds the socket to the local address' do
+ @socket = @addr.connect_from(ip_address, 0)
+
+ @socket.local_address.ip_address.should == ip_address
+
+ @socket.local_address.ip_port.should > 0
+ @socket.local_address.ip_port.should_not == @port
+ end
+ end
+
+ describe 'using an Addrinfo as the 1st argument' do
+ before do
+ @from_addr = Addrinfo.tcp(ip_address, 0)
+ end
+
+ it 'returns a Socket when no block is given' do
+ @socket = @addr.connect_from(@from_addr)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket when a block is given' do
+ @addr.connect_from(@from_addr) do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'treats the last argument as a set of options if it is a Hash' do
+ @socket = @addr.connect_from(@from_addr, timeout: 2)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'binds the socket to the local address' do
+ @socket = @addr.connect_from(@from_addr)
+
+ @socket.local_address.ip_address.should == ip_address
+
+ @socket.local_address.ip_port.should > 0
+ @socket.local_address.ip_port.should_not == @port
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/connect_spec.rb b/spec/ruby/library/socket/addrinfo/connect_spec.rb
new file mode 100644
index 0000000000..1c2dc609ca
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/connect_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo#connect' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @socket.close if @socket
+ @server.close
+ end
+
+ it 'returns a Socket when no block is given' do
+ addr = Addrinfo.tcp(ip_address, @port)
+ @socket = addr.connect
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields a Socket when a block is given' do
+ addr = Addrinfo.tcp(ip_address, @port)
+ addr.connect do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'accepts a Hash of options' do
+ addr = Addrinfo.tcp(ip_address, @port)
+ @socket = addr.connect(timeout: 2)
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/connect_to_spec.rb b/spec/ruby/library/socket/addrinfo/connect_to_spec.rb
new file mode 100644
index 0000000000..69666da19b
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/connect_to_spec.rb
@@ -0,0 +1,75 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo#connect_to' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ @addr = Addrinfo.tcp(ip_address, 0)
+ end
+
+ after do
+ @socket.close if @socket
+ @server.close
+ end
+
+ describe 'using separate arguments' do
+ it 'returns a Socket when no block is given' do
+ @socket = @addr.connect_to(ip_address, @port)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket when a block is given' do
+ @addr.connect_to(ip_address, @port) do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'treats the last argument as a set of options if it is a Hash' do
+ @socket = @addr.connect_to(ip_address, @port, timeout: 2)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'binds the Addrinfo to the local address' do
+ @socket = @addr.connect_to(ip_address, @port)
+
+ @socket.local_address.ip_address.should == ip_address
+
+ @socket.local_address.ip_port.should > 0
+ @socket.local_address.ip_port.should_not == @port
+ end
+ end
+
+ describe 'using an Addrinfo as the 1st argument' do
+ before do
+ @to_addr = Addrinfo.tcp(ip_address, @port)
+ end
+
+ it 'returns a Socket when no block is given' do
+ @socket = @addr.connect_to(@to_addr)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket when a block is given' do
+ @addr.connect_to(@to_addr) do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'treats the last argument as a set of options if it is a Hash' do
+ @socket = @addr.connect_to(@to_addr, timeout: 2)
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'binds the socket to the local address' do
+ @socket = @addr.connect_to(@to_addr)
+
+ @socket.local_address.ip_address.should == ip_address
+
+ @socket.local_address.ip_port.should > 0
+ @socket.local_address.ip_port.should_not == @port
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/family_addrinfo_spec.rb b/spec/ruby/library/socket/addrinfo/family_addrinfo_spec.rb
new file mode 100644
index 0000000000..d3419daaaf
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/family_addrinfo_spec.rb
@@ -0,0 +1,115 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#family_addrinfo' do
+ it 'raises ArgumentError if no arguments are given' do
+ addr = Addrinfo.tcp('127.0.0.1', 0)
+
+ lambda { addr.family_addrinfo }.should raise_error(ArgumentError)
+ end
+
+ describe 'using multiple arguments' do
+ describe 'with an IP Addrinfo' do
+ before do
+ @source = Addrinfo.tcp('127.0.0.1', 0)
+ end
+
+ it 'raises ArgumentError if only 1 argument is given' do
+ lambda { @source.family_addrinfo('127.0.0.1') }.should raise_error(ArgumentError)
+ end
+
+ it 'raises ArgumentError if more than 2 arguments are given' do
+ lambda { @source.family_addrinfo('127.0.0.1', 0, 666) }.should raise_error(ArgumentError)
+ end
+
+ it 'returns an Addrinfo when a host and port are given' do
+ addr = @source.family_addrinfo('127.0.0.1', 0)
+
+ addr.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @source.family_addrinfo('127.0.0.1', 0)
+ end
+
+ it 'uses the same address family as the source Addrinfo' do
+ @addr.afamily.should == @source.afamily
+ end
+
+ it 'uses the same protocol family as the source Addrinfo' do
+ @addr.pfamily.should == @source.pfamily
+ end
+
+ it 'uses the same socket type as the source Addrinfo' do
+ @addr.socktype.should == @source.socktype
+ end
+
+ it 'uses the same protocol as the source Addrinfo' do
+ @addr.protocol.should == @source.protocol
+ end
+ end
+ end
+
+ with_feature :unix_socket do
+ describe 'with a UNIX Addrinfo' do
+ before do
+ @source = Addrinfo.unix('cats')
+ end
+
+ it 'raises ArgumentError if more than 1 argument is given' do
+ lambda { @source.family_addrinfo('foo', 'bar') }.should raise_error(ArgumentError)
+ end
+
+ it 'returns an Addrinfo when a UNIX socket path is given' do
+ addr = @source.family_addrinfo('dogs')
+
+ addr.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @source.family_addrinfo('dogs')
+ end
+
+ it 'uses AF_UNIX as the address family' do
+ @addr.afamily.should == Socket::AF_UNIX
+ end
+
+ it 'uses PF_UNIX as the protocol family' do
+ @addr.pfamily.should == Socket::PF_UNIX
+ end
+
+ it 'uses the given socket path' do
+ @addr.unix_path.should == 'dogs'
+ end
+ end
+ end
+ end
+ end
+
+ describe 'using an Addrinfo as the 1st argument' do
+ before do
+ @source = Addrinfo.tcp('127.0.0.1', 0)
+ end
+
+ it 'returns the input Addrinfo' do
+ input = Addrinfo.tcp('127.0.0.2', 0)
+ @source.family_addrinfo(input).should == input
+ end
+
+ it 'raises ArgumentError if more than 1 argument is given' do
+ input = Addrinfo.tcp('127.0.0.2', 0)
+ lambda { @source.family_addrinfo(input, 666) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the protocol families don't match" do
+ input = Addrinfo.tcp('::1', 0)
+ lambda { @source.family_addrinfo(input) }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if the socket types don't match" do
+ input = Addrinfo.udp('127.0.0.1', 0)
+ lambda { @source.family_addrinfo(input) }.should raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/foreach_spec.rb b/spec/ruby/library/socket/addrinfo/foreach_spec.rb
new file mode 100644
index 0000000000..75c1d1dea2
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/foreach_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo.foreach' do
+ it 'yields Addrinfo instances to the supplied block' do
+ Addrinfo.foreach('localhost', 80) do |addr|
+ addr.should be_an_instance_of(Addrinfo)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/getaddrinfo_spec.rb b/spec/ruby/library/socket/addrinfo/getaddrinfo_spec.rb
new file mode 100644
index 0000000000..f0e4b3c986
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/getaddrinfo_spec.rb
@@ -0,0 +1,88 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo.getaddrinfo' do
+ it 'returns an Array of Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('localhost', 80)
+
+ array.should be_an_instance_of(Array)
+ array[0].should be_an_instance_of(Addrinfo)
+ end
+
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ it 'sets the IP address of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo(ip_address, 80)
+
+ array[0].ip_address.should == ip_address
+ end
+
+ it 'sets the port of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo(ip_address, 80)
+
+ array[0].ip_port.should == 80
+ end
+
+ it 'sets the address family of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo(ip_address, 80)
+
+ array[0].afamily.should == family
+ end
+
+ it 'sets the protocol family of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo(ip_address, 80)
+
+ array[0].pfamily.should == family
+ end
+ end
+
+ guard -> { SocketSpecs.ipv6_available? } do
+ it 'sets a custom protocol family of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('localhost', 80, Socket::PF_INET6)
+
+ array[0].pfamily.should == Socket::PF_INET6
+ end
+
+ it 'sets a corresponding address family based on a custom protocol family' do
+ array = Addrinfo.getaddrinfo('localhost', 80, Socket::PF_INET6)
+
+ array[0].afamily.should == Socket::AF_INET6
+ end
+ end
+
+ platform_is_not :windows do
+ it 'sets the default socket type of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('localhost', 80)
+ possible = [Socket::SOCK_STREAM, Socket::SOCK_DGRAM]
+
+ possible.should include(array[0].socktype)
+ end
+ end
+
+ it 'sets a custom socket type of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('localhost', 80, nil, Socket::SOCK_DGRAM)
+
+ array[0].socktype.should == Socket::SOCK_DGRAM
+ end
+
+ platform_is_not :windows do
+ it 'sets the default socket protocol of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('localhost', 80)
+ possible = [Socket::IPPROTO_TCP, Socket::IPPROTO_UDP]
+
+ possible.should include(array[0].protocol)
+ end
+ end
+
+ it 'sets a custom socket protocol of the Addrinfo instances' do
+ array = Addrinfo.getaddrinfo('localhost', 80, nil, nil, Socket::IPPROTO_UDP)
+
+ array[0].protocol.should == Socket::IPPROTO_UDP
+ end
+
+ it 'sets the canonical name when AI_CANONNAME is given as a flag' do
+ array = Addrinfo
+ .getaddrinfo('localhost', 80, nil, nil, nil, Socket::AI_CANONNAME)
+
+ array[0].canonname.should be_an_instance_of(String)
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/getnameinfo_spec.rb b/spec/ruby/library/socket/addrinfo/getnameinfo_spec.rb
new file mode 100644
index 0000000000..eb600f3e66
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/getnameinfo_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Addrinfo#getnameinfo' do
+ describe 'using an IP Addrinfo' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @addr = Addrinfo.tcp(ip_address, 80)
+ end
+
+ it 'returns the node and service names' do
+ host, service = @addr.getnameinfo
+
+ host.should be_an_instance_of(String)
+ service.should == 'http'
+ end
+
+ it 'accepts flags as a Fixnum as the first argument' do
+ host, service = @addr.getnameinfo(Socket::NI_NUMERICSERV)
+
+ host.should be_an_instance_of(String)
+ service.should == '80'
+ end
+ end
+ end
+
+ platform_is :linux do
+ with_feature :unix_socket do
+ describe 'using a UNIX Addrinfo' do
+ before do
+ @addr = Addrinfo.unix('cats')
+ @host = Socket.gethostname
+ end
+
+ it 'returns the hostname and UNIX socket path' do
+ host, path = @addr.getnameinfo
+
+ host.should == @host
+ path.should == 'cats'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/initialize_spec.rb b/spec/ruby/library/socket/addrinfo/initialize_spec.rb
index 7a6475018a..42a09ebd68 100644
--- a/spec/ruby/library/socket/addrinfo/initialize_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/initialize_spec.rb
@@ -1,5 +1,4 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
describe "Addrinfo#initialize" do
@@ -22,6 +21,12 @@ describe "Addrinfo#initialize" do
@addrinfo.pfamily.should == Socket::PF_UNSPEC
end
+ it 'returns AF_INET as the default address family' do
+ addr = Addrinfo.new(Socket.sockaddr_in(80, '127.0.0.1'))
+
+ addr.afamily.should == Socket::AF_INET
+ end
+
it "returns the INET6 afamily" do
@addrinfo.afamily.should == Socket::AF_INET6
end
@@ -142,7 +147,7 @@ describe "Addrinfo#initialize" do
@addrinfo.ip_port.should == 46102
end
- it "returns the Socket::UNSPEC pfamily" do
+ it "returns the Socket::PF_INET pfamily" do
@addrinfo.pfamily.should == Socket::PF_INET
end
@@ -159,6 +164,46 @@ describe "Addrinfo#initialize" do
end
end
+ describe 'with a valid IP address' do
+ # Uses AF_INET6 since AF_INET is the default, making it a better test
+ # that Addrinfo actually sets the family correctly.
+ before do
+ @sockaddr = ['AF_INET6', 80, 'hostname', '::1']
+ end
+
+ it 'returns an Addrinfo with the correct IP' do
+ addr = Addrinfo.new(@sockaddr)
+
+ addr.ip_address.should == '::1'
+ end
+
+ it 'returns an Addrinfo with the correct address family' do
+ addr = Addrinfo.new(@sockaddr)
+
+ addr.afamily.should == Socket::AF_INET6
+ end
+
+ it 'returns an Addrinfo with the correct protocol family' do
+ addr = Addrinfo.new(@sockaddr)
+
+ addr.pfamily.should == Socket::PF_INET6
+ end
+
+ it 'returns an Addrinfo with the correct port' do
+ addr = Addrinfo.new(@sockaddr)
+
+ addr.ip_port.should == 80
+ end
+ end
+
+ describe 'with an invalid IP address' do
+ it 'raises SocketError' do
+ block = lambda { Addrinfo.new(['AF_INET6', 80, 'hostname', '127.0.0.1']) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+
describe "with a family given" do
before :each do
@addrinfo = Addrinfo.new(["AF_INET", 46102, "localhost", "127.0.0.1"], Socket::PF_INET)
@@ -217,6 +262,38 @@ describe "Addrinfo#initialize" do
it "returns the 0 protocol" do
@addrinfo.protocol.should == 0
end
+
+ [:SOCK_STREAM, :SOCK_DGRAM, :SOCK_RAW].each do |type|
+ it "overwrites the socket type #{type}" do
+ sockaddr = ['AF_INET', 80, 'hostname', '127.0.0.1']
+
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(sockaddr, nil, value)
+
+ addr.socktype.should == value
+ end
+ end
+
+ with_feature :sock_packet do
+ [:SOCK_SEQPACKET].each do |type|
+ it "overwrites the socket type #{type}" do
+ sockaddr = ['AF_INET', 80, 'hostname', '127.0.0.1']
+
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(sockaddr, nil, value)
+
+ addr.socktype.should == value
+ end
+ end
+ end
+
+ it "raises SocketError when using SOCK_RDM" do
+ sockaddr = ['AF_INET', 80, 'hostname', '127.0.0.1']
+ value = Socket::SOCK_RDM
+ block = lambda { Addrinfo.new(sockaddr, nil, value) }
+
+ block.should raise_error(SocketError)
+ end
end
describe "with a family, socket type and protocol" do
@@ -250,4 +327,261 @@ describe "Addrinfo#initialize" do
end
end
+ describe 'using an Array with extra arguments' do
+ describe 'with the AF_INET6 address family and an explicit protocol family' do
+ before do
+ @sockaddr = ['AF_INET6', 80, 'hostname', '127.0.0.1']
+ end
+
+ it "raises SocketError when using any Socket constant except except AF_INET(6)/PF_INET(6)" do
+ Socket.constants.grep(/(^AF_|^PF_)(?!INET)/).each do |constant|
+ value = Socket.const_get(constant)
+ -> {
+ Addrinfo.new(@sockaddr, value)
+ }.should raise_error(SocketError)
+ end
+ end
+ end
+
+ describe 'with the AF_INET address family and an explicit socket protocol' do
+ before do
+ @sockaddr = ['AF_INET', 80, 'hostname', '127.0.0.1']
+ end
+
+ describe 'and no socket type is given' do
+ valid = [:IPPROTO_IP, :IPPROTO_UDP, :IPPROTO_HOPOPTS]
+
+ valid.each do |type|
+ it "overwrites the protocol when using #{type}" do
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(@sockaddr, nil, nil, value)
+
+ addr.protocol.should == value
+ end
+ end
+
+ platform_is_not :windows do
+ (Socket.constants.grep(/^IPPROTO/) - valid).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = lambda { Addrinfo.new(@sockaddr, nil, nil, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+
+ describe 'and the socket type is set to SOCK_DGRAM' do
+ before do
+ @socktype = Socket::SOCK_DGRAM
+ end
+
+ valid = [:IPPROTO_IP, :IPPROTO_UDP, :IPPROTO_HOPOPTS]
+
+ valid.each do |type|
+ it "overwrites the protocol when using #{type}" do
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(@sockaddr, nil, @socktype, value)
+
+ addr.protocol.should == value
+ end
+ end
+
+ platform_is_not :windows do
+ (Socket.constants.grep(/^IPPROTO/) - valid).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = lambda { Addrinfo.new(@sockaddr, nil, @socktype, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+
+ with_feature :sock_packet do
+ describe 'and the socket type is set to SOCK_PACKET' do
+ before do
+ @socktype = Socket::SOCK_PACKET
+ end
+
+ Socket.constants.grep(/^IPPROTO/).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = lambda { Addrinfo.new(@sockaddr, nil, @socktype, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+
+ describe 'and the socket type is set to SOCK_RAW' do
+ before do
+ @socktype = Socket::SOCK_RAW
+ end
+
+ Socket.constants.grep(/^IPPROTO/).each do |type|
+ it "overwrites the protocol when using #{type}" do
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(@sockaddr, nil, @socktype, value)
+
+ addr.protocol.should == value
+ end
+ end
+ end
+
+ describe 'and the socket type is set to SOCK_RDM' do
+ before do
+ @socktype = Socket::SOCK_RDM
+ end
+
+ Socket.constants.grep(/^IPPROTO/).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = lambda { Addrinfo.new(@sockaddr, nil, @socktype, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'and the socket type is set to SOCK_SEQPACKET' do
+ before do
+ @socktype = Socket::SOCK_SEQPACKET
+ end
+
+ valid = [:IPPROTO_IP, :IPPROTO_HOPOPTS]
+
+ valid.each do |type|
+ it "overwrites the protocol when using #{type}" do
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(@sockaddr, nil, @socktype, value)
+
+ addr.protocol.should == value
+ end
+ end
+
+ (Socket.constants.grep(/^IPPROTO/) - valid).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = lambda { Addrinfo.new(@sockaddr, nil, @socktype, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+
+ describe 'and the socket type is set to SOCK_STREAM' do
+ before do
+ @socktype = Socket::SOCK_STREAM
+ end
+
+ valid = [:IPPROTO_IP, :IPPROTO_TCP, :IPPROTO_HOPOPTS]
+
+ valid.each do |type|
+ it "overwrites the protocol when using #{type}" do
+ value = Socket.const_get(type)
+ addr = Addrinfo.new(@sockaddr, nil, @socktype, value)
+
+ addr.protocol.should == value
+ end
+ end
+
+ platform_is_not :windows do
+ (Socket.constants.grep(/^IPPROTO/) - valid).each do |type|
+ it "raises SocketError when using #{type}" do
+ value = Socket.const_get(type)
+ block = lambda { Addrinfo.new(@sockaddr, nil, @socktype, value) }
+
+ block.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ describe 'with Symbols' do
+ before do
+ @sockaddr = Socket.sockaddr_in(80, '127.0.0.1')
+ end
+
+ it 'returns an Addrinfo with :PF_INET family' do
+ addr = Addrinfo.new(@sockaddr, :PF_INET)
+
+ addr.pfamily.should == Socket::PF_INET
+ end
+
+ it 'returns an Addrinfo with :INET family' do
+ addr = Addrinfo.new(@sockaddr, :INET)
+
+ addr.pfamily.should == Socket::PF_INET
+ end
+
+ it 'returns an Addrinfo with :SOCK_STREAM as the socket type' do
+ addr = Addrinfo.new(@sockaddr, nil, :SOCK_STREAM)
+
+ addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'returns an Addrinfo with :STREAM as the socket type' do
+ addr = Addrinfo.new(@sockaddr, nil, :STREAM)
+
+ addr.socktype.should == Socket::SOCK_STREAM
+ end
+ end
+
+ describe 'with Strings' do
+ before do
+ @sockaddr = Socket.sockaddr_in(80, '127.0.0.1')
+ end
+
+ it 'returns an Addrinfo with "PF_INET" family' do
+ addr = Addrinfo.new(@sockaddr, 'PF_INET')
+
+ addr.pfamily.should == Socket::PF_INET
+ end
+
+ it 'returns an Addrinfo with "INET" family' do
+ addr = Addrinfo.new(@sockaddr, 'INET')
+
+ addr.pfamily.should == Socket::PF_INET
+ end
+
+ it 'returns an Addrinfo with "SOCK_STREAM" as the socket type' do
+ addr = Addrinfo.new(@sockaddr, nil, 'SOCK_STREAM')
+
+ addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'returns an Addrinfo with "STREAM" as the socket type' do
+ addr = Addrinfo.new(@sockaddr, nil, 'STREAM')
+
+ addr.socktype.should == Socket::SOCK_STREAM
+ end
+ end
+
+ with_feature :unix_socket do
+ describe 'using separate arguments for a Unix socket' do
+ before do
+ @sockaddr = Socket.pack_sockaddr_un('socket')
+ end
+
+ it 'returns an Addrinfo with the correct unix path' do
+ Addrinfo.new(@sockaddr).unix_path.should == 'socket'
+ end
+
+ it 'returns an Addrinfo with the correct protocol family' do
+ Addrinfo.new(@sockaddr).pfamily.should == Socket::PF_UNSPEC
+ end
+
+ it 'returns an Addrinfo with the correct address family' do
+ Addrinfo.new(@sockaddr).afamily.should == Socket::AF_UNIX
+ end
+ end
+ end
end
diff --git a/spec/ruby/library/socket/addrinfo/inspect_sockaddr_spec.rb b/spec/ruby/library/socket/addrinfo/inspect_sockaddr_spec.rb
index 2a5990cd47..70ca4dd4d7 100644
--- a/spec/ruby/library/socket/addrinfo/inspect_sockaddr_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/inspect_sockaddr_spec.rb
@@ -1,25 +1,50 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
-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'
+ describe 'using an IPv4 address' do
+ it 'returns a String containing the IP address and port number' do
+ addr = Addrinfo.tcp('127.0.0.1', 80)
+
+ addr.inspect_sockaddr.should == '127.0.0.1:80'
+ end
+
+ it 'returns a String containing just the IP address when no port is given' do
+ addr = Addrinfo.tcp('127.0.0.1', 0)
+
+ addr.inspect_sockaddr.should == '127.0.0.1'
+ end
end
- 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'
+ describe 'using an IPv6 address' do
+ before :each do
+ @ip = '2001:0db8:85a3:0000:0000:8a2e:0370:7334'
+ end
+
+ it 'returns a String containing the IP address and port number' do
+ Addrinfo.tcp('::1', 80).inspect_sockaddr.should == '[::1]:80'
+ Addrinfo.tcp(@ip, 80).inspect_sockaddr.should == '[2001:db8:85a3::8a2e:370:7334]:80'
+ end
+
+ it 'returns a String containing just the IP address when no port is given' do
+ Addrinfo.tcp('::1', 0).inspect_sockaddr.should == '::1'
+ Addrinfo.tcp(@ip, 0).inspect_sockaddr.should == '2001:db8:85a3::8a2e:370:7334'
+ end
end
- 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'
+ with_feature :unix_socket do
+ describe 'using a UNIX path' do
+ it 'returns a String containing the UNIX path' do
+ addr = Addrinfo.unix('/foo/bar')
+
+ addr.inspect_sockaddr.should == '/foo/bar'
+ end
+
+ it 'returns a String containing the UNIX path when using a relative path' do
+ addr = Addrinfo.unix('foo')
+
+ addr.inspect_sockaddr.should == 'UNIX foo'
+ end
end
end
end
diff --git a/spec/ruby/library/socket/addrinfo/inspect_spec.rb b/spec/ruby/library/socket/addrinfo/inspect_spec.rb
new file mode 100644
index 0000000000..98e1e83ffa
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/inspect_spec.rb
@@ -0,0 +1,65 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#inspect' do
+ describe 'using an IPv4 Addrinfo' do
+ it 'returns a String when using a TCP Addrinfo' do
+ addr = Addrinfo.tcp('127.0.0.1', 80)
+
+ addr.inspect.should == '#<Addrinfo: 127.0.0.1:80 TCP>'
+ end
+
+ it 'returns a String when using an UDP Addrinfo' do
+ addr = Addrinfo.udp('127.0.0.1', 80)
+
+ addr.inspect.should == '#<Addrinfo: 127.0.0.1:80 UDP>'
+ end
+
+ it 'returns a String when using an Addrinfo without a port' do
+ addr = Addrinfo.ip('127.0.0.1')
+
+ addr.inspect.should == '#<Addrinfo: 127.0.0.1>'
+ end
+ end
+
+ describe 'using an IPv6 Addrinfo' do
+ it 'returns a String when using a TCP Addrinfo' do
+ addr = Addrinfo.tcp('::1', 80)
+
+ addr.inspect.should == '#<Addrinfo: [::1]:80 TCP>'
+ end
+
+ it 'returns a String when using an UDP Addrinfo' do
+ addr = Addrinfo.udp('::1', 80)
+
+ addr.inspect.should == '#<Addrinfo: [::1]:80 UDP>'
+ end
+
+ it 'returns a String when using an Addrinfo without a port' do
+ addr = Addrinfo.ip('::1')
+
+ addr.inspect.should == '#<Addrinfo: ::1>'
+ end
+ end
+
+ with_feature :unix_socket do
+ describe 'using a UNIX Addrinfo' do
+ it 'returns a String' do
+ addr = Addrinfo.unix('/foo')
+
+ addr.inspect.should == '#<Addrinfo: /foo SOCK_STREAM>'
+ end
+
+ it 'returns a String when using a relative UNIX path' do
+ addr = Addrinfo.unix('foo')
+
+ addr.inspect.should == '#<Addrinfo: UNIX foo SOCK_STREAM>'
+ end
+
+ it 'returns a String when using a DGRAM socket' do
+ addr = Addrinfo.unix('/foo', Socket::SOCK_DGRAM)
+
+ addr.inspect.should == '#<Addrinfo: /foo SOCK_DGRAM>'
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ip_address_spec.rb b/spec/ruby/library/socket/addrinfo/ip_address_spec.rb
index c36399bf0c..004de37254 100644
--- a/spec/ruby/library/socket/addrinfo/ip_address_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/ip_address_spec.rb
@@ -1,5 +1,4 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
describe "Addrinfo#ip_address" do
describe "for an ipv4 socket" do
@@ -22,7 +21,7 @@ describe "Addrinfo#ip_address" do
end
end
- platform_is_not :windows do
+ with_feature :unix_socket do
describe "for a unix socket" do
before :each do
@addrinfo = Addrinfo.unix("/tmp/sock")
@@ -33,4 +32,54 @@ describe "Addrinfo#ip_address" do
end
end
end
+
+ describe 'with an Array as the socket address' do
+ it 'returns the IP as a String' do
+ sockaddr = ['AF_INET', 80, 'localhost', '127.0.0.1']
+ addr = Addrinfo.new(sockaddr)
+
+ addr.ip_address.should == '127.0.0.1'
+ end
+ end
+
+ describe 'without an IP address' do
+ before do
+ @ips = ['127.0.0.1', '0.0.0.0', '::1']
+ end
+
+ # Both these cases seem to return different values at times on MRI. Since
+ # this is network dependent we can't rely on an exact IP being returned.
+ it 'returns the local IP address when using an empty String as the IP' do
+ sockaddr = Socket.sockaddr_in(80, '')
+ addr = Addrinfo.new(sockaddr)
+
+ @ips.include?(addr.ip_address).should == true
+ end
+
+ it 'returns the local IP address when using nil as the IP' do
+ sockaddr = Socket.sockaddr_in(80, nil)
+ addr = Addrinfo.new(sockaddr)
+
+ @ips.include?(addr.ip_address).should == true
+ end
+ end
+
+ # On MRI calling Addrinfo#ip_address with AF_UNSPEC as the address family is
+ # supposed to raise a SocketError. MRI however doesn't provide a way to
+ # actually initialize an Addrinfo with AF_UNSPEC, nor does it allow stubbing
+ # of any methods since Addrinfo doesn't use any Ruby methods for checking the
+ # IP address. As a result we can only run this test on Rubinius.
+ with_feature :pure_ruby_addrinfo do
+ describe 'with a non IPv4 or IPv6 address' do
+ it 'raises SocketError' do
+ sockaddr = Socket.sockaddr_in(80, '127.0.0.1')
+ addr = Addrinfo.new(sockaddr)
+
+ addr.stub!(:ipv4?).and_return(false)
+ addr.stub!(:ipv6?).and_return(false)
+
+ lambda { addr.ip_address }.should raise_error(SocketError)
+ end
+ end
+ end
end
diff --git a/spec/ruby/library/socket/addrinfo/ip_port_spec.rb b/spec/ruby/library/socket/addrinfo/ip_port_spec.rb
index ace6c00ff4..6cf9e7a18e 100644
--- a/spec/ruby/library/socket/addrinfo/ip_port_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/ip_port_spec.rb
@@ -1,5 +1,4 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
describe "Addrinfo#ip_port" do
describe "for an ipv4 socket" do
@@ -22,7 +21,7 @@ describe "Addrinfo#ip_port" do
end
end
- platform_is_not :windows do
+ with_feature :unix_socket do
describe "for a unix socket" do
before :each do
@addrinfo = Addrinfo.unix("/tmp/sock")
diff --git a/spec/ruby/library/socket/addrinfo/ip_spec.rb b/spec/ruby/library/socket/addrinfo/ip_spec.rb
index 6eabbccef8..80e7a62df7 100644
--- a/spec/ruby/library/socket/addrinfo/ip_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/ip_spec.rb
@@ -1,5 +1,5 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
describe "Addrinfo#ip?" do
describe "for an ipv4 socket" do
@@ -22,15 +22,43 @@ describe "Addrinfo#ip?" do
end
end
- platform_is_not :windows do
+ with_feature :unix_socket do
describe "for a unix socket" do
before :each do
@addrinfo = Addrinfo.unix("/tmp/sock")
end
- it "returns Socket::AF_INET6" do
+ it "returns false" do
@addrinfo.ip?.should be_false
end
end
end
end
+
+describe 'Addrinfo.ip' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ it 'returns an Addrinfo instance' do
+ Addrinfo.ip(ip_address).should be_an_instance_of(Addrinfo)
+ end
+
+ it 'sets the IP address' do
+ Addrinfo.ip(ip_address).ip_address.should == ip_address
+ end
+
+ it 'sets the port to 0' do
+ Addrinfo.ip(ip_address).ip_port.should == 0
+ end
+
+ it 'sets the address family' do
+ Addrinfo.ip(ip_address).afamily.should == family
+ end
+
+ it 'sets the protocol family' do
+ Addrinfo.ip(ip_address).pfamily.should == family
+ end
+
+ it 'sets the socket type to 0' do
+ Addrinfo.ip(ip_address).socktype.should == 0
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ip_unpack_spec.rb b/spec/ruby/library/socket/addrinfo/ip_unpack_spec.rb
index caa34d521d..57ae79a6c8 100644
--- a/spec/ruby/library/socket/addrinfo/ip_unpack_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/ip_unpack_spec.rb
@@ -1,5 +1,4 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
describe "Addrinfo#ip_unpack" do
describe "for an ipv4 socket" do
@@ -22,7 +21,7 @@ describe "Addrinfo#ip_unpack" do
end
end
- platform_is_not :windows do
+ with_feature :unix_socket do
describe "for a unix socket" do
before :each do
@addrinfo = Addrinfo.unix("/tmp/sock")
diff --git a/spec/ruby/library/socket/addrinfo/ipv4_loopback_spec.rb b/spec/ruby/library/socket/addrinfo/ipv4_loopback_spec.rb
index bff15fd407..f5bab711db 100644
--- a/spec/ruby/library/socket/addrinfo/ipv4_loopback_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/ipv4_loopback_spec.rb
@@ -1,19 +1,16 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
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
+ Addrinfo.ip('127.0.0.1').ipv4_loopback?.should == true
+ Addrinfo.ip('127.0.0.2').ipv4_loopback?.should == true
+ Addrinfo.ip('127.255.0.1').ipv4_loopback?.should == true
+ Addrinfo.ip('127.255.255.255').ipv4_loopback?.should == true
end
it "returns false for another address" do
- @other.ipv4_loopback?.should be_false
+ Addrinfo.ip('255.255.255.0').ipv4_loopback?.should be_false
end
end
@@ -32,7 +29,7 @@ describe "Addrinfo#ipv4_loopback?" do
end
end
- platform_is_not :windows do
+ with_feature :unix_socket do
describe "for a unix socket" do
before :each do
@addrinfo = Addrinfo.unix("/tmp/sock")
diff --git a/spec/ruby/library/socket/addrinfo/ipv4_multicast_spec.rb b/spec/ruby/library/socket/addrinfo/ipv4_multicast_spec.rb
index bc6d5afa2d..81a68d3d13 100644
--- a/spec/ruby/library/socket/addrinfo/ipv4_multicast_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/ipv4_multicast_spec.rb
@@ -1,38 +1,21 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
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
+ it 'returns true for a multicast address' do
+ Addrinfo.ip('224.0.0.0').ipv4_multicast?.should == true
+ Addrinfo.ip('224.0.0.9').ipv4_multicast?.should == true
+ Addrinfo.ip('239.255.255.250').ipv4_multicast?.should == true
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 a regular addrss' do
+ Addrinfo.ip('8.8.8.8').ipv4_multicast?.should == false
+ end
- it "returns false for another address" do
- @other.ipv4_multicast?.should be_false
- end
+ it 'returns false for an IPv6 address' do
+ Addrinfo.ip('::1').ipv4_multicast?.should == false
end
- platform_is_not :windows do
+ with_feature :unix_socket do
describe "for a unix socket" do
before :each do
@addrinfo = Addrinfo.unix("/tmp/sock")
diff --git a/spec/ruby/library/socket/addrinfo/ipv4_private_spec.rb b/spec/ruby/library/socket/addrinfo/ipv4_private_spec.rb
index 6ef5f5574a..733577609e 100644
--- a/spec/ruby/library/socket/addrinfo/ipv4_private_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/ipv4_private_spec.rb
@@ -1,5 +1,4 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
describe "Addrinfo#ipv4_private?" do
describe "for an ipv4 socket" do
@@ -9,7 +8,14 @@ describe "Addrinfo#ipv4_private?" do
end
it "returns true for a private address" do
- @private.ipv4_private?.should be_true
+ Addrinfo.ip('10.0.0.0').ipv4_private?.should == true
+ Addrinfo.ip('10.0.0.5').ipv4_private?.should == true
+
+ Addrinfo.ip('172.16.0.0').ipv4_private?.should == true
+ Addrinfo.ip('172.16.0.5').ipv4_private?.should == true
+
+ Addrinfo.ip('192.168.0.0').ipv4_private?.should == true
+ Addrinfo.ip('192.168.0.5').ipv4_private?.should == true
end
it "returns false for a public address" do
@@ -27,7 +33,7 @@ describe "Addrinfo#ipv4_private?" do
end
end
- platform_is_not :windows do
+ with_feature :unix_socket do
describe "for a unix socket" do
before :each do
@addrinfo = Addrinfo.unix("/tmp/sock")
diff --git a/spec/ruby/library/socket/addrinfo/ipv4_spec.rb b/spec/ruby/library/socket/addrinfo/ipv4_spec.rb
index 7f8926a497..7cba8209b6 100644
--- a/spec/ruby/library/socket/addrinfo/ipv4_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/ipv4_spec.rb
@@ -1,5 +1,4 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
describe "Addrinfo#ipv4?" do
describe "for an ipv4 socket" do
@@ -22,7 +21,7 @@ describe "Addrinfo#ipv4?" do
end
end
- platform_is_not :windows do
+ with_feature :unix_socket do
describe "for a unix socket" do
before :each do
@addrinfo = Addrinfo.unix("/tmp/sock")
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_linklocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_linklocal_spec.rb
new file mode 100644
index 0000000000..fadc612fb7
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_linklocal_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_linklocal?' do
+ it 'returns true for a link-local address' do
+ Addrinfo.ip('fe80::').ipv6_linklocal?.should == true
+ Addrinfo.ip('fe81::').ipv6_linklocal?.should == true
+ Addrinfo.ip('fe8f::').ipv6_linklocal?.should == true
+ Addrinfo.ip('fe80::1').ipv6_linklocal?.should == true
+ end
+
+ it 'returns false for a regular address' do
+ Addrinfo.ip('::1').ipv6_linklocal?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_linklocal?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_loopback_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_loopback_spec.rb
index e078bce30c..9ff8f107bf 100644
--- a/spec/ruby/library/socket/addrinfo/ipv6_loopback_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/ipv6_loopback_spec.rb
@@ -1,5 +1,4 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
describe "Addrinfo#ipv6_loopback?" do
describe "for an ipv4 socket" do
@@ -8,7 +7,7 @@ describe "Addrinfo#ipv6_loopback?" do
@other = Addrinfo.tcp("0.0.0.0", 80)
end
- it "returns true for the loopback address" do
+ it "returns false for the loopback address" do
@loopback.ipv6_loopback?.should be_false
end
@@ -23,7 +22,7 @@ describe "Addrinfo#ipv6_loopback?" do
@other = Addrinfo.tcp("::", 80)
end
- it "returns false for the loopback address" do
+ it "returns true for the loopback address" do
@loopback.ipv6_loopback?.should be_true
end
@@ -32,7 +31,7 @@ describe "Addrinfo#ipv6_loopback?" do
end
end
- platform_is_not :windows do
+ with_feature :unix_socket do
describe "for a unix socket" do
before :each do
@addrinfo = Addrinfo.unix("/tmp/sock")
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_mc_global_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_mc_global_spec.rb
new file mode 100644
index 0000000000..9c8e4dccb4
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_mc_global_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_mc_global?' do
+ it 'returns true for a multi-cast address in the global scope' do
+ Addrinfo.ip('ff1e::').ipv6_mc_global?.should == true
+ Addrinfo.ip('fffe::').ipv6_mc_global?.should == true
+ Addrinfo.ip('ff0e::').ipv6_mc_global?.should == true
+ Addrinfo.ip('ff1e::1').ipv6_mc_global?.should == true
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_mc_global?.should == false
+ Addrinfo.ip('ff1a::').ipv6_mc_global?.should == false
+ Addrinfo.ip('ff1f::1').ipv6_mc_global?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_mc_global?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_mc_linklocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_mc_linklocal_spec.rb
new file mode 100644
index 0000000000..dd52a9ad8b
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_mc_linklocal_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_mc_linklocal?' do
+ it 'returns true for a multi-cast link-local address' do
+ Addrinfo.ip('ff12::').ipv6_mc_linklocal?.should == true
+ Addrinfo.ip('ff02::').ipv6_mc_linklocal?.should == true
+ Addrinfo.ip('fff2::').ipv6_mc_linklocal?.should == true
+ Addrinfo.ip('ff12::1').ipv6_mc_linklocal?.should == true
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_mc_linklocal?.should == false
+ Addrinfo.ip('fff1::').ipv6_mc_linklocal?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_mc_linklocal?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_mc_nodelocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_mc_nodelocal_spec.rb
new file mode 100644
index 0000000000..b3cf5ffbab
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_mc_nodelocal_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_mc_nodelocal?' do
+ it 'returns true for a multi-cast node-local address' do
+ Addrinfo.ip('ff11::').ipv6_mc_nodelocal?.should == true
+ Addrinfo.ip('ff01::').ipv6_mc_nodelocal?.should == true
+ Addrinfo.ip('fff1::').ipv6_mc_nodelocal?.should == true
+ Addrinfo.ip('ff11::1').ipv6_mc_nodelocal?.should == true
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_mc_nodelocal?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_mc_nodelocal?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_mc_orglocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_mc_orglocal_spec.rb
new file mode 100644
index 0000000000..90653dd49c
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_mc_orglocal_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_mc_orglocal?' do
+ it 'returns true for a multi-cast org-local address' do
+ Addrinfo.ip('ff18::').ipv6_mc_orglocal?.should == true
+ Addrinfo.ip('ff08::').ipv6_mc_orglocal?.should == true
+ Addrinfo.ip('fff8::').ipv6_mc_orglocal?.should == true
+ Addrinfo.ip('ff18::1').ipv6_mc_orglocal?.should == true
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_mc_orglocal?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_mc_orglocal?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_mc_sitelocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_mc_sitelocal_spec.rb
new file mode 100644
index 0000000000..686b625e0d
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_mc_sitelocal_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_mc_sitelocal?' do
+ it 'returns true for a multi-cast site-local address' do
+ Addrinfo.ip('ff15::').ipv6_mc_sitelocal?.should == true
+ Addrinfo.ip('ff05::').ipv6_mc_sitelocal?.should == true
+ Addrinfo.ip('fff5::').ipv6_mc_sitelocal?.should == true
+ Addrinfo.ip('ff15::1').ipv6_mc_sitelocal?.should == true
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_mc_sitelocal?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_mc_sitelocal?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_multicast_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_multicast_spec.rb
index cdb6c798c0..c25322869c 100644
--- a/spec/ruby/library/socket/addrinfo/ipv6_multicast_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/ipv6_multicast_spec.rb
@@ -1,5 +1,4 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
describe "Addrinfo#ipv6_multicast?" do
describe "for an ipv4 socket" do
@@ -8,7 +7,7 @@ describe "Addrinfo#ipv6_multicast?" do
@other = Addrinfo.tcp("0.0.0.0", 80)
end
- it "returns true for the loopback address" do
+ it "returns true for a multicast address" do
@multicast.ipv6_multicast?.should be_false
end
@@ -18,21 +17,24 @@ describe "Addrinfo#ipv6_multicast?" do
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
+ it "returns true for a multicast address" do
+ Addrinfo.ip('ff00::').ipv6_multicast?.should == true
+ Addrinfo.ip('ff00::1').ipv6_multicast?.should == true
+ Addrinfo.ip('ff08::1').ipv6_multicast?.should == true
+ Addrinfo.ip('fff8::1').ipv6_multicast?.should == true
+
+ Addrinfo.ip('ff02::').ipv6_multicast?.should == true
+ Addrinfo.ip('ff02::1').ipv6_multicast?.should == true
+ Addrinfo.ip('ff0f::').ipv6_multicast?.should == true
end
it "returns false for another address" do
- @other.ipv6_multicast?.should be_false
+ Addrinfo.ip('::1').ipv6_multicast?.should == false
+ Addrinfo.ip('fe80::').ipv6_multicast?.should == false
end
end
- platform_is_not :windows do
+ with_feature :unix_socket do
describe "for a unix socket" do
before :each do
@addrinfo = Addrinfo.unix("/tmp/sock")
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_sitelocal_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_sitelocal_spec.rb
new file mode 100644
index 0000000000..f0c382f00c
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_sitelocal_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_sitelocal?' do
+ it 'returns true for a site-local address' do
+ Addrinfo.ip('feef::').ipv6_sitelocal?.should == true
+ Addrinfo.ip('fee0::').ipv6_sitelocal?.should == true
+ Addrinfo.ip('fee2::').ipv6_sitelocal?.should == true
+ Addrinfo.ip('feef::1').ipv6_sitelocal?.should == true
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_sitelocal?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_sitelocal?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_spec.rb
index 02908b793f..131e38849c 100644
--- a/spec/ruby/library/socket/addrinfo/ipv6_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/ipv6_spec.rb
@@ -1,5 +1,4 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
describe "Addrinfo#ipv6?" do
describe "for an ipv4 socket" do
@@ -22,7 +21,7 @@ describe "Addrinfo#ipv6?" do
end
end
- platform_is_not :windows do
+ with_feature :unix_socket do
describe "for a unix socket" do
before :each do
@addrinfo = Addrinfo.unix("/tmp/sock")
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_to_ipv4_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_to_ipv4_spec.rb
new file mode 100644
index 0000000000..48c1706459
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_to_ipv4_spec.rb
@@ -0,0 +1,66 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_to_ipv4' do
+ it 'returns an Addrinfo for ::192.168.1.1' do
+ addr = Addrinfo.ip('::192.168.1.1').ipv6_to_ipv4
+
+ addr.should be_an_instance_of(Addrinfo)
+
+ addr.afamily.should == Socket::AF_INET
+ addr.ip_address.should == '192.168.1.1'
+ end
+
+ it 'returns an Addrinfo for ::0.0.1.1' do
+ addr = Addrinfo.ip('::0.0.1.1').ipv6_to_ipv4
+
+ addr.should be_an_instance_of(Addrinfo)
+
+ addr.afamily.should == Socket::AF_INET
+ addr.ip_address.should == '0.0.1.1'
+ end
+
+ it 'returns an Addrinfo for ::0.0.1.0' do
+ addr = Addrinfo.ip('::0.0.1.0').ipv6_to_ipv4
+
+ addr.should be_an_instance_of(Addrinfo)
+
+ addr.afamily.should == Socket::AF_INET
+ addr.ip_address.should == '0.0.1.0'
+ end
+
+ it 'returns an Addrinfo for ::0.1.0.0' do
+ addr = Addrinfo.ip('::0.1.0.0').ipv6_to_ipv4
+
+ addr.should be_an_instance_of(Addrinfo)
+
+ addr.afamily.should == Socket::AF_INET
+ addr.ip_address.should == '0.1.0.0'
+ end
+
+ it 'returns an Addrinfo for ::ffff:192.168.1.1' do
+ addr = Addrinfo.ip('::ffff:192.168.1.1').ipv6_to_ipv4
+
+ addr.should be_an_instance_of(Addrinfo)
+
+ addr.afamily.should == Socket::AF_INET
+ addr.ip_address.should == '192.168.1.1'
+ end
+
+ it 'returns nil for ::0.0.0.1' do
+ Addrinfo.ip('::0.0.0.1').ipv6_to_ipv4.should be_nil
+ end
+
+ it 'returns nil for a pure IPv6 Addrinfo' do
+ Addrinfo.ip('::1').ipv6_to_ipv4.should be_nil
+ end
+
+ it 'returns nil for an IPv4 Addrinfo' do
+ Addrinfo.ip('192.168.1.1').ipv6_to_ipv4.should be_nil
+ end
+
+ with_feature :unix_socket do
+ it 'returns nil for a UNIX Addrinfo' do
+ Addrinfo.unix('foo').ipv6_to_ipv4.should be_nil
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_unique_local_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_unique_local_spec.rb
new file mode 100644
index 0000000000..b80a15fcb0
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_unique_local_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_unique_local?' do
+ it 'returns true for an unique local IPv6 address' do
+ Addrinfo.ip('fc00::').ipv6_unique_local?.should == true
+ Addrinfo.ip('fd00::').ipv6_unique_local?.should == true
+ Addrinfo.ip('fcff::').ipv6_unique_local?.should == true
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_unique_local?.should == false
+ Addrinfo.ip('fe00::').ipv6_unique_local?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_unique_local?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_unspecified_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_unspecified_spec.rb
new file mode 100644
index 0000000000..dd79d57503
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_unspecified_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_unspecified?' do
+ it 'returns true for an unspecified IPv6 address' do
+ Addrinfo.ip('::').ipv6_unspecified?.should == true
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_unspecified?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_unspecified?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_v4compat_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_v4compat_spec.rb
new file mode 100644
index 0000000000..ab8388a21b
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_v4compat_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_v4compat?' do
+ it 'returns true for an IPv4 compatible address' do
+ Addrinfo.ip('::127.0.0.1').ipv6_v4compat?.should == true
+ Addrinfo.ip('::192.168.1.1').ipv6_v4compat?.should == true
+ end
+
+ it 'returns false for an IPv4 mapped address' do
+ Addrinfo.ip('::ffff:192.168.1.1').ipv6_v4compat?.should == false
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_v4compat?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_v4compat?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/ipv6_v4mapped_spec.rb b/spec/ruby/library/socket/addrinfo/ipv6_v4mapped_spec.rb
new file mode 100644
index 0000000000..82b272c165
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/ipv6_v4mapped_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#ipv6_v4mapped?' do
+ it 'returns true for an IPv4 compatible address' do
+ Addrinfo.ip('::ffff:192.168.1.1').ipv6_v4mapped?.should == true
+ end
+
+ it 'returns false for an IPv4 compatible address' do
+ Addrinfo.ip('::192.168.1.1').ipv6_v4mapped?.should == false
+ Addrinfo.ip('::127.0.0.1').ipv6_v4mapped?.should == false
+ end
+
+ it 'returns false for a regular IPv6 address' do
+ Addrinfo.ip('::1').ipv6_v4mapped?.should == false
+ end
+
+ it 'returns false for an IPv4 address' do
+ Addrinfo.ip('127.0.0.1').ipv6_v4mapped?.should == false
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/listen_spec.rb b/spec/ruby/library/socket/addrinfo/listen_spec.rb
new file mode 100644
index 0000000000..714a96ed6c
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/listen_spec.rb
@@ -0,0 +1,34 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#listen' do
+ before do
+ @addr = Addrinfo.tcp('127.0.0.1', 0)
+ @socket = nil
+ end
+
+ after do
+ @socket.close if @socket
+ end
+
+ it 'returns a Socket when no block is given' do
+ @socket = @addr.listen
+
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket if a block is given' do
+ @addr.listen do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes the socket if a block is given' do
+ socket = nil
+
+ @addr.listen do |sock|
+ socket = sock
+ end
+
+ socket.closed?.should == true
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/marshal_dump_spec.rb b/spec/ruby/library/socket/addrinfo/marshal_dump_spec.rb
new file mode 100644
index 0000000000..2d69a33b53
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/marshal_dump_spec.rb
@@ -0,0 +1,82 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#marshal_dump' do
+ describe 'using an IP Addrinfo' do
+ before do
+ @addr = Addrinfo.getaddrinfo('localhost', 80, :INET, :STREAM,
+ Socket::IPPROTO_TCP, Socket::AI_CANONNAME)[0]
+ end
+
+ it 'returns an Array' do
+ @addr.marshal_dump.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @addr.marshal_dump
+ end
+
+ it 'includes the address family as the 1st value' do
+ @array[0].should == 'AF_INET'
+ end
+
+ it 'includes the IP address as the 2nd value' do
+ @array[1].should == [@addr.ip_address, @addr.ip_port.to_s]
+ end
+
+ it 'includes the protocol family as the 3rd value' do
+ @array[2].should == 'PF_INET'
+ end
+
+ it 'includes the socket type as the 4th value' do
+ @array[3].should == 'SOCK_STREAM'
+ end
+
+ it 'includes the protocol as the 5th value' do
+ @array[4].should == 'IPPROTO_TCP'
+ end
+
+ it 'includes the canonical name as the 6th value' do
+ @array[5].should == @addr.canonname
+ end
+ end
+ end
+
+ with_feature :unix_socket do
+ describe 'using a UNIX Addrinfo' do
+ before do
+ @addr = Addrinfo.unix('foo')
+ end
+
+ it 'returns an Array' do
+ @addr.marshal_dump.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @addr.marshal_dump
+ end
+
+ it 'includes the address family as the 1st value' do
+ @array[0].should == 'AF_UNIX'
+ end
+
+ it 'includes the UNIX path as the 2nd value' do
+ @array[1].should == @addr.unix_path
+ end
+
+ it 'includes the protocol family as the 3rd value' do
+ @array[2].should == 'PF_UNIX'
+ end
+
+ it 'includes the socket type as the 4th value' do
+ @array[3].should == 'SOCK_STREAM'
+ end
+
+ it 'includes the protocol as the 5th value' do
+ @array[4].should == 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/marshal_load_spec.rb b/spec/ruby/library/socket/addrinfo/marshal_load_spec.rb
new file mode 100644
index 0000000000..aa20865224
--- /dev/null
+++ b/spec/ruby/library/socket/addrinfo/marshal_load_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+
+describe 'Addrinfo#marshal_load' do
+ describe 'using an IP address' do
+ it 'returns a new Addrinfo' do
+ source = Addrinfo.getaddrinfo('localhost', 80, :INET, :STREAM,
+ Socket::IPPROTO_TCP, Socket::AI_CANONNAME)[0]
+
+ addr = Marshal.load(Marshal.dump(source))
+
+ addr.afamily.should == source.afamily
+ addr.pfamily.should == source.pfamily
+ addr.socktype.should == source.socktype
+ addr.protocol.should == source.protocol
+ addr.ip_address.should == source.ip_address
+ addr.ip_port.should == source.ip_port
+ addr.canonname.should == source.canonname
+ end
+ end
+
+ with_feature :unix_socket do
+ describe 'using a UNIX socket' do
+ it 'returns a new Addrinfo' do
+ source = Addrinfo.unix('foo')
+ addr = Marshal.load(Marshal.dump(source))
+
+ addr.afamily.should == source.afamily
+ addr.pfamily.should == source.pfamily
+ addr.socktype.should == source.socktype
+ addr.protocol.should == source.protocol
+ addr.unix_path.should == source.unix_path
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/addrinfo/pfamily_spec.rb b/spec/ruby/library/socket/addrinfo/pfamily_spec.rb
index 6a6bd542f7..984744a964 100644
--- a/spec/ruby/library/socket/addrinfo/pfamily_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/pfamily_spec.rb
@@ -1,7 +1,12 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
describe "Addrinfo#pfamily" do
+ it 'returns PF_UNSPEC as the default socket family' do
+ sockaddr = Socket.pack_sockaddr_in(80, 'localhost')
+
+ Addrinfo.new(sockaddr).pfamily.should == Socket::PF_UNSPEC
+ end
+
describe "for an ipv4 socket" do
before :each do
@@ -24,7 +29,7 @@ describe "Addrinfo#pfamily" do
end
end
- platform_is_not :windows do
+ with_feature :unix_socket do
describe "for a unix socket" do
before :each do
@addrinfo = Addrinfo.unix("/tmp/sock")
diff --git a/spec/ruby/library/socket/addrinfo/protocol_spec.rb b/spec/ruby/library/socket/addrinfo/protocol_spec.rb
index 72f4f93b5c..ea143fc4a8 100644
--- a/spec/ruby/library/socket/addrinfo/protocol_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/protocol_spec.rb
@@ -1,30 +1,16 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
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
-
+ it 'returns 0 by default' do
+ Addrinfo.ip('127.0.0.1').protocol.should == 0
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
+ it 'returns a custom protocol when given' do
+ Addrinfo.tcp('127.0.0.1', 80).protocol.should == Socket::IPPROTO_TCP
+ Addrinfo.tcp('::1', 80).protocol.should == Socket::IPPROTO_TCP
end
- platform_is_not :windows do
+ with_feature :unix_socket do
describe "for a unix socket" do
before :each do
@addrinfo = Addrinfo.unix("/tmp/sock")
diff --git a/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb b/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb
index 86819a31b0..c32da5986d 100644
--- a/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb
+++ b/spec/ruby/library/socket/addrinfo/shared/to_sockaddr.rb
@@ -6,7 +6,7 @@ describe :socket_addrinfo_to_sockaddr, :shared => true do
end
it "returns a sockaddr packed structure" do
- @addrinfo.send(@method).should be_kind_of(String)
+ @addrinfo.send(@method).should == Socket.sockaddr_in(80, '127.0.0.1')
end
end
@@ -16,20 +16,36 @@ describe :socket_addrinfo_to_sockaddr, :shared => true do
end
it "returns a sockaddr packed structure" do
- @addrinfo.send(@method).should be_kind_of(String)
+ @addrinfo.send(@method).should == Socket.sockaddr_in(80, '::1')
end
end
- platform_is_not :windows do
+ with_feature :unix_socket 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)
+ @addrinfo.send(@method).should == Socket.sockaddr_un('/tmp/sock')
end
end
end
+ describe 'using a Addrinfo with just an IP address' do
+ it 'returns a String' do
+ addr = Addrinfo.ip('127.0.0.1')
+
+ addr.send(@method).should == Socket.sockaddr_in(0, '127.0.0.1')
+ end
+ end
+
+ describe 'using a Addrinfo without an IP and port' do
+ it 'returns a String' do
+ addr = Addrinfo.new(['AF_INET', 0, '', ''])
+
+ addr.send(@method).should == Socket.sockaddr_in(0, '')
+ end
+ end
+
end
diff --git a/spec/ruby/library/socket/addrinfo/socktype_spec.rb b/spec/ruby/library/socket/addrinfo/socktype_spec.rb
index c38c629c96..b994bea140 100644
--- a/spec/ruby/library/socket/addrinfo/socktype_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/socktype_spec.rb
@@ -1,30 +1,15 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
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
-
+ it 'returns 0 by default' do
+ Addrinfo.ip('127.0.0.1').socktype.should == 0
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
+ it 'returns the socket type when given' do
+ Addrinfo.tcp('127.0.0.1', 80).socktype.should == Socket::SOCK_STREAM
end
- platform_is_not :windows do
+ with_feature :unix_socket do
describe "for a unix socket" do
before :each do
@addrinfo = Addrinfo.unix("/tmp/sock")
diff --git a/spec/ruby/library/socket/addrinfo/tcp_spec.rb b/spec/ruby/library/socket/addrinfo/tcp_spec.rb
index 39406b64ee..c74c9c21c2 100644
--- a/spec/ruby/library/socket/addrinfo/tcp_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/tcp_spec.rb
@@ -1,20 +1,34 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
-describe "Addrinfo.tcp" do
+describe 'Addrinfo.tcp' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ it 'returns an Addrinfo instance' do
+ Addrinfo.tcp(ip_address, 80).should be_an_instance_of(Addrinfo)
+ end
- before :each do
- @addrinfo = Addrinfo.tcp("localhost", "smtp")
- end
+ it 'sets the IP address' do
+ Addrinfo.tcp(ip_address, 80).ip_address.should == ip_address
+ 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
+ it 'sets the port' do
+ Addrinfo.tcp(ip_address, 80).ip_port.should == 80
+ end
+
+ it 'sets the address family' do
+ Addrinfo.tcp(ip_address, 80).afamily.should == family
end
- end
+ it 'sets the protocol family' do
+ Addrinfo.tcp(ip_address, 80).pfamily.should == family
+ end
+
+ it 'sets the socket type' do
+ Addrinfo.tcp(ip_address, 80).socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'sets the socket protocol' do
+ Addrinfo.tcp(ip_address, 80).protocol.should == Socket::IPPROTO_TCP
+ end
+ end
end
diff --git a/spec/ruby/library/socket/addrinfo/to_s_spec.rb b/spec/ruby/library/socket/addrinfo/to_s_spec.rb
index 5ac31e3099..ddf994e051 100644
--- a/spec/ruby/library/socket/addrinfo/to_s_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/to_s_spec.rb
@@ -1,6 +1,5 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative 'shared/to_sockaddr'
-require 'socket'
describe "Addrinfo#to_s" do
it_behaves_like :socket_addrinfo_to_sockaddr, :to_s
diff --git a/spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb b/spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb
index eee9f961b2..b9f75454bd 100644
--- a/spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/to_sockaddr_spec.rb
@@ -1,6 +1,5 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative 'shared/to_sockaddr'
-require 'socket'
describe "Addrinfo#to_sockaddr" do
it_behaves_like :socket_addrinfo_to_sockaddr, :to_sockaddr
diff --git a/spec/ruby/library/socket/addrinfo/udp_spec.rb b/spec/ruby/library/socket/addrinfo/udp_spec.rb
index 237afdbc61..b05cbf9b0b 100644
--- a/spec/ruby/library/socket/addrinfo/udp_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/udp_spec.rb
@@ -1,20 +1,36 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
-describe "Addrinfo.udp" do
+describe 'Addrinfo.udp' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ it 'returns an Addrinfo instance' do
+ Addrinfo.udp(ip_address, 80).should be_an_instance_of(Addrinfo)
+ end
- before :each do
- @addrinfo = Addrinfo.udp("localhost", "daytime")
- end
+ it 'sets the IP address' do
+ Addrinfo.udp(ip_address, 80).ip_address.should == ip_address
+ end
+
+ it 'sets the port' do
+ Addrinfo.udp(ip_address, 80).ip_port.should == 80
+ end
+
+ it 'sets the address family' do
+ Addrinfo.udp(ip_address, 80).afamily.should == family
+ end
+
+ it 'sets the protocol family' do
+ Addrinfo.udp(ip_address, 80).pfamily.should == family
+ end
+
+ it 'sets the socket type' do
+ Addrinfo.udp(ip_address, 80).socktype.should == Socket::SOCK_DGRAM
+ end
- it "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
+ it 'sets the socket protocol' do
+ Addrinfo.udp(ip_address, 80).protocol.should == Socket::IPPROTO_UDP
+ end
end
end
-
end
diff --git a/spec/ruby/library/socket/addrinfo/unix_path_spec.rb b/spec/ruby/library/socket/addrinfo/unix_path_spec.rb
index 1168a225b0..0f25881724 100644
--- a/spec/ruby/library/socket/addrinfo/unix_path_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/unix_path_spec.rb
@@ -1,7 +1,6 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
-platform_is_not :windows do
+with_feature :unix_socket do
describe "Addrinfo#unix_path" do
describe "for an ipv4 socket" do
@@ -25,15 +24,13 @@ platform_is_not :windows do
end
end
- platform_is_not :windows do
- describe "for a unix socket" do
- before :each do
- @addrinfo = Addrinfo.unix("/tmp/sock")
- end
+ describe "for a unix socket" do
+ before :each do
+ @addrinfo = Addrinfo.unix("/tmp/sock")
+ end
- it "returns the socket path" do
- @addrinfo.unix_path.should == "/tmp/sock"
- end
+ it "returns the socket path" do
+ @addrinfo.unix_path.should == "/tmp/sock"
end
end
end
diff --git a/spec/ruby/library/socket/addrinfo/unix_spec.rb b/spec/ruby/library/socket/addrinfo/unix_spec.rb
index 599c20e815..4596ece17e 100644
--- a/spec/ruby/library/socket/addrinfo/unix_spec.rb
+++ b/spec/ruby/library/socket/addrinfo/unix_spec.rb
@@ -1,18 +1,35 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
-describe "Addrinfo.unix" do
+with_feature :unix_socket do
+ describe 'Addrinfo.unix' do
+ it 'returns an Addrinfo instance' do
+ Addrinfo.unix('socket').should be_an_instance_of(Addrinfo)
+ end
- platform_is_not :windows do
- before :each do
- @addrinfo = Addrinfo.unix("/tmp/sock")
+ it 'sets the IP address' do
+ Addrinfo.unix('socket').unix_path.should == 'socket'
+ end
+
+ it 'sets the address family' do
+ Addrinfo.unix('socket').afamily.should == Socket::AF_UNIX
+ end
+
+ it 'sets the protocol family' do
+ Addrinfo.unix('socket').pfamily.should == Socket::PF_UNIX
+ end
+
+ it 'sets the socket type' do
+ Addrinfo.unix('socket').socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'sets a custom socket type' do
+ addr = Addrinfo.unix('socket', Socket::SOCK_DGRAM)
+
+ addr.socktype.should == Socket::SOCK_DGRAM
end
- it "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"
+ it 'sets the socket protocol to 0' do
+ Addrinfo.unix('socket').protocol.should == 0
end
end
end
diff --git a/spec/ruby/library/socket/ancillarydata/cmsg_is_spec.rb b/spec/ruby/library/socket/ancillarydata/cmsg_is_spec.rb
new file mode 100644
index 0000000000..e423e0ef05
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/cmsg_is_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#cmsg_is?' do
+ describe 'using :INET, :IP, :TTL as the family, level, and type' do
+ before do
+ @data = Socket::AncillaryData.new(:INET, :IP, :TTL, '')
+ end
+
+ it 'returns true when comparing with IPPROTO_IP and IP_TTL' do
+ @data.cmsg_is?(Socket::IPPROTO_IP, Socket::IP_TTL).should == true
+ end
+
+ it 'returns true when comparing with :IP and :TTL' do
+ @data.cmsg_is?(:IP, :TTL).should == true
+ end
+
+ it 'returns false when comparing with :IP and :PKTINFO' do
+ @data.cmsg_is?(:IP, :PKTINFO).should == false
+ end
+
+ it 'returns false when comparing with :SOCKET and :RIGHTS' do
+ @data.cmsg_is?(:SOCKET, :RIGHTS).should == false
+ end
+
+ it 'raises SocketError when comparign with :IPV6 and :RIGHTS' do
+ lambda { @data.cmsg_is?(:IPV6, :RIGHTS) }.should raise_error(SocketError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/data_spec.rb b/spec/ruby/library/socket/ancillarydata/data_spec.rb
new file mode 100644
index 0000000000..5a1a446dd5
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/data_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#data' do
+ it 'returns the data as a String' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, 'ugh').data.should == 'ugh'
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/family_spec.rb b/spec/ruby/library/socket/ancillarydata/family_spec.rb
new file mode 100644
index 0000000000..b742e0c6db
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/family_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#family' do
+ it 'returns the family as a Fixnum' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, '').family.should == Socket::AF_INET
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/initialize_spec.rb b/spec/ruby/library/socket/ancillarydata/initialize_spec.rb
new file mode 100644
index 0000000000..659a29e24a
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/initialize_spec.rb
@@ -0,0 +1,282 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#initialize' do
+ describe 'using Fixnums for the family, level, and type' do
+ before do
+ @data = Socket::AncillaryData
+ .new(Socket::AF_INET, Socket::IPPROTO_IP, Socket::IP_RECVTTL, 'ugh')
+ end
+
+ it 'sets the address family' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the message level' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the message type' do
+ @data.type.should == Socket::IP_RECVTTL
+ end
+
+ it 'sets the data' do
+ @data.data.should == 'ugh'
+ end
+ end
+
+ describe 'using Symbols for the family, level, and type' do
+ before do
+ @data = Socket::AncillaryData.new(:INET, :IPPROTO_IP, :RECVTTL, 'ugh')
+ end
+
+ it 'sets the address family' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the message level' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the message type' do
+ @data.type.should == Socket::IP_RECVTTL
+ end
+
+ it 'sets the data' do
+ @data.data.should == 'ugh'
+ end
+ end
+
+ describe 'using Strings for the family, level, and type' do
+ before do
+ @data = Socket::AncillaryData.new('INET', 'IPPROTO_IP', 'RECVTTL', 'ugh')
+ end
+
+ it 'sets the address family' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the message level' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the message type' do
+ @data.type.should == Socket::IP_RECVTTL
+ end
+
+ it 'sets the data' do
+ @data.data.should == 'ugh'
+ end
+ end
+
+ describe 'using custom objects with a to_str method for the family, level, and type' do
+ before do
+ fmock = mock(:family)
+ lmock = mock(:level)
+ tmock = mock(:type)
+ dmock = mock(:data)
+
+ fmock.stub!(:to_str).and_return('INET')
+ lmock.stub!(:to_str).and_return('IP')
+ tmock.stub!(:to_str).and_return('RECVTTL')
+ dmock.stub!(:to_str).and_return('ugh')
+
+ @data = Socket::AncillaryData.new(fmock, lmock, tmock, dmock)
+ end
+
+ it 'sets the address family' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the message level' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the message type' do
+ @data.type.should == Socket::IP_RECVTTL
+ end
+
+ it 'sets the data' do
+ @data.data.should == 'ugh'
+ end
+ end
+
+ describe 'using :AF_INET as the family and :SOCKET as the level' do
+ it 'sets the type to SCM_RIGHTS when using :RIGHTS as the type argument' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, '').type.should == Socket::SCM_RIGHTS
+ end
+
+ it 'sets the type to SCM_TIMESTAMP when using :TIMESTAMP as the type argument' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :TIMESTAMP, '').type.should == Socket::SCM_TIMESTAMP
+ end
+
+ it 'raises TypeError when using a numeric string as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :IGMP, Socket::SCM_RIGHTS.to_s, '')
+ }.should raise_error(TypeError)
+ end
+
+ it 'raises SocketError when using :RECVTTL as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :SOCKET, :RECVTTL, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :MOO as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :SOCKET, :MOO, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :IP_RECVTTL as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :SOCKET, :IP_RECVTTL, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_INET as the family and :SOCKET as the level' do
+ it 'sets the type to SCM_RIGHTS when using :RIGHTS as the type argument' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, '').type.should == Socket::SCM_RIGHTS
+ end
+ end
+
+ describe 'using :AF_INET as the family and :IP as the level' do
+ it 'sets the type to IP_RECVTTL when using :RECVTTL as the type argument' do
+ Socket::AncillaryData.new(:INET, :IP, :RECVTTL, '').type.should == Socket::IP_RECVTTL
+ end
+
+ with_feature :ip_mtu do
+ it 'sets the type to IP_MTU when using :MTU as the type argument' do
+ Socket::AncillaryData.new(:INET, :IP, :MTU, '').type.should == Socket::IP_MTU
+ end
+ end
+
+ it 'raises SocketError when using :RIGHTS as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :IP, :RIGHTS, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :MOO as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :IP, :MOO, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_INET as the family and :IPV6 as the level' do
+ it 'sets the type to IPV6_CHECKSUM when using :CHECKSUM as the type argument' do
+ Socket::AncillaryData.new(:INET, :IPV6, :CHECKSUM, '').type.should == Socket::IPV6_CHECKSUM
+ end
+
+ with_feature :ipv6_nexthop do
+ it 'sets the type to IPV6_NEXTHOP when using :NEXTHOP as the type argument' do
+ Socket::AncillaryData.new(:INET, :IPV6, :NEXTHOP, '').type.should == Socket::IPV6_NEXTHOP
+ end
+ end
+
+ it 'raises SocketError when using :RIGHTS as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :IPV6, :RIGHTS, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :MOO as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :IPV6, :MOO, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_INET as the family and :TCP as the level' do
+ with_feature :tcp_cork do
+ it 'sets the type to TCP_CORK when using :CORK as the type argument' do
+ Socket::AncillaryData.new(:INET, :TCP, :CORK, '').type.should == Socket::TCP_CORK
+ end
+ end
+
+ with_feature :tcp_info do
+ it 'sets the type to TCP_INFO when using :INFO as the type argument' do
+ Socket::AncillaryData.new(:INET, :TCP, :INFO, '').type.should == Socket::TCP_INFO
+ end
+ end
+
+ it 'raises SocketError when using :RIGHTS as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :TCP, :RIGHTS, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :MOO as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :TCP, :MOO, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_INET as the family and :UDP as the level' do
+ with_feature :udp_cork do
+ it 'sets the type to UDP_CORK when using :CORK as the type argument' do
+ Socket::AncillaryData.new(:INET, :UDP, :CORK, '').type.should == Socket::UDP_CORK
+ end
+ end
+
+ it 'raises SocketError when using :RIGHTS as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :UDP, :RIGHTS, '')
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError when using :MOO as the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:INET, :UDP, :MOO, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_UNIX as the family and :SOCKET as the level' do
+ it 'sets the type to SCM_RIGHTS when using :RIGHTS as the type argument' do
+ Socket::AncillaryData.new(:UNIX, :SOCKET, :RIGHTS, '').type.should == Socket::SCM_RIGHTS
+ end
+
+ it 'raises SocketError when using :CORK sa the type argument' do
+ lambda {
+ Socket::AncillaryData.new(:UNIX, :SOCKET, :CORK, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_UNIX as the family and :IP as the level' do
+ it 'raises SocketError' do
+ lambda {
+ Socket::AncillaryData.new(:UNIX, :IP, :RECVTTL, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_UNIX as the family and :IPV6 as the level' do
+ it 'raises SocketError' do
+ lambda {
+ Socket::AncillaryData.new(:UNIX, :IPV6, :NEXTHOP, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_UNIX as the family and :TCP as the level' do
+ it 'raises SocketError' do
+ lambda {
+ Socket::AncillaryData.new(:UNIX, :TCP, :CORK, '')
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using :AF_UNIX as the family and :UDP as the level' do
+ it 'raises SocketError' do
+ lambda {
+ Socket::AncillaryData.new(:UNIX, :UDP, :CORK, '')
+ }.should raise_error(SocketError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/int_spec.rb b/spec/ruby/library/socket/ancillarydata/int_spec.rb
new file mode 100644
index 0000000000..75608a28b8
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/int_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData.int' do
+ before do
+ @data = Socket::AncillaryData.int(:INET, :SOCKET, :RIGHTS, 4)
+ end
+
+ it 'returns a Socket::AncillaryData' do
+ @data.should be_an_instance_of(Socket::AncillaryData)
+ end
+
+ it 'sets the family to AF_INET' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the level SOL_SOCKET' do
+ @data.level.should == Socket::SOL_SOCKET
+ end
+
+ it 'sets the type SCM_RIGHTS' do
+ @data.type.should == Socket::SCM_RIGHTS
+ end
+
+ it 'sets the data to a packed String' do
+ @data.data.should == [4].pack('I')
+ end
+ end
+
+ describe 'Socket::AncillaryData#int' do
+ it 'returns the data as a Fixnum' do
+ data = Socket::AncillaryData.int(:UNIX, :SOCKET, :RIGHTS, 4)
+
+ data.int.should == 4
+ end
+
+ it 'raises when the data is not a Fixnum' do
+ data = Socket::AncillaryData.new(:UNIX, :SOCKET, :RIGHTS, 'ugh')
+
+ lambda { data.int }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/ip_pktinfo_spec.rb b/spec/ruby/library/socket/ancillarydata/ip_pktinfo_spec.rb
new file mode 100644
index 0000000000..aaf5b80a23
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/ip_pktinfo_spec.rb
@@ -0,0 +1,145 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData.ip_pktinfo' do
+ describe 'with a source address and index' do
+ before do
+ @data = Socket::AncillaryData.ip_pktinfo(Addrinfo.ip('127.0.0.1'), 4)
+ end
+
+ it 'returns a Socket::AncillaryData' do
+ @data.should be_an_instance_of(Socket::AncillaryData)
+ end
+
+ it 'sets the family to AF_INET' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the level to IPPROTO_IP' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the type to IP_PKTINFO' do
+ @data.type.should == Socket::IP_PKTINFO
+ end
+ end
+
+ describe 'with a source address, index, and destination address' do
+ before do
+ source = Addrinfo.ip('127.0.0.1')
+ dest = Addrinfo.ip('127.0.0.5')
+ @data = Socket::AncillaryData.ip_pktinfo(source, 4, dest)
+ end
+
+ it 'returns a Socket::AncillaryData' do
+ @data.should be_an_instance_of(Socket::AncillaryData)
+ end
+
+ it 'sets the family to AF_INET' do
+ @data.family.should == Socket::AF_INET
+ end
+
+ it 'sets the level to IPPROTO_IP' do
+ @data.level.should == Socket::IPPROTO_IP
+ end
+
+ it 'sets the type to IP_PKTINFO' do
+ @data.type.should == Socket::IP_PKTINFO
+ end
+ end
+ end
+
+ describe 'Socket::AncillaryData#ip_pktinfo' do
+ describe 'using an Addrinfo without a port number' do
+ before do
+ @source = Addrinfo.ip('127.0.0.1')
+ @dest = Addrinfo.ip('127.0.0.5')
+ @data = Socket::AncillaryData.ip_pktinfo(@source, 4, @dest)
+ end
+
+ it 'returns an Array' do
+ @data.ip_pktinfo.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @info = @data.ip_pktinfo
+ end
+
+ it 'stores an Addrinfo at index 0' do
+ @info[0].should be_an_instance_of(Addrinfo)
+ end
+
+ it 'stores the ifindex at index 1' do
+ @info[1].should be_an_instance_of(Fixnum)
+ end
+
+ it 'stores an Addrinfo at index 2' do
+ @info[2].should be_an_instance_of(Addrinfo)
+ end
+ end
+
+ describe 'the source Addrinfo' do
+ before do
+ @addr = @data.ip_pktinfo[0]
+ end
+
+ it 'uses the correct IP address' do
+ @addr.ip_address.should == '127.0.0.1'
+ end
+
+ it 'is not the same object as the input Addrinfo' do
+ @addr.should_not == @source
+ end
+ end
+
+ describe 'the ifindex' do
+ it 'is a Fixnum' do
+ @data.ip_pktinfo[1].should == 4
+ end
+ end
+
+ describe 'the destination Addrinfo' do
+ before do
+ @addr = @data.ip_pktinfo[2]
+ end
+
+ it 'uses the correct IP address' do
+ @addr.ip_address.should == '127.0.0.5'
+ end
+
+ it 'is not the same object as the input Addrinfo' do
+ @addr.should_not == @dest
+ end
+ end
+ end
+
+ describe 'using an Addrinfo with a port number' do
+ before do
+ @source = Addrinfo.tcp('127.0.0.1', 80)
+ @dest = Addrinfo.tcp('127.0.0.5', 85)
+ @data = Socket::AncillaryData.ip_pktinfo(@source, 4, @dest)
+ end
+
+ describe 'the source Addrinfo' do
+ before do
+ @addr = @data.ip_pktinfo[0]
+ end
+
+ it 'does not contain a port number' do
+ @addr.ip_port.should == 0
+ end
+ end
+
+ describe 'the destination Addrinfo' do
+ before do
+ @addr = @data.ip_pktinfo[2]
+ end
+
+ it 'does not contain a port number' do
+ @addr.ip_port.should == 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_addr_spec.rb b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_addr_spec.rb
new file mode 100644
index 0000000000..f70fe27d6a
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_addr_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data, :ipv6_pktinfo do
+ describe 'Socket::AncillaryData#ipv6_pktinfo_addr' do
+ it 'returns an Addrinfo' do
+ data = Socket::AncillaryData.ipv6_pktinfo(Addrinfo.ip('::1'), 4)
+
+ data.ipv6_pktinfo_addr.should be_an_instance_of(Addrinfo)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_ifindex_spec.rb b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_ifindex_spec.rb
new file mode 100644
index 0000000000..bda37eec98
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_ifindex_spec.rb
@@ -0,0 +1,11 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data, :ipv6_pktinfo do
+ describe 'Socket::AncillaryData#ipv6_pktinfo_ifindex' do
+ it 'returns an Addrinfo' do
+ data = Socket::AncillaryData.ipv6_pktinfo(Addrinfo.ip('::1'), 4)
+
+ data.ipv6_pktinfo_ifindex.should == 4
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_spec.rb b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_spec.rb
new file mode 100644
index 0000000000..6315aba89c
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/ipv6_pktinfo_spec.rb
@@ -0,0 +1,89 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data, :ipv6_pktinfo do
+ describe 'Socket::AncillaryData.ipv6_pktinfo' do
+ before do
+ @data = Socket::AncillaryData.ipv6_pktinfo(Addrinfo.ip('::1'), 4)
+ end
+
+ it 'returns a Socket::AncillaryData' do
+ @data.should be_an_instance_of(Socket::AncillaryData)
+ end
+
+ it 'sets the family to AF_INET' do
+ @data.family.should == Socket::AF_INET6
+ end
+
+ it 'sets the level to IPPROTO_IP' do
+ @data.level.should == Socket::IPPROTO_IPV6
+ end
+
+ it 'sets the type to IP_PKTINFO' do
+ @data.type.should == Socket::IPV6_PKTINFO
+ end
+ end
+
+ describe 'Socket::AncillaryData#ipv6_pktinfo' do
+ describe 'using an Addrinfo without a port number' do
+ before do
+ @source = Addrinfo.ip('::1')
+ @data = Socket::AncillaryData.ipv6_pktinfo(@source, 4)
+ end
+
+ it 'returns an Array' do
+ @data.ipv6_pktinfo.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @info = @data.ipv6_pktinfo
+ end
+
+ it 'stores an Addrinfo at index 0' do
+ @info[0].should be_an_instance_of(Addrinfo)
+ end
+
+ it 'stores the ifindex at index 1' do
+ @info[1].should be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe 'the source Addrinfo' do
+ before do
+ @addr = @data.ipv6_pktinfo[0]
+ end
+
+ it 'uses the correct IP address' do
+ @addr.ip_address.should == '::1'
+ end
+
+ it 'is not the same object as the input Addrinfo' do
+ @addr.should_not == @source
+ end
+ end
+
+ describe 'the ifindex' do
+ it 'is a Fixnum' do
+ @data.ipv6_pktinfo[1].should == 4
+ end
+ end
+ end
+
+ describe 'using an Addrinfo with a port number' do
+ before do
+ @source = Addrinfo.tcp('::1', 80)
+ @data = Socket::AncillaryData.ipv6_pktinfo(@source, 4)
+ end
+
+ describe 'the source Addrinfo' do
+ before do
+ @addr = @data.ipv6_pktinfo[0]
+ end
+
+ it 'does not contain a port number' do
+ @addr.ip_port.should == 0
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/level_spec.rb b/spec/ruby/library/socket/ancillarydata/level_spec.rb
new file mode 100644
index 0000000000..40b070a6d8
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/level_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#level' do
+ it 'returns the level as a Fixnum' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, '').level.should == Socket::SOL_SOCKET
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/type_spec.rb b/spec/ruby/library/socket/ancillarydata/type_spec.rb
new file mode 100644
index 0000000000..1a4b04ed57
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/type_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData#type' do
+ it 'returns the type as a Fixnum' do
+ Socket::AncillaryData.new(:INET, :SOCKET, :RIGHTS, '').type.should == Socket::SCM_RIGHTS
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ancillarydata/unix_rights_spec.rb b/spec/ruby/library/socket/ancillarydata/unix_rights_spec.rb
new file mode 100644
index 0000000000..0bbef4c08d
--- /dev/null
+++ b/spec/ruby/library/socket/ancillarydata/unix_rights_spec.rb
@@ -0,0 +1,59 @@
+require_relative '../spec_helper'
+
+with_feature :ancillary_data do
+ describe 'Socket::AncillaryData.unix_rights' do
+ describe 'using a list of IO objects' do
+ before do
+ @data = Socket::AncillaryData.unix_rights(STDOUT, STDERR)
+ end
+
+ it 'sets the family to AF_UNIX' do
+ @data.family.should == Socket::AF_UNIX
+ end
+
+ it 'sets the level to SOL_SOCKET' do
+ @data.level.should == Socket::SOL_SOCKET
+ end
+
+ it 'sets the type to SCM_RIGHTS' do
+ @data.type.should == Socket::SCM_RIGHTS
+ end
+
+ it 'sets the data to a String containing the file descriptors' do
+ @data.data.unpack('I*').should == [STDOUT.fileno, STDERR.fileno]
+ end
+ end
+
+ describe 'using non IO objects' do
+ it 'raises TypeError' do
+ lambda { Socket::AncillaryData.unix_rights(10) }.should raise_error(TypeError)
+ end
+ end
+ end
+
+ describe 'Socket::AncillaryData#unix_rights' do
+ it 'returns the data as an Array of IO objects' do
+ data = Socket::AncillaryData.unix_rights(STDOUT, STDERR)
+
+ data.unix_rights.should == [STDOUT, STDERR]
+ end
+
+ it 'returns nil when the data is not a list of file descriptors' do
+ data = Socket::AncillaryData.new(:UNIX, :SOCKET, :RIGHTS, '')
+
+ data.unix_rights.should be_nil
+ end
+
+ it 'raises TypeError when the level is not SOL_SOCKET' do
+ data = Socket::AncillaryData.new(:INET, :IP, :RECVTTL, '')
+
+ lambda { data.unix_rights }.should raise_error(TypeError)
+ end
+
+ it 'raises TypeError when the type is not SCM_RIGHTS' do
+ data = Socket::AncillaryData.new(:INET, :SOCKET, :TIMESTAMP, '')
+
+ lambda { data.unix_rights }.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/close_read_spec.rb b/spec/ruby/library/socket/basicsocket/close_read_spec.rb
index 13894bc0c5..c989fcaf72 100644
--- a/spec/ruby/library/socket/basicsocket/close_read_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/close_read_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket::BasicSocket#close_read" do
@@ -15,13 +15,13 @@ describe "Socket::BasicSocket#close_read" do
lambda { @server.read }.should raise_error(IOError)
end
- it "it works on sockets with closed ends" do
+ it 'does not raise when called on a socket already closed for reading' do
+ @server.close_read
@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
+ it 'does not fully close the socket' do
@server.close_read
@server.closed?.should be_false
end
@@ -32,7 +32,7 @@ describe "Socket::BasicSocket#close_read" do
@server.closed?.should be_true
end
- it "raises IOError on closed socket" do
+ it 'raises IOError when called on a fully closed socket' do
@server.close
lambda { @server.close_read }.should raise_error(IOError)
end
diff --git a/spec/ruby/library/socket/basicsocket/close_write_spec.rb b/spec/ruby/library/socket/basicsocket/close_write_spec.rb
index 194df1d535..f37e0e5074 100644
--- a/spec/ruby/library/socket/basicsocket/close_write_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/close_write_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket::BasicSocket#close_write" do
@@ -15,13 +15,13 @@ describe "Socket::BasicSocket#close_write" do
lambda { @server.write("foo") }.should raise_error(IOError)
end
- it "works on sockets with closed write ends" do
+ it 'does not raise when called on a socket already closed for writing' do
+ @server.close_write
@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
+ it 'does not fully close the socket' do
@server.close_write
@server.closed?.should be_false
end
@@ -37,7 +37,7 @@ describe "Socket::BasicSocket#close_write" do
@server.closed?.should be_true
end
- it "raises IOError on closed socket" do
+ it 'raises IOError when called on a fully closed socket' do
@server.close
lambda { @server.close_write }.should raise_error(IOError)
end
diff --git a/spec/ruby/library/socket/basicsocket/connect_address_spec.rb b/spec/ruby/library/socket/basicsocket/connect_address_spec.rb
new file mode 100644
index 0000000000..cb05d3bfe1
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/connect_address_spec.rb
@@ -0,0 +1,150 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket#connect_address' do
+ describe 'using an unbound socket' do
+ after do
+ @sock.close
+ end
+
+ it 'raises SocketError' do
+ @sock = Socket.new(:INET, :STREAM)
+
+ lambda { @sock.connect_address }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using a socket bound to 0.0.0.0' do
+ before do
+ @sock = Socket.new(:INET, :STREAM)
+ @sock.bind(Socket.sockaddr_in(0, '0.0.0.0'))
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.connect_address.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'uses 127.0.0.1 as the IP address' do
+ @sock.connect_address.ip_address.should == '127.0.0.1'
+ end
+
+ it 'uses the correct port number' do
+ @sock.connect_address.ip_port.should > 0
+ end
+
+ it 'uses AF_INET as the address family' do
+ @sock.connect_address.afamily.should == Socket::AF_INET
+ end
+
+ it 'uses PF_INET as the address family' do
+ @sock.connect_address.pfamily.should == Socket::PF_INET
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.connect_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.connect_address.protocol.should == 0
+ end
+ end
+
+ describe 'using a socket bound to ::' do
+ before do
+ @sock = Socket.new(:INET6, :STREAM)
+ @sock.bind(Socket.sockaddr_in(0, '::'))
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.connect_address.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'uses ::1 as the IP address' do
+ @sock.connect_address.ip_address.should == '::1'
+ end
+
+ it 'uses the correct port number' do
+ @sock.connect_address.ip_port.should > 0
+ end
+
+ it 'uses AF_INET6 as the address family' do
+ @sock.connect_address.afamily.should == Socket::AF_INET6
+ end
+
+ it 'uses PF_INET6 as the address family' do
+ @sock.connect_address.pfamily.should == Socket::PF_INET6
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.connect_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.connect_address.protocol.should == 0
+ end
+ end
+
+ with_feature :unix_socket do
+ describe 'using an unbound UNIX socket' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @server.close
+ rm_r(@path)
+ end
+
+ it 'raises SocketError' do
+ lambda { @client.connect_address }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using a bound UNIX socket' do
+ before do
+ @path = SocketSpecs.socket_path
+ @sock = UNIXServer.new(@path)
+ end
+
+ after do
+ @sock.close
+ rm_r(@path)
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.connect_address.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'uses the correct socket path' do
+ @sock.connect_address.unix_path.should == @path
+ end
+
+ it 'uses AF_UNIX as the address family' do
+ @sock.connect_address.afamily.should == Socket::AF_UNIX
+ end
+
+ it 'uses PF_UNIX as the protocol family' do
+ @sock.connect_address.pfamily.should == Socket::PF_UNIX
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.connect_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.connect_address.protocol.should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/do_not_reverse_lookup_spec.rb b/spec/ruby/library/socket/basicsocket/do_not_reverse_lookup_spec.rb
index 479a1fdac8..85a66275f8 100644
--- a/spec/ruby/library/socket/basicsocket/do_not_reverse_lookup_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/do_not_reverse_lookup_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "BasicSocket.do_not_reverse_lookup" do
diff --git a/spec/ruby/library/socket/basicsocket/for_fd_spec.rb b/spec/ruby/library/socket/basicsocket/for_fd_spec.rb
index affe8c2428..9c9e6a8b55 100644
--- a/spec/ruby/library/socket/basicsocket/for_fd_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/for_fd_spec.rb
@@ -1,14 +1,14 @@
-
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-describe "BasicSocket#for_fd" do
+describe "BasicSocket.for_fd" do
before :each do
@server = TCPServer.new(0)
@s2 = nil
end
after :each do
+ @socket1.close if @socket1
@server.close if @server
end
@@ -18,4 +18,21 @@ describe "BasicSocket#for_fd" do
@s2.should be_kind_of(TCPServer)
@s2.fileno.should == @server.fileno
end
+
+ it 'returns a new socket for a file descriptor' do
+ @socket1 = Socket.new(:INET, :DGRAM)
+ socket2 = Socket.for_fd(@socket1.fileno)
+ socket2.autoclose = false
+
+ socket2.should be_an_instance_of(Socket)
+ socket2.fileno.should == @socket1.fileno
+ end
+
+ it 'sets the socket into binary mode' do
+ @socket1 = Socket.new(:INET, :DGRAM)
+ socket2 = Socket.for_fd(@socket1.fileno)
+ socket2.autoclose = false
+
+ socket2.binmode?.should be_true
+ end
end
diff --git a/spec/ruby/library/socket/basicsocket/getpeereid_spec.rb b/spec/ruby/library/socket/basicsocket/getpeereid_spec.rb
new file mode 100644
index 0000000000..9eeb6d0e0b
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/getpeereid_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'BasicSocket#getpeereid' do
+ with_feature :unix_socket do
+ describe 'using a UNIXSocket' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @server.close
+
+ rm_r(@path)
+ end
+
+ it 'returns an Array with the user and group ID' do
+ @client.getpeereid.should == [Process.euid, Process.egid]
+ end
+ end
+ end
+
+ describe 'using an IPSocket' do
+ after do
+ @sock.close
+ end
+
+ it 'raises NoMethodError' do
+ @sock = TCPServer.new('127.0.0.1', 0)
+ lambda { @sock.getpeereid }.should raise_error(NoMethodError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/getpeername_spec.rb b/spec/ruby/library/socket/basicsocket/getpeername_spec.rb
index c2636f764e..23c73056cd 100644
--- a/spec/ruby/library/socket/basicsocket/getpeername_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/getpeername_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket::BasicSocket#getpeername" do
@@ -19,8 +19,7 @@ describe "Socket::BasicSocket#getpeername" do
@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)
+ it 'raises Errno::ENOTCONN for a disconnected socket' do
+ lambda { @server.getpeername }.should raise_error(Errno::ENOTCONN)
end
end
diff --git a/spec/ruby/library/socket/basicsocket/getsockname_spec.rb b/spec/ruby/library/socket/basicsocket/getsockname_spec.rb
index 581eb68599..a2c5980a9e 100644
--- a/spec/ruby/library/socket/basicsocket/getsockname_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/getsockname_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket::BasicSocket#getsockname" do
@@ -20,7 +20,7 @@ describe "Socket::BasicSocket#getsockname" do
sockaddr[0].should == @socket.addr[1]
end
- it "returns empty sockaddr for unbinded sockets" do
+ it 'returns a default socket address for a disconnected socket' do
@socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
sockaddr = Socket.unpack_sockaddr_in(@socket.getsockname)
sockaddr.should == [0, "0.0.0.0"]
diff --git a/spec/ruby/library/socket/basicsocket/getsockopt_spec.rb b/spec/ruby/library/socket/basicsocket/getsockopt_spec.rb
index 7040aac5e9..926d7505e8 100644
--- a/spec/ruby/library/socket/basicsocket/getsockopt_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/getsockopt_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "BasicSocket#getsockopt" do
@@ -43,4 +43,146 @@ describe "BasicSocket#getsockopt" do
it "raises a SystemCallError with an invalid socket option" do
lambda { @sock.getsockopt Socket::SOL_SOCKET, -1 }.should raise_error(Errno::ENOPROTOOPT)
end
+
+ it 'returns a Socket::Option using a constant' do
+ opt = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE)
+
+ opt.should be_an_instance_of(Socket::Option)
+ end
+
+ it 'returns a Socket::Option for a boolean option' do
+ opt = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR)
+
+ opt.bool.should == false
+ end
+
+ it 'returns a Socket::Option for a numeric option' do
+ opt = @sock.getsockopt(Socket::IPPROTO_IP, Socket::IP_TTL)
+
+ opt.int.should be_an_instance_of(Fixnum)
+ end
+
+ it 'returns a Socket::Option for a struct option' do
+ opt = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER)
+
+ opt.linger.should == [false, 0]
+ end
+
+ it 'raises Errno::ENOPROTOOPT when requesting an invalid option' do
+ lambda { @sock.getsockopt(Socket::SOL_SOCKET, -1) }.should raise_error(Errno::ENOPROTOOPT)
+ end
+
+ describe 'using Symbols as arguments' do
+ it 'returns a Socket::Option for arguments :SOCKET and :TYPE' do
+ opt = @sock.getsockopt(:SOCKET, :TYPE)
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_TYPE
+ end
+
+ it 'returns a Socket::Option for arguments :IP and :TTL' do
+ opt = @sock.getsockopt(:IP, :TTL)
+
+ opt.level.should == Socket::IPPROTO_IP
+ opt.optname.should == Socket::IP_TTL
+ end
+
+ it 'returns a Socket::Option for arguments :SOCKET and :REUSEADDR' do
+ opt = @sock.getsockopt(:SOCKET, :REUSEADDR)
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_REUSEADDR
+ end
+
+ it 'returns a Socket::Option for arguments :SOCKET and :LINGER' do
+ opt = @sock.getsockopt(:SOCKET, :LINGER)
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_LINGER
+ end
+
+ with_feature :udp_cork do
+ it 'returns a Socket::Option for arguments :UDP and :CORK' do
+ sock = Socket.new(:INET, :DGRAM)
+ begin
+ opt = sock.getsockopt(:UDP, :CORK)
+
+ opt.level.should == Socket::IPPROTO_UDP
+ opt.optname.should == Socket::UDP_CORK
+ ensure
+ sock.close
+ end
+ end
+ end
+ end
+
+ describe 'using Strings as arguments' do
+ it 'returns a Socket::Option for arguments "SOCKET" and "TYPE"' do
+ opt = @sock.getsockopt("SOCKET", "TYPE")
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_TYPE
+ end
+
+ it 'returns a Socket::Option for arguments "IP" and "TTL"' do
+ opt = @sock.getsockopt("IP", "TTL")
+
+ opt.level.should == Socket::IPPROTO_IP
+ opt.optname.should == Socket::IP_TTL
+ end
+
+ it 'returns a Socket::Option for arguments "SOCKET" and "REUSEADDR"' do
+ opt = @sock.getsockopt("SOCKET", "REUSEADDR")
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_REUSEADDR
+ end
+
+ it 'returns a Socket::Option for arguments "SOCKET" and "LINGER"' do
+ opt = @sock.getsockopt("SOCKET", "LINGER")
+
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_LINGER
+ end
+
+ with_feature :udp_cork do
+ it 'returns a Socket::Option for arguments "UDP" and "CORK"' do
+ sock = Socket.new("INET", "DGRAM")
+ begin
+ opt = sock.getsockopt("UDP", "CORK")
+
+ opt.level.should == Socket::IPPROTO_UDP
+ opt.optname.should == Socket::UDP_CORK
+ ensure
+ sock.close
+ end
+ end
+ end
+ end
+
+ describe 'using a String based option' do
+ it 'allows unpacking of a boolean option' do
+ opt = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR).to_s
+
+ opt.unpack('i').should == [0]
+ end
+
+ it 'allows unpacking of a numeric option' do
+ opt = @sock.getsockopt(Socket::IPPROTO_IP, Socket::IP_TTL).to_s
+ array = opt.unpack('i')
+
+ array[0].should be_an_instance_of(Fixnum)
+ array[0].should > 0
+ end
+
+ it 'allows unpacking of a struct option' do
+ opt = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER).to_s
+
+ if opt.bytesize == 8
+ opt.unpack('ii').should == [0, 0]
+ else
+ opt.unpack('i').should == [0]
+ end
+ end
+ end
end
diff --git a/spec/ruby/library/socket/basicsocket/ioctl_spec.rb b/spec/ruby/library/socket/basicsocket/ioctl_spec.rb
index 6ce314b6fb..615d92bea8 100644
--- a/spec/ruby/library/socket/basicsocket/ioctl_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/ioctl_spec.rb
@@ -1,5 +1,4 @@
-require_relative '../../../spec_helper'
-require 'socket'
+require_relative '../spec_helper'
describe "Socket::BasicSocket#ioctl" do
platform_is :linux do
diff --git a/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb b/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb
index 8fc0dce4f0..1b6027d26c 100644
--- a/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/recv_nonblock_spec.rb
@@ -1,7 +1,65 @@
-require_relative '../../../spec_helper'
-require_relative '../shared/recv_nonblock'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket::BasicSocket#recv_nonblock" do
- it_behaves_like :socket_recv_nonblock, :recv_nonblock
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before :each do
+ @s1 = Socket.new(family, :DGRAM)
+ @s2 = Socket.new(family, :DGRAM)
+ end
+
+ after :each do
+ @s1.close unless @s1.closed?
+ @s2.close unless @s2.closed?
+ end
+
+ platform_is_not :windows do
+ describe 'using an unbound socket' do
+ it 'raises an exception extending IO::WaitReadable' do
+ lambda { @s1.recv_nonblock(1) }.should raise_error(IO::WaitReadable)
+ end
+ end
+ end
+
+ it "raises an exception extending IO::WaitReadable if there's no data available" do
+ @s1.bind(Socket.pack_sockaddr_in(0, ip_address))
+ 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(0, ip_address))
+ @s2.send("aaa", 0, @s1.getsockname)
+ IO.select([@s1], nil, nil, 2)
+ @s1.recv_nonblock(5).should == "aaa"
+ end
+
+ it "allows an output buffer as third argument" do
+ @s1.bind(Socket.pack_sockaddr_in(0, ip_address))
+ @s2.send("data", 0, @s1.getsockname)
+ IO.select([@s1], nil, nil, 2)
+
+ buf = "foo"
+ @s1.recv_nonblock(5, 0, buf)
+ buf.should == "data"
+ end
+
+ it "does not block if there's no data available" do
+ @s1.bind(Socket.pack_sockaddr_in(0, ip_address))
+ @s2.send("a", 0, @s1.getsockname)
+ IO.select([@s1], nil, nil, 2)
+ @s1.recv_nonblock(1).should == "a"
+ lambda {
+ @s1.recv_nonblock(5)
+ }.should raise_error(IO::WaitReadable)
+ end
+ end
end
diff --git a/spec/ruby/library/socket/basicsocket/recv_spec.rb b/spec/ruby/library/socket/basicsocket/recv_spec.rb
index 7677d4ff8c..a277dc2d97 100644
--- a/spec/ruby/library/socket/basicsocket/recv_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/recv_spec.rb
@@ -1,5 +1,5 @@
# -*- encoding: binary -*-
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "BasicSocket#recv" do
@@ -92,3 +92,68 @@ describe "BasicSocket#recv" do
socket.close
end
end
+
+describe 'BasicSocket#recv' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :DGRAM)
+ @client = Socket.new(family, :DGRAM)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'using an unbound socket' do
+ it 'blocks the caller' do
+ lambda { @server.recv(4) }.should block_caller
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ describe 'without any data available' do
+ it 'blocks the caller' do
+ lambda { @server.recv(4) }.should block_caller
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'reads the given amount of bytes' do
+ @client.write('hello')
+
+ @server.recv(2).should == 'he'
+ end
+
+ it 'reads the given amount of bytes when it exceeds the data size' do
+ @client.write('he')
+
+ @server.recv(6).should == 'he'
+ end
+
+ it 'blocks the caller when called twice without new data being available' do
+ @client.write('hello')
+
+ @server.recv(2).should == 'he'
+
+ lambda { @server.recv(4) }.should block_caller
+ end
+
+ it 'takes a peek at the data when using the MSG_PEEK flag' do
+ @client.write('hello')
+
+ @server.recv(2, Socket::MSG_PEEK).should == 'he'
+ @server.recv(2).should == 'he'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/recvmsg_nonblock_spec.rb b/spec/ruby/library/socket/basicsocket/recvmsg_nonblock_spec.rb
new file mode 100644
index 0000000000..698b9e7ba5
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/recvmsg_nonblock_spec.rb
@@ -0,0 +1,204 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'BasicSocket#recvmsg_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a disconnected socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ describe 'using an unbound socket' do
+ it 'raises an exception extending IO::WaitReadable' do
+ lambda { @server.recvmsg_nonblock }.should raise_error(IO::WaitReadable)
+ end
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ describe 'without any data available' do
+ it 'raises an exception extending IO::WaitReadable' do
+ lambda { @server.recvmsg_nonblock }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.connect(@server.getsockname)
+
+ @client.write('hello')
+
+ IO.select([@server], nil, nil, 5)
+ end
+
+ it 'returns an Array containing the data, an Addrinfo and the flags' do
+ @server.recvmsg_nonblock.should be_an_instance_of(Array)
+ end
+
+ describe 'without a maximum message length' do
+ it 'reads all the available data' do
+ @server.recvmsg_nonblock[0].should == 'hello'
+ end
+ end
+
+ describe 'with a maximum message length' do
+ platform_is_not :windows do
+ it 'reads up to the maximum amount of bytes' do
+ @server.recvmsg_nonblock(2)[0].should == 'he'
+ end
+ end
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @server.recvmsg_nonblock
+ end
+
+ it 'stores the message at index 0' do
+ @array[0].should == 'hello'
+ end
+
+ it 'stores an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+
+ platform_is_not :windows do
+ it 'stores the flags at index 2' do
+ @array[2].should be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @array[1]
+ end
+
+ it 'uses the IP address of the client' do
+ @addr.ip_address.should == @client.local_address.ip_address
+ end
+
+ it 'uses the correct address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses the correct protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses the correct socket type' do
+ @addr.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses the port number of the client' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'using a connected socket' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without any data available' do
+ it 'raises IO::WaitReadable' do
+ lambda {
+ socket, _ = @server.accept
+ begin
+ socket.recvmsg_nonblock
+ ensure
+ socket.close
+ end
+ }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+
+ @socket, _ = @server.accept
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'returns an Array containing the data, an Addrinfo and the flags' do
+ @socket.recvmsg_nonblock.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @socket.recvmsg_nonblock
+ end
+
+ it 'stores the message at index 0' do
+ @array[0].should == 'hello'
+ end
+
+ it 'stores an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+
+ it 'stores the flags at index 2' do
+ @array[2].should be_an_instance_of(Fixnum)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @array[1]
+ end
+
+ it 'raises when receiving the ip_address message' do
+ lambda { @addr.ip_address }.should raise_error(SocketError)
+ end
+
+ it 'uses the correct address family' do
+ @addr.afamily.should == Socket::AF_UNSPEC
+ end
+
+ it 'uses 0 for the protocol family' do
+ @addr.pfamily.should == 0
+ end
+
+ it 'uses the correct socket type' do
+ @addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'raises when receiving the ip_port message' do
+ lambda { @addr.ip_port }.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/recvmsg_spec.rb b/spec/ruby/library/socket/basicsocket/recvmsg_spec.rb
new file mode 100644
index 0000000000..d7de83d72b
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/recvmsg_spec.rb
@@ -0,0 +1,197 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'BasicSocket#recvmsg' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a disconnected socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ describe 'using an unbound socket' do
+ it 'blocks the caller' do
+ lambda { @server.recvmsg }.should block_caller
+ end
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ describe 'without any data available' do
+ it 'blocks the caller' do
+ lambda { @server.recvmsg }.should block_caller
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.connect(@server.getsockname)
+
+ @client.write('hello')
+ end
+
+ it 'returns an Array containing the data, an Addrinfo and the flags' do
+ @server.recvmsg.should be_an_instance_of(Array)
+ end
+
+ describe 'without a maximum message length' do
+ it 'reads all the available data' do
+ @server.recvmsg[0].should == 'hello'
+ end
+ end
+
+ describe 'with a maximum message length' do
+ it 'reads up to the maximum amount of bytes' do
+ @server.recvmsg(2)[0].should == 'he'
+ end
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @server.recvmsg
+ end
+
+ it 'stores the message at index 0' do
+ @array[0].should == 'hello'
+ end
+
+ it 'stores an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+
+ platform_is_not :windows do
+ it 'stores the flags at index 2' do
+ @array[2].should be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @array[1]
+ end
+
+ it 'uses the IP address of the client' do
+ @addr.ip_address.should == @client.local_address.ip_address
+ end
+
+ it 'uses the correct address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses the correct protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses the correct socket type' do
+ @addr.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses the port number of the client' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'using a connected socket' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without any data available' do
+ it 'blocks the caller' do
+ socket, _ = @server.accept
+ begin
+ lambda { socket.recvmsg }.should block_caller
+ ensure
+ socket.close
+ end
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ @socket, _ = @server.accept
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'returns an Array containing the data, an Addrinfo and the flags' do
+ @socket.recvmsg.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @socket.recvmsg
+ end
+
+ it 'stores the message at index 0' do
+ @array[0].should == 'hello'
+ end
+
+ it 'stores an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+
+ it 'stores the flags at index 2' do
+ @array[2].should be_an_instance_of(Fixnum)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @array[1]
+ end
+
+ it 'raises when receiving the ip_address message' do
+ lambda { @addr.ip_address }.should raise_error(SocketError)
+ end
+
+ it 'uses the correct address family' do
+ @addr.afamily.should == Socket::AF_UNSPEC
+ end
+
+ it 'returns 0 for the protocol family' do
+ @addr.pfamily.should == 0
+ end
+
+ it 'uses the correct socket type' do
+ @addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'raises when receiving the ip_port message' do
+ lambda { @addr.ip_port }.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/send_spec.rb b/spec/ruby/library/socket/basicsocket/send_spec.rb
index e2e0ce592e..c8d2af8f7a 100644
--- a/spec/ruby/library/socket/basicsocket/send_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/send_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "BasicSocket#send" do
@@ -83,3 +83,132 @@ describe "BasicSocket#send" do
data.should == 'hello'
end
end
+
+describe 'BasicSocket#send' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a disconnected socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without a destination address' do
+ it "raises #{SocketSpecs.dest_addr_req_error}" do
+ lambda { @client.send('hello', 0) }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+
+ describe 'with a destination address as a String' do
+ it 'returns the amount of sent bytes' do
+ @client.send('hello', 0, @server.getsockname).should == 5
+ end
+
+ it 'does not persist the connection after writing to the socket' do
+ @client.send('hello', 0, @server.getsockname)
+
+ lambda { @client.send('hello', 0) }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+
+ describe 'with a destination address as an Addrinfo' do
+ it 'returns the amount of sent bytes' do
+ @client.send('hello', 0, @server.connect_address).should == 5
+ end
+ end
+ end
+
+ describe 'using a connected UDP socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without a destination address argument' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'returns the amount of bytes written' do
+ @client.send('hello', 0).should == 5
+ end
+ end
+
+ describe 'with a destination address argument' do
+ before do
+ @alt_server = Socket.new(family, :DGRAM)
+
+ @alt_server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @alt_server.close
+ end
+
+ it 'sends the message to the given address instead' do
+ @client.send('hello', 0, @alt_server.getsockname).should == 5
+
+ lambda { @server.recv(5) }.should block_caller
+
+ @alt_server.recv(5).should == 'hello'
+ end
+
+ it 'does not persist the alternative connection after writing to the socket' do
+ @client.send('hello', 0, @alt_server.getsockname)
+
+ @client.connect(@server.getsockname)
+ @client.send('world', 0)
+
+ @server.recv(5).should == 'world'
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'using a connected TCP socket' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'using the MSG_OOB flag' do
+ it 'sends an out-of-band message' do
+ @server.setsockopt(:SOCKET, :OOBINLINE, true)
+
+ @client.send('a', Socket::MSG_OOB).should == 1
+
+ socket, _ = @server.accept
+ begin
+ socket.recv(1, Socket::MSG_OOB).should == 'a'
+ ensure
+ socket.close
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/sendmsg_nonblock_spec.rb b/spec/ruby/library/socket/basicsocket/sendmsg_nonblock_spec.rb
new file mode 100644
index 0000000000..de5e2aa749
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/sendmsg_nonblock_spec.rb
@@ -0,0 +1,104 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'BasicSocket#sendmsg_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a disconnected socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without a destination address' do
+ it "raises #{SocketSpecs.dest_addr_req_error}" do
+ lambda { @client.sendmsg_nonblock('hello') }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+
+ describe 'with a destination address as a String' do
+ it 'returns the amount of sent bytes' do
+ @client.sendmsg_nonblock('hello', 0, @server.getsockname).should == 5
+ end
+ end
+
+ describe 'with a destination address as an Addrinfo' do
+ it 'returns the amount of sent bytes' do
+ @client.sendmsg_nonblock('hello', 0, @server.connect_address).should == 5
+ end
+ end
+ end
+
+ describe 'using a connected UDP socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without a destination address argument' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'returns the amount of bytes written' do
+ @client.sendmsg_nonblock('hello').should == 5
+ end
+ end
+
+ describe 'with a destination address argument' do
+ before do
+ @alt_server = Socket.new(family, :DGRAM)
+ @alt_server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @alt_server.close
+ end
+
+ it 'sends the message to the given address instead' do
+ @client.sendmsg_nonblock('hello', 0, @alt_server.getsockname).should == 5
+ lambda { @server.recv(5) }.should block_caller
+ @alt_server.recv(5).should == 'hello'
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'using a connected TCP socket' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'raises IO::WaitWritable when the underlying buffer is full' do
+ lambda {
+ 10.times { @client.sendmsg_nonblock('hello' * 1_000_000) }
+ }.should raise_error(IO::WaitWritable)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/sendmsg_spec.rb b/spec/ruby/library/socket/basicsocket/sendmsg_spec.rb
new file mode 100644
index 0000000000..f2c11f443a
--- /dev/null
+++ b/spec/ruby/library/socket/basicsocket/sendmsg_spec.rb
@@ -0,0 +1,111 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'BasicSocket#sendmsg' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a disconnected socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ describe 'without a destination address' do
+ it "raises #{SocketSpecs.dest_addr_req_error}" do
+ lambda { @client.sendmsg('hello') }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+ end
+
+ describe 'with a destination address as a String' do
+ it 'returns the amount of sent bytes' do
+ @client.sendmsg('hello', 0, @server.getsockname).should == 5
+ end
+ end
+
+ describe 'with a destination address as an Addrinfo' do
+ it 'returns the amount of sent bytes' do
+ @client.sendmsg('hello', 0, @server.connect_address).should == 5
+ end
+ end
+ end
+
+ describe 'using a connected UDP socket' do
+ before do
+ @client = Socket.new(family, :DGRAM)
+ @server = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without a destination address argument' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'returns the amount of bytes written' do
+ @client.sendmsg('hello').should == 5
+ end
+ end
+
+ describe 'with a destination address argument' do
+ before do
+ @alt_server = Socket.new(family, :DGRAM)
+
+ @alt_server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @alt_server.close
+ end
+
+ it 'sends the message to the given address instead' do
+ @client.sendmsg('hello', 0, @alt_server.getsockname).should == 5
+
+ lambda { @server.recv(5) }.should block_caller
+
+ @alt_server.recv(5).should == 'hello'
+ end
+ end
+ end
+
+ platform_is_not :windows do # spurious
+ describe 'using a connected TCP socket' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'blocks when the underlying buffer is full' do
+ # Buffer sizes may differ per platform, so sadly this is the only
+ # reliable way of testing blocking behaviour.
+ lambda do
+ 10.times { @client.sendmsg('hello' * 1_000_000) }
+ end.should block_caller
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/setsockopt_spec.rb b/spec/ruby/library/socket/basicsocket/setsockopt_spec.rb
index 7d2a303699..cafcfc202a 100644
--- a/spec/ruby/library/socket/basicsocket/setsockopt_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/setsockopt_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "BasicSocket#setsockopt" do
@@ -211,3 +211,124 @@ describe "BasicSocket#setsockopt" do
end
end
end
+
+describe 'BasicSocket#setsockopt' do
+ describe 'using a STREAM socket' do
+ before do
+ @socket = Socket.new(:INET, :STREAM)
+ end
+
+ after do
+ @socket.close
+ end
+
+ describe 'using separate arguments with Symbols' do
+ it 'raises TypeError when the first argument is nil' do
+ lambda { @socket.setsockopt(nil, :REUSEADDR, true) }.should raise_error(TypeError)
+ end
+
+ it 'sets a boolean option' do
+ @socket.setsockopt(:SOCKET, :REUSEADDR, true).should == 0
+ @socket.getsockopt(:SOCKET, :REUSEADDR).bool.should == true
+ end
+
+ it 'sets an integer option' do
+ @socket.setsockopt(:IP, :TTL, 255).should == 0
+ @socket.getsockopt(:IP, :TTL).int.should == 255
+ end
+
+ it 'sets an IPv6 boolean option' do
+ socket = Socket.new(:INET6, :STREAM)
+ begin
+ socket.setsockopt(:IPV6, :V6ONLY, true).should == 0
+ socket.getsockopt(:IPV6, :V6ONLY).bool.should == true
+ ensure
+ socket.close
+ end
+ end
+
+ platform_is_not :windows do
+ it 'raises Errno::EINVAL when setting an invalid option value' do
+ lambda { @socket.setsockopt(:SOCKET, :OOBINLINE, 'bla') }.should raise_error(Errno::EINVAL)
+ end
+ end
+ end
+
+ describe 'using separate arguments with Symbols' do
+ it 'sets a boolean option' do
+ @socket.setsockopt('SOCKET', 'REUSEADDR', true).should == 0
+ @socket.getsockopt(:SOCKET, :REUSEADDR).bool.should == true
+ end
+
+ it 'sets an integer option' do
+ @socket.setsockopt('IP', 'TTL', 255).should == 0
+ @socket.getsockopt(:IP, :TTL).int.should == 255
+ end
+ end
+
+ describe 'using separate arguments with constants' do
+ it 'sets a boolean option' do
+ @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true).should == 0
+ @socket.getsockopt(:SOCKET, :REUSEADDR).bool.should == true
+ end
+
+ it 'sets an integer option' do
+ @socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_TTL, 255).should == 0
+ @socket.getsockopt(:IP, :TTL).int.should == 255
+ end
+ end
+
+ describe 'using separate arguments with custom objects' do
+ it 'sets a boolean option' do
+ level = mock(:level)
+ name = mock(:name)
+
+ level.stub!(:to_str).and_return('SOCKET')
+ name.stub!(:to_str).and_return('REUSEADDR')
+
+ @socket.setsockopt(level, name, true).should == 0
+ end
+ end
+
+ describe 'using a Socket::Option as the first argument' do
+ it 'sets a boolean option' do
+ @socket.setsockopt(Socket::Option.bool(:INET, :SOCKET, :REUSEADDR, true)).should == 0
+ @socket.getsockopt(:SOCKET, :REUSEADDR).bool.should == true
+ end
+
+ it 'sets an integer option' do
+ @socket.setsockopt(Socket::Option.int(:INET, :IP, :TTL, 255)).should == 0
+ @socket.getsockopt(:IP, :TTL).int.should == 255
+ end
+
+ it 'raises ArgumentError when passing 2 arguments' do
+ option = Socket::Option.bool(:INET, :SOCKET, :REUSEADDR, true)
+ lambda { @socket.setsockopt(option, :REUSEADDR) }.should raise_error(ArgumentError)
+ end
+
+ it 'raises TypeError when passing 3 arguments' do
+ option = Socket::Option.bool(:INET, :SOCKET, :REUSEADDR, true)
+ lambda { @socket.setsockopt(option, :REUSEADDR, true) }.should raise_error(TypeError)
+ end
+ end
+ end
+
+ with_feature :unix_socket do
+ describe 'using a UNIX socket' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close
+ rm_r @path
+ end
+
+ it 'sets a boolean option' do
+ @server.setsockopt(:SOCKET, :REUSEADDR, true)
+ @server.getsockopt(:SOCKET, :REUSEADDR).bool.should == true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/basicsocket/shutdown_spec.rb b/spec/ruby/library/socket/basicsocket/shutdown_spec.rb
index 67b15908e5..40e2d77570 100644
--- a/spec/ruby/library/socket/basicsocket/shutdown_spec.rb
+++ b/spec/ruby/library/socket/basicsocket/shutdown_spec.rb
@@ -1,6 +1,155 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-describe "Socket::BasicSocket#shutdown" do
+platform_is_not :windows do # hangs
+ describe "Socket::BasicSocket#shutdown" do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM)
+ @client = Socket.new(family, :STREAM)
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'using a Fixnum' do
+ it 'shuts down a socket for reading' do
+ @client.shutdown(Socket::SHUT_RD)
+
+ @client.recv(1).should be_empty
+ end
+
+ it 'shuts down a socket for writing' do
+ @client.shutdown(Socket::SHUT_WR)
+
+ lambda { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'shuts down a socket for reading and writing' do
+ @client.shutdown(Socket::SHUT_RDWR)
+
+ @client.recv(1).should be_empty
+
+ lambda { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'raises ArgumentError when using an invalid option' do
+ lambda { @server.shutdown(666) }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe 'using a Symbol' do
+ it 'shuts down a socket for reading using :RD' do
+ @client.shutdown(:RD)
+
+ @client.recv(1).should be_empty
+ end
+
+ it 'shuts down a socket for reading using :SHUT_RD' do
+ @client.shutdown(:SHUT_RD)
+
+ @client.recv(1).should be_empty
+ end
+
+ it 'shuts down a socket for writing using :WR' do
+ @client.shutdown(:WR)
+
+ lambda { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'shuts down a socket for writing using :SHUT_WR' do
+ @client.shutdown(:SHUT_WR)
+
+ lambda { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'shuts down a socket for reading and writing' do
+ @client.shutdown(:RDWR)
+
+ @client.recv(1).should be_empty
+
+ lambda { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'raises ArgumentError when using an invalid option' do
+ lambda { @server.shutdown(:Nope) }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using a String' do
+ it 'shuts down a socket for reading using "RD"' do
+ @client.shutdown('RD')
+
+ @client.recv(1).should be_empty
+ end
+
+ it 'shuts down a socket for reading using "SHUT_RD"' do
+ @client.shutdown('SHUT_RD')
+
+ @client.recv(1).should be_empty
+ end
+
+ it 'shuts down a socket for writing using "WR"' do
+ @client.shutdown('WR')
+
+ lambda { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'shuts down a socket for writing using "SHUT_WR"' do
+ @client.shutdown('SHUT_WR')
+
+ lambda { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+
+ it 'raises ArgumentError when using an invalid option' do
+ lambda { @server.shutdown('Nope') }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using an object that responds to #to_str' do
+ before do
+ @dummy = mock(:dummy)
+ end
+
+ it 'shuts down a socket for reading using "RD"' do
+ @dummy.stub!(:to_str).and_return('RD')
+
+ @client.shutdown(@dummy)
+
+ @client.recv(1).should be_empty
+ end
+
+ it 'shuts down a socket for reading using "SHUT_RD"' do
+ @dummy.stub!(:to_str).and_return('SHUT_RD')
+
+ @client.shutdown(@dummy)
+
+ @client.recv(1).should be_empty
+ end
+
+ it 'shuts down a socket for reading and writing' do
+ @dummy.stub!(:to_str).and_return('RDWR')
+
+ @client.shutdown(@dummy)
+
+ @client.recv(1).should be_empty
+
+ lambda { @client.write('hello') }.should raise_error(Errno::EPIPE)
+ end
+ end
+
+ describe 'using an object that does not respond to #to_str' do
+ it 'raises TypeError' do
+ lambda { @server.shutdown(mock(:dummy)) }.should raise_error(TypeError)
+ end
+ end
+ end
+ end
end
diff --git a/spec/ruby/library/socket/constants/constants_spec.rb b/spec/ruby/library/socket/constants/constants_spec.rb
index 7e87aff2ee..2d44636abd 100644
--- a/spec/ruby/library/socket/constants/constants_spec.rb
+++ b/spec/ruby/library/socket/constants/constants_spec.rb
@@ -1,6 +1,5 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-include Socket::Constants
describe "Socket::Constants" do
it "defines socket types" do
@@ -87,4 +86,17 @@ describe "Socket::Constants" do
Socket::Constants.should have_constant(c)
end
end
+
+ platform_is_not :windows do
+ it 'defines SCM options' do
+ Socket::Constants.should have_constant('SCM_CREDENTIALS')
+ end
+
+ it 'defines error options' do
+ consts = ["EAI_ADDRFAMILY", "EAI_NODATA"]
+ consts.each do |c|
+ Socket::Constants.should have_constant(c)
+ end
+ end
+ end
end
diff --git a/spec/ruby/library/socket/fixtures/classes.rb b/spec/ruby/library/socket/fixtures/classes.rb
index b8e5d2a38d..1098b04a5e 100644
--- a/spec/ruby/library/socket/fixtures/classes.rb
+++ b/spec/ruby/library/socket/fixtures/classes.rb
@@ -1,16 +1,16 @@
require 'socket'
module SocketSpecs
- # helper to get the hostname associated to 127.0.0.1
- def self.hostname
+ # helper to get the hostname associated to 127.0.0.1 or the given ip
+ def self.hostname(ip = "127.0.0.1")
# Calculate each time, without caching, since the result might
# depend on things like do_not_reverse_lookup mode, which is
# changing from test to test
- Socket.getaddrinfo("127.0.0.1", nil)[0][2]
+ Socket.getaddrinfo(ip, nil)[0][2]
end
- def self.hostnamev6
- Socket.getaddrinfo("::1", nil)[0][2]
+ def self.hostname_reverse_lookup(ip = "127.0.0.1")
+ Socket.getaddrinfo(ip, nil, 0, 0, 0, 0, true)[0][2]
end
def self.addr(which=:ipv4)
@@ -47,6 +47,61 @@ module SocketSpecs
File.delete(path) if File.exist?(path)
end
+ def self.ipv6_available?
+ @ipv6_available ||= begin
+ server = TCPServer.new('::1', 0)
+ rescue Errno::EADDRNOTAVAIL
+ :no
+ else
+ server.close
+ :yes
+ end
+ @ipv6_available == :yes
+ end
+
+ def self.each_ip_protocol
+ describe 'using IPv4' do
+ yield Socket::AF_INET, '127.0.0.1', 'AF_INET'
+ end
+
+ guard -> { SocketSpecs.ipv6_available? } do
+ describe 'using IPv6' do
+ yield Socket::AF_INET6, '::1', 'AF_INET6'
+ end
+ end
+ end
+
+ def self.loop_with_timeout(timeout = 5)
+ require 'timeout'
+ time = Time.now
+
+ loop do
+ if Time.now - time >= timeout
+ raise TimeoutError, "Did not succeed within #{timeout} seconds"
+ end
+
+ sleep 0.01 # necessary on OSX; don't know why
+ yield
+ end
+ end
+
+ def self.wait_until_success(timeout = 5)
+ loop_with_timeout(timeout) do
+ begin
+ return yield
+ rescue
+ end
+ end
+ end
+
+ def self.dest_addr_req_error
+ error = Errno::EDESTADDRREQ
+ platform_is :windows do
+ error = Errno::ENOTCONN
+ end
+ error
+ end
+
# TCPServer echo server accepting one connection
class SpecTCPServer
attr_reader :hostname, :port
diff --git a/spec/ruby/library/socket/ipsocket/addr_spec.rb b/spec/ruby/library/socket/ipsocket/addr_spec.rb
index 2e5ae51d15..07f4cb20c9 100644
--- a/spec/ruby/library/socket/ipsocket/addr_spec.rb
+++ b/spec/ruby/library/socket/ipsocket/addr_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket::IPSocket#addr" do
@@ -40,3 +40,66 @@ describe "Socket::IPSocket#addr" do
addrinfo[3].should == "127.0.0.1"
end
end
+
+describe 'Socket::IPSocket#addr' do
+ SocketSpecs.each_ip_protocol do |family, ip_address, family_name|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'without reverse lookups' do
+ before do
+ @hostname = Socket.getaddrinfo(ip_address, nil)[0][2]
+ end
+
+ it 'returns an Array containing address information' do
+ @server.addr.should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'with reverse lookups' do
+ before do
+ @hostname = Socket.getaddrinfo(ip_address, nil, nil, 0, 0, 0, true)[0][2]
+ end
+
+ describe 'using true as the argument' do
+ it 'returns an Array containing address information' do
+ @server.addr(true).should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'using :hostname as the argument' do
+ it 'returns an Array containing address information' do
+ @server.addr(:hostname).should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'using :cats as the argument' do
+ it 'raises ArgumentError' do
+ lambda { @server.addr(:cats) }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe 'with do_not_reverse_lookup disabled on socket level' do
+ before do
+ @server.do_not_reverse_lookup = false
+
+ @hostname = Socket.getaddrinfo(ip_address, nil, nil, 0, 0, 0, true)[0][2]
+ end
+
+ after do
+ @server.do_not_reverse_lookup = true
+ end
+
+ it 'returns an Array containing address information' do
+ @server.addr.should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ipsocket/getaddress_spec.rb b/spec/ruby/library/socket/ipsocket/getaddress_spec.rb
index 10161dbaea..2ee35af5b5 100644
--- a/spec/ruby/library/socket/ipsocket/getaddress_spec.rb
+++ b/spec/ruby/library/socket/ipsocket/getaddress_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket::IPSocket#getaddress" do
@@ -11,6 +11,7 @@ describe "Socket::IPSocket#getaddress" do
it "returns the IP address when passed an IP" do
IPSocket.getaddress("127.0.0.1").should == "127.0.0.1"
IPSocket.getaddress("0.0.0.0").should == "0.0.0.0"
+ IPSocket.getaddress('::1').should == '::1'
end
# There is no way to make this fail-proof on all machines, because
@@ -23,5 +24,4 @@ describe "Socket::IPSocket#getaddress" do
}.should raise_error(SocketError)
end
end
-
end
diff --git a/spec/ruby/library/socket/ipsocket/peeraddr_spec.rb b/spec/ruby/library/socket/ipsocket/peeraddr_spec.rb
index bfbe0b2c12..26aa61d1c4 100644
--- a/spec/ruby/library/socket/ipsocket/peeraddr_spec.rb
+++ b/spec/ruby/library/socket/ipsocket/peeraddr_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket::IPSocket#peeraddr" do
@@ -49,3 +49,69 @@ describe "Socket::IPSocket#peeraddr" do
addrinfo[3].should == "127.0.0.1"
end
end
+
+describe 'Socket::IPSocket#peeraddr' do
+ SocketSpecs.each_ip_protocol do |family, ip_address, family_name|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ @client = TCPSocket.new(ip_address, @port)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'without reverse lookups' do
+ before do
+ @hostname = Socket.getaddrinfo(ip_address, nil)[0][2]
+ end
+
+ it 'returns an Array containing address information' do
+ @client.peeraddr.should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'with reverse lookups' do
+ before do
+ @hostname = Socket.getaddrinfo(ip_address, nil, nil, 0, 0, 0, true)[0][2]
+ end
+
+ describe 'using true as the argument' do
+ it 'returns an Array containing address information' do
+ @client.peeraddr(true).should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'using :hostname as the argument' do
+ it 'returns an Array containing address information' do
+ @client.peeraddr(:hostname).should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+
+ describe 'using :cats as the argument' do
+ it 'raises ArgumentError' do
+ lambda { @client.peeraddr(:cats) }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe 'with do_not_reverse_lookup disabled on socket level' do
+ before do
+ @client.do_not_reverse_lookup = false
+
+ @hostname = Socket.getaddrinfo(ip_address, nil, nil, 0, 0, 0, true)[0][2]
+ @port = @client.local_address.ip_port
+ end
+
+ after do
+ @client.do_not_reverse_lookup = true
+ end
+
+ it 'returns an Array containing address information' do
+ @client.addr.should == [family_name, @port, @hostname, ip_address]
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/ipsocket/recvfrom_spec.rb b/spec/ruby/library/socket/ipsocket/recvfrom_spec.rb
index b8537fbe47..3bcb7b8f02 100644
--- a/spec/ruby/library/socket/ipsocket/recvfrom_spec.rb
+++ b/spec/ruby/library/socket/ipsocket/recvfrom_spec.rb
@@ -1,8 +1,7 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket::IPSocket#recvfrom" do
-
before :each do
@server = TCPServer.new("127.0.0.1", 0)
@port = @server.addr[1]
@@ -68,5 +67,57 @@ describe "Socket::IPSocket#recvfrom" do
# This does not apply to every platform, dependant on recvfrom(2)
# data.last.should == nil
end
+end
+
+describe 'Socket::IPSocket#recvfrom' do
+ SocketSpecs.each_ip_protocol do |family, ip_address, family_name|
+ before do
+ @server = UDPSocket.new(family)
+ @client = UDPSocket.new(family)
+
+ @server.bind(ip_address, 0)
+ @client.connect(ip_address, @server.connect_address.ip_port)
+
+ @hostname = Socket.getaddrinfo(ip_address, nil)[0][2]
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns an Array containing up to N bytes and address information' do
+ @client.write('hello')
+
+ port = @client.local_address.ip_port
+ ret = @server.recvfrom(2)
+
+ ret.should == ['he', [family_name, port, @hostname, ip_address]]
+ end
+ it 'allows specifying of flags when receiving data' do
+ @client.write('hello')
+
+ @server.recvfrom(2, Socket::MSG_PEEK)[0].should == 'he'
+
+ @server.recvfrom(2)[0].should == 'he'
+ end
+
+ describe 'using reverse lookups' do
+ before do
+ @server.do_not_reverse_lookup = false
+
+ @hostname = Socket.getaddrinfo(ip_address, nil, 0, 0, 0, 0, true)[0][2]
+ end
+
+ it 'includes the hostname in the address Array' do
+ @client.write('hello')
+
+ port = @client.local_address.ip_port
+ ret = @server.recvfrom(2)
+
+ ret.should == ['he', [family_name, port, @hostname, ip_address]]
+ end
+ end
+ end
end
diff --git a/spec/ruby/library/socket/option/bool_spec.rb b/spec/ruby/library/socket/option/bool_spec.rb
index 31be00fee3..c4f8a277ba 100644
--- a/spec/ruby/library/socket/option/bool_spec.rb
+++ b/spec/ruby/library/socket/option/bool_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket::Option.bool" do
@@ -18,8 +18,10 @@ describe "Socket::Option#bool" do
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)
+ platform_is_not :windows do
+ it 'raises TypeError when called on a non boolean option' do
+ opt = Socket::Option.linger(1, 4)
+ lambda { opt.bool }.should raise_error(TypeError)
+ end
end
end
diff --git a/spec/ruby/library/socket/option/initialize_spec.rb b/spec/ruby/library/socket/option/initialize_spec.rb
new file mode 100644
index 0000000000..986cfa8ad4
--- /dev/null
+++ b/spec/ruby/library/socket/option/initialize_spec.rb
@@ -0,0 +1,83 @@
+require_relative '../spec_helper'
+
+describe 'Socket::Option#initialize' do
+ before do
+ @bool = [0].pack('i')
+ end
+
+ describe 'using Fixnums' do
+ it 'returns a Socket::Option' do
+ opt = Socket::Option
+ .new(Socket::AF_INET, Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, @bool)
+
+ opt.should be_an_instance_of(Socket::Option)
+
+ opt.family.should == Socket::AF_INET
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_KEEPALIVE
+ opt.data.should == @bool
+ end
+ end
+
+ describe 'using Symbols' do
+ it 'returns a Socket::Option' do
+ opt = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, @bool)
+
+ opt.should be_an_instance_of(Socket::Option)
+
+ opt.family.should == Socket::AF_INET
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_KEEPALIVE
+ opt.data.should == @bool
+ end
+
+ it 'raises when using an invalid address family' do
+ lambda {
+ Socket::Option.new(:INET2, :SOCKET, :KEEPALIVE, @bool)
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises when using an invalid level' do
+ lambda {
+ Socket::Option.new(:INET, :CATS, :KEEPALIVE, @bool)
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises when using an invalid option name' do
+ lambda {
+ Socket::Option.new(:INET, :SOCKET, :CATS, @bool)
+ }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using Strings' do
+ it 'returns a Socket::Option' do
+ opt = Socket::Option.new('INET', 'SOCKET', 'KEEPALIVE', @bool)
+
+ opt.should be_an_instance_of(Socket::Option)
+
+ opt.family.should == Socket::AF_INET
+ opt.level.should == Socket::SOL_SOCKET
+ opt.optname.should == Socket::SO_KEEPALIVE
+ opt.data.should == @bool
+ end
+
+ it 'raises when using an invalid address family' do
+ lambda {
+ Socket::Option.new('INET2', 'SOCKET', 'KEEPALIVE', @bool)
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises when using an invalid level' do
+ lambda {
+ Socket::Option.new('INET', 'CATS', 'KEEPALIVE', @bool)
+ }.should raise_error(SocketError)
+ end
+
+ it 'raises when using an invalid option name' do
+ lambda {
+ Socket::Option.new('INET', 'SOCKET', 'CATS', @bool)
+ }.should raise_error(SocketError)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/option/inspect_spec.rb b/spec/ruby/library/socket/option/inspect_spec.rb
index 2a03421710..ebea940d2f 100644
--- a/spec/ruby/library/socket/option/inspect_spec.rb
+++ b/spec/ruby/library/socket/option/inspect_spec.rb
@@ -1,7 +1,6 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-require 'socket'
describe 'Socket::Option#inspect' do
it 'correctly returns SO_LINGER value' do
diff --git a/spec/ruby/library/socket/option/int_spec.rb b/spec/ruby/library/socket/option/int_spec.rb
index b270374f62..5c67ec26d8 100644
--- a/spec/ruby/library/socket/option/int_spec.rb
+++ b/spec/ruby/library/socket/option/int_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket::Option.int" do
@@ -10,6 +10,17 @@ describe "Socket::Option.int" do
so.optname.should == Socket::Constants::SO_KEEPALIVE
so.data.should == [5].pack('i')
end
+
+ it 'returns a Socket::Option' do
+ opt = Socket::Option.int(:INET, :IP, :TTL, 4)
+
+ opt.should be_an_instance_of(Socket::Option)
+
+ opt.family.should == Socket::AF_INET
+ opt.level.should == Socket::IPPROTO_IP
+ opt.optname.should == Socket::IP_TTL
+ opt.data.should == [4].pack('i')
+ end
end
describe "Socket::Option#int" do
@@ -19,10 +30,14 @@ describe "Socket::Option#int" do
so = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 32765)
so.int.should == 32765
+
+ Socket::Option.int(:INET, :IP, :TTL, 4).int.should == 4
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)
+ platform_is_not :windows do
+ it 'raises TypeError when called on a non integer option' do
+ opt = Socket::Option.linger(1, 4)
+ lambda { opt.int }.should raise_error(TypeError)
+ end
end
end
diff --git a/spec/ruby/library/socket/option/linger_spec.rb b/spec/ruby/library/socket/option/linger_spec.rb
index 2090c8a0e8..94467ebf71 100644
--- a/spec/ruby/library/socket/option/linger_spec.rb
+++ b/spec/ruby/library/socket/option/linger_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
option_pack = 'i*'
@@ -10,9 +10,11 @@ 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
@@ -53,10 +55,22 @@ describe "Socket::Option#linger" do
lambda { so.linger }.should raise_error(TypeError)
end
+ it 'raises TypeError when called on a non SOL_SOCKET/SO_LINGER option' do
+ opt = Socket::Option.int(:INET, :IP, :TTL, 4)
+
+ lambda { opt.linger }.should raise_error(TypeError)
+ end
+
platform_is_not :windows do
it "raises TypeError if option has not good size" do
so = Socket::Option.int(:AF_UNSPEC, :SOL_SOCKET, :LINGER, 1)
lambda { so.linger }.should raise_error(TypeError)
end
end
+
+ it 'raises TypeError when called on a non linger option' do
+ opt = Socket::Option.new(:INET, :SOCKET, :LINGER, '')
+
+ lambda { opt.linger }.should raise_error(TypeError)
+ end
end
diff --git a/spec/ruby/library/socket/option/new_spec.rb b/spec/ruby/library/socket/option/new_spec.rb
index b2c76fb24d..f3b7b31c91 100644
--- a/spec/ruby/library/socket/option/new_spec.rb
+++ b/spec/ruby/library/socket/option/new_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket::Option.new" do
diff --git a/spec/ruby/library/socket/shared/pack_sockaddr.rb b/spec/ruby/library/socket/shared/pack_sockaddr.rb
index 4ffa02a8d8..714becc488 100644
--- a/spec/ruby/library/socket/shared/pack_sockaddr.rb
+++ b/spec/ruby/library/socket/shared/pack_sockaddr.rb
@@ -18,10 +18,28 @@ describe :socket_pack_sockaddr_in, shared: true do
sockaddr_in = Socket.public_send(@method, nil, '127.0.0.1')
Socket.unpack_sockaddr_in(sockaddr_in).should == [0, '127.0.0.1']
end
+
+ describe 'using an IPv4 address' do
+ it 'returns a String of 16 bytes' do
+ str = Socket.public_send(@method, 80, '127.0.0.1')
+
+ str.should be_an_instance_of(String)
+ str.bytesize.should == 16
+ end
+ end
+
+ describe 'using an IPv6 address' do
+ it 'returns a String of 28 bytes' do
+ str = Socket.public_send(@method, 80, '::1')
+
+ str.should be_an_instance_of(String)
+ str.bytesize.should == 28
+ end
+ end
end
describe :socket_pack_sockaddr_un, shared: true do
- platform_is_not :windows do
+ with_feature :unix_socket 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]
@@ -40,10 +58,28 @@ describe :socket_pack_sockaddr_un, shared: true do
end
end
+ platform_is :linux do
+ it 'returns a String of 110 bytes' do
+ str = Socket.public_send(@method, '/tmp/test.sock')
+
+ str.should be_an_instance_of(String)
+ str.bytesize.should == 110
+ end
+ end
+
+ platform_is :bsd do
+ it 'returns a String of 106 bytes' do
+ str = Socket.public_send(@method, '/tmp/test.sock')
+
+ str.should be_an_instance_of(String)
+ str.bytesize.should == 106
+ end
+ end
+
platform_is_not :windows, :aix do
- it "raises if path length exceeds max size" do
+ it "raises ArgumentError for paths that are too long" do
# AIX doesn't raise error
- long_path = Array.new(512, 0).join
+ long_path = 'a' * 110
lambda { Socket.public_send(@method, long_path) }.should raise_error(ArgumentError)
end
end
diff --git a/spec/ruby/library/socket/shared/recv_nonblock.rb b/spec/ruby/library/socket/shared/recv_nonblock.rb
deleted file mode 100644
index 2b584d52a2..0000000000
--- a/spec/ruby/library/socket/shared/recv_nonblock.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-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(0, "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(0, "127.0.0.1"))
- @s2.send("aaa", 0, @s1.getsockname)
- IO.select([@s1], nil, nil, 2)
- @s1.recv_nonblock(5).should == "aaa"
- end
-
- it "allows an output buffer as third argument" do
- @s1.bind(Socket.pack_sockaddr_in(0, "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
-
- it "does not block if there's no data available" do
- @s1.bind(Socket.pack_sockaddr_in(0, "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/ruby/library/socket/shared/socketpair.rb b/spec/ruby/library/socket/shared/socketpair.rb
index 03ee0e1a52..1e08deccc2 100644
--- a/spec/ruby/library/socket/shared/socketpair.rb
+++ b/spec/ruby/library/socket/shared/socketpair.rb
@@ -19,5 +19,120 @@ describe :socket_socketpair, shared: true do
s2.close
end
end
+
+ describe 'using a Fixnum as the 1st and 2nd argument' do
+ it 'returns two Socket objects' do
+ s1, s2 = Socket.public_send(@method, Socket::AF_UNIX, Socket::SOCK_STREAM)
+
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ s1.close
+ s2.close
+ end
+ end
+
+ describe 'using a Symbol as the 1st and 2nd argument' do
+ it 'returns two Socket objects' do
+ s1, s2 = Socket.public_send(@method, :UNIX, :STREAM)
+
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ s1.close
+ s2.close
+ end
+
+ it 'raises SocketError for an unknown address family' do
+ lambda { Socket.public_send(@method, :CATS, :STREAM) }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError for an unknown socket type' do
+ lambda { Socket.public_send(@method, :UNIX, :CATS) }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using a String as the 1st and 2nd argument' do
+ it 'returns two Socket objects' do
+ s1, s2 = Socket.public_send(@method, 'UNIX', 'STREAM')
+
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ s1.close
+ s2.close
+ end
+
+ it 'raises SocketError for an unknown address family' do
+ lambda { Socket.public_send(@method, 'CATS', 'STREAM') }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError for an unknown socket type' do
+ lambda { Socket.public_send(@method, 'UNIX', 'CATS') }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'using an object that responds to #to_str as the 1st and 2nd argument' do
+ it 'returns two Socket objects' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return('UNIX')
+ type.stub!(:to_str).and_return('STREAM')
+
+ s1, s2 = Socket.public_send(@method, family, type)
+
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ s1.close
+ s2.close
+ end
+
+ it 'raises TypeError when #to_str does not return a String' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return(Socket::AF_UNIX)
+ type.stub!(:to_str).and_return(Socket::SOCK_STREAM)
+
+ lambda { Socket.public_send(@method, family, type) }.should raise_error(TypeError)
+ end
+
+ it 'raises SocketError for an unknown address family' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return('CATS')
+ type.stub!(:to_str).and_return('STREAM')
+
+ lambda { Socket.public_send(@method, family, type) }.should raise_error(SocketError)
+ end
+
+ it 'raises SocketError for an unknown socket type' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return('UNIX')
+ type.stub!(:to_str).and_return('CATS')
+
+ lambda { Socket.public_send(@method, family, type) }.should raise_error(SocketError)
+ end
+ end
+
+ it 'accepts a custom protocol as a Fixnum as the 3rd argument' do
+ s1, s2 = Socket.public_send(@method, :UNIX, :STREAM, Socket::IPPROTO_IP)
+ s1.should be_an_instance_of(Socket)
+ s2.should be_an_instance_of(Socket)
+ s1.close
+ s2.close
+ end
+
+ it 'connects the returned Socket objects' do
+ s1, s2 = Socket.public_send(@method, :UNIX, :STREAM)
+ begin
+ s1.write('hello')
+ s2.recv(5).should == 'hello'
+ ensure
+ s1.close
+ s2.close
+ end
+ end
end
end
diff --git a/spec/ruby/library/socket/socket/accept_loop_spec.rb b/spec/ruby/library/socket/socket/accept_loop_spec.rb
new file mode 100644
index 0000000000..88bd1c556c
--- /dev/null
+++ b/spec/ruby/library/socket/socket/accept_loop_spec.rb
@@ -0,0 +1,80 @@
+require_relative '../spec_helper'
+
+describe 'Socket.accept_loop' do
+ before do
+ @server = Socket.new(:INET, :STREAM)
+ @client = Socket.new(:INET, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, '127.0.0.1'))
+ @server.listen(1)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'using an Array of Sockets' do
+ describe 'without any available connections' do
+ it 'blocks the caller' do
+ lambda { Socket.accept_loop([@server]) }.should block_caller
+ end
+ end
+
+ describe 'with available connections' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'yields a Socket and an Addrinfo' do
+ conn = nil
+ addr = nil
+
+ Socket.accept_loop([@server]) do |connection, address|
+ conn = connection
+ addr = address
+ break
+ end
+
+ begin
+ conn.should be_an_instance_of(Socket)
+ addr.should be_an_instance_of(Addrinfo)
+ ensure
+ conn.close
+ end
+ end
+ end
+ end
+
+ describe 'using separate Socket arguments' do
+ describe 'without any available connections' do
+ it 'blocks the caller' do
+ lambda { Socket.accept_loop(@server) }.should block_caller
+ end
+ end
+
+ describe 'with available connections' do
+ before do
+ @client.connect(@server.getsockname)
+ end
+
+ it 'yields a Socket and an Addrinfo' do
+ conn = nil
+ addr = nil
+
+ Socket.accept_loop(@server) do |connection, address|
+ conn = connection
+ addr = address
+ break
+ end
+
+ begin
+ conn.should be_an_instance_of(Socket)
+ addr.should be_an_instance_of(Addrinfo)
+ ensure
+ conn.close
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/accept_nonblock_spec.rb b/spec/ruby/library/socket/socket/accept_nonblock_spec.rb
index 44f3be9c0e..201d472f16 100644
--- a/spec/ruby/library/socket/socket/accept_nonblock_spec.rb
+++ b/spec/ruby/library/socket/socket/accept_nonblock_spec.rb
@@ -1,8 +1,6 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-require 'socket'
-
describe "Socket#accept_nonblock" do
before :each do
@hostname = "127.0.0.1"
@@ -33,3 +31,106 @@ describe "Socket#accept_nonblock" do
@socket.accept_nonblock(exception: false).should == :wait_readable
end
end
+
+describe 'Socket#accept_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM, 0)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+ end
+
+ after do
+ @server.close unless @server.closed?
+ end
+
+ describe 'using an unbound socket' do
+ it 'raises Errno::EINVAL' do
+ lambda { @server.accept_nonblock }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ describe "using a bound socket that's not listening" do
+ before do
+ @server.bind(@sockaddr)
+ end
+
+ it 'raises Errno::EINVAL' do
+ lambda { @server.accept_nonblock }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ describe 'using a closed socket' do
+ it 'raises IOError' do
+ @server.close
+
+ lambda { @server.accept_nonblock }.should raise_error(IOError)
+ end
+ end
+
+ describe "using a bound socket that's listening" do
+ before do
+ @server.bind(@sockaddr)
+ @server.listen(1)
+ end
+
+ describe 'without a connected client' do
+ it 'raises IO::WaitReadable' do
+ lambda { @server.accept_nonblock }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'with a connected client' do
+ before do
+ addr = Socket.sockaddr_in(@server.local_address.ip_port, ip_address)
+ @client = Socket.new(family, :STREAM, 0)
+
+ @client.connect(addr)
+ end
+
+ after do
+ @socket.close if @socket
+ @client.close
+ end
+
+ it 'returns an Array containing a Socket and an Addrinfo' do
+ @socket, addrinfo = @server.accept_nonblock
+
+ @socket.should be_an_instance_of(Socket)
+ addrinfo.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @socket, @addr = @server.accept_nonblock
+ end
+
+ it 'uses AF_INET as the address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0 as the protocol' do
+ @addr.protocol.should == 0
+ end
+
+ it 'uses the same IP address as the client Socket' do
+ @addr.ip_address.should == @client.local_address.ip_address
+ end
+
+ it 'uses the same port as the client Socket' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/accept_spec.rb b/spec/ruby/library/socket/socket/accept_spec.rb
index 1658a2b81f..2c71d435ac 100644
--- a/spec/ruby/library/socket/socket/accept_spec.rb
+++ b/spec/ruby/library/socket/socket/accept_spec.rb
@@ -1,2 +1,122 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
+
+describe 'Socket#accept' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM, 0)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+ end
+
+ after do
+ @server.close unless @server.closed?
+ end
+
+ platform_is_not :windows do # hangs
+ describe 'using an unbound socket' do
+ it 'raises Errno::EINVAL' do
+ lambda { @server.accept }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ describe "using a bound socket that's not listening" do
+ before do
+ @server.bind(@sockaddr)
+ end
+
+ it 'raises Errno::EINVAL' do
+ lambda { @server.accept }.should raise_error(Errno::EINVAL)
+ end
+ end
+ end
+
+ describe 'using a closed socket' do
+ it 'raises IOError' do
+ @server.close
+
+ lambda { @server.accept }.should raise_error(IOError)
+ end
+ end
+
+ describe "using a bound socket that's listening" do
+ before do
+ @server.bind(@sockaddr)
+ @server.listen(1)
+
+ server_ip = @server.local_address.ip_port
+ @server_addr = Socket.sockaddr_in(server_ip, ip_address)
+ end
+
+ describe 'without a connected client' do
+ it 'blocks the caller until a connection is available' do
+ client = Socket.new(family, :STREAM, 0)
+ thread = Thread.new do
+ @server.accept
+ end
+
+ client.connect(@server_addr)
+
+ thread.join(5)
+ value = thread.value
+ begin
+ value.should be_an_instance_of(Array)
+ ensure
+ client.close
+ value[0].close
+ end
+ end
+ end
+
+ describe 'with a connected client' do
+ before do
+ addr = Socket.sockaddr_in(@server.local_address.ip_port, ip_address)
+ @client = Socket.new(family, :STREAM, 0)
+
+ @client.connect(addr)
+ end
+
+ after do
+ @socket.close if @socket
+ @client.close
+ end
+
+ it 'returns an Array containing a Socket and an Addrinfo' do
+ @socket, addrinfo = @server.accept
+
+ @socket.should be_an_instance_of(Socket)
+ addrinfo.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @socket, @addr = @server.accept
+ end
+
+ it 'uses AF_INET as the address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @addr.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0 as the protocol' do
+ @addr.protocol.should == 0
+ end
+
+ it 'uses the same IP address as the client Socket' do
+ @addr.ip_address.should == @client.local_address.ip_address
+ end
+
+ it 'uses the same port as the client Socket' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/bind_spec.rb b/spec/ruby/library/socket/socket/bind_spec.rb
index 09dbd624b5..338b19bbfc 100644
--- a/spec/ruby/library/socket/socket/bind_spec.rb
+++ b/spec/ruby/library/socket/socket/bind_spec.rb
@@ -1,11 +1,9 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-include Socket::Constants
-
describe "Socket#bind on SOCK_DGRAM socket" do
before :each do
- @sock = Socket.new(AF_INET, SOCK_DGRAM, 0)
+ @sock = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
@sockaddr = Socket.pack_sockaddr_in(0, "127.0.0.1")
end
@@ -45,8 +43,8 @@ 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)
+ @sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
@sockaddr = Socket.pack_sockaddr_in(0, "127.0.0.1")
end
@@ -83,3 +81,64 @@ describe "Socket#bind on SOCK_STREAM socket" do
end
end
end
+
+describe 'Socket#bind' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a packed socket address' do
+ before do
+ @socket = Socket.new(family, :DGRAM)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'returns 0 when successfully bound' do
+ @socket.bind(@sockaddr).should == 0
+ end
+
+ it 'raises Errno::EINVAL when binding to an already bound port' do
+ @socket.bind(@sockaddr)
+
+ lambda { @socket.bind(@sockaddr) }.should raise_error(Errno::EINVAL)
+ end
+
+ it 'raises Errno::EADDRNOTAVAIL when the specified sockaddr is not available' do
+ ip = family == Socket::AF_INET ? '4.3.2.1' : '::2'
+ sockaddr1 = Socket.sockaddr_in(0, ip)
+
+ lambda { @socket.bind(sockaddr1) }.should raise_error(Errno::EADDRNOTAVAIL)
+ end
+
+ platform_is_not :windows do
+ it 'raises Errno::EACCES when the user is not allowed to bind to the port' do
+ sockaddr1 = Socket.pack_sockaddr_in(1, ip_address)
+
+ lambda { @socket.bind(sockaddr1); }.should raise_error(Errno::EACCES)
+ end
+ end
+ end
+
+ describe 'using an Addrinfo' do
+ before do
+ @addr = Addrinfo.udp(ip_address, 0)
+ @socket = Socket.new(@addr.afamily, @addr.socktype)
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'binds to an Addrinfo' do
+ @socket.bind(@addr).should == 0
+ @socket.local_address.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'uses a new Addrinfo for the local address' do
+ @socket.bind(@addr)
+ @socket.local_address.should_not == @addr
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/connect_nonblock_spec.rb b/spec/ruby/library/socket/socket/connect_nonblock_spec.rb
index caac24401c..21ef2b4a94 100644
--- a/spec/ruby/library/socket/socket/connect_nonblock_spec.rb
+++ b/spec/ruby/library/socket/socket/connect_nonblock_spec.rb
@@ -1,8 +1,6 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-require 'socket'
-
describe "Socket#connect_nonblock" do
before :each do
@hostname = "127.0.0.1"
@@ -69,3 +67,55 @@ describe "Socket#connect_nonblock" do
end
end
end
+
+describe 'Socket#connect_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a DGRAM socket' do
+ before do
+ @server = Socket.new(family, :DGRAM)
+ @client = Socket.new(family, :DGRAM)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+
+ @server.bind(@sockaddr)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns 0 when successfully connected using a String' do
+ @client.connect_nonblock(@server.getsockname).should == 0
+ end
+
+ it 'returns 0 when successfully connected using an Addrinfo' do
+ @client.connect_nonblock(@server.connect_address).should == 0
+ end
+
+ it 'raises TypeError when passed a Fixnum' do
+ lambda { @client.connect_nonblock(666) }.should raise_error(TypeError)
+ end
+ end
+
+ describe 'using a STREAM socket' do
+ before do
+ @server = Socket.new(family, :STREAM)
+ @client = Socket.new(family, :STREAM)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'raises IO:EINPROGRESSWaitWritable when the connection would block' do
+ @server.bind(@sockaddr)
+
+ lambda {
+ @client.connect_nonblock(@server.getsockname)
+ }.should raise_error(IO::EINPROGRESSWaitWritable)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/connect_spec.rb b/spec/ruby/library/socket/socket/connect_spec.rb
index 1658a2b81f..1e2982bfde 100644
--- a/spec/ruby/library/socket/socket/connect_spec.rb
+++ b/spec/ruby/library/socket/socket/connect_spec.rb
@@ -1,2 +1,48 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
+
+describe 'Socket#connect' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM)
+ @client = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns 0 when connected successfully using a String' do
+ @server.listen(1)
+
+ @client.connect(@server.getsockname).should == 0
+ end
+
+ it 'returns 0 when connected successfully using an Addrinfo' do
+ @server.listen(1)
+
+ @client.connect(@server.connect_address).should == 0
+ end
+
+ it 'raises Errno::EISCONN when already connected' do
+ @server.listen(1)
+
+ @client.connect(@server.getsockname).should == 0
+
+ lambda {
+ @client.connect(@server.getsockname)
+ }.should raise_error(Errno::EISCONN)
+ end
+
+ it 'raises Errno::ECONNREFUSED or Errno::ETIMEDOUT when the connection failed' do
+ begin
+ @client.connect(@server.getsockname)
+ rescue => e
+ [Errno::ECONNREFUSED, Errno::ETIMEDOUT].include?(e.class).should == true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/for_fd_spec.rb b/spec/ruby/library/socket/socket/for_fd_spec.rb
index 6ea6d9cf83..e89228d436 100644
--- a/spec/ruby/library/socket/socket/for_fd_spec.rb
+++ b/spec/ruby/library/socket/socket/for_fd_spec.rb
@@ -1,6 +1,5 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-require 'socket'
describe "Socket.for_fd" do
before :each do
diff --git a/spec/ruby/library/socket/socket/getaddrinfo_spec.rb b/spec/ruby/library/socket/socket/getaddrinfo_spec.rb
index 64183b38d0..bc6e9e8f9d 100644
--- a/spec/ruby/library/socket/socket/getaddrinfo_spec.rb
+++ b/spec/ruby/library/socket/socket/getaddrinfo_spec.rb
@@ -1,9 +1,7 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-require 'socket'
-
-describe "Socket#getaddrinfo" do
+describe "Socket.getaddrinfo" do
before :each do
@do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup
BasicSocket.do_not_reverse_lookup = true
@@ -53,10 +51,10 @@ describe "Socket#getaddrinfo" do
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
+ # #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".
+ # address (127.0.0.1 or "::1").
it "accepts empty addresses for IPv4 passive sockets" do
res = Socket.getaddrinfo(nil, "discard",
@@ -110,3 +108,270 @@ describe "Socket#getaddrinfo" do
end
end
end
+
+describe 'Socket.getaddrinfo' do
+ describe 'without global reverse lookups' do
+ it 'returns an Array' do
+ Socket.getaddrinfo(nil, 'http').should be_an_instance_of(Array)
+ end
+
+ it 'accepts a Fixnum as the address family' do
+ array = Socket.getaddrinfo(nil, 'http', Socket::AF_INET)[0]
+
+ array[0].should == 'AF_INET'
+ array[1].should == 80
+ array[2].should == '127.0.0.1'
+ array[3].should == '127.0.0.1'
+ array[4].should == Socket::AF_INET
+ array[5].should be_an_instance_of(Fixnum)
+ array[6].should be_an_instance_of(Fixnum)
+ end
+
+ it 'accepts a Fixnum as the address family using IPv6' do
+ array = Socket.getaddrinfo(nil, 'http', Socket::AF_INET6)[0]
+
+ array[0].should == 'AF_INET6'
+ array[1].should == 80
+ array[2].should == '::1'
+ array[3].should == '::1'
+ array[4].should == Socket::AF_INET6
+ array[5].should be_an_instance_of(Fixnum)
+ array[6].should be_an_instance_of(Fixnum)
+ end
+
+ it 'accepts a Symbol as the address family' do
+ array = Socket.getaddrinfo(nil, 'http', :INET)[0]
+
+ array[0].should == 'AF_INET'
+ array[1].should == 80
+ array[2].should == '127.0.0.1'
+ array[3].should == '127.0.0.1'
+ array[4].should == Socket::AF_INET
+ array[5].should be_an_instance_of(Fixnum)
+ array[6].should be_an_instance_of(Fixnum)
+ end
+
+ it 'accepts a Symbol as the address family using IPv6' do
+ array = Socket.getaddrinfo(nil, 'http', :INET6)[0]
+
+ array[0].should == 'AF_INET6'
+ array[1].should == 80
+ array[2].should == '::1'
+ array[3].should == '::1'
+ array[4].should == Socket::AF_INET6
+ array[5].should be_an_instance_of(Fixnum)
+ array[6].should be_an_instance_of(Fixnum)
+ end
+
+ it 'accepts a String as the address family' do
+ array = Socket.getaddrinfo(nil, 'http', 'INET')[0]
+
+ array[0].should == 'AF_INET'
+ array[1].should == 80
+ array[2].should == '127.0.0.1'
+ array[3].should == '127.0.0.1'
+ array[4].should == Socket::AF_INET
+ array[5].should be_an_instance_of(Fixnum)
+ array[6].should be_an_instance_of(Fixnum)
+ end
+
+ it 'accepts a String as the address family using IPv6' do
+ array = Socket.getaddrinfo(nil, 'http', 'INET6')[0]
+
+ array[0].should == 'AF_INET6'
+ array[1].should == 80
+ array[2].should == '::1'
+ array[3].should == '::1'
+ array[4].should == Socket::AF_INET6
+ array[5].should be_an_instance_of(Fixnum)
+ array[6].should be_an_instance_of(Fixnum)
+ end
+
+ it 'accepts an object responding to #to_str as the host' do
+ dummy = mock(:dummy)
+
+ dummy.stub!(:to_str).and_return('127.0.0.1')
+
+ array = Socket.getaddrinfo(dummy, 'http')[0]
+
+ array[0].should == 'AF_INET'
+ array[1].should == 80
+ array[2].should == '127.0.0.1'
+ array[3].should == '127.0.0.1'
+ array[4].should == Socket::AF_INET
+ array[5].should be_an_instance_of(Fixnum)
+ array[6].should be_an_instance_of(Fixnum)
+ end
+
+ it 'accepts an object responding to #to_str as the address family' do
+ dummy = mock(:dummy)
+
+ dummy.stub!(:to_str).and_return('INET')
+
+ array = Socket.getaddrinfo(nil, 'http', dummy)[0]
+
+ array[0].should == 'AF_INET'
+ array[1].should == 80
+ array[2].should == '127.0.0.1'
+ array[3].should == '127.0.0.1'
+ array[4].should == Socket::AF_INET
+ array[5].should be_an_instance_of(Fixnum)
+ array[6].should be_an_instance_of(Fixnum)
+ end
+
+ ipproto_tcp = Socket::IPPROTO_TCP
+ platform_is :windows do
+ ipproto_tcp = 0
+ end
+
+ it 'accepts a Fixnum as the socket type' do
+ Socket.getaddrinfo(nil, 'http', :INET, Socket::SOCK_STREAM)[0].should == [
+ 'AF_INET',
+ 80,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ ipproto_tcp
+ ]
+ end
+
+ it 'accepts a Symbol as the socket type' do
+ Socket.getaddrinfo(nil, 'http', :INET, :STREAM)[0].should == [
+ 'AF_INET',
+ 80,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ ipproto_tcp
+ ]
+ end
+
+ it 'accepts a String as the socket type' do
+ Socket.getaddrinfo(nil, 'http', :INET, 'STREAM')[0].should == [
+ 'AF_INET',
+ 80,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ ipproto_tcp
+ ]
+ end
+
+ it 'accepts an object responding to #to_str as the socket type' do
+ dummy = mock(:dummy)
+
+ dummy.stub!(:to_str).and_return('STREAM')
+
+ Socket.getaddrinfo(nil, 'http', :INET, dummy)[0].should == [
+ 'AF_INET',
+ 80,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ ipproto_tcp
+ ]
+ end
+
+ platform_is_not :windows do
+ it 'accepts a Fixnum as the protocol family' do
+ addr = Socket.getaddrinfo(nil, 'http', :INET, :DGRAM, Socket::IPPROTO_UDP)
+
+ addr[0].should == [
+ 'AF_INET',
+ 80,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_DGRAM,
+ Socket::IPPROTO_UDP
+ ]
+ end
+ end
+
+ it 'accepts a Fixnum as the flags' do
+ addr = Socket.getaddrinfo(nil, 'http', :INET, :STREAM,
+ Socket::IPPROTO_TCP, Socket::AI_PASSIVE)
+
+ addr[0].should == [
+ 'AF_INET',
+ 80,
+ '0.0.0.0',
+ '0.0.0.0',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ Socket::IPPROTO_TCP
+ ]
+ end
+
+ it 'performs a reverse lookup when the reverse_lookup argument is true' do
+ addr = Socket.getaddrinfo(nil, 'http', :INET, :STREAM,
+ Socket::IPPROTO_TCP, 0, true)[0]
+
+ addr[0].should == 'AF_INET'
+ addr[1].should == 80
+
+ addr[2].should be_an_instance_of(String)
+ addr[2].should_not == addr[3]
+
+ addr[3].should == '127.0.0.1'
+ end
+
+ it 'performs a reverse lookup when the reverse_lookup argument is :hostname' do
+ addr = Socket.getaddrinfo(nil, 'http', :INET, :STREAM,
+ Socket::IPPROTO_TCP, 0, :hostname)[0]
+
+ addr[0].should == 'AF_INET'
+ addr[1].should == 80
+
+ addr[2].should be_an_instance_of(String)
+ addr[2].should_not == addr[3]
+
+ addr[3].should == '127.0.0.1'
+ end
+
+ it 'performs a reverse lookup when the reverse_lookup argument is :numeric' do
+ addr = Socket.getaddrinfo(nil, 'http', :INET, :STREAM,
+ Socket::IPPROTO_TCP, 0, :numeric)[0]
+
+ addr.should == [
+ 'AF_INET',
+ 80,
+ '127.0.0.1',
+ '127.0.0.1',
+ Socket::AF_INET,
+ Socket::SOCK_STREAM,
+ Socket::IPPROTO_TCP
+ ]
+ end
+ end
+
+ describe 'with global reverse lookups' do
+ before do
+ @do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup
+ BasicSocket.do_not_reverse_lookup = false
+ end
+
+ after do
+ BasicSocket.do_not_reverse_lookup = @do_not_reverse_lookup
+ end
+
+ it 'returns an address honoring the global lookup option' do
+ addr = Socket.getaddrinfo(nil, 'http', :INET)[0]
+
+ addr[0].should == 'AF_INET'
+ addr[1].should == 80
+
+ # We don't have control over this value and there's no way to test this
+ # without relying on Socket.getaddrinfo()'s own behaviour (meaning this
+ # test would faily any way of the method was not implemented correctly).
+ addr[2].should be_an_instance_of(String)
+ addr[2].should_not == addr[3]
+
+ addr[3].should == '127.0.0.1'
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/gethostbyaddr_spec.rb b/spec/ruby/library/socket/socket/gethostbyaddr_spec.rb
index 1658a2b81f..cad6ed2dd0 100644
--- a/spec/ruby/library/socket/socket/gethostbyaddr_spec.rb
+++ b/spec/ruby/library/socket/socket/gethostbyaddr_spec.rb
@@ -1,2 +1,121 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
+require 'ipaddr'
+
+describe 'Socket.gethostbyaddr' do
+ describe 'using an IPv4 address' do
+ before do
+ @addr = IPAddr.new('127.0.0.1').hton
+ end
+
+ describe 'without an explicit address family' do
+ it 'returns an Array' do
+ Socket.gethostbyaddr(@addr).should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = Socket.gethostbyaddr(@addr)
+ end
+
+ it 'includes the hostname as the first value' do
+ @array[0].should == SocketSpecs.hostname_reverse_lookup
+ end
+
+ it 'includes the aliases as the 2nd value' do
+ @array[1].should be_an_instance_of(Array)
+
+ @array[1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @array[2].should == Socket::AF_INET
+ end
+
+ it 'includes all address strings as the remaining values' do
+ @array[3].should == @addr
+
+ @array[4..-1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+ end
+ end
+
+ describe 'with an explicit address family' do
+ it 'returns an Array when using a Fixnum as the address family' do
+ Socket.gethostbyaddr(@addr, Socket::AF_INET).should be_an_instance_of(Array)
+ end
+
+ it 'returns an Array when using a Symbol as the address family' do
+ Socket.gethostbyaddr(@addr, :INET).should be_an_instance_of(Array)
+ end
+
+ it 'raises SocketError when the address is not supported by the family' do
+ lambda { Socket.gethostbyaddr(@addr, :INET6) }.should raise_error(SocketError)
+ end
+ end
+ end
+
+ guard -> { SocketSpecs.ipv6_available? } do
+ describe 'using an IPv6 address' do
+ before do
+ @addr = IPAddr.new('::1').hton
+ end
+
+ describe 'without an explicit address family' do
+ it 'returns an Array' do
+ Socket.gethostbyaddr(@addr).should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = Socket.gethostbyaddr(@addr)
+ end
+
+ it 'includes the hostname as the first value' do
+ @array[0].should == SocketSpecs.hostname_reverse_lookup("::1")
+ end
+
+ it 'includes the aliases as the 2nd value' do
+ @array[1].should be_an_instance_of(Array)
+
+ @array[1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @array[2].should == Socket::AF_INET6
+ end
+
+ it 'includes all address strings as the remaining values' do
+ @array[3].should be_an_instance_of(String)
+
+ @array[4..-1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+ end
+ end
+
+ describe 'with an explicit address family' do
+ it 'returns an Array when using a Fixnum as the address family' do
+ Socket.gethostbyaddr(@addr, Socket::AF_INET6).should be_an_instance_of(Array)
+ end
+
+ it 'returns an Array when using a Symbol as the address family' do
+ Socket.gethostbyaddr(@addr, :INET6).should be_an_instance_of(Array)
+ end
+
+ platform_is_not :windows do
+ it 'raises SocketError when the address is not supported by the family' do
+ lambda { Socket.gethostbyaddr(@addr, :INET) }.should raise_error(SocketError)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/gethostbyname_spec.rb b/spec/ruby/library/socket/socket/gethostbyname_spec.rb
index 23880bb42d..9367030e25 100644
--- a/spec/ruby/library/socket/socket/gethostbyname_spec.rb
+++ b/spec/ruby/library/socket/socket/gethostbyname_spec.rb
@@ -1,9 +1,7 @@
# -*- encoding: binary -*-
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-require 'socket'
-
describe "Socket#gethostbyname" do
it "returns broadcast address info for '<broadcast>'" do
addr = Socket.gethostbyname('<broadcast>');
@@ -15,3 +13,123 @@ describe "Socket#gethostbyname" do
addr.should == ["0.0.0.0", [], 2, "\000\000\000\000"]
end
end
+
+describe 'Socket.gethostbyname' do
+ it 'returns an Array' do
+ Socket.gethostbyname('127.0.0.1').should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = Socket.gethostbyname('127.0.0.1')
+ end
+
+ it 'includes the hostname as the first value' do
+ @array[0].should == '127.0.0.1'
+ end
+
+ it 'includes the aliases as the 2nd value' do
+ @array[1].should be_an_instance_of(Array)
+
+ @array[1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+
+ it 'includes the address type as the 3rd value' do
+ possible = [Socket::AF_INET, Socket::AF_INET6]
+
+ possible.include?(@array[2]).should == true
+ end
+
+ it 'includes the address strings as the remaining values' do
+ @array[3].should be_an_instance_of(String)
+
+ @array[4..-1].each do |val|
+ val.should be_an_instance_of(String)
+ end
+ end
+ end
+
+ describe 'using <broadcast> as the input address' do
+ describe 'the returned Array' do
+ before do
+ @addr = Socket.gethostbyname('<broadcast>')
+ end
+
+ it 'includes the broadcast address as the first value' do
+ @addr[0].should == '255.255.255.255'
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @addr[2].should == Socket::AF_INET
+ end
+
+ it 'includes the address string as the 4th value' do
+ @addr[3].should == [255, 255, 255, 255].pack('C4')
+ end
+ end
+ end
+
+ describe 'using <any> as the input address' do
+ describe 'the returned Array' do
+ before do
+ @addr = Socket.gethostbyname('<any>')
+ end
+
+ it 'includes the wildcard address as the first value' do
+ @addr[0].should == '0.0.0.0'
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @addr[2].should == Socket::AF_INET
+ end
+
+ it 'includes the address string as the 4th value' do
+ @addr[3].should == [0, 0, 0, 0].pack('C4')
+ end
+ end
+ end
+
+ describe 'using an IPv4 address' do
+ describe 'the returned Array' do
+ before do
+ @addr = Socket.gethostbyname('127.0.0.1')
+ end
+
+ it 'includes the IP address as the first value' do
+ @addr[0].should == '127.0.0.1'
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @addr[2].should == Socket::AF_INET
+ end
+
+ it 'includes the address string as the 4th value' do
+ @addr[3].should == [127, 0, 0, 1].pack('C4')
+ end
+ end
+ end
+
+ guard -> { SocketSpecs.ipv6_available? } do
+ describe 'using an IPv6 address' do
+ describe 'the returned Array' do
+ before do
+ @addr = Socket.gethostbyname('::1')
+ end
+
+ it 'includes the IP address as the first value' do
+ @addr[0].should == '::1'
+ end
+
+ it 'includes the address type as the 3rd value' do
+ @addr[2].should == Socket::AF_INET6
+ end
+
+ it 'includes the address string as the 4th value' do
+ @addr[3].should == [0, 0, 0, 0, 0, 0, 0, 1].pack('n8')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/gethostname_spec.rb b/spec/ruby/library/socket/socket/gethostname_spec.rb
index 7308d8294b..4b79747b27 100644
--- a/spec/ruby/library/socket/socket/gethostname_spec.rb
+++ b/spec/ruby/library/socket/socket/gethostname_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket.gethostname" do
diff --git a/spec/ruby/library/socket/socket/getifaddrs_spec.rb b/spec/ruby/library/socket/socket/getifaddrs_spec.rb
new file mode 100644
index 0000000000..1a767c56ab
--- /dev/null
+++ b/spec/ruby/library/socket/socket/getifaddrs_spec.rb
@@ -0,0 +1,108 @@
+require_relative '../spec_helper'
+
+describe 'Socket.getifaddrs' do
+ before do
+ @ifaddrs = Socket.getifaddrs
+ end
+
+ it 'returns an Array' do
+ @ifaddrs.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ it 'should not be empty' do
+ @ifaddrs.should_not be_empty
+ end
+
+ it 'contains instances of Socket::Ifaddr' do
+ @ifaddrs.each do |ifaddr|
+ ifaddr.should be_an_instance_of(Socket::Ifaddr)
+ end
+ end
+ end
+
+ describe 'each returned Socket::Ifaddr' do
+ it 'has an interface index' do
+ @ifaddrs.each do |ifaddr|
+ ifaddr.ifindex.should be_an_instance_of(Fixnum)
+ end
+ end
+
+ it 'has an interface name' do
+ @ifaddrs.each do |ifaddr|
+ ifaddr.name.should be_an_instance_of(String)
+ end
+ end
+
+ it 'has a set of flags' do
+ @ifaddrs.each do |ifaddr|
+ ifaddr.flags.should be_an_instance_of(Fixnum)
+ end
+ end
+ end
+
+ describe 'the Socket::Ifaddr address' do
+ before do
+ @addrs = @ifaddrs.map(&:addr).compact
+ end
+
+ it 'is an Addrinfo' do
+ @addrs.each do |addr|
+ addr.should be_an_instance_of(Addrinfo)
+ end
+ end
+
+ it 'has an address family' do
+ @addrs.each do |addr|
+ addr.afamily.should be_an_instance_of(Fixnum)
+ addr.afamily.should_not == Socket::AF_UNSPEC
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'the Socket::Ifaddr broadcast address' do
+ before do
+ @addrs = @ifaddrs.map(&:broadaddr).compact
+ end
+
+ it 'is an Addrinfo' do
+ @addrs.each do |addr|
+ addr.should be_an_instance_of(Addrinfo)
+ end
+ end
+
+ it 'has an address family' do
+ @addrs.each do |addr|
+ addr.afamily.should be_an_instance_of(Fixnum)
+ addr.afamily.should_not == Socket::AF_UNSPEC
+ end
+ end
+ end
+
+ describe 'the Socket::Ifaddr netmask address' do
+ before do
+ @addrs = @ifaddrs.map(&:netmask).compact
+ end
+
+ it 'is an Addrinfo' do
+ @addrs.each do |addr|
+ addr.should be_an_instance_of(Addrinfo)
+ end
+ end
+
+ it 'has an address family' do
+ @addrs.each do |addr|
+ addr.afamily.should be_an_instance_of(Fixnum)
+ addr.afamily.should_not == Socket::AF_UNSPEC
+ end
+ end
+
+ it 'has an IP address' do
+ @addrs.each do |addr|
+ addr.ip_address.should be_an_instance_of(String)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/getnameinfo_spec.rb b/spec/ruby/library/socket/socket/getnameinfo_spec.rb
index da6d5a46e5..394a90cb47 100644
--- a/spec/ruby/library/socket/socket/getnameinfo_spec.rb
+++ b/spec/ruby/library/socket/socket/getnameinfo_spec.rb
@@ -1,8 +1,6 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-require 'socket'
-
describe "Socket.getnameinfo" do
before :each do
@reverse_lookup = BasicSocket.do_not_reverse_lookup
@@ -62,5 +60,95 @@ describe "Socket.getnameinfo" do
name_info = Socket.getnameinfo ["AF_INET", 9, 'foo', '127.0.0.1']
name_info[1].should == 'discard'
end
+end
+
+describe 'Socket.getnameinfo' do
+ describe 'using a String as the first argument' do
+ before do
+ @addr = Socket.sockaddr_in(80, '127.0.0.1')
+ end
+
+ it 'raises SocketError when using an invalid String' do
+ lambda { Socket.getnameinfo('cats') }.should raise_error(SocketError)
+ end
+
+ describe 'without custom flags' do
+ it 'returns an Array containing the hostname and service name' do
+ Socket.getnameinfo(@addr).should == [SocketSpecs.hostname_reverse_lookup, 'http']
+ end
+ end
+
+ describe 'using NI_NUMERICHOST as the flag' do
+ it 'returns an Array containing the numeric hostname and service name' do
+ array = Socket.getnameinfo(@addr, Socket::NI_NUMERICHOST)
+
+ %w{127.0.0.1 ::1}.include?(array[0]).should == true
+
+ array[1].should == 'http'
+ end
+ end
+ end
+
+ SocketSpecs.each_ip_protocol do |family, ip_address, family_name|
+ before do
+ @hostname = SocketSpecs.hostname_reverse_lookup(ip_address)
+ end
+ describe 'using a 3 element Array as the first argument' do
+ before do
+ @addr = [family_name, 80, @hostname]
+ end
+
+ it 'raises ArgumentError when using an invalid Array' do
+ lambda { Socket.getnameinfo([family_name]) }.should raise_error(ArgumentError)
+ end
+
+ describe 'without custom flags' do
+ it 'returns an Array containing the hostname and service name' do
+ array = Socket.getnameinfo(@addr)
+ array.should be_an_instance_of(Array)
+ array[0].should include(@hostname)
+ array[1].should == 'http'
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'using NI_NUMERICHOST as the flag' do
+ it 'returns an Array containing the numeric hostname and service name' do
+ Socket.getnameinfo(@addr, Socket::NI_NUMERICHOST).should == [ip_address, 'http']
+ end
+ end
+ end
+ end
+
+ describe 'using a 4 element Array as the first argument' do
+ before do
+ @addr = [family_name, 80, ip_address, ip_address]
+ end
+
+ describe 'without custom flags' do
+ it 'returns an Array containing the hostname and service name' do
+ array = Socket.getnameinfo(@addr)
+ array.should be_an_instance_of(Array)
+ array[0].should == @hostname
+ array[1].should == 'http'
+ end
+
+ it 'uses the 3rd value as the hostname if the 4th is not present' do
+ addr = [family_name, 80, ip_address, nil]
+
+ array = Socket.getnameinfo(addr)
+ array.should be_an_instance_of(Array)
+ array[0].should == @hostname
+ array[1].should == 'http'
+ end
+ end
+
+ describe 'using NI_NUMERICHOST as the flag' do
+ it 'returns an Array containing the numeric hostname and service name' do
+ Socket.getnameinfo(@addr, Socket::NI_NUMERICHOST).should == [ip_address, 'http']
+ end
+ end
+ end
+ end
end
diff --git a/spec/ruby/library/socket/socket/getservbyname_spec.rb b/spec/ruby/library/socket/socket/getservbyname_spec.rb
index dd05de90b6..d80e948a1b 100644
--- a/spec/ruby/library/socket/socket/getservbyname_spec.rb
+++ b/spec/ruby/library/socket/socket/getservbyname_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket#getservbyname" do
@@ -10,6 +10,14 @@ describe "Socket#getservbyname" do
Socket.getservbyname('discard', 'tcp').should == 9
end
+ it 'returns the port for service "http"' do
+ Socket.getservbyname('http').should == 80
+ end
+
+ it 'returns the port for service "http" with protocol "tcp"' do
+ Socket.getservbyname('http', 'tcp').should == 80
+ end
+
it "returns the port for service 'domain' with protocol 'udp'" do
Socket.getservbyname('domain', 'udp').should == 53
end
diff --git a/spec/ruby/library/socket/socket/getservbyport_spec.rb b/spec/ruby/library/socket/socket/getservbyport_spec.rb
new file mode 100644
index 0000000000..9be2ac527e
--- /dev/null
+++ b/spec/ruby/library/socket/socket/getservbyport_spec.rb
@@ -0,0 +1,23 @@
+require_relative '../spec_helper'
+
+describe 'Socket.getservbyport' do
+ platform_is_not :windows do
+ it 'returns the service name as a String' do
+ Socket.getservbyport(514).should == 'shell'
+ end
+ end
+
+ platform_is :windows do
+ it 'returns the service name as a String' do
+ Socket.getservbyport(514).should == 'cmd'
+ end
+ end
+
+ it 'returns the service name when using a custom protocol name' do
+ Socket.getservbyport(514, 'udp').should == 'syslog'
+ end
+
+ it 'raises SocketError for an unknown port number' do
+ lambda { Socket.getservbyport(0) }.should raise_error(SocketError)
+ end
+end
diff --git a/spec/ruby/library/socket/socket/initialize_spec.rb b/spec/ruby/library/socket/socket/initialize_spec.rb
new file mode 100644
index 0000000000..375eabfbcb
--- /dev/null
+++ b/spec/ruby/library/socket/socket/initialize_spec.rb
@@ -0,0 +1,87 @@
+require_relative '../spec_helper'
+
+describe 'Socket#initialize' do
+ before do
+ @socket = nil
+ end
+
+ after do
+ @socket.close if @socket
+ end
+
+ describe 'using a Fixnum as the 1st and 2nd arguments' do
+ it 'returns a Socket' do
+ @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'using Symbols as the 1st and 2nd arguments' do
+ it 'returns a Socket' do
+ @socket = Socket.new(:INET, :STREAM)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'using Strings as the 1st and 2nd arguments' do
+ it 'returns a Socket' do
+ @socket = Socket.new('INET', 'STREAM')
+
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'using objects that respond to #to_str' do
+ it 'returns a Socket' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return('AF_INET')
+ type.stub!(:to_str).and_return('STREAM')
+
+ @socket = Socket.new(family, type)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'raises TypeError when the #to_str method does not return a String' do
+ family = mock(:family)
+ type = mock(:type)
+
+ family.stub!(:to_str).and_return(Socket::AF_INET)
+ type.stub!(:to_str).and_return(Socket::SOCK_STREAM)
+
+ lambda { Socket.new(family, type) }.should raise_error(TypeError)
+ end
+ end
+
+ describe 'using a custom protocol' do
+ it 'returns a Socket when using a Fixnum' do
+ @socket = Socket.new(:INET, :STREAM, Socket::IPPROTO_TCP)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+
+ it 'raises TypeError when using a Symbol' do
+ lambda { Socket.new(:INET, :STREAM, :TCP) }.should raise_error(TypeError)
+ end
+ end
+
+ it 'sets the do_not_reverse_lookup option' do
+ @socket = Socket.new(:INET, :STREAM)
+
+ @socket.do_not_reverse_lookup.should == Socket.do_not_reverse_lookup
+ end
+
+ it "sets basic IO accessors" do
+ @socket = Socket.new(:INET, :STREAM)
+ @socket.lineno.should == 0
+ end
+
+ it "sets the socket to binary mode" do
+ @socket = Socket.new(:INET, :STREAM)
+ @socket.binmode?.should be_true
+ end
+end
diff --git a/spec/ruby/library/socket/socket/ip_address_list_spec.rb b/spec/ruby/library/socket/socket/ip_address_list_spec.rb
new file mode 100644
index 0000000000..f97c2d7f85
--- /dev/null
+++ b/spec/ruby/library/socket/socket/ip_address_list_spec.rb
@@ -0,0 +1,50 @@
+require_relative '../spec_helper'
+
+describe 'Socket.ip_address_list' do
+ it 'returns an Array' do
+ Socket.ip_address_list.should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = Socket.ip_address_list
+ end
+
+ it 'is not empty' do
+ @array.should_not be_empty
+ end
+
+ it 'contains Addrinfo objects' do
+ @array.each do |klass|
+ klass.should be_an_instance_of(Addrinfo)
+ end
+ end
+ end
+
+ describe 'each returned Addrinfo' do
+ before do
+ @array = Socket.ip_address_list
+ end
+
+ it 'has a non-empty IP address' do
+ @array.each do |addr|
+ addr.ip_address.should be_an_instance_of(String)
+ addr.ip_address.should_not be_empty
+ end
+ end
+
+ it 'has an address family' do
+ families = [Socket::AF_INET, Socket::AF_INET6]
+
+ @array.each do |addr|
+ families.include?(addr.afamily).should == true
+ end
+ end
+
+ it 'uses 0 as the port number' do
+ @array.each do |addr|
+ addr.ip_port.should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/ipv6only_bang_spec.rb b/spec/ruby/library/socket/socket/ipv6only_bang_spec.rb
new file mode 100644
index 0000000000..73f3ce1642
--- /dev/null
+++ b/spec/ruby/library/socket/socket/ipv6only_bang_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../spec_helper'
+
+describe 'Socket#ipv6only!' do
+ before do
+ @socket = Socket.new(:INET6, :DGRAM)
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'enables IPv6 only mode' do
+ @socket.ipv6only!
+
+ @socket.getsockopt(:IPV6, :V6ONLY).bool.should == true
+ end
+end
diff --git a/spec/ruby/library/socket/socket/listen_spec.rb b/spec/ruby/library/socket/socket/listen_spec.rb
index 3ef503ec2e..326307f942 100644
--- a/spec/ruby/library/socket/socket/listen_spec.rb
+++ b/spec/ruby/library/socket/socket/listen_spec.rb
@@ -1,11 +1,9 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-include Socket::Constants
-
describe "Socket#listen" do
before :each do
- @socket = Socket.new(AF_INET, SOCK_STREAM, 0)
+ @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
end
after :each do
@@ -20,3 +18,47 @@ describe "Socket#listen" do
@socket.listen(1).should == 0
end
end
+
+describe 'Socket#listen' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'using a DGRAM socket' do
+ before do
+ @server = Socket.new(family, :DGRAM)
+ @client = Socket.new(family, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'raises Errno::EOPNOTSUPP' do
+ lambda { @server.listen(1) }.should raise_error(Errno::EOPNOTSUPP)
+ end
+ end
+
+ describe 'using a STREAM socket' do
+ before do
+ @server = Socket.new(family, :STREAM)
+ @client = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns 0' do
+ @server.listen(1).should == 0
+ end
+
+ it "raises when the given argument can't be coerced to a Fixnum" do
+ lambda { @server.listen('cats') }.should raise_error(TypeError)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/local_address_spec.rb b/spec/ruby/library/socket/socket/local_address_spec.rb
new file mode 100644
index 0000000000..3687f93a0c
--- /dev/null
+++ b/spec/ruby/library/socket/socket/local_address_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+
+describe 'Socket#local_address' do
+ before do
+ @sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, Socket::IPPROTO_TCP)
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.local_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_INET as the address family' do
+ @sock.local_address.afamily.should == Socket::AF_INET
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @sock.local_address.pfamily.should == Socket::PF_INET
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.local_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses 0.0.0.0 as the IP address' do
+ @sock.local_address.ip_address.should == '0.0.0.0'
+ end
+
+ platform_is_not :windows do
+ it 'uses 0 as the port' do
+ @sock.local_address.ip_port.should == 0
+ end
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.local_address.protocol.should == 0
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/new_spec.rb b/spec/ruby/library/socket/socket/new_spec.rb
index 1658a2b81f..b2ec607f6a 100644
--- a/spec/ruby/library/socket/socket/new_spec.rb
+++ b/spec/ruby/library/socket/socket/new_spec.rb
@@ -1,2 +1,2 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
diff --git a/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb b/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb
index a8f1f924d8..63d4724453 100644
--- a/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb
+++ b/spec/ruby/library/socket/socket/pack_sockaddr_in_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/pack_sockaddr'
diff --git a/spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb b/spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb
index 16a286cf6a..1ee0bc6157 100644
--- a/spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb
+++ b/spec/ruby/library/socket/socket/pack_sockaddr_un_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/pack_sockaddr'
diff --git a/spec/ruby/library/socket/socket/pair_spec.rb b/spec/ruby/library/socket/socket/pair_spec.rb
index db11e5d92b..292eacd38d 100644
--- a/spec/ruby/library/socket/socket/pair_spec.rb
+++ b/spec/ruby/library/socket/socket/pair_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/socketpair'
diff --git a/spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb b/spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb
index 1658a2b81f..76a07e3bfb 100644
--- a/spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb
+++ b/spec/ruby/library/socket/socket/recvfrom_nonblock_spec.rb
@@ -1,2 +1,110 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
+
+describe 'Socket#recvfrom_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :DGRAM)
+ @client = Socket.new(family, :DGRAM)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ describe 'using an unbound socket' do
+ it 'raises IO::WaitReadable' do
+ lambda { @server.recvfrom_nonblock(1) }.should raise_error(IO::WaitReadable)
+ end
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @client.connect(@server.getsockname)
+ end
+
+ describe 'without any data available' do
+ it 'raises IO::WaitReadable' do
+ lambda { @server.recvfrom_nonblock(1) }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ end
+
+ platform_is_not :windows do
+ it 'returns an Array containing the data and an Addrinfo' do
+ ret = @server.recvfrom_nonblock(1)
+
+ ret.should be_an_instance_of(Array)
+ ret.length.should == 2
+ end
+ end
+
+ describe 'the returned data' do
+ it 'is the same as the sent data' do
+ 5.times do
+ @client.write('hello')
+
+ msg, _ = @server.recvfrom_nonblock(5)
+
+ msg.should == 'hello'
+ end
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'the returned Array' do
+ before do
+ @array = @server.recvfrom_nonblock(1)
+ end
+
+ it 'contains the data at index 0' do
+ @array[0].should == 'h'
+ end
+
+ it 'contains an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @server.recvfrom_nonblock(1)[1]
+ end
+
+ it 'uses AF_INET as the address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses SOCK_DGRAM as the socket type' do
+ @addr.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses 0 as the protocol' do
+ @addr.protocol.should == 0
+ end
+
+ it 'uses the IP address of the client' do
+ @addr.ip_address.should == ip_address
+ end
+
+ it 'uses the port of the client' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/recvfrom_spec.rb b/spec/ruby/library/socket/socket/recvfrom_spec.rb
index 1658a2b81f..7f0714511c 100644
--- a/spec/ruby/library/socket/socket/recvfrom_spec.rb
+++ b/spec/ruby/library/socket/socket/recvfrom_spec.rb
@@ -1,2 +1,92 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
+
+describe 'Socket#recvfrom' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :DGRAM)
+ @client = Socket.new(family, :DGRAM)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ describe 'using an unbound socket' do
+ it 'blocks the caller' do
+ lambda { @server.recvfrom(1) }.should block_caller
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @client.connect(@server.getsockname)
+ end
+
+ describe 'without any data available' do
+ it 'blocks the caller' do
+ lambda { @server.recvfrom(1) }.should block_caller
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ end
+
+ it 'returns an Array containing the data and an Addrinfo' do
+ ret = @server.recvfrom(1)
+
+ ret.should be_an_instance_of(Array)
+ ret.length.should == 2
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @server.recvfrom(1)
+ end
+
+ it 'contains the data at index 0' do
+ @array[0].should == 'h'
+ end
+
+ it 'contains an Addrinfo at index 1' do
+ @array[1].should be_an_instance_of(Addrinfo)
+ end
+ end
+
+ describe 'the returned Addrinfo' do
+ before do
+ @addr = @server.recvfrom(1)[1]
+ end
+
+ it 'uses AF_INET as the address family' do
+ @addr.afamily.should == family
+ end
+
+ it 'uses SOCK_DGRAM as the socket type' do
+ @addr.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @addr.pfamily.should == family
+ end
+
+ it 'uses 0 as the protocol' do
+ @addr.protocol.should == 0
+ end
+
+ it 'uses the IP address of the client' do
+ @addr.ip_address.should == ip_address
+ end
+
+ it 'uses the port of the client' do
+ @addr.ip_port.should == @client.local_address.ip_port
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/remote_address_spec.rb b/spec/ruby/library/socket/socket/remote_address_spec.rb
new file mode 100644
index 0000000000..24d60d7f58
--- /dev/null
+++ b/spec/ruby/library/socket/socket/remote_address_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket#remote_address' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+ @server.listen(1)
+
+ @host = @server.local_address.ip_address
+ @port = @server.local_address.ip_port
+ @client = Socket.new(family, :STREAM, Socket::IPPROTO_TCP)
+
+ @client.connect(Socket.sockaddr_in(@port, @host))
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns an Addrinfo' do
+ @client.remote_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_INET as the address family' do
+ @client.remote_address.afamily.should == family
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @client.remote_address.pfamily.should == family
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @client.remote_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses the correct IP address' do
+ @client.remote_address.ip_address.should == @host
+ end
+
+ it 'uses the correct port' do
+ @client.remote_address.ip_port.should == @port
+ end
+
+ it 'uses 0 as the protocol' do
+ @client.remote_address.protocol.should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/sockaddr_in_spec.rb b/spec/ruby/library/socket/socket/sockaddr_in_spec.rb
index 64645f179d..8ee956ac26 100644
--- a/spec/ruby/library/socket/socket/sockaddr_in_spec.rb
+++ b/spec/ruby/library/socket/socket/sockaddr_in_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/pack_sockaddr'
diff --git a/spec/ruby/library/socket/socket/sockaddr_un_spec.rb b/spec/ruby/library/socket/socket/sockaddr_un_spec.rb
index b8f148ca13..8922ff4d6d 100644
--- a/spec/ruby/library/socket/socket/sockaddr_un_spec.rb
+++ b/spec/ruby/library/socket/socket/sockaddr_un_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/pack_sockaddr'
diff --git a/spec/ruby/library/socket/socket/socket_spec.rb b/spec/ruby/library/socket/socket/socket_spec.rb
index ee003f84ec..5a3d6733e0 100644
--- a/spec/ruby/library/socket/socket/socket_spec.rb
+++ b/spec/ruby/library/socket/socket/socket_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket" do
diff --git a/spec/ruby/library/socket/socket/socketpair_spec.rb b/spec/ruby/library/socket/socket/socketpair_spec.rb
index 495585e8dc..5b8311124e 100644
--- a/spec/ruby/library/socket/socket/socketpair_spec.rb
+++ b/spec/ruby/library/socket/socket/socketpair_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/socketpair'
diff --git a/spec/ruby/library/socket/socket/sysaccept_spec.rb b/spec/ruby/library/socket/socket/sysaccept_spec.rb
index 1658a2b81f..17b1a8a2f0 100644
--- a/spec/ruby/library/socket/socket/sysaccept_spec.rb
+++ b/spec/ruby/library/socket/socket/sysaccept_spec.rb
@@ -1,2 +1,93 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
+
+describe 'Socket#sysaccept' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :STREAM)
+ @sockaddr = Socket.sockaddr_in(0, ip_address)
+ end
+
+ after do
+ @server.close
+ end
+
+ platform_is_not :windows do # hangs
+ describe 'using an unbound socket' do
+ it 'raises Errno::EINVAL' do
+ lambda { @server.sysaccept }.should raise_error(Errno::EINVAL)
+ end
+ end
+
+ describe "using a bound socket that's not listening" do
+ before do
+ @server.bind(@sockaddr)
+ end
+
+ it 'raises Errno::EINVAL' do
+ lambda { @server.sysaccept }.should raise_error(Errno::EINVAL)
+ end
+ end
+ end
+
+ describe "using a bound socket that's listening" do
+ before do
+ @server.bind(@sockaddr)
+ @server.listen(1)
+
+ server_ip = @server.local_address.ip_port
+ @server_addr = Socket.sockaddr_in(server_ip, ip_address)
+ end
+
+ after do
+ Socket.for_fd(@fd).close if @fd
+ end
+
+ describe 'without a connected client' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ end
+
+ after do
+ @client.close
+ end
+
+ it 'blocks the caller until a connection is available' do
+ thread = Thread.new do
+ @fd, _ = @server.sysaccept
+ end
+
+ @client.connect(@server_addr)
+
+ thread.join(5)
+
+ thread.value.should be_an_instance_of(Array)
+ end
+ end
+
+ describe 'with a connected client' do
+ before do
+ @client = Socket.new(family, :STREAM)
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ end
+
+ it 'returns an Array containing a Fixnum and an Addrinfo' do
+ @fd, addrinfo = @server.sysaccept
+
+ @fd.should be_an_instance_of(Fixnum)
+ addrinfo.should be_an_instance_of(Addrinfo)
+ end
+
+ it 'returns a new file descriptor' do
+ @fd, _ = @server.sysaccept
+
+ @fd.should_not == @client.fileno
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/tcp_server_loop_spec.rb b/spec/ruby/library/socket/socket/tcp_server_loop_spec.rb
new file mode 100644
index 0000000000..617e3d445c
--- /dev/null
+++ b/spec/ruby/library/socket/socket/tcp_server_loop_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket.tcp_server_loop' do
+ describe 'when no connections are available' do
+ it 'blocks the caller' do
+ lambda { Socket.tcp_server_loop('127.0.0.1', 0) }.should block_caller
+ end
+ end
+
+ describe 'when a connection is available' do
+ before do
+ @client = Socket.new(:INET, :STREAM)
+ @port = 9998
+ end
+
+ after do
+ @sock.close if @sock
+ @client.close
+ end
+
+ it 'yields a Socket and an Addrinfo' do
+ @sock, addr = nil
+
+ thread = Thread.new do
+ Socket.tcp_server_loop('127.0.0.1', @port) do |socket, addrinfo|
+ @sock = socket
+ addr = addrinfo
+
+ break
+ end
+ end
+
+ SocketSpecs.wait_until_success do
+ @client.connect(Socket.sockaddr_in(@port, '127.0.0.1'))
+ end
+
+ # At this point the connection has been set up but the thread may not yet
+ # have returned, thus we'll need to wait a little longer for it to
+ # complete.
+ thread.join(2)
+
+ @sock.should be_an_instance_of(Socket)
+ addr.should be_an_instance_of(Addrinfo)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/tcp_server_sockets_spec.rb b/spec/ruby/library/socket/socket/tcp_server_sockets_spec.rb
new file mode 100644
index 0000000000..10c030a8ce
--- /dev/null
+++ b/spec/ruby/library/socket/socket/tcp_server_sockets_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../spec_helper'
+
+describe 'Socket.tcp_server_sockets' do
+ describe 'without a block' do
+ before do
+ @sockets = nil
+ end
+
+ after do
+ @sockets.each(&:close)
+ end
+
+ it 'returns an Array of Socket objects' do
+ @sockets = Socket.tcp_server_sockets(0)
+
+ @sockets.should be_an_instance_of(Array)
+ @sockets[0].should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'with a block' do
+ it 'yields the sockets to the supplied block' do
+ Socket.tcp_server_sockets(0) do |sockets|
+ sockets.should be_an_instance_of(Array)
+ sockets[0].should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes all sockets after the block returns' do
+ sockets = nil
+
+ Socket.tcp_server_sockets(0) { |socks| sockets = socks }
+
+ sockets.each do |socket|
+ socket.closed?.should == true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/tcp_spec.rb b/spec/ruby/library/socket/socket/tcp_spec.rb
new file mode 100644
index 0000000000..29f166ffc5
--- /dev/null
+++ b/spec/ruby/library/socket/socket/tcp_spec.rb
@@ -0,0 +1,70 @@
+require_relative '../spec_helper'
+
+describe 'Socket.tcp' do
+ before do
+ @server = Socket.new(:INET, :STREAM)
+ @client = nil
+
+ @server.bind(Socket.sockaddr_in(0, '127.0.0.1'))
+ @server.listen(1)
+
+ @host = @server.connect_address.ip_address
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @client.close if @client && !@client.closed?
+ @client = nil
+
+ @server.close
+ end
+
+ it 'returns a Socket when no block is given' do
+ @client = Socket.tcp(@host, @port)
+
+ @client.should be_an_instance_of(Socket)
+ end
+
+ it 'yields the Socket when a block is given' do
+ Socket.tcp(@host, @port) do |socket|
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes the Socket automatically when a block is given' do
+ Socket.tcp(@host, @port) do |socket|
+ @socket = socket
+ end
+
+ @socket.closed?.should == true
+ end
+
+ it 'binds to a local address and port when specified' do
+ @client = Socket.tcp(@host, @port, @host, 0)
+
+ @client.local_address.ip_address.should == @host
+
+ @client.local_address.ip_port.should > 0
+ @client.local_address.ip_port.should_not == @port
+ end
+
+ it 'raises ArgumentError when 6 arguments are provided' do
+ lambda {
+ Socket.tcp(@host, @port, @host, 0, {:connect_timeout => 1}, 10)
+ }.should raise_error(ArgumentError)
+ end
+
+ it 'connects to the server' do
+ @client = Socket.tcp(@host, @port)
+
+ @client.write('hello')
+
+ connection, _ = @server.accept
+
+ begin
+ connection.recv(5).should == 'hello'
+ ensure
+ connection.close
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/udp_server_loop_on_spec.rb b/spec/ruby/library/socket/socket/udp_server_loop_on_spec.rb
new file mode 100644
index 0000000000..1cb82d72be
--- /dev/null
+++ b/spec/ruby/library/socket/socket/udp_server_loop_on_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../spec_helper'
+
+describe 'Socket.udp_server_loop_on' do
+ before do
+ @server = Socket.new(:INET, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, '127.0.0.1'))
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'when no connections are available' do
+ it 'blocks the caller' do
+ lambda { Socket.udp_server_loop_on([@server]) }.should block_caller
+ end
+ end
+
+ describe 'when a connection is available' do
+ before do
+ @client = Socket.new(:INET, :DGRAM)
+ end
+
+ after do
+ @client.close
+ end
+
+ it 'yields the message and a Socket::UDPSource' do
+ msg = nil
+ src = nil
+
+ @client.connect(@server.getsockname)
+ @client.write('hello')
+
+ Socket.udp_server_loop_on([@server]) do |message, source|
+ msg = message
+ src = source
+
+ break
+ end
+
+ msg.should == 'hello'
+ src.should be_an_instance_of(Socket::UDPSource)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/udp_server_loop_spec.rb b/spec/ruby/library/socket/socket/udp_server_loop_spec.rb
new file mode 100644
index 0000000000..95f73a5c35
--- /dev/null
+++ b/spec/ruby/library/socket/socket/udp_server_loop_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'Socket.udp_server_loop' do
+ describe 'when no connections are available' do
+ it 'blocks the caller' do
+ lambda { Socket.udp_server_loop('127.0.0.1', 0) }.should block_caller
+ end
+ end
+
+ describe 'when a connection is available' do
+ before do
+ @client = Socket.new(:INET, :DGRAM)
+ @port = 9997
+ end
+
+ after do
+ @client.close
+ end
+
+ it 'yields the message and a Socket::UDPSource' do
+ msg, src = nil
+
+ Thread.new do
+ Socket.udp_server_loop('127.0.0.1', @port) do |message, source|
+ msg = message
+ src = source
+
+ break
+ end
+ end
+
+ # Because this will return even if the server is up and running (it's UDP
+ # after all) we'll have to write and wait until "msg" is set.
+ @client.connect(Socket.sockaddr_in(@port, '127.0.0.1'))
+
+ SocketSpecs.loop_with_timeout do
+ SocketSpecs.wait_until_success { @client.write('hello') }
+
+ break if msg
+ end
+
+ msg.should == 'hello'
+ src.should be_an_instance_of(Socket::UDPSource)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/udp_server_recv_spec.rb b/spec/ruby/library/socket/socket/udp_server_recv_spec.rb
new file mode 100644
index 0000000000..05fef4bca8
--- /dev/null
+++ b/spec/ruby/library/socket/socket/udp_server_recv_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../spec_helper'
+
+describe 'Socket.udp_server_recv' do
+ before do
+ @server = Socket.new(:INET, :DGRAM)
+ @client = Socket.new(:INET, :DGRAM)
+
+ @server.bind(Socket.sockaddr_in(0, '127.0.0.1'))
+ @client.connect(@server.getsockname)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'yields the message and a Socket::UDPSource' do
+ msg = nil
+ src = nil
+
+ @client.write('hello')
+
+ Socket.udp_server_recv([@server]) do |message, source|
+ msg = message
+ src = source
+ break
+ end
+
+ msg.should == 'hello'
+ src.should be_an_instance_of(Socket::UDPSource)
+ end
+end
diff --git a/spec/ruby/library/socket/socket/udp_server_sockets_spec.rb b/spec/ruby/library/socket/socket/udp_server_sockets_spec.rb
new file mode 100644
index 0000000000..3aeb472dda
--- /dev/null
+++ b/spec/ruby/library/socket/socket/udp_server_sockets_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../spec_helper'
+
+describe 'Socket.udp_server_sockets' do
+ describe 'without a block' do
+ before do
+ @sockets = nil
+ end
+
+ after do
+ @sockets.each(&:close)
+ end
+
+ it 'returns an Array of Socket objects' do
+ @sockets = Socket.udp_server_sockets(0)
+
+ @sockets.should be_an_instance_of(Array)
+ @sockets[0].should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'with a block' do
+ it 'yields the sockets to the supplied block' do
+ Socket.udp_server_sockets(0) do |sockets|
+ sockets.should be_an_instance_of(Array)
+ sockets[0].should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes all sockets after the block returns' do
+ sockets = nil
+
+ Socket.udp_server_sockets(0) { |socks| sockets = socks }
+
+ sockets.each do |socket|
+ socket.closed?.should == true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/unix_server_loop_spec.rb b/spec/ruby/library/socket/socket/unix_server_loop_spec.rb
new file mode 100644
index 0000000000..52c535cd45
--- /dev/null
+++ b/spec/ruby/library/socket/socket/unix_server_loop_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'Socket.unix_server_loop' do
+ before do
+ @path = SocketSpecs.socket_path
+ end
+
+ after do
+ rm_r(@path) if File.file?(@path)
+ end
+
+ describe 'when no connections are available' do
+ it 'blocks the caller' do
+ lambda { Socket.unix_server_loop(@path) }.should block_caller
+ end
+ end
+
+ describe 'when a connection is available' do
+ before do
+ @client = nil
+ end
+
+ after do
+ @sock.close if @sock
+ @client.close if @client
+ end
+
+ it 'yields a Socket and an Addrinfo' do
+ @sock, addr = nil
+
+ thread = Thread.new do
+ Socket.unix_server_loop(@path) do |socket, addrinfo|
+ @sock = socket
+ addr = addrinfo
+
+ break
+ end
+ end
+
+ @client = SocketSpecs.wait_until_success { Socket.unix(@path) }
+
+ thread.join(2)
+
+ @sock.should be_an_instance_of(Socket)
+ addr.should be_an_instance_of(Addrinfo)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/unix_server_socket_spec.rb b/spec/ruby/library/socket/socket/unix_server_socket_spec.rb
new file mode 100644
index 0000000000..fc357740fa
--- /dev/null
+++ b/spec/ruby/library/socket/socket/unix_server_socket_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'Socket.unix_server_socket' do
+ before do
+ @path = SocketSpecs.socket_path
+ end
+
+ after do
+ rm_r(@path)
+ end
+
+ describe 'when no block is given' do
+ before do
+ @socket = nil
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'returns a Socket' do
+ @socket = Socket.unix_server_socket(@path)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'when a block is given' do
+ it 'yields a Socket' do
+ Socket.unix_server_socket(@path) do |sock|
+ sock.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes the Socket when the block returns' do
+ socket = nil
+
+ Socket.unix_server_socket(@path) do |sock|
+ socket = sock
+ end
+
+ socket.should be_an_instance_of(Socket)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/unix_spec.rb b/spec/ruby/library/socket/socket/unix_spec.rb
new file mode 100644
index 0000000000..add54a097d
--- /dev/null
+++ b/spec/ruby/library/socket/socket/unix_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'Socket.unix' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @socket = nil
+ end
+
+ after do
+ @server.close
+ @socket.close if @socket
+
+ rm_r(@path)
+ end
+
+ describe 'when no block is given' do
+ it 'returns a Socket' do
+ @socket = Socket.unix(@path)
+
+ @socket.should be_an_instance_of(Socket)
+ end
+ end
+
+ describe 'when a block is given' do
+ it 'yields a Socket' do
+ Socket.unix(@path) do |sock|
+ sock.should be_an_instance_of(Socket)
+ end
+ end
+
+ it 'closes the Socket when the block returns' do
+ socket = nil
+
+ Socket.unix(@path) do |sock|
+ socket = sock
+ end
+
+ socket.closed?.should == true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/unpack_sockaddr_in_spec.rb b/spec/ruby/library/socket/socket/unpack_sockaddr_in_spec.rb
index c76a2c54de..579ae307df 100644
--- a/spec/ruby/library/socket/socket/unpack_sockaddr_in_spec.rb
+++ b/spec/ruby/library/socket/socket/unpack_sockaddr_in_spec.rb
@@ -1,9 +1,7 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-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 3333, '127.0.0.1'
Socket.unpack_sockaddr_in(sockaddr).should == [3333, '127.0.0.1']
@@ -14,7 +12,27 @@ describe "Socket.unpack_sockaddr_in" do
Socket.unpack_sockaddr_in(addrinfo).should == [3333, '127.0.0.1']
end
- platform_is_not :windows do
+ describe 'using an IPv4 address' do
+ it 'returns an Array containing the port and IP address' do
+ port = 80
+ ip = '127.0.0.1'
+ addr = Socket.pack_sockaddr_in(port, ip)
+
+ Socket.unpack_sockaddr_in(addr).should == [port, ip]
+ end
+ end
+
+ describe 'using an IPv6 address' do
+ it 'returns an Array containing the port and IP address' do
+ port = 80
+ ip = '::1'
+ addr = Socket.pack_sockaddr_in(port, ip)
+
+ Socket.unpack_sockaddr_in(addr).should == [port, ip]
+ end
+ end
+
+ with_feature :unix_socket 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)
@@ -25,5 +43,4 @@ describe "Socket.unpack_sockaddr_in" do
lambda { Socket.unpack_sockaddr_in(addrinfo) }.should raise_error(ArgumentError)
end
end
-
end
diff --git a/spec/ruby/library/socket/socket/unpack_sockaddr_un_spec.rb b/spec/ruby/library/socket/socket/unpack_sockaddr_un_spec.rb
index 8dd68b9f99..39e33c90e9 100644
--- a/spec/ruby/library/socket/socket/unpack_sockaddr_un_spec.rb
+++ b/spec/ruby/library/socket/socket/unpack_sockaddr_un_spec.rb
@@ -1,8 +1,8 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-describe 'Socket.unpack_sockaddr_un' do
- platform_is_not :windows do
+with_feature :unix_socket do
+ describe 'Socket.unpack_sockaddr_un' do
it 'decodes sockaddr to unix path' do
sockaddr = Socket.sockaddr_un('/tmp/sock')
Socket.unpack_sockaddr_un(sockaddr).should == '/tmp/sock'
diff --git a/spec/ruby/library/socket/spec_helper.rb b/spec/ruby/library/socket/spec_helper.rb
new file mode 100644
index 0000000000..5a6dea7aa7
--- /dev/null
+++ b/spec/ruby/library/socket/spec_helper.rb
@@ -0,0 +1,16 @@
+require_relative '../../spec_helper'
+require 'socket'
+
+if %w[rbx truffleruby].include?(RUBY_ENGINE)
+ MSpec.enable_feature :pure_ruby_addrinfo
+end
+
+MSpec.enable_feature :sock_packet if Socket.const_defined?(:SOCK_PACKET)
+MSpec.enable_feature :unix_socket unless PlatformGuard.windows?
+MSpec.enable_feature :udp_cork if Socket.const_defined?(:UDP_CORK)
+MSpec.enable_feature :tcp_cork if Socket.const_defined?(:TCP_CORK)
+MSpec.enable_feature :ipv6_pktinfo if Socket.const_defined?(:IPV6_PKTINFO)
+MSpec.enable_feature :ip_mtu if Socket.const_defined?(:IP_MTU)
+MSpec.enable_feature :ipv6_nexthop if Socket.const_defined?(:IPV6_NEXTHOP)
+MSpec.enable_feature :tcp_info if Socket.const_defined?(:TCP_INFO)
+MSpec.enable_feature :ancillary_data if Socket.const_defined?(:AncillaryData)
diff --git a/spec/ruby/library/socket/tcpserver/accept_nonblock_spec.rb b/spec/ruby/library/socket/tcpserver/accept_nonblock_spec.rb
index 8566081d2f..27d6a0d08e 100644
--- a/spec/ruby/library/socket/tcpserver/accept_nonblock_spec.rb
+++ b/spec/ruby/library/socket/tcpserver/accept_nonblock_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket::TCPServer.accept_nonblock" do
@@ -46,3 +46,39 @@ describe "Socket::TCPServer.accept_nonblock" do
end
end
end
+
+describe 'TCPServer#accept_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'without a connected client' do
+ it 'raises IO::WaitReadable' do
+ lambda { @server.accept_nonblock }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ platform_is_not :windows do # spurious
+ describe 'with a connected client' do
+ before do
+ @client = TCPSocket.new(ip_address, @server.connect_address.ip_port)
+ end
+
+ after do
+ @socket.close if @socket
+ @client.close
+ end
+
+ it 'returns a TCPSocket' do
+ @socket = @server.accept_nonblock
+ @socket.should be_an_instance_of(TCPSocket)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpserver/accept_spec.rb b/spec/ruby/library/socket/tcpserver/accept_spec.rb
index b374899409..f0ef9cc727 100644
--- a/spec/ruby/library/socket/tcpserver/accept_spec.rb
+++ b/spec/ruby/library/socket/tcpserver/accept_spec.rb
@@ -1,7 +1,6 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-
describe "TCPServer#accept" do
before :each do
@server = TCPServer.new("127.0.0.1", 0)
@@ -64,3 +63,37 @@ describe "TCPServer#accept" do
lambda { @server.accept }.should raise_error(IOError)
end
end
+
+describe 'TCPServer#accept' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'without a connected client' do
+ it 'blocks the caller' do
+ lambda { @server.accept }.should block_caller
+ end
+ end
+
+ describe 'with a connected client' do
+ before do
+ @client = TCPSocket.new(ip_address, @server.connect_address.ip_port)
+ end
+
+ after do
+ @socket.close if @socket
+ @client.close
+ end
+
+ it 'returns a TCPSocket' do
+ @socket = @server.accept
+ @socket.should be_an_instance_of(TCPSocket)
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpserver/gets_spec.rb b/spec/ruby/library/socket/tcpserver/gets_spec.rb
index 6324986111..936295dce2 100644
--- a/spec/ruby/library/socket/tcpserver/gets_spec.rb
+++ b/spec/ruby/library/socket/tcpserver/gets_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "TCPServer#gets" do
diff --git a/spec/ruby/library/socket/tcpserver/initialize_spec.rb b/spec/ruby/library/socket/tcpserver/initialize_spec.rb
new file mode 100644
index 0000000000..2de8c764b2
--- /dev/null
+++ b/spec/ruby/library/socket/tcpserver/initialize_spec.rb
@@ -0,0 +1,99 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'TCPServer#initialize' do
+ describe 'with a single Fixnum argument' do
+ before do
+ @server = TCPServer.new(0)
+ end
+
+ after do
+ @server.close
+ end
+
+ it 'sets the port to the given argument' do
+ @server.local_address.ip_port.should be_an_instance_of(Fixnum)
+ @server.local_address.ip_port.should > 0
+ end
+
+ platform_is_not :windows do
+ it 'sets the hostname to 0.0.0.0' do
+ @server.local_address.ip_address.should == '0.0.0.0'
+ end
+ end
+
+ it "sets the socket to binmode" do
+ @server.binmode?.should be_true
+ end
+ end
+
+ describe 'with a single String argument containing a numeric value' do
+ before do
+ @server = TCPServer.new('0')
+ end
+
+ after do
+ @server.close
+ end
+
+ it 'sets the port to the given argument' do
+ @server.local_address.ip_port.should be_an_instance_of(Fixnum)
+ @server.local_address.ip_port.should > 0
+ end
+
+ platform_is_not :windows do
+ it 'sets the hostname to 0.0.0.0' do
+ @server.local_address.ip_address.should == '0.0.0.0'
+ end
+ end
+ end
+
+ describe 'with a single String argument containing a non numeric value' do
+ it 'raises SocketError' do
+ lambda { TCPServer.new('cats') }.should raise_error(SocketError)
+ end
+ end
+
+ describe 'with a String and a Fixnum' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ end
+
+ after do
+ @server.close
+ end
+
+ it 'sets the port to the given port argument' do
+ @server.local_address.ip_port.should be_an_instance_of(Fixnum)
+ @server.local_address.ip_port.should > 0
+ end
+
+ it 'sets the hostname to the given host argument' do
+ @server.local_address.ip_address.should == ip_address
+ end
+ end
+ end
+
+ describe 'with a String and a custom object' do
+ before do
+ dummy = mock(:dummy)
+ dummy.stub!(:to_str).and_return('0')
+
+ @server = TCPServer.new('127.0.0.1', dummy)
+ end
+
+ after do
+ @server.close
+ end
+
+ it 'sets the port to the given port argument' do
+ @server.local_address.ip_port.should be_an_instance_of(Fixnum)
+ @server.local_address.ip_port.should > 0
+ end
+
+ it 'sets the hostname to the given host argument' do
+ @server.local_address.ip_address.should == '127.0.0.1'
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpserver/listen_spec.rb b/spec/ruby/library/socket/tcpserver/listen_spec.rb
index 77a1120158..1e17de06f1 100644
--- a/spec/ruby/library/socket/tcpserver/listen_spec.rb
+++ b/spec/ruby/library/socket/tcpserver/listen_spec.rb
@@ -1,18 +1,22 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-require 'socket'
-
describe 'TCPServer#listen' do
- before :each do
- @server = TCPServer.new(SocketSpecs.hostname, 0)
- end
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ end
- after :each do
- @server.close unless @server.closed?
- end
+ after do
+ @server.close
+ end
+
+ it 'returns 0' do
+ @server.listen(1).should == 0
+ end
- it 'returns 0' do
- @server.listen(10).should == 0
+ it "raises when the given argument can't be coerced to a Fixnum" do
+ lambda { @server.listen('cats') }.should raise_error(TypeError)
+ end
end
end
diff --git a/spec/ruby/library/socket/tcpserver/new_spec.rb b/spec/ruby/library/socket/tcpserver/new_spec.rb
index 7504fbe128..94033411e3 100644
--- a/spec/ruby/library/socket/tcpserver/new_spec.rb
+++ b/spec/ruby/library/socket/tcpserver/new_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "TCPServer.new" do
@@ -25,7 +25,7 @@ describe "TCPServer.new" do
addr[2].should =~ /^#{SocketSpecs.hostname}\b/
addr[3].should == '127.0.0.1'
else
- addr[2].should =~ /^#{SocketSpecs.hostnamev6}\b/
+ addr[2].should =~ /^#{SocketSpecs.hostname('::1')}\b/
addr[3].should == '::1'
end
end
diff --git a/spec/ruby/library/socket/tcpserver/sysaccept_spec.rb b/spec/ruby/library/socket/tcpserver/sysaccept_spec.rb
index aa6cb2bb50..144b6806f1 100644
--- a/spec/ruby/library/socket/tcpserver/sysaccept_spec.rb
+++ b/spec/ruby/library/socket/tcpserver/sysaccept_spec.rb
@@ -1,8 +1,6 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-require 'socket'
-
describe "TCPServer#sysaccept" do
before :each do
@server = TCPServer.new(SocketSpecs.hostname, 0)
@@ -30,3 +28,39 @@ describe "TCPServer#sysaccept" do
end
end
end
+
+describe 'TCPServer#sysaccept' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'without a connected client' do
+ it 'blocks the caller' do
+ lambda { @server.sysaccept }.should block_caller
+ end
+ end
+
+ describe 'with a connected client' do
+ before do
+ @client = TCPSocket.new(ip_address, @server.connect_address.ip_port)
+ end
+
+ after do
+ Socket.for_fd(@fd).close if @fd
+ @client.close
+ end
+
+ it 'returns a new file descriptor as a Fixnum' do
+ @fd = @server.sysaccept
+
+ @fd.should be_an_instance_of(Fixnum)
+ @fd.should_not == @client.fileno
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/gethostbyname_spec.rb b/spec/ruby/library/socket/tcpsocket/gethostbyname_spec.rb
index 9f25280a70..230d14bfb0 100644
--- a/spec/ruby/library/socket/tcpsocket/gethostbyname_spec.rb
+++ b/spec/ruby/library/socket/tcpsocket/gethostbyname_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
# TODO: verify these for windows
@@ -49,3 +49,63 @@ describe "TCPSocket#gethostbyname" do
@host_info[1].should be_kind_of(Array)
end
end
+
+describe 'TCPSocket#gethostbyname' do
+ it 'returns an Array' do
+ TCPSocket.gethostbyname('127.0.0.1').should be_an_instance_of(Array)
+ end
+
+ describe 'using a hostname' do
+ describe 'the returned Array' do
+ before do
+ @array = TCPSocket.gethostbyname('127.0.0.1')
+ end
+
+ it 'includes the canonical name as the 1st value' do
+ @array[0].should == '127.0.0.1'
+ end
+
+ it 'includes an array of alternative hostnames as the 2nd value' do
+ @array[1].should be_an_instance_of(Array)
+ end
+
+ it 'includes the address family as the 3rd value' do
+ @array[2].should be_an_instance_of(Fixnum)
+ end
+
+ it 'includes the IP addresses as all the remaining values' do
+ ips = %w{::1 127.0.0.1}
+
+ ips.include?(@array[3]).should == true
+
+ # Not all machines might have both IPv4 and IPv6 set up, so this value is
+ # optional.
+ ips.include?(@array[4]).should == true if @array[4]
+ end
+ end
+ end
+
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'the returned Array' do
+ before do
+ @array = TCPSocket.gethostbyname(ip_address)
+ end
+
+ it 'includes the IP address as the 1st value' do
+ @array[0].should == ip_address
+ end
+
+ it 'includes an empty list of aliases as the 2nd value' do
+ @array[1].should == []
+ end
+
+ it 'includes the address family as the 3rd value' do
+ @array[2].should == family
+ end
+
+ it 'includes the IP address as the 4th value' do
+ @array[3].should == ip_address
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/initialize_spec.rb b/spec/ruby/library/socket/tcpsocket/initialize_spec.rb
new file mode 100644
index 0000000000..184f9abb7d
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/initialize_spec.rb
@@ -0,0 +1,61 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'TCPSocket#initialize' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ describe 'when no server is listening on the given address' do
+ it 'raises Errno::ECONNREFUSED' do
+ lambda { TCPSocket.new(ip_address, 666) }.should raise_error(Errno::ECONNREFUSED)
+ end
+ end
+
+ describe 'when a server is listening on the given address' do
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @client.close if @client
+ @server.close
+ end
+
+ it 'returns a TCPSocket when using a Fixnum as the port' do
+ @client = TCPSocket.new(ip_address, @port)
+ @client.should be_an_instance_of(TCPSocket)
+ end
+
+ it 'returns a TCPSocket when using a String as the port' do
+ @client = TCPSocket.new(ip_address, @port.to_s)
+ @client.should be_an_instance_of(TCPSocket)
+ end
+
+ it 'raises SocketError when the port number is a non numeric String' do
+ lambda { TCPSocket.new(ip_address, 'cats') }.should raise_error(SocketError)
+ end
+
+ it 'set the socket to binmode' do
+ @client = TCPSocket.new(ip_address, @port)
+ @client.binmode?.should be_true
+ end
+
+ it 'connects to the right address' do
+ @client = TCPSocket.new(ip_address, @port)
+
+ @client.remote_address.ip_address.should == @server.local_address.ip_address
+ @client.remote_address.ip_port.should == @server.local_address.ip_port
+ end
+
+ describe 'using a local address and service' do
+ it 'binds the client socket to the local address and service' do
+ @client = TCPSocket.new(ip_address, @port, ip_address, 0)
+
+ @client.local_address.ip_address.should == ip_address
+
+ @client.local_address.ip_port.should > 0
+ @client.local_address.ip_port.should_not == @port
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/local_address_spec.rb b/spec/ruby/library/socket/tcpsocket/local_address_spec.rb
new file mode 100644
index 0000000000..ce66d5ff8f
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/local_address_spec.rb
@@ -0,0 +1,73 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'TCPSocket#local_address' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @host = @server.connect_address.ip_address
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'using an explicit hostname' do
+ before do
+ @sock = TCPSocket.new(@host, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.local_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_INET as the address family' do
+ @sock.local_address.afamily.should == family
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @sock.local_address.pfamily.should == family
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.local_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses the correct IP address' do
+ @sock.local_address.ip_address.should == @host
+ end
+
+ it 'uses a randomly assigned local port' do
+ @sock.local_address.ip_port.should > 0
+ @sock.local_address.ip_port.should_not == @port
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.local_address.protocol.should == 0
+ end
+ end
+ end
+
+ describe 'using an implicit hostname' do
+ before do
+ @sock = TCPSocket.new(nil, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct IP address' do
+ @sock.local_address.ip_address.should == @host
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb b/spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb
index 9416d5c71d..a381627a39 100644
--- a/spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb
+++ b/spec/ruby/library/socket/tcpsocket/partially_closable_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/partially_closable_sockets'
diff --git a/spec/ruby/library/socket/tcpsocket/recv_nonblock_spec.rb b/spec/ruby/library/socket/tcpsocket/recv_nonblock_spec.rb
index 23bba526e8..bfd815c658 100644
--- a/spec/ruby/library/socket/tcpsocket/recv_nonblock_spec.rb
+++ b/spec/ruby/library/socket/tcpsocket/recv_nonblock_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "TCPSocket#recv_nonblock" do
diff --git a/spec/ruby/library/socket/tcpsocket/recv_spec.rb b/spec/ruby/library/socket/tcpsocket/recv_spec.rb
new file mode 100644
index 0000000000..f380db670d
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/recv_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'TCPSocket#recv' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @client = TCPSocket.new(ip_address, @server.connect_address.ip_port)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns the message data' do
+ @client.write('hello')
+
+ socket = @server.accept
+
+ begin
+ socket.recv(5).should == 'hello'
+ ensure
+ socket.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/remote_address_spec.rb b/spec/ruby/library/socket/tcpsocket/remote_address_spec.rb
new file mode 100644
index 0000000000..eb9dabc075
--- /dev/null
+++ b/spec/ruby/library/socket/tcpsocket/remote_address_spec.rb
@@ -0,0 +1,72 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'TCPSocket#remote_address' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = TCPServer.new(ip_address, 0)
+ @host = @server.connect_address.ip_address
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'using an explicit hostname' do
+ before do
+ @sock = TCPSocket.new(@host, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.remote_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_INET as the address family' do
+ @sock.remote_address.afamily.should == family
+ end
+
+ it 'uses PF_INET as the protocol family' do
+ @sock.remote_address.pfamily.should == family
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @sock.remote_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses the correct IP address' do
+ @sock.remote_address.ip_address.should == @host
+ end
+
+ it 'uses the correct port' do
+ @sock.remote_address.ip_port.should == @port
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.remote_address.protocol.should == 0
+ end
+ end
+ end
+
+ describe 'using an implicit hostname' do
+ before do
+ @sock = TCPSocket.new(nil, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct IP address' do
+ @sock.remote_address.ip_address.should == @host
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/tcpsocket/setsockopt_spec.rb b/spec/ruby/library/socket/tcpsocket/setsockopt_spec.rb
index c9b0ff16dc..8b728b7522 100644
--- a/spec/ruby/library/socket/tcpsocket/setsockopt_spec.rb
+++ b/spec/ruby/library/socket/tcpsocket/setsockopt_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "TCPSocket#setsockopt" do
diff --git a/spec/ruby/library/socket/tcpsocket/shared/new.rb b/spec/ruby/library/socket/tcpsocket/shared/new.rb
index c66479a2d8..818bd69a91 100644
--- a/spec/ruby/library/socket/tcpsocket/shared/new.rb
+++ b/spec/ruby/library/socket/tcpsocket/shared/new.rb
@@ -1,4 +1,4 @@
-require_relative '../../../../spec_helper'
+require_relative '../../spec_helper'
require_relative '../../fixtures/classes'
describe :tcpsocket_new, shared: true do
diff --git a/spec/ruby/library/socket/udpsocket/bind_spec.rb b/spec/ruby/library/socket/udpsocket/bind_spec.rb
index be62608e1b..4dbdb285f6 100644
--- a/spec/ruby/library/socket/udpsocket/bind_spec.rb
+++ b/spec/ruby/library/socket/udpsocket/bind_spec.rb
@@ -1,8 +1,7 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-describe "UDPSocket.bind" do
-
+describe "UDPSocket#bind" do
before :each do
@socket = UDPSocket.new
end
@@ -34,9 +33,51 @@ describe "UDPSocket.bind" do
end
it "binds to INADDR_ANY if the hostname is empty" do
- @socket.bind("", 0)
+ @socket.bind("", 0).should == 0
port, host = Socket.unpack_sockaddr_in(@socket.getsockname)
host.should == "0.0.0.0"
port.should == @socket.addr[1]
end
end
+
+describe 'UDPSocket#bind' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @socket = UDPSocket.new(family)
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'binds to an address and port' do
+ @socket.bind(ip_address, 0).should == 0
+
+ @socket.local_address.ip_address.should == ip_address
+ @socket.local_address.ip_port.should > 0
+ end
+
+ it 'binds to an address and port using String arguments' do
+ @socket.bind(ip_address, '0').should == 0
+
+ @socket.local_address.ip_address.should == ip_address
+ @socket.local_address.ip_port.should > 0
+ end
+
+ it 'can receive data after being bound to an address' do
+ @socket.bind(ip_address, 0)
+
+ addr = @socket.connect_address
+ client = UDPSocket.new(family)
+
+ client.connect(addr.ip_address, addr.ip_port)
+ client.write('hello')
+
+ begin
+ @socket.recv(6).should == 'hello'
+ ensure
+ client.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/connect_spec.rb b/spec/ruby/library/socket/udpsocket/connect_spec.rb
new file mode 100644
index 0000000000..d92bdeb981
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/connect_spec.rb
@@ -0,0 +1,35 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UDPSocket#connect' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @socket = UDPSocket.new(family)
+ end
+
+ after do
+ @socket.close
+ end
+
+ it 'connects to an address even when it is not used' do
+ @socket.connect(ip_address, 9996).should == 0
+ end
+
+ it 'can send data after connecting' do
+ receiver = UDPSocket.new(family)
+
+ receiver.bind(ip_address, 0)
+
+ addr = receiver.connect_address
+
+ @socket.connect(addr.ip_address, addr.ip_port)
+ @socket.write('hello')
+
+ begin
+ receiver.recv(6).should == 'hello'
+ ensure
+ receiver.close
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/initialize_spec.rb b/spec/ruby/library/socket/udpsocket/initialize_spec.rb
new file mode 100644
index 0000000000..5df3bf9800
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/initialize_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../spec_helper'
+
+describe 'UDPSocket#initialize' do
+ after do
+ @socket.close if @socket
+ end
+
+ it 'initializes a new UDPSocket' do
+ @socket = UDPSocket.new
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'initializes a new UDPSocket using a Fixnum' do
+ @socket = UDPSocket.new(Socket::AF_INET)
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'initializes a new UDPSocket using a Symbol' do
+ @socket = UDPSocket.new(:INET)
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'initializes a new UDPSocket using a String' do
+ @socket = UDPSocket.new('INET')
+ @socket.should be_an_instance_of(UDPSocket)
+ end
+
+ it 'sets the socket to binmode' do
+ @socket = UDPSocket.new(:INET)
+ @socket.binmode?.should be_true
+ end
+
+ it 'raises Errno::EAFNOSUPPORT when given an invalid address family' do
+ lambda { UDPSocket.new(666) }.should raise_error(Errno::EAFNOSUPPORT)
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/inspect_spec.rb b/spec/ruby/library/socket/udpsocket/inspect_spec.rb
new file mode 100644
index 0000000000..201e8b3fc6
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/inspect_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../spec_helper'
+
+describe 'UDPSocket#inspect' do
+ before do
+ @socket = UDPSocket.new
+ @socket.bind('127.0.0.1', 0)
+ end
+
+ after do
+ @socket.close
+ end
+
+ ruby_version_is ""..."2.5" do
+ it 'returns a String with the fd' do
+ @socket.inspect.should == "#<UDPSocket:fd #{@socket.fileno}>"
+ end
+ end
+
+ ruby_version_is "2.5" do
+ it 'returns a String with the fd, family, address and port' do
+ port = @socket.addr[1]
+ @socket.inspect.should == "#<UDPSocket:fd #{@socket.fileno}, AF_INET, 127.0.0.1, #{port}>"
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/local_address_spec.rb b/spec/ruby/library/socket/udpsocket/local_address_spec.rb
new file mode 100644
index 0000000000..92e4cc10c7
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/local_address_spec.rb
@@ -0,0 +1,80 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UDPSocket#local_address' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :DGRAM, Socket::IPPROTO_UDP)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+
+ @host = @server.connect_address.ip_address
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'using an explicit hostname' do
+ before do
+ @sock = UDPSocket.new(family)
+
+ @sock.connect(@host, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.local_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct address family' do
+ @sock.local_address.afamily.should == family
+ end
+
+ it 'uses the correct protocol family' do
+ @sock.local_address.pfamily.should == family
+ end
+
+ it 'uses SOCK_DGRAM as the socket type' do
+ @sock.local_address.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses the correct IP address' do
+ @sock.local_address.ip_address.should == @host
+ end
+
+ it 'uses a randomly assigned local port' do
+ @sock.local_address.ip_port.should > 0
+ @sock.local_address.ip_port.should_not == @port
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.local_address.protocol.should == 0
+ end
+ end
+ end
+
+ describe 'using an implicit hostname' do
+ before do
+ @sock = UDPSocket.new(family)
+
+ @sock.connect(nil, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct IP address' do
+ @sock.local_address.ip_address.should == @host
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/new_spec.rb b/spec/ruby/library/socket/udpsocket/new_spec.rb
index b2662277c4..30e7103d16 100644
--- a/spec/ruby/library/socket/udpsocket/new_spec.rb
+++ b/spec/ruby/library/socket/udpsocket/new_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe 'UDPSocket.new' do
diff --git a/spec/ruby/library/socket/udpsocket/open_spec.rb b/spec/ruby/library/socket/udpsocket/open_spec.rb
index 2db17fe090..e4dbb2ee2a 100644
--- a/spec/ruby/library/socket/udpsocket/open_spec.rb
+++ b/spec/ruby/library/socket/udpsocket/open_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "UDPSocket.open" do
diff --git a/spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb b/spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb
new file mode 100644
index 0000000000..48b7360035
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/recvfrom_nonblock_spec.rb
@@ -0,0 +1,88 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UDPSocket#recvfrom_nonblock' do
+ SocketSpecs.each_ip_protocol do |family, ip_address, family_name|
+ before do
+ @server = UDPSocket.new(family)
+ @client = UDPSocket.new(family)
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ platform_is_not :windows do
+ describe 'using an unbound socket' do
+ it 'raises IO::WaitReadable' do
+ lambda { @server.recvfrom_nonblock(1) }.should raise_error(IO::WaitReadable)
+ end
+ end
+ end
+
+ describe 'using a bound socket' do
+ before do
+ @server.bind(ip_address, 0)
+
+ addr = @server.connect_address
+
+ @client.connect(addr.ip_address, addr.ip_port)
+ end
+
+ describe 'without any data available' do
+ it 'raises IO::WaitReadable' do
+ lambda { @server.recvfrom_nonblock(1) }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ platform_is_not :windows do
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ end
+
+ it 'returns an Array containing the data and an Array' do
+ @server.recvfrom_nonblock(1).should be_an_instance_of(Array)
+ end
+
+ describe 'the returned Array' do
+ before do
+ @array = @server.recvfrom_nonblock(1)
+ end
+
+ it 'contains the data at index 0' do
+ @array[0].should == 'h'
+ end
+
+ it 'contains an Array at index 1' do
+ @array[1].should be_an_instance_of(Array)
+ end
+ end
+
+ describe 'the returned address Array' do
+ before do
+ @addr = @server.recvfrom_nonblock(1)[1]
+ end
+
+ it 'uses the correct address family' do
+ @addr[0].should == family_name
+ end
+
+ it 'uses the port of the client' do
+ @addr[1].should == @client.local_address.ip_port
+ end
+
+ it 'uses the hostname of the client' do
+ @addr[2].should == ip_address
+ end
+
+ it 'uses the IP address of the client' do
+ @addr[3].should == ip_address
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/remote_address_spec.rb b/spec/ruby/library/socket/udpsocket/remote_address_spec.rb
new file mode 100644
index 0000000000..94889ce560
--- /dev/null
+++ b/spec/ruby/library/socket/udpsocket/remote_address_spec.rb
@@ -0,0 +1,79 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+describe 'UDPSocket#remote_address' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = Socket.new(family, :DGRAM, Socket::IPPROTO_UDP)
+
+ @server.bind(Socket.sockaddr_in(0, ip_address))
+
+ @host = @server.connect_address.ip_address
+ @port = @server.connect_address.ip_port
+ end
+
+ after do
+ @server.close
+ end
+
+ describe 'using an explicit hostname' do
+ before do
+ @sock = UDPSocket.new(family)
+
+ @sock.connect(@host, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ it 'returns an Addrinfo' do
+ @sock.remote_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct address family' do
+ @sock.remote_address.afamily.should == family
+ end
+
+ it 'uses the correct protocol family' do
+ @sock.remote_address.pfamily.should == family
+ end
+
+ it 'uses SOCK_DGRAM as the socket type' do
+ @sock.remote_address.socktype.should == Socket::SOCK_DGRAM
+ end
+
+ it 'uses the correct IP address' do
+ @sock.remote_address.ip_address.should == @host
+ end
+
+ it 'uses the correct port' do
+ @sock.remote_address.ip_port.should == @port
+ end
+
+ it 'uses 0 as the protocol' do
+ @sock.remote_address.protocol.should == 0
+ end
+ end
+ end
+
+ describe 'using an implicit hostname' do
+ before do
+ @sock = UDPSocket.new(family)
+
+ @sock.connect(nil, @port)
+ end
+
+ after do
+ @sock.close
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses the correct IP address' do
+ @sock.remote_address.ip_address.should == @host
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/send_spec.rb b/spec/ruby/library/socket/udpsocket/send_spec.rb
index 0159e25214..b84e84b056 100644
--- a/spec/ruby/library/socket/udpsocket/send_spec.rb
+++ b/spec/ruby/library/socket/udpsocket/send_spec.rb
@@ -1,7 +1,7 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
-describe "UDPSocket.send" do
+describe "UDPSocket#send" do
before :each do
@port = nil
@server_thread = Thread.new do
@@ -76,3 +76,79 @@ describe "UDPSocket.send" do
end
end
end
+
+describe 'UDPSocket#send' do
+ SocketSpecs.each_ip_protocol do |family, ip_address|
+ before do
+ @server = UDPSocket.new(family)
+ @client = UDPSocket.new(family)
+
+ @server.bind(ip_address, 0)
+
+ @addr = @server.connect_address
+ end
+
+ after do
+ @server.close
+ @client.close
+ end
+
+ describe 'using a disconnected socket' do
+ describe 'without a destination address' do
+ it "raises #{SocketSpecs.dest_addr_req_error}" do
+ lambda { @client.send('hello', 0) }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+
+ describe 'with a destination address as separate arguments' do
+ it 'returns the amount of sent bytes' do
+ @client.send('hello', 0, @addr.ip_address, @addr.ip_port).should == 5
+ end
+
+ it 'does not persist the connection after sending data' do
+ @client.send('hello', 0, @addr.ip_address, @addr.ip_port)
+
+ lambda { @client.send('hello', 0) }.should raise_error(SocketSpecs.dest_addr_req_error)
+ end
+ end
+
+ describe 'with a destination address as a single String argument' do
+ it 'returns the amount of sent bytes' do
+ @client.send('hello', 0, @server.getsockname).should == 5
+ end
+ end
+ end
+
+ describe 'using a connected socket' do
+ describe 'without an explicit destination address' do
+ before do
+ @client.connect(@addr.ip_address, @addr.ip_port)
+ end
+
+ it 'returns the amount of bytes written' do
+ @client.send('hello', 0).should == 5
+ end
+ end
+
+ describe 'with an explicit destination address' do
+ before do
+ @alt_server = UDPSocket.new(family)
+
+ @alt_server.bind(ip_address, 0)
+ end
+
+ after do
+ @alt_server.close
+ end
+
+ it 'sends the data to the given address instead' do
+ @client.send('hello', 0, @alt_server.getsockname).should == 5
+
+ lambda { @server.recv(5) }.should block_caller
+
+ @alt_server.recv(5).should == 'hello'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/udpsocket/write_spec.rb b/spec/ruby/library/socket/udpsocket/write_spec.rb
index c2a36eaa7a..e960de1baf 100644
--- a/spec/ruby/library/socket/udpsocket/write_spec.rb
+++ b/spec/ruby/library/socket/udpsocket/write_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "UDPSocket#write" do
diff --git a/spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb b/spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb
index 27603aeabd..3ebe38a090 100644
--- a/spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb
+++ b/spec/ruby/library/socket/unixserver/accept_nonblock_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "UNIXServer#accept_nonblock" do
@@ -34,3 +34,59 @@ describe "UNIXServer#accept_nonblock" do
end
end
end
+
+with_feature :unix_socket do
+ describe 'UNIXServer#accept_nonblock' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close
+ rm_r(@path)
+ end
+
+ describe 'without a client' do
+ it 'raises IO::WaitReadable' do
+ lambda { @server.accept_nonblock }.should raise_error(IO::WaitReadable)
+ end
+ end
+
+ describe 'with a client' do
+ before do
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @socket.close if @socket
+ end
+
+ describe 'without any data' do
+ it 'returns a UNIXSocket' do
+ @socket = @server.accept_nonblock
+ @socket.should be_an_instance_of(UNIXSocket)
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ end
+
+ it 'returns a UNIXSocket' do
+ @socket = @server.accept_nonblock
+ @socket.should be_an_instance_of(UNIXSocket)
+ end
+
+ describe 'the returned UNIXSocket' do
+ it 'can read the data written' do
+ @socket = @server.accept_nonblock
+ @socket.recv(5).should == 'hello'
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/accept_spec.rb b/spec/ruby/library/socket/unixserver/accept_spec.rb
index 3c34fce59a..a1570f7013 100644
--- a/spec/ruby/library/socket/unixserver/accept_spec.rb
+++ b/spec/ruby/library/socket/unixserver/accept_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
platform_is_not :windows do
@@ -59,3 +59,59 @@ platform_is_not :windows do
end
end
end
+
+with_feature :unix_socket do
+ describe 'UNIXServer#accept' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close
+ rm_r(@path)
+ end
+
+ describe 'without a client' do
+ it 'blocks the calling thread' do
+ lambda { @server.accept }.should block_caller
+ end
+ end
+
+ describe 'with a client' do
+ before do
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @socket.close if @socket
+ end
+
+ describe 'without any data' do
+ it 'returns a UNIXSocket' do
+ @socket = @server.accept
+ @socket.should be_an_instance_of(UNIXSocket)
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ end
+
+ it 'returns a UNIXSocket' do
+ @socket = @server.accept
+ @socket.should be_an_instance_of(UNIXSocket)
+ end
+
+ describe 'the returned UNIXSocket' do
+ it 'can read the data written' do
+ @socket = @server.accept
+ @socket.recv(5).should == 'hello'
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/for_fd_spec.rb b/spec/ruby/library/socket/unixserver/for_fd_spec.rb
index 9f243c50a8..4f3816ad37 100644
--- a/spec/ruby/library/socket/unixserver/for_fd_spec.rb
+++ b/spec/ruby/library/socket/unixserver/for_fd_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
platform_is_not :windows do
diff --git a/spec/ruby/library/socket/unixserver/initialize_spec.rb b/spec/ruby/library/socket/unixserver/initialize_spec.rb
new file mode 100644
index 0000000000..cb186ceb76
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/initialize_spec.rb
@@ -0,0 +1,28 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'UNIXServer#initialize' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close if @server
+ rm_r @path
+ end
+
+ it 'returns a new UNIXServer' do
+ @server.should be_an_instance_of(UNIXServer)
+ end
+
+ it 'sets the socket to binmode' do
+ @server.binmode?.should be_true
+ end
+
+ it 'raises Errno::EADDRINUSE when the socket is already in use' do
+ lambda { UNIXServer.new(@path) }.should raise_error(Errno::EADDRINUSE)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/listen_spec.rb b/spec/ruby/library/socket/unixserver/listen_spec.rb
new file mode 100644
index 0000000000..b90b3bbb09
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/listen_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'UNIXServer#listen' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close
+
+ rm_r(@path)
+ end
+
+ it 'returns 0' do
+ @server.listen(1).should == 0
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixserver/new_spec.rb b/spec/ruby/library/socket/unixserver/new_spec.rb
index 0523054952..f831f40bc6 100644
--- a/spec/ruby/library/socket/unixserver/new_spec.rb
+++ b/spec/ruby/library/socket/unixserver/new_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative 'shared/new'
describe "UNIXServer.new" do
diff --git a/spec/ruby/library/socket/unixserver/open_spec.rb b/spec/ruby/library/socket/unixserver/open_spec.rb
index 3bbfff42e5..f2506d9f6f 100644
--- a/spec/ruby/library/socket/unixserver/open_spec.rb
+++ b/spec/ruby/library/socket/unixserver/open_spec.rb
@@ -1,4 +1,5 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
require_relative 'shared/new'
describe "UNIXServer.open" do
diff --git a/spec/ruby/library/socket/unixserver/shared/new.rb b/spec/ruby/library/socket/unixserver/shared/new.rb
index e19a1582b6..35395826c9 100644
--- a/spec/ruby/library/socket/unixserver/shared/new.rb
+++ b/spec/ruby/library/socket/unixserver/shared/new.rb
@@ -1,6 +1,5 @@
-require_relative '../../../../spec_helper'
+require_relative '../../spec_helper'
require_relative '../../fixtures/classes'
-require 'tempfile'
describe :unixserver_new, shared: true do
platform_is_not :windows do
diff --git a/spec/ruby/library/socket/unixserver/sysaccept_spec.rb b/spec/ruby/library/socket/unixserver/sysaccept_spec.rb
new file mode 100644
index 0000000000..864913af82
--- /dev/null
+++ b/spec/ruby/library/socket/unixserver/sysaccept_spec.rb
@@ -0,0 +1,52 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'UNIXServer#sysaccept' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ end
+
+ after do
+ @server.close
+
+ rm_r(@path)
+ end
+
+ describe 'without a client' do
+ it 'blocks the calling thread' do
+ lambda { @server.sysaccept }.should block_caller
+ end
+ end
+
+ describe 'with a client' do
+ before do
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ Socket.for_fd(@fd).close if @fd
+ @client.close
+ end
+
+ describe 'without any data' do
+ it 'returns a Fixnum' do
+ @fd = @server.sysaccept
+ @fd.should be_an_instance_of(Fixnum)
+ end
+ end
+
+ describe 'with data available' do
+ before do
+ @client.write('hello')
+ end
+
+ it 'returns a Fixnum' do
+ @fd = @server.sysaccept
+ @fd.should be_an_instance_of(Fixnum)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/addr_spec.rb b/spec/ruby/library/socket/unixsocket/addr_spec.rb
index 4fd3e30532..e8431bea16 100644
--- a/spec/ruby/library/socket/unixsocket/addr_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/addr_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "UNIXSocket#addr" do
@@ -16,8 +16,13 @@ describe "UNIXSocket#addr" do
SocketSpecs.rm_socket @path
end
+ it "returns an array" do
+ @client.addr.should be_kind_of(Array)
+ end
+
it "returns the address family of this socket in an array" do
@client.addr[0].should == "AF_UNIX"
+ @server.addr[0].should == "AF_UNIX"
end
it "returns the path of the socket in an array if it's a server" do
@@ -27,10 +32,5 @@ describe "UNIXSocket#addr" do
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/ruby/library/socket/unixsocket/initialize_spec.rb b/spec/ruby/library/socket/unixsocket/initialize_spec.rb
new file mode 100644
index 0000000000..a6217d404f
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/initialize_spec.rb
@@ -0,0 +1,38 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'UNIXSocket#initialize' do
+ describe 'using a non existing path' do
+ it 'raises Errno::ENOENT' do
+ lambda { UNIXSocket.new(SocketSpecs.socket_path) }.should raise_error(Errno::ENOENT)
+ end
+ end
+
+ describe 'using an existing socket path' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @socket = UNIXSocket.new(@path)
+ end
+
+ after do
+ @socket.close
+ @server.close
+ rm_r(@path)
+ end
+
+ it 'returns a new UNIXSocket' do
+ @socket.should be_an_instance_of(UNIXSocket)
+ end
+
+ it 'sets the socket path to an empty String' do
+ @socket.path.should == ''
+ end
+
+ it 'sets the socket to binmode' do
+ @socket.binmode?.should be_true
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/inspect_spec.rb b/spec/ruby/library/socket/unixsocket/inspect_spec.rb
index 52b2f508c0..d2e3cabbd3 100644
--- a/spec/ruby/library/socket/unixsocket/inspect_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/inspect_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "UNIXSocket#inspect" do
diff --git a/spec/ruby/library/socket/unixsocket/local_address_spec.rb b/spec/ruby/library/socket/unixsocket/local_address_spec.rb
new file mode 100644
index 0000000000..8c77519830
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/local_address_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'UNIXSocket#local_address' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @server.close
+
+ rm_r(@path)
+ end
+
+ it 'returns an Addrinfo' do
+ @client.local_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_UNIX as the address family' do
+ @client.local_address.afamily.should == Socket::AF_UNIX
+ end
+
+ it 'uses PF_UNIX as the protocol family' do
+ @client.local_address.pfamily.should == Socket::PF_UNIX
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @client.local_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses an empty socket path' do
+ @client.local_address.unix_path.should == ''
+ end
+
+ it 'uses 0 as the protocol' do
+ @client.local_address.protocol.should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/new_spec.rb b/spec/ruby/library/socket/unixsocket/new_spec.rb
index 8b87e9f8c2..05a6b3eda2 100644
--- a/spec/ruby/library/socket/unixsocket/new_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/new_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative 'shared/new'
describe "UNIXSocket.new" do
diff --git a/spec/ruby/library/socket/unixsocket/open_spec.rb b/spec/ruby/library/socket/unixsocket/open_spec.rb
index d15487be94..6c4b7c5676 100644
--- a/spec/ruby/library/socket/unixsocket/open_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/open_spec.rb
@@ -1,4 +1,5 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
require_relative 'shared/new'
describe "UNIXSocket.open" do
diff --git a/spec/ruby/library/socket/unixsocket/pair_spec.rb b/spec/ruby/library/socket/unixsocket/pair_spec.rb
index ba69776b53..845ff76ecc 100644
--- a/spec/ruby/library/socket/unixsocket/pair_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/pair_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/partially_closable_sockets'
diff --git a/spec/ruby/library/socket/unixsocket/partially_closable_spec.rb b/spec/ruby/library/socket/unixsocket/partially_closable_spec.rb
index bbe1a8a5ba..78a64fe6be 100644
--- a/spec/ruby/library/socket/unixsocket/partially_closable_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/partially_closable_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/partially_closable_sockets'
diff --git a/spec/ruby/library/socket/unixsocket/path_spec.rb b/spec/ruby/library/socket/unixsocket/path_spec.rb
index 9808e4132d..317ffc0975 100644
--- a/spec/ruby/library/socket/unixsocket/path_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/path_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "UNIXSocket#path" do
diff --git a/spec/ruby/library/socket/unixsocket/peeraddr_spec.rb b/spec/ruby/library/socket/unixsocket/peeraddr_spec.rb
index bb22a38b8e..ec57b40970 100644
--- a/spec/ruby/library/socket/unixsocket/peeraddr_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/peeraddr_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "UNIXSocket#peeraddr" do
diff --git a/spec/ruby/library/socket/unixsocket/recv_io_spec.rb b/spec/ruby/library/socket/unixsocket/recv_io_spec.rb
index 688f1b6436..533f02a0fa 100644
--- a/spec/ruby/library/socket/unixsocket/recv_io_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/recv_io_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "UNIXSocket#recv_io" do
@@ -38,7 +38,50 @@ describe "UNIXSocket#recv_io" do
@socket = @server.accept
@io = @socket.recv_io(File)
- @io.should be_kind_of(File)
+ @io.should be_an_instance_of(File)
+ end
+ end
+end
+
+with_feature :unix_socket do
+ describe 'UNIXSocket#recv_io' do
+ before do
+ @file = File.open('/dev/null', 'w')
+ @client, @server = UNIXSocket.socketpair
+ end
+
+ after do
+ @client.close
+ @server.close
+ @io.close if @io
+ @file.close
+ end
+
+ describe 'without a custom class' do
+ it 'returns an IO' do
+ @client.send_io(@file)
+
+ @io = @server.recv_io
+ @io.should be_an_instance_of(IO)
+ end
+ end
+
+ describe 'with a custom class' do
+ it 'returns an instance of the custom class' do
+ @client.send_io(@file)
+
+ @io = @server.recv_io(File)
+ @io.should be_an_instance_of(File)
+ end
+ end
+
+ describe 'with a custom mode' do
+ it 'opens the IO using the given mode' do
+ @client.send_io(@file)
+
+ @io = @server.recv_io(File, File::WRONLY)
+ @io.should be_an_instance_of(File)
+ end
end
end
end
diff --git a/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb b/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb
index d142d84fdc..f24caaf686 100644
--- a/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/recvfrom_spec.rb
@@ -1,8 +1,7 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "UNIXSocket#recvfrom" do
-
platform_is_not :windows do
before :each do
@path = SocketSpecs.socket_path
@@ -43,5 +42,56 @@ describe "UNIXSocket#recvfrom" do
sock.close
end
end
+end
+
+
+with_feature :unix_socket do
+ describe 'UNIXSocket#recvfrom' do
+ describe 'using a socket pair' do
+ before do
+ @client, @server = UNIXSocket.socketpair
+ @client.write('hello')
+ end
+
+ after do
+ @client.close
+ @server.close
+ end
+
+ it 'returns an Array containing the data and address information' do
+ @server.recvfrom(5).should == ['hello', ['AF_UNIX', '']]
+ end
+ end
+
+ # These specs are taken from the rdoc examples on UNIXSocket#recvfrom.
+ describe 'using a UNIX socket constructed using UNIXSocket.for_fd' do
+ before do
+ @path1 = SocketSpecs.socket_path
+ @path2 = SocketSpecs.socket_path + '2'
+ rm_r(@path2)
+ @client_raw = Socket.new(:UNIX, :DGRAM)
+ @client_raw.bind(Socket.sockaddr_un(@path1))
+
+ @server_raw = Socket.new(:UNIX, :DGRAM)
+ @server_raw.bind(Socket.sockaddr_un(@path2))
+
+ @socket = UNIXSocket.for_fd(@server_raw.fileno)
+ end
+
+ after do
+ @client_raw.close
+ @server_raw.close # also closes @socket
+
+ rm_r @path1
+ rm_r @path2
+ end
+
+ it 'returns an Array containing the data and address information' do
+ @client_raw.send('hello', 0, Socket.sockaddr_un(@path2))
+
+ @socket.recvfrom(5).should == ['hello', ['AF_UNIX', @path1]]
+ end
+ end
+ end
end
diff --git a/spec/ruby/library/socket/unixsocket/remote_address_spec.rb b/spec/ruby/library/socket/unixsocket/remote_address_spec.rb
new file mode 100644
index 0000000000..0b416254d0
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/remote_address_spec.rb
@@ -0,0 +1,45 @@
+require_relative '../spec_helper'
+require_relative '../fixtures/classes'
+
+with_feature :unix_socket do
+ describe 'UNIXSocket#remote_address' do
+ before do
+ @path = SocketSpecs.socket_path
+ @server = UNIXServer.new(@path)
+ @client = UNIXSocket.new(@path)
+ end
+
+ after do
+ @client.close
+ @server.close
+
+ rm_r(@path)
+ end
+
+ it 'returns an Addrinfo' do
+ @client.remote_address.should be_an_instance_of(Addrinfo)
+ end
+
+ describe 'the returned Addrinfo' do
+ it 'uses AF_UNIX as the address family' do
+ @client.remote_address.afamily.should == Socket::AF_UNIX
+ end
+
+ it 'uses PF_UNIX as the protocol family' do
+ @client.remote_address.pfamily.should == Socket::PF_UNIX
+ end
+
+ it 'uses SOCK_STREAM as the socket type' do
+ @client.remote_address.socktype.should == Socket::SOCK_STREAM
+ end
+
+ it 'uses the correct socket path' do
+ @client.remote_address.unix_path.should == @path
+ end
+
+ it 'uses 0 as the protocol' do
+ @client.remote_address.protocol.should == 0
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/send_io_spec.rb b/spec/ruby/library/socket/unixsocket/send_io_spec.rb
index fbc99be38d..a2a7d26539 100644
--- a/spec/ruby/library/socket/unixsocket/send_io_spec.rb
+++ b/spec/ruby/library/socket/unixsocket/send_io_spec.rb
@@ -1,4 +1,4 @@
-require_relative '../../../spec_helper'
+require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "UNIXSocket#send_io" do
@@ -33,3 +33,26 @@ describe "UNIXSocket#send_io" do
end
end
end
+
+with_feature :unix_socket do
+ describe 'UNIXSocket#send_io' do
+ before do
+ @file = File.open('/dev/null', 'w')
+ @client, @server = UNIXSocket.socketpair
+ end
+
+ after do
+ @client.close
+ @server.close
+ @io.close if @io
+ @file.close
+ end
+
+ it 'sends an IO object' do
+ @client.send_io(@file)
+
+ @io = @server.recv_io
+ @io.should be_an_instance_of(IO)
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/unixsocket/shared/new.rb b/spec/ruby/library/socket/unixsocket/shared/new.rb
index afb57f95cf..76a4e1701e 100644
--- a/spec/ruby/library/socket/unixsocket/shared/new.rb
+++ b/spec/ruby/library/socket/unixsocket/shared/new.rb
@@ -1,4 +1,4 @@
-require_relative '../../../../spec_helper'
+require_relative '../../spec_helper'
require_relative '../../fixtures/classes'
describe :unixsocket_new, shared: true do
diff --git a/spec/ruby/library/socket/unixsocket/socketpair_spec.rb b/spec/ruby/library/socket/unixsocket/socketpair_spec.rb
new file mode 100644
index 0000000000..3e9646f76b
--- /dev/null
+++ b/spec/ruby/library/socket/unixsocket/socketpair_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../spec_helper'
+
+with_feature :unix_socket do
+ describe 'UNIXSocket.socketpair' do
+ before do
+ @s1, @s2 = UNIXSocket.socketpair
+ end
+
+ after do
+ @s1.close
+ @s2.close
+ end
+
+ it 'returns two UNIXSockets' do
+ @s1.should be_an_instance_of(UNIXSocket)
+ @s2.should be_an_instance_of(UNIXSocket)
+ end
+
+ it 'connects the sockets to each other' do
+ @s1.write('hello')
+
+ @s2.recv(5).should == 'hello'
+ end
+
+ it 'sets the socket paths to empty Strings' do
+ @s1.path.should == ''
+ @s2.path.should == ''
+ end
+
+ it 'sets the socket addresses to empty Strings' do
+ @s1.addr.should == ['AF_UNIX', '']
+ @s2.addr.should == ['AF_UNIX', '']
+ end
+
+ it 'sets the socket peer addresses to empty Strings' do
+ @s1.peeraddr.should == ['AF_UNIX', '']
+ @s2.peeraddr.should == ['AF_UNIX', '']
+ end
+ end
+end
diff --git a/spec/ruby/library/weakref/allocate_spec.rb b/spec/ruby/library/weakref/allocate_spec.rb
new file mode 100644
index 0000000000..76e81c4e5e
--- /dev/null
+++ b/spec/ruby/library/weakref/allocate_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require 'weakref'
+
+describe "WeakRef#allocate" do
+ it "assigns nil as the reference" do
+ lambda { WeakRef.allocate.__getobj__ }.should raise_error(WeakRef::RefError)
+ end
+end
diff --git a/spec/ruby/library/weakref/new_spec.rb b/spec/ruby/library/weakref/new_spec.rb
new file mode 100644
index 0000000000..6290e61fe3
--- /dev/null
+++ b/spec/ruby/library/weakref/new_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require 'weakref'
+
+describe "WeakRef#new" do
+ it "creates a subclass correctly" do
+ wr2 = Class.new(WeakRef) {
+ def __getobj__
+ :dummy
+ end
+ }
+ wr2.new(Object.new).__getobj__.should == :dummy
+ end
+end
diff --git a/spec/ruby/library/zlib/deflate/deflate_spec.rb b/spec/ruby/library/zlib/deflate/deflate_spec.rb
index 67bcf22d49..828880f8d8 100644
--- a/spec/ruby/library/zlib/deflate/deflate_spec.rb
+++ b/spec/ruby/library/zlib/deflate/deflate_spec.rb
@@ -25,7 +25,7 @@ describe "Zlib::Deflate.deflate" do
random_generator = Random.new(0)
deflated = ''
- Zlib.deflate(random_generator.bytes(20000)) do |chunk|
+ Zlib::Deflate.deflate(random_generator.bytes(20000)) do |chunk|
deflated << chunk
end
diff --git a/spec/ruby/library/zlib/deflate_spec.rb b/spec/ruby/library/zlib/deflate_spec.rb
new file mode 100644
index 0000000000..01538dd4e6
--- /dev/null
+++ b/spec/ruby/library/zlib/deflate_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require "zlib"
+
+describe "Zlib#deflate" do
+ it "deflates some data" do
+ Zlib.deflate("1" * 10).should == [120, 156, 51, 52, 132, 1, 0, 10, 145, 1, 235].pack('C*')
+ end
+end
diff --git a/spec/ruby/library/zlib/inflate_spec.rb b/spec/ruby/library/zlib/inflate_spec.rb
new file mode 100644
index 0000000000..393bea4f3c
--- /dev/null
+++ b/spec/ruby/library/zlib/inflate_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require "zlib"
+
+describe "Zlib#inflate" do
+ it "inflates some data" do
+ Zlib.inflate([120, 156, 51, 52, 132, 1, 0, 10, 145, 1, 235].pack('C*')).should == "1" * 10
+ end
+end
diff --git a/spec/ruby/optional/capi/encoding_spec.rb b/spec/ruby/optional/capi/encoding_spec.rb
index 33587bf03a..88b3e47833 100644
--- a/spec/ruby/optional/capi/encoding_spec.rb
+++ b/spec/ruby/optional/capi/encoding_spec.rb
@@ -46,10 +46,12 @@ describe "C-API Encoding function" do
@s = CApiEncodingSpecs.new
end
- describe "rb_enc_alias" do
- it "creates an alias for an existing Encoding" do
- @s.rb_enc_alias("ZOMGWTFBBQ", "UTF-8").should >= 0
- Encoding.find("ZOMGWTFBBQ").name.should == "UTF-8"
+ ruby_version_is "2.6" do
+ describe "rb_enc_alias" do
+ it "creates an alias for an existing Encoding" do
+ @s.rb_enc_alias("ZOMGWTFBBQ", "UTF-8").should >= 0
+ Encoding.find("ZOMGWTFBBQ").name.should == "UTF-8"
+ end
end
end
@@ -148,9 +150,11 @@ describe "C-API Encoding function" do
@s.send(@method, 1).should == -1
end
- it "returns -1 for an object without an encoding" do
- obj = Object.new
- @s.send(@method, obj).should == -1
+ ruby_version_is "2.6" do
+ it "returns -1 for an object without an encoding" do
+ obj = Object.new
+ @s.send(@method, obj).should == -1
+ end
end
end
diff --git a/spec/ruby/optional/capi/ext/encoding_spec.c b/spec/ruby/optional/capi/ext/encoding_spec.c
index 87b97f3a1b..9ab893b86a 100644
--- a/spec/ruby/optional/capi/ext/encoding_spec.c
+++ b/spec/ruby/optional/capi/ext/encoding_spec.c
@@ -94,8 +94,6 @@ static VALUE encoding_spec_rb_default_external_encoding(VALUE self) {
#endif
#ifdef HAVE_RB_ENC_ALIAS
-extern int rb_enc_alias(const char* alias, const char* orig);
-
static VALUE encoding_spec_rb_enc_alias(VALUE self, VALUE alias, VALUE orig) {
return INT2NUM(rb_enc_alias(RSTRING_PTR(alias), RSTRING_PTR(orig)));
}
diff --git a/spec/ruby/optional/capi/ext/rubyspec.h b/spec/ruby/optional/capi/ext/rubyspec.h
index 1ece9541c7..e5a90bfc88 100644
--- a/spec/ruby/optional/capi/ext/rubyspec.h
+++ b/spec/ruby/optional/capi/ext/rubyspec.h
@@ -23,6 +23,10 @@
(RUBY_VERSION_MAJOR == (major) && RUBY_VERSION_MINOR < (minor)) || \
(RUBY_VERSION_MAJOR == (major) && RUBY_VERSION_MINOR == (minor) && RUBY_VERSION_TEENY < (teeny)))
+#if RUBY_VERSION_MAJOR > 2 || (RUBY_VERSION_MAJOR == 2 && RUBY_VERSION_MINOR >= 6)
+#define RUBY_VERSION_IS_2_6
+#endif
+
#if RUBY_VERSION_MAJOR > 2 || (RUBY_VERSION_MAJOR == 2 && RUBY_VERSION_MINOR >= 4)
#define RUBY_VERSION_IS_2_4
#endif
@@ -204,7 +208,9 @@
#define HAVE_RB_DEFAULT_INTERNAL_ENCODING 1
#define HAVE_RB_DEFAULT_EXTERNAL_ENCODING 1
+#ifdef RUBY_VERSION_IS_2_6
#define HAVE_RB_ENC_ALIAS 1
+#endif
#define HAVE_RB_ENC_ASSOCIATE 1
#define HAVE_RB_ENC_ASSOCIATE_INDEX 1
#define HAVE_RB_ENC_CODEPOINT_LEN 1
diff --git a/spec/ruby/optional/capi/module_spec.rb b/spec/ruby/optional/capi/module_spec.rb
index 5ce1ed205b..b94e96e846 100644
--- a/spec/ruby/optional/capi/module_spec.rb
+++ b/spec/ruby/optional/capi/module_spec.rb
@@ -30,6 +30,20 @@ describe "CApiModule" do
}.should complain(/already initialized constant/)
CApiModuleSpecs::C::Z.should == 8
end
+
+ it "allows arbitrary names, including constant names not valid in Ruby" do
+ -> {
+ CApiModuleSpecs::C.const_set(:_INVALID, 1)
+ }.should raise_error(NameError, /wrong constant name/)
+
+ @m.rb_const_set(CApiModuleSpecs::C, :_INVALID, 2)
+ @m.rb_const_get(CApiModuleSpecs::C, :_INVALID).should == 2
+
+ # Ruby-level should still not allow access
+ -> {
+ CApiModuleSpecs::C.const_get(:_INVALID)
+ }.should raise_error(NameError, /wrong constant name/)
+ end
end
describe "rb_define_module" do
@@ -140,6 +154,16 @@ describe "CApiModule" do
it "resolves autoload constants in Object" do
@m.rb_const_get(Object, :CApiModuleSpecsAutoload).should == 123
end
+
+ it "allows arbitrary names, including constant names not valid in Ruby" do
+ -> {
+ CApiModuleSpecs::A.const_get(:_INVALID)
+ }.should raise_error(NameError, /wrong constant name/)
+
+ -> {
+ @m.rb_const_get(CApiModuleSpecs::A, :_INVALID)
+ }.should raise_error(NameError, /uninitialized constant/)
+ end
end
describe "rb_const_get_from" do
diff --git a/spec/ruby/security/cve_2010_1330_spec.rb b/spec/ruby/security/cve_2010_1330_spec.rb
new file mode 100644
index 0000000000..c41a5e0a2e
--- /dev/null
+++ b/spec/ruby/security/cve_2010_1330_spec.rb
@@ -0,0 +1,21 @@
+require_relative '../spec_helper'
+
+describe "String#gsub" do
+
+ it "resists CVE-2010-1330 by raising an exception on invalid UTF-8 bytes" do
+ # This original vulnerability talked about KCODE, which is no longer
+ # used. Instead we are forcing encodings here. But I think the idea is the
+ # same - we want to check that Ruby implementations raise an error on
+ # #gsub on a string in the UTF-8 encoding but with invalid an UTF-8 byte
+ # sequence.
+
+ str = "\xF6<script>"
+ str.force_encoding Encoding::ASCII_8BIT
+ str.gsub(/</, "&lt;").should == "\xF6&lt;script>".b
+ str.force_encoding Encoding::UTF_8
+ lambda {
+ str.gsub(/</, "&lt;")
+ }.should raise_error(ArgumentError, /invalid byte sequence in UTF-8/)
+ end
+
+end