# frozen_string_literal: true require "tab" require "formula" describe Tab do alias_matcher :be_built_with, :be_with matcher :be_poured_from_bottle do match do |actual| actual.poured_from_bottle == true end end matcher :be_built_as_bottle do match do |actual| actual.built_as_bottle == true end end subject(:tab) do described_class.new( "homebrew_version" => HOMEBREW_VERSION, "used_options" => used_options.as_flags, "unused_options" => unused_options.as_flags, "built_as_bottle" => false, "poured_from_bottle" => true, "changed_files" => [], "time" => time, "source_modified_time" => 0, "compiler" => "clang", "stdlib" => "libcxx", "runtime_dependencies" => [], "source" => { "tap" => CoreTap.instance.to_s, "path" => CoreTap.instance.path.to_s, "spec" => "stable", "versions" => { "stable" => "0.10", "head" => "HEAD-1111111", }, }, "arch" => Hardware::CPU.arch, "built_on" => DevelopmentTools.build_system_info, ) end let(:time) { Time.now.to_i } let(:unused_options) { Options.create(%w[--with-baz --without-qux]) } let(:used_options) { Options.create(%w[--with-foo --without-bar]) } let(:f) { formula { url "foo-1.0" } } let(:f_tab_path) { f.prefix/"INSTALL_RECEIPT.json" } let(:f_tab_content) { (TEST_FIXTURE_DIR/"receipt.json").read } specify "defaults" do # < 1.1.7 runtime_dependencies were wrong so are ignored stub_const("HOMEBREW_VERSION", "1.1.7") tab = described_class.empty expect(tab.homebrew_version).to eq(HOMEBREW_VERSION) expect(tab.unused_options).to be_empty expect(tab.used_options).to be_empty expect(tab.changed_files).to be_nil expect(tab).not_to be_built_as_bottle expect(tab).not_to be_poured_from_bottle expect(tab).to be_stable expect(tab).not_to be_head expect(tab.tap).to be_nil expect(tab.time).to be_nil expect(tab.runtime_dependencies).to be_nil expect(tab.stable_version).to be_nil expect(tab.head_version).to be_nil expect(tab.cxxstdlib.compiler).to eq(DevelopmentTools.default_compiler) expect(tab.cxxstdlib.type).to be_nil expect(tab.source["path"]).to be_nil end specify "#include?" do expect(tab).to include("with-foo") expect(tab).to include("without-bar") end specify "#with?" do expect(tab).to be_built_with("foo") expect(tab).to be_built_with("qux") expect(tab).not_to be_built_with("bar") expect(tab).not_to be_built_with("baz") end specify "#parsed_homebrew_version" do tab = described_class.new expect(tab.parsed_homebrew_version).to be Version::NULL tab = described_class.new(homebrew_version: "1.2.3") expect(tab.parsed_homebrew_version).to eq("1.2.3") expect(tab.parsed_homebrew_version).to be < "1.2.3-1-g12789abdf" expect(tab.parsed_homebrew_version).to be_a(Version) tab.homebrew_version = "1.2.4-567-g12789abdf" expect(tab.parsed_homebrew_version).to be > "1.2.4" expect(tab.parsed_homebrew_version).to be > "1.2.4-566-g21789abdf" expect(tab.parsed_homebrew_version).to be < "1.2.4-568-g01789abdf" tab = described_class.new(homebrew_version: "2.0.0-134-gabcdefabc-dirty") expect(tab.parsed_homebrew_version).to be > "2.0.0" expect(tab.parsed_homebrew_version).to be > "2.0.0-133-g21789abdf" expect(tab.parsed_homebrew_version).to be < "2.0.0-135-g01789abdf" end specify "#runtime_dependencies" do tab = described_class.new expect(tab.runtime_dependencies).to be_nil tab.homebrew_version = "1.1.6" expect(tab.runtime_dependencies).to be_nil tab.runtime_dependencies = [] expect(tab.runtime_dependencies).not_to be_nil tab.homebrew_version = "1.1.5" expect(tab.runtime_dependencies).to be_nil tab.homebrew_version = "1.1.7" expect(tab.runtime_dependencies).not_to be_nil tab.homebrew_version = "1.1.10" expect(tab.runtime_dependencies).not_to be_nil tab.runtime_dependencies = [{ "full_name" => "foo", "version" => "1.0" }] expect(tab.runtime_dependencies).not_to be_nil end specify "::runtime_deps_hash" do runtime_deps = [Dependency.new("foo")] foo = formula("foo") { url "foo-1.0" } stub_formula_loader foo runtime_deps_hash = described_class.runtime_deps_hash(foo, runtime_deps) tab = described_class.new tab.homebrew_version = "1.1.6" tab.runtime_dependencies = runtime_deps_hash expect(tab.runtime_dependencies).to eql( [{ "full_name" => "foo", "version" => "1.0", "revision" => 0, "pkg_version" => "1.0", "declared_directly" => false }], ) end specify "#cxxstdlib" do expect(tab.cxxstdlib.compiler).to eq(:clang) expect(tab.cxxstdlib.type).to eq(:libcxx) end specify "other attributes" do expect(tab.tap.name).to eq("homebrew/core") expect(tab.time).to eq(time) expect(tab).not_to be_built_as_bottle expect(tab).to be_poured_from_bottle end describe "::from_file" do it "parses a Tab from a file" do path = Pathname.new("#{TEST_FIXTURE_DIR}/receipt.json") tab = described_class.from_file(path) source_path = "/usr/local/Library/Taps/homebrew/homebrew-core/Formula/foo.rb" runtime_dependencies = [{ "full_name" => "foo", "version" => "1.0" }] changed_files = %w[INSTALL_RECEIPT.json bin/foo] expect(tab.used_options.sort).to eq(used_options.sort) expect(tab.unused_options.sort).to eq(unused_options.sort) expect(tab.changed_files).to eq(changed_files) expect(tab).not_to be_built_as_bottle expect(tab).to be_poured_from_bottle expect(tab).to be_stable expect(tab).not_to be_head expect(tab.tap.name).to eq("homebrew/core") expect(tab.spec).to eq(:stable) expect(tab.time).to eq(Time.at(1_403_827_774).to_i) expect(tab.cxxstdlib.compiler).to eq(:clang) expect(tab.cxxstdlib.type).to eq(:libcxx) expect(tab.runtime_dependencies).to eq(runtime_dependencies) expect(tab.stable_version.to_s).to eq("2.14") expect(tab.head_version.to_s).to eq("HEAD-0000000") expect(tab.source["path"]).to eq(source_path) end end describe "::from_file_content" do it "parses a Tab from a file" do path = Pathname.new("#{TEST_FIXTURE_DIR}/receipt.json") tab = described_class.from_file_content(path.read, path) source_path = "/usr/local/Library/Taps/homebrew/homebrew-core/Formula/foo.rb" runtime_dependencies = [{ "full_name" => "foo", "version" => "1.0" }] changed_files = %w[INSTALL_RECEIPT.json bin/foo] expect(tab.used_options.sort).to eq(used_options.sort) expect(tab.unused_options.sort).to eq(unused_options.sort) expect(tab.changed_files).to eq(changed_files) expect(tab).not_to be_built_as_bottle expect(tab).to be_poured_from_bottle expect(tab).to be_stable expect(tab).not_to be_head expect(tab.tap.name).to eq("homebrew/core") expect(tab.spec).to eq(:stable) expect(tab.time).to eq(Time.at(1_403_827_774).to_i) expect(tab.cxxstdlib.compiler).to eq(:clang) expect(tab.cxxstdlib.type).to eq(:libcxx) expect(tab.runtime_dependencies).to eq(runtime_dependencies) expect(tab.stable_version.to_s).to eq("2.14") expect(tab.head_version.to_s).to eq("HEAD-0000000") expect(tab.source["path"]).to eq(source_path) end it "can parse an old Tab file" do path = Pathname.new("#{TEST_FIXTURE_DIR}/receipt_old.json") tab = described_class.from_file_content(path.read, path) expect(tab.used_options.sort).to eq(used_options.sort) expect(tab.unused_options.sort).to eq(unused_options.sort) expect(tab).not_to be_built_as_bottle expect(tab).to be_poured_from_bottle expect(tab).to be_stable expect(tab).not_to be_head expect(tab.tap.name).to eq("homebrew/core") expect(tab.spec).to eq(:stable) expect(tab.time).to eq(Time.at(1_403_827_774).to_i) expect(tab.cxxstdlib.compiler).to eq(:clang) expect(tab.cxxstdlib.type).to eq(:libcxx) expect(tab.runtime_dependencies).to be_nil end it "raises a parse exception message including the Tab filename" do expect { described_class.from_file_content("''", "receipt.json") }.to raise_error( JSON::ParserError, /receipt.json:/, ) end end describe "::create" do it "creates a Tab" do # < 1.1.7 runtime dependencies were wrong so are ignored stub_const("HOMEBREW_VERSION", "1.1.7") # don't try to load gcc/glibc allow(DevelopmentTools).to receive_messages(needs_libc_formula?: false, needs_compiler_formula?: false) f = formula do url "foo-1.0" depends_on "bar" depends_on "user/repo/from_tap" depends_on "baz" => :build end tap = Tap.new("user", "repo") from_tap = formula("from_tap", path: tap.path/"Formula/from_tap.rb") do url "from_tap-1.0" revision 1 end stub_formula_loader from_tap stub_formula_loader formula("bar") { url "bar-2.0" } stub_formula_loader formula("baz") { url "baz-3.0" } compiler = DevelopmentTools.default_compiler stdlib = :libcxx tab = described_class.create(f, compiler, stdlib) runtime_dependencies = [ { "full_name" => "bar", "version" => "2.0", "revision" => 0, "pkg_version" => "2.0", "declared_directly" => true }, { "full_name" => "user/repo/from_tap", "version" => "1.0", "revision" => 1, "pkg_version" => "1.0_1", "declared_directly" => true }, ] expect(tab.runtime_dependencies).to eq(runtime_dependencies) expect(tab.source["path"]).to eq(f.path.to_s) end it "can create a Tab from an alias" do alias_path = CoreTap.instance.alias_dir/"bar" f = formula(alias_path: alias_path) { url "foo-1.0" } compiler = DevelopmentTools.default_compiler stdlib = :libcxx tab = described_class.create(f, compiler, stdlib) expect(tab.source["path"]).to eq(f.alias_path.to_s) end end describe "::for_keg" do subject(:tab_for_keg) { described_class.for_keg(f.prefix) } it "creates a Tab for a given Keg" do f.prefix.mkpath f_tab_path.write f_tab_content expect(tab_for_keg.tabfile).to eq(f_tab_path) end it "can create a Tab for a non-existent Keg" do f.prefix.mkpath expect(tab_for_keg.tabfile).to eq(f_tab_path) end end describe "::for_formula" do it "creates a Tab for a given Formula" do tab = described_class.for_formula(f) expect(tab.source["path"]).to eq(f.path.to_s) end it "can create a Tab for for a Formula from an alias" do alias_path = CoreTap.instance.alias_dir/"bar" f = formula(alias_path: alias_path) { url "foo-1.0" } tab = described_class.for_formula(f) expect(tab.source["path"]).to eq(alias_path.to_s) end it "creates a Tab for a given Formula with existing Tab" do f.prefix.mkpath f_tab_path.write f_tab_content tab = described_class.for_formula(f) expect(tab.tabfile).to eq(f_tab_path) end it "can create a Tab for a non-existent Formula" do f.prefix.mkpath tab = described_class.for_formula(f) expect(tab.tabfile).to be_nil end it "can create a Tab for a Formula with multiple Kegs" do f.prefix.mkpath f_tab_path.write f_tab_content f2 = formula { url "foo-2.0" } f2.prefix.mkpath expect(f2.rack).to eq(f.rack) expect(f.installed_prefixes.length).to eq(2) tab = described_class.for_formula(f) expect(tab.tabfile).to eq(f_tab_path) end it "can create a Tab for a Formula with an outdated Kegs" do f.prefix.mkpath f_tab_path.write f_tab_content f2 = formula { url "foo-2.0" } expect(f2.rack).to eq(f.rack) expect(f.installed_prefixes.length).to eq(1) tab = described_class.for_formula(f) expect(tab.tabfile).to eq(f_tab_path) end end specify "#to_json" do json_tab = described_class.new(JSON.parse(tab.to_json)) expect(json_tab.homebrew_version).to eq(tab.homebrew_version) expect(json_tab.used_options.sort).to eq(tab.used_options.sort) expect(json_tab.unused_options.sort).to eq(tab.unused_options.sort) expect(json_tab.built_as_bottle).to eq(tab.built_as_bottle) expect(json_tab.poured_from_bottle).to eq(tab.poured_from_bottle) expect(json_tab.changed_files).to eq(tab.changed_files) expect(json_tab.tap).to eq(tab.tap) expect(json_tab.spec).to eq(tab.spec) expect(json_tab.time).to eq(tab.time) expect(json_tab.compiler).to eq(tab.compiler) expect(json_tab.stdlib).to eq(tab.stdlib) expect(json_tab.runtime_dependencies).to eq(tab.runtime_dependencies) expect(json_tab.stable_version).to eq(tab.stable_version) expect(json_tab.head_version).to eq(tab.head_version) expect(json_tab.source["path"]).to eq(tab.source["path"]) expect(json_tab.arch).to eq(tab.arch.to_s) expect(json_tab.built_on["os"]).to eq(tab.built_on["os"]) end specify "#to_bottle_hash" do json_tab = described_class.new(JSON.parse(tab.to_bottle_hash.to_json)) expect(json_tab.homebrew_version).to eq(tab.homebrew_version) expect(json_tab.changed_files).to eq(tab.changed_files) expect(json_tab.source_modified_time).to eq(tab.source_modified_time) expect(json_tab.stdlib).to eq(tab.stdlib) expect(json_tab.compiler).to eq(tab.compiler) expect(json_tab.runtime_dependencies).to eq(tab.runtime_dependencies) expect(json_tab.arch).to eq(tab.arch.to_s) expect(json_tab.built_on["os"]).to eq(tab.built_on["os"]) end specify "::remap_deprecated_options" do deprecated_options = [DeprecatedOption.new("with-foo", "with-foo-new")] remapped_options = described_class.remap_deprecated_options(deprecated_options, tab.used_options) expect(remapped_options).to include(Option.new("without-bar")) expect(remapped_options).to include(Option.new("with-foo-new")) end end