rleber-textmate 0.9.7.1

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.
@@ -0,0 +1,91 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe TextMateBundleManager do
4
+ describe "locations" do
5
+
6
+ def run_locations
7
+ @location_list = capture(:stdout) { TextMateBundleManager.start(["locations"]) }.split("\n").map{|line| line.strip.split(/\s{2,}/)}
8
+ end
9
+
10
+ def locations_for_zone(zone)
11
+ @location_list.select {|l| l[0] == zone}.map{|l| l[1]}
12
+ end
13
+
14
+ context "with stubbed locations" do
15
+ before :each do
16
+ stub_github_user "foo"
17
+ stub_remote_locations
18
+ stub_local_locations
19
+ run_locations
20
+ end
21
+
22
+ it "lists locations beginning with column titles" do
23
+ @location_list.first.should == %w{Zone Short Name Location}
24
+ end
25
+
26
+ it "lists all the locations" do
27
+ @location_list.shift
28
+ @location_list.size.should == 9 # 4 "standard" overridden remote locations, plus 5 "standard" overridden local locations
29
+ end
30
+
31
+ it "lists all location information" do
32
+ @location_list[1].size.should == 4
33
+ end
34
+
35
+ it "lists local locations before remote ones" do
36
+ list = @location_list[1..-1].map {|l| l[0]}
37
+ compressed_list = []
38
+ last_element = nil
39
+ list.each do |l|
40
+ compressed_list << l if l != last_element
41
+ last_element = l
42
+ end
43
+ compressed_list.should == %w{Local Remote}
44
+ end
45
+
46
+ it "lists all local locations from highest to lowest precedence" do
47
+ locations_for_zone('Local').should == %w{user user_p system system_p app}
48
+ end
49
+
50
+ it "lists all remote locations from highest to lowest precedence" do
51
+ locations_for_zone('Remote').should == %w{personal github trunk review}
52
+ end
53
+
54
+ it "lists the correct location fields" do
55
+ @location_list[1].should == %w{Local user User User\ path}
56
+ end
57
+
58
+ it "always has 4 columns" do
59
+ @location_list.should always_have_n_columns(4)
60
+ end
61
+ end
62
+
63
+ context "when github_user is not defined" do
64
+ before :each do
65
+ stub_github_user nil
66
+ run_locations
67
+ end
68
+
69
+ it "does not include a personal github location" do
70
+ @location_list.map{|l| l[1]}.should_not include('personal')
71
+ end
72
+ end
73
+
74
+ context "when github_user is defined" do
75
+ before :each do
76
+ stub_github_user "foo"
77
+ run_locations
78
+ end
79
+
80
+ it "includes a personal github location" do
81
+ @location_list.map{|l| l[1]}.should include('personal')
82
+ end
83
+
84
+ it "sets github url to include 'foo'" do
85
+ l = @location_list.find {|l| l[1] == "personal" }
86
+ l.should_not be_nil
87
+ l[3].should match(/foo/)
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,394 @@
1
+ $TESTING=true
2
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
3
+
4
+ require 'rspec'
5
+ require 'textmate'
6
+ require 'pp'
7
+ require 'mocha'
8
+
9
+ RSPEC_REMOTE_LOCATIONS = [
10
+ {:name => :'Personal', :short => :personal, :scm => :github, :path => 'Personal path'},
11
+ {:name => :'GitHub', :short => :github, :scm => :github, :path => 'Github path'},
12
+ {:name => :'Macromates Trunk', :short => :trunk, :scm => :svn, :path => 'Trunk path'},
13
+ {:name => :'Macromates Review', :short => :review, :scm => :svn, :path => 'Macromates Review path'},
14
+ ]
15
+
16
+ RSPEC_LOCAL_LOCATIONS = [
17
+ {:name => :User, :short=>:user, :path=>"User path"},
18
+ {:name => :'User Pristine', :short=>:user_p, :path=>"User Pristine path"},
19
+ {:name => :System, :short=>:system, :path=>'System path'},
20
+ {:name => :'System Pristine', :short=>:system_p, :path=>'System Pristine path'},
21
+ {:name => :Application, :short=>:app, :path=>"Application path"},
22
+ ]
23
+
24
+ RSPEC_REMOTE_BUNDLES = {
25
+ }
26
+
27
+
28
+ RSPEC_LOCAL_BUNDLES = {
29
+ :user => [
30
+ {:name=>"user_bundle1"},
31
+ {:name=>"user_bundle2"},
32
+ ]
33
+ }
34
+
35
+ class String
36
+ def blank?
37
+ !!(self =~ /^\s*$/)
38
+ end
39
+ end
40
+
41
+ # A hack:
42
+ # Disable Thor [WARNING] Attempted to create task "..." without usage or description
43
+ class Mocha::AnyInstanceMethod
44
+ alias original_define_new_method define_new_method
45
+ def define_new_method
46
+ if stubbee <= Thor
47
+ stubbee.class_eval(%{
48
+ no_tasks do
49
+ def #{method}(*args, &block)
50
+ self.class.any_instance.mocha.method_missing(:#{method}, *args, &block)
51
+ end
52
+ end
53
+ }, __FILE__, __LINE__)
54
+ else
55
+ original_define_new_method
56
+ end
57
+ end
58
+ end
59
+
60
+ # def stub_local_bundles(bundles=nil)
61
+ # stub_local_locations
62
+ # bundles ||= RSPEC_LOCAL_BUNDLES
63
+ #
64
+ # TextMateBundleManager.class_eval <<-END
65
+ # private
66
+ #
67
+ # def bundle_status_table
68
+ # @bundle_status_table ||= #{bundles.inspect}
69
+ # end
70
+ #
71
+ # def bundle_status(path)
72
+ # basename = File.basename(path, '.*')
73
+ # res = {}
74
+ # bundles = get_local_bundle_details(File.dirname(path))
75
+ # if bundles
76
+ # res = bundles.find {|b| File.basename(b[:name], '.*') == basename }
77
+ # res ||= {}
78
+ # end
79
+ # res
80
+ # end
81
+ #
82
+ # def get_local_bundle_details(path)
83
+ # l = find_local_location_by(:path, path)
84
+ # return [] unless l
85
+ # bundles = bundle_status_table[l[:short]]
86
+ # return [] unless bundles
87
+ # bundles
88
+ # end
89
+ #
90
+ # def get_local_bundles(path)
91
+ # get_local_bundle_details(path).map {|b| b[:name] + '.tmbundle'}
92
+ # end
93
+ # END
94
+ # end
95
+
96
+ RSpec.configure do |config|
97
+ config.mock_framework = :mocha
98
+
99
+ def capture(stream)
100
+ begin
101
+ stream = stream.to_s
102
+ eval "$#{stream} = StringIO.new"
103
+ yield
104
+ result = eval("$#{stream}").string
105
+ ensure
106
+ eval("$#{stream} = #{stream.upcase}")
107
+ end
108
+
109
+ result
110
+ end
111
+ alias :silence :capture
112
+
113
+ def stub_github_user(user)
114
+ TextMateBundleManager.any_instance.stubs(:current_github_user).returns(user)
115
+ end
116
+
117
+ def stub_local_locations(location_specs=nil)
118
+ location_specs ||= RSPEC_LOCAL_LOCATIONS
119
+ TextMateBundleManager.any_instance.stubs(:local_locations).returns(location_specs)
120
+ location_specs
121
+ end
122
+
123
+ def stub_remote_locations(location_specs=nil)
124
+ location_specs ||= RSPEC_REMOTE_LOCATIONS
125
+ TextMateBundleManager.any_instance.stubs(:remote_locations).returns(location_specs)
126
+ location_specs
127
+ end
128
+
129
+ def bundle_details_by_path(locations, bundles)
130
+ locations.inject({}) do |hsh, l|
131
+ location_bundles = bundles[l[:short]]
132
+ hsh[l[:path]] = location_bundles if location_bundles && location_bundles.size > 0
133
+ hsh
134
+ end
135
+ end
136
+
137
+ def bundle_status_table_for(locations, bundles)
138
+ bundle_status_table = {}
139
+ bundle_details_by_path(locations, bundles).each do |path, bundle_detail_array|
140
+ bundle_detail_array.each do |bundle_detail|
141
+ name = bundle_detail[:name]
142
+ name += '.tmbundle' unless name =~ /\./
143
+ bundle_status_table[File.join(path, name)] = bundle_detail
144
+ end
145
+ end
146
+ bundle_status_table
147
+ end
148
+
149
+ def bundle_names_by_path(locations, bundles)
150
+ bundle_names_table = {}
151
+ bundle_details_by_path(locations, bundles).each do |path, bundle_detail_array|
152
+ bundle_names_table[path] = bundle_detail_array.map do |b|
153
+ name = b[:name]
154
+ name += '.tmbundle' unless name =~ /\./
155
+ name
156
+ end
157
+ end
158
+ bundle_names_table
159
+ end
160
+
161
+ def stub_remote_bundles(locations=nil,bundles=nil)
162
+ locs = stub_remote_locations(locations)
163
+ bundles ||= RSPEC_REMOTE_BUNDLES
164
+
165
+ # A total hack, but the only way I can figure out to make this work...
166
+ TextMateBundleManager.class_eval <<-END
167
+ no_tasks do
168
+ def get_remote_bundles(path)
169
+ #{bundle_names_by_path(locs, bundles).inspect}[path] || []
170
+ end
171
+ END
172
+ end
173
+
174
+ def stub_local_bundles(locations=nil, bundles=nil)
175
+ locs = stub_local_locations(locations)
176
+ bundles ||= RSPEC_LOCAL_BUNDLES
177
+
178
+ # A total hack, but the only way I can figure out to make this work...
179
+ TextMateBundleManager.class_eval <<-END
180
+ no_tasks do
181
+ def get_local_bundles(path)
182
+ #{bundle_names_by_path(locs, bundles).inspect}[path] || []
183
+ end
184
+
185
+ def bundle_status(path)
186
+ #{bundle_status_table_for(locs, bundles).inspect}[path] || {}
187
+ end
188
+ end
189
+ END
190
+ end
191
+
192
+ module CustomMatchers
193
+
194
+ class ArrayRowMatcher
195
+ def initialize(expected)
196
+ @expected = expected
197
+ end
198
+
199
+ def matches?(actual)
200
+ matches_all?(actual)
201
+ end
202
+
203
+ def matches_all?(actual)
204
+ @actual = actual
205
+ failed = false
206
+ actual.each do |row|
207
+ return false unless matches_row?(row)
208
+ end
209
+ return true
210
+ end
211
+
212
+ def matches_row?(row)
213
+ raise "#{self}#matches_row? not implemented"
214
+ end
215
+
216
+ def format_row_groups(rows)
217
+ last_row = -1
218
+ row_groups = []
219
+ rows.each do |row|
220
+ if row == last_row + 1
221
+ if row_groups.size == 0 # Shouldn't happen, but why not?
222
+ row_groups << [row, row]
223
+ else
224
+ last_group = row_groups.pop
225
+ row_groups << [last_group.first, row]
226
+ end
227
+ else
228
+ row_groups << [row, row]
229
+ end
230
+ last_row = row
231
+ end
232
+ formatted_groups = row_groups.map do |group|
233
+ if group.first == group.last
234
+ group.first.to_s
235
+ else
236
+ "#{group.first}..#{group.last}"
237
+ end
238
+ end
239
+ formatted_rows = formatted_groups.join(',')
240
+ end
241
+
242
+ def classify_rows
243
+ bad_rows = []
244
+ good_rows = []
245
+ @actual.each_with_index do |row, i|
246
+ if matches_row?(row)
247
+ good_rows << i
248
+ else
249
+ bad_rows << i
250
+ end
251
+ end
252
+ [good_rows, bad_rows]
253
+ end
254
+ end
255
+
256
+ class AlwaysHasNColumns < ArrayRowMatcher
257
+
258
+ def column_counts
259
+ @actual.map {|row| row.size }
260
+ end
261
+
262
+ def max_columns
263
+ column_counts.max
264
+ end
265
+
266
+ def min_columns
267
+ column_counts.min
268
+ end
269
+
270
+ def matches_row?(row)
271
+ @expected == row.size
272
+ end
273
+
274
+ def failure_message_for_should
275
+ max = max_columns
276
+ min = min_columns
277
+ good_rows, bad_rows = classify_rows
278
+ if max == min
279
+ "expected table to have #{@expected} columns. It actually has #{min} columns. Row[0]=#{@actual[0].inspect}\n"
280
+ elsif bad_rows.size > 0
281
+ msg = "expected table to have #{@expected} columns. It actually has #{min}..#{max} columns.\n" \
282
+ "Bad rows: #{format_row_groups(bad_rows)}\n" \
283
+ "Row[#{bad_rows[0]}] is bad, for instance: #{@actual[bad_rows[0]].inspect}"
284
+ if good_rows.size > 0
285
+ good_rows_msg = "\nGood rows: #{format_row_groups(good_rows)}\n" \
286
+ "Row[#{good_rows[0]}] is good, for instance: #{@actual[good_rows[0]].inspect}"
287
+ else
288
+ good_rows_msg = "\nNo good rows"
289
+ end
290
+ msg << good_rows_msg
291
+ else
292
+ "expected table to have #{@expected} columns. It actually has #{min}..#{max} columns, but I can't find a bad row."
293
+ end
294
+ end
295
+
296
+ def failure_message_for_should_not
297
+ max = max_columns
298
+ min = min_columns
299
+ if max == min
300
+ "expected table not to have #{@expected} columns, but it does. Row[0]=#{@actual[0].inspect}\n"
301
+ else
302
+ "expected table not to have #{@expected} columns. It actually has #{min}..#{max} columns, which shouldn't have caused this example to fail."
303
+ end
304
+ end
305
+ end
306
+
307
+ def always_have_n_columns(expected)
308
+ AlwaysHasNColumns.new(expected)
309
+ end
310
+
311
+ class AlwaysMatcher < ArrayRowMatcher
312
+
313
+ def matches_row?(row)
314
+ row.match(@expected)
315
+ end
316
+
317
+ def failure_message_for_should
318
+ good_rows, bad_rows = classify_rows
319
+ msg = "expected rows of table to always match #{@expected.inspect}, but it doesn't."
320
+ if bad_rows.size > 0
321
+ msg << "\nBad rows: #{format_row_groups(bad_rows)}" \
322
+ "\nRow[#{bad_rows[0]}] is bad, for instance: #{@actual[bad_rows[0]].inspect}"
323
+ else
324
+ msg << "\nNo bad rows."
325
+ end
326
+ if good_rows.size > 0
327
+ msg << "\nGood rows: #{format_row_groups(good_rows)}" \
328
+ "\nRow[#{good_rows[0]}] is good, for instance: #{@actual[good_rows[0]].inspect}"
329
+ else
330
+ msg << "\nNo good rows"
331
+ end
332
+ msg
333
+ end
334
+
335
+ def failure_message_for_should_not
336
+ "expected rows of table to never match #{@expected.inspect}, but they do. Row[0]=#{@actual[0].inspect}"
337
+ end
338
+ end
339
+
340
+ def always_match(expected)
341
+ AlwaysMatcher.new(expected)
342
+ end
343
+
344
+ class AlwaysColumnMatcher < ArrayRowMatcher
345
+
346
+ def initialize(n, match)
347
+ super(match)
348
+ @n = n
349
+ end
350
+
351
+ def matches_row?(row)
352
+ # puts "Checking row: #{format_row(row)}"
353
+ row[@n].match(@expected)
354
+ end
355
+
356
+ def format_row(row)
357
+ res = []
358
+ row.each_with_index do |cell, i|
359
+ res << (i==@n ? "<<#{cell.inspect}>>" : cell.inspect)
360
+ end
361
+ "[#{res.join(',')}]"
362
+ end
363
+
364
+ def failure_message_for_should
365
+ good_rows, bad_rows = classify_rows
366
+ msg = "expected column #{@n} of table to always match #{@expected.inspect}, but it doesn't."
367
+ if bad_rows.size > 0
368
+ msg << "\nBad rows: #{format_row_groups(bad_rows)}" \
369
+ "\nRow[#{bad_rows[0]}] is bad, for instance: #{format_row(@actual[bad_rows[0]])}"
370
+ else
371
+ msg << "\nNo bad rows."
372
+ end
373
+ if good_rows.size > 0
374
+ msg << "\nGood rows: #{format_row_groups(good_rows)}" \
375
+ "\nRow[#{good_rows[0]}] is good, for instance: #{format_row(@actual[good_rows[0]])}"
376
+ else
377
+ msg << "\nNo good rows"
378
+ end
379
+ msg
380
+ end
381
+
382
+ def failure_message_for_should_not
383
+ "expected column #{@n} of table to never match #{@expected.inspect}, but they do. Row[0]=#{format_row(@actual[0])}"
384
+ end
385
+ end
386
+
387
+ def always_match_in_column(n, match)
388
+ AlwaysColumnMatcher.new(n, match)
389
+ end
390
+
391
+ end
392
+ config.include(CustomMatchers)
393
+
394
+ end