foodcritic 3.0.3 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +6 -14
  2. data/CHANGELOG.md +65 -0
  3. data/README.md +1 -1
  4. data/chef_dsl_metadata/chef_11.10.0.json +10272 -0
  5. data/chef_dsl_metadata/chef_11.10.2.json +10272 -0
  6. data/chef_dsl_metadata/chef_11.10.4.json +10272 -0
  7. data/chef_dsl_metadata/chef_11.6.2.json +9566 -0
  8. data/chef_dsl_metadata/chef_11.8.0.json +10074 -0
  9. data/chef_dsl_metadata/chef_11.8.2.json +10074 -0
  10. data/features/001_check_node_access.feature +60 -0
  11. data/features/003_check_for_chef_server.feature +5 -0
  12. data/features/006_check_file_mode.feature +1 -0
  13. data/features/022_check_for_dodgy_conditions_within_loop.feature +10 -0
  14. data/features/040_check_raw_git_usage.feature +6 -0
  15. data/features/047_check_for_attribute_assignment_without_precedence.feature +2 -0
  16. data/features/build_framework_support.feature +10 -0
  17. data/features/exclude_paths_to_lint.feature +19 -0
  18. data/features/show_lines_matched.feature +4 -4
  19. data/features/sort_warnings.feature +1 -1
  20. data/features/step_definitions/cookbook_steps.rb +117 -24
  21. data/features/support/command_helpers.rb +45 -12
  22. data/features/support/env.rb +5 -0
  23. data/lib/foodcritic/api.rb +122 -99
  24. data/lib/foodcritic/ast.rb +6 -7
  25. data/lib/foodcritic/chef.rb +24 -23
  26. data/lib/foodcritic/command_line.rb +49 -41
  27. data/lib/foodcritic/domain.rb +12 -10
  28. data/lib/foodcritic/dsl.rb +4 -7
  29. data/lib/foodcritic/error_checker.rb +0 -3
  30. data/lib/foodcritic/linter.rb +45 -43
  31. data/lib/foodcritic/notifications.rb +32 -32
  32. data/lib/foodcritic/output.rb +3 -6
  33. data/lib/foodcritic/rake_task.rb +9 -10
  34. data/lib/foodcritic/rules.rb +278 -240
  35. data/lib/foodcritic/template.rb +6 -13
  36. data/lib/foodcritic/version.rb +1 -1
  37. data/lib/foodcritic/xml.rb +1 -3
  38. data/man/foodcritic.1 +6 -2
  39. data/man/foodcritic.1.ronn +4 -1
  40. data/spec/foodcritic/linter_spec.rb +2 -2
  41. data/spec/regression/expected-output.txt +296 -1
  42. metadata +160 -138
@@ -0,0 +1,60 @@
1
+ Feature: Check Node Access
2
+
3
+ In order to be consistent in the way I access node attributes and to avoid confusing people new to Ruby
4
+ As a developer
5
+ I want to identify if the cookbooks access node attributes with symbols rather than strings
6
+
7
+ Scenario: Cookbook recipe accesses attributes via symbols
8
+ Given a cookbook with a single recipe that reads node attributes via symbols
9
+ When I check the cookbook
10
+ Then the node access warning 001 should be displayed
11
+
12
+ Scenario: Cookbook recipe accesses multiple attributes via symbols
13
+ Given a cookbook with a single recipe that accesses multiple node attributes via symbols
14
+ When I check the cookbook
15
+ Then the node access warning 001 should be displayed for each match
16
+
17
+ Scenario: Assignment of node attributes accessed via symbols
18
+ Given a cookbook with a single recipe that assigns node attributes accessed via symbols to a local variable
19
+ When I check the cookbook
20
+ Then the node access warning 001 should be displayed
21
+
22
+ Scenario: Cookbook recipe accesses nested attributes via symbols
23
+ Given a cookbook with a single recipe that accesses nested node attributes via symbols
24
+ When I check the cookbook
25
+ Then the node access warning 001 should be displayed twice for the same line
26
+
27
+ Scenario: Cookbook recipe accesses attributes via strings
28
+ Given a cookbook with a single recipe that reads node attributes via strings
29
+ When I check the cookbook
30
+ Then the node access warning 001 should not be displayed
31
+
32
+ Scenario: Cookbook recipe access attributes via strings and searches
33
+ Given a cookbook with a single recipe that searches based on a node attribute accessed via strings
34
+ When I check the cookbook
35
+ Then the node access warning 001 should not be displayed
36
+
37
+ Scenario: Cookbook recipe access attributes via symbols for template
38
+ Given a cookbook with a single recipe that passes node attributes accessed via symbols to a template
39
+ When I check the cookbook
40
+ Then the node access warning 001 should be displayed against the variables
41
+
42
+ Scenario: Cookbook recipe sets default attributes via symbols
43
+ Given a cookbook that declares default attributes via symbols
44
+ When I check the cookbook
45
+ Then the node access warning 001 should be displayed against the attributes file
46
+
47
+ Scenario: Cookbook recipe overrides attributes via symbols
48
+ Given a cookbook that declares override attributes via symbols
49
+ When I check the cookbook
50
+ Then the node access warning 001 should be displayed against the attributes file
51
+
52
+ Scenario: Cookbook recipe sets attributes via symbols
53
+ Given a cookbook that declares set attributes via symbols
54
+ When I check the cookbook
55
+ Then the node access warning 001 should be displayed against the attributes file
56
+
57
+ Scenario: Cookbook recipe sets normal attributes via symbols
58
+ Given a cookbook that declares normal attributes via symbols
59
+ When I check the cookbook
60
+ Then the node access warning 001 should be displayed against the attributes file
@@ -46,6 +46,11 @@ Feature: Check for Chef Server
46
46
  When I check the cookbook
47
47
  Then the check for server warning 003 should not be displayed against the condition
48
48
 
49
+ Scenario: Search checking for server (ternary)
50
+ Given a cookbook with a single recipe that searches but checks first (ternary) to see if this is server
51
+ When I check the cookbook
52
+ Then the check for server warning 003 should not be displayed against the condition
53
+
49
54
  Scenario Outline: Search checking for server (return)
50
55
  Given a cookbook with a single recipe that searches but returns first (<format>) if search is not supported
51
56
  When I check the cookbook
@@ -25,6 +25,7 @@ Feature: Check file mode
25
25
  | file | 644 | invalid |
26
26
  | file | 044 | invalid |
27
27
  | file | "0644" | valid |
28
+ | file | ary[1] | valid |
28
29
  | template | 00400 | valid |
29
30
  | template | 400 | invalid |
30
31
  | template | "400" | valid |
@@ -31,3 +31,13 @@ Feature: Check for dodgy resource conditions within a loop
31
31
  Given a resource declared with a guard within a loop with multiple block arguments
32
32
  When I check the cookbook
33
33
  Then the dodgy resource condition warning 022 should not be shown
34
+
35
+ Scenario: Resource guard contains a block
36
+ Given a resource that declares a guard containing a block
37
+ When I check the cookbook
38
+ Then the dodgy resource condition warning 022 should not be shown
39
+
40
+ Scenario: Loop in a definition
41
+ Given a resource declared within a definition
42
+ When I check the cookbook
43
+ Then the dodgy resource condition warning 022 should not be shown
@@ -31,7 +31,13 @@ Feature: Check for direct usage of git
31
31
  | git fetch origin | should |
32
32
  | git checkout master | should |
33
33
  | git reset --hard | should |
34
+ | git status && git pull | should |
34
35
  | git show | should not |
35
36
  | echo 'bob' && git show | should not |
36
37
  | gitk | should not |
37
38
  | curl http://github.com/ | should not |
39
+
40
+ Scenario: Multiple execute resources
41
+ Given a cookbook recipe with multiple execute resources where the last uses git
42
+ When I check the cookbook
43
+ Then the execute resource used to run git commands warning 040 should be displayed against the last resource
@@ -21,8 +21,10 @@ Feature: Check for attribute assignment without specified precedence
21
21
  | node.normal['foo'] = 'bar' | should not |
22
22
  | node.default['foo'] = 'bar' | should not |
23
23
  | node.force_default['foo'] = 'bar' | should not |
24
+ | node.default!['foo'] = 'bar' | should not |
24
25
  | node.set['foo'] = 'bar' | should not |
25
26
  | node.override['foo'] = 'bar' | should not |
27
+ | node.override!['foo'] = 'bar' | should not |
26
28
  | node.force_override['foo'] = 'bar' | should not |
27
29
  | node.automatic_attrs['foo'] = 'bar' | should not |
28
30
  | node['foos'] << 'bar' | should |
@@ -67,6 +67,16 @@ I want to be able to invoke the lint tool from my build
67
67
  | style | {:fail_tags => ['correctness,style']} | fail | FC002 |
68
68
  | style,correctness | {:fail_tags => [], :tags => ['correctness']} | succeed | FC006 |
69
69
 
70
+ @context
71
+ Scenario: Specify that contexts should be shown
72
+ Given a cookbook with a single recipe that reads node attributes via symbols,strings
73
+ And the cookbook has a Gemfile that includes rake and foodcritic
74
+ And a Rakefile that defines a lint task with a block setting options to {:context => true}
75
+ When I run the build
76
+ Then the recipe filename should be displayed
77
+ And the attribute consistency warning 019 should be displayed below
78
+ And the line number and line of code that triggered the warning should be displayed
79
+
70
80
  Scenario Outline: Specify paths to lint
71
81
  Given a cookbook that has <problems> problems
72
82
  And the cookbook has a Gemfile that includes rake and foodcritic
@@ -0,0 +1,19 @@
1
+ Feature: Exclude paths from being linted
2
+
3
+ In order to avoid linting some paths that are not really from the cookbook
4
+ As a developer
5
+ I want to be able to exclude some files or directories from the passed paths
6
+
7
+ Scenario: Don't exclude a non cookbook directory
8
+ Given a cookbook that has style problems
9
+ And unit tests under a top-level test directory
10
+ When I check the cookbook without excluding the test directory
11
+ Then warnings will be displayed against the tests
12
+ And the style warning 002 should be displayed
13
+
14
+ Scenario: Exclude a non cookbook directory
15
+ Given a cookbook that has style problems
16
+ And unit tests under a top-level test directory
17
+ When I check the cookbook excluding the test directory
18
+ Then no warnings will be displayed against the tests
19
+ And the style warning 002 should be displayed
@@ -6,15 +6,15 @@ Feature: Show Lines Matched
6
6
  I want to be able to see the lines the warning matches against, with context
7
7
 
8
8
  Scenario: Recipe with a single warning
9
- Given a cookbook with a single recipe that reads node attributes via symbols,strings
9
+ Given a cookbook with a single recipe that reads node attributes via symbols
10
10
  When I check the cookbook, specifying that context should be shown
11
11
  Then the recipe filename should be displayed
12
- And the attribute consistency warning 019 should be displayed below
12
+ And the node access warning 001 should be displayed below
13
13
  And the line number and line of code that triggered the warning should be displayed
14
14
 
15
15
  Scenario: Recipe with a multiple warnings of the same type
16
- Given a cookbook with a single recipe that reads multiple node attributes via symbols,strings
16
+ Given a cookbook with a single recipe that accesses multiple node attributes via symbols
17
17
  When I check the cookbook, specifying that context should be shown
18
18
  Then the recipe filename should be displayed
19
- And the attribute consistency warning 019 should be displayed below
19
+ And the node access warning 001 should be displayed below
20
20
  And the line number and line of code that triggered the warnings should be displayed
@@ -7,4 +7,4 @@ Feature: Sort warnings
7
7
  Scenario: Recipe has warnings on lines that don't sort non-numerically
8
8
  Given a cookbook with a single recipe which accesses node attributes with symbols on lines 2 and 10
9
9
  When I check the cookbook
10
- Then the attribute consistency warning 019 should warn on lines 2 and 10 in that order
10
+ Then the node access warning 001 should warn on lines 2 and 10 in that order
@@ -758,6 +758,20 @@ Given 'a cookbook recipe with a service resource with an action specified via a
758
758
  }.strip
759
759
  end
760
760
 
761
+ Given 'a cookbook recipe with multiple execute resources where the last uses git' do
762
+ write_recipe %q{
763
+ execute "one" do
764
+ command "ls -al"
765
+ end
766
+ execute "two" do
767
+ command "df -H"
768
+ end
769
+ execute "three" do
770
+ command "git clone https://example.org/bar.git"
771
+ end
772
+ }.strip
773
+ end
774
+
761
775
  Given 'a cookbook template that uses all variables passed' do
762
776
  write_recipe %q{
763
777
  template "/tmp/config.conf" do
@@ -1045,13 +1059,10 @@ Given 'a cookbook with a single recipe that mixes node access types in an interp
1045
1059
  }
1046
1060
  end
1047
1061
 
1048
- Given 'a cookbook with a single recipe that reads multiple node attributes via symbols,strings' do
1062
+ Given 'a cookbook with a single recipe that accesses multiple node attributes via symbols' do
1049
1063
  write_recipe %q{
1050
1064
  node[:foo] = 'bar'
1051
- node[:baz] = 'foo'
1052
- node[:wham] = 'shazam'
1053
- node['testing'] = 'bar'
1054
- node['testing2'] = 'bar2'
1065
+ node[:testing] = 'bar'
1055
1066
  }
1056
1067
  end
1057
1068
 
@@ -1159,12 +1170,12 @@ Given 'a cookbook with a single recipe which accesses node attributes with symbo
1159
1170
  # Here we access the node attributes via a symbol
1160
1171
  foo = node[:foo]
1161
1172
 
1162
- # String access is in the majority
1163
- node['foo']
1164
- node['bar']
1165
- node['baz']
1173
+ directory "/tmp/foo" do
1174
+ owner "root"
1175
+ group "root"
1176
+ action :create
1177
+ end
1166
1178
 
1167
- # Second access via a symbol
1168
1179
  bar = node[:bar]
1169
1180
  }
1170
1181
  end
@@ -1203,6 +1214,12 @@ Given /^a cookbook with a single recipe that searches but checks first( \(string
1203
1214
  }
1204
1215
  end
1205
1216
 
1217
+ Given 'a cookbook with a single recipe that searches but checks first (ternary) to see if this is server' do
1218
+ write_recipe %Q{
1219
+ required_node = Chef::Config[:solo] ? node : search(:node, query).first
1220
+ }
1221
+ end
1222
+
1206
1223
  Given /^a cookbook with a single recipe that searches but checks with a negative first to see if this is server$/ do
1207
1224
  write_recipe %q{
1208
1225
  unless Chef::Config['solo']
@@ -1437,6 +1454,40 @@ Given 'a resource declared with a guard within a loop with multiple block argume
1437
1454
  }
1438
1455
  end
1439
1456
 
1457
+ Given 'a resource that declares a guard containing a block' do
1458
+ write_recipe %q{
1459
+ template '/etc/foo' do
1460
+ not_if do
1461
+ s = false
1462
+ node['mylist'].each do |realm|
1463
+ s = node['mylist'][realm].empty?
1464
+ break if s
1465
+ end
1466
+ s
1467
+ end
1468
+ owner 'root'
1469
+ group 'root'
1470
+ mode '0644'
1471
+ source 'foo.erb'
1472
+ end
1473
+ }
1474
+ end
1475
+
1476
+
1477
+ Given 'a resource declared within a definition' do
1478
+ write_recipe %q{
1479
+ define :toto, {
1480
+ } do
1481
+ [:a, :b].each do |x|
1482
+ package x do
1483
+ not_if { node['foo'] == x }
1484
+ action :install
1485
+ end
1486
+ end
1487
+ end
1488
+ }
1489
+ end
1490
+
1440
1491
  Given /^a rule that (declares|does not declare) a version constraint(?: of ([^ ]+)? to ([^ ]+)?)?$/ do |constraint, from, to|
1441
1492
  if from || to
1442
1493
  rule_with_version_constraint(from, to)
@@ -1736,6 +1787,10 @@ When /^I check both cookbooks with the command-line (.*)$/ do |command_line|
1736
1787
  run_lint(cmds)
1737
1788
  end
1738
1789
 
1790
+ When 'I check both roles directories' do
1791
+ run_lint ['-R', 'roles1', '-R', 'roles2']
1792
+ end
1793
+
1739
1794
  When 'I check the cookbooks, role and environment together' do
1740
1795
  run_lint([
1741
1796
  '-B', 'cookbooks/another_example', '-B', 'cookbooks/example',
@@ -1752,14 +1807,15 @@ When 'I check the environment directory' do
1752
1807
  run_lint ['-E', 'environments']
1753
1808
  end
1754
1809
 
1755
- When 'I check both roles directories' do
1756
- run_lint ['-R', 'roles1', '-R', 'roles2']
1757
- end
1758
-
1759
1810
  When 'I check the eu environment file only' do
1760
1811
  run_lint ['-E', 'environments/production_eu.rb']
1761
1812
  end
1762
1813
 
1814
+ When /^I check the cookbook( without)? excluding the ([^ ]+) directory$/ do |no_exclude, dir|
1815
+ options = no_exclude.nil? ? ['-X', dir] : []
1816
+ run_lint(options + ['cookbooks/example'])
1817
+ end
1818
+
1763
1819
  When 'I check the recipe' do
1764
1820
  run_lint(["cookbooks/example/recipes/default.rb"])
1765
1821
  end
@@ -1852,11 +1908,11 @@ Then 'a warning for the custom rule should be displayed' do
1852
1908
  end
1853
1909
 
1854
1910
  Then 'all options should be documented in the man page' do
1855
- man_page_options.should == usage_options_for_diff
1911
+ man_page_options.must_equal usage_options_for_diff
1856
1912
  end
1857
1913
 
1858
1914
  Then /^an? '([^']+)' error should be displayed$/ do |expected_error|
1859
- last_error.should include expected_error
1915
+ last_error.must_include expected_error
1860
1916
  end
1861
1917
 
1862
1918
  Then 'the attribute consistency warning 019 should be shown for both of the recipes that use symbols' do
@@ -1873,6 +1929,10 @@ Then /^the bare attribute keys warning 044 should not be displayed against the (
1873
1929
  expect_warning 'FC044', {:expect_warning => false, :line => 2, :file_type => :attributes}
1874
1930
  end
1875
1931
 
1932
+ Then 'the execute resource used to run git commands warning 040 should be displayed against the last resource' do
1933
+ expect_warning 'FC040', {:line => 7}
1934
+ end
1935
+
1876
1936
  Then /^the LWRP does not notify when updated warning 017 should( not)? be shown against the :([^ ]+) action$/ do |not_shown, action|
1877
1937
  line = action == 'create' ? 1 : 8
1878
1938
  expect_warning('FC017', :file_type => :provider, :expect_warning => ! not_shown, :line => line)
@@ -1914,15 +1974,19 @@ end
1914
1974
 
1915
1975
  Then /^the lint task will be listed( under the different name)?$/ do |diff_name|
1916
1976
  expected_name = diff_name ? 'lint' : 'foodcritic'
1917
- build_tasks.should include([expected_name, 'Lint Chef cookbooks'])
1977
+ build_tasks.must_include([expected_name, 'Lint Chef cookbooks'])
1918
1978
  end
1919
1979
 
1920
1980
  Then 'no error should have occurred' do
1921
1981
  assert_no_error_occurred
1922
1982
  end
1923
1983
 
1924
- Then /^no warnings will be displayed against the tests$/ do
1925
- assert_no_test_warnings
1984
+ Then /^(no )?warnings will be displayed against the tests$/ do |no_display|
1985
+ if no_display.nil?
1986
+ assert_test_warnings
1987
+ else
1988
+ assert_no_test_warnings
1989
+ end
1926
1990
  end
1927
1991
 
1928
1992
  Then 'the attribute consistency warning 019 should warn on lines 2 and 10 in that order' do
@@ -1982,7 +2046,7 @@ Then /^the build will (succeed|fail) with (?:no )?warnings(.*)$/ do |build_outco
1982
2046
  end
1983
2047
 
1984
2048
  Then 'the check for server warning 003 should not be displayed against the condition' do
1985
- expect_warning("FC003", :line => 5, :expect_warning => false)
2049
+ expect_warning("FC003", :line => nil, :expect_warning => false)
1986
2050
  end
1987
2051
 
1988
2052
  Then /^the check for server warning 003 should not be displayed against the search after the (.*) conditional$/ do |format|
@@ -2038,10 +2102,10 @@ end
2038
2102
 
2039
2103
  Then /^the line number and line of code that triggered the warning(s)? should be displayed$/ do |multiple|
2040
2104
  if multiple.nil?
2041
- expect_line_shown 2, "log node['foo']"
2105
+ expect_line_shown 1, "log node[:foo]"
2042
2106
  else
2043
- expect_line_shown 4, " node['testing'] = 'bar'"
2044
- expect_line_shown 5, " node['testing2'] = 'bar2'"
2107
+ expect_line_shown 1, "node[:foo] = 'bar'"
2108
+ expect_line_shown 2, " node[:testing] = 'bar'"
2045
2109
  end
2046
2110
  end
2047
2111
 
@@ -2053,6 +2117,35 @@ Then /^the no leading cookbook name warning 029 should be (not )?shown$/ do |sho
2053
2117
  expect_warning('FC029', :line => 1, :expect_warning => should_not.nil?, :file => 'metadata.rb')
2054
2118
  end
2055
2119
 
2120
+ Then 'the node access warning 001 should be displayed for each match' do
2121
+ expect_warning('FC001', :line => 1)
2122
+ expect_warning('FC001', :line => 2)
2123
+ end
2124
+
2125
+ Then 'the node access warning 001 should be displayed against the variables' do
2126
+ expect_warning('FC001', :line => 4)
2127
+ expect_warning('FC001', :line => 5)
2128
+ end
2129
+
2130
+ Then 'the node access warning 001 should be displayed twice for the same line' do
2131
+ expect_warning('FC001', :line => 1, :num_occurrences => 2)
2132
+ end
2133
+
2134
+ Then 'the node access warning 001 should warn on lines 2 and 10 in that order' do
2135
+ expected_warnings = [2, 10].map do |line|
2136
+ "FC001: Use strings in preference to symbols to access node attributes: cookbooks/example/recipes/default.rb:#{line}"
2137
+ end
2138
+ expect_output(expected_warnings.join("\n"))
2139
+ end
2140
+
2141
+ Then 'the node access warning 001 should be displayed for the recipe' do
2142
+ expect_warning('FC001')
2143
+ end
2144
+
2145
+ Then 'the node access warning 001 should not be displayed for the attributes' do
2146
+ expect_warning("FC001", :file_type => :attributes, :line => 1, :expect_warning => false)
2147
+ end
2148
+
2056
2149
  Then 'the prefer chef_gem to manual install warning 025 should be shown' do
2057
2150
  expect_warning('FC025', :line => nil)
2058
2151
  end
@@ -2115,7 +2208,7 @@ end
2115
2208
 
2116
2209
  Then 'the usage text should include an option for specifying tags that will fail the build' do
2117
2210
  expect_usage_option('f', 'epic-fail TAGS',
2118
- "Fail the build if any of the specified tags are matched ('any' -> fail on any match).")
2211
+ "Fail the build based on tags. Use 'any' to fail on all warnings.")
2119
2212
  end
2120
2213
 
2121
2214
  Then /^the warnings shown should be (.*)$/ do |warnings|
@@ -5,8 +5,16 @@ module FoodCritic
5
5
  # Unless the environment variable FC_FORK_PROCESS is set to 'true' then the features will be run in the same process.
6
6
  module CommandHelpers
7
7
 
8
+ include MiniTest::Assertions
9
+
10
+ attr_writer :assertions
11
+ def assertions
12
+ @assertions ||= 0
13
+ end
14
+
8
15
  # The warning codes and messages displayed to the end user.
9
16
  WARNINGS = {
17
+ 'FC001' => 'Use strings in preference to symbols to access node attributes',
10
18
  'FC002' => 'Avoid string interpolation where not required',
11
19
  'FC003' => 'Check whether you are running with chef server before using server-specific features',
12
20
  'FC004' => 'Use a service resource to start and stop services',
@@ -131,6 +139,12 @@ module FoodCritic
131
139
  expect_output(Regexp.new(expected_switch))
132
140
  end
133
141
 
142
+ def has_test_warnings?(output)
143
+ output.split("\n").grep(/FC[0-9]+:/).map do |warn|
144
+ File.basename(File.dirname(warn.split(':').take(3).last.strip))
145
+ end.include?('test')
146
+ end
147
+
134
148
  def man_page_options
135
149
  man_path = Pathname.new(__FILE__) + '../../../man/foodcritic.1.ronn'
136
150
  option_lines = File.read(man_path).split('## ').find do |s|
@@ -167,10 +181,10 @@ module FoodCritic
167
181
  :description => 'Only check against rules valid for this version of Chef.'},
168
182
 
169
183
  {:short => 'f', :long => 'epic-fail TAGS',
170
- :description => "Fail the build if any of the specified tags are matched ('any' -> fail on any match)."},
184
+ :description => "Fail the build based on tags. Use 'any' to fail on all warnings."},
171
185
 
172
186
  {:short => 't', :long => 'tags TAGS',
173
- :description => 'Only check against rules with the specified tags.'},
187
+ :description => 'Check against (or exclude ~) rules with the specified tags.'},
174
188
 
175
189
  {:short => 'B', :long => 'cookbook-path PATH',
176
190
  :description => 'Cookbook path(s) to check.'},
@@ -191,7 +205,10 @@ module FoodCritic
191
205
  :description => 'Specify grammar to use when validating search syntax.'},
192
206
 
193
207
  {:short => 'V', :long => 'version',
194
- :description => 'Display the foodcritic version.'}
208
+ :description => 'Display the foodcritic version.'},
209
+
210
+ {:short => 'X', :long => 'exclude PATH',
211
+ :description => 'Exclude path(s) from being linted.'}
195
212
 
196
213
  ]
197
214
  end
@@ -213,9 +230,9 @@ module FoodCritic
213
230
  # @param [String] output The warning to check for.
214
231
  def expect_output(output)
215
232
  if output.respond_to?(:~)
216
- @review.should match(output)
233
+ @review.must_match(output)
217
234
  else
218
- @review.should include(output)
235
+ @review.must_include(output)
219
236
  end
220
237
  end
221
238
 
@@ -224,20 +241,32 @@ module FoodCritic
224
241
  # @param [String] output The output to check for.
225
242
  def expect_no_output(output)
226
243
  if output.respond_to?(:~)
227
- @review.should_not match(output)
244
+ @review.wont_match(output)
228
245
  else
229
- @review.should_not include(output)
246
+ @review.wont_include(output)
230
247
  end
231
248
  end
232
249
 
233
250
  # Assert that an error occurred following a lint check.
234
251
  def assert_error_occurred
235
- @status.should_not == 0
252
+ @status.wont_equal 0
236
253
  end
237
254
 
238
255
  # Assert that no error occurred following a lint check.
239
256
  def assert_no_error_occurred
240
- @status.should == 0
257
+ @status.must_equal 0
258
+ end
259
+
260
+ # Assert that warnings have not been raised against the test code which
261
+ # should have been excluded from linting.
262
+ def assert_no_test_warnings
263
+ refute has_test_warnings?(@review)
264
+ end
265
+
266
+ # Assert that warnings have been raised against the test code which
267
+ # shouldn't have been excluded from linting.
268
+ def assert_test_warnings
269
+ assert has_test_warnings?(@review)
241
270
  end
242
271
 
243
272
  # Run a lint check with the provided command line arguments.
@@ -278,9 +307,13 @@ module FoodCritic
278
307
  # Assert that warnings have not been raised against the test code which
279
308
  # should have been excluded from linting.
280
309
  def assert_no_test_warnings
281
- all_output.split("\n").grep(/FC[0-9]+:/).map do |warn|
282
- File.basename(File.dirname(warn.split(':').take(3).last.strip))
283
- end.should_not include 'test'
310
+ refute has_test_warnings?(all_output)
311
+ end
312
+
313
+ # Assert that warnings have been raised against the test code which
314
+ # shouldn't have been excluded from linting.
315
+ def assert_test_warnings
316
+ assert has_test_warnings?(all_output)
284
317
  end
285
318
 
286
319
  # The available tasks for this build