mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00
Merge pull request #20108 from Homebrew/macos_version-upgrade-type-sigil
MacOSVersion: enable strong typing, expand tests
This commit is contained in:
commit
ad28cc7cf5
@ -52,4 +52,4 @@ FORMULA_COMPONENT_PRECEDENCE_LIST = T.let([
|
|||||||
[{ name: :caveats, type: :method_definition }],
|
[{ name: :caveats, type: :method_definition }],
|
||||||
[{ name: :plist_options, type: :method_call }, { name: :plist, type: :method_definition }],
|
[{ name: :plist_options, type: :method_call }, { name: :plist, type: :method_definition }],
|
||||||
[{ name: :test, type: :block_call }],
|
[{ name: :test, type: :block_call }],
|
||||||
].freeze, T::Array[[{ name: Symbol, type: Symbol }]])
|
].freeze, T::Array[T::Array[{ name: Symbol, type: Symbol }]])
|
||||||
|
@ -486,7 +486,7 @@ module Cask
|
|||||||
def add_implicit_macos_dependency
|
def add_implicit_macos_dependency
|
||||||
return if (cask_depends_on = @depends_on).present? && cask_depends_on.macos.present?
|
return if (cask_depends_on = @depends_on).present? && cask_depends_on.macos.present?
|
||||||
|
|
||||||
depends_on macos: ">= :#{MacOSVersion::SYMBOLS.key MacOSVersion::SYMBOLS.values.min}"
|
depends_on macos: ">= #{MacOSVersion.new(HOMEBREW_MACOS_OLDEST_ALLOWED).to_sym.inspect}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Declare conflicts that keep a cask from installing or working correctly.
|
# Declare conflicts that keep a cask from installing or working correctly.
|
||||||
|
@ -52,16 +52,17 @@ module Cask
|
|||||||
raise "Only a single 'depends_on macos' is allowed." if defined?(@macos)
|
raise "Only a single 'depends_on macos' is allowed." if defined?(@macos)
|
||||||
|
|
||||||
# workaround for https://github.com/sorbet/sorbet/issues/6860
|
# workaround for https://github.com/sorbet/sorbet/issues/6860
|
||||||
first_arg = args.first&.to_s
|
first_arg = args.first
|
||||||
|
first_arg_s = first_arg&.to_s
|
||||||
|
|
||||||
begin
|
begin
|
||||||
@macos = if args.count > 1
|
@macos = if args.count > 1
|
||||||
MacOSRequirement.new([args], comparator: "==")
|
MacOSRequirement.new([args], comparator: "==")
|
||||||
elsif MacOSVersion::SYMBOLS.key?(args.first)
|
elsif first_arg.is_a?(Symbol) && MacOSVersion::SYMBOLS.key?(first_arg)
|
||||||
MacOSRequirement.new([args.first], comparator: "==")
|
MacOSRequirement.new([args.first], comparator: "==")
|
||||||
elsif (md = /^\s*(?<comparator><|>|[=<>]=)\s*:(?<version>\S+)\s*$/.match(first_arg))
|
elsif (md = /^\s*(?<comparator><|>|[=<>]=)\s*:(?<version>\S+)\s*$/.match(first_arg_s))
|
||||||
MacOSRequirement.new([T.must(md[:version]).to_sym], comparator: md[:comparator])
|
MacOSRequirement.new([T.must(md[:version]).to_sym], comparator: md[:comparator])
|
||||||
elsif (md = /^\s*(?<comparator><|>|[=<>]=)\s*(?<version>\S+)\s*$/.match(first_arg))
|
elsif (md = /^\s*(?<comparator><|>|[=<>]=)\s*(?<version>\S+)\s*$/.match(first_arg_s))
|
||||||
MacOSRequirement.new([md[:version]], comparator: md[:comparator])
|
MacOSRequirement.new([md[:version]], comparator: md[:comparator])
|
||||||
# This is not duplicate of the first case: see `args.first` and a different comparator.
|
# This is not duplicate of the first case: see `args.first` and a different comparator.
|
||||||
else # rubocop:disable Lint/DuplicateBranch
|
else # rubocop:disable Lint/DuplicateBranch
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
# typed: strong
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "version"
|
require "version"
|
||||||
@ -10,6 +10,7 @@ class MacOSVersion < Version
|
|||||||
sig { returns(T.nilable(T.any(String, Symbol))) }
|
sig { returns(T.nilable(T.any(String, Symbol))) }
|
||||||
attr_reader :version
|
attr_reader :version
|
||||||
|
|
||||||
|
sig { params(version: T.nilable(T.any(String, Symbol))).void }
|
||||||
def initialize(version)
|
def initialize(version)
|
||||||
@version = version
|
@version = version
|
||||||
super "unknown or unsupported macOS version: #{version.inspect}"
|
super "unknown or unsupported macOS version: #{version.inspect}"
|
||||||
@ -18,7 +19,7 @@ class MacOSVersion < Version
|
|||||||
|
|
||||||
# NOTE: When removing symbols here, ensure that they are added
|
# NOTE: When removing symbols here, ensure that they are added
|
||||||
# to `DEPRECATED_MACOS_VERSIONS` in `MacOSRequirement`.
|
# to `DEPRECATED_MACOS_VERSIONS` in `MacOSRequirement`.
|
||||||
SYMBOLS = {
|
SYMBOLS = T.let({
|
||||||
tahoe: "26",
|
tahoe: "26",
|
||||||
sequoia: "15",
|
sequoia: "15",
|
||||||
sonoma: "14",
|
sonoma: "14",
|
||||||
@ -30,7 +31,7 @@ class MacOSVersion < Version
|
|||||||
high_sierra: "10.13",
|
high_sierra: "10.13",
|
||||||
sierra: "10.12",
|
sierra: "10.12",
|
||||||
el_capitan: "10.11",
|
el_capitan: "10.11",
|
||||||
}.freeze
|
}.freeze, T::Hash[Symbol, String])
|
||||||
|
|
||||||
sig { params(macos_version: MacOSVersion).returns(Version) }
|
sig { params(macos_version: MacOSVersion).returns(Version) }
|
||||||
def self.kernel_major_version(macos_version)
|
def self.kernel_major_version(macos_version)
|
||||||
@ -57,7 +58,9 @@ class MacOSVersion < Version
|
|||||||
|
|
||||||
super(T.must(version))
|
super(T.must(version))
|
||||||
|
|
||||||
@comparison_cache = {}
|
@comparison_cache = T.let({}, T::Hash[T.untyped, T.nilable(Integer)])
|
||||||
|
@pretty_name = T.let(nil, T.nilable(String))
|
||||||
|
@sym = T.let(nil, T.nilable(Symbol))
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { override.params(other: T.untyped).returns(T.nilable(Integer)) }
|
sig { override.params(other: T.untyped).returns(T.nilable(Integer)) }
|
||||||
@ -95,7 +98,7 @@ class MacOSVersion < Version
|
|||||||
|
|
||||||
sig { returns(Symbol) }
|
sig { returns(Symbol) }
|
||||||
def to_sym
|
def to_sym
|
||||||
return @sym if defined?(@sym)
|
return @sym if @sym
|
||||||
|
|
||||||
sym = SYMBOLS.invert.fetch(strip_patch.to_s, :dunno)
|
sym = SYMBOLS.invert.fetch(strip_patch.to_s, :dunno)
|
||||||
|
|
||||||
@ -106,7 +109,7 @@ class MacOSVersion < Version
|
|||||||
|
|
||||||
sig { returns(String) }
|
sig { returns(String) }
|
||||||
def pretty_name
|
def pretty_name
|
||||||
return @pretty_name if defined?(@pretty_name)
|
return @pretty_name if @pretty_name
|
||||||
|
|
||||||
pretty_name = to_sym.to_s.split("_").map(&:capitalize).join(" ").freeze
|
pretty_name = to_sym.to_s.split("_").map(&:capitalize).join(" ").freeze
|
||||||
|
|
||||||
@ -154,5 +157,7 @@ class MacOSVersion < Version
|
|||||||
# Represents the absence of a version.
|
# Represents the absence of a version.
|
||||||
#
|
#
|
||||||
# NOTE: Constructor needs to called with an arbitrary macOS-like version which is then set to `nil`.
|
# NOTE: Constructor needs to called with an arbitrary macOS-like version which is then set to `nil`.
|
||||||
NULL = MacOSVersion.new("10.0").tap { |v| v.instance_variable_set(:@version, nil) }.freeze
|
NULL = T.let(MacOSVersion.new("10.0").tap do |v|
|
||||||
|
T.let(v, MacOSVersion).instance_variable_set(:@version, nil)
|
||||||
|
end.freeze, MacOSVersion)
|
||||||
end
|
end
|
||||||
|
@ -4,12 +4,15 @@ require "macos_version"
|
|||||||
|
|
||||||
RSpec.describe MacOSVersion do
|
RSpec.describe MacOSVersion do
|
||||||
let(:version) { described_class.new("10.14") }
|
let(:version) { described_class.new("10.14") }
|
||||||
|
let(:tahoe_major) { described_class.new("26.0") }
|
||||||
let(:big_sur_major) { described_class.new("11.0") }
|
let(:big_sur_major) { described_class.new("11.0") }
|
||||||
let(:big_sur_update) { described_class.new("11.1") }
|
let(:big_sur_update) { described_class.new("11.1") }
|
||||||
|
let(:frozen_version) { described_class.new("10.14").freeze }
|
||||||
|
|
||||||
describe ".kernel_major_version" do
|
describe "::kernel_major_version" do
|
||||||
it "returns the kernel major version" do
|
it "returns the kernel major version" do
|
||||||
expect(described_class.kernel_major_version(version)).to eq "18"
|
expect(described_class.kernel_major_version(version)).to eq "18"
|
||||||
|
expect(described_class.kernel_major_version(tahoe_major)).to eq "25"
|
||||||
expect(described_class.kernel_major_version(big_sur_major)).to eq "20"
|
expect(described_class.kernel_major_version(big_sur_major)).to eq "20"
|
||||||
expect(described_class.kernel_major_version(big_sur_update)).to eq "20"
|
expect(described_class.kernel_major_version(big_sur_update)).to eq "20"
|
||||||
end
|
end
|
||||||
@ -19,12 +22,43 @@ RSpec.describe MacOSVersion do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "::from_symbol" do
|
||||||
|
it "raises an error if the symbol is not a valid macOS version" do
|
||||||
|
expect do
|
||||||
|
described_class.from_symbol(:foo)
|
||||||
|
end.to raise_error(MacOSVersion::Error, "unknown or unsupported macOS version: :foo")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates a new version from a valid macOS version" do
|
||||||
|
symbol_version = described_class.from_symbol(:mojave)
|
||||||
|
expect(symbol_version).to eq(version)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#new" do
|
||||||
|
it "raises an error if the version is not a valid macOS version" do
|
||||||
|
expect do
|
||||||
|
described_class.new("1.2")
|
||||||
|
end.to raise_error(MacOSVersion::Error, 'unknown or unsupported macOS version: "1.2"')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates a new version from a valid macOS version" do
|
||||||
|
string_version = described_class.new("11")
|
||||||
|
expect(string_version).to eq(:big_sur)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
specify "comparison with Symbol" do
|
specify "comparison with Symbol" do
|
||||||
expect(version).to be > :high_sierra
|
expect(version).to be > :high_sierra
|
||||||
expect(version).to eq :mojave
|
expect(version).to eq :mojave
|
||||||
# We're explicitly testing the `===` operator results here.
|
# We're explicitly testing the `===` operator results here.
|
||||||
expect(version).to be === :mojave # rubocop:disable Style/CaseEquality
|
expect(version).to be === :mojave # rubocop:disable Style/CaseEquality
|
||||||
expect(version).to be < :catalina
|
expect(version).to be < :catalina
|
||||||
|
|
||||||
|
# This should work like a normal comparison but the result won't be added
|
||||||
|
# to the `@comparison_cache` hash because the object is frozen.
|
||||||
|
expect(frozen_version).to eq :mojave
|
||||||
|
expect(frozen_version.instance_variable_get(:@comparison_cache)).to eq({})
|
||||||
end
|
end
|
||||||
|
|
||||||
specify "comparison with Integer" do
|
specify "comparison with Integer" do
|
||||||
@ -64,44 +98,90 @@ RSpec.describe MacOSVersion do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#new" do
|
describe "#strip_patch" do
|
||||||
it "raises an error if the version is not a valid macOS version" do
|
let(:catalina_update) { described_class.new("10.15.1") }
|
||||||
expect do
|
|
||||||
described_class.new("1.2")
|
it "returns the version without the patch" do
|
||||||
end.to raise_error(MacOSVersion::Error, 'unknown or unsupported macOS version: "1.2"')
|
expect(big_sur_update.strip_patch).to eq(described_class.new("11"))
|
||||||
|
expect(catalina_update.strip_patch).to eq(described_class.new("10.15"))
|
||||||
end
|
end
|
||||||
|
|
||||||
it "creates a new version from a valid macOS version" do
|
it "returns self if version is null" do
|
||||||
string_version = described_class.new("11")
|
expect(described_class::NULL.strip_patch).to be described_class::NULL
|
||||||
expect(string_version).to eq(:big_sur)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#from_symbol" do
|
specify "#to_sym" do
|
||||||
it "raises an error if the symbol is not a valid macOS version" do
|
version_symbol = :mojave
|
||||||
expect do
|
|
||||||
described_class.from_symbol(:foo)
|
|
||||||
end.to raise_error(MacOSVersion::Error, "unknown or unsupported macOS version: :foo")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "creates a new version from a valid macOS version" do
|
# We call this more than once to exercise the caching logic
|
||||||
symbol_version = described_class.from_symbol(:mojave)
|
expect(version.to_sym).to eq(version_symbol)
|
||||||
expect(symbol_version).to eq(version)
|
expect(version.to_sym).to eq(version_symbol)
|
||||||
end
|
|
||||||
|
# This should work like a normal but the symbol won't be stored as the
|
||||||
|
# `@sym` instance variable because the object is frozen.
|
||||||
|
expect(frozen_version.to_sym).to eq(version_symbol)
|
||||||
|
expect(frozen_version.instance_variable_get(:@sym)).to be_nil
|
||||||
|
|
||||||
|
expect(described_class::NULL.to_sym).to eq(:dunno)
|
||||||
end
|
end
|
||||||
|
|
||||||
specify "#pretty_name" do
|
specify "#pretty_name" do
|
||||||
|
version_pretty_name = "Mojave"
|
||||||
|
|
||||||
expect(described_class.new("10.11").pretty_name).to eq("El Capitan")
|
expect(described_class.new("10.11").pretty_name).to eq("El Capitan")
|
||||||
expect(described_class.new("10.14").pretty_name).to eq("Mojave")
|
|
||||||
|
# We call this more than once to exercise the caching logic
|
||||||
|
expect(version.pretty_name).to eq(version_pretty_name)
|
||||||
|
expect(version.pretty_name).to eq(version_pretty_name)
|
||||||
|
|
||||||
|
# This should work like a normal but the computed name won't be stored as
|
||||||
|
# the `@pretty_name` instance variable because the object is frozen.
|
||||||
|
expect(frozen_version.pretty_name).to eq(version_pretty_name)
|
||||||
|
expect(frozen_version.instance_variable_get(:@pretty_name)).to be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
specify "#inspect" do
|
specify "#inspect" do
|
||||||
expect(described_class.new("11").inspect).to eq("#<MacOSVersion: \"11\">")
|
expect(described_class.new("11").inspect).to eq("#<MacOSVersion: \"11\">")
|
||||||
end
|
end
|
||||||
|
|
||||||
specify "#requires_nehalem_cpu?", :needs_macos do
|
specify "#outdated_release?" do
|
||||||
expect(Hardware::CPU).to receive(:type).at_least(:twice).and_return(:intel)
|
expect(described_class.new(described_class::SYMBOLS.values.first).outdated_release?).to be false
|
||||||
expect(described_class.new("10.14").requires_nehalem_cpu?).to be true
|
expect(described_class.new("10.0").outdated_release?).to be true
|
||||||
expect(described_class.new("10.12").requires_nehalem_cpu?).to be false
|
end
|
||||||
|
|
||||||
|
specify "#prerelease?" do
|
||||||
|
expect(described_class.new("1000").prerelease?).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "#unsupported_release?" do
|
||||||
|
expect(described_class.new("10.0").unsupported_release?).to be true
|
||||||
|
expect(described_class.new("1000").prerelease?).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#requires_nehalem_cpu?", :needs_macos do
|
||||||
|
context "when CPU is Intel" do
|
||||||
|
it "returns true if version requires a Nehalem CPU" do
|
||||||
|
allow(Hardware::CPU).to receive(:type).and_return(:intel)
|
||||||
|
expect(described_class.new("10.14").requires_nehalem_cpu?).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns false if version does not require a Nehalem CPU" do
|
||||||
|
allow(Hardware::CPU).to receive(:type).and_return(:intel)
|
||||||
|
expect(described_class.new("10.12").requires_nehalem_cpu?).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when CPU is not Intel" do
|
||||||
|
it "raises an error" do
|
||||||
|
allow(Hardware::CPU).to receive(:type).and_return(:arm)
|
||||||
|
expect { described_class.new("10.14").requires_nehalem_cpu? }
|
||||||
|
.to raise_error(ArgumentError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns false when version is null" do
|
||||||
|
expect(described_class::NULL.requires_nehalem_cpu?).to be false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user