[DRE-commits] [ruby-guard] 10/13: support test

Daisuke Higuchi dai at moszumanska.debian.org
Tue Nov 28 08:10:09 UTC 2017


This is an automated email from the git hooks/post-receive script.

dai pushed a commit to branch master
in repository ruby-guard.

commit 566f7cdeff57c2e2d23e7cce085e08ba59bc197c
Author: HIGUCHI Daisuke (VDR dai) <dai at debian.org>
Date:   Tue Nov 28 16:57:54 2017 +0900

    support test
---
 debian/control                         |    1 +
 debian/patches/add_specs.patch         | 7722 ++++++++++++++++++++++++++++++++
 debian/patches/disable_simplecov.patch |   19 +
 debian/patches/pending_examples.patch  |   27 +
 debian/patches/series                  |    4 +
 debian/ruby-tests.rake                 |    7 +
 debian/rules                           |    3 +
 7 files changed, 7783 insertions(+)

diff --git a/debian/control b/debian/control
index b31ee6b..a2d9220 100644
--- a/debian/control
+++ b/debian/control
@@ -5,6 +5,7 @@ Maintainer: Debian Ruby Extras Maintainers <pkg-ruby-extras-maintainers at lists.al
 Uploaders: HIGUCHI Daisuke (VDR dai) <dai at debian.org>
 Build-Depends: debhelper (>= 10~),
                gem2deb,
+               ruby-rspec-core,
                pry (>= 0.9.12),
                ruby-formatador (>= 0.2.4),
                ruby-listen (<< 4.0),
diff --git a/debian/patches/add_specs.patch b/debian/patches/add_specs.patch
new file mode 100644
index 0000000..c2d03c3
--- /dev/null
+++ b/debian/patches/add_specs.patch
@@ -0,0 +1,7722 @@
+Description: add specs
+Author: HIGUCHI Daisuke (VDR dai) <dai at debian.org>
+Origin: upstream
+Forwarded: not-needed
+Last-Update: 2017-11-28
+
+Index: ruby-guard/spec/.gitignore
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/.gitignore
+@@ -0,0 +1 @@
++/fake-home/
+Index: ruby-guard/spec/lib/guard/bin_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/bin_spec.rb
+@@ -0,0 +1,137 @@
++path = File.expand_path("../../../../bin/guard", __FILE__)
++load path
++
++RSpec.describe GuardReloader do
++  let(:config) { instance_double(described_class::Config) }
++
++  subject { described_class.new(config) }
++
++  let(:guard_core_path) { "/home/me/.rvm/gems/ruby-2.2.2/bin/_guard-core" }
++
++  before do
++    allow(described_class::Config).to receive(:new).and_return(config)
++
++    allow(config).to receive(:current_bundler_gemfile).
++      and_return(bundle_gemfile_env)
++
++    allow(config).to receive(:using_bundler?).and_return(bundle_gemfile_env)
++    allow(config).to receive(:guard_core_path).and_return(guard_core_path)
++
++    allow(config).to receive(:program_arguments).and_return(%w(foo bar baz))
++    allow(config).to receive(:using_rubygems?).and_return(rubygems_deps_env)
++    allow(config).to receive(:program_path).and_return(program_path)
++  end
++
++  let(:program_path) { Pathname("/home/me/.rvm/gems/ruby-2.2.2/bin/guard") }
++  let(:rubygems_deps_env) { nil } # or any gemfile path
++
++  context "when running with bundler" do
++    let(:bundle_gemfile_env) { "./Gemfile" }
++
++    it "sets up bundler" do
++      expect(config).to receive(:setup_bundler)
++      subject.setup
++    end
++  end
++
++  context "when not running with bundler" do
++    let(:bundle_gemfile_env) { nil }
++
++    context "when running with rubygems_gemdeps" do
++      let(:rubygems_deps_env) { "-" } # or any gemfile path
++
++      it "sets up rubygems" do
++        expect(config).to receive(:setup_rubygems_for_deps)
++        subject.setup
++      end
++    end
++
++    context "when not running with rubygems_gemdeps" do
++      let(:rubygems_deps_env) { nil }
++
++      context "when running as binstub" do
++        let(:program_path) { Pathname("/my/project/bin/guard") }
++
++        context "when the relative Gemfile exists" do
++          before do
++            allow(config).to receive(:exist?).
++              with(Pathname("/my/project/Gemfile")).and_return(true)
++
++            allow(config).to receive(:setup_bundler)
++            allow(config).to receive(:setup_bundler_env)
++          end
++
++          it "sets up bundler" do
++            expect(config).to receive(:setup_bundler)
++            subject.setup
++          end
++
++          it "sets the Gemfile" do
++            expect(config).to receive(:setup_bundler_env).
++              with("/my/project/Gemfile")
++            subject.setup
++          end
++        end
++
++        context "when the relative Gemfile does not exist" do
++          before do
++            allow(config).to receive(:exist?).
++              with(Pathname("/my/project/Gemfile")).and_return(false)
++
++            allow(config).to receive(:exist?).with(Pathname("Gemfile")).
++              and_return(false)
++          end
++
++          it "does not setup bundler" do
++            subject.setup
++          end
++
++          it "does not setup rubygems" do
++            subject.setup
++          end
++
++          it "shows no warning" do
++            expect(STDERR).to_not receive(:puts)
++            subject.setup
++          end
++        end
++      end
++
++      context "when not run as binstub" do
++        let(:program_path) do
++          Pathname("/home/me/.rvm/gems/ruby-2.2.2/bin/guard")
++        end
++
++        before do
++          allow(config).to receive(:exist?).with(
++            Pathname("/home/me/.rvm/gems/ruby-2.2.2/Gemfile")
++          ).and_return(false)
++        end
++
++        context "when Gemfile exists" do
++          before do
++            allow(config).to receive(:exist?).with(Pathname("Gemfile")).
++              and_return(true)
++          end
++
++          it "shows a warning" do
++            expect(STDERR).to receive(:puts).with(/Warning: you have a Gemfile/)
++            subject.setup
++          end
++        end
++
++        context "when no Gemfile exists" do
++          before do
++            allow(config).to receive(:exist?).with(Pathname("Gemfile")).
++              and_return(false)
++          end
++
++          it "shows no warning" do
++            expect(STDERR).to_not receive(:puts)
++            subject.setup
++          end
++        end
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/cli/environments/bundler_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/cli/environments/bundler_spec.rb
+@@ -0,0 +1,60 @@
++require "guard/cli/environments/bundler"
++
++# TODO: instead of shared examples, use have_received if possible
++RSpec.shared_examples "avoids Bundler warning" do
++  it "does not show the Bundler warning" do
++    expect(Guard::UI).to_not have_received(:info).with(/Guard here!/)
++  end
++end
++
++RSpec.shared_examples "shows Bundler warning" do
++  it "shows the Bundler warning" do
++    expect(Guard::UI).to have_received(:info).with(/Guard here!/)
++  end
++end
++
++RSpec.describe Guard::Cli::Environments::Bundler do
++  describe "#verify" do
++    let(:gemdeps) { nil }
++    let(:gemfile) { nil }
++
++    before do
++      allow(ENV).to receive(:[]).with("BUNDLE_GEMFILE").and_return(gemfile)
++      allow(ENV).to receive(:[]).with("RUBYGEMS_GEMDEPS").and_return(gemdeps)
++
++      allow(File).to receive(:exist?).with("Gemfile").
++        and_return(gemfile_present)
++
++      subject.verify
++    end
++
++    context "without an existing Gemfile" do
++      let(:gemfile_present) { false }
++      include_examples "avoids Bundler warning"
++    end
++
++    context "with an existing Gemfile" do
++      let(:gemfile_present) { true }
++
++      context "with Bundler" do
++        let(:gemdeps) { nil }
++        let(:gemfile) { "Gemfile" }
++        include_examples "avoids Bundler warning"
++      end
++
++      context "without Bundler" do
++        let(:gemfile) { nil }
++
++        context "with Rubygems Gemfile autodetection or custom Gemfile" do
++          let(:gemdeps) { "-" }
++          include_examples "avoids Bundler warning"
++        end
++
++        context "without Rubygems Gemfile handling" do
++          let(:gemdeps) { nil }
++          include_examples "shows Bundler warning"
++        end
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/cli/environments/evaluate_only_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/cli/environments/evaluate_only_spec.rb
+@@ -0,0 +1,73 @@
++require "guard/cli/environments/evaluate_only"
++
++RSpec.describe Guard::Cli::Environments::EvaluateOnly do
++  subject { described_class.new(options) }
++  let(:options) { double("options") }
++
++  describe "#evaluate" do
++    let(:evaluator) { instance_double("Guard::Guardfile::Evaluator") }
++    let(:state) { instance_double("Guard::Internals::State") }
++    let(:session) { instance_double("Guard::Internals::Session") }
++
++    before do
++      allow(Guard::Guardfile::Evaluator).to receive(:new).and_return(evaluator)
++      allow(evaluator).to receive(:evaluate)
++      allow(Guard).to receive(:init)
++      allow(Guard).to receive(:state).and_return(state)
++      allow(state).to receive(:session).and_return(session)
++      allow(session).to receive(:evaluator_options)
++    end
++
++    it "calls Guard.init" do
++      expect(Guard).to receive(:init)
++      subject.evaluate
++    end
++
++    it "initializes Guard with options" do
++      expect(Guard).to receive(:init).with(options)
++      subject.evaluate
++    end
++
++    it "evaluates the guardfile" do
++      expect(evaluator).to receive(:evaluate)
++      subject.evaluate
++    end
++
++    it "passes options to evaluator" do
++      evaluator_options = double("evaluator_options")
++      allow(session).to receive(:evaluator_options).
++        and_return(evaluator_options)
++
++      expect(Guard::Guardfile::Evaluator).to receive(:new).
++        with(evaluator_options).and_return(evaluator)
++
++      subject.evaluate
++    end
++
++    [
++      Guard::Dsl::Error,
++      Guard::Guardfile::Evaluator::NoPluginsError,
++      Guard::Guardfile::Evaluator::NoGuardfileError,
++      Guard::Guardfile::Evaluator::NoCustomGuardfile
++    ].each do |error_class|
++      context "when a #{error_class} error occurs" do
++        before do
++          allow(Guard).to receive(:init).
++            and_raise(error_class, "#{error_class} error!")
++        end
++
++        it "aborts" do
++          expect { subject.evaluate }.to raise_error(SystemExit)
++        end
++
++        it "shows error message" do
++          expect(Guard::UI).to receive(:error).with(/#{error_class} error!/)
++          begin
++            subject.evaluate
++          rescue SystemExit
++          end
++        end
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/cli/environments/valid_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/cli/environments/valid_spec.rb
+@@ -0,0 +1,253 @@
++require "guard/cli/environments/valid"
++require "guard/cli/environments/bundler"
++
++RSpec.describe Guard::Cli::Environments::Valid do
++  subject { described_class.new(options) }
++  let(:options) { double("options") }
++
++  before do
++    # TODO: start should be an instance method of something
++    allow(Guard).to receive(:start)
++  end
++
++  describe "#start_guard" do
++    let(:bundler) { instance_double("Guard::Cli::Environments::Bundler") }
++
++    before do
++      allow(Guard::Cli::Environments::Bundler).to receive(:new).
++        and_return(bundler)
++
++      allow(bundler).to receive(:verify)
++    end
++
++    context "with a valid bundler setup" do
++      before do
++        allow(bundler).to receive(:verify)
++
++        allow(options).to receive(:[]).with(:no_bundler_warning).
++          and_return(false)
++      end
++
++      it "starts guard" do
++        expect(Guard).to receive(:start)
++        subject.start_guard
++      end
++
++      it "start guard with options" do
++        expect(Guard).to receive(:start).with(options)
++        subject.start_guard
++      end
++
++      it "returns exit code" do
++        exitcode = double("exitcode")
++        expect(Guard).to receive(:start).and_return(exitcode)
++        expect(subject.start_guard).to be(exitcode)
++      end
++
++      [
++        Guard::Dsl::Error,
++        Guard::Guardfile::Evaluator::NoPluginsError,
++        Guard::Guardfile::Evaluator::NoGuardfileError,
++        Guard::Guardfile::Evaluator::NoCustomGuardfile
++      ].each do |error_class|
++        context "when a #{error_class} error occurs" do
++          before do
++            allow(Guard).to receive(:start).
++              and_raise(error_class, "#{error_class} error!")
++          end
++
++          it "aborts" do
++            expect { subject.start_guard }.to raise_error(SystemExit)
++          end
++
++          it "shows error message" do
++            expect(Guard::UI).to receive(:error).with(/#{error_class} error!/)
++            begin
++              subject.start_guard
++            rescue SystemExit
++            end
++          end
++        end
++      end
++    end
++
++    context "without no_bundler_warning option" do
++      subject { described_class.new(no_bundler_warning: false) }
++
++      it "verifies bundler presence" do
++        expect(bundler).to receive(:verify)
++        subject.start_guard
++      end
++
++      context "without a valid bundler setup" do
++        before do
++          allow(bundler).to receive(:verify).and_raise(SystemExit)
++        end
++
++        it "does not start guard" do
++          expect(Guard).to_not receive(:start)
++
++          begin
++            subject.start_guard
++          rescue SystemExit
++          end
++        end
++      end
++    end
++
++    context "with no_bundler_warning option" do
++      subject { described_class.new(no_bundler_warning: true) }
++
++      it "does not verify bundler presence" do
++        expect(bundler).to_not receive(:verify)
++        subject.start_guard
++      end
++
++      it "starts guard" do
++        expect(Guard).to receive(:start)
++        subject.start_guard
++      end
++    end
++
++    describe "return value" do
++      let(:exitcode) { double("Fixnum") }
++      subject { described_class.new(no_bundler_warning: true) }
++
++      before do
++        allow(Guard).to receive(:start).and_return(exitcode)
++      end
++
++      it "matches return value of Guard.start" do
++        expect(subject.start_guard).to be(exitcode)
++      end
++    end
++  end
++
++  describe "#initialize_guardfile" do
++    let(:evaluator) { instance_double("Guard::Guardfile::Evaluator") }
++    let(:generator) { instance_double("Guard::Guardfile::Generator") }
++    let(:state) { instance_double("Guard::Internals::State") }
++    let(:session) { instance_double("Guard::Internals::Session") }
++
++    before do
++      stub_file("Gemfile")
++
++      allow(evaluator).to receive(:evaluate)
++      allow(generator).to receive(:create_guardfile)
++      allow(generator).to receive(:initialize_all_templates)
++
++      allow(session).to receive(:evaluator_options)
++      allow(state).to receive(:session).and_return(session)
++
++      allow(Guard::Internals::State).to receive(:new).and_return(state)
++      allow(Guard::Guardfile::Evaluator).to receive(:new).and_return(evaluator)
++      allow(Guard::Guardfile::Generator).to receive(:new).and_return(generator)
++    end
++
++    context "with bare option" do
++      before do
++        expect(options).to receive(:[]).with(:bare).and_return(true)
++      end
++
++      it "Only creates the Guardfile without initializing any Guard template" do
++        allow(evaluator).to receive(:evaluate).
++          and_raise(Guard::Guardfile::Evaluator::NoGuardfileError)
++
++        allow(File).to receive(:exist?).with("Gemfile").and_return(false)
++        expect(generator).to receive(:create_guardfile)
++        expect(generator).to_not receive(:initialize_template)
++        expect(generator).to_not receive(:initialize_all_templates)
++
++        subject.initialize_guardfile
++      end
++
++      it "returns an exit code" do
++        # TODO: ideally, we'd capture known exceptions and return nonzero
++        expect(subject.initialize_guardfile).to be_zero
++      end
++    end
++
++    context "with no bare option" do
++      before do
++        expect(options).to receive(:[]).with(:bare).and_return(false)
++      end
++
++      it "evaluates created or existing guardfile" do
++        expect(evaluator).to receive(:evaluate)
++        subject.initialize_guardfile
++      end
++
++      it "creates a Guardfile" do
++        expect(evaluator).to receive(:evaluate).
++          and_raise(Guard::Guardfile::Evaluator::NoGuardfileError).once
++        expect(evaluator).to receive(:evaluate)
++
++        expect(Guard::Guardfile::Generator).to receive(:new).
++          and_return(generator)
++        expect(generator).to receive(:create_guardfile)
++
++        subject.initialize_guardfile
++      end
++
++      it "initializes templates of all installed Guards" do
++        allow(File).to receive(:exist?).with("Gemfile").and_return(false)
++
++        expect(generator).to receive(:initialize_all_templates)
++
++        subject.initialize_guardfile
++      end
++
++      it "initializes each passed template" do
++        allow(File).to receive(:exist?).with("Gemfile").and_return(false)
++
++        expect(generator).to receive(:initialize_template).with("rspec")
++        expect(generator).to receive(:initialize_template).with("pow")
++
++        subject.initialize_guardfile(%w(rspec pow))
++      end
++
++      context "when passed a guard name" do
++        context "when the Guardfile is empty" do
++          before do
++            allow(evaluator).to receive(:evaluate).
++              and_raise Guard::Guardfile::Evaluator::NoPluginsError
++            allow(generator).to receive(:initialize_template)
++          end
++
++          it "works without without errors" do
++            expect(subject.initialize_guardfile(%w(rspec))).to be_zero
++          end
++
++          it "adds the template" do
++            expect(generator).to receive(:initialize_template).with("rspec")
++            subject.initialize_guardfile(%w(rspec))
++          end
++        end
++
++        it "initializes the template of the passed Guard" do
++          expect(generator).to receive(:initialize_template).with("rspec")
++          subject.initialize_guardfile(%w(rspec))
++        end
++      end
++
++      it "returns an exit code" do
++        expect(subject.initialize_guardfile).to be_zero
++      end
++
++      context "when passed an unknown guard name" do
++        before do
++          expect(generator).to receive(:initialize_template).with("foo").
++            and_raise(Guard::Guardfile::Generator::NoSuchPlugin, "foo")
++        end
++
++        it "returns an exit code" do
++          expect(::Guard::UI).to receive(:error).with(
++            "Could not load 'guard/foo' or '~/.guard/templates/foo'"\
++            " or find class Guard::Foo\n"
++          )
++          expect(subject.initialize_guardfile(%w(foo))).to be(1)
++        end
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/cli_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/cli_spec.rb
+@@ -0,0 +1,155 @@
++require "guard/cli"
++
++RSpec.describe Guard::CLI do
++  let(:valid_environment) { instance_double("Guard::Cli::Environments::Valid") }
++  let(:bare_environment) do
++    instance_double("Guard::Cli::Environments::EvaluateOnly")
++  end
++
++  let(:dsl_describer) { instance_double("Guard::DslDescriber") }
++
++  before do
++    @options = {}
++    allow(subject).to receive(:options).and_return(@options)
++    allow(::Guard::DslDescriber).to receive(:new).with(no_args).
++      and_return(dsl_describer)
++
++    allow(Guard::Cli::Environments::EvaluateOnly).to receive(:new).
++      and_return(bare_environment)
++
++    allow(Guard::Cli::Environments::Valid).to receive(:new).
++      and_return(valid_environment)
++  end
++
++  describe "#start" do
++    before do
++      allow(valid_environment).to receive(:start_guard).and_return(0)
++    end
++
++    it "delegates to Guard::Environment.start" do
++      pending "needs JRuby support first" if defined?(JRUBY_VERSION)
++
++      expect(valid_environment).to receive(:start_guard).and_return(0)
++      begin
++        subject.start
++      rescue SystemExit
++      end
++    end
++
++    it "exits with given exit code" do
++      pending "needs JRuby support first" if defined?(JRUBY_VERSION)
++
++      allow(valid_environment).to receive(:start_guard).and_return(4)
++      expect { subject.start }.to raise_error(SystemExit) do |exception|
++        expect(exception.status).to eq(4)
++        exception
++      end
++    end
++
++    it "passes options" do
++      pending "needs JRuby support first" if defined?(JRUBY_VERSION)
++
++      expect(Guard::Cli::Environments::Valid).to receive(:new).with(@options).
++        and_return(valid_environment)
++      begin
++        subject.start
++      rescue SystemExit
++      end
++    end
++  end
++
++  describe "#list" do
++    before do
++      allow(bare_environment).to receive(:evaluate)
++      allow(dsl_describer).to receive(:list)
++      subject.list
++    end
++
++    it "calls the evaluation" do
++      expect(bare_environment).to have_received(:evaluate)
++    end
++
++    it "outputs the Guard plugins list" do
++      expect(dsl_describer).to have_received(:list)
++    end
++  end
++
++  describe "#notifiers" do
++    before do
++      allow(bare_environment).to receive(:evaluate)
++      allow(dsl_describer).to receive(:notifiers)
++      subject.notifiers
++    end
++
++    it "calls the evaluation" do
++      expect(bare_environment).to have_received(:evaluate)
++    end
++
++    it "outputs the notifiers list" do
++      expect(dsl_describer).to have_received(:notifiers)
++    end
++  end
++
++  describe "#version" do
++    it "shows the current version" do
++      expect(STDOUT).to receive(:puts).with(/#{ ::Guard::VERSION }/)
++      subject.version
++    end
++  end
++
++  describe "#init" do
++    before do
++      allow(Guard::Cli::Environments::Valid).to receive(:new).
++        and_return(valid_environment)
++      allow(valid_environment).to receive(:initialize_guardfile).and_return(0)
++    end
++
++    it "delegates to Guard::Environment.start" do
++      begin
++        subject.init
++      rescue SystemExit
++      end
++    end
++
++    it "exits with given exit code" do
++      allow(valid_environment).to receive(:initialize_guardfile).and_return(4)
++      expect { subject.init }.to raise_error(SystemExit) do |exception|
++        expect(exception.status).to eq(4)
++      end
++    end
++
++    it "passes options" do
++      expect(Guard::Cli::Environments::Valid).to receive(:new).with(@options).
++        and_return(valid_environment)
++      begin
++        subject.init
++      rescue SystemExit
++      end
++    end
++
++    it "passes plugin names" do
++      plugins = [double("plugin1"), double("plugin2")]
++      expect(valid_environment).to receive(:initialize_guardfile).with(plugins)
++      begin
++        subject.init(*plugins)
++      rescue SystemExit
++      end
++    end
++  end
++
++  describe "#show" do
++    before do
++      allow(bare_environment).to receive(:evaluate)
++      allow(dsl_describer).to receive(:show)
++      subject.show
++    end
++
++    it "calls the evaluation" do
++      expect(bare_environment).to have_received(:evaluate)
++    end
++
++    it "outputs the Guard::DslDescriber.list result" do
++      expect(dsl_describer).to have_received(:show)
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/commander_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/commander_spec.rb
+@@ -0,0 +1,272 @@
++require "guard/commander"
++
++RSpec.describe Guard::Commander do
++  subject { Guard }
++
++  let(:interactor) { instance_double("Guard::Interactor") }
++  let(:runner) { instance_double("Guard::Runner", run: true) }
++
++  let(:scope) { instance_double("Guard::Internals::Scope") }
++  let(:state) { instance_double("Guard::Internals::State") }
++  let(:session) { instance_double("Guard::Internals::Session") }
++
++  before do
++    allow(state).to receive(:scope).and_return(scope)
++    allow(state).to receive(:session).and_return(session)
++    allow(Guard).to receive(:state).and_return(state)
++
++    allow(Guard::Interactor).to receive(:new) { interactor }
++    allow(Guard::Runner).to receive(:new).and_return(runner)
++  end
++
++  describe ".start" do
++    let(:listener) do
++      instance_double("Listen::Listener", start: true, stop: true)
++    end
++
++    let(:watched_dir) { Dir.pwd }
++
++    before do
++      stub_guardfile(" ")
++      stub_user_guard_rb
++
++      # from stop()
++      allow(Guard).to receive(:setup)
++      allow(Guard).to receive(:listener).and_return(listener)
++      allow(session).to receive(:watchdirs).and_return(%w(dir1 dir2))
++      allow(Guard).to receive(:interactor).and_return(interactor)
++
++      # Simulate Ctrl-D in Pry, or Ctrl-C in non-interactive mode
++      allow(interactor).to receive(:foreground).and_return(:exit)
++
++      allow(interactor).to receive(:background)
++      allow(Guard::Notifier).to receive(:disconnect)
++    end
++
++    it "calls Guard setup" do
++      expect(Guard).to receive(:setup).with(foo: "bar")
++      Guard.start(foo: "bar")
++    end
++
++    it "displays an info message" do
++      expect(Guard::UI).to receive(:info).
++        with("Guard is now watching at 'dir1', 'dir2'")
++
++      Guard.start
++    end
++
++    it "tell the runner to run the :start task" do
++      expect(runner).to receive(:run).with(:start)
++      allow(listener).to receive(:stop)
++      Guard.start
++    end
++
++    it "start the listener" do
++      expect(listener).to receive(:start)
++
++      Guard.start
++    end
++
++    context "when finished" do
++      it "stops everything" do
++        expect(interactor).to receive(:foreground).and_return(:exit)
++
++        # From stop()
++        expect(interactor).to receive(:background)
++        expect(listener).to receive(:stop)
++        expect(runner).to receive(:run).with(:stop)
++        expect(Guard::UI).to receive(:info).with("Bye bye...", reset: true)
++
++        Guard.start
++      end
++    end
++  end
++
++  describe ".stop" do
++    let(:runner) { instance_double("Guard::Runner", run: true) }
++    let(:listener) { instance_double("Listen::Listener", stop: true) }
++
++    before do
++      allow(Guard::Notifier).to receive(:disconnect)
++      allow(Guard).to receive(:listener).and_return(listener)
++      allow(listener).to receive(:stop)
++      allow(Guard).to receive(:interactor).and_return(interactor)
++      allow(interactor).to receive(:background)
++
++      Guard.stop
++    end
++
++    it "turns off the interactor" do
++      expect(interactor).to have_received(:background)
++    end
++
++    it "turns the notifier off" do
++      expect(Guard::Notifier).to have_received(:disconnect)
++    end
++
++    it "tell the runner to run the :stop task" do
++      expect(runner).to have_received(:run).with(:stop)
++    end
++
++    it "stops the listener" do
++      expect(listener).to have_received(:stop)
++    end
++  end
++
++  describe ".reload" do
++    let(:runner) { instance_double("Guard::Runner", run: true) }
++    let(:group) { instance_double("Guard::Group", name: "frontend") }
++
++    before do
++      allow(Guard::Notifier).to receive(:connect)
++      allow(Guard::UI).to receive(:info)
++      allow(Guard::UI).to receive(:clear)
++
++      allow(scope).to receive(:titles).and_return(["all"])
++
++      stub_guardfile(" ")
++      stub_user_guard_rb
++    end
++
++    it "clears the screen" do
++      expect(Guard::UI).to receive(:clear)
++
++      Guard.reload
++    end
++
++    it "reloads Guard" do
++      expect(runner).to receive(:run).with(:reload, groups: [group])
++      Guard.reload(groups: [group])
++    end
++  end
++
++  describe ".run_all" do
++    let(:group) { instance_double("Guard::Group", name: "frontend") }
++
++    before do
++      allow(::Guard::Notifier).to receive(:connect)
++      allow(::Guard::UI).to receive(:action_with_scopes)
++      allow(::Guard::UI).to receive(:clear)
++    end
++
++    context "with a given scope" do
++      it "runs all with the scope" do
++        expect(runner).to receive(:run).with(:run_all, groups: [group])
++
++        subject.run_all(groups: [group])
++      end
++    end
++
++    context "with an empty scope" do
++      it "runs all" do
++        expect(runner).to receive(:run).with(:run_all, {})
++
++        subject.run_all
++      end
++    end
++  end
++
++  describe ".pause" do
++    context "when unpaused" do
++      let(:listener) { instance_double("Listen::Listener") }
++
++      before do
++        allow(::Guard::Notifier).to receive(:connect)
++        allow(Guard).to receive(:listener).and_return(listener)
++        allow(listener).to receive(:paused?) { false }
++      end
++
++      [:toggle, nil, :paused].each do |mode|
++        context "with #{mode.inspect}" do
++          before do
++            allow(listener).to receive(:pause)
++          end
++
++          it "pauses" do
++            expect(listener).to receive(:pause)
++            Guard.pause(mode)
++          end
++
++          it "shows a message" do
++            expected = /File event handling has been paused/
++            expect(Guard::UI).to receive(:info).with(expected)
++            Guard.pause(mode)
++          end
++        end
++      end
++
++      context "with :unpaused" do
++        it "does nothing" do
++          expect(listener).to_not receive(:start)
++          expect(listener).to_not receive(:pause)
++          Guard.pause(:unpaused)
++        end
++      end
++
++      context "with invalid parameter" do
++        it "raises an ArgumentError" do
++          expect { Guard.pause(:invalid) }.
++            to raise_error(ArgumentError, "invalid mode: :invalid")
++        end
++      end
++    end
++
++    context "when already paused" do
++      let(:listener) { instance_double("Listen::Listener") }
++
++      before do
++        allow(::Guard::Notifier).to receive(:connect)
++        allow(Guard).to receive(:listener).and_return(listener)
++        allow(listener).to receive(:paused?) { true }
++      end
++
++      [:toggle, nil, :unpaused].each do |mode|
++        context "with #{mode.inspect}" do
++          before do
++            allow(listener).to receive(:start)
++          end
++
++          it "unpauses" do
++            expect(listener).to receive(:start)
++            Guard.pause(mode)
++          end
++
++          it "shows a message" do
++            expected = /File event handling has been resumed/
++            expect(Guard::UI).to receive(:info).with(expected)
++            Guard.pause(mode)
++          end
++        end
++      end
++
++      context "with :paused" do
++        it "does nothing" do
++          expect(listener).to_not receive(:start)
++          expect(listener).to_not receive(:pause)
++          Guard.pause(:paused)
++        end
++      end
++
++      context "with invalid parameter" do
++        it "raises an ArgumentError" do
++          expect { Guard.pause(:invalid) }.
++            to raise_error(ArgumentError, "invalid mode: :invalid")
++        end
++      end
++    end
++  end
++
++  describe ".show" do
++    let(:dsl_describer) { instance_double("Guard::DslDescriber") }
++
++    before do
++      allow(Guard::DslDescriber).to receive(:new).with(no_args).
++        and_return(dsl_describer)
++    end
++
++    it "shows list of plugins" do
++      expect(dsl_describer).to receive(:show)
++      Guard.show
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/commands/all_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/commands/all_spec.rb
+@@ -0,0 +1,83 @@
++require "guard/plugin"
++
++require "guard/commands/all"
++
++require "guard/internals/session"
++require "guard/internals/state"
++
++RSpec.describe Guard::Commands::All do
++  let(:foo_group) { instance_double(Guard::Group) }
++  let(:bar_guard) { instance_double(Guard::Plugin) }
++  let(:output) { instance_double(Pry::Output) }
++
++  let(:state) { instance_double("Guard::Internals::State") }
++  let(:session) { instance_double("Guard::Internals::Session") }
++
++  class FakePry < Pry::Command
++    def self.output
++    end
++  end
++
++  before do
++    allow(session).to receive(:convert_scope).with(given_scope).
++      and_return(converted_scope)
++
++    allow(state).to receive(:session).and_return(session)
++    allow(Guard).to receive(:state).and_return(state)
++
++    allow(FakePry).to receive(:output).and_return(output)
++    allow(Pry::Commands).to receive(:create_command).with("all") do |&block|
++      FakePry.instance_eval(&block)
++    end
++
++    described_class.import
++  end
++
++  context "without scope" do
++    let(:given_scope) { [] }
++    let(:converted_scope) { [{ groups: [], plugins: [] }, []] }
++
++    it "runs the :run_all action" do
++      expect(Guard).to receive(:async_queue_add).
++        with([:guard_run_all, groups: [], plugins: []])
++
++      FakePry.process
++    end
++  end
++
++  context "with a valid Guard group scope" do
++    let(:given_scope) { ["foo"] }
++    let(:converted_scope) { [{ groups: [foo_group], plugins: [] }, []] }
++
++    it "runs the :run_all action with the given scope" do
++      expect(Guard).to receive(:async_queue_add).
++        with([:guard_run_all, groups: [foo_group], plugins: []])
++
++      FakePry.process("foo")
++    end
++  end
++
++  context "with a valid Guard plugin scope" do
++    let(:given_scope) { ["bar"] }
++    let(:converted_scope) { [{ groups: [], plugins: [bar_guard] }, []] }
++
++    it "runs the :run_all action with the given scope" do
++      expect(Guard).to receive(:async_queue_add).
++        with([:guard_run_all, plugins: [bar_guard], groups: []])
++
++      FakePry.process("bar")
++    end
++  end
++
++  context "with an invalid scope" do
++    let(:given_scope) { ["baz"] }
++    let(:converted_scope) { [{ groups: [], plugins: [] }, ["baz"]] }
++
++    it "does not run the action" do
++      expect(output).to receive(:puts).with("Unknown scopes: baz")
++      expect(Guard).to_not receive(:async_queue_add)
++
++      FakePry.process("baz")
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/commands/change_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/commands/change_spec.rb
+@@ -0,0 +1,46 @@
++require "guard/commands/change"
++
++RSpec.describe Guard::Commands::Change do
++  let(:output) { instance_double(Pry::Output) }
++
++  class FakePry < Pry::Command
++    def self.output
++    end
++  end
++
++  before do
++    allow(FakePry).to receive(:output).and_return(output)
++    allow(Pry::Commands).to receive(:create_command).with("change") do |&block|
++      FakePry.instance_eval(&block)
++    end
++
++    described_class.import
++  end
++
++  context "with a file" do
++    it "runs the :run_on_changes action with the given file" do
++      expect(::Guard).to receive(:async_queue_add).
++        with(modified: ["foo"], added: [], removed: [])
++
++      FakePry.process("foo")
++    end
++  end
++
++  context "with multiple files" do
++    it "runs the :run_on_changes action with the given files" do
++      expect(::Guard).to receive(:async_queue_add).
++        with(modified: %w(foo bar baz), added: [], removed: [])
++
++      FakePry.process("foo", "bar", "baz")
++    end
++  end
++
++  context "without a file" do
++    it "does not run the :run_on_changes action" do
++      expect(::Guard).to_not receive(:async_queue_add)
++      expect(output).to receive(:puts).with("Please specify a file.")
++
++      FakePry.process
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/commands/notification_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/commands/notification_spec.rb
+@@ -0,0 +1,24 @@
++require "guard/commands/notification"
++
++RSpec.describe Guard::Commands::Notification do
++  let(:output) { instance_double(Pry::Output) }
++
++  class FakePry < Pry::Command
++    def self.output; end
++  end
++
++  before do
++    allow(FakePry).to receive(:output).and_return(output)
++    allow(Pry::Commands).to receive(:create_command).
++      with("notification") do |&block|
++      FakePry.instance_eval(&block)
++    end
++
++    described_class.import
++  end
++
++  it "toggles the Guard notifier" do
++    expect(::Guard::Notifier).to receive(:toggle)
++    FakePry.process
++  end
++end
+Index: ruby-guard/spec/lib/guard/commands/pause_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/commands/pause_spec.rb
+@@ -0,0 +1,22 @@
++require "guard/commands/pause"
++
++RSpec.describe Guard::Commands::Pause do
++  class FakePry < Pry::Command
++    def self.output
++    end
++  end
++
++  before do
++    allow(FakePry).to receive(:output).and_return(output)
++    allow(Pry::Commands).to receive(:create_command).with("pause") do |&block|
++      FakePry.instance_eval(&block)
++    end
++
++    described_class.import
++  end
++
++  it "tells Guard to pause" do
++    expect(::Guard).to receive(:async_queue_add).with([:guard_pause])
++    FakePry.process
++  end
++end
+Index: ruby-guard/spec/lib/guard/commands/reload_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/commands/reload_spec.rb
+@@ -0,0 +1,77 @@
++require "guard/commands/reload"
++
++require "guard/internals/session"
++require "guard/internals/state"
++
++RSpec.describe Guard::Commands::Reload do
++  let(:output) { instance_double(Pry::Output) }
++
++  let(:state) { instance_double("Guard::Internals::State") }
++  let(:session) { instance_double("Guard::Internals::Session") }
++
++  let(:foo_group) { instance_double(Guard::Group) }
++  let(:bar_guard) { instance_double(Guard::Plugin) }
++
++  class FakePry < Pry::Command
++    def self.output; end
++  end
++
++  before do
++    allow(session).to receive(:convert_scope).with(given_scope).
++      and_return(converted_scope)
++
++    allow(state).to receive(:session).and_return(session)
++    allow(Guard).to receive(:state).and_return(state)
++
++    allow(FakePry).to receive(:output).and_return(output)
++    allow(Pry::Commands).to receive(:create_command).with("reload") do |&block|
++      FakePry.instance_eval(&block)
++    end
++
++    described_class.import
++  end
++
++  context "without scope" do
++    let(:given_scope) { [] }
++    let(:converted_scope) { [{ groups: [], plugins: [] }, []] }
++
++    it "triggers the :reload action" do
++      expect(Guard).to receive(:async_queue_add).
++        with([:guard_reload, { groups: [], plugins: [] }])
++      FakePry.process
++    end
++  end
++
++  context "with a valid Guard group scope" do
++    let(:given_scope) { ["foo"] }
++    let(:converted_scope) { [{ groups: [foo_group], plugins: [] }, []] }
++
++    it "triggers the :reload action with the given scope" do
++      expect(Guard).to receive(:async_queue_add).
++        with([:guard_reload, { groups: [foo_group], plugins: [] }])
++      FakePry.process("foo")
++    end
++  end
++
++  context "with a valid Guard plugin scope" do
++    let(:given_scope) { ["bar"] }
++    let(:converted_scope) { [{ groups: [], plugins: [bar_guard] }, []] }
++
++    it "triggers the :reload action with the given scope" do
++      expect(Guard).to receive(:async_queue_add).
++        with([:guard_reload, { plugins: [bar_guard], groups: [] }])
++      FakePry.process("bar")
++    end
++  end
++
++  context "with an invalid scope" do
++    let(:given_scope) { ["baz"] }
++    let(:converted_scope) { [{ groups: [], plugins: [] }, ["baz"]] }
++
++    it "does not trigger the action" do
++      allow(output).to receive(:puts).with("Unknown scopes: baz")
++      expect(Guard).to_not receive(:async_queue_add)
++      FakePry.process("baz")
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/commands/scope_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/commands/scope_spec.rb
+@@ -0,0 +1,81 @@
++require "guard/commands/scope"
++
++require "guard/internals/session"
++require "guard/internals/state"
++
++RSpec.describe Guard::Commands::Scope do
++  let(:output) { instance_double(Pry::Output) }
++
++  let(:state) { instance_double("Guard::Internals::State") }
++  let(:scope) { instance_double("Guard::Internals::Scope") }
++  let(:session) { instance_double("Guard::Internals::Session") }
++
++  let(:foo_group) { instance_double(Guard::Group) }
++  let(:bar_guard) { instance_double(Guard::PluginUtil) }
++
++  class FakePry < Pry::Command
++    def self.output; end
++  end
++
++  before do
++    allow(session).to receive(:convert_scope).with(given_scope).
++      and_return(converted_scope)
++
++    allow(state).to receive(:session).and_return(session)
++    allow(Guard).to receive(:state).and_return(state)
++
++    allow(FakePry).to receive(:output).and_return(output)
++    allow(Pry::Commands).to receive(:create_command).with("scope") do |&block|
++      FakePry.instance_eval(&block)
++    end
++
++    allow(state).to receive(:scope).and_return(scope)
++    allow(Guard).to receive(:state).and_return(state)
++
++    described_class.import
++  end
++
++  context "without scope" do
++    let(:given_scope) { [] }
++    let(:converted_scope) { [{ groups: [], plugins: [] }, []] }
++
++    it "does not call :scope= and shows usage" do
++      expect(output).to receive(:puts).with("Usage: scope <scope>")
++      expect(scope).to_not receive(:from_interactor)
++      FakePry.process
++    end
++  end
++
++  context "with a valid Guard group scope" do
++    let(:given_scope) { ["foo"] }
++    let(:converted_scope) { [{ groups: [foo_group], plugins: [] }, []] }
++
++    it "sets up the scope with the given scope" do
++      expect(scope).to receive(:from_interactor).
++        with(groups: [foo_group], plugins: [])
++      FakePry.process("foo")
++    end
++  end
++
++  context "with a valid Guard plugin scope" do
++    let(:given_scope) { ["bar"] }
++    let(:converted_scope) { [{ groups: [], plugins: [bar_guard] }, []] }
++
++    it "runs the :scope= action with the given scope" do
++      expect(scope).to receive(:from_interactor).
++        with(plugins: [bar_guard], groups: [])
++      FakePry.process("bar")
++    end
++  end
++
++  context "with an invalid scope" do
++    let(:given_scope) { ["baz"] }
++    let(:converted_scope) { [{ groups: [], plugins: [] }, ["baz"]] }
++
++    it "does not change the scope and shows unknown scopes" do
++      expect(output).to receive(:puts).with("Unknown scopes: baz")
++      expect(scope).to_not receive(:from_interactor)
++      FakePry.process("baz")
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/commands/show_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/commands/show_spec.rb
+@@ -0,0 +1,26 @@
++require "guard/commands/show"
++
++# TODO: we only need the async queue
++require "guard"
++
++RSpec.describe Guard::Commands::Show do
++  let(:output) { instance_double(Pry::Output) }
++
++  class FakePry < Pry::Command
++    def self.output; end
++  end
++
++  before do
++    allow(FakePry).to receive(:output).and_return(output)
++    allow(Pry::Commands).to receive(:create_command).with("show") do |&block|
++      FakePry.instance_eval(&block)
++    end
++
++    described_class.import
++  end
++
++  it "tells Guard to output DSL description" do
++    expect(::Guard).to receive(:async_queue_add).with([:guard_show])
++    FakePry.process
++  end
++end
+Index: ruby-guard/spec/lib/guard/config_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/config_spec.rb
+@@ -0,0 +1,22 @@
++require "guard/config"
++
++RSpec.describe Guard::Config, exclude_stubs: [:Nenv] do
++  it { is_expected.to respond_to(:strict?) }
++  it { is_expected.to respond_to(:silence_deprecations?) }
++
++  describe ".strict?" do
++    before do
++      allow(subject).to receive(:strict?).and_return(result)
++    end
++
++    context "when GUARD_STRICT is set to a 'true' value" do
++      let(:result) { true }
++      it { is_expected.to be_strict }
++    end
++
++    context "when GUARD_STRICT is set to a 'false' value" do
++      let(:result) { false }
++      it { is_expected.to_not be_strict }
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/deprecated/dsl_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/deprecated/dsl_spec.rb
+@@ -0,0 +1,57 @@
++require "guard/config"
++
++unless Guard::Config.new.strict?
++
++  # Require listen now, so the requiring below doesn't use File methods
++  require "listen"
++
++  require "guard/deprecated/dsl"
++
++  require "guard/ui"
++  require "guard/config"
++
++  RSpec.describe Guard::Deprecated::Dsl do
++    subject do
++      module TestModule; end.tap { |mod| described_class.add_deprecated(mod) }
++    end
++
++    describe ".evaluate_guardfile" do
++      before { stub_user_guard_rb }
++      before { stub_guardfile(" ") }
++      before { stub_user_guardfile }
++      before { stub_user_project_guardfile }
++      let(:evaluator) { instance_double("Guard::Guardfile::Evaluator") }
++
++      before do
++        # TODO: this is a workaround for a bad require loop
++        allow_any_instance_of(Guard::Config).to receive(:strict?).
++          and_return(false)
++
++        require "guard/guardfile/evaluator"
++
++        allow(Guard::Guardfile::Evaluator).to receive(:new).
++          and_return(evaluator)
++
++        allow(evaluator).to receive(:evaluate_guardfile)
++
++        allow(Guard::UI).to receive(:deprecation)
++      end
++
++      it "displays a deprecation warning to the user" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Dsl::ClassMethods::EVALUATE_GUARDFILE)
++
++        subject.evaluate_guardfile
++      end
++
++      it "delegates to Guard::Guardfile::Generator" do
++        expect(Guard::Guardfile::Evaluator).to receive(:new).
++          with(foo: "bar") { evaluator }
++
++        expect(evaluator).to receive(:evaluate_guardfile)
++
++        subject.evaluate_guardfile(foo: "bar")
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/deprecated/evaluator_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/deprecated/evaluator_spec.rb
+@@ -0,0 +1,50 @@
++require "guard/config"
++
++unless Guard::Config.new.strict?
++
++  require "guard/deprecated/evaluator"
++  require "guard/internals/state"
++
++  RSpec.describe Guard::Deprecated::Evaluator do
++    subject do
++      class TestClass
++        def evaluate
++        end
++      end
++      described_class.add_deprecated(TestClass)
++      TestClass.new
++    end
++
++    let(:state) { instance_double("Guard::Internals::State") }
++
++    before do
++      allow(Guard::UI).to receive(:deprecation)
++      allow(Guard).to receive(:state).and_return(state)
++    end
++
++    describe "#evaluate_guardfile" do
++      before do
++        allow(subject).to receive(:evaluate)
++      end
++
++      it "displays a deprecation warning to the user" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Evaluator::EVALUATE_GUARDFILE)
++        subject.evaluate_guardfile
++      end
++
++      it "calls the recommended method" do
++        expect(subject).to receive(:evaluate)
++        subject.evaluate_guardfile
++      end
++    end
++
++    describe "#reevaluate_guardfile" do
++      it "displays a deprecation warning to the user" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Evaluator::REEVALUATE_GUARDFILE)
++        subject.reevaluate_guardfile
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/deprecated/guard_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/deprecated/guard_spec.rb
+@@ -0,0 +1,422 @@
++require "guard/config"
++
++unless Guard::Config.new.strict?
++
++  # require guard, to avoid circular require
++  require "guard"
++  # require "guard/deprecated/guard"
++
++  require "guard/config"
++
++  RSpec.describe Guard::Deprecated::Guard do
++    let(:session) { instance_double("Guard::Internals::Session") }
++    let(:state) { instance_double("Guard::Internals::State") }
++    let(:plugins) { instance_double("Guard::Internals::Plugins") }
++    let(:groups) { instance_double("Guard::Internals::Groups") }
++    let(:scope) { instance_double("Guard::Internals::Scope") }
++
++    subject do
++      module TestModule
++        def self.listener
++        end
++
++        def self._pluginless_guardfile?
++          false
++        end
++      end
++      TestModule.tap { |mod| described_class.add_deprecated(mod) }
++    end
++
++    before do
++      allow(session).to receive(:evaluator_options).and_return({})
++      allow(state).to receive(:scope).and_return(scope)
++      allow(state).to receive(:session).and_return(session)
++      allow(session).to receive(:plugins).and_return(plugins)
++      allow(session).to receive(:groups).and_return(groups)
++      allow(::Guard).to receive(:state).and_return(state)
++      allow(Guard::UI).to receive(:deprecation)
++      allow(plugins).to receive(:all)
++      allow(groups).to receive(:all)
++    end
++
++    describe ".guards" do
++      before do
++      end
++
++      it "displays a deprecation warning to the user" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::GUARDS)
++        subject.guards
++      end
++
++      it "delegates to Plugins" do
++        expect(plugins).to receive(:all).with(group: "backend")
++        subject.guards(group: "backend")
++      end
++    end
++
++    describe ".add_guard" do
++      before { allow(plugins).to receive(:add).with("rspec", {}) }
++
++      it "displays a deprecation warning to the user" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::ADD_GUARD)
++
++        subject.add_guard("rspec")
++      end
++
++      it "delegates to Guard.plugins" do
++        expect(subject).to receive(:add_plugin).with("rspec", group: "backend")
++
++        subject.add_guard("rspec", group: "backend")
++      end
++    end
++
++    describe ".get_guard_class" do
++      let(:plugin_util) do
++        instance_double("Guard::PluginUtil", plugin_class: true)
++      end
++
++      before do
++        allow(Guard::PluginUtil).to receive(:new).with("rspec").
++          and_return(plugin_util)
++      end
++
++      it "displays a deprecation warning to the user" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::GET_GUARD_CLASS)
++        subject.get_guard_class("rspec")
++      end
++
++      it "delegates to Guard::PluginUtil" do
++        expect(plugin_util).to receive(:plugin_class).
++          with(fail_gracefully: false)
++        subject.get_guard_class("rspec")
++      end
++
++      describe ":fail_gracefully" do
++        it "pass it to get_guard_class" do
++          expect(plugin_util).to receive(:plugin_class).
++            with(fail_gracefully: true)
++          subject.get_guard_class("rspec", true)
++        end
++      end
++    end
++
++    describe ".locate_guard" do
++      let(:plugin_util) do
++        instance_double("Guard::PluginUtil", plugin_location: true)
++      end
++
++      before do
++        allow(Guard::PluginUtil).to receive(:new) { plugin_util }
++      end
++
++      it "displays a deprecation warning to the user" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::LOCATE_GUARD)
++
++        subject.locate_guard("rspec")
++      end
++
++      it "delegates to Guard::PluginUtil" do
++        expect(Guard::PluginUtil).to receive(:new).with("rspec") { plugin_util }
++        expect(plugin_util).to receive(:plugin_location)
++
++        subject.locate_guard("rspec")
++      end
++    end
++
++    describe ".guard_gem_names" do
++      before { allow(Guard::PluginUtil).to receive(:plugin_names) }
++
++      it "displays a deprecation warning to the user" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::GUARD_GEM_NAMES)
++
++        subject.guard_gem_names
++      end
++
++      it "delegates to Guard::PluginUtil" do
++        expect(Guard::PluginUtil).to receive(:plugin_names)
++
++        subject.guard_gem_names
++      end
++    end
++
++    describe ".running" do
++      it "show deprecation warning" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::RUNNING)
++
++        subject.running
++      end
++    end
++
++    describe ".lock" do
++      it "show deprecation warning" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::LOCK)
++
++        subject.lock
++      end
++    end
++
++    describe ".listener=" do
++      it "show deprecation warning" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::LISTENER_ASSIGN)
++
++        subject.listener = 123
++      end
++
++      it "provides and alternative implementation" do
++        subject.listener = 123
++      end
++    end
++
++    describe "reset_evaluator" do
++      it "show deprecation warning" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::RESET_EVALUATOR)
++        subject.reset_evaluator({})
++      end
++    end
++
++    describe "evaluator" do
++      before do
++        allow(Guard::Guardfile::Evaluator).to receive(:new).
++          and_return(double("evaluator"))
++      end
++      it "show deprecation warning" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::EVALUATOR)
++        subject.evaluator
++      end
++    end
++
++    describe "evaluate_guardfile" do
++      let(:evaluator) { instance_double("Guard::Guardfile::Evaluator") }
++
++      before do
++        allow(::Guard::Guardfile::Evaluator).to receive(:new).
++          and_return(evaluator)
++        allow(evaluator).to receive(:evaluate)
++      end
++
++      it "show deprecation warning" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::EVALUATOR)
++        subject.evaluate_guardfile
++      end
++
++      it "evaluates the guardfile" do
++        expect(evaluator).to receive(:evaluate)
++        subject.evaluate_guardfile
++      end
++    end
++
++    describe "options" do
++      before do
++        allow(session).to receive(:clearing?)
++        allow(session).to receive(:debug?)
++        allow(session).to receive(:watchdirs)
++        allow(session).to receive(:notify_options).and_return({})
++        allow(session).to receive(:interactor_name)
++      end
++
++      it "show deprecation warning" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::OPTIONS)
++        subject.options
++      end
++
++      describe ":clear" do
++        before do
++          allow(session).to receive(:clearing?).and_return(clearing)
++        end
++        context "when being set to true" do
++          let(:clearing) { true }
++          it "sets the clearing option accordingly" do
++            expect(session).to receive(:clearing).with(true)
++            subject.options[:clear] = true
++          end
++        end
++
++        context "when being set to false" do
++          let(:clearing) { false }
++          it "sets the clearing option accordingly" do
++            expect(session).to receive(:clearing).with(false)
++            subject.options[:clear] = false
++          end
++        end
++
++        context "when being read" do
++          context "when not set" do
++            let(:clearing) { false }
++            it "provides an alternative implementation" do
++              expect(subject.options).to include(clear: false)
++            end
++          end
++
++          context "when set" do
++            let(:clearing) { true }
++            it "provides an alternative implementation" do
++              expect(subject.options).to include(clear: true)
++            end
++          end
++        end
++      end
++
++      # TODO: other options?
++    end
++
++    describe ".add_group" do
++      before do
++        allow(groups).to receive(:add)
++      end
++
++      it "show deprecation warning" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::ADD_GROUP)
++        subject.add_group(:foo)
++      end
++
++      it "adds a group" do
++        group = instance_double("Guard::Group")
++        expect(groups).to receive(:add).with(:foo, bar: 3).and_return(group)
++        expect(subject.add_group(:foo, bar: 3)).to eq(group)
++      end
++    end
++
++    describe ".add_plugin" do
++      before do
++        allow(plugins).to receive(:add)
++      end
++
++      it "show deprecation warning" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::ADD_PLUGIN)
++        subject.add_plugin(:foo)
++      end
++
++      it "adds a plugin" do
++        plugin = instance_double("Guard::Plugin")
++        expect(plugins).to receive(:add).with(:foo, bar: 3).and_return(plugin)
++        expect(subject.add_plugin(:foo, bar: 3)).to be(plugin)
++      end
++    end
++
++    describe ".group" do
++      let(:array) { instance_double(Array) }
++
++      before do
++        allow(groups).to receive(:all).with(:foo).and_return(array)
++        allow(array).to receive(:first)
++      end
++
++      it "show deprecation warning" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::GROUP)
++        subject.group(:foo)
++      end
++
++      it "provides a similar implementation" do
++        group = instance_double("Guard::Group")
++        expect(array).to receive(:first).and_return(group)
++        expect(subject.group(:foo)).to be(group)
++      end
++    end
++
++    describe ".plugin" do
++      let(:array) { instance_double(Array) }
++      let(:plugin) { instance_double("Guard::Plugin") }
++
++      before do
++        allow(plugins).to receive(:all).with(:foo).and_return(array)
++        allow(array).to receive(:first).and_return(plugin)
++      end
++
++      it "show deprecation warning" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::PLUGIN)
++        subject.plugin(:foo)
++      end
++
++      it "provides a similar implementation" do
++        expect(subject.plugin(:foo)).to be(plugin)
++      end
++    end
++
++    describe ".groups" do
++      let(:array) { instance_double(Array) }
++
++      before do
++        allow(groups).to receive(:all).with(:foo).and_return(array)
++      end
++
++      it "show deprecation warning" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::GROUPS)
++        subject.groups(:foo)
++      end
++
++      it "provides a similar implementation" do
++        expect(subject.groups(:foo)).to be(array)
++      end
++    end
++
++    describe ".plugins" do
++      let(:array) { instance_double(Array) }
++
++      before do
++        allow(plugins).to receive(:all).with(:foo).and_return(array)
++      end
++
++      it "show deprecation warning" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::PLUGINS)
++        subject.plugins(:foo)
++      end
++
++      it "provides a similar implementation" do
++        expect(subject.plugins(:foo)).to be(array)
++      end
++    end
++
++    describe ".scope" do
++      let(:hash) { instance_double(Hash) }
++
++      before do
++        allow(scope).to receive(:to_hash).and_return(hash)
++      end
++
++      it "show deprecation warning" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::SCOPE)
++        subject.scope
++      end
++
++      it "provides a similar implementation" do
++        expect(subject.scope).to be(hash)
++      end
++    end
++
++    describe ".scope=" do
++      before do
++        allow(scope).to receive(:from_interactor).with(foo: :bar)
++      end
++
++      it "show deprecation warning" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guard::ClassMethods::SCOPE_ASSIGN)
++        subject.scope = { foo: :bar }
++      end
++
++      it "provides a similar implementation" do
++        expect(scope).to receive(:from_interactor).with(foo: :bar)
++        subject.scope = { foo: :bar }
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/deprecated/guardfile_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/deprecated/guardfile_spec.rb
+@@ -0,0 +1,93 @@
++require "guard/config"
++
++unless Guard::Config.new.strict?
++
++  require "guard/deprecated/guardfile"
++
++  RSpec.describe Guard::Deprecated::Guardfile do
++    subject do
++      module TestModule; end.tap { |mod| described_class.add_deprecated(mod) }
++    end
++
++    let(:generator) { instance_double("Guard::Guardfile::Generator") }
++
++    before do
++      allow(Guard::UI).to receive(:deprecation)
++    end
++
++    describe ".create_guardfile" do
++      before do
++        allow(File).to receive(:exist?).with("Guardfile").and_return(false)
++        template = Guard::Guardfile::Generator::GUARDFILE_TEMPLATE
++        allow(FileUtils).to receive(:cp).with(template, "Guardfile")
++
++        allow(Guard::Guardfile::Generator).to receive(:new).
++          and_return(generator)
++
++        allow(generator).to receive(:create_guardfile)
++      end
++
++      it "displays a deprecation warning to the user" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guardfile::ClassMethods::CREATE_GUARDFILE)
++
++        subject.create_guardfile
++      end
++
++      it "delegates to Guard::Guardfile::Generator" do
++        expect(Guard::Guardfile::Generator).to receive(:new).
++          with(foo: "bar") { generator }
++
++        expect(generator).to receive(:create_guardfile)
++
++        subject.create_guardfile(foo: "bar")
++      end
++    end
++
++    describe ".initialize_template" do
++      before do
++        expect(Guard::Guardfile::Generator).to receive(:new) do
++          generator
++        end
++
++        allow(generator).to receive(:initialize_template)
++      end
++
++      it "displays a deprecation warning to the user" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Guardfile::ClassMethods::INITIALIZE_TEMPLATE)
++
++        subject.initialize_template("rspec")
++      end
++
++      it "delegates to Guard::Guardfile::Generator" do
++        expect(generator).to receive(:initialize_template).with("rspec")
++
++        subject.initialize_template("rspec")
++      end
++    end
++
++    describe ".initialize_all_templates" do
++      before do
++        expect(Guard::Guardfile::Generator).to receive(:new) do
++          generator
++        end
++
++        allow(generator).to receive(:initialize_all_templates)
++      end
++
++      it "displays a deprecation warning to the user" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(described_class::ClassMethods::INITIALIZE_ALL_TEMPLATES)
++
++        subject.initialize_all_templates
++      end
++
++      it "delegates to Guard::Guardfile::Generator" do
++        expect(generator).to receive(:initialize_all_templates)
++
++        subject.initialize_all_templates
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/deprecated/watcher_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/deprecated/watcher_spec.rb
+@@ -0,0 +1,49 @@
++require "guard/config"
++
++unless Guard::Config.new.strict?
++
++  require "guard/deprecated/watcher"
++  require "guard/guardfile/evaluator"
++
++  RSpec.describe Guard::Deprecated::Watcher do
++    let(:session) { instance_double("Guard::Internals::Session") }
++
++    subject do
++      module TestModule; end.tap { |mod| described_class.add_deprecated(mod) }
++    end
++
++    let(:evaluator) { instance_double("Guard::Guardfile::Evaluator") }
++    let(:options) { { guardfile: "foo" } }
++
++    let(:state) { instance_double("Guard::Internals::State") }
++
++    before do
++      allow(session).to receive(:evaluator_options).and_return(options)
++      allow(state).to receive(:session).and_return(session)
++      allow(Guard).to receive(:state).and_return(state)
++
++      allow(evaluator).to receive(:guardfile_path).
++        and_return(File.expand_path("foo"))
++
++      allow(::Guard::Guardfile::Evaluator).to receive(:new).with(options).
++        and_return(evaluator)
++
++      allow(Guard::UI).to receive(:deprecation)
++    end
++
++    describe ".match_guardfile?" do
++      it "displays a deprecation warning to the user" do
++        expect(Guard::UI).to receive(:deprecation).
++          with(Guard::Deprecated::Watcher::ClassMethods::MATCH_GUARDFILE)
++
++        files = %w(foo bar)
++        subject.match_guardfile?(files)
++      end
++
++      it "matches against current guardfile" do
++        expect(subject.match_guardfile?(%w(foo bar))).to be(true)
++        expect(subject.match_guardfile?(%w(bar))).to be(false)
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/dsl_describer_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/dsl_describer_spec.rb
+@@ -0,0 +1,164 @@
++# encoding: utf-8
++require "guard/plugin"
++require "guard/dsl_describer"
++require "formatador"
++
++RSpec.describe Guard::DslDescriber do
++  let(:interactor) { instance_double(Guard::Interactor) }
++  let(:env) { double("ENV") }
++
++  let(:session) { instance_double("Guard::Internals::Session") }
++  let(:plugins) { instance_double("Guard::Internals::Plugins") }
++  let(:groups) { instance_double("Guard::Internals::Groups") }
++  let(:state) { instance_double("Guard::Internals::State") }
++
++  before do
++    allow(session).to receive(:groups).and_return(groups)
++    allow(session).to receive(:plugins).and_return(plugins)
++    allow(state).to receive(:session).and_return(session)
++    allow(Guard).to receive(:state).and_return(state)
++
++    allow(env).to receive(:[]).with("GUARD_NOTIFY_PID")
++    allow(env).to receive(:[]).with("GUARD_NOTIFY")
++    allow(env).to receive(:[]).with("GUARD_NOTIFIERS")
++    allow(env).to receive(:[]=).with("GUARD_NOTIFIERS", anything)
++
++    allow(Guard::Notifier).to receive(:turn_on)
++
++    stub_const "Guard::Test", class_double("Guard::Plugin")
++    stub_const "Guard::Another", class_double("Guard::Plugin")
++
++    @output = ""
++
++    # Strip escape sequences
++    allow(STDOUT).to receive(:tty?).and_return(false)
++
++    # Capture formatador output
++    Thread.current[:formatador] = Formatador.new
++    allow(Thread.current[:formatador]).to receive(:print) do |msg|
++      @output << msg
++    end
++  end
++
++  describe "#list" do
++    let(:result) do
++      <<-OUTPUT
++  +---------+-----------+
++  | Plugin  | Guardfile |
++  +---------+-----------+
++  | Another | ✔         |
++  | Even    | ✘         |
++  | More    | ✘         |
++  | Test    | ✔         |
++  +---------+-----------+
++      OUTPUT
++    end
++
++    let(:another) { instance_double("Guard::Plugin", title: "Another") }
++    let(:test) { instance_double("Guard::Plugin", title: "Test") }
++
++    before do
++      allow(plugins).to receive(:all).with("another").and_return([another])
++      allow(plugins).to receive(:all).with("test").and_return([test])
++      allow(plugins).to receive(:all).with("even").and_return([])
++      allow(plugins).to receive(:all).with("more").and_return([])
++
++      allow(Guard::PluginUtil).to receive(:plugin_names) do
++        %w(test another even more)
++      end
++    end
++
++    it "lists the available Guards declared as strings or symbols" do
++      subject.list
++      expect(@output).to eq result
++    end
++  end
++
++  describe ".show" do
++    let(:result) do
++      <<-OUTPUT
++  +---------+---------+--------+-------+
++  | Group   | Plugin  | Option | Value |
++  +---------+---------+--------+-------+
++  | Default | Test    | a      | :b    |
++  |         |         | c      | :d    |
++  +---------+---------+--------+-------+
++  | A       | Test    | x      | 1     |
++  |         |         | y      | 2     |
++  +---------+---------+--------+-------+
++  | B       | Another |        |       |
++  +---------+---------+--------+-------+
++      OUTPUT
++    end
++
++    before do
++      allow(groups).to receive(:all).and_return [
++        instance_double("Guard::Group", name: :default, title: "Default"),
++        instance_double("Guard::Group", name: :a, title: "A"),
++        instance_double("Guard::Group", name: :b, title: "B"),
++      ]
++
++      allow(plugins).to receive(:all).with(group: :default) do
++        options = { a: :b, c: :d }
++        [instance_double("Guard::Plugin", title: "Test", options: options)]
++      end
++
++      allow(plugins).to receive(:all).with(group: :a) do
++        options = { x: 1, y: 2 }
++        [instance_double("Guard::Plugin", title: "Test", options: options)]
++      end
++
++      allow(plugins).to receive(:all).with(group: :b).and_return [
++        instance_double("Guard::Plugin", title: "Another", options: [])
++      ]
++    end
++
++    it "shows the Guards and their options" do
++      subject.show
++      expect(@output).to eq result
++    end
++  end
++
++  describe ".notifiers" do
++    let(:result) do
++      <<-OUTPUT
++  +----------------+-----------+------+--------+-------+
++  | Name           | Available | Used | Option | Value |
++  +----------------+-----------+------+--------+-------+
++  | gntp           | ✔         | ✔    | sticky | true  |
++  +----------------+-----------+------+--------+-------+
++  | terminal_title | ✘         | ✘    |        |       |
++  +----------------+-----------+------+--------+-------+
++      OUTPUT
++    end
++
++    before do
++      allow(Guard::Notifier).to receive(:supported).and_return(
++        gntp: ::Notiffany::Notifier::GNTP,
++        terminal_title: ::Notiffany::Notifier::TerminalTitle
++      )
++
++      allow(Guard::Notifier).to receive(:connect).once.ordered
++      allow(Guard::Notifier).to receive(:detected).
++        and_return([{ name: :gntp, options: { sticky: true } }])
++
++      allow(Guard::Notifier).to receive(:disconnect).once.ordered
++    end
++
++    it "properly connects and disconnects" do
++      expect(Guard::Notifier).to receive(:connect).once.ordered
++      expect(::Guard::Notifier).to receive(:detected).once.ordered.and_return [
++        { name: :gntp, options: { sticky: true } }
++      ]
++
++      expect(Guard::Notifier).to receive(:disconnect).once.ordered
++
++      subject.notifiers
++    end
++
++    it "shows the notifiers and their options" do
++      subject.notifiers
++      expect(@output).to eq result
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/dsl_reader_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/dsl_reader_spec.rb
+@@ -0,0 +1,58 @@
++require "guard/dsl_reader"
++
++RSpec.describe Guard::DslReader, exclude_stubs: [Guard::Dsl] do
++  methods = %w(
++    initialize guard notification interactor group watch callback
++    ignore ignore! logger scope directories clearing
++  ).map(&:to_sym)
++
++  methods.each do |meth|
++    describe "\##{meth} signature" do
++      it "matches base signature" do
++        expected = Guard::Dsl.instance_method(meth).arity
++        expect(subject.method(meth).arity).to eq(expected)
++      end
++    end
++  end
++
++  describe "guard" do
++    context "when it is a String" do
++      let(:name) { "foo" }
++      it "works without errors" do
++        expect { subject.guard(name, bar: :baz) }.to_not raise_error
++      end
++
++      it "reports the name as a String" do
++        subject.guard("foo", bar: :baz)
++        expect(subject.plugin_names).to eq(%w(foo))
++      end
++    end
++
++    context "when it is a Symbol" do
++      let(:name) { :foo }
++      it "works without errors" do
++        expect { subject.guard(name, bar: :baz) }.to_not raise_error
++      end
++
++      it "reports the name as a String" do
++        subject.guard(name, bar: :baz)
++        expect(subject.plugin_names).to eq(%w(foo))
++      end
++    end
++  end
++
++  describe "plugin_names" do
++    it "returns encountered names" do
++      subject.guard("foo", bar: :baz)
++      subject.guard("bar", bar: :baz)
++      subject.guard("baz", bar: :baz)
++      expect(subject.plugin_names).to eq(%w(foo bar baz))
++    end
++  end
++
++  describe "notification" do
++    it "handles arguments without errors" do
++      expect { subject.notification(:off) }.to_not raise_error
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/dsl_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/dsl_spec.rb
+@@ -0,0 +1,671 @@
++require "guard/plugin"
++
++require "guard/dsl"
++
++RSpec.describe Guard::Dsl do
++  let(:ui_config) { instance_double("Guard::UI::Config") }
++
++  let(:guardfile_evaluator) { instance_double(Guard::Guardfile::Evaluator) }
++  let(:interactor) { instance_double(Guard::Interactor) }
++  let(:listener) { instance_double("Listen::Listener") }
++
++  let(:session) { instance_double("Guard::Internals::Session") }
++  let(:plugins) { instance_double("Guard::Internals::Plugins") }
++  let(:groups) { instance_double("Guard::Internals::Groups") }
++  let(:state) { instance_double("Guard::Internals::State") }
++  let(:scope) { instance_double("Guard::Internals::Scope") }
++
++  let(:evaluator) do
++    proc do |contents|
++      Guard::Dsl.new.evaluate(contents, "", 1)
++    end
++  end
++
++  before do
++    stub_user_guard_rb
++    stub_const "Guard::Foo", instance_double(Guard::Plugin)
++    stub_const "Guard::Bar", instance_double(Guard::Plugin)
++    stub_const "Guard::Baz", instance_double(Guard::Plugin)
++    allow(Guard::Notifier).to receive(:turn_on)
++    allow(Guard::Interactor).to receive(:new).and_return(interactor)
++
++    allow(state).to receive(:scope).and_return(scope)
++    allow(session).to receive(:plugins).and_return(plugins)
++    allow(session).to receive(:groups).and_return(groups)
++    allow(state).to receive(:session).and_return(session)
++    allow(Guard).to receive(:state).and_return(state)
++
++    # For backtrace cleanup
++    allow(ENV).to receive(:[]).with("GEM_HOME").and_call_original
++    allow(ENV).to receive(:[]).with("GEM_PATH").and_call_original
++
++    allow(Guard::UI::Config).to receive(:new).and_return(ui_config)
++  end
++
++  describe "#ignore" do
++    context "with ignore regexps" do
++      let(:contents) { "ignore %r{^foo}, /bar/" }
++
++      it "adds ignored regexps to the listener" do
++        expect(session).to receive(:guardfile_ignore=).with([/^foo/, /bar/])
++        evaluator.call(contents)
++      end
++    end
++
++    context "with multiple ignore calls" do
++      let(:contents) { "ignore(/foo/); ignore(/bar/)" }
++
++      it "adds all ignored regexps to the listener" do
++        expect(session).to receive(:guardfile_ignore=).with([/foo/]).once
++        expect(session).to receive(:guardfile_ignore=).with([/bar/]).once
++        evaluator.call(contents)
++      end
++    end
++  end
++
++  describe "#ignore!" do
++    context "when ignoring only foo* and *bar*" do
++      let(:contents) { "ignore! %r{^foo}, /bar/" }
++
++      it "replaces listener regexps" do
++        expect(session).to receive(:guardfile_ignore_bang=).
++          with([[/^foo/, /bar/]])
++
++        evaluator.call(contents)
++      end
++    end
++
++    context "when ignoring *.txt and *.zip and ignoring! only foo*" do
++      let(:contents) { "ignore! %r{.txt$}, /.*\\.zip/\n ignore! %r{^foo}" }
++
++      it "replaces listener ignores, but keeps ignore! ignores" do
++        allow(session).to receive(:guardfile_ignore_bang=).
++          with([[/.txt$/, /.*\.zip/]])
++
++        expect(session).to receive(:guardfile_ignore_bang=).
++          with([[/.txt$/, /.*\.zip/], [/^foo/]])
++
++        evaluator.call(contents)
++      end
++    end
++  end
++
++  # TODO: remove this hack (after deprecating filter)
++  def method_for(klass, meth)
++    klass.instance_method(meth)
++  end
++
++  # TODO: deprecated #filter
++  describe "#filter alias method" do
++    subject { method_for(described_class, :filter) }
++    it { is_expected.to eq(method_for(described_class, :ignore)) }
++  end
++
++  # TODO: deprecated #filter
++  describe "#filter! alias method" do
++    subject { method_for(described_class, :filter!) }
++    it { is_expected.to eq(method_for(described_class, :ignore!)) }
++  end
++
++  describe "#notification" do
++    context "when notification" do
++      let(:contents) { "notification :growl" }
++
++      it "adds a notification to the notifier" do
++        expect(session).to receive(:guardfile_notification=).with(growl: {})
++
++        evaluator.call(contents)
++      end
++    end
++
++    context "with multiple notifications" do
++      let(:contents) do
++        "notification :growl\nnotification :ruby_gntp, host: '192.168.1.5'"
++      end
++
++      it "adds multiple notifiers" do
++        expect(session).to receive(:guardfile_notification=).with(growl: {})
++        expect(session).to receive(:guardfile_notification=).with(
++          ruby_gntp: { host: "192.168.1.5" }
++        )
++
++        evaluator.call(contents)
++      end
++    end
++  end
++
++  describe "#interactor" do
++    context "with interactor :off" do
++      let(:contents) { "interactor :off" }
++      it "disables the interactions with :off" do
++        expect(Guard::Interactor).to receive(:enabled=).with(false)
++        evaluator.call(contents)
++      end
++    end
++
++    context "with interactor options" do
++      let(:contents) { 'interactor option1: \'a\', option2: 123' }
++      it "passes the options to the interactor" do
++        expect(Guard::Interactor).to receive(:options=).
++          with(option1: "a", option2: 123)
++
++        evaluator.call(contents)
++      end
++    end
++  end
++
++  describe "#group" do
++    context "no plugins in group" do
++      let(:contents) { "group :w" }
++
++      it "displays an error" do
++        expect(::Guard::UI).to receive(:error).
++          with("No Guard plugins found in the group 'w',"\
++               " please add at least one.")
++
++        evaluator.call(contents)
++      end
++    end
++
++    context "group named :all" do
++      let(:contents) { "group :all" }
++
++      it "raises an error" do
++        expect { evaluator.call(contents) }.
++          to raise_error(
++            Guard::Dsl::Error,
++            /'all' is not an allowed group name!/
++          )
++      end
++    end
++
++    context 'group named "all"' do
++      let(:contents) { "group 'all'" }
++
++      it "raises an error" do
++        expect { evaluator.call(contents) }.
++          to raise_error(
++            Guard::Dsl::Error,
++            /'all' is not an allowed group name!/
++          )
++      end
++    end
++
++    context "with a valid guardfile" do
++      let(:contents) { valid_guardfile_string }
++
++      it "evaluates all groups" do
++        expect(groups).to receive(:add).with(:w, {})
++        expect(groups).to receive(:add).with(:y, {})
++        expect(groups).to receive(:add).with(:x, halt_on_fail: true)
++
++        expect(plugins).to receive(:add).
++          with(:pow, watchers: [], callbacks: [], group: :default)
++
++        expect(plugins).to receive(:add).
++          with(:test, watchers: [], callbacks: [], group: :w)
++
++        expect(plugins).to receive(:add).
++          with(:rspec, watchers: [], callbacks: [], group: :x).twice
++
++        expect(plugins).to receive(:add).
++          with(:less, watchers: [], callbacks: [], group: :y)
++
++        expect(session).to receive(:guardfile_notification=).with(growl: {})
++        evaluator.call(contents)
++      end
++    end
++
++    context "with multiple names" do
++      let(:contents) { "group :foo, :bar do; end" }
++      it "adds all given groups" do
++        expect(groups).to receive(:add).with(:foo, {})
++        expect(groups).to receive(:add).with(:bar, {})
++
++        evaluator.call(contents)
++      end
++    end
++  end
++
++  describe "#guard" do
++    context "with single-quoted name" do
++      let(:contents) { 'guard \'test\'' }
++
++      it "loads a guard specified as a quoted string from the DSL" do
++        expect(plugins).to receive(:add).
++          with("test", watchers: [], callbacks: [], group: :default)
++
++        evaluator.call(contents)
++      end
++    end
++
++    context "with double-quoted name" do
++      let(:contents) { 'guard "test"' }
++
++      it "loads a guard specified as a double quoted string from the DSL" do
++        expect(plugins).to receive(:add).
++          with("test", watchers: [], callbacks: [], group: :default)
++
++        evaluator.call(contents)
++      end
++    end
++
++    context "with symbol for name" do
++      let(:contents) { "guard :test" }
++
++      it "loads a guard specified as a symbol from the DSL" do
++        expect(plugins).to receive(:add).
++          with(:test, watchers: [], callbacks: [], group: :default)
++
++        evaluator.call(contents)
++      end
++    end
++
++    context "with name as symbol in parens" do
++      let(:contents) { "guard(:test)" }
++
++      it "adds the plugin" do
++        expect(plugins).to receive(:add).
++          with(:test, watchers: [], callbacks: [], group: :default)
++        evaluator.call(contents)
++      end
++    end
++
++    context "with options" do
++      let(:contents) { 'guard \'test\', opt_a: 1, opt_b: \'fancy\'' }
++
++      it "passes options to plugin" do
++        options = {
++          watchers: [],
++          callbacks: [],
++          opt_a: 1,
++          opt_b: "fancy",
++          group: :default
++        }
++
++        expect(plugins).to receive(:add).with("test", options)
++        evaluator.call(contents)
++      end
++    end
++
++    context "with groups" do
++      let(:contents) { "group :foo do; group :bar do; guard :test; end; end" }
++
++      it "adds plugin with group info" do
++        expect(groups).to receive(:add).with(:foo, {})
++        expect(groups).to receive(:add).with(:bar, {})
++        expect(plugins).to receive(:add).
++          with(:test, watchers: [], callbacks: [], group: :bar)
++
++        evaluator.call(contents)
++      end
++    end
++
++    context "with plugins in custom and default groups" do
++      let(:contents) do
++        "group :foo do; group :bar do; guard :test; end; end; guard :rspec"
++      end
++
++      it "assigns plugins to correct groups" do
++        expect(groups).to receive(:add).with(:foo, {})
++        expect(groups).to receive(:add).with(:bar, {})
++
++        expect(plugins).to receive(:add).
++          with(:test, watchers: [], callbacks: [], group: :bar)
++
++        expect(plugins).to receive(:add).
++          with(:rspec, watchers: [], callbacks: [], group: :default)
++
++        evaluator.call(contents)
++      end
++    end
++  end
++
++  describe "#watch" do
++    # TODO: this is testing too much
++    context "with watchers" do
++      let(:watcher_a) do
++        instance_double("Guard::Watcher", pattern: "a", action: proc { "b" })
++      end
++
++      let(:watcher_c) do
++        instance_double("Guard::Watcher", pattern: "c", action: nil)
++      end
++
++      let(:contents) do
++        '
++        guard :dummy do
++           watch(\'a\') { \'b\' }
++           watch(\'c\')
++        end'
++      end
++
++      it "should receive watchers when specified" do
++        call_params = {
++          watchers: [anything, anything],
++          callbacks: [],
++          group: :default
++        }
++
++        expect(plugins).to receive(:add).
++          with(:dummy, call_params) do |_, options|
++          expect(options[:watchers].size).to eq 2
++          expect(options[:watchers][0].pattern).to eq "a"
++          expect(options[:watchers][0].action.call).to eq proc { "b" }.call
++          expect(options[:watchers][1].pattern).to eq "c"
++          expect(options[:watchers][1].action).to be_nil
++        end
++
++        allow(Guard::Watcher).to receive(:new).with("a", anything).
++          and_return(watcher_a)
++
++        allow(Guard::Watcher).to receive(:new).with("c", nil).
++          and_return(watcher_c)
++
++        evaluator.call(contents)
++      end
++    end
++
++    context "with watch in main scope" do
++      let(:contents) { 'watch(\'a\')' }
++      let(:watcher) do
++        instance_double("Guard::Watcher", pattern: "a", action: nil)
++      end
++
++      it "should create an implicit no-op guard when outside a guard block" do
++        plugin_options = {
++          watchers: [anything],
++          callbacks: [],
++          group: :default
++        }
++
++        expect(plugins).to receive(:add).
++          with(:plugin, plugin_options) do |_, options|
++          expect(options[:watchers].size).to eq 1
++          expect(options[:watchers][0].pattern).to eq "a"
++          expect(options[:watchers][0].action).to be_nil
++        end
++
++        allow(Guard::Watcher).to receive(:new).with("a", nil).
++          and_return(watcher)
++
++        evaluator.call(contents)
++      end
++    end
++  end
++
++  describe "#callback" do
++    context "with " do
++      let(:contents) do
++        '
++        guard :rspec do
++
++          callback(:start_end) do |plugin, event, args|
++            "#{plugin.title} executed \'#{event}\' hook with #{args}!"
++          end
++
++          callback(MyCustomCallback, [:start_begin, :run_all_begin])
++        end'
++      end
++
++      it "creates callbacks for the guard" do
++        class MyCustomCallback
++          def self.call(_plugin, _event, _args)
++            # do nothing
++          end
++        end
++
++        params = {
++          watchers: [],
++          callbacks: [anything, anything],
++          group: :default
++        }
++
++        expect(plugins).to receive(:add).with(:rspec, params) do |_, opt|
++          # TODO: this whole block is too verbose, tests too many things at
++          # once and needs refactoring
++
++          expect(opt[:callbacks].size).to eq 2
++
++          callback_0 = opt[:callbacks][0]
++
++          expect(callback_0[:events]).to eq :start_end
++
++          plugin = instance_double("Guard::Plugin", title: "RSpec")
++          result = callback_0[:listener].call(plugin, :start_end, "foo")
++
++          expect(result).to eq 'RSpec executed \'start_end\' hook'\
++            " with foo!"
++
++          callback_1 = opt[:callbacks][1]
++          expect(callback_1[:events]).to eq [:start_begin, :run_all_begin]
++          expect(callback_1[:listener]).to eq MyCustomCallback
++        end
++
++        evaluator.call(contents)
++      end
++    end
++
++    context "without a guard block" do
++      let(:contents) do
++        '
++        callback(:start_end) do |plugin, event, args|
++          "#{plugin} executed \'#{event}\' hook with #{args}!"
++        end
++
++        callback(MyCustomCallback, [:start_begin, :run_all_begin])'
++      end
++
++      it "fails" do
++        expect { evaluator.call(contents) }.to raise_error(/guard block/i)
++      end
++    end
++  end
++
++  describe "#logger" do
++    before do
++      Guard::UI.options = nil
++      allow(ui_config).to receive(:merge!)
++    end
++
++    after do
++      Guard::UI.reset_logger
++      Guard::UI.options = nil
++    end
++
++    describe "options" do
++      let(:contents) { "" }
++
++      before do
++        evaluator.call(contents)
++      end
++
++      subject { ui_config }
++
++      context "with logger level :errror" do
++        let(:contents) { "logger level: :error" }
++        it { is_expected.to have_received(:merge!).with(level: :error) }
++      end
++
++      context "with logger level 'errror'" do
++        let(:contents) { 'logger level: \'error\'' }
++        it { is_expected.to have_received(:merge!).with(level: :error) }
++      end
++
++      context "with logger template" do
++        let(:contents) { 'logger template: \':message - :severity\'' }
++        it do
++          is_expected.to have_received(:merge!).
++            with(template: ":message - :severity")
++        end
++      end
++
++      context "with a logger time format" do
++        let(:contents) { 'logger time_format: \'%Y\'' }
++        it { is_expected.to have_received(:merge!).with(time_format: "%Y") }
++      end
++
++      context "with a logger only filter from a symbol" do
++        let(:contents) { "logger only: :cucumber" }
++        it { is_expected.to have_received(:merge!).with(only: /cucumber/i) }
++      end
++
++      context "with logger only filter from a string" do
++        let(:contents) { 'logger only: \'jasmine\'' }
++        it { is_expected.to have_received(:merge!).with(only: /jasmine/i) }
++      end
++
++      context "with logger only filter from an array of symbols and string" do
++        let(:contents) { 'logger only: [:rspec, \'cucumber\']' }
++        it do
++          is_expected.to have_received(:merge!).
++            with(only: /rspec|cucumber/i)
++        end
++      end
++
++      context "with logger except filter from a symbol" do
++        let(:contents) { "logger except: :jasmine" }
++        it { is_expected.to have_received(:merge!).with(except: /jasmine/i) }
++      end
++
++      context "with logger except filter from a string" do
++        let(:contents) { 'logger except: \'jasmine\'' }
++        it { is_expected.to have_received(:merge!).with(except: /jasmine/i) }
++      end
++
++      context "with logger except filter from array of symbols and string" do
++        let(:contents) { 'logger except: [:rspec, \'cucumber\', :jasmine]' }
++        it do
++          is_expected.to have_received(:merge!).
++            with(except: /rspec|cucumber|jasmine/i)
++        end
++      end
++    end
++
++    context "with invalid options" do
++      context "for the log level" do
++        let(:contents) { "logger level: :baz" }
++
++        it "shows a warning" do
++          expect(Guard::UI).to receive(:warning).
++            with "Invalid log level `baz` ignored."\
++            " Please use either :debug, :info, :warn or :error."
++
++          evaluator.call(contents)
++        end
++
++        it "does not set the invalid value" do
++          expect(ui_config).to receive(:merge!).with({})
++          evaluator.call(contents)
++        end
++      end
++
++      context "when having both the :only and :except options" do
++        let(:contents) { "logger only: :jasmine, except: :rspec" }
++
++        it "shows a warning" do
++          expect(Guard::UI).to receive(:warning).
++            with "You cannot specify the logger options"\
++            " :only and :except at the same time."
++          evaluator.call(contents)
++        end
++
++        it "removes the options" do
++          expect(ui_config).to receive(:merge!).with({})
++          evaluator.call(contents)
++        end
++      end
++    end
++  end
++
++  describe "#scope" do
++    context "with any parameters" do
++      let(:contents) { "scope plugins: [:foo, :bar]" }
++
++      it "sets the guardfile's default scope" do
++        expect(session).to receive(:guardfile_scope).with(plugins: [:foo, :bar])
++        evaluator.call(contents)
++      end
++    end
++  end
++
++  describe "#directories" do
++    context "with valid directories" do
++      let(:contents) { "directories %w(foo bar)" }
++      before do
++        allow(Dir).to receive(:exist?).with("foo").and_return(true)
++        allow(Dir).to receive(:exist?).with("bar").and_return(true)
++      end
++
++      it "sets the watchdirs to given values" do
++        expect(session).to receive(:watchdirs=).with(%w(foo bar))
++        evaluator.call(contents)
++      end
++    end
++
++    context "with no parameters" do
++      let(:contents) { "directories []" }
++
++      it "sets the watchdirs to empty" do
++        expect(session).to receive(:watchdirs=).with([])
++        evaluator.call(contents)
++      end
++    end
++
++    context "with non-existing directory" do
++      let(:contents) { "directories ['foo']" }
++
++      before do
++        allow(Dir).to receive(:exist?).with("foo").and_return(false)
++      end
++
++      it "fails with an error" do
++        expect(session).to_not receive(:watchdirs=)
++        expect do
++          evaluator.call(contents)
++        end.to raise_error(Guard::Dsl::Error, /Directory "foo" does not exist!/)
++      end
++    end
++  end
++
++  describe "#clear" do
++    context "with clear :off" do
++      let(:contents) { "clearing :off" }
++      it "disables clearing the screen after every task" do
++        expect(session).to receive(:clearing).with(false)
++        evaluator.call(contents)
++      end
++    end
++
++    context "with clear :on" do
++      let(:contents) { "clearing :on" }
++      it "enabled clearing the screen after every task" do
++        expect(session).to receive(:clearing).with(true)
++        evaluator.call(contents)
++      end
++    end
++  end
++
++  private
++
++  def valid_guardfile_string
++    '
++    notification :growl
++
++    guard :pow
++
++    group :w do
++      guard :test
++    end
++
++    group :x, halt_on_fail: true do
++      guard :rspec
++      guard :rspec
++    end
++
++    group :y do
++      guard :less
++    end
++    '
++  end
++end
+Index: ruby-guard/spec/lib/guard/group_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/group_spec.rb
+@@ -0,0 +1,34 @@
++require "guard/group"
++
++RSpec.describe Guard::Group do
++  subject { described_class.new(name, options) }
++
++  let(:name) { :foo }
++  let(:options) { {} }
++
++  describe "#name" do
++    specify { expect(subject.name).to eq :foo }
++
++    context "when initialized from a string" do
++      let(:name) { "foo" }
++      specify { expect(subject.name).to eq :foo }
++    end
++  end
++
++  describe "#options" do
++    context "when provided" do
++      let(:options) { { halt_on_fail: true } }
++      specify { expect(subject.options).to eq options }
++    end
++  end
++
++  describe "#title" do
++    specify { expect(subject.title).to eq "Foo" }
++  end
++
++  describe "#to_s" do
++    specify do
++      expect(subject.to_s).to eq "#<Guard::Group @name=foo @options={}>"
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/guardfile/evaluator_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/guardfile/evaluator_spec.rb
+@@ -0,0 +1,220 @@
++require "guard/guardfile/evaluator"
++
++# TODO: shouldn't be necessary
++require "guard"
++
++RSpec.describe Guard::Guardfile::Evaluator do
++  let(:options) { {} }
++  subject { described_class.new(options) }
++
++  let!(:local_guardfile) { (Pathname.pwd + "Guardfile").to_s }
++  let!(:home_guardfile) { (Pathname("~").expand_path + ".Guardfile").to_s }
++  let!(:home_config) { (Pathname("~").expand_path + ".guard.rb").to_s }
++
++  let(:valid_guardfile_string) { "group :foo; do guard :bar; end; end; " }
++
++  let(:dsl) { instance_double("Guard::Dsl") }
++
++  let(:rel_guardfile) do
++    Pathname("../relative_path_to_Guardfile").expand_path.to_s
++  end
++
++  before do
++    allow(Guard::Interactor).to receive(:new).with(false)
++    allow(Guard::Dsl).to receive(:new).and_return(dsl)
++    allow(dsl).to receive(:instance_eval)
++  end
++
++  describe ".evaluate" do
++    describe "error cases" do
++      context "with an invalid Guardfile" do
++        let(:options) { { contents: "guard :foo Bad Guardfile" } }
++
++        it "displays an error message and raises original exception" do
++          stub_user_guard_rb
++
++          allow(dsl).to receive(:evaluate).
++            and_raise(Guard::Dsl::Error,
++                      "Invalid Guardfile, original error is:")
++
++          expect { subject.evaluate }.to raise_error(Guard::Dsl::Error)
++        end
++      end
++
++      context "with no Guardfile at all" do
++        it "displays an error message and exits" do
++          stub_guardfile
++          stub_user_guardfile
++          stub_user_project_guardfile
++
++          expect { subject.evaluate }.
++            to raise_error(Guard::Guardfile::Evaluator::NoGuardfileError)
++        end
++      end
++
++      context "with a problem reading a Guardfile" do
++        let(:path) { File.expand_path("Guardfile") }
++
++        before do
++          stub_user_project_guardfile
++          stub_guardfile(" ") do
++            fail Errno::EACCES.new("permission error")
++          end
++        end
++
++        it "displays an error message and exits" do
++          expect(Guard::UI).to receive(:error).with(/^Error reading file/)
++          expect { subject.evaluate }.to raise_error(SystemExit)
++        end
++      end
++
++      context "with empty Guardfile content" do
++        let(:options) { { contents: "" } }
++
++        it "displays an error message about no plugins" do
++          stub_user_guard_rb
++          stub_guardfile(" ")
++          allow(dsl).to receive(:evaluate).with("", "", 1)
++
++          expect { subject.evaluate }.
++            to raise_error(Guard::Guardfile::Evaluator::NoPluginsError)
++        end
++      end
++
++      context "when provided :contents is nil" do
++        before do
++          # Anything
++          stub_guardfile("guard :foo")
++
++          stub_user_guard_rb
++          stub_user_project_guardfile
++          stub_user_guardfile
++        end
++
++        it "does not raise error and skip it" do
++          allow(dsl).to receive(:evaluate).with("guard :foo", anything, 1)
++
++          expect(Guard::UI).to_not receive(:error)
++          expect do
++            described_class.new(contents: nil).evaluate
++          end.to_not raise_error
++        end
++      end
++
++      context "with a non-existing Guardfile given" do
++        let(:non_existing_path) { "/non/existing/path/to/Guardfile" }
++        let(:options) { { guardfile: non_existing_path } }
++
++        before do
++          stub_file(non_existing_path)
++        end
++
++        it "raises error" do
++          expect { subject.evaluate }.
++            to raise_error(Guard::Guardfile::Evaluator::NoCustomGuardfile)
++        end
++      end
++    end
++
++    describe "selection of the Guardfile data contents" do
++      context "with a valid :contents option" do
++        before do
++          stub_user_guard_rb
++          allow(dsl).to receive(:evaluate)
++        end
++
++        context "with inline content and other Guardfiles available" do
++          let(:inline_code) { "guard :foo" }
++          let(:options) do
++            {
++              contents: inline_code,
++              guardfile: "/abc/Guardfile"
++            }
++          end
++
++          before do
++            stub_file("/abc/Guardfile", "guard :bar")
++            stub_guardfile("guard :baz")
++            stub_user_guardfile("guard :buz")
++          end
++
++          it "gives ultimate precedence to inline content" do
++            expect(dsl).to receive(:evaluate).with(inline_code, "", 1)
++            subject.evaluate
++          end
++        end
++      end
++
++      context "with the :guardfile option" do
++        let(:options) { { guardfile: "../relative_path_to_Guardfile" } }
++
++        before do
++          stub_file(File.expand_path("../relative_path_to_Guardfile"),
++                    valid_guardfile_string)
++          allow(dsl).to receive(:evaluate).
++            with(valid_guardfile_string, anything, 1)
++        end
++      end
++    end
++  end
++
++  describe "#inline?" do
++    before do
++      allow(dsl).to receive(:evaluate)
++      stub_guardfile("guard :bar")
++      stub_user_guard_rb
++      subject.evaluate
++    end
++
++    context "when content is provided" do
++      let(:options) { { guardfile_contents: "guard :foo" } }
++      it { is_expected.to be_inline }
++    end
++
++    context "when no content is provided" do
++      let(:options) { {} }
++      it { is_expected.to_not be_inline }
++    end
++  end
++
++  describe ".guardfile_include?" do
++    subject do
++      evaluator = described_class.new(options)
++      evaluator.evaluate
++      evaluator
++    end
++
++    let(:dsl_reader) { instance_double(Guard::DslReader) }
++
++    before do
++      allow(dsl).to receive(:evaluate)
++      allow(Guard::DslReader).to receive(:new).and_return(dsl_reader)
++      allow(dsl_reader).to receive(:evaluate)
++      stub_user_guard_rb
++    end
++
++    context "when plugin is present" do
++      let(:options) { { contents: 'guard "test" {watch("c")}' } }
++
++      it "returns true" do
++        allow(dsl_reader).
++          to receive(:evaluate).with('guard "test" {watch("c")}', "", 1)
++
++        allow(dsl_reader).to receive(:plugin_names).and_return(["test"])
++        expect(subject).to be_guardfile_include("test")
++      end
++    end
++
++    context "when plugin is not present" do
++      let(:options) { { contents: 'guard "other" {watch("c")}' } }
++
++      it "returns false" do
++        allow(dsl_reader).
++          to receive(:evaluate).with('guard "test" {watch("c")}', "", 1)
++
++        allow(dsl_reader).to receive(:plugin_names).and_return(["other"])
++        expect(subject).to_not be_guardfile_include("test")
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/guardfile/generator_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/guardfile/generator_spec.rb
+@@ -0,0 +1,141 @@
++require "guard/guardfile/generator"
++
++RSpec.describe Guard::Guardfile::Generator do
++  let(:plugin_util) { instance_double("Guard::PluginUtil") }
++  let(:guardfile_generator) { described_class.new }
++
++  it "has a valid Guardfile template" do
++    allow(File).to receive(:exist?).
++      with(described_class::GUARDFILE_TEMPLATE).and_call_original
++
++    expect(File.exist?(described_class::GUARDFILE_TEMPLATE)).to be_truthy
++  end
++
++  describe "#create_guardfile" do
++    context "with an existing Guardfile" do
++      before do
++        allow_any_instance_of(Pathname).to receive(:exist?).and_return(true)
++      end
++
++      it "does not copy the Guardfile template or notify the user" do
++        expect(::Guard::UI).to_not receive(:info)
++        expect(FileUtils).to_not receive(:cp)
++        begin
++          subject.create_guardfile
++        rescue SystemExit
++        end
++      end
++
++      it "does not display information" do
++        expect(::Guard::UI).to_not receive(:info)
++        begin
++          subject.create_guardfile
++        rescue SystemExit
++        end
++      end
++
++      it "displays an error message" do
++        expect(::Guard::UI).to receive(:error).
++          with(%r{Guardfile already exists at .*/Guardfile})
++        begin
++          subject.create_guardfile
++        rescue SystemExit
++        end
++      end
++
++      it "aborts" do
++        expect { subject.create_guardfile }.to raise_error(SystemExit)
++      end
++    end
++
++    context "without an existing Guardfile" do
++      before do
++        allow_any_instance_of(Pathname).to receive(:exist?).and_return(false)
++        allow(FileUtils).to receive(:cp)
++      end
++
++      it "does not display any kind of error or abort" do
++        expect(::Guard::UI).to_not receive(:error)
++        expect(described_class).to_not receive(:abort)
++        described_class.new.create_guardfile
++      end
++
++      it "copies the Guardfile template and notifies the user" do
++        expect(::Guard::UI).to receive(:info)
++        expect(FileUtils).to receive(:cp)
++
++        described_class.new.create_guardfile
++      end
++    end
++  end
++
++  describe "#initialize_template" do
++    context "with an installed Guard implementation" do
++      before do
++        expect(Guard::PluginUtil).to receive(:new) { plugin_util }
++
++        expect(plugin_util).to receive(:plugin_class) do
++          double("Guard::Foo").as_null_object
++        end
++      end
++
++      it "initializes the Guard" do
++        expect(plugin_util).to receive(:add_to_guardfile)
++        described_class.new.initialize_template("foo")
++      end
++    end
++
++    context "with a user defined template" do
++      let(:template) { File.join(described_class::HOME_TEMPLATES, "/bar") }
++
++      it "copies the Guardfile template and initializes the Guard" do
++        expect(IO).to receive(:read).
++          with(template).and_return "Template content"
++
++        expected = "\nTemplate content\n"
++
++        expect(IO).to receive(:binwrite).
++          with("Guardfile", expected, open_args: ["a"])
++
++        allow(plugin_util).to receive(:plugin_class).with(fail_gracefully: true)
++
++        allow(Guard::PluginUtil).to receive(:new).with("bar").
++          and_return(plugin_util)
++
++        described_class.new.initialize_template("bar")
++      end
++    end
++
++    context "when the passed guard can't be found" do
++      before do
++        expect(::Guard::PluginUtil).to receive(:new) { plugin_util }
++        allow(plugin_util).to receive(:plugin_class) { nil }
++        path = File.expand_path("~/.guard/templates/foo")
++        expect(IO).to receive(:read).with(path) do
++          fail Errno::ENOENT
++        end
++      end
++
++      it "notifies the user about the problem" do
++        expect { described_class.new.initialize_template("foo") }.
++          to raise_error(Guard::Guardfile::Generator::Error)
++      end
++    end
++  end
++
++  describe "#initialize_all_templates" do
++    let(:plugins) { %w(rspec spork phpunit) }
++
++    before do
++      expect(::Guard::PluginUtil).to receive(:plugin_names) { plugins }
++    end
++
++    it "calls Guard.initialize_template on all installed plugins" do
++      plugins.each do |g|
++        expect(subject).to receive(:initialize_template).with(g)
++      end
++
++      subject.initialize_all_templates
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/interactor_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/interactor_spec.rb
+@@ -0,0 +1,166 @@
++require "guard/interactor"
++
++# TODO: this shouldn't be necessary
++require "guard/jobs/pry_wrapper"
++require "guard/jobs/sleep"
++
++RSpec.describe Guard::Interactor do
++  let!(:pry_interactor) { instance_double("Guard::Jobs::PryWrapper") }
++  let!(:sleep_interactor) { instance_double("Guard::Jobs::Sleep") }
++  let(:pry_class) { class_double("Guard::Jobs::PryWrapper") }
++  let(:sleep_class) { class_double("Guard::Jobs::Sleep") }
++
++  before do
++    stub_const("Guard::Jobs::PryWrapper", pry_class)
++    stub_const("Guard::Jobs::Sleep", sleep_class)
++
++    allow(Guard::Jobs::PryWrapper).to receive(:new).and_return(pry_interactor)
++    allow(Guard::Jobs::Sleep).to receive(:new).and_return(sleep_interactor)
++
++    @interactor_enabled = described_class.enabled?
++    described_class.enabled = nil
++  end
++
++  after { described_class.enabled = @interactor_enabled }
++
++  describe ".enabled & .enabled=" do
++    it "returns true by default" do
++      expect(described_class).to be_enabled
++    end
++
++    context "interactor not enabled" do
++      before { described_class.enabled = false }
++
++      it "returns false" do
++        expect(described_class).to_not be_enabled
++      end
++    end
++  end
++
++  describe ".options & .options=" do
++    before { described_class.options = nil }
++
++    it "returns {} by default" do
++      expect(described_class.options).to eq({})
++    end
++
++    context "options set to { foo: :bar }" do
++      before { described_class.options = { foo: :bar } }
++
++      it "returns { foo: :bar }" do
++        expect(described_class.options).to eq(foo: :bar)
++      end
++    end
++  end
++
++  context "when enabled" do
++    before { described_class.enabled = true }
++
++    describe "#foreground" do
++      it "starts Pry" do
++        expect(pry_interactor).to receive(:foreground)
++        subject.foreground
++      end
++    end
++
++    describe "#background" do
++      it "hides Pry" do
++        expect(pry_interactor).to receive(:background)
++        subject.background
++      end
++    end
++
++    describe "#handle_interrupt" do
++      it "interrupts Pry" do
++        expect(pry_interactor).to receive(:handle_interrupt)
++        subject.handle_interrupt
++      end
++    end
++  end
++
++  context "when disabled" do
++    before { described_class.enabled = false }
++
++    describe "#foreground" do
++      it "sleeps" do
++        expect(sleep_interactor).to receive(:foreground)
++        subject.foreground
++      end
++    end
++
++    describe "#background" do
++      it "wakes up from sleep" do
++        expect(sleep_interactor).to receive(:background)
++        subject.background
++      end
++    end
++
++    describe "#handle_interrupt" do
++      it "interrupts sleep" do
++        expect(sleep_interactor).to receive(:handle_interrupt)
++        subject.handle_interrupt
++      end
++    end
++  end
++
++  describe "job selection" do
++    subject do
++      Guard::Interactor.new(no_interactions)
++      Guard::Interactor
++    end
++
++    before do
++      Guard::Interactor.enabled = dsl_enabled
++    end
++
++    context "when enabled from the DSL" do
++      let(:dsl_enabled) { true }
++
++      context "when enabled from the commandline" do
++        let(:no_interactions) { false }
++        it "uses only pry" do
++          expect(pry_class).to receive(:new)
++          expect(sleep_class).to_not receive(:new)
++          subject
++        end
++        it { is_expected.to be_enabled }
++      end
++
++      context "when disabled from the commandline" do
++        let(:no_interactions) { true }
++        it "uses only sleeper" do
++          expect(pry_class).to_not receive(:new)
++          expect(sleep_class).to receive(:new)
++          subject
++        end
++
++        # TODO: this is both a useless case and incorrect value
++        it { is_expected.to be_enabled }
++      end
++    end
++
++    context "when disabled from the DSL" do
++      let(:dsl_enabled) { false }
++
++      context "when enabled from the commandline" do
++        let(:no_interactions) { false }
++        it "uses only sleeper" do
++          expect(pry_class).to_not receive(:new)
++          expect(sleep_class).to receive(:new)
++          subject
++        end
++        it { is_expected.to_not be_enabled }
++      end
++
++      context "when disabled from the commandline" do
++        let(:no_interactions) { true }
++        it "uses only sleeper" do
++          expect(pry_class).to_not receive(:new)
++          expect(sleep_class).to receive(:new)
++          subject
++        end
++        it { is_expected.to_not be_enabled }
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/internals/debugging_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/internals/debugging_spec.rb
+@@ -0,0 +1,112 @@
++require "guard/internals/debugging"
++
++RSpec.describe Guard::Internals::Debugging do
++  let(:null) { IO::NULL }
++  let(:ui) { class_double(::Guard::UI) }
++  let(:tracing) { class_spy(::Guard::Internals::Tracing) }
++
++  before do
++    stub_const("::Guard::Internals::Tracing", tracing)
++    stub_const("::Guard::UI", ui)
++    allow(ui).to receive(:debug)
++    allow(ui).to receive(:level=)
++    allow(Thread).to receive(:abort_on_exception=)
++  end
++
++  after do
++    described_class.send(:_reset)
++  end
++
++  describe "#start" do
++    it "traces Kernel.system" do
++      expect(tracing).to receive(:trace).with(Kernel, :system) do |*_, &block|
++        expect(ui).to receive(:debug).with("Command execution: foo")
++        block.call "foo"
++      end
++      described_class.start
++    end
++
++    it "traces Kernel.`" do
++      expect(tracing).to receive(:trace).with(Kernel, :`) do |*_, &block|
++        expect(ui).to receive(:debug).with("Command execution: foo")
++        block.call("foo")
++      end
++
++      described_class.start
++    end
++
++    it "traces Open3.popen3" do
++      expect(tracing).to receive(:trace).with(Open3, :popen3) do |*_, &block|
++        expect(ui).to receive(:debug).with("Command execution: foo")
++        block.call("foo")
++      end
++
++      described_class.start
++    end
++
++    it "traces Kernel.spawn" do
++      expect(tracing).to receive(:trace).with(Kernel, :spawn) do |*_, &block|
++        expect(ui).to receive(:debug).with("Command execution: foo")
++        block.call("foo")
++      end
++
++      described_class.start
++    end
++
++    context "when not started" do
++      before { described_class.start }
++
++      it "sets logger to debug" do
++        expect(ui).to have_received(:level=).with(Logger::DEBUG)
++      end
++
++      it "makes threads abort on exceptions" do
++        expect(Thread).to have_received(:abort_on_exception=).with(true)
++      end
++    end
++
++    context "when already started" do
++      before do
++        allow(tracing).to receive(:trace)
++        described_class.start
++      end
++
++      it "does not set log level" do
++        expect(ui).to_not receive(:level=)
++        described_class.start
++      end
++    end
++  end
++
++  describe "#stop" do
++    context "when already started" do
++      before do
++        described_class.start
++        described_class.stop
++      end
++
++      it "sets logger level to info" do
++        expect(ui).to have_received(:level=).with(Logger::INFO)
++      end
++
++      it "untraces Kernel.system" do
++        expect(tracing).to have_received(:untrace).with(Kernel, :system)
++      end
++
++      it "untraces Kernel.`" do
++        expect(tracing).to have_received(:untrace).with(Kernel, :`)
++      end
++
++      it "untraces Open3.popen3" do
++        expect(tracing).to have_received(:untrace).with(Kernel, :popen3)
++      end
++    end
++
++    context "when not started" do
++      it "does not set logger level" do
++        described_class.stop
++        expect(ui).to_not have_received(:level=)
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/internals/groups_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/internals/groups_spec.rb
+@@ -0,0 +1,121 @@
++require "guard/internals/groups"
++
++RSpec.describe Guard::Internals::Groups do
++  describe "#all" do
++    let(:common) { instance_double("Guard::Group", name: :common) }
++    let(:default) { instance_double("Guard::Group", name: :default) }
++
++    before do
++      allow(Guard::Group).to receive(:new).with(:common).and_return(common)
++      allow(Guard::Group).to receive(:new).with(:default).and_return(default)
++    end
++
++    context "with only default groups" do
++      it "initializes the groups" do
++        expect(subject.all.map(&:name)).to eq [:common, :default]
++      end
++    end
++
++    context "with existing groups" do
++      let(:frontend) { instance_double("Guard::Group", name: :frontend) }
++      let(:backend) { instance_double("Guard::Group", name: :backend) }
++
++      before do
++        allow(Guard::Group).to receive(:new).with(:frontend, {}).
++          and_return(frontend)
++
++        allow(Guard::Group).to receive(:new).with(:backend, {}).
++          and_return(backend)
++
++        subject.add(:frontend)
++        subject.add(:backend)
++      end
++
++      context "with no arguments" do
++        let(:args) { [] }
++        it "returns all groups" do
++          expect(subject.all(*args)).to eq [common, default, frontend, backend]
++        end
++      end
++
++      context "with a string argument" do
++        it "returns an array of groups if plugins are found" do
++          expect(subject.all("backend")).to eq [backend]
++        end
++      end
++
++      context "with a symbol argument matching a group" do
++        it "returns an array of groups if plugins are found" do
++          expect(subject.all(:backend)).to eq [backend]
++        end
++      end
++
++      context "with a symbol argument not matching a group" do
++        it "returns an empty array when no group is found" do
++          expect(subject.all(:foo)).to be_empty
++        end
++      end
++
++      context "with a regexp argument matching a group" do
++        it "returns an array of groups" do
++          expect(subject.all(/^back/)).to eq [backend]
++        end
++      end
++
++      context "with a regexp argument not matching a group" do
++        it "returns an empty array when no group is found" do
++          expect(subject.all(/back$/)).to be_empty
++        end
++      end
++    end
++  end
++
++  # TOOD: test adding with options
++  describe "#add" do
++    let(:common) { instance_double("Guard::Group", name: :common) }
++    let(:default) { instance_double("Guard::Group", name: :default) }
++
++    before do
++      allow(Guard::Group).to receive(:new).with(:common).and_return(common)
++      allow(Guard::Group).to receive(:new).with(:default).and_return(default)
++    end
++
++    context "with existing groups" do
++      let(:frontend) { instance_double("Guard::Group", name: :frontend) }
++      let(:backend) { instance_double("Guard::Group", name: :backend) }
++
++      before do
++        allow(Guard::Group).to receive(:new).with("frontend", {}).
++          and_return(frontend)
++
++        subject.add("frontend")
++      end
++
++      it "add the given group" do
++        subject.add("frontend")
++        expect(subject.all).to match_array([common, default, frontend])
++      end
++
++      it "add the given group with options" do
++        subject.add("frontend", foo: :bar)
++        expect(subject.all).to match_array([common, default, frontend])
++      end
++
++      # TODO: what if group is added multiple times with different options?
++
++      context "with an existing group" do
++        before { subject.add("frontend") }
++
++        it "does not add duplicate groups when name is a string" do
++          subject.add("frontend")
++          expect(subject.all).to match_array([common, default, frontend])
++        end
++
++        it "does not add duplicate groups when name is a symbol" do
++          subject.add(:frontend)
++          expect(subject.all).to match_array([common, default, frontend])
++        end
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/internals/plugins_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/internals/plugins_spec.rb
+@@ -0,0 +1,138 @@
++require "guard/internals/plugins"
++
++RSpec.describe Guard::Internals::Plugins do
++  def stub_plugin(name, group)
++    instance_double("Guard::Plugin", name: name, group: group)
++  end
++
++  # TODO: all this is crazy
++  let(:frontend) { instance_double("Guard::Group", name: :frontend) }
++  let(:backend) { instance_double("Guard::Group", name: :backend) }
++
++  let(:foo_bar_frontend) { stub_plugin("foobar", frontend) }
++  let(:foo_baz_frontend) { stub_plugin("foobaz", frontend) }
++  let(:foo_bar_backend) { stub_plugin("foobar", backend) }
++  let(:foo_baz_backend) { stub_plugin("foobaz", backend) }
++
++  let(:pu_foobar) { instance_double("Guard::PluginUtil") }
++  let(:pu_foobaz) { instance_double("Guard::PluginUtil") }
++
++  before do
++    allow(Guard::PluginUtil).to receive(:new).with("foobar").
++      and_return(pu_foobar)
++
++    allow(Guard::PluginUtil).to receive(:new).with("foobaz").
++      and_return(pu_foobaz)
++
++    allow(pu_foobar).to receive(:initialize_plugin).with(group: "frontend").
++      and_return(foo_bar_frontend)
++
++    allow(pu_foobaz).to receive(:initialize_plugin).with(group: "frontend").
++      and_return(foo_baz_frontend)
++
++    allow(pu_foobar).to receive(:initialize_plugin).with(group: "backend").
++      and_return(foo_bar_backend)
++
++    allow(pu_foobaz).to receive(:initialize_plugin).with(group: "backend").
++      and_return(foo_baz_backend)
++  end
++
++  describe "#all" do
++    before do
++      subject.add("foobar", group: "frontend")
++      subject.add("foobaz", group: "frontend")
++      subject.add("foobar", group: "backend")
++      subject.add("foobaz", group: "backend")
++    end
++
++    context "with no arguments" do
++      let(:args) { [] }
++      it "returns all plugins" do
++        expect(subject.all(*args)).to eq [
++          foo_bar_frontend,
++          foo_baz_frontend,
++          foo_bar_backend,
++          foo_baz_backend
++        ]
++      end
++    end
++
++    context "find a plugin by as string" do
++      it "returns an array of plugins if plugins are found" do
++        expect(subject.all("foo-bar")).
++          to match_array([foo_bar_backend, foo_bar_frontend])
++      end
++    end
++
++    context "find a plugin by as symbol" do
++      it "returns an array of plugins if plugins are found" do
++        expect(subject.all(:'foo-bar')).
++          to match_array([foo_bar_backend, foo_bar_frontend])
++      end
++
++      it "returns an empty array when no plugin is found" do
++        expect(subject.all("foo-foo")).to be_empty
++      end
++    end
++
++    context "find plugins matching a regexp" do
++      it "returns an array of plugins if plugins are found" do
++        expect(subject.all(/^foobar/)).
++          to match_array([foo_bar_backend, foo_bar_frontend])
++      end
++
++      it "returns an empty array when no plugin is found" do
++        expect(subject.all(/foo$/)).to be_empty
++      end
++    end
++
++    context "find plugins by their group as a string" do
++      it "returns an array of plugins if plugins are found" do
++        expect(subject.all(group: "backend")).
++          to eq [foo_bar_backend, foo_baz_backend]
++      end
++    end
++
++    context "find plugins by their group as a symbol" do
++      it "returns an array of plugins if plugins are found" do
++        expect(subject.all(group: :frontend)).
++          to eq [foo_bar_frontend, foo_baz_frontend]
++      end
++
++      it "returns an empty array when no plugin is found" do
++        expect(subject.all(group: :unknown)).to be_empty
++      end
++    end
++
++    context "find plugins by their group & name" do
++      it "returns an array of plugins if plugins are found" do
++        expect(subject.all(group: "backend", name: "foo-bar")).
++          to eq [foo_bar_backend]
++      end
++
++      it "returns an empty array when no plugin is found" do
++        expect(subject.all(group: :unknown, name: :'foo-baz')).
++          to be_empty
++      end
++    end
++  end
++
++  describe "#remove" do
++    before do
++      subject.add("foobar", group: "frontend")
++      subject.add("foobaz", group: "frontend")
++      subject.add("foobar", group: "backend")
++      subject.add("foobaz", group: "backend")
++    end
++
++    it "removes given plugin" do
++      subject.remove(foo_bar_frontend)
++
++      expect(subject.all).to match_array [
++        foo_baz_frontend,
++        foo_bar_backend,
++        foo_baz_backend
++      ]
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/internals/scope_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/internals/scope_spec.rb
+@@ -0,0 +1,93 @@
++require "guard/internals/scope"
++
++RSpec.describe Guard::Internals::Scope do
++  let(:session) { instance_double("Guard::Internals::Session") }
++  let(:state) { instance_double("Guard::Internals::State") }
++
++  let(:groups) { instance_double("Guard::Internals::Groups") }
++  let(:plugins) { instance_double("Guard::Internals::Plugins") }
++
++  let(:foo_plugin) { instance_double("Guard::Plugin", name: :foo) }
++  let(:bar_plugin) { instance_double("Guard::Plugin", name: :bar) }
++  let(:baz_plugin) { instance_double("Guard::Plugin", name: :baz) }
++
++  let(:foo_group) { instance_double("Guard::Group", name: :foo) }
++  let(:bar_group) { instance_double("Guard::Group", name: :bar) }
++  let(:baz_group) { instance_double("Guard::Group", name: :baz) }
++
++  before do
++    allow(groups).to receive(:all).with("foo").and_return([foo_group])
++    allow(groups).to receive(:all).with("bar").and_return([bar_group])
++    allow(groups).to receive(:all).with("baz").and_return([baz_group])
++    allow(groups).to receive(:all).with(:baz).and_return([baz_group])
++
++    allow(plugins).to receive(:all).with("foo").and_return([foo_plugin])
++    allow(plugins).to receive(:all).with("bar").and_return([bar_plugin])
++    allow(plugins).to receive(:all).with("baz").and_return([baz_plugin])
++    allow(plugins).to receive(:all).with(:baz).and_return([baz_plugin])
++
++    allow(session).to receive(:cmdline_plugins).and_return([])
++    allow(session).to receive(:cmdline_groups).and_return([])
++    allow(session).to receive(:groups).and_return(groups)
++    allow(session).to receive(:plugins).and_return(plugins)
++
++    allow(state).to receive(:session).and_return(session)
++    allow(Guard).to receive(:state).and_return(state)
++
++    allow(session).to receive(:guardfile_plugin_scope).and_return([])
++    allow(session).to receive(:guardfile_group_scope).and_return([])
++  end
++
++  # TODO: move to Session?
++  describe "#to_hash" do
++    [:group, :plugin].each do |scope|
++      describe scope.inspect do
++        let(:hash) do
++          subject.to_hash[:"#{scope}s"].map(&:name).map(&:to_s)
++        end
++
++        # NOTE: interactor returns objects
++        context "when set from interactor" do
++          before do
++            stub_obj = send("baz_#{scope}")
++            subject.from_interactor(:"#{scope}s" => stub_obj)
++          end
++
++          it "uses interactor scope" do
++            expect(hash).to contain_exactly("baz")
++          end
++        end
++
++        context "when not set in interactor" do
++          context "when set in commandline" do
++            before do
++              allow(session).to receive(:"cmdline_#{scope}s").
++                and_return(%w(baz))
++            end
++
++            it "uses commandline scope" do
++              expect(hash).to contain_exactly("baz")
++            end
++          end
++
++          context "when not set in commandline" do
++            context "when set in Guardfile" do
++              before do
++                allow(session).to receive(:"guardfile_#{scope}_scope").
++                  and_return(%w(baz))
++              end
++
++              it "uses guardfile scope" do
++                expect(hash).to contain_exactly("baz")
++              end
++            end
++          end
++        end
++      end
++    end
++  end
++
++  describe "#titles" do
++    pending
++  end
++end
+Index: ruby-guard/spec/lib/guard/internals/session_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/internals/session_spec.rb
+@@ -0,0 +1,282 @@
++require "guard/internals/session"
++
++RSpec.describe Guard::Internals::Session do
++  let(:options) { {} }
++  subject { described_class.new(options) }
++
++  let(:plugins) { instance_double("Guard::Internals::Plugins") }
++  let(:groups) { instance_double("Guard::Internals::Plugins") }
++
++  before do
++    allow(Guard::Internals::Plugins).to receive(:new).and_return(plugins)
++    allow(Guard::Internals::Groups).to receive(:new).and_return(groups)
++  end
++
++  describe "#initialize" do
++    describe "#listener_args" do
++      subject { described_class.new(options).listener_args }
++
++      context "with a single watchdir" do
++        let(:options) { { watchdir: ["/usr"] } }
++        let(:dir) { Gem.win_platform? ? "C:/usr" : "/usr" }
++        it { is_expected.to eq [:to, dir, {}] }
++      end
++
++      context "with multiple watchdirs" do
++        let(:options) { { watchdir: ["/usr", "/bin"] } }
++        let(:dir1) { Gem.win_platform? ? "C:/usr" : "/usr" }
++        let(:dir2) { Gem.win_platform? ? "C:/bin" : "/bin" }
++        it { is_expected.to eq [:to, dir1, dir2, {}] }
++      end
++
++      context "with force_polling option" do
++        let(:options) { { force_polling: true } }
++        it { is_expected.to eq [:to, Dir.pwd, force_polling: true] }
++      end
++
++      context "with latency option" do
++        let(:options) { { latency: 1.5 } }
++        it { is_expected.to eq [:to, Dir.pwd, latency: 1.5] }
++      end
++    end
++
++    context "with the plugin option" do
++      let(:options) do
++        {
++          plugin: %w(cucumber jasmine),
++          guardfile_contents: "guard :jasmine do; end; "\
++          "guard :cucumber do; end; guard :coffeescript do; end"
++        }
++      end
++
++      let(:jasmine) { instance_double("Guard::Plugin") }
++      let(:cucumber) { instance_double("Guard::Plugin") }
++      let(:coffeescript) { instance_double("Guard::Plugin") }
++
++      before do
++        stub_const "Guard::Jasmine", jasmine
++        stub_const "Guard::Cucumber", cucumber
++        stub_const "Guard::CoffeeScript", coffeescript
++      end
++
++      it "initializes the plugin scope" do
++        allow(plugins).to receive(:add).with("cucumber", {}).
++          and_return(cucumber)
++
++        allow(plugins).to receive(:add).with("jasmine", {}).
++          and_return(jasmine)
++
++        expect(subject.cmdline_plugins).to match_array(%w(cucumber jasmine))
++      end
++    end
++
++    context "with the group option" do
++      let(:options) do
++        {
++          group: %w(backend frontend),
++          guardfile_contents: "group :backend do; end; "\
++          "group :frontend do; end; group :excluded do; end"
++        }
++      end
++
++      before do
++        g3 = instance_double("Guard::Group", name: :backend, options: {})
++        g4 = instance_double("Guard::Group", name: :frontend, options: {})
++        allow(Guard::Group).to receive(:new).with("backend", {}).and_return(g3)
++        allow(Guard::Group).to receive(:new).with("frontend", {}).and_return(g4)
++      end
++
++      it "initializes the group scope" do
++        expect(subject.cmdline_groups).to match_array(%w(backend frontend))
++      end
++    end
++  end
++
++  describe "#clearing" do
++    context "when not set" do
++      context "when clearing is not set from commandline" do
++        it { is_expected.to_not be_clearing }
++      end
++
++      context "when clearing is set from commandline" do
++        let(:options) { { clear: false } }
++        it { is_expected.to_not be_clearing }
++      end
++    end
++
++    context "when set from guardfile" do
++      context "when set to :on" do
++        before { subject.clearing(true) }
++        it { is_expected.to be_clearing }
++      end
++
++      context "when set to :off" do
++        before { subject.clearing(false) }
++        it { is_expected.to_not be_clearing }
++      end
++    end
++  end
++
++  describe "#guardfile_ignore=" do
++    context "when set from guardfile" do
++      before { subject.guardfile_ignore = [/foo/] }
++      specify { expect(subject.guardfile_ignore).to eq([/foo/]) }
++    end
++
++    context "when set multiple times from guardfile" do
++      before do
++        subject.guardfile_ignore = [/foo/]
++        subject.guardfile_ignore = [/bar/]
++      end
++      specify { expect(subject.guardfile_ignore).to eq([/foo/, /bar/]) }
++    end
++
++    context "when unset" do
++      specify { expect(subject.guardfile_ignore).to eq([]) }
++    end
++  end
++
++  describe "#guardfile_ignore_bang=" do
++    context "when set from guardfile" do
++      before { subject.guardfile_ignore_bang = [/foo/] }
++      specify { expect(subject.guardfile_ignore_bang).to eq([/foo/]) }
++    end
++
++    context "when unset" do
++      specify { expect(subject.guardfile_ignore_bang).to eq([]) }
++    end
++  end
++
++  describe "#guardfile_scope" do
++    before do
++      subject.guardfile_scope(scope)
++    end
++
++    context "with a groups scope" do
++      let(:scope) { { groups: [:foo] } }
++      it "sets the groups" do
++        expect(subject.guardfile_group_scope).to eq([:foo])
++      end
++    end
++
++    context "with a group scope" do
++      let(:scope) { { group: [:foo] } }
++      it "sets the groups" do
++        expect(subject.guardfile_group_scope).to eq([:foo])
++      end
++    end
++
++    context "with a plugin scope" do
++      let(:scope) { { plugin: [:foo] } }
++      it "sets the plugins" do
++        expect(subject.guardfile_plugin_scope).to eq([:foo])
++      end
++    end
++
++    context "with a plugins scope" do
++      let(:scope) { { plugins: [:foo] } }
++      it "sets the plugins" do
++        expect(subject.guardfile_plugin_scope).to eq([:foo])
++      end
++    end
++  end
++
++  describe ".convert_scope" do
++    let(:foo) { instance_double("Guard::Plugin", name: "foo") }
++    let(:bar) { instance_double("Guard::Plugin", name: "bar") }
++    let(:backend) { instance_double("Guard::Group", name: "backend") }
++    let(:frontend) { instance_double("Guard::Group", name: "frontend") }
++
++    before do
++      stub_const "Guard::Foo", class_double("Guard::Plugin")
++      stub_const "Guard::Bar", class_double("Guard::Plugin")
++
++      allow(plugins).to receive(:all).with("backend").and_return([])
++      allow(plugins).to receive(:all).with("frontend").and_return([])
++      allow(plugins).to receive(:all).with("foo").and_return([foo])
++      allow(plugins).to receive(:all).with("bar").and_return([bar])
++      allow(plugins).to receive(:all).with("unknown").and_return([])
++      allow(plugins).to receive(:all).with("scope").and_return([])
++
++      allow(groups).to receive(:all).with("backend").and_return([backend])
++      allow(groups).to receive(:all).with("frontend").and_return([frontend])
++      allow(groups).to receive(:all).with("unknown").and_return([])
++      allow(groups).to receive(:all).with("scope").and_return([])
++    end
++
++    it "returns a group scope" do
++      scopes, = subject.convert_scope %w(backend)
++      expect(scopes).to eq(groups: [backend], plugins: [])
++      scopes, = subject.convert_scope %w(frontend)
++      expect(scopes).to eq(groups: [frontend], plugins: [])
++    end
++
++    it "returns a plugin scope" do
++      scopes, = subject.convert_scope %w(foo)
++      expect(scopes).to eq(plugins: [foo], groups: [])
++      scopes, = subject.convert_scope %w(bar)
++      expect(scopes).to eq(plugins: [bar], groups: [])
++    end
++
++    it "returns multiple group scopes" do
++      scopes, = subject.convert_scope %w(backend frontend)
++      expected = { groups: [backend, frontend], plugins: [] }
++      expect(scopes).to eq(expected)
++    end
++
++    it "returns multiple plugin scopes" do
++      scopes, = subject.convert_scope %w(foo bar)
++      expect(scopes).to eq(plugins: [foo, bar], groups: [])
++    end
++
++    it "returns a plugin and group scope" do
++      scopes, = subject.convert_scope %w(foo backend)
++      expect(scopes).to eq(plugins: [foo], groups: [backend])
++    end
++
++    it "returns the unkown scopes" do
++      _, unknown = subject.convert_scope %w(unknown scope)
++      expect(unknown).to eq %w(unknown scope)
++    end
++  end
++
++  describe "#guardfile_notification=" do
++    context "when set from guardfile" do
++      before do
++        subject.guardfile_notification = { foo: { bar: :baz } }
++      end
++
++      specify do
++        expect(subject.notify_options).to eq(
++          notify: true,
++          notifiers: {
++            foo: { bar: :baz }
++          }
++        )
++      end
++    end
++
++    context "when set multiple times from guardfile" do
++      before do
++        subject.guardfile_notification = { foo: { param: 1 } }
++        subject.guardfile_notification = { bar: { param: 2 } }
++      end
++
++      it "merges results" do
++        expect(subject.notify_options).to eq(
++          notify: true,
++          notifiers: {
++            foo: { param: 1 },
++            bar: { param: 2 }
++          }
++        )
++      end
++    end
++
++    context "when unset" do
++      specify do
++        expect(subject.notify_options).to eq(notify: true, notifiers: {})
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/internals/state_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/internals/state_spec.rb
+@@ -0,0 +1,45 @@
++require "guard/internals/state"
++
++RSpec.describe Guard::Internals::State do
++  let(:options) { {} }
++  subject { described_class.new(options) }
++
++  let(:scope) { instance_double("Guard::Internals::Scope") }
++  let(:plugins) { instance_double("Guard::Internals::Plugins") }
++  let(:groups) { instance_double("Guard::Internals::Groups") }
++  let(:session) { instance_double("Guard::Internals::Session") }
++
++  before do
++    allow(Guard::Internals::Session).to receive(:new).and_return(session)
++    allow(Guard::Internals::Scope).to receive(:new).and_return(scope)
++    allow(session).to receive(:debug?).and_return(false)
++    allow(session).to receive(:plugins).and_return(plugins)
++    allow(session).to receive(:groups).and_return(groups)
++  end
++
++  describe "#initialize" do
++    describe "debugging" do
++      let(:options) { { debug: debug } }
++      before do
++        allow(session).to receive(:debug?).and_return(debug)
++        expect(Guard::Internals::Session).to receive(:new).with(debug: debug)
++      end
++
++      context "when debug is set to true" do
++        let(:debug) { true }
++        it "sets up debugging" do
++          expect(Guard::Internals::Debugging).to receive(:start)
++          subject
++        end
++      end
++
++      context "when debug is set to false" do
++        let(:debug) { false }
++        it "does not set up debugging" do
++          expect(Guard::Internals::Debugging).to_not receive(:start)
++          subject
++        end
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/internals/tracing_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/internals/tracing_spec.rb
+@@ -0,0 +1,113 @@
++require "guard/internals/tracing"
++
++RSpec.describe Guard::Internals::Tracing do
++  let(:null) { IO::NULL }
++
++  # NOTE: Calling system() is different from calling Kernel.system()
++  #
++  # We can capture system() calls by stubbing Kernel.system, but to capture
++  # Kernel.system() calls, we need to stub the module's metaclass methods.
++  #
++  # Stubbing just Kernel.system isn't "deep" enough, but not only that,
++  # we don't want to stub here, we want to TEST the stubbing
++  #
++  describe "Module method tracing" do
++    let(:result) { Kernel.send(meth, *args) }
++    subject { result }
++
++    let(:callback) { double("callback", call: true) }
++
++    # Since we can't stub the C code in Ruby, only "right" way to test this is:
++    # actually call a real command and compare the output
++    before { allow(Kernel).to receive(meth).and_call_original }
++
++    context "when tracing" do
++      before do
++        described_class.trace(Kernel, meth) { |*args| callback.call(*args) }
++        subject
++      end
++
++      after { described_class.untrace(Kernel, meth) }
++
++      context "with no command arguments" do
++        let(:args) { ["echo >#{null}"] }
++
++        context "when #system" do
++          let(:meth) { "system" }
++
++          it { is_expected.to eq(true) }
++
++          it "outputs command" do
++            expect(callback).to have_received(:call).with("echo >#{null}")
++          end
++        end
++
++        context "when backticks" do
++          let(:meth) { :` }
++
++          it { is_expected.to eq("") }
++
++          it "outputs command" do
++            expect(callback).to have_received(:call).with("echo >#{null}")
++          end
++        end
++      end
++
++      context "with command arguments" do
++        let(:args) { %w(true 123) }
++
++        context "when #system" do
++          let(:meth) { "system" }
++
++          it { is_expected.to eq(true) }
++
++          it "outputs command arguments" do
++            expect(callback).to have_received(:call).with("true", "123")
++          end
++        end
++      end
++    end
++
++    context "when not tracing" do
++      before { subject }
++
++      context "with no command arguments" do
++        let(:args) { ["echo test > #{null}"] }
++
++        context "when #system" do
++          let(:meth) { :system }
++
++          it { is_expected.to eq(true) }
++
++          it "does not output anything" do
++            expect(callback).to_not have_received(:call)
++          end
++        end
++
++        context "when backticks" do
++          let(:meth) { :` }
++
++          it { is_expected.to eq("") }
++
++          it "does not output anything" do
++            expect(callback).to_not have_received(:call)
++          end
++        end
++      end
++
++      context "with command arguments" do
++        let(:args) { %w(true 123) }
++
++        context "when #system" do
++          let(:meth) { :system }
++
++          it { is_expected.to eq(true) }
++
++          it "does not output anything" do
++            expect(callback).to_not have_received(:call)
++          end
++        end
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/internals/traps_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/internals/traps_spec.rb
+@@ -0,0 +1,34 @@
++require "guard/internals/traps"
++
++RSpec.describe Guard::Internals::Traps do
++  describe ".handle" do
++    let(:signal_class) { class_double(Signal) }
++
++    before do
++      stub_const("Signal", signal_class)
++    end
++
++    context "with a supported signal name" do
++      let(:signal) { "USR1" }
++
++      it "sets up a handler" do
++        allow(Signal).to receive(:list).and_return("USR1" => 10)
++        allow(Signal).to receive(:trap).with(signal) do |_, &block|
++          block.call
++        end
++
++        expect { |b| described_class.handle(signal, &b) }.to yield_control
++      end
++    end
++
++    context "with an unsupported signal name" do
++      let(:signal) { "ABCD" }
++
++      it "does not set a handler" do
++        allow(Signal).to receive(:list).and_return("KILL" => 9)
++        expect(Signal).to_not receive(:trap)
++        described_class.handle(signal)
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/jobs/pry_wrapper_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/jobs/pry_wrapper_spec.rb
+@@ -0,0 +1,183 @@
++require "guard/jobs/pry_wrapper"
++
++RSpec.describe Guard::Jobs::PryWrapper do
++  subject { described_class.new({}) }
++  let(:listener) { instance_double("Listen::Listener") }
++  let(:pry_config) { double("pry_config") }
++  let(:pry_history) { double("pry_history") }
++  let(:pry_commands) { double("pry_commands") }
++  let(:pry_hooks) { double("pry_hooks") }
++  let(:terminal_settings) { instance_double("Guard::Jobs::TerminalSettings") }
++
++  let(:session) { instance_double("Guard::Internals::Session") }
++  let(:plugins) { instance_double("Guard::Internals::Plugins") }
++  let(:groups) { instance_double("Guard::Internals::Groups") }
++  let(:state) { instance_double("Guard::Internals::State") }
++  let(:scope) { instance_double("Guard::Internals::Scope") }
++
++  before do
++    # TODO: this are here to mock out Pry completely
++    allow(pry_config).to receive(:prompt=)
++    allow(pry_config).to receive(:should_load_rc=)
++    allow(pry_config).to receive(:should_load_local_rc=)
++    allow(pry_config).to receive(:hooks).and_return(pry_hooks)
++    allow(pry_config).to receive(:history).and_return(pry_history)
++    allow(pry_config).to receive(:commands).and_return(pry_commands)
++    allow(pry_history).to receive(:file=)
++    allow(pry_commands).to receive(:alias_command)
++    allow(pry_commands).to receive(:create_command)
++    allow(pry_commands).to receive(:command)
++    allow(pry_commands).to receive(:block_command)
++    allow(pry_hooks).to receive(:add_hook)
++
++    allow(Guard).to receive(:listener).and_return(listener)
++    allow(Pry).to receive(:config).and_return(pry_config)
++    allow(Shellany::Sheller).to receive(:run).with(*%w(hash stty)) { false }
++
++    allow(groups).to receive(:all).and_return([])
++    allow(session).to receive(:groups).and_return(groups)
++
++    allow(plugins).to receive(:all).and_return([])
++    allow(session).to receive(:plugins).and_return(plugins)
++    allow(state).to receive(:session).and_return(session)
++    allow(state).to receive(:scope).and_return(scope)
++    allow(Guard).to receive(:state).and_return(state)
++
++    allow(Guard::Commands::All).to receive(:import)
++    allow(Guard::Commands::Change).to receive(:import)
++    allow(Guard::Commands::Reload).to receive(:import)
++    allow(Guard::Commands::Pause).to receive(:import)
++    allow(Guard::Commands::Notification).to receive(:import)
++    allow(Guard::Commands::Show).to receive(:import)
++    allow(Guard::Commands::Scope).to receive(:import)
++
++    allow(Guard::Jobs::TerminalSettings).to receive(:new).
++      and_return(terminal_settings)
++
++    allow(terminal_settings).to receive(:configurable?).and_return(false)
++    allow(terminal_settings).to receive(:save)
++    allow(terminal_settings).to receive(:restore)
++  end
++
++  describe "#foreground" do
++    before do
++      allow(Pry).to receive(:start) do
++        # sleep for a long time (anything > 0.6)
++        sleep 2
++      end
++    end
++
++    after do
++      subject.background
++    end
++
++    it "waits for Pry thread to finish" do
++      was_alive = false
++
++      Thread.new do
++        sleep 0.1
++        was_alive = subject.send(:thread).alive?
++        subject.background
++      end
++
++      subject.foreground # blocks
++      expect(was_alive).to be
++    end
++
++    it "prevents the Pry thread from being killed too quickly" do
++      start = Time.now.to_f
++
++      Thread.new do
++        sleep 0.1
++        subject.background
++      end
++
++      subject.foreground # blocks
++      killed_moment = Time.now.to_f
++
++      expect(killed_moment - start).to be > 0.5
++    end
++
++    it "return :stopped when brought into background" do
++      Thread.new do
++        sleep 0.1
++        subject.background
++      end
++
++      expect(subject.foreground).to be(:stopped)
++    end
++  end
++
++  describe "#background" do
++    before do
++      allow(Pry).to receive(:start) do
++        # 0.5 is enough for Pry, so we use 0.4
++        sleep 0.4
++      end
++    end
++
++    it "kills the Pry thread" do
++      subject.foreground
++      sleep 1 # give Pry 0.5 sec to boot
++      subject.background
++      sleep 0.25 # to let Pry get killed asynchronously
++
++      expect(subject.send(:thread)).to be_nil
++    end
++  end
++
++  describe "#_prompt(ending_char)" do
++    let(:prompt) { subject.send(:_prompt, ">") }
++
++    before do
++      allow(Shellany::Sheller).to receive(:run).with(*%w(hash stty)) { false }
++      allow(scope).to receive(:titles).and_return(["all"])
++
++      allow(listener).to receive(:paused?).and_return(false)
++
++      expect(Pry).to receive(:view_clip).and_return("main")
++    end
++
++    let(:pry) { instance_double(Pry, input_array: []) }
++
++    context "Guard is not paused" do
++      it 'displays "guard"' do
++        expect(prompt.call(double, 0, pry)).
++          to eq "[0] guard(main)> "
++      end
++    end
++
++    context "Guard is paused" do
++      before do
++        allow(listener).to receive(:paused?).and_return(true)
++      end
++
++      it 'displays "pause"' do
++        expect(prompt.call(double, 0, pry)).
++          to eq "[0] pause(main)> "
++      end
++    end
++
++    context "with a groups scope" do
++      before do
++        allow(scope).to receive(:titles).and_return(%w(Backend Frontend))
++      end
++
++      it "displays the group scope title in the prompt" do
++        expect(prompt.call(double, 0, pry)).
++          to eq "[0] Backend,Frontend guard(main)> "
++      end
++    end
++
++    context "with a plugins scope" do
++      before do
++        allow(scope).to receive(:titles).and_return(%w(RSpec Ronn))
++      end
++
++      it "displays the group scope title in the prompt" do
++        result = prompt.call(double, 0, pry)
++        expect(result).to eq "[0] RSpec,Ronn guard(main)> "
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/jobs/sleep_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/jobs/sleep_spec.rb
+@@ -0,0 +1,59 @@
++require "guard/jobs/sleep"
++
++RSpec.describe Guard::Jobs::Sleep do
++  subject { described_class.new({}) }
++
++  describe "#foreground" do
++    it "sleeps" do
++      status = "unknown"
++
++      Thread.new do
++        sleep 0.1
++        status = Thread.main.status
++        subject.background
++      end
++
++      subject.foreground
++
++      expect(status).to eq("sleep")
++    end
++
++    it "returns :stopped when put to background" do
++      Thread.new do
++        sleep 0.1
++        subject.background
++      end
++
++      expect(subject.foreground).to eq(:stopped)
++    end
++  end
++
++  describe "#background" do
++    it "wakes up main thread" do
++      status = "unknown"
++
++      Thread.new do
++        sleep 0.1 # give enough time for foreground to put main thread to sleep
++
++        subject.background
++
++        sleep 0.1 # cause test to fail every time (without busy loop below)
++
++        status = Thread.main.status
++
++        Thread.main.wakeup # to get "red" in TDD without hanging
++      end
++
++      subject.foreground # go to sleep
++
++      # Keep main thread busy until above thread has a chance to get status
++      begin
++        value = 0
++        Timeout.timeout(0.1) { loop { value += 1 } }
++      rescue Timeout::Error
++      end
++
++      expect(status).to eq("run")
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/notifier_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/notifier_spec.rb
+@@ -0,0 +1,81 @@
++require "guard/notifier"
++
++RSpec.describe Guard::Notifier do
++  subject { described_class }
++  let(:notifier) { instance_double("Notiffany::Notifier") }
++
++  before do
++    allow(Notiffany::Notifier).to receive(:new).and_return(notifier)
++  end
++
++  after do
++    Guard::Notifier.instance_variable_set(:@notifier, nil)
++  end
++
++  describe "toggle_notification" do
++    before do
++      allow(notifier).to receive(:enabled?).and_return(true)
++    end
++
++    context "with available notifiers" do
++      context "when currently on" do
++        before do
++          allow(notifier).to receive(:active?).and_return(true)
++          subject.connect
++        end
++
++        it "suspends notifications" do
++          expect(notifier).to receive(:turn_off)
++          subject.toggle
++        end
++      end
++
++      context "when currently off" do
++        before do
++          subject.connect
++          allow(notifier).to receive(:active?).and_return(false)
++        end
++
++        it "resumes notifications" do
++          expect(notifier).to receive(:turn_on)
++          subject.toggle
++        end
++      end
++    end
++  end
++
++  describe ".notify" do
++    before do
++      subject.connect
++      allow(notifier).to receive(:notify)
++    end
++
++    context "with no options" do
++      it "notifies" do
++        expect(notifier).to receive(:notify).with("A", {})
++        subject.notify("A")
++      end
++    end
++
++    context "with multiple parameters" do
++      it "notifies" do
++        expect(notifier).to receive(:notify).
++          with("A", priority: 2, image: :failed)
++        subject.notify("A", priority: 2, image: :failed)
++      end
++    end
++
++    context "with a runtime error" do
++      before do
++        allow(notifier).to receive(:notify).and_raise(RuntimeError, "an error")
++      end
++
++      it "shows an error" do
++        expect(Guard::UI).to receive(:error).
++          with(/Notification failed for .+: an error/)
++
++        subject.notify("A", priority: 2, image: :failed)
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/options_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/options_spec.rb
+@@ -0,0 +1,31 @@
++require "guard/options"
++
++RSpec.describe Guard::Options do
++  describe ".initialize" do
++    it "handles nil options" do
++      expect { described_class.new(nil) }.to_not raise_error
++    end
++
++    it "has indifferent access" do
++      options = described_class.new({ foo: "bar" }, "foo2" => "baz")
++
++      expect(options[:foo]).to eq "bar"
++      expect(options["foo"]).to eq "bar"
++
++      expect(options[:foo2]).to eq "baz"
++      expect(options["foo2"]).to eq "baz"
++    end
++
++    it "can be passed defaults" do
++      options = described_class.new({}, foo: "bar")
++
++      expect(options[:foo]).to eq "bar"
++    end
++
++    it "merges the sensible defaults to the given options" do
++      options = described_class.new({ plugin: ["rspec"] }, plugin: ["test"])
++
++      expect(options[:plugin]).to eq ["rspec"]
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/plugin_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/plugin_spec.rb
+@@ -0,0 +1,235 @@
++require "guard/plugin"
++
++require "guard/watcher"
++
++RSpec.describe Guard::Plugin do
++  let(:default) { instance_double("Guard::Group") }
++  let(:test) { instance_double("Guard::Group") }
++
++  let(:session) { instance_double("Guard::Internals::Session") }
++  let(:groups) { instance_double("Guard::Internals::Groups") }
++  let(:state) { instance_double("Guard::Internals::State") }
++
++  before do
++    allow(groups).to receive(:add).with(:default).and_return(default)
++    allow(groups).to receive(:add).with(:test).and_return(test)
++
++    allow(session).to receive(:groups).and_return(groups)
++    allow(state).to receive(:session).and_return(session)
++    allow(Guard).to receive(:state).and_return(state)
++  end
++
++  # TODO: this should already be done in spec_helper!
++  after do
++    klass = described_class
++    klass.instance_variables.each do |var|
++      klass.instance_variable_set(var, nil)
++    end
++  end
++
++  describe "#initialize" do
++    it "assigns the defined watchers" do
++      watchers = [double("foo")]
++      expect(Guard::Plugin.new(watchers: watchers).watchers).to eq watchers
++    end
++
++    it "assigns the defined options" do
++      options = { a: 1, b: 2 }
++      expect(Guard::Plugin.new(options).options).to eq options
++    end
++
++    context "with a group in the options" do
++      it "assigns the given group" do
++        expect(Guard::Plugin.new(group: :test).group).to eq test
++      end
++    end
++
++    context "without a group in the options" do
++      it "assigns a default group" do
++        allow(groups).to receive(:add).with(:default).and_return(default)
++        expect(Guard::Plugin.new.group).to eq default
++      end
++    end
++
++    context "with a callback" do
++      it "adds the callback" do
++        block = instance_double(Proc)
++        events = [:start_begin, :start_end]
++        callbacks = [{ events: events, listener: block }]
++        Guard::Plugin.new(callbacks: callbacks)
++        expect(Guard::Plugin.callbacks.first[0][0].callbacks).to eq(callbacks)
++      end
++    end
++  end
++
++  context "with a plugin instance" do
++    subject do
++      module Guard
++        class DuMmy < Guard::Plugin
++        end
++      end
++      Guard::DuMmy
++    end
++
++    after do
++      Guard.send(:remove_const, :DuMmy)
++    end
++
++    describe ".non_namespaced_classname" do
++      it "remove the Guard:: namespace" do
++        expect(subject.non_namespaced_classname).to eq "DuMmy"
++      end
++    end
++
++    describe ".non_namespaced_name" do
++      it "remove the Guard:: namespace and downcase" do
++        expect(subject.non_namespaced_name).to eq "dummy"
++      end
++    end
++
++    describe ".template" do
++      before do
++        allow(File).to receive(:read)
++      end
++
++      it "reads the default template" do
++        expect(File).to receive(:read).
++          with("/guard-dummy/lib/guard/dummy/templates/Guardfile") { true }
++
++        subject.template("/guard-dummy")
++      end
++    end
++
++    describe "#name" do
++      it "outputs the short plugin name" do
++        expect(subject.new.name).to eq "dummy"
++      end
++    end
++
++    describe "#title" do
++      it "outputs the plugin title" do
++        expect(subject.new.title).to eq "DuMmy"
++      end
++    end
++
++    describe "#to_s" do
++      let(:default) { instance_double("Guard::Group", name: :default) }
++
++      it "output the short plugin name" do
++        expect(subject.new.to_s).
++          to match(/#<Guard::DuMmy @name=dummy .*>/)
++      end
++    end
++  end
++
++  let(:listener) { instance_double(Proc, call: nil) }
++
++  describe ".add_callback" do
++    let(:foo) { double("foo plugin") }
++
++    it "can add a run_on_modifications callback" do
++      described_class.add_callback(
++        listener,
++        foo,
++        :run_on_modifications_begin
++      )
++
++      result = described_class.callbacks[[foo, :run_on_modifications_begin]]
++      expect(result).to include(listener)
++    end
++
++    it "can add multiple callbacks" do
++      described_class.add_callback(listener, foo, [:event1, :event2])
++
++      result = described_class.callbacks[[foo, :event1]]
++      expect(result).to include(listener)
++
++      result = described_class.callbacks[[foo, :event2]]
++      expect(result).to include(listener)
++    end
++  end
++
++  describe ".notify" do
++    let(:foo) { double("foo plugin") }
++    let(:bar) { double("bar plugin") }
++
++    before do
++      described_class.add_callback(listener, foo, :start_begin)
++    end
++
++    it "sends :call to the given Guard class's start_begin callback" do
++      expect(listener).to receive(:call).with(foo, :start_begin, "args")
++      described_class.notify(foo, :start_begin, "args")
++    end
++
++    it "sends :call to the given Guard class's start_begin callback" do
++      expect(listener).to receive(:call).with(foo, :start_begin, "args")
++      described_class.notify(foo, :start_begin, "args")
++    end
++
++    it "runs only the given callbacks" do
++      listener2 = double("listener2")
++      described_class.add_callback(listener2, foo, :start_end)
++      expect(listener2).to_not receive(:call).with(foo, :start_end)
++      described_class.notify(foo, :start_begin)
++    end
++
++    it "runs callbacks only for the guard given" do
++      described_class.add_callback(listener, bar, :start_begin)
++      expect(listener).to_not receive(:call).with(bar, :start_begin)
++      described_class.notify(foo, :start_begin)
++    end
++  end
++
++  describe "#hook" do
++    let(:foo) { double("foo plugin") }
++
++    before do
++      described_class.add_callback(listener, foo, :start_begin)
++    end
++
++    it "notifies the hooks" do
++      class Foo < described_class
++        def run_all
++          hook :begin
++          hook :end
++        end
++      end
++
++      foo = Foo.new
++      expect(described_class).to receive(:notify).with(foo, :run_all_begin)
++      expect(described_class).to receive(:notify).with(foo, :run_all_end)
++      foo.run_all
++    end
++
++    it "passes the hooks name" do
++      class Foo < described_class
++        def start
++          hook "my_hook"
++        end
++      end
++
++      foo = Foo.new
++      expect(described_class).to receive(:notify).with(foo, :my_hook)
++      foo.start
++    end
++
++    it "accepts extra arguments" do
++      class Foo < described_class
++        def stop
++          hook :begin, "args"
++          hook "special_sauce", "first_arg", "second_arg"
++        end
++      end
++      foo = Foo.new
++
++      expect(described_class).to receive(:notify).
++        with(foo, :stop_begin, "args")
++
++      expect(described_class).to receive(:notify).
++        with(foo, :special_sauce, "first_arg", "second_arg")
++
++      foo.stop
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/plugin_util_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/plugin_util_spec.rb
+@@ -0,0 +1,303 @@
++require "guard/plugin_util"
++
++require "guard/guardfile/evaluator"
++
++RSpec.describe Guard::PluginUtil do
++  let(:guard_rspec_class) { class_double("Guard::Plugin") }
++  let(:guard_rspec) { instance_double("Guard::Plugin") }
++  let(:evaluator) { instance_double("Guard::Guardfile::Evaluator") }
++  let(:session) { instance_double("Guard::Internals::Session") }
++  let(:state) { instance_double("Guard::Internals::State") }
++
++  before do
++    allow(session).to receive(:evaluator_options).and_return({})
++    allow(state).to receive(:session).and_return(session)
++    allow(Guard).to receive(:state).and_return(state)
++    allow(Guard::Guardfile::Evaluator).to receive(:new).and_return(evaluator)
++  end
++
++  describe ".plugin_names" do
++    before do
++      spec = Gem::Specification
++      gems = [
++        instance_double(spec, name: "guard-myplugin"),
++        instance_double(spec, name: "gem1", full_gem_path: "/gem1"),
++        instance_double(spec, name: "gem2", full_gem_path: "/gem2"),
++        instance_double(spec, name: "guard-compat"),
++      ]
++      allow(File).to receive(:exist?).
++        with("/gem1/lib/guard/gem1.rb") { false }
++
++      allow(File).to receive(:exist?).
++        with("/gem2/lib/guard/gem2.rb") { true }
++
++      gem = class_double(Gem::Specification)
++      stub_const("Gem::Specification", gem)
++      expect(Gem::Specification).to receive(:find_all) { gems }
++    end
++
++    it "returns the list of guard gems" do
++      expect(described_class.plugin_names).to include("myplugin")
++    end
++
++    it "returns the list of embedded guard gems" do
++      expect(described_class.plugin_names).to include("gem2")
++    end
++
++    it "ignores guard-compat" do
++      expect(described_class.plugin_names).to_not include("compat")
++    end
++  end
++
++  describe "#initialize" do
++    it "accepts a name without guard-" do
++      expect(described_class.new("rspec").name).to eq "rspec"
++    end
++
++    it "accepts a name with guard-" do
++      expect(described_class.new("guard-rspec").name).to eq "rspec"
++    end
++  end
++
++  describe "#initialize_plugin" do
++    let(:plugin_util) { described_class.new("rspec") }
++
++    before do
++      allow_any_instance_of(described_class).
++        to receive(:plugin_class).
++        and_return(guard_rspec_class)
++    end
++
++    context "with a plugin inheriting from Guard::Plugin" do
++      before do
++        expect(guard_rspec_class).to receive(:superclass) { ::Guard::Plugin }
++      end
++
++      it "instantiate the plugin using the new API" do
++        options = { watchers: ["watcher"], group: "foo" }
++        expect(guard_rspec_class).to receive(:new).with(options) { guard_rspec }
++
++        expect(plugin_util.initialize_plugin(options)).to eq guard_rspec
++      end
++    end
++  end
++
++  describe "#plugin_location" do
++    subject { described_class.new("rspec") }
++
++    it "returns the path of a Guard gem" do
++      expect(Gem::Specification).to receive(:find_by_name).
++        with("guard-rspec") { double(full_gem_path: "gems/guard-rspec") }
++      expect(subject.plugin_location).to eq "gems/guard-rspec"
++    end
++  end
++
++  describe "#plugin_class" do
++    after do
++      # TODO: use RSpec's stub const
++      consts = [:Classname,
++                :DashedClassName,
++                :UnderscoreClassName,
++                :VSpec,
++                :Inline]
++
++      consts.each do |const|
++        begin
++          Guard.send(:remove_const, const)
++        rescue NameError
++        end
++      end
++    end
++
++    it "reports an error if the class is not found" do
++      expect(::Guard::UI).to receive(:error).with(/Could not load/)
++      expect(::Guard::UI).to receive(:error).with(/Error is: cannot load/)
++      expect(::Guard::UI).to receive(:error).with(/plugin_util.rb/)
++
++      plugin = described_class.new("notAGuardClass")
++      allow(plugin).to receive(:require).with("guard/notaguardclass").
++        and_raise(LoadError, "cannot load such file --")
++
++      plugin.plugin_class
++    end
++
++    context "with a nested Guard class" do
++      it "resolves the Guard class from string" do
++        plugin = described_class.new("classname")
++        expect(plugin).to receive(:require) do |classname|
++          expect(classname).to eq "guard/classname"
++          module Guard
++            class Classname
++            end
++          end
++        end
++        expect(plugin.plugin_class).to eq Guard::Classname
++      end
++
++      it "resolves the Guard class from symbol" do
++        plugin = described_class.new(:classname)
++        expect(plugin).to receive(:require) do |classname|
++          expect(classname).to eq "guard/classname"
++          module Guard
++            class Classname
++            end
++          end
++        end
++        expect(plugin.plugin_class).to eq Guard::Classname
++      end
++    end
++
++    context "with a name with dashes" do
++      it "returns the Guard class" do
++        plugin = described_class.new("dashed-class-name")
++        expect(plugin).to receive(:require) do |classname|
++          expect(classname).to eq "guard/dashed-class-name"
++          module Guard
++            class DashedClassName
++            end
++          end
++        end
++        expect(plugin.plugin_class).to eq Guard::DashedClassName
++      end
++    end
++
++    context "with a name with underscores" do
++      it "returns the Guard class" do
++        plugin = described_class.new("underscore_class_name")
++        expect(plugin).to receive(:require) do |classname|
++          expect(classname).to eq "guard/underscore_class_name"
++          module Guard
++            class UnderscoreClassName
++            end
++          end
++        end
++        expect(plugin.plugin_class).to eq Guard::UnderscoreClassName
++      end
++    end
++
++    context "with a name like VSpec" do
++      it "returns the Guard class" do
++        plugin = described_class.new("vspec")
++        mod = nil
++        allow(plugin).to receive(:require) do |classname|
++          expect(classname).to eq "guard/vspec"
++          module Guard
++            class VSpec
++            end
++          end
++          mod = Guard::VSpec
++        end
++        expect(plugin.plugin_class).to eq mod
++        expect(mod).to be
++      end
++    end
++
++    context "with an inline Guard class" do
++      subject { described_class.new("inline") }
++      let(:plugin_class) { class_double("Guard::Plugin") }
++
++      it "returns the Guard class" do
++        allow(Guard).to receive(:constants).and_return([:Inline])
++        allow(Guard).to receive(:const_get).with(:Inline).
++          and_return(plugin_class)
++
++        expect(subject).to_not receive(:require)
++        expect(subject.plugin_class).to eq plugin_class
++      end
++    end
++
++    context "when set to fail gracefully" do
++      options = { fail_gracefully: true }
++      subject { described_class.new("notAGuardClass") }
++      it "does not print error messages on fail" do
++        expect(::Guard::UI).to_not receive(:error)
++        plugin = subject
++        allow(plugin).to receive(:require).and_raise(LoadError)
++        expect(subject.plugin_class(options)).to be_nil
++      end
++    end
++  end
++
++  describe "#add_to_guardfile" do
++    before do
++      allow(Guard::Guardfile::Evaluator).to receive(:new).and_return(evaluator)
++      allow(evaluator).to receive(:evaluate)
++    end
++
++    context "when the Guard is already in the Guardfile" do
++      before do
++        allow(evaluator).to receive(:guardfile_include?) { true }
++      end
++
++      it "shows an info message" do
++        expect(::Guard::UI).to receive(:info).
++          with "Guardfile already includes myguard guard"
++
++        described_class.new("myguard").add_to_guardfile
++      end
++    end
++
++    context "when Guardfile is empty" do
++      let(:plugin_util) { described_class.new("myguard") }
++      let(:plugin_class) { class_double("Guard::Plugin") }
++      let(:location) { "/Users/me/projects/guard-myguard" }
++      let(:gem_spec) { instance_double("Gem::Specification") }
++      let(:io) { StringIO.new }
++
++      before do
++        allow(evaluator).to receive(:evaluate).
++          and_raise(Guard::Guardfile::Evaluator::NoPluginsError)
++
++        allow(gem_spec).to receive(:full_gem_path).and_return(location)
++        allow(evaluator).to receive(:guardfile_include?) { false }
++        allow(Guard).to receive(:constants).and_return([:MyGuard])
++        allow(Guard).to receive(:const_get).with(:MyGuard).
++          and_return(plugin_class)
++
++        allow(Gem::Specification).to receive(:find_by_name).
++          with("guard-myguard").and_return(gem_spec)
++
++        allow(plugin_class).to receive(:template).with(location).
++          and_return("Template content")
++
++        allow(File).to receive(:read).with("Guardfile") { "Guardfile content" }
++        allow(File).to receive(:open).with("Guardfile", "wb").and_yield io
++      end
++
++      it "appends the template to the Guardfile" do
++        plugin_util.add_to_guardfile
++        expect(io.string).to eq "Guardfile content\n\nTemplate content\n"
++      end
++    end
++
++    context "when the Guard is not in the Guardfile" do
++      let(:plugin_util) { described_class.new("myguard") }
++      let(:plugin_class) { class_double("Guard::Plugin") }
++      let(:location) { "/Users/me/projects/guard-myguard" }
++      let(:gem_spec) { instance_double("Gem::Specification") }
++      let(:io) { StringIO.new }
++
++      before do
++        allow(gem_spec).to receive(:full_gem_path).and_return(location)
++        allow(evaluator).to receive(:guardfile_include?) { false }
++        allow(Guard).to receive(:constants).and_return([:MyGuard])
++        allow(Guard).to receive(:const_get).with(:MyGuard).
++          and_return(plugin_class)
++
++        allow(Gem::Specification).to receive(:find_by_name).
++          with("guard-myguard").and_return(gem_spec)
++
++        allow(plugin_class).to receive(:template).with(location).
++          and_return("Template content")
++
++        allow(File).to receive(:read).with("Guardfile") { "Guardfile content" }
++        allow(File).to receive(:open).with("Guardfile", "wb").and_yield io
++      end
++
++      it "appends the template to the Guardfile" do
++        plugin_util.add_to_guardfile
++        expect(io.string).to eq "Guardfile content\n\nTemplate content\n"
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/runner_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/runner_spec.rb
+@@ -0,0 +1,439 @@
++require "guard/runner"
++
++require "guard/plugin"
++
++RSpec.describe Guard::Runner do
++  let(:ui_config) { instance_double("Guard::UI::Config") }
++  let(:backend_group) do
++    instance_double("Guard::Group", options: {}, name: :backend)
++  end
++
++  let(:frontend_group) do
++    instance_double("Guard::Group", options: {}, name: :frontend)
++  end
++
++  let(:foo_plugin) { double("foo", group: backend_group, hook: nil) }
++  let(:bar_plugin) { double("bar", group: frontend_group, hook: nil) }
++  let(:baz_plugin) { double("baz", group: frontend_group, hook: nil) }
++
++  let(:scope) { instance_double("Guard::Internals::Scope") }
++  let(:plugins) { instance_double("Guard::Internals::Plugins") }
++  let(:state) { instance_double("Guard::Internals::State") }
++  let(:session) { instance_double("Guard::Internals::Session") }
++
++  before do
++    allow(session).to receive(:plugins).and_return(plugins)
++    allow(state).to receive(:session).and_return(session)
++    allow(state).to receive(:scope).and_return(scope)
++    allow(Guard).to receive(:state).and_return(state)
++
++    allow(Guard::UI::Config).to receive(:new).and_return(ui_config)
++  end
++
++  before do
++    Guard::UI.options = nil
++  end
++
++  after do
++    Guard::UI.reset_logger
++    Guard::UI.options = nil
++  end
++
++  describe "#run" do
++    before do
++      allow(scope).to receive(:grouped_plugins).with({}).
++        and_return([[nil, [foo_plugin, bar_plugin, baz_plugin]]])
++
++      allow(ui_config).to receive(:with_progname).and_yield
++    end
++
++    it "executes supervised task on all registered plugins implementing it" do
++      [foo_plugin, bar_plugin].each do |plugin|
++        expect(plugin).to receive(:my_hard_task)
++      end
++
++      subject.run(:my_hard_task)
++    end
++
++    it "marks an action as unit of work" do
++      expect(Lumberjack).to receive(:unit_of_work)
++      subject.run(:my_task)
++    end
++
++    context "with interrupted task" do
++      before do
++        allow(foo_plugin).to receive(:failing).and_raise(Interrupt)
++        # allow(Guard).to receive(:plugins).and_return([foo_plugin])
++      end
++
++      it "catches the thrown symbol" do
++        expect { subject.run(:failing) }.to_not throw_symbol(:task_has_failed)
++      end
++    end
++
++    context "with a scope" do
++      let(:scope_hash) { { plugin: :bar } }
++
++      it "executes the supervised task on the specified plugin only" do
++        expect(scope).to receive(:grouped_plugins).with(scope_hash).
++          and_return([[nil, [bar_plugin]]])
++
++        expect(bar_plugin).to receive(:my_task)
++        expect(foo_plugin).to_not receive(:my_task)
++        expect(baz_plugin).to_not receive(:my_task)
++
++        subject.run(:my_task, scope_hash)
++      end
++    end
++
++    context "with no scope" do
++      let(:scope_hash) { nil }
++
++      it "executes the supervised task using current scope" do
++        expect(bar_plugin).to receive(:my_task)
++        expect(foo_plugin).to receive(:my_task)
++        expect(baz_plugin).to receive(:my_task)
++
++        subject.run(:my_task, scope_hash)
++      end
++    end
++  end
++
++  describe "#run_on_changes" do
++    let(:changes) { [[], [], []] }
++    let(:watcher_module) { Guard::Watcher }
++
++    before do
++      allow(watcher_module).to receive(:match_files) { [] }
++      allow(Guard::UI).to receive(:clear)
++
++      allow(foo_plugin).to receive(:regular_without_arg) { fail "not stubbed" }
++      allow(foo_plugin).to receive(:regular_with_arg) { fail "not stubbed" }
++      allow(foo_plugin).to receive(:failing) { fail "not stubbed" }
++
++      # TODO: runner shouldn't have to know about these
++      allow(foo_plugin).to receive(:run_on_modifications) { fail "not stubbed" }
++      allow(foo_plugin).to receive(:run_on_change) { fail "not stubbed" }
++      allow(foo_plugin).to receive(:run_on_additions) { fail "not stubbed" }
++      allow(foo_plugin).to receive(:run_on_removals) { fail "not stubbed" }
++      allow(foo_plugin).to receive(:run_on_deletion) { fail "not stubbed" }
++
++      allow(foo_plugin).to receive(:my_task)
++      allow(bar_plugin).to receive(:my_task)
++      allow(baz_plugin).to receive(:my_task)
++
++      allow(foo_plugin).to receive(:name).and_return("Foo")
++
++      allow(scope).to receive(:grouped_plugins) do |args|
++        fail "stub me (#{args.inspect})!"
++      end
++
++      # disable reevaluator
++      allow(scope).to receive(:grouped_plugins).with(group: :common).
++        and_return([[nil, []]])
++
++      # foo in default group
++      allow(scope).to receive(:grouped_plugins).with(group: :default).
++        and_return([[nil, [foo_plugin]]])
++
++      allow(scope).to receive(:grouped_plugins).with(no_args).
++        and_return([[nil, [foo_plugin]]])
++
++      allow(ui_config).to receive(:with_progname).and_yield
++    end
++
++    it "always calls UI.clearable" do
++      expect(Guard::UI).to receive(:clearable)
++      expect(scope).to receive(:grouped_plugins).with(no_args).
++        and_return([[nil, [foo_plugin]]])
++
++      subject.run_on_changes(*changes)
++    end
++
++    context "when clearable" do
++      it "clear UI" do
++        expect(Guard::UI).to receive(:clear)
++        expect(scope).to receive(:grouped_plugins).with(no_args).
++          and_return([[nil, [foo_plugin]]])
++        subject.run_on_changes(*changes)
++      end
++    end
++
++    context "with no changes" do
++      it "does not run any task" do
++        %w(
++          run_on_modifications
++          run_on_change
++          run_on_additions
++          run_on_removals
++          run_on_deletion
++        ).each do |task|
++          expect(foo_plugin).to_not receive(task.to_sym)
++        end
++        subject.run_on_changes(*changes)
++      end
++    end
++
++    context "with modified files but modified paths is empty" do
++      let(:modified) { %w(file.txt image.png) }
++
++      before do
++        changes[0] = modified
++        expect(watcher_module).to receive(:match_files).once.
++          with(foo_plugin, modified).and_return([])
++
++        # stub so respond_to? works
++      end
++
++      it "does not call run anything" do
++        expect(foo_plugin).to_not receive(:run_on_modifications)
++        subject.run_on_changes(*changes)
++      end
++    end
++
++    context "with modified paths" do
++      let(:modified) { %w(file.txt image.png) }
++
++      before do
++        changes[0] = modified
++        expect(watcher_module).to receive(:match_files).
++          with(foo_plugin, modified).and_return(modified)
++      end
++
++      it "executes the :run_first_task_found task" do
++        expect(foo_plugin).to receive(:run_on_modifications).with(modified) {}
++        subject.run_on_changes(*changes)
++      end
++    end
++
++    context "with added files but added paths is empty" do
++      let(:added) { %w(file.txt image.png) }
++
++      before do
++        changes[0] = added
++        expect(watcher_module).to receive(:match_files).once.
++          with(foo_plugin, added).and_return([])
++      end
++
++      it "does not call run anything" do
++        expect(foo_plugin).to_not receive(:run_on_additions)
++        subject.run_on_changes(*changes)
++      end
++    end
++
++    context "with added paths" do
++      let(:added) { %w(file.txt image.png) }
++
++      before do
++        changes[1] = added
++        expect(watcher_module).to receive(:match_files).
++          with(foo_plugin, added).and_return(added)
++      end
++
++      it "executes the :run_on_additions task" do
++        expect(foo_plugin).to receive(:run_on_additions).with(added) {}
++        subject.run_on_changes(*changes)
++      end
++    end
++
++    context "with non-matching removed paths" do
++      let(:removed) { %w(file.txt image.png) }
++
++      before do
++        changes[2] = removed
++        expect(watcher_module).to receive(:match_files).once.
++          with(foo_plugin, removed) { [] }
++
++        # stub so respond_to? works
++        allow(foo_plugin).to receive(:run_on_removals)
++      end
++
++      it "does not call tasks" do
++        expect(foo_plugin).to_not receive(:run_on_removals)
++        subject.run_on_changes(*changes)
++      end
++    end
++
++    context "with matching removed paths" do
++      let(:removed) { %w(file.txt image.png) }
++
++      before do
++        changes[2] = removed
++        expect(watcher_module).to receive(:match_files).
++          with(foo_plugin, removed) { removed }
++      end
++
++      it "executes the :run_on_removals task" do
++        expect(foo_plugin).to receive(:run_on_removals).with(removed) {}
++        subject.run_on_changes(*changes)
++      end
++    end
++  end
++
++  describe "#_supervise" do
++    before do
++      allow(ui_config).to receive(:with_progname).and_yield
++    end
++
++    it "executes the task on the passed guard" do
++      expect(foo_plugin).to receive(:my_task)
++      subject.send(:_supervise, foo_plugin, :my_task)
++    end
++
++    context "with a task that succeeds" do
++      context "without any arguments" do
++        before do
++          allow(foo_plugin).to receive(:regular_without_arg) { true }
++        end
++
++        it "does not remove the Guard" do
++          expect(plugins).to_not receive(:remove)
++          subject.send(:_supervise, foo_plugin, :regular_without_arg)
++        end
++
++        it "returns the result of the task" do
++          result = subject.send(:_supervise, foo_plugin, :regular_without_arg)
++          expect(result).to be_truthy
++        end
++
++        it "calls :begin and :end hooks" do
++          expect(foo_plugin).to receive(:hook).
++            with("regular_without_arg_begin")
++
++          expect(foo_plugin).to receive(:hook).
++            with("regular_without_arg_end", true)
++
++          subject.send(:_supervise, foo_plugin, :regular_without_arg)
++        end
++
++        it "passes the result of the supervised method to the :end hook" do
++          expect(foo_plugin).to receive(:hook).
++            with("regular_without_arg_begin")
++
++          expect(foo_plugin).to receive(:hook).
++            with("regular_without_arg_end", true)
++
++          subject.send(:_supervise, foo_plugin, :regular_without_arg)
++        end
++      end
++
++      context "with arguments" do
++        before do
++          allow(foo_plugin).to receive(:regular_with_arg).
++            with("given_path") { "I'm a success" }
++        end
++
++        it "does not remove the Guard" do
++          expect(plugins).to_not receive(:remove)
++          subject.send(
++            :_supervise,
++            foo_plugin,
++            :regular_with_arg,
++            "given_path"
++          )
++        end
++
++        it "returns the result of the task" do
++          result = subject.send(
++            :_supervise,
++            foo_plugin,
++            :regular_with_arg,
++            "given_path"
++          )
++
++          expect(result).to eq "I'm a success"
++        end
++      end
++    end
++
++    context "with a task that throws :task_has_failed" do
++      before do
++        allow(foo_plugin).to receive(:failing) { throw :task_has_failed }
++      end
++
++      context "in a group" do
++        context "with halt_on_fail: true" do
++          before { backend_group.options[:halt_on_fail] = true }
++
++          it "throws :task_has_failed" do
++            expect do
++              subject.send(:_supervise, foo_plugin, :failing)
++            end.to throw_symbol(:task_has_failed)
++          end
++        end
++
++        context "with halt_on_fail: false" do
++          before { backend_group.options[:halt_on_fail] = false }
++
++          it "catches :task_has_failed" do
++            expect do
++              subject.send(:_supervise, foo_plugin, :failing)
++            end.to_not throw_symbol(:task_has_failed)
++          end
++        end
++      end
++    end
++
++    context "with a task that raises an exception" do
++      before do
++        allow(foo_plugin).to receive(:failing) { fail "I break your system" }
++        allow(plugins).to receive(:remove).with(foo_plugin)
++      end
++
++      it "removes the Guard" do
++        expect(plugins).to receive(:remove).with(foo_plugin) {}
++        subject.send(:_supervise, foo_plugin, :failing)
++      end
++
++      it "display an error to the user" do
++        expect(::Guard::UI).to receive :error
++        expect(::Guard::UI).to receive :info
++
++        subject.send(:_supervise, foo_plugin, :failing)
++      end
++
++      it "returns the exception" do
++        failing_result = subject.send(:_supervise, foo_plugin, :failing)
++        expect(failing_result).to be_kind_of(Exception)
++        expect(failing_result.message).to eq "I break your system"
++      end
++
++      it "calls the default begin hook but not the default end hook" do
++        expect(foo_plugin).to receive(:hook).with("failing_begin")
++        expect(foo_plugin).to_not receive(:hook).with("failing_end")
++        subject.send(:_supervise, foo_plugin, :failing)
++      end
++    end
++  end
++
++  describe ".stopping_symbol_for" do
++    let(:guard_plugin) { instance_double("Guard::Plugin") }
++    let(:group) { instance_double("Guard::Group", title: "Foo") }
++
++    before do
++      allow(guard_plugin).to receive(:group).and_return(group)
++    end
++
++    context "for a group with :halt_on_fail" do
++      before do
++        allow(group).to receive(:options).and_return(halt_on_fail: true)
++      end
++
++      it "returns :no_catch" do
++        symbol = described_class.stopping_symbol_for(guard_plugin)
++        expect(symbol).to eq :no_catch
++      end
++    end
++
++    context "for a group without :halt_on_fail" do
++      before do
++        allow(group).to receive(:options).and_return(halt_on_fail: false)
++      end
++
++      it "returns :task_has_failed" do
++        symbol = described_class.stopping_symbol_for(guard_plugin)
++        expect(symbol).to eq :task_has_failed
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/terminal_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/terminal_spec.rb
+@@ -0,0 +1,65 @@
++require "guard/terminal"
++
++RSpec.describe Guard::Terminal do
++  subject { described_class }
++  it { is_expected.to respond_to(:clear) }
++
++  let(:sheller) { class_double("Shellany::Sheller") }
++
++  before do
++    stub_const("Shellany::Sheller", sheller)
++  end
++
++  describe ".clear" do
++    context "when on UNIX" do
++      before { allow(Gem).to receive(:win_platform?).and_return(false) }
++
++      context "when the clear command exists" do
++        let(:result) { [0, "\e[H\e[2J", ""] }
++
++        it "clears the screen using 'clear'" do
++          expect(sheller).to receive(:system).with("printf '\33c\e[3J';").
++            and_return(result)
++          ::Guard::Terminal.clear
++        end
++      end
++
++      context "when the clear command fails" do
++        let(:result) { [nil, nil, "Guard failed to run \"clear;\""] }
++
++        before do
++          allow(sheller).to receive(:system).with("printf '\33c\e[3J';").
++            and_return(result)
++        end
++
++        it "fails" do
++          expect { ::Guard::Terminal.clear }.
++            to raise_error(Errno::ENOENT, /Guard failed to run "clear;"/)
++        end
++      end
++    end
++
++    context "when on Windows" do
++      before { allow(Gem).to receive(:win_platform?).and_return(true) }
++
++      it "clears the screen" do
++        result = [0, "\f", ""]
++        expect(sheller).to receive(:system).with("cls").and_return(result)
++        ::Guard::Terminal.clear
++      end
++
++      context "when the clear command fails" do
++        let(:result) { [nil, nil, "Guard failed to run \"cls\""] }
++
++        before do
++          allow(sheller).to receive(:system).with("cls").and_return(result)
++        end
++
++        it "fails" do
++          expect { ::Guard::Terminal.clear }.
++            to raise_error(Errno::ENOENT, /Guard failed to run "cls"/)
++        end
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/ui/config_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/ui/config_spec.rb
+@@ -0,0 +1,73 @@
++require "guard/ui/config"
++
++RSpec.describe Guard::UI::Config do
++  describe "#device" do
++    context "when not set" do
++      context "when accessed as a method" do
++        it "returns $stderr" do
++          expect(subject.device).to be($stderr)
++        end
++      end
++
++      context "when accessed as a string" do
++        it "returns $stderr" do
++          expect(subject["device"]).to be($stderr)
++        end
++      end
++
++      context "when accessed as a symbol" do
++        it "returns $stderr" do
++          expect(subject[:device]).to be($stderr)
++        end
++      end
++    end
++  end
++
++  describe "#logger_config" do
++    let(:options) { {} }
++    subject { described_class.new(options) }
++
++    let(:logger_config) { instance_double("Guard::UI::Logger::Config") }
++
++    before do
++      allow(Guard::UI::Logger::Config).to receive(:new).
++        and_return(logger_config)
++    end
++
++    context "with defaults" do
++      it "provides a logger config" do
++        expect(subject.logger_config).to be(logger_config)
++      end
++    end
++
++    context "with deprecated options set" do
++      context "when set using a string" do
++        subject { described_class.new('time_format': "foo") }
++
++        it "passes deprecated options to logger" do
++          expect(Guard::UI::Logger::Config).to receive(:new).
++            with(time_format: "foo")
++          subject
++        end
++
++        it "provides a logger config" do
++          expect(subject.logger_config).to be(logger_config)
++        end
++      end
++
++      context "when set using a symbol" do
++        let(:options) { { time_format: "foo" } }
++
++        it "passes deprecated options to logger" do
++          expect(Guard::UI::Logger::Config).to receive(:new).
++            with(time_format: "foo")
++          subject
++        end
++
++        it "provides a logger config" do
++          expect(subject.logger_config).to be(logger_config)
++        end
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/ui/logger_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/ui/logger_spec.rb
+@@ -0,0 +1,22 @@
++require "guard/ui/logger"
++
++RSpec.describe Guard::UI::Logger::Config do
++  describe "defaults" do
++    it "flushes device by default" do
++      expect(subject[:flush_seconds]).to eq(0)
++    end
++  end
++
++  describe "#level=" do
++    context "with a valid value" do
++      before do
++        subject.level = Logger::WARN
++      end
++
++      it "stores the level" do
++        expect(subject[:level]).to eq(Logger::WARN)
++        expect(subject["level"]).to eq(Logger::WARN)
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/ui_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/ui_spec.rb
+@@ -0,0 +1,363 @@
++require "guard/notifier"
++
++# NOTE: this is here so that no UI does not require anything,
++# since it could be activated by guard plugins during development
++# (it they are tested by other guard plugins)
++#
++# TODO: regardless, the dependency on Guard.state should be removed
++#
++require "guard/ui"
++
++require "guard/internals/session"
++
++RSpec.describe Guard::UI do
++  let(:interactor) { instance_double("Guard::Interactor") }
++  let(:logger) { instance_double("Lumberjack::Logger") }
++  let(:config) { instance_double("Guard::UI::Config") }
++  let(:logger_config) { instance_double("Guard::UI::Logger::Config") }
++
++  let(:terminal) { class_double("Guard::Terminal") }
++
++  let(:session) { instance_double("Guard::Internals::Session") }
++  let(:state) { instance_double("Guard::Internals::State") }
++  let(:scope) { instance_double("Guard::Internals::Scope") }
++
++  before do
++    allow(state).to receive(:scope).and_return(scope)
++    allow(state).to receive(:session).and_return(session)
++    allow(Guard).to receive(:state).and_return(state)
++
++    stub_const("Guard::Terminal", terminal)
++
++    allow(Guard::Notifier).to receive(:turn_on) {}
++
++    allow(Lumberjack::Logger).to receive(:new).and_return(logger)
++    allow(Guard::UI::Config).to receive(:new).and_return(config)
++    allow(Guard::UI::Logger::Config).to receive(:new).and_return(logger_config)
++
++    # The spec helper stubs all UI classes, so other specs doesn't have
++    # to explicit take care of it. We unstub and move the stubs one layer
++    # down just for this spec.
++    allow(Guard::UI).to receive(:info).and_call_original
++    allow(Guard::UI).to receive(:warning).and_call_original
++    allow(Guard::UI).to receive(:error).and_call_original
++    allow(Guard::UI).to receive(:deprecation).and_call_original
++    allow(Guard::UI).to receive(:debug).and_call_original
++
++    allow(logger).to receive(:info)
++    allow(logger).to receive(:warn)
++    allow(logger).to receive(:error)
++    allow(logger).to receive(:debug)
++
++    allow(config).to receive(:device)
++    allow(config).to receive(:only)
++    allow(config).to receive(:except)
++
++    allow(config).to receive(:logger_config).and_return(logger_config)
++
++    allow($stderr).to receive(:print)
++  end
++
++  before do
++    Guard::UI.options = nil
++  end
++
++  after do
++    Guard::UI.reset_logger
++    Guard::UI.options = nil
++  end
++
++  describe ".logger" do
++    before do
++      allow(config).to receive(:device).and_return(device)
++    end
++
++    context "with no logger set yet" do
++      let(:device) { "foo.log" }
++
++      it "returns the logger instance" do
++        expect(Guard::UI.logger).to be(logger)
++      end
++
++      it "sets the logger device" do
++        expect(Lumberjack::Logger).to receive(:new).
++          with(device, logger_config)
++
++        Guard::UI.logger
++      end
++    end
++  end
++
++  describe ".level=" do
++    before do
++      allow(logger).to receive(:level=)
++      allow(logger_config).to receive(:level=)
++    end
++
++    context "when logger is set up" do
++      before { Guard::UI.logger }
++
++      it "sets the logger's level" do
++        level = Logger::WARN
++        expect(logger).to receive(:level=).with(level)
++        Guard::UI.level = level
++      end
++
++      it "sets the logger's config level" do
++        level = Logger::WARN
++        expect(logger_config).to receive(:level=).with(level)
++        Guard::UI.level = level
++      end
++    end
++
++    context "when logger is not set up yet" do
++      before { Guard::UI.reset_logger }
++
++      it "sets the logger's config level" do
++        level = Logger::WARN
++        expect(logger_config).to receive(:level=).with(level)
++        Guard::UI.level = level
++      end
++
++      it "does not autocreate the logger" do
++        level = Logger::WARN
++        expect(logger).to_not receive(:level=).with(level)
++        Guard::UI.level = level
++      end
++    end
++  end
++
++  describe ".options=" do
++    let(:new_config) { instance_double("Guard::UI::Config") }
++
++    before do
++      allow(Guard::UI::Config).to receive(:new).with(hi: :ho).
++        and_return(new_config)
++
++      allow(new_config).to receive(:[]).with(:hi).and_return(:ho)
++    end
++
++    it "sets the logger options" do
++      Guard::UI.options = { hi: :ho }
++      expect(Guard::UI.options[:hi]).to eq :ho
++    end
++  end
++
++  shared_examples_for "a logger method" do
++    it "resets the line with the :reset option" do
++      expect(Guard::UI).to receive :reset_line
++      Guard::UI.send(ui_method, input, reset: true)
++    end
++
++    it "logs the message with the given severity" do
++      expect(logger).to receive(severity).with(output)
++      Guard::UI.send(ui_method, input)
++    end
++
++    context "with the :only option" do
++      before { allow(config).to receive(:only).and_return(/A/) }
++
++      it "allows logging matching messages" do
++        expect(logger).to receive(severity).with(output)
++        Guard::UI.send(ui_method, input, plugin: "A")
++      end
++
++      it "prevents logging other messages" do
++        expect(logger).to_not receive(severity)
++        Guard::UI.send(ui_method, input, plugin: "B")
++      end
++    end
++
++    context "with the :except option" do
++      before { allow(config).to receive(:except).and_return(/A/) }
++
++      it "prevents logging matching messages" do
++        expect(logger).to_not receive(severity)
++        Guard::UI.send(ui_method, input, plugin: "A")
++      end
++
++      it "allows logging other messages" do
++        expect(logger).to receive(severity).with(output)
++        Guard::UI.send(ui_method, input, plugin: "B")
++      end
++    end
++  end
++
++  describe ".info" do
++    it_behaves_like "a logger method" do
++      let(:ui_method) { :info }
++      let(:severity) { :info }
++      let(:input) { "Info" }
++      let(:output) { "Info" }
++    end
++  end
++
++  describe ".warning" do
++    it_behaves_like "a logger method" do
++      let(:ui_method) { :warning }
++      let(:severity) { :warn }
++      let(:input) { "Warning" }
++      let(:output) { "\e[0;33mWarning\e[0m" }
++    end
++  end
++
++  describe ".error" do
++    it_behaves_like "a logger method" do
++      let(:ui_method) { :error }
++      let(:severity) { :error }
++      let(:input) { "Error" }
++      let(:output) { "\e[0;31mError\e[0m" }
++    end
++  end
++
++  describe ".deprecation" do
++    before do
++      allow(ENV).to receive(:[]).with("GUARD_GEM_SILENCE_DEPRECATIONS").
++        and_return(value)
++    end
++
++    context "with GUARD_GEM_SILENCE_DEPRECATIONS set to 1" do
++      let(:value) { "1" }
++
++      it "silences deprecations" do
++        expect(Guard::UI.logger).to_not receive(:warn)
++        Guard::UI.deprecation "Deprecator message"
++      end
++    end
++
++    context "with GUARD_GEM_SILENCE_DEPRECATIONS unset" do
++      let(:value) { nil }
++
++      it_behaves_like "a logger method" do
++        let(:ui_method) { :deprecation }
++        let(:severity) { :warn }
++        let(:input) { "Deprecated" }
++        let(:output) do
++          /^\e\[0;33mDeprecated\nDeprecation backtrace: .*\e\[0m$/m
++        end
++      end
++    end
++  end
++
++  describe ".debug" do
++    it_behaves_like "a logger method" do
++      let(:ui_method) { :debug }
++      let(:severity) { :debug }
++      let(:input) { "Debug" }
++      let(:output) { "\e[0;33mDebug\e[0m" }
++    end
++  end
++
++  describe ".clear" do
++    context "with UI set up and ready" do
++      before do
++        allow(session).to receive(:clear?).and_return(false)
++        Guard::UI.reset_and_clear
++      end
++
++      context "when clear option is disabled" do
++        it "does not clear the output" do
++          expect(terminal).to_not receive(:clear)
++          Guard::UI.clear
++        end
++      end
++
++      context "when clear option is enabled" do
++        before do
++          allow(session).to receive(:clear?).and_return(true)
++        end
++
++        context "when the screen is marked as needing clearing" do
++          before { Guard::UI.clearable }
++
++          it "clears the output" do
++            expect(terminal).to receive(:clear)
++            Guard::UI.clear
++          end
++
++          it "clears the output only once" do
++            expect(terminal).to receive(:clear).once
++            Guard::UI.clear
++            Guard::UI.clear
++          end
++
++          context "when the command fails" do
++            before do
++              allow(terminal).to receive(:clear).
++                and_raise(Errno::ENOENT, "failed to run command")
++            end
++
++            it "shows a warning" do
++              expect(logger).to receive(:warn) do |arg|
++                expect(arg).to match(/failed to run command/)
++              end
++              Guard::UI.clear
++            end
++          end
++        end
++
++        context "when the screen has just been cleared" do
++          before { Guard::UI.clear }
++
++          it "does not clear" do
++            expect(terminal).to_not receive(:clear)
++            Guard::UI.clear
++          end
++
++          context "when forced" do
++            let(:opts) { { force: true } }
++
++            it "clears the outputs if forced" do
++              expect(terminal).to receive(:clear)
++              Guard::UI.clear(opts)
++            end
++          end
++        end
++      end
++    end
++  end
++
++  describe ".action_with_scopes" do
++    let(:rspec) { double("Rspec", title: "Rspec") }
++    let(:jasmine) { double("Jasmine", title: "Jasmine") }
++    let(:group) { instance_double("Guard::Group", title: "Frontend") }
++
++    context "with a plugins scope" do
++      it "shows the plugin scoped action" do
++        allow(scope).to receive(:titles).with(plugins: [rspec, jasmine]).
++          and_return(%w(Rspec Jasmine))
++
++        expect(Guard::UI).to receive(:info).with("Reload Rspec, Jasmine")
++        Guard::UI.action_with_scopes("Reload", plugins: [rspec, jasmine])
++      end
++    end
++
++    context "with a groups scope" do
++      it "shows the group scoped action" do
++        allow(scope).to receive(:titles).with(groups: [group]).
++          and_return(%w(Frontend))
++
++        expect(Guard::UI).to receive(:info).with("Reload Frontend")
++        Guard::UI.action_with_scopes("Reload", groups: [group])
++      end
++    end
++
++    context "without a scope" do
++      context "with a global plugin scope" do
++        it "shows the global plugin scoped action" do
++          allow(scope).to receive(:titles).and_return(%w(Rspec Jasmine))
++          expect(Guard::UI).to receive(:info).with("Reload Rspec, Jasmine")
++          Guard::UI.action_with_scopes("Reload", {})
++        end
++      end
++
++      context "with a global group scope" do
++        it "shows the global group scoped action" do
++          allow(scope).to receive(:titles).and_return(%w(Frontend))
++          expect(Guard::UI).to receive(:info).with("Reload Frontend")
++          Guard::UI.action_with_scopes("Reload", {})
++        end
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/watcher/pattern/deprecated_regexp_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/watcher/pattern/deprecated_regexp_spec.rb
+@@ -0,0 +1,28 @@
++require "guard/watcher/pattern/deprecated_regexp"
++
++RSpec.describe Guard::Watcher::Pattern::DeprecatedRegexp do
++  describe ".deprecated?" do
++    specify { expect(described_class.new("^spec_helper.rb")).to be_deprecated }
++    specify { expect(described_class.new("spec_helper.rb$")).to be_deprecated }
++  end
++
++  describe "Matcher returned by .convert" do
++    let(:matcher) { Guard::Watcher::Pattern::Matcher }
++
++    before { allow(matcher).to receive(:new) }
++
++    {
++      "^foo.rb" => /^foo.rb/,
++      "foo.rb$" => /foo.rb$/,
++      'foo\.rb' => /foo\.rb/,
++      ".*rb" => /.*rb/,
++    }.each do |pattern, regexp|
++      context "with #{pattern}" do
++        it "creates a Matcher with #{regexp}" do
++          expect(matcher).to receive(:new).with(regexp)
++          described_class.convert(pattern)
++        end
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/watcher/pattern/match_result_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/watcher/pattern/match_result_spec.rb
+@@ -0,0 +1,46 @@
++require "guard/watcher/pattern/match_result"
++
++RSpec.describe Guard::Watcher::Pattern::MatchResult do
++  let(:match_result) { double("match_data") }
++  let(:original_value) { "foo/bar.rb" }
++  subject { described_class.new(match_result, original_value) }
++
++  describe "#initialize" do
++    context "with valid arguments" do
++      it "does not fail" do
++        expect { subject }.to_not raise_error
++      end
++    end
++  end
++
++  describe "#[]" do
++    context "with a valid match" do
++      let(:match_result) { double("match_data", to_a: %w(foo bar baz)) }
++
++      context "when asked for the non-first item" do
++        let(:index) { 1 }
++        it "returns the value at given index" do
++          expect(subject[index]).to eq("bar")
++        end
++      end
++
++      context "when asked for the first item" do
++        let(:index) { 0 }
++        it "returns the full original value" do
++          expect(subject[index]).to eq("foo/bar.rb")
++        end
++      end
++
++      context "when asked for a name match via a symbol" do
++        let(:index) { :foo }
++        before do
++          allow(match_result).to receive(:[]).with(:foo).and_return("baz")
++        end
++
++        it "returns the value by name" do
++          expect(subject[index]).to eq("baz")
++        end
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/watcher/pattern/matcher_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/watcher/pattern/matcher_spec.rb
+@@ -0,0 +1,82 @@
++require "guard/watcher/pattern/matcher"
++
++RSpec.describe Guard::Watcher::Pattern::Matcher do
++  subject { described_class.new(obj) }
++  describe "#match" do
++    let(:expected) { double("match_result") }
++
++    context "when constructed with valid matcher object" do
++      let(:obj) { double("matcher") }
++
++      context "when matched against a Pathname" do
++        before do
++          allow(obj).to receive(:match).and_return(expected)
++        end
++        let(:filename) { Pathname("foo.rb") }
++
++        it "returns the match result" do
++          expect(subject.match(filename)).to be(expected)
++        end
++
++        it "passes the Pathname to the matcher" do
++          allow(obj).to receive(:match).with(filename)
++          subject.match(filename)
++        end
++      end
++
++      context "when matched against a String" do
++        before do
++          allow(obj).to receive(:match).and_return(expected)
++        end
++        let(:filename) { "foo.rb" }
++
++        it "returns the match result" do
++          expect(subject.match(filename)).to be(expected)
++        end
++
++        it "passes a Pathname to the matcher" do
++          allow(obj).to receive(:match).with(Pathname(filename))
++          subject.match(filename)
++        end
++      end
++    end
++  end
++
++  describe "integration" do
++    describe "#match result" do
++      subject { described_class.new(obj).match(filename) }
++      context "when constructed with valid regexp" do
++        let(:obj) { /foo.rb$/ }
++
++        context "when matched file is a string" do
++          context "when filename matches" do
++            let(:filename) { "foo.rb" }
++            specify { expect(subject.to_a).to eq(["foo.rb"]) }
++          end
++
++          context "when filename does not match" do
++            let(:filename) { "bar.rb" }
++            specify { expect(subject).to be_nil }
++          end
++        end
++
++        context "when matched file is an unclean Pathname" do
++          context "when filename matches" do
++            let(:filename) { Pathname("./foo.rb") }
++            specify { expect(subject.to_a).to eq(["foo.rb"]) }
++          end
++
++          context "when filename does not match" do
++            let(:filename) { Pathname("./bar.rb") }
++            specify { expect(subject).to be_nil }
++          end
++        end
++
++        context "when matched file contains a $" do
++          let(:filename) { Pathname("lib$/foo.rb") }
++          specify { expect(subject.to_a).to eq(["foo.rb"]) }
++        end
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/watcher/pattern/pathname_path_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/watcher/pattern/pathname_path_spec.rb
+@@ -0,0 +1,35 @@
++require "guard/watcher/pattern/pathname_path"
++
++RSpec.describe Guard::Watcher::Pattern::PathnamePath do
++  subject { described_class.new(path) }
++  describe "#match result" do
++    subject { described_class.new(path).match(filename) }
++    context "when constructed with an unclean Pathname" do
++      let(:path) { Pathname("./foo.rb") }
++
++      context "when matched file is a string" do
++        context "when filename matches" do
++          let(:filename) { "foo.rb" }
++          specify { expect(subject).to eq([Pathname("foo.rb")]) }
++        end
++
++        context "when filename does not match" do
++          let(:filename) { "bar.rb" }
++          specify { expect(subject).to be_nil }
++        end
++      end
++
++      context "when matched file is an unclean Pathname" do
++        context "when filename matches" do
++          let(:filename) { Pathname("./foo.rb") }
++          specify { expect(subject).to eq([Pathname("foo.rb")]) }
++        end
++
++        context "when filename does not match" do
++          let(:filename) { Pathname("./bar.rb") }
++          specify { expect(subject).to be_nil }
++        end
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/watcher/pattern/simple_path_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/watcher/pattern/simple_path_spec.rb
+@@ -0,0 +1,35 @@
++require "guard/watcher/pattern/simple_path"
++
++RSpec.describe Guard::Watcher::Pattern::SimplePath do
++  subject { described_class.new(path) }
++
++  describe "#match result" do
++    context "when constructed with filename string" do
++      let(:path) { "foo.rb" }
++
++      context "when matched file is a string" do
++        context "when filename matches" do
++          let(:filename) { "foo.rb" }
++          specify { expect(subject.match(filename)).to eq(["foo.rb"]) }
++        end
++
++        context "when filename does not match" do
++          let(:filename) { "bar.rb" }
++          specify { expect(subject.match(filename)).to be_nil }
++        end
++      end
++
++      context "when matched file is an unclean Pathname" do
++        context "when filename matches" do
++          let(:filename) { Pathname("./foo.rb") }
++          specify { expect(subject.match(filename)).to eq(["foo.rb"]) }
++        end
++
++        context "when filename does not match" do
++          let(:filename) { Pathname("./bar.rb") }
++          specify { expect(subject.match(filename)).to be_nil }
++        end
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/watcher/pattern_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/watcher/pattern_spec.rb
+@@ -0,0 +1,37 @@
++require "guard/watcher/pattern"
++
++RSpec.describe Guard::Watcher::Pattern do
++  describe ".create" do
++    subject { described_class.create(pattern) }
++
++    context "when a string is given" do
++      let(:pattern) { "foo.rb" }
++      it { is_expected.to be_a(described_class::SimplePath) }
++    end
++
++    context "when a Pathname is given" do
++      let(:pattern) { Pathname("foo.rb") }
++      it { is_expected.to be_a(described_class::PathnamePath) }
++    end
++
++    context "when an regexp string is given" do
++      let(:pattern) { "^foo.*$" }
++      it { is_expected.to be_a(described_class::Matcher) }
++      it "shows a warning" do
++        expect(described_class::DeprecatedRegexp).
++          to receive(:show_deprecation).with(pattern)
++        subject
++      end
++    end
++
++    context "when a regexp is given" do
++      let(:pattern) { /foo\.rb/ }
++      it { is_expected.to be_a(described_class::Matcher) }
++    end
++
++    context "when a custom matcher" do
++      let(:pattern) { Class.new { def match; end } }
++      it { is_expected.to be_a(described_class::Matcher) }
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard/watcher_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard/watcher_spec.rb
+@@ -0,0 +1,351 @@
++require "guard/watcher"
++
++# TODO: shouldn't be needed
++require "guard/guardfile/evaluator"
++
++RSpec.describe Guard::Watcher do
++  let(:args) { [] }
++  subject { described_class.new(*args) }
++  describe "#initialize" do
++    context "with no arguments" do
++      let(:args) { [] }
++      it "raises an error" do
++        expect { subject }.to raise_error(ArgumentError)
++      end
++    end
++
++    context "with a pattern parameter" do
++      let(:pattern) { ["spec_helper.rb"] }
++      let(:args) { [pattern] }
++
++      it "creates a matcher" do
++        expect(described_class::Pattern).to receive(:create).with(pattern)
++        subject
++      end
++    end
++  end
++
++  describe "#action" do
++    it "sets the action to nothing by default" do
++      expect(described_class.new(/spec_helper\.rb/).action).to be_nil
++    end
++
++    it "sets the action to the supplied block" do
++      action = ->(m) { "spec/#{m[1]}_spec.rb" }
++      expect(described_class.new(%r{^lib/(.*).rb}, action).action).to eq action
++    end
++  end
++
++  describe ".match_files" do
++    let(:plugin) { instance_double("Guard::Plugin", options: {}) }
++
++    def matched(files)
++      described_class.match_files(plugin, files)
++    end
++
++    context "without a watcher action" do
++      before do
++        allow(plugin).to receive(:watchers).
++          and_return([described_class.new(pattern)])
++      end
++
++      context "with a regex pattern" do
++        let(:pattern) { /.*_spec\.rb/ }
++        it "returns the paths that matches the regex" do
++          expect(matched(%w(foo_spec.rb foo.rb))).to eq %w(foo_spec.rb)
++        end
++      end
++
++      context "with a string pattern" do
++        let(:pattern) { "foo_spec.rb" }
++        it "returns the path that matches the string" do
++          expect(matched(%w(foo_spec.rb foo.rb))).to eq ["foo_spec.rb"]
++        end
++      end
++    end
++
++    context "with a watcher action without parameter" do
++      context "for a watcher that matches file strings" do
++        before do
++          klass = described_class
++          allow(plugin).to receive(:watchers).and_return(
++            [
++              klass.new("spec_helper.rb", -> { "spec" }),
++              klass.new("addition.rb", -> { 1 + 1 }),
++              klass.new("hash.rb", -> { Hash[:foo, "bar"] }),
++              klass.new("array.rb", -> { %w(foo bar) }),
++              klass.new("blank.rb", -> { "" }),
++              klass.new(/^uptime\.rb/, -> { "" })
++            ]
++          )
++        end
++
++        it "returns a single file specified within the action" do
++          expect(matched(%w(spec_helper.rb))).to eq ["spec"]
++        end
++
++        it "returns multiple files specified within the action" do
++          expect(matched(%w(hash.rb))).to eq %w(foo bar)
++        end
++
++        it "combines files from results of different actions" do
++          expect(matched(%w(spec_helper.rb array.rb))).to eq %w(spec foo bar)
++        end
++
++        context "when action returns non-string or array of non-strings" do
++          it "returns nothing" do
++            expect(matched(%w(addition.rb))).to eq []
++          end
++        end
++
++        it "returns nothing if the action response is empty" do
++          expect(matched(%w(blank.rb))).to eq []
++        end
++
++        it "returns nothing if the action returns nothing" do
++          expect(matched(%w(uptime.rb))).to eq []
++        end
++      end
++
++      context "for a watcher that matches information objects" do
++        before do
++          allow(plugin).to receive(:options).and_return(any_return: true)
++
++          klass = described_class
++          allow(plugin).to receive(:watchers).and_return(
++            [
++              klass.new("spec_helper.rb", -> { "spec" }),
++              klass.new("addition.rb", -> { 1 + 1 }),
++              klass.new("hash.rb", -> { Hash[:foo, "bar"] }),
++              klass.new("array.rb", -> { %w(foo bar) }),
++              klass.new("blank.rb", -> { "" }),
++              klass.new(/^uptime\.rb/, -> { "" })
++            ]
++          )
++        end
++
++        it "returns a single file specified within the action" do
++          expect(matched(%w(spec_helper.rb)).class).to be Array
++          expect(matched(%w(spec_helper.rb))).to_not be_empty
++        end
++
++        it "returns multiple files specified within the action" do
++          expect(matched(%w(hash.rb))).to eq [{ foo: "bar" }]
++        end
++
++        it "combines the results of different actions" do
++          expect(matched(%w(spec_helper.rb array.rb))).
++            to eq ["spec", %w(foo bar)]
++        end
++
++        it "returns the evaluated addition argument in an array" do
++          expect(matched(%w(addition.rb)).class).to be(Array)
++          expect(matched(%w(addition.rb))[0]).to eq 2
++        end
++
++        it "returns nothing if the action response is empty string" do
++          expect(matched(%w(blank.rb))).to eq [""]
++        end
++
++        it "returns nothing if the action returns empty string" do
++          expect(matched(%w(uptime.rb))).to eq [""]
++        end
++      end
++    end
++
++    context "with a watcher action that takes a parameter" do
++      context "for a watcher that matches file strings" do
++        before do
++          klass = described_class
++          allow(plugin).to receive(:watchers).and_return [
++            klass.new(%r{lib/(.*)\.rb}, ->(m) { "spec/#{m[1]}_spec.rb" }),
++            klass.new(/addition(.*)\.rb/, ->(_m) { 1 + 1 }),
++            klass.new("hash.rb", ->(_m) { Hash[:foo, "bar"] }),
++            klass.new(/array(.*)\.rb/, ->(_m) { %w(foo bar) }),
++            klass.new(/blank(.*)\.rb/, ->(_m) { "" }),
++            klass.new(/^uptime\.rb/, -> { "" })
++          ]
++        end
++
++        it "returns a substituted single file specified within the action" do
++          expect(matched(%w(lib/foo.rb))).to eq ["spec/foo_spec.rb"]
++        end
++
++        it "returns multiple files specified within the action" do
++          expect(matched(%w(hash.rb))).to eq %w(foo bar)
++        end
++
++        it "combines results of different actions" do
++          expect(matched(%w(lib/foo.rb array.rb))).
++            to eq %w(spec/foo_spec.rb foo bar)
++        end
++
++        it "returns nothing if action returns non-string or non-string array" do
++          expect(matched(%w(addition.rb))).to eq []
++        end
++
++        it "returns nothing if the action response is empty" do
++          expect(matched(%w(blank.rb))).to eq []
++        end
++
++        it "returns nothing if the action returns nothing" do
++          expect(matched(%w(uptime.rb))).to eq []
++        end
++      end
++
++      context "for a watcher that matches information objects" do
++        before do
++          allow(plugin).to receive(:options).and_return(any_return: true)
++
++          kl = described_class
++          allow(plugin).to receive(:watchers).and_return(
++            [
++              kl.new(%r{lib/(.*)\.rb}, ->(m) { "spec/#{m[1]}_spec.rb" }),
++              kl.new(/addition(.*)\.rb/, ->(m) { (1 + 1).to_s + m[0] }),
++              kl.new("hash.rb", ->(m) { { foo: "bar", file_name: m[0] } }),
++              kl.new(/array(.*)\.rb/, ->(m) { ["foo", "bar", m[0]] }),
++              kl.new(/blank(.*)\.rb/, ->(_m) { "" }),
++              kl.new(/^uptime\.rb/, -> { "" })
++            ]
++          )
++        end
++
++        it "returns a substituted single file specified within the action" do
++          expect(matched(%w(lib/foo.rb))).to eq %w(spec/foo_spec.rb)
++        end
++
++        it "returns a hash specified within the action" do
++          expect(matched(%w(hash.rb))).to eq [
++            { foo: "bar", file_name: "hash.rb" }
++          ]
++        end
++
++        it "combinines results of different actions" do
++          expect(matched(%w(lib/foo.rb array.rb))).
++            to eq ["spec/foo_spec.rb", %w(foo bar array.rb)]
++        end
++
++        it "returns the evaluated addition argument + the path" do
++          expect(matched(%w(addition.rb))).to eq ["2addition.rb"]
++        end
++
++        it "returns nothing if the action response is empty string" do
++          expect(matched(%w(blank.rb))).to eq [""]
++        end
++
++        it "returns nothing if the action returns is IO::NULL" do
++          expect(matched(%w(uptime.rb))).to eq [""]
++        end
++      end
++    end
++
++    context "with an exception that is raised" do
++      before do
++        allow(plugin).to receive(:watchers).and_return(
++          [described_class.new("evil.rb", -> { fail "EVIL" })]
++        )
++      end
++
++      it "displays the error and backtrace" do
++        expect(Guard::UI).to receive(:error) do |msg|
++          expect(msg).to include("Problem with watch action!")
++          expect(msg).to include("EVIL")
++        end
++
++        described_class.match_files(plugin, ["evil.rb"])
++      end
++    end
++
++    context "for ambiguous watchers" do
++      before do
++        expect(plugin).to receive(:watchers).and_return [
++          described_class.new("awesome_helper.rb", -> {}),
++          described_class.new(/.+some_helper.rb/, -> { "foo.rb" }),
++          described_class.new(/.+_helper.rb/, -> { "bar.rb" }),
++        ]
++      end
++
++      context "when the :first_match option is turned off" do
++        before do
++          allow(plugin).to receive(:options).and_return(first_match: false)
++        end
++
++        it "returns multiple files by combining the results of the watchers" do
++          expect(described_class.match_files(
++                   plugin, ["awesome_helper.rb"]
++          )).to eq(["foo.rb", "bar.rb"])
++        end
++      end
++
++      context "when the :first_match option is turned on" do
++        before do
++          plugin.options[:first_match] = true
++        end
++
++        it "returns only the files from the first watcher" do
++          expect(described_class.match_files(
++                   plugin, ["awesome_helper.rb"]
++          )).to eq(["foo.rb"])
++        end
++      end
++    end
++  end
++
++  describe "#match" do
++    subject { described_class.new(pattern).match(file) }
++
++    let(:matcher) { instance_double(described_class::Pattern::Matcher) }
++    let(:match) { instance_double(described_class::Pattern::MatchResult) }
++
++    before do
++      allow(described_class::Pattern).to receive(:create).with(pattern).
++        and_return(matcher)
++
++      allow(matcher).to receive(:match).with(pattern).
++        and_return(match_data)
++
++      allow(described_class::Pattern::MatchResult).to receive(:new).
++        with(match_data, file).and_return(match)
++    end
++
++    context "with a valid pattern" do
++      let(:pattern) { "foo.rb" }
++      context "with a valid file name to match" do
++        let(:file) { "foo.rb" }
++        context "when matching is successful" do
++          let(:match_data) { double("match data", to_a: ["foo"]) }
++          it "returns the match result" do
++            expect(subject).to be(match)
++          end
++        end
++
++        context "when matching is not successful" do
++          let(:match_data) { nil }
++          it "returns nil" do
++            expect(subject).to be_nil
++          end
++        end
++      end
++    end
++  end
++
++  describe "integration" do
++    describe "#match" do
++      subject { described_class.new(pattern) }
++      context "with a named regexp pattern" do
++        let(:pattern) { /(?<foo>.*)_spec\.rb/ }
++
++        context "with a watcher that matches a file" do
++          specify do
++            expect(subject.match("bar_spec.rb")[0]).to eq("bar_spec.rb")
++            expect(subject.match("bar_spec.rb")[1]).to eq("bar")
++          end
++
++          it "provides the match by name" do
++            expect(subject.match("bar_spec.rb")[:foo]).to eq("bar")
++          end
++        end
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/lib/guard_spec.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/lib/guard_spec.rb
+@@ -0,0 +1,247 @@
++require "guard"
++
++RSpec.describe Guard do
++  # Initialize before Guard::Interactor const is stubbed
++  let!(:interactor) { instance_double("Guard::Interactor") }
++
++  let(:guardfile) { File.expand_path("Guardfile") }
++  let(:traps) { Guard::Internals::Traps }
++
++  let(:evaluator) { instance_double("Guard::Guardfile::Evaluator") }
++
++  let(:plugins) { instance_double("Guard::Internals::Plugins") }
++  let(:scope) { instance_double("Guard::Internals::Scope") }
++  let(:session) { instance_double("Guard::Internals::Session") }
++  let(:state) { instance_double("Guard::Internals::State") }
++  let(:queue) { instance_double("Guard::Internals::Queue") }
++
++  before do
++    allow(Guard::Interactor).to receive(:new).and_return(interactor)
++    allow(Guard::Guardfile::Evaluator).to receive(:new).and_return(evaluator)
++
++    allow(session).to receive(:debug?).and_return(false)
++    allow(session).to receive(:plugins).and_return(plugins)
++    allow(state).to receive(:session).and_return(session)
++    allow(Guard::Internals::Session).to receive(:new).and_return(session)
++    allow(Guard::Internals::Scope).to receive(:new).and_return(scope)
++    allow(Guard::Internals::Queue).to receive(:new).and_return(queue)
++  end
++
++  # TODO: setup has too many responsibilities
++  describe ".setup" do
++    subject { Guard.setup(options) }
++
++    let(:options) { { my_opts: true, guardfile: guardfile } }
++
++    let(:listener) { instance_double("Listen::Listener") }
++
++    before do
++      allow(Listen).to receive(:to).with(Dir.pwd, {}) { listener }
++
++      stub_guardfile(" ")
++      stub_user_guard_rb
++
++      g1 = instance_double("Guard::Group", name: :common, options: {})
++      g2 = instance_double("Guard::Group", name: :default, options: {})
++      allow(Guard::Group).to receive(:new).with(:common).and_return(g1)
++      allow(Guard::Group).to receive(:new).with(:default).and_return(g2)
++
++      allow(evaluator).to receive(:inline?).and_return(false)
++      allow(evaluator).to receive(:custom?).and_return(false)
++      allow(evaluator).to receive(:evaluate)
++      allow(Guard::Guardfile::Evaluator).to receive(:new).and_return(evaluator)
++
++      allow(Guard::Notifier).to receive(:connect)
++
++      allow(Guard::UI).to receive(:reset_and_clear)
++      allow(plugins).to receive(:all).and_return([])
++
++      allow(session).to receive(:listener_args).and_return([:to, Dir.pwd, {}])
++      allow(session).to receive(:evaluator_options).and_return({})
++      allow(session).to receive(:cmdline_groups).and_return({})
++      allow(session).to receive(:cmdline_plugins).and_return({})
++      allow(session).to receive(:notify_options).and_return(notify: true)
++      allow(session).to receive(:interactor_name).and_return(:foo)
++      allow(session).to receive(:guardfile_ignore).and_return([])
++      allow(session).to receive(:guardfile_ignore_bang).and_return([])
++
++      allow(listener).to receive(:ignore)
++      allow(listener).to receive(:ignore!)
++
++      allow(Guard::Internals::State).to receive(:new).and_return(state)
++    end
++
++    it "returns itself for chaining" do
++      expect(subject).to be Guard
++    end
++
++    it "initializes the listener" do
++      allow(Listen).to receive(:to).
++        with("/foo", latency: 2, wait_for_delay: 1).and_return(listener)
++
++      allow(session).to receive(:listener_args).and_return(
++        [:to, "/foo", { latency: 2, wait_for_delay: 1 }]
++      )
++      subject
++    end
++
++    it "initializes the interactor" do
++      expect(Guard::Interactor).to receive(:new).with(false)
++      subject
++    end
++
++    context "trapping signals" do
++      before do
++        allow(traps).to receive(:handle)
++      end
++
++      it "sets up USR1 trap for pausing" do
++        expect(traps).to receive(:handle).with("USR1") { |_, &b| b.call }
++        expect(Guard).to receive(:async_queue_add).
++          with([:guard_pause, :paused])
++        subject
++      end
++
++      it "sets up USR2 trap for unpausing" do
++        expect(traps).to receive(:handle).with("USR2") { |_, &b| b.call }
++        expect(Guard).to receive(:async_queue_add).
++          with([:guard_pause, :unpaused])
++        subject
++      end
++
++      it "sets up INT trap for cancelling or quitting interactor" do
++        expect(traps).to receive(:handle).with("INT") { |_, &b| b.call }
++        expect(interactor).to receive(:handle_interrupt)
++        subject
++      end
++    end
++
++    it "evaluates the Guardfile" do
++      expect(evaluator).to receive(:evaluate)
++      allow(Guard::Guardfile::Evaluator).to receive(:new).and_return(evaluator)
++
++      subject
++    end
++
++    describe "listener" do
++      subject { listener }
++
++      context "with ignores 'ignore(/foo/)' and 'ignore!(/bar/)'" do
++        before do
++          allow(evaluator).to receive(:evaluate) do
++            allow(session).to receive(:guardfile_ignore).and_return([/foo/])
++            allow(session).to receive(:guardfile_ignore_bang).
++              and_return([/bar/])
++          end
++          Guard.setup(options)
++        end
++
++        it { is_expected.to have_received(:ignore).with([/foo/]) }
++        it { is_expected.to have_received(:ignore!).with([/bar/]) }
++      end
++
++      context "without ignores" do
++        before { Guard.setup(options) }
++        it { is_expected.to_not have_received(:ignore) }
++        it { is_expected.to_not have_received(:ignore!) }
++      end
++    end
++
++    it "displays an error message when no guard are defined in Guardfile" do
++      expect(Guard::UI).to receive(:error).
++        with("No plugins found in Guardfile, please add at least one.")
++
++      subject
++    end
++
++    it "connects to the notifier" do
++      expect(Guard::Notifier).to receive(:connect).with(notify: true)
++      subject
++    end
++
++    context "with the group option" do
++      let(:options) { { group: %w(frontend backend) } }
++      it "passes options to session" do
++        expect(Guard::Internals::State).to receive(:new).with(options)
++        subject
++      end
++    end
++
++    context "with the plugin option" do
++      let(:options) { { plugin: %w(cucumber jasmine) } }
++      it "passes options to session" do
++        expect(Guard::Internals::State).to receive(:new).with(options)
++        subject
++      end
++    end
++
++    describe ".interactor" do
++      subject { Guard::Interactor }
++
++      before do
++        expect(session).to receive(:interactor_name).and_return(type)
++        Guard.setup(options)
++      end
++
++      context "with interactions enabled" do
++        let(:type) { :pry_wrapper }
++        let(:options) { { no_interactions: false } }
++        it { is_expected.to have_received(:new).with(false) }
++      end
++
++      context "with interactions disabled" do
++        let(:type) { :sleep }
++        let(:options) { { no_interactions: true } }
++        it { is_expected.to have_received(:new).with(true) }
++      end
++    end
++
++    describe "UI" do
++      subject { Guard::UI }
++
++      context "when clearing is configured" do
++        before { Guard.setup(options) }
++        it { is_expected.to have_received(:reset_and_clear) }
++      end
++    end
++  end
++
++  describe "._relative_pathname" do
++    subject { Guard.send(:_relative_pathname, raw_path) }
++
++    let(:pwd) { Pathname("/project") }
++
++    before { allow(Pathname).to receive(:pwd).and_return(pwd) }
++
++    context "with file in project directory" do
++      let(:raw_path) { "/project/foo" }
++      it { is_expected.to eq(Pathname("foo")) }
++    end
++
++    context "with file within project" do
++      let(:raw_path) { "/project/spec/models/foo_spec.rb" }
++      it { is_expected.to eq(Pathname("spec/models/foo_spec.rb")) }
++    end
++
++    context "with file in parent directory" do
++      let(:raw_path) { "/foo" }
++      it { is_expected.to eq(Pathname("../foo")) }
++    end
++
++    context "with file on another drive (e.g. Windows)" do
++      let(:raw_path) { "d:/project/foo" }
++      let(:pathname) { instance_double(Pathname) }
++
++      before do
++        allow_any_instance_of(Pathname).to receive(:relative_path_from).
++          with(pwd).and_raise(ArgumentError)
++      end
++
++      it { is_expected.to eq(Pathname.new("d:/project/foo")) }
++    end
++  end
++
++  describe "#relevant_changes?" do
++    pending
++  end
++end
+Index: ruby-guard/spec/spec_helper.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/spec_helper.rb
+@@ -0,0 +1,290 @@
++# This file was generated by the `rspec --init` command. Conventionally, all
++# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
++# The generated `.rspec` file contains `--require spec_helper` which will cause
++# this file to always be loaded, without a need to explicitly require it in any
++# files.
++#
++# Given that it is always loaded, you are encouraged to keep this file as
++# light-weight as possible. Requiring heavyweight dependencies from this file
++# will add to the boot time of your test suite on EVERY test run, even for an
++# individual file that may not need all of that loaded. Instead, consider making
++# a separate helper file that requires the additional dependencies and performs
++# the additional setup, and require it from the spec files that actually need
++# it.
++#
++# The `.rspec` file also contains a few flags that are not defaults but that
++# users commonly want.
++#
++
++require "fileutils"
++
++require "simplecov"
++SimpleCov.start
++
++ENV["GUARD_SPECS_RUNNING"] = "1"
++
++path = "#{File.expand_path('..', __FILE__)}/support/**/*.rb"
++Dir[path].each { |f| require f }
++
++# TODO: these shouldn't be necessary with proper specs
++
++def stub_guardfile(contents = nil, &block)
++  stub_file(File.expand_path("Guardfile"), contents, &block)
++end
++
++def stub_user_guardfile(contents = nil, &block)
++  stub_file(File.expand_path("~/.Guardfile"), contents, &block)
++end
++
++def stub_user_guard_rb(contents = nil, &block)
++  stub_file(File.expand_path("~/.guard.rb"), contents, &block)
++end
++
++def stub_user_project_guardfile(contents = nil, &block)
++  stub_file(File.expand_path(".Guardfile"), contents, &block)
++end
++
++def stub_mod(mod, excluded)
++  mod.constants.each do |klass_name|
++    klass = mod.const_get(klass_name)
++    if klass.is_a?(Class)
++      unless klass == described_class
++        unless excluded.include?(klass)
++          inst = instance_double(klass)
++          allow(klass).to receive(:new).and_return(inst)
++          # TODO: use object_double?
++          class_double(klass.to_s).
++            as_stubbed_const(transfer_nested_constants: true)
++        end
++      end
++    elsif klass.is_a?(Module)
++      stub_mod(klass, excluded)
++    end
++  end
++end
++
++def stub_file(path, contents = nil, &block)
++  exists = !contents.nil?
++  allow(File).to receive(:exist?).with(path).and_return(exists)
++  if exists
++    if block.nil?
++      allow(IO).to receive(:read).with(path).and_return(contents)
++    else
++      allow(IO).to receive(:read).with(path) do
++        yield
++      end
++    end
++  else
++    allow(IO).to receive(:read).with(path) do
++      fail Errno::ENOENT
++    end
++  end
++end
++
++# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
++RSpec.configure do |config|
++  # rspec-expectations config goes here. You can use an alternate
++  # assertion/expectation library such as wrong or the stdlib/minitest
++  # assertions if you prefer.
++  config.expect_with :rspec do |expectations|
++    # This option will default to `true` in RSpec 4. It makes the `description`
++    # and `failure_message` of custom matchers include text for helper methods
++    # defined using `chain`, e.g.:
++    # be_bigger_than(2).and_smaller_than(4).description
++    #   # => "be bigger than 2 and smaller than 4"
++    # ...rather than:
++    #   # => "be bigger than 2"
++    expectations.include_chain_clauses_in_custom_matcher_descriptions = true
++  end
++
++  # rspec-mocks config goes here. You can use an alternate test double
++  # library (such as bogus or mocha) by changing the `mock_with` option here.
++  config.mock_with :rspec do |mocks|
++    # Prevents you from mocking or stubbing a method that does not exist on
++    # a real object. This is generally recommended, and will default to
++    # `true` in RSpec 4.
++    mocks.verify_partial_doubles = true
++  end
++
++  # The settings below are suggested to provide a good initial experience
++  # with RSpec, but feel free to customize to your heart's content.
++
++  # These two settings work together to allow you to limit a spec run
++  # to individual examples or groups you care about by tagging them with
++  # `:focus` metadata. When nothing is tagged with `:focus`, all examples
++  # get run.
++  # config.filter_run :focus
++  config.filter_run focus: ENV["CI"] != "true"
++
++  config.run_all_when_everything_filtered = true
++
++  # Limits the available syntax to the non-monkey patched syntax that is
++  # recommended.
++  #
++  # For more details, see:
++  #   - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
++  #   - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
++  #   - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
++  config.disable_monkey_patching!
++
++  # This setting enables warnings. It's recommended, but in some cases may
++  # be too noisy due to issues in dependencies.
++  # config.warnings = true
++
++  # Many RSpec users commonly either run the entire suite or an individual
++  # file, and it's useful to allow more verbose output when running an
++  # individual spec file.
++  if config.files_to_run.one?
++    # Use the documentation formatter for detailed output,
++    # unless a formatter has already been configured
++    # (e.g. via a command-line flag).
++    #
++    # This is set in .rspec file
++    # config.default_formatter = "doc"
++  end
++
++  # Print the 10 slowest examples and example groups at the
++  # end of the spec run, to help surface which specs are running
++  # particularly slow.
++  # config.profile_examples = 10
++
++  # Run specs in random order to surface order dependencies. If you find an
++  # order dependency and want to debug it, you can fix the order by providing
++  # the seed, which is printed after each run.
++  #     --seed 1234
++  config.order = :random
++
++  # Seed global randomization in this process using the `--seed` CLI option.
++  # Setting this allows you to use `--seed` to deterministically reproduce
++  # test failures related to randomization by passing the same `--seed` value
++  # as the one that triggered the failure.
++  Kernel.srand config.seed
++
++  config.raise_errors_for_deprecations!
++
++  config.mock_with :rspec do |mocks|
++    mocks.verify_doubled_constant_names = true
++    mocks.verify_partial_doubles = true
++  end
++
++  config.before(:each) do |example|
++    stub_const("FileUtils", class_double(FileUtils))
++
++    excluded = []
++    excluded += Array(example.metadata[:exclude_stubs])
++    excluded << Guard::Config if Guard.constants.include?(:Config)
++    excluded << Guard::Options if Guard.constants.include?(:Options)
++    excluded << Guard::Jobs::Base if Guard.constants.include?(:Jobs)
++
++    excluded << Guard::DuMmy if Guard.constants.include?(:DuMmy)
++
++    if Guard.constants.include?(:Notifier)
++      if Guard::Notifier.constants.include?(:NotServer)
++        excluded << Guard::Notifier::NotServer
++      end
++      if Guard::Notifier.constants.include?(:FooBar)
++        excluded << Guard::Notifier::FooBar
++      end
++      if Guard::Notifier.constants.include?(:Base)
++        excluded << Guard::Notifier::Base
++      end
++    end
++
++    modules = [Guard]
++    modules << Listen if Object.const_defined?(:Listen)
++    modules << Shellany if Object.const_defined?(:Shellany)
++    modules << Notiffany if Object.const_defined?(:Notiffany)
++    modules.each do |mod|
++      stub_mod(mod, excluded)
++    end
++
++    allow(ENV).to receive(:[]=) do |*args|
++      abort "stub me: ENV[#{args.first}]= #{args.map(&:inspect)[1..-1] * ','}!"
++    end
++
++    allow(ENV).to receive(:[]) do |*args|
++      abort "stub me: ENV[#{args.first}]!"
++    end
++
++    allow(ENV).to receive(:key?) do |*args|
++      fail "stub me: ENV.key?(#{args.first})!"
++    end
++
++    # NOTE: call original, so we can run tests depending on this variable
++    allow(ENV).to receive(:[]).with("GUARD_STRICT").and_call_original
++
++    # FIXME: instead, properly stub PluginUtil in the evaluator specs!
++    # and remove this!
++    allow(ENV).to receive(:[]).with("SPEC_OPTS").and_call_original
++
++    # FIXME: properly stub out Pry instead of this!
++    allow(ENV).to receive(:[]).with("ANSICON").and_call_original
++    allow(ENV).to receive(:[]).with("TERM").and_call_original
++
++    # Needed for debugging
++    allow(ENV).to receive(:[]).with("DISABLE_PRY").and_call_original
++    allow(ENV).to receive(:[]).with("PRYRC").and_call_original
++    allow(ENV).to receive(:[]).with("PAGER").and_call_original
++
++    # Workarounds for Cli inheriting from Thor
++    allow(ENV).to receive(:[]).with("ANSICON").and_call_original
++    allow(ENV).to receive(:[]).with("THOR_SHELL").and_call_original
++    allow(ENV).to receive(:[]).with("GEM_SKIP").and_call_original
++
++    %w(read write exist?).each do |meth|
++      allow(File).to receive(meth.to_sym).with(anything) do |*args, &_block|
++        abort "stub me! (File.#{meth}(#{args.map(&:inspect).join(', ')}))"
++      end
++    end
++
++    %w(read write binwrite binread).each do |meth|
++      allow(IO).to receive(meth.to_sym).with(anything) do |*args, &_block|
++        abort "stub me! (IO.#{meth}(#{args.map(&:inspect).join(', ')}))"
++      end
++    end
++
++    %w(exist?).each do |meth|
++      allow_any_instance_of(Pathname).
++        to receive(meth.to_sym) do |*args, &_block|
++        obj = args.first
++        formatted_args = args[1..-1].map(&:inspect).join(", ")
++        abort "stub me! (#{obj.inspect}##{meth}(#{formatted_args}))"
++      end
++    end
++
++    allow(Dir).to receive(:exist?).with(anything) do |*args, &_block|
++      abort "stub me! (Dir#exist?(#{args.map(&:inspect) * ', '}))"
++    end
++
++    if Guard.const_defined?("UI") && Guard::UI.respond_to?(:info)
++      # Stub all UI methods, so no visible output appears for the UI class
++      allow(Guard::UI).to receive(:info)
++      allow(Guard::UI).to receive(:warning)
++      allow(Guard::UI).to receive(:error)
++      allow(Guard::UI).to receive(:debug)
++      allow(Guard::UI).to receive(:deprecation)
++    end
++
++    allow(Kernel).to receive(:system) do |*args|
++      fail "stub for Kernel.system() called with: #{args.inspect}"
++    end
++
++    # TODO: use metadata to stub out all used classes
++    if Guard.const_defined?("Sheller")
++      unless example.metadata[:sheller_specs]
++        allow(Guard::Sheller).to receive(:run) do |*args|
++          fail "stub for Sheller.run() called with: #{args.inspect}"
++        end
++      end
++    end
++  end
++
++  config.after(:each) do
++    # Reset everything
++    (Guard.constants + [Guard]).each do |klass|
++      klass.instance_variables.each do |var|
++        klass.instance_variable_set(var, nil)
++      end
++    end
++  end
++end
+Index: ruby-guard/spec/support/gems_helper.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/support/gems_helper.rb
+@@ -0,0 +1,20 @@
++def growl_installed?
++  require "growl"
++  true
++rescue LoadError
++  false
++end
++
++def libnotify_installed?
++  require "libnotify"
++  true
++rescue LoadError
++  false
++end
++
++def rbnotifu_installed?
++  require "rb-notifu"
++  true
++rescue LoadError
++  false
++end
+Index: ruby-guard/spec/support/platform_helper.rb
+===================================================================
+--- /dev/null
++++ ruby-guard/spec/support/platform_helper.rb
+@@ -0,0 +1,11 @@
++def mac?
++  RbConfig::CONFIG["target_os"] =~ /darwin/i
++end
++
++def linux?
++  RbConfig::CONFIG["target_os"] =~ /linux/i
++end
++
++def windows?
++  RbConfig::CONFIG["target_os"] =~ /mswin|mingw/i
++end
diff --git a/debian/patches/disable_simplecov.patch b/debian/patches/disable_simplecov.patch
new file mode 100644
index 0000000..1018244
--- /dev/null
+++ b/debian/patches/disable_simplecov.patch
@@ -0,0 +1,19 @@
+Description: disable simplecov
+Author: HIGUCHI Daisuke (VDR dai) <dai at debian.org>
+Forwarded: not-needed
+Last-Update: 2017-11-28
+
+Index: ruby-guard/spec/spec_helper.rb
+===================================================================
+--- ruby-guard.orig/spec/spec_helper.rb
++++ ruby-guard/spec/spec_helper.rb
+@@ -18,9 +18,6 @@
+ 
+ require "fileutils"
+ 
+-require "simplecov"
+-SimpleCov.start
+-
+ ENV["GUARD_SPECS_RUNNING"] = "1"
+ 
+ path = "#{File.expand_path('..', __FILE__)}/support/**/*.rb"
diff --git a/debian/patches/pending_examples.patch b/debian/patches/pending_examples.patch
new file mode 100644
index 0000000..840c009
--- /dev/null
+++ b/debian/patches/pending_examples.patch
@@ -0,0 +1,27 @@
+Description: pending examples
+Author: HIGUCHI Daisuke (VDR dai) <dai at debian.org>
+Forwarded: not-needed
+Last-Update: 2017-11-28
+
+Index: ruby-guard/spec/lib/guard/plugin_util_spec.rb
+===================================================================
+--- ruby-guard.orig/spec/lib/guard/plugin_util_spec.rb
++++ ruby-guard/spec/lib/guard/plugin_util_spec.rb
+@@ -16,7 +16,7 @@ RSpec.describe Guard::PluginUtil do
+     allow(Guard::Guardfile::Evaluator).to receive(:new).and_return(evaluator)
+   end
+ 
+-  describe ".plugin_names" do
++  xdescribe ".plugin_names" do
+     before do
+       spec = Gem::Specification
+       gems = [
+@@ -224,7 +224,7 @@ RSpec.describe Guard::PluginUtil do
+       allow(evaluator).to receive(:evaluate)
+     end
+ 
+-    context "when the Guard is already in the Guardfile" do
++    xcontext "when the Guard is already in the Guardfile" do
+       before do
+         allow(evaluator).to receive(:guardfile_include?) { true }
+       end
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..ac1efd7
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,4 @@
+do_not_use_gem_bin_path.patch
+add_specs.patch
+disable_simplecov.patch
+pending_examples.patch
diff --git a/debian/ruby-tests.rake b/debian/ruby-tests.rake
new file mode 100644
index 0000000..6e530f3
--- /dev/null
+++ b/debian/ruby-tests.rake
@@ -0,0 +1,7 @@
+require "rspec/core/rake_task"
+
+RSpec::Core::RakeTask.new(:spec) do |t|
+  t.rspec_opts = "-I spec -r spec_helper -f d --no-fail-fast"
+end
+
+task :default => :spec
diff --git a/debian/rules b/debian/rules
index 3454d59..7ab40e2 100755
--- a/debian/rules
+++ b/debian/rules
@@ -4,3 +4,6 @@ export GEM2DEB_TEST_RUNNER = --check-dependencies
 
 %:
 	dh $@ --buildsystem=ruby --with ruby
+
+override_dh_auto_install:
+	PATH="$(CURDIR)/bin:$$PATH" dh_auto_install

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-guard.git



More information about the Pkg-ruby-extras-commits mailing list