command_mapper 0.1.0.pre1
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 +7 -0
- data/.github/workflows/ruby.yml +27 -0
- data/.gitignore +10 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +25 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +20 -0
- data/README.md +369 -0
- data/Rakefile +12 -0
- data/commnad_mapper.gemspec +61 -0
- data/gemspec.yml +23 -0
- data/lib/command_mapper/arg.rb +75 -0
- data/lib/command_mapper/argument.rb +142 -0
- data/lib/command_mapper/command.rb +606 -0
- data/lib/command_mapper/exceptions.rb +19 -0
- data/lib/command_mapper/option.rb +282 -0
- data/lib/command_mapper/option_value.rb +21 -0
- data/lib/command_mapper/sudo.rb +73 -0
- data/lib/command_mapper/types/enum.rb +35 -0
- data/lib/command_mapper/types/hex.rb +82 -0
- data/lib/command_mapper/types/input_dir.rb +35 -0
- data/lib/command_mapper/types/input_file.rb +35 -0
- data/lib/command_mapper/types/input_path.rb +29 -0
- data/lib/command_mapper/types/key_value.rb +131 -0
- data/lib/command_mapper/types/key_value_list.rb +45 -0
- data/lib/command_mapper/types/list.rb +90 -0
- data/lib/command_mapper/types/map.rb +64 -0
- data/lib/command_mapper/types/num.rb +50 -0
- data/lib/command_mapper/types/str.rb +85 -0
- data/lib/command_mapper/types/type.rb +102 -0
- data/lib/command_mapper/types.rb +6 -0
- data/lib/command_mapper/version.rb +4 -0
- data/lib/command_mapper.rb +2 -0
- data/spec/arg_spec.rb +137 -0
- data/spec/argument_spec.rb +513 -0
- data/spec/commnad_spec.rb +1175 -0
- data/spec/exceptions_spec.rb +14 -0
- data/spec/option_spec.rb +882 -0
- data/spec/option_value_spec.rb +17 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/sudo_spec.rb +24 -0
- data/spec/types/enum_spec.rb +31 -0
- data/spec/types/hex_spec.rb +158 -0
- data/spec/types/input_dir_spec.rb +30 -0
- data/spec/types/input_file_spec.rb +34 -0
- data/spec/types/input_path_spec.rb +32 -0
- data/spec/types/key_value_list_spec.rb +100 -0
- data/spec/types/key_value_spec.rb +272 -0
- data/spec/types/list_spec.rb +143 -0
- data/spec/types/map_spec.rb +62 -0
- data/spec/types/num_spec.rb +90 -0
- data/spec/types/str_spec.rb +232 -0
- data/spec/types/type_spec.rb +59 -0
- metadata +118 -0
@@ -0,0 +1,1175 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'command_mapper/command'
|
3
|
+
|
4
|
+
describe CommandMapper::Command do
|
5
|
+
module TestCommand
|
6
|
+
class WithCommandName < CommandMapper::Command
|
7
|
+
command 'foo'
|
8
|
+
end
|
9
|
+
|
10
|
+
class NoCommandName < CommandMapper::Command
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe ".command_name" do
|
15
|
+
subject { command_class }
|
16
|
+
|
17
|
+
context "when @command_name has been set" do
|
18
|
+
let(:command_class) { TestCommand::WithCommandName }
|
19
|
+
|
20
|
+
it "must return the defined command name" do
|
21
|
+
expect(subject.command_name).to eq('foo')
|
22
|
+
end
|
23
|
+
|
24
|
+
it "must freeze the given command name" do
|
25
|
+
expect(subject.command_name).to be_frozen
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when no .command has been defined" do
|
30
|
+
let(:command_class) { TestCommand::NoCommandName }
|
31
|
+
|
32
|
+
it "must raise NotImplementedError" do
|
33
|
+
expect {
|
34
|
+
subject.command_name
|
35
|
+
}.to raise_error(NotImplementedError,"#{command_class} did not call command(...)")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe ".command_name" do
|
41
|
+
subject { command_class }
|
42
|
+
|
43
|
+
context "when @command_name has been set" do
|
44
|
+
let(:command_class) { TestCommand::WithCommandName }
|
45
|
+
|
46
|
+
it "must set .command_name" do
|
47
|
+
expect(subject.command_name).to eq('foo')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module TestCommand
|
53
|
+
class EmptyCommand < CommandMapper::Command
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe ".options" do
|
58
|
+
subject { command_class }
|
59
|
+
|
60
|
+
context "when the command has no defined options" do
|
61
|
+
let(:command_class) { TestCommand::EmptyCommand }
|
62
|
+
|
63
|
+
it { expect(subject.options).to be_empty }
|
64
|
+
end
|
65
|
+
|
66
|
+
context "and when the command inherits from another command class" do
|
67
|
+
module TestCommand
|
68
|
+
class BaseClassWithOptions < CommandMapper::Command
|
69
|
+
option "--foo"
|
70
|
+
option "--bar"
|
71
|
+
end
|
72
|
+
|
73
|
+
class InheritedOptions < BaseClassWithOptions
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
let(:command_class) { TestCommand::InheritedOptions }
|
78
|
+
let(:command_superclass) { TestCommand::BaseClassWithOptions }
|
79
|
+
|
80
|
+
it "must copy the options defined in the superclass" do
|
81
|
+
expect(subject.options).to eq(command_superclass.options)
|
82
|
+
end
|
83
|
+
|
84
|
+
context "and when the class defines options of it's own" do
|
85
|
+
module TestCommand
|
86
|
+
class InheritsAndDefinesOptions < BaseClassWithOptions
|
87
|
+
option "--baz"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
let(:command_class) { TestCommand::InheritsAndDefinesOptions }
|
92
|
+
|
93
|
+
it "must copy the options defined in the superclass" do
|
94
|
+
expect(subject.options).to include(command_superclass.options)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "must define it's own options" do
|
98
|
+
expect(command_class.options[:baz]).to be_kind_of(CommandMapper::Option)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "must not modify the superclass's options" do
|
102
|
+
expect(command_superclass.options[:baz]).to be(nil)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe ".option" do
|
109
|
+
module TestCommand
|
110
|
+
class DefinesItsOwnOptions < CommandMapper::Command
|
111
|
+
command 'test' do
|
112
|
+
option '--foo'
|
113
|
+
option '--bar'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
let(:command_class) { TestCommand::DefinesItsOwnOptions }
|
119
|
+
|
120
|
+
subject { command_class }
|
121
|
+
|
122
|
+
it "must add options to .options" do
|
123
|
+
expect(subject.options[:foo]).to be_kind_of(CommandMapper::Option)
|
124
|
+
expect(subject.options[:foo].flag).to eq('--foo')
|
125
|
+
|
126
|
+
expect(subject.options[:bar]).to be_kind_of(CommandMapper::Option)
|
127
|
+
expect(subject.options[:bar].flag).to eq('--bar')
|
128
|
+
end
|
129
|
+
|
130
|
+
it "must define a reader method for each option" do
|
131
|
+
expect(subject.instance_methods(false)).to include(:foo)
|
132
|
+
expect(subject.instance_methods(false)).to include(:bar)
|
133
|
+
end
|
134
|
+
|
135
|
+
it "must define a writter method for each option" do
|
136
|
+
expect(subject.instance_methods(false)).to include(:foo=)
|
137
|
+
expect(subject.instance_methods(false)).to include(:bar=)
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "reader method" do
|
141
|
+
subject { command_class.new }
|
142
|
+
|
143
|
+
let(:value) { "test_reading" }
|
144
|
+
|
145
|
+
before do
|
146
|
+
subject.instance_variable_set('@foo',value)
|
147
|
+
end
|
148
|
+
|
149
|
+
it "must read the options value from @options" do
|
150
|
+
expect(subject.foo).to be(value)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe "writter method" do
|
155
|
+
subject { command_class.new }
|
156
|
+
|
157
|
+
let(:value) { "test_writing" }
|
158
|
+
|
159
|
+
before { subject.foo = value }
|
160
|
+
|
161
|
+
it "must read the options value from @options" do
|
162
|
+
expect(subject.instance_variable_get('@foo')).to be(value)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
context "when given a short flag" do
|
167
|
+
context "and it's length is < 3" do
|
168
|
+
module TestCommand
|
169
|
+
class EmptyCommand < CommandMapper::Command
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
let(:command_class) { TestCommand::EmptyCommand }
|
174
|
+
|
175
|
+
it "must raise an ArgumentError" do
|
176
|
+
expect {
|
177
|
+
subject.option '-o'
|
178
|
+
}.to raise_error(ArgumentError,"cannot infer a name from short option flag: \"-o\"")
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
context "but it's length is >= 3" do
|
183
|
+
module TestCommand
|
184
|
+
class ShortOptionWithoutName < CommandMapper::Command
|
185
|
+
option "-ip"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
let(:command_class) { TestCommand::ShortOptionWithoutName }
|
190
|
+
|
191
|
+
it "must register an option based on the short flag" do
|
192
|
+
expect(subject.options[:ip]).to be_kind_of(Option)
|
193
|
+
expect(subject.options[:ip].name).to eq(:ip)
|
194
|
+
expect(subject.options[:ip].flag).to eq("-ip")
|
195
|
+
end
|
196
|
+
|
197
|
+
it "must define a reader method for the option" do
|
198
|
+
expect(subject.instance_methods(false)).to include(:ip)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "must define a writter method for the option" do
|
202
|
+
expect(subject.instance_methods(false)).to include(:ip=)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
context "when the argument shares the same name as an internal method" do
|
208
|
+
let(:command_class) { Class.new(described_class) }
|
209
|
+
let(:flag) { "--flag" }
|
210
|
+
let(:name) { :command_argv }
|
211
|
+
|
212
|
+
it do
|
213
|
+
expect {
|
214
|
+
command_class.option flag, name: name
|
215
|
+
}.to raise_error(ArgumentError,"option #{flag.inspect} with name #{name.inspect} cannot override the internal method with same name: ##{name}")
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context "when the argument flag maps to an existing internal method" do
|
220
|
+
let(:command_class) { Class.new(described_class) }
|
221
|
+
let(:flag) { "--command-argv" }
|
222
|
+
let(:name) { :command_argv }
|
223
|
+
|
224
|
+
it do
|
225
|
+
expect {
|
226
|
+
command_class.option(flag)
|
227
|
+
}.to raise_error(ArgumentError,"option #{flag.inspect} maps to method name ##{name} and cannot override the internal method with same name: ##{name}")
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
describe ".arguments" do
|
233
|
+
subject { command_class }
|
234
|
+
|
235
|
+
context "when the command has no defined arguments" do
|
236
|
+
let(:command_class) { TestCommand::EmptyCommand }
|
237
|
+
|
238
|
+
it { expect(subject.arguments).to be_empty }
|
239
|
+
end
|
240
|
+
|
241
|
+
context "when the comand does have defined arguments" do
|
242
|
+
module TestCommand
|
243
|
+
class BaseClassWithOptions < CommandMapper::Command
|
244
|
+
argument :foo
|
245
|
+
argument :bar
|
246
|
+
end
|
247
|
+
|
248
|
+
class InheritedOptions < BaseClassWithOptions
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
let(:command_class) { TestCommand::InheritedOptions }
|
253
|
+
let(:command_superclass) { TestCommand::BaseClassWithOptions }
|
254
|
+
|
255
|
+
it "must copy the arguments defined in the superclass" do
|
256
|
+
expect(subject.arguments).to eq(command_superclass.arguments)
|
257
|
+
end
|
258
|
+
|
259
|
+
context "and when the class defines arguments of it's own" do
|
260
|
+
module TestCommand
|
261
|
+
class InheritsAndDefinesOptions < BaseClassWithOptions
|
262
|
+
argument :baz
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
let(:command_class) { TestCommand::InheritsAndDefinesOptions }
|
267
|
+
|
268
|
+
it "must copy the arguments defined in the superclass" do
|
269
|
+
expect(subject.arguments).to include(command_superclass.arguments)
|
270
|
+
end
|
271
|
+
|
272
|
+
it "must define it's own arguments" do
|
273
|
+
expect(command_class.arguments[:baz]).to be_kind_of(Argument)
|
274
|
+
end
|
275
|
+
|
276
|
+
it "must not modify the superclass's arguments" do
|
277
|
+
expect(command_superclass.arguments[:baz]).to be(nil)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
describe ".argument" do
|
284
|
+
module TestCommand
|
285
|
+
class DefinesArgument < CommandMapper::Command
|
286
|
+
command 'test' do
|
287
|
+
argument :foo
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
let(:command_class) { TestCommand::DefinesArgument }
|
293
|
+
|
294
|
+
subject { command_class }
|
295
|
+
|
296
|
+
it "must register an argument with the given name" do
|
297
|
+
expect(subject.arguments[:foo]).to be_kind_of(Argument)
|
298
|
+
expect(subject.arguments[:foo].name).to eq(:foo)
|
299
|
+
end
|
300
|
+
|
301
|
+
it "must define a reader method for the argument" do
|
302
|
+
expect(subject.instance_methods(false)).to include(:foo)
|
303
|
+
end
|
304
|
+
|
305
|
+
it "must define a writter method for the argument" do
|
306
|
+
expect(subject.instance_methods(false)).to include(:foo=)
|
307
|
+
end
|
308
|
+
|
309
|
+
describe "reader method" do
|
310
|
+
subject { command_class.new }
|
311
|
+
|
312
|
+
let(:value) { "test_reading" }
|
313
|
+
|
314
|
+
before do
|
315
|
+
subject.instance_variable_set('@foo',value)
|
316
|
+
end
|
317
|
+
|
318
|
+
it "must read the options value from @arguments" do
|
319
|
+
expect(subject.foo).to be(value)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
describe "writter method" do
|
324
|
+
subject { command_class.new }
|
325
|
+
|
326
|
+
let(:value) { "test_writing" }
|
327
|
+
|
328
|
+
before { subject.foo = value }
|
329
|
+
|
330
|
+
it "must read the options value from @arguments" do
|
331
|
+
expect(subject.instance_variable_get('@foo')).to be(value)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
context "when the argument shares the same name as an internal method" do
|
336
|
+
let(:command_class) { Class.new(described_class) }
|
337
|
+
let(:name) { :command_argv }
|
338
|
+
|
339
|
+
it do
|
340
|
+
expect {
|
341
|
+
command_class.argument(name)
|
342
|
+
}.to raise_error(ArgumentError,"argument #{name.inspect} cannot override internal method with same name: ##{name}")
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
describe ".subcommands" do
|
348
|
+
subject { command_class }
|
349
|
+
|
350
|
+
context "when the command has no defined subcommands" do
|
351
|
+
let(:command_class) { TestCommand::EmptyCommand }
|
352
|
+
|
353
|
+
it { expect(subject.subcommands).to be_empty }
|
354
|
+
end
|
355
|
+
|
356
|
+
context "when the comand does have defined subcommands" do
|
357
|
+
module TestCommand
|
358
|
+
class BaseClassWithOptions < CommandMapper::Command
|
359
|
+
subcommand :foo do
|
360
|
+
end
|
361
|
+
|
362
|
+
subcommand :bar do
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
class InheritedOptions < BaseClassWithOptions
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
let(:command_class) { TestCommand::InheritedOptions }
|
371
|
+
let(:command_superclass) { TestCommand::BaseClassWithOptions }
|
372
|
+
|
373
|
+
it "must copy the subcommands defined in the superclass" do
|
374
|
+
expect(subject.subcommands).to eq(command_superclass.subcommands)
|
375
|
+
end
|
376
|
+
|
377
|
+
context "and when the class defines subcommands of it's own" do
|
378
|
+
module TestCommand
|
379
|
+
class InheritsAndDefinesOptions < BaseClassWithOptions
|
380
|
+
|
381
|
+
subcommand :baz do
|
382
|
+
end
|
383
|
+
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
let(:command_class) { TestCommand::InheritsAndDefinesOptions }
|
388
|
+
|
389
|
+
it "must copy the subcommands defined in the superclass" do
|
390
|
+
expect(subject.subcommands).to include(command_superclass.subcommands)
|
391
|
+
end
|
392
|
+
|
393
|
+
it "must define it's own subcommands" do
|
394
|
+
expect(command_class.subcommands[:baz]).to eq(command_class::Baz)
|
395
|
+
end
|
396
|
+
|
397
|
+
it "must not modify the superclass's subcommands" do
|
398
|
+
expect(command_superclass.subcommands[:baz]).to be(nil)
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
describe ".subcommand" do
|
405
|
+
module TestCommand
|
406
|
+
class DefinesSubcommand < CommandMapper::Command
|
407
|
+
command 'cmd' do
|
408
|
+
subcommand "subcmd" do
|
409
|
+
option '--foo'
|
410
|
+
option '--bar'
|
411
|
+
argument :baz
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
let(:command_class) { TestCommand::DefinesSubcommand }
|
418
|
+
|
419
|
+
subject { command_class }
|
420
|
+
|
421
|
+
it "must add the subcommand to .subcommands using the method name" do
|
422
|
+
expect(subject.subcommands[:subcmd]).to be(command_class::Subcmd)
|
423
|
+
end
|
424
|
+
|
425
|
+
it "must define a constant for the new Subcommand class" do
|
426
|
+
expect(subject.const_get('Subcmd')).to (be < described_class)
|
427
|
+
end
|
428
|
+
|
429
|
+
it "must initialize a new Command with the given subcommand name" do
|
430
|
+
expect(subject.const_get('Subcmd').command_name).to eq("subcmd")
|
431
|
+
end
|
432
|
+
|
433
|
+
it "must define a reader method for the subcommand" do
|
434
|
+
expect(subject.instance_methods(false)).to include(:subcmd)
|
435
|
+
end
|
436
|
+
|
437
|
+
it "must define a writter method for the subcommand" do
|
438
|
+
expect(subject.instance_methods(false)).to include(:subcmd=)
|
439
|
+
end
|
440
|
+
|
441
|
+
context "when the reader method is called" do
|
442
|
+
let(:foo) { 'value1' }
|
443
|
+
let(:bar) { 'value2' }
|
444
|
+
let(:baz) { 'value3' }
|
445
|
+
|
446
|
+
subject { command_class.new }
|
447
|
+
|
448
|
+
context "when no subcommand data has been populated" do
|
449
|
+
it "must return nil" do
|
450
|
+
expect(subject.subcmd).to be(nil)
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
context "when the subcommand has been set" do
|
455
|
+
before do
|
456
|
+
subject.subcmd = {foo: foo, bar: bar, baz: baz}
|
457
|
+
end
|
458
|
+
|
459
|
+
it "must return an instance of the defined subcommand class" do
|
460
|
+
expect(subject.subcmd).to be_kind_of(command_class::Subcmd)
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
context "with a block" do
|
465
|
+
before do
|
466
|
+
subject.subcmd do |sub|
|
467
|
+
sub.foo = foo
|
468
|
+
sub.bar = bar
|
469
|
+
sub.baz = baz
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
it "must use the block to populate the new subcommand instance" do
|
474
|
+
expect(subject.subcmd.foo).to eq(foo)
|
475
|
+
expect(subject.subcmd.bar).to eq(bar)
|
476
|
+
expect(subject.subcmd.baz).to eq(baz)
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
context "when the writter method is called" do
|
482
|
+
let(:foo) { 'value1' }
|
483
|
+
let(:bar) { 'value2' }
|
484
|
+
let(:baz) { 'value3' }
|
485
|
+
|
486
|
+
context "with a Hash" do
|
487
|
+
subject { command_class.new }
|
488
|
+
|
489
|
+
before do
|
490
|
+
subject.subcmd = {foo: foo, bar: bar, baz: baz}
|
491
|
+
end
|
492
|
+
|
493
|
+
it "must set @command_subcommand to an instance of the defined subcommand class " do
|
494
|
+
expect(subject.instance_variable_get('@command_subcommand')).to be_kind_of(command_class::Subcmd)
|
495
|
+
end
|
496
|
+
|
497
|
+
it "must set the attributes of the subcommand" do
|
498
|
+
expect(subject.subcmd.foo).to eq(foo)
|
499
|
+
expect(subject.subcmd.bar).to eq(bar)
|
500
|
+
expect(subject.subcmd.baz).to eq(baz)
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
context "when nil" do
|
505
|
+
subject { command_class.new }
|
506
|
+
|
507
|
+
before do
|
508
|
+
subject.subcmd = {foo: foo, bar: bar, baz: baz}
|
509
|
+
subject.subcmd = nil
|
510
|
+
end
|
511
|
+
|
512
|
+
it "must set @commnad_subcommand to nil" do
|
513
|
+
expect(subject.instance_variable_get('@command_subcommand')).to be(nil)
|
514
|
+
end
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
context "when the subcommand name contains a '-'" do
|
519
|
+
module TestCommand
|
520
|
+
class DefinesSubcommand < CommandMapper::Command
|
521
|
+
subcommand 'sub-cmd' do
|
522
|
+
option '--foo'
|
523
|
+
option '--bar'
|
524
|
+
argument :baz
|
525
|
+
end
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
let(:command_class) { TestCommand::DefinesSubcommand }
|
530
|
+
|
531
|
+
it "must add the subcommand to .subcommands using the method name" do
|
532
|
+
expect(subject.subcommands[:sub_cmd]).to be(command_class::SubCmd)
|
533
|
+
end
|
534
|
+
|
535
|
+
it "must define a CamelCased subcommand constant" do
|
536
|
+
expect(subject.const_get('SubCmd')).to (be < described_class)
|
537
|
+
end
|
538
|
+
|
539
|
+
it "must replace any '-' characters with '_' for the reader method" do
|
540
|
+
expect(subject.instance_methods(false)).to include(:sub_cmd)
|
541
|
+
end
|
542
|
+
|
543
|
+
it "must replace any '-' characters with '_' for the writer method" do
|
544
|
+
expect(subject.instance_methods(false)).to include(:sub_cmd=)
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
context "when the subcommand shares the same name as an internal method" do
|
549
|
+
let(:command_class) { Class.new(described_class) }
|
550
|
+
let(:name) { "command-argv" }
|
551
|
+
let(:method_name) { 'command_argv' }
|
552
|
+
|
553
|
+
it do
|
554
|
+
expect {
|
555
|
+
command_class.subcommand(name) do
|
556
|
+
end
|
557
|
+
}.to raise_error(ArgumentError,"subcommand #{name.inspect} maps to method name ##{method_name} and cannot override the internal method with same name: ##{method_name}")
|
558
|
+
end
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
module TestCommand
|
563
|
+
class ExampleCommand < CommandMapper::Command
|
564
|
+
command 'test' do
|
565
|
+
option '--opt1', value: {required: true}
|
566
|
+
option '--opt2', value: {required: true}
|
567
|
+
option '--opt3', value: {required: true}
|
568
|
+
|
569
|
+
argument :arg1, required: false
|
570
|
+
argument :arg2, required: false
|
571
|
+
argument :arg3, required: false
|
572
|
+
|
573
|
+
subcommand 'subcmd' do
|
574
|
+
option '--sub-opt1', value: {required: true}
|
575
|
+
|
576
|
+
argument :sub_arg1, required: true
|
577
|
+
end
|
578
|
+
end
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
let(:opt1) { "foo" }
|
583
|
+
let(:opt2) { "bar" }
|
584
|
+
let(:opt3) { "baz" }
|
585
|
+
let(:arg1) { "foo" }
|
586
|
+
let(:arg2) { "bar" }
|
587
|
+
let(:arg3) { "baz" }
|
588
|
+
let(:env) { {'FOO' => 'bar'} }
|
589
|
+
|
590
|
+
let(:command_class) { TestCommand::ExampleCommand }
|
591
|
+
|
592
|
+
describe ".run" do
|
593
|
+
let(:command_instance) { double(:command_instance) }
|
594
|
+
let(:return_value) { double(:boolean) }
|
595
|
+
|
596
|
+
subject { command_class }
|
597
|
+
|
598
|
+
context "when called with a Hash of params" do
|
599
|
+
let(:params) do
|
600
|
+
{opt1: opt1, arg1: arg1}
|
601
|
+
end
|
602
|
+
|
603
|
+
it "must initialize a new command with the Hash of params and call #run" do
|
604
|
+
if RUBY_VERSION < '3.'
|
605
|
+
expect(subject).to receive(:new).with({},params).and_return(command_instance)
|
606
|
+
else
|
607
|
+
expect(subject).to receive(:new).with(params).and_return(command_instance)
|
608
|
+
end
|
609
|
+
|
610
|
+
expect(command_instance).to receive(:run_command).and_return(return_value)
|
611
|
+
|
612
|
+
expect(subject.run(params)).to be(return_value)
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
616
|
+
context "when called with keyword aguments" do
|
617
|
+
let(:kwargs) do
|
618
|
+
{opt1: opt1, arg1: arg1}
|
619
|
+
end
|
620
|
+
|
621
|
+
it "must initialize a new command with the keyword arguments and call #run" do
|
622
|
+
expect(subject).to receive(:new).with({},**kwargs).and_return(command_instance)
|
623
|
+
expect(command_instance).to receive(:run_command).and_return(return_value)
|
624
|
+
|
625
|
+
expect(subject.run(**kwargs)).to be(return_value)
|
626
|
+
end
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
630
|
+
describe ".capture" do
|
631
|
+
let(:command_instance) { double(:command_instance) }
|
632
|
+
let(:return_value) { double(:string) }
|
633
|
+
|
634
|
+
subject { command_class }
|
635
|
+
|
636
|
+
context "when called with a Hash of params" do
|
637
|
+
let(:params) do
|
638
|
+
{opt1: opt1, arg1: arg1}
|
639
|
+
end
|
640
|
+
|
641
|
+
it "must initialize a new command with the Hash of params and call #capture" do
|
642
|
+
if RUBY_VERSION < '3.'
|
643
|
+
expect(subject).to receive(:new).with({},params).and_return(command_instance)
|
644
|
+
else
|
645
|
+
expect(subject).to receive(:new).with(params).and_return(command_instance)
|
646
|
+
end
|
647
|
+
|
648
|
+
expect(command_instance).to receive(:capture_command).and_return(return_value)
|
649
|
+
|
650
|
+
expect(subject.capture(params)).to be(return_value)
|
651
|
+
end
|
652
|
+
end
|
653
|
+
|
654
|
+
context "when called with keyword aguments" do
|
655
|
+
let(:kwargs) do
|
656
|
+
{opt1: opt1, arg1: arg1}
|
657
|
+
end
|
658
|
+
|
659
|
+
it "must initialize a new command with the keyword arguments and call #capture" do
|
660
|
+
expect(subject).to receive(:new).with({},**kwargs).and_return(command_instance)
|
661
|
+
expect(command_instance).to receive(:capture_command).and_return(return_value)
|
662
|
+
|
663
|
+
expect(subject.capture(**kwargs)).to be(return_value)
|
664
|
+
end
|
665
|
+
end
|
666
|
+
end
|
667
|
+
|
668
|
+
describe ".popen" do
|
669
|
+
let(:command_instance) { double(:command_instance) }
|
670
|
+
let(:return_value) { double(:io) }
|
671
|
+
|
672
|
+
subject { command_class }
|
673
|
+
|
674
|
+
context "when called with a Hash of params" do
|
675
|
+
let(:params) do
|
676
|
+
{opt1: opt1, arg1: arg1}
|
677
|
+
end
|
678
|
+
|
679
|
+
it "must initialize a new command with the Hash of params and call #popen" do
|
680
|
+
if RUBY_VERSION < '3.'
|
681
|
+
expect(subject).to receive(:new).with({},params).and_return(command_instance)
|
682
|
+
else
|
683
|
+
expect(subject).to receive(:new).with(params).and_return(command_instance)
|
684
|
+
end
|
685
|
+
|
686
|
+
expect(command_instance).to receive(:popen_command).and_return(return_value)
|
687
|
+
|
688
|
+
expect(subject.popen(params)).to be(return_value)
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
context "when called with keyword aguments" do
|
693
|
+
let(:kwargs) do
|
694
|
+
{opt1: opt1, arg1: arg1}
|
695
|
+
end
|
696
|
+
|
697
|
+
it "must initialize a new command with the keyword arguments and call #popen" do
|
698
|
+
expect(subject).to receive(:new).with({},**kwargs).and_return(command_instance)
|
699
|
+
expect(command_instance).to receive(:popen_command).and_return(return_value)
|
700
|
+
|
701
|
+
expect(subject.popen(**kwargs)).to be(return_value)
|
702
|
+
end
|
703
|
+
end
|
704
|
+
end
|
705
|
+
|
706
|
+
describe ".sudo" do
|
707
|
+
let(:command_instance) { double(:command_instance) }
|
708
|
+
let(:return_value) { double(:boolean) }
|
709
|
+
|
710
|
+
subject { command_class }
|
711
|
+
|
712
|
+
context "when called with a Hash of params" do
|
713
|
+
let(:params) do
|
714
|
+
{opt1: opt1, arg1: arg1}
|
715
|
+
end
|
716
|
+
|
717
|
+
it "must initialize a new command with the Hash of params and call #sudo" do
|
718
|
+
if RUBY_VERSION < '3.'
|
719
|
+
expect(subject).to receive(:new).with({},params).and_return(command_instance)
|
720
|
+
else
|
721
|
+
expect(subject).to receive(:new).with(params).and_return(command_instance)
|
722
|
+
end
|
723
|
+
|
724
|
+
expect(command_instance).to receive(:sudo_command).and_return(return_value)
|
725
|
+
|
726
|
+
expect(subject.sudo(params)).to be(return_value)
|
727
|
+
end
|
728
|
+
end
|
729
|
+
|
730
|
+
context "when called with keyword aguments" do
|
731
|
+
let(:kwargs) do
|
732
|
+
{opt1: opt1, arg1: arg1}
|
733
|
+
end
|
734
|
+
|
735
|
+
it "must initialize a new command with the keyword arguments and call #sudo" do
|
736
|
+
expect(subject).to receive(:new).with({},**kwargs).and_return(command_instance)
|
737
|
+
expect(command_instance).to receive(:sudo_command).and_return(return_value)
|
738
|
+
|
739
|
+
expect(subject.sudo(**kwargs)).to be(return_value)
|
740
|
+
end
|
741
|
+
end
|
742
|
+
end
|
743
|
+
|
744
|
+
describe "#initialize" do
|
745
|
+
subject { command_class.new() }
|
746
|
+
|
747
|
+
it "must default #command_name to self.class.command" do
|
748
|
+
expect(subject.command_name).to eq(command_class.command_name)
|
749
|
+
end
|
750
|
+
|
751
|
+
it "must default #command_env to {}" do
|
752
|
+
expect(subject.command_env).to eq({})
|
753
|
+
end
|
754
|
+
|
755
|
+
it "must default #command_subcommand to nil" do
|
756
|
+
expect(subject.command_subcommand).to be(nil)
|
757
|
+
end
|
758
|
+
|
759
|
+
it "must default option values to nil" do
|
760
|
+
expect(subject.opt1).to be(nil)
|
761
|
+
expect(subject.opt2).to be(nil)
|
762
|
+
expect(subject.opt3).to be(nil)
|
763
|
+
end
|
764
|
+
|
765
|
+
it "must default argument values to nil" do
|
766
|
+
expect(subject.arg1).to be(nil)
|
767
|
+
expect(subject.arg2).to be(nil)
|
768
|
+
expect(subject.arg3).to be(nil)
|
769
|
+
end
|
770
|
+
|
771
|
+
context "when initialized with a Hash of options and arguments" do
|
772
|
+
let(:params) do
|
773
|
+
{opt1: opt1, opt2: opt2, opt3: opt3, arg1: arg1, arg2: arg2, arg3: arg3}
|
774
|
+
end
|
775
|
+
|
776
|
+
subject { command_class.new(params) }
|
777
|
+
|
778
|
+
it "must set option values" do
|
779
|
+
expect(subject.opt1).to be(opt1)
|
780
|
+
expect(subject.opt2).to be(opt2)
|
781
|
+
expect(subject.opt3).to be(opt3)
|
782
|
+
end
|
783
|
+
|
784
|
+
it "must set argument values" do
|
785
|
+
expect(subject.arg1).to be(arg1)
|
786
|
+
expect(subject.arg2).to be(arg2)
|
787
|
+
expect(subject.arg3).to be(arg3)
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
context "when initialized with additional keywords" do
|
792
|
+
let(:keywords) do
|
793
|
+
{opt1: opt1, opt2: opt2, opt3: opt3, arg1: arg1, arg2: arg2, arg3: arg3}
|
794
|
+
end
|
795
|
+
|
796
|
+
subject { command_class.new(**keywords) }
|
797
|
+
|
798
|
+
it "must set option values" do
|
799
|
+
expect(subject.opt1).to be(opt1)
|
800
|
+
expect(subject.opt2).to be(opt2)
|
801
|
+
expect(subject.opt3).to be(opt3)
|
802
|
+
end
|
803
|
+
|
804
|
+
it "must set argument values" do
|
805
|
+
expect(subject.arg1).to be(arg1)
|
806
|
+
expect(subject.arg2).to be(arg2)
|
807
|
+
expect(subject.arg3).to be(arg3)
|
808
|
+
end
|
809
|
+
end
|
810
|
+
|
811
|
+
context "when initialized with command_name: ..." do
|
812
|
+
let(:command_name) { 'foo2' }
|
813
|
+
|
814
|
+
subject { command_class.new(command_name: command_name) }
|
815
|
+
|
816
|
+
it "must set #command_name" do
|
817
|
+
expect(subject.command_name).to eq(command_name)
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
context "when initialized with command_path: ..." do
|
822
|
+
let(:command_path) { '/path/to/foo' }
|
823
|
+
|
824
|
+
subject { command_class.new(command_path: command_path) }
|
825
|
+
|
826
|
+
it "must set #command_path" do
|
827
|
+
expect(subject.command_path).to eq(command_path)
|
828
|
+
end
|
829
|
+
end
|
830
|
+
|
831
|
+
context "when initialized with command_env: {...}" do
|
832
|
+
subject { command_class.new(command_env: env) }
|
833
|
+
|
834
|
+
it "must populate #command_env" do
|
835
|
+
expect(subject.command_env).to eq(env)
|
836
|
+
end
|
837
|
+
end
|
838
|
+
end
|
839
|
+
|
840
|
+
describe "#[]" do
|
841
|
+
let(:name) { :opt1 }
|
842
|
+
let(:value) { 'test' }
|
843
|
+
|
844
|
+
subject { command_class.new(opt1: value) }
|
845
|
+
|
846
|
+
it "must call the method with the same given name" do
|
847
|
+
expect(subject).to receive(name).and_return(value)
|
848
|
+
|
849
|
+
expect(subject[name]).to be(value)
|
850
|
+
end
|
851
|
+
|
852
|
+
context "when there is no reader method of the same name" do
|
853
|
+
let(:name) { :fubar }
|
854
|
+
|
855
|
+
it do
|
856
|
+
expect {
|
857
|
+
subject[name]
|
858
|
+
}.to raise_error(ArgumentError,"#{command_class} does not define ##{name}")
|
859
|
+
end
|
860
|
+
end
|
861
|
+
end
|
862
|
+
|
863
|
+
describe "#[]=" do
|
864
|
+
let(:name) { :opt2 }
|
865
|
+
let(:value) { 'new_value' }
|
866
|
+
|
867
|
+
subject { command_class.new }
|
868
|
+
|
869
|
+
it "must call the writter method with the same given name" do
|
870
|
+
expect(subject).to receive(:"#{name}=").with(value).and_return(value)
|
871
|
+
|
872
|
+
subject[name] = value
|
873
|
+
end
|
874
|
+
|
875
|
+
it "must return the new value" do
|
876
|
+
expect(subject[name] = value).to be(value)
|
877
|
+
end
|
878
|
+
|
879
|
+
context "when there is no reader method of the same name" do
|
880
|
+
let(:name) { :fubar }
|
881
|
+
|
882
|
+
it do
|
883
|
+
expect {
|
884
|
+
subject[name] = value
|
885
|
+
}.to raise_error(ArgumentError,"#{command_class} does not define ##{name}=")
|
886
|
+
end
|
887
|
+
end
|
888
|
+
end
|
889
|
+
|
890
|
+
describe "#command_argv" do
|
891
|
+
context "when the command has no options or arguments set" do
|
892
|
+
subject { command_class.new }
|
893
|
+
|
894
|
+
it "must return an argv only containing the command name" do
|
895
|
+
expect(subject.command_argv).to eq([subject.class.command_name])
|
896
|
+
end
|
897
|
+
|
898
|
+
context "but the command has required arguments" do
|
899
|
+
module TestCommand
|
900
|
+
class CommandWithRequiredArguments < CommandMapper::Command
|
901
|
+
command "test" do
|
902
|
+
option '--opt1', value: {required: true}
|
903
|
+
option '--opt2', value: {required: true}
|
904
|
+
option '--opt3', value: {required: true}
|
905
|
+
|
906
|
+
argument :arg1, required: false
|
907
|
+
argument :arg2, required: true
|
908
|
+
argument :arg3, required: false
|
909
|
+
end
|
910
|
+
end
|
911
|
+
end
|
912
|
+
|
913
|
+
let(:command_class) { TestCommand::CommandWithRequiredArguments }
|
914
|
+
|
915
|
+
it do
|
916
|
+
expect {
|
917
|
+
subject.command_argv
|
918
|
+
}.to raise_error(ArgumentRequired,"argument arg2 is required")
|
919
|
+
end
|
920
|
+
end
|
921
|
+
end
|
922
|
+
|
923
|
+
context "when the command is initialized with the command_path: keyword" do
|
924
|
+
let(:command_path) { '/path/to/foo' }
|
925
|
+
|
926
|
+
subject { command_class.new(command_path: command_path) }
|
927
|
+
|
928
|
+
it "must override the command name" do
|
929
|
+
expect(subject.command_argv).to eq([subject.command_path])
|
930
|
+
end
|
931
|
+
end
|
932
|
+
|
933
|
+
context "when the command has options set" do
|
934
|
+
subject { command_class.new({opt1: opt1, opt2: opt2, opt3: opt3}) }
|
935
|
+
|
936
|
+
it "must return an argv containing the command name and option flags followed by values" do
|
937
|
+
expect(subject.command_argv).to eq(
|
938
|
+
[
|
939
|
+
subject.class.command_name,
|
940
|
+
'--opt1', opt1,
|
941
|
+
'--opt2', opt2,
|
942
|
+
'--opt3', opt3
|
943
|
+
]
|
944
|
+
)
|
945
|
+
end
|
946
|
+
end
|
947
|
+
|
948
|
+
context "when the command has arguments set" do
|
949
|
+
subject { command_class.new({arg1: arg1, arg2: arg2, arg3: arg3}) }
|
950
|
+
|
951
|
+
it "must return an argv containing the command name and argument values" do
|
952
|
+
expect(subject.command_argv).to eq(
|
953
|
+
[subject.command_name, arg1, arg2, arg3]
|
954
|
+
)
|
955
|
+
end
|
956
|
+
|
957
|
+
context "when the arguments are initialized in a different order" do
|
958
|
+
subject { command_class.new({arg2: arg2, arg1: arg1, arg3: arg3}) }
|
959
|
+
|
960
|
+
it "must return the argument values in the order the arguments were defined" do
|
961
|
+
expect(subject.command_argv).to eq(
|
962
|
+
[subject.command_name, arg1, arg2, arg3]
|
963
|
+
)
|
964
|
+
end
|
965
|
+
end
|
966
|
+
|
967
|
+
context "and when one of the argument values starts with a '-'" do
|
968
|
+
let(:arg2) { "--bar" }
|
969
|
+
|
970
|
+
it "must separate the arguments with a '--'" do
|
971
|
+
expect(subject.command_argv).to eq(
|
972
|
+
[subject.command_name, "--", arg1, arg2, arg3]
|
973
|
+
)
|
974
|
+
end
|
975
|
+
end
|
976
|
+
end
|
977
|
+
|
978
|
+
context "when the command has both options and arguments set" do
|
979
|
+
subject do
|
980
|
+
command_class.new(
|
981
|
+
{
|
982
|
+
opt1: opt1, opt2: opt2, opt3: opt3,
|
983
|
+
arg1: arg1, arg2: arg2, arg3: arg3
|
984
|
+
}
|
985
|
+
)
|
986
|
+
end
|
987
|
+
|
988
|
+
it "must return an argv containing the command name, options flags and values, then argument values" do
|
989
|
+
expect(subject.command_argv).to eq(
|
990
|
+
[
|
991
|
+
subject.command_name,
|
992
|
+
'--opt1', opt1,
|
993
|
+
'--opt2', opt2,
|
994
|
+
'--opt3', opt3,
|
995
|
+
arg1, arg2, arg3
|
996
|
+
]
|
997
|
+
)
|
998
|
+
end
|
999
|
+
end
|
1000
|
+
|
1001
|
+
context "when the command has a subcommand set" do
|
1002
|
+
let(:sub_opt1) { 'foo' }
|
1003
|
+
let(:sub_arg1) { 'bar' }
|
1004
|
+
|
1005
|
+
subject do
|
1006
|
+
command_class.new(
|
1007
|
+
{
|
1008
|
+
subcmd: {sub_opt1: sub_opt1, sub_arg1: sub_arg1}
|
1009
|
+
}
|
1010
|
+
)
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
it "must return an argv containing the command name, sub-command name, subcommand options and arguments" do
|
1014
|
+
expect(subject.command_argv).to eq(
|
1015
|
+
[
|
1016
|
+
subject.command_name,
|
1017
|
+
'subcmd', '--sub-opt1', sub_opt1, sub_arg1
|
1018
|
+
]
|
1019
|
+
)
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
context "and when the command also has options set" do
|
1023
|
+
subject do
|
1024
|
+
command_class.new(
|
1025
|
+
{
|
1026
|
+
opt1: opt1, opt2: opt2, opt3: opt3,
|
1027
|
+
subcmd: {sub_opt1: sub_opt1, sub_arg1: sub_arg1}
|
1028
|
+
}
|
1029
|
+
)
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
it "must return an argv containing the command name, global options, sub-command name, subcommand options and arguments" do
|
1033
|
+
expect(subject.command_argv).to eq(
|
1034
|
+
[
|
1035
|
+
subject.command_name,
|
1036
|
+
'--opt1', opt1,
|
1037
|
+
'--opt2', opt2,
|
1038
|
+
'--opt3', opt3,
|
1039
|
+
'subcmd', '--sub-opt1', sub_opt1, sub_arg1
|
1040
|
+
]
|
1041
|
+
)
|
1042
|
+
end
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
context "and when the command also has arguments set" do
|
1046
|
+
subject do
|
1047
|
+
command_class.new(
|
1048
|
+
{
|
1049
|
+
opt1: opt1, opt2: opt2, opt3: opt3,
|
1050
|
+
arg1: arg1, arg2: arg2, arg3: arg3,
|
1051
|
+
subcmd: {sub_opt1: sub_opt1, sub_arg1: sub_arg1}
|
1052
|
+
}
|
1053
|
+
)
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
it "must return an argv containing the sub-command's options and arguments, instead of the command's arguments" do
|
1057
|
+
expect(subject.command_argv).to eq(
|
1058
|
+
[
|
1059
|
+
subject.command_name,
|
1060
|
+
'--opt1', opt1,
|
1061
|
+
'--opt2', opt2,
|
1062
|
+
'--opt3', opt3,
|
1063
|
+
'subcmd', '--sub-opt1', sub_opt1, sub_arg1
|
1064
|
+
]
|
1065
|
+
)
|
1066
|
+
end
|
1067
|
+
end
|
1068
|
+
end
|
1069
|
+
end
|
1070
|
+
|
1071
|
+
describe "#command_string" do
|
1072
|
+
let(:opt1) { "foo bar" }
|
1073
|
+
let(:arg1) { "baz qux" }
|
1074
|
+
|
1075
|
+
subject { command_class.new({opt1: opt1, arg1: arg1}) }
|
1076
|
+
|
1077
|
+
let(:escaped_command) { Shellwords.shelljoin(subject.command_argv) }
|
1078
|
+
|
1079
|
+
it "must escape the command option values and argument values" do
|
1080
|
+
expect(subject.command_string).to eq(escaped_command)
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
context "when initialized with command_env: {...}" do
|
1084
|
+
let(:env) { {"FOO" => "bar baz"} }
|
1085
|
+
|
1086
|
+
let(:escaped_env) do
|
1087
|
+
env.map { |name,value|
|
1088
|
+
"#{Shellwords.shellescape(name)}=#{Shellwords.shellescape(value)}"
|
1089
|
+
}.join(' ')
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
let(:escaped_command) { Shellwords.shelljoin(subject.command_argv) }
|
1093
|
+
|
1094
|
+
subject { command_class.new({opt1: opt1, arg1: arg1}, command_env: env) }
|
1095
|
+
|
1096
|
+
it "must escape both the env variables and the command" do
|
1097
|
+
expect(subject.command_string).to eq(
|
1098
|
+
"#{escaped_env} #{escaped_command}"
|
1099
|
+
)
|
1100
|
+
end
|
1101
|
+
end
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
describe "#run_command" do
|
1105
|
+
subject { command_class.new({opt1: opt1, arg1: arg1}, command_env: env) }
|
1106
|
+
|
1107
|
+
it "must pass the command's env and argv to Kenrel.system" do
|
1108
|
+
expect(subject).to receive(:system).with(env,*subject.command_argv)
|
1109
|
+
|
1110
|
+
subject.run_command
|
1111
|
+
end
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
describe "#capture_command" do
|
1115
|
+
subject { command_class.new({opt1: opt1, arg1: arg1}, command_env: env) }
|
1116
|
+
|
1117
|
+
it "must pass the command's env and argv to `...`" do
|
1118
|
+
expect(subject).to receive(:`).with(subject.command_string)
|
1119
|
+
|
1120
|
+
subject.capture_command
|
1121
|
+
end
|
1122
|
+
end
|
1123
|
+
|
1124
|
+
describe "#popen_command" do
|
1125
|
+
subject { command_class.new({opt1: opt1, arg1: arg1}, command_env: env) }
|
1126
|
+
|
1127
|
+
it "must pass the command's env, argv, and to IO.popen" do
|
1128
|
+
expect(IO).to receive(:popen).with(env,subject.command_argv)
|
1129
|
+
|
1130
|
+
subject.popen_command
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
context "when a open mode is given" do
|
1134
|
+
let(:mode) { 'w' }
|
1135
|
+
|
1136
|
+
it "must pass the command's env, argv, and the mode to IO.popen" do
|
1137
|
+
expect(IO).to receive(:popen).with(env,subject.command_argv,mode)
|
1138
|
+
|
1139
|
+
subject.popen_command(mode)
|
1140
|
+
end
|
1141
|
+
end
|
1142
|
+
end
|
1143
|
+
|
1144
|
+
describe "#sudo!" do
|
1145
|
+
subject { command_class.new({opt1: opt1, arg1: arg1}, command_env: env) }
|
1146
|
+
|
1147
|
+
let(:expected_argv) { [command_class.command, "--opt1", opt1, arg1] }
|
1148
|
+
|
1149
|
+
it "must pass the command's env and argv, and to IO.popen" do
|
1150
|
+
expect(Sudo).to receive(:run).with({command: subject.command_argv}, command_env: env)
|
1151
|
+
|
1152
|
+
subject.sudo_command
|
1153
|
+
end
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
describe "#to_s" do
|
1157
|
+
let(:opt1) { "foo bar" }
|
1158
|
+
let(:arg1) { "baz qux" }
|
1159
|
+
let(:env) { {"FOO" => "bar baz"} }
|
1160
|
+
|
1161
|
+
subject { command_class.new({opt1: opt1, arg1: arg1}, command_env: env) }
|
1162
|
+
|
1163
|
+
it "must call #command_string" do
|
1164
|
+
expect(subject.to_s).to eq(subject.command_string)
|
1165
|
+
end
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
describe "#to_a" do
|
1169
|
+
subject { command_class.new({opt1: opt1, arg1: arg1}) }
|
1170
|
+
|
1171
|
+
it "must call #command_argv" do
|
1172
|
+
expect(subject.to_a).to eq(subject.command_argv)
|
1173
|
+
end
|
1174
|
+
end
|
1175
|
+
end
|