flog 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +15 -1
- data/Manifest.txt +17 -0
- data/bin/flog +52 -23
- data/lib/flog.rb +230 -179
- data/lib/gauntlet_flog.rb +193 -0
- data/spec/flog_command_spec.rb +352 -0
- data/spec/flog_integration_spec.rb +944 -0
- data/spec/flog_spec.rb +1123 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +36 -0
- data/spec_fixtures/collection/bigger_example/acts/date_range.rb +199 -0
- data/spec_fixtures/collection/bigger_example/acts/range.rb +391 -0
- data/spec_fixtures/collection/bigger_example/association_extensions/date_ranged.rb +11 -0
- data/spec_fixtures/collection/bigger_example/association_extensions/ranged.rb +13 -0
- data/spec_fixtures/collection/bigger_example/reflection_extensions/ranged.rb +50 -0
- data/spec_fixtures/directory/bot_filter.rb +70 -0
- data/spec_fixtures/directory/bot_parser.rb +79 -0
- data/spec_fixtures/directory/bot_parser_format.rb +23 -0
- data/spec_fixtures/directory/bot_sender.rb +46 -0
- data/spec_fixtures/empty/empty.rb +0 -0
- data/spec_fixtures/simple/simple.rb +191 -0
- metadata +21 -4
@@ -0,0 +1,193 @@
|
|
1
|
+
#!/usr/bin/ruby -ws
|
2
|
+
|
3
|
+
$: << 'lib' << '../../ParseTree/dev/lib' << '../../flog/dev/lib'
|
4
|
+
|
5
|
+
$v ||= false # HACK
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'flog'
|
9
|
+
|
10
|
+
require 'gauntlet'
|
11
|
+
require 'pp'
|
12
|
+
|
13
|
+
class FlogGauntlet < Gauntlet
|
14
|
+
$owners = {}
|
15
|
+
$score_file = 'flog-scores.yml'
|
16
|
+
$misc_error = {:total => -1, :average => -1, :methods => {}}
|
17
|
+
$syntax_error = {:total => -2, :average => -2, :methods => {}}
|
18
|
+
$no_gem = {:total => -4, :average => -4, :methods => {}}
|
19
|
+
|
20
|
+
# copied straight from hoedown.rb
|
21
|
+
my_projects = %w[InlineFortran ParseTree RubyInline RubyToC
|
22
|
+
ZenHacks ZenTest bfts box_layout
|
23
|
+
change_class flay flog gauntlet heckle
|
24
|
+
hoe image_science miniunit minitest
|
25
|
+
minitest_tu_shim png ruby2ruby ruby_parser
|
26
|
+
rubyforge test-unit un vlad zenprofile
|
27
|
+
zentest]
|
28
|
+
|
29
|
+
MY_PROJECTS = Regexp.union(*my_projects)
|
30
|
+
|
31
|
+
def run name
|
32
|
+
warn name
|
33
|
+
self.data[name] = score_for '.'
|
34
|
+
self.dirty = true
|
35
|
+
end
|
36
|
+
|
37
|
+
def display_report max = 10
|
38
|
+
scores = @data.reject { |k,v| v[:total].nil? or v[:methods].empty? }
|
39
|
+
project_numbers = scores.map { |k,v| [k, v[:methods].values] }
|
40
|
+
project_stats = project_numbers.map { |k,v| [k, scores[k][:size], v.average, v.stddev] }
|
41
|
+
|
42
|
+
method_count = 0
|
43
|
+
project_stats.each do |_, n, _, _|
|
44
|
+
method_count += n
|
45
|
+
end
|
46
|
+
|
47
|
+
group_by_owner
|
48
|
+
|
49
|
+
title "Statistics" do
|
50
|
+
flog_numbers = scores.map { |k,v| v[:total] }
|
51
|
+
method_counts = scores.map { |k,v| v[:size] }
|
52
|
+
|
53
|
+
puts "total # gems : %8d" % scores.size
|
54
|
+
puts "total # methods : %8d" % method_count
|
55
|
+
puts "avg methods / gem : %8.2f (%8.2f stddev)" % [method_counts.average, method_counts.stddev]
|
56
|
+
puts "avg flog / gem : %8.2f (%8.2f stddev)" % [flog_numbers.average, flog_numbers.stddev]
|
57
|
+
end
|
58
|
+
|
59
|
+
worst = scores.sort_by { |k,v| -v[:total] }.first(max)
|
60
|
+
report_worst "Worst Projects EVAR", worst do |project, score|
|
61
|
+
owner = $owners[project].join(', ') rescue nil
|
62
|
+
owner = "Some Lazy Bastard" if owner.empty?
|
63
|
+
raise "#{project} seems not to have an owner" if owner.nil?
|
64
|
+
[score[:total], project, owner]
|
65
|
+
end
|
66
|
+
|
67
|
+
worst = {}
|
68
|
+
scores.each do |long_name, spec|
|
69
|
+
name = long_name.sub(/-(\d+\.)*\d+\.gem$/, '')
|
70
|
+
spec[:methods].each do |method_name, score|
|
71
|
+
worst[[name, method_name]] = score
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
worst = worst.sort_by { |_,v| -v }.first(max)
|
76
|
+
|
77
|
+
max_size = worst.map { |(name, meth), score| name.size }.max
|
78
|
+
title "Worth Methods EVAR"
|
79
|
+
worst.each_with_index do |((name, meth), score), i|
|
80
|
+
puts "%3d: %9.2f: %-#{max_size}s %s" % [i + 1, score, name, meth]
|
81
|
+
end
|
82
|
+
|
83
|
+
report "Methods per Gem", project_stats.sort_by { |n, c, a, sd| -c }.first(max)
|
84
|
+
report "Avg Flog / Method", project_stats.sort_by { |n, c, a, sd| -a }.first(max)
|
85
|
+
|
86
|
+
$score_per_owner = Hash.new(0.0)
|
87
|
+
$projects_per_owner = Hash.new { |h,k| h[k] = {} }
|
88
|
+
$owners.each do |project, owners|
|
89
|
+
next unless scores.has_key? project # bad project
|
90
|
+
owners.each do |owner|
|
91
|
+
next if owner =~ /FI\XME full name|NOT Ryan Davis/
|
92
|
+
score = scores[project][:total] || 1000000
|
93
|
+
$projects_per_owner[owner][project] = score
|
94
|
+
$score_per_owner[owner] += score
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
report_bad_people "Top Flog Scores per Developer" do
|
99
|
+
$projects_per_owner.sort_by { |k,v| -v.values.average }.first(max)
|
100
|
+
end
|
101
|
+
|
102
|
+
report_bad_people "Most Prolific Developers" do |k,v|
|
103
|
+
$projects_per_owner.sort_by { |k,v| [-v.size, -$score_per_owner[k]] }.first(max)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
############################################################
|
108
|
+
# OTHER
|
109
|
+
############################################################
|
110
|
+
|
111
|
+
def score_for dir
|
112
|
+
# files = `find #{dir} -name \\*.rb | grep -v gen.*templ`.split(/\n/)
|
113
|
+
files = Dir["**/*.rb"].reject { |f| f =~ /gen.*templ|gemspec.rb/ }
|
114
|
+
|
115
|
+
flogger = Flog.new
|
116
|
+
flogger.flog_files files
|
117
|
+
methods = flogger.totals.reject { |k,v| k =~ /\#none$/ }
|
118
|
+
n = 20
|
119
|
+
topN = Hash[*methods.sort_by { |k,v| -v }.first(n).flatten]
|
120
|
+
{
|
121
|
+
:max => methods.values.max,
|
122
|
+
:total => flogger.total,
|
123
|
+
:size => methods.size,
|
124
|
+
:average => flogger.average,
|
125
|
+
:stddev => flogger.stddev,
|
126
|
+
:methods => topN,
|
127
|
+
}
|
128
|
+
rescue SyntaxError => e
|
129
|
+
warn e.inspect + " at " + e.backtrace.first(5).join(', ') if $v
|
130
|
+
$syntax_error.dup
|
131
|
+
rescue => e
|
132
|
+
warn e.inspect + " at " + e.backtrace.first(5).join(', ') if $v
|
133
|
+
$misc_error.dup
|
134
|
+
end
|
135
|
+
|
136
|
+
def title heading
|
137
|
+
puts
|
138
|
+
puts "#{heading}:"
|
139
|
+
puts
|
140
|
+
yield if block_given?
|
141
|
+
end
|
142
|
+
|
143
|
+
def report title, data
|
144
|
+
max = data.map { |d| d.first.size }.max
|
145
|
+
|
146
|
+
title "Top #{data.size} #{title}" if title
|
147
|
+
data.each_with_index do |(n, c, a, s), i|
|
148
|
+
puts "%4d: %-#{max}s: %5d methods, %8.2f +/- %8.2f flog" % [i + 1, n, c, a, s]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def report_bad_people section
|
153
|
+
title section
|
154
|
+
bad_people = yield
|
155
|
+
max_size = bad_people.map { |a| a.first.size }.max
|
156
|
+
fmt = "%4d: %-#{max_size}s: %2d projects %8.1f tot %8.1f avg"
|
157
|
+
bad_people.each_with_index do |(name, projects), i|
|
158
|
+
avg = projects.values.average
|
159
|
+
puts fmt % [i + 1, name, projects.size, $score_per_owner[name], avg]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def report_worst section, data
|
164
|
+
title section do
|
165
|
+
max_size = data.map { |k| k.first.size }.max
|
166
|
+
data.each_with_index do |(k,v), i|
|
167
|
+
puts "%3d: %9.2f: %-#{max_size}s %s" % [i + 1, *yield(k, v)]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def group_by_owner
|
173
|
+
latest_gems.each do |spec|
|
174
|
+
name = spec.name
|
175
|
+
owner = spec.authors.compact
|
176
|
+
owner = Array(spec.email) if owner.empty?
|
177
|
+
owner.map! { |o| o.sub(/\s*[^ \w@.].*$/, '') }
|
178
|
+
owner = ["NOT Ryan Davis"] if owner.include? "Ryan Davis" and name !~ MY_PROJECTS
|
179
|
+
|
180
|
+
# because we screwed these up back before hoe
|
181
|
+
owner << "Eric Hodel" if name =~ /bfts|RubyToC|ParseTree|heckle/
|
182
|
+
|
183
|
+
$owners[spec.full_name] = owner.uniq || 'omg I have no idea'
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
max = (ARGV.shift || 10).to_i
|
189
|
+
filter = ARGV.shift
|
190
|
+
filter = Regexp.new filter if filter
|
191
|
+
flogger = FlogGauntlet.new
|
192
|
+
flogger.run_the_gauntlet filter
|
193
|
+
flogger.display_report max
|
@@ -0,0 +1,352 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
require 'flog'
|
3
|
+
|
4
|
+
describe 'flog command' do
|
5
|
+
before :each do
|
6
|
+
@flog = stub('Flog', :flog_files => true, :report => true)
|
7
|
+
Flog.stubs(:new).returns(@flog)
|
8
|
+
self.stubs(:exit)
|
9
|
+
self.stubs(:puts)
|
10
|
+
end
|
11
|
+
|
12
|
+
def run_command
|
13
|
+
eval File.read(File.join(File.dirname(__FILE__), *%w[.. bin flog]))
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'when no command-line arguments are specified' do
|
17
|
+
before :each do
|
18
|
+
Object.send(:remove_const, :ARGV)
|
19
|
+
ARGV = []
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should run' do
|
23
|
+
lambda { run_command }.should_not raise_error(Errno::ENOENT)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should not alter the include path' do
|
27
|
+
@paths = $:.dup
|
28
|
+
run_command
|
29
|
+
$:.should == @paths
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should create a Flog instance' do
|
33
|
+
Flog.expects(:new).returns(@flog)
|
34
|
+
run_command
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should not have any options flags set' do
|
38
|
+
Flog.expects(:new).with({}).returns(@flog)
|
39
|
+
run_command
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should call flog_files on the Flog instance' do
|
43
|
+
@flog.expects(:flog_files)
|
44
|
+
run_command
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should pass '-' (for the file path) to flog_files on the instance" do
|
48
|
+
@flog.expects(:flog_files).with(['-'])
|
49
|
+
run_command
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should call report on the Flog instance' do
|
53
|
+
@flog.expects(:report)
|
54
|
+
run_command
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should exit with status 0' do
|
58
|
+
self.expects(:exit).with(0)
|
59
|
+
run_command
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "when -a is specified on the command-line" do
|
64
|
+
before :each do
|
65
|
+
Object.send(:remove_const, :ARGV)
|
66
|
+
ARGV = ['-a']
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should create a Flog instance' do
|
70
|
+
Flog.expects(:new).returns(@flog)
|
71
|
+
run_command
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should set the option to show all methods" do
|
75
|
+
Flog.expects(:new).with(:all => true).returns(@flog)
|
76
|
+
run_command
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should exit with status 0' do
|
80
|
+
self.expects(:exit).with(0)
|
81
|
+
run_command
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "when --all is specified on the command-line" do
|
86
|
+
before :each do
|
87
|
+
Object.send(:remove_const, :ARGV)
|
88
|
+
ARGV = ['--all']
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should create a Flog instance' do
|
92
|
+
Flog.expects(:new).returns(@flog)
|
93
|
+
run_command
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should set the option to show all methods" do
|
97
|
+
Flog.expects(:new).with(:all => true).returns(@flog)
|
98
|
+
run_command
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should exit with status 0' do
|
102
|
+
self.expects(:exit).with(0)
|
103
|
+
run_command
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "when -s is specified on the command-line" do
|
108
|
+
before :each do
|
109
|
+
Object.send(:remove_const, :ARGV)
|
110
|
+
ARGV = ['-s']
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should create a Flog instance' do
|
114
|
+
Flog.expects(:new).returns(@flog)
|
115
|
+
run_command
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should set the option to show only the score" do
|
119
|
+
Flog.expects(:new).with(:score => true).returns(@flog)
|
120
|
+
run_command
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'should exit with status 0' do
|
124
|
+
self.expects(:exit).with(0)
|
125
|
+
run_command
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "when --score is specified on the command-line" do
|
130
|
+
before :each do
|
131
|
+
Object.send(:remove_const, :ARGV)
|
132
|
+
ARGV = ['--score']
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'should create a Flog instance' do
|
136
|
+
Flog.expects(:new).returns(@flog)
|
137
|
+
run_command
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should set the option to show only the score" do
|
141
|
+
Flog.expects(:new).with(:score => true).returns(@flog)
|
142
|
+
run_command
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should exit with status 0' do
|
146
|
+
self.expects(:exit).with(0)
|
147
|
+
run_command
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe "when -m is specified on the command-line" do
|
152
|
+
before :each do
|
153
|
+
Object.send(:remove_const, :ARGV)
|
154
|
+
ARGV = ['-m']
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should create a Flog instance' do
|
158
|
+
Flog.expects(:new).returns(@flog)
|
159
|
+
run_command
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should set the option to report on methods only" do
|
163
|
+
Flog.expects(:new).with(:methods => true).returns(@flog)
|
164
|
+
run_command
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'should exit with status 0' do
|
168
|
+
self.expects(:exit).with(0)
|
169
|
+
run_command
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe "when --methods-only is specified on the command-line" do
|
174
|
+
before :each do
|
175
|
+
Object.send(:remove_const, :ARGV)
|
176
|
+
ARGV = ['--methods-only']
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should create a Flog instance' do
|
180
|
+
Flog.expects(:new).returns(@flog)
|
181
|
+
run_command
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should set the option to report on methods only" do
|
185
|
+
Flog.expects(:new).with(:methods => true).returns(@flog)
|
186
|
+
run_command
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'should exit with status 0' do
|
190
|
+
self.expects(:exit).with(0)
|
191
|
+
run_command
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe "when -v is specified on the command-line" do
|
196
|
+
before :each do
|
197
|
+
Object.send(:remove_const, :ARGV)
|
198
|
+
ARGV = ['-v']
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'should create a Flog instance' do
|
202
|
+
Flog.expects(:new).returns(@flog)
|
203
|
+
run_command
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should set the option to be verbose" do
|
207
|
+
Flog.expects(:new).with(:verbose => true).returns(@flog)
|
208
|
+
run_command
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'should exit with status 0' do
|
212
|
+
self.expects(:exit).with(0)
|
213
|
+
run_command
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
describe "when --verbose is specified on the command-line" do
|
218
|
+
before :each do
|
219
|
+
Object.send(:remove_const, :ARGV)
|
220
|
+
ARGV = ['--verbose']
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'should create a Flog instance' do
|
224
|
+
Flog.expects(:new).returns(@flog)
|
225
|
+
run_command
|
226
|
+
end
|
227
|
+
|
228
|
+
it "should set the option to be verbose" do
|
229
|
+
Flog.expects(:new).with(:verbose => true).returns(@flog)
|
230
|
+
run_command
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'should exit with status 0' do
|
234
|
+
self.expects(:exit).with(0)
|
235
|
+
run_command
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
describe "when -h is specified on the command-line" do
|
240
|
+
before :each do
|
241
|
+
Object.send(:remove_const, :ARGV)
|
242
|
+
ARGV = ['-h']
|
243
|
+
end
|
244
|
+
|
245
|
+
it "should display help information" do
|
246
|
+
self.expects(:puts)
|
247
|
+
run_command
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'should not create a Flog instance' do
|
251
|
+
Flog.expects(:new).never
|
252
|
+
run_command
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'should exit with status 0' do
|
256
|
+
self.expects(:exit).with(0)
|
257
|
+
run_command
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
describe "when --help is specified on the command-line" do
|
262
|
+
before :each do
|
263
|
+
Object.send(:remove_const, :ARGV)
|
264
|
+
ARGV = ['--help']
|
265
|
+
end
|
266
|
+
|
267
|
+
it "should display help information" do
|
268
|
+
self.expects(:puts)
|
269
|
+
run_command
|
270
|
+
end
|
271
|
+
|
272
|
+
it 'should not create a Flog instance' do
|
273
|
+
Flog.expects(:new).never
|
274
|
+
run_command
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'should exit with status 0' do
|
278
|
+
self.expects(:exit).with(0)
|
279
|
+
run_command
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
describe 'when -I is specified on the command-line' do
|
284
|
+
before :each do
|
285
|
+
Object.send(:remove_const, :ARGV)
|
286
|
+
ARGV = ['-I /tmp,/etc']
|
287
|
+
end
|
288
|
+
|
289
|
+
before :each do
|
290
|
+
@paths = $:.dup
|
291
|
+
end
|
292
|
+
|
293
|
+
it "should append each ':' separated path to $:" do
|
294
|
+
run_command
|
295
|
+
$:.should_not == @paths
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'should create a Flog instance' do
|
299
|
+
Flog.expects(:new).returns(@flog)
|
300
|
+
run_command
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'should exit with status 0' do
|
304
|
+
self.expects(:exit).with(0)
|
305
|
+
run_command
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
describe 'when -b is specified on the command-line' do
|
310
|
+
before :each do
|
311
|
+
Object.send(:remove_const, :ARGV)
|
312
|
+
ARGV = ['-b']
|
313
|
+
end
|
314
|
+
|
315
|
+
it 'should create a Flog instance' do
|
316
|
+
Flog.expects(:new).returns(@flog)
|
317
|
+
run_command
|
318
|
+
end
|
319
|
+
|
320
|
+
it "should set the option to provide 'blame' information" do
|
321
|
+
Flog.expects(:new).with(:blame => true).returns(@flog)
|
322
|
+
run_command
|
323
|
+
end
|
324
|
+
|
325
|
+
it 'should exit with status 0' do
|
326
|
+
self.expects(:exit).with(0)
|
327
|
+
run_command
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
describe 'when --blame is specified on the command-line' do
|
332
|
+
before :each do
|
333
|
+
Object.send(:remove_const, :ARGV)
|
334
|
+
ARGV = ['--blame']
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'should create a Flog instance' do
|
338
|
+
Flog.expects(:new).returns(@flog)
|
339
|
+
run_command
|
340
|
+
end
|
341
|
+
|
342
|
+
it "should set the option to provide 'blame' information" do
|
343
|
+
Flog.expects(:new).with(:blame => true).returns(@flog)
|
344
|
+
run_command
|
345
|
+
end
|
346
|
+
|
347
|
+
it 'should exit with status 0' do
|
348
|
+
self.expects(:exit).with(0)
|
349
|
+
run_command
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|