droxi 0.0.5 → 0.1.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.
- checksums.yaml +4 -4
- data/README.md +9 -1
- data/Rakefile +18 -5
- data/bin/droxi +2 -3
- data/droxi.gemspec +2 -2
- data/lib/droxi.rb +45 -33
- data/lib/droxi/commands.rb +117 -97
- data/lib/droxi/complete.rb +36 -31
- data/lib/droxi/settings.rb +51 -52
- data/lib/droxi/state.rb +72 -69
- data/lib/droxi/text.rb +27 -37
- data/spec/all.rb +10 -0
- data/spec/commands_spec.rb +285 -114
- data/spec/complete_spec.rb +93 -28
- data/spec/settings_spec.rb +37 -4
- data/spec/state_spec.rb +51 -23
- data/spec/testutils.rb +65 -0
- data/spec/text_spec.rb +5 -5
- metadata +3 -2
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
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
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],
|
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.
|
31
|
+
def self.terminal_width
|
38
32
|
require 'readline'
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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,
|
49
|
-
|
50
|
-
|
51
|
-
|
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,
|
58
|
-
|
59
|
-
|
60
|
-
head,
|
61
|
-
|
62
|
-
|
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')
|
data/spec/commands_spec.rb
CHANGED
@@ -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
|
-
|
55
|
-
|
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, '
|
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
|
-
|
74
|
+
TestUtils.structure(client, state, 'source')
|
75
|
+
TestUtils.not_structure(client, state, 'dest')
|
104
76
|
Commands::CP.exec(client, state, 'source', 'dest')
|
105
|
-
|
106
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
120
|
-
|
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
|
-
|
134
|
-
Commands::FORGET
|
135
|
-
|
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
|
-
|
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
|
-
|
220
|
+
TestUtils.exact_structure(client, state, 'test')
|
193
221
|
state.pwd = '/testing'
|
194
|
-
|
195
|
-
|
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
|
-
|
203
|
-
|
204
|
-
|
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
|
-
|
212
|
-
lines =
|
213
|
-
|
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]).
|
218
|
-
|
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
|
-
|
225
|
-
|
226
|
-
lines =
|
227
|
-
|
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
|
-
|
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
|
-
|
237
|
-
|
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
|
-
|
296
|
+
TestUtils.structure(client, state, 'source')
|
297
|
+
TestUtils.not_structure(client, state, 'dest')
|
252
298
|
Commands::MV.exec(client, state, 'source', 'dest')
|
253
|
-
|
254
|
-
|
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
|
-
|
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
|
-
|
261
|
-
|
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
|
-
|
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
|
-
|
268
|
-
|
269
|
-
|
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
|
-
|
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
|
-
|
290
|
-
|
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
|
-
|
297
|
-
|
298
|
-
|
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
|
-
|
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
|
-
|
383
|
+
TestUtils.structure(client, state, 'test')
|
308
384
|
Commands::RM.exec(client, state, '/testing/test')
|
309
|
-
|
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
|
-
|
314
|
-
|
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
|