diff --git a/Library/Homebrew/dev-cmd/tests.rb b/Library/Homebrew/dev-cmd/tests.rb index 9d93fc9e86..244fbe0270 100644 --- a/Library/Homebrew/dev-cmd/tests.rb +++ b/Library/Homebrew/dev-cmd/tests.rb @@ -7,10 +7,21 @@ require "tap" module Homebrew module_function + def run_tests(executable, files, args = []) + opts = [] + opts << "--serialize-stdout" if ENV["CI"] + + system "bundle", "exec", executable, *opts, "--", *args, "--", *files + + return if $?.success? + Homebrew.failed = true + end + def tests HOMEBREW_LIBRARY_PATH.cd do ENV.delete "HOMEBREW_VERBOSE" ENV.delete "VERBOSE" + ENV.delete("HOMEBREW_CASK_OPTS") ENV["HOMEBREW_NO_ANALYTICS_THIS_RUN"] = "1" ENV["HOMEBREW_DEVELOPER"] = "1" ENV["TESTOPTS"] = "-v" if ARGV.verbose? @@ -45,27 +56,35 @@ module Homebrew # Make it easier to reproduce test runs. ENV["SEED"] = ARGV.next if ARGV.include? "--seed" - files = Dir.glob("test/**/*_test.rb") + 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/") } - opts = [] - opts << "--serialize-stdout" if ENV["CI"] - - args = [] - args << "--trace" if ARGV.include? "--trace" + test_args = [] + test_args << "--trace" if ARGV.include? "--trace" if ARGV.value("only") test_name, test_method = ARGV.value("only").split(":", 2) - files = Dir.glob("test/{#{test_name},#{test_name}/**/*}_test.rb") - args << "--name=test_#{test_method}" if test_method + files = Dir.glob("test/{#{test_name},#{test_name}/**/*}_{spec,test}.rb") + test_args << "--name=test_#{test_method}" if test_method end - args += ARGV.named.select { |v| v[/^TEST(OPTS)?=/] } + test_files = files.select { |p| p.end_with?("_test.rb") } + spec_files = files.select { |p| p.end_with?("_spec.rb") } - system "bundle", "exec", "parallel_test", *opts, "--", *args, "--", *files + test_args += ARGV.named.select { |v| v[/^TEST(OPTS)?=/] } + run_tests "parallel_test", test_files, test_args - Homebrew.failed = !$?.success? + spec_args = [ + "--color", + "-I", HOMEBREW_LIBRARY_PATH/"test", + "--require", "spec_helper", + "--format", "progress", + "--format", "ParallelTests::RSpec::RuntimeLogger", + "--out", "tmp/parallel_runtime_rspec.log" + ] + run_tests "parallel_rspec", spec_files, spec_args if (fs_leak_log = HOMEBREW_LIBRARY_PATH/"tmp/fs_leak.log").file? fs_leak_log_content = fs_leak_log.read diff --git a/Library/Homebrew/test/ARGV_spec.rb b/Library/Homebrew/test/ARGV_spec.rb new file mode 100644 index 0000000000..bcb7c3f70e --- /dev/null +++ b/Library/Homebrew/test/ARGV_spec.rb @@ -0,0 +1,149 @@ +require "extend/ARGV" + +describe HomebrewArgvExtension do + subject { argv.extend(HomebrewArgvExtension) } + let(:argv) { ["mxcl"] } + + describe "#formulae" do + it "raises an error when a Formula is unavailable" do + expect { subject.formulae }.to raise_error FormulaUnavailableError + end + + context "when there are no Formulae" do + let(:argv) { [] } + + it "returns an empty array" do + expect(subject.formulae).to be_empty + end + end + end + + describe "#casks" do + it "returns an empty array if there is no match" do + expect(subject.casks).to eq [] + end + end + + describe "#kegs" do + context "when there are matching Kegs" do + before(:each) do + keg = HOMEBREW_CELLAR + "mxcl/10.0" + keg.mkpath + end + + it "returns an array of Kegs" do + expect(subject.kegs.length).to eq 1 + end + end + + context "when there are no matching Kegs" do + let(:argv) { [] } + + it "returns an empty array" do + expect(subject.kegs).to be_empty + end + end + end + + describe "#named" do + let(:argv) { ["foo", "--debug", "-v"] } + + it "returns an array of non-option arguments" do + expect(subject.named).to eq ["foo"] + end + + context "when there are no named arguments" do + let(:argv) { [] } + + it "returns an empty array" do + expect(subject.named).to be_empty + end + end + end + + describe "#options_only" do + let(:argv) { ["--foo", "-vds", "a", "b", "cdefg"] } + + it "returns an array of option arguments" do + expect(subject.options_only).to eq ["--foo", "-vds"] + end + end + + describe "#flags_only" do + let(:argv) { ["--foo", "-vds", "a", "b", "cdefg"] } + + it "returns an array of flags" do + expect(subject.flags_only).to eq ["--foo"] + end + end + + describe "#empty?" do + let(:argv) { [] } + + it "returns true if it is empty" do + expect(subject).to be_empty + end + end + + describe "#switch?" do + let(:argv) { ["-ns", "-i", "--bar", "-a-bad-arg"] } + + it "returns true if the given string is a switch" do + %w[n s i].each do |s| + expect(subject.switch?(s)).to be true + end + end + + it "returns false if the given string is not a switch" do + %w[b ns bar --bar -n a bad arg].each do |s| + expect(subject.switch?(s)).to be false + end + end + end + + describe "#flag?" do + let(:argv) { ["--foo", "-bq", "--bar"] } + + it "returns true if the given string is a flag" do + expect(subject.flag?("--foo")).to eq true + expect(subject.flag?("--bar")).to eq true + end + + it "returns true if there is a switch with the same initial character" do + expect(subject.flag?("--baz")).to eq true + expect(subject.flag?("--qux")).to eq true + end + + it "returns false if there is no matching flag" do + expect(subject.flag?("--frotz")).to eq false + expect(subject.flag?("--debug")).to eq false + end + end + + describe "#value" do + let(:argv) { ["--foo=", "--bar=ab"] } + + it "returns the value for a given string" do + expect(subject.value("foo")).to eq "" + expect(subject.value("bar")).to eq "ab" + end + + it "returns nil if there is no matching argument" do + expect(subject.value("baz")).to be nil + end + end + + describe "#values" do + let(:argv) { ["--foo=", "--bar=a", "--baz=b,c"] } + + it "returns the value for a given argument" do + expect(subject.values("foo")).to eq [] + expect(subject.values("bar")).to eq ["a"] + expect(subject.values("baz")).to eq ["b", "c"] + end + + it "returns nil if there is no matching argument" do + expect(subject.values("qux")).to be nil + end + end +end diff --git a/Library/Homebrew/test/ARGV_test.rb b/Library/Homebrew/test/ARGV_test.rb deleted file mode 100644 index e93d09c394..0000000000 --- a/Library/Homebrew/test/ARGV_test.rb +++ /dev/null @@ -1,79 +0,0 @@ -require "testing_env" -require "extend/ARGV" - -class ArgvExtensionTests < Homebrew::TestCase - def setup - super - @argv = [].extend(HomebrewArgvExtension) - end - - def test_argv_formulae - @argv.unshift "mxcl" - assert_raises(FormulaUnavailableError) { @argv.formulae } - end - - def test_argv_casks - @argv.unshift "mxcl" - assert_equal [], @argv.casks - end - - def test_argv_kegs - keg = HOMEBREW_CELLAR + "mxcl/10.0" - keg.mkpath - @argv << "mxcl" - assert_equal 1, @argv.kegs.length - end - - def test_argv_named - @argv << "foo" << "--debug" << "-v" - assert_equal %w[foo], @argv.named - end - - def test_options_only - @argv << "--foo" << "-vds" << "a" << "b" << "cdefg" - assert_equal %w[--foo -vds], @argv.options_only - end - - def test_flags_only - @argv << "--foo" << "-vds" << "a" << "b" << "cdefg" - assert_equal %w[--foo], @argv.flags_only - end - - def test_empty_argv - assert_empty @argv.named - assert_empty @argv.kegs - assert_empty @argv.formulae - assert_empty @argv - end - - def test_switch? - @argv << "-ns" << "-i" << "--bar" << "-a-bad-arg" - %w[n s i].each { |s| assert @argv.switch?(s) } - %w[b ns bar --bar -n a bad arg].each { |s| assert !@argv.switch?(s) } - end - - def test_flag? - @argv << "--foo" << "-bq" << "--bar" - assert @argv.flag?("--foo") - assert @argv.flag?("--bar") - assert @argv.flag?("--baz") - assert @argv.flag?("--qux") - assert !@argv.flag?("--frotz") - assert !@argv.flag?("--debug") - end - - def test_value - @argv << "--foo=" << "--bar=ab" - assert_equal "", @argv.value("foo") - assert_equal "ab", @argv.value("bar") - assert_nil @argv.value("baz") - end - - def test_values - @argv << "--foo=" << "--bar=a" << "--baz=b,c" - assert_equal [], @argv.values("foo") - assert_equal ["a"], @argv.values("bar") - assert_equal ["b", "c"], @argv.values("baz") - assert_nil @argv.values("qux") - end -end diff --git a/Library/Homebrew/test/Gemfile b/Library/Homebrew/test/Gemfile index 5641684a72..25c8ce7ecb 100644 --- a/Library/Homebrew/test/Gemfile +++ b/Library/Homebrew/test/Gemfile @@ -4,6 +4,9 @@ gem "mocha" gem "minitest" gem "minitest-reporters" gem "parallel_tests" +gem "rspec" +gem "rspec-its", require: false +gem "rspec-wait", require: false group :coverage do gem "simplecov", require: false diff --git a/Library/Homebrew/test/Gemfile.lock b/Library/Homebrew/test/Gemfile.lock index 65ac7ef567..fba45d77b3 100644 --- a/Library/Homebrew/test/Gemfile.lock +++ b/Library/Homebrew/test/Gemfile.lock @@ -7,6 +7,7 @@ GEM json simplecov url + diff-lcs (1.3) docile (1.1.5) json (2.0.3) metaclass (0.0.4) @@ -21,6 +22,24 @@ GEM parallel (1.10.0) parallel_tests (2.13.0) parallel + rspec (3.5.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-core (3.5.4) + rspec-support (~> 3.5.0) + rspec-expectations (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-its (1.2.0) + rspec-core (>= 3.0.0) + rspec-expectations (>= 3.0.0) + rspec-mocks (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-support (3.5.0) + rspec-wait (0.0.9) + rspec (>= 3, < 4) ruby-progressbar (1.8.1) simplecov (0.13.0) docile (~> 1.1.0) @@ -38,7 +57,10 @@ DEPENDENCIES minitest-reporters mocha parallel_tests + rspec + rspec-its + rspec-wait simplecov BUNDLED WITH - 1.13.7 + 1.14.3 diff --git a/Library/Homebrew/test/bash_spec.rb b/Library/Homebrew/test/bash_spec.rb new file mode 100644 index 0000000000..1b0f150664 --- /dev/null +++ b/Library/Homebrew/test/bash_spec.rb @@ -0,0 +1,52 @@ +require "open3" + +RSpec::Matchers.define :have_valid_bash_syntax do + match do |file| + stdout, stderr, status = Open3.capture3("/bin/bash", "-n", file) + + @actual = [file, stderr] + + stdout.empty? && status.success? + end + + failure_message do |(file, stderr)| + "expected that #{file} is a valid Bash file:\n#{stderr}" + end +end + +describe "Bash" do + context "brew" do + subject { HOMEBREW_LIBRARY_PATH.parent.parent/"bin/brew" } + it { is_expected.to have_valid_bash_syntax } + end + + context "every `.sh` file" do + it "has valid bash syntax" do + Pathname.glob("#{HOMEBREW_LIBRARY_PATH}/**/*.sh").each do |path| + relative_path = path.relative_path_from(HOMEBREW_LIBRARY_PATH) + next if relative_path.to_s.start_with?("shims/", "test/", "vendor/") + + expect(path).to have_valid_bash_syntax + end + end + end + + context "Bash completion" do + subject { HOMEBREW_LIBRARY_PATH.parent.parent/"completions/bash/brew" } + it { is_expected.to have_valid_bash_syntax } + end + + context "every shim script" do + it "has valid bash syntax" do + # These have no file extension, but can be identified by their shebang. + (HOMEBREW_LIBRARY_PATH/"shims").find do |path| + next if path.directory? + next if path.symlink? + next unless path.executable? + next unless path.read(12) == "#!/bin/bash\n" + + expect(path).to have_valid_bash_syntax + end + end + end +end diff --git a/Library/Homebrew/test/bash_test.rb b/Library/Homebrew/test/bash_test.rb deleted file mode 100644 index 671ab5ec8d..0000000000 --- a/Library/Homebrew/test/bash_test.rb +++ /dev/null @@ -1,35 +0,0 @@ -require "testing_env" - -class BashTests < Homebrew::TestCase - def assert_valid_bash_syntax(file) - return unless file.exist? - output = Utils.popen_read("/bin/bash -n #{file} 2>&1") - assert $?.success?, output - end - - def test_bin_brew - assert_valid_bash_syntax HOMEBREW_LIBRARY_PATH.parent.parent/"bin/brew" - end - - def test_bash_code - Pathname.glob("#{HOMEBREW_LIBRARY_PATH}/**/*.sh").each do |pn| - pn_relative = pn.relative_path_from(HOMEBREW_LIBRARY_PATH) - next if pn_relative.to_s.start_with?("shims/", "test/", "vendor/") - assert_valid_bash_syntax pn - end - end - - def test_bash_completion - script = HOMEBREW_LIBRARY_PATH.parent.parent/"completions/bash/brew" - assert_valid_bash_syntax script - end - - def test_bash_shims - # These have no file extension, but can be identified by their shebang. - (HOMEBREW_LIBRARY_PATH/"shims").find do |pn| - next if pn.directory? || pn.symlink? - next unless pn.executable? && pn.read(12) == "#!/bin/bash\n" - assert_valid_bash_syntax pn - end - end -end diff --git a/Library/Homebrew/test/blacklist_spec.rb b/Library/Homebrew/test/blacklist_spec.rb new file mode 100644 index 0000000000..89d254893f --- /dev/null +++ b/Library/Homebrew/test/blacklist_spec.rb @@ -0,0 +1,111 @@ +require "blacklist" + +RSpec::Matchers.define :be_blacklisted do + match do |actual| + blacklisted?(actual) + end +end + +describe "Blacklist" do + context "rubygems" do + %w[gem rubygem rubygems].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "latex" do + %w[latex tex tex-live texlive TexLive].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "pip" do + subject { "pip" } + + it { is_expected.to be_blacklisted } + end + + context "pil" do + subject { "pil" } + + it { is_expected.to be_blacklisted } + end + + context "macruby" do + subject { "MacRuby" } + + it { is_expected.to be_blacklisted } + end + + context "lzma" do + %w[lzma liblzma].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "gtest" do + %w[gtest googletest google-test].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "gmock" do + %w[gmock googlemock google-mock].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "sshpass" do + subject { "sshpass" } + + it { is_expected.to be_blacklisted } + end + + context "gsutil" do + subject { "gsutil" } + + it { is_expected.to be_blacklisted } + end + + context "clojure" do + subject { "clojure" } + + it { is_expected.to be_blacklisted } + end + + context "osmium" do + %w[osmium Osmium].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "gfortran" do + subject { "gfortran" } + + it { is_expected.to be_blacklisted } + end + + context "play" do + subject { "play" } + + it { is_expected.to be_blacklisted } + end + + context "haskell_platform" do + subject { "haskell-platform" } + + it { is_expected.to be_blacklisted } + end +end diff --git a/Library/Homebrew/test/blacklist_test.rb b/Library/Homebrew/test/blacklist_test.rb deleted file mode 100644 index 585a354844..0000000000 --- a/Library/Homebrew/test/blacklist_test.rb +++ /dev/null @@ -1,68 +0,0 @@ -require "testing_env" -require "blacklist" - -class BlacklistTests < Homebrew::TestCase - def assert_blacklisted(s) - assert blacklisted?(s), "'#{s}' should be blacklisted" - end - - def test_rubygems - %w[gem rubygem rubygems].each { |s| assert_blacklisted s } - end - - def test_latex - %w[latex tex tex-live texlive TexLive].each { |s| assert_blacklisted s } - end - - def test_pip - assert_blacklisted "pip" - end - - def test_pil - assert_blacklisted "pil" - end - - def test_macruby - assert_blacklisted "MacRuby" - end - - def test_lzma - %w[lzma liblzma].each { |s| assert_blacklisted s } - end - - def test_gtest - %w[gtest googletest google-test].each { |s| assert_blacklisted s } - end - - def test_gmock - %w[gmock googlemock google-mock].each { |s| assert_blacklisted s } - end - - def test_sshpass - assert_blacklisted "sshpass" - end - - def test_gsutil - assert_blacklisted "gsutil" - end - - def test_clojure - assert_blacklisted "clojure" - end - - def test_osmium - %w[osmium Osmium].each { |s| assert_blacklisted s } - end - - def test_gfortran - assert_blacklisted "gfortran" - end - - def test_play - assert_blacklisted "play" - end - - def test_haskell_platform - assert_blacklisted "haskell-platform" - end -end diff --git a/Library/Homebrew/test/spec_helper.rb b/Library/Homebrew/test/spec_helper.rb new file mode 100644 index 0000000000..7762b385c8 --- /dev/null +++ b/Library/Homebrew/test/spec_helper.rb @@ -0,0 +1,77 @@ +require "find" +require "pathname" +require "rspec/its" +require "rspec/wait" +require "set" + +if ENV["HOMEBREW_TESTS_COVERAGE"] + require "simplecov" +end + +$LOAD_PATH.unshift(File.expand_path("#{ENV["HOMEBREW_LIBRARY"]}/Homebrew")) +$LOAD_PATH.unshift(File.expand_path("#{ENV["HOMEBREW_LIBRARY"]}/Homebrew/test/support/lib")) + +require "global" +require "tap" + +require "test/support/helper/shutup" + +TEST_DIRECTORIES = [ + CoreTap.instance.path/"Formula", + HOMEBREW_CACHE, + HOMEBREW_CACHE_FORMULA, + HOMEBREW_CELLAR, + HOMEBREW_LOCK_DIR, + HOMEBREW_LOGS, + HOMEBREW_TEMP, +].freeze + +RSpec.configure do |config| + config.order = :random + config.include(Test::Helper::Shutup) + config.around(:each) do |example| + begin + TEST_DIRECTORIES.each(&:mkpath) + + @__files_before_test = Find.find(TEST_TMPDIR).map { |f| f.sub(TEST_TMPDIR, "") } + + @__argv = ARGV.dup + @__env = ENV.to_hash # dup doesn't work on ENV + + example.run + ensure + ARGV.replace(@__argv) + ENV.replace(@__env) + + Tab.clear_cache + + FileUtils.rm_rf [ + TEST_DIRECTORIES.map(&:children), + HOMEBREW_LINKED_KEGS, + HOMEBREW_PINNED_KEGS, + HOMEBREW_PREFIX/".git", + HOMEBREW_PREFIX/"bin", + HOMEBREW_PREFIX/"share", + HOMEBREW_PREFIX/"opt", + HOMEBREW_PREFIX/"Caskroom", + HOMEBREW_LIBRARY/"Taps/caskroom", + HOMEBREW_LIBRARY/"Taps/homebrew/homebrew-bundle", + HOMEBREW_LIBRARY/"Taps/homebrew/homebrew-foo", + HOMEBREW_LIBRARY/"Taps/homebrew/homebrew-services", + HOMEBREW_LIBRARY/"Taps/homebrew/homebrew-shallow", + HOMEBREW_REPOSITORY/".git", + CoreTap.instance.path/".git", + CoreTap.instance.alias_dir, + CoreTap.instance.path/"formula_renames.json", + ] + + files_after_test = Find.find(TEST_TMPDIR).map { |f| f.sub(TEST_TMPDIR, "") } + + diff = Set.new(@__files_before_test).difference(Set.new(files_after_test)) + expect(diff).to be_empty, <<-EOS.undent + file leak detected: + #{diff.map { |f| " #{f}" }.join("\n")} + EOS + end + end +end diff --git a/Library/Homebrew/test/support/helper/test_case.rb b/Library/Homebrew/test/support/helper/test_case.rb index daa6634943..568beb947b 100644 --- a/Library/Homebrew/test/support/helper/test_case.rb +++ b/Library/Homebrew/test/support/helper/test_case.rb @@ -12,9 +12,6 @@ module Homebrew include Test::Helper::Shutup include Test::Helper::VersionAssertions - TEST_SHA1 = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef".freeze - TEST_SHA256 = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef".freeze - TEST_DIRECTORIES = [ CoreTap.instance.path/"Formula", HOMEBREW_CACHE, diff --git a/Library/Homebrew/test/support/lib/config.rb b/Library/Homebrew/test/support/lib/config.rb index f6fdb4ecbf..fb5c210fee 100644 --- a/Library/Homebrew/test/support/lib/config.rb +++ b/Library/Homebrew/test/support/lib/config.rb @@ -38,3 +38,6 @@ TESTBALL_SHA256 = "91e3f7930c98d7ccfb288e115ed52d06b0e5bc16fec7dce8bdda865300270 TESTBALL_PATCHES_SHA256 = "799c2d551ac5c3a5759bea7796631a7906a6a24435b52261a317133a0bfb34d9".freeze PATCH_A_SHA256 = "83404f4936d3257e65f176c4ffb5a5b8d6edd644a21c8d8dcc73e22a6d28fcfa".freeze PATCH_B_SHA256 = "57958271bb802a59452d0816e0670d16c8b70bdf6530bcf6f78726489ad89b90".freeze + +TEST_SHA1 = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef".freeze +TEST_SHA256 = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef".freeze