Merge branch 'master' into mlh-outdated-packages
This commit is contained in:
commit
d6f2a1c6fa
@ -43,3 +43,5 @@ RSpec/MultipleExpectations:
|
||||
Max: 26
|
||||
RSpec/NestedGroups:
|
||||
Max: 5
|
||||
RSpec/MultipleMemoizedHelpers:
|
||||
Max: 12
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
|
||||
require "English"
|
||||
|
||||
SimpleCov.enable_for_subprocesses true
|
||||
|
||||
SimpleCov.start do
|
||||
coverage_dir File.expand_path("../test/coverage", File.realpath(__FILE__))
|
||||
root File.expand_path("..", File.realpath(__FILE__))
|
||||
@ -12,9 +14,21 @@ SimpleCov.start do
|
||||
# tests to be dropped. This causes random fluctuations in test coverage.
|
||||
merge_timeout 86400
|
||||
|
||||
at_fork do |pid|
|
||||
# This needs a unique name so it won't be ovewritten
|
||||
command_name "#{SimpleCov.command_name} (#{pid})"
|
||||
|
||||
# be quiet, the parent process will be in charge of output and checking coverage totals
|
||||
print_error_status = false
|
||||
end
|
||||
|
||||
if ENV["HOMEBREW_INTEGRATION_TEST"]
|
||||
# This needs a unique name so it won't be ovewritten
|
||||
command_name "#{ENV["HOMEBREW_INTEGRATION_TEST"]} (#{$PROCESS_ID})"
|
||||
|
||||
# be quiet, the parent process will be in charge of output and checking coverage totals
|
||||
print_error_status = false
|
||||
|
||||
at_exit do
|
||||
exit_code = $ERROR_INFO.nil? ? 0 : $ERROR_INFO.status
|
||||
$stdout.reopen("/dev/null")
|
||||
|
||||
@ -10,7 +10,7 @@ GEM
|
||||
ast (2.4.1)
|
||||
bindata (2.4.8)
|
||||
byebug (11.1.3)
|
||||
codecov (0.2.5)
|
||||
codecov (0.2.6)
|
||||
colorize
|
||||
json
|
||||
simplecov
|
||||
@ -112,11 +112,11 @@ GEM
|
||||
parser (>= 2.7.1.4)
|
||||
rubocop-performance (1.7.1)
|
||||
rubocop (>= 0.82.0)
|
||||
rubocop-rspec (1.42.0)
|
||||
rubocop (>= 0.87.0)
|
||||
rubocop-rspec (1.43.1)
|
||||
rubocop (~> 0.87)
|
||||
ruby-macho (2.2.0)
|
||||
ruby-progressbar (1.10.1)
|
||||
simplecov (0.18.5)
|
||||
simplecov (0.19.0)
|
||||
docile (~> 1.1)
|
||||
simplecov-html (~> 0.11)
|
||||
simplecov-html (0.12.2)
|
||||
|
||||
@ -6,12 +6,18 @@ case "$HOMEBREW_SYSTEM" in
|
||||
esac
|
||||
|
||||
# Force UTF-8 to avoid encoding issues for users with broken locale settings.
|
||||
if [[ "$(locale charmap 2>/dev/null)" != "UTF-8" ]]
|
||||
then
|
||||
if [[ -n "$HOMEBREW_MACOS" ]]
|
||||
then
|
||||
if [[ "$(locale charmap)" != "UTF-8" ]]
|
||||
then
|
||||
export LC_ALL="en_US.UTF-8"
|
||||
fi
|
||||
else
|
||||
if ! command -v locale >/dev/null
|
||||
then
|
||||
export LC_ALL=C
|
||||
elif [[ "$(locale charmap)" != "UTF-8" ]]
|
||||
then
|
||||
locales=$(locale -a)
|
||||
c_utf_regex='\bC\.(utf8|UTF-8)\b'
|
||||
en_us_regex='\ben_US\.(utf8|UTF-8)\b'
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Settings for the build environment.
|
||||
#
|
||||
# @api private
|
||||
class BuildEnvironment
|
||||
def initialize(*settings)
|
||||
@settings = Set.new(*settings)
|
||||
@ -23,19 +26,15 @@ class BuildEnvironment
|
||||
@settings.include? :userpaths
|
||||
end
|
||||
|
||||
# DSL for specifying build environment settings.
|
||||
module DSL
|
||||
def env(*settings)
|
||||
@env ||= BuildEnvironment.new
|
||||
@env.merge(settings)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
|
||||
def build_env_keys(env)
|
||||
%w[
|
||||
KEYS = %w[
|
||||
CC CXX LD OBJC OBJCXX
|
||||
HOMEBREW_CC HOMEBREW_CXX
|
||||
CFLAGS CXXFLAGS CPPFLAGS LDFLAGS SDKROOT MAKEFLAGS
|
||||
@ -47,11 +46,15 @@ module Homebrew
|
||||
MAKE GIT CPP
|
||||
ACLOCAL_PATH PATH CPATH
|
||||
LD_LIBRARY_PATH LD_RUN_PATH LD_PRELOAD LIBRARY_PATH
|
||||
].select { |key| env.key?(key) }
|
||||
].freeze
|
||||
private_constant :KEYS
|
||||
|
||||
def self.keys(env)
|
||||
KEYS & env.keys
|
||||
end
|
||||
|
||||
def dump_build_env(env, f = $stdout)
|
||||
keys = build_env_keys(env)
|
||||
def self.dump(env, f = $stdout)
|
||||
keys = self.keys(env)
|
||||
keys -= %w[CC CXX OBJC OBJCXX] if env["CC"] == env["HOMEBREW_CC"]
|
||||
|
||||
keys.each do |key|
|
||||
|
||||
@ -18,6 +18,10 @@ module Cask
|
||||
def to_a
|
||||
[true]
|
||||
end
|
||||
|
||||
def summarize
|
||||
"true"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -47,6 +47,16 @@ module Cask
|
||||
"dr" => "doctor",
|
||||
}.freeze
|
||||
|
||||
DEPRECATED_COMMANDS = {
|
||||
Cmd::Cache => "brew --cache --cask",
|
||||
Cmd::Doctor => "brew doctor --verbose",
|
||||
Cmd::Home => "brew home",
|
||||
Cmd::List => "brew list --cask",
|
||||
Cmd::Outdated => "brew outdated --cask",
|
||||
Cmd::Reinstall => "brew reinstall",
|
||||
Cmd::Upgrade => "brew upgrade --cask",
|
||||
}.freeze
|
||||
|
||||
def self.description
|
||||
max_command_len = Cmd.commands.map(&:length).max
|
||||
|
||||
@ -212,6 +222,11 @@ module Cask
|
||||
detect_external_command(*argv) ||
|
||||
[args.remaining.empty? ? NullCommand : UnknownSubcommand.new(args.remaining.first), argv]
|
||||
|
||||
# TODO: enable for next major/minor release
|
||||
# if (replacement = DEPRECATED_COMMANDS[command])
|
||||
# odeprecated "brew cask #{command.command_name}", replacement
|
||||
# end
|
||||
|
||||
if args.help?
|
||||
puts command.help
|
||||
else
|
||||
|
||||
@ -28,9 +28,9 @@ module Cask
|
||||
switch "--online",
|
||||
description: "Run additional, slower style checks that require a network connection"
|
||||
switch "--new-cask",
|
||||
description: "Run various additional style checks to determine if a new cask is eligible
|
||||
for Homebrew. This should be used when creating new casks and implies
|
||||
`--strict` and `--online`"
|
||||
description: "Run various additional style checks to determine if a new cask is eligible " \
|
||||
"for Homebrew. This should be used when creating new casks and implies " \
|
||||
"`--strict` and `--online`"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -8,8 +8,6 @@ module Cask
|
||||
end
|
||||
|
||||
def run
|
||||
# odeprecated "brew cask home", "brew home"
|
||||
|
||||
if casks.none?
|
||||
odebug "Opening project homepage"
|
||||
self.class.open_url "https://brew.sh/"
|
||||
|
||||
@ -6,8 +6,15 @@ require "formula"
|
||||
require "cask/cask_loader"
|
||||
require "set"
|
||||
|
||||
module Homebrew
|
||||
# Helper class for cleaning up the Homebrew cache.
|
||||
#
|
||||
# @api private
|
||||
class Cleanup
|
||||
CLEANUP_DEFAULT_DAYS = 30
|
||||
private_constant :CLEANUP_DEFAULT_DAYS
|
||||
|
||||
# `Pathname` refinement with helper functions for cleaning up files.
|
||||
module CleanupRefinement
|
||||
refine Pathname do
|
||||
def incomplete?
|
||||
@ -124,8 +131,6 @@ end
|
||||
|
||||
using CleanupRefinement
|
||||
|
||||
module Homebrew
|
||||
class Cleanup
|
||||
extend Predicable
|
||||
|
||||
PERIODIC_CLEAN_FILE = (HOMEBREW_CACHE/".cleaned").freeze
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cli/named_args"
|
||||
require "ostruct"
|
||||
|
||||
module Homebrew
|
||||
@ -19,7 +20,7 @@ module Homebrew
|
||||
|
||||
# Can set these because they will be overwritten by freeze_named_args!
|
||||
# (whereas other values below will only be overwritten if passed).
|
||||
self[:named_args] = []
|
||||
self[:named_args] = NamedArgs.new
|
||||
self[:remaining] = []
|
||||
end
|
||||
|
||||
@ -28,18 +29,12 @@ module Homebrew
|
||||
end
|
||||
|
||||
def freeze_named_args!(named_args)
|
||||
# Reset cache values reliant on named_args
|
||||
@formulae = nil
|
||||
@formulae_and_casks = nil
|
||||
@resolved_formulae = nil
|
||||
@resolved_formulae_casks = nil
|
||||
@formulae_paths = nil
|
||||
@casks = nil
|
||||
@loaded_casks = nil
|
||||
@kegs = nil
|
||||
@kegs_casks = nil
|
||||
|
||||
self[:named_args] = named_args.freeze
|
||||
self[:named_args] = NamedArgs.new(
|
||||
*named_args.freeze,
|
||||
override_spec: spec(nil),
|
||||
force_bottle: force_bottle?,
|
||||
flags: flags_only,
|
||||
)
|
||||
end
|
||||
|
||||
def freeze_processed_options!(processed_options)
|
||||
@ -54,7 +49,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
def named
|
||||
named_args || []
|
||||
named_args || NamedArgs.new
|
||||
end
|
||||
|
||||
def no_named?
|
||||
@ -62,102 +57,39 @@ module Homebrew
|
||||
end
|
||||
|
||||
def formulae
|
||||
require "formula"
|
||||
|
||||
@formulae ||= (downcased_unique_named - casks).map do |name|
|
||||
Formulary.factory(name, spec, force_bottle: force_bottle?, flags: flags_only)
|
||||
end.uniq(&:name).freeze
|
||||
named.to_formulae
|
||||
end
|
||||
|
||||
def formulae_and_casks
|
||||
@formulae_and_casks ||= begin
|
||||
formulae_and_casks = []
|
||||
|
||||
downcased_unique_named.each do |name|
|
||||
formulae_and_casks << Formulary.factory(name, spec)
|
||||
rescue FormulaUnavailableError
|
||||
begin
|
||||
formulae_and_casks << Cask::CaskLoader.load(name)
|
||||
rescue Cask::CaskUnavailableError
|
||||
raise "No available formula or cask with the name \"#{name}\""
|
||||
end
|
||||
end
|
||||
|
||||
formulae_and_casks.freeze
|
||||
end
|
||||
named.to_formulae_and_casks
|
||||
end
|
||||
|
||||
def resolved_formulae
|
||||
require "formula"
|
||||
|
||||
@resolved_formulae ||= (downcased_unique_named - casks).map do |name|
|
||||
Formulary.resolve(name, spec: spec(nil), force_bottle: force_bottle?, flags: flags_only)
|
||||
end.uniq(&:name).freeze
|
||||
named.to_resolved_formulae
|
||||
end
|
||||
|
||||
def resolved_formulae_casks
|
||||
@resolved_formulae_casks ||= begin
|
||||
resolved_formulae = []
|
||||
casks = []
|
||||
|
||||
downcased_unique_named.each do |name|
|
||||
resolved_formulae << Formulary.resolve(name, spec: spec(nil),
|
||||
force_bottle: force_bottle?, flags: flags_only)
|
||||
rescue FormulaUnavailableError
|
||||
begin
|
||||
casks << Cask::CaskLoader.load(name)
|
||||
rescue Cask::CaskUnavailableError
|
||||
raise "No available formula or cask with the name \"#{name}\""
|
||||
end
|
||||
end
|
||||
|
||||
[resolved_formulae.freeze, casks.freeze].freeze
|
||||
end
|
||||
named.to_resolved_formulae_to_casks
|
||||
end
|
||||
|
||||
def formulae_paths
|
||||
@formulae_paths ||= (downcased_unique_named - casks).map do |name|
|
||||
Formulary.path(name)
|
||||
end.uniq.freeze
|
||||
named.to_formulae_paths
|
||||
end
|
||||
|
||||
def casks
|
||||
@casks ||= downcased_unique_named.grep(HOMEBREW_CASK_TAP_CASK_REGEX)
|
||||
.freeze
|
||||
named.homebrew_tap_cask_names
|
||||
end
|
||||
|
||||
def loaded_casks
|
||||
@loaded_casks ||= downcased_unique_named.map(&Cask::CaskLoader.method(:load)).freeze
|
||||
named.to_casks
|
||||
end
|
||||
|
||||
def kegs
|
||||
@kegs ||= downcased_unique_named.map do |name|
|
||||
resolve_keg name
|
||||
rescue NoSuchKegError => e
|
||||
if (reason = Homebrew::MissingFormula.suggest_command(name, "uninstall"))
|
||||
$stderr.puts reason
|
||||
end
|
||||
raise e
|
||||
end.freeze
|
||||
named.to_kegs
|
||||
end
|
||||
|
||||
def kegs_casks
|
||||
@kegs_casks ||= begin
|
||||
kegs = []
|
||||
casks = []
|
||||
|
||||
downcased_unique_named.each do |name|
|
||||
kegs << resolve_keg(name)
|
||||
rescue NoSuchKegError
|
||||
begin
|
||||
casks << Cask::CaskLoader.load(name)
|
||||
rescue Cask::CaskUnavailableError
|
||||
raise "No installed keg or cask with the name \"#{name}\""
|
||||
end
|
||||
end
|
||||
|
||||
[kegs.freeze, casks.freeze].freeze
|
||||
end
|
||||
named.to_kegs_to_casks
|
||||
end
|
||||
|
||||
def build_stable?
|
||||
@ -218,17 +150,6 @@ module Homebrew
|
||||
@cli_args.freeze
|
||||
end
|
||||
|
||||
def downcased_unique_named
|
||||
# Only lowercase names, not paths, bottle filenames or URLs
|
||||
named.map do |arg|
|
||||
if arg.include?("/") || arg.end_with?(".tar.gz") || File.exist?(arg)
|
||||
arg
|
||||
else
|
||||
arg.downcase
|
||||
end
|
||||
end.uniq
|
||||
end
|
||||
|
||||
def spec(default = :stable)
|
||||
if HEAD?
|
||||
:head
|
||||
@ -238,50 +159,6 @@ module Homebrew
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_keg(name)
|
||||
require "keg"
|
||||
require "formula"
|
||||
require "missing_formula"
|
||||
|
||||
raise UsageError if name.blank?
|
||||
|
||||
rack = Formulary.to_rack(name.downcase)
|
||||
|
||||
dirs = rack.directory? ? rack.subdirs : []
|
||||
raise NoSuchKegError, rack.basename if dirs.empty?
|
||||
|
||||
linked_keg_ref = HOMEBREW_LINKED_KEGS/rack.basename
|
||||
opt_prefix = HOMEBREW_PREFIX/"opt/#{rack.basename}"
|
||||
|
||||
begin
|
||||
if opt_prefix.symlink? && opt_prefix.directory?
|
||||
Keg.new(opt_prefix.resolved_path)
|
||||
elsif linked_keg_ref.symlink? && linked_keg_ref.directory?
|
||||
Keg.new(linked_keg_ref.resolved_path)
|
||||
elsif dirs.length == 1
|
||||
Keg.new(dirs.first)
|
||||
else
|
||||
f = if name.include?("/") || File.exist?(name)
|
||||
Formulary.factory(name)
|
||||
else
|
||||
Formulary.from_rack(rack)
|
||||
end
|
||||
|
||||
unless (prefix = f.installed_prefix).directory?
|
||||
raise MultipleVersionsInstalledError, "#{rack.basename} has multiple installed versions"
|
||||
end
|
||||
|
||||
Keg.new(prefix)
|
||||
end
|
||||
rescue FormulaUnavailableError
|
||||
raise MultipleVersionsInstalledError, <<~EOS
|
||||
Multiple kegs installed to #{rack}
|
||||
However we don't know which one you refer to.
|
||||
Please delete (with rm -rf!) all but one and then try again.
|
||||
EOS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
180
Library/Homebrew/cli/named_args.rb
Normal file
180
Library/Homebrew/cli/named_args.rb
Normal file
@ -0,0 +1,180 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "delegate"
|
||||
|
||||
module Homebrew
|
||||
module CLI
|
||||
class NamedArgs < SimpleDelegator
|
||||
def initialize(*args, override_spec: nil, force_bottle: false, flags: [])
|
||||
@args = args
|
||||
@override_spec = override_spec
|
||||
@force_bottle = force_bottle
|
||||
@flags = flags
|
||||
|
||||
__setobj__(@args)
|
||||
end
|
||||
|
||||
def to_formulae
|
||||
@to_formulae ||= (downcased_unique_named - homebrew_tap_cask_names).map do |name|
|
||||
Formulary.factory(name, spec, force_bottle: @force_bottle, flags: @flags)
|
||||
end.uniq(&:name).freeze
|
||||
end
|
||||
|
||||
def to_formulae_and_casks
|
||||
@to_formulae_and_casks ||= begin
|
||||
formulae_and_casks = []
|
||||
|
||||
downcased_unique_named.each do |name|
|
||||
formulae_and_casks << Formulary.factory(name, spec)
|
||||
|
||||
puts "Treating #{name} as a formula. For the cask, use homebrew/cask/#{name}" if cask_exists_with_ref name
|
||||
rescue FormulaUnavailableError
|
||||
begin
|
||||
formulae_and_casks << Cask::CaskLoader.load(name)
|
||||
rescue Cask::CaskUnavailableError
|
||||
raise "No available formula or cask with the name \"#{name}\""
|
||||
end
|
||||
end
|
||||
|
||||
formulae_and_casks.freeze
|
||||
end
|
||||
end
|
||||
|
||||
def to_resolved_formulae
|
||||
@to_resolved_formulae ||= (downcased_unique_named - homebrew_tap_cask_names).map do |name|
|
||||
Formulary.resolve(name, spec: spec(nil), force_bottle: @force_bottle, flags: @flags)
|
||||
end.uniq(&:name).freeze
|
||||
end
|
||||
|
||||
def to_resolved_formulae_to_casks
|
||||
@to_resolved_formulae_to_casks ||= begin
|
||||
resolved_formulae = []
|
||||
casks = []
|
||||
|
||||
downcased_unique_named.each do |name|
|
||||
resolved_formulae << Formulary.resolve(name, spec: spec(nil), force_bottle: @force_bottle, flags: @flags)
|
||||
|
||||
puts "Treating #{name} as a formula. For the cask, use homebrew/cask/#{name}" if cask_exists_with_ref name
|
||||
rescue FormulaUnavailableError
|
||||
begin
|
||||
casks << Cask::CaskLoader.load(name)
|
||||
rescue Cask::CaskUnavailableError
|
||||
raise "No available formula or cask with the name \"#{name}\""
|
||||
end
|
||||
end
|
||||
|
||||
[resolved_formulae.freeze, casks.freeze].freeze
|
||||
end
|
||||
end
|
||||
|
||||
def to_formulae_paths
|
||||
@to_formulae_paths ||= (downcased_unique_named - homebrew_tap_cask_names).map do |name|
|
||||
Formulary.path(name)
|
||||
end.uniq.freeze
|
||||
end
|
||||
|
||||
def to_casks
|
||||
@to_casks ||= downcased_unique_named.map(&Cask::CaskLoader.method(:load)).freeze
|
||||
end
|
||||
|
||||
def to_kegs
|
||||
@to_kegs ||= downcased_unique_named.map do |name|
|
||||
resolve_keg name
|
||||
rescue NoSuchKegError => e
|
||||
if (reason = Homebrew::MissingFormula.suggest_command(name, "uninstall"))
|
||||
$stderr.puts reason
|
||||
end
|
||||
raise e
|
||||
end.freeze
|
||||
end
|
||||
|
||||
def to_kegs_to_casks
|
||||
@to_kegs_to_casks ||= begin
|
||||
kegs = []
|
||||
casks = []
|
||||
|
||||
downcased_unique_named.each do |name|
|
||||
kegs << resolve_keg(name)
|
||||
|
||||
puts "Treating #{name} as a keg. For the cask, use homebrew/cask/#{name}" if cask_exists_with_ref name
|
||||
rescue NoSuchKegError, FormulaUnavailableError
|
||||
begin
|
||||
casks << Cask::CaskLoader.load(name)
|
||||
rescue Cask::CaskUnavailableError
|
||||
raise "No installed keg or cask with the name \"#{name}\""
|
||||
end
|
||||
end
|
||||
|
||||
[kegs.freeze, casks.freeze].freeze
|
||||
end
|
||||
end
|
||||
|
||||
def homebrew_tap_cask_names
|
||||
downcased_unique_named.grep(HOMEBREW_CASK_TAP_CASK_REGEX)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def downcased_unique_named
|
||||
# Only lowercase names, not paths, bottle filenames or URLs
|
||||
map do |arg|
|
||||
if arg.include?("/") || arg.end_with?(".tar.gz") || File.exist?(arg)
|
||||
arg
|
||||
else
|
||||
arg.downcase
|
||||
end
|
||||
end.uniq
|
||||
end
|
||||
|
||||
def spec(default = :stable)
|
||||
@override_spec || default
|
||||
end
|
||||
|
||||
def resolve_keg(name)
|
||||
raise UsageError if name.blank?
|
||||
|
||||
rack = Formulary.to_rack(name.downcase)
|
||||
|
||||
dirs = rack.directory? ? rack.subdirs : []
|
||||
raise NoSuchKegError, rack.basename if dirs.empty?
|
||||
|
||||
linked_keg_ref = HOMEBREW_LINKED_KEGS/rack.basename
|
||||
opt_prefix = HOMEBREW_PREFIX/"opt/#{rack.basename}"
|
||||
|
||||
begin
|
||||
if opt_prefix.symlink? && opt_prefix.directory?
|
||||
Keg.new(opt_prefix.resolved_path)
|
||||
elsif linked_keg_ref.symlink? && linked_keg_ref.directory?
|
||||
Keg.new(linked_keg_ref.resolved_path)
|
||||
elsif dirs.length == 1
|
||||
Keg.new(dirs.first)
|
||||
else
|
||||
f = if name.include?("/") || File.exist?(name)
|
||||
Formulary.factory(name)
|
||||
else
|
||||
Formulary.from_rack(rack)
|
||||
end
|
||||
|
||||
unless (prefix = f.installed_prefix).directory?
|
||||
raise MultipleVersionsInstalledError, "#{rack.basename} has multiple installed versions"
|
||||
end
|
||||
|
||||
Keg.new(prefix)
|
||||
end
|
||||
rescue FormulaUnavailableError
|
||||
raise MultipleVersionsInstalledError, <<~EOS
|
||||
Multiple kegs installed to #{rack}
|
||||
However we don't know which one you refer to.
|
||||
Please delete (with rm -rf!) all but one and then try again.
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
def cask_exists_with_ref(ref)
|
||||
Cask::CaskLoader.load ref
|
||||
rescue Cask::CaskUnavailableError
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -43,9 +43,9 @@ module Homebrew
|
||||
Utils::Shell.from_path(args.shell)
|
||||
end
|
||||
|
||||
env_keys = build_env_keys(ENV)
|
||||
env_keys = BuildEnvironment.keys(ENV)
|
||||
if shell.nil?
|
||||
dump_build_env ENV
|
||||
BuildEnvironment.dump ENV
|
||||
else
|
||||
env_keys.each do |key|
|
||||
puts Utils::Shell.export_value(key, ENV[key], shell)
|
||||
|
||||
@ -25,10 +25,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
homepages = args.formulae_and_casks.map do |formula_or_cask|
|
||||
disclaimer = disclaimers(formula_or_cask)
|
||||
disclaimer = " (#{disclaimer})" if disclaimer.present?
|
||||
|
||||
puts "Opening homepage for #{name_of(formula_or_cask)}#{disclaimer}"
|
||||
puts "Opening homepage for #{name_of(formula_or_cask)}"
|
||||
formula_or_cask.homepage
|
||||
end
|
||||
|
||||
@ -42,15 +39,4 @@ module Homebrew
|
||||
"Cask #{formula_or_cask.token}"
|
||||
end
|
||||
end
|
||||
|
||||
def disclaimers(formula_or_cask)
|
||||
return unless formula_or_cask.is_a? Formula
|
||||
|
||||
begin
|
||||
cask = Cask::CaskLoader.load formula_or_cask.name
|
||||
"for the cask, use #{cask.tap.name}/#{cask.token}"
|
||||
rescue Cask::CaskUnavailableError
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -2,9 +2,11 @@
|
||||
|
||||
require "compilers"
|
||||
|
||||
# Combination of C++ standard library and compiler.
|
||||
class CxxStdlib
|
||||
include CompilerConstants
|
||||
|
||||
# Error for when a formula's dependency was built with a different C++ standard library.
|
||||
class CompatibilityError < StandardError
|
||||
def initialize(formula, dep, stdlib)
|
||||
super <<~EOS
|
||||
@ -17,8 +19,8 @@ class CxxStdlib
|
||||
def self.create(type, compiler)
|
||||
raise ArgumentError, "Invalid C++ stdlib type: #{type}" if type && ![:libstdcxx, :libcxx].include?(type)
|
||||
|
||||
klass = (compiler.to_s =~ GNU_GCC_REGEXP) ? GnuStdlib : AppleStdlib
|
||||
klass.new(type, compiler)
|
||||
apple_compiler = compiler.to_s.match?(GNU_GCC_REGEXP) ? false : true
|
||||
CxxStdlib.new(type, compiler, apple_compiler)
|
||||
end
|
||||
|
||||
def self.check_compatibility(formula, deps, keg, compiler)
|
||||
@ -35,9 +37,10 @@ class CxxStdlib
|
||||
|
||||
attr_reader :type, :compiler
|
||||
|
||||
def initialize(type, compiler)
|
||||
def initialize(type, compiler, apple_compiler)
|
||||
@type = type
|
||||
@compiler = compiler.to_sym
|
||||
@apple_compiler = apple_compiler
|
||||
end
|
||||
|
||||
# If either package doesn't use C++, all is well.
|
||||
@ -72,15 +75,7 @@ class CxxStdlib
|
||||
"#<#{self.class.name}: #{compiler} #{type}>"
|
||||
end
|
||||
|
||||
class AppleStdlib < CxxStdlib
|
||||
def apple_compiler?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
class GnuStdlib < CxxStdlib
|
||||
def apple_compiler?
|
||||
false
|
||||
end
|
||||
@apple_compiler
|
||||
end
|
||||
end
|
||||
|
||||
@ -11,7 +11,7 @@ module IRB
|
||||
|
||||
def start_within(binding)
|
||||
unless @setup_done
|
||||
setup(nil)
|
||||
setup(nil, argv: [])
|
||||
@setup_done = true
|
||||
end
|
||||
|
||||
|
||||
@ -350,6 +350,11 @@ module Homebrew
|
||||
"LGPL-3.0" => ["LGPL-3.0-only", "LGPL-3.0-or-later"],
|
||||
}.freeze
|
||||
|
||||
PERMITTED_FORMULA_LICENSE_MISMATCHES = {
|
||||
"cmockery" => "0.1.2",
|
||||
"scw@1" => "1.20",
|
||||
}.freeze
|
||||
|
||||
def audit_license
|
||||
if formula.license.present?
|
||||
non_standard_licenses = formula.license.map do |license|
|
||||
@ -380,12 +385,13 @@ module Homebrew
|
||||
|
||||
return unless @online
|
||||
|
||||
user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*}) if @new_formula
|
||||
user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*})
|
||||
return if user.blank?
|
||||
|
||||
github_license = GitHub.get_repo_license(user, repo)
|
||||
return if github_license && (formula.license + ["NOASSERTION"]).include?(github_license)
|
||||
return if PERMITTED_LICENSE_MISMATCHES[github_license]&.any? { |license| formula.license.include? license }
|
||||
return if PERMITTED_FORMULA_LICENSE_MISMATCHES[formula.name] == formula.version
|
||||
|
||||
problem "Formula license #{formula.license} does not match GitHub license #{Array(github_license)}."
|
||||
|
||||
|
||||
@ -294,7 +294,10 @@ module Homebrew
|
||||
"",
|
||||
]
|
||||
end
|
||||
new_contents = inreplace_pairs(formula.path, replacement_pairs.uniq.compact, args: args)
|
||||
new_contents = Utils::Inreplace.inreplace_pairs(formula.path,
|
||||
replacement_pairs.uniq.compact,
|
||||
read_only_run: read_only_run,
|
||||
silent: args.quiet?)
|
||||
|
||||
new_formula_version = formula_version(formula, requested_spec, new_contents)
|
||||
|
||||
@ -462,34 +465,6 @@ module Homebrew
|
||||
[remote_url, username]
|
||||
end
|
||||
|
||||
def inreplace_pairs(path, replacement_pairs, args:)
|
||||
read_only_run = args.dry_run? && !args.write?
|
||||
if read_only_run
|
||||
str = path.open("r") { |f| Formulary.ensure_utf8_encoding(f).read }
|
||||
contents = StringInreplaceExtension.new(str)
|
||||
replacement_pairs.each do |old, new|
|
||||
ohai "replace #{old.inspect} with #{new.inspect}" unless args.quiet?
|
||||
raise "No old value for new value #{new}! Did you pass the wrong arguments?" unless old
|
||||
|
||||
contents.gsub!(old, new)
|
||||
end
|
||||
raise Utils::InreplaceError, path => contents.errors unless contents.errors.empty?
|
||||
|
||||
path.atomic_write(contents.inreplace_string) if args.write?
|
||||
contents.inreplace_string
|
||||
else
|
||||
Utils::Inreplace.inreplace(path) do |s|
|
||||
replacement_pairs.each do |old, new|
|
||||
ohai "replace #{old.inspect} with #{new.inspect}" unless args.quiet?
|
||||
raise "No old value for new value #{new}! Did you pass the wrong arguments?" unless old
|
||||
|
||||
s.gsub!(old, new)
|
||||
end
|
||||
end
|
||||
path.open("r") { |f| Formulary.ensure_utf8_encoding(f).read }
|
||||
end
|
||||
end
|
||||
|
||||
def formula_version(formula, spec, contents = nil)
|
||||
name = formula.name
|
||||
path = formula.path
|
||||
|
||||
@ -49,7 +49,7 @@ module Homebrew
|
||||
return
|
||||
end
|
||||
|
||||
ohai "#{prs.size} matching pull requests:"
|
||||
ohai "#{prs.count} matching pull #{"request".pluralize(prs.count)}:"
|
||||
pr_urls = []
|
||||
prs.each do |pr|
|
||||
puts "#{tap.full_name unless tap.core_tap?}##{pr["number"]}: #{pr["title"]}"
|
||||
|
||||
@ -99,6 +99,15 @@ class DevelopmentTools
|
||||
def subversion_handles_most_https_certificates?
|
||||
true
|
||||
end
|
||||
|
||||
def build_system_info
|
||||
{
|
||||
"os" => ENV["HOMEBREW_SYSTEM"],
|
||||
"os_version" => OS_VERSION,
|
||||
"cpu_family" => Hardware::CPU.family,
|
||||
}
|
||||
end
|
||||
alias generic_build_system_info build_system_info
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -12,6 +12,9 @@ require "cask/caskroom"
|
||||
require "cask/quarantine"
|
||||
|
||||
module Homebrew
|
||||
# Module containing diagnostic checks.
|
||||
#
|
||||
# @api private
|
||||
module Diagnostic
|
||||
def self.missing_deps(ff, hide = nil)
|
||||
missing = {}
|
||||
@ -25,44 +28,7 @@ module Homebrew
|
||||
missing
|
||||
end
|
||||
|
||||
class Volumes
|
||||
def initialize
|
||||
@volumes = get_mounts
|
||||
end
|
||||
|
||||
def which(path)
|
||||
vols = get_mounts path
|
||||
|
||||
# no volume found
|
||||
return -1 if vols.empty?
|
||||
|
||||
vol_index = @volumes.index(vols[0])
|
||||
# volume not found in volume list
|
||||
return -1 if vol_index.nil?
|
||||
|
||||
vol_index
|
||||
end
|
||||
|
||||
def get_mounts(path = nil)
|
||||
vols = []
|
||||
# get the volume of path, if path is nil returns all volumes
|
||||
|
||||
args = %w[/bin/df -P]
|
||||
args << path if path
|
||||
|
||||
Utils.popen_read(*args) do |io|
|
||||
io.each_line do |line|
|
||||
case line.chomp
|
||||
# regex matches: /dev/disk0s2 489562928 440803616 48247312 91% /
|
||||
when /^.+\s+[0-9]+\s+[0-9]+\s+[0-9]+\s+[0-9]{1,3}%\s+(.+)/
|
||||
vols << Regexp.last_match(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
vols
|
||||
end
|
||||
end
|
||||
|
||||
# Diagnostic checks.
|
||||
class Checks
|
||||
def initialize(verbose = true)
|
||||
@verbose = verbose
|
||||
|
||||
@ -369,7 +369,7 @@ class BuildError < RuntimeError
|
||||
ohai "Configuration"
|
||||
SystemConfig.dump_verbose_config
|
||||
ohai "ENV"
|
||||
Homebrew.dump_build_env(env)
|
||||
BuildEnvironment.dump env
|
||||
puts
|
||||
onoe "#{formula.full_name} #{formula.version} did not build"
|
||||
unless (logs = Dir["#{formula.logs}/*"]).empty?
|
||||
|
||||
@ -38,7 +38,7 @@ module Homebrew
|
||||
f.close
|
||||
return if system f.path
|
||||
|
||||
<<~EOS.undent
|
||||
<<~EOS
|
||||
The directory #{HOMEBREW_TEMP} does not permit executing
|
||||
programs. It is likely mounted as "noexec". Please set HOMEBREW_TEMP
|
||||
in your #{shell_profile} to a different directory, for example:
|
||||
|
||||
@ -14,6 +14,12 @@ module Homebrew
|
||||
"/system/bin/linker64",
|
||||
"/system/bin/linker",
|
||||
].freeze
|
||||
private_constant :DYNAMIC_LINKERS
|
||||
|
||||
def perform_preinstall_checks(all_fatal: false, cc: nil)
|
||||
generic_perform_preinstall_checks(all_fatal: all_fatal, cc: cc)
|
||||
symlink_ld_so
|
||||
end
|
||||
|
||||
def check_cpu
|
||||
return if Hardware::CPU.intel? && Hardware::CPU.is_64_bit?
|
||||
@ -28,6 +34,7 @@ module Homebrew
|
||||
end
|
||||
abort message
|
||||
end
|
||||
private_class_method :check_cpu
|
||||
|
||||
def symlink_ld_so
|
||||
brew_ld_so = HOMEBREW_PREFIX/"lib/ld.so"
|
||||
@ -42,10 +49,6 @@ module Homebrew
|
||||
FileUtils.mkdir_p HOMEBREW_PREFIX/"lib"
|
||||
FileUtils.ln_sf ld_so, brew_ld_so
|
||||
end
|
||||
|
||||
def perform_preinstall_checks(all_fatal: false, cc: nil)
|
||||
generic_perform_preinstall_checks(all_fatal: all_fatal, cc: cc)
|
||||
symlink_ld_so
|
||||
end
|
||||
private_class_method :symlink_ld_so
|
||||
end
|
||||
end
|
||||
|
||||
@ -56,5 +56,13 @@ class DevelopmentTools
|
||||
brew install gcc
|
||||
EOS
|
||||
end
|
||||
|
||||
def build_system_info
|
||||
build_info = {
|
||||
"xcode" => MacOS::Xcode.version.to_s.presence,
|
||||
"clt" => MacOS::CLT.version.to_s.presence,
|
||||
}
|
||||
generic_build_system_info.merge build_info
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -2,6 +2,44 @@
|
||||
|
||||
module Homebrew
|
||||
module Diagnostic
|
||||
class Volumes
|
||||
def initialize
|
||||
@volumes = get_mounts
|
||||
end
|
||||
|
||||
def which(path)
|
||||
vols = get_mounts path
|
||||
|
||||
# no volume found
|
||||
return -1 if vols.empty?
|
||||
|
||||
vol_index = @volumes.index(vols[0])
|
||||
# volume not found in volume list
|
||||
return -1 if vol_index.nil?
|
||||
|
||||
vol_index
|
||||
end
|
||||
|
||||
def get_mounts(path = nil)
|
||||
vols = []
|
||||
# get the volume of path, if path is nil returns all volumes
|
||||
|
||||
args = %w[/bin/df -P]
|
||||
args << path if path
|
||||
|
||||
Utils.popen_read(*args) do |io|
|
||||
io.each_line do |line|
|
||||
case line.chomp
|
||||
# regex matches: /dev/disk0s2 489562928 440803616 48247312 91% /
|
||||
when /^.+\s+[0-9]+\s+[0-9]+\s+[0-9]+\s+[0-9]{1,3}%\s+(.+)/
|
||||
vols << Regexp.last_match(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
vols
|
||||
end
|
||||
end
|
||||
|
||||
class Checks
|
||||
undef fatal_build_from_source_checks, supported_configuration_checks,
|
||||
build_from_source_checks
|
||||
|
||||
@ -2020,7 +2020,7 @@ class Formula
|
||||
|
||||
SystemConfig.dump_verbose_config(log)
|
||||
log.puts
|
||||
Homebrew.dump_build_env(env, log)
|
||||
BuildEnvironment.dump env, log
|
||||
|
||||
raise BuildError.new(self, cmd, args, env)
|
||||
end
|
||||
|
||||
@ -21,7 +21,6 @@ require "cmd/install"
|
||||
require "find"
|
||||
|
||||
class FormulaInstaller
|
||||
include Homebrew::Install
|
||||
include FormulaCellarChecks
|
||||
extend Predicable
|
||||
|
||||
@ -261,7 +260,9 @@ class FormulaInstaller
|
||||
lock
|
||||
|
||||
start_time = Time.now
|
||||
perform_build_from_source_checks if !formula.bottle_unneeded? && !pour_bottle? && DevelopmentTools.installed?
|
||||
if !formula.bottle_unneeded? && !pour_bottle? && DevelopmentTools.installed?
|
||||
Homebrew::Install.perform_build_from_source_checks
|
||||
end
|
||||
|
||||
# not in initialize so upgrade can unlink the active keg before calling this
|
||||
# function but after instantiating this class so that it can avoid having to
|
||||
|
||||
@ -64,6 +64,9 @@ class FormulaVersions
|
||||
versions_seen = (map.keys + [f.pkg_version]).uniq.length
|
||||
end
|
||||
return map if versions_seen > MAX_VERSIONS_DEPTH
|
||||
rescue MacOSVersionError => e
|
||||
odebug "#{e} in #{name} at revision #{rev}" if debug?
|
||||
break
|
||||
end
|
||||
map
|
||||
end
|
||||
|
||||
@ -6,9 +6,27 @@ require "hardware"
|
||||
require "development_tools"
|
||||
|
||||
module Homebrew
|
||||
# Helper module for performing (pre-)install checks.
|
||||
#
|
||||
# @api private
|
||||
module Install
|
||||
module_function
|
||||
|
||||
def perform_preinstall_checks(all_fatal: false, cc: nil)
|
||||
check_cpu
|
||||
attempt_directory_creation
|
||||
check_cc_argv(cc)
|
||||
diagnostic_checks(:supported_configuration_checks, fatal: all_fatal)
|
||||
diagnostic_checks(:fatal_preinstall_checks)
|
||||
end
|
||||
alias generic_perform_preinstall_checks perform_preinstall_checks
|
||||
module_function :generic_perform_preinstall_checks
|
||||
|
||||
def perform_build_from_source_checks(all_fatal: false)
|
||||
diagnostic_checks(:fatal_build_from_source_checks)
|
||||
diagnostic_checks(:build_from_source_checks, fatal: all_fatal)
|
||||
end
|
||||
|
||||
def check_cpu
|
||||
return if Hardware::CPU.intel? && Hardware::CPU.is_64_bit?
|
||||
|
||||
@ -24,6 +42,7 @@ module Homebrew
|
||||
end
|
||||
abort message
|
||||
end
|
||||
private_class_method :check_cpu
|
||||
|
||||
def attempt_directory_creation
|
||||
Keg::MUST_EXIST_DIRECTORIES.each do |dir|
|
||||
@ -38,6 +57,7 @@ module Homebrew
|
||||
nil
|
||||
end
|
||||
end
|
||||
private_class_method :attempt_directory_creation
|
||||
|
||||
def check_cc_argv(cc)
|
||||
return unless cc
|
||||
@ -48,21 +68,7 @@ module Homebrew
|
||||
#{@checks.please_create_pull_requests}
|
||||
EOS
|
||||
end
|
||||
|
||||
def perform_preinstall_checks(all_fatal: false, cc: nil)
|
||||
check_cpu
|
||||
attempt_directory_creation
|
||||
check_cc_argv(cc)
|
||||
diagnostic_checks(:supported_configuration_checks, fatal: all_fatal)
|
||||
diagnostic_checks(:fatal_preinstall_checks)
|
||||
end
|
||||
alias generic_perform_preinstall_checks perform_preinstall_checks
|
||||
module_function :generic_perform_preinstall_checks
|
||||
|
||||
def perform_build_from_source_checks(all_fatal: false)
|
||||
diagnostic_checks(:fatal_build_from_source_checks)
|
||||
diagnostic_checks(:build_from_source_checks, fatal: all_fatal)
|
||||
end
|
||||
private_class_method :check_cc_argv
|
||||
|
||||
def diagnostic_checks(type, fatal: true)
|
||||
@checks ||= Diagnostic::Checks.new
|
||||
@ -80,6 +86,7 @@ module Homebrew
|
||||
end
|
||||
exit 1 if failed && fatal
|
||||
end
|
||||
private_class_method :diagnostic_checks
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -57,6 +57,10 @@ module OS
|
||||
nil
|
||||
end
|
||||
|
||||
def sdk_path
|
||||
nil
|
||||
end
|
||||
|
||||
module Xcode
|
||||
module_function
|
||||
|
||||
|
||||
@ -36,6 +36,7 @@ class Tab < OpenStruct
|
||||
"stdlib" => stdlib,
|
||||
"aliases" => formula.aliases,
|
||||
"runtime_dependencies" => Tab.runtime_deps_hash(runtime_deps),
|
||||
"arch" => Hardware::CPU.arch,
|
||||
"source" => {
|
||||
"path" => formula.specified_path.to_s,
|
||||
"tap" => formula.tap&.name,
|
||||
@ -47,6 +48,7 @@ class Tab < OpenStruct
|
||||
"version_scheme" => formula.version_scheme,
|
||||
},
|
||||
},
|
||||
"built_on" => DevelopmentTools.build_system_info,
|
||||
}
|
||||
|
||||
new(attributes)
|
||||
@ -198,6 +200,7 @@ class Tab < OpenStruct
|
||||
"version_scheme" => 0,
|
||||
},
|
||||
},
|
||||
"built_on" => DevelopmentTools.generic_build_system_info,
|
||||
}
|
||||
|
||||
new(attributes)
|
||||
@ -344,6 +347,7 @@ class Tab < OpenStruct
|
||||
"aliases" => aliases,
|
||||
"runtime_dependencies" => runtime_dependencies,
|
||||
"source" => source,
|
||||
"built_on" => built_on,
|
||||
}
|
||||
|
||||
JSON.generate(attributes, options)
|
||||
|
||||
@ -25,14 +25,9 @@ describe Cask::Cmd::Cache, :cask do
|
||||
cache: Cask::Cache.path, **local_caffeine.url.specs
|
||||
).cached_location
|
||||
|
||||
expect do
|
||||
described_class.run("local-transmission", "local-caffeine")
|
||||
end.to output("#{transmission_location}\n#{caffeine_location}\n").to_stdout
|
||||
end
|
||||
|
||||
it "properly handles Casks that are not present" do
|
||||
expect {
|
||||
described_class.run("notacask")
|
||||
}.to raise_error(Cask::CaskUnavailableError)
|
||||
expect(described_class.cached_location(local_transmission))
|
||||
.to eql transmission_location
|
||||
expect(described_class.cached_location(local_caffeine))
|
||||
.to eql caffeine_location
|
||||
end
|
||||
end
|
||||
|
||||
@ -120,7 +120,7 @@ describe Caveats do
|
||||
"plist_test.plist"
|
||||
end
|
||||
end
|
||||
allow(ENV).to receive(:[]).with("TMUX").and_return(true)
|
||||
ENV["TMUX"] = "1"
|
||||
allow(Homebrew).to receive(:_system).with("/usr/bin/pbpaste").and_return(false)
|
||||
caveats = described_class.new(f).caveats
|
||||
|
||||
|
||||
@ -5,9 +5,9 @@ require "cleanup"
|
||||
require "cask/cache"
|
||||
require "fileutils"
|
||||
|
||||
using CleanupRefinement
|
||||
using Homebrew::Cleanup::CleanupRefinement
|
||||
|
||||
describe CleanupRefinement do
|
||||
describe Homebrew::Cleanup::CleanupRefinement do
|
||||
describe "::prune?" do
|
||||
alias_matcher :be_pruned, :be_prune
|
||||
|
||||
|
||||
110
Library/Homebrew/test/cli/named_args_spec.rb
Normal file
110
Library/Homebrew/test/cli/named_args_spec.rb
Normal file
@ -0,0 +1,110 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cli/named_args"
|
||||
|
||||
describe Homebrew::CLI::NamedArgs do
|
||||
let(:foo) do
|
||||
formula "foo" do
|
||||
url "https://brew.sh"
|
||||
version "1.0"
|
||||
end
|
||||
end
|
||||
|
||||
let(:foo_keg) do
|
||||
path = (HOMEBREW_CELLAR/"foo/1.0").resolved_path
|
||||
mkdir_p path
|
||||
Keg.new(path)
|
||||
end
|
||||
|
||||
let(:bar) do
|
||||
formula "bar" do
|
||||
url "https://brew.sh"
|
||||
version "1.0"
|
||||
end
|
||||
end
|
||||
|
||||
let(:bar_keg) do
|
||||
path = (HOMEBREW_CELLAR/"bar/1.0").resolved_path
|
||||
mkdir_p path
|
||||
Keg.new(path)
|
||||
end
|
||||
|
||||
let(:baz) do
|
||||
Cask::CaskLoader.load(+<<~RUBY)
|
||||
cask "baz" do
|
||||
version "1.0"
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
describe "#to_formulae" do
|
||||
it "returns formulae" do
|
||||
stub_formula_loader foo, call_original: true
|
||||
stub_formula_loader bar
|
||||
|
||||
expect(described_class.new("foo", "bar").to_formulae).to eq [foo, bar]
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_formulae_and_casks" do
|
||||
it "returns formulae and casks" do
|
||||
stub_formula_loader foo, call_original: true
|
||||
stub_cask_loader baz, call_original: true
|
||||
|
||||
expect(described_class.new("foo", "baz").to_formulae_and_casks).to eq [foo, baz]
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_resolved_formulae" do
|
||||
it "returns resolved formulae" do
|
||||
allow(Formulary).to receive(:resolve).and_return(foo, bar)
|
||||
|
||||
expect(described_class.new("foo", "bar").to_resolved_formulae).to eq [foo, bar]
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_resolved_formulae_to_casks" do
|
||||
it "returns resolved formulae, as well as casks" do
|
||||
allow(Formulary).to receive(:resolve).and_call_original
|
||||
allow(Formulary).to receive(:resolve).with("foo", any_args).and_return foo
|
||||
stub_cask_loader baz, call_original: true
|
||||
|
||||
resolved_formulae, casks = described_class.new("foo", "baz").to_resolved_formulae_to_casks
|
||||
|
||||
expect(resolved_formulae).to eq [foo]
|
||||
expect(casks).to eq [baz]
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_casks" do
|
||||
it "returns casks" do
|
||||
stub_cask_loader baz
|
||||
|
||||
expect(described_class.new("baz").to_casks).to eq [baz]
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_kegs" do
|
||||
it "returns kegs" do
|
||||
named_args = described_class.new("foo", "bar")
|
||||
allow(named_args).to receive(:resolve_keg).with("foo").and_return foo_keg
|
||||
allow(named_args).to receive(:resolve_keg).with("bar").and_return bar_keg
|
||||
|
||||
expect(named_args.to_kegs).to eq [foo_keg, bar_keg]
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_kegs_to_casks" do
|
||||
it "returns kegs, as well as casks" do
|
||||
named_args = described_class.new("foo", "baz")
|
||||
allow(named_args).to receive(:resolve_keg).and_call_original
|
||||
allow(named_args).to receive(:resolve_keg).with("foo").and_return foo_keg
|
||||
stub_cask_loader baz, call_original: true
|
||||
|
||||
kegs, casks = named_args.to_kegs_to_casks
|
||||
|
||||
expect(kegs).to eq [foo_keg]
|
||||
expect(casks).to eq [baz]
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -305,4 +305,12 @@ describe Utils::Inreplace do
|
||||
end
|
||||
}.to raise_error(Utils::InreplaceError)
|
||||
end
|
||||
|
||||
describe "#inreplace_pairs" do
|
||||
it "raises error if there is no old value" do
|
||||
expect {
|
||||
described_class.inreplace_pairs(file.path, [[nil, "f"]])
|
||||
}.to raise_error(Utils::InreplaceError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -36,6 +36,7 @@ $LOAD_PATH.push(File.expand_path("#{ENV["HOMEBREW_LIBRARY"]}/Homebrew/test/suppo
|
||||
require_relative "../global"
|
||||
|
||||
require "test/support/no_seed_progress_formatter"
|
||||
require "test/support/helper/cask"
|
||||
require "test/support/helper/fixtures"
|
||||
require "test/support/helper/formula"
|
||||
require "test/support/helper/mktmpdir"
|
||||
@ -86,6 +87,7 @@ RSpec.configure do |config|
|
||||
|
||||
config.include(RuboCop::RSpec::ExpectOffense)
|
||||
|
||||
config.include(Test::Helper::Cask)
|
||||
config.include(Test::Helper::Fixtures)
|
||||
config.include(Test::Helper::Formula)
|
||||
config.include(Test::Helper::MkTmpDir)
|
||||
|
||||
16
Library/Homebrew/test/support/helper/cask.rb
Normal file
16
Library/Homebrew/test/support/helper/cask.rb
Normal file
@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cask/cask_loader"
|
||||
|
||||
module Test
|
||||
module Helper
|
||||
module Cask
|
||||
def stub_cask_loader(cask, ref = cask.token, call_original: false)
|
||||
allow(::Cask::CaskLoader).to receive(:for).and_call_original if call_original
|
||||
|
||||
loader = ::Cask::CaskLoader::FromInstanceLoader.new cask
|
||||
allow(::Cask::CaskLoader).to receive(:for).with(ref).and_return(loader)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -11,7 +11,9 @@ module Test
|
||||
|
||||
# Use a stubbed {Formulary::FormulaLoader} to make a given formula be found
|
||||
# when loading from {Formulary} with `ref`.
|
||||
def stub_formula_loader(formula, ref = formula.full_name)
|
||||
def stub_formula_loader(formula, ref = formula.full_name, call_original: false)
|
||||
allow(Formulary).to receive(:loader_for).and_call_original if call_original
|
||||
|
||||
loader = double(get_formula: formula)
|
||||
allow(Formulary).to receive(:loader_for).with(ref, from: :keg).and_return(loader)
|
||||
allow(Formulary).to receive(:loader_for).with(ref, from: nil).and_return(loader)
|
||||
|
||||
@ -11,6 +11,8 @@ module Utils
|
||||
end
|
||||
|
||||
module Inreplace
|
||||
module_function
|
||||
|
||||
# Sometimes we have to change a bit before we install. Mostly we
|
||||
# prefer a patch but if you need the `prefix` of this formula in the
|
||||
# patch you have to resort to `inreplace`, because in the patch
|
||||
@ -42,6 +44,23 @@ module Utils
|
||||
|
||||
raise InreplaceError, errors unless errors.empty?
|
||||
end
|
||||
module_function :inreplace
|
||||
|
||||
def inreplace_pairs(path, replacement_pairs, read_only_run: false, silent: false)
|
||||
str = File.open(path, "rb", &:read)
|
||||
contents = StringInreplaceExtension.new(str)
|
||||
replacement_pairs.each do |old, new|
|
||||
ohai "replace #{old.inspect} with #{new.inspect}" unless silent
|
||||
unless old
|
||||
contents.errors << "No old value for new value #{new}! Did you pass the wrong arguments?"
|
||||
next
|
||||
end
|
||||
|
||||
contents.gsub!(old, new)
|
||||
end
|
||||
raise InreplaceError, path => contents.errors unless contents.errors.empty?
|
||||
|
||||
Pathname(path).atomic_write(contents.inreplace_string) unless read_only_run
|
||||
contents.inreplace_string
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -51,7 +51,7 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parallel-1.19.2/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parallel_tests-3.1.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parser-2.7.1.4/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rainbow-3.0.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-runtime-0.5.5866/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-runtime-0.5.5869/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parlour-4.0.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/patchelf-1.2.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/plist-3.5.0/lib"
|
||||
@ -74,9 +74,9 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-progressbar-1.10
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unicode-display_width-1.7.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-0.88.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-performance-1.7.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rspec-1.42.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rspec-1.43.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-macho-2.2.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-static-0.5.5866-universal-darwin-19/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-0.5.5866/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-static-0.5.5869-universal-darwin-19/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-0.5.5869/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/thor-1.0.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/tapioca-0.4.1/lib"
|
||||
|
||||
@ -1,72 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module RSpec
|
||||
# Check that the first argument to the top level describe is a constant.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# describe 'Do something' do
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# describe TestedClass do
|
||||
# subject { described_class }
|
||||
# end
|
||||
#
|
||||
# describe 'TestedClass::VERSION' do
|
||||
# subject { Object.const_get(self.class.description) }
|
||||
# end
|
||||
#
|
||||
# describe "A feature example", type: :feature do
|
||||
# end
|
||||
class DescribeClass < Cop
|
||||
include RuboCop::RSpec::TopLevelDescribe
|
||||
|
||||
MSG = 'The first argument to describe should be '\
|
||||
'the class or module being tested.'
|
||||
|
||||
def_node_matcher :valid_describe?, <<-PATTERN
|
||||
{
|
||||
(send #{RSPEC} :describe const ...)
|
||||
(send #{RSPEC} :describe)
|
||||
}
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :describe_with_rails_metadata?, <<-PATTERN
|
||||
(send #{RSPEC} :describe !const ...
|
||||
(hash <#rails_metadata? ...>)
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :rails_metadata?, <<-PATTERN
|
||||
(pair
|
||||
(sym :type)
|
||||
(sym {
|
||||
:channel :controller :helper :job :mailer :model :request
|
||||
:routing :view :feature :system :mailbox
|
||||
}
|
||||
)
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def on_top_level_describe(node, (described_value, _))
|
||||
return if shared_group?(root_node)
|
||||
return if valid_describe?(node)
|
||||
return if describe_with_rails_metadata?(node)
|
||||
return if string_constant_describe?(described_value)
|
||||
|
||||
add_offense(described_value)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def string_constant_describe?(described_value)
|
||||
described_value.str_type? &&
|
||||
described_value.value =~ /^((::)?[A-Z]\w*)+$/
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,90 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module RSpec
|
||||
# Checks if an example group does not include any tests.
|
||||
#
|
||||
# This cop is configurable using the `CustomIncludeMethods` option
|
||||
#
|
||||
# @example usage
|
||||
#
|
||||
# # bad
|
||||
# describe Bacon do
|
||||
# let(:bacon) { Bacon.new(chunkiness) }
|
||||
# let(:chunkiness) { false }
|
||||
#
|
||||
# context 'extra chunky' do # flagged by rubocop
|
||||
# let(:chunkiness) { true }
|
||||
# end
|
||||
#
|
||||
# it 'is chunky' do
|
||||
# expect(bacon.chunky?).to be_truthy
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# describe Bacon do
|
||||
# let(:bacon) { Bacon.new(chunkiness) }
|
||||
# let(:chunkiness) { false }
|
||||
#
|
||||
# it 'is chunky' do
|
||||
# expect(bacon.chunky?).to be_truthy
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# @example configuration
|
||||
#
|
||||
# # .rubocop.yml
|
||||
# # RSpec/EmptyExampleGroup:
|
||||
# # CustomIncludeMethods:
|
||||
# # - include_tests
|
||||
#
|
||||
# # spec_helper.rb
|
||||
# RSpec.configure do |config|
|
||||
# config.alias_it_behaves_like_to(:include_tests)
|
||||
# end
|
||||
#
|
||||
# # bacon_spec.rb
|
||||
# describe Bacon do
|
||||
# let(:bacon) { Bacon.new(chunkiness) }
|
||||
# let(:chunkiness) { false }
|
||||
#
|
||||
# context 'extra chunky' do # not flagged by rubocop
|
||||
# let(:chunkiness) { true }
|
||||
#
|
||||
# include_tests 'shared tests'
|
||||
# end
|
||||
# end
|
||||
#
|
||||
class EmptyExampleGroup < Cop
|
||||
MSG = 'Empty example group detected.'
|
||||
|
||||
def_node_search :contains_example?, <<-PATTERN
|
||||
{
|
||||
#{(Examples::ALL + Includes::ALL).send_pattern}
|
||||
(send _ #custom_include? ...)
|
||||
}
|
||||
PATTERN
|
||||
|
||||
def on_block(node)
|
||||
return unless example_group?(node) && !contains_example?(node)
|
||||
|
||||
add_offense(node.send_node)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def custom_include?(method_name)
|
||||
custom_include_methods.include?(method_name)
|
||||
end
|
||||
|
||||
def custom_include_methods
|
||||
cop_config
|
||||
.fetch('CustomIncludeMethods', [])
|
||||
.map(&:to_sym)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,47 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module RSpec
|
||||
# Checks that memoized helper names use the configured style.
|
||||
#
|
||||
# @example EnforcedStyle: snake_case (default)
|
||||
# # bad
|
||||
# let(:userName) { 'Adam' }
|
||||
# subject(:userName) { 'Adam' }
|
||||
#
|
||||
# # good
|
||||
# let(:user_name) { 'Adam' }
|
||||
# subject(:user_name) { 'Adam' }
|
||||
#
|
||||
# @example EnforcedStyle: camelCase
|
||||
# # bad
|
||||
# let(:user_name) { 'Adam' }
|
||||
# subject(:user_name) { 'Adam' }
|
||||
#
|
||||
# # good
|
||||
# let(:userName) { 'Adam' }
|
||||
# subject(:userName) { 'Adam' }
|
||||
class VariableName < Cop
|
||||
include ConfigurableNaming
|
||||
include RuboCop::RSpec::Variable
|
||||
|
||||
MSG = 'Use %<style>s for variable names.'
|
||||
|
||||
def on_send(node)
|
||||
variable_definition?(node) do |variable|
|
||||
return if variable.dstr_type? || variable.dsym_type?
|
||||
|
||||
check_name(node, variable.value, variable.loc.expression)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def message(style)
|
||||
format(MSG, style: style)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,44 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module RSpec
|
||||
# Helper methods for top level example group cops
|
||||
module TopLevelGroup
|
||||
extend RuboCop::NodePattern::Macros
|
||||
include RuboCop::RSpec::Language
|
||||
|
||||
def_node_matcher :example_or_shared_group?,
|
||||
(ExampleGroups::ALL + SharedGroups::ALL).block_pattern
|
||||
|
||||
def on_block(node)
|
||||
return unless respond_to?(:on_top_level_group)
|
||||
return unless top_level_group?(node)
|
||||
|
||||
on_top_level_group(node)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def top_level_group?(node)
|
||||
top_level_groups.include?(node)
|
||||
end
|
||||
|
||||
def top_level_groups
|
||||
@top_level_groups ||=
|
||||
top_level_nodes.select { |n| example_or_shared_group?(n) }
|
||||
end
|
||||
|
||||
def top_level_nodes
|
||||
if root_node.begin_type?
|
||||
root_node.children
|
||||
else
|
||||
[root_node]
|
||||
end
|
||||
end
|
||||
|
||||
def root_node
|
||||
processed_source.ast
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -74,7 +74,7 @@ RSpec/ContextWording:
|
||||
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ContextWording
|
||||
|
||||
RSpec/DescribeClass:
|
||||
Description: Check that the first argument to the top level describe is a constant.
|
||||
Description: Check that the first argument to the top-level describe is a constant.
|
||||
Enabled: true
|
||||
VersionAdded: '1.0'
|
||||
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescribeClass
|
||||
@ -381,7 +381,7 @@ RSpec/MissingExampleGroupArgument:
|
||||
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExampleGroupArgument
|
||||
|
||||
RSpec/MultipleDescribes:
|
||||
Description: Checks for multiple top level describes.
|
||||
Description: Checks for multiple top-level example groups.
|
||||
Enabled: true
|
||||
VersionAdded: '1.0'
|
||||
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleDescribes
|
||||
@ -394,6 +394,14 @@ RSpec/MultipleExpectations:
|
||||
VersionChanged: '1.21'
|
||||
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleExpectations
|
||||
|
||||
RSpec/MultipleMemoizedHelpers:
|
||||
Description: Checks if example groups contain too many `let` and `subject` calls.
|
||||
Enabled: true
|
||||
AllowSubject: true
|
||||
Max: 5
|
||||
VersionAdded: '1.43'
|
||||
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleMemoizedHelpers
|
||||
|
||||
RSpec/MultipleSubjects:
|
||||
Description: Checks if an example group defines `subject` multiple times.
|
||||
Enabled: true
|
||||
@ -558,7 +566,9 @@ RSpec/VariableName:
|
||||
SupportedStyles:
|
||||
- snake_case
|
||||
- camelCase
|
||||
IgnoredPatterns: []
|
||||
VersionAdded: '1.40'
|
||||
VersionChanged: '1.43'
|
||||
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/VariableName
|
||||
|
||||
RSpec/VerifiedDoubles:
|
||||
@ -19,11 +19,12 @@ require_relative 'rubocop/rspec/example_group'
|
||||
require_relative 'rubocop/rspec/example'
|
||||
require_relative 'rubocop/rspec/hook'
|
||||
require_relative 'rubocop/rspec/variable'
|
||||
require_relative 'rubocop/cop/rspec/base'
|
||||
require_relative 'rubocop/cop/rspec/cop'
|
||||
require_relative 'rubocop/rspec/align_let_brace'
|
||||
require_relative 'rubocop/rspec/factory_bot'
|
||||
require_relative 'rubocop/rspec/final_end_location'
|
||||
require_relative 'rubocop/rspec/blank_line_separation'
|
||||
require_relative 'rubocop/rspec/empty_line_separation'
|
||||
require_relative 'rubocop/rspec/corrector/move_node'
|
||||
|
||||
RuboCop::RSpec::Inject.defaults!
|
||||
@ -17,7 +17,7 @@ module RuboCop
|
||||
# let(:baz) { bar }
|
||||
# let(:a) { b }
|
||||
#
|
||||
class AlignLeftLetBrace < Cop
|
||||
class AlignLeftLetBrace < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Align left let brace'
|
||||
@ -17,7 +17,7 @@ module RuboCop
|
||||
# let(:baz) { bar }
|
||||
# let(:a) { b }
|
||||
#
|
||||
class AlignRightLetBrace < Cop
|
||||
class AlignRightLetBrace < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Align right let brace'
|
||||
@ -22,7 +22,7 @@ module RuboCop
|
||||
# allow(my_instance).to receive(:foo)
|
||||
# end
|
||||
# end
|
||||
class AnyInstance < Cop
|
||||
class AnyInstance < Base
|
||||
MSG = 'Avoid stubbing using `%<method>s`.'
|
||||
|
||||
def_node_matcher :disallowed_stub, <<-PATTERN
|
||||
@ -25,7 +25,7 @@ module RuboCop
|
||||
# some_method
|
||||
# test.run
|
||||
# end
|
||||
class AroundBlock < Cop
|
||||
class AroundBlock < Base
|
||||
MSG_NO_ARG = 'Test object should be passed to around block.'
|
||||
MSG_UNUSED_ARG = 'You should call `%<arg>s.call` '\
|
||||
'or `%<arg>s.run`.'
|
||||
@ -17,7 +17,7 @@ module RuboCop
|
||||
# # Patterns:
|
||||
# # - '_test.rb$'
|
||||
# # - '(?:^|/)test/'
|
||||
class Cop < ::RuboCop::Cop::Base
|
||||
class Base < ::RuboCop::Cop::Base
|
||||
include RuboCop::RSpec::Language
|
||||
include RuboCop::RSpec::Language::NodePattern
|
||||
|
||||
@ -30,8 +30,8 @@ module RuboCop
|
||||
)
|
||||
|
||||
# Invoke the original inherited hook so our cops are recognized
|
||||
def self.inherited(subclass)
|
||||
RuboCop::Cop::Cop.inherited(subclass)
|
||||
def self.inherited(subclass) # rubocop:disable Lint/MissingSuper
|
||||
RuboCop::Cop::Base.inherited(subclass)
|
||||
end
|
||||
|
||||
def relevant_file?(file)
|
||||
@ -41,7 +41,7 @@ module RuboCop
|
||||
private
|
||||
|
||||
def relevant_rubocop_rspec_file?(file)
|
||||
rspec_pattern =~ file
|
||||
rspec_pattern.match?(file)
|
||||
end
|
||||
|
||||
def rspec_pattern
|
||||
@ -19,7 +19,7 @@ module RuboCop
|
||||
# expect(foo).to be 1.0
|
||||
# expect(foo).to be(true)
|
||||
#
|
||||
class Be < Cop
|
||||
class Be < Base
|
||||
MSG = 'Don\'t use `be` without an argument.'
|
||||
|
||||
def_node_matcher :be_without_args, <<-PATTERN
|
||||
@ -35,7 +35,7 @@ module RuboCop
|
||||
# necessarily the same type as `b` since the `#==` operator can
|
||||
# coerce objects for comparison.
|
||||
#
|
||||
class BeEql < Cop
|
||||
class BeEql < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Prefer `be` over `eql`.'
|
||||
@ -23,7 +23,7 @@ module RuboCop
|
||||
# before(:each) { Widget.create }
|
||||
# after(:each) { Widget.delete_all }
|
||||
# end
|
||||
class BeforeAfterAll < Cop
|
||||
class BeforeAfterAll < Base
|
||||
MSG = 'Beware of using `%<hook>s` as it may cause state to leak '\
|
||||
'between tests. If you are using `rspec-rails`, and '\
|
||||
'`use_transactional_fixtures` is enabled, then records created '\
|
||||
@ -23,7 +23,7 @@ module RuboCop
|
||||
# expect(page).to have_current_path("/callback")
|
||||
# expect(page).to have_current_path(/widgets/)
|
||||
#
|
||||
class CurrentPathExpectation < Cop
|
||||
class CurrentPathExpectation < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Do not set an RSpec expectation on `current_path` in ' \
|
||||
@ -40,7 +40,7 @@ module RuboCop
|
||||
# # ...
|
||||
# end
|
||||
# end
|
||||
class FeatureMethods < Cop
|
||||
class FeatureMethods < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `%<replacement>s` instead of `%<method>s`.'
|
||||
@ -55,15 +55,18 @@ module RuboCop
|
||||
feature: :describe
|
||||
}.freeze
|
||||
|
||||
def_node_matcher :capybara_speak,
|
||||
SelectorSet.new(MAP.keys).node_pattern_union
|
||||
|
||||
def_node_matcher :spec?, <<-PATTERN
|
||||
(block
|
||||
(send #{RSPEC} {:describe :feature} ...)
|
||||
(send #rspec? {:describe :feature} ...)
|
||||
...)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :feature_method, <<-PATTERN
|
||||
(block
|
||||
$(send #{RSPEC} ${#{MAP.keys.map(&:inspect).join(' ')}} ...)
|
||||
$(send #rspec? $#capybara_speak ...)
|
||||
...)
|
||||
PATTERN
|
||||
|
||||
@ -26,7 +26,7 @@ module RuboCop
|
||||
# expect(page).to have_css('.foo', visible: :all)
|
||||
# expect(page).to have_link('my link', visible: :hidden)
|
||||
#
|
||||
class VisibilityMatcher < Cop
|
||||
class VisibilityMatcher < Base
|
||||
MSG_FALSE = 'Use `:all` or `:hidden` instead of `false`.'
|
||||
MSG_TRUE = 'Use `:visible` instead of `true`.'
|
||||
CAPYBARA_MATCHER_METHODS = %i[
|
||||
@ -23,13 +23,13 @@ module RuboCop
|
||||
# describe '.foo_bar' do
|
||||
# # ...
|
||||
# end
|
||||
class ContextMethod < Cop
|
||||
class ContextMethod < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `describe` for testing methods.'
|
||||
|
||||
def_node_matcher :context_method, <<-PATTERN
|
||||
(block (send #{RSPEC} :context $(str #method_name?) ...) ...)
|
||||
(block (send #rspec? :context $(str #method_name?) ...) ...)
|
||||
PATTERN
|
||||
|
||||
def on_block(node)
|
||||
@ -34,11 +34,11 @@ module RuboCop
|
||||
# context 'when the display name is not present' do
|
||||
# # ...
|
||||
# end
|
||||
class ContextWording < Cop
|
||||
class ContextWording < Base
|
||||
MSG = 'Start context description with %<prefixes>s.'
|
||||
|
||||
def_node_matcher :context_wording, <<-PATTERN
|
||||
(block (send #{RSPEC} { :context :shared_context } $(str #bad_prefix?) ...) ...)
|
||||
(block (send #rspec? { :context :shared_context } $(str #bad_prefix?) ...) ...)
|
||||
PATTERN
|
||||
|
||||
def on_block(node)
|
||||
@ -51,7 +51,7 @@ module RuboCop
|
||||
private
|
||||
|
||||
def bad_prefix?(description)
|
||||
!prefixes.include?(description.split.first)
|
||||
!prefixes.include?(description.split(/\b/).first)
|
||||
end
|
||||
|
||||
def joined_prefixes
|
||||
10
Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-rspec-1.43.1/lib/rubocop/cop/rspec/cop.rb
vendored
Normal file
10
Library/Homebrew/vendor/bundle/ruby/2.6.0/gems/rubocop-rspec-1.43.1/lib/rubocop/cop/rspec/cop.rb
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module RSpec
|
||||
# @deprecated Use ::RuboCop::Cop::RSpec::Base instead
|
||||
Cop = Base
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,63 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module RSpec
|
||||
# Check that the first argument to the top-level describe is a constant.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
# describe 'Do something' do
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# describe TestedClass do
|
||||
# subject { described_class }
|
||||
# end
|
||||
#
|
||||
# describe 'TestedClass::VERSION' do
|
||||
# subject { Object.const_get(self.class.description) }
|
||||
# end
|
||||
#
|
||||
# describe "A feature example", type: :feature do
|
||||
# end
|
||||
class DescribeClass < Base
|
||||
include RuboCop::RSpec::TopLevelGroup
|
||||
|
||||
MSG = 'The first argument to describe should be '\
|
||||
'the class or module being tested.'
|
||||
|
||||
def_node_matcher :rails_metadata?, <<-PATTERN
|
||||
(pair
|
||||
(sym :type)
|
||||
(sym { :channel :controller :helper :job :mailer :model :request
|
||||
:routing :view :feature :system :mailbox })
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :example_group_with_rails_metadata?, <<~PATTERN
|
||||
(send #rspec? :describe ... (hash <#rails_metadata? ...>))
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :not_a_const_described, <<~PATTERN
|
||||
(send #rspec? :describe $[!const !#string_constant?] ...)
|
||||
PATTERN
|
||||
|
||||
def on_top_level_group(top_level_node)
|
||||
return if example_group_with_rails_metadata?(top_level_node.send_node)
|
||||
|
||||
not_a_const_described(top_level_node.send_node) do |described|
|
||||
add_offense(described)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def string_constant?(described)
|
||||
described.str_type? &&
|
||||
described.value.match?(/^(?:(?:::)?[A-Z]\w*)+$/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -16,17 +16,25 @@ module RuboCop
|
||||
#
|
||||
# describe MyClass, '.my_class_method' do
|
||||
# end
|
||||
class DescribeMethod < Cop
|
||||
include RuboCop::RSpec::TopLevelDescribe
|
||||
class DescribeMethod < Base
|
||||
include RuboCop::RSpec::TopLevelGroup
|
||||
|
||||
MSG = 'The second argument to describe should be the method '\
|
||||
"being tested. '#instance' or '.class'."
|
||||
|
||||
def on_top_level_describe(_node, (_, second_arg))
|
||||
return unless second_arg&.str_type?
|
||||
return if second_arg.str_content.start_with?('#', '.')
|
||||
def_node_matcher :second_argument, <<~PATTERN
|
||||
(block
|
||||
(send #rspec? :describe _first_argument $(str _) ...) ...
|
||||
)
|
||||
PATTERN
|
||||
|
||||
add_offense(second_arg)
|
||||
def on_top_level_group(node)
|
||||
second_argument = second_argument(node)
|
||||
|
||||
return unless second_argument
|
||||
return if second_argument.str_content.start_with?('#', '.')
|
||||
|
||||
add_offense(second_argument)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -17,11 +17,11 @@ module RuboCop
|
||||
# end
|
||||
#
|
||||
# @see https://github.com/rspec/rspec-core/issues/1610
|
||||
class DescribeSymbol < Cop
|
||||
class DescribeSymbol < Base
|
||||
MSG = 'Avoid describing symbols.'
|
||||
|
||||
def_node_matcher :describe_symbol?, <<-PATTERN
|
||||
(send #{RSPEC} :describe $sym ...)
|
||||
(send #rspec? :describe $sym ...)
|
||||
PATTERN
|
||||
|
||||
def on_send(node)
|
||||
@ -54,7 +54,7 @@ module RuboCop
|
||||
# end
|
||||
# end
|
||||
#
|
||||
class DescribedClass < Cop
|
||||
class DescribedClass < Base
|
||||
extend AutoCorrector
|
||||
include ConfigurableEnforcedStyle
|
||||
|
||||
@ -142,7 +142,7 @@ module RuboCop
|
||||
if style == :described_class
|
||||
offensive_described_class?(node)
|
||||
else
|
||||
node.send_type? && node.method_name == :described_class
|
||||
node.send_type? && node.method?(:described_class)
|
||||
end
|
||||
end
|
||||
|
||||
@ -19,7 +19,7 @@ module RuboCop
|
||||
# end
|
||||
#
|
||||
# @see https://github.com/rubocop-hq/rubocop-rspec/issues/735
|
||||
class DescribedClassModuleWrapping < Cop
|
||||
class DescribedClassModuleWrapping < Base
|
||||
MSG = 'Avoid opening modules and defining specs within them.'
|
||||
|
||||
def_node_search :find_rspec_blocks,
|
||||
@ -41,7 +41,7 @@ module RuboCop
|
||||
# describe 'display name presence' do
|
||||
# # ...
|
||||
# end
|
||||
class Dialect < Cop
|
||||
class Dialect < Base
|
||||
extend AutoCorrector
|
||||
include MethodPreference
|
||||
|
||||
@ -0,0 +1,174 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module RSpec
|
||||
# Checks if an example group does not include any tests.
|
||||
#
|
||||
# This cop is configurable using the `CustomIncludeMethods` option
|
||||
#
|
||||
# @example usage
|
||||
#
|
||||
# # bad
|
||||
# describe Bacon do
|
||||
# let(:bacon) { Bacon.new(chunkiness) }
|
||||
# let(:chunkiness) { false }
|
||||
#
|
||||
# context 'extra chunky' do # flagged by rubocop
|
||||
# let(:chunkiness) { true }
|
||||
# end
|
||||
#
|
||||
# it 'is chunky' do
|
||||
# expect(bacon.chunky?).to be_truthy
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # good
|
||||
# describe Bacon do
|
||||
# let(:bacon) { Bacon.new(chunkiness) }
|
||||
# let(:chunkiness) { false }
|
||||
#
|
||||
# it 'is chunky' do
|
||||
# expect(bacon.chunky?).to be_truthy
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# @example configuration
|
||||
#
|
||||
# # .rubocop.yml
|
||||
# # RSpec/EmptyExampleGroup:
|
||||
# # CustomIncludeMethods:
|
||||
# # - include_tests
|
||||
#
|
||||
# # spec_helper.rb
|
||||
# RSpec.configure do |config|
|
||||
# config.alias_it_behaves_like_to(:include_tests)
|
||||
# end
|
||||
#
|
||||
# # bacon_spec.rb
|
||||
# describe Bacon do
|
||||
# let(:bacon) { Bacon.new(chunkiness) }
|
||||
# let(:chunkiness) { false }
|
||||
#
|
||||
# context 'extra chunky' do # not flagged by rubocop
|
||||
# let(:chunkiness) { true }
|
||||
#
|
||||
# include_tests 'shared tests'
|
||||
# end
|
||||
# end
|
||||
#
|
||||
class EmptyExampleGroup < Base
|
||||
MSG = 'Empty example group detected.'
|
||||
|
||||
# @!method example_group_body(node)
|
||||
# Match example group blocks and yield their body
|
||||
#
|
||||
# @example source that matches
|
||||
# describe 'example group' do
|
||||
# it { is_expected.to be }
|
||||
# end
|
||||
#
|
||||
# @param node [RuboCop::AST::Node]
|
||||
# @yield [RuboCop::AST::Node] example group body
|
||||
def_node_matcher :example_group_body, <<~PATTERN
|
||||
(block #{ExampleGroups::ALL.send_pattern} args $_)
|
||||
PATTERN
|
||||
|
||||
# @!method example_or_group_or_include?(node)
|
||||
# Match examples, example groups and includes
|
||||
#
|
||||
# @example source that matches
|
||||
# it { is_expected.to fly }
|
||||
# describe('non-empty example groups too') { }
|
||||
# it_behaves_like 'an animal'
|
||||
# it_behaves_like('a cat') { let(:food) { 'milk' } }
|
||||
# it_has_root_access
|
||||
#
|
||||
# @param node [RuboCop::AST::Node]
|
||||
# @return [Array<RuboCop::AST::Node>] matching nodes
|
||||
def_node_matcher :example_or_group_or_include?, <<~PATTERN
|
||||
{
|
||||
#{Examples::ALL.block_pattern}
|
||||
#{ExampleGroups::ALL.block_pattern}
|
||||
#{Includes::ALL.send_pattern}
|
||||
#{Includes::ALL.block_pattern}
|
||||
(send nil? #custom_include? ...)
|
||||
}
|
||||
PATTERN
|
||||
|
||||
# @!method examples_inside_block?(node)
|
||||
# Match examples defined inside a block which is not a hook
|
||||
#
|
||||
# @example source that matches
|
||||
# %w(r g b).each do |color|
|
||||
# it { is_expected.to have_color(color) }
|
||||
# end
|
||||
#
|
||||
# @example source that does not match
|
||||
# before do
|
||||
# it { is_expected.to fall_into_oblivion }
|
||||
# end
|
||||
#
|
||||
# @param node [RuboCop::AST::Node]
|
||||
# @return [Array<RuboCop::AST::Node>] matching nodes
|
||||
def_node_matcher :examples_inside_block?, <<~PATTERN
|
||||
(block !#{Hooks::ALL.send_pattern} _ #examples?)
|
||||
PATTERN
|
||||
|
||||
# @!method examples_directly_or_in_block?(node)
|
||||
# Match examples or examples inside blocks
|
||||
#
|
||||
# @example source that matches
|
||||
# it { expect(drink).to be_cold }
|
||||
# context('when winter') { it { expect(drink).to be_hot } }
|
||||
# (1..5).each { |divisor| it { is_expected.to divide_by(divisor) } }
|
||||
#
|
||||
# @param node [RuboCop::AST::Node]
|
||||
# @return [Array<RuboCop::AST::Node>] matching nodes
|
||||
def_node_matcher :examples_directly_or_in_block?, <<~PATTERN
|
||||
{
|
||||
#example_or_group_or_include?
|
||||
#examples_inside_block?
|
||||
}
|
||||
PATTERN
|
||||
|
||||
# @!method examples?(node)
|
||||
# Matches examples defined in scopes where they could run
|
||||
#
|
||||
# @example source that matches
|
||||
# it { expect(myself).to be_run }
|
||||
# describe { it { i_run_as_well } }
|
||||
#
|
||||
# @example source that does not match
|
||||
# before { it { whatever here wont run anyway } }
|
||||
#
|
||||
# @param node [RuboCop::AST::Node]
|
||||
# @return [Array<RuboCop::AST::Node>] matching nodes
|
||||
def_node_matcher :examples?, <<~PATTERN
|
||||
{
|
||||
#examples_directly_or_in_block?
|
||||
(begin <#examples_directly_or_in_block? ...>)
|
||||
}
|
||||
PATTERN
|
||||
|
||||
def on_block(node)
|
||||
example_group_body(node) do |body|
|
||||
add_offense(node.send_node) unless examples?(body)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def custom_include?(method_name)
|
||||
custom_include_methods.include?(method_name)
|
||||
end
|
||||
|
||||
def custom_include_methods
|
||||
cop_config
|
||||
.fetch('CustomIncludeMethods', [])
|
||||
.map(&:to_sym)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -22,7 +22,7 @@ module RuboCop
|
||||
# create_feed
|
||||
# end
|
||||
# after(:all) { cleanup_feed }
|
||||
class EmptyHook < Cop
|
||||
class EmptyHook < Base
|
||||
extend AutoCorrector
|
||||
include RuboCop::Cop::RangeHelp
|
||||
|
||||
@ -41,22 +41,18 @@ module RuboCop
|
||||
# it { two }
|
||||
# end
|
||||
#
|
||||
class EmptyLineAfterExample < Cop
|
||||
class EmptyLineAfterExample < Base
|
||||
extend AutoCorrector
|
||||
include RuboCop::RSpec::BlankLineSeparation
|
||||
include RuboCop::RSpec::EmptyLineSeparation
|
||||
|
||||
MSG = 'Add an empty line after `%<example>s`.'
|
||||
|
||||
def on_block(node)
|
||||
return unless example?(node)
|
||||
return if last_child?(node)
|
||||
return if allowed_one_liner?(node)
|
||||
|
||||
missing_separating_line(node) do |location|
|
||||
msg = format(MSG, example: node.method_name)
|
||||
add_offense(location, message: msg) do |corrector|
|
||||
corrector.insert_after(location.end, "\n")
|
||||
end
|
||||
missing_separating_line_offense(node) do |method|
|
||||
format(MSG, example: method)
|
||||
end
|
||||
end
|
||||
|
||||
@ -23,21 +23,17 @@ module RuboCop
|
||||
# end
|
||||
# end
|
||||
#
|
||||
class EmptyLineAfterExampleGroup < Cop
|
||||
class EmptyLineAfterExampleGroup < Base
|
||||
extend AutoCorrector
|
||||
include RuboCop::RSpec::BlankLineSeparation
|
||||
include RuboCop::RSpec::EmptyLineSeparation
|
||||
|
||||
MSG = 'Add an empty line after `%<example_group>s`.'
|
||||
|
||||
def on_block(node)
|
||||
return unless example_group?(node)
|
||||
return if last_child?(node)
|
||||
|
||||
missing_separating_line(node) do |location|
|
||||
msg = format(MSG, example_group: node.method_name)
|
||||
add_offense(location, message: msg) do |corrector|
|
||||
corrector.insert_after(location.end, "\n")
|
||||
end
|
||||
missing_separating_line_offense(node) do |method|
|
||||
format(MSG, example_group: method)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -16,24 +16,21 @@ module RuboCop
|
||||
# let(:something) { other }
|
||||
#
|
||||
# it { does_something }
|
||||
class EmptyLineAfterFinalLet < Cop
|
||||
class EmptyLineAfterFinalLet < Base
|
||||
extend AutoCorrector
|
||||
include RuboCop::RSpec::BlankLineSeparation
|
||||
include RuboCop::RSpec::EmptyLineSeparation
|
||||
|
||||
MSG = 'Add an empty line after the last `let` block.'
|
||||
MSG = 'Add an empty line after the last `%<let>s`.'
|
||||
|
||||
def on_block(node)
|
||||
return unless example_group_with_body?(node)
|
||||
|
||||
latest_let = node.body.child_nodes.select { |child| let?(child) }.last
|
||||
final_let = node.body.child_nodes.reverse.find { |child| let?(child) }
|
||||
|
||||
return if latest_let.nil?
|
||||
return if last_child?(latest_let)
|
||||
return if final_let.nil?
|
||||
|
||||
missing_separating_line(latest_let) do |location|
|
||||
add_offense(location) do |corrector|
|
||||
corrector.insert_after(location.end, "\n")
|
||||
end
|
||||
missing_separating_line_offense(final_let) do |method|
|
||||
format(MSG, let: method)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -33,21 +33,17 @@ module RuboCop
|
||||
#
|
||||
# it { does_something }
|
||||
#
|
||||
class EmptyLineAfterHook < Cop
|
||||
class EmptyLineAfterHook < Base
|
||||
extend AutoCorrector
|
||||
include RuboCop::RSpec::BlankLineSeparation
|
||||
include RuboCop::RSpec::EmptyLineSeparation
|
||||
|
||||
MSG = 'Add an empty line after `%<hook>s`.'
|
||||
|
||||
def on_block(node)
|
||||
return unless hook?(node)
|
||||
return if last_child?(node)
|
||||
|
||||
missing_separating_line(node) do |location|
|
||||
msg = format(MSG, hook: node.method_name)
|
||||
add_offense(location, message: msg) do |corrector|
|
||||
corrector.insert_after(location.end, "\n")
|
||||
end
|
||||
missing_separating_line_offense(node) do |method|
|
||||
format(MSG, hook: method)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -14,20 +14,17 @@ module RuboCop
|
||||
# subject(:obj) { described_class }
|
||||
#
|
||||
# let(:foo) { bar }
|
||||
class EmptyLineAfterSubject < Cop
|
||||
class EmptyLineAfterSubject < Base
|
||||
extend AutoCorrector
|
||||
include RuboCop::RSpec::BlankLineSeparation
|
||||
include RuboCop::RSpec::EmptyLineSeparation
|
||||
|
||||
MSG = 'Add empty line after `subject`.'
|
||||
MSG = 'Add an empty line after `%<subject>s`.'
|
||||
|
||||
def on_block(node)
|
||||
return unless subject?(node) && !in_spec_block?(node)
|
||||
return if last_child?(node)
|
||||
|
||||
missing_separating_line(node) do |location|
|
||||
add_offense(location) do |corrector|
|
||||
corrector.insert_after(location.end, "\n")
|
||||
end
|
||||
missing_separating_line_offense(node) do |method|
|
||||
format(MSG, subject: method)
|
||||
end
|
||||
end
|
||||
|
||||
@ -25,7 +25,7 @@ module RuboCop
|
||||
# result = service.call
|
||||
# expect(result).to be(true)
|
||||
# end
|
||||
class ExampleLength < Cop
|
||||
class ExampleLength < Base
|
||||
include CodeLength
|
||||
|
||||
MSG = 'Example has too many lines [%<total>d/%<max>d].'
|
||||
@ -47,7 +47,7 @@ module RuboCop
|
||||
# result = service.call
|
||||
# expect(result).to be(true)
|
||||
# end
|
||||
class ExampleWithoutDescription < Cop
|
||||
class ExampleWithoutDescription < Base
|
||||
include ConfigurableEnforcedStyle
|
||||
|
||||
MSG_DEFAULT_ARGUMENT = 'Omit the argument when you want to ' \
|
||||
@ -29,7 +29,7 @@ module RuboCop
|
||||
# # good
|
||||
# it 'does things' do
|
||||
# end
|
||||
class ExampleWording < Cop
|
||||
class ExampleWording < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG_SHOULD = 'Do not use should when describing your tests.'
|
||||
@ -47,9 +47,9 @@ module RuboCop
|
||||
|
||||
def on_block(node)
|
||||
it_description(node) do |description_node, message|
|
||||
if message =~ SHOULD_PREFIX
|
||||
if message.match?(SHOULD_PREFIX)
|
||||
add_wording_offense(description_node, MSG_SHOULD)
|
||||
elsif message =~ IT_PREFIX
|
||||
elsif message.match?(IT_PREFIX)
|
||||
add_wording_offense(description_node, MSG_IT)
|
||||
end
|
||||
end
|
||||
@ -77,7 +77,7 @@ module RuboCop
|
||||
def replacement_text(node)
|
||||
text = text(node)
|
||||
|
||||
if text =~ SHOULD_PREFIX
|
||||
if text.match?(SHOULD_PREFIX)
|
||||
RuboCop::RSpec::Wording.new(
|
||||
text,
|
||||
ignore: ignored_words,
|
||||
@ -16,7 +16,7 @@ module RuboCop
|
||||
# expect(pattern).to eq(/foo/)
|
||||
# expect(name).to eq("John")
|
||||
#
|
||||
class ExpectActual < Cop
|
||||
class ExpectActual < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Provide the actual you are testing to `expect(...)`.'
|
||||
@ -29,7 +29,7 @@ module RuboCop
|
||||
# expect { run }.to change { Foo.bar(:count) }
|
||||
# expect { run }.to change { user.reload.name }
|
||||
#
|
||||
class ExpectChange < Cop
|
||||
class ExpectChange < Base
|
||||
extend AutoCorrector
|
||||
include ConfigurableEnforcedStyle
|
||||
|
||||
@ -20,7 +20,7 @@ module RuboCop
|
||||
# it do
|
||||
# expect(something).to eq 'foo'
|
||||
# end
|
||||
class ExpectInHook < Cop
|
||||
class ExpectInHook < Base
|
||||
MSG = 'Do not use `%<expect>s` in `%<hook>s` hook'
|
||||
|
||||
def_node_search :expectation, Expectations::ALL.send_pattern
|
||||
@ -14,7 +14,7 @@ module RuboCop
|
||||
#
|
||||
# # good
|
||||
# expect { my_app.print_report }.to output('Hello World').to_stdout
|
||||
class ExpectOutput < Cop
|
||||
class ExpectOutput < Base
|
||||
MSG = 'Use `expect { ... }.to output(...).to_%<name>s` '\
|
||||
'instead of mutating $%<name>s.'
|
||||
|
||||
@ -24,7 +24,7 @@ module RuboCop
|
||||
#
|
||||
# # good
|
||||
# count { 1 }
|
||||
class AttributeDefinedStatically < Cop
|
||||
class AttributeDefinedStatically < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use a block to declare attribute values.'
|
||||
@ -39,7 +39,7 @@ module RuboCop
|
||||
|
||||
def on_block(node)
|
||||
attributes = factory_attributes(node) || []
|
||||
attributes = [attributes] unless attributes.is_a?(Array)
|
||||
attributes = [attributes] unless attributes.is_a?(Array) # rubocop:disable Style/ArrayCoercion, Lint/RedundantCopDisableDirective
|
||||
|
||||
attributes.each do |attribute|
|
||||
next unless offensive_receiver?(attribute.receiver, node)
|
||||
@ -84,7 +84,7 @@ module RuboCop
|
||||
def autocorrect_replacing_parens(corrector, node)
|
||||
left_braces, right_braces = braces(node)
|
||||
|
||||
corrector.replace(node.location.begin, ' ' + left_braces)
|
||||
corrector.replace(node.location.begin, " #{left_braces}")
|
||||
corrector.replace(node.location.end, right_braces)
|
||||
end
|
||||
|
||||
@ -24,7 +24,7 @@ module RuboCop
|
||||
#
|
||||
# # good
|
||||
# 3.times { create :user }
|
||||
class CreateList < Cop
|
||||
class CreateList < Base
|
||||
extend AutoCorrector
|
||||
include ConfigurableEnforcedStyle
|
||||
|
||||
@ -44,7 +44,7 @@ module RuboCop
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :factory_list_call, <<-PATTERN
|
||||
(send ${(const nil? {:FactoryGirl :FactoryBot}) nil?} :create_list (sym $_) (int $_) $...)
|
||||
(send {(const nil? {:FactoryGirl :FactoryBot}) nil?} :create_list (sym _) (int $_) ...)
|
||||
PATTERN
|
||||
|
||||
def on_block(node)
|
||||
@ -60,7 +60,7 @@ module RuboCop
|
||||
def on_send(node)
|
||||
return unless style == :n_times
|
||||
|
||||
factory_list_call(node) do |_receiver, _factory, count, _|
|
||||
factory_list_call(node) do |count|
|
||||
message = format(MSG_N_TIMES, number: count)
|
||||
add_offense(node.loc.selector, message: message) do |corrector|
|
||||
TimesCorrector.new(node).call(corrector)
|
||||
@ -79,7 +79,7 @@ module RuboCop
|
||||
end
|
||||
|
||||
# :nodoc
|
||||
class Corrector
|
||||
module Corrector
|
||||
private
|
||||
|
||||
def build_options_string(options)
|
||||
@ -102,7 +102,9 @@ module RuboCop
|
||||
end
|
||||
|
||||
# :nodoc
|
||||
class TimesCorrector < Corrector
|
||||
class TimesCorrector
|
||||
include Corrector
|
||||
|
||||
def initialize(node)
|
||||
@node = node
|
||||
end
|
||||
@ -130,7 +132,9 @@ module RuboCop
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
class CreateListCorrector < Corrector
|
||||
class CreateListCorrector
|
||||
include Corrector
|
||||
|
||||
def initialize(node)
|
||||
@node = node.parent
|
||||
end
|
||||
@ -19,7 +19,7 @@ module RuboCop
|
||||
# # good
|
||||
# factory :foo, class: 'Foo' do
|
||||
# end
|
||||
class FactoryClassName < Cop
|
||||
class FactoryClassName < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = "Pass '%<class_name>s' string instead of `%<class_name>s` " \
|
||||
@ -56,35 +56,43 @@ module RuboCop
|
||||
# # good
|
||||
# my_class_spec.rb # describe MyClass, '#method'
|
||||
#
|
||||
class FilePath < Cop
|
||||
include RuboCop::RSpec::TopLevelDescribe
|
||||
class FilePath < Base
|
||||
include RuboCop::RSpec::TopLevelGroup
|
||||
|
||||
MSG = 'Spec path should end with `%<suffix>s`.'
|
||||
|
||||
def_node_search :const_described?, '(send _ :describe (const ...) ...)'
|
||||
def_node_matcher :const_described, <<~PATTERN
|
||||
(block
|
||||
$(send #rspec? _example_group $(const ...) $...) ...
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def_node_search :routing_metadata?, '(pair (sym :type) (sym :routing))'
|
||||
|
||||
def on_top_level_describe(node, args)
|
||||
return unless const_described?(node) && single_top_level_describe?
|
||||
return if routing_spec?(args)
|
||||
def on_top_level_group(node)
|
||||
return unless top_level_groups.one?
|
||||
|
||||
glob = glob_for(args)
|
||||
const_described(node) do |send_node, described_class, arguments|
|
||||
next if routing_spec?(arguments)
|
||||
|
||||
return if filename_ends_with?(glob)
|
||||
|
||||
add_offense(
|
||||
node,
|
||||
message: format(MSG, suffix: glob)
|
||||
)
|
||||
ensure_correct_file_path(send_node, described_class, arguments)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_correct_file_path(send_node, described_class, arguments)
|
||||
glob = glob_for(described_class, arguments.first)
|
||||
return if filename_ends_with?(glob)
|
||||
|
||||
add_offense(send_node, message: format(MSG, suffix: glob))
|
||||
end
|
||||
|
||||
def routing_spec?(args)
|
||||
args.any?(&method(:routing_metadata?))
|
||||
end
|
||||
|
||||
def glob_for((described_class, method_name))
|
||||
def glob_for(described_class, method_name)
|
||||
return glob_for_spec_suffix_only? if spec_suffix_only?
|
||||
|
||||
"#{expected_path(described_class)}#{name_glob(method_name)}*_spec.rb"
|
||||
@ -94,10 +102,10 @@ module RuboCop
|
||||
'*_spec.rb'
|
||||
end
|
||||
|
||||
def name_glob(name)
|
||||
return unless name&.str_type?
|
||||
def name_glob(method_name)
|
||||
return unless method_name&.str_type?
|
||||
|
||||
"*#{name.str_content.gsub(/\W/, '')}" unless ignore_methods?
|
||||
"*#{method_name.str_content.gsub(/\W/, '')}" unless ignore_methods?
|
||||
end
|
||||
|
||||
def expected_path(constant)
|
||||
@ -19,23 +19,19 @@ module RuboCop
|
||||
# # good
|
||||
# describe MyClass do
|
||||
# end
|
||||
class Focus < Cop
|
||||
class Focus < Base
|
||||
MSG = 'Focused spec found.'
|
||||
|
||||
focusable =
|
||||
ExampleGroups::GROUPS +
|
||||
ExampleGroups::SKIPPED +
|
||||
Examples::EXAMPLES +
|
||||
Examples::SKIPPED +
|
||||
Examples::PENDING
|
||||
|
||||
focused = ExampleGroups::FOCUSED + Examples::FOCUSED
|
||||
|
||||
FOCUSABLE_SELECTORS = focusable.node_pattern_union
|
||||
def_node_matcher :focusable_selector?,
|
||||
(ExampleGroups::GROUPS + ExampleGroups::SKIPPED +
|
||||
Examples::EXAMPLES + Examples::SKIPPED +
|
||||
Examples::PENDING).node_pattern_union
|
||||
|
||||
def_node_matcher :metadata, <<-PATTERN
|
||||
{(send #{RSPEC} #{FOCUSABLE_SELECTORS} <$(sym :focus) ...>)
|
||||
(send #{RSPEC} #{FOCUSABLE_SELECTORS} ... (hash <$(pair (sym :focus) true) ...>))}
|
||||
{(send #rspec? #focusable_selector? <$(sym :focus) ...>)
|
||||
(send #rspec? #focusable_selector? ... (hash <$(pair (sym :focus) true) ...>))}
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :focused_block?, focused.send_pattern
|
||||
@ -57,21 +57,20 @@ module RuboCop
|
||||
# before(:example) do
|
||||
# # ...
|
||||
# end
|
||||
class HookArgument < Cop
|
||||
class HookArgument < Base
|
||||
extend AutoCorrector
|
||||
include ConfigurableEnforcedStyle
|
||||
|
||||
IMPLICIT_MSG = 'Omit the default `%<scope>p` ' \
|
||||
'argument for RSpec hooks.'
|
||||
IMPLICIT_MSG = 'Omit the default `%<scope>p` argument for RSpec hooks.'
|
||||
EXPLICIT_MSG = 'Use `%<scope>p` for RSpec hooks.'
|
||||
|
||||
HOOKS = Hooks::ALL.node_pattern_union.freeze
|
||||
def_node_matcher :hook?, Hooks::ALL.node_pattern_union
|
||||
|
||||
def_node_matcher :scoped_hook, <<-PATTERN
|
||||
(block $(send _ #{HOOKS} (sym ${:each :example})) ...)
|
||||
(block $(send _ #hook? (sym ${:each :example})) ...)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :unscoped_hook, "(block $(send _ #{HOOKS}) ...)"
|
||||
def_node_matcher :unscoped_hook, '(block $(send _ #hook?) ...)'
|
||||
|
||||
def on_block(node)
|
||||
hook(node) do |method_send, scope_name|
|
||||
@ -23,7 +23,7 @@ module RuboCop
|
||||
# expect(foo).to be
|
||||
# end
|
||||
#
|
||||
class HooksBeforeExamples < Cop
|
||||
class HooksBeforeExamples < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Move `%<hook>s` above the examples in the group.'
|
||||
@ -16,7 +16,7 @@ module RuboCop
|
||||
# it 'changes something to a new value' do
|
||||
# expect { do_something }.to change(something).to(new_value)
|
||||
# end
|
||||
class ImplicitBlockExpectation < Cop
|
||||
class ImplicitBlockExpectation < Base
|
||||
MSG = 'Avoid implicit block expectations.'
|
||||
|
||||
def_node_matcher :lambda?, <<-PATTERN
|
||||
@ -24,7 +24,7 @@ module RuboCop
|
||||
# # good
|
||||
# it { should be_truthy }
|
||||
#
|
||||
class ImplicitExpect < Cop
|
||||
class ImplicitExpect < Base
|
||||
extend AutoCorrector
|
||||
include ConfigurableEnforcedStyle
|
||||
|
||||
@ -26,7 +26,7 @@ module RuboCop
|
||||
# # good
|
||||
# it { expect(subject).to be_truthy }
|
||||
#
|
||||
class ImplicitSubject < Cop
|
||||
class ImplicitSubject < Base
|
||||
extend AutoCorrector
|
||||
include ConfigurableEnforcedStyle
|
||||
|
||||
@ -49,9 +49,10 @@ module RuboCop
|
||||
|
||||
def autocorrect(corrector, node)
|
||||
replacement = 'expect(subject)'
|
||||
if node.method_name == :should
|
||||
case node.method_name
|
||||
when :should
|
||||
replacement += '.to'
|
||||
elsif node.method_name == :should_not
|
||||
when :should_not
|
||||
replacement += '.not_to'
|
||||
end
|
||||
|
||||
@ -62,13 +63,14 @@ module RuboCop
|
||||
example = node.ancestors.find { |parent| example?(parent) }
|
||||
return false if example.nil?
|
||||
|
||||
example.method_name == :its || allowed_by_style?(example)
|
||||
example.method?(:its) || allowed_by_style?(example)
|
||||
end
|
||||
|
||||
def allowed_by_style?(example)
|
||||
if style == :single_line_only
|
||||
case style
|
||||
when :single_line_only
|
||||
example.single_line?
|
||||
elsif style == :single_statement_only
|
||||
when :single_statement_only
|
||||
!example.body.begin_type?
|
||||
else
|
||||
false
|
||||
@ -18,7 +18,7 @@ module RuboCop
|
||||
# expect(foo).to have_received(:bar)
|
||||
# end
|
||||
#
|
||||
class InstanceSpy < Cop
|
||||
class InstanceSpy < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `instance_spy` when you check your double '\
|
||||
@ -46,7 +46,7 @@ module RuboCop
|
||||
# it { expect(foo).to be_empty }
|
||||
# end
|
||||
#
|
||||
class InstanceVariable < Cop
|
||||
class InstanceVariable < Base
|
||||
include RuboCop::RSpec::TopLevelGroup
|
||||
|
||||
MSG = 'Avoid instance variables – use let, ' \
|
||||
@ -15,7 +15,7 @@ module RuboCop
|
||||
#
|
||||
# # good
|
||||
# expect(foo).to be_something
|
||||
class InvalidPredicateMatcher < Cop
|
||||
class InvalidPredicateMatcher < Base
|
||||
MSG = 'Omit `?` from `%<matcher>s`.'
|
||||
|
||||
def_node_matcher :invalid_predicate_matcher?, <<-PATTERN
|
||||
@ -18,7 +18,7 @@ module RuboCop
|
||||
#
|
||||
# # good
|
||||
# it_should_behave_like 'a foo'
|
||||
class ItBehavesLike < Cop
|
||||
class ItBehavesLike < Base
|
||||
extend AutoCorrector
|
||||
include ConfigurableEnforcedStyle
|
||||
|
||||
@ -15,7 +15,7 @@ module RuboCop
|
||||
# it 'validates users' do
|
||||
# expect([user1, user2, user3]).to all(be_valid)
|
||||
# end
|
||||
class IteratedExpectation < Cop
|
||||
class IteratedExpectation < Base
|
||||
MSG = 'Prefer using the `all` matcher instead ' \
|
||||
'of iterating over an array.'
|
||||
|
||||
@ -31,7 +31,7 @@ module RuboCop
|
||||
# it { expect_something }
|
||||
# it { expect_something_else }
|
||||
#
|
||||
class LeadingSubject < Cop
|
||||
class LeadingSubject < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Declare `subject` above any other `%<offending>s` declarations.'
|
||||
@ -43,33 +43,36 @@ module RuboCop
|
||||
end
|
||||
|
||||
def check_previous_nodes(node)
|
||||
node.parent.each_child_node do |sibling|
|
||||
if offending?(sibling)
|
||||
msg = format(MSG, offending: sibling.method_name)
|
||||
offending_node(node) do |offender|
|
||||
msg = format(MSG, offending: offender.method_name)
|
||||
add_offense(node, message: msg) do |corrector|
|
||||
autocorrect(corrector, node)
|
||||
autocorrect(corrector, node, offender)
|
||||
end
|
||||
end
|
||||
|
||||
break if offending?(sibling) || sibling.equal?(node)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def autocorrect(corrector, node)
|
||||
first_node = find_first_offending_node(node)
|
||||
def offending_node(node)
|
||||
node.parent.each_child_node.find do |sibling|
|
||||
break if sibling.equal?(node)
|
||||
|
||||
yield sibling if offending?(sibling)
|
||||
end
|
||||
end
|
||||
|
||||
def autocorrect(corrector, node, sibling)
|
||||
RuboCop::RSpec::Corrector::MoveNode.new(
|
||||
node, corrector, processed_source
|
||||
).move_before(first_node)
|
||||
).move_before(sibling)
|
||||
end
|
||||
|
||||
def offending?(node)
|
||||
let?(node) || hook?(node) || example?(node)
|
||||
end
|
||||
|
||||
def find_first_offending_node(node)
|
||||
node.parent.children.find { |sibling| offending?(sibling) }
|
||||
let?(node) ||
|
||||
hook?(node) ||
|
||||
example?(node) ||
|
||||
spec_group?(node) ||
|
||||
include?(node)
|
||||
end
|
||||
|
||||
def in_spec_block?(node)
|
||||
@ -93,7 +93,7 @@ module RuboCop
|
||||
# stub_const('SomeModule::SomeClass', foo_class)
|
||||
# end
|
||||
# end
|
||||
class LeakyConstantDeclaration < Cop
|
||||
class LeakyConstantDeclaration < Base
|
||||
MSG_CONST = 'Stub constant instead of declaring explicitly.'
|
||||
MSG_CLASS = 'Stub class constant instead of declaring explicitly.'
|
||||
MSG_MODULE = 'Stub module constant instead of declaring explicitly.'
|
||||
@ -30,7 +30,7 @@ module RuboCop
|
||||
# it 'checks what some does' do
|
||||
# expect(some).to be
|
||||
# end
|
||||
class LetBeforeExamples < Cop
|
||||
class LetBeforeExamples < Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Move `let` before the examples in the group.'
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user