diff options
Diffstat (limited to 'spec')
29 files changed, 1351 insertions, 55 deletions
diff --git a/spec/bundler/bundler_spec.rb b/spec/bundler/bundler_spec.rb index 94d4096cd3..06435b9888 100644 --- a/spec/bundler/bundler_spec.rb +++ b/spec/bundler/bundler_spec.rb @@ -2,6 +2,7 @@ # frozen_string_literal: true require "bundler" +require "tmpdir" RSpec.describe Bundler do describe "#load_gemspec_uncached" do @@ -189,6 +190,34 @@ EOF end end + describe "#mkdir_p" do + it "creates a folder at the given path" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + + Bundler.mkdir_p(bundled_app.join("foo", "bar")) + expect(bundled_app.join("foo", "bar")).to exist + end + + context "when mkdir_p requires sudo" do + it "creates a new folder using sudo" do + expect(Bundler).to receive(:requires_sudo?).and_return(true) + expect(Bundler).to receive(:sudo).and_return true + Bundler.mkdir_p(bundled_app.join("foo")) + end + end + + context "with :no_sudo option" do + it "forces mkdir_p to not use sudo" do + expect(Bundler).to receive(:requires_sudo?).and_return(true) + expect(Bundler).to_not receive(:sudo) + Bundler.mkdir_p(bundled_app.join("foo"), :no_sudo => true) + end + end + end + describe "#user_home" do context "home directory is set" do it "should return the user home" do @@ -198,6 +227,57 @@ EOF allow(File).to receive(:writable?).with(path).and_return true expect(Bundler.user_home).to eq(Pathname(path)) end + + context "is not a directory" do + it "should issue a warning and return a temporary user home" do + path = "/home/oggy" + allow(Bundler.rubygems).to receive(:user_home).and_return(path) + allow(File).to receive(:directory?).with(path).and_return false + allow(Etc).to receive(:getlogin).and_return("USER") + allow(Dir).to receive(:tmpdir).and_return("/TMP") + allow(FileTest).to receive(:exist?).with("/TMP/bundler/home").and_return(true) + expect(FileUtils).to receive(:mkpath).with("/TMP/bundler/home/USER") + message = <<EOF +`/home/oggy` is not a directory. +Bundler will use `/TMP/bundler/home/USER' as your home directory temporarily. +EOF + expect(Bundler.ui).to receive(:warn).with(message) + expect(Bundler.user_home).to eq(Pathname("/TMP/bundler/home/USER")) + end + end + + context "is not writable" do + let(:path) { "/home/oggy" } + let(:dotbundle) { "/home/oggy/.bundle" } + + it "should issue a warning and return a temporary user home" do + allow(Bundler.rubygems).to receive(:user_home).and_return(path) + allow(File).to receive(:directory?).with(path).and_return true + allow(File).to receive(:writable?).with(path).and_return false + allow(File).to receive(:directory?).with(dotbundle).and_return false + allow(Etc).to receive(:getlogin).and_return("USER") + allow(Dir).to receive(:tmpdir).and_return("/TMP") + allow(FileTest).to receive(:exist?).with("/TMP/bundler/home").and_return(true) + expect(FileUtils).to receive(:mkpath).with("/TMP/bundler/home/USER") + message = <<EOF +`/home/oggy` is not writable. +Bundler will use `/TMP/bundler/home/USER' as your home directory temporarily. +EOF + expect(Bundler.ui).to receive(:warn).with(message) + expect(Bundler.user_home).to eq(Pathname("/TMP/bundler/home/USER")) + end + + context ".bundle exists and have correct permissions" do + it "should return the user home" do + allow(Bundler.rubygems).to receive(:user_home).and_return(path) + allow(File).to receive(:directory?).with(path).and_return true + allow(File).to receive(:writable?).with(path).and_return false + allow(File).to receive(:directory?).with(dotbundle).and_return true + allow(File).to receive(:writable?).with(dotbundle).and_return true + expect(Bundler.user_home).to eq(Pathname(path)) + end + end + end end context "home directory is not set" do @@ -228,6 +308,72 @@ EOF end end + describe "#requires_sudo?" do + let!(:tmpdir) { Dir.mktmpdir } + let(:bundle_path) { Pathname("#{tmpdir}/bundle") } + + def clear_cached_requires_sudo + # Private in ruby 1.8.7 + return unless Bundler.instance_variable_defined?(:@requires_sudo_ran) + Bundler.send(:remove_instance_variable, :@requires_sudo_ran) + Bundler.send(:remove_instance_variable, :@requires_sudo) + end + + before do + clear_cached_requires_sudo + allow(Bundler).to receive(:which).with("sudo").and_return("/usr/bin/sudo") + allow(Bundler).to receive(:bundle_path).and_return(bundle_path) + end + + after do + FileUtils.rm_rf(tmpdir) + clear_cached_requires_sudo + end + + subject { Bundler.requires_sudo? } + + context "bundle_path doesn't exist" do + it { should be false } + + context "and parent dir can't be written" do + before do + FileUtils.chmod(0o500, tmpdir) + end + + it { should be true } + end + + context "with unwritable files in a parent dir" do + # Regression test for https://github.com/bundler/bundler/pull/6316 + # It doesn't matter if there are other unwritable files so long as + # bundle_path can be created + before do + file = File.join(tmpdir, "unrelated_file") + FileUtils.touch(file) + FileUtils.chmod(0o400, file) + end + + it { should be false } + end + end + + context "bundle_path exists" do + before do + FileUtils.mkdir_p(bundle_path) + end + + it { should be false } + + context "and is unwritable" do + before do + FileUtils.chmod(0o500, bundle_path) + end + + it { should be true } + end + end + end + context "user cache dir" do let(:home_path) { Pathname.new(ENV["HOME"]) } diff --git a/spec/bundler/plugin/events_spec.rb b/spec/bundler/plugin/events_spec.rb new file mode 100644 index 0000000000..b09e915682 --- /dev/null +++ b/spec/bundler/plugin/events_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +RSpec.describe Bundler::Plugin::Events do + context "plugin events" do + describe "#define" do + it "raises when redefining a constant" do + expect do + Bundler::Plugin::Events.send(:define, :GEM_BEFORE_INSTALL_ALL, "another-value") + end.to raise_error(ArgumentError) + end + + it "can define a new constant" do + Bundler::Plugin::Events.send(:define, :NEW_CONSTANT, "value") + expect(Bundler::Plugin::Events::NEW_CONSTANT).to eq("value") + end + end + end +end diff --git a/spec/bundler/plugin_spec.rb b/spec/bundler/plugin_spec.rb index 68a7e32ad1..eaa0b80905 100644 --- a/spec/bundler/plugin_spec.rb +++ b/spec/bundler/plugin_spec.rb @@ -251,6 +251,16 @@ RSpec.describe Bundler::Plugin do end end + describe "#add_hook" do + it "raises an ArgumentError on an unregistered event" do + ran = false + expect do + Plugin.add_hook("unregistered-hook") { ran = true } + end.to raise_error(ArgumentError) + expect(ran).to be(false) + end + end + describe "#hook" do before do path = lib_path("foo-plugin") @@ -258,7 +268,13 @@ RSpec.describe Bundler::Plugin do s.write "plugins.rb", code end - allow(index).to receive(:hook_plugins).with(event). + Bundler::Plugin::Events.send(:reset) + Bundler::Plugin::Events.send(:define, :EVENT_1, "event-1") + Bundler::Plugin::Events.send(:define, :EVENT_2, "event-2") + + allow(index).to receive(:hook_plugins).with(Bundler::Plugin::Events::EVENT_1). + and_return(["foo-plugin"]) + allow(index).to receive(:hook_plugins).with(Bundler::Plugin::Events::EVENT_2). and_return(["foo-plugin"]) allow(index).to receive(:plugin_path).with("foo-plugin").and_return(path) allow(index).to receive(:load_paths).with("foo-plugin").and_return([]) @@ -268,11 +284,15 @@ RSpec.describe Bundler::Plugin do Bundler::Plugin::API.hook("event-1") { puts "hook for event 1" } RUBY - let(:event) { "event-1" } + it "raises an ArgumentError on an unregistered event" do + expect do + Plugin.hook("unregistered-hook") + end.to raise_error(ArgumentError) + end it "executes the hook" do out = capture(:stdout) do - Plugin.hook("event-1") + Plugin.hook(Bundler::Plugin::Events::EVENT_1) end.strip expect(out).to eq("hook for event 1") @@ -280,17 +300,15 @@ RSpec.describe Bundler::Plugin do context "single plugin declaring more than one hook" do let(:code) { <<-RUBY } - Bundler::Plugin::API.hook("event-1") {} - Bundler::Plugin::API.hook("event-2") {} + Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT_1) {} + Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT_2) {} puts "loaded" RUBY - let(:event) { /event-1|event-2/ } - it "evals plugins.rb once" do out = capture(:stdout) do - Plugin.hook("event-1") - Plugin.hook("event-2") + Plugin.hook(Bundler::Plugin::Events::EVENT_1) + Plugin.hook(Bundler::Plugin::Events::EVENT_2) end.strip expect(out).to eq("loaded") @@ -299,12 +317,12 @@ RSpec.describe Bundler::Plugin do context "a block is passed" do let(:code) { <<-RUBY } - Bundler::Plugin::API.hook("#{event}") { |&blk| blk.call } + Bundler::Plugin::API.hook(Bundler::Plugin::Events::EVENT_1) { |&blk| blk.call } RUBY it "is passed to the hook" do out = capture(:stdout) do - Plugin.hook("event-1") { puts "win" } + Plugin.hook(Bundler::Plugin::Events::EVENT_1) { puts "win" } end.strip expect(out).to eq("win") diff --git a/spec/commands/add_spec.rb b/spec/commands/add_spec.rb index d1f2050aa0..2e9ef6b923 100644 --- a/spec/commands/add_spec.rb +++ b/spec/commands/add_spec.rb @@ -17,6 +17,14 @@ RSpec.describe "bundle add" do G end + context "when no gems are specified" do + it "shows error" do + bundle "add" + + expect(last_command.bundler_err).to include("Please specify gems to add") + end + end + describe "without version specified" do it "version requirement becomes ~> major.minor.patch when resolved version is < 1.0" do bundle "add 'bar'" @@ -75,11 +83,21 @@ RSpec.describe "bundle add" do describe "with --source" do it "adds dependency with specified source" do bundle "add 'foo' --source='file://#{gem_repo2}'" + expect(bundled_app("Gemfile").read).to match(%r{gem "foo", "~> 2.0", :source => "file:\/\/#{gem_repo2}"}) expect(the_bundle).to include_gems "foo 2.0" end end + describe "with --skip-install" do + it "adds gem to Gemfile but is not installed" do + bundle "add foo --skip-install --version=2.0" + + expect(bundled_app("Gemfile").read).to match(/gem "foo", "= 2.0"/) + expect(the_bundle).to_not include_gems "foo 2.0" + end + end + it "using combination of short form options works like long form" do bundle "add 'foo' -s='file://#{gem_repo2}' -g='development' -v='~>1.0'" expect(bundled_app("Gemfile").read).to include %(gem "foo", "~> 1.0", :group => [:development], :source => "file://#{gem_repo2}") @@ -106,4 +124,94 @@ RSpec.describe "bundle add" do bundle "add 'baz' --source='file://does/not/exist'" expect(out).to include("Could not fetch specs from file://does/not/exist/") end + + describe "with --optimistic" do + it "adds optimistic version" do + bundle! "add 'foo' --optimistic" + expect(bundled_app("Gemfile").read).to include %(gem "foo", ">= 2.0") + expect(the_bundle).to include_gems "foo 2.0" + end + end + + describe "with --strict option" do + it "adds strict version" do + bundle! "add 'foo' --strict" + expect(bundled_app("Gemfile").read).to include %(gem "foo", "= 2.0") + expect(the_bundle).to include_gems "foo 2.0" + end + end + + describe "with no option" do + it "adds pessimistic version" do + bundle! "add 'foo'" + expect(bundled_app("Gemfile").read).to include %(gem "foo", "~> 2.0") + expect(the_bundle).to include_gems "foo 2.0" + end + end + + describe "with --optimistic and --strict" do + it "throws error" do + bundle "add 'foo' --strict --optimistic" + + expect(out).to include("You can not specify `--strict` and `--optimistic` at the same time") + end + end + + context "multiple gems" do + it "adds multiple gems to gemfile" do + bundle! "add bar baz" + + expect(bundled_app("Gemfile").read).to match(/gem "bar", "~> 0.12.3"/) + expect(bundled_app("Gemfile").read).to match(/gem "baz", "~> 1.2"/) + end + + it "throws error if any of the specified gems are present in the gemfile with different version" do + bundle "add weakling bar" + + expect(out).to include("You cannot specify the same gem twice with different version requirements") + expect(out).to include("You specified: weakling (~> 0.0.1) and weakling (>= 0).") + end + end + + describe "when a gem is added which is already specified in Gemfile with version" do + it "shows an error when added with different version requirement" do + install_gemfile <<-G + source "file://#{gem_repo2}" + gem "rack", "1.0" + G + + bundle "add 'rack' --version=1.1" + + expect(out).to include("You cannot specify the same gem twice with different version requirements") + expect(out).to include("If you want to update the gem version, run `bundle update rack`. You may also need to change the version requirement specified in the Gemfile if it's too restrictive") + end + + it "shows error when added without version requirements" do + install_gemfile <<-G + source "file://#{gem_repo2}" + gem "rack", "1.0" + G + + bundle "add 'rack'" + + expect(out).to include("Gem already added.") + expect(out).to include("You cannot specify the same gem twice with different version requirements") + expect(out).not_to include("If you want to update the gem version, run `bundle update rack`. You may also need to change the version requirement specified in the Gemfile if it's too restrictive") + end + end + + describe "when a gem is added which is already specified in Gemfile without version" do + it "shows an error when added with different version requirement" do + install_gemfile <<-G + source "file://#{gem_repo2}" + gem "rack" + G + + bundle "add 'rack' --version=1.1" + + expect(out).to include("You cannot specify the same gem twice with different version requirements") + expect(out).to include("If you want to update the gem version, run `bundle update rack`.") + expect(out).not_to include("You may also need to change the version requirement specified in the Gemfile if it's too restrictive") + end + end end diff --git a/spec/commands/binstubs_spec.rb b/spec/commands/binstubs_spec.rb index 8157173b42..ad859a21d5 100644 --- a/spec/commands/binstubs_spec.rb +++ b/spec/commands/binstubs_spec.rb @@ -39,6 +39,18 @@ RSpec.describe "bundle binstubs <gem>" do expect(bundled_app("bin/rails")).to exist end + it "allows installing all binstubs" do + install_gemfile! <<-G + source "file://#{gem_repo1}" + gem "rails" + G + + bundle! :binstubs, :all => true + + expect(bundled_app("bin/rails")).to exist + expect(bundled_app("bin/rake")).to exist + end + it "displays an error when used without any gem" do install_gemfile <<-G source "file://#{gem_repo1}" @@ -50,6 +62,17 @@ RSpec.describe "bundle binstubs <gem>" do expect(out).to include("`bundle binstubs` needs at least one gem to run.") end + it "displays an error when used with --all and gems" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + + bundle "binstubs rack", :all => true + expect(last_command).to be_failure + expect(last_command.bundler_err).to include("Cannot specify --all with specific gems") + end + context "when generating bundle binstub outside bundler" do it "should abort" do install_gemfile <<-G diff --git a/spec/commands/exec_spec.rb b/spec/commands/exec_spec.rb index 2fe2667363..5ece0bccc3 100644 --- a/spec/commands/exec_spec.rb +++ b/spec/commands/exec_spec.rb @@ -789,6 +789,8 @@ __FILE__: #{path.to_s.inspect} end it "overrides disable_shared_gems so bundler can be found" do + skip "bundler 1.16.x is not support with Ruby 2.6 on Travis CI" if RUBY_VERSION >= "2.6" + file = bundled_app("file_that_bundle_execs.rb") create_file(file, <<-RB) #!#{Gem.ruby} diff --git a/spec/commands/install_spec.rb b/spec/commands/install_spec.rb index b6e89d70da..394f672fef 100644 --- a/spec/commands/install_spec.rb +++ b/spec/commands/install_spec.rb @@ -321,6 +321,56 @@ RSpec.describe "bundle install with gem sources" do expect(File.exist?(bundled_app("Gemfile.lock"))).to eq(true) end + context "throws a warning if a gem is added twice in Gemfile" do + it "without version requirements" do + install_gemfile <<-G + source "file://#{gem_repo2}" + gem "rack" + gem "rack" + G + + expect(out).to include("Your Gemfile lists the gem rack (>= 0) more than once.") + expect(out).to include("Remove any duplicate entries and specify the gem only once (per group).") + expect(out).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.") + end + + it "with same versions" do + install_gemfile <<-G + source "file://#{gem_repo2}" + gem "rack", "1.0" + gem "rack", "1.0" + G + + expect(out).to include("Your Gemfile lists the gem rack (= 1.0) more than once.") + expect(out).to include("Remove any duplicate entries and specify the gem only once (per group).") + expect(out).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.") + end + end + + context "throws an error if a gem is added twice in Gemfile" do + it "when version of one dependency is not specified" do + install_gemfile <<-G + source "file://#{gem_repo2}" + gem "rack" + gem "rack", "1.0" + G + + expect(out).to include("You cannot specify the same gem twice with different version requirements") + expect(out).to include("You specified: rack (>= 0) and rack (= 1.0).") + end + + it "when different versions of both dependencies are specified" do + install_gemfile <<-G + source "file://#{gem_repo2}" + gem "rack", "1.0" + gem "rack", "1.1" + G + + expect(out).to include("You cannot specify the same gem twice with different version requirements") + expect(out).to include("You specified: rack (= 1.0) and rack (= 1.1).") + end + end + it "gracefully handles error when rubygems server is unavailable" do install_gemfile <<-G, :artifice => nil source "file://#{gem_repo1}" @@ -355,7 +405,7 @@ RSpec.describe "bundle install with gem sources" do expect(last_command.stdboth).not_to match(/Error Report/i) expect(last_command.bundler_err).to include("An error occurred while installing ajp-rails (0.0.0), and Bundler cannot continue."). - and include("Make sure that `gem install ajp-rails -v '0.0.0' --source 'file://localhost#{gem_repo2}/'` succeeds before bundling.") + and include(normalize_uri_file("Make sure that `gem install ajp-rails -v '0.0.0' --source 'file://localhost#{gem_repo2}/'` succeeds before bundling.")) end it "doesn't blow up when the local .bundle/config is empty" do diff --git a/spec/commands/list_spec.rb b/spec/commands/list_spec.rb index 4ebe934ca7..5305176c65 100644 --- a/spec/commands/list_spec.rb +++ b/spec/commands/list_spec.rb @@ -4,21 +4,72 @@ RSpec.describe "bundle list", :bundler => "2" do before do install_gemfile <<-G source "file://#{gem_repo1}" + gem "rack" + gem "rspec", :group => [:test] G end context "with name-only and paths option" do it "raises an error" do bundle "list --name-only --paths" + expect(out).to eq "The `--name-only` and `--paths` options cannot be used together" end end + context "with without-group and only-group option" do + it "raises an error" do + bundle "list --without-group dev --only-group test" + + expect(out).to eq "The `--only-group` and `--without-group` options cannot be used together" + end + end + + describe "with without-group option" do + context "when group is present" do + it "prints the gems not in the specified group" do + bundle! "list --without-group test" + + expect(out).to include(" * rack (1.0.0)") + expect(out).not_to include(" * rspec (1.2.7)") + end + end + + context "when group is not found" do + it "raises an error" do + bundle "list --without-group random" + + expect(out).to eq "`random` group could not be found." + end + end + end + + describe "with only-group option" do + context "when group is present" do + it "prints the gems in the specified group" do + bundle! "list --only-group default" + + expect(out).to include(" * rack (1.0.0)") + expect(out).not_to include(" * rspec (1.2.7)") + end + end + + context "when group is not found" do + it "raises an error" do + bundle "list --only-group random" + + expect(out).to eq "`random` group could not be found." + end + end + end + context "with name-only option" do it "prints only the name of the gems in the bundle" do bundle "list --name-only" - expect(out).to eq "rack" + + expect(out).to include("rack") + expect(out).to include("rspec") end end diff --git a/spec/commands/lock_spec.rb b/spec/commands/lock_spec.rb index 81eba1ceda..f4997b0620 100644 --- a/spec/commands/lock_spec.rb +++ b/spec/commands/lock_spec.rb @@ -19,7 +19,7 @@ RSpec.describe "bundle lock" do gem "foo" G - @lockfile = strip_lockfile <<-L + @lockfile = strip_lockfile(normalize_uri_file(<<-L)) GEM remote: file://localhost#{repo}/ specs: @@ -257,7 +257,7 @@ RSpec.describe "bundle lock" do simulate_platform(mingw) { bundle! :lock } - expect(the_bundle.lockfile).to read_as(strip_whitespace(<<-G)) + expect(the_bundle.lockfile).to read_as(normalize_uri_file(strip_whitespace(<<-G))) GEM remote: file://localhost#{gem_repo4}/ specs: @@ -282,7 +282,7 @@ RSpec.describe "bundle lock" do simulate_platform(rb) { bundle! :lock } - expect(the_bundle.lockfile).to read_as(strip_whitespace(<<-G)) + expect(the_bundle.lockfile).to read_as(normalize_uri_file(strip_whitespace(<<-G))) GEM remote: file://localhost#{gem_repo4}/ specs: diff --git a/spec/commands/outdated_spec.rb b/spec/commands/outdated_spec.rb index 877e648f5d..394db664d8 100644 --- a/spec/commands/outdated_spec.rb +++ b/spec/commands/outdated_spec.rb @@ -764,4 +764,31 @@ RSpec.describe "bundle outdated" do end end end + + describe "with --only-explicit" do + it "does not report outdated dependent gems" do + build_repo4 do + build_gem "weakling", %w[0.2 0.3] do |s| + s.add_dependency "bar", "~> 2.1" + end + build_gem "bar", %w[2.1 2.2] + end + + install_gemfile <<-G + source "file://#{gem_repo4}" + gem 'weakling', '0.2' + gem 'bar', '2.1' + G + + gemfile <<-G + source "file://#{gem_repo4}" + gem 'weakling' + G + + bundle "outdated --only-explicit" + + expect(out).to include("weakling (newest 0.3") + expect(out).not_to include("bar (newest 2.2") + end + end end diff --git a/spec/commands/remove_spec.rb b/spec/commands/remove_spec.rb new file mode 100644 index 0000000000..37594b1ece --- /dev/null +++ b/spec/commands/remove_spec.rb @@ -0,0 +1,571 @@ +# frozen_string_literal: true + +RSpec.describe "bundle remove" do + context "when no gems are specified" do + it "throws error" do + gemfile <<-G + source "file://#{gem_repo1}" + G + + bundle "remove" + + expect(out).to include("Please specify gems to remove.") + end + end + + context "when --install flag is specified" do + it "removes gems from .bundle" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + G + + bundle! "remove rack --install" + + expect(out).to include("rack was removed.") + expect(the_bundle).to_not include_gems "rack" + end + end + + describe "remove single gem from gemfile" do + context "when gem is present in gemfile" do + it "shows success for removed gem" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when gem is not present in gemfile" do + it "shows warning for gem that could not be removed" do + gemfile <<-G + source "file://#{gem_repo1}" + G + + bundle "remove rack" + + expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile")} so it could not be removed.") + end + end + end + + describe "remove mutiple gems from gemfile" do + context "when all gems are present in gemfile" do + it "shows success fir all removed gems" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + gem "rails" + G + + bundle! "remove rack rails" + + expect(out).to include("rack was removed.") + expect(out).to include("rails was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when some gems are not present in the gemfile" do + it "shows warning for those not present and success for those that can be removed" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rails" + gem "minitest" + gem "rspec" + G + + bundle "remove rails rack minitest" + + expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile")} so it could not be removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + gem "rails" + gem "minitest" + gem "rspec" + G + end + end + end + + context "with inline groups" do + it "removes the specified gem" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack", :group => [:dev] + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + describe "with group blocks" do + context "when single group block with gem to be removed is present" do + it "removes the group block" do + gemfile <<-G + source "file://#{gem_repo1}" + + group :test do + gem "rspec" + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when an empty block is also present" do + it "removes all empty blocks" do + gemfile <<-G + source "file://#{gem_repo1}" + + group :test do + gem "rspec" + end + + group :dev do + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when the gem belongs to mutiple groups" do + it "removes the groups" do + gemfile <<-G + source "file://#{gem_repo1}" + + group :test, :serioustest do + gem "rspec" + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when the gem is present in mutiple groups" do + it "removes all empty blocks" do + gemfile <<-G + source "file://#{gem_repo1}" + + group :one do + gem "rspec" + end + + group :two do + gem "rspec" + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + end + + describe "nested group blocks" do + context "when all the groups will be empty after removal" do + it "removes the empty nested blocks" do + gemfile <<-G + source "file://#{gem_repo1}" + + group :test do + group :serioustest do + gem "rspec" + end + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when outer group will not be empty after removal" do + it "removes only empty blocks" do + install_gemfile <<-G + source "file://#{gem_repo1}" + + group :test do + gem "rack-test" + + group :serioustest do + gem "rspec" + end + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + group :test do + gem "rack-test" + + end + G + end + end + + context "when inner group will not be empty after removal" do + it "removes only empty blocks" do + install_gemfile <<-G + source "file://#{gem_repo1}" + + group :test do + group :serioustest do + gem "rspec" + gem "rack-test" + end + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + group :test do + group :serioustest do + gem "rack-test" + end + end + G + end + end + end + + describe "arbitrary gemfile" do + context "when mutiple gems are present in same line" do + it "shows warning for gems not removed" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rack"; gem "rails" + G + + bundle "remove rails" + + expect(out).to include("Gems could not be removed. rack (>= 0) would also have been removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + gem "rack"; gem "rails" + G + end + end + + context "when some gems could not be removed" do + it "shows warning for gems not removed and success for those removed" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem"rack" + gem"rspec" + gem "rails" + gem "minitest" + G + + bundle! "remove rails rack rspec minitest" + + expect(out).to include("rails was removed.") + expect(out).to include("minitest was removed.") + expect(out).to include("rack, rspec could not be removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + gem"rack" + gem"rspec" + G + end + end + end + + context "with sources" do + before do + build_repo gem_repo3 do + build_gem "rspec" + end + end + + it "removes gems and empty source blocks" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + + source "file://#{gem_repo3}" do + gem "rspec" + end + G + + bundle! "install" + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + gem "rack" + G + end + end + + describe "with eval_gemfile" do + context "when gems are present in both gemfiles" do + it "removes the gems" do + create_file "Gemfile-other", <<-G + gem "rack" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + + gem "rack" + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + end + end + + context "when gems are present in other gemfile" do + it "removes the gems" do + create_file "Gemfile-other", <<-G + gem "rack" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + G + + bundle! "remove rack" + + expect(bundled_app("Gemfile-other").read).to_not include("gem \"rack\"") + expect(out).to include("rack was removed.") + end + end + + context "when gems to be removed are not specified in any of the gemfiles" do + it "throws error for the gems not present" do + # an empty gemfile + # indicating the gem is not present in the gemfile + create_file "Gemfile-other", <<-G + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + G + + bundle "remove rack" + + expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile")} so it could not be removed.") + end + end + + context "when the gem is present in parent file but not in gemfile specified by eval_gemfile" do + it "removes the gem" do + create_file "Gemfile-other", <<-G + gem "rails" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + gem "rack" + G + + bundle "remove rack" + + expect(out).to include("rack was removed.") + expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile-other")} so it could not be removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + G + end + end + + context "when gems can not be removed from other gemfile" do + it "shows error" do + create_file "Gemfile-other", <<-G + gem "rails"; gem "rack" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + gem "rack" + G + + bundle "remove rack" + + expect(out).to include("rack was removed.") + expect(out).to include("Gems could not be removed. rails (>= 0) would also have been removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + G + end + end + + context "when gems could not be removed from parent gemfile" do + it "shows error" do + create_file "Gemfile-other", <<-G + gem "rack" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + gem "rails"; gem "rack" + G + + bundle "remove rack" + + expect(out).to include("Gems could not be removed. rails (>= 0) would also have been removed.") + expect(bundled_app("Gemfile-other").read).to include("gem \"rack\"") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + gem "rails"; gem "rack" + G + end + end + + context "when gem present in gemfiles but could not be removed from one from one of them" do + it "removes gem which can be removed and shows warning for file from which it can not be removed" do + create_file "Gemfile-other", <<-G + gem "rack" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + gem"rack" + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + expect(bundled_app("Gemfile-other").read).to_not include("gem \"rack\"") + end + end + end + + context "with install_if" do + it "removes gems inside blocks and empty blocks" do + install_gemfile <<-G + source "file://#{gem_repo1}" + + install_if(lambda { false }) do + gem "rack" + end + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "with env" do + it "removes gems inside blocks and empty blocks" do + install_gemfile <<-G + source "file://#{gem_repo1}" + + env "BUNDLER_TEST" do + gem "rack" + end + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "with gemspec" do + it "should not remove the gem" do + build_lib("foo", :path => tmp.join("foo")) do |s| + s.write("foo.gemspec", "") + s.add_dependency "rack" + end + + install_gemfile(<<-G) + source "file://#{gem_repo1}" + gemspec :path => '#{tmp.join("foo")}', :name => 'foo' + G + + bundle! "remove foo" + + expect(out).to include("foo could not be removed.") + end + end +end diff --git a/spec/commands/update_spec.rb b/spec/commands/update_spec.rb index af09c145c1..b06eaa47e2 100644 --- a/spec/commands/update_spec.rb +++ b/spec/commands/update_spec.rb @@ -189,6 +189,23 @@ RSpec.describe "bundle update" do expect(the_bundle).not_to include_gems "rack 1.2" end + context "when conservatively updating a group with non-group sub-deps" do + it "should update only specified group gems" do + install_gemfile <<-G + source "file://#{gem_repo2}" + gem "activemerchant", :group => :development + gem "activesupport" + G + update_repo2 do + build_gem "activemerchant", "2.0" + build_gem "activesupport", "3.0" + end + bundle "update --conservative --group development" + expect(the_bundle).to include_gems "activemerchant 2.0" + expect(the_bundle).not_to include_gems "activesupport 3.0" + end + end + context "when there is a source with the same name as a gem in a group" do before :each do build_git "foo", :path => lib_path("activesupport") diff --git a/spec/install/failure_spec.rb b/spec/install/failure_spec.rb index 27abe9fcc3..49e2771dca 100644 --- a/spec/install/failure_spec.rb +++ b/spec/install/failure_spec.rb @@ -18,7 +18,7 @@ RSpec.describe "bundle install" do source "file:\/\/localhost#{gem_repo2}" gem "rails" G - expect(last_command.bundler_err).to end_with(<<-M.strip) + expect(last_command.bundler_err).to end_with(normalize_uri_file(<<-M.strip)) An error occurred while installing activesupport (2.3.2), and Bundler cannot continue. Make sure that `gem install activesupport -v '2.3.2' --source 'file://localhost#{gem_repo2}/'` succeeds before bundling. @@ -29,6 +29,66 @@ In Gemfile: M end + context "when installing a git gem" do + it "does not tell the user to run 'gem install'" do + build_git "activesupport", "2.3.2", :path => lib_path("activesupport") do |s| + s.extensions << "Rakefile" + s.write "Rakefile", <<-RUBY + task :default do + abort "make installing activesupport-2.3.2 fail" + end + RUBY + end + + install_gemfile <<-G + source "file:\/\/localhost#{gem_repo1}" + gem "rails" + gem "activesupport", :git => "#{lib_path("activesupport")}" + G + + expect(last_command.bundler_err).to end_with(<<-M.strip) +An error occurred while installing activesupport (2.3.2), and Bundler cannot continue. + +In Gemfile: + rails was resolved to 2.3.2, which depends on + actionmailer was resolved to 2.3.2, which depends on + activesupport + M + end + end + + context "when installing a gem using a git block" do + it "does not tell the user to run 'gem install'" do + build_git "activesupport", "2.3.2", :path => lib_path("activesupport") do |s| + s.extensions << "Rakefile" + s.write "Rakefile", <<-RUBY + task :default do + abort "make installing activesupport-2.3.2 fail" + end + RUBY + end + + install_gemfile <<-G + source "file:\/\/localhost#{gem_repo1}" + gem "rails" + + git "#{lib_path("activesupport")}" do + gem "activesupport" + end + G + + expect(last_command.bundler_err).to end_with(<<-M.strip) +An error occurred while installing activesupport (2.3.2), and Bundler cannot continue. + + +In Gemfile: + rails was resolved to 2.3.2, which depends on + actionmailer was resolved to 2.3.2, which depends on + activesupport + M + end + end + it "prints out the hint for the remote source when available" do build_repo2 do build_gem "activesupport", "2.3.2" do |s| @@ -51,7 +111,7 @@ In Gemfile: gem "rails" end G - expect(last_command.bundler_err).to end_with(<<-M.strip) + expect(last_command.bundler_err).to end_with(normalize_uri_file(<<-M.strip)) An error occurred while installing activesupport (2.3.2), and Bundler cannot continue. Make sure that `gem install activesupport -v '2.3.2' --source 'file://localhost#{gem_repo2}/'` succeeds before bundling. diff --git a/spec/install/gemfile/gemspec_spec.rb b/spec/install/gemfile/gemspec_spec.rb index 86cd2d8f3f..7ce037730e 100644 --- a/spec/install/gemfile/gemspec_spec.rb +++ b/spec/install/gemfile/gemspec_spec.rb @@ -442,7 +442,7 @@ RSpec.describe "bundle install from an existing gemspec" do context "as a runtime dependency" do it "keeps java dependencies in the lockfile" do expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY" - expect(lockfile).to eq strip_whitespace(<<-L) + expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L)) PATH remote: . specs: @@ -473,7 +473,7 @@ RSpec.describe "bundle install from an existing gemspec" do it "keeps java dependencies in the lockfile" do expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY" - expect(lockfile).to eq strip_whitespace(<<-L) + expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L)) PATH remote: . specs: @@ -505,7 +505,7 @@ RSpec.describe "bundle install from an existing gemspec" do it "keeps java dependencies in the lockfile" do expect(the_bundle).to include_gems "foo 1.0", "indirect_platform_specific 1.0", "platform_specific 1.0 RUBY" - expect(lockfile).to eq strip_whitespace(<<-L) + expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L)) PATH remote: . specs: @@ -543,7 +543,7 @@ RSpec.describe "bundle install from an existing gemspec" do context "as a runtime dependency" do it "keeps java dependencies in the lockfile" do expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY" - expect(lockfile).to eq strip_whitespace(<<-L) + expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L)) GEM remote: file://localhost#{gem_repo2}/ specs: @@ -574,7 +574,7 @@ RSpec.describe "bundle install from an existing gemspec" do it "keeps java dependencies in the lockfile" do expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY" - expect(lockfile).to eq strip_whitespace(<<-L) + expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L)) GEM remote: file://localhost#{gem_repo2}/ specs: @@ -606,7 +606,7 @@ RSpec.describe "bundle install from an existing gemspec" do it "keeps java dependencies in the lockfile" do expect(the_bundle).to include_gems "foo 1.0", "indirect_platform_specific 1.0", "platform_specific 1.0 RUBY" - expect(lockfile).to eq strip_whitespace(<<-L) + expect(lockfile).to eq normalize_uri_file(strip_whitespace(<<-L)) GEM remote: file://localhost#{gem_repo2}/ specs: diff --git a/spec/install/gemfile/sources_spec.rb b/spec/install/gemfile/sources_spec.rb index 2cadc61161..384deca7c2 100644 --- a/spec/install/gemfile/sources_spec.rb +++ b/spec/install/gemfile/sources_spec.rb @@ -33,7 +33,7 @@ RSpec.describe "bundle install with gems on multiple sources" do expect(out).to have_major_deprecation a_string_including("Your Gemfile contains multiple primary sources.") expect(out).to include("Warning: the gem 'rack' was found in multiple sources.") - expect(out).to include("Installed from: file://localhost#{gem_repo1}") + expect(out).to include(normalize_uri_file("Installed from: file://localhost#{gem_repo1}")) expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1") end @@ -63,7 +63,7 @@ RSpec.describe "bundle install with gems on multiple sources" do expect(out).to have_major_deprecation a_string_including("Your Gemfile contains multiple primary sources.") expect(out).to include("Warning: the gem 'rack' was found in multiple sources.") - expect(out).to include("Installed from: file://localhost#{gem_repo1}") + expect(out).to include(normalize_uri_file("Installed from: file://localhost#{gem_repo1}")) expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1") end end @@ -253,7 +253,7 @@ RSpec.describe "bundle install with gems on multiple sources" do bundle :install expect(out).to have_major_deprecation a_string_including("Your Gemfile contains multiple primary sources.") expect(out).to include("Warning: the gem 'rack' was found in multiple sources.") - expect(out).to include("Installed from: file://localhost#{gem_repo2}") + expect(out).to include(normalize_uri_file("Installed from: file://localhost#{gem_repo2}")) expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0") end end @@ -634,7 +634,7 @@ RSpec.describe "bundle install with gems on multiple sources" do gem "depends_on_rack" G expect(last_command).to be_failure - expect(last_command.stderr).to eq strip_whitespace(<<-EOS).strip + expect(last_command.stderr).to eq normalize_uri_file(strip_whitespace(<<-EOS).strip) The gem 'rack' was found in multiple relevant sources. * rubygems repository file://localhost#{gem_repo1}/ or installed locally * rubygems repository file://localhost#{gem_repo4}/ or installed locally diff --git a/spec/install/gems/mirror_spec.rb b/spec/install/gems/mirror_spec.rb index 89302615f1..4c35b8f206 100644 --- a/spec/install/gems/mirror_spec.rb +++ b/spec/install/gems/mirror_spec.rb @@ -13,7 +13,7 @@ RSpec.describe "bundle install with a mirror configured" do it "installs from the normal location" do bundle :install - expect(out).to include("Fetching source index from file://localhost#{gem_repo1}") + expect(out).to include(normalize_uri_file("Fetching source index from file://localhost#{gem_repo1}")) expect(the_bundle).to include_gems "rack 1.0" end end @@ -31,8 +31,8 @@ RSpec.describe "bundle install with a mirror configured" do it "installs the gem from the mirror" do bundle :install - expect(out).to include("Fetching source index from file://localhost#{gem_repo1}") - expect(out).not_to include("Fetching source index from file://localhost#{gem_repo2}") + expect(out).to include(normalize_uri_file("Fetching source index from file://localhost#{gem_repo1}")) + expect(out).not_to include(normalize_uri_file("Fetching source index from file://localhost#{gem_repo2}")) expect(the_bundle).to include_gems "rack 1.0" end end diff --git a/spec/install/gemspecs_spec.rb b/spec/install/gemspecs_spec.rb index 5dca6f68c0..35f96367f7 100644 --- a/spec/install/gemspecs_spec.rb +++ b/spec/install/gemspecs_spec.rb @@ -1,3 +1,4 @@ +# encoding: utf-8 # frozen_string_literal: true RSpec.describe "bundle install" do @@ -63,6 +64,33 @@ RSpec.describe "bundle install" do expect(out).to include("Bundle complete!") end + it "reads gemspecs respecting their encoding" do + skip "Unicode is not supported on Ruby 1.x without extra work" if RUBY_VERSION < "2.0" + + create_file "version.rb", <<-RUBY + module Persistent💎 + VERSION = "0.0.1" + end + RUBY + + create_file "persistent-dmnd.gemspec", <<-G + require_relative "version" + + Gem::Specification.new do |gem| + gem.name = "persistent-dmnd" + gem.version = Persistent💎::VERSION + gem.author = "Ivo Anjo" + gem.summary = "Unscratchable stuff" + end + G + + install_gemfile <<-G + gemspec + G + + expect(out).to include("Bundle complete!") + end + context "when ruby version is specified in gemspec and gemfile" do it "installs when patch level is not specified and the version matches" do build_lib("foo", :path => bundled_app) do |s| diff --git a/spec/install/post_bundle_message_spec.rb b/spec/install/post_bundle_message_spec.rb index 53a93845c2..eadc8a4d85 100644 --- a/spec/install/post_bundle_message_spec.rb +++ b/spec/install/post_bundle_message_spec.rb @@ -116,7 +116,7 @@ RSpec.describe "post bundle message" do gem "rack" gem "not-a-gem", :group => :development G - expect(out).to include <<-EOS.strip + expect(out).to include normalize_uri_file(<<-EOS.strip) Could not find gem 'not-a-gem' in rubygems repository file://localhost#{gem_repo1}/ or installed locally. The source does not contain any versions of 'not-a-gem' EOS diff --git a/spec/install/process_lock_spec.rb b/spec/install/process_lock_spec.rb index 02217f493b..be8fd04fdd 100644 --- a/spec/install/process_lock_spec.rb +++ b/spec/install/process_lock_spec.rb @@ -20,5 +20,16 @@ RSpec.describe "process lock spec" do thread.join expect(the_bundle).to include_gems "rack 1.0" end + + context "when creating a lock raises Errno::ENOTSUP", :ruby => ">= 1.9" do + before { allow(File).to receive(:open).and_raise(Errno::ENOTSUP) } + + it "skips creating the lock file and yields" do + processed = false + Bundler::ProcessLock.lock(default_bundle_path) { processed = true } + + expect(processed).to eq true + end + end end end diff --git a/spec/lock/lockfile_bundler_1_spec.rb b/spec/lock/lockfile_bundler_1_spec.rb index 233e3f63c4..a8615d4c89 100644 --- a/spec/lock/lockfile_bundler_1_spec.rb +++ b/spec/lock/lockfile_bundler_1_spec.rb @@ -1240,7 +1240,7 @@ RSpec.describe "the lockfile format", :bundler => "< 2" do expect(the_bundle).to include_gems "omg 1.0" # Confirm that duplicate specs do not appear - expect(File.read(bundled_app("Gemfile.lock"))).to eq(strip_whitespace(<<-L)) + lockfile_should_be(<<-L) GIT remote: #{lib_path("omg")} revision: #{revision} diff --git a/spec/lock/lockfile_spec.rb b/spec/lock/lockfile_spec.rb index 5be77de7ef..53c832445f 100644 --- a/spec/lock/lockfile_spec.rb +++ b/spec/lock/lockfile_spec.rb @@ -1279,7 +1279,7 @@ RSpec.describe "the lockfile format", :bundler => "2" do expect(the_bundle).to include_gems "omg 1.0" # Confirm that duplicate specs do not appear - expect(File.read(bundled_app("Gemfile.lock"))).to eq(strip_whitespace(<<-L)) + lockfile_should_be(<<-L) GEM remote: file://localhost#{gem_repo1}/ specs: diff --git a/spec/plugins/hook_spec.rb b/spec/plugins/hook_spec.rb index 8bdf61a8ab..53062095e2 100644 --- a/spec/plugins/hook_spec.rb +++ b/spec/plugins/hook_spec.rb @@ -1,27 +1,109 @@ # frozen_string_literal: true RSpec.describe "hook plugins" do - before do - build_repo2 do - build_plugin "before-install-plugin" do |s| - s.write "plugins.rb", <<-RUBY - Bundler::Plugin::API.hook "before-install-all" do |deps| - puts "gems to be installed \#{deps.map(&:name).join(", ")}" - end - RUBY + context "before-install-all hook" do + before do + build_repo2 do + build_plugin "before-install-all-plugin" do |s| + s.write "plugins.rb", <<-RUBY + Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_INSTALL_ALL do |deps| + puts "gems to be installed \#{deps.map(&:name).join(", ")}" + end + RUBY + end end + + bundle "plugin install before-install-all-plugin --source file://#{gem_repo2}" + end + + it "runs before all rubygems are installed" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rake" + gem "rack" + G + + expect(out).to include "gems to be installed rake, rack" + end + end + + context "before-install hook" do + before do + build_repo2 do + build_plugin "before-install-plugin" do |s| + s.write "plugins.rb", <<-RUBY + Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_INSTALL do |spec_install| + puts "installing gem \#{spec_install.name}" + end + RUBY + end + end + + bundle "plugin install before-install-plugin --source file://#{gem_repo2}" + end + + it "runs before each rubygem is installed" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rake" + gem "rack" + G + + expect(out).to include "installing gem rake" + expect(out).to include "installing gem rack" end + end + + context "after-install-all hook" do + before do + build_repo2 do + build_plugin "after-install-all-plugin" do |s| + s.write "plugins.rb", <<-RUBY + Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_INSTALL_ALL do |deps| + puts "installed gems \#{deps.map(&:name).join(", ")}" + end + RUBY + end + end + + bundle "plugin install after-install-all-plugin --source file://#{gem_repo2}" + end + + it "runs after each rubygem is installed" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rake" + gem "rack" + G - bundle "plugin install before-install-plugin --source file://#{gem_repo2}" + expect(out).to include "installed gems rake, rack" + end end - it "runs after a rubygem is installed" do - install_gemfile <<-G - source "file://#{gem_repo1}" - gem "rake" - gem "rack" - G + context "after-install hook" do + before do + build_repo2 do + build_plugin "after-install-plugin" do |s| + s.write "plugins.rb", <<-RUBY + Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_INSTALL do |spec_install| + puts "installed gem \#{spec_install.name} : \#{spec_install.state}" + end + RUBY + end + end + + bundle "plugin install after-install-plugin --source file://#{gem_repo2}" + end + + it "runs after each rubygem is installed" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rake" + gem "rack" + G - expect(out).to include "gems to be installed rake, rack" + expect(out).to include "installed gem rake : installed" + expect(out).to include "installed gem rack : installed" + end end end diff --git a/spec/resolver/basic_spec.rb b/spec/resolver/basic_spec.rb index c829243a9f..6d2dea2fb3 100644 --- a/spec/resolver/basic_spec.rb +++ b/spec/resolver/basic_spec.rb @@ -80,6 +80,13 @@ RSpec.describe "Resolving" do should_resolve_as %w[need-pre-1.0.0 activesupport-3.0.0.beta1] end + it "selects a pre-release if it's specified in the Gemfile" do + dep "activesupport", "= 3.0.0.beta" + dep "actionpack" + + should_resolve_as %w[activesupport-3.0.0.beta actionpack-3.0.0.beta rack-1.1 rack-mount-0.6] + end + it "raises an exception if a child dependency is not resolved" do @index = a_unresovable_child_index dep "chef_app_error" diff --git a/spec/runtime/setup_spec.rb b/spec/runtime/setup_spec.rb index e84cdaabf5..cd87971238 100644 --- a/spec/runtime/setup_spec.rb +++ b/spec/runtime/setup_spec.rb @@ -857,6 +857,50 @@ end expect(out).to eq("true\ntrue") end + context "with bundler is located in symlinked GEM_HOME" do + let(:gem_home) { Dir.mktmpdir } + let(:symlinked_gem_home) { Tempfile.new("gem_home") } + let(:bundler_dir) { File.expand_path("../../..", __FILE__) } + let(:bundler_lib) { File.join(bundler_dir, "lib") } + + before do + FileUtils.ln_sf(gem_home, symlinked_gem_home.path) + gems_dir = File.join(gem_home, "gems") + specifications_dir = File.join(gem_home, "specifications") + Dir.mkdir(gems_dir) + Dir.mkdir(specifications_dir) + + FileUtils.ln_s(bundler_dir, File.join(gems_dir, "bundler-#{Bundler::VERSION}")) + + gemspec = File.read("#{bundler_dir}/bundler.gemspec"). + sub("Bundler::VERSION", %("#{Bundler::VERSION}")) + gemspec = gemspec.lines.reject {|line| line =~ %r{lib/bundler/version} }.join + + File.open(File.join(specifications_dir, "bundler.gemspec"), "wb") do |f| + f.write(gemspec) + end + end + + it "should succesfully require 'bundler/setup'" do + install_gemfile "" + + ENV["GEM_PATH"] = symlinked_gem_home.path + + ruby <<-R + if $LOAD_PATH.include?("#{bundler_lib}") + # We should use bundler from GEM_PATH for this test, so we should + # remove path to the bundler source tree + $LOAD_PATH.delete("#{bundler_lib}") + else + raise "We don't have #{bundler_lib} in $LOAD_PATH" + end + puts (require 'bundler/setup') + R + + expect(out).to eql("true") + end + end + it "stubs out Gem.refresh so it does not reveal system gems" do system_gems "rack-1.0.0" @@ -1166,7 +1210,7 @@ end #{Bundler::VERSION} L - lock + normalize_uri_file(lock) end before do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 228b9e5aa3..56094b72f5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -28,6 +28,18 @@ require "bundler/vendored_fileutils" require "uri" require "digest" +# Delete the default copy of Bundler that RVM installs for us when running in CI +require "fileutils" +if ENV.select {|k, _v| k =~ /TRAVIS/ }.any? && Gem::Version.new(Gem::VERSION) > Gem::Version.new("2.0") + Dir.glob(File.join(Gem::Specification.default_specifications_dir, "bundler*.gemspec")).each do |file| + FileUtils.rm_rf(file) + end + + Dir.glob(File.join(RbConfig::CONFIG["sitelibdir"], "bundler*")).each do |file| + FileUtils.rm_rf(file) + end +end + if File.expand_path(__FILE__) =~ %r{([^\w/\.-])} abort "The bundler specs cannot be run from a path that contains special characters (particularly #{$1.inspect})" end diff --git a/spec/support/builders.rb b/spec/support/builders.rb index e496df6ecb..fa32b12b65 100644 --- a/spec/support/builders.rb +++ b/spec/support/builders.rb @@ -601,7 +601,7 @@ module Spec @spec.rubygems_version = options[:rubygems_version] def @spec.mark_version; end - def @spec.validate; end + def @spec.validate(*); end end case options[:gemspec] diff --git a/spec/support/helpers.rb b/spec/support/helpers.rb index 1b52ed5258..e2b96f5d21 100644 --- a/spec/support/helpers.rb +++ b/spec/support/helpers.rb @@ -280,6 +280,15 @@ module Spec str.gsub(/^#{spaces}/, "") end + def normalize_uri_file(str) + # URI::File of Ruby 2.6 normalize localhost variable with file protocol. + if defined?(URI::File) + str.gsub(%r{file:\/\/localhost}, "file://") + else + str + end + end + def install_gemfile(*args) gemfile(*args) opts = args.last.is_a?(Hash) ? args.last : {} @@ -310,7 +319,11 @@ module Spec raise "OMG `#{path}` does not exist!" unless File.exist?(path) - gem_command! :install, "--no-rdoc --no-ri --ignore-dependencies '#{path}'" + if Gem::VERSION < "2.0.0" + gem_command! :install, "--no-rdoc --no-ri --ignore-dependencies '#{path}'" + else + gem_command! :install, "--no-document --ignore-dependencies '#{path}'" + end bundler_path && bundler_path.rmtree end end diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index 782257a222..8e17be3a02 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -236,7 +236,11 @@ module Spec end def lockfile_should_be(expected) - expect(bundled_app("Gemfile.lock")).to read_as(strip_whitespace(expected)) + expect(bundled_app("Gemfile.lock")).to read_as(normalize_uri_file(strip_whitespace(expected))) + end + + def gemfile_should_be(expected) + expect(bundled_app("Gemfile")).to read_as(strip_whitespace(expected)) end end end diff --git a/spec/support/rubygems_ext.rb b/spec/support/rubygems_ext.rb index 3627e5a71d..806933fe2f 100644 --- a/spec/support/rubygems_ext.rb +++ b/spec/support/rubygems_ext.rb @@ -59,7 +59,11 @@ module Spec no_reqs.map!(&:first) reqs.map! {|name, req| "'#{name}:#{req}'" } deps = reqs.concat(no_reqs).join(" ") - cmd = "gem install #{deps} --no-rdoc --no-ri --conservative" + cmd = if Gem::VERSION < "2.0.0" + "gem install #{deps} --no-rdoc --no-ri --conservative" + else + "gem install #{deps} --no-document --conservative" + end puts cmd system(cmd) || raise("Installing gems #{deps} for the tests to use failed!") end |