Skip to content

Commit 6746ef2

Browse files
Merge specs checking CLI flags and subcommands are documented
We had them duplicated, but with slightly different features: * The ones in `other/cli_man_pages.rb` enforced a specific structure to document CLI options, so were less likely to have false positives. * The ones in `quality_spec.rb` were able to check subcommands and their flags. This commit merges both and preserves the best of both.
1 parent 9272169 commit 6746ef2

File tree

2 files changed

+56
-78
lines changed

2 files changed

+56
-78
lines changed

bundler/spec/other/cli_man_pages_spec.rb

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,72 @@
11
# frozen_string_literal: true
22

33
RSpec.describe "bundle commands" do
4-
it "expects all commands to have a man page" do
5-
Bundler::CLI.all_commands.each_key do |command_name|
6-
next if command_name == "cli_help"
4+
it "expects all commands to have all options and subcommands documented" do
5+
check_commands!(Bundler::CLI)
76

8-
expect(man_page(command_name)).to exist
7+
Bundler::CLI.subcommand_classes.each_value do |klass|
8+
check_commands!(klass)
99
end
1010
end
1111

12-
it "expects all commands to have all options documented" do
13-
Bundler::CLI.all_commands.each do |command_name, command|
14-
next if command_name == "cli_help"
12+
private
13+
14+
def check_commands!(command_class)
15+
command_class.commands.each do |command_name, command|
16+
next if command.is_a?(Bundler::Thor::HiddenCommand)
17+
18+
if command_class == Bundler::CLI
19+
man_page = man_page(command_name)
20+
expect(man_page).to exist
21+
22+
check_options!(command, man_page)
23+
else
24+
man_page = man_page(command.ancestor_name)
25+
expect(man_page).to exist
1526

16-
man_page_content = man_page(command_name).read
27+
check_options!(command, man_page)
28+
check_subcommand!(command_name, man_page)
29+
end
30+
end
31+
end
1732

18-
command.options.each do |_, option|
19-
aliases = option.aliases
20-
formatted_aliases = aliases.sort.map {|name| "`#{name}`" }.join(", ") if aliases
33+
def check_options!(command, man_page)
34+
command.options.each do |_, option|
35+
check_option!(option, man_page)
36+
end
37+
end
2138

22-
help = if option.type == :boolean
23-
"* #{append_aliases("`#{option.switch_name}`", formatted_aliases)}:"
24-
elsif option.enum
25-
formatted_aliases = "`#{option.switch_name}`" if aliases.empty? && option.lazy_default
26-
"* #{prepend_aliases(option.enum.sort.map {|enum| "`#{option.switch_name}=#{enum}`" }.join(", "), formatted_aliases)}:"
27-
else
28-
names = [option.switch_name, *aliases]
29-
value =
30-
case option.type
31-
when :array then "<list>"
32-
when :numeric then "<number>"
33-
else option.name.upcase
34-
end
39+
def check_option!(option, man_page)
40+
man_page_content = man_page.read
3541

36-
value = option.type != :numeric && option.lazy_default ? "[=#{value}]" : "=#{value}"
42+
aliases = option.aliases
43+
formatted_aliases = aliases.sort.map {|name| "`#{name}`" }.join(", ") if aliases
3744

38-
"* #{names.map {|name| "`#{name}#{value}`" }.join(", ")}:"
45+
help = if option.type == :boolean
46+
"* #{append_aliases("`#{option.switch_name}`", formatted_aliases)}:"
47+
elsif option.enum
48+
formatted_aliases = "`#{option.switch_name}`" if aliases.empty? && option.lazy_default
49+
"* #{prepend_aliases(option.enum.sort.map {|enum| "`#{option.switch_name}=#{enum}`" }.join(", "), formatted_aliases)}:"
50+
else
51+
names = [option.switch_name, *aliases]
52+
value =
53+
case option.type
54+
when :array then "<list>"
55+
when :numeric then "<number>"
56+
else option.name.upcase
3957
end
4058

41-
expect(man_page_content).to include(help)
42-
end
59+
value = option.type != :numeric && option.lazy_default ? "[=#{value}]" : "=#{value}"
60+
61+
"* #{names.map {|name| "`#{name}#{value}`" }.join(", ")}:"
4362
end
63+
64+
expect(man_page_content).to include(help)
4465
end
4566

46-
private
67+
def check_subcommand!(name, man_page)
68+
expect(man_page.read).to match(name)
69+
end
4770

4871
def append_aliases(text, aliases)
4972
return text if aliases.empty?
@@ -57,6 +80,10 @@ def prepend_aliases(text, aliases)
5780
"#{aliases}, #{text}"
5881
end
5982

83+
def man_page_content(command_name)
84+
man_page(command_name).read
85+
end
86+
6087
def man_page(command_name)
6188
source_root.join("lib/bundler/man/bundle-#{command_name}.1.ronn")
6289
end

bundler/spec/quality_spec.rb

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -251,58 +251,9 @@ def check_for_specific_pronouns(filename)
251251
expect(lib_code).to eq(spec_code)
252252
end
253253

254-
it "documents all cli command options in their associated man pages" do
255-
commands = normalize_commands_and_options(Bundler::CLI)
256-
cli_and_man_pages_in_sync!(commands)
257-
258-
Bundler::CLI.subcommand_classes.each do |_, klass|
259-
subcommands = normalize_commands_and_options(klass)
260-
261-
cli_and_man_pages_in_sync!(subcommands)
262-
end
263-
end
264-
265254
private
266255

267256
def each_line(filename, &block)
268257
File.readlines(filename, encoding: "UTF-8").each_with_index(&block)
269258
end
270-
271-
def normalize_commands_and_options(command_class)
272-
commands = {}
273-
274-
command_class.commands.each do |command_name, command|
275-
next if command.is_a?(Bundler::Thor::HiddenCommand)
276-
277-
key = command.ancestor_name || command_name
278-
commands[key] ||= []
279-
# Verify that all subcommands are documented in the main command's man page.
280-
commands[key] << command_name unless command_class == Bundler::CLI
281-
282-
command.options.each do |_, option|
283-
commands[key] << option.switch_name
284-
end
285-
end
286-
287-
commands
288-
end
289-
290-
def cli_and_man_pages_in_sync!(commands)
291-
commands.each do |command_name, opts|
292-
man_page_path = man_tracked_files.find {|f| File.basename(f) == "bundle-#{command_name}.1.ronn" }
293-
expect(man_page_path).to_not be_nil, "The command #{command_name} has no associated man page."
294-
295-
next if opts.empty?
296-
297-
man_page_content = File.read(man_page_path)
298-
opts.each do |option_name|
299-
error_msg = <<~EOM
300-
The command #{command_name} has no mention of the option or subcommand `#{option_name}` in its man page.
301-
Document the `#{option_name}` in the man page to discard this error.
302-
EOM
303-
304-
expect(man_page_content).to match(option_name), error_msg
305-
end
306-
end
307-
end
308259
end

0 commit comments

Comments
 (0)