# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1

require File.expand_path("../../base", __FILE__)
require "json"
require "pathname"
require "tempfile"
require "tmpdir"

require "vagrant/util/file_mode"
require "vagrant/util/platform"

describe Vagrant::Environment do
  include_context "unit"
  include_context "capability_helpers"

  let(:env) do
    isolated_environment.tap do |e|
      e.box3("base", "1.0", :virtualbox)
      e.vagrantfile <<-VF
      Vagrant.configure("2") do |config|
        config.vm.box = "base"
      end
      VF
    end
  end

  let(:instance)  { env.create_vagrant_env }
  subject { instance }

  describe "#can_install_provider?" do
    let(:plugin_hosts) { {} }
    let(:plugin_host_caps) { {} }

    before do
      m = Vagrant.plugin("2").manager
      allow(m).to receive(:hosts).and_return(plugin_hosts)
      allow(m).to receive(:host_capabilities).and_return(plugin_host_caps)

      # Detect the host
      env.vagrantfile <<-VF
      Vagrant.configure("2") do |config|
        config.vagrant.host = nil
      end
      VF

      # Setup the foo host by default
      plugin_hosts[:foo] = [detect_class(true), nil]
    end

    it "should return whether it can install or not" do
      plugin_host_caps[:foo] = { provider_install_foo: Class }

      expect(subject.can_install_provider?(:foo)).to be(true)
      expect(subject.can_install_provider?(:bar)).to be(false)
    end
  end

  describe "#install_provider" do
    let(:host) { double(:host) }

    before do
      allow(subject).to receive(:host).and_return(host)
    end

    it "should install the correct provider" do
      expect(host).to receive(:capability).with(:provider_install_foo)

      subject.install_provider(:foo)
    end
  end

  describe "#gems_path" do
    it "is set to Vagrant::Bundler defined path" do
      instance = described_class.new
      expect(instance.gems_path).to eq(Vagrant::Bundler.instance.plugin_gem_path)
    end
  end

  describe "#home_path" do
    it "is set to the home path given" do
      Dir.mktmpdir("vagrant-test-env-home-path-given") do |dir|
        instance = described_class.new(home_path: dir)
        expect(instance.home_path).to eq(Pathname.new(dir))
      end
    end

    it "is set to the environmental variable VAGRANT_HOME" do
      Dir.mktmpdir("vagrant-test-env-home-env-var") do |dir|
        instance = with_temp_env("VAGRANT_HOME" => dir.to_s) do
          described_class.new
        end

        expect(instance.home_path).to eq(Pathname.new(dir))
      end
    end

    it "throws an exception if inaccessible", skip_windows: true do
      expect {
        described_class.new(home_path: "/")
      }.to raise_error(Vagrant::Errors::HomeDirectoryNotAccessible)
    end

    context "with setup version file" do
      it "creates a setup version flie" do
        path = subject.home_path.join("setup_version")
        expect(path).to be_file
        expect(path.read).to eq(Vagrant::Environment::CURRENT_SETUP_VERSION)
      end

      it "is okay if it has the current version" do
        Dir.mktmpdir("vagrant-test-env-current-version") do |dir|
          Pathname.new(dir).join("setup_version").open("w") do |f|
            f.write(Vagrant::Environment::CURRENT_SETUP_VERSION)
          end

          instance = described_class.new(home_path: dir)
          path = instance.home_path.join("setup_version")
          expect(path).to be_file
          expect(path.read).to eq(Vagrant::Environment::CURRENT_SETUP_VERSION)
        end
      end

      it "raises an exception if the version is newer than ours" do
        Dir.mktmpdir("vagrant-test-env-newer-version") do |dir|
          Pathname.new(dir).join("setup_version").open("w") do |f|
            f.write("100.5")
          end

          expect { described_class.new(home_path: dir) }.
            to raise_error(Vagrant::Errors::HomeDirectoryLaterVersion)
        end
      end

      it "raises an exception if there is an unknown home directory version" do
        Dir.mktmpdir("vagrant-test-env-unknown-home") do |dir|
          Pathname.new(dir).join("setup_version").open("w") do |f|
            f.write("0.7")
          end

          expect { described_class.new(home_path: dir) }.
            to raise_error(Vagrant::Errors::HomeDirectoryUnknownVersion)
        end
      end
    end

    context "upgrading a v1.1 directory structure" do
      let(:env) { isolated_environment }

      before do
        env.homedir.join("setup_version").open("w") do |f|
          f.write("1.1")
        end

        allow_any_instance_of(Vagrant::UI::Silent).
          to receive(:ask)
      end

      it "replaces the setup version with the new version" do
        expect(subject.home_path.join("setup_version").read).
          to eq(Vagrant::Environment::CURRENT_SETUP_VERSION)
      end

      it "moves the boxes into the new directory structure" do
        # Kind of hacky but avoids two instantiations of BoxCollection
        allow(Vagrant::Environment).to receive(:boxes)
          .and_return(double("boxes"))

        collection = double("collection")
        expect(Vagrant::BoxCollection).to receive(:new).with(
          env.homedir.join("boxes"), anything).twice.and_return(collection)
        expect(collection).to receive(:upgrade_v1_1_v1_5).once
        subject
      end
    end
  end

  describe "#host" do
    let(:plugin_hosts) { {} }
    let(:plugin_host_caps) { {} }

    before do
      m = Vagrant.plugin("2").manager
      allow(m).to receive(:hosts).and_return(plugin_hosts)
      allow(m).to receive(:host_capabilities).and_return(plugin_host_caps)
    end

    it "should default to some host even if there are none" do
      env.vagrantfile <<-VF
      Vagrant.configure("2") do |config|
        config.vagrant.host = nil
      end
      VF

      expect(subject.host).to be
    end

    it "should attempt to detect a host if no host is set" do
      env.vagrantfile <<-VF
      Vagrant.configure("2") do |config|
        config.vagrant.host = nil
      end
      VF

      plugin_hosts[:foo] = [detect_class(true), nil]
      plugin_host_caps[:foo] = { bar: Class }

      result = subject.host
      expect(result.capability?(:bar)).to be(true)
    end

    it "should attempt to detect a host if host is :detect" do
      env.vagrantfile <<-VF
      Vagrant.configure("2") do |config|
        config.vagrant.host = :detect
      end
      VF

      plugin_hosts[:foo] = [detect_class(true), nil]
      plugin_host_caps[:foo] = { bar: Class }

      result = subject.host
      expect(result.capability?(:bar)).to be(true)
    end

    it "should use an exact host if specified" do
      env.vagrantfile <<-VF
      Vagrant.configure("2") do |config|
        config.vagrant.host = "foo"
      end
      VF

      plugin_hosts[:foo] = [detect_class(false), nil]
      plugin_hosts[:bar] = [detect_class(true), nil]
      plugin_host_caps[:foo] = { bar: Class }

      result = subject.host
      expect(result.capability?(:bar)).to be(true)
    end

    it "should raise an error if an exact match was specified but not found" do
      env.vagrantfile <<-VF
      Vagrant.configure("2") do |config|
        config.vagrant.host = "bar"
      end
      VF

      expect { subject.host }.
        to raise_error(Vagrant::Errors::HostExplicitNotDetected)
    end
  end

  describe "#lock" do
    def lock_count
      subject.data_dir.
        children.
        find_all { |c| c.to_s.end_with?("lock") }.
        length
    end

    it "does nothing if no block is given" do
      subject.lock
    end

    it "locks the environment" do
      another = env.create_vagrant_env
      raised  = false

      subject.lock do
        begin
          another.lock {}
        rescue Vagrant::Errors::EnvironmentLockedError
          raised = true
        end
      end

      expect(raised).to be(true)
    end

    it "allows nested locks on the same environment" do
      success = false

      subject.lock do
        subject.lock do
          success = true
        end
      end

      expect(success).to be(true)
    end

    it "cleans up all lock files" do
      inner_count = nil

      expect(lock_count).to eq(0)
      subject.lock do
        inner_count = lock_count
      end

      expect(inner_count).to_not be_nil
      expect(inner_count).to eq(2)
      expect(lock_count).to eq(1)
    end
  end

  describe "#machine" do
    # A helper to register a provider for use in tests.
    def register_provider(name, config_class=nil, options=nil)
      provider_cls = Class.new(VagrantTests::DummyProvider)

      register_plugin("2") do |p|
        p.provider(name, options) { provider_cls }

        if config_class
          p.config(name, :provider) { config_class }
        end
      end

      provider_cls
    end

    it "should return a machine object with the correct provider" do
      # Create a provider
      foo_provider = register_provider("foo")

      # Create the configuration
      isolated_env = isolated_environment do |e|
        e.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
  config.vm.box = "base"
  config.vm.define "foo"
end
VF

        e.box3("base", "1.0", :foo)
      end

      # Verify that we can get the machine
      env = isolated_env.create_vagrant_env
      machine = env.machine(:foo, :foo)
      expect(machine).to be_kind_of(Vagrant::Machine)
      expect(machine.name).to eq(:foo)
      expect(machine.provider).to be_kind_of(foo_provider)
      expect(machine.provider_config).to be_nil
    end

    it "should return a machine object with the machine configuration" do
      # Create a provider
      foo_config = Class.new(Vagrant.plugin("2", :config)) do
        attr_accessor :value
      end

      foo_provider = register_provider("foo", foo_config)

      # Create the configuration
      isolated_env = isolated_environment do |e|
        e.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
  config.vm.box = "base"
  config.vm.define "foo"

  config.vm.provider :foo do |fooconfig|
    fooconfig.value = 100
  end
end
VF

        e.box3("base", "1.0", :foo)
      end

      # Verify that we can get the machine
      env = isolated_env.create_vagrant_env
      machine = env.machine(:foo, :foo)
      expect(machine).to be_kind_of(Vagrant::Machine)
      expect(machine.name).to eq(:foo)
      expect(machine.provider).to be_kind_of(foo_provider)
      expect(machine.provider_config.value).to eq(100)
    end

    it "should cache the machine objects by name and provider" do
      # Create a provider
      foo_provider = register_provider("foo")
      bar_provider = register_provider("bar")

      # Create the configuration
      isolated_env = isolated_environment do |e|
        e.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
  config.vm.box = "base"
  config.vm.define "vm1"
  config.vm.define "vm2"
end
VF

        e.box3("base", "1.0", :foo)
        e.box3("base", "1.0", :bar)
      end

      env = isolated_env.create_vagrant_env
      vm1_foo = env.machine(:vm1, :foo)
      vm1_bar = env.machine(:vm1, :bar)
      vm2_foo = env.machine(:vm2, :foo)

      expect(vm1_foo).to eql(env.machine(:vm1, :foo))
      expect(vm1_bar).to eql(env.machine(:vm1, :bar))
      expect(vm1_foo).not_to eql(vm1_bar)
      expect(vm2_foo).to eql(env.machine(:vm2, :foo))
    end

    it "should load a machine without a box" do
      register_provider("foo")

      environment = isolated_environment do |env|
        env.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
  config.vm.box = "i-dont-exist"
end
VF
      end

      env = environment.create_vagrant_env
      machine = env.machine(:default, :foo)
      expect(machine.box).to be_nil
    end

    it "should load the machine configuration" do
      register_provider("foo")

      environment = isolated_environment do |env|
        env.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
  config.ssh.port = 1
  config.vm.box = "base"

  config.vm.define "vm1" do |inner|
    inner.ssh.port = 100
  end
end
VF

        env.box3("base", "1.0", :foo)
      end

      env = environment.create_vagrant_env
      machine = env.machine(:vm1, :foo)
      expect(machine.config.ssh.port).to eq(100)
      expect(machine.config.vm.box).to eq("base")
    end

    it "should load the box configuration for a box" do
      register_provider("foo")

      environment = isolated_environment do |env|
        env.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
  config.vm.box = "base"
end
VF

        env.box3("base", "1.0", :foo, vagrantfile: <<-VF)
Vagrant.configure("2") do |config|
  config.ssh.port = 100
end
VF
      end

      env = environment.create_vagrant_env
      machine = env.machine(:default, :foo)
      expect(machine.config.ssh.port).to eq(100)
    end

    it "should load the box configuration for a box and custom Vagrantfile name" do
      register_provider("foo")

      environment = isolated_environment do |env|
        env.file("some_other_name", <<-VF)
Vagrant.configure("2") do |config|
  config.vm.box = "base"
end
VF

        env.box3("base", "1.0", :foo, vagrantfile: <<-VF)
Vagrant.configure("2") do |config|
  config.ssh.port = 100
end
VF
      end

      env = with_temp_env("VAGRANT_VAGRANTFILE" => "some_other_name") do
        environment.create_vagrant_env
      end

      machine = env.machine(:default, :foo)
      expect(machine.config.ssh.port).to eq(100)
    end

    it "should load the box configuration for other formats for a box" do
      register_provider("foo", nil, box_format: "bar")

      environment = isolated_environment do |env|
        env.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
  config.vm.box = "base"
end
VF

        env.box3("base", "1.0", :bar, vagrantfile: <<-VF)
Vagrant.configure("2") do |config|
  config.ssh.port = 100
end
VF
      end

      env = environment.create_vagrant_env
      machine = env.machine(:default, :foo)
      expect(machine.config.ssh.port).to eq(100)
    end

    it "prefer sooner formats when multiple box formats are available" do
      register_provider("foo", nil, box_format: ["fA", "fB"])

      environment = isolated_environment do |env|
        env.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
  config.vm.box = "base"
end
VF

        env.box3("base", "1.0", :fA, vagrantfile: <<-VF)
Vagrant.configure("2") do |config|
  config.ssh.port = 100
end
VF

        env.box3("base", "1.0", :fB, vagrantfile: <<-VF)
Vagrant.configure("2") do |config|
  config.ssh.port = 200
end
VF
      end

      env = environment.create_vagrant_env
      machine = env.machine(:default, :foo)
      expect(machine.config.ssh.port).to eq(100)
    end

    it "should load the proper version of a box" do
      register_provider("foo")

      environment = isolated_environment do |env|
        env.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
  config.vm.box = "base"
  config.vm.box_version = "~> 1.2"
end
VF

        env.box3("base", "1.0", :foo, vagrantfile: <<-VF)
Vagrant.configure("2") do |config|
  config.ssh.port = 100
end
VF

        env.box3("base", "1.5", :foo, vagrantfile: <<-VF)
Vagrant.configure("2") do |config|
  config.ssh.port = 200
end
VF
      end

      env = environment.create_vagrant_env
      machine = env.machine(:default, :foo)
      expect(machine.config.ssh.port).to eq(200)
    end

    it "should load the provider override if set" do
      register_provider("bar")
      register_provider("foo")

      isolated_env = isolated_environment do |e|
        e.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
  config.vm.box = "foo"

  config.vm.provider :foo do |_, c|
    c.vm.box = "bar"
  end
end
VF
      end

      env = isolated_env.create_vagrant_env
      foo_vm = env.machine(:default, :foo)
      bar_vm = env.machine(:default, :bar)
      expect(foo_vm.config.vm.box).to eq("bar")
      expect(bar_vm.config.vm.box).to eq("foo")
    end

    it "should reload the cache if refresh is set" do
      # Create a provider
      foo_provider = register_provider("foo")

      # Create the configuration
      isolated_env = isolated_environment do |e|
        e.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
  config.vm.box = "base"
end
VF

        e.box3("base", "1.0", :foo)
      end

      env = isolated_env.create_vagrant_env
      vm1 = env.machine(:default, :foo)
      vm2 = env.machine(:default, :foo, true)
      vm3 = env.machine(:default, :foo)

      expect(vm1).not_to eql(vm2)
      expect(vm2).to eql(vm3)
    end

    it "should raise an error if the VM is not found" do
      expect { instance.machine("i-definitely-dont-exist", :virtualbox) }.
        to raise_error(Vagrant::Errors::MachineNotFound)
    end

    it "should raise an error if the provider is not found" do
      expect { instance.machine(:default, :lol_no) }.
        to raise_error(Vagrant::Errors::ProviderNotFound)
    end
  end

  describe "#machine_index" do
    it "returns a machine index" do
      expect(subject.machine_index).to be_kind_of(Vagrant::MachineIndex)
    end

    it "caches the result" do
      result = subject.machine_index
      expect(subject.machine_index).to equal(result)
    end

    it "uses a directory within the home directory by default" do
      klass = double("machine_index")
      stub_const("Vagrant::MachineIndex", klass)
      # Need to stub the mappers here since the Vagrant::MachineIndex module is getting
      # stubbed as a double. This is causing the mapper definition to fail on account
      # of the Input to the mapper not being a module. The mapper has no impact on this 
      # test otherwise.
      stub_mapper = Class.new {}
      stub_const("VagrantPlugins::CommandServe::Mappers", stub_mapper)

      expect(klass).to receive(:new).with(any_args) do |path|
        expect(path.to_s.start_with?(subject.home_path.to_s)).to be(true)
        true
      end

      subject.machine_index
    end
  end

  describe "active machines" do
    it "should be empty if there is no root path" do
      Dir.mktmpdir("vagrant-test-env-no-root-path") do |temp_dir|
        instance = described_class.new(cwd: temp_dir)
        expect(instance.active_machines).to be_empty
      end
    end

    it "should be empty if the machines folder doesn't exist" do
      folder = instance.local_data_path.join("machines")
      expect(folder).not_to be_exist

      expect(instance.active_machines).to be_empty
    end

    it "should return the name and provider of active machines" do
      machines = instance.local_data_path.join("machines")

      # Valid machine, with "foo" and virtualbox
      machine_foo = machines.join("foo/virtualbox")
      machine_foo.mkpath
      machine_foo.join("id").open("w+") { |f| f.write("") }

      # Invalid machine (no ID)
      machine_bar = machines.join("bar/virtualbox")
      machine_bar.mkpath

      expect(instance.active_machines).to eq([[:foo, :virtualbox]])
    end
  end

  describe "batching" do
    let(:batch) do
      double("batch") do |b|
        allow(b).to receive(:run)
      end
    end

    context "without the disabling env var" do
      it "should run without disabling parallelization" do
        with_temp_env("VAGRANT_NO_PARALLEL" => nil) do
          expect(Vagrant::BatchAction).to receive(:new).with(true).and_return(batch)
          expect(batch).to receive(:run)

          instance.batch {}
        end
      end

      it "should run with disabling parallelization if explicit" do
        with_temp_env("VAGRANT_NO_PARALLEL" => nil) do
          expect(Vagrant::BatchAction).to receive(:new).with(false).and_return(batch)
          expect(batch).to receive(:run)

          instance.batch(false) {}
        end
      end
    end

    context "with the disabling env var" do
      it "should run with disabling parallelization" do
        with_temp_env("VAGRANT_NO_PARALLEL" => "yes") do
          expect(Vagrant::BatchAction).to receive(:new).with(false).and_return(batch)
          expect(batch).to receive(:run)

          instance.batch {}
        end
      end
    end
  end

  describe "current working directory" do
    it "is the cwd by default" do
      Dir.mktmpdir("vagrant-test-env-cwd-default") do |temp_dir|
        Dir.chdir(temp_dir) do
          with_temp_env("VAGRANT_CWD" => nil) do
            expect(described_class.new.cwd).to eq(Pathname.new(Dir.pwd))
          end
        end
      end
    end

    it "is set to the cwd given" do
      Dir.mktmpdir("vagrant-test-env-set-cwd") do |directory|
        instance = described_class.new(cwd: directory)
        expect(instance.cwd).to eq(Pathname.new(directory))
      end
    end

    it "is set to the environmental variable VAGRANT_CWD" do
      Dir.mktmpdir("vagrant-test-env-set-vagrant-cwd") do |directory|
        instance = with_temp_env("VAGRANT_CWD" => directory) do
          described_class.new
        end

        expect(instance.cwd).to eq(Pathname.new(directory))
      end
    end

    it "raises an exception if the CWD doesn't exist" do
      expect { described_class.new(cwd: "doesntexist") }.
        to raise_error(Vagrant::Errors::EnvironmentNonExistentCWD)
    end
  end

  describe "default provider" do
    let(:plugin_providers) { {} }

    before do
      m = Vagrant.plugin("2").manager
      allow(m).to receive(:providers).and_return(plugin_providers)
      allow_any_instance_of(described_class).to receive(:process_configured_plugins)
    end

    it "is the highest matching usable provider" do
      plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }]
      plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }]
      plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }]
      plugin_providers[:boom] = [provider_usable_class(true), { priority: 3 }]

      with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil,
                    "VAGRANT_PREFERRED_PROVIDERS" => nil) do
        expect(subject.default_provider).to eq(:bar)
      end
    end

    it "is the highest matching usable provider that is defaultable" do
      plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }]
      plugin_providers[:bar] = [
        provider_usable_class(true), { defaultable: false, priority: 7 }]
      plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }]

      with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil,
                    "VAGRANT_PREFERRED_PROVIDERS" => nil) do
        expect(subject.default_provider).to eq(:foo)
      end
    end

    it "is the highest matching usable provider that isn't excluded" do
      plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }]
      plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }]
      plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }]
      plugin_providers[:boom] = [provider_usable_class(true), { priority: 3 }]

      with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil,
                    "VAGRANT_PREFERRED_PROVIDERS" => nil) do
        expect(subject.default_provider(exclude: [:bar, :foo])).to eq(:boom)
      end
    end

    it "is the default provider set if usable" do
      plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }]
      plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }]
      plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }]
      plugin_providers[:boom] = [provider_usable_class(true), { priority: 3 }]

      with_temp_env("VAGRANT_DEFAULT_PROVIDER" => "baz",
                    "VAGRANT_PREFERRED_PROVIDERS" => nil) do
        expect(subject.default_provider).to eq(:baz)
      end
    end

    it "is the default provider set even if unusable" do
      plugin_providers[:baz] = [provider_usable_class(false), { priority: 5 }]
      plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }]
      plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }]

      with_temp_env("VAGRANT_DEFAULT_PROVIDER" => "baz",
                    "VAGRANT_PREFERRED_PROVIDERS" => nil) do
        expect(subject.default_provider).to eq(:baz)
      end
    end

    it "is the usable despite default if not forced" do
      plugin_providers[:baz] = [provider_usable_class(false), { priority: 5 }]
      plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }]
      plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }]

      with_temp_env("VAGRANT_DEFAULT_PROVIDER" => "baz",
                    "VAGRANT_PREFERRED_PROVIDERS" => nil) do
        expect(subject.default_provider(force_default: false)).to eq(:bar)
      end
    end

    it "prefers the default even if not forced" do
      plugin_providers[:baz] = [provider_usable_class(true), { priority: 5 }]
      plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }]
      plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }]

      with_temp_env("VAGRANT_DEFAULT_PROVIDER" => "baz",
                    "VAGRANT_PREFERRED_PROVIDERS" => nil) do
        expect(subject.default_provider(force_default: false)).to eq(:baz)
      end
    end

    it "uses the first usable provider that isn't the default if excluded" do
      plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }]
      plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }]
      plugin_providers[:baz] = [provider_usable_class(true), { priority: 8 }]

      with_temp_env("VAGRANT_DEFAULT_PROVIDER" => "baz",
                    "VAGRANT_PREFERRED_PROVIDERS" => nil) do
        expect(subject.default_provider(
          exclude: [:baz], force_default: false)).to eq(:bar)
      end
    end

    it "raise an error if nothing else is usable" do
      plugin_providers[:foo] = [provider_usable_class(false), { priority: 5 }]
      plugin_providers[:bar] = [provider_usable_class(false), { priority: 5 }]
      plugin_providers[:baz] = [provider_usable_class(false), { priority: 5 }]

      with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil,
                    "VAGRANT_PREFERRED_PROVIDERS" => nil) do
        expect { subject.default_provider }.to raise_error(
          Vagrant::Errors::NoDefaultProvider)
      end
    end

    it "is the provider in the Vagrantfile that is preferred and usable" do
      subject.vagrantfile.config.vm.provider "foo"
      subject.vagrantfile.config.vm.provider "bar"
      subject.vagrantfile.config.vm.finalize!

      plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }]
      plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }]
      plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }]
      plugin_providers[:boom] = [provider_usable_class(true), { priority: 3 }]

      with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil,
                    "VAGRANT_PREFERRED_PROVIDERS" => 'baz,bar') do
        expect(subject.default_provider).to eq(:bar)
      end
    end

    it "is the provider in the Vagrantfile that is usable" do
      subject.vagrantfile.config.vm.provider "foo"
      subject.vagrantfile.config.vm.provider "bar"
      subject.vagrantfile.config.vm.finalize!

      plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }]
      plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }]
      plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }]
      plugin_providers[:boom] = [provider_usable_class(true), { priority: 3 }]

      with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil,
                    "VAGRANT_PREFERRED_PROVIDERS" => nil) do
        expect(subject.default_provider).to eq(:foo)
      end
    end

    it "is the provider in the Vagrantfile that is usable even if only one specified (1)" do
      subject.vagrantfile.config.vm.provider "foo"
      subject.vagrantfile.config.vm.finalize!

      plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }]
      plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }]

      with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil,
                    "VAGRANT_PREFERRED_PROVIDERS" => nil) do
        expect(subject.default_provider).to eq(:foo)
      end
    end

    it "is the provider in the Vagrantfile that is usable even if only one specified (2)" do
      subject.vagrantfile.config.vm.provider "bar"
      subject.vagrantfile.config.vm.finalize!

      plugin_providers[:foo] = [provider_usable_class(true), { priority: 7 }]
      plugin_providers[:bar] = [provider_usable_class(true), { priority: 5 }]

      with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil,
                    "VAGRANT_PREFERRED_PROVIDERS" => nil) do
        expect(subject.default_provider).to eq(:bar)
      end
    end

    it "is the preferred usable provider outside the Vagrantfile" do
      subject.vagrantfile.config.vm.provider "foo"
      subject.vagrantfile.config.vm.finalize!

      plugin_providers[:foo] = [provider_usable_class(false), { priority: 5 }]
      plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }]
      plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }]
      plugin_providers[:boom] = [provider_usable_class(true), { priority: 3 }]

      with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil,
                    "VAGRANT_PREFERRED_PROVIDERS" => 'boom,baz') do
        expect(subject.default_provider).to eq(:boom)
      end
    end

    it "is the highest usable provider outside the Vagrantfile" do
      subject.vagrantfile.config.vm.provider "foo"
      subject.vagrantfile.config.vm.finalize!

      plugin_providers[:foo] = [provider_usable_class(false), { priority: 5 }]
      plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }]
      plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }]
      plugin_providers[:boom] = [provider_usable_class(true), { priority: 3 }]

      with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil,
                    "VAGRANT_PREFERRED_PROVIDERS" => nil) do
        expect(subject.default_provider).to eq(:bar)
      end
    end

    it "is the provider in the Vagrantfile that is usable for a machine" do
      subject.vagrantfile.config.vm.provider "foo"
      subject.vagrantfile.config.vm.define "sub" do |v|
        v.vm.provider "bar"
      end
      subject.vagrantfile.config.vm.finalize!

      plugin_providers[:foo] = [provider_usable_class(true), { priority: 5 }]
      plugin_providers[:bar] = [provider_usable_class(true), { priority: 7 }]
      plugin_providers[:baz] = [provider_usable_class(true), { priority: 2 }]
      plugin_providers[:boom] = [provider_usable_class(true), { priority: 3 }]

      with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil,
                    "VAGRANT_PREFERRED_PROVIDERS" => nil) do
        expect(subject.default_provider(machine: :sub)).to eq(:bar)
      end
    end
  end

  describe "local data path" do
    it "is set to the proper default" do
      default = instance.root_path.join(described_class::DEFAULT_LOCAL_DATA)
      expect(instance.local_data_path).to eq(default)
    end

    it "is expanded relative to the cwd" do
      Dir.mktmpdir("vagrant-test-env-relative-cwd") do |temp_dir|
        Dir.chdir(temp_dir) do
          instance = described_class.new(local_data_path: "foo")
          expect(instance.local_data_path).to eq(instance.cwd.join("foo"))
        end
      end
    end

    it "is set to the given value" do
      Dir.mktmpdir("vagrant-test-env-set-given") do |dir|
        instance = described_class.new(local_data_path: dir)
        expect(instance.local_data_path.to_s).to eq(dir)
      end
    end

    context "with environmental variable VAGRANT_DOTFILE_PATH set to the empty string" do
      it "is set to the default, from the work directory" do
        with_temp_env("VAGRANT_DOTFILE_PATH" => "") do
          instance = env.create_vagrant_env
          expect(instance.cwd).to eq(env.workdir)
          expect(instance.local_data_path.to_s).to eq(File.join(env.workdir, ".vagrant"))
        end
      end

      it "is set to the default, from a sub-directory of the work directory" do
        Dir.mktmpdir("sub-directory", env.workdir) do |temp_dir|
          with_temp_env("VAGRANT_DOTFILE_PATH" => "") do
            instance = env.create_vagrant_env(cwd: temp_dir)
            expect(instance.cwd.to_s).to eq(temp_dir)
            expect(instance.local_data_path.to_s).to eq(File.join(env.workdir, ".vagrant"))
          end
        end
      end
    end

    context "with environmental variable VAGRANT_DOTFILE_PATH set to an absolute path" do
      it "is set to VAGRANT_DOTFILE_PATH from the work directory" do
        Dir.mktmpdir("sub-directory", env.workdir) do |temp_dir|
          dotfile_path = File.join(temp_dir, ".vagrant-custom")

          with_temp_env("VAGRANT_DOTFILE_PATH" => dotfile_path) do
            instance = env.create_vagrant_env
            expect(instance.cwd).to eq(env.workdir)
            expect(instance.local_data_path.to_s).to eq(dotfile_path)
          end
        end
      end

      it "is set to VAGRANT_DOTFILE_PATH from a sub-directory of the work directory" do
        Dir.mktmpdir("sub-directory", env.workdir) do |temp_dir|
          dotfile_path = File.join(temp_dir, ".vagrant-custom")

          with_temp_env("VAGRANT_DOTFILE_PATH" => dotfile_path) do
            instance = env.create_vagrant_env(cwd: temp_dir)
            expect(instance.cwd.to_s).to eq(temp_dir)
            expect(instance.local_data_path.to_s).to eq(dotfile_path)
          end
        end
      end
    end

    context "with environmental variable VAGRANT_DOTFILE_PATH set to a relative path" do
      it "is set relative to the the work directory, from the work directory" do
        Dir.mktmpdir("sub-directory", env.workdir) do |temp_dir|
          with_temp_env("VAGRANT_DOTFILE_PATH" => ".vagrant-custom") do
            instance = env.create_vagrant_env
            expect(instance.cwd).to eq(env.workdir)
            expect(instance.local_data_path.to_s).to eq(File.join(env.workdir, ".vagrant-custom"))
          end
        end
      end

      it "is set relative to the the work directory, from a sub-directory of the work directory" do
        Dir.mktmpdir("sub-directory", env.workdir) do |temp_dir|
          with_temp_env("VAGRANT_DOTFILE_PATH" => ".vagrant-custom") do
            instance = env.create_vagrant_env(cwd: temp_dir)
            expect(instance.cwd.to_s).to eq(temp_dir)
            expect(instance.local_data_path.to_s).to eq(File.join(env.workdir, ".vagrant-custom"))
          end
        end
      end

      it "is set to the empty string when there is no valid work directory" do
        Dir.mktmpdir("out-of-tree-directory") do |temp_dir|
          with_temp_env("VAGRANT_DOTFILE_PATH" => ".vagrant-custom") do
            instance = env.create_vagrant_env(cwd: temp_dir)
            expect(instance.cwd.to_s).to eq(temp_dir)
            expect(instance.local_data_path.to_s).to eq("")
          end
        end
      end
    end

    context "with environmental variable VAGRANT_DOTFILE_PATH set with tilde" do
      it "is set relative to the user's home directory" do
        with_temp_env("VAGRANT_DOTFILE_PATH" => "~/.vagrant") do
          instance = env.create_vagrant_env
          expect(instance.cwd).to eq(env.workdir)
          expect(instance.local_data_path.to_s).to eq(File.join(Dir.home, ".vagrant"))
        end
      end
    end

    describe "upgrading V1 dotfiles" do
      let(:v1_dotfile_tempfile) do
        Tempfile.new("vagrant-upgrade-dotfile").tap do |f|
          f.close
        end
      end

      let(:v1_dotfile)          { Pathname.new(v1_dotfile_tempfile.path) }
      let(:local_data_path)     { v1_dotfile_tempfile.path }
      let(:instance) { described_class.new(local_data_path: local_data_path) }

      after do
        FileUtils.rm_rf(local_data_path)
      end

      it "should be fine if dotfile is empty" do
        v1_dotfile.open("w+") do |f|
          f.write("")
        end

        expect { instance }.to_not raise_error
      end

      it "should upgrade all active VMs" do
        active_vms = {
          "foo" => "foo_id",
          "bar" => "bar_id"
        }

        v1_dotfile.open("w+") do |f|
          f.write(JSON.dump({
            "active" => active_vms
          }))
        end

        expect { instance }.to_not raise_error

        local_data_pathname = Pathname.new(local_data_path)
        foo_id_file = local_data_pathname.join("machines/foo/virtualbox/id")
        expect(foo_id_file).to be_file
        expect(foo_id_file.read).to eq("foo_id")

        bar_id_file = local_data_pathname.join("machines/bar/virtualbox/id")
        expect(bar_id_file).to be_file
        expect(bar_id_file.read).to eq("bar_id")
      end

      it "should raise an error if invalid JSON" do
        v1_dotfile.open("w+") do |f|
          f.write("bad")
        end

        expect { instance }.
          to raise_error(Vagrant::Errors::DotfileUpgradeJSONError)
      end
    end
  end

  describe "copying the private SSH key" do
    it "copies the SSH key into the home directory" do
      env = isolated_environment
      instance = described_class.new(home_path: env.homedir)

      pk = env.homedir.join("insecure_private_key")
      expect(pk).to be_exist

      if !Vagrant::Util::Platform.windows?
        expect(Vagrant::Util::FileMode.from_octal(pk.stat.mode)).to eq("600")
      end
    end
  end

  it "has a box collection pointed to the proper directory" do
    collection = instance.boxes
    expect(collection).to be_kind_of(Vagrant::BoxCollection)
    expect(collection.directory).to eq(instance.boxes_path)

    # Reach into some internal state here but not sure how else
    # to test this at the moment.
    expect(collection.instance_variable_get(:@hook)).
      to eq(instance.method(:hook))
  end

  describe "action runner" do
    it "has an action runner" do
      expect(instance.action_runner).to be_kind_of(Vagrant::Action::Runner)
    end

    it "has a `ui` in the globals" do
      result = nil
      callable = lambda { |env| result = env[:ui] }

      instance.action_runner.run(callable)
      expect(result).to eql(instance.ui)
    end
  end

  describe "#pushes" do
    it "returns the pushes from the Vagrantfile config" do
      environment = isolated_environment do |env|
        env.vagrantfile(<<-VF.gsub(/^ {10}/, ''))
          Vagrant.configure("2") do |config|
            config.push.define "noop"
          end
        VF
      end

      env = environment.create_vagrant_env
      expect(env.pushes).to eq([:noop])
    end
  end

  describe "#push" do
    let(:push_class) do
      Class.new(Vagrant.plugin("2", :push)) do
        def self.pushed?
          !!class_variable_get(:@@pushed)
        end

        def push
          self.class.class_variable_set(:@@pushed, true)
        end
      end
    end

    it "raises an exception when the push does not exist" do
      expect { instance.push("lolwatbacon") }
        .to raise_error(Vagrant::Errors::PushStrategyNotDefined)
    end

    it "raises an exception if the strategy does not exist" do
      environment = isolated_environment do |env|
        env.vagrantfile(<<-VF.gsub(/^ {10}/, ''))
          Vagrant.configure("2") do |config|
            config.push.define "lolwatbacon"
          end
        VF
      end

      env = environment.create_vagrant_env
      expect { env.push("lolwatbacon") }
        .to raise_error(Vagrant::Errors::PushStrategyNotLoaded)
    end

    it "executes the push action" do
      register_plugin("2") do |plugin|
        plugin.name "foo"

        plugin.push(:foo) do
          push_class
        end
      end

      environment = isolated_environment do |env|
        env.vagrantfile(<<-VF.gsub(/^ {10}/, ''))
          Vagrant.configure("2") do |config|
            config.push.define "foo"
          end
        VF
      end

      env = environment.create_vagrant_env
      env.push("foo")
      expect(push_class.pushed?).to be(true)
    end
  end

  describe "#hook" do
    it "should call the action runner with the proper hook" do
      hook_name = :foo

      expect(instance.action_runner).to receive(:run).with(any_args) { |callable, env|
        expect(env[:action_name]).to eq(hook_name)
      }

      instance.hook(hook_name)
    end

    it "should return the result of the action runner run" do
      expect(instance.action_runner).to receive(:run).and_return(:foo)

      expect(instance.hook(:bar)).to eq(:foo)
    end

    it "should allow passing in a custom action runner" do
      expect(instance.action_runner).not_to receive(:run)
      other_runner = double("runner")
      expect(other_runner).to receive(:run).and_return(:foo)

      expect(instance.hook(:bar, runner: other_runner)).to eq(:foo)
    end

    it "should allow passing in custom data" do
      expect(instance.action_runner).to receive(:run).with(any_args) { |callable, env|
        expect(env[:foo]).to eq(:bar)
      }

      instance.hook(:foo, foo: :bar)
    end

    it "should allow passing a custom callable" do
      expect(instance.action_runner).to receive(:run).with(any_args) { |callable, env|
        expect(callable).to eq(:what)
      }

      instance.hook(:foo, callable: :what)
    end
  end

  describe "primary machine name" do
    it "should be the only machine if not a multi-machine environment" do
      expect(instance.primary_machine_name).to eq(instance.machine_names.first)
    end

    it "should be the machine marked as the primary" do
      environment = isolated_environment do |env|
        env.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
  config.vm.box = "base"
  config.vm.define :foo
  config.vm.define :bar, primary: true
end
VF

        env.box3("base", "1.0", :virtualbox)
      end

      env = environment.create_vagrant_env
      expect(env.primary_machine_name).to eq(:bar)
    end

    it "should be nil if no primary is specified in a multi-machine environment" do
      environment = isolated_environment do |env|
        env.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
  config.vm.box = "base"
  config.vm.define :foo
  config.vm.define :bar
end
VF

        env.box3("base", "1.0", :virtualbox)
      end

      env = environment.create_vagrant_env
      expect(env.primary_machine_name).to be_nil
    end
  end

  describe "loading configuration" do
    it "should load global configuration" do
      environment = isolated_environment do |env|
        env.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
  config.ssh.port = 200
end
VF
      end

      env = environment.create_vagrant_env
      expect(env.vagrantfile.config.ssh.port).to eq(200)
    end

    it "should load from a custom Vagrantfile" do
      environment = isolated_environment do |env|
        env.file("non_standard_name", <<-VF)
Vagrant.configure("2") do |config|
  config.ssh.port = 200
end
VF
      end

      env = environment.create_vagrant_env(vagrantfile_name: "non_standard_name")
      expect(env.vagrantfile.config.ssh.port).to eq(200)
    end

    it "should load from a custom Vagrantfile specified by env var" do
      environment = isolated_environment do |env|
        env.file("some_other_name", <<-VF)
Vagrant.configure("2") do |config|
  config.ssh.port = 400
end
VF
      end

      env = with_temp_env("VAGRANT_VAGRANTFILE" => "some_other_name") do
        environment.create_vagrant_env
      end

      expect(env.vagrantfile.config.ssh.port).to eq(400)
    end
  end

  describe "ui" do
    it "should be a silent UI by default" do
      expect(described_class.new.ui).to be_kind_of(Vagrant::UI::Silent)
    end

    it "should be a UI given in the constructor" do
      # Create a custom UI for our test
      class CustomUI < Vagrant::UI::Interface; end

      instance = described_class.new(ui_class: CustomUI)
      expect(instance.ui).to be_kind_of(CustomUI)
    end
  end

  describe "#unload" do
    it "should run the unload hook" do
      expect(instance).to receive(:hook).with(:environment_unload).once
      instance.unload
    end
  end

  describe "getting machine names" do
    it "should return the default machine if no multi-VM is used" do
      # Create the config
      isolated_env = isolated_environment do |e|
        e.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
end
VF
      end

      env = isolated_env.create_vagrant_env
      expect(env.machine_names).to eq([:default])
    end

    it "should return the machine names in order" do
      # Create the config
      isolated_env = isolated_environment do |e|
        e.vagrantfile(<<-VF)
Vagrant.configure("2") do |config|
  config.vm.define "foo"
  config.vm.define "bar"
end
VF
      end

      env = isolated_env.create_vagrant_env
      expect(env.machine_names).to eq([:foo, :bar])
    end
  end

  describe "guess_provider" do
    before { allow_any_instance_of(described_class).to receive(:process_configured_plugins) }

    it "should return the default provider by default" do
      expect(subject).to receive(:default_provider).and_return("default_provider")
      expect(subject.send(:guess_provider)).to eq("default_provider")
    end

    context "when provider is defined via command line argument" do
      before { stub_const("ARGV", argv) }

      context "when provider is given as single argument" do
        let(:argv) { ["--provider=single_arg"] }

        it "should return the provider name" do
          expect(subject.send(:guess_provider)).to eq(:single_arg)
        end
      end

      context "when provider is given as two arguments" do
        let(:argv) { ["--provider", "double_arg"] }

        it "should return the provider name" do
          expect(subject.send(:guess_provider)).to eq(:double_arg)
        end
      end
    end

    context "when no default provider is available" do
      before {
        expect(subject).to receive(:default_provider).
          and_raise(Vagrant::Errors::NoDefaultProvider) }

      it "should return a nil value" do
        expect(subject.send(:guess_provider)).to be_nil
      end
    end
  end

  describe "#find_configured_plugins" do
    before do
      allow_any_instance_of(described_class).to receive(:guess_provider).and_return(:dummy)
      allow_any_instance_of(described_class).to receive(:process_configured_plugins)
    end

    it "should find no plugins when no plugins are configured" do
      expect(subject.send(:find_configured_plugins)).to be_empty
    end

    context "when plugins are defined in the Vagrantfile" do
      before do
        env.vagrantfile <<-VF
          Vagrant.configure("2") do |config|
            config.vagrant.plugins = "vagrant-plugin"
          end
          VF
      end

      it "should return the vagrant-plugin" do
        expect(subject.send(:find_configured_plugins).keys).to include("vagrant-plugin")
      end
    end

    context "when plugins are defined in the Vagrantfile of a box" do
      before do
        env.box3("foo", "1.0", :dummy, vagrantfile: <<-VF)
          Vagrant.configure("2") do |config|
            config.vagrant.plugins = "vagrant-plugin"
          end
        VF
        env.vagrantfile <<-VF
          Vagrant.configure("2") do |config|
            config.vm.box = "foo"
          end
        VF
      end

      it "should return the vagrant-plugin" do
        expect(subject.send(:find_configured_plugins).keys).to include("vagrant-plugin")
      end
    end

    context "when the box does not match the provider" do
      before do
        env.box3("foo", "1.0", :other, vagrantfile: <<-VF)
          Vagrant.configure("2") do |config|
            config.vagrant.plugins = "vagrant-plugin"
          end
        VF
        env.vagrantfile <<-VF
          Vagrant.configure("2") do |config|
            config.vm.box = "foo"
          end
        VF
      end

      it "should not return the vagrant-plugin" do
        expect(subject.send(:find_configured_plugins).keys).not_to include("vagrant-plugin")
      end
    end
  end

  describe "#process_configured_plugins" do
    let(:env) do
      isolated_environment.tap do |e|
        e.box3("base", "1.0", :virtualbox)
        e.vagrantfile(vagrantfile)
      end
    end

    let(:vagrantfile) do
      'Vagrant.configure("2"){ |config| config.vm.box = "base" }'
    end

    let(:plugin_manager) {
      double("plugin_manager", installed_plugins: installed_plugins, local_file: local_file)
    }

    let(:installed_plugins) { {} }
    let(:local_file) { double("local_file", installed_plugins: local_installed_plugins) }
    let(:local_installed_plugins) { {} }

    before do
      allow(Vagrant::Plugin::Manager).to receive(:instance).and_return(plugin_manager)
      allow(plugin_manager).to receive(:globalize!)
      allow(plugin_manager).to receive(:localize!)
      allow(plugin_manager).to receive(:load_plugins)
    end

    context "when local data directory does not exist" do
      let(:local_file) { nil }

      it "should properly return empty result" do
        expect(instance.send(:process_configured_plugins)).to be_empty
      end
    end

    context "plugins are disabled" do
      before{ allow(Vagrant).to receive(:plugins_enabled?).and_return(false) }

      it "should return empty result" do
        expect(instance.send(:process_configured_plugins)).to be_nil
      end
    end

    context "when vagrant is invalid" do
      let(:vagrantfile) { 'Vagrant.configure("2"){ |config| config.vagrant.bad_key = true }' }

      it "should raise a configuration error" do
        expect { instance.send(:process_configured_plugins) }.to raise_error(Vagrant::Errors::ConfigInvalid)
      end
    end

    context "with local plugins defined" do
      let(:vagrantfile) { 'Vagrant.configure("2"){ |config| config.vagrant.plugins = "vagrant" }' }
      let(:installed_plugins) { {"vagrant" => true} }

      context "with plugin already installed" do

        it "should not attempt to install a plugin" do
          expect(plugin_manager).not_to receive(:install_plugin)
          expect(instance.send(:process_configured_plugins)).to eq(local_installed_plugins)
        end
      end

      context "without plugin installed" do

        before { allow(instance).to receive(:exit) }

        it "should prompt user before installation" do
          expect(instance.ui).to receive(:ask).and_return("n")
          expect(plugin_manager).to receive(:installed_plugins).and_return({})
          expect { instance.send(:process_configured_plugins) }.to raise_error(Vagrant::Errors::PluginMissingLocalError)
        end

        it "should install plugin" do
          expect(instance.ui).to receive(:ask).and_return("y")
          expect(plugin_manager).to receive(:installed_plugins).and_return({})
          expect(plugin_manager).to receive(:install_plugin).and_return(double("spec", "name" => "vagrant", "version" => "1"))
          instance.send(:process_configured_plugins)
        end

        it "should exit after install" do
          expect(instance.ui).to receive(:ask).and_return("y")
          expect(plugin_manager).to receive(:installed_plugins).and_return({})
          expect(plugin_manager).to receive(:install_plugin).and_return(double("spec", "name" => "vagrant", "version" => "1"))
          expect(instance).to receive(:exit)
          instance.send(:process_configured_plugins)
        end
      end
    end
  end

  describe "#setup_local_data_path" do
    before do
      allow(FileUtils).to receive(:mkdir_p).and_call_original
      allow(FileUtils).to receive(:cp).and_call_original
    end

    it "should create an rgloader path" do
      expect(FileUtils).to receive(:mkdir_p).with(/(?!home)rgloader/)
      instance
    end

    it "should write the rgloader file" do
      expect(FileUtils).to receive(:cp).with(anything, /(?!home)rgloader.*rb$/)
      instance
    end
  end
end
