From d5d7b6c3dbe028672de90f1d5a04590e933030c5 Mon Sep 17 00:00:00 2001 From: Rylan Polster Date: Sun, 31 Jan 2021 14:50:29 -0500 Subject: [PATCH] style: remove RSpec/MultipleDescribes violations Co-authored-by: Nanda H Krishna --- Library/Homebrew/test/ENV_spec.rb | 360 ++-- Library/Homebrew/test/cleanup_spec.rb | 42 +- Library/Homebrew/test/dependency_spec.rb | 16 +- .../test/download_strategies/abstract_spec.rb | 37 + .../download_strategies/curl_post_spec.rb | 58 + .../test/download_strategies/curl_spec.rb | 191 +++ .../test/download_strategies/detector_spec.rb | 35 + .../test/download_strategies/git_spec.rb | 69 + .../download_strategies/github_git_spec.rb | 17 + .../download_strategies/subversion_spec.rb | 36 + .../test/download_strategies/vcs_spec.rb | 17 + .../Homebrew/test/download_strategies_spec.rb | 432 ----- Library/Homebrew/test/exceptions_spec.rb | 410 ++--- .../bottle_disable_reason_spec.rb} | 14 - .../formula_support/keg_only_reason_spec.rb | 18 + .../test/language/python/shebang_spec.rb | 47 + .../test/language/python/virtualenv_spec.rb | 98 ++ Library/Homebrew/test/language/python_spec.rb | 137 -- .../test/options/deprecated_option_spec.rb | 33 + Library/Homebrew/test/options/option_spec.rb | 30 + Library/Homebrew/test/options_spec.rb | 55 - Library/Homebrew/test/os/mac/mach_spec.rb | 330 ++-- Library/Homebrew/test/patch_spec.rb | 40 +- .../rubocops/checksum/checksum_case_spec.rb | 70 + .../rubocops/{ => checksum}/checksum_spec.rb | 66 - .../test/rubocops/class/class_name_spec.rb | 44 + .../test/rubocops/class/test_present.rb | 17 + .../{class_spec.rb => class/test_spec.rb} | 53 - .../rubocops/deprecate_disable/date_spec.rb | 152 ++ .../reason_spec.rb} | 148 -- .../rubocops/lines/class_inheritance_spec.rb | 20 + Library/Homebrew/test/rubocops/lines_spec.rb | 1485 ----------------- .../livecheck/regex_case_insensitive_spec.rb | 46 + .../livecheck/regex_extension_spec.rb | 46 + .../livecheck/regex_if_page_match_spec.rb | 36 + .../livecheck/regex_parentheses_spec.rb | 46 + .../test/rubocops/livecheck/skip_spec.rb | 44 + .../rubocops/livecheck/url_provided_spec.rb | 34 + .../rubocops/livecheck/url_symbol_spec.rb | 43 + .../Homebrew/test/rubocops/livecheck_spec.rb | 271 --- .../rubocops/text/assert_statements_spec.rb | 54 + .../test/rubocops/text/comments_spec.rb | 58 + .../test/rubocops/text/license_arrays_spec.rb | 59 + .../test/rubocops/text/licenses_spec.rb | 88 + .../test/rubocops/text/make_check_spec.rb | 46 + .../test/rubocops/text/miscellaneous_spec.rb | 483 ++++++ .../test/rubocops/text/mpi_check_spec.rb | 29 + .../rubocops/text/option_declarations_spec.rb | 159 ++ .../rubocops/text/python_versions_spec.rb | 184 ++ .../rubocops/text/safe_popen_commands_spec.rb | 59 + .../test/rubocops/text/shell_commands_spec.rb | 212 +++ .../rubocops/text/shell_variables_spec.rb | 86 + .../test/rubocops/text/strict_spec.rb | 136 ++ Library/Homebrew/test/rubocops/text_spec.rb | 132 -- .../Homebrew/test/rubocops/urls/git_spec.rb | 119 ++ .../test/rubocops/urls/git_strict_spec.rb | 89 + .../Homebrew/test/rubocops/urls/pypi_spec.rb | 39 + Library/Homebrew/test/rubocops/urls_spec.rb | 235 --- .../test/software_spec/bottle_spec.rb | 51 + .../Homebrew/test/software_spec/head_spec.rb | 16 + Library/Homebrew/test/software_spec_spec.rb | 59 - Library/Homebrew/test/tap_spec.rb | 106 +- Library/Homebrew/test/version_spec.rb | 114 +- 63 files changed, 3961 insertions(+), 3795 deletions(-) create mode 100644 Library/Homebrew/test/download_strategies/abstract_spec.rb create mode 100644 Library/Homebrew/test/download_strategies/curl_post_spec.rb create mode 100644 Library/Homebrew/test/download_strategies/curl_spec.rb create mode 100644 Library/Homebrew/test/download_strategies/detector_spec.rb create mode 100644 Library/Homebrew/test/download_strategies/git_spec.rb create mode 100644 Library/Homebrew/test/download_strategies/github_git_spec.rb create mode 100644 Library/Homebrew/test/download_strategies/subversion_spec.rb create mode 100644 Library/Homebrew/test/download_strategies/vcs_spec.rb delete mode 100644 Library/Homebrew/test/download_strategies_spec.rb rename Library/Homebrew/test/{formula_support_spec.rb => formula_support/bottle_disable_reason_spec.rb} (58%) create mode 100644 Library/Homebrew/test/formula_support/keg_only_reason_spec.rb create mode 100644 Library/Homebrew/test/language/python/shebang_spec.rb create mode 100644 Library/Homebrew/test/language/python/virtualenv_spec.rb create mode 100644 Library/Homebrew/test/options/deprecated_option_spec.rb create mode 100644 Library/Homebrew/test/options/option_spec.rb create mode 100644 Library/Homebrew/test/rubocops/checksum/checksum_case_spec.rb rename Library/Homebrew/test/rubocops/{ => checksum}/checksum_spec.rb (56%) create mode 100644 Library/Homebrew/test/rubocops/class/class_name_spec.rb create mode 100644 Library/Homebrew/test/rubocops/class/test_present.rb rename Library/Homebrew/test/rubocops/{class_spec.rb => class/test_spec.rb} (53%) create mode 100644 Library/Homebrew/test/rubocops/deprecate_disable/date_spec.rb rename Library/Homebrew/test/rubocops/{deprecate_disable_spec.rb => deprecate_disable/reason_spec.rb} (69%) create mode 100644 Library/Homebrew/test/rubocops/lines/class_inheritance_spec.rb create mode 100644 Library/Homebrew/test/rubocops/livecheck/regex_case_insensitive_spec.rb create mode 100644 Library/Homebrew/test/rubocops/livecheck/regex_extension_spec.rb create mode 100644 Library/Homebrew/test/rubocops/livecheck/regex_if_page_match_spec.rb create mode 100644 Library/Homebrew/test/rubocops/livecheck/regex_parentheses_spec.rb create mode 100644 Library/Homebrew/test/rubocops/livecheck/skip_spec.rb create mode 100644 Library/Homebrew/test/rubocops/livecheck/url_provided_spec.rb create mode 100644 Library/Homebrew/test/rubocops/livecheck/url_symbol_spec.rb delete mode 100644 Library/Homebrew/test/rubocops/livecheck_spec.rb create mode 100644 Library/Homebrew/test/rubocops/text/assert_statements_spec.rb create mode 100644 Library/Homebrew/test/rubocops/text/comments_spec.rb create mode 100644 Library/Homebrew/test/rubocops/text/license_arrays_spec.rb create mode 100644 Library/Homebrew/test/rubocops/text/licenses_spec.rb create mode 100644 Library/Homebrew/test/rubocops/text/make_check_spec.rb create mode 100644 Library/Homebrew/test/rubocops/text/miscellaneous_spec.rb create mode 100644 Library/Homebrew/test/rubocops/text/mpi_check_spec.rb create mode 100644 Library/Homebrew/test/rubocops/text/option_declarations_spec.rb create mode 100644 Library/Homebrew/test/rubocops/text/python_versions_spec.rb create mode 100644 Library/Homebrew/test/rubocops/text/safe_popen_commands_spec.rb create mode 100644 Library/Homebrew/test/rubocops/text/shell_commands_spec.rb create mode 100644 Library/Homebrew/test/rubocops/text/shell_variables_spec.rb create mode 100644 Library/Homebrew/test/rubocops/text/strict_spec.rb create mode 100644 Library/Homebrew/test/rubocops/urls/git_spec.rb create mode 100644 Library/Homebrew/test/rubocops/urls/git_strict_spec.rb create mode 100644 Library/Homebrew/test/rubocops/urls/pypi_spec.rb create mode 100644 Library/Homebrew/test/software_spec/bottle_spec.rb create mode 100644 Library/Homebrew/test/software_spec/head_spec.rb diff --git a/Library/Homebrew/test/ENV_spec.rb b/Library/Homebrew/test/ENV_spec.rb index 9a68c60a5d..7d1befdbbd 100644 --- a/Library/Homebrew/test/ENV_spec.rb +++ b/Library/Homebrew/test/ENV_spec.rb @@ -3,205 +3,207 @@ require "extend/ENV" -shared_examples EnvActivation do - subject(:env) { env_activation.extend(described_class) } +describe "ENV" do + shared_examples EnvActivation do + subject(:env) { env_activation.extend(described_class) } - let(:env_activation) { {}.extend(EnvActivation) } + let(:env_activation) { {}.extend(EnvActivation) } - it "supports switching compilers" do - subject.clang - expect(subject["LD"]).to be nil - expect(subject["CC"]).to eq(subject["OBJC"]) - end - - describe "#with_build_environment" do - it "restores the environment" do - before = subject.dup - - subject.with_build_environment do - subject["foo"] = "bar" - end - - expect(subject["foo"]).to be nil - expect(subject).to eq(before) + it "supports switching compilers" do + subject.clang + expect(subject["LD"]).to be nil + expect(subject["CC"]).to eq(subject["OBJC"]) end - it "ensures the environment is restored" do - before = subject.dup + describe "#with_build_environment" do + it "restores the environment" do + before = subject.dup - expect { subject.with_build_environment do subject["foo"] = "bar" - raise StandardError end - }.to raise_error(StandardError) - expect(subject["foo"]).to be nil - expect(subject).to eq(before) - end - - it "returns the value of the block" do - expect(subject.with_build_environment { 1 }).to eq(1) - end - - it "does not mutate the interface" do - expected = subject.methods - - subject.with_build_environment do - expect(subject.methods).to eq(expected) + expect(subject["foo"]).to be nil + expect(subject).to eq(before) end - expect(subject.methods).to eq(expected) + it "ensures the environment is restored" do + before = subject.dup + + expect { + subject.with_build_environment do + subject["foo"] = "bar" + raise StandardError + end + }.to raise_error(StandardError) + + expect(subject["foo"]).to be nil + expect(subject).to eq(before) + end + + it "returns the value of the block" do + expect(subject.with_build_environment { 1 }).to eq(1) + end + + it "does not mutate the interface" do + expected = subject.methods + + subject.with_build_environment do + expect(subject.methods).to eq(expected) + end + + expect(subject.methods).to eq(expected) + end + end + + describe "#append" do + it "appends to an existing key" do + subject["foo"] = "bar" + subject.append "foo", "1" + expect(subject["foo"]).to eq("bar 1") + end + + it "appends to an existing empty key" do + subject["foo"] = "" + subject.append "foo", "1" + expect(subject["foo"]).to eq("1") + end + + it "appends to a non-existent key" do + subject.append "foo", "1" + expect(subject["foo"]).to eq("1") + end + + # NOTE: this may be a wrong behavior; we should probably reject objects that + # do not respond to #to_str. For now this documents existing behavior. + it "coerces a value to a string" do + subject.append "foo", 42 + expect(subject["foo"]).to eq("42") + end + end + + describe "#prepend" do + it "prepends to an existing key" do + subject["foo"] = "bar" + subject.prepend "foo", "1" + expect(subject["foo"]).to eq("1 bar") + end + + it "prepends to an existing empty key" do + subject["foo"] = "" + subject.prepend "foo", "1" + expect(subject["foo"]).to eq("1") + end + + it "prepends to a non-existent key" do + subject.prepend "foo", "1" + expect(subject["foo"]).to eq("1") + end + + # NOTE: this may be a wrong behavior; we should probably reject objects that + # do not respond to #to_str. For now this documents existing behavior. + it "coerces a value to a string" do + subject.prepend "foo", 42 + expect(subject["foo"]).to eq("42") + end + end + + describe "#append_path" do + it "appends to a path" do + subject.append_path "FOO", "/usr/bin" + expect(subject["FOO"]).to eq("/usr/bin") + + subject.append_path "FOO", "/bin" + expect(subject["FOO"]).to eq("/usr/bin#{File::PATH_SEPARATOR}/bin") + end + end + + describe "#prepend_path" do + it "prepends to a path" do + subject.prepend_path "FOO", "/usr/local" + expect(subject["FOO"]).to eq("/usr/local") + + subject.prepend_path "FOO", "/usr" + expect(subject["FOO"]).to eq("/usr#{File::PATH_SEPARATOR}/usr/local") + end + end + + describe "#compiler" do + it "allows switching compilers" do + subject.public_send("gcc-6") + expect(subject.compiler).to eq("gcc-6") + end + end + + example "deparallelize_block_form_restores_makeflags" do + subject["MAKEFLAGS"] = "-j4" + + subject.deparallelize do + expect(subject["MAKEFLAGS"]).to be nil + end + + expect(subject["MAKEFLAGS"]).to eq("-j4") + end + + describe "#sensitive_environment" do + it "list sensitive environment" do + subject["SECRET_TOKEN"] = "password" + expect(subject.sensitive_environment).to include("SECRET_TOKEN") + end + end + + describe "#clear_sensitive_environment!" do + it "removes sensitive environment variables" do + subject["SECRET_TOKEN"] = "password" + subject.clear_sensitive_environment! + expect(subject).not_to include("SECRET_TOKEN") + end + + it "leaves non-sensitive environment variables alone" do + subject["FOO"] = "bar" + subject.clear_sensitive_environment! + expect(subject["FOO"]).to eq "bar" + end + end + + describe "#compiler_any_clang?" do + it "returns true for llvm_clang" do + expect(subject.compiler_any_clang?(:llvm_clang)).to be true + end end end - describe "#append" do - it "appends to an existing key" do - subject["foo"] = "bar" - subject.append "foo", "1" - expect(subject["foo"]).to eq("bar 1") - end - - it "appends to an existing empty key" do - subject["foo"] = "" - subject.append "foo", "1" - expect(subject["foo"]).to eq("1") - end - - it "appends to a non-existent key" do - subject.append "foo", "1" - expect(subject["foo"]).to eq("1") - end - - # NOTE: this may be a wrong behavior; we should probably reject objects that - # do not respond to #to_str. For now this documents existing behavior. - it "coerces a value to a string" do - subject.append "foo", 42 - expect(subject["foo"]).to eq("42") - end + describe Stdenv do + include_examples EnvActivation end - describe "#prepend" do - it "prepends to an existing key" do - subject["foo"] = "bar" - subject.prepend "foo", "1" - expect(subject["foo"]).to eq("1 bar") + describe Superenv do + include_examples EnvActivation + + it "initializes deps" do + expect(env.deps).to eq([]) + expect(env.keg_only_deps).to eq([]) end - it "prepends to an existing empty key" do - subject["foo"] = "" - subject.prepend "foo", "1" - expect(subject["foo"]).to eq("1") - end + describe "#cxx11" do + it "supports gcc-5" do + env["HOMEBREW_CC"] = "gcc-5" + env.cxx11 + expect(env["HOMEBREW_CCCFG"]).to include("x") + end - it "prepends to a non-existent key" do - subject.prepend "foo", "1" - expect(subject["foo"]).to eq("1") - end + example "supports gcc-6" do + env["HOMEBREW_CC"] = "gcc-6" + env.cxx11 + expect(env["HOMEBREW_CCCFG"]).to include("x") + end - # NOTE: this may be a wrong behavior; we should probably reject objects that - # do not respond to #to_str. For now this documents existing behavior. - it "coerces a value to a string" do - subject.prepend "foo", 42 - expect(subject["foo"]).to eq("42") - end - end - - describe "#append_path" do - it "appends to a path" do - subject.append_path "FOO", "/usr/bin" - expect(subject["FOO"]).to eq("/usr/bin") - - subject.append_path "FOO", "/bin" - expect(subject["FOO"]).to eq("/usr/bin#{File::PATH_SEPARATOR}/bin") - end - end - - describe "#prepend_path" do - it "prepends to a path" do - subject.prepend_path "FOO", "/usr/local" - expect(subject["FOO"]).to eq("/usr/local") - - subject.prepend_path "FOO", "/usr" - expect(subject["FOO"]).to eq("/usr#{File::PATH_SEPARATOR}/usr/local") - end - end - - describe "#compiler" do - it "allows switching compilers" do - subject.public_send("gcc-6") - expect(subject.compiler).to eq("gcc-6") - end - end - - example "deparallelize_block_form_restores_makeflags" do - subject["MAKEFLAGS"] = "-j4" - - subject.deparallelize do - expect(subject["MAKEFLAGS"]).to be nil - end - - expect(subject["MAKEFLAGS"]).to eq("-j4") - end - - describe "#sensitive_environment" do - it "list sensitive environment" do - subject["SECRET_TOKEN"] = "password" - expect(subject.sensitive_environment).to include("SECRET_TOKEN") - end - end - - describe "#clear_sensitive_environment!" do - it "removes sensitive environment variables" do - subject["SECRET_TOKEN"] = "password" - subject.clear_sensitive_environment! - expect(subject).not_to include("SECRET_TOKEN") - end - - it "leaves non-sensitive environment variables alone" do - subject["FOO"] = "bar" - subject.clear_sensitive_environment! - expect(subject["FOO"]).to eq "bar" - end - end - - describe "#compiler_any_clang?" do - it "returns true for llvm_clang" do - expect(subject.compiler_any_clang?(:llvm_clang)).to be true - end - end -end - -describe Stdenv do - include_examples EnvActivation -end - -describe Superenv do - include_examples EnvActivation - - it "initializes deps" do - expect(env.deps).to eq([]) - expect(env.keg_only_deps).to eq([]) - end - - describe "#cxx11" do - it "supports gcc-5" do - env["HOMEBREW_CC"] = "gcc-5" - env.cxx11 - expect(env["HOMEBREW_CCCFG"]).to include("x") - end - - example "supports gcc-6" do - env["HOMEBREW_CC"] = "gcc-6" - env.cxx11 - expect(env["HOMEBREW_CCCFG"]).to include("x") - end - - it "supports clang" do - env["HOMEBREW_CC"] = "clang" - env.cxx11 - expect(env["HOMEBREW_CCCFG"]).to include("x") - expect(env["HOMEBREW_CCCFG"]).to include("g") + it "supports clang" do + env["HOMEBREW_CC"] = "clang" + env.cxx11 + expect(env["HOMEBREW_CCCFG"]).to include("x") + expect(env["HOMEBREW_CCCFG"]).to include("g") + end end end end diff --git a/Library/Homebrew/test/cleanup_spec.rb b/Library/Homebrew/test/cleanup_spec.rb index a582b007c7..75dbe01560 100644 --- a/Library/Homebrew/test/cleanup_spec.rb +++ b/Library/Homebrew/test/cleanup_spec.rb @@ -8,28 +8,6 @@ require "fileutils" using Homebrew::Cleanup::CleanupRefinement -describe Homebrew::Cleanup::CleanupRefinement do - describe "::prune?" do - alias_matcher :be_pruned, :be_prune - - subject(:path) { HOMEBREW_CACHE/"foo" } - - before do - path.mkpath - end - - it "returns true when ctime and mtime < days_default" do - allow_any_instance_of(Pathname).to receive(:ctime).and_return(2.days.ago) - allow_any_instance_of(Pathname).to receive(:mtime).and_return(2.days.ago) - expect(path.prune?(1)).to be true - end - - it "returns false when ctime and mtime >= days_default" do - expect(path.prune?(2)).to be false - end - end -end - describe Homebrew::Cleanup do subject(:cleanup) { described_class.new } @@ -49,6 +27,26 @@ describe Homebrew::Cleanup do FileUtils.rm_rf HOMEBREW_LIBRARY/"Homebrew" end + describe "::CleanupRefinement::prune?" do + alias_matcher :be_pruned, :be_prune + + subject(:path) { HOMEBREW_CACHE/"foo" } + + before do + path.mkpath + end + + it "returns true when ctime and mtime < days_default" do + allow_any_instance_of(Pathname).to receive(:ctime).and_return(2.days.ago) + allow_any_instance_of(Pathname).to receive(:mtime).and_return(2.days.ago) + expect(path.prune?(1)).to be true + end + + it "returns false when ctime and mtime >= days_default" do + expect(path.prune?(2)).to be false + end + end + describe "::cleanup" do it "removes .DS_Store and lock files" do cleanup.clean! diff --git a/Library/Homebrew/test/dependency_spec.rb b/Library/Homebrew/test/dependency_spec.rb index dea2c83982..b20c1ef6a7 100644 --- a/Library/Homebrew/test/dependency_spec.rb +++ b/Library/Homebrew/test/dependency_spec.rb @@ -100,16 +100,16 @@ describe Dependency do expect(foo1).not_to eq(foo3) expect(foo1).not_to eql(foo3) end -end -describe TapDependency do - subject(:dependency) { described_class.new("foo/bar/dog") } + describe TapDependency do + subject(:dependency) { described_class.new("foo/bar/dog") } - specify "#tap" do - expect(dependency.tap).to eq(Tap.new("foo", "bar")) - end + specify "#tap" do + expect(dependency.tap).to eq(Tap.new("foo", "bar")) + end - specify "#option_names" do - expect(dependency.option_names).to eq(%w[dog]) + specify "#option_names" do + expect(dependency.option_names).to eq(%w[dog]) + end end end diff --git a/Library/Homebrew/test/download_strategies/abstract_spec.rb b/Library/Homebrew/test/download_strategies/abstract_spec.rb new file mode 100644 index 0000000000..00fe1a146f --- /dev/null +++ b/Library/Homebrew/test/download_strategies/abstract_spec.rb @@ -0,0 +1,37 @@ +# typed: false +# frozen_string_literal: true + +require "download_strategy" + +describe AbstractDownloadStrategy do + subject(:strategy) { described_class.new(url, name, version, **specs) } + + let(:specs) { {} } + let(:name) { "foo" } + let(:url) { "https://example.com/foo.tar.gz" } + let(:version) { nil } + let(:args) { %w[foo bar baz] } + + specify "#source_modified_time" do + Mktemp.new("mtime") do + FileUtils.touch "foo", mtime: Time.now - 10 + FileUtils.touch "bar", mtime: Time.now - 100 + FileUtils.ln_s "not-exist", "baz" + expect(strategy.source_modified_time).to eq(File.mtime("foo")) + end + end + + context "when specs[:bottle]" do + let(:specs) { { bottle: true } } + + it "extends Pourable" do + expect(strategy).to be_a_kind_of(AbstractDownloadStrategy::Pourable) + end + end + + context "without specs[:bottle]" do + it "is does not extend Pourable" do + expect(strategy).not_to be_a_kind_of(AbstractDownloadStrategy::Pourable) + end + end +end diff --git a/Library/Homebrew/test/download_strategies/curl_post_spec.rb b/Library/Homebrew/test/download_strategies/curl_post_spec.rb new file mode 100644 index 0000000000..0d47b8c6f0 --- /dev/null +++ b/Library/Homebrew/test/download_strategies/curl_post_spec.rb @@ -0,0 +1,58 @@ +# typed: false +# frozen_string_literal: true + +require "download_strategy" + +describe CurlPostDownloadStrategy do + subject(:strategy) { described_class.new(url, name, version, **specs) } + + let(:name) { "foo" } + let(:url) { "https://example.com/foo.tar.gz" } + let(:version) { "1.2.3" } + let(:specs) { {} } + + describe "#fetch" do + before do + strategy.temporary_path.dirname.mkpath + FileUtils.touch strategy.temporary_path + end + + context "with :using and :data specified" do + let(:specs) { + { + using: :post, + data: { + form: "data", + is: "good", + }, + } + } + + it "adds the appropriate curl args" do + expect(strategy).to receive(:system_command).with( + /curl/, + hash_including(args: array_including_cons("-d", "form=data").and(array_including_cons("-d", "is=good"))), + ) + .at_least(:once) + .and_return(instance_double(SystemCommand::Result, success?: true, stdout: "", assert_success!: nil)) + + strategy.fetch + end + end + + context "with :using but no :data" do + let(:specs) { { using: :post } } + + it "adds the appropriate curl args" do + expect(strategy).to receive(:system_command).with( + /curl/, + hash_including(args: array_including_cons("-X", "POST")), + ) + .at_least(:once) + .and_return(instance_double(SystemCommand::Result, success?: true, stdout: "", assert_success!: nil)) + + strategy.fetch + end + end + end +end diff --git a/Library/Homebrew/test/download_strategies/curl_spec.rb b/Library/Homebrew/test/download_strategies/curl_spec.rb new file mode 100644 index 0000000000..6ea5e5be0c --- /dev/null +++ b/Library/Homebrew/test/download_strategies/curl_spec.rb @@ -0,0 +1,191 @@ +# typed: false +# frozen_string_literal: true + +require "download_strategy" + +describe CurlDownloadStrategy do + subject(:strategy) { described_class.new(url, name, version, **specs) } + + let(:name) { "foo" } + let(:url) { "https://example.com/foo.tar.gz" } + let(:version) { "1.2.3" } + let(:specs) { { user: "download:123456" } } + + it "parses the opts and sets the corresponding args" do + expect(strategy.send(:_curl_args)).to eq(["--user", "download:123456"]) + end + + describe "#cached_location" do + subject(:location) { described_class.new(url, name, version, **specs).cached_location } + + context "when URL ends with file" do + it { + expect(location).to eq( + HOMEBREW_CACHE/"downloads/3d1c0ae7da22be9d83fb1eb774df96b7c4da71d3cf07e1cb28555cf9a5e5af70--foo.tar.gz", + ) + } + end + + context "when URL file is in middle" do + let(:url) { "https://example.com/foo.tar.gz/from/this/mirror" } + + it { + expect(location).to eq( + HOMEBREW_CACHE/"downloads/1ab61269ba52c83994510b1e28dd04167a2f2e8393a35a9c50c1f7d33fd8f619--foo.tar.gz", + ) + } + end + end + + describe "#fetch" do + before do + strategy.temporary_path.dirname.mkpath + FileUtils.touch strategy.temporary_path + end + + it "calls curl with default arguments" do + expect(strategy).to receive(:curl).with( + "--location", + "--remote-time", + "--continue-at", "0", + "--output", an_instance_of(Pathname), + url, + an_instance_of(Hash) + ) + + strategy.fetch + end + + context "with an explicit user agent" do + let(:specs) { { user_agent: "Mozilla/25.0.1" } } + + it "adds the appropriate curl args" do + expect(strategy).to receive(:system_command).with( + /curl/, + hash_including(args: array_including_cons("--user-agent", "Mozilla/25.0.1")), + ) + .at_least(:once) + .and_return(instance_double(SystemCommand::Result, success?: true, stdout: "", assert_success!: nil)) + + strategy.fetch + end + end + + context "with a generalized fake user agent" do + alias_matcher :a_string_matching, :match + + let(:specs) { { user_agent: :fake } } + + it "adds the appropriate curl args" do + expect(strategy).to receive(:system_command).with( + /curl/, + hash_including(args: array_including_cons( + "--user-agent", + a_string_matching(/Mozilla.*Mac OS X 10.*AppleWebKit/), + )), + ) + .at_least(:once) + .and_return(instance_double(SystemCommand::Result, success?: true, stdout: "", assert_success!: nil)) + + strategy.fetch + end + end + + context "with cookies set" do + let(:specs) { + { + cookies: { + coo: "k/e", + mon: "ster", + }, + } + } + + it "adds the appropriate curl args and does not URL-encode the cookies" do + expect(strategy).to receive(:system_command).with( + /curl/, + hash_including(args: array_including_cons("-b", "coo=k/e;mon=ster")), + ) + .at_least(:once) + .and_return(instance_double(SystemCommand::Result, success?: true, stdout: "", assert_success!: nil)) + + strategy.fetch + end + end + + context "with referer set" do + let(:specs) { { referer: "https://somehost/also" } } + + it "adds the appropriate curl args" do + expect(strategy).to receive(:system_command).with( + /curl/, + hash_including(args: array_including_cons("-e", "https://somehost/also")), + ) + .at_least(:once) + .and_return(instance_double(SystemCommand::Result, success?: true, stdout: "", assert_success!: nil)) + + strategy.fetch + end + end + + context "with headers set" do + alias_matcher :a_string_matching, :match + + let(:specs) { { headers: ["foo", "bar"] } } + + it "adds the appropriate curl args" do + expect(strategy).to receive(:system_command).with( + /curl/, + hash_including(args: array_including_cons("--header", "foo").and(array_including_cons("--header", "bar"))), + ) + .at_least(:once) + .and_return(instance_double(SystemCommand::Result, success?: true, stdout: "", assert_success!: nil)) + + strategy.fetch + end + end + end + + describe "#cached_location" do + context "with a file name trailing the URL path" do + let(:url) { "https://example.com/cask.dmg" } + + its("cached_location.extname") { is_expected.to eq(".dmg") } + end + + context "with a file name trailing the first query parameter" do + let(:url) { "https://example.com/download?file=cask.zip&a=1" } + + its("cached_location.extname") { is_expected.to eq(".zip") } + end + + context "with a file name trailing the second query parameter" do + let(:url) { "https://example.com/dl?a=1&file=cask.zip&b=2" } + + its("cached_location.extname") { is_expected.to eq(".zip") } + end + + context "with an unusually long query string" do + let(:url) do + [ + "https://node49152.ssl.fancycdn.example.com", + "/fancycdn/node/49152/file/upload/download", + "?cask_class=zf920df", + "&cask_group=2348779087242312", + "&cask_archive_file_name=cask.zip", + "&signature=CGmDulxL8pmutKTlCleNTUY%2FyO9Xyl5u9yVZUE0", + "uWrjadjuz67Jp7zx3H7NEOhSyOhu8nzicEHRBjr3uSoOJzwkLC8L", + "BLKnz%2B2X%2Biq5m6IdwSVFcLp2Q1Hr2kR7ETn3rF1DIq5o0lHC", + "yzMmyNe5giEKJNW8WF0KXriULhzLTWLSA3ZTLCIofAdRiiGje1kN", + "YY3C0SBqymQB8CG3ONn5kj7CIGbxrDOq5xI2ZSJdIyPysSX7SLvE", + "DBw2KdR24q9t1wfjS9LUzelf5TWk6ojj8p9%2FHjl%2Fi%2FVCXN", + "N4o1mW%2FMayy2tTY1qcC%2FTmqI1ulZS8SNuaSgr9Iys9oDF1%2", + "BPK%2B4Sg==", + ].join + end + + its("cached_location.extname") { is_expected.to eq(".zip") } + its("cached_location.to_path.length") { is_expected.to be_between(0, 255) } + end + end +end diff --git a/Library/Homebrew/test/download_strategies/detector_spec.rb b/Library/Homebrew/test/download_strategies/detector_spec.rb new file mode 100644 index 0000000000..ecbb13cbc2 --- /dev/null +++ b/Library/Homebrew/test/download_strategies/detector_spec.rb @@ -0,0 +1,35 @@ +# typed: false +# frozen_string_literal: true + +require "download_strategy" + +describe DownloadStrategyDetector do + describe "::detect" do + subject(:strategy_detector) { described_class.detect(url, strategy) } + + let(:url) { Object.new } + let(:strategy) { nil } + + context "when given Git URL" do + let(:url) { "git://example.com/foo.git" } + + it { is_expected.to eq(GitDownloadStrategy) } + end + + context "when given a GitHub Git URL" do + let(:url) { "https://github.com/homebrew/brew.git" } + + it { is_expected.to eq(GitHubGitDownloadStrategy) } + end + + it "defaults to curl" do + expect(strategy_detector).to eq(CurlDownloadStrategy) + end + + it "raises an error when passed an unrecognized strategy" do + expect { + described_class.detect("foo", Class.new) + }.to raise_error(TypeError) + end + end +end diff --git a/Library/Homebrew/test/download_strategies/git_spec.rb b/Library/Homebrew/test/download_strategies/git_spec.rb new file mode 100644 index 0000000000..a9d8e6215b --- /dev/null +++ b/Library/Homebrew/test/download_strategies/git_spec.rb @@ -0,0 +1,69 @@ +# typed: false +# frozen_string_literal: true + +require "download_strategy" + +describe GitDownloadStrategy do + subject(:strategy) { described_class.new(url, name, version) } + + let(:name) { "baz" } + let(:url) { "https://github.com/homebrew/foo" } + let(:version) { nil } + let(:cached_location) { subject.cached_location } + + before do + @commit_id = 1 + FileUtils.mkpath cached_location + end + + def git_commit_all + system "git", "add", "--all" + system "git", "commit", "-m", "commit number #{@commit_id}" + @commit_id += 1 + end + + def setup_git_repo + system "git", "init" + system "git", "remote", "add", "origin", "https://github.com/Homebrew/homebrew-foo" + FileUtils.touch "README" + git_commit_all + end + + describe "#source_modified_time" do + it "returns the right modification time" do + cached_location.cd do + setup_git_repo + end + expect(strategy.source_modified_time.to_i).to eq(1_485_115_153) + end + end + + specify "#last_commit" do + cached_location.cd do + setup_git_repo + FileUtils.touch "LICENSE" + git_commit_all + end + expect(strategy.last_commit).to eq("f68266e") + end + + describe "#fetch_last_commit" do + let(:url) { "file://#{remote_repo}" } + let(:version) { Version.create("HEAD") } + let(:remote_repo) { HOMEBREW_PREFIX/"remote_repo" } + + before { remote_repo.mkpath } + + after { FileUtils.rm_rf remote_repo } + + it "fetches the hash of the last commit" do + remote_repo.cd do + setup_git_repo + FileUtils.touch "LICENSE" + git_commit_all + end + + expect(strategy.fetch_last_commit).to eq("f68266e") + end + end +end diff --git a/Library/Homebrew/test/download_strategies/github_git_spec.rb b/Library/Homebrew/test/download_strategies/github_git_spec.rb new file mode 100644 index 0000000000..26948c5b1f --- /dev/null +++ b/Library/Homebrew/test/download_strategies/github_git_spec.rb @@ -0,0 +1,17 @@ +# typed: false +# frozen_string_literal: true + +require "download_strategy" + +describe GitHubGitDownloadStrategy do + subject(:strategy) { described_class.new(url, name, version) } + + let(:name) { "brew" } + let(:url) { "https://github.com/homebrew/brew.git" } + let(:version) { nil } + + it "parses the URL and sets the corresponding instance variables" do + expect(strategy.instance_variable_get(:@user)).to eq("homebrew") + expect(strategy.instance_variable_get(:@repo)).to eq("brew") + end +end diff --git a/Library/Homebrew/test/download_strategies/subversion_spec.rb b/Library/Homebrew/test/download_strategies/subversion_spec.rb new file mode 100644 index 0000000000..04eed96b85 --- /dev/null +++ b/Library/Homebrew/test/download_strategies/subversion_spec.rb @@ -0,0 +1,36 @@ +# typed: false +# frozen_string_literal: true + +require "download_strategy" + +describe SubversionDownloadStrategy do + subject(:strategy) { described_class.new(url, name, version, **specs) } + + let(:name) { "foo" } + let(:url) { "https://example.com/foo.tar.gz" } + let(:version) { "1.2.3" } + let(:specs) { {} } + + describe "#fetch" do + context "with :trust_cert set" do + let(:specs) { { trust_cert: true } } + + it "adds the appropriate svn args" do + expect(strategy).to receive(:system_command!) + .with("svn", hash_including(args: array_including("--trust-server-cert", "--non-interactive"))) + strategy.fetch + end + end + + context "with :revision set" do + let(:specs) { { revision: "10" } } + + it "adds svn arguments for :revision" do + expect(strategy).to receive(:system_command!) + .with("svn", hash_including(args: array_including_cons("-r", "10"))) + + strategy.fetch + end + end + end +end diff --git a/Library/Homebrew/test/download_strategies/vcs_spec.rb b/Library/Homebrew/test/download_strategies/vcs_spec.rb new file mode 100644 index 0000000000..d4406088b7 --- /dev/null +++ b/Library/Homebrew/test/download_strategies/vcs_spec.rb @@ -0,0 +1,17 @@ +# typed: false +# frozen_string_literal: true + +require "download_strategy" + +describe VCSDownloadStrategy do + let(:url) { "https://example.com/bar" } + let(:version) { nil } + + describe "#cached_location" do + it "returns the path of the cached resource" do + allow_any_instance_of(described_class).to receive(:cache_tag).and_return("foo") + downloader = described_class.new(url, "baz", version) + expect(downloader.cached_location).to eq(HOMEBREW_CACHE/"baz--foo") + end + end +end diff --git a/Library/Homebrew/test/download_strategies_spec.rb b/Library/Homebrew/test/download_strategies_spec.rb deleted file mode 100644 index 5d1a20603d..0000000000 --- a/Library/Homebrew/test/download_strategies_spec.rb +++ /dev/null @@ -1,432 +0,0 @@ -# typed: false -# frozen_string_literal: true - -require "download_strategy" - -describe AbstractDownloadStrategy do - subject(:strategy) { described_class.new(url, name, version, **specs) } - - let(:specs) { {} } - let(:name) { "foo" } - let(:url) { "https://example.com/foo.tar.gz" } - let(:version) { nil } - let(:args) { %w[foo bar baz] } - - specify "#source_modified_time" do - Mktemp.new("mtime") do - FileUtils.touch "foo", mtime: Time.now - 10 - FileUtils.touch "bar", mtime: Time.now - 100 - FileUtils.ln_s "not-exist", "baz" - expect(strategy.source_modified_time).to eq(File.mtime("foo")) - end - end - - context "when specs[:bottle]" do - let(:specs) { { bottle: true } } - - it "extends Pourable" do - expect(strategy).to be_a_kind_of(AbstractDownloadStrategy::Pourable) - end - end - - context "without specs[:bottle]" do - it "is does not extend Pourable" do - expect(strategy).not_to be_a_kind_of(AbstractDownloadStrategy::Pourable) - end - end -end - -describe VCSDownloadStrategy do - let(:url) { "https://example.com/bar" } - let(:version) { nil } - - describe "#cached_location" do - it "returns the path of the cached resource" do - allow_any_instance_of(described_class).to receive(:cache_tag).and_return("foo") - downloader = described_class.new(url, "baz", version) - expect(downloader.cached_location).to eq(HOMEBREW_CACHE/"baz--foo") - end - end -end - -describe GitHubGitDownloadStrategy do - subject(:strategy) { described_class.new(url, name, version) } - - let(:name) { "brew" } - let(:url) { "https://github.com/homebrew/brew.git" } - let(:version) { nil } - - it "parses the URL and sets the corresponding instance variables" do - expect(strategy.instance_variable_get(:@user)).to eq("homebrew") - expect(strategy.instance_variable_get(:@repo)).to eq("brew") - end -end - -describe GitDownloadStrategy do - subject(:strategy) { described_class.new(url, name, version) } - - let(:name) { "baz" } - let(:url) { "https://github.com/homebrew/foo" } - let(:version) { nil } - let(:cached_location) { subject.cached_location } - - before do - @commit_id = 1 - FileUtils.mkpath cached_location - end - - def git_commit_all - system "git", "add", "--all" - system "git", "commit", "-m", "commit number #{@commit_id}" - @commit_id += 1 - end - - def setup_git_repo - system "git", "init" - system "git", "remote", "add", "origin", "https://github.com/Homebrew/homebrew-foo" - FileUtils.touch "README" - git_commit_all - end - - describe "#source_modified_time" do - it "returns the right modification time" do - cached_location.cd do - setup_git_repo - end - expect(strategy.source_modified_time.to_i).to eq(1_485_115_153) - end - end - - specify "#last_commit" do - cached_location.cd do - setup_git_repo - FileUtils.touch "LICENSE" - git_commit_all - end - expect(strategy.last_commit).to eq("f68266e") - end - - describe "#fetch_last_commit" do - let(:url) { "file://#{remote_repo}" } - let(:version) { Version.create("HEAD") } - let(:remote_repo) { HOMEBREW_PREFIX/"remote_repo" } - - before { remote_repo.mkpath } - - after { FileUtils.rm_rf remote_repo } - - it "fetches the hash of the last commit" do - remote_repo.cd do - setup_git_repo - FileUtils.touch "LICENSE" - git_commit_all - end - - expect(strategy.fetch_last_commit).to eq("f68266e") - end - end -end - -describe CurlDownloadStrategy do - subject(:strategy) { described_class.new(url, name, version, **specs) } - - let(:name) { "foo" } - let(:url) { "https://example.com/foo.tar.gz" } - let(:version) { "1.2.3" } - let(:specs) { { user: "download:123456" } } - - it "parses the opts and sets the corresponding args" do - expect(strategy.send(:_curl_args)).to eq(["--user", "download:123456"]) - end - - describe "#cached_location" do - subject(:location) { described_class.new(url, name, version, **specs).cached_location } - - context "when URL ends with file" do - it { - expect(location).to eq( - HOMEBREW_CACHE/"downloads/3d1c0ae7da22be9d83fb1eb774df96b7c4da71d3cf07e1cb28555cf9a5e5af70--foo.tar.gz", - ) - } - end - - context "when URL file is in middle" do - let(:url) { "https://example.com/foo.tar.gz/from/this/mirror" } - - it { - expect(location).to eq( - HOMEBREW_CACHE/"downloads/1ab61269ba52c83994510b1e28dd04167a2f2e8393a35a9c50c1f7d33fd8f619--foo.tar.gz", - ) - } - end - end - - describe "#fetch" do - before do - strategy.temporary_path.dirname.mkpath - FileUtils.touch strategy.temporary_path - end - - it "calls curl with default arguments" do - expect(strategy).to receive(:curl).with( - "--location", - "--remote-time", - "--continue-at", "0", - "--output", an_instance_of(Pathname), - url, - an_instance_of(Hash) - ) - - strategy.fetch - end - - context "with an explicit user agent" do - let(:specs) { { user_agent: "Mozilla/25.0.1" } } - - it "adds the appropriate curl args" do - expect(strategy).to receive(:system_command).with( - /curl/, - hash_including(args: array_including_cons("--user-agent", "Mozilla/25.0.1")), - ) - .at_least(:once) - .and_return(instance_double(SystemCommand::Result, success?: true, stdout: "", assert_success!: nil)) - - strategy.fetch - end - end - - context "with a generalized fake user agent" do - alias_matcher :a_string_matching, :match - - let(:specs) { { user_agent: :fake } } - - it "adds the appropriate curl args" do - expect(strategy).to receive(:system_command).with( - /curl/, - hash_including(args: array_including_cons( - "--user-agent", - a_string_matching(/Mozilla.*Mac OS X 10.*AppleWebKit/), - )), - ) - .at_least(:once) - .and_return(instance_double(SystemCommand::Result, success?: true, stdout: "", assert_success!: nil)) - - strategy.fetch - end - end - - context "with cookies set" do - let(:specs) { - { - cookies: { - coo: "k/e", - mon: "ster", - }, - } - } - - it "adds the appropriate curl args and does not URL-encode the cookies" do - expect(strategy).to receive(:system_command).with( - /curl/, - hash_including(args: array_including_cons("-b", "coo=k/e;mon=ster")), - ) - .at_least(:once) - .and_return(instance_double(SystemCommand::Result, success?: true, stdout: "", assert_success!: nil)) - - strategy.fetch - end - end - - context "with referer set" do - let(:specs) { { referer: "https://somehost/also" } } - - it "adds the appropriate curl args" do - expect(strategy).to receive(:system_command).with( - /curl/, - hash_including(args: array_including_cons("-e", "https://somehost/also")), - ) - .at_least(:once) - .and_return(instance_double(SystemCommand::Result, success?: true, stdout: "", assert_success!: nil)) - - strategy.fetch - end - end - - context "with headers set" do - alias_matcher :a_string_matching, :match - - let(:specs) { { headers: ["foo", "bar"] } } - - it "adds the appropriate curl args" do - expect(strategy).to receive(:system_command).with( - /curl/, - hash_including(args: array_including_cons("--header", "foo").and(array_including_cons("--header", "bar"))), - ) - .at_least(:once) - .and_return(instance_double(SystemCommand::Result, success?: true, stdout: "", assert_success!: nil)) - - strategy.fetch - end - end - end - - describe "#cached_location" do - context "with a file name trailing the URL path" do - let(:url) { "https://example.com/cask.dmg" } - - its("cached_location.extname") { is_expected.to eq(".dmg") } - end - - context "with a file name trailing the first query parameter" do - let(:url) { "https://example.com/download?file=cask.zip&a=1" } - - its("cached_location.extname") { is_expected.to eq(".zip") } - end - - context "with a file name trailing the second query parameter" do - let(:url) { "https://example.com/dl?a=1&file=cask.zip&b=2" } - - its("cached_location.extname") { is_expected.to eq(".zip") } - end - - context "with an unusually long query string" do - let(:url) do - [ - "https://node49152.ssl.fancycdn.example.com", - "/fancycdn/node/49152/file/upload/download", - "?cask_class=zf920df", - "&cask_group=2348779087242312", - "&cask_archive_file_name=cask.zip", - "&signature=CGmDulxL8pmutKTlCleNTUY%2FyO9Xyl5u9yVZUE0", - "uWrjadjuz67Jp7zx3H7NEOhSyOhu8nzicEHRBjr3uSoOJzwkLC8L", - "BLKnz%2B2X%2Biq5m6IdwSVFcLp2Q1Hr2kR7ETn3rF1DIq5o0lHC", - "yzMmyNe5giEKJNW8WF0KXriULhzLTWLSA3ZTLCIofAdRiiGje1kN", - "YY3C0SBqymQB8CG3ONn5kj7CIGbxrDOq5xI2ZSJdIyPysSX7SLvE", - "DBw2KdR24q9t1wfjS9LUzelf5TWk6ojj8p9%2FHjl%2Fi%2FVCXN", - "N4o1mW%2FMayy2tTY1qcC%2FTmqI1ulZS8SNuaSgr9Iys9oDF1%2", - "BPK%2B4Sg==", - ].join - end - - its("cached_location.extname") { is_expected.to eq(".zip") } - its("cached_location.to_path.length") { is_expected.to be_between(0, 255) } - end - end -end - -describe CurlPostDownloadStrategy do - subject(:strategy) { described_class.new(url, name, version, **specs) } - - let(:name) { "foo" } - let(:url) { "https://example.com/foo.tar.gz" } - let(:version) { "1.2.3" } - let(:specs) { {} } - - describe "#fetch" do - before do - strategy.temporary_path.dirname.mkpath - FileUtils.touch strategy.temporary_path - end - - context "with :using and :data specified" do - let(:specs) { - { - using: :post, - data: { - form: "data", - is: "good", - }, - } - } - - it "adds the appropriate curl args" do - expect(strategy).to receive(:system_command).with( - /curl/, - hash_including(args: array_including_cons("-d", "form=data").and(array_including_cons("-d", "is=good"))), - ) - .at_least(:once) - .and_return(instance_double(SystemCommand::Result, success?: true, stdout: "", assert_success!: nil)) - - strategy.fetch - end - end - - context "with :using but no :data" do - let(:specs) { { using: :post } } - - it "adds the appropriate curl args" do - expect(strategy).to receive(:system_command).with( - /curl/, - hash_including(args: array_including_cons("-X", "POST")), - ) - .at_least(:once) - .and_return(instance_double(SystemCommand::Result, success?: true, stdout: "", assert_success!: nil)) - - strategy.fetch - end - end - end -end - -describe SubversionDownloadStrategy do - subject(:strategy) { described_class.new(url, name, version, **specs) } - - let(:name) { "foo" } - let(:url) { "https://example.com/foo.tar.gz" } - let(:version) { "1.2.3" } - let(:specs) { {} } - - describe "#fetch" do - context "with :trust_cert set" do - let(:specs) { { trust_cert: true } } - - it "adds the appropriate svn args" do - expect(strategy).to receive(:system_command!) - .with("svn", hash_including(args: array_including("--trust-server-cert", "--non-interactive"))) - strategy.fetch - end - end - - context "with :revision set" do - let(:specs) { { revision: "10" } } - - it "adds svn arguments for :revision" do - expect(strategy).to receive(:system_command!) - .with("svn", hash_including(args: array_including_cons("-r", "10"))) - - strategy.fetch - end - end - end -end - -describe DownloadStrategyDetector do - describe "::detect" do - subject(:strategy_detector) { described_class.detect(url, strategy) } - - let(:url) { Object.new } - let(:strategy) { nil } - - context "when given Git URL" do - let(:url) { "git://example.com/foo.git" } - - it { is_expected.to eq(GitDownloadStrategy) } - end - - context "when given a GitHub Git URL" do - let(:url) { "https://github.com/homebrew/brew.git" } - - it { is_expected.to eq(GitHubGitDownloadStrategy) } - end - - it "defaults to curl" do - expect(strategy_detector).to eq(CurlDownloadStrategy) - end - - it "raises an error when passed an unrecognized strategy" do - expect { - described_class.detect("foo", Class.new) - }.to raise_error(TypeError) - end - end -end diff --git a/Library/Homebrew/test/exceptions_spec.rb b/Library/Homebrew/test/exceptions_spec.rb index 87b3f44dd2..be1373e0fd 100644 --- a/Library/Homebrew/test/exceptions_spec.rb +++ b/Library/Homebrew/test/exceptions_spec.rb @@ -3,229 +3,231 @@ require "exceptions" -describe MultipleVersionsInstalledError do - subject { - described_class.new <<~EOS - foo has multiple installed versions - Run `brew uninstall --force foo` to remove all versions. - EOS - } - - its(:to_s) { - is_expected.to eq <<~EOS - foo has multiple installed versions - Run `brew uninstall --force foo` to remove all versions. - EOS - } -end - -describe NoSuchKegError do - subject { described_class.new("foo") } - - its(:to_s) { is_expected.to eq("No such keg: #{HOMEBREW_CELLAR}/foo") } -end - -describe FormulaValidationError do - subject(:error) { described_class.new("foo", "sha257", "magic") } - - its(:to_s) { - expect(error.to_s).to eq(%q(invalid attribute for formula 'foo': sha257 ("magic"))) - } -end - -describe FormulaUnavailableError do - subject(:error) { described_class.new("foo") } - - describe "#dependent_s" do - it "returns nil if there is no dependent" do - expect(error.dependent_s).to be nil - end - - it "returns nil if it depended on by itself" do - error.dependent = "foo" - expect(error.dependent_s).to be nil - end - - it "returns a string if there is a dependent" do - error.dependent = "foobar" - expect(error.dependent_s).to eq(" (dependency of foobar)") - end - end - - context "without a dependent" do - its(:to_s) { is_expected.to eq('No available formula with the name "foo".') } - end - - context "with a dependent" do - before do - error.dependent = "foobar" - end - - its(:to_s) { - expect(error.to_s).to eq('No available formula with the name "foo" (dependency of foobar).') +describe "Exception" do + describe MultipleVersionsInstalledError do + subject { + described_class.new <<~EOS + foo has multiple installed versions + Run `brew uninstall --force foo` to remove all versions. + EOS } - end -end - -describe TapFormulaUnavailableError do - subject { described_class.new(tap, "foo") } - - let(:tap) { double(Tap, user: "u", repo: "r", to_s: "u/r", installed?: false) } - - its(:to_s) { is_expected.to match(%r{Please tap it and then try again: brew tap u/r}) } -end - -describe FormulaClassUnavailableError do - subject(:error) { described_class.new("foo", "foo.rb", "Foo", list) } - - let(:mod) do - Module.new do - class Bar < Requirement; end - - class Baz < Formula; end - end - end - - context "no classes" do - let(:list) { [] } its(:to_s) { - expect(error.to_s).to match(/Expected to find class Foo, but found no classes\./) + is_expected.to eq <<~EOS + foo has multiple installed versions + Run `brew uninstall --force foo` to remove all versions. + EOS } end - context "class not derived from Formula" do - let(:list) { [mod.const_get(:Bar)] } + describe NoSuchKegError do + subject { described_class.new("foo") } + + its(:to_s) { is_expected.to eq("No such keg: #{HOMEBREW_CELLAR}/foo") } + end + + describe FormulaValidationError do + subject(:error) { described_class.new("foo", "sha257", "magic") } its(:to_s) { - expect(error.to_s).to match(/Expected to find class Foo, but only found: Bar \(not derived from Formula!\)\./) + expect(error.to_s).to eq(%q(invalid attribute for formula 'foo': sha257 ("magic"))) } end - context "class derived from Formula" do - let(:list) { [mod.const_get(:Baz)] } + describe FormulaUnavailableError do + subject(:error) { described_class.new("foo") } - its(:to_s) { is_expected.to match(/Expected to find class Foo, but only found: Baz\./) } - end -end + describe "#dependent_s" do + it "returns nil if there is no dependent" do + expect(error.dependent_s).to be nil + end -describe FormulaUnreadableError do - subject { described_class.new("foo", formula_error) } + it "returns nil if it depended on by itself" do + error.dependent = "foo" + expect(error.dependent_s).to be nil + end - let(:formula_error) { LoadError.new("bar") } + it "returns a string if there is a dependent" do + error.dependent = "foobar" + expect(error.dependent_s).to eq(" (dependency of foobar)") + end + end - its(:to_s) { is_expected.to eq("foo: bar") } -end + context "without a dependent" do + its(:to_s) { is_expected.to eq('No available formula with the name "foo".') } + end -describe TapUnavailableError do - subject { described_class.new("foo") } + context "with a dependent" do + before do + error.dependent = "foobar" + end - its(:to_s) { is_expected.to eq("No available tap foo.\n") } -end - -describe TapAlreadyTappedError do - subject { described_class.new("foo") } - - its(:to_s) { is_expected.to eq("Tap foo already tapped.\n") } -end - -describe BuildError do - subject { described_class.new(formula, "badprg", %w[arg1 arg2], {}) } - - let(:formula) { double(Formula, name: "foo") } - - its(:to_s) { is_expected.to eq("Failed executing: badprg arg1 arg2") } -end - -describe OperationInProgressError do - subject { described_class.new("foo") } - - its(:to_s) { is_expected.to match(/Operation already in progress for foo/) } -end - -describe FormulaInstallationAlreadyAttemptedError do - subject { described_class.new(formula) } - - let(:formula) { double(Formula, full_name: "foo/bar") } - - its(:to_s) { is_expected.to eq("Formula installation already attempted: foo/bar") } -end - -describe FormulaConflictError do - subject { described_class.new(formula, [conflict]) } - - let(:formula) { double(Formula, full_name: "foo/qux") } - let(:conflict) { double(name: "bar", reason: "I decided to") } - - its(:to_s) { is_expected.to match(/Please `brew unlink bar` before continuing\./) } -end - -describe CompilerSelectionError do - subject { described_class.new(formula) } - - let(:formula) { double(Formula, full_name: "foo") } - - its(:to_s) { is_expected.to match(/foo cannot be built with any available compilers\./) } -end - -describe CurlDownloadStrategyError do - context "file does not exist" do - subject { described_class.new("file:///tmp/foo") } - - its(:to_s) { is_expected.to eq("File does not exist: /tmp/foo") } + its(:to_s) { + expect(error.to_s).to eq('No available formula with the name "foo" (dependency of foobar).') + } + end end - context "download failed" do - subject { described_class.new("https://brew.sh") } + describe TapFormulaUnavailableError do + subject { described_class.new(tap, "foo") } - its(:to_s) { is_expected.to eq("Download failed: https://brew.sh") } + let(:tap) { double(Tap, user: "u", repo: "r", to_s: "u/r", installed?: false) } + + its(:to_s) { is_expected.to match(%r{Please tap it and then try again: brew tap u/r}) } + end + + describe FormulaClassUnavailableError do + subject(:error) { described_class.new("foo", "foo.rb", "Foo", list) } + + let(:mod) do + Module.new do + class Bar < Requirement; end + + class Baz < Formula; end + end + end + + context "no classes" do + let(:list) { [] } + + its(:to_s) { + expect(error.to_s).to match(/Expected to find class Foo, but found no classes\./) + } + end + + context "class not derived from Formula" do + let(:list) { [mod.const_get(:Bar)] } + + its(:to_s) { + expect(error.to_s).to match(/Expected to find class Foo, but only found: Bar \(not derived from Formula!\)\./) + } + end + + context "class derived from Formula" do + let(:list) { [mod.const_get(:Baz)] } + + its(:to_s) { is_expected.to match(/Expected to find class Foo, but only found: Baz\./) } + end + end + + describe FormulaUnreadableError do + subject { described_class.new("foo", formula_error) } + + let(:formula_error) { LoadError.new("bar") } + + its(:to_s) { is_expected.to eq("foo: bar") } + end + + describe TapUnavailableError do + subject { described_class.new("foo") } + + its(:to_s) { is_expected.to eq("No available tap foo.\n") } + end + + describe TapAlreadyTappedError do + subject { described_class.new("foo") } + + its(:to_s) { is_expected.to eq("Tap foo already tapped.\n") } + end + + describe BuildError do + subject { described_class.new(formula, "badprg", %w[arg1 arg2], {}) } + + let(:formula) { double(Formula, name: "foo") } + + its(:to_s) { is_expected.to eq("Failed executing: badprg arg1 arg2") } + end + + describe OperationInProgressError do + subject { described_class.new("foo") } + + its(:to_s) { is_expected.to match(/Operation already in progress for foo/) } + end + + describe FormulaInstallationAlreadyAttemptedError do + subject { described_class.new(formula) } + + let(:formula) { double(Formula, full_name: "foo/bar") } + + its(:to_s) { is_expected.to eq("Formula installation already attempted: foo/bar") } + end + + describe FormulaConflictError do + subject { described_class.new(formula, [conflict]) } + + let(:formula) { double(Formula, full_name: "foo/qux") } + let(:conflict) { double(name: "bar", reason: "I decided to") } + + its(:to_s) { is_expected.to match(/Please `brew unlink bar` before continuing\./) } + end + + describe CompilerSelectionError do + subject { described_class.new(formula) } + + let(:formula) { double(Formula, full_name: "foo") } + + its(:to_s) { is_expected.to match(/foo cannot be built with any available compilers\./) } + end + + describe CurlDownloadStrategyError do + context "file does not exist" do + subject { described_class.new("file:///tmp/foo") } + + its(:to_s) { is_expected.to eq("File does not exist: /tmp/foo") } + end + + context "download failed" do + subject { described_class.new("https://brew.sh") } + + its(:to_s) { is_expected.to eq("Download failed: https://brew.sh") } + end + end + + describe ErrorDuringExecution do + subject { described_class.new(["badprg", "arg1", "arg2"], status: status) } + + let(:status) { instance_double(Process::Status, exitstatus: 17) } + + its(:to_s) { is_expected.to eq("Failure while executing; `badprg arg1 arg2` exited with 17.") } + end + + describe ChecksumMismatchError do + subject { described_class.new("/file.tar.gz", hash1, hash2) } + + let(:hash1) { double(hash_type: "sha256", to_s: "deadbeef") } + let(:hash2) { double(hash_type: "sha256", to_s: "deadcafe") } + + its(:to_s) { is_expected.to match(/SHA256 mismatch/) } + end + + describe ResourceMissingError do + subject { described_class.new(formula, resource) } + + let(:formula) { double(Formula, full_name: "bar") } + let(:resource) { double(inspect: "") } + + its(:to_s) { is_expected.to eq("bar does not define resource ") } + end + + describe DuplicateResourceError do + subject { described_class.new(resource) } + + let(:resource) { double(inspect: "") } + + its(:to_s) { is_expected.to eq("Resource is defined more than once") } + end + + describe BottleFormulaUnavailableError do + subject { described_class.new("/foo.bottle.tar.gz", "foo/1.0/.brew/foo.rb") } + + let(:formula) { double(Formula, full_name: "foo") } + + its(:to_s) { is_expected.to match(/This bottle does not contain the formula file/) } + end + + describe BuildFlagsError do + subject { described_class.new(["-s"]) } + + its(:to_s) { is_expected.to match(/flag:\s+-s\nrequires building tools/) } end end - -describe ErrorDuringExecution do - subject { described_class.new(["badprg", "arg1", "arg2"], status: status) } - - let(:status) { instance_double(Process::Status, exitstatus: 17) } - - its(:to_s) { is_expected.to eq("Failure while executing; `badprg arg1 arg2` exited with 17.") } -end - -describe ChecksumMismatchError do - subject { described_class.new("/file.tar.gz", hash1, hash2) } - - let(:hash1) { double(hash_type: "sha256", to_s: "deadbeef") } - let(:hash2) { double(hash_type: "sha256", to_s: "deadcafe") } - - its(:to_s) { is_expected.to match(/SHA256 mismatch/) } -end - -describe ResourceMissingError do - subject { described_class.new(formula, resource) } - - let(:formula) { double(Formula, full_name: "bar") } - let(:resource) { double(inspect: "") } - - its(:to_s) { is_expected.to eq("bar does not define resource ") } -end - -describe DuplicateResourceError do - subject { described_class.new(resource) } - - let(:resource) { double(inspect: "") } - - its(:to_s) { is_expected.to eq("Resource is defined more than once") } -end - -describe BottleFormulaUnavailableError do - subject { described_class.new("/foo.bottle.tar.gz", "foo/1.0/.brew/foo.rb") } - - let(:formula) { double(Formula, full_name: "foo") } - - its(:to_s) { is_expected.to match(/This bottle does not contain the formula file/) } -end - -describe BuildFlagsError do - subject { described_class.new(["-s"]) } - - its(:to_s) { is_expected.to match(/flag:\s+-s\nrequires building tools/) } -end diff --git a/Library/Homebrew/test/formula_support_spec.rb b/Library/Homebrew/test/formula_support/bottle_disable_reason_spec.rb similarity index 58% rename from Library/Homebrew/test/formula_support_spec.rb rename to Library/Homebrew/test/formula_support/bottle_disable_reason_spec.rb index 6fcd00f439..fb9c0f0c8e 100644 --- a/Library/Homebrew/test/formula_support_spec.rb +++ b/Library/Homebrew/test/formula_support/bottle_disable_reason_spec.rb @@ -3,20 +3,6 @@ require "formula_support" -describe KegOnlyReason do - describe "#to_s" do - it "returns the reason provided" do - r = described_class.new :provided_by_macos, "test" - expect(r.to_s).to eq("test") - end - - it "returns a default message when no reason is provided" do - r = described_class.new :provided_by_macos, "" - expect(r.to_s).to match(/^macOS already provides/) - end - end -end - describe BottleDisableReason do specify ":unneeded" do bottle_disable_reason = described_class.new :unneeded, nil diff --git a/Library/Homebrew/test/formula_support/keg_only_reason_spec.rb b/Library/Homebrew/test/formula_support/keg_only_reason_spec.rb new file mode 100644 index 0000000000..1186e5851f --- /dev/null +++ b/Library/Homebrew/test/formula_support/keg_only_reason_spec.rb @@ -0,0 +1,18 @@ +# typed: false +# frozen_string_literal: true + +require "formula_support" + +describe KegOnlyReason do + describe "#to_s" do + it "returns the reason provided" do + r = described_class.new :provided_by_macos, "test" + expect(r.to_s).to eq("test") + end + + it "returns a default message when no reason is provided" do + r = described_class.new :provided_by_macos, "" + expect(r.to_s).to match(/^macOS already provides/) + end + end +end diff --git a/Library/Homebrew/test/language/python/shebang_spec.rb b/Library/Homebrew/test/language/python/shebang_spec.rb new file mode 100644 index 0000000000..b269129cc0 --- /dev/null +++ b/Library/Homebrew/test/language/python/shebang_spec.rb @@ -0,0 +1,47 @@ +# typed: false +# frozen_string_literal: true + +require "language/python" +require "utils/shebang" + +describe Language::Python::Shebang do + let(:file) { Tempfile.new("python-shebang") } + let(:python_f) do + formula "python" do + url "https://brew.sh/python-1.0.tgz" + end + end + let(:f) do + formula "foo" do + url "https://brew.sh/foo-1.0.tgz" + + depends_on "python" + end + end + + before do + file.write <<~EOS + #!/usr/bin/env python3 + a + b + c + EOS + file.flush + end + + after { file.unlink } + + describe "#detected_python_shebang" do + it "can be used to replace Python shebangs" do + expect(Formulary).to receive(:factory).with(python_f.name).and_return(python_f) + Utils::Shebang.rewrite_shebang described_class.detected_python_shebang(f), file + + expect(File.read(file)).to eq <<~EOS + #!#{HOMEBREW_PREFIX}/opt/python/bin/python3 + a + b + c + EOS + end + end +end diff --git a/Library/Homebrew/test/language/python/virtualenv_spec.rb b/Library/Homebrew/test/language/python/virtualenv_spec.rb new file mode 100644 index 0000000000..948363dda3 --- /dev/null +++ b/Library/Homebrew/test/language/python/virtualenv_spec.rb @@ -0,0 +1,98 @@ +# typed: false +# frozen_string_literal: true + +require "language/python" +require "resource" + +describe Language::Python::Virtualenv::Virtualenv, :needs_python do + subject(:virtualenv) { described_class.new(formula, dir, "python") } + + let(:dir) { mktmpdir } + + let(:resource) { double("resource", stage: true) } + let(:formula_bin) { dir/"formula_bin" } + let(:formula) { double("formula", resource: resource, bin: formula_bin) } + + describe "#create" do + it "creates a venv" do + expect(formula).to receive(:system).with("python", "-m", "venv", dir) + virtualenv.create + end + end + + describe "#pip_install" do + it "accepts a string" do + expect(formula).to receive(:system) + .with(dir/"bin/pip", "install", "-v", "--no-deps", + "--no-binary", ":all:", "--no-user", "--ignore-installed", "foo") + .and_return(true) + virtualenv.pip_install "foo" + end + + it "accepts a multi-line strings" do + expect(formula).to receive(:system) + .with(dir/"bin/pip", "install", "-v", "--no-deps", + "--no-binary", ":all:", "--no-user", "--ignore-installed", "foo", "bar") + .and_return(true) + + virtualenv.pip_install <<~EOS + foo + bar + EOS + end + + it "accepts an array" do + expect(formula).to receive(:system) + .with(dir/"bin/pip", "install", "-v", "--no-deps", + "--no-binary", ":all:", "--no-user", "--ignore-installed", "foo") + .and_return(true) + + expect(formula).to receive(:system) + .with(dir/"bin/pip", "install", "-v", "--no-deps", + "--no-binary", ":all:", "--no-user", "--ignore-installed", "bar") + .and_return(true) + + virtualenv.pip_install ["foo", "bar"] + end + + it "accepts a Resource" do + res = Resource.new("test") + + expect(res).to receive(:stage).and_yield + expect(formula).to receive(:system) + .with(dir/"bin/pip", "install", "-v", "--no-deps", + "--no-binary", ":all:", "--no-user", "--ignore-installed", Pathname.pwd) + .and_return(true) + + virtualenv.pip_install res + end + end + + describe "#pip_install_and_link" do + let(:src_bin) { dir/"bin" } + let(:dest_bin) { formula.bin } + + it "can link scripts" do + src_bin.mkpath + + expect(src_bin/"kilroy").not_to exist + expect(dest_bin/"kilroy").not_to exist + + FileUtils.touch src_bin/"irrelevant" + bin_before = Dir.glob(src_bin/"*") + FileUtils.touch src_bin/"kilroy" + bin_after = Dir.glob(src_bin/"*") + + expect(virtualenv).to receive(:pip_install).with("foo") + expect(Dir).to receive(:[]).with(src_bin/"*").twice.and_return(bin_before, bin_after) + + virtualenv.pip_install_and_link "foo" + + expect(src_bin/"kilroy").to exist + expect(dest_bin/"kilroy").to exist + expect(dest_bin/"kilroy").to be_a_symlink + expect((src_bin/"kilroy").realpath).to eq((dest_bin/"kilroy").realpath) + expect(dest_bin/"irrelevant").not_to exist + end + end +end diff --git a/Library/Homebrew/test/language/python_spec.rb b/Library/Homebrew/test/language/python_spec.rb index 8d5581d63a..6446b40d56 100644 --- a/Library/Homebrew/test/language/python_spec.rb +++ b/Library/Homebrew/test/language/python_spec.rb @@ -2,8 +2,6 @@ # frozen_string_literal: true require "language/python" -require "resource" -require "utils/shebang" describe Language::Python, :needs_python do describe "#major_minor_version" do @@ -33,138 +31,3 @@ describe Language::Python, :needs_python do end end end - -describe Language::Python::Shebang do - let(:file) { Tempfile.new("python-shebang") } - let(:python_f) do - formula "python" do - url "https://brew.sh/python-1.0.tgz" - end - end - let(:f) do - formula "foo" do - url "https://brew.sh/foo-1.0.tgz" - - depends_on "python" - end - end - - before do - file.write <<~EOS - #!/usr/bin/env python3 - a - b - c - EOS - file.flush - end - - after { file.unlink } - - describe "#detected_python_shebang" do - it "can be used to replace Python shebangs" do - expect(Formulary).to receive(:factory).with(python_f.name).and_return(python_f) - Utils::Shebang.rewrite_shebang described_class.detected_python_shebang(f), file - - expect(File.read(file)).to eq <<~EOS - #!#{HOMEBREW_PREFIX}/opt/python/bin/python3 - a - b - c - EOS - end - end -end - -describe Language::Python::Virtualenv::Virtualenv do - subject(:virtualenv) { described_class.new(formula, dir, "python") } - - let(:dir) { mktmpdir } - - let(:resource) { double("resource", stage: true) } - let(:formula_bin) { dir/"formula_bin" } - let(:formula) { double("formula", resource: resource, bin: formula_bin) } - - describe "#create" do - it "creates a venv" do - expect(formula).to receive(:system).with("python", "-m", "venv", dir) - virtualenv.create - end - end - - describe "#pip_install" do - it "accepts a string" do - expect(formula).to receive(:system) - .with(dir/"bin/pip", "install", "-v", "--no-deps", - "--no-binary", ":all:", "--no-user", "--ignore-installed", "foo") - .and_return(true) - virtualenv.pip_install "foo" - end - - it "accepts a multi-line strings" do - expect(formula).to receive(:system) - .with(dir/"bin/pip", "install", "-v", "--no-deps", - "--no-binary", ":all:", "--no-user", "--ignore-installed", "foo", "bar") - .and_return(true) - - virtualenv.pip_install <<~EOS - foo - bar - EOS - end - - it "accepts an array" do - expect(formula).to receive(:system) - .with(dir/"bin/pip", "install", "-v", "--no-deps", - "--no-binary", ":all:", "--no-user", "--ignore-installed", "foo") - .and_return(true) - - expect(formula).to receive(:system) - .with(dir/"bin/pip", "install", "-v", "--no-deps", - "--no-binary", ":all:", "--no-user", "--ignore-installed", "bar") - .and_return(true) - - virtualenv.pip_install ["foo", "bar"] - end - - it "accepts a Resource" do - res = Resource.new("test") - - expect(res).to receive(:stage).and_yield - expect(formula).to receive(:system) - .with(dir/"bin/pip", "install", "-v", "--no-deps", - "--no-binary", ":all:", "--no-user", "--ignore-installed", Pathname.pwd) - .and_return(true) - - virtualenv.pip_install res - end - end - - describe "#pip_install_and_link" do - let(:src_bin) { dir/"bin" } - let(:dest_bin) { formula.bin } - - it "can link scripts" do - src_bin.mkpath - - expect(src_bin/"kilroy").not_to exist - expect(dest_bin/"kilroy").not_to exist - - FileUtils.touch src_bin/"irrelevant" - bin_before = Dir.glob(src_bin/"*") - FileUtils.touch src_bin/"kilroy" - bin_after = Dir.glob(src_bin/"*") - - expect(virtualenv).to receive(:pip_install).with("foo") - expect(Dir).to receive(:[]).with(src_bin/"*").twice.and_return(bin_before, bin_after) - - virtualenv.pip_install_and_link "foo" - - expect(src_bin/"kilroy").to exist - expect(dest_bin/"kilroy").to exist - expect(dest_bin/"kilroy").to be_a_symlink - expect((src_bin/"kilroy").realpath).to eq((dest_bin/"kilroy").realpath) - expect(dest_bin/"irrelevant").not_to exist - end - end -end diff --git a/Library/Homebrew/test/options/deprecated_option_spec.rb b/Library/Homebrew/test/options/deprecated_option_spec.rb new file mode 100644 index 0000000000..78eaec475f --- /dev/null +++ b/Library/Homebrew/test/options/deprecated_option_spec.rb @@ -0,0 +1,33 @@ +# typed: false +# frozen_string_literal: true + +require "options" + +describe DeprecatedOption do + subject(:option) { described_class.new("foo", "bar") } + + specify "#old" do + expect(option.old).to eq("foo") + end + + specify "#old_flag" do + expect(option.old_flag).to eq("--foo") + end + + specify "#current" do + expect(option.current).to eq("bar") + end + + specify "#current_flag" do + expect(option.current_flag).to eq("--bar") + end + + specify "equality" do + foobar = described_class.new("foo", "bar") + boofar = described_class.new("boo", "far") + expect(foobar).to eq(option) + expect(option).to eq(foobar) + expect(boofar).not_to eq(option) + expect(option).not_to eq(boofar) + end +end diff --git a/Library/Homebrew/test/options/option_spec.rb b/Library/Homebrew/test/options/option_spec.rb new file mode 100644 index 0000000000..0300763188 --- /dev/null +++ b/Library/Homebrew/test/options/option_spec.rb @@ -0,0 +1,30 @@ +# typed: false +# frozen_string_literal: true + +require "options" + +describe Option do + subject(:option) { described_class.new("foo") } + + specify "#to_s" do + expect(option.to_s).to eq("--foo") + end + + specify "equality" do + foo = described_class.new("foo") + bar = described_class.new("bar") + expect(option).to eq(foo) + expect(option).not_to eq(bar) + expect(option).to eql(foo) + expect(option).not_to eql(bar) + end + + specify "#description" do + expect(option.description).to be_empty + expect(described_class.new("foo", "foo").description).to eq("foo") + end + + specify "#inspect" do + expect(option.inspect).to eq("#") + end +end diff --git a/Library/Homebrew/test/options_spec.rb b/Library/Homebrew/test/options_spec.rb index 52ed91a17e..62e2fb420f 100644 --- a/Library/Homebrew/test/options_spec.rb +++ b/Library/Homebrew/test/options_spec.rb @@ -3,61 +3,6 @@ require "options" -describe Option do - subject(:option) { described_class.new("foo") } - - specify "#to_s" do - expect(option.to_s).to eq("--foo") - end - - specify "equality" do - foo = described_class.new("foo") - bar = described_class.new("bar") - expect(option).to eq(foo) - expect(option).not_to eq(bar) - expect(option).to eql(foo) - expect(option).not_to eql(bar) - end - - specify "#description" do - expect(option.description).to be_empty - expect(described_class.new("foo", "foo").description).to eq("foo") - end - - specify "#inspect" do - expect(option.inspect).to eq("#") - end -end - -describe DeprecatedOption do - subject(:option) { described_class.new("foo", "bar") } - - specify "#old" do - expect(option.old).to eq("foo") - end - - specify "#old_flag" do - expect(option.old_flag).to eq("--foo") - end - - specify "#current" do - expect(option.current).to eq("bar") - end - - specify "#current_flag" do - expect(option.current_flag).to eq("--bar") - end - - specify "equality" do - foobar = described_class.new("foo", "bar") - boofar = described_class.new("boo", "far") - expect(foobar).to eq(option) - expect(option).to eq(foobar) - expect(boofar).not_to eq(option) - expect(option).not_to eq(boofar) - end -end - describe Options do subject(:options) { described_class.new } diff --git a/Library/Homebrew/test/os/mac/mach_spec.rb b/Library/Homebrew/test/os/mac/mach_spec.rb index 1bc6f59f97..f9e4102d96 100644 --- a/Library/Homebrew/test/os/mac/mach_spec.rb +++ b/Library/Homebrew/test/os/mac/mach_spec.rb @@ -1,177 +1,179 @@ # typed: false # frozen_string_literal: true -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) +describe "Mach-O" do + describe "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 - 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 + describe ArchitectureListExtension do + let(:archs) { [:i386, :x86_64, :ppc7400, :ppc64].extend(described_class) } + + specify "universal checks" do + expect(archs).to be_universal + + 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 + end + + specify "architecture flags" do + pn = dylib_path("fat") + expect(pn.archs).to be_universal + expect(pn.archs.as_arch_flags).to eq("-arch x86_64 -arch i386") + end 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 + describe "text executables" do + let(:pn) { HOMEBREW_PREFIX/"an_executable" } - 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 + after { pn.unlink } - 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 "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 "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 "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 "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 - - 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 - end - - specify "architecture flags" do - pn = dylib_path("fat") - expect(pn.archs).to be_universal - expect(pn.archs.as_arch_flags).to eq("-arch x86_64 -arch i386") - end -end - -describe "text executables" do - let(:pn) { HOMEBREW_PREFIX/"an_executable" } - - after { 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) + 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 end diff --git a/Library/Homebrew/test/patch_spec.rb b/Library/Homebrew/test/patch_spec.rb index cd03b0a100..2c4dbd2952 100644 --- a/Library/Homebrew/test/patch_spec.rb +++ b/Library/Homebrew/test/patch_spec.rb @@ -75,32 +75,32 @@ describe Patch do expect(patch.patch_files.count).to eq(7) end end -end -describe EmbeddedPatch do - describe "#new" do - subject { described_class.new(:p1) } + describe EmbeddedPatch do + describe "#new" do + subject { described_class.new(:p1) } - its(:inspect) { is_expected.to eq("#") } - end -end - -describe ExternalPatch do - subject(:patch) { described_class.new(:p1) { url "file:///my.patch" } } - - describe "#url" do - its(:url) { is_expected.to eq("file:///my.patch") } + its(:inspect) { is_expected.to eq("#") } + end end - describe "#inspect" do - its(:inspect) { is_expected.to eq('#') } - end + describe ExternalPatch do + subject(:patch) { described_class.new(:p1) { url "file:///my.patch" } } - describe "#cached_download" do - before do - allow(patch.resource).to receive(:cached_download).and_return("/tmp/foo.tar.gz") + describe "#url" do + its(:url) { is_expected.to eq("file:///my.patch") } end - its(:cached_download) { is_expected.to eq("/tmp/foo.tar.gz") } + describe "#inspect" do + its(:inspect) { is_expected.to eq('#') } + end + + describe "#cached_download" do + before do + allow(patch.resource).to receive(:cached_download).and_return("/tmp/foo.tar.gz") + end + + its(:cached_download) { is_expected.to eq("/tmp/foo.tar.gz") } + end end end diff --git a/Library/Homebrew/test/rubocops/checksum/checksum_case_spec.rb b/Library/Homebrew/test/rubocops/checksum/checksum_case_spec.rb new file mode 100644 index 0000000000..b66c2a163d --- /dev/null +++ b/Library/Homebrew/test/rubocops/checksum/checksum_case_spec.rb @@ -0,0 +1,70 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/checksum" + +describe RuboCop::Cop::FormulaAudit::ChecksumCase do + subject(:cop) { described_class.new } + + context "when auditing spec checksums" do + it "reports an offense if a checksum contains uppercase letters" do + expect_offense(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + stable do + url "https://github.com/foo-lang/foo-compiler/archive/0.18.0.tar.gz" + sha256 "5cf6e1ae0A645b426c0a7cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea0e9a" + ^ sha256 should be lowercase + + resource "foo-package" do + url "https://github.com/foo-lang/foo-package/archive/0.18.0.tar.gz" + sha256 "5cf6e1Ae0a645b426b047aa4cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea9" + ^ sha256 should be lowercase + end + end + end + RUBY + end + + it "reports and corrects an offense if a checksum outside a `stable` block contains uppercase letters" do + expect_offense(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + resource "foo-outside" do + url "https://github.com/foo-lang/foo-outside/archive/0.18.0.tar.gz" + sha256 "A4cc7cd3f7d1605ffa1ac5755cf6e1ae0a645b426b047a6a39a8b2268ddc7ea9" + ^ sha256 should be lowercase + end + stable do + url "https://github.com/foo-lang/foo-compiler/archive/0.18.0.tar.gz" + sha256 "5cf6e1ae0a645b426c0a7cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea0e9a" + + resource "foo-package" do + url "https://github.com/foo-lang/foo-package/archive/0.18.0.tar.gz" + sha256 "5cf6e1ae0a645b426b047aa4cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea9" + end + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + resource "foo-outside" do + url "https://github.com/foo-lang/foo-outside/archive/0.18.0.tar.gz" + sha256 "a4cc7cd3f7d1605ffa1ac5755cf6e1ae0a645b426b047a6a39a8b2268ddc7ea9" + end + stable do + url "https://github.com/foo-lang/foo-compiler/archive/0.18.0.tar.gz" + sha256 "5cf6e1ae0a645b426c0a7cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea0e9a" + + resource "foo-package" do + url "https://github.com/foo-lang/foo-package/archive/0.18.0.tar.gz" + sha256 "5cf6e1ae0a645b426b047aa4cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea9" + end + end + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/checksum_spec.rb b/Library/Homebrew/test/rubocops/checksum/checksum_spec.rb similarity index 56% rename from Library/Homebrew/test/rubocops/checksum_spec.rb rename to Library/Homebrew/test/rubocops/checksum/checksum_spec.rb index 7869f65b0f..ac8f05bc28 100644 --- a/Library/Homebrew/test/rubocops/checksum_spec.rb +++ b/Library/Homebrew/test/rubocops/checksum/checksum_spec.rb @@ -91,69 +91,3 @@ describe RuboCop::Cop::FormulaAudit::Checksum do end end end - -describe RuboCop::Cop::FormulaAudit::ChecksumCase do - subject(:cop) { described_class.new } - - context "when auditing spec checksums" do - it "reports an offense if a checksum contains uppercase letters" do - expect_offense(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - stable do - url "https://github.com/foo-lang/foo-compiler/archive/0.18.0.tar.gz" - sha256 "5cf6e1ae0A645b426c0a7cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea0e9a" - ^ sha256 should be lowercase - - resource "foo-package" do - url "https://github.com/foo-lang/foo-package/archive/0.18.0.tar.gz" - sha256 "5cf6e1Ae0a645b426b047aa4cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea9" - ^ sha256 should be lowercase - end - end - end - RUBY - end - - it "reports and corrects an offense if a checksum outside a `stable` block contains uppercase letters" do - expect_offense(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - resource "foo-outside" do - url "https://github.com/foo-lang/foo-outside/archive/0.18.0.tar.gz" - sha256 "A4cc7cd3f7d1605ffa1ac5755cf6e1ae0a645b426b047a6a39a8b2268ddc7ea9" - ^ sha256 should be lowercase - end - stable do - url "https://github.com/foo-lang/foo-compiler/archive/0.18.0.tar.gz" - sha256 "5cf6e1ae0a645b426c0a7cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea0e9a" - - resource "foo-package" do - url "https://github.com/foo-lang/foo-package/archive/0.18.0.tar.gz" - sha256 "5cf6e1ae0a645b426b047aa4cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea9" - end - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - resource "foo-outside" do - url "https://github.com/foo-lang/foo-outside/archive/0.18.0.tar.gz" - sha256 "a4cc7cd3f7d1605ffa1ac5755cf6e1ae0a645b426b047a6a39a8b2268ddc7ea9" - end - stable do - url "https://github.com/foo-lang/foo-compiler/archive/0.18.0.tar.gz" - sha256 "5cf6e1ae0a645b426c0a7cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea0e9a" - - resource "foo-package" do - url "https://github.com/foo-lang/foo-package/archive/0.18.0.tar.gz" - sha256 "5cf6e1ae0a645b426b047aa4cc7cd3f7d1605ffa1ac5756a39a8b2268ddc7ea9" - end - end - end - RUBY - end - end -end diff --git a/Library/Homebrew/test/rubocops/class/class_name_spec.rb b/Library/Homebrew/test/rubocops/class/class_name_spec.rb new file mode 100644 index 0000000000..edee9ce238 --- /dev/null +++ b/Library/Homebrew/test/rubocops/class/class_name_spec.rb @@ -0,0 +1,44 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/class" + +describe RuboCop::Cop::FormulaAudit::ClassName do + subject(:cop) { described_class.new } + + corrected_source = <<~RUBY + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + end + RUBY + + it "reports and corrects an offense when using ScriptFileFormula" do + expect_offense(<<~RUBY) + class Foo < ScriptFileFormula + ^^^^^^^^^^^^^^^^^ ScriptFileFormula is deprecated, use Formula instead + url 'https://brew.sh/foo-1.0.tgz' + end + RUBY + expect_correction(corrected_source) + end + + it "reports and corrects an offense when using GithubGistFormula" do + expect_offense(<<~RUBY) + class Foo < GithubGistFormula + ^^^^^^^^^^^^^^^^^ GithubGistFormula is deprecated, use Formula instead + url 'https://brew.sh/foo-1.0.tgz' + end + RUBY + expect_correction(corrected_source) + end + + it "reports and corrects an offense when using AmazonWebServicesFormula" do + expect_offense(<<~RUBY) + class Foo < AmazonWebServicesFormula + ^^^^^^^^^^^^^^^^^^^^^^^^ AmazonWebServicesFormula is deprecated, use Formula instead + url 'https://brew.sh/foo-1.0.tgz' + end + RUBY + expect_correction(corrected_source) + end +end diff --git a/Library/Homebrew/test/rubocops/class/test_present.rb b/Library/Homebrew/test/rubocops/class/test_present.rb new file mode 100644 index 0000000000..2728fcc074 --- /dev/null +++ b/Library/Homebrew/test/rubocops/class/test_present.rb @@ -0,0 +1,17 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/class" + +describe RuboCop::Cop::FormulaAuditStrict::TestPresent do + subject(:cop) { described_class.new } + + it "reports an offense when there is no test block" do + expect_offense(<<~RUBY) + class Foo < Formula + ^^^^^^^^^^^^^^^^^^^ A `test do` test block should be added + url 'https://brew.sh/foo-1.0.tgz' + end + RUBY + end +end diff --git a/Library/Homebrew/test/rubocops/class_spec.rb b/Library/Homebrew/test/rubocops/class/test_spec.rb similarity index 53% rename from Library/Homebrew/test/rubocops/class_spec.rb rename to Library/Homebrew/test/rubocops/class/test_spec.rb index 15c793b229..b9b03161b8 100644 --- a/Library/Homebrew/test/rubocops/class_spec.rb +++ b/Library/Homebrew/test/rubocops/class/test_spec.rb @@ -3,46 +3,6 @@ require "rubocops/class" -describe RuboCop::Cop::FormulaAudit::ClassName do - subject(:cop) { described_class.new } - - corrected_source = <<~RUBY - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - end - RUBY - - it "reports and corrects an offense when using ScriptFileFormula" do - expect_offense(<<~RUBY) - class Foo < ScriptFileFormula - ^^^^^^^^^^^^^^^^^ ScriptFileFormula is deprecated, use Formula instead - url 'https://brew.sh/foo-1.0.tgz' - end - RUBY - expect_correction(corrected_source) - end - - it "reports and corrects an offense when using GithubGistFormula" do - expect_offense(<<~RUBY) - class Foo < GithubGistFormula - ^^^^^^^^^^^^^^^^^ GithubGistFormula is deprecated, use Formula instead - url 'https://brew.sh/foo-1.0.tgz' - end - RUBY - expect_correction(corrected_source) - end - - it "reports and corrects an offense when using AmazonWebServicesFormula" do - expect_offense(<<~RUBY) - class Foo < AmazonWebServicesFormula - ^^^^^^^^^^^^^^^^^^^^^^^^ AmazonWebServicesFormula is deprecated, use Formula instead - url 'https://brew.sh/foo-1.0.tgz' - end - RUBY - expect_correction(corrected_source) - end -end - describe RuboCop::Cop::FormulaAudit::Test do subject(:cop) { described_class.new } @@ -117,16 +77,3 @@ describe RuboCop::Cop::FormulaAudit::Test do RUBY end end - -describe RuboCop::Cop::FormulaAuditStrict::TestPresent do - subject(:cop) { described_class.new } - - it "reports an offense when there is no test block" do - expect_offense(<<~RUBY) - class Foo < Formula - ^^^^^^^^^^^^^^^^^^^ A `test do` test block should be added - url 'https://brew.sh/foo-1.0.tgz' - end - RUBY - end -end diff --git a/Library/Homebrew/test/rubocops/deprecate_disable/date_spec.rb b/Library/Homebrew/test/rubocops/deprecate_disable/date_spec.rb new file mode 100644 index 0000000000..e46e5ab8d2 --- /dev/null +++ b/Library/Homebrew/test/rubocops/deprecate_disable/date_spec.rb @@ -0,0 +1,152 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/deprecate_disable" + +describe RuboCop::Cop::FormulaAudit::DeprecateDisableDate do + subject(:cop) { described_class.new } + + context "when auditing `deprecate!`" do + it "reports and corrects an offense if `date` is not ISO 8601 compliant" do + expect_offense(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + deprecate! date: "June 25, 2020" + ^^^^^^^^^^^^^^^ Use `2020-06-25` to comply with ISO 8601 + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + deprecate! date: "2020-06-25" + end + RUBY + end + + it "reports and corrects an offense if `date` is not ISO 8601 compliant (with `reason`)" do + expect_offense(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + deprecate! because: "is broken", date: "June 25, 2020" + ^^^^^^^^^^^^^^^ Use `2020-06-25` to comply with ISO 8601 + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + deprecate! because: "is broken", date: "2020-06-25" + end + RUBY + end + + it "reports no offenses if `date` is ISO 8601 compliant" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + deprecate! date: "2020-06-25" + end + RUBY + end + + it "reports no offenses if `date` is ISO 8601 compliant (with `reason`)" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + deprecate! because: "is broken", date: "2020-06-25" + end + RUBY + end + + it "reports no offenses if no `date` is specified" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + deprecate! + end + RUBY + end + + it "reports no offenses if no `date` is specified (with `reason`)" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + deprecate! because: "is broken" + end + RUBY + end + end + + context "when auditing `disable!`" do + it "reports and corrects an offense if `date` is not ISO 8601 compliant" do + expect_offense(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + disable! date: "June 25, 2020" + ^^^^^^^^^^^^^^^ Use `2020-06-25` to comply with ISO 8601 + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + disable! date: "2020-06-25" + end + RUBY + end + + it "reports and corrects an offense if `date` is not ISO 8601 compliant (with `reason`)" do + expect_offense(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + disable! because: "is broken", date: "June 25, 2020" + ^^^^^^^^^^^^^^^ Use `2020-06-25` to comply with ISO 8601 + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + disable! because: "is broken", date: "2020-06-25" + end + RUBY + end + + it "reports no offenses if `date` is ISO 8601 compliant" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + disable! date: "2020-06-25" + end + RUBY + end + + it "reports no offenses if `date` is ISO 8601 compliant (with `reason`)" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + disable! because: "is broken", date: "2020-06-25" + end + RUBY + end + + it "reports no offenses if no `date` is specified" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + disable! + end + RUBY + end + + it "reports no offenses if no `date` is specified (with `reason`)" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + url 'https://brew.sh/foo-1.0.tgz' + disable! because: "is broken" + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/deprecate_disable_spec.rb b/Library/Homebrew/test/rubocops/deprecate_disable/reason_spec.rb similarity index 69% rename from Library/Homebrew/test/rubocops/deprecate_disable_spec.rb rename to Library/Homebrew/test/rubocops/deprecate_disable/reason_spec.rb index 59c40133d7..0ae834561b 100644 --- a/Library/Homebrew/test/rubocops/deprecate_disable_spec.rb +++ b/Library/Homebrew/test/rubocops/deprecate_disable/reason_spec.rb @@ -3,154 +3,6 @@ require "rubocops/deprecate_disable" -describe RuboCop::Cop::FormulaAudit::DeprecateDisableDate do - subject(:cop) { described_class.new } - - context "when auditing `deprecate!`" do - it "reports and corrects an offense if `date` is not ISO 8601 compliant" do - expect_offense(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - deprecate! date: "June 25, 2020" - ^^^^^^^^^^^^^^^ Use `2020-06-25` to comply with ISO 8601 - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - deprecate! date: "2020-06-25" - end - RUBY - end - - it "reports and corrects an offense if `date` is not ISO 8601 compliant (with `reason`)" do - expect_offense(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - deprecate! because: "is broken", date: "June 25, 2020" - ^^^^^^^^^^^^^^^ Use `2020-06-25` to comply with ISO 8601 - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - deprecate! because: "is broken", date: "2020-06-25" - end - RUBY - end - - it "reports no offenses if `date` is ISO 8601 compliant" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - deprecate! date: "2020-06-25" - end - RUBY - end - - it "reports no offenses if `date` is ISO 8601 compliant (with `reason`)" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - deprecate! because: "is broken", date: "2020-06-25" - end - RUBY - end - - it "reports no offenses if no `date` is specified" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - deprecate! - end - RUBY - end - - it "reports no offenses if no `date` is specified (with `reason`)" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - deprecate! because: "is broken" - end - RUBY - end - end - - context "when auditing `disable!`" do - it "reports and corrects an offense if `date` is not ISO 8601 compliant" do - expect_offense(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - disable! date: "June 25, 2020" - ^^^^^^^^^^^^^^^ Use `2020-06-25` to comply with ISO 8601 - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - disable! date: "2020-06-25" - end - RUBY - end - - it "reports and corrects an offense if `date` is not ISO 8601 compliant (with `reason`)" do - expect_offense(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - disable! because: "is broken", date: "June 25, 2020" - ^^^^^^^^^^^^^^^ Use `2020-06-25` to comply with ISO 8601 - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - disable! because: "is broken", date: "2020-06-25" - end - RUBY - end - - it "reports no offenses if `date` is ISO 8601 compliant" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - disable! date: "2020-06-25" - end - RUBY - end - - it "reports no offenses if `date` is ISO 8601 compliant (with `reason`)" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - disable! because: "is broken", date: "2020-06-25" - end - RUBY - end - - it "reports no offenses if no `date` is specified" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - disable! - end - RUBY - end - - it "reports no offenses if no `date` is specified (with `reason`)" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - url 'https://brew.sh/foo-1.0.tgz' - disable! because: "is broken" - end - RUBY - end - end -end - describe RuboCop::Cop::FormulaAudit::DeprecateDisableReason do subject(:cop) { described_class.new } diff --git a/Library/Homebrew/test/rubocops/lines/class_inheritance_spec.rb b/Library/Homebrew/test/rubocops/lines/class_inheritance_spec.rb new file mode 100644 index 0000000000..72e7153347 --- /dev/null +++ b/Library/Homebrew/test/rubocops/lines/class_inheritance_spec.rb @@ -0,0 +1,20 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/lines" + +describe RuboCop::Cop::FormulaAudit::ClassInheritance do + subject(:cop) { described_class.new } + + context "when auditing formula class inheritance" do + it "reports an offense when not using spaces for class inheritance" do + expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo, :exist?` instead of `assert File.exist? "default.ini"` - end - RUBY - end - - it "reports an offense when assert ... exist? is used with a negation" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - assert !File.exist?("default.ini") - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `refute_predicate , :exist?` instead of `assert !File.exist?("default.ini")` - end - RUBY - end - - it "reports an offense when assert ... executable? is used without a negation" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - assert File.executable? f - ^^^^^^^^^^^^^^^^^^ Use `assert_predicate , :executable?` instead of `assert File.executable? f` - end - RUBY - end - end -end - -describe RuboCop::Cop::FormulaAudit::OptionDeclarations do - subject(:cop) { described_class.new } - - context "when auditing options" do - it "reports an offense when `build.without?` is used in homebrew/core" do - expect_offense(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def install - build.without? "bar" - ^^^^^^^^^^^^^^^^^^^^ Formulae in homebrew/core should not use `build.without?`. - end - end - RUBY - end - - it "reports an offense when `build.with?` is used in homebrew/core" do - expect_offense(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def install - build.with? "bar" - ^^^^^^^^^^^^^^^^^ Formulae in homebrew/core should not use `build.with?`. - end - end - RUBY - end - - it "reports an offense when `build.without?` is used for a conditional dependency" do - expect_offense(<<~RUBY) - class Foo < Formula - depends_on "bar" if build.without?("baz") - ^^^^^^^^^^^^^^^^^^^^^ Use `:optional` or `:recommended` instead of `if build.without?("baz")` - end - RUBY - end - - it "reports an offense when `build.without?` is used for a conditional dependency" do - expect_offense(<<~RUBY) - class Foo < Formula - depends_on "bar" if build.with?("baz") - ^^^^^^^^^^^^^^^^^^ Use `:optional` or `:recommended` instead of `if build.with?("baz")` - end - RUBY - end - - it "reports an offense when `build.without?` is used with `unless`" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def post_install - return unless build.without? "bar" - ^^^^^^^^^^^^^^^^^^^^ Use if build.with? "bar" instead of unless build.without? "bar" - end - end - RUBY - end - - it "reports an offense when `build.with?` is used with `unless`" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def post_install - return unless build.with? "bar" - ^^^^^^^^^^^^^^^^^ Use if build.without? "bar" instead of unless build.with? "bar" - end - end - RUBY - end - - it "reports an offense when `build.with?` is negated" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def post_install - return if !build.with? "bar" - ^^^^^^^^^^^^^^^^^^ Don't negate 'build.with?': use 'build.without?' - end - end - RUBY - end - - it "reports an offense when `build.without?` is negated" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def post_install - return if !build.without? "bar" - ^^^^^^^^^^^^^^^^^^^^^ Don't negate 'build.without?': use 'build.with?' - end - end - RUBY - end - - it "reports an offense when a `build.without?` conditional is unnecessary" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def post_install - return if build.without? "--without-bar" - ^^^^^^^^^^^^^^^ Don't duplicate 'without': Use `build.without? \"bar\"` to check for \"--without-bar\" - end - end - RUBY - end - - it "reports an offense when a `build.with?` conditional is unnecessary" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def post_install - return if build.with? "--with-bar" - ^^^^^^^^^^^^ Don't duplicate 'with': Use `build.with? \"bar\"` to check for \"--with-bar\" - end - end - RUBY - end - - it "reports an offense when `build.include?` is used" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def post_install - return if build.include? "foo" - ^^^^^^^^^^^^^^^^^^^^ `build.include?` is deprecated - end - end - RUBY - end - - it "reports an offense when `def option` is used" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - - def options - ^^^^^^^^^^^ Use new-style option definitions - [["--bar", "desc"]] - end - end - RUBY - end - end -end - -describe RuboCop::Cop::FormulaAudit::MpiCheck do - subject(:cop) { described_class.new } - - context "when auditing MPI dependencies" do - it "reports and corrects an offense when using depends_on \"mpich\" in homebrew/core" do - expect_offense(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - depends_on "mpich" - ^^^^^^^^^^^^^^^^^^ Formulae in homebrew/core should use 'depends_on "open-mpi"' instead of 'depends_on "mpich"'. - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - depends_on "open-mpi" - end - RUBY - end - end -end - -describe RuboCop::Cop::FormulaAudit::SafePopenCommands do - subject(:cop) { described_class.new } - - context "when auditing popen commands" do - it "reports and corrects `Utils.popen_read` usage" do - expect_offense(<<~RUBY) - class Foo < Formula - def install - Utils.popen_read "foo" - ^^^^^^^^^^^^^^^^^^^^^^ Use `Utils.safe_popen_read` instead of `Utils.popen_read` - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - def install - Utils.safe_popen_read "foo" - end - end - RUBY - end - - it "reports and corrects `Utils.popen_write` usage" do - expect_offense(<<~RUBY) - class Foo < Formula - def install - Utils.popen_write "foo" - ^^^^^^^^^^^^^^^^^^^^^^^ Use `Utils.safe_popen_write` instead of `Utils.popen_write` - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - def install - Utils.safe_popen_write "foo" - end - end - RUBY - end - - it "does not report an offense when `Utils.popen_read` is used in a test block" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - def install; end - test do - Utils.popen_read "foo" - end - end - RUBY - end - end -end - -describe RuboCop::Cop::FormulaAudit::ShellVariables do - subject(:cop) { described_class.new } - - context "when auditing shell variables" do - it "reports and corrects unexpanded shell variables in `Utils.popen`" do - expect_offense(<<~RUBY) - class Foo < Formula - def install - Utils.popen "SHELL=bash foo" - ^^^^^^^^^^^^^^^^ Use `Utils.popen({ "SHELL" => "bash" }, "foo")` instead of `Utils.popen "SHELL=bash foo"` - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - def install - Utils.popen { "SHELL" => "bash" }, "foo" - end - end - RUBY - end - - it "reports and corrects unexpanded shell variables in `Utils.safe_popen_read`" do - expect_offense(<<~RUBY) - class Foo < Formula - def install - Utils.safe_popen_read "SHELL=bash foo" - ^^^^^^^^^^^^^^^^ Use `Utils.safe_popen_read({ "SHELL" => "bash" }, "foo")` instead of `Utils.safe_popen_read "SHELL=bash foo"` - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - def install - Utils.safe_popen_read { "SHELL" => "bash" }, "foo" - end - end - RUBY - end - - it "reports and corrects unexpanded shell variables in `Utils.safe_popen_write`" do - expect_offense(<<~RUBY) - class Foo < Formula - def install - Utils.safe_popen_write "SHELL=bash foo" - ^^^^^^^^^^^^^^^^ Use `Utils.safe_popen_write({ "SHELL" => "bash" }, "foo")` instead of `Utils.safe_popen_write "SHELL=bash foo"` - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - def install - Utils.safe_popen_write { "SHELL" => "bash" }, "foo" - end - end - RUBY - end - - it "reports and corrects unexpanded shell variables while preserving string interpolation" do - expect_offense(<<~RUBY) - class Foo < Formula - def install - Utils.popen "SHELL=bash \#{bin}/foo" - ^^^^^^^^^^^^^^^^^^^^^^^ Use `Utils.popen({ "SHELL" => "bash" }, "\#{bin}/foo")` instead of `Utils.popen "SHELL=bash \#{bin}/foo"` - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - def install - Utils.popen { "SHELL" => "bash" }, "\#{bin}/foo" - end - end - RUBY - end - end -end - -describe RuboCop::Cop::FormulaAudit::LicenseArrays do - subject(:cop) { described_class.new } - - context "when auditing license arrays" do - it "reports no offenses for license strings" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - license "MIT" - end - RUBY - end - - it "reports no offenses for license symbols" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - license :public_domain - end - RUBY - end - - it "reports no offenses for license hashes" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - license any_of: ["MIT", "0BSD"] - end - RUBY - end - - it "reports and corrects use of a license array" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - license ["MIT", "0BSD"] - ^^^^^^^^^^^^^^^^^^^^^^^ Use `license any_of: ["MIT", "0BSD"]` instead of `license ["MIT", "0BSD"]` - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - license any_of: ["MIT", "0BSD"] - end - RUBY - end - end -end - -describe RuboCop::Cop::FormulaAudit::Licenses do - subject(:cop) { described_class.new } - - context "when auditing licenses" do - it "reports no offenses for license strings" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - license "MIT" - end - RUBY - end - - it "reports no offenses for license symbols" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - license :public_domain - end - RUBY - end - - it "reports no offenses for license hashes" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - license any_of: ["MIT", "0BSD"] - end - RUBY - end - - it "reports no offenses for license exceptions" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - license "MIT" => { with: "LLVM-exception" } - end - RUBY - end - - it "reports no offenses for multiline nested license hashes" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - license any_of: [ - "MIT", - all_of: ["0BSD", "Zlib"], - ] - end - RUBY - end - - it "reports no offenses for multiline nested license hashes with exceptions" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - license any_of: [ - "MIT", - all_of: ["0BSD", "Zlib"], - "GPL-2.0-only" => { with: "LLVM-exception" }, - ] - end - RUBY - end - - it "reports an offense for nested license hashes on a single line" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - license any_of: ["MIT", all_of: ["0BSD", "Zlib"]] - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Split nested license declarations onto multiple lines - end - RUBY - end - end -end - -describe RuboCop::Cop::FormulaAudit::PythonVersions do - subject(:cop) { described_class.new } - - context "when auditing Python versions" do - it "reports no offenses for Python with no dependency" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - def install - puts "python@3.8" - end - end - RUBY - end - - it "reports no offenses for unversioned Python references" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - depends_on "python@3.9" - - def install - puts "python" - end - end - RUBY - end - - it "reports no offenses for Python with no version" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - depends_on "python@3.9" - - def install - puts "python3" - end - end - RUBY - end - - it "reports no offenses when a Python reference matches its dependency" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - depends_on "python@3.9" - - def install - puts "python@3.9" - end - end - RUBY - end - - it "reports no offenses when a Python reference matches its dependency without `@`" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - depends_on "python@3.9" - - def install - puts "python3.9" - end - end - RUBY - end - - it "reports no offenses when a Python reference matches its two-digit dependency" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - depends_on "python@3.10" - - def install - puts "python@3.10" - end - end - RUBY - end - - it "reports no offenses when a Python reference matches its two-digit dependency without `@`" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - depends_on "python@3.10" - - def install - puts "python3.10" - end - end - RUBY - end - - it "reports and corrects Python references with mismatched versions" do - expect_offense(<<~RUBY) - class Foo < Formula - depends_on "python@3.9" - - def install - puts "python@3.8" - ^^^^^^^^^^^^ References to `python@3.8` should match the specified python dependency (`python@3.9`) - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - depends_on "python@3.9" - - def install - puts "python@3.9" - end - end - RUBY - end - - it "reports and corrects Python references with mismatched versions without `@`" do - expect_offense(<<~RUBY) - class Foo < Formula - depends_on "python@3.9" - - def install - puts "python3.8" - ^^^^^^^^^^^ References to `python3.8` should match the specified python dependency (`python3.9`) - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - depends_on "python@3.9" - - def install - puts "python3.9" - end - end - RUBY - end - - it "reports and corrects Python references with mismatched two-digit versions" do - expect_offense(<<~RUBY) - class Foo < Formula - depends_on "python@3.11" - - def install - puts "python@3.10" - ^^^^^^^^^^^^^ References to `python@3.10` should match the specified python dependency (`python@3.11`) - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - depends_on "python@3.11" - - def install - puts "python@3.11" - end - end - RUBY - end - - it "reports and corrects Python references with mismatched two-digit versions without `@`" do - expect_offense(<<~RUBY) - class Foo < Formula - depends_on "python@3.11" - - def install - puts "python3.10" - ^^^^^^^^^^^^ References to `python3.10` should match the specified python dependency (`python3.11`) - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - depends_on "python@3.11" - - def install - puts "python3.11" - end - end - RUBY - end - end -end - -describe RuboCop::Cop::FormulaAudit::Miscellaneous do - subject(:cop) { described_class.new } - - context "when auditing formula miscellany" do - it "reports an offense for unneeded `FileUtils` usage" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - FileUtils.mv "hello" - ^^^^^^^^^^^^^^^^^^^^ Don\'t need \'FileUtils.\' before mv - end - RUBY - end - - it "reports an offense for long `inreplace` block variable names" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - inreplace "foo" do |longvar| - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ \"inreplace do |s|\" is preferred over \"|longvar|\". - somerandomCall(longvar) - end - end - RUBY - end - - it "reports an offense for invalid `rebuild` numbers" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - bottle do - rebuild 0 - ^^^^^^^^^ 'rebuild 0' should be removed - sha256 "fe0679b932dd43a87fd415b609a7fbac7a069d117642ae8ebaac46ae1fb9f0b3" => :sierra - end - end - RUBY - end - - it "reports an offense when `OS.linux?` is used in homebrew/core" do - expect_offense(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - bottle do - if OS.linux? - ^^^^^^^^^ Don\'t use OS.linux?; homebrew/core only supports macOS - nil - end - sha256 "fe0679b932dd43a87fd415b609a7fbac7a069d117642ae8ebaac46ae1fb9f0b3" => :sierra - end - end - RUBY - end - - it "reports an offense when a useless `fails_with :llvm` is used" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - bottle do - sha256 "fe0679b932dd43a87fd415b609a7fbac7a069d117642ae8ebaac46ae1fb9f0b3" => :sierra - end - fails_with :llvm do - ^^^^^^^^^^^^^^^^ 'fails_with :llvm' is now a no-op so should be removed - build 2335 - cause "foo" - end - end - RUBY - end - - it "reports an offense when `def test` is used" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - - def test - ^^^^^^^^ Use new-style test definitions (test do) - assert_equals "1", "1" - end - end - RUBY - end - - it "reports an offense when `skip_clean` is used" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - skip_clean :all - ^^^^^^^^^^^^^^^ `skip_clean :all` is deprecated; brew no longer strips symbols. Pass explicit paths to prevent Homebrew from removing empty folders. - end - RUBY - end - - it "reports an offense when `build.universal?` is used" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - if build.universal? - ^^^^^^^^^^^^^^^^ macOS has been 64-bit only since 10.6 so build.universal? is deprecated. - "foo" - end - end - RUBY - end - - it "reports no offenses when `build.universal?` is used in an exempt formula" do - expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/wine.rb") - class Wine < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - if build.universal? - "foo" - end - end - RUBY - end - - it "reports an offense when `ENV.universal_binary` is used" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - if build? - ENV.universal_binary - ^^^^^^^^^^^^^^^^^^^^ macOS has been 64-bit only since 10.6 so ENV.universal_binary is deprecated. - end - end - RUBY - end - - it "reports no offenses when `ENV.universal_binary` is used in an exempt formula" do - expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/wine.rb") - class Wine < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - if build? - ENV.universal_binary - end - end - RUBY - end - - it "reports an offense when `install_name_tool` is called" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - system "install_name_tool", "-id" - ^^^^^^^^^^^^^^^^^^^ Use ruby-macho instead of calling "install_name_tool" - end - RUBY - end - - it "reports an offense when `npm install` is called without Language::Node arguments" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - system "npm", "install" - ^^^^^^^^^^^^^^^^^^^^^^^ Use Language::Node for npm install args - end - RUBY - end - - it "reports no offenses when `npm install` is called without Language::Node arguments in an exempt formula" do - expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/kibana@4.4.rb") - class KibanaAT44 < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - system "npm", "install" - end - RUBY - end - - it "reports an offense when `depends_on` is called with an instance" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - depends_on FOO::BAR.new - ^^^^^^^^^^^^ `depends_on` can take requirement classes instead of instances - end - RUBY - end - - it "reports an offense when `Dir` is called without a globbing argument" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - rm_rf Dir["src/{llvm,test,librustdoc,etc/snapshot.pyc}"] - rm_rf Dir["src/snapshot.pyc"] - ^^^^^^^^^^^^^^^^^^ Dir(["src/snapshot.pyc"]) is unnecessary; just use "src/snapshot.pyc" - end - RUBY - end - - it "reports an offense when executing a system command for which there is a Ruby FileUtils equivalent" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - system "mkdir", "foo" - ^^^^^^^ Use the `mkdir` Ruby method instead of `system "mkdir", "foo"` - end - RUBY - end - - it "reports an offense when top-level functions are defined outside of a class body" do - expect_offense(<<~RUBY) - def test - ^^^^^^^^ Define method test in the class body, not at the top-level - nil - end - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - end - RUBY - end - - it 'reports an offense when `man+"man8"` is used' do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def install - man1.install man+"man8" => "faad.1" - ^^^^^^ "man+"man8"" should be "man8" - end - end - RUBY - end - - it "reports an offense when a hard-coded `gcc` is referenced" do - expect_offense(<<~'RUBY') - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def install - system "/usr/bin/gcc", "foo" - ^^^^^^^^^^^^^^ Use "#{ENV.cc}" instead of hard-coding "gcc" - end - end - RUBY - end - - it "reports an offense when a hard-coded `g++` is referenced" do - expect_offense(<<~'RUBY') - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def install - system "/usr/bin/g++", "-o", "foo", "foo.cc" - ^^^^^^^^^^^^^^ Use "#{ENV.cxx}" instead of hard-coding "g++" - end - end - RUBY - end - - it "reports an offense when a hard-coded `llvm-g++` is set as COMPILER_PATH" do - expect_offense(<<~'RUBY') - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def install - ENV["COMPILER_PATH"] = "/usr/bin/llvm-g++" - ^^^^^^^^^^^^^^^^^^^ Use "#{ENV.cxx}" instead of hard-coding "llvm-g++" - end - end - RUBY - end - - it "reports an offense when a hard-coded `gcc` is set as COMPILER_PATH" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def install - ENV["COMPILER_PATH"] = "/usr/bin/gcc" - ^^^^^^^^^^^^^^ Use \"\#{ENV.cc}\" instead of hard-coding \"gcc\" - end - end - RUBY - end - - it "reports an offense when the formula path shortcut `man` could be used" do - expect_offense(<<~'RUBY') - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def install - mv "#{share}/man", share - ^^^^ "#{share}/man" should be "#{man}" - end - end - RUBY - end - - it "reports an offense when the formula path shortcut `libexec` could be used" do - expect_offense(<<~'RUBY') - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def install - mv "#{prefix}/libexec", share - ^^^^^^^^ "#{prefix}/libexec" should be "#{libexec}" - end - end - RUBY - end - - it "reports an offense when the formula path shortcut `info` could be used" do - expect_offense(<<~'RUBY') - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def install - system "./configure", "--INFODIR=#{prefix}/share/info" - ^^^^^^^^^^^ "#{prefix}/share/info" should be "#{info}" - end - end - RUBY - end - - it "reports an offense when the formula path shortcut `man8` could be used" do - expect_offense(<<~'RUBY') - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - def install - system "./configure", "--MANDIR=#{prefix}/share/man/man8" - ^^^^^^^^^^^^^^^ "#{prefix}/share/man/man8" should be "#{man8}" - end - end - RUBY - end - - it "reports an offense when unvendored lua modules are used" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - depends_on "lpeg" => :lua51 - ^^^^^^ lua modules should be vendored rather than use deprecated `depends_on \"lpeg\" => :lua51` - end - RUBY - end - - it "reports an offense when `export` is used to set environment variables" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - system "export", "var=value" - ^^^^^^^^ Use ENV instead of invoking 'export' to modify the environment - end - RUBY - end - - it "reports an offense when dependencies with invalid options are used" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - depends_on "foo" => "with-bar" - ^^^^^^^^^^ Dependency foo should not use option with-bar - end - RUBY - end - - it "reports an offense when dependencies with invalid options are used in an array" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - depends_on "httpd" => [:build, :test] - depends_on "foo" => [:optional, "with-bar"] - ^^^^^^^^^^ Dependency foo should not use option with-bar - depends_on "icu4c" => [:optional, "c++11"] - ^^^^^^^ Dependency icu4c should not use option c++11 - end - RUBY - end - - it "reports an offense when `build.head?` could be used instead of checking `version`" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - if version == "HEAD" - ^^^^^^^^^^^^^^^^^ Use 'build.head?' instead of inspecting 'version' - foo() - end - end - RUBY - end - - it "reports an offense when `ARGV.include? (--HEAD)` is used" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - test do - head = ARGV.include? "--HEAD" - ^^^^ Use build instead of ARGV to check options - ^^^^^^^^^^^^^^^^^^^^^^ Use "if build.head?" instead - end - end - RUBY - end - - it "reports an offense when `needs :openmp` is used" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - needs :openmp - ^^^^^^^^^^^^^ 'needs :openmp' should be replaced with 'depends_on \"gcc\"' - end - RUBY - end - - it "reports an offense when `MACOS_VERSION` is used" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - test do - version = MACOS_VERSION - ^^^^^^^^^^^^^ Use MacOS.version instead of MACOS_VERSION - end - end - RUBY - end - - it "reports an offense when `build.with?` is used for a conditional dependency" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - depends_on "foo" if build.with? "foo" - ^^^^^^^^^^^^^^^^ Replace depends_on "foo" if build.with? "foo" with depends_on "foo" => :optional - end - RUBY - end - - it "reports an offense when `build.without?` is used for a negated conditional dependency" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - depends_on :foo unless build.without? "foo" - ^^^^^^^^^^^^^^^ Replace depends_on :foo unless build.without? "foo" with depends_on :foo => :recommended - end - RUBY - end - - it "reports an offense when `build.include?` is used for a negated conditional dependency" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - depends_on :foo unless build.include? "without-foo" - ^^^^^^^^^^^^^^^ Replace depends_on :foo unless build.include? "without-foo" with depends_on :foo => :recommended - end - RUBY - end - end -end - -describe RuboCop::Cop::FormulaAuditStrict::MakeCheck do - subject(:cop) { described_class.new } - - let(:path) { Tap::TAP_DIRECTORY/"homebrew/homebrew-core" } - - before do - path.mkpath - (path/"style_exceptions").mkpath - end - - def setup_style_exceptions - (path/"style_exceptions/make_check_allowlist.json").write <<~JSON - [ "bar" ] - JSON - end - - it "reports an offense when formulae in homebrew/core run build-time checks" do - setup_style_exceptions - - expect_offense(<<~RUBY, "#{path}/Formula/foo.rb") - class Foo < Formula - desc "foo" - url 'https://brew.sh/foo-1.0.tgz' - system "make", "-j1", "test" - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Formulae in homebrew/core (except e.g. cryptography, libraries) should not run build-time checks - end - RUBY - end - - it "reports no offenses when exempted formulae in homebrew/core run build-time checks" do - setup_style_exceptions - - expect_no_offenses(<<~RUBY, "#{path}/Formula/bar.rb") - class Bar < Formula - desc "bar" - url 'https://brew.sh/bar-1.0.tgz' - system "make", "-j1", "test" - end - RUBY - end -end - -describe RuboCop::Cop::FormulaAuditStrict::ShellCommands do - subject(:cop) { described_class.new } - - context "when auditing shell commands" do - it "reports and corrects an offense when `system` arguments should be separated" do - expect_offense(<<~RUBY) - class Foo < Formula - def install - system "foo bar" - ^^^^^^^^^ Separate `system` commands into `\"foo\", \"bar\"` - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - def install - system "foo", "bar" - end - end - RUBY - end - - it "reports and corrects an offense when `system` arguments with string interpolation should be separated" do - expect_offense(<<~RUBY) - class Foo < Formula - def install - system "\#{bin}/foo bar" - ^^^^^^^^^^^^^^^^ Separate `system` commands into `\"\#{bin}/foo\", \"bar\"` - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - def install - system "\#{bin}/foo", "bar" - end - end - RUBY - end - - it "reports no offenses when `system` with metacharacter arguments are called" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - def install - system "foo bar > baz" - end - end - RUBY - end - - it "reports no offenses when trailing arguments to `system` are unseparated" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - def install - system "foo", "bar baz" - end - end - RUBY - end - - it "reports no offenses when `Utils.popen` arguments are unseparated" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - def install - Utils.popen("foo bar") - end - end - RUBY - end - - it "reports and corrects an offense when `Utils.popen_read` arguments are unseparated" do - expect_offense(<<~RUBY) - class Foo < Formula - def install - Utils.popen_read("foo bar") - ^^^^^^^^^ Separate `Utils.popen_read` commands into `\"foo\", \"bar\"` - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - def install - Utils.popen_read("foo", "bar") - end - end - RUBY - end - - it "reports and corrects an offense when `Utils.safe_popen_read` arguments are unseparated" do - expect_offense(<<~RUBY) - class Foo < Formula - def install - Utils.safe_popen_read("foo bar") - ^^^^^^^^^ Separate `Utils.safe_popen_read` commands into `\"foo\", \"bar\"` - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - def install - Utils.safe_popen_read("foo", "bar") - end - end - RUBY - end - - it "reports and corrects an offense when `Utils.popen_write` arguments are unseparated" do - expect_offense(<<~RUBY) - class Foo < Formula - def install - Utils.popen_write("foo bar") - ^^^^^^^^^ Separate `Utils.popen_write` commands into `\"foo\", \"bar\"` - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - def install - Utils.popen_write("foo", "bar") - end - end - RUBY - end - - it "reports and corrects an offense when `Utils.safe_popen_write` arguments are unseparated" do - expect_offense(<<~RUBY) - class Foo < Formula - def install - Utils.safe_popen_write("foo bar") - ^^^^^^^^^ Separate `Utils.safe_popen_write` commands into `\"foo\", \"bar\"` - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - def install - Utils.safe_popen_write("foo", "bar") - end - end - RUBY - end - - it "reports and corrects an offense when `Utils.popen_read` arguments with interpolation are unseparated" do - expect_offense(<<~RUBY) - class Foo < Formula - def install - Utils.popen_read("\#{bin}/foo bar") - ^^^^^^^^^^^^^^^^ Separate `Utils.popen_read` commands into `\"\#{bin}/foo\", \"bar\"` - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - def install - Utils.popen_read("\#{bin}/foo", "bar") - end - end - RUBY - end - - it "reports no offenses when `Utils.popen_read` arguments with metacharacters are unseparated" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - def install - Utils.popen_read("foo bar > baz") - end - end - RUBY - end - - it "reports no offenses when trailing arguments to `Utils.popen_read` are unseparated" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - def install - Utils.popen_read("foo", "bar baz") - end - end - RUBY - end - - it "reports and corrects an offense when `Utils.popen_read` arguments are unseparated after a shell variable" do - expect_offense(<<~RUBY) - class Foo < Formula - def install - Utils.popen_read({ "SHELL" => "bash"}, "foo bar") - ^^^^^^^^^ Separate `Utils.popen_read` commands into `\"foo\", \"bar\"` - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - def install - Utils.popen_read({ "SHELL" => "bash"}, "foo", "bar") - end - end - RUBY - end - end -end diff --git a/Library/Homebrew/test/rubocops/livecheck/regex_case_insensitive_spec.rb b/Library/Homebrew/test/rubocops/livecheck/regex_case_insensitive_spec.rb new file mode 100644 index 0000000000..689ad4f3cd --- /dev/null +++ b/Library/Homebrew/test/rubocops/livecheck/regex_case_insensitive_spec.rb @@ -0,0 +1,46 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/livecheck" + +describe RuboCop::Cop::FormulaAudit::LivecheckRegexCaseInsensitive do + subject(:cop) { described_class.new } + + it "reports an offense when the `regex` is not case-insensitive" do + expect_offense(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + url :stable + regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Regexes should be case-insensitive unless sensitivity is explicitly required for proper matching. + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + url :stable + regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) + end + end + RUBY + end + + it "reports no offenses when the `regex` is case-insensitive" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + url :stable + regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) + end + end + RUBY + end +end diff --git a/Library/Homebrew/test/rubocops/livecheck/regex_extension_spec.rb b/Library/Homebrew/test/rubocops/livecheck/regex_extension_spec.rb new file mode 100644 index 0000000000..88b33c585a --- /dev/null +++ b/Library/Homebrew/test/rubocops/livecheck/regex_extension_spec.rb @@ -0,0 +1,46 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/livecheck" + +describe RuboCop::Cop::FormulaAudit::LivecheckRegexExtension do + subject(:cop) { described_class.new } + + it "reports an offense when the `regex` does not use `\\.t` for archive file extensions" do + expect_offense(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + url :stable + regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.tgz}i) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `\\.t` instead of `\\.tgz` + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + url :stable + regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) + end + end + RUBY + end + + it "reports no offenses when the `regex` uses `\\.t` for archive file extensions" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + url :stable + regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) + end + end + RUBY + end +end diff --git a/Library/Homebrew/test/rubocops/livecheck/regex_if_page_match_spec.rb b/Library/Homebrew/test/rubocops/livecheck/regex_if_page_match_spec.rb new file mode 100644 index 0000000000..e0ae8ac882 --- /dev/null +++ b/Library/Homebrew/test/rubocops/livecheck/regex_if_page_match_spec.rb @@ -0,0 +1,36 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/livecheck" + +describe RuboCop::Cop::FormulaAudit::LivecheckRegexIfPageMatch do + subject(:cop) { described_class.new } + + it "reports an offense when there is no `regex` for `strategy :page_match`" do + expect_offense(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + ^^^^^^^^^^^^ A `regex` is required if `strategy :page_match` is present. + url :stable + strategy :page_match + end + end + RUBY + end + + it "reports no offenses when a `regex` is specified for `strategy :page_match`" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + url :stable + strategy :page_match + regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) + end + end + RUBY + end +end diff --git a/Library/Homebrew/test/rubocops/livecheck/regex_parentheses_spec.rb b/Library/Homebrew/test/rubocops/livecheck/regex_parentheses_spec.rb new file mode 100644 index 0000000000..f0fbd85901 --- /dev/null +++ b/Library/Homebrew/test/rubocops/livecheck/regex_parentheses_spec.rb @@ -0,0 +1,46 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/livecheck" + +describe RuboCop::Cop::FormulaAudit::LivecheckRegexParentheses do + subject(:cop) { described_class.new } + + it "reports an offense when the `regex` call in the livecheck block does not use parentheses" do + expect_offense(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + url :stable + regex %r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The `regex` call should always use parentheses. + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + url :stable + regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) + end + end + RUBY + end + + it "reports no offenses when the `regex` call in the livecheck block uses parentheses" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + url :stable + regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) + end + end + RUBY + end +end diff --git a/Library/Homebrew/test/rubocops/livecheck/skip_spec.rb b/Library/Homebrew/test/rubocops/livecheck/skip_spec.rb new file mode 100644 index 0000000000..4c5742f198 --- /dev/null +++ b/Library/Homebrew/test/rubocops/livecheck/skip_spec.rb @@ -0,0 +1,44 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/livecheck" + +describe RuboCop::Cop::FormulaAudit::LivecheckSkip do + subject(:cop) { described_class.new } + + it "reports an offense when a skipped formula's livecheck block contains other information" do + expect_offense(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + ^^^^^^^^^^^^ Skipped formulae must not contain other livecheck information. + skip "Not maintained" + url :stable + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + skip "Not maintained" + end + end + RUBY + end + + it "reports no offenses when a skipped formula's livecheck block contains no other information" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + skip "Not maintained" + end + end + RUBY + end +end diff --git a/Library/Homebrew/test/rubocops/livecheck/url_provided_spec.rb b/Library/Homebrew/test/rubocops/livecheck/url_provided_spec.rb new file mode 100644 index 0000000000..cc40cbc0b5 --- /dev/null +++ b/Library/Homebrew/test/rubocops/livecheck/url_provided_spec.rb @@ -0,0 +1,34 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/livecheck" + +describe RuboCop::Cop::FormulaAudit::LivecheckUrlProvided do + subject(:cop) { described_class.new } + + it "reports an offense when a `url` is not specified in the livecheck block" do + expect_offense(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + ^^^^^^^^^^^^ A `url` must be provided to livecheck. + regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) + end + end + RUBY + end + + it "reports no offenses when a `url` is specified in the livecheck block" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + url :stable + regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) + end + end + RUBY + end +end diff --git a/Library/Homebrew/test/rubocops/livecheck/url_symbol_spec.rb b/Library/Homebrew/test/rubocops/livecheck/url_symbol_spec.rb new file mode 100644 index 0000000000..6ce25c3203 --- /dev/null +++ b/Library/Homebrew/test/rubocops/livecheck/url_symbol_spec.rb @@ -0,0 +1,43 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/livecheck" + +describe RuboCop::Cop::FormulaAudit::LivecheckUrlSymbol do + subject(:cop) { described_class.new } + + it "reports an offense when the `url` specified in the livecheck block is identical to a formula URL" do + expect_offense(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + url "https://brew.sh/foo-1.0.tgz" + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `url :stable` + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + url :stable + end + end + RUBY + end + + it "reports no offenses when the `url` specified in the livecheck block is not identical to a formula URL" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + livecheck do + url "https://brew.sh/foo/releases/" + end + end + RUBY + end +end diff --git a/Library/Homebrew/test/rubocops/livecheck_spec.rb b/Library/Homebrew/test/rubocops/livecheck_spec.rb deleted file mode 100644 index 33bb7cf2e0..0000000000 --- a/Library/Homebrew/test/rubocops/livecheck_spec.rb +++ /dev/null @@ -1,271 +0,0 @@ -# typed: false -# frozen_string_literal: true - -require "rubocops/livecheck" - -describe RuboCop::Cop::FormulaAudit::LivecheckSkip do - subject(:cop) { described_class.new } - - it "reports an offense when a skipped formula's livecheck block contains other information" do - expect_offense(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - ^^^^^^^^^^^^ Skipped formulae must not contain other livecheck information. - skip "Not maintained" - url :stable - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - skip "Not maintained" - end - end - RUBY - end - - it "reports no offenses when a skipped formula's livecheck block contains no other information" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - skip "Not maintained" - end - end - RUBY - end -end - -describe RuboCop::Cop::FormulaAudit::LivecheckUrlProvided do - subject(:cop) { described_class.new } - - it "reports an offense when a `url` is not specified in the livecheck block" do - expect_offense(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - ^^^^^^^^^^^^ A `url` must be provided to livecheck. - regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) - end - end - RUBY - end - - it "reports no offenses when a `url` is specified in the livecheck block" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - url :stable - regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) - end - end - RUBY - end -end - -describe RuboCop::Cop::FormulaAudit::LivecheckUrlSymbol do - subject(:cop) { described_class.new } - - it "reports an offense when the `url` specified in the livecheck block is identical to a formula URL" do - expect_offense(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - url "https://brew.sh/foo-1.0.tgz" - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `url :stable` - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - url :stable - end - end - RUBY - end - - it "reports no offenses when the `url` specified in the livecheck block is not identical to a formula URL" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - url "https://brew.sh/foo/releases/" - end - end - RUBY - end -end - -describe RuboCop::Cop::FormulaAudit::LivecheckRegexParentheses do - subject(:cop) { described_class.new } - - it "reports an offense when the `regex` call in the livecheck block does not use parentheses" do - expect_offense(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - url :stable - regex %r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The `regex` call should always use parentheses. - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - url :stable - regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) - end - end - RUBY - end - - it "reports no offenses when the `regex` call in the livecheck block uses parentheses" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - url :stable - regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) - end - end - RUBY - end -end - -describe RuboCop::Cop::FormulaAudit::LivecheckRegexExtension do - subject(:cop) { described_class.new } - - it "reports an offense when the `regex` does not use `\\.t` for archive file extensions" do - expect_offense(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - url :stable - regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.tgz}i) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `\\.t` instead of `\\.tgz` - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - url :stable - regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) - end - end - RUBY - end - - it "reports no offenses when the `regex` uses `\\.t` for archive file extensions" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - url :stable - regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) - end - end - RUBY - end -end - -describe RuboCop::Cop::FormulaAudit::LivecheckRegexIfPageMatch do - subject(:cop) { described_class.new } - - it "reports an offense when there is no `regex` for `strategy :page_match`" do - expect_offense(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - ^^^^^^^^^^^^ A `regex` is required if `strategy :page_match` is present. - url :stable - strategy :page_match - end - end - RUBY - end - - it "rreports no offenses when a `regex` is specified for `strategy :page_match`" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - url :stable - strategy :page_match - regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) - end - end - RUBY - end -end - -describe RuboCop::Cop::FormulaAudit::LivecheckRegexCaseInsensitive do - subject(:cop) { described_class.new } - - it "reports an offense when the `regex` is not case-insensitive" do - expect_offense(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - url :stable - regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Regexes should be case-insensitive unless sensitivity is explicitly required for proper matching. - end - end - RUBY - - expect_correction(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - url :stable - regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) - end - end - RUBY - end - - it "reports no offenses when the `regex` is case-insensitive" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - livecheck do - url :stable - regex(%r{href=.*?/formula[._-]v?(\\d+(?:\\.\\d+)+)\\.t}i) - end - end - RUBY - end -end diff --git a/Library/Homebrew/test/rubocops/text/assert_statements_spec.rb b/Library/Homebrew/test/rubocops/text/assert_statements_spec.rb new file mode 100644 index 0000000000..90cce65e23 --- /dev/null +++ b/Library/Homebrew/test/rubocops/text/assert_statements_spec.rb @@ -0,0 +1,54 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/lines" + +describe RuboCop::Cop::FormulaAudit::AssertStatements do + subject(:cop) { described_class.new } + + context "when auditing formula assertions" do + it "reports an offense when assert ... include is used" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + assert File.read("inbox").include?("Sample message 1") + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `assert_match` instead of `assert ...include?` + end + RUBY + end + + it "reports an offense when assert ... exist? is used without a negation" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + assert File.exist? "default.ini" + ^^^^^^^^^^^^^^^^^^^^^^^^^ Use `assert_predicate , :exist?` instead of `assert File.exist? "default.ini"` + end + RUBY + end + + it "reports an offense when assert ... exist? is used with a negation" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + assert !File.exist?("default.ini") + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `refute_predicate , :exist?` instead of `assert !File.exist?("default.ini")` + end + RUBY + end + + it "reports an offense when assert ... executable? is used without a negation" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + assert File.executable? f + ^^^^^^^^^^^^^^^^^^ Use `assert_predicate , :executable?` instead of `assert File.executable? f` + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/text/comments_spec.rb b/Library/Homebrew/test/rubocops/text/comments_spec.rb new file mode 100644 index 0000000000..0861b7f543 --- /dev/null +++ b/Library/Homebrew/test/rubocops/text/comments_spec.rb @@ -0,0 +1,58 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/lines" + +describe RuboCop::Cop::FormulaAudit::Comments do + subject(:cop) { described_class.new } + + context "when auditing comment text" do + it "reports an offense when commented cmake calls exist" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + # system "cmake", ".", *std_cmake_args + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Please remove default template comments + end + RUBY + end + + it "reports an offense when default template comments exist" do + expect_offense(<<~RUBY) + class Foo < Formula + # PLEASE REMOVE + ^^^^^^^^^^^^^^^ Please remove default template comments + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + end + RUBY + end + + it "reports an offense when `depends_on` is commented" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + # depends_on "foo" + ^^^^^^^^^^^^^^^^^^ Commented-out dependency "foo" + end + RUBY + end + + it "reports an offense if citation tags are present" do + expect_offense(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + # cite Howell_2009: + ^^^^^^^^^^^^^^^^^^^ Formulae in homebrew/core should not use `cite` comments + # doi "10.111/222.x" + ^^^^^^^^^^^^^^^^^^^^ Formulae in homebrew/core should not use `doi` comments + # tag "software" + ^^^^^^^^^^^^^^^^ Formulae in homebrew/core should not use `tag` comments + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/text/license_arrays_spec.rb b/Library/Homebrew/test/rubocops/text/license_arrays_spec.rb new file mode 100644 index 0000000000..2cbadfaa60 --- /dev/null +++ b/Library/Homebrew/test/rubocops/text/license_arrays_spec.rb @@ -0,0 +1,59 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/lines" + +describe RuboCop::Cop::FormulaAudit::LicenseArrays do + subject(:cop) { described_class.new } + + context "when auditing license arrays" do + it "reports no offenses for license strings" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license "MIT" + end + RUBY + end + + it "reports no offenses for license symbols" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license :public_domain + end + RUBY + end + + it "reports no offenses for license hashes" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license any_of: ["MIT", "0BSD"] + end + RUBY + end + + it "reports and corrects use of a license array" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license ["MIT", "0BSD"] + ^^^^^^^^^^^^^^^^^^^^^^^ Use `license any_of: ["MIT", "0BSD"]` instead of `license ["MIT", "0BSD"]` + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license any_of: ["MIT", "0BSD"] + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/text/licenses_spec.rb b/Library/Homebrew/test/rubocops/text/licenses_spec.rb new file mode 100644 index 0000000000..5fc3fe1f74 --- /dev/null +++ b/Library/Homebrew/test/rubocops/text/licenses_spec.rb @@ -0,0 +1,88 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/lines" + +describe RuboCop::Cop::FormulaAudit::Licenses do + subject(:cop) { described_class.new } + + context "when auditing licenses" do + it "reports no offenses for license strings" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license "MIT" + end + RUBY + end + + it "reports no offenses for license symbols" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license :public_domain + end + RUBY + end + + it "reports no offenses for license hashes" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license any_of: ["MIT", "0BSD"] + end + RUBY + end + + it "reports no offenses for license exceptions" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license "MIT" => { with: "LLVM-exception" } + end + RUBY + end + + it "reports no offenses for multiline nested license hashes" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license any_of: [ + "MIT", + all_of: ["0BSD", "Zlib"], + ] + end + RUBY + end + + it "reports no offenses for multiline nested license hashes with exceptions" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license any_of: [ + "MIT", + all_of: ["0BSD", "Zlib"], + "GPL-2.0-only" => { with: "LLVM-exception" }, + ] + end + RUBY + end + + it "reports an offense for nested license hashes on a single line" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + license any_of: ["MIT", all_of: ["0BSD", "Zlib"]] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Split nested license declarations onto multiple lines + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/text/make_check_spec.rb b/Library/Homebrew/test/rubocops/text/make_check_spec.rb new file mode 100644 index 0000000000..da902cdfe0 --- /dev/null +++ b/Library/Homebrew/test/rubocops/text/make_check_spec.rb @@ -0,0 +1,46 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/lines" + +describe RuboCop::Cop::FormulaAuditStrict::MakeCheck do + subject(:cop) { described_class.new } + + let(:path) { Tap::TAP_DIRECTORY/"homebrew/homebrew-core" } + + before do + path.mkpath + (path/"style_exceptions").mkpath + end + + def setup_style_exceptions + (path/"style_exceptions/make_check_allowlist.json").write <<~JSON + [ "bar" ] + JSON + end + + it "reports an offense when formulae in homebrew/core run build-time checks" do + setup_style_exceptions + + expect_offense(<<~RUBY, "#{path}/Formula/foo.rb") + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + system "make", "-j1", "test" + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Formulae in homebrew/core (except e.g. cryptography, libraries) should not run build-time checks + end + RUBY + end + + it "reports no offenses when exempted formulae in homebrew/core run build-time checks" do + setup_style_exceptions + + expect_no_offenses(<<~RUBY, "#{path}/Formula/bar.rb") + class Bar < Formula + desc "bar" + url 'https://brew.sh/bar-1.0.tgz' + system "make", "-j1", "test" + end + RUBY + end +end diff --git a/Library/Homebrew/test/rubocops/text/miscellaneous_spec.rb b/Library/Homebrew/test/rubocops/text/miscellaneous_spec.rb new file mode 100644 index 0000000000..1fa1963e97 --- /dev/null +++ b/Library/Homebrew/test/rubocops/text/miscellaneous_spec.rb @@ -0,0 +1,483 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/lines" + +describe RuboCop::Cop::FormulaAudit::Miscellaneous do + subject(:cop) { described_class.new } + + context "when auditing formula miscellany" do + it "reports an offense for unneeded `FileUtils` usage" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + FileUtils.mv "hello" + ^^^^^^^^^^^^^^^^^^^^ Don\'t need \'FileUtils.\' before mv + end + RUBY + end + + it "reports an offense for long `inreplace` block variable names" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + inreplace "foo" do |longvar| + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ \"inreplace do |s|\" is preferred over \"|longvar|\". + somerandomCall(longvar) + end + end + RUBY + end + + it "reports an offense for invalid `rebuild` numbers" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + bottle do + rebuild 0 + ^^^^^^^^^ 'rebuild 0' should be removed + sha256 "fe0679b932dd43a87fd415b609a7fbac7a069d117642ae8ebaac46ae1fb9f0b3" => :sierra + end + end + RUBY + end + + it "reports an offense when `OS.linux?` is used in homebrew/core" do + expect_offense(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + bottle do + if OS.linux? + ^^^^^^^^^ Don\'t use OS.linux?; homebrew/core only supports macOS + nil + end + sha256 "fe0679b932dd43a87fd415b609a7fbac7a069d117642ae8ebaac46ae1fb9f0b3" => :sierra + end + end + RUBY + end + + it "reports an offense when a useless `fails_with :llvm` is used" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + bottle do + sha256 "fe0679b932dd43a87fd415b609a7fbac7a069d117642ae8ebaac46ae1fb9f0b3" => :sierra + end + fails_with :llvm do + ^^^^^^^^^^^^^^^^ 'fails_with :llvm' is now a no-op so should be removed + build 2335 + cause "foo" + end + end + RUBY + end + + it "reports an offense when `def test` is used" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + + def test + ^^^^^^^^ Use new-style test definitions (test do) + assert_equals "1", "1" + end + end + RUBY + end + + it "reports an offense when `skip_clean` is used" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + skip_clean :all + ^^^^^^^^^^^^^^^ `skip_clean :all` is deprecated; brew no longer strips symbols. Pass explicit paths to prevent Homebrew from removing empty folders. + end + RUBY + end + + it "reports an offense when `build.universal?` is used" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + if build.universal? + ^^^^^^^^^^^^^^^^ macOS has been 64-bit only since 10.6 so build.universal? is deprecated. + "foo" + end + end + RUBY + end + + it "reports no offenses when `build.universal?` is used in an exempt formula" do + expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/wine.rb") + class Wine < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + if build.universal? + "foo" + end + end + RUBY + end + + it "reports an offense when `ENV.universal_binary` is used" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + if build? + ENV.universal_binary + ^^^^^^^^^^^^^^^^^^^^ macOS has been 64-bit only since 10.6 so ENV.universal_binary is deprecated. + end + end + RUBY + end + + it "reports no offenses when `ENV.universal_binary` is used in an exempt formula" do + expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/wine.rb") + class Wine < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + if build? + ENV.universal_binary + end + end + RUBY + end + + it "reports an offense when `install_name_tool` is called" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + system "install_name_tool", "-id" + ^^^^^^^^^^^^^^^^^^^ Use ruby-macho instead of calling "install_name_tool" + end + RUBY + end + + it "reports an offense when `npm install` is called without Language::Node arguments" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + system "npm", "install" + ^^^^^^^^^^^^^^^^^^^^^^^ Use Language::Node for npm install args + end + RUBY + end + + it "reports no offenses when `npm install` is called without Language::Node arguments in an exempt formula" do + expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/kibana@4.4.rb") + class KibanaAT44 < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + system "npm", "install" + end + RUBY + end + + it "reports an offense when `depends_on` is called with an instance" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + depends_on FOO::BAR.new + ^^^^^^^^^^^^ `depends_on` can take requirement classes instead of instances + end + RUBY + end + + it "reports an offense when `Dir` is called without a globbing argument" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + rm_rf Dir["src/{llvm,test,librustdoc,etc/snapshot.pyc}"] + rm_rf Dir["src/snapshot.pyc"] + ^^^^^^^^^^^^^^^^^^ Dir(["src/snapshot.pyc"]) is unnecessary; just use "src/snapshot.pyc" + end + RUBY + end + + it "reports an offense when executing a system command for which there is a Ruby FileUtils equivalent" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + system "mkdir", "foo" + ^^^^^^^ Use the `mkdir` Ruby method instead of `system "mkdir", "foo"` + end + RUBY + end + + it "reports an offense when top-level functions are defined outside of a class body" do + expect_offense(<<~RUBY) + def test + ^^^^^^^^ Define method test in the class body, not at the top-level + nil + end + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + end + RUBY + end + + it 'reports an offense when `man+"man8"` is used' do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def install + man1.install man+"man8" => "faad.1" + ^^^^^^ "man+"man8"" should be "man8" + end + end + RUBY + end + + it "reports an offense when a hard-coded `gcc` is referenced" do + expect_offense(<<~'RUBY') + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def install + system "/usr/bin/gcc", "foo" + ^^^^^^^^^^^^^^ Use "#{ENV.cc}" instead of hard-coding "gcc" + end + end + RUBY + end + + it "reports an offense when a hard-coded `g++` is referenced" do + expect_offense(<<~'RUBY') + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def install + system "/usr/bin/g++", "-o", "foo", "foo.cc" + ^^^^^^^^^^^^^^ Use "#{ENV.cxx}" instead of hard-coding "g++" + end + end + RUBY + end + + it "reports an offense when a hard-coded `llvm-g++` is set as COMPILER_PATH" do + expect_offense(<<~'RUBY') + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def install + ENV["COMPILER_PATH"] = "/usr/bin/llvm-g++" + ^^^^^^^^^^^^^^^^^^^ Use "#{ENV.cxx}" instead of hard-coding "llvm-g++" + end + end + RUBY + end + + it "reports an offense when a hard-coded `gcc` is set as COMPILER_PATH" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def install + ENV["COMPILER_PATH"] = "/usr/bin/gcc" + ^^^^^^^^^^^^^^ Use \"\#{ENV.cc}\" instead of hard-coding \"gcc\" + end + end + RUBY + end + + it "reports an offense when the formula path shortcut `man` could be used" do + expect_offense(<<~'RUBY') + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def install + mv "#{share}/man", share + ^^^^ "#{share}/man" should be "#{man}" + end + end + RUBY + end + + it "reports an offense when the formula path shortcut `libexec` could be used" do + expect_offense(<<~'RUBY') + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def install + mv "#{prefix}/libexec", share + ^^^^^^^^ "#{prefix}/libexec" should be "#{libexec}" + end + end + RUBY + end + + it "reports an offense when the formula path shortcut `info` could be used" do + expect_offense(<<~'RUBY') + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def install + system "./configure", "--INFODIR=#{prefix}/share/info" + ^^^^^^^^^^^ "#{prefix}/share/info" should be "#{info}" + end + end + RUBY + end + + it "reports an offense when the formula path shortcut `man8` could be used" do + expect_offense(<<~'RUBY') + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def install + system "./configure", "--MANDIR=#{prefix}/share/man/man8" + ^^^^^^^^^^^^^^^ "#{prefix}/share/man/man8" should be "#{man8}" + end + end + RUBY + end + + it "reports an offense when unvendored lua modules are used" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + depends_on "lpeg" => :lua51 + ^^^^^^ lua modules should be vendored rather than use deprecated `depends_on \"lpeg\" => :lua51` + end + RUBY + end + + it "reports an offense when `export` is used to set environment variables" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + system "export", "var=value" + ^^^^^^^^ Use ENV instead of invoking 'export' to modify the environment + end + RUBY + end + + it "reports an offense when dependencies with invalid options are used" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + depends_on "foo" => "with-bar" + ^^^^^^^^^^ Dependency foo should not use option with-bar + end + RUBY + end + + it "reports an offense when dependencies with invalid options are used in an array" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + depends_on "httpd" => [:build, :test] + depends_on "foo" => [:optional, "with-bar"] + ^^^^^^^^^^ Dependency foo should not use option with-bar + depends_on "icu4c" => [:optional, "c++11"] + ^^^^^^^ Dependency icu4c should not use option c++11 + end + RUBY + end + + it "reports an offense when `build.head?` could be used instead of checking `version`" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + if version == "HEAD" + ^^^^^^^^^^^^^^^^^ Use 'build.head?' instead of inspecting 'version' + foo() + end + end + RUBY + end + + it "reports an offense when `ARGV.include? (--HEAD)` is used" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + test do + head = ARGV.include? "--HEAD" + ^^^^ Use build instead of ARGV to check options + ^^^^^^^^^^^^^^^^^^^^^^ Use "if build.head?" instead + end + end + RUBY + end + + it "reports an offense when `needs :openmp` is used" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + needs :openmp + ^^^^^^^^^^^^^ 'needs :openmp' should be replaced with 'depends_on \"gcc\"' + end + RUBY + end + + it "reports an offense when `MACOS_VERSION` is used" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + test do + version = MACOS_VERSION + ^^^^^^^^^^^^^ Use MacOS.version instead of MACOS_VERSION + end + end + RUBY + end + + it "reports an offense when `build.with?` is used for a conditional dependency" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + depends_on "foo" if build.with? "foo" + ^^^^^^^^^^^^^^^^ Replace depends_on "foo" if build.with? "foo" with depends_on "foo" => :optional + end + RUBY + end + + it "reports an offense when `build.without?` is used for a negated conditional dependency" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + depends_on :foo unless build.without? "foo" + ^^^^^^^^^^^^^^^ Replace depends_on :foo unless build.without? "foo" with depends_on :foo => :recommended + end + RUBY + end + + it "reports an offense when `build.include?` is used for a negated conditional dependency" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + depends_on :foo unless build.include? "without-foo" + ^^^^^^^^^^^^^^^ Replace depends_on :foo unless build.include? "without-foo" with depends_on :foo => :recommended + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/text/mpi_check_spec.rb b/Library/Homebrew/test/rubocops/text/mpi_check_spec.rb new file mode 100644 index 0000000000..56c944c2d0 --- /dev/null +++ b/Library/Homebrew/test/rubocops/text/mpi_check_spec.rb @@ -0,0 +1,29 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/lines" + +describe RuboCop::Cop::FormulaAudit::MpiCheck do + subject(:cop) { described_class.new } + + context "when auditing MPI dependencies" do + it "reports and corrects an offense when using depends_on \"mpich\" in homebrew/core" do + expect_offense(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + depends_on "mpich" + ^^^^^^^^^^^^^^^^^^ Formulae in homebrew/core should use 'depends_on "open-mpi"' instead of 'depends_on "mpich"'. + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + depends_on "open-mpi" + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/text/option_declarations_spec.rb b/Library/Homebrew/test/rubocops/text/option_declarations_spec.rb new file mode 100644 index 0000000000..2371c0bd6b --- /dev/null +++ b/Library/Homebrew/test/rubocops/text/option_declarations_spec.rb @@ -0,0 +1,159 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/lines" + +describe RuboCop::Cop::FormulaAudit::OptionDeclarations do + subject(:cop) { described_class.new } + + context "when auditing options" do + it "reports an offense when `build.without?` is used in homebrew/core" do + expect_offense(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def install + build.without? "bar" + ^^^^^^^^^^^^^^^^^^^^ Formulae in homebrew/core should not use `build.without?`. + end + end + RUBY + end + + it "reports an offense when `build.with?` is used in homebrew/core" do + expect_offense(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def install + build.with? "bar" + ^^^^^^^^^^^^^^^^^ Formulae in homebrew/core should not use `build.with?`. + end + end + RUBY + end + + it "reports an offense when `build.without?` is used for a conditional dependency" do + expect_offense(<<~RUBY) + class Foo < Formula + depends_on "bar" if build.without?("baz") + ^^^^^^^^^^^^^^^^^^^^^ Use `:optional` or `:recommended` instead of `if build.without?("baz")` + end + RUBY + end + + it "reports an offense when `build.without?` is used for a conditional dependency" do + expect_offense(<<~RUBY) + class Foo < Formula + depends_on "bar" if build.with?("baz") + ^^^^^^^^^^^^^^^^^^ Use `:optional` or `:recommended` instead of `if build.with?("baz")` + end + RUBY + end + + it "reports an offense when `build.without?` is used with `unless`" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def post_install + return unless build.without? "bar" + ^^^^^^^^^^^^^^^^^^^^ Use if build.with? "bar" instead of unless build.without? "bar" + end + end + RUBY + end + + it "reports an offense when `build.with?` is used with `unless`" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def post_install + return unless build.with? "bar" + ^^^^^^^^^^^^^^^^^ Use if build.without? "bar" instead of unless build.with? "bar" + end + end + RUBY + end + + it "reports an offense when `build.with?` is negated" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def post_install + return if !build.with? "bar" + ^^^^^^^^^^^^^^^^^^ Don't negate 'build.with?': use 'build.without?' + end + end + RUBY + end + + it "reports an offense when `build.without?` is negated" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def post_install + return if !build.without? "bar" + ^^^^^^^^^^^^^^^^^^^^^ Don't negate 'build.without?': use 'build.with?' + end + end + RUBY + end + + it "reports an offense when a `build.without?` conditional is unnecessary" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def post_install + return if build.without? "--without-bar" + ^^^^^^^^^^^^^^^ Don't duplicate 'without': Use `build.without? \"bar\"` to check for \"--without-bar\" + end + end + RUBY + end + + it "reports an offense when a `build.with?` conditional is unnecessary" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def post_install + return if build.with? "--with-bar" + ^^^^^^^^^^^^ Don't duplicate 'with': Use `build.with? \"bar\"` to check for \"--with-bar\" + end + end + RUBY + end + + it "reports an offense when `build.include?` is used" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + def post_install + return if build.include? "foo" + ^^^^^^^^^^^^^^^^^^^^ `build.include?` is deprecated + end + end + RUBY + end + + it "reports an offense when `def option` is used" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url 'https://brew.sh/foo-1.0.tgz' + + def options + ^^^^^^^^^^^ Use new-style option definitions + [["--bar", "desc"]] + end + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/text/python_versions_spec.rb b/Library/Homebrew/test/rubocops/text/python_versions_spec.rb new file mode 100644 index 0000000000..372e4d9598 --- /dev/null +++ b/Library/Homebrew/test/rubocops/text/python_versions_spec.rb @@ -0,0 +1,184 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/lines" + +describe RuboCop::Cop::FormulaAudit::PythonVersions do + subject(:cop) { described_class.new } + + context "when auditing Python versions" do + it "reports no offenses for Python with no dependency" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + def install + puts "python@3.8" + end + end + RUBY + end + + it "reports no offenses for unversioned Python references" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + depends_on "python@3.9" + + def install + puts "python" + end + end + RUBY + end + + it "reports no offenses for Python with no version" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + depends_on "python@3.9" + + def install + puts "python3" + end + end + RUBY + end + + it "reports no offenses when a Python reference matches its dependency" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + depends_on "python@3.9" + + def install + puts "python@3.9" + end + end + RUBY + end + + it "reports no offenses when a Python reference matches its dependency without `@`" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + depends_on "python@3.9" + + def install + puts "python3.9" + end + end + RUBY + end + + it "reports no offenses when a Python reference matches its two-digit dependency" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + depends_on "python@3.10" + + def install + puts "python@3.10" + end + end + RUBY + end + + it "reports no offenses when a Python reference matches its two-digit dependency without `@`" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + depends_on "python@3.10" + + def install + puts "python3.10" + end + end + RUBY + end + + it "reports and corrects Python references with mismatched versions" do + expect_offense(<<~RUBY) + class Foo < Formula + depends_on "python@3.9" + + def install + puts "python@3.8" + ^^^^^^^^^^^^ References to `python@3.8` should match the specified python dependency (`python@3.9`) + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + depends_on "python@3.9" + + def install + puts "python@3.9" + end + end + RUBY + end + + it "reports and corrects Python references with mismatched versions without `@`" do + expect_offense(<<~RUBY) + class Foo < Formula + depends_on "python@3.9" + + def install + puts "python3.8" + ^^^^^^^^^^^ References to `python3.8` should match the specified python dependency (`python3.9`) + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + depends_on "python@3.9" + + def install + puts "python3.9" + end + end + RUBY + end + + it "reports and corrects Python references with mismatched two-digit versions" do + expect_offense(<<~RUBY) + class Foo < Formula + depends_on "python@3.11" + + def install + puts "python@3.10" + ^^^^^^^^^^^^^ References to `python@3.10` should match the specified python dependency (`python@3.11`) + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + depends_on "python@3.11" + + def install + puts "python@3.11" + end + end + RUBY + end + + it "reports and corrects Python references with mismatched two-digit versions without `@`" do + expect_offense(<<~RUBY) + class Foo < Formula + depends_on "python@3.11" + + def install + puts "python3.10" + ^^^^^^^^^^^^ References to `python3.10` should match the specified python dependency (`python3.11`) + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + depends_on "python@3.11" + + def install + puts "python3.11" + end + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/text/safe_popen_commands_spec.rb b/Library/Homebrew/test/rubocops/text/safe_popen_commands_spec.rb new file mode 100644 index 0000000000..156c186a06 --- /dev/null +++ b/Library/Homebrew/test/rubocops/text/safe_popen_commands_spec.rb @@ -0,0 +1,59 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/lines" + +describe RuboCop::Cop::FormulaAudit::SafePopenCommands do + subject(:cop) { described_class.new } + + context "when auditing popen commands" do + it "reports and corrects `Utils.popen_read` usage" do + expect_offense(<<~RUBY) + class Foo < Formula + def install + Utils.popen_read "foo" + ^^^^^^^^^^^^^^^^^^^^^^ Use `Utils.safe_popen_read` instead of `Utils.popen_read` + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + def install + Utils.safe_popen_read "foo" + end + end + RUBY + end + + it "reports and corrects `Utils.popen_write` usage" do + expect_offense(<<~RUBY) + class Foo < Formula + def install + Utils.popen_write "foo" + ^^^^^^^^^^^^^^^^^^^^^^^ Use `Utils.safe_popen_write` instead of `Utils.popen_write` + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + def install + Utils.safe_popen_write "foo" + end + end + RUBY + end + + it "does not report an offense when `Utils.popen_read` is used in a test block" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + def install; end + test do + Utils.popen_read "foo" + end + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/text/shell_commands_spec.rb b/Library/Homebrew/test/rubocops/text/shell_commands_spec.rb new file mode 100644 index 0000000000..9699092063 --- /dev/null +++ b/Library/Homebrew/test/rubocops/text/shell_commands_spec.rb @@ -0,0 +1,212 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/lines" + +describe RuboCop::Cop::FormulaAuditStrict::ShellCommands do + subject(:cop) { described_class.new } + + context "when auditing shell commands" do + it "reports and corrects an offense when `system` arguments should be separated" do + expect_offense(<<~RUBY) + class Foo < Formula + def install + system "foo bar" + ^^^^^^^^^ Separate `system` commands into `\"foo\", \"bar\"` + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + def install + system "foo", "bar" + end + end + RUBY + end + + it "reports and corrects an offense when `system` arguments with string interpolation should be separated" do + expect_offense(<<~RUBY) + class Foo < Formula + def install + system "\#{bin}/foo bar" + ^^^^^^^^^^^^^^^^ Separate `system` commands into `\"\#{bin}/foo\", \"bar\"` + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + def install + system "\#{bin}/foo", "bar" + end + end + RUBY + end + + it "reports no offenses when `system` with metacharacter arguments are called" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + def install + system "foo bar > baz" + end + end + RUBY + end + + it "reports no offenses when trailing arguments to `system` are unseparated" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + def install + system "foo", "bar baz" + end + end + RUBY + end + + it "reports no offenses when `Utils.popen` arguments are unseparated" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + def install + Utils.popen("foo bar") + end + end + RUBY + end + + it "reports and corrects an offense when `Utils.popen_read` arguments are unseparated" do + expect_offense(<<~RUBY) + class Foo < Formula + def install + Utils.popen_read("foo bar") + ^^^^^^^^^ Separate `Utils.popen_read` commands into `\"foo\", \"bar\"` + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + def install + Utils.popen_read("foo", "bar") + end + end + RUBY + end + + it "reports and corrects an offense when `Utils.safe_popen_read` arguments are unseparated" do + expect_offense(<<~RUBY) + class Foo < Formula + def install + Utils.safe_popen_read("foo bar") + ^^^^^^^^^ Separate `Utils.safe_popen_read` commands into `\"foo\", \"bar\"` + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + def install + Utils.safe_popen_read("foo", "bar") + end + end + RUBY + end + + it "reports and corrects an offense when `Utils.popen_write` arguments are unseparated" do + expect_offense(<<~RUBY) + class Foo < Formula + def install + Utils.popen_write("foo bar") + ^^^^^^^^^ Separate `Utils.popen_write` commands into `\"foo\", \"bar\"` + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + def install + Utils.popen_write("foo", "bar") + end + end + RUBY + end + + it "reports and corrects an offense when `Utils.safe_popen_write` arguments are unseparated" do + expect_offense(<<~RUBY) + class Foo < Formula + def install + Utils.safe_popen_write("foo bar") + ^^^^^^^^^ Separate `Utils.safe_popen_write` commands into `\"foo\", \"bar\"` + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + def install + Utils.safe_popen_write("foo", "bar") + end + end + RUBY + end + + it "reports and corrects an offense when `Utils.popen_read` arguments with interpolation are unseparated" do + expect_offense(<<~RUBY) + class Foo < Formula + def install + Utils.popen_read("\#{bin}/foo bar") + ^^^^^^^^^^^^^^^^ Separate `Utils.popen_read` commands into `\"\#{bin}/foo\", \"bar\"` + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + def install + Utils.popen_read("\#{bin}/foo", "bar") + end + end + RUBY + end + + it "reports no offenses when `Utils.popen_read` arguments with metacharacters are unseparated" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + def install + Utils.popen_read("foo bar > baz") + end + end + RUBY + end + + it "reports no offenses when trailing arguments to `Utils.popen_read` are unseparated" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + def install + Utils.popen_read("foo", "bar baz") + end + end + RUBY + end + + it "reports and corrects an offense when `Utils.popen_read` arguments are unseparated after a shell variable" do + expect_offense(<<~RUBY) + class Foo < Formula + def install + Utils.popen_read({ "SHELL" => "bash"}, "foo bar") + ^^^^^^^^^ Separate `Utils.popen_read` commands into `\"foo\", \"bar\"` + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + def install + Utils.popen_read({ "SHELL" => "bash"}, "foo", "bar") + end + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/text/shell_variables_spec.rb b/Library/Homebrew/test/rubocops/text/shell_variables_spec.rb new file mode 100644 index 0000000000..c10bcb26f4 --- /dev/null +++ b/Library/Homebrew/test/rubocops/text/shell_variables_spec.rb @@ -0,0 +1,86 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/lines" + +describe RuboCop::Cop::FormulaAudit::ShellVariables do + subject(:cop) { described_class.new } + + context "when auditing shell variables" do + it "reports and corrects unexpanded shell variables in `Utils.popen`" do + expect_offense(<<~RUBY) + class Foo < Formula + def install + Utils.popen "SHELL=bash foo" + ^^^^^^^^^^^^^^^^ Use `Utils.popen({ "SHELL" => "bash" }, "foo")` instead of `Utils.popen "SHELL=bash foo"` + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + def install + Utils.popen { "SHELL" => "bash" }, "foo" + end + end + RUBY + end + + it "reports and corrects unexpanded shell variables in `Utils.safe_popen_read`" do + expect_offense(<<~RUBY) + class Foo < Formula + def install + Utils.safe_popen_read "SHELL=bash foo" + ^^^^^^^^^^^^^^^^ Use `Utils.safe_popen_read({ "SHELL" => "bash" }, "foo")` instead of `Utils.safe_popen_read "SHELL=bash foo"` + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + def install + Utils.safe_popen_read { "SHELL" => "bash" }, "foo" + end + end + RUBY + end + + it "reports and corrects unexpanded shell variables in `Utils.safe_popen_write`" do + expect_offense(<<~RUBY) + class Foo < Formula + def install + Utils.safe_popen_write "SHELL=bash foo" + ^^^^^^^^^^^^^^^^ Use `Utils.safe_popen_write({ "SHELL" => "bash" }, "foo")` instead of `Utils.safe_popen_write "SHELL=bash foo"` + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + def install + Utils.safe_popen_write { "SHELL" => "bash" }, "foo" + end + end + RUBY + end + + it "reports and corrects unexpanded shell variables while preserving string interpolation" do + expect_offense(<<~RUBY) + class Foo < Formula + def install + Utils.popen "SHELL=bash \#{bin}/foo" + ^^^^^^^^^^^^^^^^^^^^^^^ Use `Utils.popen({ "SHELL" => "bash" }, "\#{bin}/foo")` instead of `Utils.popen "SHELL=bash \#{bin}/foo"` + end + end + RUBY + + expect_correction(<<~RUBY) + class Foo < Formula + def install + Utils.popen { "SHELL" => "bash" }, "\#{bin}/foo" + end + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/text/strict_spec.rb b/Library/Homebrew/test/rubocops/text/strict_spec.rb new file mode 100644 index 0000000000..f2ebfaedb7 --- /dev/null +++ b/Library/Homebrew/test/rubocops/text/strict_spec.rb @@ -0,0 +1,136 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/text" + +describe RuboCop::Cop::FormulaAuditStrict::Text do + subject(:cop) { described_class.new } + + context "when auditing formula text in homebrew/core" do + it "reports an offense if `env :userpaths` is present" do + expect_offense(<<~RUBY) + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + env :userpaths + ^^^^^^^^^^^^^^ `env :userpaths` in homebrew/core formulae is deprecated + end + RUBY + end + + it "reports an offense if `env :std` is present in homebrew/core" do + expect_offense(<<~RUBY, "/homebrew-core/") + class Foo < Formula + url "https://brew.sh/foo-1.0.tgz" + + env :std + ^^^^^^^^ `env :std` in homebrew/core formulae is deprecated + end + RUBY + end + + it %Q(reports an offense if "\#{share}/" is present) do + expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + def install + ohai "\#{share}/foo" + ^^^^^^^^^^^^^^ Use `\#{pkgshare}` instead of `\#{share}/foo` + end + end + RUBY + + expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + def install + ohai "\#{share}/foo/bar" + ^^^^^^^^^^^^^^^^^^ Use `\#{pkgshare}` instead of `\#{share}/foo` + end + end + RUBY + + expect_offense(<<~RUBY, "/homebrew-core/Formula/foolibc++.rb") + class Foolibcxx < Formula + def install + ohai "\#{share}/foolibc++" + ^^^^^^^^^^^^^^^^^^^^ Use `\#{pkgshare}` instead of `\#{share}/foolibc++` + end + end + RUBY + end + + it 'reports an offense if `share/""` is present' do + expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + def install + ohai share/"foo" + ^^^^^^^^^^^ Use `pkgshare` instead of `share/"foo"` + end + end + RUBY + + expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + def install + ohai share/"foo/bar" + ^^^^^^^^^^^^^^^ Use `pkgshare` instead of `share/"foo"` + end + end + RUBY + + expect_offense(<<~RUBY, "/homebrew-core/Formula/foolibc++.rb") + class Foolibcxx < Formula + def install + ohai share/"foolibc++" + ^^^^^^^^^^^^^^^^^ Use `pkgshare` instead of `share/"foolibc++"` + end + end + RUBY + end + + it %Q(reports no offenses if "\#{share}/" doesn't match formula name) do + expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + def install + ohai "\#{share}/foo-bar" + end + end + RUBY + end + + it 'reports no offenses if `share/""` is not present' do + expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + def install + ohai share/"foo-bar" + end + end + RUBY + + expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + def install + ohai share/"bar" + end + end + RUBY + + expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + def install + ohai share/"bar/foo" + end + end + RUBY + end + + it %Q(reports no offenses if formula name appears afer "\#{share}/") do + expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + def install + ohai "\#{share}/bar/foo" + end + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/text_spec.rb b/Library/Homebrew/test/rubocops/text_spec.rb index ba03b19eb2..1a20ca69d7 100644 --- a/Library/Homebrew/test/rubocops/text_spec.rb +++ b/Library/Homebrew/test/rubocops/text_spec.rb @@ -274,135 +274,3 @@ describe RuboCop::Cop::FormulaAudit::Text do end end end - -describe RuboCop::Cop::FormulaAuditStrict::Text do - subject(:cop) { described_class.new } - - context "when auditing formula text in homebrew/core" do - it "reports an offense if `env :userpaths` is present" do - expect_offense(<<~RUBY) - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - env :userpaths - ^^^^^^^^^^^^^^ `env :userpaths` in homebrew/core formulae is deprecated - end - RUBY - end - - it "reports an offense if `env :std` is present in homebrew/core" do - expect_offense(<<~RUBY, "/homebrew-core/") - class Foo < Formula - url "https://brew.sh/foo-1.0.tgz" - - env :std - ^^^^^^^^ `env :std` in homebrew/core formulae is deprecated - end - RUBY - end - - it %Q(reports an offense if "\#{share}/" is present) do - expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") - class Foo < Formula - def install - ohai "\#{share}/foo" - ^^^^^^^^^^^^^^ Use `\#{pkgshare}` instead of `\#{share}/foo` - end - end - RUBY - - expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") - class Foo < Formula - def install - ohai "\#{share}/foo/bar" - ^^^^^^^^^^^^^^^^^^ Use `\#{pkgshare}` instead of `\#{share}/foo` - end - end - RUBY - - expect_offense(<<~RUBY, "/homebrew-core/Formula/foolibc++.rb") - class Foolibcxx < Formula - def install - ohai "\#{share}/foolibc++" - ^^^^^^^^^^^^^^^^^^^^ Use `\#{pkgshare}` instead of `\#{share}/foolibc++` - end - end - RUBY - end - - it 'reports an offense if `share/""` is present' do - expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") - class Foo < Formula - def install - ohai share/"foo" - ^^^^^^^^^^^ Use `pkgshare` instead of `share/"foo"` - end - end - RUBY - - expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") - class Foo < Formula - def install - ohai share/"foo/bar" - ^^^^^^^^^^^^^^^ Use `pkgshare` instead of `share/"foo"` - end - end - RUBY - - expect_offense(<<~RUBY, "/homebrew-core/Formula/foolibc++.rb") - class Foolibcxx < Formula - def install - ohai share/"foolibc++" - ^^^^^^^^^^^^^^^^^ Use `pkgshare` instead of `share/"foolibc++"` - end - end - RUBY - end - - it %Q(reports no offenses if "\#{share}/" doesn't match formula name) do - expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/foo.rb") - class Foo < Formula - def install - ohai "\#{share}/foo-bar" - end - end - RUBY - end - - it 'reports no offenses if `share/""` is not present' do - expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/foo.rb") - class Foo < Formula - def install - ohai share/"foo-bar" - end - end - RUBY - - expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/foo.rb") - class Foo < Formula - def install - ohai share/"bar" - end - end - RUBY - - expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/foo.rb") - class Foo < Formula - def install - ohai share/"bar/foo" - end - end - RUBY - end - - it %Q(reports no offenses if formula name appears afer "\#{share}/") do - expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/foo.rb") - class Foo < Formula - def install - ohai "\#{share}/bar/foo" - end - end - RUBY - end - end -end diff --git a/Library/Homebrew/test/rubocops/urls/git_spec.rb b/Library/Homebrew/test/rubocops/urls/git_spec.rb new file mode 100644 index 0000000000..a4f77f45db --- /dev/null +++ b/Library/Homebrew/test/rubocops/urls/git_spec.rb @@ -0,0 +1,119 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/urls" + +describe RuboCop::Cop::FormulaAudit::GitUrls do + subject(:cop) { described_class.new } + + context "when a git URL is used" do + it "reports no offenses with a non-git URL" do + expect_no_offenses(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url "https://foo.com" + end + RUBY + end + + it "reports no offenses with both a tag and a revision" do + expect_no_offenses(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url "https://github.com/foo/bar.git", + tag: "v1.0.0", + revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + end + RUBY + end + + it "reports no offenses with both a tag, revision and `shallow` before" do + expect_no_offenses(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url "https://github.com/foo/bar.git", + shallow: false, + tag: "v1.0.0", + revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + end + RUBY + end + + it "reports no offenses with both a tag, revision and `shallow` after" do + expect_no_offenses(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url "https://github.com/foo/bar.git", + tag: "v1.0.0", + revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + shallow: false + end + RUBY + end + + it "reports an offense with no `revision`" do + expect_offense(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url "https://github.com/foo/bar.git", + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Formulae in homebrew/core should specify a revision for git URLs + tag: "v1.0.0" + end + RUBY + end + + it "reports an offense with no `revision` and `shallow`" do + expect_offense(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url "https://github.com/foo/bar.git", + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Formulae in homebrew/core should specify a revision for git URLs + shallow: false, + tag: "v1.0.0" + end + RUBY + end + + it "reports no offenses with no `tag`" do + expect_no_offenses(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url "https://github.com/foo/bar.git", + revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + end + RUBY + end + + it "reports no offenses with no `tag` and `shallow`" do + expect_no_offenses(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url "https://github.com/foo/bar.git", + revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + shallow: false + end + RUBY + end + + it "reports no offenses with missing arguments in `head`" do + expect_no_offenses(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url "https://foo.com" + head do + url "https://github.com/foo/bar.git" + end + end + RUBY + end + + it "reports no offenses for non-core taps" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url "https://github.com/foo/bar.git" + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/urls/git_strict_spec.rb b/Library/Homebrew/test/rubocops/urls/git_strict_spec.rb new file mode 100644 index 0000000000..d5fc0cc251 --- /dev/null +++ b/Library/Homebrew/test/rubocops/urls/git_strict_spec.rb @@ -0,0 +1,89 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/urls" + +describe RuboCop::Cop::FormulaAuditStrict::GitUrls do + subject(:cop) { described_class.new } + + context "when a git URL is used" do + it "reports no offenses with both a tag and a revision" do + expect_no_offenses(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url "https://github.com/foo/bar.git", + tag: "v1.0.0", + revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + end + RUBY + end + + it "reports no offenses with both a tag, revision and `shallow` before" do + expect_no_offenses(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url "https://github.com/foo/bar.git", + shallow: false, + tag: "v1.0.0", + revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + end + RUBY + end + + it "reports no offenses with both a tag, revision and `shallow` after" do + expect_no_offenses(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url "https://github.com/foo/bar.git", + tag: "v1.0.0", + revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + shallow: false + end + RUBY + end + + it "reports an offense with no `tag`" do + expect_offense(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url "https://github.com/foo/bar.git", + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Formulae in homebrew/core should specify a tag for git URLs + revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + end + RUBY + end + + it "reports an offense with no `tag` and `shallow`" do + expect_offense(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url "https://github.com/foo/bar.git", + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Formulae in homebrew/core should specify a tag for git URLs + revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + shallow: false + end + RUBY + end + + it "reports no offenses with missing arguments in `head`" do + expect_no_offenses(<<~RUBY, "/homebrew-core/") + class Foo < Formula + desc "foo" + url "https://foo.com" + head do + url "https://github.com/foo/bar.git" + end + end + RUBY + end + + it "reports no offenses for non-core taps" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url "https://github.com/foo/bar.git" + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/urls/pypi_spec.rb b/Library/Homebrew/test/rubocops/urls/pypi_spec.rb new file mode 100644 index 0000000000..516a616d01 --- /dev/null +++ b/Library/Homebrew/test/rubocops/urls/pypi_spec.rb @@ -0,0 +1,39 @@ +# typed: false +# frozen_string_literal: true + +require "rubocops/urls" + +describe RuboCop::Cop::FormulaAudit::PyPiUrls do + subject(:cop) { described_class.new } + + context "when a pypi URL is used" do + it "reports an offense for pypi.python.org urls" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url "https://pypi.python.org/packages/source/foo/foo-0.1.tar.gz" + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use the `Source` url found on PyPI downloads page (`https://pypi.org/project/foo/#files`) + end + RUBY + end + + it "reports an offense for short file.pythonhosted.org urls" do + expect_offense(<<~RUBY) + class Foo < Formula + desc "foo" + url "https://files.pythonhosted.org/packages/source/f/foo/foo-0.1.tar.gz" + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use the `Source` url found on PyPI downloads page (`https://pypi.org/project/foo/#files`) + end + RUBY + end + + it "reports no offenses for long file.pythonhosted.org urls" do + expect_no_offenses(<<~RUBY) + class Foo < Formula + desc "foo" + url "https://files.pythonhosted.org/packages/a0/b1/a01b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f/foo-0.1.tar.gz" + end + RUBY + end + end +end diff --git a/Library/Homebrew/test/rubocops/urls_spec.rb b/Library/Homebrew/test/rubocops/urls_spec.rb index 0e5dc29a10..77b221bd0e 100644 --- a/Library/Homebrew/test/rubocops/urls_spec.rb +++ b/Library/Homebrew/test/rubocops/urls_spec.rb @@ -240,238 +240,3 @@ describe RuboCop::Cop::FormulaAudit::Urls do end end end - -describe RuboCop::Cop::FormulaAudit::PyPiUrls do - subject(:cop) { described_class.new } - - context "when a pypi URL is used" do - it "reports an offense for pypi.python.org urls" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url "https://pypi.python.org/packages/source/foo/foo-0.1.tar.gz" - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use the `Source` url found on PyPI downloads page (`https://pypi.org/project/foo/#files`) - end - RUBY - end - - it "reports an offense for short file.pythonhosted.org urls" do - expect_offense(<<~RUBY) - class Foo < Formula - desc "foo" - url "https://files.pythonhosted.org/packages/source/f/foo/foo-0.1.tar.gz" - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use the `Source` url found on PyPI downloads page (`https://pypi.org/project/foo/#files`) - end - RUBY - end - - it "reports no offenses for long file.pythonhosted.org urls" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - desc "foo" - url "https://files.pythonhosted.org/packages/a0/b1/a01b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f/foo-0.1.tar.gz" - end - RUBY - end - end -end - -describe RuboCop::Cop::FormulaAudit::GitUrls do - subject(:cop) { described_class.new } - - context "when a git URL is used" do - it "reports no offenses with a non-git URL" do - expect_no_offenses(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url "https://foo.com" - end - RUBY - end - - it "reports no offenses with both a tag and a revision" do - expect_no_offenses(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url "https://github.com/foo/bar.git", - tag: "v1.0.0", - revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - end - RUBY - end - - it "reports no offenses with both a tag, revision and `shallow` before" do - expect_no_offenses(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url "https://github.com/foo/bar.git", - shallow: false, - tag: "v1.0.0", - revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - end - RUBY - end - - it "reports no offenses with both a tag, revision and `shallow` after" do - expect_no_offenses(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url "https://github.com/foo/bar.git", - tag: "v1.0.0", - revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - shallow: false - end - RUBY - end - - it "reports an offense with no `revision`" do - expect_offense(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url "https://github.com/foo/bar.git", - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Formulae in homebrew/core should specify a revision for git URLs - tag: "v1.0.0" - end - RUBY - end - - it "reports an offense with no `revision` and `shallow`" do - expect_offense(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url "https://github.com/foo/bar.git", - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Formulae in homebrew/core should specify a revision for git URLs - shallow: false, - tag: "v1.0.0" - end - RUBY - end - - it "reports no offenses with no `tag`" do - expect_no_offenses(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url "https://github.com/foo/bar.git", - revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - end - RUBY - end - - it "reports no offenses with no `tag` and `shallow`" do - expect_no_offenses(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url "https://github.com/foo/bar.git", - revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - shallow: false - end - RUBY - end - - it "reports no offenses with missing arguments in `head`" do - expect_no_offenses(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url "https://foo.com" - head do - url "https://github.com/foo/bar.git" - end - end - RUBY - end - - it "reports no offenses for non-core taps" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - desc "foo" - url "https://github.com/foo/bar.git" - end - RUBY - end - end -end - -describe RuboCop::Cop::FormulaAuditStrict::GitUrls do - subject(:cop) { described_class.new } - - context "when a git URL is used" do - it "reports no offenses with both a tag and a revision" do - expect_no_offenses(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url "https://github.com/foo/bar.git", - tag: "v1.0.0", - revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - end - RUBY - end - - it "reports no offenses with both a tag, revision and `shallow` before" do - expect_no_offenses(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url "https://github.com/foo/bar.git", - shallow: false, - tag: "v1.0.0", - revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - end - RUBY - end - - it "reports no offenses with both a tag, revision and `shallow` after" do - expect_no_offenses(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url "https://github.com/foo/bar.git", - tag: "v1.0.0", - revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - shallow: false - end - RUBY - end - - it "reports an offense with no `tag`" do - expect_offense(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url "https://github.com/foo/bar.git", - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Formulae in homebrew/core should specify a tag for git URLs - revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - end - RUBY - end - - it "reports an offense with no `tag` and `shallow`" do - expect_offense(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url "https://github.com/foo/bar.git", - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Formulae in homebrew/core should specify a tag for git URLs - revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - shallow: false - end - RUBY - end - - it "reports no offenses with missing arguments in `head`" do - expect_no_offenses(<<~RUBY, "/homebrew-core/") - class Foo < Formula - desc "foo" - url "https://foo.com" - head do - url "https://github.com/foo/bar.git" - end - end - RUBY - end - - it "reports no offenses for non-core taps" do - expect_no_offenses(<<~RUBY) - class Foo < Formula - desc "foo" - url "https://github.com/foo/bar.git" - end - RUBY - end - end -end diff --git a/Library/Homebrew/test/software_spec/bottle_spec.rb b/Library/Homebrew/test/software_spec/bottle_spec.rb new file mode 100644 index 0000000000..360f159bed --- /dev/null +++ b/Library/Homebrew/test/software_spec/bottle_spec.rb @@ -0,0 +1,51 @@ +# typed: false +# frozen_string_literal: true + +require "software_spec" + +describe BottleSpecification do + subject(:bottle_spec) { described_class.new } + + describe "#sha256" do + it "works without cellar" do + checksums = { + snow_leopard_32: "deadbeef" * 8, + snow_leopard: "faceb00c" * 8, + lion: "baadf00d" * 8, + mountain_lion: "8badf00d" * 8, + } + + checksums.each_pair do |cat, digest| + bottle_spec.sha256(digest => cat) + checksum, = bottle_spec.checksum_for(cat) + expect(Checksum.new(digest)).to eq(checksum) + end + end + + it "works with cellar" do + checksums = [ + { cellar: :any_skip_relocation, tag: :snow_leopard_32, digest: "deadbeef" * 8 }, + { cellar: :any, tag: :snow_leopard, digest: "faceb00c" * 8 }, + { cellar: "/usr/local/Cellar", tag: :lion, digest: "baadf00d" * 8 }, + { cellar: Homebrew::DEFAULT_CELLAR, tag: :mountain_lion, digest: "8badf00d" * 8 }, + ] + + checksums.each do |checksum| + bottle_spec.sha256(checksum[:tag] => checksum[:digest], cellar: checksum[:cellar]) + digest, tag, cellar = bottle_spec.checksum_for(checksum[:tag]) + expect(Checksum.new(checksum[:digest])).to eq(digest) + expect(checksum[:tag]).to eq(tag) + checksum[:cellar] ||= Homebrew::DEFAULT_CELLAR + expect(checksum[:cellar]).to eq(cellar) + end + end + end + + %w[root_url prefix cellar rebuild].each do |method| + specify "##{method}" do + object = Object.new + bottle_spec.public_send(method, object) + expect(bottle_spec.public_send(method)).to eq(object) + end + end +end diff --git a/Library/Homebrew/test/software_spec/head_spec.rb b/Library/Homebrew/test/software_spec/head_spec.rb new file mode 100644 index 0000000000..89ee2ca6b9 --- /dev/null +++ b/Library/Homebrew/test/software_spec/head_spec.rb @@ -0,0 +1,16 @@ +# typed: false +# frozen_string_literal: true + +require "software_spec" + +describe HeadSoftwareSpec do + subject(:head_spec) { described_class.new } + + specify "#version" do + expect(head_spec.version).to eq(Version.create("HEAD")) + end + + specify "#verify_download_integrity" do + expect(head_spec.verify_download_integrity(Object.new)).to be nil + end +end diff --git a/Library/Homebrew/test/software_spec_spec.rb b/Library/Homebrew/test/software_spec_spec.rb index 85ff8e1b10..24b50bb8da 100644 --- a/Library/Homebrew/test/software_spec_spec.rb +++ b/Library/Homebrew/test/software_spec_spec.rb @@ -173,62 +173,3 @@ describe SoftwareSpec do end end end - -describe HeadSoftwareSpec do - subject(:head_spec) { described_class.new } - - specify "#version" do - expect(head_spec.version).to eq(Version.create("HEAD")) - end - - specify "#verify_download_integrity" do - expect(head_spec.verify_download_integrity(Object.new)).to be nil - end -end - -describe BottleSpecification do - subject(:bottle_spec) { described_class.new } - - describe "#sha256" do - it "works without cellar" do - checksums = { - snow_leopard_32: "deadbeef" * 8, - snow_leopard: "faceb00c" * 8, - lion: "baadf00d" * 8, - mountain_lion: "8badf00d" * 8, - } - - checksums.each_pair do |cat, digest| - bottle_spec.sha256(digest => cat) - checksum, = bottle_spec.checksum_for(cat) - expect(Checksum.new(digest)).to eq(checksum) - end - end - - it "works with cellar" do - checksums = [ - { cellar: :any_skip_relocation, tag: :snow_leopard_32, digest: "deadbeef" * 8 }, - { cellar: :any, tag: :snow_leopard, digest: "faceb00c" * 8 }, - { cellar: "/usr/local/Cellar", tag: :lion, digest: "baadf00d" * 8 }, - { cellar: Homebrew::DEFAULT_CELLAR, tag: :mountain_lion, digest: "8badf00d" * 8 }, - ] - - checksums.each do |checksum| - bottle_spec.sha256(checksum[:tag] => checksum[:digest], cellar: checksum[:cellar]) - digest, tag, cellar = bottle_spec.checksum_for(checksum[:tag]) - expect(Checksum.new(checksum[:digest])).to eq(digest) - expect(checksum[:tag]).to eq(tag) - checksum[:cellar] ||= Homebrew::DEFAULT_CELLAR - expect(checksum[:cellar]).to eq(cellar) - end - end - end - - %w[root_url prefix cellar rebuild].each do |method| - specify "##{method}" do - object = Object.new - bottle_spec.public_send(method, object) - expect(bottle_spec.public_send(method)).to eq(object) - end - end -end diff --git a/Library/Homebrew/test/tap_spec.rb b/Library/Homebrew/test/tap_spec.rb index 06288eb633..03bf027c9f 100644 --- a/Library/Homebrew/test/tap_spec.rb +++ b/Library/Homebrew/test/tap_spec.rb @@ -452,64 +452,64 @@ describe Tap do end end end -end -describe CoreTap do - subject(:core_tap) { described_class.new } + describe CoreTap do + subject(:core_tap) { described_class.new } - specify "attributes" do - expect(core_tap.user).to eq("Homebrew") - expect(core_tap.repo).to eq("core") - expect(core_tap.name).to eq("homebrew/core") - expect(core_tap.command_files).to eq([]) - expect(core_tap).to be_installed - expect(core_tap).not_to be_pinned - expect(core_tap).to be_official - expect(core_tap).to be_a_core_tap - end - - specify "forbidden operations" do - expect { core_tap.uninstall }.to raise_error(RuntimeError) - expect { core_tap.pin }.to raise_error(RuntimeError) - expect { core_tap.unpin }.to raise_error(RuntimeError) - end - - specify "files" do - path = Tap::TAP_DIRECTORY/"homebrew/homebrew-core" - formula_file = core_tap.formula_dir/"foo.rb" - formula_file.write <<~RUBY - class Foo < Formula - url "https://brew.sh/foo-1.0.tar.gz" - end - RUBY - - formula_list_file_json = '{ "foo": "foo1", "bar": "bar1" }' - formula_list_file_contents = { "foo" => "foo1", "bar" => "bar1" } - %w[ - formula_renames.json - tap_migrations.json - audit_exceptions/formula_list.json - style_exceptions/formula_hash.json - pypi_formula_mappings.json - ].each do |file| - (path/file).write formula_list_file_json + specify "attributes" do + expect(core_tap.user).to eq("Homebrew") + expect(core_tap.repo).to eq("core") + expect(core_tap.name).to eq("homebrew/core") + expect(core_tap.command_files).to eq([]) + expect(core_tap).to be_installed + expect(core_tap).not_to be_pinned + expect(core_tap).to be_official + expect(core_tap).to be_a_core_tap end - alias_file = core_tap.alias_dir/"bar" - alias_file.parent.mkpath - ln_s formula_file, alias_file + specify "forbidden operations" do + expect { core_tap.uninstall }.to raise_error(RuntimeError) + expect { core_tap.pin }.to raise_error(RuntimeError) + expect { core_tap.unpin }.to raise_error(RuntimeError) + end - expect(core_tap.formula_files).to eq([formula_file]) - expect(core_tap.formula_names).to eq(["foo"]) - expect(core_tap.alias_files).to eq([alias_file]) - expect(core_tap.aliases).to eq(["bar"]) - expect(core_tap.alias_table).to eq("bar" => "foo") - expect(core_tap.alias_reverse_table).to eq("foo" => ["bar"]) + specify "files" do + path = Tap::TAP_DIRECTORY/"homebrew/homebrew-core" + formula_file = core_tap.formula_dir/"foo.rb" + formula_file.write <<~RUBY + class Foo < Formula + url "https://brew.sh/foo-1.0.tar.gz" + end + RUBY - expect(core_tap.formula_renames).to eq formula_list_file_contents - expect(core_tap.tap_migrations).to eq formula_list_file_contents - expect(core_tap.audit_exceptions).to eq({ formula_list: formula_list_file_contents }) - expect(core_tap.style_exceptions).to eq({ formula_hash: formula_list_file_contents }) - expect(core_tap.pypi_formula_mappings).to eq formula_list_file_contents + formula_list_file_json = '{ "foo": "foo1", "bar": "bar1" }' + formula_list_file_contents = { "foo" => "foo1", "bar" => "bar1" } + %w[ + formula_renames.json + tap_migrations.json + audit_exceptions/formula_list.json + style_exceptions/formula_hash.json + pypi_formula_mappings.json + ].each do |file| + (path/file).write formula_list_file_json + end + + alias_file = core_tap.alias_dir/"bar" + alias_file.parent.mkpath + ln_s formula_file, alias_file + + expect(core_tap.formula_files).to eq([formula_file]) + expect(core_tap.formula_names).to eq(["foo"]) + expect(core_tap.alias_files).to eq([alias_file]) + expect(core_tap.aliases).to eq(["bar"]) + expect(core_tap.alias_table).to eq("bar" => "foo") + expect(core_tap.alias_reverse_table).to eq("foo" => ["bar"]) + + expect(core_tap.formula_renames).to eq formula_list_file_contents + expect(core_tap.tap_migrations).to eq formula_list_file_contents + expect(core_tap.audit_exceptions).to eq({ formula_list: formula_list_file_contents }) + expect(core_tap.style_exceptions).to eq({ formula_hash: formula_list_file_contents }) + expect(core_tap.pypi_formula_mappings).to eq formula_list_file_contents + end end end diff --git a/Library/Homebrew/test/version_spec.rb b/Library/Homebrew/test/version_spec.rb index 2df4494387..2a0cf93629 100644 --- a/Library/Homebrew/test/version_spec.rb +++ b/Library/Homebrew/test/version_spec.rb @@ -7,70 +7,68 @@ describe Version do specify ".formula_optionally_versioned_regex" do expect(described_class.formula_optionally_versioned_regex("foo")).to match("foo@1.2") end -end -describe Version::Token do - specify "#inspect" do - expect(described_class.new("foo").inspect).to eq('#') - end + describe Version::Token do + specify "#inspect" do + expect(described_class.new("foo").inspect).to eq('#') + end - specify "#to_s" do - expect(described_class.new("foo").to_s).to eq("foo") - end + specify "#to_s" do + expect(described_class.new("foo").to_s).to eq("foo") + end - it "can be compared against nil" do - expect(described_class.create("2")).to be > nil - expect(described_class.create("p194")).to be > nil - end + it "can be compared against nil" do + expect(described_class.create("2")).to be > nil + expect(described_class.create("p194")).to be > nil + end - it "can be compared against Version::NULL_TOKEN" do - expect(described_class.create("2")).to be > Version::NULL_TOKEN - expect(described_class.create("p194")).to be > Version::NULL_TOKEN - end + it "can be compared against Version::NULL_TOKEN" do + expect(described_class.create("2")).to be > Version::NULL_TOKEN + expect(described_class.create("p194")).to be > Version::NULL_TOKEN + end - it "can be compared against strings" do - expect(described_class.create("2")).to be == "2" - expect(described_class.create("p194")).to be == "p194" - expect(described_class.create("1")).to be == 1 - end + it "can be compared against strings" do + expect(described_class.create("2")).to be == "2" + expect(described_class.create("p194")).to be == "p194" + expect(described_class.create("1")).to be == 1 + end - specify "comparison returns nil for non-token" do - v = described_class.create("1") - expect(v <=> Object.new).to be nil - expect { v > Object.new }.to raise_error(ArgumentError) - end + specify "comparison returns nil for non-token" do + v = described_class.create("1") + expect(v <=> Object.new).to be nil + expect { v > Object.new }.to raise_error(ArgumentError) + end - describe "#to_str" do - it "implicitly converts token to string" do - expect(String.try_convert(described_class.new("foo"))).not_to be nil + describe "#to_str" do + it "implicitly converts token to string" do + expect(String.try_convert(described_class.new("foo"))).not_to be nil + end end end -end -describe Version::NULL do - it "is always smaller" do - expect(described_class).to be < Version.create("1") + describe Version::NULL do + it "is always smaller" do + expect(described_class).to be < Version.create("1") + end + + it "is never greater" do + expect(described_class).not_to be > Version.create("0") + end + + it "isn't equal to itself" do + expect(described_class).not_to eql(described_class) + end + + it "creates an empty string" do + expect(described_class.to_s).to eq("") + end + + it "produces NaN as a Float" do + # Float::NAN is not equal to itself so compare object IDs + expect(described_class.to_f.object_id).to eql(Float::NAN.object_id) + end end - it "is never greater" do - expect(described_class).not_to be > Version.create("0") - end - - it "isn't equal to itself" do - expect(described_class).not_to eql(described_class) - end - - it "creates an empty string" do - expect(described_class.to_s).to eq("") - end - - it "produces NaN as a Float" do - # Float::NAN is not equal to itself so compare object IDs - expect(described_class.to_f.object_id).to eql(Float::NAN.object_id) - end -end - -describe Version do describe "::NULL_TOKEN" do subject(:null_version) { described_class::NULL_TOKEN } @@ -853,12 +851,12 @@ describe Version do .to be_detected_from("https://github.com/foo/bar.git", tag: "v1.2.3-beta1") end end -end -describe Pathname do - specify "#version" do - d = HOMEBREW_CELLAR/"foo-0.1.9" - d.mkpath - expect(d.version).to eq(Version.create("0.1.9")) + describe Pathname do + specify "#version" do + d = HOMEBREW_CELLAR/"foo-0.1.9" + d.mkpath + expect(d.version).to eq(Version.create("0.1.9")) + end end end