diff --git a/Library/Homebrew/brew.sh b/Library/Homebrew/brew.sh
index 81a52f474402b275054cf45ea456c0184a1274c7..11c1a6c5948dbeab7ff39673fa7c7fd326161002 100644
--- a/Library/Homebrew/brew.sh
+++ b/Library/Homebrew/brew.sh
@@ -207,6 +207,7 @@ case "$HOMEBREW_COMMAND" in
   up)          HOMEBREW_COMMAND="update" ;;
   ln)          HOMEBREW_COMMAND="link" ;;
   instal)      HOMEBREW_COMMAND="install" ;; # gem does the same
+  uninstal)    HOMEBREW_COMMAND="uninstall" ;;
   rm)          HOMEBREW_COMMAND="uninstall" ;;
   remove)      HOMEBREW_COMMAND="uninstall" ;;
   configure)   HOMEBREW_COMMAND="diy" ;;
diff --git a/Library/Homebrew/cask/lib/hbc/cli.rb b/Library/Homebrew/cask/lib/hbc/cli.rb
index 73ab121d2dbf0f9c8855ae9cc9f9d9d88c7d20cb..8e178e3737e7890e2da2004c65ac82785e313cfc 100644
--- a/Library/Homebrew/cask/lib/hbc/cli.rb
+++ b/Library/Homebrew/cask/lib/hbc/cli.rb
@@ -37,6 +37,7 @@ module Hbc
       "-S"       => "search",    # verb starting with "-" is questionable
       "up"       => "update",
       "instal"   => "install",   # gem does the same
+      "uninstal" => "uninstall",
       "rm"       => "uninstall",
       "remove"   => "uninstall",
       "abv"      => "info",
diff --git a/Library/Homebrew/cask/lib/hbc/macos.rb b/Library/Homebrew/cask/lib/hbc/macos.rb
index 5c1515d0c7bdd93939dacca54328285f612e73ea..d47e04fb246f7424c02b106f9483fb92077f6cc7 100644
--- a/Library/Homebrew/cask/lib/hbc/macos.rb
+++ b/Library/Homebrew/cask/lib/hbc/macos.rb
@@ -181,6 +181,7 @@ module OS
       "/usr/include",
       "/usr/lib",
       "/usr/libexec",
+      "/usr/libexec/cups",
       "/usr/local",
       "/usr/local/Cellar",
       "/usr/local/Frameworks",
diff --git a/Library/Homebrew/dev-cmd/bump-formula-pr.rb b/Library/Homebrew/dev-cmd/bump-formula-pr.rb
index ea2daf1c42565507b4c40e6cdb0b18af9c89a1b0..bfe9c777674112b77dd04b2d9f354d5dbc80d7ed 100644
--- a/Library/Homebrew/dev-cmd/bump-formula-pr.rb
+++ b/Library/Homebrew/dev-cmd/bump-formula-pr.rb
@@ -80,7 +80,8 @@ module Homebrew
 
   def fetch_pull_requests(formula)
     GitHub.issues_for_formula(formula.name, tap: formula.tap).select do |pr|
-      pr["html_url"].include?("/pull/")
+      pr["html_url"].include?("/pull/") &&
+        /(^|\s)#{Regexp.quote(formula.name)}(:|\s|$)/i =~ pr["title"]
     end
   rescue GitHub::RateLimitExceededError => e
     opoo e.message
diff --git a/Library/Homebrew/dev-cmd/tests.rb b/Library/Homebrew/dev-cmd/tests.rb
index 244fbe0270cb9b3ae86b859f49851ab39420d6f4..6276d1c1b7f22e945f6f8764f439a61262a3632a 100644
--- a/Library/Homebrew/dev-cmd/tests.rb
+++ b/Library/Homebrew/dev-cmd/tests.rb
@@ -57,9 +57,7 @@ module Homebrew
       ENV["SEED"] = ARGV.next if ARGV.include? "--seed"
 
       files = Dir.glob("test/**/*_{spec,test}.rb")
-                 .reject { |p| !OS.mac? && p.start_with?("test/os/mac/") }
-                 .reject { |p| !OS.mac? && p.start_with?("test/cask/") }
-                 .reject { |p| p.start_with?("test/vendor/bundle/") }
+                 .reject { |p| !OS.mac? && p =~ %r{^test/(os/mac|cask)(/.*|_(test|spec)\.rb)$} }
 
       test_args = []
       test_args << "--trace" if ARGV.include? "--trace"
@@ -84,6 +82,8 @@ module Homebrew
         "--format", "ParallelTests::RSpec::RuntimeLogger",
         "--out", "tmp/parallel_runtime_rspec.log"
       ]
+      spec_args << "--tag" << "~needs_macos" unless OS.mac?
+
       run_tests "parallel_rspec", spec_files, spec_args
 
       if (fs_leak_log = HOMEBREW_LIBRARY_PATH/"tmp/fs_leak.log").file?
diff --git a/Library/Homebrew/extend/ENV/shared.rb b/Library/Homebrew/extend/ENV/shared.rb
index 7b468574ad30a689fafa723c5ec622faf8870fe6..3b07e0a71ae87e661fc4ced04bd8e16378695585 100644
--- a/Library/Homebrew/extend/ENV/shared.rb
+++ b/Library/Homebrew/extend/ENV/shared.rb
@@ -269,7 +269,7 @@ module SharedEnvExtension
   # @private
   def gcc_version_formula(name)
     version = name[GNU_GCC_REGEXP, 1]
-    gcc_version_name = "gcc#{version.delete(".")}"
+    gcc_version_name = "gcc@#{version}"
 
     gcc = Formulary.factory("gcc")
     if gcc.version_suffix == version
@@ -286,7 +286,6 @@ module SharedEnvExtension
     rescue FormulaUnavailableError => e
       raise <<-EOS.undent
       Homebrew GCC requested, but formula #{e.name} not found!
-      You may need to: brew tap homebrew/versions
       EOS
     end
 
diff --git a/Library/Homebrew/extend/os/mac/keg_relocate.rb b/Library/Homebrew/extend/os/mac/keg_relocate.rb
index f44a97b31d0eab0eb1aa7daf120c658ef0b0ebd1..476e5da4a472d107d8ef1fecfe4e5123dfff9245 100644
--- a/Library/Homebrew/extend/os/mac/keg_relocate.rb
+++ b/Library/Homebrew/extend/os/mac/keg_relocate.rb
@@ -78,13 +78,19 @@ class Keg
     end
   end
 
+  def filename_contains_metavariable?(fn)
+    fn =~ /^@(loader_|executable_|r)path/
+  end
+
   def each_install_name_for(file, &block)
     dylibs = file.dynamically_linked_libraries
-    dylibs.reject! { |fn| fn =~ /^@(loader_|executable_|r)path/ }
+    dylibs.reject! { |fn| filename_contains_metavariable?(fn) }
     dylibs.each(&block)
   end
 
   def dylib_id_for(file)
+    return file.dylib_id if filename_contains_metavariable?(file.dylib_id)
+
     # The new dylib ID should have the same basename as the old dylib ID, not
     # the basename of the file itself.
     basename = File.basename(file.dylib_id)
diff --git a/Library/Homebrew/extend/os/mac/requirements/java_requirement.rb b/Library/Homebrew/extend/os/mac/requirements/java_requirement.rb
index 451e0001b5efefdf66e00c867333ddc59b7d81cf..5f989bb56e0b45a6a3a5370b2f06df41931502e8 100644
--- a/Library/Homebrew/extend/os/mac/requirements/java_requirement.rb
+++ b/Library/Homebrew/extend/os/mac/requirements/java_requirement.rb
@@ -1,4 +1,4 @@
-class JavaRequirement
+class JavaRequirement < Requirement
   cask "java"
 
   env do
diff --git a/Library/Homebrew/formula_versions.rb b/Library/Homebrew/formula_versions.rb
index 34b766fde1a5156d9248849e15a9edb5be05a613..28c2a3be85fd37f4530ac75a80301cf47befe201 100644
--- a/Library/Homebrew/formula_versions.rb
+++ b/Library/Homebrew/formula_versions.rb
@@ -15,7 +15,6 @@ class FormulaVersions
     @repository = formula.tap.path
     @entry_name = @path.relative_path_from(repository).to_s
     @max_depth = options[:max_depth]
-    @current_formula = formula
   end
 
   def rev_list(branch)
@@ -65,33 +64,25 @@ class FormulaVersions
 
     attributes.each do |attribute|
       attributes_map[attribute] ||= {}
-      # Set the attributes for the current formula in case it's not been
-      # committed yet.
-      set_attribute_map(attributes_map[attribute], @current_formula, attribute)
     end
 
     rev_list(branch) do |rev|
       formula_at_revision(rev) do |f|
         attributes.each do |attribute|
-          set_attribute_map(attributes_map[attribute], f, attribute)
+          map = attributes_map[attribute]
+          if f.stable
+            map[:stable] ||= {}
+            map[:stable][f.stable.version] ||= []
+            map[:stable][f.stable.version] << f.send(attribute)
+          end
+          next unless f.devel
+          map[:devel] ||= {}
+          map[:devel][f.devel.version] ||= []
+          map[:devel][f.devel.version] << f.send(attribute)
         end
       end
     end
 
     attributes_map
   end
-
-  private
-
-  def set_attribute_map(map, f, attribute)
-    if f.stable
-      map[:stable] ||= {}
-      map[:stable][f.stable.version] ||= []
-      map[:stable][f.stable.version] << f.send(attribute)
-    end
-    return unless f.devel
-    map[:devel] ||= {}
-    map[:devel][f.devel.version] ||= []
-    map[:devel][f.devel.version] << f.send(attribute)
-  end
 end
diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb
index cf85ba03f61db1dec58ce3dce1016f18e1be2c8d..4a65f87044e9332918bf557fe24828579db28c65 100644
--- a/Library/Homebrew/formulary.rb
+++ b/Library/Homebrew/formulary.rb
@@ -4,7 +4,7 @@ require "tap"
 # The Formulary is responsible for creating instances of Formula.
 # It is not meant to be used directly from formulae.
 
-class Formulary
+module Formulary
   FORMULAE = {}
 
   def self.formula_class_defined?(path)
diff --git a/Library/Homebrew/global.rb b/Library/Homebrew/global.rb
index 5e88947c9343eda38bd5d062cc82df898678ae3c..391f5b0121fda46d2583ecb31dc379f0b497d006 100644
--- a/Library/Homebrew/global.rb
+++ b/Library/Homebrew/global.rb
@@ -69,6 +69,7 @@ HOMEBREW_INTERNAL_COMMAND_ALIASES = {
   "up" => "update",
   "ln" => "link",
   "instal" => "install", # gem does the same
+  "uninstal" => "uninstall",
   "rm" => "uninstall",
   "remove" => "uninstall",
   "configure" => "diy",
diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb
index 17ff3bcef8fecb19cbe4c8209f005568b8f13dfd..94e3ff55bf5ff5978b111087d4c9515a254ef49b 100644
--- a/Library/Homebrew/keg.rb
+++ b/Library/Homebrew/keg.rb
@@ -239,6 +239,10 @@ class Keg
 
   def remove_opt_record
     opt_record.unlink
+    aliases.each do |a|
+      next if !opt_record.symlink? && !opt_record.exist?
+      (opt_record.parent/a).delete
+    end
     opt_record.parent.rmdir_if_possible
   end
 
@@ -461,9 +465,20 @@ class Keg
     @oldname_opt_record = nil
   end
 
+  def aliases
+    Formula[rack.basename.to_s].aliases
+  rescue FormulaUnavailableError
+    []
+  end
+
   def optlink(mode = OpenStruct.new)
     opt_record.delete if opt_record.symlink? || opt_record.exist?
     make_relative_symlink(opt_record, path, mode)
+    aliases.each do |a|
+      alias_opt_record = opt_record.parent/a
+      alias_opt_record.delete if alias_opt_record.symlink? || alias_opt_record.exist?
+      make_relative_symlink(alias_opt_record, path, mode)
+    end
 
     return unless oldname_opt_record
     oldname_opt_record.delete
diff --git a/Library/Homebrew/requirements/ruby_requirement.rb b/Library/Homebrew/requirements/ruby_requirement.rb
index a890435a58389414924c7c9b28704b4ce90124e4..327c131708f459d2ba4db84dffe6a3f7a679f604 100644
--- a/Library/Homebrew/requirements/ruby_requirement.rb
+++ b/Library/Homebrew/requirements/ruby_requirement.rb
@@ -8,16 +8,14 @@ class RubyRequirement < Requirement
     super
   end
 
-  satisfy build_env: false do
-    which_all("ruby").detect do |ruby|
-      version = /\d\.\d/.match Utils.popen_read(ruby, "--version")
-      next unless version
-      Version.create(version.to_s) >= Version.create(@version)
-    end
+  satisfy(build_env: false) { new_enough_ruby }
+
+  env do
+    ENV.prepend_path "PATH", new_enough_ruby
   end
 
   def message
-    s = "Ruby #{@version} is required to install this formula."
+    s = "Ruby >= #{@version} is required to install this formula."
     s += super
     s
   end
@@ -33,4 +31,28 @@ class RubyRequirement < Requirement
       name
     end
   end
+
+  private
+
+  def new_enough_ruby
+    rubies.detect { |ruby| new_enough?(ruby) }
+  end
+
+  def rubies
+    rubies = which_all("ruby")
+    ruby_formula = Formula["ruby"]
+    if ruby_formula && ruby_formula.installed?
+      rubies.unshift ruby_formula.bin/"ruby"
+    end
+    rubies.uniq
+  end
+
+  def new_enough?(ruby)
+    version = Utils.popen_read(ruby, "-e", "print RUBY_VERSION").strip
+    version =~ /^\d+\.\d+/ && Version.create(version) >= min_version
+  end
+
+  def min_version
+    @min_version ||= Version.create(@version)
+  end
 end
diff --git a/Library/Homebrew/test/analytics_test.rb b/Library/Homebrew/test/analytics_test.rb
deleted file mode 100644
index 37040f3cd8873fdafb75628173ad469bee244a2c..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/analytics_test.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require "testing_env"
-
-class IntegrationCommandTestAnalytics < IntegrationCommandTestCase
-  def test_analytics
-    HOMEBREW_REPOSITORY.cd do
-      shutup do
-        system "git", "init"
-      end
-    end
-
-    assert_match "Analytics is disabled (by HOMEBREW_NO_ANALYTICS)",
-      cmd("analytics", "HOMEBREW_NO_ANALYTICS" => "1")
-
-    cmd("analytics", "off")
-    assert_match "Analytics is disabled",
-      cmd("analytics", "HOMEBREW_NO_ANALYTICS" => nil)
-
-    cmd("analytics", "on")
-    assert_match "Analytics is enabled", cmd("analytics",
-      "HOMEBREW_NO_ANALYTICS" => nil)
-
-    assert_match "Invalid usage", cmd_fail("analytics", "on", "off")
-    assert_match "Invalid usage", cmd_fail("analytics", "testball")
-    cmd("analytics", "regenerate-uuid")
-  end
-end
diff --git a/Library/Homebrew/test/blacklist_spec.rb b/Library/Homebrew/test/blacklist_spec.rb
index 89d254893fcaeae9e87b199750306d8b66a3b46d..01882167deed9d9f449301adf91489d037503d85 100644
--- a/Library/Homebrew/test/blacklist_spec.rb
+++ b/Library/Homebrew/test/blacklist_spec.rb
@@ -1,12 +1,8 @@
 require "blacklist"
 
-RSpec::Matchers.define :be_blacklisted do
-  match do |actual|
-    blacklisted?(actual)
-  end
-end
-
 describe "Blacklist" do
+  matcher(:be_blacklisted) { match(&method(:blacklisted?)) }
+
   context "rubygems" do
     %w[gem rubygem rubygems].each do |s|
       subject { s }
@@ -103,9 +99,17 @@ describe "Blacklist" do
     it { is_expected.to be_blacklisted }
   end
 
-  context "haskell_platform" do
+  context "haskell-platform" do
     subject { "haskell-platform" }
 
     it { is_expected.to be_blacklisted }
   end
+
+  context "xcode", :needs_macos do
+    %w[xcode Xcode].each do |s|
+      subject { s }
+
+      it { is_expected.to be_blacklisted }
+    end
+  end
 end
diff --git a/Library/Homebrew/test/cmd/analytics_spec.rb b/Library/Homebrew/test/cmd/analytics_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..aed3a7a33747754d786fcc20c223828fe179b863
--- /dev/null
+++ b/Library/Homebrew/test/cmd/analytics_spec.rb
@@ -0,0 +1,52 @@
+describe "brew analytics", :integration_test do
+  before(:each) do
+    HOMEBREW_REPOSITORY.cd do
+      shutup do
+        system "git", "init"
+      end
+    end
+  end
+
+  it "is disabled when HOMEBREW_NO_ANALYTICS is set" do
+    expect { brew "analytics", "HOMEBREW_NO_ANALYTICS" => "1" }
+      .to output(/Analytics is disabled \(by HOMEBREW_NO_ANALYTICS\)/).to_stdout
+      .and not_to_output.to_stderr
+      .and be_a_success
+  end
+
+  context "when HOMEBREW_NO_ANALYTICS is unset" do
+    it "is disabled after running `brew analytics off`" do
+      brew "analytics", "off"
+      expect { brew "analytics", "HOMEBREW_NO_ANALYTICS" => nil }
+        .to output(/Analytics is disabled/).to_stdout
+        .and not_to_output.to_stderr
+        .and be_a_success
+    end
+
+    it "is enabled after running `brew analytics on`" do
+      brew "analytics", "on"
+      expect { brew "analytics", "HOMEBREW_NO_ANALYTICS" => nil }
+        .to output(/Analytics is enabled/).to_stdout
+        .and not_to_output.to_stderr
+        .and be_a_success
+    end
+  end
+
+  it "fails when running `brew analytics on off`" do
+    expect { brew "analytics", "on", "off" }
+      .to output(/Invalid usage/).to_stderr
+      .and not_to_output.to_stdout
+      .and be_a_failure
+  end
+
+  it "fails when running `brew analytics testball`" do
+    expect { brew "analytics", "testball" }
+      .to output(/Invalid usage/).to_stderr
+      .and not_to_output.to_stdout
+      .and be_a_failure
+  end
+
+  it "can generate a new UUID" do
+    expect { brew "analytics", "regenerate-uuid" }.to be_a_success
+  end
+end
diff --git a/Library/Homebrew/test/cmd/search_remote_tap_spec.rb b/Library/Homebrew/test/cmd/search_remote_tap_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..99c2cc20b59a397c4cc043a64ac32de94237f70b
--- /dev/null
+++ b/Library/Homebrew/test/cmd/search_remote_tap_spec.rb
@@ -0,0 +1,19 @@
+require "cmd/search"
+
+describe Homebrew do
+  specify "#search_tap" do
+    json_response = {
+      "tree" => [
+        {
+          "path" => "Formula/not-a-formula.rb",
+          "type" => "blob",
+        },
+      ],
+    }
+
+    allow(GitHub).to receive(:open).and_yield(json_response)
+
+    expect(described_class.search_tap("homebrew", "not-a-tap", "not-a-formula"))
+      .to eq(["homebrew/not-a-tap/not-a-formula"])
+  end
+end
diff --git a/Library/Homebrew/test/compiler_failure_spec.rb b/Library/Homebrew/test/compiler_failure_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b4fab0b2705278adc95e926fb6f2c5193a88d0b6
--- /dev/null
+++ b/Library/Homebrew/test/compiler_failure_spec.rb
@@ -0,0 +1,37 @@
+require "compilers"
+
+RSpec::Matchers.alias_matcher :fail_with, :be_fails_with
+
+describe CompilerFailure do
+  describe "::create" do
+    it "creates a failure when given a symbol" do
+      failure = described_class.create(:clang)
+      expect(failure).to fail_with(double("Compiler", name: :clang, version: 425))
+    end
+
+    it "can be given a build number in a block" do
+      failure = described_class.create(:clang) { build 211 }
+      expect(failure).to fail_with(double("Compiler", name: :clang, version: 210))
+      expect(failure).not_to fail_with(double("Compiler", name: :clang, version: 318))
+    end
+
+    it "can be given an empty block" do
+      failure = described_class.create(:clang) {}
+      expect(failure).to fail_with(double("Compiler", name: :clang, version: 425))
+    end
+
+    it "creates a failure when given a hash" do
+      failure = described_class.create(gcc: "4.8")
+      expect(failure).to fail_with(double("Compiler", name: "gcc-4.8", version: "4.8"))
+      expect(failure).to fail_with(double("Compiler", name: "gcc-4.8", version: "4.8.1"))
+      expect(failure).not_to fail_with(double("Compiler", name: "gcc-4.7", version: "4.7"))
+    end
+
+    it "creates a failure when given a hash and a block with aversion" do
+      failure = described_class.create(gcc: "4.8") { version "4.8.1" }
+      expect(failure).to fail_with(double("Compiler", name: "gcc-4.8", version: "4.8"))
+      expect(failure).to fail_with(double("Compiler", name: "gcc-4.8", version: "4.8.1"))
+      expect(failure).not_to fail_with(double("Compiler", name: "gcc-4.8", version: "4.8.2"))
+    end
+  end
+end
diff --git a/Library/Homebrew/test/compiler_failure_test.rb b/Library/Homebrew/test/compiler_failure_test.rb
deleted file mode 100644
index c0b74d1282266ccb6da6ec2eb33a93cb4e2f578b..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/compiler_failure_test.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-require "testing_env"
-require "compilers"
-
-class CompilerFailureTests < Homebrew::TestCase
-  Compiler = Struct.new(:name, :version)
-
-  def assert_fails_with(compiler, failure)
-    assert_operator failure, :fails_with?, compiler
-  end
-
-  def refute_fails_with(compiler, failure)
-    refute_operator failure, :fails_with?, compiler
-  end
-
-  def compiler(name, version)
-    Compiler.new(name, version)
-  end
-
-  def create(spec, &block)
-    CompilerFailure.create(spec, &block)
-  end
-
-  def test_create_with_symbol
-    failure = create(:clang)
-    assert_fails_with compiler(:clang, 425), failure
-  end
-
-  def test_create_with_block
-    failure = create(:clang) { build 211 }
-    assert_fails_with compiler(:clang, 210), failure
-    refute_fails_with compiler(:clang, 318), failure
-  end
-
-  def test_create_with_block_without_build
-    failure = create(:clang) {}
-    assert_fails_with compiler(:clang, 425), failure
-  end
-
-  def test_create_with_hash
-    failure = create(gcc: "4.8")
-    assert_fails_with compiler("gcc-4.8", "4.8"), failure
-    assert_fails_with compiler("gcc-4.8", "4.8.1"), failure
-    refute_fails_with compiler("gcc-4.7", "4.7"), failure
-  end
-
-  def test_create_with_hash_and_version
-    failure = create(gcc: "4.8") { version "4.8.1" }
-    assert_fails_with compiler("gcc-4.8", "4.8"), failure
-    assert_fails_with compiler("gcc-4.8", "4.8.1"), failure
-    refute_fails_with compiler("gcc-4.8", "4.8.2"), failure
-  end
-end
diff --git a/Library/Homebrew/test/compiler_selector_spec.rb b/Library/Homebrew/test/compiler_selector_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0f6f6b5f244c22b93d3f567d0f4f0444158464ee
--- /dev/null
+++ b/Library/Homebrew/test/compiler_selector_spec.rb
@@ -0,0 +1,122 @@
+require "compilers"
+require "software_spec"
+
+describe CompilerSelector do
+  subject { described_class.new(software_spec, versions, compilers) }
+  let(:compilers) { [:clang, :gcc, :llvm, :gnu] }
+  let(:software_spec) { SoftwareSpec.new }
+  let(:cc) { :clang }
+  let(:versions) do
+    double(
+      gcc_4_0_build_version: Version::NULL,
+      gcc_build_version: Version.create("5666"),
+      llvm_build_version: Version::NULL,
+      clang_build_version: Version.create("425"),
+    )
+  end
+
+  before(:each) do
+    allow(versions).to receive(:non_apple_gcc_version) do |name|
+      case name
+      when "gcc-4.8" then Version.create("4.8.1")
+      when "gcc-4.7" then Version.create("4.7.1")
+      else Version::NULL
+      end
+    end
+  end
+
+  describe "#compiler" do
+    it "raises an error if no matching compiler can be found" do
+      software_spec.fails_with(:clang)
+      software_spec.fails_with(:llvm)
+      software_spec.fails_with(:gcc)
+      software_spec.fails_with(gcc: "4.8")
+      software_spec.fails_with(gcc: "4.7")
+
+      expect { subject.compiler }.to raise_error(CompilerSelectionError)
+    end
+
+    it "defaults to cc" do
+      expect(subject.compiler).to eq(cc)
+    end
+
+    it "returns gcc if it fails with clang" do
+      software_spec.fails_with(:clang)
+      expect(subject.compiler).to eq(:gcc)
+    end
+
+    it "returns clang if it fails with llvm" do
+      software_spec.fails_with(:llvm)
+      expect(subject.compiler).to eq(:clang)
+    end
+
+    it "returns clang if it fails with gcc" do
+      software_spec.fails_with(:gcc)
+      expect(subject.compiler).to eq(:clang)
+    end
+
+    it "returns clang if it fails with non-Apple gcc" do
+      software_spec.fails_with(gcc: "4.8")
+      expect(subject.compiler).to eq(:clang)
+    end
+
+    it "still returns gcc-4.8 if it fails with gcc without a specific version" do
+      software_spec.fails_with(:clang)
+      software_spec.fails_with(:gcc)
+      expect(subject.compiler).to eq("gcc-4.8")
+    end
+
+    it "returns gcc if it fails with clang and llvm" do
+      software_spec.fails_with(:clang)
+      software_spec.fails_with(:llvm)
+      expect(subject.compiler).to eq(:gcc)
+    end
+
+    it "returns clang if it fails with gcc and llvm" do
+      software_spec.fails_with(:gcc)
+      software_spec.fails_with(:llvm)
+      expect(subject.compiler).to eq(:clang)
+    end
+
+    example "returns gcc if it fails with a specific gcc version" do
+      software_spec.fails_with(:clang)
+      software_spec.fails_with(gcc: "4.8")
+      expect(subject.compiler).to eq(:gcc)
+    end
+
+    example "returns a lower version of gcc if it fails with the highest version" do
+      software_spec.fails_with(:clang)
+      software_spec.fails_with(:gcc)
+      software_spec.fails_with(:llvm)
+      software_spec.fails_with(gcc: "4.8")
+      expect(subject.compiler).to eq("gcc-4.7")
+    end
+
+    it "prefers gcc" do
+      software_spec.fails_with(:clang)
+      software_spec.fails_with(:gcc)
+      expect(subject.compiler).to eq("gcc-4.8")
+    end
+
+    it "raises an error when gcc is missing" do
+      allow(versions).to receive(:gcc_build_version).and_return(Version::NULL)
+
+      software_spec.fails_with(:clang)
+      software_spec.fails_with(:llvm)
+      software_spec.fails_with(gcc: "4.8")
+      software_spec.fails_with(gcc: "4.7")
+
+      expect { subject.compiler }.to raise_error(CompilerSelectionError)
+    end
+
+    it "raises an error when llvm and gcc are missing" do
+      allow(versions).to receive(:gcc_build_version).and_return(Version::NULL)
+
+      software_spec.fails_with(:clang)
+      software_spec.fails_with(gcc: "4.8")
+      software_spec.fails_with(gcc: "4.7")
+
+      expect { subject.compiler }.to raise_error(CompilerSelectionError)
+    end
+  end
+end
diff --git a/Library/Homebrew/test/compiler_selector_test.rb b/Library/Homebrew/test/compiler_selector_test.rb
deleted file mode 100644
index aa1a6f97ec44eef515975594a27a8b495382a9bd..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/compiler_selector_test.rb
+++ /dev/null
@@ -1,117 +0,0 @@
-require "testing_env"
-require "compilers"
-require "software_spec"
-
-class CompilerSelectorTests < Homebrew::TestCase
-  class Double < SoftwareSpec
-    def <<(cc)
-      fails_with(cc)
-      self
-    end
-  end
-
-  class CompilerVersions
-    attr_accessor :gcc_4_0_build_version, :gcc_build_version,
-      :clang_build_version
-
-    def initialize
-      @gcc_4_0_build_version = Version::NULL
-      @gcc_build_version = Version.create("5666")
-      @llvm_build_version = Version::NULL
-      @clang_build_version = Version.create("425")
-    end
-
-    def non_apple_gcc_version(name)
-      case name
-      when "gcc-4.8" then Version.create("4.8.1")
-      when "gcc-4.7" then Version.create("4.7.1")
-      else Version::NULL
-      end
-    end
-  end
-
-  def setup
-    super
-    @f  = Double.new
-    @cc = :clang
-    @versions = CompilerVersions.new
-    @selector = CompilerSelector.new(
-      @f, @versions, [:clang, :gcc, :llvm, :gnu]
-    )
-  end
-
-  def actual_cc
-    @selector.compiler
-  end
-
-  def test_all_compiler_failures
-    @f << :clang << :llvm << :gcc << { gcc: "4.8" } << { gcc: "4.7" }
-    assert_raises(CompilerSelectionError) { actual_cc }
-  end
-
-  def test_no_compiler_failures
-    assert_equal @cc, actual_cc
-  end
-
-  def test_fails_with_clang
-    @f << :clang
-    assert_equal :gcc, actual_cc
-  end
-
-  def test_fails_with_llvm
-    @f << :llvm
-    assert_equal :clang, actual_cc
-  end
-
-  def test_fails_with_gcc
-    @f << :gcc
-    assert_equal :clang, actual_cc
-  end
-
-  def test_fails_with_non_apple_gcc
-    @f << { gcc: "4.8" }
-    assert_equal :clang, actual_cc
-  end
-
-  def test_mixed_failures_1
-    @f << :clang << :gcc
-    assert_equal "gcc-4.8", actual_cc
-  end
-
-  def test_mixed_failures_2
-    @f << :clang << :llvm
-    assert_equal :gcc, actual_cc
-  end
-
-  def test_mixed_failures_3
-    @f << :gcc << :llvm
-    assert_equal :clang, actual_cc
-  end
-
-  def test_mixed_failures_4
-    @f << :clang << { gcc: "4.8" }
-    assert_equal :gcc, actual_cc
-  end
-
-  def test_mixed_failures_5
-    @f << :clang << :gcc << :llvm << { gcc: "4.8" }
-    assert_equal "gcc-4.7", actual_cc
-  end
-
-  def test_gcc_precedence
-    @f << :clang << :gcc
-    assert_equal "gcc-4.8", actual_cc
-  end
-
-  def test_missing_gcc
-    @versions.gcc_build_version = Version::NULL
-    @f << :clang << :llvm << { gcc: "4.8" } << { gcc: "4.7" }
-    assert_raises(CompilerSelectionError) { actual_cc }
-  end
-
-  def test_missing_llvm_and_gcc
-    @versions.gcc_build_version = Version::NULL
-    @f << :clang << { gcc: "4.8" } << { gcc: "4.7" }
-    assert_raises(CompilerSelectionError) { actual_cc }
-  end
-end
diff --git a/Library/Homebrew/test/dependency_expansion_spec.rb b/Library/Homebrew/test/dependency_expansion_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f955237a971752f67268f93826a201535558fc6e
--- /dev/null
+++ b/Library/Homebrew/test/dependency_expansion_spec.rb
@@ -0,0 +1,136 @@
+require "dependency"
+
+describe Dependency do
+  def build_dep(name, tags = [], deps = [])
+    dep = described_class.new(name.to_s, tags)
+    allow(dep).to receive(:to_formula).and_return(double(deps: deps, name: name))
+    dep
+  end
+
+  let(:foo) { build_dep(:foo) }
+  let(:bar) { build_dep(:bar) }
+  let(:baz) { build_dep(:baz) }
+  let(:qux) { build_dep(:qux) }
+  let(:deps) { [foo, bar, baz, qux] }
+  let(:formula) { double(deps: deps, name: "f") }
+
+  describe "::expand" do
+    it "yields dependent and dependency pairs" do
+      i = 0
+      described_class.expand(formula) do |dependent, dep|
+        expect(dependent).to eq(formula)
+        expect(deps[i]).to eq(dep)
+        i += 1
+      end
+    end
+
+    it "returns the dependencies" do
+      expect(described_class.expand(formula)).to eq(deps)
+    end
+
+    it "prunes all when given a block with ::prune" do
+      expect(described_class.expand(formula) { described_class.prune }).to be_empty
+    end
+
+    it "can prune selectively" do
+      deps = described_class.expand(formula) do |_, dep|
+        described_class.prune if dep.name == "foo"
+      end
+
+      expect(deps).to eq([bar, baz, qux])
+    end
+
+    it "preserves dependency order" do
+      allow(foo).to receive(:to_formula).and_return(double(name: "f", deps: [qux, baz]))
+      expect(described_class.expand(formula)).to eq([qux, baz, foo, bar])
+    end
+  end
+
+  it "skips optionals by default" do
+    deps = [build_dep(:foo, [:optional]), bar, baz, qux]
+    f = double(deps: deps, build: double(with?: false), name: "f")
+    expect(described_class.expand(f)).to eq([bar, baz, qux])
+  end
+
+  it "keeps recommended dependencies by default" do
+    deps = [build_dep(:foo, [:recommended]), bar, baz, qux]
+    f = double(deps: deps, build: double(with?: true), name: "f")
+    expect(described_class.expand(f)).to eq(deps)
+  end
+
+  it "merges repeated dependencies with differing options" do
+    foo2 = build_dep(:foo, ["option"])
+    baz2 = build_dep(:baz, ["option"])
+    deps << foo2 << baz2
+    deps = [foo2, bar, baz2, qux]
+    deps.zip(described_class.expand(formula)) do |expected, actual|
+      expect(expected.tags).to eq(actual.tags)
+      expect(expected).to eq(actual)
+    end
+  end
+
+  it "merges dependencies and perserves env_proc" do
+    env_proc = double
+    dep = described_class.new("foo", [], env_proc)
+    allow(dep).to receive(:to_formula).and_return(double(deps: [], name: "foo"))
+    deps.replace([dep])
+    expect(described_class.expand(formula).first.env_proc).to eq(env_proc)
+  end
+
+  it "merges tags without duplicating them" do
+    foo2 = build_dep(:foo, ["option"])
+    foo3 = build_dep(:foo, ["option"])
+    deps << foo2 << foo3
+
+    expect(described_class.expand(formula).first.tags).to eq(%w[option])
+  end
+
+  it "skips parent but yields children with ::skip" do
+    f = double(
+      name: "f",
+      deps: [
+        build_dep(:foo, [], [bar, baz]),
+        build_dep(:foo, [], [baz]),
+      ],
+    )
+
+    deps = described_class.expand(f) do |_dependent, dep|
+      described_class.skip if %w[foo qux].include? dep.name
+    end
+
+    expect(deps).to eq([bar, baz])
+  end
+
+  it "keeps dependency but prunes recursive dependencies with ::keep_but_prune_recursive_deps" do
+    foo = build_dep(:foo, [:build], bar)
+    baz = build_dep(:baz, [:build])
+    f = double(name: "f", deps: [foo, baz])
+
+    deps = described_class.expand(f) do |_dependent, dep|
+      described_class.keep_but_prune_recursive_deps if dep.build?
+    end
+
+    expect(deps).to eq([foo, baz])
+  end
+
+  it "returns only the dependencies given as a collection as second argument" do
+    expect(formula.deps).to eq([foo, bar, baz, qux])
+    expect(described_class.expand(formula, [bar, baz])).to eq([bar, baz])
+  end
+
+  it "doesn't raise an error when a dependency is cyclic" do
+    foo = build_dep(:foo)
+    bar = build_dep(:bar, [], [foo])
+    allow(foo).to receive(:to_formula).and_return(double(deps: [bar], name: foo.name))
+    f = double(name: "f", deps: [foo, bar])
+    expect { described_class.expand(f) }.not_to raise_error
+  end
+
+  it "cleans the expand stack" do
+    foo = build_dep(:foo)
+    allow(foo).to receive(:to_formula).and_raise(FormulaUnavailableError, foo.name)
+    f = double(name: "f", deps: [foo])
+    expect { described_class.expand(f) }.to raise_error(FormulaUnavailableError)
+    expect(described_class.instance_variable_get(:@expand_stack)).to be_empty
+  end
+end
diff --git a/Library/Homebrew/test/dependency_expansion_test.rb b/Library/Homebrew/test/dependency_expansion_test.rb
deleted file mode 100644
index 58a731121345ba9721fa9cd52c6598b2dd3b9b40..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/dependency_expansion_test.rb
+++ /dev/null
@@ -1,138 +0,0 @@
-require "testing_env"
-require "dependency"
-
-class DependencyExpansionTests < Homebrew::TestCase
-  def build_dep(name, tags = [], deps = [])
-    dep = Dependency.new(name.to_s, tags)
-    dep.stubs(:to_formula).returns(stub(deps: deps, name: name))
-    dep
-  end
-
-  def setup
-    super
-    @foo = build_dep(:foo)
-    @bar = build_dep(:bar)
-    @baz = build_dep(:baz)
-    @qux = build_dep(:qux)
-    @deps = [@foo, @bar, @baz, @qux]
-    @f    = stub(deps: @deps, name: "f")
-  end
-
-  def test_expand_yields_dependent_and_dep_pairs
-    i = 0
-    Dependency.expand(@f) do |dependent, dep|
-      assert_equal @f, dependent
-      assert_equal dep, @deps[i]
-      i += 1
-    end
-  end
-
-  def test_expand_no_block
-    assert_equal @deps, Dependency.expand(@f)
-  end
-
-  def test_expand_prune_all
-    assert_empty Dependency.expand(@f) { Dependency.prune }
-  end
-
-  def test_expand_selective_pruning
-    deps = Dependency.expand(@f) do |_, dep|
-      Dependency.prune if dep.name == "foo"
-    end
-
-    assert_equal [@bar, @baz, @qux], deps
-  end
-
-  def test_expand_preserves_dependency_order
-    @foo.stubs(:to_formula).returns(stub(name: "f", deps: [@qux, @baz]))
-    assert_equal [@qux, @baz, @foo, @bar], Dependency.expand(@f)
-  end
-
-  def test_expand_skips_optionals_by_default
-    deps = [build_dep(:foo, [:optional]), @bar, @baz, @qux]
-    f = stub(deps: deps, build: stub(with?: false), name: "f")
-    assert_equal [@bar, @baz, @qux], Dependency.expand(f)
-  end
-
-  def test_expand_keeps_recommendeds_by_default
-    deps = [build_dep(:foo, [:recommended]), @bar, @baz, @qux]
-    f = stub(deps: deps, build: stub(with?: true), name: "f")
-    assert_equal deps, Dependency.expand(f)
-  end
-
-  def test_merges_repeated_deps_with_differing_options
-    @foo2 = build_dep(:foo, ["option"])
-    @baz2 = build_dep(:baz, ["option"])
-    @deps << @foo2 << @baz2
-    deps = [@foo2, @bar, @baz2, @qux]
-    deps.zip(Dependency.expand(@f)) do |expected, actual|
-      assert_equal expected.tags, actual.tags
-      assert_equal expected, actual
-    end
-  end
-
-  def test_merger_preserves_env_proc
-    env_proc = stub
-    dep = Dependency.new("foo", [], env_proc)
-    dep.stubs(:to_formula).returns(stub(deps: [], name: "foo"))
-    @deps.replace [dep]
-    assert_equal env_proc, Dependency.expand(@f).first.env_proc
-  end
-
-  def test_merged_tags_no_dupes
-    @foo2 = build_dep(:foo, ["option"])
-    @foo3 = build_dep(:foo, ["option"])
-    @deps << @foo2 << @foo3
-
-    assert_equal %w[option], Dependency.expand(@f).first.tags
-  end
-
-  def test_skip_skips_parent_but_yields_children
-    f = stub(
-      name: "f",
-      deps: [
-        build_dep(:foo, [], [@bar, @baz]),
-        build_dep(:foo, [], [@baz]),
-      ],
-    )
-
-    deps = Dependency.expand(f) do |_dependent, dep|
-      Dependency.skip if %w[foo qux].include? dep.name
-    end
-
-    assert_equal [@bar, @baz], deps
-  end
-
-  def test_keep_dep_but_prune_recursive_deps
-    foo = build_dep(:foo, [:build], @bar)
-    baz = build_dep(:baz, [:build])
-    f = stub(name: "f", deps: [foo, baz])
-
-    deps = Dependency.expand(f) do |_dependent, dep|
-      Dependency.keep_but_prune_recursive_deps if dep.build?
-    end
-
-    assert_equal [foo, baz], deps
-  end
-
-  def test_deps_with_collection_argument
-    assert_equal [@foo, @bar, @baz, @qux], @f.deps
-    assert_equal [@bar, @baz], Dependency.expand(@f, [@bar, @baz])
-  end
-
-  def test_cyclic_dependency
-    foo = build_dep(:foo)
-    bar = build_dep(:bar, [], [foo])
-    foo.stubs(:to_formula).returns(stub(deps: [bar], name: "foo"))
-    f = stub(name: "f", deps: [foo, bar])
-    assert_nothing_raised { Dependency.expand(f) }
-  end
-
-  def test_clean_expand_stack
-    foo = build_dep(:foo)
-    foo.stubs(:to_formula).raises(FormulaUnavailableError, "foo")
-    f = stub(name: "f", deps: [foo])
-    assert_raises(FormulaUnavailableError) { Dependency.expand(f) }
-    assert_empty Dependency.instance_variable_get(:@expand_stack)
-  end
-end
diff --git a/Library/Homebrew/test/diagnostic_spec.rb b/Library/Homebrew/test/diagnostic_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e749a3b0f488af2dcfbd0f407bed945b57886814
--- /dev/null
+++ b/Library/Homebrew/test/diagnostic_spec.rb
@@ -0,0 +1,188 @@
+require "diagnostic"
+
+describe Homebrew::Diagnostic::Checks do
+  specify "#inject_file_list" do
+    expect(subject.inject_file_list([], "foo:\n")).to eq("foo:\n")
+    expect(subject.inject_file_list(%w[/a /b], "foo:\n")).to eq("foo:\n  /a\n  /b\n")
+  end
+
+  specify "#check_path_for_trailing_slashes" do
+    ENV["PATH"] += File::PATH_SEPARATOR + "/foo/bar/"
+    expect(subject.check_path_for_trailing_slashes)
+      .to match("Some directories in your path end in a slash")
+  end
+
+  specify "#check_for_anaconda" do
+    Dir.mktmpdir do |path|
+      anaconda = "#{path}/anaconda"
+      python = "#{path}/python"
+      FileUtils.touch anaconda
+      File.open(python, "w") do |file|
+        file.write("#! #{`which bash`}\necho -n '#{python}'\n")
+      end
+      FileUtils.chmod 0755, anaconda
+      FileUtils.chmod 0755, python
+
+      ENV["PATH"] = path + File::PATH_SEPARATOR + ENV["PATH"]
+
+      expect(subject.check_for_anaconda).to match("Anaconda")
+    end
+  end
+
+  specify "#check_access_homebrew_repository" do
+    begin
+      mode = HOMEBREW_REPOSITORY.stat.mode & 0777
+      HOMEBREW_REPOSITORY.chmod 0555
+
+      expect(subject.check_access_homebrew_repository)
+        .to match("#{HOMEBREW_REPOSITORY} is not writable.")
+    ensure
+      HOMEBREW_REPOSITORY.chmod mode
+    end
+  end
+
+  specify "#check_access_logs" do
+    begin
+      mode = HOMEBREW_LOGS.stat.mode & 0777
+      HOMEBREW_LOGS.chmod 0555
+
+      expect(subject.check_access_logs)
+        .to match("#{HOMEBREW_LOGS} isn't writable.")
+    ensure
+      HOMEBREW_LOGS.chmod mode
+    end
+  end
+
+  specify "#check_access_cache" do
+    begin
+      mode = HOMEBREW_CACHE.stat.mode & 0777
+      HOMEBREW_CACHE.chmod 0555
+      expect(subject.check_access_cache)
+        .to match("#{HOMEBREW_CACHE} isn't writable.")
+    ensure
+      HOMEBREW_CACHE.chmod mode
+    end
+  end
+
+  specify "#check_access_cellar" do
+    begin
+      mode = HOMEBREW_CELLAR.stat.mode & 0777
+      HOMEBREW_CELLAR.chmod 0555
+
+      expect(subject.check_access_cellar)
+        .to match("#{HOMEBREW_CELLAR} isn't writable.")
+    ensure
+      HOMEBREW_CELLAR.chmod mode
+    end
+  end
+
+  specify "#check_homebrew_prefix" do
+    ENV.delete("JENKINS_HOME")
+    # the integration tests are run in a special prefix
+    expect(subject.check_homebrew_prefix)
+      .to match("Your Homebrew's prefix is not /usr/local.")
+  end
+
+  specify "#check_user_path_1" do
+    bin = HOMEBREW_PREFIX/"bin"
+    sep = File::PATH_SEPARATOR
+    # ensure /usr/bin is before HOMEBREW_PREFIX/bin in the PATH
+    ENV["PATH"] = "/usr/bin#{sep}#{bin}#{sep}" +
+                  ENV["PATH"].gsub(%r{(?:^|#{sep})(?:/usr/bin|#{bin})}, "")
+
+    # ensure there's at least one file with the same name in both /usr/bin/ and
+    # HOMEBREW_PREFIX/bin/
+    (bin/File.basename(Dir["/usr/bin/*"].first)).mkpath
+
+    expect(subject.check_user_path_1)
+      .to match("/usr/bin occurs before #{HOMEBREW_PREFIX}/bin")
+  end
+
+  specify "#check_user_path_2" do
+    ENV["PATH"] = ENV["PATH"].gsub \
+      %r{(?:^|#{File::PATH_SEPARATOR})#{HOMEBREW_PREFIX}/bin}, ""
+
+    expect(subject.check_user_path_1).to be nil
+    expect(subject.check_user_path_2)
+      .to match("Homebrew's bin was not found in your PATH.")
+  end
+
+  specify "#check_user_path_3" do
+    begin
+      sbin = HOMEBREW_PREFIX/"sbin"
+      ENV["PATH"] = "#{HOMEBREW_PREFIX}/bin#{File::PATH_SEPARATOR}" +
+                    ENV["PATH"].gsub(/(?:^|#{Regexp.escape(File::PATH_SEPARATOR)})#{Regexp.escape(sbin)}/, "")
+      (sbin/"something").mkpath
+
+      expect(subject.check_user_path_1).to be nil
+      expect(subject.check_user_path_2).to be nil
+      expect(subject.check_user_path_3)
+        .to match("Homebrew's sbin was not found in your PATH")
+    ensure
+      sbin.rmtree
+    end
+  end
+
+  specify "#check_user_curlrc" do
+    Dir.mktmpdir do |path|
+      FileUtils.touch "#{path}/.curlrc"
+      ENV["CURL_HOME"] = path
+
+      expect(subject.check_user_curlrc).to match("You have a curlrc file")
+    end
+  end
+
+  specify "#check_for_config_scripts" do
+    Dir.mktmpdir do |path|
+      file = "#{path}/foo-config"
+      FileUtils.touch file
+      FileUtils.chmod 0755, file
+      ENV["PATH"] = "#{path}#{File::PATH_SEPARATOR}#{ENV["PATH"]}"
+
+      expect(subject.check_for_config_scripts)
+        .to match('"config" scripts exist')
+    end
+  end
+
+  specify "#check_dyld_vars" do
+    ENV["DYLD_INSERT_LIBRARIES"] = "foo"
+    expect(subject.check_dyld_vars).to match("Setting DYLD_INSERT_LIBRARIES")
+  end
+
+  specify "#check_for_symlinked_cellar" do
+    begin
+      HOMEBREW_CELLAR.rmtree
+
+      Dir.mktmpdir do |path|
+        FileUtils.ln_s path, HOMEBREW_CELLAR
+
+        expect(subject.check_for_symlinked_cellar).to match(path)
+      end
+    ensure
+      HOMEBREW_CELLAR.unlink
+      HOMEBREW_CELLAR.mkpath
+    end
+  end
+
+  specify "#check_tmpdir" do
+    ENV["TMPDIR"] = "/i/don/t/exis/t"
+    expect(subject.check_tmpdir).to match("doesn't exist")
+  end
+
+  specify "#check_for_external_cmd_name_conflict" do
+    Dir.mktmpdir do |path1|
+      Dir.mktmpdir do |path2|
+        [path1, path2].each do |path|
+          cmd = "#{path}/brew-foo"
+          FileUtils.touch cmd
+          FileUtils.chmod 0755, cmd
+        end
+
+        ENV["PATH"] = [path1, path2, ENV["PATH"]].join File::PATH_SEPARATOR
+
+        expect(subject.check_for_external_cmd_name_conflict)
+          .to match("brew-foo")
+      end
+    end
+  end
+end
diff --git a/Library/Homebrew/test/diagnostic_test.rb b/Library/Homebrew/test/diagnostic_test.rb
deleted file mode 100644
index 7a1fb25f7930bc4a4f7c7b213fcc72342272d859..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/diagnostic_test.rb
+++ /dev/null
@@ -1,192 +0,0 @@
-require "testing_env"
-require "fileutils"
-require "pathname"
-require "diagnostic"
-
-class DiagnosticChecksTest < Homebrew::TestCase
-  def setup
-    super
-    @checks = Homebrew::Diagnostic::Checks.new
-  end
-
-  def test_inject_file_list
-    assert_equal "foo:\n",
-      @checks.inject_file_list([], "foo:\n")
-    assert_equal "foo:\n  /a\n  /b\n",
-      @checks.inject_file_list(%w[/a /b], "foo:\n")
-  end
-
-  def test_check_path_for_trailing_slashes
-    ENV["PATH"] += File::PATH_SEPARATOR + "/foo/bar/"
-    assert_match "Some directories in your path end in a slash",
-      @checks.check_path_for_trailing_slashes
-  end
-
-  def test_check_for_anaconda
-    mktmpdir do |path|
-      anaconda = "#{path}/anaconda"
-      python = "#{path}/python"
-      FileUtils.touch anaconda
-      File.open(python, "w") do |file|
-        file.write("#! #{`which bash`}\necho -n '#{python}'\n")
-      end
-      FileUtils.chmod 0755, anaconda
-      FileUtils.chmod 0755, python
-
-      ENV["PATH"] = path + File::PATH_SEPARATOR + ENV["PATH"]
-
-      assert_match "Anaconda",
-        @checks.check_for_anaconda
-    end
-  end
-
-  def test_check_access_homebrew_repository
-    mod = HOMEBREW_REPOSITORY.stat.mode & 0777
-    HOMEBREW_REPOSITORY.chmod 0555
-
-    assert_match "#{HOMEBREW_REPOSITORY} is not writable.",
-      @checks.check_access_homebrew_repository
-  ensure
-    HOMEBREW_REPOSITORY.chmod mod
-  end
-
-  def test_check_access_logs
-    mod = HOMEBREW_LOGS.stat.mode & 0777
-    HOMEBREW_LOGS.chmod 0555
-
-    assert_match "#{HOMEBREW_LOGS} isn't writable.",
-      @checks.check_access_logs
-  ensure
-    HOMEBREW_LOGS.chmod mod
-  end
-
-  def test_check_access_cache
-    mod = HOMEBREW_CACHE.stat.mode & 0777
-    HOMEBREW_CACHE.chmod 0555
-    assert_match "#{HOMEBREW_CACHE} isn't writable.",
-      @checks.check_access_cache
-  ensure
-    HOMEBREW_CACHE.chmod mod
-  end
-
-  def test_check_access_cellar
-    mod = HOMEBREW_CELLAR.stat.mode & 0777
-    HOMEBREW_CELLAR.chmod 0555
-
-    assert_match "#{HOMEBREW_CELLAR} isn't writable.",
-      @checks.check_access_cellar
-  ensure
-    HOMEBREW_CELLAR.chmod mod
-  end
-
-  def test_check_homebrew_prefix
-    ENV.delete("JENKINS_HOME")
-    # the integration tests are run in a special prefix
-    assert_match "Your Homebrew's prefix is not /usr/local.",
-      @checks.check_homebrew_prefix
-  end
-
-  def test_check_user_path_usr_bin_before_homebrew
-    bin = HOMEBREW_PREFIX/"bin"
-    sep = File::PATH_SEPARATOR
-    # ensure /usr/bin is before HOMEBREW_PREFIX/bin in the PATH
-    ENV["PATH"] = "/usr/bin#{sep}#{bin}#{sep}" +
-                  ENV["PATH"].gsub(%r{(?:^|#{sep})(?:/usr/bin|#{bin})}, "")
-
-    # ensure there's at least one file with the same name in both /usr/bin/ and
-    # HOMEBREW_PREFIX/bin/
-    (bin/File.basename(Dir["/usr/bin/*"].first)).mkpath
-
-    assert_match "/usr/bin occurs before #{HOMEBREW_PREFIX}/bin",
-      @checks.check_user_path_1
-  end
-
-  def test_check_user_path_bin
-    ENV["PATH"] = ENV["PATH"].gsub \
-      %r{(?:^|#{File::PATH_SEPARATOR})#{HOMEBREW_PREFIX}/bin}, ""
-
-    assert_nil @checks.check_user_path_1
-    assert_match "Homebrew's bin was not found in your PATH.",
-      @checks.check_user_path_2
-  end
-
-  def test_check_user_path_sbin
-    sbin = HOMEBREW_PREFIX/"sbin"
-    ENV["PATH"] = "#{HOMEBREW_PREFIX}/bin#{File::PATH_SEPARATOR}" +
-                  ENV["PATH"].gsub(/(?:^|#{Regexp.escape(File::PATH_SEPARATOR)})#{Regexp.escape(sbin)}/, "")
-    (sbin/"something").mkpath
-
-    assert_nil @checks.check_user_path_1
-    assert_nil @checks.check_user_path_2
-    assert_match "Homebrew's sbin was not found in your PATH",
-      @checks.check_user_path_3
-  ensure
-    sbin.rmtree
-  end
-
-  def test_check_user_curlrc
-    mktmpdir do |path|
-      FileUtils.touch "#{path}/.curlrc"
-      ENV["CURL_HOME"] = path
-
-      assert_match "You have a curlrc file",
-        @checks.check_user_curlrc
-    end
-  end
-
-  def test_check_for_config_scripts
-    mktmpdir do |path|
-      file = "#{path}/foo-config"
-      FileUtils.touch file
-      FileUtils.chmod 0755, file
-      ENV["PATH"] = "#{path}#{File::PATH_SEPARATOR}#{ENV["PATH"]}"
-
-      assert_match '"config" scripts exist',
-        @checks.check_for_config_scripts
-    end
-  end
-
-  def test_check_dyld_vars
-    ENV["DYLD_INSERT_LIBRARIES"] = "foo"
-    assert_match "Setting DYLD_INSERT_LIBRARIES",
-      @checks.check_dyld_vars
-  end
-
-  def test_check_for_symlinked_cellar
-    HOMEBREW_CELLAR.rmtree
-
-    mktmpdir do |path|
-      FileUtils.ln_s path, HOMEBREW_CELLAR
-
-      assert_match path,
-        @checks.check_for_symlinked_cellar
-    end
-
-  ensure
-    HOMEBREW_CELLAR.unlink
-    HOMEBREW_CELLAR.mkpath
-  end
-
-  def test_check_tmpdir
-    ENV["TMPDIR"] = "/i/don/t/exis/t"
-    assert_match "doesn't exist",
-      @checks.check_tmpdir
-  end
-
-  def test_check_for_external_cmd_name_conflict
-    mktmpdir do |path1|
-      mktmpdir do |path2|
-        [path1, path2].each do |path|
-          cmd = "#{path}/brew-foo"
-          FileUtils.touch cmd
-          FileUtils.chmod 0755, cmd
-        end
-
-        ENV["PATH"] = [path1, path2, ENV["PATH"]].join File::PATH_SEPARATOR
-
-        assert_match "brew-foo",
-          @checks.check_for_external_cmd_name_conflict
-      end
-    end
-  end
-end
diff --git a/Library/Homebrew/test/formula_lock_spec.rb b/Library/Homebrew/test/formula_lock_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9b5ece8131157a737fb27044535216a7f3244db3
--- /dev/null
+++ b/Library/Homebrew/test/formula_lock_spec.rb
@@ -0,0 +1,34 @@
+require "formula_lock"
+
+describe FormulaLock do
+  subject { described_class.new("foo") }
+
+  describe "#lock" do
+    it "does not raise an error when already locked" do
+      subject.lock
+
+      expect { subject.lock }.not_to raise_error
+    end
+
+    it "raises an error if a lock already exists" do
+      subject.lock
+
+      expect {
+        described_class.new("foo").lock
+      }.to raise_error(OperationInProgressError)
+    end
+  end
+
+  describe "#unlock" do
+    it "does not raise an error when already unlocked" do
+      expect { subject.unlock }.not_to raise_error
+    end
+
+    it "unlocks a locked Formula" do
+      subject.lock
+      subject.unlock
+
+      expect { described_class.new("foo").lock }.not_to raise_error
+    end
+  end
+end
diff --git a/Library/Homebrew/test/formula_lock_test.rb b/Library/Homebrew/test/formula_lock_test.rb
deleted file mode 100644
index 13244555d08065a2f75cacc837a23bceb0f9d40c..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/formula_lock_test.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require "testing_env"
-require "formula_lock"
-
-class FormulaLockTests < Homebrew::TestCase
-  def setup
-    super
-    @lock = FormulaLock.new("foo")
-    @lock.lock
-  end
-
-  def teardown
-    @lock.unlock
-    super
-  end
-
-  def test_locking_file_with_existing_lock_raises_error
-    assert_raises(OperationInProgressError) { FormulaLock.new("foo").lock }
-  end
-
-  def test_locking_existing_lock_suceeds
-    assert_nothing_raised { @lock.lock }
-  end
-end
diff --git a/Library/Homebrew/test/formula_pin_spec.rb b/Library/Homebrew/test/formula_pin_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..909bfbc2b3ee59262fd9f0a81f1566c17e1d416a
--- /dev/null
+++ b/Library/Homebrew/test/formula_pin_spec.rb
@@ -0,0 +1,41 @@
+require "formula_pin"
+
+describe FormulaPin do
+  subject { described_class.new(formula) }
+  let(:name) { "double" }
+  let(:formula) { double(Formula, name: name, rack: HOMEBREW_CELLAR/name) }
+
+  before(:each) do
+    formula.rack.mkpath
+
+    allow(formula).to receive(:installed_prefixes) do
+      formula.rack.directory? ? formula.rack.subdirs : []
+    end
+
+    allow(formula).to receive(:installed_kegs) do
+      formula.installed_prefixes.map { |prefix| Keg.new(prefix) }
+    end
+  end
+
+  it "is not pinnable by default" do
+    expect(subject).not_to be_pinnable
+  end
+
+  it "is pinnable if the Keg exists" do
+    (formula.rack/"0.1").mkpath
+    expect(subject).to be_pinnable
+  end
+
+  specify "#pin and #unpin" do
+    (formula.rack/"0.1").mkpath
+
+    subject.pin
+    expect(subject).to be_pinned
+    expect(HOMEBREW_PINNED_KEGS/name).to be_a_directory
+    expect(HOMEBREW_PINNED_KEGS.children.count).to eq(1)
+
+    subject.unpin
+    expect(subject).not_to be_pinned
+    expect(HOMEBREW_PINNED_KEGS).not_to be_a_directory
+  end
+end
diff --git a/Library/Homebrew/test/formula_pin_test.rb b/Library/Homebrew/test/formula_pin_test.rb
deleted file mode 100644
index 7e3c7efa0474cdf5cbd0260f57a02b0b3f7f2305..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/formula_pin_test.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require "testing_env"
-require "formula_pin"
-
-class FormulaPinTests < Homebrew::TestCase
-  class FormulaDouble
-    def name
-      "double"
-    end
-
-    def rack
-      HOMEBREW_CELLAR/name
-    end
-
-    def installed_prefixes
-      rack.directory? ? rack.subdirs : []
-    end
-
-    def installed_kegs
-      installed_prefixes.map { |d| Keg.new d }
-    end
-  end
-
-  def setup
-    super
-    @f   = FormulaDouble.new
-    @pin = FormulaPin.new(@f)
-    @f.rack.mkpath
-  end
-
-  def test_not_pinnable
-    refute_predicate @pin, :pinnable?
-  end
-
-  def test_pinnable_if_kegs_exist
-    (@f.rack/"0.1").mkpath
-    assert_predicate @pin, :pinnable?
-  end
-
-  def test_unpin
-    (@f.rack/"0.1").mkpath
-    @pin.pin
-
-    assert_predicate @pin, :pinned?
-    assert_equal 1, HOMEBREW_PINNED_KEGS.children.length
-
-    @pin.unpin
-
-    refute_predicate @pin, :pinned?
-    refute_predicate HOMEBREW_PINNED_KEGS, :directory?
-  end
-end
diff --git a/Library/Homebrew/test/formulary_spec.rb b/Library/Homebrew/test/formulary_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8e9d2358961b74f7747a408723d0404c96ab679f
--- /dev/null
+++ b/Library/Homebrew/test/formulary_spec.rb
@@ -0,0 +1,219 @@
+require "formula"
+require "formula_installer"
+require "utils/bottles"
+
+describe Formulary do
+  let(:formula_name) { "testball_bottle" }
+  let(:formula_path) { CoreTap.new.formula_dir/"#{formula_name}.rb" }
+  let(:formula_content) do
+    <<-EOS.undent
+      class #{subject.class_s(formula_name)} < Formula
+        url "file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz"
+        sha256 TESTBALL_SHA256
+
+        bottle do
+          cellar :any_skip_relocation
+          root_url "file://#{bottle_dir}"
+          sha256 "9abc8ce779067e26556002c4ca6b9427b9874d25f0cafa7028e05b5c5c410cb4" => :#{Utils::Bottles.tag}
+        end
+
+        def install
+          prefix.install "bin"
+          prefix.install "libexec"
+        end
+      end
+    EOS
+  end
+  let(:bottle_dir) { Pathname.new("#{TEST_FIXTURE_DIR}/bottles") }
+  let(:bottle) { bottle_dir/"testball_bottle-0.1.#{Utils::Bottles.tag}.bottle.tar.gz" }
+
+  describe "::class_s" do
+    it "replaces '+' with 'x'" do
+      expect(subject.class_s("foo++")).to eq("Fooxx")
+    end
+
+    it "converts a string to PascalCase" do
+      expect(subject.class_s("shell.fm")).to eq("ShellFm")
+      expect(subject.class_s("s-lang")).to eq("SLang")
+      expect(subject.class_s("pkg-config")).to eq("PkgConfig")
+      expect(subject.class_s("foo_bar")).to eq("FooBar")
+    end
+
+    it "replaces '@' with 'AT'" do
+      expect(subject.class_s("openssl@1.1")).to eq("OpensslAT11")
+    end
+  end
+
+  describe "::factory" do
+    before(:each) do
+      formula_path.write formula_content
+    end
+
+    it "returns a Formula" do
+      expect(subject.factory(formula_name)).to be_kind_of(Formula)
+    end
+
+    it "returns a Formula when given a fully qualified name" do
+      expect(subject.factory("homebrew/core/#{formula_name}")).to be_kind_of(Formula)
+    end
+
+    it "raises an error if the Formula cannot be found" do
+      expect {
+        subject.factory("not_existed_formula")
+      }.to raise_error(FormulaUnavailableError)
+    end
+
+    context "if the Formula has the wrong class" do
+      let(:formula_name) { "giraffe" }
+      let(:formula_content) do
+        <<-EOS.undent
+          class Wrong#{subject.class_s(formula_name)} < Formula
+          end
+        EOS
+      end
+
+      it "raises an error" do
+        expect {
+          subject.factory(formula_name)
+        }.to raise_error(FormulaClassUnavailableError)
+      end
+    end
+
+    it "returns a Formula when given a path" do
+      expect(subject.factory(formula_path)).to be_kind_of(Formula)
+    end
+
+    it "returns a Formula when given a URL" do
+      formula = shutup do
+        subject.factory("file://#{formula_path}")
+      end
+
+      expect(formula).to be_kind_of(Formula)
+    end
+
+    it "returns a Formula when given a bottle" do
+      formula = subject.factory(bottle)
+      expect(formula).to be_kind_of(Formula)
+      expect(formula.local_bottle_path).to eq(bottle.realpath)
+    end
+
+    it "returns a Formula when given an alias" do
+      alias_dir = CoreTap.instance.alias_dir
+      alias_dir.mkpath
+      alias_path = alias_dir/"foo"
+      FileUtils.ln_s formula_path, alias_path
+      result = subject.factory("foo")
+      expect(result).to be_kind_of(Formula)
+      expect(result.alias_path).to eq(alias_path.to_s)
+    end
+
+    context "with installed Formula" do
+      let(:formula) { subject.factory(formula_path) }
+      let(:installer) { FormulaInstaller.new(formula) }
+
+      it "returns a Formula when given a rack" do
+        shutup do
+          installer.install
+        end
+
+        f = subject.from_rack(formula.rack)
+        expect(f).to be_kind_of(Formula)
+        expect(f.build).to be_kind_of(Tab)
+      end
+
+      it "returns a Formula when given a Keg" do
+        shutup do
+          installer.install
+        end
+
+        keg = Keg.new(formula.prefix)
+        f = subject.from_keg(keg)
+        expect(f).to be_kind_of(Formula)
+        expect(f.build).to be_kind_of(Tab)
+      end
+    end
+
+    context "from Tap" do
+      let(:tap) { Tap.new("homebrew", "foo") }
+      let(:formula_path) { tap.path/"#{formula_name}.rb" }
+
+      it "returns a Formula when given a name" do
+        expect(subject.factory(formula_name)).to be_kind_of(Formula)
+      end
+
+      it "returns a Formula from an Alias path" do
+        alias_dir = tap.path/"Aliases"
+        alias_dir.mkpath
+        FileUtils.ln_s formula_path, alias_dir/"bar"
+        expect(subject.factory("bar")).to be_kind_of(Formula)
+      end
+
+      it "raises an error when the Formula cannot be found" do
+        expect {
+          subject.factory("#{tap}/not_existed_formula")
+        }.to raise_error(TapFormulaUnavailableError)
+      end
+
+      it "returns a Formula when given a fully qualified name" do
+        expect(subject.factory("#{tap}/#{formula_name}")).to be_kind_of(Formula)
+      end
+
+      it "raises an error if a Formula is in multiple Taps" do
+        begin
+          another_tap = Tap.new("homebrew", "bar")
+          (another_tap.path/"#{formula_name}.rb").write formula_content
+          expect {
+            subject.factory(formula_name)
+          }.to raise_error(TapFormulaAmbiguityError)
+        ensure
+          another_tap.path.rmtree
+        end
+      end
+    end
+  end
+
+  specify "::from_contents" do
+    expect(subject.from_contents(formula_name, formula_path, formula_content)).to be_kind_of(Formula)
+  end
+
+  specify "::to_rack" do
+    expect(subject.to_rack(formula_name)).to eq(HOMEBREW_CELLAR/formula_name)
+
+    (HOMEBREW_CELLAR/formula_name).mkpath
+    expect(subject.to_rack(formula_name)).to eq(HOMEBREW_CELLAR/formula_name)
+
+    expect {
+      subject.to_rack("a/b/#{formula_name}")
+    }.to raise_error(TapFormulaUnavailableError)
+  end
+
+  describe "::find_with_priority" do
+    let(:core_path) { CoreTap.new.formula_dir/"#{formula_name}.rb" }
+    let(:tap) { Tap.new("homebrew", "foo") }
+    let(:tap_path) { tap.path/"#{formula_name}.rb" }
+
+    before(:each) do
+      core_path.write formula_content
+      tap_path.write formula_content
+    end
+
+    it "prioritizes core Formulae" do
+      formula = subject.find_with_priority(formula_name)
+      expect(formula).to be_kind_of(Formula)
+      expect(formula.path).to eq(core_path)
+    end
+
+    it "prioritizes Formulae from pinned Taps" do
+      begin
+        tap.pin
+        formula = shutup do
+          subject.find_with_priority(formula_name)
+        end
+        expect(formula).to be_kind_of(Formula)
+        expect(formula.path).to eq(tap_path.realpath)
+      ensure
+        tap.pinned_symlink_path.parent.parent.rmtree
+      end
+    end
+  end
+end
diff --git a/Library/Homebrew/test/formulary_test.rb b/Library/Homebrew/test/formulary_test.rb
deleted file mode 100644
index ea7ecf8d0569eb8b03396f63c597a6218e147759..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/formulary_test.rb
+++ /dev/null
@@ -1,189 +0,0 @@
-require "testing_env"
-require "formula"
-require "formula_installer"
-require "utils/bottles"
-
-class FormularyTest < Homebrew::TestCase
-  def test_class_naming
-    assert_equal "ShellFm", Formulary.class_s("shell.fm")
-    assert_equal "Fooxx", Formulary.class_s("foo++")
-    assert_equal "SLang", Formulary.class_s("s-lang")
-    assert_equal "PkgConfig", Formulary.class_s("pkg-config")
-    assert_equal "FooBar", Formulary.class_s("foo_bar")
-    assert_equal "OpensslAT11", Formulary.class_s("openssl@1.1")
-  end
-end
-
-class FormularyFactoryTest < Homebrew::TestCase
-  def setup
-    super
-    @name = "testball_bottle"
-    @path = CoreTap.new.formula_dir/"#{@name}.rb"
-    @bottle_dir = Pathname.new("#{TEST_FIXTURE_DIR}/bottles")
-    @bottle = @bottle_dir/"testball_bottle-0.1.#{Utils::Bottles.tag}.bottle.tar.gz"
-    @path.write <<-EOS.undent
-      class #{Formulary.class_s(@name)} < Formula
-        url "file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz"
-        sha256 TESTBALL_SHA256
-
-        bottle do
-          cellar :any_skip_relocation
-          root_url "file://#{@bottle_dir}"
-          sha256 "9abc8ce779067e26556002c4ca6b9427b9874d25f0cafa7028e05b5c5c410cb4" => :#{Utils::Bottles.tag}
-        end
-
-        def install
-          prefix.install "bin"
-          prefix.install "libexec"
-        end
-      end
-    EOS
-  end
-
-  def test_factory
-    assert_kind_of Formula, Formulary.factory(@name)
-  end
-
-  def test_factory_with_fully_qualified_name
-    assert_kind_of Formula, Formulary.factory("homebrew/core/#{@name}")
-  end
-
-  def test_formula_unavailable_error
-    assert_raises(FormulaUnavailableError) { Formulary.factory("not_existed_formula") }
-  end
-
-  def test_formula_class_unavailable_error
-    name = "giraffe"
-    path = CoreTap.new.formula_dir/"#{name}.rb"
-    path.write "class Wrong#{Formulary.class_s(name)} < Formula\nend\n"
-
-    assert_raises(FormulaClassUnavailableError) { Formulary.factory(name) }
-  end
-
-  def test_factory_from_path
-    assert_kind_of Formula, Formulary.factory(@path)
-  end
-
-  def test_factory_from_url
-    formula = shutup { Formulary.factory("file://#{@path}") }
-    assert_kind_of Formula, formula
-  ensure
-    formula.path.unlink
-  end
-
-  def test_factory_from_bottle
-    formula = Formulary.factory(@bottle)
-    assert_kind_of Formula, formula
-    assert_equal @bottle.realpath, formula.local_bottle_path
-  end
-
-  def test_factory_from_alias
-    alias_dir = CoreTap.instance.alias_dir
-    alias_dir.mkpath
-    alias_path = alias_dir/"foo"
-    FileUtils.ln_s @path, alias_path
-    result = Formulary.factory("foo")
-    assert_kind_of Formula, result
-    assert_equal alias_path.to_s, result.alias_path
-  end
-
-  def test_factory_from_rack_and_from_keg
-    formula = Formulary.factory(@path)
-    installer = FormulaInstaller.new(formula)
-    shutup { installer.install }
-    keg = Keg.new(formula.prefix)
-    f = Formulary.from_rack(formula.rack)
-    assert_kind_of Formula, f
-    assert_kind_of Tab, f.build
-    f = Formulary.from_keg(keg)
-    assert_kind_of Formula, f
-    assert_kind_of Tab, f.build
-  ensure
-    keg.unlink
-  end
-
-  def test_load_from_contents
-    assert_kind_of Formula, Formulary.from_contents(@name, @path, @path.read)
-  end
-
-  def test_to_rack
-    assert_equal HOMEBREW_CELLAR/@name, Formulary.to_rack(@name)
-    (HOMEBREW_CELLAR/@name).mkpath
-    assert_equal HOMEBREW_CELLAR/@name, Formulary.to_rack(@name)
-    assert_raises(TapFormulaUnavailableError) { Formulary.to_rack("a/b/#{@name}") }
-  end
-end
-
-class FormularyTapFactoryTest < Homebrew::TestCase
-  def setup
-    super
-    @name = "foo"
-    @tap = Tap.new "homebrew", "foo"
-    @path = @tap.path/"#{@name}.rb"
-    @code = <<-EOS.undent
-      class #{Formulary.class_s(@name)} < Formula
-        url "foo-1.0"
-      end
-    EOS
-    @path.write @code
-  end
-
-  def test_factory_tap_formula
-    assert_kind_of Formula, Formulary.factory(@name)
-  end
-
-  def test_factory_tap_alias
-    alias_dir = @tap.path/"Aliases"
-    alias_dir.mkpath
-    FileUtils.ln_s @path, alias_dir/"bar"
-    assert_kind_of Formula, Formulary.factory("bar")
-  end
-
-  def test_tap_formula_unavailable_error
-    assert_raises(TapFormulaUnavailableError) { Formulary.factory("#{@tap}/not_existed_formula") }
-  end
-
-  def test_factory_tap_formula_with_fully_qualified_name
-    assert_kind_of Formula, Formulary.factory("#{@tap}/#{@name}")
-  end
-
-  def test_factory_ambiguity_tap_formulae
-    another_tap = Tap.new "homebrew", "bar"
-    (another_tap.path/"#{@name}.rb").write @code
-    assert_raises(TapFormulaAmbiguityError) { Formulary.factory(@name) }
-  ensure
-    another_tap.path.rmtree
-  end
-end
-
-class FormularyTapPriorityTest < Homebrew::TestCase
-  def setup
-    super
-    @name = "foo"
-    @core_path = CoreTap.new.formula_dir/"#{@name}.rb"
-    @tap = Tap.new "homebrew", "foo"
-    @tap_path = @tap.path/"#{@name}.rb"
-    code = <<-EOS.undent
-      class #{Formulary.class_s(@name)} < Formula
-        url "foo-1.0"
-      end
-    EOS
-    @core_path.write code
-    @tap_path.write code
-  end
-
-  def test_find_with_priority_core_formula
-    formula = Formulary.find_with_priority(@name)
-    assert_kind_of Formula, formula
-    assert_equal @core_path, formula.path
-  end
-
-  def test_find_with_priority_tap_formula
-    @tap.pin
-    formula = shutup { Formulary.find_with_priority(@name) }
-    assert_kind_of Formula, formula
-    assert_equal @tap_path.realpath, formula.path
-  ensure
-    @tap.pinned_symlink_path.parent.parent.rmtree
-  end
-end
diff --git a/Library/Homebrew/test/gpg2_requirement_spec.rb b/Library/Homebrew/test/gpg2_requirement_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f46b31196fbd09cad9fe954381600cf4df059fd3
--- /dev/null
+++ b/Library/Homebrew/test/gpg2_requirement_spec.rb
@@ -0,0 +1,23 @@
+require "requirements/gpg2_requirement"
+require "fileutils"
+
+describe GPG2Requirement do
+  let(:dir) { @dir = Pathname.new(Dir.mktmpdir) }
+
+  after(:each) do
+    FileUtils.rm_rf dir unless @dir.nil?
+  end
+
+  describe "#satisfied?" do
+    it "returns true if GPG2 is installed" do
+      ENV["PATH"] = dir/"bin"
+      (dir/"bin/gpg").write <<-EOS.undent
+        #!/bin/bash
+        echo 2.0.30
+      EOS
+      FileUtils.chmod 0755, dir/"bin/gpg"
+
+      expect(subject).to be_satisfied
+    end
+  end
+end
diff --git a/Library/Homebrew/test/gpg2_requirement_test.rb b/Library/Homebrew/test/gpg2_requirement_test.rb
deleted file mode 100644
index 3297c28512b39ef85eea42d33cc85ab11e6c9aa1..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/gpg2_requirement_test.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require "testing_env"
-require "requirements/gpg2_requirement"
-require "fileutils"
-
-class GPG2RequirementTests < Homebrew::TestCase
-  def setup
-    super
-    @dir = Pathname.new(mktmpdir)
-    (@dir/"bin/gpg").write <<-EOS.undent
-      #!/bin/bash
-      echo 2.0.30
-    EOS
-    FileUtils.chmod 0755, @dir/"bin/gpg"
-  end
-
-  def teardown
-    FileUtils.rm_rf @dir
-    super
-  end
-
-  def test_satisfied
-    ENV["PATH"] = @dir/"bin"
-    assert_predicate GPG2Requirement.new, :satisfied?
-  end
-end
diff --git a/Library/Homebrew/test/inreplace_spec.rb b/Library/Homebrew/test/inreplace_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5be44f50db8bf864f430b5aba3021cc7b2d43ef0
--- /dev/null
+++ b/Library/Homebrew/test/inreplace_spec.rb
@@ -0,0 +1,251 @@
+require "extend/string"
+require "tempfile"
+require "utils/inreplace"
+
+describe StringInreplaceExtension do
+  subject { string.extend(described_class) }
+
+  describe "#change_make_var!" do
+    context "flag" do
+      context "with spaces" do
+        let(:string) do
+          <<-EOS.undent
+            OTHER=def
+            FLAG = abc
+            FLAG2=abc
+          EOS
+        end
+
+        it "is successfully replaced" do
+          subject.change_make_var! "FLAG", "def"
+          expect(subject).to eq <<-EOS.undent
+            OTHER=def
+            FLAG=def
+            FLAG2=abc
+          EOS
+        end
+
+        it "is successfully appended" do
+          subject.change_make_var! "FLAG", "\\1 def"
+          expect(subject).to eq <<-EOS.undent
+            OTHER=def
+            FLAG=abc def
+            FLAG2=abc
+          EOS
+        end
+      end
+
+      context "with tabs" do
+        let(:string) do
+          <<-EOS.undent
+            CFLAGS\t=\t-Wall -O2
+            LDFLAGS\t=\t-lcrypto -lssl
+          EOS
+        end
+
+        it "is successfully replaced" do
+          subject.change_make_var! "CFLAGS", "-O3"
+          expect(subject).to eq <<-EOS.undent
+            CFLAGS=-O3
+            LDFLAGS\t=\t-lcrypto -lssl
+          EOS
+        end
+      end
+    end
+
+    context "empty flag between other flags" do
+      let(:string) do
+        <<-EOS.undent
+          OTHER=def
+          FLAG =
+          FLAG2=abc
+        EOS
+      end
+
+      it "is successfully replaced" do
+        subject.change_make_var! "FLAG", "def"
+        expect(subject).to eq <<-EOS.undent
+          OTHER=def
+          FLAG=def
+          FLAG2=abc
+        EOS
+      end
+    end
+
+    context "empty flag" do
+      let(:string) do
+        <<-EOS.undent
+          FLAG =
+          mv file_a file_b
+        EOS
+      end
+
+      it "is successfully replaced" do
+        subject.change_make_var! "FLAG", "def"
+        expect(subject).to eq <<-EOS.undent
+          FLAG=def
+          mv file_a file_b
+        EOS
+      end
+    end
+
+    context "shell-style variable" do
+      let(:string) do
+        <<-EOS.undent
+          OTHER=def
+          FLAG=abc
+          FLAG2=abc
+        EOS
+      end
+
+      it "is successfully replaced" do
+        subject.change_make_var! "FLAG", "def"
+        expect(subject).to eq <<-EOS.undent
+          OTHER=def
+          FLAG=def
+          FLAG2=abc
+        EOS
+      end
+    end
+  end
+
+  describe "#remove_make_var!" do
+    context "flag" do
+      context "with spaces" do
+        let(:string) do
+          <<-EOS.undent
+            OTHER=def
+            FLAG = abc
+            FLAG2 = def
+          EOS
+        end
+
+        it "is successfully removed" do
+          subject.remove_make_var! "FLAG"
+          expect(subject).to eq <<-EOS.undent
+            OTHER=def
+            FLAG2 = def
+          EOS
+        end
+      end
+
+      context "with tabs" do
+        let(:string) do
+          <<-EOS.undent
+            CFLAGS\t=\t-Wall -O2
+            LDFLAGS\t=\t-lcrypto -lssl
+          EOS
+        end
+
+        it "is successfully removed" do
+          subject.remove_make_var! "LDFLAGS"
+          expect(subject).to eq <<-EOS.undent
+            CFLAGS\t=\t-Wall -O2
+          EOS
+        end
+      end
+    end
+
+    context "multiple flags" do
+      let(:string) do
+        <<-EOS.undent
+          OTHER=def
+          FLAG = abc
+          FLAG2 = def
+          OTHER2=def
+        EOS
+      end
+
+      specify "are be successfully removed" do
+        subject.remove_make_var! ["FLAG", "FLAG2"]
+        expect(subject).to eq <<-EOS.undent
+          OTHER=def
+          OTHER2=def
+        EOS
+      end
+    end
+  end
+
+  describe "#get_make_var" do
+    context "with spaces" do
+      let(:string) do
+        <<-EOS.undent
+          CFLAGS = -Wall -O2
+          LDFLAGS = -lcrypto -lssl
+        EOS
+      end
+
+      it "extracts the value for a given variable" do
+        expect(subject.get_make_var("CFLAGS")).to eq("-Wall -O2")
+      end
+    end
+
+    context "with tabs" do
+      let(:string) do
+        <<-EOS.undent
+          CFLAGS\t=\t-Wall -O2
+          LDFLAGS\t=\t-lcrypto -lssl
+        EOS
+      end
+
+      it "extracts the value for a given variable" do
+        expect(subject.get_make_var("CFLAGS")).to eq("-Wall -O2")
+      end
+    end
+  end
+
+  describe "#sub!" do
+    let(:string) { "foo" }
+
+    it "replaces the first occurence" do
+      subject.sub!("o", "e")
+      expect(subject).to eq("feo")
+    end
+  end
+
+  describe "#gsub!" do
+    let(:string) { "foo" }
+
+    it "replaces the all occurences" do
+      subject.gsub!("o", "e") # rubocop:disable Performance/StringReplacement
+      expect(subject).to eq("fee")
+    end
+  end
+end
+
+describe Utils::Inreplace do
+  let(:file) { Tempfile.new("test") }
+
+  before(:each) do
+    file.write <<-EOS.undent
+      a
+      b
+      c
+    EOS
+  end
+
+  after(:each) { file.unlink }
+
+  it "raises error if there is nothing to replace" do
+    expect {
+      described_class.inreplace file.path, "d", "f"
+    }.to raise_error(Utils::InreplaceError)
+  end
+
+  it "raises error if there is nothing to replace" do
+    expect {
+      described_class.inreplace(file.path) do |s|
+        s.gsub!("d", "f") # rubocop:disable Performance/StringReplacement
+      end
+    }.to raise_error(Utils::InreplaceError)
+  end
+
+  it "raises error if there is nothing to replace" do
+    expect {
+      described_class.inreplace(file.path) do |s|
+        s.change_make_var! "VAR", "value"
+        s.remove_make_var! "VAR2"
+      end
+    }.to raise_error(Utils::InreplaceError)
+  end
+end
diff --git a/Library/Homebrew/test/inreplace_test.rb b/Library/Homebrew/test/inreplace_test.rb
deleted file mode 100644
index 0e62f9d3f9691ddcc93b8f48138db9bfe283f29e..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/inreplace_test.rb
+++ /dev/null
@@ -1,119 +0,0 @@
-require "testing_env"
-require "extend/string"
-require "utils/inreplace"
-
-class InreplaceTest < Homebrew::TestCase
-  def test_change_make_var
-    # Replace flag
-    s1 = "OTHER=def\nFLAG = abc\nFLAG2=abc"
-    s1.extend(StringInreplaceExtension)
-    s1.change_make_var! "FLAG", "def"
-    assert_equal "OTHER=def\nFLAG=def\nFLAG2=abc", s1
-  end
-
-  def test_change_make_var_empty
-    # Replace empty flag
-    s1 = "OTHER=def\nFLAG = \nFLAG2=abc"
-    s1.extend(StringInreplaceExtension)
-    s1.change_make_var! "FLAG", "def"
-    assert_equal "OTHER=def\nFLAG=def\nFLAG2=abc", s1
-  end
-
-  def test_change_make_var_empty_2
-    # Replace empty flag
-    s1 = "FLAG = \nmv file_a file_b"
-    s1.extend(StringInreplaceExtension)
-    s1.change_make_var! "FLAG", "def"
-    assert_equal "FLAG=def\nmv file_a file_b", s1
-  end
-
-  def test_change_make_var_append
-    # Append to flag
-    s1 = "OTHER=def\nFLAG = abc\nFLAG2=abc"
-    s1.extend(StringInreplaceExtension)
-    s1.change_make_var! "FLAG", "\\1 def"
-    assert_equal "OTHER=def\nFLAG=abc def\nFLAG2=abc", s1
-  end
-
-  def test_change_make_var_shell_style
-    # Shell variables have no spaces around =
-    s1 = "OTHER=def\nFLAG=abc\nFLAG2=abc"
-    s1.extend(StringInreplaceExtension)
-    s1.change_make_var! "FLAG", "def"
-    assert_equal "OTHER=def\nFLAG=def\nFLAG2=abc", s1
-  end
-
-  def test_remove_make_var
-    # Replace flag
-    s1 = "OTHER=def\nFLAG = abc\nFLAG2 = def"
-    s1.extend(StringInreplaceExtension)
-    s1.remove_make_var! "FLAG"
-    assert_equal "OTHER=def\nFLAG2 = def", s1
-  end
-
-  def test_remove_make_vars
-    # Replace flag
-    s1 = "OTHER=def\nFLAG = abc\nFLAG2 = def\nOTHER2=def"
-    s1.extend(StringInreplaceExtension)
-    s1.remove_make_var! ["FLAG", "FLAG2"]
-    assert_equal "OTHER=def\nOTHER2=def", s1
-  end
-
-  def test_get_make_var
-    s = "CFLAGS = -Wall -O2\nLDFLAGS = -lcrypto -lssl"
-    s.extend(StringInreplaceExtension)
-    assert_equal "-Wall -O2", s.get_make_var("CFLAGS")
-  end
-
-  def test_change_make_var_with_tabs
-    s = "CFLAGS\t=\t-Wall -O2\nLDFLAGS\t=\t-lcrypto -lssl"
-    s.extend(StringInreplaceExtension)
-
-    assert_equal "-Wall -O2", s.get_make_var("CFLAGS")
-
-    s.change_make_var! "CFLAGS", "-O3"
-    assert_equal "CFLAGS=-O3\nLDFLAGS\t=\t-lcrypto -lssl", s
-
-    s.remove_make_var! "LDFLAGS"
-    assert_equal "CFLAGS=-O3\n", s
-  end
-
-  def test_sub_gsub
-    s = "foo"
-    s.extend(StringInreplaceExtension)
-
-    s.sub!("f", "b")
-    assert_equal "boo", s
-
-    # Under current context, we are testing `String#gsub!`, so let's disable rubocop temporarily.
-    s.gsub!("o", "e") # rubocop:disable Performance/StringReplacement
-    assert_equal "bee", s
-  end
-
-  def test_inreplace_errors
-    require "tempfile"
-    extend(Utils::Inreplace)
-
-    file = Tempfile.new("test")
-
-    file.write "a\nb\nc\n"
-
-    assert_raises(Utils::InreplaceError) do
-      inreplace file.path, "d", "f"
-    end
-
-    assert_raises(Utils::InreplaceError) do
-      # Under current context, we are testing `String#gsub!`, so let's disable rubocop temporarily.
-      inreplace(file.path) { |s| s.gsub!("d", "f") } # rubocop:disable Performance/StringReplacement
-    end
-
-    assert_raises(Utils::InreplaceError) do
-      inreplace(file.path) do |s|
-        s.change_make_var! "VAR", "value"
-        s.remove_make_var! "VAR2"
-      end
-    end
-  ensure
-    file.unlink
-  end
-end
diff --git a/Library/Homebrew/test/java_requirement_spec.rb b/Library/Homebrew/test/java_requirement_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5adf64c7c2d943630b520f11ea3b21c2f5a4a69a
--- /dev/null
+++ b/Library/Homebrew/test/java_requirement_spec.rb
@@ -0,0 +1,107 @@
+require "requirements/java_requirement"
+
+describe JavaRequirement do
+  subject { described_class.new([]) }
+
+  before(:each) do
+    ENV["JAVA_HOME"] = nil
+  end
+
+  describe "#message" do
+    its(:message) { is_expected.to match(/Java is required to install this formula./) }
+  end
+
+  describe "#inspect" do
+    subject { described_class.new(%w[1.7+]) }
+    its(:inspect) { is_expected.to eq('#<JavaRequirement: "java" [] version="1.7+">') }
+  end
+
+  describe "#display_s" do
+    context "without specific version" do
+      its(:display_s) { is_expected.to eq("java") }
+    end
+
+    context "with version 1.8" do
+      subject { described_class.new(%w[1.8]) }
+      its(:display_s) { is_expected.to eq("java = 1.8") }
+    end
+
+    context "with version 1.8+" do
+      subject { described_class.new(%w[1.8+]) }
+      its(:display_s) { is_expected.to eq("java >= 1.8") }
+    end
+  end
+
+  describe "#satisfied?" do
+    subject { described_class.new(%w[1.8]) }
+
+    it "returns false if no `java` executable can be found" do
+      allow(File).to receive(:executable?).and_return(false)
+      expect(subject).not_to be_satisfied
+    end
+
+    it "returns true if #preferred_java returns a path" do
+      allow(subject).to receive(:preferred_java).and_return(Pathname.new("/usr/bin/java"))
+      expect(subject).to be_satisfied
+    end
+
+    context "when #possible_javas contains paths" do
+      let(:path) { Pathname.new(Dir.mktmpdir) }
+      let(:java) { path/"java" }
+
+      def setup_java_with_version(version)
+        IO.write java, <<-EOS.undent
+          #!/bin/sh
+          echo 'java version "#{version}"'
+        EOS
+        FileUtils.chmod "+x", java
+      end
+
+      before(:each) do
+        allow(subject).to receive(:possible_javas).and_return([java])
+      end
+
+      after(:each) do
+        path.rmtree
+      end
+
+      context "and 1.7 is required" do
+        subject { described_class.new(%w[1.7]) }
+
+        it "returns false if all are lower" do
+          setup_java_with_version "1.6.0_5"
+          expect(subject).not_to be_satisfied
+        end
+
+        it "returns true if one is equal" do
+          setup_java_with_version "1.7.0_5"
+          expect(subject).to be_satisfied
+        end
+
+        it "returns false if all are higher" do
+          setup_java_with_version "1.8.0_5"
+          expect(subject).not_to be_satisfied
+        end
+      end
+
+      context "and 1.7+ is required" do
+        subject { described_class.new(%w[1.7+]) }
+
+        it "returns false if all are lower" do
+          setup_java_with_version "1.6.0_5"
+          expect(subject).not_to be_satisfied
+        end
+
+        it "returns true if one is equal" do
+          setup_java_with_version "1.7.0_5"
+          expect(subject).to be_satisfied
+        end
+
+        it "returns true if one is higher" do
+          setup_java_with_version "1.8.0_5"
+          expect(subject).to be_satisfied
+        end
+      end
+    end
+  end
+end
diff --git a/Library/Homebrew/test/java_requirement_test.rb b/Library/Homebrew/test/java_requirement_test.rb
deleted file mode 100644
index d0b51f92cfe72357bb500af330b88119eee9b826..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/java_requirement_test.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-require "testing_env"
-require "requirements/java_requirement"
-
-class JavaRequirementTests < Homebrew::TestCase
-  def setup
-    super
-    ENV["JAVA_HOME"] = nil
-  end
-
-  def test_message
-    a = JavaRequirement.new([])
-    assert_match(/Java is required to install this formula./, a.message)
-  end
-
-  def test_inspect
-    a = JavaRequirement.new(%w[1.7+])
-    assert_equal a.inspect, '#<JavaRequirement: "java" [] version="1.7+">'
-  end
-
-  def test_display_s
-    x = JavaRequirement.new([])
-    assert_equal x.display_s, "java"
-    y = JavaRequirement.new(%w[1.8])
-    assert_equal y.display_s, "java = 1.8"
-    z = JavaRequirement.new(%w[1.8+])
-    assert_equal z.display_s, "java >= 1.8"
-  end
-
-  def test_satisfied?
-    a = JavaRequirement.new(%w[1.8])
-    File.stubs(:executable?).returns(false)
-    refute_predicate a, :satisfied?
-
-    b = JavaRequirement.new([])
-    b.stubs(:preferred_java).returns(Pathname.new("/usr/bin/java"))
-    assert_predicate b, :satisfied?
-
-    c = JavaRequirement.new(%w[1.7+])
-    c.stubs(:possible_javas).returns([Pathname.new("/usr/bin/java")])
-    Utils.stubs(:popen_read).returns('java version "1.6.0_5"')
-    refute_predicate c, :satisfied?
-    Utils.stubs(:popen_read).returns('java version "1.8.0_5"')
-    assert_predicate c, :satisfied?
-
-    d = JavaRequirement.new(%w[1.7])
-    d.stubs(:possible_javas).returns([Pathname.new("/usr/bin/java")])
-    Utils.stubs(:popen_read).returns('java version "1.8.0_5"')
-    refute_predicate d, :satisfied?
-  end
-end
diff --git a/Library/Homebrew/test/os/mac/blacklist_test.rb b/Library/Homebrew/test/os/mac/blacklist_test.rb
deleted file mode 100644
index 26dd84ed9f888992fdc5bda51df6f501ac4e3c9e..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/os/mac/blacklist_test.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require "testing_env"
-require "blacklist"
-
-class OSMacBlacklistTests < Homebrew::TestCase
-  def assert_blacklisted(s)
-    assert blacklisted?(s), "'#{s}' should be blacklisted"
-  end
-
-  def test_xcode
-    %w[xcode Xcode].each { |s| assert_blacklisted s }
-  end
-end
diff --git a/Library/Homebrew/test/os/mac/bottle_tag_test.rb b/Library/Homebrew/test/os/mac/bottle_tag_test.rb
deleted file mode 100644
index 996bd4d536468ffb4fe18b698de78c7e40285850..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/os/mac/bottle_tag_test.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-require "testing_env"
-require "utils/bottles"
-
-class OSMacBottleTagTests < Homebrew::TestCase
-  def test_tag_tiger_ppc
-    MacOS.stubs(:version).returns(MacOS::Version.new("10.4"))
-    Hardware::CPU.stubs(:type).returns(:ppc)
-    Hardware::CPU.stubs(:family).returns(:foo)
-    MacOS.stubs(:prefer_64_bit?).returns(false)
-    assert_equal :tiger_foo, Utils::Bottles.tag
-  end
-
-  def test_tag_tiger_intel
-    MacOS.stubs(:version).returns(MacOS::Version.new("10.4"))
-    Hardware::CPU.stubs(:type).returns(:intel)
-    MacOS.stubs(:prefer_64_bit?).returns(false)
-    assert_equal :tiger, Utils::Bottles.tag
-  end
-
-  def test_tag_tiger_ppc_64
-    MacOS.stubs(:version).returns(MacOS::Version.new("10.4"))
-    Hardware::CPU.stubs(:type).returns(:ppc)
-    Hardware::CPU.stubs(:family).returns(:g5)
-    MacOS.stubs(:prefer_64_bit?).returns(true)
-    assert_equal :tiger_g5_64, Utils::Bottles.tag
-  end
-
-  # Note that this will probably never be used
-  def test_tag_tiger_intel_64
-    MacOS.stubs(:version).returns(MacOS::Version.new("10.4"))
-    Hardware::CPU.stubs(:type).returns(:intel)
-    MacOS.stubs(:prefer_64_bit?).returns(true)
-    assert_equal :tiger_64, Utils::Bottles.tag
-  end
-
-  def test_tag_leopard_intel
-    MacOS.stubs(:version).returns(MacOS::Version.new("10.5"))
-    Hardware::CPU.stubs(:type).returns(:intel)
-    MacOS.stubs(:prefer_64_bit?).returns(false)
-    assert_equal :leopard, Utils::Bottles.tag
-  end
-
-  def test_tag_leopard_ppc_64
-    MacOS.stubs(:version).returns(MacOS::Version.new("10.5"))
-    Hardware::CPU.stubs(:type).returns(:ppc)
-    Hardware::CPU.stubs(:family).returns(:g5)
-    MacOS.stubs(:prefer_64_bit?).returns(true)
-    assert_equal :leopard_g5_64, Utils::Bottles.tag
-  end
-
-  def test_tag_leopard_intel_64
-    MacOS.stubs(:version).returns(MacOS::Version.new("10.5"))
-    Hardware::CPU.stubs(:type).returns(:intel)
-    MacOS.stubs(:prefer_64_bit?).returns(true)
-    assert_equal :leopard_64, Utils::Bottles.tag
-  end
-
-  def test_tag_snow_leopard_32
-    MacOS.stubs(:version).returns(MacOS::Version.new("10.6"))
-    Hardware::CPU.stubs(:is_64_bit?).returns(false)
-    assert_equal :snow_leopard_32, Utils::Bottles.tag
-  end
-
-  def test_tag_snow_leopard_64
-    MacOS.stubs(:version).returns(MacOS::Version.new("10.6"))
-    Hardware::CPU.stubs(:is_64_bit?).returns(true)
-    assert_equal :snow_leopard, Utils::Bottles.tag
-  end
-
-  def test_tag_lion
-    MacOS.stubs(:version).returns(MacOS::Version.new("10.7"))
-    assert_equal :lion, Utils::Bottles.tag
-  end
-
-  def test_tag_mountain_lion
-    MacOS.stubs(:version).returns(MacOS::Version.new("10.8"))
-    assert_equal :mountain_lion, Utils::Bottles.tag
-  end
-end
diff --git a/Library/Homebrew/test/os/mac/java_requirement_spec.rb b/Library/Homebrew/test/os/mac/java_requirement_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f6404db926c4311ff8505bc7ada6362a5c422088
--- /dev/null
+++ b/Library/Homebrew/test/os/mac/java_requirement_spec.rb
@@ -0,0 +1,34 @@
+require "requirements/java_requirement"
+require "fileutils"
+
+describe JavaRequirement do
+  subject { described_class.new(%w[1.8]) }
+  let(:java_home) { Dir.mktmpdir }
+  let(:java_home_path) { Pathname.new(java_home) }
+
+  before(:each) do
+    FileUtils.mkdir java_home_path/"bin"
+    FileUtils.touch java_home_path/"bin/java"
+    allow(subject).to receive(:preferred_java).and_return(java_home_path/"bin/java")
+    expect(subject).to be_satisfied
+  end
+
+  after(:each) { java_home_path.rmtree }
+
+  specify "Apple Java environment" do
+    expect(ENV).to receive(:prepend_path)
+    expect(ENV).to receive(:append_to_cflags)
+
+    subject.modify_build_environment
+    expect(ENV["JAVA_HOME"]).to eq(java_home)
+  end
+
+  specify "Oracle Java environment" do
+    FileUtils.mkdir java_home_path/"include"
+    expect(ENV).to receive(:prepend_path)
+    expect(ENV).to receive(:append_to_cflags).twice
+
+    subject.modify_build_environment
+    expect(ENV["JAVA_HOME"]).to eq(java_home)
+  end
+end
diff --git a/Library/Homebrew/test/os/mac/java_requirement_test.rb b/Library/Homebrew/test/os/mac/java_requirement_test.rb
deleted file mode 100644
index 83c1af95ca3c0715fc0a821c92fa5332379a0af4..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/os/mac/java_requirement_test.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-require "testing_env"
-require "requirements/java_requirement"
-require "fileutils"
-
-class OSMacJavaRequirementTests < Homebrew::TestCase
-  def setup
-    super
-    @java_req = JavaRequirement.new(%w[1.8])
-    @tmp_java_home = mktmpdir
-    @tmp_pathname = Pathname.new(@tmp_java_home)
-    FileUtils.mkdir @tmp_pathname/"bin"
-    FileUtils.touch @tmp_pathname/"bin/java"
-    @java_req.stubs(:preferred_java).returns(@tmp_pathname/"bin/java")
-    @java_req.satisfied?
-  end
-
-  def test_java_env_apple
-    ENV.expects(:prepend_path)
-    ENV.expects(:append_to_cflags)
-    @java_req.modify_build_environment
-    assert_equal ENV["JAVA_HOME"], @tmp_java_home
-  end
-
-  def test_java_env_oracle
-    FileUtils.mkdir @tmp_pathname/"include"
-    ENV.expects(:prepend_path)
-    ENV.expects(:append_to_cflags).twice
-    @java_req.modify_build_environment
-    assert_equal ENV["JAVA_HOME"], @tmp_java_home
-  end
-end
diff --git a/Library/Homebrew/test/os/mac/keg_spec.rb b/Library/Homebrew/test/os/mac/keg_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..562c2ba6a69152388b6131a5b97d040a5ac0d9c2
--- /dev/null
+++ b/Library/Homebrew/test/os/mac/keg_spec.rb
@@ -0,0 +1,32 @@
+require "keg"
+
+describe Keg do
+  include FileUtils
+
+  subject { described_class.new(keg_path) }
+
+  describe "#mach_o_files" do
+    let(:keg_path) { HOMEBREW_CELLAR/"a/1.0" }
+
+    before(:each) { (keg_path/"lib").mkpath }
+
+    after(:each) { subject.unlink }
+
+    it "skips hardlinks" do
+      cp dylib_path("i386"), keg_path/"lib/i386.dylib"
+      ln keg_path/"lib/i386.dylib", keg_path/"lib/i386_hardlink.dylib"
+
+      subject.link
+      expect(subject.mach_o_files.count).to eq(1)
+    end
+
+    it "isn't confused by symlinks" do
+      cp dylib_path("i386"), keg_path/"lib/i386.dylib"
+      ln keg_path/"lib/i386.dylib", keg_path/"lib/i386_hardlink.dylib"
+      ln_s keg_path/"lib/i386.dylib", keg_path/"lib/i386_symlink.dylib"
+
+      subject.link
+      expect(subject.mach_o_files.count).to eq(1)
+    end
+  end
+end
diff --git a/Library/Homebrew/test/os/mac/keg_test.rb b/Library/Homebrew/test/os/mac/keg_test.rb
deleted file mode 100644
index d1103415dece628c685ee42d6471407bbea01fe1..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/os/mac/keg_test.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-require "testing_env"
-require "keg"
-require "stringio"
-
-class OSMacLinkTests < Homebrew::TestCase
-  include FileUtils
-
-  def setup
-    super
-
-    keg = HOMEBREW_CELLAR.join("foo", "1.0")
-    keg.join("bin").mkpath
-
-    %w[hiworld helloworld goodbye_cruel_world].each do |file|
-      touch keg.join("bin", file)
-    end
-
-    @keg = Keg.new(keg)
-    @dst = HOMEBREW_PREFIX.join("bin", "helloworld")
-    @nonexistent = Pathname.new("/some/nonexistent/path")
-
-    @mode = OpenStruct.new
-
-    @old_stdout = $stdout
-    $stdout = StringIO.new
-
-    mkpath HOMEBREW_PREFIX/"bin"
-    mkpath HOMEBREW_PREFIX/"lib"
-  end
-
-  def teardown
-    @keg.unlink
-
-    $stdout = @old_stdout
-
-    rmtree HOMEBREW_PREFIX/"lib"
-
-    super
-  end
-
-  def test_mach_o_files_skips_hardlinks
-    a = HOMEBREW_CELLAR/"a/1.0"
-    (a/"lib").mkpath
-    FileUtils.cp dylib_path("i386"), a/"lib/i386.dylib"
-    FileUtils.ln a/"lib/i386.dylib", a/"lib/i386_link.dylib"
-
-    keg = Keg.new(a)
-    keg.link
-
-    assert_equal 1, keg.mach_o_files.size
-  ensure
-    keg.unlink
-  end
-
-  def test_mach_o_files_isnt_confused_by_symlinks
-    a = HOMEBREW_CELLAR/"a/1.0"
-    (a/"lib").mkpath
-    FileUtils.cp dylib_path("i386"), a/"lib/i386.dylib"
-    FileUtils.ln a/"lib/i386.dylib", a/"lib/i386_link.dylib"
-    FileUtils.ln_s a/"lib/i386.dylib", a/"lib/1.dylib"
-
-    keg = Keg.new(a)
-    keg.link
-
-    assert_equal 1, keg.mach_o_files.size
-  ensure
-    keg.unlink
-  end
-end
diff --git a/Library/Homebrew/test/os/mac/language_test.rb b/Library/Homebrew/test/os/mac/language_test.rb
deleted file mode 100644
index e328db5e00d3d060c795356ee2dd2edaf53d1918..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/os/mac/language_test.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-require "testing_env"
-require "locale"
-require "os/mac"
-
-class OSMacLanguageTests < Homebrew::TestCase
-  def test_languages_format
-    OS::Mac.languages.each do |language|
-      assert_nothing_raised do
-        Locale.parse(language)
-      end
-    end
-  end
-
-  def test_language_format
-    assert_nothing_raised do
-      Locale.parse(OS::Mac.language)
-    end
-  end
-end
diff --git a/Library/Homebrew/test/os/mac/mach_spec.rb b/Library/Homebrew/test/os/mac/mach_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5c9aafcbb9e3ab4675b8f182e744e8ca285bc0c5
--- /dev/null
+++ b/Library/Homebrew/test/os/mac/mach_spec.rb
@@ -0,0 +1,198 @@
+describe "Mach-O Pathname tests" do
+  specify "fat dylib" do
+    pn = dylib_path("fat")
+    expect(pn).to be_universal
+    expect(pn).not_to be_i386
+    expect(pn).not_to be_x86_64
+    expect(pn).not_to be_ppc7400
+    expect(pn).not_to be_ppc64
+    expect(pn).to be_dylib
+    expect(pn).not_to be_mach_o_executable
+    expect(pn).not_to be_text_executable
+    expect(pn.arch).to eq(:universal)
+  end
+
+  specify "i386 dylib" do
+    pn = dylib_path("i386")
+    expect(pn).not_to be_universal
+    expect(pn).to be_i386
+    expect(pn).not_to be_x86_64
+    expect(pn).not_to be_ppc7400
+    expect(pn).not_to be_ppc64
+    expect(pn).to be_dylib
+    expect(pn).not_to be_mach_o_executable
+    expect(pn).not_to be_text_executable
+    expect(pn).not_to be_mach_o_bundle
+  end
+
+  specify "x86_64 dylib" do
+    pn = dylib_path("x86_64")
+    expect(pn).not_to be_universal
+    expect(pn).not_to be_i386
+    expect(pn).to be_x86_64
+    expect(pn).not_to be_ppc7400
+    expect(pn).not_to be_ppc64
+    expect(pn).to be_dylib
+    expect(pn).not_to be_mach_o_executable
+    expect(pn).not_to be_text_executable
+    expect(pn).not_to be_mach_o_bundle
+  end
+
+  specify "Mach-O executable" do
+    pn = Pathname.new("#{TEST_FIXTURE_DIR}/mach/a.out")
+    expect(pn).to be_universal
+    expect(pn).not_to be_i386
+    expect(pn).not_to be_x86_64
+    expect(pn).not_to be_ppc7400
+    expect(pn).not_to be_ppc64
+    expect(pn).not_to be_dylib
+    expect(pn).to be_mach_o_executable
+    expect(pn).not_to be_text_executable
+    expect(pn).not_to be_mach_o_bundle
+  end
+
+  specify "fat bundle" do
+    pn = bundle_path("fat")
+    expect(pn).to be_universal
+    expect(pn).not_to be_i386
+    expect(pn).not_to be_x86_64
+    expect(pn).not_to be_ppc7400
+    expect(pn).not_to be_ppc64
+    expect(pn).not_to be_dylib
+    expect(pn).not_to be_mach_o_executable
+    expect(pn).not_to be_text_executable
+    expect(pn).to be_mach_o_bundle
+  end
+
+  specify "i386 bundle" do
+    pn = bundle_path("i386")
+    expect(pn).not_to be_universal
+    expect(pn).to be_i386
+    expect(pn).not_to be_x86_64
+    expect(pn).not_to be_ppc7400
+    expect(pn).not_to be_ppc64
+    expect(pn).not_to be_dylib
+    expect(pn).not_to be_mach_o_executable
+    expect(pn).not_to be_text_executable
+    expect(pn).to be_mach_o_bundle
+  end
+
+  specify "x86_64 bundle" do
+    pn = bundle_path("x86_64")
+    expect(pn).not_to be_universal
+    expect(pn).not_to be_i386
+    expect(pn).to be_x86_64
+    expect(pn).not_to be_ppc7400
+    expect(pn).not_to be_ppc64
+    expect(pn).not_to be_dylib
+    expect(pn).not_to be_mach_o_executable
+    expect(pn).not_to be_text_executable
+    expect(pn).to be_mach_o_bundle
+  end
+
+  specify "non-Mach-O" do
+    pn = Pathname.new("#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz")
+    expect(pn).not_to be_universal
+    expect(pn).not_to be_i386
+    expect(pn).not_to be_x86_64
+    expect(pn).not_to be_ppc7400
+    expect(pn).not_to be_ppc64
+    expect(pn).not_to be_dylib
+    expect(pn).not_to be_mach_o_executable
+    expect(pn).not_to be_text_executable
+    expect(pn).not_to be_mach_o_bundle
+    expect(pn.arch).to eq(:dunno)
+  end
+end
+
+describe ArchitectureListExtension do
+  let(:archs) { [:i386, :x86_64, :ppc7400, :ppc64].extend(described_class) }
+
+  specify "universal checks" do
+    expect(archs).to be_universal
+    expect(archs).to be_intel_universal
+    expect(archs).to be_ppc_universal
+    expect(archs).to be_cross_universal
+    expect(archs).to be_fat
+
+    non_universal = [:i386].extend(described_class)
+    expect(non_universal).not_to be_universal
+
+    intel_only = [:i386, :x86_64].extend(described_class)
+    expect(intel_only).to be_universal
+    expect(intel_only).not_to be_ppc_universal
+    expect(intel_only).not_to be_cross_universal
+
+    ppc_only = [:ppc970, :ppc64].extend(described_class)
+    expect(ppc_only).to be_universal
+    expect(ppc_only).not_to be_intel_universal
+    expect(ppc_only).not_to be_cross_universal
+
+    cross = [:ppc7400, :i386].extend(described_class)
+    expect(cross).to be_universal
+    expect(cross).not_to be_intel_universal
+    expect(cross).not_to be_ppc_universal
+  end
+
+  specify "messaging flags" do
+    archs.remove_ppc!
+    expect(archs.length).to eq(2)
+    expect(archs.as_arch_flags).to match(/-arch i386/)
+    expect(archs.as_arch_flags).to match(/-arch x86_64/)
+  end
+
+  specify "architecture flags" do
+    pn = dylib_path("fat")
+    expect(pn.archs).to be_intel_universal
+    expect(pn.archs.as_arch_flags).to eq("-arch x86_64 -arch i386")
+    expect(pn.archs.as_cmake_arch_flags).to eq("x86_64;i386")
+  end
+end
+
+describe "text executables" do
+  let(:pn) { HOMEBREW_PREFIX/"an_executable" }
+
+  after(:each) { pn.unlink }
+
+  specify "simple shebang" do
+    pn.write "#!/bin/sh"
+    expect(pn).not_to be_universal
+    expect(pn).not_to be_i386
+    expect(pn).not_to be_x86_64
+    expect(pn).not_to be_ppc7400
+    expect(pn).not_to be_ppc64
+    expect(pn).not_to be_dylib
+    expect(pn).not_to be_mach_o_executable
+    expect(pn).to be_text_executable
+    expect(pn.archs).to eq([])
+    expect(pn.arch).to eq(:dunno)
+  end
+
+  specify "shebang with options" do
+    pn.write "#! /usr/bin/perl -w"
+    expect(pn).not_to be_universal
+    expect(pn).not_to be_i386
+    expect(pn).not_to be_x86_64
+    expect(pn).not_to be_ppc7400
+    expect(pn).not_to be_ppc64
+    expect(pn).not_to be_dylib
+    expect(pn).not_to be_mach_o_executable
+    expect(pn).to be_text_executable
+    expect(pn.archs).to eq([])
+    expect(pn.arch).to eq(:dunno)
+  end
+
+  specify "malformed shebang" do
+    pn.write " #!"
+    expect(pn).not_to be_universal
+    expect(pn).not_to be_i386
+    expect(pn).not_to be_x86_64
+    expect(pn).not_to be_ppc7400
+    expect(pn).not_to be_ppc64
+    expect(pn).not_to be_dylib
+    expect(pn).not_to be_mach_o_executable
+    expect(pn).not_to be_text_executable
+    expect(pn.archs).to eq([])
+    expect(pn.arch).to eq(:dunno)
+  end
+end
diff --git a/Library/Homebrew/test/os/mac/mach_test.rb b/Library/Homebrew/test/os/mac/mach_test.rb
deleted file mode 100644
index ed0424be6595f14703b4a21d9684830686d71355..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/os/mac/mach_test.rb
+++ /dev/null
@@ -1,211 +0,0 @@
-require "testing_env"
-
-class MachOPathnameTests < Homebrew::TestCase
-  def test_fat_dylib
-    pn = dylib_path("fat")
-    assert_predicate pn, :universal?
-    refute_predicate pn, :i386?
-    refute_predicate pn, :x86_64?
-    refute_predicate pn, :ppc7400?
-    refute_predicate pn, :ppc64?
-    assert_predicate pn, :dylib?
-    refute_predicate pn, :mach_o_executable?
-    refute_predicate pn, :text_executable?
-    assert_equal :universal, pn.arch
-  end
-
-  def test_i386_dylib
-    pn = dylib_path("i386")
-    refute_predicate pn, :universal?
-    assert_predicate pn, :i386?
-    refute_predicate pn, :x86_64?
-    refute_predicate pn, :ppc7400?
-    refute_predicate pn, :ppc64?
-    assert_predicate pn, :dylib?
-    refute_predicate pn, :mach_o_executable?
-    refute_predicate pn, :text_executable?
-    refute_predicate pn, :mach_o_bundle?
-  end
-
-  def test_x86_64_dylib
-    pn = dylib_path("x86_64")
-    refute_predicate pn, :universal?
-    refute_predicate pn, :i386?
-    assert_predicate pn, :x86_64?
-    refute_predicate pn, :ppc7400?
-    refute_predicate pn, :ppc64?
-    assert_predicate pn, :dylib?
-    refute_predicate pn, :mach_o_executable?
-    refute_predicate pn, :text_executable?
-    refute_predicate pn, :mach_o_bundle?
-  end
-
-  def test_mach_o_executable
-    pn = Pathname.new("#{TEST_FIXTURE_DIR}/mach/a.out")
-    assert_predicate pn, :universal?
-    refute_predicate pn, :i386?
-    refute_predicate pn, :x86_64?
-    refute_predicate pn, :ppc7400?
-    refute_predicate pn, :ppc64?
-    refute_predicate pn, :dylib?
-    assert_predicate pn, :mach_o_executable?
-    refute_predicate pn, :text_executable?
-    refute_predicate pn, :mach_o_bundle?
-  end
-
-  def test_fat_bundle
-    pn = bundle_path("fat")
-    assert_predicate pn, :universal?
-    refute_predicate pn, :i386?
-    refute_predicate pn, :x86_64?
-    refute_predicate pn, :ppc7400?
-    refute_predicate pn, :ppc64?
-    refute_predicate pn, :dylib?
-    refute_predicate pn, :mach_o_executable?
-    refute_predicate pn, :text_executable?
-    assert_predicate pn, :mach_o_bundle?
-  end
-
-  def test_i386_bundle
-    pn = bundle_path("i386")
-    refute_predicate pn, :universal?
-    assert_predicate pn, :i386?
-    refute_predicate pn, :x86_64?
-    refute_predicate pn, :ppc7400?
-    refute_predicate pn, :ppc64?
-    refute_predicate pn, :dylib?
-    refute_predicate pn, :mach_o_executable?
-    refute_predicate pn, :text_executable?
-    assert_predicate pn, :mach_o_bundle?
-  end
-
-  def test_x86_64_bundle
-    pn = bundle_path("x86_64")
-    refute_predicate pn, :universal?
-    refute_predicate pn, :i386?
-    assert_predicate pn, :x86_64?
-    refute_predicate pn, :ppc7400?
-    refute_predicate pn, :ppc64?
-    refute_predicate pn, :dylib?
-    refute_predicate pn, :mach_o_executable?
-    refute_predicate pn, :text_executable?
-    assert_predicate pn, :mach_o_bundle?
-  end
-
-  def test_non_mach_o
-    pn = Pathname.new("#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz")
-    refute_predicate pn, :universal?
-    refute_predicate pn, :i386?
-    refute_predicate pn, :x86_64?
-    refute_predicate pn, :ppc7400?
-    refute_predicate pn, :ppc64?
-    refute_predicate pn, :dylib?
-    refute_predicate pn, :mach_o_executable?
-    refute_predicate pn, :text_executable?
-    refute_predicate pn, :mach_o_bundle?
-    assert_equal :dunno, pn.arch
-  end
-end
-
-class ArchitectureListExtensionTests < MachOPathnameTests
-  def setup
-    super
-    @archs = [:i386, :x86_64, :ppc7400, :ppc64].extend(ArchitectureListExtension)
-  end
-
-  def test_architecture_list_extension_universal_checks
-    assert_predicate @archs, :universal?
-    assert_predicate @archs, :intel_universal?
-    assert_predicate @archs, :ppc_universal?
-    assert_predicate @archs, :cross_universal?
-    assert_predicate @archs, :fat?
-
-    non_universal = [:i386].extend ArchitectureListExtension
-    refute_predicate non_universal, :universal?
-
-    intel_only = [:i386, :x86_64].extend ArchitectureListExtension
-    assert_predicate intel_only, :universal?
-    refute_predicate intel_only, :ppc_universal?
-    refute_predicate intel_only, :cross_universal?
-
-    ppc_only = [:ppc970, :ppc64].extend ArchitectureListExtension
-    assert_predicate ppc_only, :universal?
-    refute_predicate ppc_only, :intel_universal?
-    refute_predicate ppc_only, :cross_universal?
-
-    cross = [:ppc7400, :i386].extend ArchitectureListExtension
-    assert_predicate cross, :universal?
-    refute_predicate cross, :intel_universal?
-    refute_predicate cross, :ppc_universal?
-  end
-
-  def test_architecture_list_extension_massaging_flags
-    @archs.remove_ppc!
-    assert_equal 2, @archs.length
-    assert_match(/-arch i386/, @archs.as_arch_flags)
-    assert_match(/-arch x86_64/, @archs.as_arch_flags)
-  end
-
-  def test_architecture_list_arch_flags_methods
-    pn = dylib_path("fat")
-    assert_predicate pn.archs, :intel_universal?
-    assert_equal "-arch x86_64 -arch i386", pn.archs.as_arch_flags
-    assert_equal "x86_64;i386", pn.archs.as_cmake_arch_flags
-  end
-end
-
-class TextExecutableTests < Homebrew::TestCase
-  attr_reader :pn
-
-  def setup
-    super
-    @pn = HOMEBREW_PREFIX.join("an_executable")
-  end
-
-  def teardown
-    HOMEBREW_PREFIX.join("an_executable").unlink
-    super
-  end
-
-  def test_simple_shebang
-    pn.write "#!/bin/sh"
-    refute_predicate pn, :universal?
-    refute_predicate pn, :i386?
-    refute_predicate pn, :x86_64?
-    refute_predicate pn, :ppc7400?
-    refute_predicate pn, :ppc64?
-    refute_predicate pn, :dylib?
-    refute_predicate pn, :mach_o_executable?
-    assert_predicate pn, :text_executable?
-    assert_equal [], pn.archs
-    assert_equal :dunno, pn.arch
-  end
-
-  def test_shebang_with_options
-    pn.write "#! /usr/bin/perl -w"
-    refute_predicate pn, :universal?
-    refute_predicate pn, :i386?
-    refute_predicate pn, :x86_64?
-    refute_predicate pn, :ppc7400?
-    refute_predicate pn, :ppc64?
-    refute_predicate pn, :dylib?
-    refute_predicate pn, :mach_o_executable?
-    assert_predicate pn, :text_executable?
-    assert_equal [], pn.archs
-    assert_equal :dunno, pn.arch
-  end
-
-  def test_malformed_shebang
-    pn.write " #!"
-    refute_predicate pn, :universal?
-    refute_predicate pn, :i386?
-    refute_predicate pn, :x86_64?
-    refute_predicate pn, :ppc7400?
-    refute_predicate pn, :ppc64?
-    refute_predicate pn, :dylib?
-    refute_predicate pn, :mach_o_executable?
-    refute_predicate pn, :text_executable?
-    assert_equal [], pn.archs
-    assert_equal :dunno, pn.arch
-  end
-end
diff --git a/Library/Homebrew/test/os/mac/version_spec.rb b/Library/Homebrew/test/os/mac/version_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..797207b6788b44c34d6ac1825771ae3a5fc11242
--- /dev/null
+++ b/Library/Homebrew/test/os/mac/version_spec.rb
@@ -0,0 +1,51 @@
+require "version"
+require "os/mac/version"
+
+describe OS::Mac::Version do
+  subject { described_class.new("10.7") }
+
+  specify "comparison with Symbol" do
+    expect(subject).to be > :snow_leopard
+    expect(subject).to be == :lion
+    expect(subject).to be === :lion # rubocop:disable Style/CaseEquality
+    expect(subject).to be < :mountain_lion
+  end
+
+  specify "comparison with Fixnum" do
+    expect(subject).to be > 10
+    expect(subject).to be < 11
+  end
+
+  specify "comparison with Float" do
+    expect(subject).to be > 10.6
+    expect(subject).to be == 10.7
+    expect(subject).to be === 10.7 # rubocop:disable Style/CaseEquality
+    expect(subject).to be < 10.8
+  end
+
+  specify "comparison with String" do
+    expect(subject).to be > "10.6"
+    expect(subject).to be == "10.7"
+    expect(subject).to be === "10.7" # rubocop:disable Style/CaseEquality
+    expect(subject).to be < "10.8"
+  end
+
+  specify "comparison with Version" do
+    expect(subject).to be > Version.create("10.6")
+    expect(subject).to be == Version.create("10.7")
+    expect(subject).to be === Version.create("10.7") # rubocop:disable Style/CaseEquality
+    expect(subject).to be < Version.create("10.8")
+  end
+
+  specify "#from_symbol" do
+    expect(described_class.from_symbol(:lion)).to eq(subject)
+    expect { described_class.from_symbol(:foo) }
+      .to raise_error(ArgumentError)
+  end
+
+  specify "#pretty_name" do
+    expect(described_class.new("10.11").pretty_name).to eq("El Capitan")
+    expect(described_class.new("10.8").pretty_name).to eq("Mountain Lion")
+    expect(described_class.new("10.10").pretty_name).to eq("Yosemite")
+  end
+end
diff --git a/Library/Homebrew/test/os/mac/version_test.rb b/Library/Homebrew/test/os/mac/version_test.rb
deleted file mode 100644
index ba42176917a71f0542b4c6eca435cdcba79cd12a..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/os/mac/version_test.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-require "testing_env"
-require "version"
-require "os/mac/version"
-
-class OSMacVersionTests < Homebrew::TestCase
-  def setup
-    super
-    @v = MacOS::Version.new("10.7")
-  end
-
-  def test_compare_with_symbol
-    assert_operator @v, :>, :snow_leopard
-    assert_operator @v, :==, :lion
-    assert_operator @v, :===, :lion
-    assert_operator @v, :<, :mountain_lion
-  end
-
-  def test_compare_with_fixnum
-    assert_operator @v, :>, 10
-    assert_operator @v, :<, 11
-  end
-
-  def test_compare_with_float
-    assert_operator @v, :>, 10.6
-    assert_operator @v, :==, 10.7
-    assert_operator @v, :===, 10.7
-    assert_operator @v, :<, 10.8
-  end
-
-  def test_compare_with_string
-    assert_operator @v, :>, "10.6"
-    assert_operator @v, :==, "10.7"
-    assert_operator @v, :===, "10.7"
-    assert_operator @v, :<, "10.8"
-  end
-
-  def test_compare_with_version
-    assert_operator @v, :>, Version.create("10.6")
-    assert_operator @v, :==, Version.create("10.7")
-    assert_operator @v, :===, Version.create("10.7")
-    assert_operator @v, :<, Version.create("10.8")
-  end
-
-  def test_from_symbol
-    assert_equal @v, MacOS::Version.from_symbol(:lion)
-    assert_raises(ArgumentError) { MacOS::Version.from_symbol(:foo) }
-  end
-
-  def test_pretty_name
-    assert_equal "El Capitan", MacOS::Version.new("10.11").pretty_name
-    assert_equal "Mountain Lion", MacOS::Version.new("10.8").pretty_name
-    assert_equal "Yosemite", MacOS::Version.new("10.10").pretty_name
-  end
-end
diff --git a/Library/Homebrew/test/os/mac_spec.rb b/Library/Homebrew/test/os/mac_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..64178245112d47dc8bb6b52afff92b6ce7a8f94b
--- /dev/null
+++ b/Library/Homebrew/test/os/mac_spec.rb
@@ -0,0 +1,22 @@
+require "locale"
+require "os/mac"
+
+describe OS::Mac do
+  describe "::languages" do
+    specify "all languages can be parsed by Locale::parse" do
+      subject.languages.each do |language|
+        expect { Locale.parse(language) }.not_to raise_error
+      end
+    end
+  end
+
+  describe "::language" do
+    it "returns the first item from #languages" do
+      expect(subject.language).to eq(subject.languages.first)
+    end
+
+    it "can be parsed by Locale::parse" do
+      expect { Locale.parse(subject.language) }.not_to raise_error
+    end
+  end
+end
diff --git a/Library/Homebrew/test/pathname_spec.rb b/Library/Homebrew/test/pathname_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..da12d4347a852dbc8fb19a441bed42b31eb1222a
--- /dev/null
+++ b/Library/Homebrew/test/pathname_spec.rb
@@ -0,0 +1,300 @@
+require "tmpdir"
+require "extend/pathname"
+require "install_renamed"
+
+describe Pathname do
+  include FileUtils
+
+  let(:src) { Pathname.new(Dir.mktmpdir) }
+  let(:dst) { Pathname.new(Dir.mktmpdir) }
+  let(:file) { src/"foo" }
+  let(:dir) { src/"bar" }
+
+  after(:each) { rm_rf [src, dst] }
+
+  describe DiskUsageExtension do
+    before(:each) do
+      mkdir_p dir/"a-directory"
+      touch [dir/".DS_Store", dir/"a-file"]
+      File.truncate(dir/"a-file", 1_048_576)
+      ln_s dir/"a-file", dir/"a-symlink"
+      ln dir/"a-file", dir/"a-hardlink"
+    end
+
+    describe "#file_count" do
+      it "returns the number of files in a directory" do
+        expect(dir.file_count).to eq(3)
+      end
+    end
+
+    describe "#abv" do
+      context "when called on a directory" do
+        it "returns a string with the file count and disk usage" do
+          expect(dir.abv).to eq("3 files, 1M")
+        end
+      end
+
+      context "when called on a file" do
+        it "returns the disk usage" do
+          expect((dir/"a-file").abv).to eq("1M")
+        end
+      end
+    end
+  end
+
+  describe "#rmdir_if_possible" do
+    before(:each) { mkdir_p dir }
+
+    it "returns true and removes a directory if it doesn't contain files" do
+      expect(dir.rmdir_if_possible).to be true
+      expect(dir).not_to exist
+    end
+
+    it "returns false and doesn't delete a directory if it contains files" do
+      touch dir/"foo"
+      expect(dir.rmdir_if_possible).to be false
+      expect(dir).to be_a_directory
+    end
+
+    it "ignores .DS_Store files" do
+      touch dir/".DS_Store"
+      expect(dir.rmdir_if_possible).to be true
+      expect(dir).not_to exist
+    end
+  end
+
+  describe "#write" do
+    it "creates a file and writes to it" do
+      expect(file).not_to exist
+      file.write("CONTENT")
+      expect(File.read(file)).to eq("CONTENT")
+    end
+
+    it "raises an error if the file already exists" do
+      touch file
+      expect { file.write("CONTENT") }.to raise_error(RuntimeError)
+    end
+  end
+
+  describe "#append_lines" do
+    it "appends lines to a file" do
+      touch file
+
+      file.append_lines("CONTENT")
+      expect(File.read(file)).to eq <<-EOS.undent
+        CONTENT
+      EOS
+
+      file.append_lines("CONTENTS")
+      expect(File.read(file)).to eq <<-EOS.undent
+        CONTENT
+        CONTENTS
+      EOS
+    end
+
+    it "raises an error if the file does not exist" do
+      expect(file).not_to exist
+      expect { file.append_lines("CONTENT") }.to raise_error(RuntimeError)
+    end
+  end
+
+  describe "#atomic_write" do
+    it "atomically replaces a file" do
+      touch file
+      file.atomic_write("CONTENT")
+      expect(File.read(file)).to eq("CONTENT")
+    end
+
+    it "preserves permissions" do
+      File.open(file, "w", 0100777).close
+      file.atomic_write("CONTENT")
+      expect(file.stat.mode).to eq(0100777 & ~File.umask)
+    end
+
+    it "preserves default permissions" do
+      file.atomic_write("CONTENT")
+      sentinel = file.parent.join("sentinel")
+      touch sentinel
+      expect(file.stat.mode).to eq(sentinel.stat.mode)
+    end
+  end
+
+  describe "#ensure_writable" do
+    it "makes a file writable and restores permissions afterwards" do
+      touch file
+      chmod 0555, file
+      expect(file).not_to be_writable
+      file.ensure_writable do
+        expect(file).to be_writable
+      end
+      expect(file).not_to be_writable
+    end
+  end
+
+  describe "#extname" do
+    it "supports common multi-level archives" do
+      expect(Pathname.new("foo-0.1.tar.gz").extname).to eq(".tar.gz")
+      expect(Pathname.new("foo-0.1.cpio.gz").extname).to eq(".cpio.gz")
+    end
+  end
+
+  describe "#stem" do
+    it "returns the basename without double extensions" do
+      expect(Pathname("foo-0.1.tar.gz").stem).to eq("foo-0.1")
+      expect(Pathname("foo-0.1.cpio.gz").stem).to eq("foo-0.1")
+    end
+  end
+
+  describe "#install" do
+    before(:each) do
+      (src/"a.txt").write "This is sample file a."
+      (src/"b.txt").write "This is sample file b."
+    end
+
+    it "raises an error if the file doesn't exist" do
+      expect { dst.install "non_existent_file" }.to raise_error(Errno::ENOENT)
+    end
+
+    it "installs a file to a directory with its basename" do
+      touch file
+      dst.install(file)
+      expect(dst/file.basename).to exist
+      expect(file).not_to exist
+    end
+
+    it "creates intermediate directories" do
+      touch file
+      expect(dir).not_to be_a_directory
+      dir.install(file)
+      expect(dir).to be_a_directory
+    end
+
+    it "can install a file" do
+      dst.install src/"a.txt"
+      expect(dst/"a.txt").to exist, "a.txt was not installed"
+      expect(dst/"b.txt").not_to exist, "b.txt was installed."
+    end
+
+    it "can install an array of files" do
+      dst.install [src/"a.txt", src/"b.txt"]
+
+      expect(dst/"a.txt").to exist, "a.txt was not installed"
+      expect(dst/"b.txt").to exist, "b.txt was not installed"
+    end
+
+    it "can install a directory" do
+      bin = src/"bin"
+      bin.mkpath
+      mv Dir[src/"*.txt"], bin
+      dst.install bin
+
+      expect(dst/"bin/a.txt").to exist, "a.txt was not installed"
+      expect(dst/"bin/b.txt").to exist, "b.txt was not installed"
+    end
+
+    it "supports renaming files" do
+      dst.install src/"a.txt" => "c.txt"
+
+      expect(dst/"c.txt").to exist, "c.txt was not installed"
+      expect(dst/"a.txt").not_to exist, "a.txt was installed but not renamed"
+      expect(dst/"b.txt").not_to exist, "b.txt was installed"
+    end
+
+    it "supports renaming multiple files" do
+      dst.install(src/"a.txt" => "c.txt", src/"b.txt" => "d.txt")
+
+      expect(dst/"c.txt").to exist, "c.txt was not installed"
+      expect(dst/"d.txt").to exist, "d.txt was not installed"
+      expect(dst/"a.txt").not_to exist, "a.txt was installed but not renamed"
+      expect(dst/"b.txt").not_to exist, "b.txt was installed but not renamed"
+    end
+
+    it "supports renaming directories" do
+      bin = src/"bin"
+      bin.mkpath
+      mv Dir[src/"*.txt"], bin
+      dst.install bin => "libexec"
+
+      expect(dst/"bin").not_to exist, "bin was installed but not renamed"
+      expect(dst/"libexec/a.txt").to exist, "a.txt was not installed"
+      expect(dst/"libexec/b.txt").to exist, "b.txt was not installed"
+    end
+
+    it "can install directories as relative symlinks" do
+      bin = src/"bin"
+      bin.mkpath
+      mv Dir[src/"*.txt"], bin
+      dst.install_symlink bin
+
+      expect(dst/"bin").to be_a_symlink
+      expect(dst/"bin").to be_a_directory
+      expect(dst/"bin/a.txt").to exist
+      expect(dst/"bin/b.txt").to exist
+      expect((dst/"bin").readlink).to be_relative
+    end
+
+    it "can install relative paths as symlinks" do
+      dst.install_symlink "foo" => "bar"
+      expect((dst/"bar").readlink).to eq(Pathname.new("foo"))
+    end
+  end
+
+  describe InstallRenamed do
+    before(:each) do
+      dst.extend(InstallRenamed)
+    end
+
+    it "renames the installed file if it already exists" do
+      file.write "a"
+      dst.install file
+
+      file.write "b"
+      dst.install file
+
+      expect(File.read(dst/file.basename)).to eq("a")
+      expect(File.read(dst/"#{file.basename}.default")).to eq("b")
+    end
+
+    it "renames the installed directory" do
+      file.write "a"
+      dst.install src
+      expect(File.read(dst/src.basename/file.basename)).to eq("a")
+    end
+
+    it "recursively renames directories" do
+      (dst/dir.basename).mkpath
+      (dst/dir.basename/"another_file").write "a"
+      dir.mkpath
+      (dir/"another_file").write "b"
+      dst.install dir
+      expect(File.read(dst/dir.basename/"another_file.default")).to eq("b")
+    end
+  end
+
+  describe "#cp_path_sub" do
+    it "copies a file and replaces the given pattern" do
+      file.write "a"
+      file.cp_path_sub src, dst
+      expect(File.read(dst/file.basename)).to eq("a")
+    end
+
+    it "copies a directory and replaces the given pattern" do
+      dir.mkpath
+      dir.cp_path_sub src, dst
+      expect(dst/dir.basename).to be_a_directory
+    end
+  end
+end
+
+describe FileUtils do
+  let(:dst) { Pathname.new(Dir.mktmpdir) }
+
+  describe "#mkdir" do
+    it "creates indermediate directories" do
+      described_class.mkdir dst/"foo/bar/baz" do
+        expect(dst/"foo/bar/baz").to exist, "foo/bar/baz was not created"
+        expect(dst/"foo/bar/baz").to be_a_directory, "foo/bar/baz was not a directory structure"
+      end
+    end
+  end
+end
diff --git a/Library/Homebrew/test/pathname_test.rb b/Library/Homebrew/test/pathname_test.rb
deleted file mode 100644
index b48a26fbd3dadce56bffb2e0c6b8af2809ab5b61..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/pathname_test.rb
+++ /dev/null
@@ -1,263 +0,0 @@
-require "testing_env"
-require "tmpdir"
-require "extend/pathname"
-require "install_renamed"
-
-module PathnameTestExtension
-  include FileUtils
-
-  def setup
-    super
-    @src  = Pathname.new(mktmpdir)
-    @dst  = Pathname.new(mktmpdir)
-    @file = @src/"foo"
-    @dir  = @src/"bar"
-  end
-end
-
-class PathnameTests < Homebrew::TestCase
-  include PathnameTestExtension
-
-  def test_disk_usage_extension
-    mkdir_p @dir/"a-directory"
-    touch @dir/".DS_Store"
-    touch @dir/"a-file"
-    File.truncate(@dir/"a-file", 1_048_576)
-    ln_s @dir/"a-file", @dir/"a-symlink"
-    ln @dir/"a-file", @dir/"a-hardlink"
-    assert_equal 3, @dir.file_count
-    assert_equal "3 files, 1M", @dir.abv
-    assert_equal "1M", (@dir/"a-file").abv
-  end
-
-  def test_rmdir_if_possible
-    mkdir_p @dir
-    touch @dir/"foo"
-
-    assert !@dir.rmdir_if_possible
-    assert_predicate @dir, :directory?
-
-    rm_f @dir/"foo"
-    assert @dir.rmdir_if_possible
-    refute_predicate @dir, :exist?
-  end
-
-  def test_rmdir_if_possible_ignore_ds_store
-    mkdir_p @dir
-    touch @dir/".DS_Store"
-    assert @dir.rmdir_if_possible
-    refute_predicate @dir, :exist?
-  end
-
-  def test_write
-    @file.write("CONTENT")
-    assert_equal "CONTENT", File.read(@file)
-  end
-
-  def test_write_does_not_overwrite
-    touch @file
-    assert_raises(RuntimeError) { @file.write("CONTENT") }
-  end
-
-  def test_append_lines
-    touch @file
-    @file.append_lines("CONTENT")
-    assert_equal "CONTENT\n", File.read(@file)
-    @file.append_lines("CONTENTS")
-    assert_equal "CONTENT\nCONTENTS\n", File.read(@file)
-  end
-
-  def test_append_lines_does_not_create
-    assert_raises(RuntimeError) { @file.append_lines("CONTENT") }
-  end
-
-  def test_atomic_write
-    touch @file
-    @file.atomic_write("CONTENT")
-    assert_equal "CONTENT", File.read(@file)
-  end
-
-  def test_atomic_write_preserves_permissions
-    File.open(@file, "w", 0100777) {}
-    @file.atomic_write("CONTENT")
-    assert_equal 0100777 & ~File.umask, @file.stat.mode
-  end
-
-  def test_atomic_write_preserves_default_permissions
-    @file.atomic_write("CONTENT")
-    sentinel = @file.parent.join("sentinel")
-    touch sentinel
-    assert_equal sentinel.stat.mode, @file.stat.mode
-  end
-
-  def test_ensure_writable
-    touch @file
-    chmod 0555, @file
-    @file.ensure_writable { assert_predicate @file, :writable? }
-    refute_predicate @file, :writable?
-  end
-
-  def test_extname
-    assert_equal ".tar.gz", Pathname("foo-0.1.tar.gz").extname
-    assert_equal ".cpio.gz", Pathname("foo-0.1.cpio.gz").extname
-  end
-
-  def test_stem
-    assert_equal "foo-0.1", Pathname("foo-0.1.tar.gz").stem
-    assert_equal "foo-0.1", Pathname("foo-0.1.cpio.gz").stem
-  end
-
-  def test_install_missing_file
-    assert_raises(Errno::ENOENT) { @dst.install "non_existent_file" }
-  end
-
-  def test_install_removes_original
-    touch @file
-    @dst.install(@file)
-
-    assert_predicate @dst/@file.basename, :exist?
-    refute_predicate @file, :exist?
-  end
-
-  def test_install_creates_intermediate_directories
-    touch @file
-    refute_predicate @dir, :directory?
-    @dir.install(@file)
-    assert_predicate @dir, :directory?
-  end
-
-  def test_install_renamed
-    @dst.extend(InstallRenamed)
-
-    @file.write "a"
-    @dst.install @file
-    @file.write "b"
-    @dst.install @file
-
-    assert_equal "a", File.read(@dst/@file.basename)
-    assert_equal "b", File.read(@dst/"#{@file.basename}.default")
-  end
-
-  def test_install_renamed_directory
-    @dst.extend(InstallRenamed)
-    @file.write "a"
-    @dst.install @src
-    assert_equal "a", File.read(@dst/@src.basename/@file.basename)
-  end
-
-  def test_install_renamed_directory_recursive
-    @dst.extend(InstallRenamed)
-    (@dst/@dir.basename).mkpath
-    (@dst/@dir.basename/"another_file").write "a"
-    @dir.mkpath
-    (@dir/"another_file").write "b"
-    @dst.install @dir
-    assert_equal "b", File.read(@dst/@dir.basename/"another_file.default")
-  end
-
-  def test_cp_path_sub_file
-    @file.write "a"
-    @file.cp_path_sub @src, @dst
-    assert_equal "a", File.read(@dst/"foo")
-  end
-
-  def test_cp_path_sub_directory
-    @dir.mkpath
-    @dir.cp_path_sub @src, @dst
-    assert_predicate @dst/@dir.basename, :directory?
-  end
-end
-
-class PathnameInstallTests < Homebrew::TestCase
-  include PathnameTestExtension
-
-  def setup
-    super
-    (@src/"a.txt").write "This is sample file a."
-    (@src/"b.txt").write "This is sample file b."
-  end
-
-  def test_install
-    @dst.install @src/"a.txt"
-
-    assert_predicate @dst/"a.txt", :exist?, "a.txt was not installed"
-    refute_predicate @dst/"b.txt", :exist?, "b.txt was installed."
-  end
-
-  def test_install_list
-    @dst.install [@src/"a.txt", @src/"b.txt"]
-
-    assert_predicate @dst/"a.txt", :exist?, "a.txt was not installed"
-    assert_predicate @dst/"b.txt", :exist?, "b.txt was not installed"
-  end
-
-  def test_install_glob
-    @dst.install Dir[@src/"*.txt"]
-
-    assert_predicate @dst/"a.txt", :exist?, "a.txt was not installed"
-    assert_predicate @dst/"b.txt", :exist?, "b.txt was not installed"
-  end
-
-  def test_install_directory
-    bin = @src/"bin"
-    bin.mkpath
-    mv Dir[@src/"*.txt"], bin
-    @dst.install bin
-
-    assert_predicate @dst/"bin/a.txt", :exist?, "a.txt was not installed"
-    assert_predicate @dst/"bin/b.txt", :exist?, "b.txt was not installed"
-  end
-
-  def test_install_rename
-    @dst.install @src/"a.txt" => "c.txt"
-
-    assert_predicate @dst/"c.txt", :exist?, "c.txt was not installed"
-    refute_predicate @dst/"a.txt", :exist?, "a.txt was installed but not renamed"
-    refute_predicate @dst/"b.txt", :exist?, "b.txt was installed"
-  end
-
-  def test_install_rename_more
-    @dst.install(@src/"a.txt" => "c.txt", @src/"b.txt" => "d.txt")
-
-    assert_predicate @dst/"c.txt", :exist?, "c.txt was not installed"
-    assert_predicate @dst/"d.txt", :exist?, "d.txt was not installed"
-    refute_predicate @dst/"a.txt", :exist?, "a.txt was installed but not renamed"
-    refute_predicate @dst/"b.txt", :exist?, "b.txt was installed but not renamed"
-  end
-
-  def test_install_rename_directory
-    bin = @src/"bin"
-    bin.mkpath
-    mv Dir[@src/"*.txt"], bin
-    @dst.install bin => "libexec"
-
-    refute_predicate @dst/"bin", :exist?, "bin was installed but not renamed"
-    assert_predicate @dst/"libexec/a.txt", :exist?, "a.txt was not installed"
-    assert_predicate @dst/"libexec/b.txt", :exist?, "b.txt was not installed"
-  end
-
-  def test_install_symlink
-    bin = @src/"bin"
-    bin.mkpath
-    mv Dir[@src/"*.txt"], bin
-    @dst.install_symlink bin
-
-    assert_predicate @dst/"bin", :symlink?
-    assert_predicate @dst/"bin", :directory?
-    assert_predicate @dst/"bin/a.txt", :exist?
-    assert_predicate @dst/"bin/b.txt", :exist?
-    assert_predicate((@dst/"bin").readlink, :relative?)
-  end
-
-  def test_install_relative_symlink
-    @dst.install_symlink "foo" => "bar"
-    assert_equal Pathname.new("foo"), (@dst/"bar").readlink
-  end
-
-  def test_mkdir_creates_intermediate_directories
-    mkdir @dst/"foo/bar/baz" do
-      assert_predicate @dst/"foo/bar/baz", :exist?, "foo/bar/baz was not created"
-      assert_predicate @dst/"foo/bar/baz", :directory?, "foo/bar/baz was not a directory structure"
-    end
-  end
-end
diff --git a/Library/Homebrew/test/resource_spec.rb b/Library/Homebrew/test/resource_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6a04195389f93188bf98678b5050816749742caa
--- /dev/null
+++ b/Library/Homebrew/test/resource_spec.rb
@@ -0,0 +1,147 @@
+require "resource"
+
+describe Resource do
+  subject { described_class.new("test") }
+
+  describe "#url" do
+    it "sets the URL" do
+      subject.url("foo")
+      expect(subject.url).to eq("foo")
+    end
+
+    it "can set the URL with specifications" do
+      subject.url("foo", branch: "master")
+      expect(subject.url).to eq("foo")
+      expect(subject.specs).to eq(branch: "master")
+    end
+
+    it "can set the URL with a custom download strategy class" do
+      strategy = Class.new(AbstractDownloadStrategy)
+      subject.url("foo", using: strategy)
+      expect(subject.url).to eq("foo")
+      expect(subject.download_strategy).to eq(strategy)
+    end
+
+    it "can set the URL with specifications and a custom download strategy class" do
+      strategy = Class.new(AbstractDownloadStrategy)
+      subject.url("foo", using: strategy, branch: "master")
+      expect(subject.url).to eq("foo")
+      expect(subject.specs).to eq(branch: "master")
+      expect(subject.download_strategy).to eq(strategy)
+    end
+
+    it "can set the URL with a custom download strategy symbol" do
+      subject.url("foo", using: :git)
+      expect(subject.url).to eq("foo")
+      expect(subject.download_strategy).to eq(GitDownloadStrategy)
+    end
+
+    it "raises an error if the download strategy class is unkown" do
+      expect { subject.url("foo", using: Class.new) }.to raise_error(TypeError)
+    end
+
+    it "does not mutate the specifications hash" do
+      specs = { using: :git, branch: "master" }
+      subject.url("foo", specs)
+      expect(subject.specs).to eq(branch: "master")
+      expect(subject.using).to eq(:git)
+      expect(specs).to eq(using: :git, branch: "master")
+    end
+  end
+
+  describe "#version" do
+    it "sets the version" do
+      subject.version("1.0")
+      expect(subject.version).to eq(Version.parse("1.0"))
+      expect(subject.version).not_to be_detected_from_url
+    end
+
+    it "can detect the version from a URL" do
+      subject.url("http://example.com/foo-1.0.tar.gz")
+      expect(subject.version).to eq(Version.parse("1.0"))
+      expect(subject.version).to be_detected_from_url
+    end
+
+    it "can set the version with a scheme" do
+      klass = Class.new(Version)
+      subject.version klass.new("1.0")
+      expect(subject.version).to eq(Version.parse("1.0"))
+      expect(subject.version).to be_a(klass)
+    end
+
+    it "can set the version from a tag" do
+      subject.url("http://example.com/foo-1.0.tar.gz", tag: "v1.0.2")
+      expect(subject.version).to eq(Version.parse("1.0.2"))
+      expect(subject.version).to be_detected_from_url
+    end
+
+    it "rejects non-string versions" do
+      expect { subject.version(1) }.to raise_error(TypeError)
+      expect { subject.version(2.0) }.to raise_error(TypeError)
+      expect { subject.version(Object.new) }.to raise_error(TypeError)
+    end
+
+    it "returns nil if unset" do
+      expect(subject.version).to be nil
+    end
+  end
+
+  describe "#mirrors" do
+    it "is empty by defaults" do
+      expect(subject.mirrors).to be_empty
+    end
+
+    it "returns an array of mirrors added with #mirror" do
+      subject.mirror("foo")
+      subject.mirror("bar")
+      expect(subject.mirrors).to eq(%w[foo bar])
+    end
+  end
+
+  describe "#checksum" do
+    it "returns nil if unset" do
+      expect(subject.checksum).to be nil
+    end
+
+    it "returns the checksum set with #sha256" do
+      subject.sha256(TEST_SHA256)
+      expect(subject.checksum).to eq(Checksum.new(:sha256, TEST_SHA256))
+    end
+  end
+
+  describe "#download_strategy" do
+    it "returns the download strategy" do
+      strategy = Object.new
+      expect(DownloadStrategyDetector)
+        .to receive(:detect).with("foo", nil).and_return(strategy)
+      subject.url("foo")
+      expect(subject.download_strategy).to eq(strategy)
+    end
+  end
+
+  specify "#verify_download_integrity_missing" do
+    fn = Pathname.new("test")
+
+    allow(fn).to receive(:file?).and_return(true)
+    expect(fn).to receive(:verify_checksum).and_raise(ChecksumMissingError)
+    expect(fn).to receive(:sha256)
+
+    shutup do
+      subject.verify_download_integrity(fn)
+    end
+  end
+
+  specify "#verify_download_integrity_mismatch" do
+    fn = double(file?: true)
+    checksum = subject.sha256(TEST_SHA256)
+
+    expect(fn).to receive(:verify_checksum).with(checksum)
+      .and_raise(ChecksumMismatchError.new(fn, checksum, Object.new))
+
+    shutup do
+      expect {
+        subject.verify_download_integrity(fn)
+      }.to raise_error(ChecksumMismatchError)
+    end
+  end
+end
diff --git a/Library/Homebrew/test/resource_test.rb b/Library/Homebrew/test/resource_test.rb
deleted file mode 100644
index d982a7c3373f4220e8b9a82fe87aa4000bf570c6..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/resource_test.rb
+++ /dev/null
@@ -1,133 +0,0 @@
-require "testing_env"
-require "resource"
-
-class ResourceTests < Homebrew::TestCase
-  def setup
-    super
-    @resource = Resource.new("test")
-  end
-
-  def test_url
-    @resource.url("foo")
-    assert_equal "foo", @resource.url
-  end
-
-  def test_url_with_specs
-    @resource.url("foo", branch: "master")
-    assert_equal "foo", @resource.url
-    assert_equal({ branch: "master" }, @resource.specs)
-  end
-
-  def test_url_with_custom_download_strategy_class
-    strategy = Class.new(AbstractDownloadStrategy)
-    @resource.url("foo", using: strategy)
-    assert_equal "foo", @resource.url
-    assert_equal strategy, @resource.download_strategy
-  end
-
-  def test_url_with_specs_and_download_strategy
-    strategy = Class.new(AbstractDownloadStrategy)
-    @resource.url("foo", using: strategy, branch: "master")
-    assert_equal "foo", @resource.url
-    assert_equal({ branch: "master" }, @resource.specs)
-    assert_equal strategy, @resource.download_strategy
-  end
-
-  def test_url_with_custom_download_strategy_symbol
-    @resource.url("foo", using: :git)
-    assert_equal "foo", @resource.url
-    assert_equal GitDownloadStrategy, @resource.download_strategy
-  end
-
-  def test_raises_for_unknown_download_strategy_class
-    assert_raises(TypeError) { @resource.url("foo", using: Class.new) }
-  end
-
-  def test_does_not_mutate_specs_hash
-    specs = { using: :git, branch: "master" }
-    @resource.url("foo", specs)
-    assert_equal({ branch: "master" }, @resource.specs)
-    assert_equal(:git, @resource.using)
-    assert_equal({ using: :git, branch: "master" }, specs)
-  end
-
-  def test_version
-    @resource.version("1.0")
-    assert_version_equal "1.0", @resource.version
-    refute_predicate @resource.version, :detected_from_url?
-  end
-
-  def test_version_from_url
-    @resource.url("http://example.com/foo-1.0.tar.gz")
-    assert_version_equal "1.0", @resource.version
-    assert_predicate @resource.version, :detected_from_url?
-  end
-
-  def test_version_with_scheme
-    klass = Class.new(Version)
-    @resource.version klass.new("1.0")
-    assert_version_equal "1.0", @resource.version
-    assert_instance_of klass, @resource.version
-  end
-
-  def test_version_from_tag
-    @resource.url("http://example.com/foo-1.0.tar.gz", tag: "v1.0.2")
-    assert_version_equal "1.0.2", @resource.version
-    assert_predicate @resource.version, :detected_from_url?
-  end
-
-  def test_rejects_non_string_versions
-    assert_raises(TypeError) { @resource.version(1) }
-    assert_raises(TypeError) { @resource.version(2.0) }
-    assert_raises(TypeError) { @resource.version(Object.new) }
-  end
-
-  def test_version_when_url_is_not_set
-    assert_nil @resource.version
-  end
-
-  def test_mirrors
-    assert_empty @resource.mirrors
-    @resource.mirror("foo")
-    @resource.mirror("bar")
-    assert_equal %w[foo bar], @resource.mirrors
-  end
-
-  def test_checksum_setters
-    assert_nil @resource.checksum
-    @resource.sha256(TEST_SHA256)
-    assert_equal Checksum.new(:sha256, TEST_SHA256), @resource.checksum
-  end
-
-  def test_download_strategy
-    strategy = Object.new
-    DownloadStrategyDetector
-      .expects(:detect).with("foo", nil).returns(strategy)
-    @resource.url("foo")
-    assert_equal strategy, @resource.download_strategy
-  end
-
-  def test_verify_download_integrity_missing
-    fn = Pathname.new("test")
-
-    fn.stubs(file?: true)
-    fn.expects(:verify_checksum).raises(ChecksumMissingError)
-    fn.expects(:sha256)
-
-    shutup { @resource.verify_download_integrity(fn) }
-  end
-
-  def test_verify_download_integrity_mismatch
-    fn = stub(file?: true)
-    checksum = @resource.sha256(TEST_SHA256)
-
-    fn.expects(:verify_checksum).with(checksum)
-      .raises(ChecksumMismatchError.new(fn, checksum, Object.new))
-
-    shutup do
-      assert_raises(ChecksumMismatchError) do
-        @resource.verify_download_integrity(fn)
-      end
-    end
-  end
-end
diff --git a/Library/Homebrew/test/search_remote_tap_test.rb b/Library/Homebrew/test/search_remote_tap_test.rb
deleted file mode 100644
index 9dd9ee6544413d67de0cd3d75824229a941896a4..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/search_remote_tap_test.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-require "testing_env"
-require "cmd/search"
-
-class SearchRemoteTapTests < Homebrew::TestCase
-  def test_search_remote_tap
-    json_response = {
-      "tree" => [
-        {
-          "path" => "Formula/not-a-formula.rb",
-          "type" => "blob",
-        },
-      ],
-    }
-
-    GitHub.stubs(:open).yields(json_response)
-
-    assert_equal ["homebrew/not-a-tap/not-a-formula"], Homebrew.search_tap("homebrew", "not-a-tap", "not-a-formula")
-  end
-end
diff --git a/Library/Homebrew/test/shell_test.rb b/Library/Homebrew/test/shell_test.rb
deleted file mode 100644
index 97048970285e2833d0d3b977c46462bed5676c37..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/shell_test.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-require "testing_env"
-require "utils/shell"
-
-class ShellSmokeTest < Homebrew::TestCase
-  def test_path_to_shell
-    # raw command name
-    assert_equal :bash, Utils::Shell.path_to_shell("bash")
-    # full path
-    assert_equal :bash, Utils::Shell.path_to_shell("/bin/bash")
-    # versions
-    assert_equal :zsh, Utils::Shell.path_to_shell("zsh-5.2")
-    # strip newline too
-    assert_equal :zsh, Utils::Shell.path_to_shell("zsh-5.2\n")
-  end
-
-  def test_path_to_shell_failure
-    assert_nil Utils::Shell.path_to_shell("")
-    assert_nil Utils::Shell.path_to_shell("@@@@@@")
-    assert_nil Utils::Shell.path_to_shell("invalid_shell-4.2")
-  end
-
-  def test_sh_quote
-    assert_equal "''", Utils::Shell.sh_quote("")
-    assert_equal "\\\\", Utils::Shell.sh_quote("\\")
-    assert_equal "'\n'", Utils::Shell.sh_quote("\n")
-    assert_equal "\\$", Utils::Shell.sh_quote("$")
-    assert_equal "word", Utils::Shell.sh_quote("word")
-  end
-
-  def test_csh_quote
-    assert_equal "''", Utils::Shell.csh_quote("")
-    assert_equal "\\\\", Utils::Shell.csh_quote("\\")
-    # note this test is different than for sh
-    assert_equal "'\\\n'", Utils::Shell.csh_quote("\n")
-    assert_equal "\\$", Utils::Shell.csh_quote("$")
-    assert_equal "word", Utils::Shell.csh_quote("word")
-  end
-
-  def prepend_path_shell(shell, path, fragment)
-    ENV["SHELL"] = shell
-
-    prepend_message = Utils::Shell.prepend_path_in_shell_profile(path)
-    assert(
-      prepend_message.start_with?(fragment),
-      "#{shell}: expected #{prepend_message} to match #{fragment}",
-    )
-  end
-
-  def test_prepend_path_in_shell_profile
-    prepend_path_shell "/bin/tcsh", "/path", "echo 'setenv PATH /path"
-
-    prepend_path_shell "/bin/bash", "/path", "echo 'export PATH=\"/path"
-
-    prepend_path_shell "/usr/local/bin/fish", "/path", "echo 'set -g fish_user_paths \"/path\" $fish_user_paths' >>"
-  end
-end
diff --git a/Library/Homebrew/test/software_spec_spec.rb b/Library/Homebrew/test/software_spec_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5fd4f598a978eb2c13f0ce6e4d47158b3c185dc4
--- /dev/null
+++ b/Library/Homebrew/test/software_spec_spec.rb
@@ -0,0 +1,184 @@
+require "software_spec"
+
+RSpec::Matchers.alias_matcher :have_defined_resource, :be_resource_defined
+RSpec::Matchers.alias_matcher :have_defined_option, :be_option_defined
+
+describe SoftwareSpec do
+  let(:owner) { double(name: "some_name", full_name: "some_name", tap: "homebrew/core") }
+
+  describe "#resource" do
+    it "defines a resource" do
+      subject.resource("foo") { url "foo-1.0" }
+      expect(subject).to have_defined_resource("foo")
+    end
+
+    it "sets itself to be the resource's owner" do
+      subject.resource("foo") { url "foo-1.0" }
+      subject.owner = owner
+      subject.resources.each_value do |r|
+        expect(r.owner).to eq(subject)
+      end
+    end
+
+    it "receives the owner's version if it has no own version" do
+      subject.url("foo-42")
+      subject.resource("bar") { url "bar" }
+      subject.owner = owner
+
+      expect(subject.resource("bar").version).to eq("42")
+    end
+
+    it "raises an error when duplicate resources are defined" do
+      subject.resource("foo") { url "foo-1.0" }
+      expect {
+        subject.resource("foo") { url "foo-1.0" }
+      }.to raise_error(DuplicateResourceError)
+    end
+
+    it "raises an error when accessing missing resources" do
+      subject.owner = owner
+      expect {
+        subject.resource("foo")
+      }.to raise_error(ResourceMissingError)
+    end
+  end
+
+  describe "#owner" do
+    it "sets the owner" do
+      subject.owner = owner
+      expect(subject.owner).to eq(owner)
+    end
+
+    it "sets the name" do
+      subject.owner = owner
+      expect(subject.name).to eq(owner.name)
+    end
+  end
+
+  describe "#option" do
+    it "defines an option" do
+      subject.option("foo")
+      expect(subject).to have_defined_option("foo")
+    end
+
+    it "raises an error when it begins with dashes" do
+      expect {
+        subject.option("--foo")
+      }.to raise_error(ArgumentError)
+    end
+
+    it "raises an error when name is empty" do
+      expect {
+        subject.option("")
+      }.to raise_error(ArgumentError)
+    end
+
+    it "special cases the cxx11 option" do
+      subject.option(:cxx11)
+      expect(subject).to have_defined_option("c++11")
+      expect(subject).not_to have_defined_option("cxx11")
+    end
+
+    it "supports options with descriptions" do
+      subject.option("bar", "description")
+      expect(subject.options.first.description).to eq("description")
+    end
+
+    it "defaults to an empty string when no description is given" do
+      subject.option("foo")
+      expect(subject.options.first.description).to eq("")
+    end
+  end
+
+  describe "#deprecated_option" do
+    it "allows specifying deprecated options" do
+      subject.deprecated_option("foo" => "bar")
+      expect(subject.deprecated_options).not_to be_empty
+      expect(subject.deprecated_options.first.old).to eq("foo")
+      expect(subject.deprecated_options.first.current).to eq("bar")
+    end
+
+    it "allows specifying deprecated options as a Hash from an Array/String to an Array/String" do
+      subject.deprecated_option(["foo1", "foo2"] => "bar1", "foo3" => ["bar2", "bar3"])
+      expect(subject.deprecated_options).to include(DeprecatedOption.new("foo1", "bar1"))
+      expect(subject.deprecated_options).to include(DeprecatedOption.new("foo2", "bar1"))
+      expect(subject.deprecated_options).to include(DeprecatedOption.new("foo3", "bar2"))
+      expect(subject.deprecated_options).to include(DeprecatedOption.new("foo3", "bar3"))
+    end
+
+    it "raises an error when empty" do
+      expect {
+        subject.deprecated_option({})
+      }.to raise_error(ArgumentError)
+    end
+  end
+
+  describe "#depends_on" do
+    it "allows specifying dependencies" do
+      subject.depends_on("foo")
+      expect(subject.deps.first.name).to eq("foo")
+    end
+
+    it "allows specifying optional dependencies" do
+      subject.depends_on "foo" => :optional
+      expect(subject).to have_defined_option("with-foo")
+    end
+
+    it "allows specifying recommended dependencies" do
+      subject.depends_on "bar" => :recommended
+      expect(subject).to have_defined_option("without-bar")
+    end
+  end
+
+  specify "explicit options override defaupt depends_on option description" do
+    subject.option("with-foo", "blah")
+    subject.depends_on("foo" => :optional)
+    expect(subject.options.first.description).to eq("blah")
+  end
+
+  describe "#patch" do
+    it "adds a patch" do
+      subject.patch(:p1, :DATA)
+      expect(subject.patches.count).to eq(1)
+      expect(subject.patches.first.strip).to eq(:p1)
+    end
+  end
+end
+
+describe HeadSoftwareSpec do
+  specify "#version" do
+    expect(subject.version).to eq(Version.create("HEAD"))
+  end
+
+  specify "#verify_download_integrity" do
+    expect(subject.verify_download_integrity(Object.new)).to be nil
+  end
+end
+
+describe BottleSpecification do
+  specify "#sha256" do
+    checksums = {
+      snow_leopard_32: "deadbeef" * 8,
+      snow_leopard: "faceb00c" * 8,
+      lion: "baadf00d" * 8,
+      mountain_lion: "8badf00d" * 8,
+    }
+
+    checksums.each_pair do |cat, digest|
+      subject.sha256(digest => cat)
+    end
+
+    checksums.each_pair do |cat, digest|
+      checksum, = subject.checksum_for(cat)
+      expect(Checksum.new(:sha256, digest)).to eq(checksum)
+    end
+  end
+
+  %w[root_url prefix cellar rebuild].each do |method|
+    specify "##{method}" do
+      object = Object.new
+      subject.public_send(method, object)
+      expect(subject.public_send(method)).to eq(object)
+    end
+  end
+end
diff --git a/Library/Homebrew/test/software_spec_test.rb b/Library/Homebrew/test/software_spec_test.rb
deleted file mode 100644
index 026265a4a12379b4a70c148c48ed643af1f6b6e4..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/software_spec_test.rb
+++ /dev/null
@@ -1,184 +0,0 @@
-require "testing_env"
-require "software_spec"
-
-class SoftwareSpecTests < Homebrew::TestCase
-  def setup
-    super
-    @spec = SoftwareSpec.new
-  end
-
-  def test_resource
-    @spec.resource("foo") { url "foo-1.0" }
-    assert @spec.resource_defined?("foo")
-  end
-
-  def test_raises_when_duplicate_resources_are_defined
-    @spec.resource("foo") { url "foo-1.0" }
-    assert_raises(DuplicateResourceError) do
-      @spec.resource("foo") { url "foo-1.0" }
-    end
-  end
-
-  def test_raises_when_accessing_missing_resources
-    @spec.owner = Class.new do
-      def name
-        "test"
-      end
-
-      def full_name
-        "test"
-      end
-
-      def tap
-        "homebrew/core"
-      end
-    end.new
-    assert_raises(ResourceMissingError) { @spec.resource("foo") }
-  end
-
-  def test_set_owner
-    owner = stub name: "some_name",
-                 full_name: "some_name",
-                 tap: "homebrew/core"
-    @spec.owner = owner
-    assert_equal owner, @spec.owner
-  end
-
-  def test_resource_owner
-    @spec.resource("foo") { url "foo-1.0" }
-    @spec.owner = stub name: "some_name",
-                       full_name: "some_name",
-                       tap: "homebrew/core"
-    assert_equal "some_name", @spec.name
-    @spec.resources.each_value { |r| assert_equal @spec, r.owner }
-  end
-
-  def test_resource_without_version_receives_owners_version
-    @spec.url("foo-42")
-    @spec.resource("bar") { url "bar" }
-    @spec.owner = stub name: "some_name",
-                       full_name: "some_name",
-                       tap: "homebrew/core"
-    assert_version_equal "42", @spec.resource("bar").version
-  end
-
-  def test_option
-    @spec.option("foo")
-    assert @spec.option_defined?("foo")
-  end
-
-  def test_option_raises_when_begins_with_dashes
-    assert_raises(ArgumentError) { @spec.option("--foo") }
-  end
-
-  def test_option_raises_when_name_empty
-    assert_raises(ArgumentError) { @spec.option("") }
-  end
-
-  def test_cxx11_option_special_case
-    @spec.option(:cxx11)
-    assert @spec.option_defined?("c++11")
-    refute @spec.option_defined?("cxx11")
-  end
-
-  def test_option_description
-    @spec.option("bar", "description")
-    assert_equal "description", @spec.options.first.description
-  end
-
-  def test_option_description_defaults_to_empty_string
-    @spec.option("foo")
-    assert_equal "", @spec.options.first.description
-  end
-
-  def test_deprecated_option
-    @spec.deprecated_option("foo" => "bar")
-    refute_empty @spec.deprecated_options
-    assert_equal "foo", @spec.deprecated_options.first.old
-    assert_equal "bar", @spec.deprecated_options.first.current
-  end
-
-  def test_deprecated_options
-    @spec.deprecated_option(["foo1", "foo2"] => "bar1", "foo3" => ["bar2", "bar3"])
-    assert_includes @spec.deprecated_options, DeprecatedOption.new("foo1", "bar1")
-    assert_includes @spec.deprecated_options, DeprecatedOption.new("foo2", "bar1")
-    assert_includes @spec.deprecated_options, DeprecatedOption.new("foo3", "bar2")
-    assert_includes @spec.deprecated_options, DeprecatedOption.new("foo3", "bar3")
-  end
-
-  def test_deprecated_option_raises_when_empty
-    assert_raises(ArgumentError) { @spec.deprecated_option({}) }
-  end
-
-  def test_depends_on
-    @spec.depends_on("foo")
-    assert_equal "foo", @spec.deps.first.name
-  end
-
-  def test_dependency_option_integration
-    @spec.depends_on "foo" => :optional
-    @spec.depends_on "bar" => :recommended
-    assert @spec.option_defined?("with-foo")
-    assert @spec.option_defined?("without-bar")
-  end
-
-  def test_explicit_options_override_default_dep_option_description
-    @spec.option("with-foo", "blah")
-    @spec.depends_on("foo" => :optional)
-    assert_equal "blah", @spec.options.first.description
-  end
-
-  def test_patch
-    @spec.patch :p1, :DATA
-    assert_equal 1, @spec.patches.length
-    assert_equal :p1, @spec.patches.first.strip
-  end
-end
-
-class HeadSoftwareSpecTests < Homebrew::TestCase
-  def setup
-    super
-    @spec = HeadSoftwareSpec.new
-  end
-
-  def test_version
-    assert_version_equal "HEAD", @spec.version
-  end
-
-  def test_verify_download_integrity
-    assert_nil @spec.verify_download_integrity(Object.new)
-  end
-end
-
-class BottleSpecificationTests < Homebrew::TestCase
-  def setup
-    super
-    @spec = BottleSpecification.new
-  end
-
-  def test_checksum_setters
-    checksums = {
-      snow_leopard_32: "deadbeef"*8,
-      snow_leopard: "faceb00c"*8,
-      lion: "baadf00d"*8,
-      mountain_lion: "8badf00d"*8,
-    }
-
-    checksums.each_pair do |cat, digest|
-      @spec.sha256(digest => cat)
-    end
-
-    checksums.each_pair do |cat, digest|
-      checksum, = @spec.checksum_for(cat)
-      assert_equal Checksum.new(:sha256, digest), checksum
-    end
-  end
-
-  def test_other_setters
-    double = Object.new
-    %w[root_url prefix cellar rebuild].each do |method|
-      @spec.send(method, double)
-      assert_equal double, @spec.send(method)
-    end
-  end
-end
diff --git a/Library/Homebrew/test/spec_helper.rb b/Library/Homebrew/test/spec_helper.rb
index 67e5f555a182155f0f29a5a6eb01456e92546532..e4349c8a00853f79439e4f4bf7815d830cf602ee 100644
--- a/Library/Homebrew/test/spec_helper.rb
+++ b/Library/Homebrew/test/spec_helper.rb
@@ -15,6 +15,8 @@ require "global"
 require "tap"
 
 require "test/support/helper/shutup"
+require "test/support/helper/fixtures"
+require "test/support/helper/spec/shared_context/integration_test"
 
 TEST_DIRECTORIES = [
   CoreTap.instance.path/"Formula",
@@ -29,9 +31,10 @@ TEST_DIRECTORIES = [
 RSpec.configure do |config|
   config.order = :random
   config.include(Test::Helper::Shutup)
+  config.include(Test::Helper::Fixtures)
   config.before(:each) do |example|
     if example.metadata[:needs_macos]
-      skip "not on macOS" unless OS.mac?
+      skip "Not on macOS." unless OS.mac?
     end
 
     if example.metadata[:needs_python]
@@ -42,6 +45,8 @@ RSpec.configure do |config|
     begin
       TEST_DIRECTORIES.each(&:mkpath)
 
+      @__homebrew_failed = Homebrew.failed?
+
       @__files_before_test = Find.find(TEST_TMPDIR).map { |f| f.sub(TEST_TMPDIR, "") }
 
       @__argv = ARGV.dup
@@ -81,6 +86,10 @@ RSpec.configure do |config|
         file leak detected:
         #{diff.map { |f| "  #{f}" }.join("\n")}
       EOS
+
+      Homebrew.failed = @__homebrew_failed
     end
   end
 end
+
+RSpec::Matchers.alias_matcher :have_failed, :be_failed
diff --git a/Library/Homebrew/test/string_spec.rb b/Library/Homebrew/test/string_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d1b820b66107f5dc2904819d58802c35a5d98dff
--- /dev/null
+++ b/Library/Homebrew/test/string_spec.rb
@@ -0,0 +1,49 @@
+require "extend/string"
+
+describe String do
+  describe "#undent" do
+    it "removes leading whitespace, taking the first line as reference" do
+      string = <<-EOS.undent
+        hi
+........my friend over
+          there
+      EOS
+
+      expect(string).to eq("hi\n........my friend over\n  there\n")
+    end
+
+    it "removes nothing if the text is not indented" do
+      string = <<-EOS.undent
+hi
+I'm not indented
+      EOS
+
+      expect(string).to eq("hi\nI'm not indented\n")
+    end
+
+    it "can be nested" do
+      nested_string = <<-EOS.undent
+        goodbye
+      EOS
+
+      string = <<-EOS.undent
+        hello
+        #{nested_string}
+      EOS
+
+      expect(string).to eq("hello\ngoodbye\n\n")
+    end
+  end
+end
+
+describe StringInreplaceExtension do
+  subject { string.extend(described_class) }
+  let(:string) { "foobar" }
+
+  describe "#sub!" do
+    it "adds an error to #errors when no replacement was made" do
+      subject.sub! "not here", "test"
+      expect(subject.errors).to eq(['expected replacement of "not here" with "test"'])
+    end
+  end
+end
diff --git a/Library/Homebrew/test/string_test.rb b/Library/Homebrew/test/string_test.rb
deleted file mode 100644
index 497c4badb15b021e238279ad3b5e786ded90739c..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/string_test.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require "testing_env"
-require "extend/string"
-
-class StringTest < Homebrew::TestCase
-  def test_undent
-    undented = <<-EOS.undent
-    hi
-....my friend over
-    there
-    EOS
-    assert_equal "hi\n....my friend over\nthere\n", undented
-  end
-
-  def test_undent_not_indented
-    undented = <<-EOS.undent
-hi
-I'm not indented
-    EOS
-    assert_equal "hi\nI'm not indented\n", undented
-  end
-
-  def test_undent_nested
-    nest = <<-EOS.undent
-      goodbye
-    EOS
-
-    undented = <<-EOS.undent
-      hello
-      #{nest}
-    EOS
-
-    assert_equal "hello\ngoodbye\n\n", undented
-  end
-
-  def test_inreplace_sub_failure
-    s = "foobar".extend StringInreplaceExtension
-    s.sub! "not here", "test"
-    assert_equal ['expected replacement of "not here" with "test"'], s.errors
-  end
-end
diff --git a/Library/Homebrew/test/support/helper/fixtures.rb b/Library/Homebrew/test/support/helper/fixtures.rb
new file mode 100644
index 0000000000000000000000000000000000000000..716fe200816e5889d937bc0e5ee63f9b14bc9d46
--- /dev/null
+++ b/Library/Homebrew/test/support/helper/fixtures.rb
@@ -0,0 +1,13 @@
+module Test
+  module Helper
+    module Fixtures
+      def dylib_path(name)
+        Pathname.new("#{TEST_FIXTURE_DIR}/mach/#{name}.dylib")
+      end
+
+      def bundle_path(name)
+        Pathname.new("#{TEST_FIXTURE_DIR}/mach/#{name}.bundle")
+      end
+    end
+  end
+end
diff --git a/Library/Homebrew/test/support/helper/spec/shared_context/integration_test.rb b/Library/Homebrew/test/support/helper/spec/shared_context/integration_test.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dd2271a3a41cf96e9458c0286a220968b894e7ec
--- /dev/null
+++ b/Library/Homebrew/test/support/helper/spec/shared_context/integration_test.rb
@@ -0,0 +1,94 @@
+require "rspec"
+require "open3"
+
+RSpec::Matchers.define_negated_matcher :not_to_output, :output
+RSpec::Matchers.define_negated_matcher :be_a_failure, :be_a_success
+
+RSpec.shared_context "integration test" do
+  extend RSpec::Matchers::DSL
+
+  matcher :be_a_success do
+    match do |actual|
+      status = actual.is_a?(Proc) ? actual.call : actual
+      status.respond_to?(:success?) && status.success?
+    end
+
+    def supports_block_expectations?
+      true
+    end
+
+    # It needs to be nested like this:
+    #
+    #   expect {
+    #     expect {
+    #       # command
+    #     }.to be_a_success
+    #   }.to output(something).to_stdout
+    #
+    # rather than this:
+    #
+    #   expect {
+    #     expect {
+    #       # command
+    #     }.to output(something).to_stdout
+    #   }.to be_a_success
+    #
+    def expects_call_stack_jump?
+      true
+    end
+  end
+
+  before(:each) do
+    (HOMEBREW_PREFIX/"bin").mkpath
+    FileUtils.touch HOMEBREW_PREFIX/"bin/brew"
+  end
+
+  after(:each) do
+    FileUtils.rm HOMEBREW_PREFIX/"bin/brew"
+    FileUtils.rmdir HOMEBREW_PREFIX/"bin"
+  end
+
+  # Generate unique ID to be able to
+  # properly merge coverage results.
+  def command_id_from_args(args)
+    @command_count ||= 0
+    pretty_args = args.join(" ").gsub(TEST_TMPDIR, "@TMPDIR@")
+    file_and_line = caller[1].sub(/(.*\d+):.*/, '\1')
+                             .sub("#{HOMEBREW_LIBRARY_PATH}/test/", "")
+    "#{file_and_line}:brew #{pretty_args}:#{@command_count += 1}"
+  end
+
+  # Runs a `brew` command with the test configuration
+  # and with coverage reporting enabled.
+  def brew(*args)
+    env = args.last.is_a?(Hash) ? args.pop : {}
+
+    env.merge!(
+      "HOMEBREW_BREW_FILE" => HOMEBREW_PREFIX/"bin/brew",
+      "HOMEBREW_INTEGRATION_TEST" => command_id_from_args(args),
+      "HOMEBREW_TEST_TMPDIR" => TEST_TMPDIR,
+      "HOMEBREW_DEVELOPER" => ENV["HOMEBREW_DEVELOPER"],
+    )
+
+    ruby_args = [
+      "-W0",
+      "-I", "#{HOMEBREW_LIBRARY_PATH}/test/support/lib",
+      "-I", HOMEBREW_LIBRARY_PATH.to_s,
+      "-rconfig"
+    ]
+    ruby_args << "-rsimplecov" if ENV["HOMEBREW_TESTS_COVERAGE"]
+    ruby_args << "-rtest/support/helper/integration_mocks"
+    ruby_args << (HOMEBREW_LIBRARY_PATH/"brew.rb").resolved_path.to_s
+
+    Bundler.with_original_env do
+      stdout, stderr, status = Open3.capture3(env, RUBY_PATH, *ruby_args, *args)
+      $stdout.print stdout
+      $stderr.print stderr
+      status
+    end
+  end
+end
+
+RSpec.configure do |config|
+  config.include_context "integration test", :integration_test
+end
diff --git a/Library/Homebrew/test/utils/bottles/bintray_spec.rb b/Library/Homebrew/test/utils/bottles/bintray_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a7dfc00eabe1eaddcc1c20dcf792fb41f27d51a1
--- /dev/null
+++ b/Library/Homebrew/test/utils/bottles/bintray_spec.rb
@@ -0,0 +1,18 @@
+require "utils/bottles"
+
+describe Utils::Bottles::Bintray do
+  describe "::package" do
+    it "converts a Formula name to a package name" do
+      expect(described_class.package("openssl@1.1")).to eq("openssl:1.1")
+      expect(described_class.package("gtk+")).to eq("gtkx")
+      expect(described_class.package("llvm")).to eq("llvm")
+    end
+  end
+
+  describe "::repository" do
+    it "returns the repository for a given Tap" do
+      expect(described_class.repository(Tap.new("homebrew", "bintray-test")))
+        .to eq("bottles-bintray-test")
+    end
+  end
+end
diff --git a/Library/Homebrew/test/utils/bottles/bottles_spec.rb b/Library/Homebrew/test/utils/bottles/bottles_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8b54b0b3449c2f5acb26f51e60cd4567caf426b7
--- /dev/null
+++ b/Library/Homebrew/test/utils/bottles/bottles_spec.rb
@@ -0,0 +1,80 @@
+require "utils/bottles"
+
+describe Utils::Bottles do
+  describe "#tag", :needs_macos do
+    it "returns :tiger_foo on Tiger PowerPC" do
+      allow(MacOS).to receive(:version).and_return(MacOS::Version.new("10.4"))
+      allow(Hardware::CPU).to receive(:type).and_return(:ppc)
+      allow(Hardware::CPU).to receive(:family).and_return(:foo)
+      allow(MacOS).to receive(:prefer_64_bit?).and_return(false)
+      expect(described_class.tag).to eq(:tiger_foo)
+    end
+
+    it "returns :tiger on Tiger Intel" do
+      allow(MacOS).to receive(:version).and_return(MacOS::Version.new("10.4"))
+      allow(Hardware::CPU).to receive(:type).and_return(:intel)
+      allow(MacOS).to receive(:prefer_64_bit?).and_return(false)
+      expect(described_class.tag).to eq(:tiger)
+    end
+
+    it "returns :tiger_g5_64 on Tiger PowerPC 64-bit" do
+      allow(MacOS).to receive(:version).and_return(MacOS::Version.new("10.4"))
+      allow(Hardware::CPU).to receive(:type).and_return(:ppc)
+      allow(Hardware::CPU).to receive(:family).and_return(:g5)
+      allow(MacOS).to receive(:prefer_64_bit?).and_return(true)
+      expect(described_class.tag).to eq(:tiger_g5_64)
+    end
+
+    # Note that this will probably never be used
+    it "returns :tiger_64 on Tiger Intel 64-bit" do
+      allow(MacOS).to receive(:version).and_return(MacOS::Version.new("10.4"))
+      allow(Hardware::CPU).to receive(:type).and_return(:intel)
+      allow(MacOS).to receive(:prefer_64_bit?).and_return(true)
+      expect(described_class.tag).to eq(:tiger_64)
+    end
+
+    it "returns :leopard on Leopard Intel" do
+      allow(MacOS).to receive(:version).and_return(MacOS::Version.new("10.5"))
+      allow(Hardware::CPU).to receive(:type).and_return(:intel)
+      allow(MacOS).to receive(:prefer_64_bit?).and_return(false)
+      expect(described_class.tag).to eq(:leopard)
+    end
+
+    it "returns :leopard_g5_64 on Leopard PowerPC 64-bit" do
+      allow(MacOS).to receive(:version).and_return(MacOS::Version.new("10.5"))
+      allow(Hardware::CPU).to receive(:type).and_return(:ppc)
+      allow(Hardware::CPU).to receive(:family).and_return(:g5)
+      allow(MacOS).to receive(:prefer_64_bit?).and_return(true)
+      expect(described_class.tag).to eq(:leopard_g5_64)
+    end
+
+    it "returns :leopard_64 on Leopard Intel 64-bit" do
+      allow(MacOS).to receive(:version).and_return(MacOS::Version.new("10.5"))
+      allow(Hardware::CPU).to receive(:type).and_return(:intel)
+      allow(MacOS).to receive(:prefer_64_bit?).and_return(true)
+      expect(described_class.tag).to eq(:leopard_64)
+    end
+
+    it "returns :snow_leopard_32 on Snow Leopard 32-bit" do
+      allow(MacOS).to receive(:version).and_return(MacOS::Version.new("10.6"))
+      allow(Hardware::CPU).to receive(:is_64_bit?).and_return(false)
+      expect(described_class.tag).to eq(:snow_leopard_32)
+    end
+
+    it "returns :snow_leopard on Snow Leopard 64-bit" do
+      allow(MacOS).to receive(:version).and_return(MacOS::Version.new("10.6"))
+      allow(Hardware::CPU).to receive(:is_64_bit?).and_return(true)
+      expect(described_class.tag).to eq(:snow_leopard)
+    end
+
+    it "returns :lion on Lion" do
+      allow(MacOS).to receive(:version).and_return(MacOS::Version.new("10.7"))
+      expect(described_class.tag).to eq(:lion)
+    end
+
+    it "returns :mountain_lion on Mountain Lion" do
+      allow(MacOS).to receive(:version).and_return(MacOS::Version.new("10.8"))
+      expect(described_class.tag).to eq(:mountain_lion)
+    end
+  end
+end
diff --git a/Library/Homebrew/test/utils/popen_spec.rb b/Library/Homebrew/test/utils/popen_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e3704a876ad8e6433289c348ba86390c2bbf3dc0
--- /dev/null
+++ b/Library/Homebrew/test/utils/popen_spec.rb
@@ -0,0 +1,28 @@
+require "utils/popen"
+
+describe Utils do
+  describe "::popen_read" do
+    it "reads the standard output of a given command" do
+      expect(subject.popen_read("sh", "-c", "echo success").chomp).to eq("success")
+      expect($?).to be_a_success
+    end
+
+    it "can be given a block to manually read from the pipe" do
+      expect(
+        subject.popen_read("sh", "-c", "echo success") do |pipe|
+          pipe.read.chomp
+        end,
+      ).to eq("success")
+      expect($?).to be_a_success
+    end
+  end
+
+  describe "::popen_write" do
+    it "with supports writing to a command's standard input" do
+      subject.popen_write("grep", "-q", "success") do |pipe|
+        pipe.write("success\n")
+      end
+      expect($?).to be_a_success
+    end
+  end
+end
diff --git a/Library/Homebrew/test/utils/shell_spec.rb b/Library/Homebrew/test/utils/shell_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c44bd82533f793b65a7a3b81de4d49238fd3057b
--- /dev/null
+++ b/Library/Homebrew/test/utils/shell_spec.rb
@@ -0,0 +1,93 @@
+require "utils/shell"
+
+describe Utils::Shell do
+  describe "::shell_profile" do
+    it "returns ~/.bash_profile by default" do
+      ENV["SHELL"] = "/bin/another_shell"
+      expect(subject.shell_profile).to eq("~/.bash_profile")
+    end
+
+    it "returns ~/.bash_profile for Sh" do
+      ENV["SHELL"] = "/bin/another_shell"
+      expect(subject.shell_profile).to eq("~/.bash_profile")
+    end
+
+    it "returns ~/.bash_profile for Bash" do
+      ENV["SHELL"] = "/bin/bash"
+      expect(subject.shell_profile).to eq("~/.bash_profile")
+    end
+
+    it "returns ~/.zshrc for Zsh" do
+      ENV["SHELL"] = "/bin/zsh"
+      expect(subject.shell_profile).to eq("~/.zshrc")
+    end
+
+    it "returns ~/.kshrc for Ksh" do
+      ENV["SHELL"] = "/bin/ksh"
+      expect(subject.shell_profile).to eq("~/.kshrc")
+    end
+  end
+
+  describe "::path_to_shell" do
+    it "supports a raw command name" do
+      expect(subject.path_to_shell("bash")).to eq(:bash)
+    end
+
+    it "supports full paths" do
+      expect(subject.path_to_shell("/bin/bash")).to eq(:bash)
+    end
+
+    it "supports versions" do
+      expect(subject.path_to_shell("zsh-5.2")).to eq(:zsh)
+    end
+
+    it "strips newlines" do
+      expect(subject.path_to_shell("zsh-5.2\n")).to eq(:zsh)
+    end
+
+    it "returns nil when input is invalid" do
+      expect(subject.path_to_shell("")).to be nil
+      expect(subject.path_to_shell("@@@@@@")).to be nil
+      expect(subject.path_to_shell("invalid_shell-4.2")).to be nil
+    end
+  end
+
+  specify "::sh_quote" do
+    expect(subject.sh_quote("")).to eq("''")
+    expect(subject.sh_quote("\\")).to eq("\\\\")
+    expect(subject.sh_quote("\n")).to eq("'\n'")
+    expect(subject.sh_quote("$")).to eq("\\$")
+    expect(subject.sh_quote("word")).to eq("word")
+  end
+
+  specify "::csh_quote" do
+    expect(subject.csh_quote("")).to eq("''")
+    expect(subject.csh_quote("\\")).to eq("\\\\")
+    # note this test is different than for sh
+    expect(subject.csh_quote("\n")).to eq("'\\\n'")
+    expect(subject.csh_quote("$")).to eq("\\$")
+    expect(subject.csh_quote("word")).to eq("word")
+  end
+
+  describe "::prepend_path_in_shell_profile" do
+    let(:path) { "/my/path" }
+
+    it "supports Tcsh" do
+      ENV["SHELL"] = "/bin/tcsh"
+      expect(subject.prepend_path_in_shell_profile(path))
+        .to start_with("echo 'setenv PATH #{path}:$")
+    end
+
+    it "supports Bash" do
+      ENV["SHELL"] = "/bin/bash"
+      expect(subject.prepend_path_in_shell_profile(path))
+        .to start_with("echo 'export PATH=\"#{path}:$")
+    end
+
+    it "supports Fish" do
+      ENV["SHELL"] = "/usr/local/bin/fish"
+      expect(subject.prepend_path_in_shell_profile(path))
+        .to start_with("echo 'set -g fish_user_paths \"#{path}\" $fish_user_paths' >>")
+    end
+  end
+end
diff --git a/Library/Homebrew/test/utils_spec.rb b/Library/Homebrew/test/utils_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..040ad630bea05ae0c603c447617787061723631d
--- /dev/null
+++ b/Library/Homebrew/test/utils_spec.rb
@@ -0,0 +1,277 @@
+require "utils"
+
+describe "globally-scoped helper methods" do
+  let(:dir) { @dir = Pathname.new(Dir.mktmpdir) }
+
+  after(:each) { dir.rmtree unless @dir.nil? }
+
+  def esc(code)
+    /(\e\[\d+m)*\e\[#{code}m/
+  end
+
+  describe "#ofail" do
+    it "sets Homebrew.failed to true" do
+      expect {
+        ofail "foo"
+      }.to output("Error: foo\n").to_stderr
+
+      expect(Homebrew).to have_failed
+    end
+  end
+
+  describe "#odie" do
+    it "exits with 1" do
+      expect(self).to receive(:exit).and_return(1)
+      expect {
+        odie "foo"
+      }.to output("Error: foo\n").to_stderr
+    end
+  end
+
+  describe "#pretty_installed" do
+    subject { pretty_installed("foo") }
+
+    context "when $stdout is a TTY" do
+      before(:each) { allow($stdout).to receive(:tty?).and_return(true) }
+
+      context "with HOMEBREW_NO_EMOJI unset" do
+        before(:each) { ENV.delete("HOMEBREW_NO_EMOJI") }
+
+        it "returns a string with a colored checkmark" do
+          expect(subject)
+            .to match(/#{esc 1}foo #{esc 32}✔#{esc 0}/)
+        end
+      end
+
+      context "with HOMEBREW_NO_EMOJI set" do
+        before(:each) { ENV["HOMEBREW_NO_EMOJI"] = "1" }
+
+        it "returns a string with colored info" do
+          expect(subject)
+            .to match(/#{esc 1}foo \(installed\)#{esc 0}/)
+        end
+      end
+    end
+
+    context "when $stdout is not a TTY" do
+      before(:each) { allow($stdout).to receive(:tty?).and_return(false) }
+
+      it "returns plain text" do
+        expect(subject).to eq("foo")
+      end
+    end
+  end
+
+  describe "#pretty_uninstalled" do
+    subject { pretty_uninstalled("foo") }
+
+    context "when $stdout is a TTY" do
+      before(:each) { allow($stdout).to receive(:tty?).and_return(true) }
+
+      context "with HOMEBREW_NO_EMOJI unset" do
+        before(:each) { ENV.delete("HOMEBREW_NO_EMOJI") }
+
+        it "returns a string with a colored checkmark" do
+          expect(subject)
+            .to match(/#{esc 1}foo #{esc 31}✘#{esc 0}/)
+        end
+      end
+
+      context "with HOMEBREW_NO_EMOJI set" do
+        before(:each) { ENV["HOMEBREW_NO_EMOJI"] = "1" }
+
+        it "returns a string with colored info" do
+          expect(subject)
+            .to match(/#{esc 1}foo \(uninstalled\)#{esc 0}/)
+        end
+      end
+    end
+
+    context "when $stdout is not a TTY" do
+      before(:each) { allow($stdout).to receive(:tty?).and_return(false) }
+
+      it "returns plain text" do
+        expect(subject).to eq("foo")
+      end
+    end
+  end
+
+  describe "#interactive_shell" do
+    let(:shell) { dir/"myshell" }
+
+    it "starts an interactive shell session" do
+      IO.write shell, <<-EOS.undent
+        #!/bin/sh
+        echo called > "#{dir}/called"
+      EOS
+
+      FileUtils.chmod 0755, shell
+
+      ENV["SHELL"] = shell
+
+      expect { interactive_shell }.not_to raise_error
+      expect(dir/"called").to exist
+    end
+  end
+
+  describe "#with_custom_locale" do
+    it "temporarily overrides the system locale" do
+      ENV["LC_ALL"] = "en_US.UTF-8"
+
+      with_custom_locale("C") do
+        expect(ENV["LC_ALL"]).to eq("C")
+      end
+
+      expect(ENV["LC_ALL"]).to eq("en_US.UTF-8")
+    end
+  end
+
+  describe "#run_as_not_developer" do
+    it "temporarily unsets HOMEBREW_DEVELOPER" do
+      ENV["HOMEBREW_DEVELOPER"] = "foo"
+
+      run_as_not_developer do
+        expect(ENV["HOMEBREW_DEVELOPER"]).to be nil
+      end
+
+      expect(ENV["HOMEBREW_DEVELOPER"]).to eq("foo")
+    end
+  end
+
+  describe "#which" do
+    let(:cmd) { dir/"foo" }
+
+    before(:each) { FileUtils.touch cmd }
+
+    it "returns the first executable that is found" do
+      cmd.chmod 0744
+      expect(which(File.basename(cmd), File.dirname(cmd))).to eq(cmd)
+    end
+
+    it "skips non-executables" do
+      expect(which(File.basename(cmd), File.dirname(cmd))).to be nil
+    end
+
+    it "skips malformed path and doesn't fail" do
+      # 'which' should not fail if a path is malformed
+      # see https://github.com/Homebrew/legacy-homebrew/issues/32789 for an example
+      cmd.chmod 0744
+
+      # ~~ will fail because ~foo resolves to foo's home and there is no '~' user
+      path = ["~~", File.dirname(cmd)].join(File::PATH_SEPARATOR)
+      expect(which(File.basename(cmd), path)).to eq(cmd)
+    end
+  end
+
+  describe "#which_all" do
+    let(:cmd1) { dir/"foo" }
+    let(:cmd2) { dir/"bar/foo" }
+    let(:cmd3) { dir/"bar/baz/foo" }
+
+    before(:each) do
+      (dir/"bar/baz").mkpath
+
+      FileUtils.touch cmd2
+
+      [cmd1, cmd3].each do |cmd|
+        FileUtils.touch cmd
+        cmd.chmod 0744
+      end
+    end
+
+    it "returns an array of all executables that are found" do
+      path = [
+        "#{dir}/bar/baz",
+        "#{dir}/baz:#{dir}",
+        "~baduserpath",
+      ].join(File::PATH_SEPARATOR)
+      expect(which_all("foo", path)).to eq([cmd3, cmd1])
+    end
+  end
+
+  specify "#which_editor" do
+    ENV["HOMEBREW_EDITOR"] = "vemate"
+    expect(which_editor).to eq("vemate")
+  end
+
+  specify "#gzip" do
+    Dir.mktmpdir do |path|
+      path = Pathname.new(path)
+      somefile = path/"somefile"
+      FileUtils.touch somefile
+      expect(gzip(somefile)[0].to_s).to eq("#{somefile}.gz")
+      expect(Pathname.new("#{somefile}.gz")).to exist
+    end
+  end
+
+  specify "#capture_stderr" do
+    err = capture_stderr do
+      $stderr.print "test"
+    end
+
+    expect(err).to eq("test")
+  end
+
+  describe "#pretty_duration" do
+    it "converts seconds to a human-readable string" do
+      expect(pretty_duration(1)).to eq("1 second")
+      expect(pretty_duration(2.5)).to eq("2 seconds")
+      expect(pretty_duration(42)).to eq("42 seconds")
+      expect(pretty_duration(240)).to eq("4 minutes")
+      expect(pretty_duration(252.45)).to eq("4 minutes 12 seconds")
+    end
+  end
+
+  specify "#plural" do
+    expect(plural(1)).to eq("")
+    expect(plural(0)).to eq("s")
+    expect(plural(42)).to eq("s")
+    expect(plural(42, "")).to eq("")
+  end
+
+  specify "#disk_usage_readable" do
+    expect(disk_usage_readable(1)).to eq("1B")
+    expect(disk_usage_readable(1000)).to eq("1000B")
+    expect(disk_usage_readable(1024)).to eq("1K")
+    expect(disk_usage_readable(1025)).to eq("1K")
+    expect(disk_usage_readable(4_404_020)).to eq("4.2M")
+    expect(disk_usage_readable(4_509_715_660)).to eq("4.2G")
+  end
+
+  describe "#number_readable" do
+    it "returns a string with thousands separators" do
+      expect(number_readable(1)).to eq("1")
+      expect(number_readable(1_000)).to eq("1,000")
+      expect(number_readable(1_000_000)).to eq("1,000,000")
+    end
+  end
+
+  specify "#truncate_text_to_approximate_size" do
+    glue = "\n[...snip...]\n" # hard-coded copy from truncate_text_to_approximate_size
+    n = 20
+    long_s = "x" * 40
+
+    s = truncate_text_to_approximate_size(long_s, n)
+    expect(s.length).to eq(n)
+    expect(s).to match(/^x+#{Regexp.escape(glue)}x+$/)
+
+    s = truncate_text_to_approximate_size(long_s, n, front_weight: 0.0)
+    expect(s).to eq(glue + ("x" * (n - glue.length)))
+
+    s = truncate_text_to_approximate_size(long_s, n, front_weight: 1.0)
+    expect(s).to eq(("x" * (n - glue.length)) + glue)
+  end
+
+  describe "#odeprecated" do
+    it "raises a MethodDeprecatedError" do
+      ENV.delete("HOMEBREW_DEVELOPER")
+      expect {
+        odeprecated(
+          "method", "replacement",
+          caller: ["#{HOMEBREW_LIBRARY}/Taps/homebrew/homebrew-core/"],
+          disable: true
+        )
+      }.to raise_error(MethodDeprecatedError, %r{method.*replacement.*homebrew/homebrew-core.*homebrew/core}m)
+    end
+  end
+end
diff --git a/Library/Homebrew/test/utils_test.rb b/Library/Homebrew/test/utils_test.rb
deleted file mode 100644
index 1f2fb7b55f7f50511e2658d287dc8c1a6d9c3af7..0000000000000000000000000000000000000000
--- a/Library/Homebrew/test/utils_test.rb
+++ /dev/null
@@ -1,252 +0,0 @@
-require "testing_env"
-require "utils"
-require "tempfile"
-require "utils/shell"
-
-class UtilTests < Homebrew::TestCase
-  def setup
-    super
-    @dir = Pathname.new(mktmpdir)
-  end
-
-  def esc(code)
-    /(\e\[\d+m)*\e\[#{code}m/
-  end
-
-  def test_ofail
-    shutup { ofail "foo" }
-    assert Homebrew.failed?
-  ensure
-    Homebrew.failed = false
-  end
-
-  def test_odie
-    expects(:exit).returns 1
-    shutup { odie "foo" }
-  end
-
-  def test_pretty_installed
-    $stdout.stubs(:tty?).returns true
-    ENV.delete("HOMEBREW_NO_EMOJI")
-    tty_with_emoji_output = /\A#{esc 1}foo #{esc 32}✔#{esc 0}\Z/
-    assert_match tty_with_emoji_output, pretty_installed("foo")
-
-    ENV["HOMEBREW_NO_EMOJI"] = "1"
-    tty_no_emoji_output = /\A#{esc 1}foo \(installed\)#{esc 0}\Z/
-    assert_match tty_no_emoji_output, pretty_installed("foo")
-
-    $stdout.stubs(:tty?).returns false
-    assert_equal "foo", pretty_installed("foo")
-  end
-
-  def test_pretty_uninstalled
-    $stdout.stubs(:tty?).returns true
-    ENV.delete("HOMEBREW_NO_EMOJI")
-    tty_with_emoji_output = /\A#{esc 1}foo #{esc 31}✘#{esc 0}\Z/
-    assert_match tty_with_emoji_output, pretty_uninstalled("foo")
-
-    ENV["HOMEBREW_NO_EMOJI"] = "1"
-    tty_no_emoji_output = /\A#{esc 1}foo \(uninstalled\)#{esc 0}\Z/
-    assert_match tty_no_emoji_output, pretty_uninstalled("foo")
-
-    $stdout.stubs(:tty?).returns false
-    assert_equal "foo", pretty_uninstalled("foo")
-  end
-
-  def test_interactive_shell
-    mktmpdir do |path|
-      shell = "#{path}/myshell"
-      File.open(shell, "w") do |file|
-        file.write "#!/bin/sh\necho called > #{path}/called\n"
-      end
-      FileUtils.chmod 0755, shell
-      ENV["SHELL"] = shell
-      assert_nothing_raised { interactive_shell }
-      assert File.exist? "#{path}/called"
-    end
-  end
-
-  def test_with_custom_locale
-    ENV["LC_ALL"] = "en_US.UTF-8"
-    with_custom_locale("C") do
-      assert_equal "C", ENV["LC_ALL"]
-    end
-    assert_equal "en_US.UTF-8", ENV["LC_ALL"]
-  end
-
-  def test_run_as_not_developer
-    ENV["HOMEBREW_DEVELOPER"] = "foo"
-    run_as_not_developer do
-      assert_nil ENV["HOMEBREW_DEVELOPER"]
-    end
-    assert_equal "foo", ENV["HOMEBREW_DEVELOPER"]
-  end
-
-  def test_put_columns_empty
-    out, err = capture_io do
-      puts Formatter.columns([])
-    end
-
-    assert_equal out, "\n"
-    assert_equal err, ""
-  end
-
-  def test_which
-    cmd = @dir/"foo"
-    FileUtils.touch cmd
-    cmd.chmod 0744
-    assert_equal Pathname.new(cmd),
-      which(File.basename(cmd), File.dirname(cmd))
-  end
-
-  def test_which_skip_non_executables
-    cmd = @dir/"foo"
-    FileUtils.touch cmd
-    assert_nil which(File.basename(cmd), File.dirname(cmd))
-  end
-
-  def test_which_skip_malformed_path
-    # 'which' should not fail if a path is malformed
-    # see https://github.com/Homebrew/legacy-homebrew/issues/32789 for an example
-    cmd = @dir/"foo"
-    FileUtils.touch cmd
-    cmd.chmod 0744
-
-    # ~~ will fail because ~foo resolves to foo's home and there is no '~' user
-    # here
-    assert_equal Pathname.new(cmd),
-      which(File.basename(cmd), "~~#{File::PATH_SEPARATOR}#{File.dirname(cmd)}")
-  end
-
-  def test_which_all
-    (@dir/"bar/baz").mkpath
-    cmd1 = @dir/"foo"
-    cmd2 = @dir/"bar/foo"
-    cmd3 = @dir/"bar/baz/foo"
-    FileUtils.touch cmd2
-    [cmd1, cmd3].each do |cmd|
-      FileUtils.touch cmd
-      cmd.chmod 0744
-    end
-    assert_equal [cmd3, cmd1],
-      which_all("foo", "#{@dir}/bar/baz:#{@dir}/baz:#{@dir}:~baduserpath")
-  end
-
-  def test_which_editor
-    ENV["HOMEBREW_EDITOR"] = "vemate"
-    assert_equal "vemate", which_editor
-  end
-
-  def test_gzip
-    mktmpdir do |path|
-      somefile = "#{path}/somefile"
-      FileUtils.touch somefile
-      assert_equal "#{somefile}.gz",
-        gzip(somefile)[0].to_s
-      assert File.exist?("#{somefile}.gz")
-    end
-  end
-
-  def test_capture_stderr
-    assert_equal "test\n", capture_stderr { $stderr.puts "test" }
-  end
-
-  def test_shell_profile
-    ENV["SHELL"] = "/bin/sh"
-    assert_equal "~/.bash_profile", Utils::Shell.shell_profile
-    ENV["SHELL"] = "/bin/bash"
-    assert_equal "~/.bash_profile", Utils::Shell.shell_profile
-    ENV["SHELL"] = "/bin/another_shell"
-    assert_equal "~/.bash_profile", Utils::Shell.shell_profile
-    ENV["SHELL"] = "/bin/zsh"
-    assert_equal "~/.zshrc", Utils::Shell.shell_profile
-    ENV["SHELL"] = "/bin/ksh"
-    assert_equal "~/.kshrc", Utils::Shell.shell_profile
-  end
-
-  def test_popen_read
-    out = Utils.popen_read("sh", "-c", "echo success").chomp
-    assert_equal "success", out
-    assert_predicate $?, :success?
-  end
-
-  def test_popen_read_with_block
-    out = Utils.popen_read("sh", "-c", "echo success") do |pipe|
-      pipe.read.chomp
-    end
-    assert_equal "success", out
-    assert_predicate $?, :success?
-  end
-
-  def test_popen_write_with_block
-    Utils.popen_write("grep", "-q", "success") do |pipe|
-      pipe.write("success\n")
-    end
-    assert_predicate $?, :success?
-  end
-
-  def test_pretty_duration
-    assert_equal "1 second", pretty_duration(1)
-    assert_equal "2 seconds", pretty_duration(2.5)
-    assert_equal "42 seconds", pretty_duration(42)
-    assert_equal "4 minutes", pretty_duration(240)
-    assert_equal "4 minutes 12 seconds", pretty_duration(252.45)
-  end
-
-  def test_plural
-    assert_equal "", plural(1)
-    assert_equal "s", plural(0)
-    assert_equal "s", plural(42)
-    assert_equal "", plural(42, "")
-  end
-
-  def test_disk_usage_readable
-    assert_equal "1B", disk_usage_readable(1)
-    assert_equal "1000B", disk_usage_readable(1000)
-    assert_equal "1K", disk_usage_readable(1024)
-    assert_equal "1K", disk_usage_readable(1025)
-    assert_equal "4.2M", disk_usage_readable(4_404_020)
-    assert_equal "4.2G", disk_usage_readable(4_509_715_660)
-  end
-
-  def test_number_readable
-    assert_equal "1", number_readable(1)
-    assert_equal "1,000", number_readable(1_000)
-    assert_equal "1,000,000", number_readable(1_000_000)
-  end
-
-  def test_truncate_text_to_approximate_size
-    glue = "\n[...snip...]\n" # hard-coded copy from truncate_text_to_approximate_size
-    n = 20
-    long_s = "x" * 40
-    s = truncate_text_to_approximate_size(long_s, n)
-    assert_equal n, s.length
-    assert_match(/^x+#{Regexp.escape(glue)}x+$/, s)
-    s = truncate_text_to_approximate_size(long_s, n, front_weight: 0.0)
-    assert_equal glue + ("x" * (n - glue.length)), s
-    s = truncate_text_to_approximate_size(long_s, n, front_weight: 1.0)
-    assert_equal(("x" * (n - glue.length)) + glue, s)
-  end
-
-  def test_odeprecated
-    ENV.delete("HOMEBREW_DEVELOPER")
-    e = assert_raises(MethodDeprecatedError) do
-      odeprecated("method", "replacement",
-        caller: ["#{HOMEBREW_LIBRARY}/Taps/homebrew/homebrew-core/"],
-        disable: true)
-    end
-    assert_match "method", e.message
-    assert_match "replacement", e.message
-    assert_match "homebrew/homebrew-core", e.message
-    assert_match "homebrew/core", e.message
-  end
-
-  def test_bottles_bintray
-    assert_equal "openssl:1.1", Utils::Bottles::Bintray.package("openssl@1.1")
-    assert_equal "gtkx", Utils::Bottles::Bintray.package("gtk+")
-    assert_equal "llvm", Utils::Bottles::Bintray.package("llvm")
-
-    tap = Tap.new("homebrew", "bintray-test")
-    assert_equal "bottles-bintray-test", Utils::Bottles::Bintray.repository(tap)
-  end
-end