ronin-core 0.1.0.beta1
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/.document +5 -0
- data/.github/workflows/ruby.yml +41 -0
- data/.gitignore +12 -0
- data/.rspec +1 -0
- data/.rubocop.yml +160 -0
- data/.ruby-version +1 -0
- data/.yardopts +1 -0
- data/COPYING.txt +165 -0
- data/ChangeLog.md +11 -0
- data/Gemfile +30 -0
- data/README.md +299 -0
- data/Rakefile +34 -0
- data/examples/ruby_shell.rb +11 -0
- data/gemspec.yml +28 -0
- data/lib/ronin/core/class_registry.rb +246 -0
- data/lib/ronin/core/cli/command.rb +87 -0
- data/lib/ronin/core/cli/command_shell/command.rb +110 -0
- data/lib/ronin/core/cli/command_shell.rb +345 -0
- data/lib/ronin/core/cli/generator/options/author.rb +106 -0
- data/lib/ronin/core/cli/generator/options/description.rb +54 -0
- data/lib/ronin/core/cli/generator/options/reference.rb +60 -0
- data/lib/ronin/core/cli/generator/options/summary.rb +54 -0
- data/lib/ronin/core/cli/generator.rb +238 -0
- data/lib/ronin/core/cli/logging.rb +59 -0
- data/lib/ronin/core/cli/options/param.rb +68 -0
- data/lib/ronin/core/cli/options/values/arches.rb +45 -0
- data/lib/ronin/core/cli/options/values/oses.rb +32 -0
- data/lib/ronin/core/cli/printing/arch.rb +71 -0
- data/lib/ronin/core/cli/printing/metadata.rb +113 -0
- data/lib/ronin/core/cli/printing/os.rb +54 -0
- data/lib/ronin/core/cli/printing/params.rb +69 -0
- data/lib/ronin/core/cli/ruby_shell.rb +131 -0
- data/lib/ronin/core/cli/shell.rb +186 -0
- data/lib/ronin/core/git.rb +73 -0
- data/lib/ronin/core/home.rb +86 -0
- data/lib/ronin/core/metadata/authors/author.rb +241 -0
- data/lib/ronin/core/metadata/authors.rb +120 -0
- data/lib/ronin/core/metadata/description.rb +100 -0
- data/lib/ronin/core/metadata/id.rb +88 -0
- data/lib/ronin/core/metadata/references.rb +87 -0
- data/lib/ronin/core/metadata/summary.rb +78 -0
- data/lib/ronin/core/metadata/version.rb +74 -0
- data/lib/ronin/core/params/exceptions.rb +38 -0
- data/lib/ronin/core/params/mixin.rb +317 -0
- data/lib/ronin/core/params/param.rb +137 -0
- data/lib/ronin/core/params/types/boolean.rb +64 -0
- data/lib/ronin/core/params/types/enum.rb +107 -0
- data/lib/ronin/core/params/types/float.rb +68 -0
- data/lib/ronin/core/params/types/integer.rb +100 -0
- data/lib/ronin/core/params/types/numeric.rb +106 -0
- data/lib/ronin/core/params/types/regexp.rb +67 -0
- data/lib/ronin/core/params/types/string.rb +118 -0
- data/lib/ronin/core/params/types/type.rb +54 -0
- data/lib/ronin/core/params/types/uri.rb +72 -0
- data/lib/ronin/core/params/types.rb +62 -0
- data/lib/ronin/core/params.rb +19 -0
- data/lib/ronin/core/version.rb +24 -0
- data/ronin-core.gemspec +59 -0
- data/spec/class_registry_spec.rb +224 -0
- data/spec/cli/command_shell/command_spec.rb +113 -0
- data/spec/cli/command_shell_spec.rb +1114 -0
- data/spec/cli/command_spec.rb +16 -0
- data/spec/cli/fixtures/irb_command +8 -0
- data/spec/cli/fixtures/template/dir/file1.txt +1 -0
- data/spec/cli/fixtures/template/dir/file2.txt +1 -0
- data/spec/cli/fixtures/template/file.erb +1 -0
- data/spec/cli/fixtures/template/file.txt +1 -0
- data/spec/cli/generator/options/author_spec.rb +121 -0
- data/spec/cli/generator/options/description_spec.rb +45 -0
- data/spec/cli/generator/options/reference_spec.rb +53 -0
- data/spec/cli/generator/options/summary_spec.rb +45 -0
- data/spec/cli/generator_spec.rb +244 -0
- data/spec/cli/logging_spec.rb +95 -0
- data/spec/cli/options/param_spec.rb +67 -0
- data/spec/cli/options/values/arches_spec.rb +62 -0
- data/spec/cli/printing/arch_spec.rb +130 -0
- data/spec/cli/printing/metadata_spec.rb +211 -0
- data/spec/cli/printing/os_spec.rb +64 -0
- data/spec/cli/printing/params_spec.rb +63 -0
- data/spec/cli/ruby_shell.rb +99 -0
- data/spec/cli/shell_spec.rb +211 -0
- data/spec/fixtures/example_class_registry/base_class.rb +9 -0
- data/spec/fixtures/example_class_registry/classes/loaded_class.rb +9 -0
- data/spec/fixtures/example_class_registry/classes/name_mismatch.rb +9 -0
- data/spec/fixtures/example_class_registry/classes/no_module.rb +4 -0
- data/spec/fixtures/example_class_registry.rb +8 -0
- data/spec/git_spec.rb +58 -0
- data/spec/home_spec.rb +64 -0
- data/spec/metadata/authors/author_spec.rb +335 -0
- data/spec/metadata/authors_spec.rb +126 -0
- data/spec/metadata/description_spec.rb +74 -0
- data/spec/metadata/id_spec.rb +92 -0
- data/spec/metadata/references_spec.rb +100 -0
- data/spec/metadata/summary_spec.rb +74 -0
- data/spec/metadata/version_spec.rb +72 -0
- data/spec/params/mixin_spec.rb +484 -0
- data/spec/params/param_spec.rb +164 -0
- data/spec/params/types/boolean_spec.rb +56 -0
- data/spec/params/types/enum_spec.rb +94 -0
- data/spec/params/types/float_spec.rb +107 -0
- data/spec/params/types/integer_spec.rb +155 -0
- data/spec/params/types/numeric_spec.rb +138 -0
- data/spec/params/types/regexp_spec.rb +64 -0
- data/spec/params/types/string_spec.rb +174 -0
- data/spec/params/types/type_spec.rb +14 -0
- data/spec/params/types/uri_spec.rb +62 -0
- data/spec/spec_helper.rb +11 -0
- metadata +252 -0
|
@@ -0,0 +1,1114 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'ronin/core/cli/command_shell'
|
|
3
|
+
|
|
4
|
+
describe Ronin::Core::CLI::CommandShell do
|
|
5
|
+
describe ".commands" do
|
|
6
|
+
subject { shell_class }
|
|
7
|
+
|
|
8
|
+
context "when no commands have been defined in the shell class" do
|
|
9
|
+
module TestCommandShell
|
|
10
|
+
class ShellWithNoCommands < Ronin::Core::CLI::CommandShell
|
|
11
|
+
shell_name 'test'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
let(:shell_class) { TestCommandShell::ShellWithNoCommands }
|
|
16
|
+
|
|
17
|
+
it "must return a Hash only containing the help command" do
|
|
18
|
+
expect(subject.commands.keys).to eq(%w[help])
|
|
19
|
+
expect(subject.commands['help']).to be_kind_of(described_class::Command)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
context "when commands have been defined in the shell class" do
|
|
24
|
+
module TestCommandShell
|
|
25
|
+
class ShellWithCommands < Ronin::Core::CLI::CommandShell
|
|
26
|
+
shell_name 'test'
|
|
27
|
+
|
|
28
|
+
command :foo, summary: 'Foo command'
|
|
29
|
+
def foo
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
let(:shell_class) { TestCommandShell::ShellWithCommands }
|
|
35
|
+
|
|
36
|
+
it "must return the Hash of command names and Command classes" do
|
|
37
|
+
expect(subject.commands['foo']).to be_kind_of(described_class::Command)
|
|
38
|
+
expect(subject.commands['foo'].name).to eq(:foo)
|
|
39
|
+
expect(subject.commands['foo'].summary).to eq('Foo command')
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
context "but when the commands are defined in the superclass" do
|
|
44
|
+
module TestCommandShell
|
|
45
|
+
class ShellWithInheritedCommands < ShellWithCommands
|
|
46
|
+
shell_name 'test'
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
let(:shell_superclass) do
|
|
51
|
+
TestCommandShell::ShellWithCommands
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
let(:shell_class) do
|
|
55
|
+
TestCommandShell::ShellWithInheritedCommands
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "must return the commands defined in the superclass" do
|
|
59
|
+
expect(subject.commands['foo']).to be(shell_superclass.commands['foo'])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context "but additional commands are defined in the sub-class" do
|
|
63
|
+
module TestCommandShell
|
|
64
|
+
class ShellWithInheritedCommandsAndItsOwnCommands < ShellWithCommands
|
|
65
|
+
shell_name 'test'
|
|
66
|
+
command :bar, summary: 'Bar command'
|
|
67
|
+
def bar
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
let(:shell_class) do
|
|
73
|
+
TestCommandShell::ShellWithInheritedCommandsAndItsOwnCommands
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "must contain the commands defined in the subclass" do
|
|
77
|
+
expect(subject.commands['bar']).to be_kind_of(described_class::Command)
|
|
78
|
+
expect(subject.commands['bar'].name).to eq(:bar)
|
|
79
|
+
expect(subject.commands['bar'].summary).to eq('Bar command')
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "must also contain the commands defined in the superclass" do
|
|
83
|
+
expect(subject.commands['foo']).to be(shell_superclass.commands['foo'])
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "must not modify the superclass'es .commands" do
|
|
87
|
+
expect(shell_superclass.commands['foo']).to be_kind_of(described_class::Command)
|
|
88
|
+
expect(shell_superclass.commands['foo'].name).to eq(:foo)
|
|
89
|
+
expect(shell_superclass.commands['foo'].summary).to eq('Foo command')
|
|
90
|
+
|
|
91
|
+
expect(shell_superclass.commands['bar']).to be(nil)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
context "but the commands defined in the sub-class override those in the superclass" do
|
|
96
|
+
module TestCommandShell
|
|
97
|
+
class ShellThatOverridesInheritedCommands < ShellWithCommands
|
|
98
|
+
shell_name 'test'
|
|
99
|
+
command :foo, summary: 'Overrided foo command'
|
|
100
|
+
def foo
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
let(:shell_class) { TestCommandShell::ShellThatOverridesInheritedCommands }
|
|
106
|
+
|
|
107
|
+
it "must contain the commands overridden in the sub-class" do
|
|
108
|
+
expect(subject.commands['foo']).to_not be(shell_superclass.commands['foo'])
|
|
109
|
+
expect(subject.commands['foo'].summary).to eq('Overrided foo command')
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it "must not modify the superclass'es .commands" do
|
|
113
|
+
expect(shell_superclass.commands['foo']).to be_kind_of(described_class::Command)
|
|
114
|
+
expect(shell_superclass.commands['foo'].name).to eq(:foo)
|
|
115
|
+
expect(shell_superclass.commands['foo'].summary).to eq('Foo command')
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
describe ".parse_command" do
|
|
122
|
+
subject { described_class }
|
|
123
|
+
|
|
124
|
+
let(:command_name) { 'foo' }
|
|
125
|
+
|
|
126
|
+
context "when given a single command name" do
|
|
127
|
+
let(:line) { command_name.to_s }
|
|
128
|
+
|
|
129
|
+
it "must return the command name" do
|
|
130
|
+
expect(subject.parse_command(line)).to eq(
|
|
131
|
+
[command_name]
|
|
132
|
+
)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
context "when given a command name and additional arguments" do
|
|
137
|
+
let(:arg1) { "bar" }
|
|
138
|
+
let(:arg2) { "baz" }
|
|
139
|
+
let(:line) { "#{command_name} #{arg1} #{arg2}" }
|
|
140
|
+
|
|
141
|
+
it "must return the command name and an Array of arguments" do
|
|
142
|
+
expect(subject.parse_command(line)).to eq(
|
|
143
|
+
[command_name, arg1, arg2]
|
|
144
|
+
)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
context "but the arguments are in quotes" do
|
|
148
|
+
let(:line) { "#{command_name} \"#{arg1} #{arg2}\"" }
|
|
149
|
+
|
|
150
|
+
it "must keep quoted arguments together" do
|
|
151
|
+
expect(subject.parse_command(line)).to eq(
|
|
152
|
+
[command_name, "#{arg1} #{arg2}"]
|
|
153
|
+
)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
describe "#complete" do
|
|
160
|
+
module TestCommandShell
|
|
161
|
+
class ShellWithCompletions < Ronin::Core::CLI::CommandShell
|
|
162
|
+
shell_name 'test'
|
|
163
|
+
|
|
164
|
+
command :foo, summary: 'Foo command'
|
|
165
|
+
def foo
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
command :bar, completions: %w[arg1 arg2 foo], summary: 'Bar command'
|
|
169
|
+
def bar
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
command :baz, summary: 'Baz command'
|
|
173
|
+
def baz
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
let(:shell_class) { TestCommandShell::ShellWithCompletions }
|
|
179
|
+
subject { shell_class.new }
|
|
180
|
+
|
|
181
|
+
context "when the input is empty" do
|
|
182
|
+
let(:preposing) { '' }
|
|
183
|
+
let(:word) { '' }
|
|
184
|
+
|
|
185
|
+
it "must return all available command names" do
|
|
186
|
+
expect(subject.complete(word,preposing)).to eq(subject.class.commands.keys)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
context "when the input does not contain a space" do
|
|
191
|
+
let(:preposing) { '' }
|
|
192
|
+
let(:word) { 'ba' }
|
|
193
|
+
|
|
194
|
+
it "must return the matching command names" do
|
|
195
|
+
expect(subject.complete(word,preposing)).to eq(%w[bar baz])
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
context "when the input does contain a space" do
|
|
200
|
+
context "and the input starts with a known command" do
|
|
201
|
+
let(:command) { 'bar' }
|
|
202
|
+
let(:arg) { "arg" }
|
|
203
|
+
let(:preposing) { "#{command} " }
|
|
204
|
+
let(:word) { arg }
|
|
205
|
+
|
|
206
|
+
context "and the command defines an Array of completion values" do
|
|
207
|
+
it "must return the command's argument values that match the end of the input" do
|
|
208
|
+
expect(subject.complete(word,preposing)).to eq(
|
|
209
|
+
subject.class.commands[command].completions.select { |value|
|
|
210
|
+
value.start_with?(word)
|
|
211
|
+
}
|
|
212
|
+
)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
context "but the input contains multiple spaces" do
|
|
216
|
+
let(:preposing) { "#{command} bla bla " }
|
|
217
|
+
|
|
218
|
+
it "must still return the command's argument values that match the end of the input" do
|
|
219
|
+
expect(subject.complete(word,preposing)).to eq(
|
|
220
|
+
subject.class.commands[command].completions.select { |value|
|
|
221
|
+
value.start_with?(word)
|
|
222
|
+
}
|
|
223
|
+
)
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
context "and the command defines a completion method name" do
|
|
229
|
+
module TestCommandShell
|
|
230
|
+
class ShellWithCompletionMethod < Ronin::Core::CLI::CommandShell
|
|
231
|
+
shell_name 'test'
|
|
232
|
+
|
|
233
|
+
command :foo, summary: 'Foo command'
|
|
234
|
+
def foo
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
command :bar, summary: 'Bar command',
|
|
238
|
+
completions: :bar_completion
|
|
239
|
+
def bar
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def bar_completion(arg,preposing)
|
|
243
|
+
%w[
|
|
244
|
+
foo
|
|
245
|
+
arg-A
|
|
246
|
+
arg-B
|
|
247
|
+
bar
|
|
248
|
+
]
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
command :baz, summary: 'Baz command'
|
|
252
|
+
def baz
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
let(:shell_class) { TestCommandShell::ShellWithCompletionMethod }
|
|
258
|
+
|
|
259
|
+
it "must call the completion method with the argument and filter the results" do
|
|
260
|
+
completion_method = subject.class.commands[command].completions
|
|
261
|
+
|
|
262
|
+
expect(subject.complete(word,preposing)).to eq(
|
|
263
|
+
subject.send(completion_method,word,preposing).select { |value|
|
|
264
|
+
value.start_with?(word)
|
|
265
|
+
}
|
|
266
|
+
)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
context "but the completion method was not defined" do
|
|
270
|
+
module TestCommandShell
|
|
271
|
+
class ShellWithMissingCompletionMethod < Ronin::Core::CLI::CommandShell
|
|
272
|
+
shell_name 'test'
|
|
273
|
+
|
|
274
|
+
command :foo, summary: 'Foo command'
|
|
275
|
+
def foo
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
command :bar, summary: 'Bar command',
|
|
279
|
+
completions: :bar_completion
|
|
280
|
+
def bar
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
command :baz, summary: 'Baz command'
|
|
284
|
+
def baz
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
let(:shell_class) do
|
|
290
|
+
TestCommandShell::ShellWithMissingCompletionMethod
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
it "must call the completion method with the argument" do
|
|
294
|
+
expect {
|
|
295
|
+
subject.complete(word,preposing)
|
|
296
|
+
}.to raise_error(NotImplementedError,"#{subject.class}#bar_completion was not defined")
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
context "but the input does not start with a known command" do
|
|
303
|
+
let(:command) { 'does_not_exist' }
|
|
304
|
+
let(:arg) { "arg" }
|
|
305
|
+
let(:preposing) { "#{command} " }
|
|
306
|
+
let(:word) { arg }
|
|
307
|
+
|
|
308
|
+
it "must return nil" do
|
|
309
|
+
expect(subject.complete(word,preposing)).to be(nil)
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
context "when the input ends with a space" do
|
|
315
|
+
context "and the input starts with a known command" do
|
|
316
|
+
let(:command) { 'bar' }
|
|
317
|
+
|
|
318
|
+
let(:preposing) { "#{command} " }
|
|
319
|
+
let(:word) { '' }
|
|
320
|
+
|
|
321
|
+
it "must return all possible completion values for the command" do
|
|
322
|
+
expect(subject.complete(word,preposing)).to eq(
|
|
323
|
+
subject.class.commands[command].completions
|
|
324
|
+
)
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
context "but the input does not start with a known command" do
|
|
329
|
+
let(:command) { 'does_not_exist' }
|
|
330
|
+
|
|
331
|
+
let(:preposing) { '' }
|
|
332
|
+
let(:word) { command }
|
|
333
|
+
|
|
334
|
+
it "must return nil" do
|
|
335
|
+
expect(subject.complete(word,preposing)).to eq([])
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
module TestCommandShell
|
|
342
|
+
class TestCommandShell < Ronin::Core::CLI::CommandShell
|
|
343
|
+
shell_name 'test'
|
|
344
|
+
|
|
345
|
+
command :foo, summary: 'Foo command'
|
|
346
|
+
def foo
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
command :bar, summary: 'Bar command'
|
|
350
|
+
def bar
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
let(:shell_class) { TestCommandShell::TestCommandShell }
|
|
356
|
+
subject { shell_class.new }
|
|
357
|
+
|
|
358
|
+
describe "#exec" do
|
|
359
|
+
it "must call the underlying command method" do
|
|
360
|
+
expect(subject).to receive(:foo)
|
|
361
|
+
|
|
362
|
+
subject.exec('foo')
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
describe "#call" do
|
|
367
|
+
context "when the command exists" do
|
|
368
|
+
context "but the command does not accept any arguments" do
|
|
369
|
+
module TestCommandShell
|
|
370
|
+
class ShellWithCommandWithNoArgs < Ronin::Core::CLI::CommandShell
|
|
371
|
+
shell_name 'test'
|
|
372
|
+
|
|
373
|
+
command :cmd, summary: 'Example command'
|
|
374
|
+
def cmd
|
|
375
|
+
puts "#{__method__} called"
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
let(:shell_class) do
|
|
381
|
+
TestCommandShell::ShellWithCommandWithNoArgs
|
|
382
|
+
end
|
|
383
|
+
let(:name) { 'cmd' }
|
|
384
|
+
|
|
385
|
+
context "and no arguments are given" do
|
|
386
|
+
let(:args) { [] }
|
|
387
|
+
|
|
388
|
+
it "must call the command method and return true" do
|
|
389
|
+
expect {
|
|
390
|
+
expect(subject.call(name,*args)).to be(true)
|
|
391
|
+
}.to output("#{name} called#{$/}").to_stdout
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
context "and one argument is given" do
|
|
396
|
+
let(:arg) { "foo" }
|
|
397
|
+
let(:args) { [arg] }
|
|
398
|
+
|
|
399
|
+
it "must print an error and return false" do
|
|
400
|
+
expect {
|
|
401
|
+
expect(subject.call(name,*args)).to be(false)
|
|
402
|
+
}.to output("#{name}: too many arguments given#{$/}").to_stderr
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
context "and the command accepts an argument" do
|
|
408
|
+
module TestCommandShell
|
|
409
|
+
class ShellWithCommandWithArg < Ronin::Core::CLI::CommandShell
|
|
410
|
+
shell_name 'test'
|
|
411
|
+
|
|
412
|
+
command :cmd_with_arg, usage: 'ARG',
|
|
413
|
+
summary: 'Example command with arg'
|
|
414
|
+
def cmd_with_arg(arg)
|
|
415
|
+
puts "#{__method__} called: arg=#{arg}"
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
let(:shell_class) do
|
|
421
|
+
TestCommandShell::ShellWithCommandWithArg
|
|
422
|
+
end
|
|
423
|
+
let(:name) { 'cmd_with_arg' }
|
|
424
|
+
|
|
425
|
+
context "and no arguments are given" do
|
|
426
|
+
let(:args) { [] }
|
|
427
|
+
|
|
428
|
+
it "must print an error and return false" do
|
|
429
|
+
expect {
|
|
430
|
+
expect(subject.call(name,*args)).to be(false)
|
|
431
|
+
}.to output("#{name}: too few arguments given#{$/}").to_stderr
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
context "and one argument is given" do
|
|
436
|
+
let(:arg) { "foo" }
|
|
437
|
+
let(:args) { [arg] }
|
|
438
|
+
|
|
439
|
+
it "must call the command mehtod with the argument and return true" do
|
|
440
|
+
expect {
|
|
441
|
+
expect(subject.call(name,*args)).to be(true)
|
|
442
|
+
}.to output("#{name} called: arg=#{arg}#{$/}").to_stdout
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
context "and more than one arguments is given" do
|
|
447
|
+
let(:arg1) { "foo" }
|
|
448
|
+
let(:arg2) { "bar" }
|
|
449
|
+
let(:args) { [arg1, arg2] }
|
|
450
|
+
|
|
451
|
+
it "must print an error and return false" do
|
|
452
|
+
expect {
|
|
453
|
+
expect(subject.call(name,*args)).to be(false)
|
|
454
|
+
}.to output("#{name}: too many arguments given#{$/}").to_stderr
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
context "and the command accepts multiple arguments" do
|
|
460
|
+
module TestCommandShell
|
|
461
|
+
class ShellWithCommandWithMultipleArgs < Ronin::Core::CLI::CommandShell
|
|
462
|
+
shell_name 'test'
|
|
463
|
+
|
|
464
|
+
command :cmd_with_args, usage: 'ARG1 ARG2',
|
|
465
|
+
summary: 'Example command with multiple arg'
|
|
466
|
+
def cmd_with_args(arg1,arg2)
|
|
467
|
+
puts "#{__method__} called: arg1=#{arg1} arg2=#{arg2}"
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
let(:shell_class) do
|
|
473
|
+
TestCommandShell::ShellWithCommandWithMultipleArgs
|
|
474
|
+
end
|
|
475
|
+
let(:name) { 'cmd_with_args' }
|
|
476
|
+
|
|
477
|
+
context "and no arguments are given" do
|
|
478
|
+
let(:args) { [] }
|
|
479
|
+
|
|
480
|
+
it "must print an error and return false" do
|
|
481
|
+
expect {
|
|
482
|
+
expect(subject.call(name,*args)).to be(false)
|
|
483
|
+
}.to output("#{name}: too few arguments given#{$/}").to_stderr
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
context "and one argument is given" do
|
|
488
|
+
let(:arg) { "foo" }
|
|
489
|
+
let(:args) { [arg] }
|
|
490
|
+
|
|
491
|
+
it "must print an error and return false" do
|
|
492
|
+
expect {
|
|
493
|
+
expect(subject.call(name,*args)).to be(false)
|
|
494
|
+
}.to output("#{name}: too few arguments given#{$/}").to_stderr
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
context "and two arguments are given" do
|
|
499
|
+
let(:arg1) { "foo" }
|
|
500
|
+
let(:arg2) { "bar" }
|
|
501
|
+
let(:args) { [arg1, arg2] }
|
|
502
|
+
|
|
503
|
+
it "must call the command method and return true" do
|
|
504
|
+
expect {
|
|
505
|
+
expect(subject.call(name,*args)).to be(true)
|
|
506
|
+
}.to output("#{name} called: arg1=#{arg1} arg2=#{arg2}#{$/}").to_stdout
|
|
507
|
+
end
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
context "and more than two arguments is given" do
|
|
511
|
+
let(:arg1) { "foo" }
|
|
512
|
+
let(:arg2) { "bar" }
|
|
513
|
+
let(:arg3) { "baz" }
|
|
514
|
+
let(:args) { [arg1, arg2, arg3] }
|
|
515
|
+
|
|
516
|
+
it "must print an error and return false" do
|
|
517
|
+
expect {
|
|
518
|
+
expect(subject.call(name,*args)).to be(false)
|
|
519
|
+
}.to output("#{name}: too many arguments given#{$/}").to_stderr
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
context "and the command accepts an optional argument" do
|
|
525
|
+
module TestCommandShell
|
|
526
|
+
class ShellWithCommandWithOptionalArg < Ronin::Core::CLI::CommandShell
|
|
527
|
+
shell_name 'test'
|
|
528
|
+
|
|
529
|
+
command :cmd_with_opt_arg, usage: '[ARG]',
|
|
530
|
+
summary: 'Example command with optional arg'
|
|
531
|
+
def cmd_with_opt_arg(arg=nil)
|
|
532
|
+
puts "#{__method__} called: arg=#{arg}"
|
|
533
|
+
end
|
|
534
|
+
end
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
let(:shell_class) do
|
|
538
|
+
TestCommandShell::ShellWithCommandWithOptionalArg
|
|
539
|
+
end
|
|
540
|
+
let(:name) { 'cmd_with_opt_arg' }
|
|
541
|
+
|
|
542
|
+
context "and no arguments are given" do
|
|
543
|
+
let(:args) { [] }
|
|
544
|
+
|
|
545
|
+
it "must call the command mehtod and return true" do
|
|
546
|
+
expect {
|
|
547
|
+
expect(subject.call(name,*args)).to be(true)
|
|
548
|
+
}.to output("#{name} called: arg=#{$/}").to_stdout
|
|
549
|
+
end
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
context "and one argument is given" do
|
|
553
|
+
let(:arg) { "foo" }
|
|
554
|
+
let(:args) { [arg] }
|
|
555
|
+
|
|
556
|
+
it "must call the command mehtod with the argument and return true" do
|
|
557
|
+
expect {
|
|
558
|
+
expect(subject.call(name,*args)).to be(true)
|
|
559
|
+
}.to output("#{name} called: arg=#{arg}#{$/}").to_stdout
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
context "and more than one argument is given" do
|
|
564
|
+
let(:arg1) { "foo" }
|
|
565
|
+
let(:arg2) { "bar" }
|
|
566
|
+
let(:args) { [arg1, arg2] }
|
|
567
|
+
|
|
568
|
+
it "must print an error and return false" do
|
|
569
|
+
expect {
|
|
570
|
+
expect(subject.call(name,*args)).to be(false)
|
|
571
|
+
}.to output("#{name}: too many arguments given#{$/}").to_stderr
|
|
572
|
+
end
|
|
573
|
+
end
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
context "and the command accepts an argument and an optional argument" do
|
|
577
|
+
module TestCommandShell
|
|
578
|
+
class ShellWithCommandWithArgAndOptionalArg < Ronin::Core::CLI::CommandShell
|
|
579
|
+
shell_name 'test'
|
|
580
|
+
|
|
581
|
+
command :cmd_with_arg_and_opt_arg, usage: 'ARG1 [ARG2]',
|
|
582
|
+
summary: 'Example command with arg and optional arg'
|
|
583
|
+
def cmd_with_arg_and_opt_arg(arg1,arg2=nil)
|
|
584
|
+
puts "#{__method__} called: arg1=#{arg1} arg2=#{arg2}"
|
|
585
|
+
end
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
let(:shell_class) do
|
|
590
|
+
TestCommandShell::ShellWithCommandWithArgAndOptionalArg
|
|
591
|
+
end
|
|
592
|
+
let(:name) { 'cmd_with_arg_and_opt_arg' }
|
|
593
|
+
|
|
594
|
+
context "and no arguments are given" do
|
|
595
|
+
let(:args) { [] }
|
|
596
|
+
|
|
597
|
+
it "must print an error and return false" do
|
|
598
|
+
expect {
|
|
599
|
+
expect(subject.call(name,*args)).to be(false)
|
|
600
|
+
}.to output("#{name}: too few arguments given#{$/}").to_stderr
|
|
601
|
+
end
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
context "and one argument is given" do
|
|
605
|
+
let(:arg) { "foo" }
|
|
606
|
+
let(:args) { [arg] }
|
|
607
|
+
|
|
608
|
+
it "must call the command mehtod with the argument and return true" do
|
|
609
|
+
expect {
|
|
610
|
+
expect(subject.call(name,*args)).to be(true)
|
|
611
|
+
}.to output("#{name} called: arg1=#{arg} arg2=#{$/}").to_stdout
|
|
612
|
+
end
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
context "and two arguments are given" do
|
|
616
|
+
let(:arg1) { "foo" }
|
|
617
|
+
let(:arg2) { "bar" }
|
|
618
|
+
let(:args) { [arg1, arg2] }
|
|
619
|
+
|
|
620
|
+
it "must call the command mehtod with the argument and return true" do
|
|
621
|
+
expect {
|
|
622
|
+
expect(subject.call(name,*args)).to be(true)
|
|
623
|
+
}.to output("#{name} called: arg1=#{arg1} arg2=#{arg2}#{$/}").to_stdout
|
|
624
|
+
end
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
context "and more than two arguments are given" do
|
|
628
|
+
let(:arg1) { "foo" }
|
|
629
|
+
let(:arg2) { "bar" }
|
|
630
|
+
let(:arg3) { "baz" }
|
|
631
|
+
let(:args) { [arg1, arg2, arg3] }
|
|
632
|
+
|
|
633
|
+
it "must print an error and return false" do
|
|
634
|
+
expect {
|
|
635
|
+
expect(subject.call(name,*args)).to be(false)
|
|
636
|
+
}.to output("#{name}: too many arguments given#{$/}").to_stderr
|
|
637
|
+
end
|
|
638
|
+
end
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
context "and the command accepts a splat of arguments" do
|
|
642
|
+
module TestCommandShell
|
|
643
|
+
class ShellWithCommandWithSplatArgs < Ronin::Core::CLI::CommandShell
|
|
644
|
+
shell_name 'test'
|
|
645
|
+
|
|
646
|
+
command :cmd_with_splat_args, usage: '[ARGS...]',
|
|
647
|
+
summary: 'Example command with splat args'
|
|
648
|
+
def cmd_with_splat_args(*args)
|
|
649
|
+
puts "#{__method__} called: args=#{args.join(' ')}"
|
|
650
|
+
end
|
|
651
|
+
end
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
let(:shell_class) do
|
|
655
|
+
TestCommandShell::ShellWithCommandWithSplatArgs
|
|
656
|
+
end
|
|
657
|
+
let(:name) { 'cmd_with_splat_args' }
|
|
658
|
+
|
|
659
|
+
context "and no arguments are given" do
|
|
660
|
+
let(:args) { [] }
|
|
661
|
+
|
|
662
|
+
it "must call the command mehtod with no arguments and return true" do
|
|
663
|
+
expect {
|
|
664
|
+
expect(subject.call(name,*args)).to be(true)
|
|
665
|
+
}.to output("#{name} called: args=#{$/}").to_stdout
|
|
666
|
+
end
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
context "and one argument is given" do
|
|
670
|
+
let(:arg) { "foo" }
|
|
671
|
+
let(:args) { [arg] }
|
|
672
|
+
|
|
673
|
+
it "must call the command mehtod with the argument and return true" do
|
|
674
|
+
expect {
|
|
675
|
+
expect(subject.call(name,*args)).to be(true)
|
|
676
|
+
}.to output("#{name} called: args=#{arg}#{$/}").to_stdout
|
|
677
|
+
end
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
context "and multiple arguments are given" do
|
|
681
|
+
let(:arg1) { "foo" }
|
|
682
|
+
let(:arg2) { "bar" }
|
|
683
|
+
let(:args) { [arg1, arg2] }
|
|
684
|
+
|
|
685
|
+
it "must call the command mehtod with the argument and return true" do
|
|
686
|
+
expect {
|
|
687
|
+
expect(subject.call(name,*args)).to be(true)
|
|
688
|
+
}.to output("#{name} called: args=#{arg1} #{arg2}#{$/}").to_stdout
|
|
689
|
+
end
|
|
690
|
+
end
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
context "and the command accepts an argument and splat arguments" do
|
|
694
|
+
module TestCommandShell
|
|
695
|
+
class ShellWithCommandWithArgAndSplatArgs < Ronin::Core::CLI::CommandShell
|
|
696
|
+
shell_name 'test'
|
|
697
|
+
|
|
698
|
+
command :cmd_with_arg_and_splat_args, usage: 'ARG [ARGS...]',
|
|
699
|
+
summary: 'Example command with arg and splat args'
|
|
700
|
+
def cmd_with_arg_and_splat_args(arg,*args)
|
|
701
|
+
puts "#{__method__} called: arg=#{arg} args=#{args.join(' ')}"
|
|
702
|
+
end
|
|
703
|
+
end
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
let(:shell_class) do
|
|
707
|
+
TestCommandShell::ShellWithCommandWithArgAndSplatArgs
|
|
708
|
+
end
|
|
709
|
+
let(:name) { 'cmd_with_arg_and_splat_args' }
|
|
710
|
+
|
|
711
|
+
context "and no arguments are given" do
|
|
712
|
+
let(:args) { [] }
|
|
713
|
+
|
|
714
|
+
it "must print an error and return false" do
|
|
715
|
+
expect {
|
|
716
|
+
expect(subject.call(name,*args)).to be(false)
|
|
717
|
+
}.to output("#{name}: too few arguments given#{$/}").to_stderr
|
|
718
|
+
end
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
context "and one argument is given" do
|
|
722
|
+
let(:arg) { "foo" }
|
|
723
|
+
let(:args) { [arg] }
|
|
724
|
+
|
|
725
|
+
it "must call the command mehtod with the argument and return true" do
|
|
726
|
+
expect {
|
|
727
|
+
expect(subject.call(name,*args)).to be(true)
|
|
728
|
+
}.to output("#{name} called: arg=#{arg} args=#{$/}").to_stdout
|
|
729
|
+
end
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
context "and two arguments are given" do
|
|
733
|
+
let(:arg1) { "foo" }
|
|
734
|
+
let(:arg2) { "bar" }
|
|
735
|
+
let(:args) { [arg1, arg2] }
|
|
736
|
+
|
|
737
|
+
it "must call the command mehtod with the argument and return true" do
|
|
738
|
+
expect {
|
|
739
|
+
expect(subject.call(name,*args)).to be(true)
|
|
740
|
+
}.to output("#{name} called: arg=#{arg1} args=#{arg2}#{$/}").to_stdout
|
|
741
|
+
end
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
context "and more than two arguments are given" do
|
|
745
|
+
let(:arg1) { "foo" }
|
|
746
|
+
let(:arg2) { "bar" }
|
|
747
|
+
let(:arg3) { "baz" }
|
|
748
|
+
let(:args) { [arg1, arg2, arg3] }
|
|
749
|
+
|
|
750
|
+
it "must call the command mehtod with the argument and return true" do
|
|
751
|
+
expect {
|
|
752
|
+
expect(subject.call(name,*args)).to be(true)
|
|
753
|
+
}.to output("#{name} called: arg=#{arg1} args=#{arg2} #{arg3}#{$/}").to_stdout
|
|
754
|
+
end
|
|
755
|
+
end
|
|
756
|
+
end
|
|
757
|
+
|
|
758
|
+
context "and the command accepts an optional argument and splat arguments" do
|
|
759
|
+
module TestCommandShell
|
|
760
|
+
class ShellWithCommandWithOptionalArgAndSplatArgs < Ronin::Core::CLI::CommandShell
|
|
761
|
+
shell_name 'test'
|
|
762
|
+
|
|
763
|
+
command :cmd_with_opt_arg_and_splat_args, usage: '[ARG] [ARGS...]',
|
|
764
|
+
summary: 'Example command with arg and splat args'
|
|
765
|
+
def cmd_with_opt_arg_and_splat_args(arg=nil,*args)
|
|
766
|
+
puts "#{__method__} called: arg=#{arg} args=#{args.join(' ')}"
|
|
767
|
+
end
|
|
768
|
+
end
|
|
769
|
+
end
|
|
770
|
+
|
|
771
|
+
let(:shell_class) do
|
|
772
|
+
TestCommandShell::ShellWithCommandWithOptionalArgAndSplatArgs
|
|
773
|
+
end
|
|
774
|
+
let(:name) { 'cmd_with_opt_arg_and_splat_args' }
|
|
775
|
+
|
|
776
|
+
context "and no arguments are given" do
|
|
777
|
+
let(:args) { [] }
|
|
778
|
+
|
|
779
|
+
it "must call the command method with no arguments and return true" do
|
|
780
|
+
expect {
|
|
781
|
+
expect(subject.call(name,*args)).to be(true)
|
|
782
|
+
}.to output("#{name} called: arg= args=#{$/}").to_stdout
|
|
783
|
+
end
|
|
784
|
+
end
|
|
785
|
+
|
|
786
|
+
context "and one argument is given" do
|
|
787
|
+
let(:arg) { "foo" }
|
|
788
|
+
let(:args) { [arg] }
|
|
789
|
+
|
|
790
|
+
it "must call the command mehtod with the argument and return true" do
|
|
791
|
+
expect {
|
|
792
|
+
expect(subject.call(name,*args)).to be(true)
|
|
793
|
+
}.to output("#{name} called: arg=#{arg} args=#{$/}").to_stdout
|
|
794
|
+
end
|
|
795
|
+
end
|
|
796
|
+
|
|
797
|
+
context "and two arguments are given" do
|
|
798
|
+
let(:arg1) { "foo" }
|
|
799
|
+
let(:arg2) { "bar" }
|
|
800
|
+
let(:args) { [arg1, arg2] }
|
|
801
|
+
|
|
802
|
+
it "must call the command mehtod with the argument and return true" do
|
|
803
|
+
expect {
|
|
804
|
+
expect(subject.call(name,*args)).to be(true)
|
|
805
|
+
}.to output("#{name} called: arg=#{arg1} args=#{arg2}#{$/}").to_stdout
|
|
806
|
+
end
|
|
807
|
+
end
|
|
808
|
+
|
|
809
|
+
context "and more than two arguments are given" do
|
|
810
|
+
let(:arg1) { "foo" }
|
|
811
|
+
let(:arg2) { "bar" }
|
|
812
|
+
let(:arg3) { "baz" }
|
|
813
|
+
let(:args) { [arg1, arg2, arg3] }
|
|
814
|
+
|
|
815
|
+
it "must call the command mehtod with the argument and return true" do
|
|
816
|
+
expect {
|
|
817
|
+
expect(subject.call(name,*args)).to be(true)
|
|
818
|
+
}.to output("#{name} called: arg=#{arg1} args=#{arg2} #{arg3}#{$/}").to_stdout
|
|
819
|
+
end
|
|
820
|
+
end
|
|
821
|
+
end
|
|
822
|
+
|
|
823
|
+
context "but the command method raises an exception" do
|
|
824
|
+
module TestCommandShell
|
|
825
|
+
class ShellWithCommandThatRaisesAnException < Ronin::Core::CLI::CommandShell
|
|
826
|
+
|
|
827
|
+
command 'cmd', summary: 'Test command'
|
|
828
|
+
def cmd
|
|
829
|
+
raise("error!")
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
end
|
|
833
|
+
end
|
|
834
|
+
|
|
835
|
+
let(:shell_class) do
|
|
836
|
+
TestCommandShell::ShellWithCommandThatRaisesAnException
|
|
837
|
+
end
|
|
838
|
+
let(:name) { 'cmd' }
|
|
839
|
+
|
|
840
|
+
it "must print an error message and return false" do
|
|
841
|
+
expect {
|
|
842
|
+
expect(subject.call(name)).to be(false)
|
|
843
|
+
}.to output(/an unhandled exception occurred in the #{name} command/).to_stderr
|
|
844
|
+
end
|
|
845
|
+
end
|
|
846
|
+
end
|
|
847
|
+
|
|
848
|
+
context "when the command does not exist" do
|
|
849
|
+
let(:name) { "does_not_exist" }
|
|
850
|
+
|
|
851
|
+
it "must print an error message and return false" do
|
|
852
|
+
expect {
|
|
853
|
+
expect(subject.call(name)).to be(false)
|
|
854
|
+
}.to output("unknown command: #{name}#{$/}").to_stderr
|
|
855
|
+
end
|
|
856
|
+
end
|
|
857
|
+
|
|
858
|
+
context "when the command was defined but no method is defined" do
|
|
859
|
+
module TestCommandShell
|
|
860
|
+
class ShellWithCommandButNoMethod < Ronin::Core::CLI::CommandShell
|
|
861
|
+
|
|
862
|
+
command 'cmd', summary: 'Test command'
|
|
863
|
+
|
|
864
|
+
end
|
|
865
|
+
end
|
|
866
|
+
|
|
867
|
+
let(:shell_class) do
|
|
868
|
+
TestCommandShell::ShellWithCommandButNoMethod
|
|
869
|
+
end
|
|
870
|
+
let(:name) { 'cmd' }
|
|
871
|
+
|
|
872
|
+
it "must raise NotImplementedError" do
|
|
873
|
+
expect {
|
|
874
|
+
subject.call(name)
|
|
875
|
+
}.to raise_error(NotImplementedError,"#{shell_class}##{name} was not defined for the #{name.inspect} command")
|
|
876
|
+
end
|
|
877
|
+
end
|
|
878
|
+
end
|
|
879
|
+
|
|
880
|
+
describe "#command_missing" do
|
|
881
|
+
let(:name) { "foo" }
|
|
882
|
+
|
|
883
|
+
it "must print an error message and return false" do
|
|
884
|
+
expect {
|
|
885
|
+
expect(subject.command_missing(name)).to be(false)
|
|
886
|
+
}.to output("unknown command: #{name}#{$/}").to_stderr
|
|
887
|
+
end
|
|
888
|
+
end
|
|
889
|
+
|
|
890
|
+
describe "#command_not_found" do
|
|
891
|
+
let(:name) { "foo" }
|
|
892
|
+
|
|
893
|
+
it "must print an error message and return false" do
|
|
894
|
+
expect {
|
|
895
|
+
subject.command_not_found(name)
|
|
896
|
+
}.to output("unknown command: #{name}#{$/}").to_stderr
|
|
897
|
+
end
|
|
898
|
+
end
|
|
899
|
+
|
|
900
|
+
describe "#help" do
|
|
901
|
+
module TestCommandShell
|
|
902
|
+
class ShellWithCommandsWithoutUsages < Ronin::Core::CLI::CommandShell
|
|
903
|
+
shell_name 'test'
|
|
904
|
+
|
|
905
|
+
command :foo, summary: 'Foo command'
|
|
906
|
+
def foo
|
|
907
|
+
end
|
|
908
|
+
|
|
909
|
+
command :bar, summary: 'Bar command'
|
|
910
|
+
def bar
|
|
911
|
+
end
|
|
912
|
+
end
|
|
913
|
+
|
|
914
|
+
class ShellWithCommandsWithCustomUsages < Ronin::Core::CLI::CommandShell
|
|
915
|
+
shell_name 'test'
|
|
916
|
+
|
|
917
|
+
command :foo, usage: 'ARG',
|
|
918
|
+
summary: 'Foo command'
|
|
919
|
+
def foo(arg)
|
|
920
|
+
end
|
|
921
|
+
|
|
922
|
+
command :bar, usage: 'ARG1 [ARG2]',
|
|
923
|
+
summary: 'Bar command'
|
|
924
|
+
def bar(arg1,arg2=nil)
|
|
925
|
+
end
|
|
926
|
+
end
|
|
927
|
+
end
|
|
928
|
+
|
|
929
|
+
context "when called with no arguments" do
|
|
930
|
+
context "but the shell has no commands" do
|
|
931
|
+
let(:shell_class) do
|
|
932
|
+
TestCommandShell::ShellWithNoCommands
|
|
933
|
+
end
|
|
934
|
+
|
|
935
|
+
it "must list the help command" do
|
|
936
|
+
expect {
|
|
937
|
+
subject.help
|
|
938
|
+
}.to output(
|
|
939
|
+
[
|
|
940
|
+
" help [COMMAND]\tPrints the list of commands or additional help",
|
|
941
|
+
''
|
|
942
|
+
].join($/)
|
|
943
|
+
).to_stdout
|
|
944
|
+
end
|
|
945
|
+
end
|
|
946
|
+
|
|
947
|
+
context "but the commands do not have usage strings" do
|
|
948
|
+
let(:shell_class) do
|
|
949
|
+
TestCommandShell::ShellWithCommandsWithoutUsages
|
|
950
|
+
end
|
|
951
|
+
|
|
952
|
+
it "must print the command names and summaries in two columns" do
|
|
953
|
+
expect {
|
|
954
|
+
subject.help
|
|
955
|
+
}.to output(
|
|
956
|
+
[
|
|
957
|
+
" help [COMMAND]\tPrints the list of commands or additional help",
|
|
958
|
+
" foo \tFoo command",
|
|
959
|
+
" bar \tBar command",
|
|
960
|
+
''
|
|
961
|
+
].join($/)
|
|
962
|
+
).to_stdout
|
|
963
|
+
end
|
|
964
|
+
end
|
|
965
|
+
|
|
966
|
+
context "when the commands defines custom usages" do
|
|
967
|
+
let(:shell_class) do
|
|
968
|
+
TestCommandShell::ShellWithCommandsWithCustomUsages
|
|
969
|
+
end
|
|
970
|
+
|
|
971
|
+
it "must print the custom usages next to the command names with extra right-padding" do
|
|
972
|
+
expect {
|
|
973
|
+
subject.help
|
|
974
|
+
}.to output(
|
|
975
|
+
[
|
|
976
|
+
" help [COMMAND] \tPrints the list of commands or additional help",
|
|
977
|
+
" foo ARG \tFoo command",
|
|
978
|
+
" bar ARG1 [ARG2]\tBar command",
|
|
979
|
+
''
|
|
980
|
+
].join($/)
|
|
981
|
+
).to_stdout
|
|
982
|
+
end
|
|
983
|
+
end
|
|
984
|
+
end
|
|
985
|
+
|
|
986
|
+
context "when called with a command name" do
|
|
987
|
+
let(:command) { 'bar' }
|
|
988
|
+
|
|
989
|
+
context "but the command does not have a usage string" do
|
|
990
|
+
context "nor does the command have any additional help output" do
|
|
991
|
+
let(:shell_class) do
|
|
992
|
+
TestCommandShell::ShellWithCommandsWithoutUsages
|
|
993
|
+
end
|
|
994
|
+
|
|
995
|
+
it "must print the command's name and summary" do
|
|
996
|
+
expect {
|
|
997
|
+
subject.help(command)
|
|
998
|
+
}.to output(
|
|
999
|
+
[
|
|
1000
|
+
"usage: bar",
|
|
1001
|
+
'',
|
|
1002
|
+
'Bar command',
|
|
1003
|
+
''
|
|
1004
|
+
].join($/)
|
|
1005
|
+
).to_stdout
|
|
1006
|
+
end
|
|
1007
|
+
end
|
|
1008
|
+
|
|
1009
|
+
context "and the command has additional help output" do
|
|
1010
|
+
module TestCommandShell
|
|
1011
|
+
class ShellWithCommandsWithNoUsagesButWithHelp < Ronin::Core::CLI::CommandShell
|
|
1012
|
+
shell_name 'test'
|
|
1013
|
+
|
|
1014
|
+
command :foo, summary: 'Foo command',
|
|
1015
|
+
help: 'The foo command that does things.'
|
|
1016
|
+
def foo
|
|
1017
|
+
end
|
|
1018
|
+
|
|
1019
|
+
command :bar, summary: 'Bar command',
|
|
1020
|
+
help: 'The bar command that does things.'
|
|
1021
|
+
def bar
|
|
1022
|
+
end
|
|
1023
|
+
end
|
|
1024
|
+
end
|
|
1025
|
+
|
|
1026
|
+
let(:shell_class) do
|
|
1027
|
+
TestCommandShell::ShellWithCommandsWithNoUsagesButWithHelp
|
|
1028
|
+
end
|
|
1029
|
+
|
|
1030
|
+
it "must print the command name and additional help output" do
|
|
1031
|
+
expect {
|
|
1032
|
+
subject.help(command)
|
|
1033
|
+
}.to output(
|
|
1034
|
+
[
|
|
1035
|
+
"usage: bar",
|
|
1036
|
+
'',
|
|
1037
|
+
'The bar command that does things.',
|
|
1038
|
+
''
|
|
1039
|
+
].join($/)
|
|
1040
|
+
).to_stdout
|
|
1041
|
+
end
|
|
1042
|
+
end
|
|
1043
|
+
end
|
|
1044
|
+
|
|
1045
|
+
context "and the command does have a usage string" do
|
|
1046
|
+
context "but the command does not have any additional help output" do
|
|
1047
|
+
let(:shell_class) do
|
|
1048
|
+
TestCommandShell::ShellWithCommandsWithCustomUsages
|
|
1049
|
+
end
|
|
1050
|
+
|
|
1051
|
+
it "must print the command name, usage, and summary" do
|
|
1052
|
+
expect {
|
|
1053
|
+
subject.help(command)
|
|
1054
|
+
}.to output(
|
|
1055
|
+
[
|
|
1056
|
+
"usage: bar ARG1 [ARG2]",
|
|
1057
|
+
'',
|
|
1058
|
+
'Bar command',
|
|
1059
|
+
''
|
|
1060
|
+
].join($/)
|
|
1061
|
+
).to_stdout
|
|
1062
|
+
end
|
|
1063
|
+
end
|
|
1064
|
+
|
|
1065
|
+
context "and the command does have any additional help output" do
|
|
1066
|
+
module TestCommandShell
|
|
1067
|
+
class ShellWithCommandsWithUsagesButWithHelp < Ronin::Core::CLI::CommandShell
|
|
1068
|
+
shell_name 'test'
|
|
1069
|
+
|
|
1070
|
+
command :foo, usage: 'ARG',
|
|
1071
|
+
summary: 'Foo command',
|
|
1072
|
+
help: 'The foo command that does things.'
|
|
1073
|
+
def foo(arg)
|
|
1074
|
+
end
|
|
1075
|
+
|
|
1076
|
+
command :bar, usage: 'ARG1 [ARG2]',
|
|
1077
|
+
summary: 'Bar command',
|
|
1078
|
+
help: 'The bar command that does things.'
|
|
1079
|
+
def bar(arg1,arg2=nil)
|
|
1080
|
+
end
|
|
1081
|
+
end
|
|
1082
|
+
end
|
|
1083
|
+
|
|
1084
|
+
let(:shell_class) do
|
|
1085
|
+
TestCommandShell::ShellWithCommandsWithUsagesButWithHelp
|
|
1086
|
+
end
|
|
1087
|
+
|
|
1088
|
+
it "must print the command name, usage, and additional help output" do
|
|
1089
|
+
expect {
|
|
1090
|
+
subject.help(command)
|
|
1091
|
+
}.to output(
|
|
1092
|
+
[
|
|
1093
|
+
"usage: bar ARG1 [ARG2]",
|
|
1094
|
+
'',
|
|
1095
|
+
'The bar command that does things.',
|
|
1096
|
+
''
|
|
1097
|
+
].join($/)
|
|
1098
|
+
).to_stdout
|
|
1099
|
+
end
|
|
1100
|
+
end
|
|
1101
|
+
end
|
|
1102
|
+
|
|
1103
|
+
context "but the command name isn't a defined command" do
|
|
1104
|
+
let(:command) { 'does_not_exist' }
|
|
1105
|
+
|
|
1106
|
+
it "must print an 'unknown command' error" do
|
|
1107
|
+
expect {
|
|
1108
|
+
subject.help(command)
|
|
1109
|
+
}.to output("help: unknown command: #{command}#{$/}").to_stderr
|
|
1110
|
+
end
|
|
1111
|
+
end
|
|
1112
|
+
end
|
|
1113
|
+
end
|
|
1114
|
+
end
|