diff options
Diffstat (limited to 'spec')
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(/</, "<").should == "\xF6<script>".b + str.force_encoding Encoding::UTF_8 + lambda { + str.gsub(/</, "<") + }.should raise_error(ArgumentError, /invalid byte sequence in UTF-8/) + end + +end |