summaryrefslogtreecommitdiff
path: root/spec/ruby/core/kernel/shared/require.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/kernel/shared/require.rb')
-rw-r--r--spec/ruby/core/kernel/shared/require.rb217
1 files changed, 146 insertions, 71 deletions
diff --git a/spec/ruby/core/kernel/shared/require.rb b/spec/ruby/core/kernel/shared/require.rb
index 56377684fb..52f86f73e5 100644
--- a/spec/ruby/core/kernel/shared/require.rb
+++ b/spec/ruby/core/kernel/shared/require.rb
@@ -20,8 +20,8 @@ describe :kernel_require_basic, shared: true do
it "raises a LoadError if the file does not exist" do
path = File.expand_path "nonexistent.rb", CODE_LOADING_DIR
- File.exist?(path).should be_false
- lambda { @object.send(@method, path) }.should raise_error(LoadError)
+ File.should_not.exist?(path)
+ -> { @object.send(@method, path) }.should raise_error(LoadError)
ScratchPad.recorded.should == []
end
@@ -41,8 +41,8 @@ describe :kernel_require_basic, shared: true do
end
it "raises a LoadError" do
- File.exist?(@path).should be_true
- lambda { @object.send(@method, @path) }.should raise_error(LoadError)
+ File.should.exist?(@path)
+ -> { @object.send(@method, @path) }.should raise_error(LoadError)
end
end
end
@@ -57,19 +57,19 @@ describe :kernel_require_basic, shared: true do
end
it "raises a TypeError if passed nil" do
- lambda { @object.send(@method, nil) }.should raise_error(TypeError)
+ -> { @object.send(@method, nil) }.should raise_error(TypeError)
end
- it "raises a TypeError if passed a Fixnum" do
- lambda { @object.send(@method, 42) }.should raise_error(TypeError)
+ it "raises a TypeError if passed an Integer" do
+ -> { @object.send(@method, 42) }.should raise_error(TypeError)
end
it "raises a TypeError if passed an Array" do
- lambda { @object.send(@method, []) }.should raise_error(TypeError)
+ -> { @object.send(@method, []) }.should raise_error(TypeError)
end
it "raises a TypeError if passed an object that does not provide #to_str" do
- lambda { @object.send(@method, mock("not a filename")) }.should raise_error(TypeError)
+ -> { @object.send(@method, mock("not a filename")) }.should raise_error(TypeError)
end
it "raises a TypeError if passed an object that has #to_s but not #to_str" do
@@ -77,14 +77,14 @@ describe :kernel_require_basic, shared: true do
name.stub!(:to_s).and_return("load_fixture.rb")
$LOAD_PATH << "."
Dir.chdir CODE_LOADING_DIR do
- lambda { @object.send(@method, name) }.should raise_error(TypeError)
+ -> { @object.send(@method, name) }.should raise_error(TypeError)
end
end
it "raises a TypeError if #to_str does not return a String" do
name = mock("#to_str returns nil")
name.should_receive(:to_str).at_least(1).times.and_return(nil)
- lambda { @object.send(@method, name) }.should raise_error(TypeError)
+ -> { @object.send(@method, name) }.should raise_error(TypeError)
end
it "calls #to_path on non-String objects" do
@@ -160,17 +160,25 @@ describe :kernel_require_basic, shared: true do
ScratchPad.recorded.should == [:loaded]
end
+ it "accepts an Object with #to_path in $LOAD_PATH" do
+ obj = mock("to_path")
+ obj.should_receive(:to_path).at_least(:once).and_return(CODE_LOADING_DIR)
+ $LOAD_PATH << obj
+ @object.send(@method, "load_fixture.rb").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
it "does not require file twice after $LOAD_PATH change" do
$LOAD_PATH << CODE_LOADING_DIR
@object.require("load_fixture.rb").should be_true
- $LOAD_PATH.unshift CODE_LOADING_DIR + "/gem"
+ $LOAD_PATH.push CODE_LOADING_DIR + "/gem"
@object.require("load_fixture.rb").should be_false
ScratchPad.recorded.should == [:loaded]
end
it "does not resolve a ./ relative path against $LOAD_PATH entries" do
$LOAD_PATH << CODE_LOADING_DIR
- lambda do
+ -> do
@object.send(@method, "./load_fixture.rb")
end.should raise_error(LoadError)
ScratchPad.recorded.should == []
@@ -178,7 +186,7 @@ describe :kernel_require_basic, shared: true do
it "does not resolve a ../ relative path against $LOAD_PATH entries" do
$LOAD_PATH << CODE_LOADING_DIR
- lambda do
+ -> do
@object.send(@method, "../code/load_fixture.rb")
end.should raise_error(LoadError)
ScratchPad.recorded.should == []
@@ -204,18 +212,46 @@ end
describe :kernel_require, shared: true do
describe "(path resolution)" do
+ it "loads .rb file when passed absolute path without extension" do
+ path = File.expand_path "load_fixture", CODE_LOADING_DIR
+ @object.send(@method, path).should be_true
+ # This should _not_ be [:no_ext]
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ platform_is :linux, :darwin do
+ it "loads c-extension file when passed absolute path without extension when no .rb is present" do
+ # the error message is specific to what dlerror() returns
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture"
+ -> { @object.send(@method, path) }.should raise_error(LoadError)
+ end
+ end
+
+ platform_is :darwin do
+ it "loads .bundle file when passed absolute path with .so" do
+ # the error message is specific to what dlerror() returns
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture.so"
+ -> { @object.send(@method, path) }.should raise_error(LoadError)
+ end
+ end
+
+ it "does not try an extra .rb if the path already ends in .rb" do
+ path = File.join CODE_LOADING_DIR, "d", "load_fixture.rb"
+ -> { @object.send(@method, path) }.should raise_error(LoadError)
+ end
+
# For reference see [ruby-core:24155] in which matz confirms this feature is
# intentional for security reasons.
it "does not load a bare filename unless the current working directory is in $LOAD_PATH" do
Dir.chdir CODE_LOADING_DIR do
- lambda { @object.require("load_fixture.rb") }.should raise_error(LoadError)
+ -> { @object.require("load_fixture.rb") }.should raise_error(LoadError)
ScratchPad.recorded.should == []
end
end
it "does not load a relative path unless the current working directory is in $LOAD_PATH" do
Dir.chdir File.dirname(CODE_LOADING_DIR) do
- lambda do
+ -> do
@object.require("code/load_fixture.rb")
end.should raise_error(LoadError)
ScratchPad.recorded.should == []
@@ -229,6 +265,17 @@ describe :kernel_require, shared: true do
}.should complain(/circular require considered harmful/, verbose: true)
ScratchPad.recorded.should == [:loaded]
end
+
+ ruby_bug "#17340", ''...'3.3' do
+ it "loads a file concurrently" do
+ path = File.expand_path "concurrent_require_fixture.rb", CODE_LOADING_DIR
+ ScratchPad.record(@object)
+ -> {
+ @object.require(path)
+ }.should_not complain(/circular require considered harmful/, verbose: true)
+ ScratchPad.recorded.join
+ end
+ end
end
describe "(non-extensioned path)" do
@@ -242,12 +289,29 @@ describe :kernel_require, shared: true do
@object.require("load_fixture").should be_true
ScratchPad.recorded.should == [:loaded]
end
+
+ it "does not load a feature twice when $LOAD_PATH has been modified" do
+ $LOAD_PATH.replace [CODE_LOADING_DIR]
+ @object.require("load_fixture").should be_true
+ $LOAD_PATH.replace [File.expand_path("b", CODE_LOADING_DIR), CODE_LOADING_DIR]
+ @object.require("load_fixture").should be_false
+ end
+
+ it "stores the missing path in a LoadError object" do
+ path = "abcd1234"
+
+ -> {
+ @object.send(@method, path)
+ }.should raise_error(LoadError) { |e|
+ e.path.should == path
+ }
+ end
end
describe "(file extensions)" do
it "loads a .rb extensioned file when passed a non-extensioned path" do
path = File.expand_path "load_fixture", CODE_LOADING_DIR
- File.exist?(path).should be_true
+ File.should.exist?(path)
@object.require(path).should be_true
ScratchPad.recorded.should == [:loaded]
end
@@ -271,7 +335,7 @@ describe :kernel_require, shared: true do
it "loads a .rb extensioned file when passed a non-.rb extensioned path" do
path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR
- File.exist?(path).should be_true
+ File.should.exist?(path)
@object.require(path).should be_true
ScratchPad.recorded.should == [:loaded]
end
@@ -335,6 +399,21 @@ describe :kernel_require, shared: true do
loaded_feature = $LOADED_FEATURES.last
ScratchPad.recorded.should == [loaded_feature]
end
+
+ it "requires only once when a new matching file added to path" do
+ @object.require('load_fixture').should be_true
+ ScratchPad.recorded.should == [:loaded]
+
+ symlink_to_code_dir_two = tmp("codesymlinktwo")
+ File.symlink("#{CODE_LOADING_DIR}/b", symlink_to_code_dir_two)
+ begin
+ $LOAD_PATH.unshift(symlink_to_code_dir_two)
+
+ @object.require('load_fixture').should be_false
+ ensure
+ rm_r symlink_to_code_dir_two
+ end
+ end
end
describe "with symlinks in the required feature and $LOAD_PATH" do
@@ -354,26 +433,13 @@ describe :kernel_require, shared: true 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
+ 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
@@ -381,7 +447,7 @@ describe :kernel_require, shared: true do
it "does not store the path if the load fails" do
$LOAD_PATH << CODE_LOADING_DIR
saved_loaded_features = $LOADED_FEATURES.dup
- lambda { @object.require("raise_fixture.rb") }.should raise_error(RuntimeError)
+ -> { @object.require("raise_fixture.rb") }.should raise_error(RuntimeError)
$LOADED_FEATURES.should == saved_loaded_features
end
@@ -527,41 +593,30 @@ describe :kernel_require, shared: true do
ScratchPad.recorded.should == []
end
- ruby_version_is ""..."2.5" do
- it "complex, enumerator, rational, thread and unicode_normalize are already required" do
- provided = %w[complex enumerator rational thread unicode_normalize]
- features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems')
- provided.each { |feature|
- features.should =~ /\b#{feature}\.(rb|so|jar)$/
- }
+ it "unicode_normalize is part of core and not $LOADED_FEATURES" do
+ features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems')
+ features.lines.each { |feature|
+ feature.should_not include("unicode_normalize")
+ }
- code = provided.map { |f| "puts require #{f.inspect}\n" }.join
- required = ruby_exe(code, options: '--disable-gems')
- required.should == "false\n" * provided.size
- end
+ -> { @object.require("unicode_normalize") }.should raise_error(LoadError)
end
- ruby_version_is "2.5" do
- it "complex, enumerator, rational and thread are already required" do
- provided = %w[complex enumerator rational thread]
- features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems')
- provided.each { |feature|
- features.should =~ /\b#{feature}\.(rb|so|jar)$/
- }
-
- code = provided.map { |f| "puts require #{f.inspect}\n" }.join
- required = ruby_exe(code, options: '--disable-gems')
- required.should == "false\n" * provided.size
+ it "does not load a file earlier on the $LOAD_PATH when other similar features were already loaded" do
+ Dir.chdir CODE_LOADING_DIR do
+ @object.send(@method, "../code/load_fixture").should be_true
end
+ ScratchPad.recorded.should == [:loaded]
- it "unicode_normalize is part of core and not $LOADED_FEATURES" do
- features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems')
- features.lines.each { |feature|
- feature.should_not include("unicode_normalize")
- }
+ $LOAD_PATH.unshift "#{CODE_LOADING_DIR}/b"
+ # This loads because the above load was not on the $LOAD_PATH
+ @object.send(@method, "load_fixture").should be_true
+ ScratchPad.recorded.should == [:loaded, :loaded]
- -> { @object.require("unicode_normalize") }.should raise_error(LoadError)
- end
+ $LOAD_PATH.unshift "#{CODE_LOADING_DIR}/c"
+ # This does not load because the above load was on the $LOAD_PATH
+ @object.send(@method, "load_fixture").should be_false
+ ScratchPad.recorded.should == [:loaded, :loaded]
end
end
@@ -683,7 +738,7 @@ describe :kernel_require, shared: true do
Thread.current[:wait_for] = t2
Thread.current[:con_raise] = true
- lambda {
+ -> {
@object.require(@path)
}.should raise_error(RuntimeError)
@@ -724,7 +779,7 @@ describe :kernel_require, shared: true do
t1 = Thread.new do
Thread.current[:con_raise] = true
- lambda {
+ -> {
@object.require(@path)
}.should raise_error(RuntimeError)
@@ -764,10 +819,30 @@ describe :kernel_require, shared: true do
it "stores the missing path in a LoadError object" do
path = "abcd1234"
- lambda {
+ -> {
@object.send(@method, path)
}.should raise_error(LoadError) { |e|
e.path.should == path
}
end
+
+ platform_is :linux, :darwin do
+ it "does not store the missing path in a LoadError object when c-extension file exists but loading fails and passed absolute path without extension" do
+ # the error message is specific to what dlerror() returns
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture"
+ -> { @object.send(@method, path) }.should raise_error(LoadError) { |e|
+ e.path.should == nil
+ }
+ end
+ end
+
+ platform_is :darwin do
+ it "does not store the missing path in a LoadError object when c-extension file exists but loading fails and passed absolute path with extension" do
+ # the error message is specific to what dlerror() returns
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture.bundle"
+ -> { @object.send(@method, path) }.should raise_error(LoadError) { |e|
+ e.path.should == nil
+ }
+ end
+ end
end