hammer_cli 0.10.2 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 230906bc19a41759805ad05e51cee6244938cc5e
4
- data.tar.gz: 7e4389026de508ce212c3af34823bc1606b6851e
3
+ metadata.gz: f466081646f301304855a180aec480a3c90e1653
4
+ data.tar.gz: b33e6d9d98d4746890ffd72080cd6f3bf272dd3b
5
5
  SHA512:
6
- metadata.gz: 55c6841e58e1645e48eb83e147d662abc847cf99f18df1f5636b6fb9a09be79e133d99429fa626c3e6ca2456003091e7e74f2813ac43dc22ee163c0f2862b54d
7
- data.tar.gz: ddeec7d17a560050722bddc473bbc2ff8bba5e967c3cf3f7772249fcf300ca38d03db2f5d8d75df0be5e1b10025eafeef257bf4b8ca5280d71720537ea0e6a0f
6
+ metadata.gz: 659186808832cd2531ef847a046eded3270789a73a052c8565c0cadbde7de977fe3b3d18bfb2d362ed2458dc4dd8f4ce826a828f78eafceb2815f727b5ee2395
7
+ data.tar.gz: dceeed449044ee96e0fe969bdf4ed2906c6b27b6de91988f70a83ec72a65d218d7e3ef720ced4195420a5697ca9369e1786afc992bac6020d9f7fae597295297
@@ -21,7 +21,7 @@ echo "deb http://deb.theforeman.org/ squeeze nightly" > /etc/apt/sources.list.d/
21
21
  and update the keys
22
22
 
23
23
  ```bash
24
- wget -q http://deb.theforeman.org/foreman.asc -O- | apt-key add -
24
+ wget -q https://deb.theforeman.org/foreman.asc -O- | apt-key add -
25
25
  ```
26
26
 
27
27
  #### Step 2: install hammer core
data/doc/release_notes.md CHANGED
@@ -1,12 +1,17 @@
1
1
  Release notes
2
2
  =============
3
3
 
4
- ### 0.10.2 (2017-05-05)
4
+ ### 0.11.0 (2017-08-01)
5
+ * Add full-help command ([#20181](http://projects.theforeman.org/issues/20181))
6
+ * Replace CSV parser in List Normalizer ([#17135](http://projects.theforeman.org/issues/17135))
7
+ * Make sure assert_match is called in correct context ([#19573](http://projects.theforeman.org/issues/19573))
8
+ * Fix merging .edit.po into .po files ([#17671](http://projects.theforeman.org/issues/17671))
9
+ * Error strings with % in them break messages ([#11178](http://projects.theforeman.org/issues/11178))
10
+ * Log what exception handler is used ([#19470](http://projects.theforeman.org/issues/19470))
11
+ * Interpolate variables in translations correctly ([#19083](http://projects.theforeman.org/issues/19083))
5
12
  * Retry command on session expiry ([#19431](http://projects.theforeman.org/issues/19431))
6
13
  * Prevent fetching of non-CA certificates ([#19391](http://projects.theforeman.org/issues/19391))
7
14
  * Use local ca cert store instead of ssl_ca_path ([#19390](http://projects.theforeman.org/issues/19390))
8
-
9
- ### 0.10.1 (2017-04-24)
10
15
  * Require apipie-bindings >= 0.2.0 ([#19083](http://projects.theforeman.org/issues/19083))
11
16
  * Install server CA cert without root access ([#19083](http://projects.theforeman.org/issues/19083))
12
17
 
@@ -0,0 +1,80 @@
1
+ module HammerCLI
2
+ class CSVParser
3
+
4
+ def initialize
5
+ reset_parser
6
+ end
7
+
8
+ def parse(data)
9
+ return [] if data.nil?
10
+ reset_parser
11
+ data.each_char do |char|
12
+ handle_escape(char) || handle_quoting(char) || handle_comma(char) || add_to_buffer(char)
13
+ end
14
+ raise ArgumentError.new(_("Illegal quoting in %{buffer}") % { :buffer => @raw_buffer }) unless @last_quote.nil?
15
+ clean_buffer
16
+ @value
17
+ end
18
+
19
+ private
20
+
21
+ def handle_comma(char)
22
+ if char == ','
23
+ clean_buffer
24
+ true
25
+ else
26
+ false
27
+ end
28
+ end
29
+
30
+ def handle_quoting(char)
31
+ if @last_quote.nil? && ["'", '"'].include?(char)
32
+ @last_quote = char
33
+ @raw_buffer += char
34
+ true
35
+ elsif @last_quote == char
36
+ @last_quote = nil
37
+ @raw_buffer += char
38
+ true
39
+ elsif @last_quote
40
+ add_to_buffer(char)
41
+ true
42
+ else
43
+ false
44
+ end
45
+ end
46
+
47
+ def handle_escape(char)
48
+ if @escape
49
+ add_to_buffer(char)
50
+ @escape = false
51
+ true
52
+ elsif char == '\\'
53
+ @escape = true
54
+ @raw_buffer += char
55
+ true
56
+ else
57
+ false
58
+ end
59
+ end
60
+
61
+ def add_to_buffer(char)
62
+ @buffer += char
63
+ @raw_buffer += char
64
+ end
65
+
66
+ def reset_parser
67
+ @value = []
68
+ @buffer = ''
69
+ @raw_buffer = ''
70
+ @escape = false
71
+ @last_quote = nil
72
+ end
73
+
74
+ def clean_buffer
75
+ @value << @buffer
76
+ @raw_buffer = ''
77
+ @buffer = ''
78
+ end
79
+ end
80
+ end
@@ -27,6 +27,7 @@ module HammerCLI
27
27
  def handle_exception(e, options={})
28
28
  @options = options
29
29
  handler = mappings.reverse.find { |m| e.class.respond_to?(:"<=") ? e.class <= m[0] : false }
30
+ @logger.debug "Using exception handler #{self.class}##{handler[1]}"
30
31
  return send(handler[1], e) if handler
31
32
  raise e
32
33
  end
@@ -0,0 +1,84 @@
1
+ require 'hammer_cli/abstract'
2
+
3
+ module HammerCLI
4
+ class FullHelpCommand < HammerCLI::AbstractCommand
5
+ option "--md", :flag, _("Format output in markdown")
6
+
7
+ def execute
8
+ @adapter = option_md? ? MDAdapter.new : TxtAdapter.new
9
+ print_heading
10
+ print_help
11
+ HammerCLI::EX_OK
12
+ end
13
+
14
+ private
15
+
16
+ def print_heading
17
+ @adapter.print_heading(_('Hammer CLI help'))
18
+ @adapter.print_toc(HammerCLI::MainCommand)
19
+ end
20
+
21
+ def print_help(name='hammer', command=HammerCLI::MainCommand, desc='')
22
+ @adapter.print_command(name, desc, command.new(name).help)
23
+
24
+ command.recognised_subcommands.each do |sub_cmd|
25
+ print_help(@adapter.command_name(name, sub_cmd.names.first), sub_cmd.subcommand_class, sub_cmd.description)
26
+ end
27
+ end
28
+
29
+ class MDAdapter
30
+ def command_name(parent, command_name)
31
+ "#{parent} #{command_name}"
32
+ end
33
+
34
+ def print_command(name, description, help)
35
+ print_heading(name, name.split.length)
36
+ puts description
37
+ puts
38
+ puts "```"
39
+ puts help
40
+ puts "```"
41
+ puts
42
+ end
43
+
44
+ def print_toc(cmd)
45
+ names = cmd.recognised_subcommands.collect do |sub_cmd|
46
+ sub_cmd.names[0]
47
+ end
48
+ names.sort.each do |name|
49
+ puts "- [%s](#hammer-%s)" % [name, name.gsub(' ', '-')]
50
+ end
51
+ puts
52
+ end
53
+
54
+ def print_heading(text, level=1)
55
+ puts '#'*level + ' ' + text
56
+ end
57
+ end
58
+
59
+ class TxtAdapter
60
+ def command_name(parent, command_name)
61
+ "#{parent} > #{command_name}"
62
+ end
63
+
64
+ def print_command(name, description, help)
65
+ print_heading(name, 2)
66
+ puts description
67
+ puts
68
+ puts help
69
+ puts
70
+ end
71
+
72
+ def print_toc(cmd)
73
+ end
74
+
75
+ def print_heading(text, level=1)
76
+ ch = (level > 1) ? '-' : '='
77
+ puts text
78
+ puts ch * text.length
79
+ end
80
+ end
81
+ end
82
+
83
+ HammerCLI::MainCommand.subcommand "full-help", _("Print help for all hammer commands"), HammerCLI::FullHelpCommand
84
+ end
@@ -1,5 +1,5 @@
1
1
  require 'json'
2
- require 'csv'
2
+ require 'hammer_cli/csv_parser'
3
3
 
4
4
  module HammerCLI
5
5
  module Options
@@ -79,25 +79,14 @@ module HammerCLI
79
79
  end
80
80
  end
81
81
 
82
- CSV_ERROR_MESSAGES = {
83
- /Missing or stray quote/ => _('Missing or stray quote.'),
84
- /Unquoted fields do not allow/ => _('Unquoted fields do not allow \r or \n.'),
85
- /Illegal quoting/ => _('Illegal quoting.'),
86
- /Unclosed quoted field/ => _('Unclosed quoted field.'),
87
- /Field size exceeded/ => _('Field size exceeded.')
88
- }
89
82
 
90
83
  class List < AbstractNormalizer
91
-
92
84
  def description
93
- _("Comma separated list of values. Values containing comma should be double quoted")
85
+ _("Comma separated list of values. Values containing comma should be quoted or escaped with backslash")
94
86
  end
95
87
 
96
88
  def format(val)
97
- (val.is_a?(String) && !val.empty?) ? CSV.parse_line(val) : []
98
- rescue CSV::MalformedCSVError => e
99
- message = CSV_ERROR_MESSAGES.find { |pattern,| pattern.match e.message } || [e.message]
100
- raise ArgumentError.new(message.last)
89
+ (val.is_a?(String) && !val.empty?) ? HammerCLI::CSVParser.new.parse(val) : []
101
90
  end
102
91
  end
103
92
 
@@ -2,8 +2,6 @@ module HammerCLI
2
2
  module Testing
3
3
  module CommandAssertions
4
4
  class CommandExpectation
5
- include MiniTest::Assertions
6
-
7
5
  attr_accessor :expected_out, :expected_err, :expected_exit_code
8
6
 
9
7
  def initialize(expected_out="", expected_err="", expected_exit_code=0)
@@ -12,10 +10,10 @@ module HammerCLI
12
10
  @expected_exit_code = expected_exit_code
13
11
  end
14
12
 
15
- def assert_match(actual_result)
16
- assert_equal_or_match @expected_err, actual_result.err
17
- assert_equal_or_match @expected_out, actual_result.out
18
- assert_exit_code_equal @expected_exit_code, actual_result.exit_code
13
+ def assert_match(test_ctx, actual_result)
14
+ test_ctx.assert_equal_or_match @expected_err, actual_result.err
15
+ test_ctx.assert_equal_or_match @expected_out, actual_result.out
16
+ test_ctx.assert_exit_code_equal @expected_exit_code, actual_result.exit_code
19
17
  end
20
18
  end
21
19
 
@@ -54,15 +52,14 @@ module HammerCLI
54
52
  end
55
53
 
56
54
  def assert_cmd(expectation, actual_result)
57
- expectation.assert_match(actual_result)
55
+ expectation.assert_match(self, actual_result)
58
56
  end
59
57
 
60
58
  def assert_equal_or_match(expected, actual)
61
- case expected
62
- when String
59
+ if expected.is_a? String
63
60
  assert_equal(expected, actual)
64
- when MatcherBase
65
- expected.assert_match(actual)
61
+ elsif expected.respond_to? :assert_match
62
+ expected.assert_match(self, actual)
66
63
  else
67
64
  msg = actual
68
65
  assert_match(expected, actual, msg)
@@ -8,8 +8,8 @@ module HammerCLI
8
8
  @expected = expected
9
9
  end
10
10
 
11
- def assert_match(actual)
12
- assert_equal(@expected, actual)
11
+ def assert_match(test_ctx, actual)
12
+ test_ctx.assert_equal(@expected, actual)
13
13
  end
14
14
  end
15
15
 
@@ -18,9 +18,9 @@ module HammerCLI
18
18
  @expected = FieldMatcher.matcher(label, value)
19
19
  end
20
20
 
21
- def assert_match(actual)
21
+ def assert_match(test_ctx, actual)
22
22
  message = "Regex /#{@expected.source}/ didn't match the output:\n#{actual}"
23
- assert(@expected =~ actual, message)
23
+ test_ctx.assert(@expected =~ actual, message)
24
24
  end
25
25
 
26
26
  def self.matcher(label, value)
@@ -36,7 +36,7 @@ module HammerCLI
36
36
  @ignore_whitespace = options.fetch(:ignore_whitespace, true)
37
37
  end
38
38
 
39
- def assert_match(actual)
39
+ def assert_match(test_ctx, actual)
40
40
  if @ignore_whitespace
41
41
  expected_lines = strip_lines(@expected_lines)
42
42
  actual = strip_lines(actual.split("\n")).join("\n")
@@ -46,7 +46,7 @@ module HammerCLI
46
46
  expected_lines = expected_lines.join("\n")
47
47
 
48
48
  message = "Output didn't contain expected lines:\n" + diff(expected_lines, actual)
49
- assert(actual.include?(expected_lines), message)
49
+ test_ctx.assert(actual.include?(expected_lines), message)
50
50
  end
51
51
 
52
52
  protected
@@ -64,9 +64,9 @@ module HammerCLI
64
64
  end
65
65
  end
66
66
 
67
- def assert_match(actual)
67
+ def assert_match(test_ctx, actual)
68
68
  @line_matchers.each do |matcher|
69
- matcher.assert_match(actual)
69
+ matcher.assert_match(test_ctx, actual)
70
70
  end
71
71
  end
72
72
  end
@@ -76,7 +76,7 @@ module HammerCLI
76
76
  @expected_values = expected
77
77
  end
78
78
 
79
- def assert_match(actual)
79
+ def assert_match(test_ctx, actual)
80
80
  message = [
81
81
  "Regex didn't match the output.",
82
82
  "Expected regex:",
@@ -87,7 +87,7 @@ module HammerCLI
87
87
  actual
88
88
  ].join("\n")
89
89
 
90
- assert(line_regexp =~ actual, message)
90
+ test_ctx.assert(line_regexp =~ actual, message)
91
91
  end
92
92
 
93
93
  protected
@@ -8,10 +8,11 @@ class String
8
8
  name = name[0]
9
9
  params[name.to_s] || params[name.to_sym]
10
10
  end
11
-
12
- self.gsub(/%[<]([^>]*)[>]/, '%').gsub(/%[{]([^}]*)[}]/, '%s') % array_params
11
+ self.gsub(/%[<]([^>]*)[>]/, '%')
12
+ .gsub(/%[{]([^}]*)[}]/, '%s')
13
+ .gsub(/\%(\W?[^bBdiouxXeEfgGaAcps])/, '%%\1') % array_params
13
14
  else
14
- self % params
15
+ self.gsub(/\%(\W?[^bBdiouxXeEfgGaAcps])/, '%%\1') % params
15
16
  end
16
17
  end
17
18
 
@@ -1,5 +1,5 @@
1
1
  module HammerCLI
2
2
  def self.version
3
- @version ||= Gem::Version.new '0.10.2'
3
+ @version ||= Gem::Version.new '0.11.0'
4
4
  end
5
5
  end
data/lib/hammer_cli.rb CHANGED
@@ -21,4 +21,4 @@ require 'hammer_cli/apipie'
21
21
  # extend MainCommand
22
22
  require 'hammer_cli/shell'
23
23
  require 'hammer_cli/defaults'
24
-
24
+ require 'hammer_cli/full_help'
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,54 @@
1
+ require File.join(File.dirname(__FILE__), '../test_helper')
2
+ require 'hammer_cli/csv_parser'
3
+
4
+ describe HammerCLI::CSVParser do
5
+
6
+ describe 'parse' do
7
+ let(:parser) { HammerCLI::CSVParser.new }
8
+
9
+ it "parses nil" do
10
+ parser.parse(nil).must_equal []
11
+ end
12
+
13
+ it "parses empty string" do
14
+ parser.parse('').must_equal ['']
15
+ end
16
+
17
+ it "parses single value" do
18
+ parser.parse('a').must_equal ['a']
19
+ end
20
+
21
+ it "parses a dquoted string" do
22
+ parser.parse('\"a').must_equal ['"a']
23
+ end
24
+
25
+ it "parses a quoted string" do
26
+ parser.parse("Mary\\'s").must_equal ["Mary's"]
27
+ end
28
+
29
+ it "should parse a comma separated string" do
30
+ parser.parse("a,b,c").must_equal ['a', 'b', 'c']
31
+ end
32
+
33
+ it "parses a string with escaped comma" do
34
+ parser.parse('a\,b,c').must_equal ['a,b', 'c']
35
+ end
36
+
37
+ it "should parse a comma separated string with quotes" do
38
+ parser.parse('a,b,\\"c\\"').must_equal ['a', 'b', '"c"']
39
+ end
40
+
41
+ it "parses a comma separated string with values including comma" do
42
+ parser.parse('a,b,"c,d"').must_equal ['a', 'b', 'c,d']
43
+ end
44
+
45
+ it "parses a comma separated string with values including comma (dquotes)" do
46
+ parser.parse("a,b,'c,d'").must_equal ['a', 'b', 'c,d']
47
+ end
48
+
49
+ it "raises quoting error" do
50
+ err = proc { parser.parse('1,"3,4""s') }.must_raise ArgumentError
51
+ err.message.must_equal "Illegal quoting in \"3,4\"\"s"
52
+ end
53
+ end
54
+ end
@@ -51,7 +51,8 @@ describe HammerCLI::ExceptionHandler do
51
51
  ex = RestClient::ResourceNotFound.new
52
52
  output.default_adapter = :silent
53
53
  handler.handle_exception(ex)
54
- @log_output.readline.strip.must_match /ERROR Exception : (Resource )?Not Found/
54
+ assert_match /Using exception handler HammerCLI::ExceptionHandler#handle_not_found/, @log_output.readline.strip
55
+ assert_match /ERROR Exception : (Resource )?Not Found/, @log_output.readline.strip
55
56
  end
56
57
 
57
58
  end
@@ -38,8 +38,12 @@ describe HammerCLI::Options::Normalizers do
38
38
  formatter.format('a,b,"c,d"').must_equal ['a', 'b', 'c,d']
39
39
  end
40
40
 
41
+ it "should parse a comma separated string with values including comma (doublequotes)" do
42
+ formatter.format("a,b,'c,d'").must_equal ['a', 'b', 'c,d']
43
+ end
44
+
41
45
  it "should parse a comma separated string containig double quotes" do
42
- formatter.format('a,b,""c""').must_equal ['a', 'b', '"c"']
46
+ formatter.format('a,b,\"c\"').must_equal ['a', 'b', '"c"']
43
47
  end
44
48
 
45
49
  it "should catch quoting errors" do
@@ -15,6 +15,7 @@ describe String do
15
15
  let(:str) { "AA%<a>s BB%<b>s" }
16
16
  let(:curly_str) { "AA%{a} BB%{b}" }
17
17
  let(:pos_str) { "AA%s BB%s" }
18
+ let(:str_with_percent) { "Error: AA%<a>s BB%<b>s <%# template error %> verify this %>" }
18
19
 
19
20
  it "should not fail without expected parameters" do
20
21
  str.format({}).must_equal 'AA BB'
@@ -36,6 +37,10 @@ describe String do
36
37
  curly_str.format(:a => 'A', :b => 'B').must_equal 'AAA BBB'
37
38
  end
38
39
 
40
+ it "should not fail due to presence of percent chars in string" do
41
+ str_with_percent.format({}).must_equal 'Error: AA BB <%# template error %> verify this %>'
42
+ end
43
+
39
44
  end
40
45
 
41
46
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hammer_cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.2
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Bačovský
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-05-05 00:00:00.000000000 Z
12
+ date: 2017-08-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: clamp
@@ -188,11 +188,13 @@ files:
188
188
  - lib/hammer_cli/completer.rb
189
189
  - lib/hammer_cli/connection.rb
190
190
  - lib/hammer_cli/context.rb
191
+ - lib/hammer_cli/csv_parser.rb
191
192
  - lib/hammer_cli/defaults.rb
192
193
  - lib/hammer_cli/defaults_commands.rb
193
194
  - lib/hammer_cli/exception_handler.rb
194
195
  - lib/hammer_cli/exceptions.rb
195
196
  - lib/hammer_cli/exit_codes.rb
197
+ - lib/hammer_cli/full_help.rb
196
198
  - lib/hammer_cli/help/builder.rb
197
199
  - lib/hammer_cli/help/text_builder.rb
198
200
  - lib/hammer_cli/i18n.rb
@@ -263,6 +265,7 @@ files:
263
265
  - test/unit/ca_cert_manager_test.rb
264
266
  - test/unit/completer_test.rb
265
267
  - test/unit/connection_test.rb
268
+ - test/unit/csv_parser_test.rb
266
269
  - test/unit/defaults_test.rb
267
270
  - test/unit/exception_handler_test.rb
268
271
  - test/unit/fixtures/apipie/architectures.json
@@ -329,16 +332,16 @@ signing_key:
329
332
  specification_version: 4
330
333
  summary: Universal command-line interface
331
334
  test_files:
332
- - test/unit/apipie/command_test.rb
333
335
  - test/unit/apipie/option_builder_test.rb
334
336
  - test/unit/apipie/option_definition_test.rb
335
337
  - test/unit/apipie/api_connection_test.rb
338
+ - test/unit/apipie/command_test.rb
336
339
  - test/unit/options/matcher_test.rb
337
- - test/unit/options/normalizers_test.rb
338
340
  - test/unit/options/option_collector_test.rb
339
341
  - test/unit/options/option_definition_test.rb
340
342
  - test/unit/options/sources/command_line_test.rb
341
343
  - test/unit/options/sources/saved_defaults_test.rb
344
+ - test/unit/options/normalizers_test.rb
342
345
  - test/unit/output/adapter/abstract_test.rb
343
346
  - test/unit/output/adapter/base_test.rb
344
347
  - test/unit/output/adapter/csv_test.rb
@@ -346,16 +349,14 @@ test_files:
346
349
  - test/unit/output/adapter/table_test.rb
347
350
  - test/unit/output/adapter/yaml_test.rb
348
351
  - test/unit/output/definition_test.rb
349
- - test/unit/output/dsl_test.rb
350
352
  - test/unit/output/field_filter_test.rb
351
353
  - test/unit/output/fields_test.rb
352
354
  - test/unit/output/formatters_test.rb
353
355
  - test/unit/output/output_test.rb
354
356
  - test/unit/output/record_collection_test.rb
357
+ - test/unit/output/dsl_test.rb
355
358
  - test/unit/abstract_test.rb
356
359
  - test/unit/completer_test.rb
357
- - test/unit/connection_test.rb
358
- - test/unit/defaults_test.rb
359
360
  - test/unit/fixtures/apipie/architectures.json
360
361
  - test/unit/fixtures/apipie/documented.json
361
362
  - test/unit/fixtures/defaults/defaults.yml
@@ -374,10 +375,13 @@ test_files:
374
375
  - test/unit/option_builder_test.rb
375
376
  - test/unit/settings_test.rb
376
377
  - test/unit/test_helper.rb
377
- - test/unit/utils_test.rb
378
+ - test/unit/ca_cert_manager_test.rb
378
379
  - test/unit/validator_test.rb
380
+ - test/unit/connection_test.rb
381
+ - test/unit/defaults_test.rb
382
+ - test/unit/csv_parser_test.rb
379
383
  - test/unit/exception_handler_test.rb
380
- - test/unit/ca_cert_manager_test.rb
384
+ - test/unit/utils_test.rb
381
385
  - test/functional/defaults_test.rb
382
386
  - test/functional/help_test.rb
383
387
  - test/functional/test_helper.rb