Merge pull request #8137 from waldyrious/add-desc-stanza-to-cask-dsl

Add a `desc` stanza to the cask DSL
This commit is contained in:
Markus Reiter 2020-08-09 01:25:32 +02:00 committed by GitHub
commit d5dfe4d6a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 406 additions and 242 deletions

View File

@ -1,5 +1,9 @@
inherit_from: ./Homebrew/.rubocop.yml inherit_from: ./Homebrew/.rubocop.yml
Cask/Desc:
Description: 'Ensure that the desc stanza conforms to various content and style checks.'
Enabled: true
Cask/HomepageMatchesUrl: Cask/HomepageMatchesUrl:
Description: 'Ensure that the homepage and url match, otherwise add a comment. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/stanzas/url.md#when-url-and-homepage-hostnames-differ-add-a-comment' Description: 'Ensure that the homepage and url match, otherwise add a comment. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/stanzas/url.md#when-url-and-homepage-hostnames-differ-add-a-comment'
Enabled: true Enabled: true
@ -13,11 +17,11 @@ Cask/NoDslVersion:
Enabled: true Enabled: true
Cask/StanzaGrouping: Cask/StanzaGrouping:
Description: 'Ensure that cask stanzas are grouped correctly. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/CONTRIBUTING.md#stanza-order' Description: 'Ensure that cask stanzas are grouped correctly. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/readme.md#stanza-order'
Enabled: true Enabled: true
Cask/StanzaOrder: Cask/StanzaOrder:
Description: 'Ensure that cask stanzas are sorted correctly. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/CONTRIBUTING.md#stanza-order' Description: 'Ensure that cask stanzas are sorted correctly. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/readme.md#stanza-order'
Enabled: true Enabled: true
# don't want these for casks but re-enabled for Library/Homebrew # don't want these for casks but re-enabled for Library/Homebrew

View File

@ -155,6 +155,7 @@ module Cask
{ {
"token" => token, "token" => token,
"name" => name, "name" => name,
"desc" => desc,
"homepage" => homepage, "homepage" => homepage,
"url" => url, "url" => url,
"appcast" => appcast, "appcast" => appcast,

View File

@ -30,6 +30,7 @@ module Cask
url "https://" url "https://"
name "" name ""
desc ""
homepage "" homepage ""
app "" app ""

View File

@ -64,6 +64,7 @@ module Cask
:caveats, :caveats,
:conflicts_with, :conflicts_with,
:container, :container,
:desc,
:depends_on, :depends_on,
:homepage, :homepage,
:language, :language,
@ -93,6 +94,10 @@ module Cask
@name.concat(args.flatten) @name.concat(args.flatten)
end end
def desc(description = nil)
set_unique_stanza(:desc, description.nil?) { description }
end
def set_unique_stanza(stanza, should_return) def set_unique_stanza(stanza, should_return)
return instance_variable_get("@#{stanza}") if should_return return instance_variable_get("@#{stanza}") if should_return

View File

@ -29,7 +29,13 @@ module RuboCop
end end
def cask_token def cask_token
@cask_token ||= pair_node.val_node.children.first @cask_token ||= begin
if dsl_version?
pair_node.val_node.children.first
else
method_node.first_argument.str_content
end
end
end end
def hash_node def hash_node

View File

@ -6,7 +6,7 @@ module RuboCop
module Constants module Constants
STANZA_GROUPS = [ STANZA_GROUPS = [
[:version, :sha256], [:version, :sha256],
[:url, :appcast, :name, :homepage], [:url, :appcast, :desc, :name, :homepage],
[ [
:auto_updates, :auto_updates,
:conflicts_with, :conflicts_with,

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
require "forwardable"
require "uri"
require "rubocops/cask/mixin/on_desc_stanza"
require "rubocops/shared/desc_helper"
module RuboCop
module Cop
module Cask
# This cop audits `desc` in Casks.
# See the `DescHelper` module for details of the checks.
class Desc < Cop
include OnDescStanza
include DescHelper
def on_desc_stanza(stanza)
name = cask_block.header.cask_token
desc_call = stanza.stanza_node
audit_desc(:cask, name, desc_call)
end
def autocorrect(node)
name = cask_block.header.cask_token
autocorrect_desc(node, name)
end
end
end
end
end

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
module RuboCop
module Cop
module Cask
# Common functionality for checking desc stanzas.
module OnDescStanza
extend Forwardable
include CaskHelp
def on_cask(cask_block)
@cask_block = cask_block
toplevel_stanzas.select(&:desc?).each do |stanza|
on_desc_stanza(stanza)
end
end
private
attr_reader :cask_block
def_delegators :cask_block,
:toplevel_stanzas
end
end
end
end

View File

@ -6,7 +6,7 @@ module RuboCop
module Cop module Cop
module Cask module Cask
# This cop checks that a cask's stanzas are grouped correctly. # This cop checks that a cask's stanzas are grouped correctly.
# See https://github.com/Homebrew/homebrew-cask/blob/HEAD/CONTRIBUTING.md#stanza-order # See https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/readme.md#stanza-order
# for more info. # for more info.
class StanzaGrouping < Cop class StanzaGrouping < Cop
extend Forwardable extend Forwardable

View File

@ -6,7 +6,7 @@ module RuboCop
module Cop module Cop
module Cask module Cask
# This cop checks that a cask's stanzas are ordered correctly. # This cop checks that a cask's stanzas are ordered correctly.
# See https://github.com/Homebrew/homebrew-cask/blob/HEAD/CONTRIBUTING.md#stanza-order # See https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/readme.md#stanza-order
# for more info. # for more info.
class StanzaOrder < Cop class StanzaOrder < Cop
extend Forwardable extend Forwardable

View File

@ -10,11 +10,13 @@ ensure
end end
require "extend/string" require "extend/string"
require "rubocops/shared/helper_functions"
module RuboCop module RuboCop
module Cop module Cop
class FormulaCop < Cop class FormulaCop < Cop
include RangeHelp include RangeHelp
include HelperFunctions
attr_accessor :file_path attr_accessor :file_path
@ -32,28 +34,6 @@ module RuboCop
audit_formula(node, class_node, parent_class_node, @body) audit_formula(node, class_node, parent_class_node, @body)
end end
# Checks for regex match of pattern in the node and
# sets the appropriate instance variables to report the match
def regex_match_group(node, pattern)
string_repr = string_content(node).encode("UTF-8", invalid: :replace)
match_object = string_repr.match(pattern)
return unless match_object
node_begin_pos = start_column(node)
line_begin_pos = line_start_column(node)
@column = if node_begin_pos == line_begin_pos
node_begin_pos + match_object.begin(0) - line_begin_pos
else
node_begin_pos + match_object.begin(0) - line_begin_pos + 1
end
@length = match_object.to_s.length
@line_no = line_number(node)
@source_buf = source_buffer(node)
@offense_source_range = source_range(@source_buf, @line_no, @column, @length)
@offensive_node = node
match_object
end
# Yields to block when there is a match. # Yields to block when there is a match.
# @param urls [Array] url/mirror method call nodes # @param urls [Array] url/mirror method call nodes
# @param regex [Regexp] pattern to match urls # @param regex [Regexp] pattern to match urls
@ -442,26 +422,11 @@ module RuboCop
end end
end end
# Returns the begin position of the node's line in source code
def line_start_column(node)
node.source_range.source_buffer.line_range(node.loc.line).begin_pos
end
# Returns the begin position of the node in source code
def start_column(node)
node.source_range.begin_pos
end
# Returns the ending position of the node in source code # Returns the ending position of the node in source code
def end_column(node) def end_column(node)
node.source_range.end_pos node.source_range.end_pos
end end
# Returns the line number of the node
def line_number(node)
node.loc.line
end
# Returns the class node's name, nil if not a class node # Returns the class node's name, nil if not a class node
def class_name(node) def class_name(node)
@offensive_node = node @offensive_node = node
@ -484,35 +449,6 @@ module RuboCop
block.loc.end.line - block.loc.begin.line block.loc.end.line - block.loc.begin.line
end end
# Source buffer is required as an argument to report style violations
def source_buffer(node)
node.source_range.source_buffer
end
# Returns the string representation if node is of type str(plain) or dstr(interpolated) or const
def string_content(node)
case node.type
when :str
node.str_content
when :dstr
content = ""
node.each_child_node(:str, :begin) do |child|
content += if child.begin_type?
child.source
else
child.str_content
end
end
content
when :const
node.const_name
when :sym
node.children.first.to_s
else
""
end
end
# Returns true if the formula is versioned # Returns true if the formula is versioned
def versioned_formula? def versioned_formula?
@formula_name.include?("@") @formula_name.include?("@")
@ -532,10 +468,6 @@ module RuboCop
match_obj[1] match_obj[1]
end end
def problem(msg)
add_offense(@offensive_node, location: @offense_source_range, message: msg)
end
private private
def formula_class?(node) def formula_class?(node)

View File

@ -1,105 +1,24 @@
# frozen_string_literal: true # frozen_string_literal: true
require "rubocops/extend/formula" require "rubocops/extend/formula"
require "rubocops/shared/desc_helper"
require "extend/string" require "extend/string"
module RuboCop module RuboCop
module Cop module Cop
module FormulaAudit module FormulaAudit
# This cop audits `desc` in Formulae. # This cop audits `desc` in Formulae.
# # See the `DescHelper` module for details of the checks.
# - Checks for existence of `desc`
# - Checks if size of `desc` > 80
# - Checks for leading/trailing whitespace in `desc`
# - Checks if `desc` begins with an article
# - Checks for correct usage of `command-line` in `desc`
# - Checks description starts with a capital letter
# - Checks if `desc` contains the formula name
# - Checks if `desc` ends with a full stop (apart from in the case of "etc.")
class Desc < FormulaCop class Desc < FormulaCop
VALID_LOWERCASE_WORDS = %w[ include DescHelper
macOS
].freeze
def audit_formula(_node, _class_node, _parent_class_node, body_node) def audit_formula(_node, _class_node, _parent_class_node, body_node)
desc_call = find_node_method_by_name(body_node, :desc) desc_call = find_node_method_by_name(body_node, :desc)
audit_desc(:formula, @formula_name, desc_call)
# Check if a formula's desc is present
if desc_call.nil?
problem "Formula should have a desc (Description)."
return
end
desc = parameters(desc_call).first
# Check the formula's desc length. Should be >0 and <80 characters.
pure_desc_length = string_content(desc).length
if pure_desc_length.zero?
problem "The desc (description) should not be an empty string."
return
end
# Check for leading whitespace.
problem "Description shouldn't have a leading space" if regex_match_group(desc, /^\s+/)
# Check for trailing whitespace.
problem "Description shouldn't have a trailing space" if regex_match_group(desc, /\s+$/)
# Check if command-line is wrongly used in formula's desc
if match = regex_match_group(desc, /(command ?line)/i)
c = match.to_s[0]
problem "Description should use \"#{c}ommand-line\" instead of \"#{match}\""
end
# Check if a/an are used in a formula's desc
if match = regex_match_group(desc, /^(an?)\s/i)
problem "Description shouldn't start with an indefinite article, i.e. \"#{match.to_s.strip}\""
end
# Check if invalid uppercase words are at the start of a
# formula's desc
if !VALID_LOWERCASE_WORDS.include?(string_content(desc).split.first) &&
regex_match_group(desc, /^[a-z]/)
problem "Description should start with a capital letter"
end
# Check if formula's desc starts with formula's name
if regex_match_group(desc, /^#{@formula_name} /i)
problem "Description shouldn't start with the formula name"
end
# Check if a full stop is used at the end of a formula's desc (apart from in the case of "etc.")
if regex_match_group(desc, /\.$/) && !string_content(desc).end_with?("etc.")
problem "Description shouldn't end with a full stop"
end
desc_length = "#{@formula_name}: #{string_content(desc)}".length
max_desc_length = 80
return if desc_length <= max_desc_length
problem "Description is too long. \"name: desc\" should be less than #{max_desc_length} characters. " \
"Length is calculated as #{@formula_name} + desc. (currently #{desc_length})"
end end
def autocorrect(node) def autocorrect(node)
lambda do |corrector| autocorrect_desc(node, @formula_name)
correction = node.source
first_word = string_content(node).split.first
unless VALID_LOWERCASE_WORDS.include?(first_word)
first_char = first_word.to_s[0]
correction.sub!(/^(['"]?)([a-z])/, "\\1#{first_char.upcase}") if first_char
end
correction.sub!(/^(['"]?)an?\s/i, "\\1")
correction.gsub!(/(ommand ?line)/i, "ommand-line")
correction.gsub!(/(^|[^a-z])#{@formula_name}([^a-z]|$)/i, "\\1\\2")
correction.gsub!(/^(['"]?)\s+/, "\\1")
correction.gsub!(/\s+(['"]?)$/, "\\1")
correction.gsub!(/\.(['"]?)$/, "\\1")
correction.gsub!(/^\s+/, "")
correction.gsub!(/\s+$/, "")
corrector.insert_before(node.source_range, correction)
corrector.remove(node.source_range)
end
end end
end end
end end

View File

@ -11,6 +11,7 @@ require "rubocops/cask/extend/string"
require "rubocops/cask/extend/node" require "rubocops/cask/extend/node"
require "rubocops/cask/mixin/cask_help" require "rubocops/cask/mixin/cask_help"
require "rubocops/cask/mixin/on_homepage_stanza" require "rubocops/cask/mixin/on_homepage_stanza"
require "rubocops/cask/desc"
require "rubocops/cask/homepage_matches_url" require "rubocops/cask/homepage_matches_url"
require "rubocops/cask/homepage_url_trailing_slash" require "rubocops/cask/homepage_url_trailing_slash"
require "rubocops/cask/no_dsl_version" require "rubocops/cask/no_dsl_version"

View File

@ -0,0 +1,99 @@
# frozen_string_literal: true
require "rubocops/shared/helper_functions"
module RuboCop
module Cop
# This module performs common checks the `desc` field in both Formulae and Casks.
module DescHelper
include HelperFunctions
VALID_LOWERCASE_WORDS = %w[
macOS
].freeze
def audit_desc(type, name, desc_call)
# Check if a desc is present.
if desc_call.nil?
problem "#{type.to_s.capitalize} should have a desc (Description)."
return
end
desc = desc_call.first_argument
# Check if the desc is empty.
pure_desc_length = string_content(desc).length
if pure_desc_length.zero?
problem "The desc (description) should not be an empty string."
return
end
# Check the desc for leading whitespace.
problem "Description shouldn't have leading spaces." if regex_match_group(desc, /^\s+/)
# Check the desc for trailing whitespace.
problem "Description shouldn't have trailing spaces." if regex_match_group(desc, /\s+$/)
# Check if "command-line" is spelled incorrectly in the desc.
if match = regex_match_group(desc, /(command ?line)/i)
c = match.to_s[0]
problem "Description should use \"#{c}ommand-line\" instead of \"#{match}\"."
end
# Check if the desc starts with "A" or "An".
if match = regex_match_group(desc, /^(an?)(?=\s)/i)
problem "Description shouldn't start with an indefinite article, i.e. \"#{match}\"."
end
# Check if invalid lowercase words are at the start of a desc.
if !VALID_LOWERCASE_WORDS.include?(string_content(desc).split.first) &&
regex_match_group(desc, /^[a-z]/)
problem "Description should start with a capital letter."
end
# Check if the desc starts with the formula's or cask's name.
problem "Description shouldn't start with the #{type} name." if regex_match_group(desc, /^#{name} /i)
# Check if a full stop is used at the end of a desc (apart from in the case of "etc.").
if regex_match_group(desc, /\.$/) && !string_content(desc).end_with?("etc.")
problem "Description shouldn't end with a full stop."
end
# Check if the desc length exceeds 80 characters.
desc_length = "#{name}: #{string_content(desc)}".length
max_desc_length = 80
return if desc_length <= max_desc_length
problem "Description is too long. \"name: desc\" should be less than #{max_desc_length} characters. " \
"The current combined length is #{desc_length}."
end
def autocorrect_desc(node, name)
lambda do |corrector|
/\A(?<quote>["'])(?<correction>.*)(?:\k<quote>)\Z/ =~ node.source
next if correction.nil?
correction.gsub!(/^\s+/, "")
correction.gsub!(/\s+$/, "")
correction.sub!(/^an?\s+/i, "")
first_word = correction.split.first
unless VALID_LOWERCASE_WORDS.include?(first_word)
first_char = first_word.to_s[0]
correction[0] = first_char.upcase if first_char
end
correction.gsub!(/(ommand ?line)/i, "ommand-line")
correction.gsub!(/(^|[^a-z])#{name}([^a-z]|$)/i, "\\1\\2")
correction.gsub!(/^\s+/, "")
correction.gsub!(/\s+$/, "")
correction.gsub!(/\.$/, "")
corrector.replace(node.source_range, "#{quote}#{correction}#{quote}")
end
end
end
end
end

View File

@ -0,0 +1,79 @@
# frozen_string_literal: true
module RuboCop
module Cop
module HelperFunctions
include RangeHelp
# Checks for regex match of pattern in the node and
# sets the appropriate instance variables to report the match
def regex_match_group(node, pattern)
string_repr = string_content(node).encode("UTF-8", invalid: :replace)
match_object = string_repr.match(pattern)
return unless match_object
node_begin_pos = start_column(node)
line_begin_pos = line_start_column(node)
@column = if node_begin_pos == line_begin_pos
node_begin_pos + match_object.begin(0) - line_begin_pos
else
node_begin_pos + match_object.begin(0) - line_begin_pos + 1
end
@length = match_object.to_s.length
@line_no = line_number(node)
@source_buf = source_buffer(node)
@offense_source_range = source_range(@source_buf, @line_no, @column, @length)
@offensive_node = node
match_object
end
# Returns the begin position of the node's line in source code
def line_start_column(node)
node.source_range.source_buffer.line_range(node.loc.line).begin_pos
end
# Returns the begin position of the node in source code
def start_column(node)
node.source_range.begin_pos
end
# Returns the line number of the node
def line_number(node)
node.loc.line
end
# Source buffer is required as an argument to report style violations
def source_buffer(node)
node.source_range.source_buffer
end
# Returns the string representation if node is of type str(plain) or dstr(interpolated) or const
def string_content(node)
case node.type
when :str
node.str_content
when :dstr
content = ""
node.each_child_node(:str, :begin) do |child|
content += if child.begin_type?
child.source
else
child.str_content
end
end
content
when :const
node.const_name
when :sym
node.children.first.to_s
else
""
end
end
def problem(msg)
add_offense(@offensive_node, location: @offense_source_range, message: msg)
end
end
end
end

View File

@ -35,6 +35,7 @@ describe Cask::Cmd::Create, :cask do
url "https://" url "https://"
name "" name ""
desc ""
homepage "" homepage ""
app "" app ""

View File

@ -90,7 +90,7 @@ describe Cask::Cmd::List, :cask do
let(:casks) { ["local-caffeine", "local-transmission"] } let(:casks) { ["local-caffeine", "local-transmission"] }
let(:expected_output) { let(:expected_output) {
<<~EOS <<~EOS
[{"token":"local-caffeine","name":[],"homepage":"https://brew.sh/","url":"file:///usr/local/Homebrew/Library/Homebrew/test/support/fixtures/cask/caffeine.zip","appcast":null,"version":"1.2.3","sha256":"67cdb8a02803ef37fdbf7e0be205863172e41a561ca446cd84f0d7ab35a99d94","artifacts":[["Caffeine.app"]],"caveats":null,"depends_on":{},"conflicts_with":null,"container":null,"auto_updates":null},{"token":"local-transmission","name":["Transmission"],"homepage":"https://brew.sh/","url":"file:///usr/local/Homebrew/Library/Homebrew/test/support/fixtures/cask/transmission-2.61.dmg","appcast":null,"version":"2.61","sha256":"e44ffa103fbf83f55c8d0b1bea309a43b2880798dae8620b1ee8da5e1095ec68","artifacts":[["Transmission.app"]],"caveats":null,"depends_on":{},"conflicts_with":null,"container":null,"auto_updates":null}] [{"token":"local-caffeine","name":[],"desc":null,"homepage":"https://brew.sh/","url":"file:///usr/local/Homebrew/Library/Homebrew/test/support/fixtures/cask/caffeine.zip","appcast":null,"version":"1.2.3","sha256":"67cdb8a02803ef37fdbf7e0be205863172e41a561ca446cd84f0d7ab35a99d94","artifacts":[["Caffeine.app"]],"caveats":null,"depends_on":{},"conflicts_with":null,"container":null,"auto_updates":null},{"token":"local-transmission","name":["Transmission"],"desc":null,"homepage":"https://brew.sh/","url":"file:///usr/local/Homebrew/Library/Homebrew/test/support/fixtures/cask/transmission-2.61.dmg","appcast":null,"version":"2.61","sha256":"e44ffa103fbf83f55c8d0b1bea309a43b2880798dae8620b1ee8da5e1095ec68","artifacts":[["Transmission.app"]],"caveats":null,"depends_on":{},"conflicts_with":null,"container":null,"auto_updates":null}]
EOS EOS
} }

View File

@ -107,6 +107,16 @@ describe Cask::DSL, :cask do
end end
end end
describe "desc stanza" do
it "lets you set the description via a desc stanza" do
cask = Cask::Cask.new("desc-cask") do
desc "The package's description"
end
expect(cask.desc).to eq("The package's description")
end
end
describe "sha256 stanza" do describe "sha256 stanza" do
it "lets you set checksum via sha256" do it "lets you set checksum via sha256" do
cask = Cask::Cask.new("checksum-cask") do cask = Cask::Cask.new("checksum-cask") do

View File

@ -0,0 +1,52 @@
# frozen_string_literal: true
require "rubocops/rubocop-cask"
require "test/rubocops/cask/shared_examples/cask_cop"
describe RuboCop::Cop::Cask::Desc do
include CaskCop
subject(:cop) { described_class.new }
context "with incorrect `desc` stanza" do
let(:source) {
<<~RUBY
cask "foo" do
desc "A bar program"
end
RUBY
}
let(:correct_source) {
<<~RUBY
cask "foo" do
desc "Bar program"
end
RUBY
}
let(:expected_offenses) do
[{
message: "Description shouldn't start with an indefinite article, i.e. \"A\".",
severity: :convention,
line: 2,
column: 8,
source: "A",
}]
end
include_examples "reports offenses"
include_examples "autocorrects source"
end
context "with correct `desc` stanza" do
let(:source) {
<<~RUBY
cask "foo" do
desc "Bar program"
end
RUBY
}
include_examples "does not report any offenses"
end
end

View File

@ -8,9 +8,6 @@ describe RuboCop::Cop::Cask::NoDslVersion do
subject(:cop) { described_class.new } subject(:cop) { described_class.new }
context "with header method `cask`" do
let(:header_method) { "cask" }
context "with no dsl version" do context "with no dsl version" do
let(:source) { "cask 'foo' do; end" } let(:source) { "cask 'foo' do; end" }
@ -35,4 +32,3 @@ describe RuboCop::Cop::Cask::NoDslVersion do
include_examples "autocorrects source" include_examples "autocorrects source"
end end
end end
end

View File

@ -15,7 +15,7 @@ describe RuboCop::Cop::FormulaAudit::Desc do
RUBY RUBY
end end
it "reports an offense when desc is an empty string" do it "When desc is an empty string" do
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
class Foo < Formula class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz' url 'https://brew.sh/foo-1.0.tgz'
@ -30,81 +30,30 @@ describe RuboCop::Cop::FormulaAudit::Desc do
class Foo < Formula class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz' url 'https://brew.sh/foo-1.0.tgz'
desc 'Bar#{"bar" * 29}' desc 'Bar#{"bar" * 29}'
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Description is too long. "name: desc" should be less than 80 characters. Length is calculated as foo + desc. (currently 95) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Description is too long. "name: desc" should be less than 80 characters. The current combined length is 95.
end end
RUBY RUBY
end end
it "When desc is multiline string" do it "When desc is a multiline string" do
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
class Foo < Formula class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz' url 'https://brew.sh/foo-1.0.tgz'
desc 'Bar#{"bar" * 9}'\ desc 'Bar#{"bar" * 9}'\
'#{"foo" * 21}' '#{"foo" * 21}'
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Description is too long. "name: desc" should be less than 80 characters. Length is calculated as foo + desc. (currently 98) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Description is too long. "name: desc" should be less than 80 characters. The current combined length is 98.
end end
RUBY RUBY
end end
end end
context "When auditing formula desc" do context "When auditing formula desc" do
it "When wrong \"command-line\" usage in desc" do
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
desc 'command line'
^ Description should start with a capital letter
^^^^^^^^^^^^ Description should use \"command-line\" instead of \"command line\"
end
RUBY
end
it "When an article is used in desc" do
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
desc 'An aardvark'
^^^ Description shouldn\'t start with an indefinite article, i.e. \"An\"
end
RUBY
end
it "When an lowercase letter starts a desc" do
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
desc 'bar'
^ Description should start with a capital letter
end
RUBY
end
it "When formula name is in desc" do
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
desc 'Foo is a foobar'
^^^^ Description shouldn\'t start with the formula name
end
RUBY
end
it "When the description ends with a full stop" do
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
desc 'Description with a full stop at the end.'
^ Description shouldn\'t end with a full stop
end
RUBY
end
it "When the description starts with a leading space" do it "When the description starts with a leading space" do
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
class Foo < Formula class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz' url 'https://brew.sh/foo-1.0.tgz'
desc ' Description with a leading space' desc ' Description with a leading space'
^ Description shouldn\'t have a leading space ^ Description shouldn\'t have leading spaces.
end end
RUBY RUBY
end end
@ -114,7 +63,58 @@ describe RuboCop::Cop::FormulaAudit::Desc do
class Foo < Formula class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz' url 'https://brew.sh/foo-1.0.tgz'
desc 'Description with a trailing space ' desc 'Description with a trailing space '
^ Description shouldn\'t have a trailing space ^ Description shouldn\'t have trailing spaces.
end
RUBY
end
it "When \"command-line\" is incorrectly spelled in the desc" do
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
desc 'command line'
^ Description should start with a capital letter.
^^^^^^^^^^^^ Description should use \"command-line\" instead of \"command line\".
end
RUBY
end
it "When an article is used in the desc" do
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
desc 'An aardvark'
^^ Description shouldn\'t start with an indefinite article, i.e. \"An\".
end
RUBY
end
it "When the desc starts with a lowercase letter" do
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
desc 'bar'
^ Description should start with a capital letter.
end
RUBY
end
it "When the desc starts with the formula name" do
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
desc 'Foo is a foobar'
^^^^ Description shouldn\'t start with the formula name.
end
RUBY
end
it "When the description ends with a full stop" do
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
desc 'Description with a full stop at the end.'
^ Description shouldn\'t end with a full stop.
end end
RUBY RUBY
end end
@ -130,7 +130,7 @@ describe RuboCop::Cop::FormulaAudit::Desc do
correct_source = <<~RUBY correct_source = <<~RUBY
class Foo < Formula class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz' url 'https://brew.sh/foo-1.0.tgz'
desc 'an bar: command-line' desc 'Bar: command-line'
end end
RUBY RUBY