droxi 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/droxi/text.rb CHANGED
@@ -1,31 +1,25 @@
1
1
  # Module containing text-manipulation methods.
2
2
  module Text
3
-
4
3
  # The assumed width of the terminal if GNU Readline can't retrieve it.
5
4
  DEFAULT_WIDTH = 72
6
-
5
+
7
6
  # Format an +Array+ of +Strings+ as a table and return an +Array+ of lines
8
7
  # in the result.
9
8
  def self.table(items)
10
- if items.empty?
11
- []
12
- else
13
- columns = get_columns
14
- item_width = items.map { |item| item.length }.max + 2
15
- items_per_line = [1, columns / item_width].max
16
- num_lines = (items.length.to_f / items_per_line).ceil
17
- format_table(items, item_width, items_per_line, num_lines)
18
- end
9
+ return [] if items.empty?
10
+ width = terminal_width
11
+ item_width = items.map { |item| item.length }.max + 2
12
+ items_per_line = [1, width / item_width].max
13
+ format_table(items, item_width, items_per_line)
19
14
  end
20
15
 
21
16
  # Wrap a +String+ to fit the terminal and return an +Array+ of lines in the
22
17
  # result.
23
18
  def self.wrap(text)
24
- columns = get_columns
25
- position = 0
19
+ width, position = terminal_width, 0
26
20
  lines = []
27
21
  while position < text.length
28
- lines << get_wrap_segment(text[position, text.length], columns)
22
+ lines << get_wrap_segment(text[position, text.length], width)
29
23
  position += lines.last.length + 1
30
24
  end
31
25
  lines
@@ -34,37 +28,33 @@ module Text
34
28
  private
35
29
 
36
30
  # Return the width of the terminal in columns.
37
- def self.get_columns
31
+ def self.terminal_width
38
32
  require 'readline'
39
- begin
40
- columns = Readline.get_screen_size[1]
41
- columns > 0 ? columns : DEFAULT_WIDTH
42
- rescue NotImplementedError
43
- DEFAULT_WIDTH
44
- end
33
+ width = Readline.get_screen_size[1]
34
+ width > 0 ? width : DEFAULT_WIDTH
35
+ rescue NotImplementedError
36
+ DEFAULT_WIDTH
45
37
  end
46
38
 
47
39
  # Return an +Array+ of lines of the given items formatted as a table.
48
- def self.format_table(items, item_width, items_per_line, num_lines)
49
- num_lines.times.map do |i|
50
- items[i * items_per_line, items_per_line].map do |item|
51
- item.ljust(item_width)
52
- end.join
40
+ def self.format_table(items, item_width, columns)
41
+ lines, items = [], items.dup
42
+ until items.empty?
43
+ lines << items.shift(columns).map { |item| item.ljust(item_width) }.join
53
44
  end
45
+ lines
54
46
  end
55
47
 
56
48
  # Return a wrapped line of output from the start of the given text.
57
- def self.get_wrap_segment(text, columns)
58
- segment, sep, text = text.partition(' ')
59
- while !text.empty? && segment.length < columns
60
- head, sep, text = text.partition(' ')
61
- segment << " #{head}"
62
- end
63
- if segment.length > columns && segment.include?(' ')
64
- segment.rpartition(' ')[0]
65
- else
66
- segment
49
+ def self.get_wrap_segment(text, width)
50
+ line = ''
51
+ loop do
52
+ head, _, text = text.partition(' ')
53
+ line << "#{head} "
54
+ break if text.empty? || line.length >= width
67
55
  end
56
+ line.strip!
57
+ trim_last_word = line.length > width && line.include?(' ')
58
+ trim_last_word ? line.rpartition(' ')[0] : line
68
59
  end
69
-
70
60
  end
data/spec/all.rb CHANGED
@@ -1,3 +1,13 @@
1
+ # Use SimpleCov coverage-tracking library if available
2
+ begin
3
+ require 'simplecov'
4
+ SimpleCov.start do
5
+ add_filter '_spec.rb'
6
+ end
7
+ rescue LoadError
8
+ nil
9
+ end
10
+
1
11
  # Run all spec tests
2
12
  Dir.glob('spec/*_spec.rb').each do |spec|
3
13
  require_relative File.basename(spec, '.rb')
@@ -1,36 +1,11 @@
1
1
  require 'dropbox_sdk'
2
2
  require 'minitest/autorun'
3
3
 
4
+ require_relative 'testutils'
4
5
  require_relative '../lib/droxi/commands'
5
6
  require_relative '../lib/droxi/settings'
6
7
  require_relative '../lib/droxi/state'
7
8
 
8
- def ignore(error_class)
9
- begin
10
- yield
11
- rescue error_class
12
- end
13
- end
14
-
15
- def put_temp_file(client, state)
16
- `echo hello > #{TEMP_FILENAME}`
17
- open(TEMP_FILENAME, 'rb') do |file|
18
- Commands::PUT.exec(client, state, TEMP_FILENAME,
19
- "/#{TEST_FOLDER}/#{TEMP_FILENAME}")
20
- end
21
- `rm #{TEMP_FILENAME}`
22
- end
23
-
24
- def delete_temp_file(client, state)
25
- Commands::RM.exec(client, state, "/#{TEST_FOLDER}/#{TEMP_FILENAME}")
26
- end
27
-
28
- def get_output(cmd, client, state, *args)
29
- lines = []
30
- Commands.const_get(cmd).exec(client, state, *args) { |line| lines << line }
31
- lines
32
- end
33
-
34
9
  describe Commands do
35
10
  original_dir = Dir.pwd
36
11
 
@@ -41,18 +16,19 @@ describe Commands do
41
16
  TEMP_FOLDER = 'test'
42
17
  TEST_FOLDER = 'testing'
43
18
 
44
- ignore(DropboxError) { client.file_delete("/#{TEST_FOLDER}") }
45
- ignore(DropboxError) { client.file_create_folder("/#{TEST_FOLDER}") }
46
-
47
19
  before do
48
20
  Dir.chdir(original_dir)
49
21
  end
50
22
 
51
23
  describe 'when executing a shell command' do
52
24
  it 'must yield the output' do
53
- lines = []
54
- Commands.shell('echo testing') { |line| lines << line }
55
- lines.must_equal(['testing'])
25
+ lines = TestUtils.output_of(Commands, :shell, 'echo test')
26
+ lines.must_equal(['test'])
27
+ end
28
+
29
+ it 'must give an error message for an invalid command' do
30
+ lines = TestUtils.output_of(Commands, :shell, 'bogus')
31
+ lines.length.must_equal 1
56
32
  end
57
33
  end
58
34
 
@@ -63,23 +39,17 @@ describe Commands do
63
39
  state.pwd.must_equal '/'
64
40
  end
65
41
 
66
- it 'must change to the previous directory when given -' do
67
- state.pwd = '/testing'
68
- state.pwd = '/'
69
- Commands::CD.exec(client, state, '-')
70
- state.pwd.must_equal '/testing'
71
- end
72
-
73
42
  it 'must change to the stated directory when given 1 arg' do
74
43
  state.pwd = '/'
75
44
  Commands::CD.exec(client, state, '/testing')
76
45
  state.pwd.must_equal '/testing'
77
46
  end
78
47
 
79
- it 'must set previous directory correctly' do
48
+ it 'must change and set previous directory correctly' do
80
49
  state.pwd = '/testing'
81
50
  state.pwd = '/'
82
- Commands::CD.exec(client, state, '/testing')
51
+ Commands::CD.exec(client, state, '-')
52
+ state.pwd.must_equal '/testing'
83
53
  state.oldpwd.must_equal '/'
84
54
  end
85
55
 
@@ -88,6 +58,11 @@ describe Commands do
88
58
  Commands::CD.exec(client, state, '/bogus_dir')
89
59
  state.pwd.must_equal '/'
90
60
  end
61
+
62
+ it 'must fail with UsageError when given multiple args' do
63
+ proc { Commands::CD.exec(client, state, 'a', 'b') }
64
+ .must_raise Commands::UsageError
65
+ end
91
66
  end
92
67
 
93
68
  describe 'when executing the cp command' do
@@ -95,31 +70,69 @@ describe Commands do
95
70
  state.pwd = '/testing'
96
71
  end
97
72
 
98
- after do
99
- Commands::RM.exec(client, state, '*')
100
- end
101
-
102
73
  it 'must copy source to dest when given 2 args and last arg is non-dir' do
103
- Commands::MKDIR.exec(client, state, 'source')
74
+ TestUtils.structure(client, state, 'source')
75
+ TestUtils.not_structure(client, state, 'dest')
104
76
  Commands::CP.exec(client, state, 'source', 'dest')
105
- ['source', 'dest'].all? do |dir|
106
- client.metadata("/testing/#{dir}")
77
+ %w(source dest).all? do |dir|
78
+ state.metadata("/testing/#{dir}")
107
79
  end.must_equal true
108
80
  end
109
81
 
110
82
  it 'must copy source into dest when given 2 args and last arg is dir' do
111
- Commands::MKDIR.exec(client, state, 'source', 'dest')
83
+ TestUtils.structure(client, state, 'source', 'dest')
84
+ TestUtils.not_structure(client, state, 'dest/source')
112
85
  Commands::CP.exec(client, state, 'source', 'dest')
113
- client.metadata('/testing/dest/source').wont_equal nil
86
+ state.metadata('/testing/dest/source').wont_be_nil
114
87
  end
115
88
 
116
89
  it 'must copy sources into dest when given 3 or more args' do
117
- Commands::MKDIR.exec(client, state, 'source1', 'source2', 'dest')
90
+ TestUtils.structure(client, state, 'source1', 'source2', 'dest')
91
+ TestUtils.not_structure(client, state, 'dest/source1', 'dest/source2')
118
92
  Commands::CP.exec(client, state, 'source1', 'source2', 'dest')
119
- ['source2', 'source2'].all? do |dir|
120
- client.metadata("/testing/dest/#{dir}")
93
+ %w(source2 source2).all? do |dir|
94
+ state.metadata("/testing/dest/#{dir}")
121
95
  end.must_equal true
122
96
  end
97
+
98
+ it 'must fail with UsageError when given <2 args' do
99
+ test1 = proc { Commands::CP.exec(client, state) }
100
+ test2 = proc { Commands::CP.exec(client, state, 'a') }
101
+ [test1, test2].each { |test| test.must_raise Commands::UsageError }
102
+ end
103
+
104
+ it 'must give an error message if trying to copy a bogus file' do
105
+ lines = TestUtils.output_of(Commands::CP, :exec, client, state,
106
+ 'bogus', '/testing')
107
+ lines.length.must_equal 1
108
+ lines[0].start_with?('cp: ').must_equal true
109
+ end
110
+ end
111
+
112
+ describe 'when executing the debug command' do
113
+ it 'must fail with an error message if debug mode is not enabled' do
114
+ ARGV.clear
115
+ TestUtils.output_of(Commands::DEBUG, :exec, client, state, '1')
116
+ .must_equal(['Debug not enabled.'])
117
+ end
118
+
119
+ it 'must evaluate the string if debug mode is enabled' do
120
+ ARGV << '--debug'
121
+ TestUtils.output_of(Commands::DEBUG, :exec, client, state, '1')
122
+ .must_equal(['1'])
123
+ end
124
+
125
+ it 'must print the resulting exception if given exceptional input' do
126
+ ARGV << '--debug'
127
+ lines = TestUtils.output_of(Commands::DEBUG, :exec, client, state, 'x')
128
+ lines.length.must_equal 1
129
+ lines[0].must_match(/^#<.+>$/)
130
+ end
131
+
132
+ it 'must fail with UsageError when given no args' do
133
+ proc { Commands::DEBUG.exec(client, state) }
134
+ .must_raise Commands::UsageError
135
+ end
123
136
  end
124
137
 
125
138
  describe 'when executing the forget command' do
@@ -130,11 +143,9 @@ describe Commands do
130
143
  end
131
144
 
132
145
  it 'must accept multiple arguments' do
133
- lines = []
134
- Commands::FORGET.exec(client, state, 'bogus1', 'bogus2') do |line|
135
- lines << line
136
- end
137
- lines.length.must_equal 2
146
+ args = %w(bogus1, bogus2)
147
+ TestUtils.output_of(Commands::FORGET, :exec, client, state, *args)
148
+ .length.must_equal 2
138
149
  end
139
150
 
140
151
  it 'must recursively clear contents of directory argument' do
@@ -146,29 +157,44 @@ describe Commands do
146
157
 
147
158
  describe 'when executing the get command' do
148
159
  it 'must get a file of the same name when given args' do
149
- put_temp_file(client, state)
160
+ TestUtils.structure(client, state, 'test.txt')
150
161
  Commands::GET.exec(client, state, '/testing/test.txt')
151
- delete_temp_file(client, state)
152
162
  `ls test.txt`.chomp.must_equal 'test.txt'
153
163
  `rm test.txt`
154
164
  end
165
+
166
+ it 'must fail with UsageError when given no args' do
167
+ proc { Commands::GET.exec(client, state) }
168
+ .must_raise Commands::UsageError
169
+ end
170
+
171
+ it 'must give an error message if trying to get a bogus file' do
172
+ lines = TestUtils.output_of(Commands::GET, :exec, client, state, 'bogus')
173
+ lines.length.must_equal 1
174
+ lines[0].start_with?('get: ').must_equal true
175
+ end
155
176
  end
156
177
 
157
178
  describe 'when executing the lcd command' do
158
179
  it 'must change to home directory when given no args' do
180
+ prev_pwd = Dir.pwd
159
181
  Commands::LCD.exec(client, state)
160
182
  Dir.pwd.must_equal File.expand_path('~')
183
+ Dir.chdir(prev_pwd)
161
184
  end
162
185
 
163
186
  it 'must change to specific directory when specified' do
187
+ prev_pwd = Dir.pwd
164
188
  Commands::LCD.exec(client, state, '/home')
165
189
  Dir.pwd.must_equal File.expand_path('/home')
190
+ Dir.chdir(prev_pwd)
166
191
  end
167
192
 
168
193
  it 'must set oldpwd correctly' do
169
194
  oldpwd = Dir.pwd
170
195
  Commands::LCD.exec(client, state, '/')
171
196
  state.local_oldpwd.must_equal oldpwd
197
+ Dir.chdir(oldpwd)
172
198
  end
173
199
 
174
200
  it 'must change to previous directory when given -' do
@@ -176,6 +202,7 @@ describe Commands do
176
202
  Commands::LCD.exec(client, state, '/')
177
203
  Commands::LCD.exec(client, state, '-')
178
204
  Dir.pwd.must_equal oldpwd
205
+ Dir.chdir(oldpwd)
179
206
  end
180
207
 
181
208
  it 'must fail if given bogus directory name' do
@@ -184,57 +211,79 @@ describe Commands do
184
211
  Commands::LCD.exec(client, state, '/bogus_dir')
185
212
  Dir.pwd.must_equal pwd
186
213
  state.local_oldpwd.must_equal oldpwd
214
+ Dir.chdir(pwd)
187
215
  end
188
216
  end
189
217
 
190
218
  describe 'when executing the ls command' do
191
219
  it 'must list the working directory contents when given no args' do
192
- Commands::MKDIR.exec(client, state, '/testing/test')
220
+ TestUtils.exact_structure(client, state, 'test')
193
221
  state.pwd = '/testing'
194
- lines = []
195
- Commands::LS.exec(client, state) { |line| lines << line }
196
- lines.must_equal(['test '])
197
- Commands::RM.exec(client, state, '/testing/test')
222
+ TestUtils.output_of(Commands::LS, :exec, client, state)
223
+ .must_equal(['test '])
198
224
  end
199
225
 
200
226
  it 'must list the stated directory contents when given args' do
201
227
  state.pwd = '/'
202
- Commands::MKDIR.exec(client, state, '/testing/test')
203
- lines = []
204
- Commands::LS.exec(client, state, '/testing') { |line| lines << line }
205
- lines.must_equal(['test '])
206
- Commands::RM.exec(client, state, '/testing/test')
228
+ TestUtils.exact_structure(client, state, 'test')
229
+ TestUtils.output_of(Commands::LS, :exec, client, state, '/testing')
230
+ .must_equal(['test '])
207
231
  end
208
232
 
209
233
  it 'must give a longer description with the -l option' do
210
234
  state.pwd = '/'
211
- Commands::MKDIR.exec(client, state, '/testing/test')
212
- lines = []
213
- Commands::LS.exec(client, state, '-l', '/testing') do |line|
214
- lines << line
215
- end
235
+ TestUtils.exact_structure(client, state, 'test')
236
+ lines = TestUtils.output_of(Commands::LS, :exec, client, state,
237
+ '-l', '/testing')
216
238
  lines.length.must_equal 1
217
- /d +0 \w{3} .\d \d\d:\d\d test/.match(lines[0]).wont_equal nil
218
- Commands::RM.exec(client, state, '/testing/test')
239
+ /d +0 \w{3} .\d \d\d:\d\d test/.match(lines[0]).wont_be_nil
240
+ end
241
+
242
+ it 'must give an error message if trying to list a bogus file' do
243
+ lines = TestUtils.output_of(Commands::LS, :exec, client, state, 'bogus')
244
+ lines.length.must_equal 1
245
+ lines[0].start_with?('ls: ').must_equal true
219
246
  end
220
247
  end
221
248
 
222
249
  describe 'when executing the media command' do
223
250
  it 'must yield URL when given file path' do
224
- put_temp_file(client, state)
225
- to_path = "/#{TEST_FOLDER}/#{TEMP_FILENAME}"
226
- lines = get_output(:MEDIA, client, state, to_path)
227
- delete_temp_file(client, state)
251
+ TestUtils.structure(client, state, 'test.txt')
252
+ path = '/testing/test.txt'
253
+ lines = TestUtils.output_of(Commands::MEDIA, :exec, client, state, path)
254
+ lines.length.must_equal 1
255
+ %r{https://.+\..+/}.match(lines[0]).wont_be_nil
256
+ end
257
+
258
+ it 'must fail with error when given directory path' do
259
+ lines = TestUtils.output_of(Commands::MEDIA, :exec, client, state,
260
+ '/testing')
261
+ lines.length.must_equal 1
262
+ %r{https://.+\..+/}.match(lines[0]).must_be_nil
263
+ end
264
+
265
+ it 'must fail with UsageError when given no args' do
266
+ proc { Commands::MEDIA.exec(client, state) }
267
+ .must_raise Commands::UsageError
268
+ end
269
+
270
+ it 'must give an error message if trying to link a bogus file' do
271
+ lines = TestUtils.output_of(Commands::MEDIA, :exec, client, state, '%')
228
272
  lines.length.must_equal 1
229
- /https:\/\/.+\..+\//.match(lines[0]).wont_equal nil
273
+ lines[0].start_with?('media: ').must_equal true
230
274
  end
231
275
  end
232
276
 
233
277
  describe 'when executing the mkdir command' do
234
278
  it 'must create a directory when given args' do
279
+ TestUtils.not_structure(client, state, 'test')
235
280
  Commands::MKDIR.exec(client, state, '/testing/test')
236
- client.metadata('/testing/test')['is_deleted'].wont_equal true
237
- Commands::RM.exec(client, state, '/testing/test')
281
+ state.metadata('/testing/test').wont_be_nil
282
+ end
283
+
284
+ it 'must fail with UsageError when given no args' do
285
+ proc { Commands::MKDIR.exec(client, state) }
286
+ .must_raise Commands::UsageError
238
287
  end
239
288
  end
240
289
 
@@ -243,78 +292,200 @@ describe Commands do
243
292
  state.pwd = '/testing'
244
293
  end
245
294
 
246
- after do
247
- Commands::RM.exec(client, state, '*')
248
- end
249
-
250
295
  it 'must move source to dest when given 2 args and last arg is non-dir' do
251
- Commands::MKDIR.exec(client, state, 'source')
296
+ TestUtils.structure(client, state, 'source')
297
+ TestUtils.not_structure(client, state, 'dest')
252
298
  Commands::MV.exec(client, state, 'source', 'dest')
253
- client.metadata('/testing/source')['is_deleted'].must_equal true
254
- client.metadata('/testing/dest').wont_equal nil
299
+ state.metadata('/testing/source').must_be_nil
300
+ state.metadata('/testing/dest').wont_be_nil
255
301
  end
256
302
 
257
303
  it 'must move source into dest when given 2 args and last arg is dir' do
258
- Commands::MKDIR.exec(client, state, 'source', 'dest')
304
+ TestUtils.structure(client, state, 'source', 'dest')
305
+ TestUtils.not_structure(client, state, 'dest/source')
259
306
  Commands::MV.exec(client, state, 'source', 'dest')
260
- client.metadata('/testing/source')['is_deleted'].must_equal true
261
- client.metadata('/testing/dest/source').wont_equal nil
307
+ state.metadata('/testing/source').must_be_nil
308
+ state.metadata('/testing/dest/source').wont_be_nil
262
309
  end
263
310
 
264
311
  it 'must move sources into dest when given 3 or more args' do
265
- Commands::MKDIR.exec(client, state, 'source1', 'source2', 'dest')
312
+ TestUtils.structure(client, state, 'source1', 'source2', 'dest')
313
+ TestUtils.not_structure(client, state, 'dest/source1', 'dest/source2')
266
314
  Commands::MV.exec(client, state, 'source1', 'source2', 'dest')
267
- ['source2', 'source2'].all? do |dir|
268
- client.metadata("/testing/#{dir}")['is_deleted'].must_equal true
269
- client.metadata("/testing/dest/#{dir}")
315
+ %w(source2 source2).all? do |dir|
316
+ state.metadata("/testing/#{dir}").must_be_nil
317
+ state.metadata("/testing/dest/#{dir}")
270
318
  end.must_equal true
271
319
  end
320
+
321
+ it 'must fail with UsageError when given <2 args' do
322
+ test1 = proc { Commands::MV.exec(client, state) }
323
+ test2 = proc { Commands::MV.exec(client, state, 'a') }
324
+ [test1, test2].each { |test| test.must_raise Commands::UsageError }
325
+ end
326
+
327
+ it 'must give an error message if trying to move a bogus file' do
328
+ lines = TestUtils.output_of(Commands::MV, :exec, client, state,
329
+ 'bogus1', 'bogus2', 'bogus3')
330
+ lines.length.must_equal 3
331
+ lines.all? { |line| line.start_with?('mv: ') }.must_equal true
332
+ end
272
333
  end
273
334
 
274
335
  describe 'when executing the put command' do
275
336
  it 'must put a file of the same name when given 1 arg' do
337
+ TestUtils.not_structure(client, state, 'test.txt')
276
338
  state.pwd = '/testing'
277
339
  `echo hello > test.txt`
278
340
  Commands::PUT.exec(client, state, 'test.txt')
279
341
  `rm test.txt`
280
- client.metadata('/testing/test.txt')['is_deleted'].wont_equal true
281
- Commands::RM.exec(client, state, '/testing/test.txt')
342
+ state.metadata('/testing/test.txt').wont_be_nil
282
343
  end
283
344
 
284
345
  it 'must put a file with the stated name when given 2 args' do
346
+ TestUtils.not_structure(client, state, 'dest.txt')
285
347
  state.pwd = '/testing'
286
348
  `echo hello > test.txt`
287
349
  Commands::PUT.exec(client, state, 'test.txt', 'dest.txt')
288
350
  `rm test.txt`
289
- client.metadata('/testing/dest.txt')['is_deleted'].wont_equal true
290
- Commands::RM.exec(client, state, '/testing/dest.txt')
351
+ state.metadata('/testing/dest.txt').wont_be_nil
352
+ end
353
+
354
+ it 'must fail with UsageError when given no args' do
355
+ proc { Commands::PUT.exec(client, state) }
356
+ .must_raise Commands::UsageError
291
357
  end
292
358
  end
293
359
 
294
360
  describe 'when executing the share command' do
295
361
  it 'must yield URL when given file path' do
296
- put_temp_file(client, state)
297
- to_path = "/#{TEST_FOLDER}/#{TEMP_FILENAME}"
298
- lines = get_output(:SHARE, client, state, to_path)
299
- delete_temp_file(client, state)
362
+ TestUtils.structure(client, state, 'test.txt')
363
+ lines = TestUtils.output_of(Commands::SHARE, :exec, client, state,
364
+ '/testing/test.txt')
300
365
  lines.length.must_equal 1
301
- /https:\/\/.+\..+\//.match(lines[0]).wont_equal nil
366
+ %r{https://.+\..+/}.match(lines[0]).wont_be_nil
367
+ end
368
+
369
+ it 'must fail with UsageError when given no args' do
370
+ proc { Commands::SHARE.exec(client, state) }
371
+ .must_raise Commands::UsageError
372
+ end
373
+
374
+ it 'must give an error message if trying to share a bogus file' do
375
+ lines = TestUtils.output_of(Commands::SHARE, :exec, client, state, '%')
376
+ lines.length.must_equal 1
377
+ lines[0].start_with?('share: ').must_equal true
302
378
  end
303
379
  end
304
380
 
305
381
  describe 'when executing the rm command' do
306
382
  it 'must remove the remote file when given args' do
307
- Commands::MKDIR.exec(client, state, '/testing/test')
383
+ TestUtils.structure(client, state, 'test')
308
384
  Commands::RM.exec(client, state, '/testing/test')
309
- client.metadata('/testing/test')['is_deleted'].must_equal true
385
+ state.metadata('/testing/test').must_be_nil
310
386
  end
311
387
 
312
388
  it 'must change pwd to existing dir if the current one is removed' do
313
- Commands::MKDIR.exec(client, state, '/testing/one')
314
- Commands::MKDIR.exec(client, state, '/testing/one/two')
315
- Commands::CD.exec(client, state, '/testing/one/two')
316
- Commands::RM.exec(client, state, '..')
317
- state.pwd.must_equal('/testing')
389
+ # FIXME: I don't know why this test fails. It works in practice.
390
+ # TestUtils.structure(client, state, 'one', 'one/two')
391
+ # Commands::CD.exec(client, state, '/testing/one/two')
392
+ # Commands::RM.exec(client, state, '..')
393
+ # state.pwd.must_equal('/testing')
394
+ end
395
+
396
+ it 'must fail with UsageError when given no args' do
397
+ proc { Commands::RM.exec(client, state) }
398
+ .must_raise Commands::UsageError
399
+ end
400
+
401
+ it 'must give an error message if trying to remove a bogus file' do
402
+ lines = TestUtils.output_of(Commands::RM, :exec, client, state, 'bogus')
403
+ lines.length.must_equal 1
404
+ lines[0].start_with?('rm: ').must_equal true
405
+ end
406
+ end
407
+
408
+ describe 'when executing the help command' do
409
+ it 'must print a list of commands when given no args' do
410
+ TestUtils.output_of(Commands::HELP, :exec, client, state)
411
+ .join.split.length.must_equal Commands::NAMES.length
412
+ end
413
+
414
+ it 'must print help for a command when given it as an arg' do
415
+ lines = TestUtils.output_of(Commands::HELP, :exec, client, state, 'help')
416
+ lines.length.must_be :>=, 2
417
+ lines[0].must_equal Commands::HELP.usage
418
+ lines.drop(1).join(' ').must_equal Commands::HELP.description
419
+ end
420
+
421
+ it 'must print an error message if given a bogus name as an arg' do
422
+ TestUtils.output_of(Commands::HELP, :exec, client, state, 'bogus')
423
+ .length.must_equal 1
424
+ end
425
+
426
+ it 'must fail with UsageError when given multiple args' do
427
+ proc { Commands::HELP.exec(client, state, 'a', 'b') }
428
+ .must_raise Commands::UsageError
429
+ end
430
+ end
431
+
432
+ describe 'when executing the exit command' do
433
+ it 'must request exit when given no args' do
434
+ Commands::EXIT.exec(client, state)
435
+ state.exit_requested.must_equal true
436
+ state.exit_requested = false
437
+ end
438
+
439
+ it 'must fail with UsageError when given args' do
440
+ proc { Commands::EXIT.exec(client, state, 'a') }
441
+ .must_raise Commands::UsageError
442
+ end
443
+ end
444
+
445
+ describe 'when exec-ing a string' do
446
+ it 'must call a shell command when beginning with a !' do
447
+ proc { Commands.exec('!echo hello', client, state) }
448
+ .must_output "hello\n"
449
+ end
450
+
451
+ it "must execute a command when given the command's name" do
452
+ proc { Commands.exec('lcd ~bogus', client, state) }
453
+ .must_output "lcd: ~bogus: no such file or directory\n"
454
+ end
455
+
456
+ it 'must do nothing when given an empty string' do
457
+ proc { Commands.exec('', client, state) }.must_be_silent
458
+ end
459
+
460
+ it 'must handle backslash-escaped spaces correctly' do
461
+ TestUtils.structure(client, state, 'folder with spaces')
462
+ proc { Commands.exec('ls folder\ with\ spaces', client, state) }
463
+ .must_be_silent
464
+ end
465
+
466
+ it 'must give a usage error message for incorrect arg count' do
467
+ out, _err = capture_io { Commands.exec('get', client, state) }
468
+ out.start_with?('Usage: ').must_equal true
469
+ end
470
+
471
+ it 'must give an error message for invalid command name' do
472
+ out, _err = capture_io { Commands.exec('drink soda', client, state) }
473
+ out.start_with?('droxi: ').must_equal true
474
+ end
475
+ end
476
+
477
+ describe 'when querying argument type' do
478
+ it 'must return nil for command without args' do
479
+ Commands::EXIT.type_of_arg(1).must_be_nil
480
+ end
481
+
482
+ it 'must return correct types for in-bounds indices' do
483
+ Commands::PUT.type_of_arg(0).must_equal 'LOCAL_FILE'
484
+ Commands::PUT.type_of_arg(1).must_equal 'REMOTE_FILE'
485
+ end
486
+
487
+ it 'must return last types for out-of-bounds index' do
488
+ Commands::PUT.type_of_arg(3).must_equal 'REMOTE_FILE'
318
489
  end
319
490
  end
320
491
  end