caliph 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/caliph.rb +4 -0
- data/lib/caliph/command-chain.rb +58 -0
- data/lib/caliph/command-line-dsl.rb +11 -0
- data/lib/caliph/command-line.rb +185 -0
- data/lib/caliph/command-run-result.rb +129 -0
- data/lib/caliph/define-op.rb +29 -0
- data/lib/caliph/shell-escaped.rb +25 -0
- data/lib/caliph/testing/mock-command-line.rb +67 -0
- data/lib/caliph/testing/record-commands.rb +38 -0
- data/spec/command-chain.rb +9 -0
- data/spec/command-line-dsl.rb +48 -0
- data/spec/command-line.rb +135 -0
- data/spec_help/gem_test_suite.rb +17 -0
- data/spec_help/spec_helper.rb +11 -0
- metadata +67 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2b0f24364e478c354208e04a309d201339ebaa57
|
4
|
+
data.tar.gz: 1de70f9e8bb89ee483b7d28ffbcbb76d10428687
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 94590b96bf71222807e9b9c96c3a5ea52cd89a826191169608d02603879edb3bc02948bea3f92e97fdf0366baedcf1ad1403c0a6248f857ee79051ffaa24de13
|
7
|
+
data.tar.gz: 255d5824af358a59fdfb92b8798bd62015f81cfe9f242d51e9f1ac9c0318c50bfa9ae20dd9bce372567eea5775927db1e48c947daabbd4f0cd84f60cf8c80e6f
|
data/lib/caliph.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'caliph/define-op'
|
2
|
+
|
3
|
+
module Caliph
|
4
|
+
class CommandChain < CommandLine
|
5
|
+
include DefineOp
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@commands = []
|
9
|
+
@command_environment = {}
|
10
|
+
super(nil)
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :commands
|
14
|
+
|
15
|
+
def add(cmd)
|
16
|
+
yield cmd if block_given?
|
17
|
+
@commands << cmd
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
#Honestly this is sub-optimal - biggest driver for considering the
|
22
|
+
#mini-shell approach here.
|
23
|
+
def command_environment
|
24
|
+
@command_environment = @commands.reverse.inject(@command_environment) do |env, command|
|
25
|
+
env.merge(command.command_environment)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def name
|
30
|
+
@name || @commands.last.name
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class WrappingChain < CommandChain
|
35
|
+
define_op('-')
|
36
|
+
|
37
|
+
def command
|
38
|
+
@commands.map{|cmd| cmd.command}.join(" -- ")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class PrereqChain < CommandChain
|
43
|
+
define_op('&')
|
44
|
+
|
45
|
+
def command
|
46
|
+
@commands.map{|cmd| cmd.command}.join(" && ")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class PipelineChain < CommandChain
|
51
|
+
define_op('|')
|
52
|
+
|
53
|
+
def command
|
54
|
+
@commands.map{|cmd| cmd.command}.join(" | ")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'caliph/define-op'
|
2
|
+
require 'caliph/command-run-result'
|
3
|
+
|
4
|
+
module Caliph
|
5
|
+
class CommandLine
|
6
|
+
include DefineOp
|
7
|
+
|
8
|
+
class << self
|
9
|
+
attr_accessor :output_stream
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(executable, *options)
|
13
|
+
@output_stream = self.class.output_stream || $stderr
|
14
|
+
@executable = executable.to_s
|
15
|
+
@options = options
|
16
|
+
@redirections = []
|
17
|
+
@env = {}
|
18
|
+
yield self if block_given?
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_accessor :name, :executable, :options, :env, :output_stream
|
22
|
+
attr_reader :redirections
|
23
|
+
|
24
|
+
alias_method :command_environment, :env
|
25
|
+
|
26
|
+
def set_env(name, value)
|
27
|
+
command_environment[name] = value
|
28
|
+
return self
|
29
|
+
end
|
30
|
+
|
31
|
+
def verbose
|
32
|
+
#::Rake.verbose && ::Rake.verbose != ::Rake::FileUtilsExt::DEFAULT
|
33
|
+
end
|
34
|
+
|
35
|
+
def name
|
36
|
+
@name || executable
|
37
|
+
end
|
38
|
+
|
39
|
+
# The command as a string, including arguments and options
|
40
|
+
def command
|
41
|
+
([executable] + options_composition + @redirections).join(" ")
|
42
|
+
end
|
43
|
+
|
44
|
+
# The command as a string, including arguments and options, plus prefixed
|
45
|
+
# environment variables.
|
46
|
+
def string_format
|
47
|
+
(command_environment.map do |key, value|
|
48
|
+
[key, value].join("=")
|
49
|
+
end + [command]).join(" ")
|
50
|
+
end
|
51
|
+
|
52
|
+
def options_composition
|
53
|
+
options
|
54
|
+
end
|
55
|
+
|
56
|
+
def redirect_to(stream, path)
|
57
|
+
@redirections << "#{stream}>#{path}"
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def redirect_from(path, stream)
|
62
|
+
@redirections << "#{stream}<#{path}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def copy_stream_to(from, to)
|
66
|
+
@redirections << "#{from}>&#{to}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def redirect_stdout(path)
|
70
|
+
redirect_to(1, path)
|
71
|
+
end
|
72
|
+
|
73
|
+
def redirect_stderr(path)
|
74
|
+
redirect_to(2, path)
|
75
|
+
end
|
76
|
+
|
77
|
+
def redirect_stdin(path)
|
78
|
+
redirect_from(path, 0)
|
79
|
+
end
|
80
|
+
|
81
|
+
def redirect_both(path)
|
82
|
+
redirect_stdout(path).redirect_stderr(path)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Run the command, wait for termination, and collect the results.
|
86
|
+
# Returns an instance of CommandRunResult that contains the output
|
87
|
+
# and exit code of the command.
|
88
|
+
#
|
89
|
+
# This version adds some information to STDOUT to document that the
|
90
|
+
# command is running. For a terser version, call #execute directly
|
91
|
+
def run
|
92
|
+
report string_format + " ", false
|
93
|
+
result = execute
|
94
|
+
report "=> #{result.exit_code}"
|
95
|
+
report result.format_streams if verbose
|
96
|
+
return result
|
97
|
+
ensure
|
98
|
+
report "" if verbose
|
99
|
+
end
|
100
|
+
|
101
|
+
# Fork a new process for the command, then terminate our process.
|
102
|
+
def run_as_replacement
|
103
|
+
output_stream.puts "Ceding execution to: "
|
104
|
+
output_stream.puts string_format
|
105
|
+
Process.exec(command_environment, command)
|
106
|
+
end
|
107
|
+
alias replace_us run_as_replacement
|
108
|
+
|
109
|
+
# Run the command in the background. The command can survive the caller.
|
110
|
+
def run_detached
|
111
|
+
pid, out, err = spawn_process
|
112
|
+
Process.detach(pid)
|
113
|
+
return pid, out, err
|
114
|
+
end
|
115
|
+
alias spin_off run_detached
|
116
|
+
|
117
|
+
# Run the command, wait for termination, and collect the results.
|
118
|
+
# Returns an instance of CommandRunResult that contains the output
|
119
|
+
# and exit code of the command.
|
120
|
+
#
|
121
|
+
def execute
|
122
|
+
collect_result(*spawn_process)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Run the command in parallel with the parent process - will kill it if it
|
126
|
+
# outlasts us
|
127
|
+
def run_in_background
|
128
|
+
pid, out, err = spawn_process
|
129
|
+
Process.detach(pid)
|
130
|
+
at_exit do
|
131
|
+
kill_process(pid)
|
132
|
+
end
|
133
|
+
return pid, out, err
|
134
|
+
end
|
135
|
+
alias background run_in_background
|
136
|
+
|
137
|
+
# Given a process ID for a running command and a pair of stdout/stdin
|
138
|
+
# streams, records the results of the command and returns them in a
|
139
|
+
# CommandRunResult instance.
|
140
|
+
def collect_result(pid, host_stdout, host_stderr)
|
141
|
+
result = CommandRunResult.new(pid, self)
|
142
|
+
result.streams = {1 => host_stdout, 2 => host_stderr}
|
143
|
+
result.wait
|
144
|
+
return result
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
def kill_process(pid)
|
149
|
+
Process.kill("INT", pid)
|
150
|
+
end
|
151
|
+
|
152
|
+
def complete(pid, out, err)
|
153
|
+
kill_process(pid)
|
154
|
+
collect_result(pid, out, err)
|
155
|
+
end
|
156
|
+
|
157
|
+
def report(message, newline=true)
|
158
|
+
output_stream.print(message + (newline ? "\n" : ""))
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
def succeeds?
|
163
|
+
run.succeeded?
|
164
|
+
end
|
165
|
+
|
166
|
+
def must_succeed!
|
167
|
+
run.must_succeed!
|
168
|
+
end
|
169
|
+
|
170
|
+
def spawn_process
|
171
|
+
host_stdout, cmd_stdout = IO.pipe
|
172
|
+
host_stderr, cmd_stderr = IO.pipe
|
173
|
+
|
174
|
+
pid = Process.spawn(command_environment, command, :out => cmd_stdout, :err => cmd_stderr)
|
175
|
+
cmd_stdout.close
|
176
|
+
cmd_stderr.close
|
177
|
+
|
178
|
+
return pid, host_stdout, host_stderr
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module Caliph
|
2
|
+
class CommandRunResult
|
3
|
+
def initialize(pid, command)
|
4
|
+
@command = command
|
5
|
+
@pid = pid
|
6
|
+
|
7
|
+
#####
|
8
|
+
@process_status = nil
|
9
|
+
@streams = {}
|
10
|
+
@consume_timeout = nil
|
11
|
+
end
|
12
|
+
attr_reader :process_status, :pid
|
13
|
+
attr_accessor :consume_timeout, :streams
|
14
|
+
|
15
|
+
def stdout
|
16
|
+
@streams[1]
|
17
|
+
end
|
18
|
+
|
19
|
+
def stderr
|
20
|
+
@streams[2]
|
21
|
+
end
|
22
|
+
|
23
|
+
def exit_code
|
24
|
+
@process_status.exitstatus
|
25
|
+
end
|
26
|
+
alias exit_status exit_code
|
27
|
+
|
28
|
+
def succeeded?
|
29
|
+
must_succeed!
|
30
|
+
return true
|
31
|
+
rescue
|
32
|
+
return false
|
33
|
+
end
|
34
|
+
|
35
|
+
def format_streams
|
36
|
+
"stdout:#{stdout.nil? || stdout.empty? ? "[empty]\n" : "\n#{stdout}"}" +
|
37
|
+
"stderr:#{stderr.nil? || stderr.empty? ? "[empty]\n" : "\n#{stderr}"}---"
|
38
|
+
end
|
39
|
+
|
40
|
+
def must_succeed!
|
41
|
+
case exit_code
|
42
|
+
when 0
|
43
|
+
return exit_code
|
44
|
+
else
|
45
|
+
fail "Command #{@command.inspect} failed with exit status #{exit_code}: \n#{format_streams}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def wait
|
50
|
+
@accumulators = {}
|
51
|
+
waits = {}
|
52
|
+
@buffered_echo = []
|
53
|
+
|
54
|
+
ioes = streams.values
|
55
|
+
ioes.each do |io|
|
56
|
+
@accumulators[io] = []
|
57
|
+
waits[io] = 3
|
58
|
+
end
|
59
|
+
begin_echoing = Time.now + (@consume_timeout || 3)
|
60
|
+
|
61
|
+
@live_ioes = ioes.dup
|
62
|
+
|
63
|
+
until @live_ioes.empty? do
|
64
|
+
newpid, @process_status = Process.waitpid2(pid, Process::WNOHANG)
|
65
|
+
|
66
|
+
unless @process_status.nil?
|
67
|
+
consume_buffers(@live_ioes)
|
68
|
+
break
|
69
|
+
end
|
70
|
+
|
71
|
+
timeout = 0
|
72
|
+
|
73
|
+
if !@buffered_echo.nil?
|
74
|
+
timeout = begin_echoing - Time.now
|
75
|
+
if timeout < 0
|
76
|
+
@command.report ""
|
77
|
+
@command.report "Long running command output:"
|
78
|
+
@command.report @buffered_echo.join
|
79
|
+
@buffered_echo = nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
if timeout > 0
|
84
|
+
result = IO::select(@live_ioes, [], @live_ioes, timeout)
|
85
|
+
else
|
86
|
+
result = IO::select(@live_ioes, [], @live_ioes, 1)
|
87
|
+
end
|
88
|
+
|
89
|
+
unless result.nil? #timeout
|
90
|
+
readable, _writeable, errored = *result
|
91
|
+
unless errored.empty?
|
92
|
+
raise "Error on IO: #{errored.inspect}"
|
93
|
+
end
|
94
|
+
|
95
|
+
consume_buffers(readable)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
if @process_status.nil?
|
100
|
+
newpid, @process_status = Process.waitpid2(pid)
|
101
|
+
end
|
102
|
+
|
103
|
+
ioes.each do |io|
|
104
|
+
io.close
|
105
|
+
end
|
106
|
+
@streams = Hash[ioes.each_with_index.map{|io, index| [index + 1, @accumulators[io].join]}]
|
107
|
+
end
|
108
|
+
|
109
|
+
def consume_buffers(readable)
|
110
|
+
if not(readable.nil? or readable.empty?)
|
111
|
+
readable.each do |io|
|
112
|
+
begin
|
113
|
+
while chunk = io.read_nonblock(4096)
|
114
|
+
if @buffered_echo.nil?
|
115
|
+
@command.report chunk, false
|
116
|
+
else
|
117
|
+
@buffered_echo << chunk
|
118
|
+
end
|
119
|
+
@accumulators[io] << chunk
|
120
|
+
end
|
121
|
+
rescue IO::WaitReadable => ex
|
122
|
+
rescue EOFError => ex
|
123
|
+
@live_ioes.delete(io)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Caliph
|
2
|
+
module DefineOp
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def define_chain_op(opname, klass)
|
9
|
+
define_method(opname) do |other|
|
10
|
+
unless CommandLine === other
|
11
|
+
other = CommandLine.new(*[*other])
|
12
|
+
end
|
13
|
+
chain = nil
|
14
|
+
if klass === self
|
15
|
+
chain = self
|
16
|
+
else
|
17
|
+
chain = klass.new
|
18
|
+
chain.add(self)
|
19
|
+
end
|
20
|
+
chain.add(other)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def define_op(opname)
|
25
|
+
CommandLine.define_chain_op(opname, self)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'caliph/command-line'
|
2
|
+
|
3
|
+
module Caliph
|
4
|
+
class ShellEscaped < CommandLine
|
5
|
+
def initialize(cmd)
|
6
|
+
@escaped = cmd
|
7
|
+
end
|
8
|
+
|
9
|
+
def command
|
10
|
+
"'" + @escaped.string_format.gsub(/'/,"\'") + "'"
|
11
|
+
end
|
12
|
+
|
13
|
+
def command_environment
|
14
|
+
{}
|
15
|
+
end
|
16
|
+
|
17
|
+
def name
|
18
|
+
@name || @escaped.name
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
command
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'caliph/command-line'
|
2
|
+
|
3
|
+
module Caliph
|
4
|
+
class MockCommandResult < CommandRunResult
|
5
|
+
def self.create(*args)
|
6
|
+
if args.length == 1
|
7
|
+
args = [args[0], {1 => ""}]
|
8
|
+
end
|
9
|
+
|
10
|
+
if String == args[1]
|
11
|
+
args[1] = {1 => args[1]}
|
12
|
+
end
|
13
|
+
|
14
|
+
return self.new(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(code, streams)
|
18
|
+
@streams = streams
|
19
|
+
@exit_code = code
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :exit_code, :streams
|
23
|
+
|
24
|
+
alias exit_status exit_code
|
25
|
+
end
|
26
|
+
|
27
|
+
class CommandLine
|
28
|
+
def self.execute(*args)
|
29
|
+
fail "Command line executed in specs without 'expect_command' or 'expect_some_commands'"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module CommandLineExampleGroup
|
34
|
+
include CommandLineDSL
|
35
|
+
module MockingExecute
|
36
|
+
def execute
|
37
|
+
Caliph::CommandLine.execute(command)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def self.included(group)
|
43
|
+
group.before :each do
|
44
|
+
@original_execute = Caliph::CommandLine.instance_method(:execute)
|
45
|
+
unless MockingExecute > Caliph::CommandLine
|
46
|
+
Caliph::CommandLine.send(:include, MockingExecute)
|
47
|
+
end
|
48
|
+
Caliph::CommandLine.send(:remove_method, :execute)
|
49
|
+
end
|
50
|
+
|
51
|
+
group.after :each do
|
52
|
+
Caliph::CommandLine.send(:define_method, :execute, @original_execute)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
#Registers indifference as to exactly what commands get called
|
57
|
+
def expect_some_commands
|
58
|
+
Caliph::CommandLine.should_receive(:execute).any_number_of_times.and_return(MockCommandResult.create(0))
|
59
|
+
end
|
60
|
+
|
61
|
+
#Registers an expectation about a command being run - expectations are
|
62
|
+
#ordered
|
63
|
+
def expect_command(cmd, *result)
|
64
|
+
Caliph::CommandLine.should_receive(:execute, :expected_from => caller(1)[0]).with(cmd).ordered.and_return(MockCommandResult.create(*result))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'caliph/command-line'
|
2
|
+
|
3
|
+
module Caliph
|
4
|
+
class CommandLine
|
5
|
+
@@commands = []
|
6
|
+
alias original_execute execute
|
7
|
+
|
8
|
+
def execute
|
9
|
+
result = original_execute
|
10
|
+
@@commands << [command, result]
|
11
|
+
return result
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
attr_accessor :command_recording_path
|
16
|
+
|
17
|
+
def command_recording_path
|
18
|
+
@command_recording_path ||= ENV['CALIPH_CMDREC']
|
19
|
+
end
|
20
|
+
|
21
|
+
def emit_recording
|
22
|
+
io = $stderr
|
23
|
+
if command_recording_path
|
24
|
+
io = File.open(command_recording_path, "w")
|
25
|
+
else
|
26
|
+
io.puts "Set CALIPH_CMDREC to write to a path"
|
27
|
+
end
|
28
|
+
@@commands.each do |pair|
|
29
|
+
io.puts "[/#{pair[0]}/, #{[pair[1].exit_code, pair[1].streams].inspect}]"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
at_exit do
|
37
|
+
Caliph::CommandLine.emit_recording
|
38
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'caliph/command-line-dsl'
|
2
|
+
|
3
|
+
describe Caliph::CommandLineDSL do
|
4
|
+
include described_class
|
5
|
+
|
6
|
+
describe "using the - operator" do
|
7
|
+
let :command do
|
8
|
+
cmd("sudo") - ["gem", "install", "bundler"]
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should define commands" do
|
12
|
+
command.should be_an_instance_of(Caliph::WrappingChain)
|
13
|
+
command.should have(2).commands
|
14
|
+
command.commands[0].should be_an_instance_of(Caliph::CommandLine)
|
15
|
+
command.commands[1].should be_an_instance_of(Caliph::CommandLine)
|
16
|
+
command.command.should == "sudo -- gem install bundler"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "using the | operator" do
|
21
|
+
let :command do
|
22
|
+
cmd("cat", "/etc/passwd") | ["grep", "root"]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should define commands" do
|
26
|
+
command.should be_an_instance_of(Caliph::PipelineChain)
|
27
|
+
command.should have(2).commands
|
28
|
+
command.commands[0].should be_an_instance_of(Caliph::CommandLine)
|
29
|
+
command.commands[1].should be_an_instance_of(Caliph::CommandLine)
|
30
|
+
command.command.should == "cat /etc/passwd | grep root"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "using the & operator" do
|
35
|
+
let :command do
|
36
|
+
p method(:cmd).source_location
|
37
|
+
cmd("cd", "/tmp/trash") & %w{rm -rf *}
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should define commands" do
|
41
|
+
command.should be_an_instance_of(Caliph::PrereqChain)
|
42
|
+
command.should have(2).commands
|
43
|
+
command.commands[0].should be_an_instance_of(Caliph::CommandLine)
|
44
|
+
command.commands[1].should be_an_instance_of(Caliph::CommandLine)
|
45
|
+
command.command.should == "cd /tmp/trash && rm -rf *"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'caliph'
|
2
|
+
require 'caliph/testing/mock-command-line'
|
3
|
+
|
4
|
+
require 'caliph/testing/record-commands'
|
5
|
+
|
6
|
+
Caliph::CommandLine.command_recording_path = "/dev/null"
|
7
|
+
|
8
|
+
describe Caliph::CommandLine do
|
9
|
+
let :commandline do
|
10
|
+
Caliph::CommandLine.new('echo', "-n") do |cmd|
|
11
|
+
cmd.options << "Some text"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have a name set" do
|
16
|
+
commandline.name.should == "echo"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should produce a command string" do
|
20
|
+
commandline.command.should == "echo -n Some text"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should succeed" do
|
24
|
+
commandline.succeeds?.should be_true
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should not complain about success" do
|
28
|
+
expect do
|
29
|
+
commandline.must_succeed!
|
30
|
+
end.to_not raise_error
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
describe Caliph::CommandLine, "setting environment variables" do
|
36
|
+
let :commandline do
|
37
|
+
Caliph::CommandLine.new("env") do |cmd|
|
38
|
+
cmd.env["TEST_ENV"] = "indubitably"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
let :result do
|
43
|
+
commandline.run
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should succeed" do
|
47
|
+
result.succeeded?.should be_true
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should alter the command's environment variables" do
|
51
|
+
result.stdout.should =~ /TEST_ENV.*indubitably/
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
describe Caliph::CommandLine, 'redirecting' do
|
57
|
+
let :commandline do
|
58
|
+
Caliph::CommandLine.new("env")
|
59
|
+
end
|
60
|
+
|
61
|
+
let :result do
|
62
|
+
commandline.string_format
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should allow redirect stdout' do
|
66
|
+
commandline.redirect_stdout('some_file')
|
67
|
+
result.should =~ /1>some_file$/
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should allow redirect stderr' do
|
71
|
+
commandline.redirect_stderr('some_file')
|
72
|
+
result.should =~ /2>some_file$/
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should allow chain redirects' do
|
76
|
+
commandline.redirect_stdout('stdout_file').redirect_stderr('stderr_file')
|
77
|
+
result.should =~ /\b1>stdout_file\b/
|
78
|
+
result.should =~ /\b2>stderr_file\b/
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should redirect both' do
|
82
|
+
commandline.redirect_both('output_file')
|
83
|
+
result.should =~ /\b1>output_file\b/
|
84
|
+
result.should =~ /\b2>output_file\b/
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe Caliph::PipelineChain do
|
89
|
+
let :commandline do
|
90
|
+
Caliph::PipelineChain.new do |chain|
|
91
|
+
chain.add Caliph::CommandLine.new("env")
|
92
|
+
chain.add Caliph::CommandLine.new("cat") do |cmd|
|
93
|
+
cmd.env["TEST_ENV"] = "indubitably"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
let :result do
|
99
|
+
commandline.run
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should produce the right command" do
|
103
|
+
commandline.command.should == 'env | cat'
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should produce a runnable command with format_string" do
|
107
|
+
commandline.string_format.should == 'TEST_ENV=indubitably env | cat'
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should succeed" do
|
111
|
+
result.succeeded?.should be_true
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should alter the command's environment variables" do
|
115
|
+
result.stdout.should =~ /TEST_ENV.*indubitably/
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
|
121
|
+
describe Caliph::CommandLine, "that fails" do
|
122
|
+
let :commandline do
|
123
|
+
Caliph::CommandLine.new("false")
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should not succeed" do
|
127
|
+
commandline.succeeds?.should == false
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should raise error if succeed demanded" do
|
131
|
+
expect do
|
132
|
+
commandline.must_succeed
|
133
|
+
end.to raise_error
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
puts Dir::pwd
|
2
|
+
require 'test/unit'
|
3
|
+
begin
|
4
|
+
require 'spec'
|
5
|
+
rescue LoadError
|
6
|
+
false
|
7
|
+
end
|
8
|
+
|
9
|
+
class RSpecTest < Test::Unit::TestCase
|
10
|
+
def test_that_rspec_is_available
|
11
|
+
assert_nothing_raised("\n\n * RSpec isn't available - please run: gem install rspec *\n\n"){ ::Spec }
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_that_specs_pass
|
15
|
+
assert(system(*%w{spec -f e -p **/*.rb spec}),"\n\n * Specs failed *\n\n")
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'rspec/core/formatters/base_formatter'
|
3
|
+
|
4
|
+
require 'caliph'
|
5
|
+
require 'cadre/rspec'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.run_all_when_everything_filtered = true
|
9
|
+
config.add_formatter(Cadre::RSpec::NotifyOnCompleteFormatter)
|
10
|
+
config.add_formatter(Cadre::RSpec::QuickfixFormatter)
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: caliph
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Evan Dorn
|
8
|
+
- Judson Lester
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-06-12 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: |2
|
15
|
+
TDD-suitable Ruby tool for generating command-line commands via an OOP interface.
|
16
|
+
email:
|
17
|
+
- evan@lrdesign.com
|
18
|
+
- judson@lrdesign.com
|
19
|
+
executables: []
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- lib/caliph.rb
|
24
|
+
- lib/caliph/command-chain.rb
|
25
|
+
- lib/caliph/command-line-dsl.rb
|
26
|
+
- lib/caliph/command-line.rb
|
27
|
+
- lib/caliph/command-run-result.rb
|
28
|
+
- lib/caliph/define-op.rb
|
29
|
+
- lib/caliph/shell-escaped.rb
|
30
|
+
- lib/caliph/testing/mock-command-line.rb
|
31
|
+
- lib/caliph/testing/record-commands.rb
|
32
|
+
- spec/command-chain.rb
|
33
|
+
- spec/command-line-dsl.rb
|
34
|
+
- spec/command-line.rb
|
35
|
+
- spec_help/gem_test_suite.rb
|
36
|
+
- spec_help/spec_helper.rb
|
37
|
+
homepage: ''
|
38
|
+
licenses:
|
39
|
+
- MIT
|
40
|
+
metadata: {}
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options:
|
43
|
+
- --inline-source
|
44
|
+
- --main
|
45
|
+
- doc/README
|
46
|
+
- --title
|
47
|
+
- caliph-0.1.0 Documentation
|
48
|
+
require_paths:
|
49
|
+
- lib/
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
requirements: []
|
61
|
+
rubyforge_project: caliph
|
62
|
+
rubygems_version: 2.2.2
|
63
|
+
signing_key:
|
64
|
+
specification_version: 4
|
65
|
+
summary: TDD-suitable Ruby tool for generating command-line commands via an OOP interface.
|
66
|
+
test_files:
|
67
|
+
- spec_help/gem_test_suite.rb
|