Module: Tap::Test::ShellTest

Includes:
Utils
Defined in:
lib/tap/test/shell_test.rb,
lib/tap/test/shell_test/regexp_escape.rb

Overview

A module for testing shell scripts.

require 'test/unit'
class ShellTestSample < Test::Unit::TestCase
  include Tap::Test::ShellTest

  # these are the default sh_test options used 
  # in tests like test_sh_command_alias
  self.sh_test_options = {
    :cmd_pattern => '% inspect_argv',
    :cmd => 'ruby -e "puts ARGV.inspect"'
  }

  def test_echo
    assert_equal "goodnight moon", sh("echo goodnight moon").strip
  end

  def test_echo_using_sh_test
    sh_test %q{
echo goodnight moon
goodnight moon
}
  end

  def test_sh_command_alias
    sh_test("% inspect_env") do |output|
      assert output !~ /NEW_ENV_VAR/
    end

    sh_test("NEW_ENV_VAR=blue % inspect_env") do |output|
      assert output =~ /NEW_ENV_VAR=blue/
    end
  end
end

Defined Under Namespace

Classes: RegexpEscape

Instance Method Summary collapse

Instance Method Details

#assert_alike(a, b, msg = nil) ⇒ Object

Asserts whether or not b is like a (which should be a Regexp), and provides a more readable output in the case of a failure as compared with assert_match.

If a is a string, then indentation is stripped off and it is turned into a RegexpEscape. Using that syntax, all these pass:

assert_alike %q{
the time is: :...:
now!
}, "the time is: #{Time.now}\nnow!\n"

assert_alike %q{
  the time is: :...:
  now!
}, "the time is: #{Time.now}\nnow!\n"

  assert_alike %q{
  the time is: :...:
  now!
  }, "the time is: #{Time.now}\nnow!\n"

Use assert_alike! to prevent indentation stripping (conversion to a RegexpEscape is still in effect).



297
298
299
300
# File 'lib/tap/test/shell_test.rb', line 297

def assert_alike(a, b, msg=nil)
  a = strip_indent(a) if a.kind_of?(String)
  assert_alike!(a, b, msg)
end

#assert_alike!(a, b, msg = nil) ⇒ Object

Same as assert_alike but without indentation stripping.



303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/tap/test/shell_test.rb', line 303

def assert_alike!(a, b, msg=nil)
  a = RegexpEscape.new(a) if a.kind_of?(String)
  
  if b =~ a
    assert true
  else
    flunk %Q{
#{msg}
================= expected output like ==================
#{whitespace_escape(a)}
======================== but was ========================
#{whitespace_escape(b)}
=========================================================
}
  end
end

#assert_output_equal(a, b, msg = nil) ⇒ Object

Asserts whether or not the a and b strings are equal, with a more readable output than assert_equal for large strings (especially large strings with significant whitespace).

One gotcha is that assert_output_equal lstrips indentation off of ‘a’, so that these all pass:

assert_output_equal %q{
line one
line two
}, "line one\nline two\n"

assert_output_equal %q{
  line one
  line two
}, "line one\nline two\n

  assert_output_equal %q{
  line one
  line two
  }, "line one\nline two\n"

Use the assert_output_equal! method to prevent indentation stripping.



252
253
254
255
# File 'lib/tap/test/shell_test.rb', line 252

def assert_output_equal(a, b, msg=nil)
  a = strip_indent(a)
  assert_output_equal!(a, b, msg)
end

#assert_output_equal!(a, b, msg = nil) ⇒ Object

Same as assert_output_equal but without indentation stripping.



258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/tap/test/shell_test.rb', line 258

def assert_output_equal!(a, b, msg=nil)
  if a == b
    assert true
  else
    flunk %Q{
#{msg}
==================== expected output ====================
#{whitespace_escape(a)}
======================== but was ========================
#{whitespace_escape(b)}
=========================================================
}
  end
end

#quiet?Boolean

Returns true if the ENV variable ‘QUIET’ is true or nil. When quiet, ShellTest does not print any extra information to $stdout.

If ‘VERBOSE’ and ‘QUIET’ are both set, verbose wins.

Returns:

  • (Boolean)


63
64
65
66
67
68
# File 'lib/tap/test/shell_test.rb', line 63

def quiet?
  return false if verbose?
  
  quiet = ENV['QUIET']
  quiet.nil? || quiet =~ /^true$/i ? true : false
end

#setupObject

Sets up the ShellTest module. Be sure to call super if you override setup in an including module.



47
48
49
50
# File 'lib/tap/test/shell_test.rb', line 47

def setup
  super
  @notify_method_name = true
end

#sh(cmd, options = {}) ⇒ Object

Executes the command using IO.popen and returns the stdout content.

Note

On Windows this method requires the win32-popen3 utility. If it is not available, it will have to be installed:

% gem install win32-open3


78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/tap/test/shell_test.rb', line 78

def sh(cmd, options={})
  if @notify_method_name && !quiet?
    @notify_method_name = false
    puts
    puts method_name 
  end
  
  original_cmd = cmd
  if cmd_pattern = options[:cmd_pattern]
    cmd = cmd.sub(cmd_pattern, options[:cmd].to_s)
  end
  
  start = Time.now
  result = with_env(options[:env], options[:replace_env]) do
    IO.popen(cmd) do |io|
      yield(io) if block_given?
      io.read
    end
  end
  
  finish = Time.now
  elapsed = "%.3f" % [finish-start]
  puts "  (#{elapsed}s) #{verbose? ? cmd : original_cmd}" unless quiet?
  result
end

#sh_match(cmd, *regexps) {|result| ... } ⇒ Object

Similar to sh_test, but matches the output against each of the regexps. A hash of sh options can be provided as the last argument; it will be merged with the default sh_test_options.

The output is yielded to the block, if given, for further validation. Returns the sh output.

Yields:

  • (result)


199
200
201
202
203
204
205
206
207
208
209
# File 'lib/tap/test/shell_test.rb', line 199

def sh_match(cmd, *regexps)
  options = regexps.last.kind_of?(Hash) ? regexps.pop : {}
  options = sh_test_options.merge(options)
  result = sh(cmd, options)

  regexps.each do |regexp|
    assert_match regexp, result, cmd
  end
  yield(result) if block_given?
  result
end

#sh_test(cmd, options = {}) {|result| ... } ⇒ Object

Peforms a shell test. Shell tests execute the command and yield the $stdout result to the block for validation. The command is executed through sh, ie using IO.popen.

Options provided to sh_test are merged with the sh_test_options set for the class.

Command Aliases

The options allow specification of a command pattern that gets replaced with a command alias. Only the first instance of the command pattern is replaced. In addition, shell tests allow the expected result to be specified inline with the command. Used together, these allow multiple tests of a complex command to be specified easily:

opts = {
  :cmd_pattern => '% argv_inspect',
  :cmd => 'ruby -e "puts ARGV.inspect"'
}

sh_test %Q{
% argv_inspect goodnight moon
["goodnight", "moon"]
}, opts

sh_test %Q{
% argv_inspect hello world
["hello", "world"]
}, opts

Indents

To improve the readability of tests, sh_test will lstrip each line in the expected output to the same degree as the command line. So for instance these all pass:

sh_test %Q{
% argv_inspect hello world
["hello", "world"]
}, opts

sh_test %Q{
    % argv_inspect hello world
    ["hello", "world"]
}, opts

    sh_test %Q{
    % argv_inspect hello world
    ["hello", "world"]
    }, opts

Turn off indent stripping by specifying :indent => false.

ENV variables

Options may specify a hash of env variables that will be set in the subprocess.

sh_test %Q{
ruby -e "puts ENV['SAMPLE']"
value
}, :env => {'SAMPLE' => 'value'}

Note it is better to specify env variables in this way rather than through the command trick ‘VAR=value cmd …’, as that syntax does not work on Windows. As a point of interest, see gist.github.com/107363 for a demonstration of ENV variables being inherited by subprocesses.

Yields:

  • (result)


173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/tap/test/shell_test.rb', line 173

def sh_test(cmd, options={})
  options = sh_test_options.merge(options)
  
  # strip indentiation if possible
  if cmd =~ /\A(?:\s*?\n)?( *)(.*?\n)(.*)\z/m
    indent, cmd, expected = $1, $2, $3
    cmd.strip!
    
    if indent.length > 0 && options[:indents]
      expected.gsub!(/^ {0,#{indent.length}}/, '')
    end
  end
  
  result = sh(cmd, options)
  
  assert_equal(expected, result, cmd) if expected
  yield(result) if block_given?
  result
end

#sh_test_optionsObject

Returns a hash of default sh_test options.



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/tap/test/shell_test.rb', line 212

def sh_test_options
  {
    :cmd_pattern => '% ',
    :cmd => '2>&1 ',
    :indents => true,
    :env => {
      'TAPFILE'   => nil,
      'TAP_GEMS'  => nil, 
      'TAP_PATH'  => nil,
      'TAPENV'    => nil,
      'TAPRC'     => nil,
      'TAP_DEBUG' => nil
    },
    :replace_env => false
  }
end

#verbose?Boolean

Returns true if the ENV variable ‘VERBOSE’ is true. When verbose, ShellTest prints the expanded commands of sh_test to $stdout.

Returns:

  • (Boolean)


54
55
56
57
# File 'lib/tap/test/shell_test.rb', line 54

def verbose?
  verbose = ENV['VERBOSE']
  verbose && verbose =~ /^true$/i ? true : false
end