linecook 1.2.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/{History → History.rdoc} +3 -2
- data/README.rdoc +93 -0
- data/bin/linecook +32 -56
- data/bin/linecook_run +19 -6
- data/bin/linecook_scp +12 -4
- data/doc/vm_setup.rdoc +75 -0
- data/lib/linecook.rb +3 -2
- data/lib/linecook/attributes.rb +33 -8
- data/lib/linecook/command.rb +61 -0
- data/lib/linecook/command_set.rb +85 -0
- data/lib/linecook/command_utils.rb +20 -0
- data/lib/linecook/commands/build.rb +108 -57
- data/lib/linecook/commands/compile.rb +181 -0
- data/lib/linecook/commands/{helper.rb → compile_helper.rb} +123 -94
- data/lib/linecook/commands/run.rb +43 -39
- data/lib/linecook/commands/snapshot.rb +24 -24
- data/lib/linecook/commands/ssh.rb +7 -7
- data/lib/linecook/commands/start.rb +10 -10
- data/lib/linecook/commands/state.rb +7 -7
- data/lib/linecook/commands/stop.rb +3 -3
- data/lib/linecook/commands/{vbox_command.rb → virtual_box_command.rb} +31 -29
- data/lib/linecook/cookbook.rb +149 -131
- data/lib/linecook/executable.rb +28 -0
- data/lib/linecook/package.rb +177 -361
- data/lib/linecook/proxy.rb +4 -10
- data/lib/linecook/recipe.rb +289 -369
- data/lib/linecook/test.rb +114 -98
- data/lib/linecook/utils.rb +31 -41
- data/lib/linecook/version.rb +2 -6
- metadata +120 -68
- data/HowTo/Control Virtual Machines +0 -106
- data/HowTo/Generate Scripts +0 -268
- data/HowTo/Run Scripts +0 -87
- data/HowTo/Setup Virtual Machines +0 -76
- data/README +0 -117
- data/lib/linecook/commands.rb +0 -11
- data/lib/linecook/commands/command.rb +0 -58
- data/lib/linecook/commands/command_error.rb +0 -12
- data/lib/linecook/commands/env.rb +0 -89
- data/lib/linecook/commands/init.rb +0 -86
- data/lib/linecook/commands/package.rb +0 -57
- data/lib/linecook/template.rb +0 -17
- data/lib/linecook/test/command_parser.rb +0 -75
- data/lib/linecook/test/file_test.rb +0 -197
- data/lib/linecook/test/regexp_escape.rb +0 -86
- data/lib/linecook/test/shell_test.rb +0 -177
- data/lib/linecook/test/shim.rb +0 -71
- data/templates/Gemfile +0 -3
- data/templates/Rakefile +0 -146
- data/templates/_gitignore +0 -4
- data/templates/attributes/project_name.rb +0 -3
- data/templates/config/ssh +0 -14
- data/templates/cookbook +0 -10
- data/templates/files/example.txt +0 -1
- data/templates/helpers/project_name/echo.erb +0 -4
- data/templates/packages/abox.yml +0 -2
- data/templates/project_name.gemspec +0 -30
- data/templates/recipes/abox.rb +0 -16
- data/templates/templates/example.erb +0 -1
- data/templates/test/project_name_test.rb +0 -24
- data/templates/test/test_helper.rb +0 -14
@@ -1,57 +0,0 @@
|
|
1
|
-
require 'linecook/commands/command'
|
2
|
-
require 'linecook/cookbook'
|
3
|
-
require 'linecook/package'
|
4
|
-
require 'fileutils'
|
5
|
-
require 'yaml'
|
6
|
-
|
7
|
-
module Linecook
|
8
|
-
module Commands
|
9
|
-
|
10
|
-
# :startdoc::desc generates a package
|
11
|
-
#
|
12
|
-
# Generates the package specified at
|
13
|
-
# 'project_dir/packages/package_name.yml'. The package file should be a
|
14
|
-
# YAML files that specifies a package env. The full path to package file
|
15
|
-
# can be given instead of package name using the --file option.
|
16
|
-
#
|
17
|
-
# If a cookbook file is present in the project_dir then it will be used to
|
18
|
-
# resolve resources available to the package. See the env command to
|
19
|
-
# interrogate a package env.
|
20
|
-
class Package < Command
|
21
|
-
config :project_dir, '.', :short => :d # the project directory
|
22
|
-
config :force, false, :short => :f, &c.flag # force creation
|
23
|
-
config :quiet, false, &c.flag # silence output
|
24
|
-
|
25
|
-
def process(package_file, package_dir=nil)
|
26
|
-
package_dir ||= default_package_dir(package_file)
|
27
|
-
package_dir = File.expand_path(package_dir)
|
28
|
-
package = Linecook::Package.init(package_file, project_dir)
|
29
|
-
|
30
|
-
dependencies = package_dependencies(package) + [package_file]
|
31
|
-
if force || !FileUtils.uptodate?(package_dir, dependencies)
|
32
|
-
package.build
|
33
|
-
package.export(package_dir)
|
34
|
-
$stdout.puts package_dir unless quiet
|
35
|
-
end
|
36
|
-
|
37
|
-
package_dir
|
38
|
-
end
|
39
|
-
|
40
|
-
def package_dependencies(package)
|
41
|
-
dependencies = []
|
42
|
-
package.manifest.values.collect do |resources|
|
43
|
-
dependencies.concat resources.values
|
44
|
-
end
|
45
|
-
|
46
|
-
$LOAD_PATH.each do |path|
|
47
|
-
dependencies.concat Dir.glob("#{path}/**/*.rb")
|
48
|
-
end
|
49
|
-
dependencies
|
50
|
-
end
|
51
|
-
|
52
|
-
def default_package_dir(package_file)
|
53
|
-
package_file.chomp(File.extname(package_file))
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
data/lib/linecook/template.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
require 'erb'
|
2
|
-
require 'ostruct'
|
3
|
-
|
4
|
-
module Linecook
|
5
|
-
class Template
|
6
|
-
attr_reader :erb
|
7
|
-
|
8
|
-
def initialize(filename)
|
9
|
-
@erb = ERB.new File.read(filename)
|
10
|
-
@erb.filename = filename
|
11
|
-
end
|
12
|
-
|
13
|
-
def build(locals={})
|
14
|
-
erb.result OpenStruct.new(locals).instance_eval('binding')
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,75 +0,0 @@
|
|
1
|
-
module Linecook
|
2
|
-
module Test
|
3
|
-
class CommandParser
|
4
|
-
attr_reader :ps1
|
5
|
-
attr_reader :ps2
|
6
|
-
attr_reader :prefix
|
7
|
-
attr_reader :suffix
|
8
|
-
|
9
|
-
def initialize(options={})
|
10
|
-
options = {
|
11
|
-
:ps1 => '% ',
|
12
|
-
:ps2 => '> ',
|
13
|
-
:prefix => '0<&- 2>&1 ',
|
14
|
-
:suffix => ''
|
15
|
-
}.merge(options)
|
16
|
-
|
17
|
-
@ps1 = options[:ps1]
|
18
|
-
@ps2 = options[:ps2]
|
19
|
-
@prefix = options[:prefix]
|
20
|
-
@suffix = options[:suffix]
|
21
|
-
end
|
22
|
-
|
23
|
-
def parse_cmd(cmd)
|
24
|
-
cmd =~ /.*?#\s*(?:\[(\d+)\])?\s*(\.{3})?/
|
25
|
-
exit_status = $1 ? $1.to_i : 0
|
26
|
-
output = $2 ? nil : ""
|
27
|
-
|
28
|
-
[cmd, output, exit_status]
|
29
|
-
end
|
30
|
-
|
31
|
-
def parse(script)
|
32
|
-
commands = []
|
33
|
-
|
34
|
-
command, output, exit_status = nil, "", 0
|
35
|
-
script.each_line do |line|
|
36
|
-
case
|
37
|
-
when line.index(ps1) == 0
|
38
|
-
if command
|
39
|
-
commands << ["#{prefix}#{command}#{suffix}", output, exit_status]
|
40
|
-
end
|
41
|
-
|
42
|
-
command, output, exit_status = parse_cmd lchomp(ps1, line)
|
43
|
-
|
44
|
-
when command.nil?
|
45
|
-
unless line.strip.empty?
|
46
|
-
command, output, exit_status = parse_cmd(line)
|
47
|
-
end
|
48
|
-
|
49
|
-
when line.index(ps2) == 0
|
50
|
-
command << lchomp(ps2, line)
|
51
|
-
|
52
|
-
when output.nil?
|
53
|
-
output = line
|
54
|
-
|
55
|
-
else
|
56
|
-
output << line
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
if command
|
61
|
-
commands << ["#{prefix}#{command}#{suffix}", output, exit_status]
|
62
|
-
end
|
63
|
-
|
64
|
-
commands
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
|
69
|
-
def lchomp(prefix, line) # :nodoc:
|
70
|
-
length = prefix.length
|
71
|
-
line[length, line.length - length]
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,197 +0,0 @@
|
|
1
|
-
require 'linecook/test/shim'
|
2
|
-
|
3
|
-
module Linecook
|
4
|
-
module Test
|
5
|
-
module FileTest
|
6
|
-
module ClassMethods
|
7
|
-
attr_accessor :class_dir
|
8
|
-
|
9
|
-
attr_reader :cleanup_method_registry
|
10
|
-
|
11
|
-
def cleanup_methods
|
12
|
-
@cleanup_methods ||= begin
|
13
|
-
cleanup_methods = {}
|
14
|
-
|
15
|
-
ancestors.reverse.each do |ancestor|
|
16
|
-
next unless ancestor.kind_of?(ClassMethods)
|
17
|
-
ancestor.cleanup_method_registry.each_pair do |key, value|
|
18
|
-
if value.nil?
|
19
|
-
cleanup_methods.delete(key)
|
20
|
-
else
|
21
|
-
cleanup_methods[key] = value
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
cleanup_methods
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def reset_cleanup_methods
|
31
|
-
@cleanup_methods = nil
|
32
|
-
end
|
33
|
-
|
34
|
-
protected
|
35
|
-
|
36
|
-
def self.initialize(base)
|
37
|
-
# Infers the test directory from the calling file.
|
38
|
-
# 'some_class_test.rb' => 'some_class_test'
|
39
|
-
calling_file = caller[1].gsub(/:\d+(:in .*)?$/, "")
|
40
|
-
base.class_dir = calling_file.chomp(File.extname(calling_file))
|
41
|
-
|
42
|
-
base.reset_cleanup_methods
|
43
|
-
unless base.instance_variable_defined?(:@cleanup_method_registry)
|
44
|
-
base.instance_variable_set(:@cleanup_method_registry, {})
|
45
|
-
end
|
46
|
-
|
47
|
-
unless base.instance_variable_defined?(:@cleanup_paths)
|
48
|
-
base.instance_variable_set(:@cleanup_paths, ['.'])
|
49
|
-
end
|
50
|
-
|
51
|
-
unless base.instance_variable_defined?(:@cleanup)
|
52
|
-
base.instance_variable_set(:@cleanup, true)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def inherited(base) # :nodoc:
|
57
|
-
ClassMethods.initialize(base)
|
58
|
-
super
|
59
|
-
end
|
60
|
-
|
61
|
-
def define_method_cleanup(method_name, dirs)
|
62
|
-
reset_cleanup_methods
|
63
|
-
cleanup_method_registry[method_name.to_sym] = dirs
|
64
|
-
end
|
65
|
-
|
66
|
-
def remove_method_cleanup(method_name)
|
67
|
-
reset_cleanup_methods
|
68
|
-
cleanup_method_registry.delete(method_name.to_sym)
|
69
|
-
end
|
70
|
-
|
71
|
-
def undef_method_cleanup(method_name)
|
72
|
-
reset_cleanup_methods
|
73
|
-
cleanup_method_registry[method_name.to_sym] = nil
|
74
|
-
end
|
75
|
-
|
76
|
-
def cleanup_paths(*dirs)
|
77
|
-
@cleanup_paths = dirs
|
78
|
-
end
|
79
|
-
|
80
|
-
def cleanup(*method_names)
|
81
|
-
if method_names.empty?
|
82
|
-
@cleanup = true
|
83
|
-
else
|
84
|
-
method_names.each do |method_name|
|
85
|
-
define_method_cleanup method_name, @cleanup_paths
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def no_cleanup(*method_names)
|
91
|
-
if method_names.empty?
|
92
|
-
@cleanup = false
|
93
|
-
else
|
94
|
-
method_names.each do |method_name|
|
95
|
-
undef_method_cleanup method_name
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def method_added(sym)
|
101
|
-
if @cleanup && !cleanup_method_registry.has_key?(sym.to_sym) && sym.to_s[0, 5] == "test_"
|
102
|
-
cleanup sym
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
module ModuleMethods
|
108
|
-
module_function
|
109
|
-
|
110
|
-
def included(base)
|
111
|
-
base.extend ClassMethods
|
112
|
-
base.extend ModuleMethods unless base.kind_of?(Class)
|
113
|
-
|
114
|
-
ClassMethods.initialize(base)
|
115
|
-
super
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
extend ModuleMethods
|
120
|
-
|
121
|
-
def setup
|
122
|
-
super
|
123
|
-
cleanup
|
124
|
-
end
|
125
|
-
|
126
|
-
def teardown
|
127
|
-
Dir.chdir(user_dir)
|
128
|
-
|
129
|
-
unless ENV["KEEP_OUTPUTS"] == "true"
|
130
|
-
cleanup
|
131
|
-
|
132
|
-
dir = method_dir
|
133
|
-
while dir != class_dir
|
134
|
-
dir = File.dirname(dir)
|
135
|
-
Dir.rmdir(dir)
|
136
|
-
end rescue(SystemCallError)
|
137
|
-
end
|
138
|
-
|
139
|
-
super
|
140
|
-
end
|
141
|
-
|
142
|
-
def user_dir
|
143
|
-
@user_dir ||= File.expand_path('.')
|
144
|
-
end
|
145
|
-
|
146
|
-
def class_dir
|
147
|
-
@class_dir ||= File.expand_path(self.class.class_dir, user_dir)
|
148
|
-
end
|
149
|
-
|
150
|
-
def method_dir
|
151
|
-
@method_dir ||= File.expand_path(method_name.to_s, class_dir)
|
152
|
-
end
|
153
|
-
|
154
|
-
def cleanup_methods
|
155
|
-
self.class.cleanup_methods
|
156
|
-
end
|
157
|
-
|
158
|
-
def cleanup
|
159
|
-
if cleanup_paths = cleanup_methods[method_name.to_sym]
|
160
|
-
cleanup_paths.each {|relative_path| remove(relative_path) }
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
def path(relative_path)
|
165
|
-
path = File.expand_path(relative_path, method_dir)
|
166
|
-
|
167
|
-
unless path.index(method_dir) == 0
|
168
|
-
raise "does not make a path relative to method_dir: #{relative_path.inspect}"
|
169
|
-
end
|
170
|
-
|
171
|
-
path
|
172
|
-
end
|
173
|
-
|
174
|
-
def prepare(relative_path, content=nil, &block)
|
175
|
-
target = path(relative_path)
|
176
|
-
|
177
|
-
if File.exists?(target)
|
178
|
-
FileUtils.rm(target)
|
179
|
-
else
|
180
|
-
target_dir = File.dirname(target)
|
181
|
-
FileUtils.mkdir_p(target_dir) unless File.exists?(target_dir)
|
182
|
-
end
|
183
|
-
|
184
|
-
FileUtils.touch(target)
|
185
|
-
File.open(target, 'w') {|io| io << content } if content
|
186
|
-
File.open(target, 'a', &block) if block
|
187
|
-
|
188
|
-
target
|
189
|
-
end
|
190
|
-
|
191
|
-
def remove(relative_path)
|
192
|
-
full_path = path(relative_path)
|
193
|
-
FileUtils.rm_r(full_path) if File.exists?(full_path)
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
@@ -1,86 +0,0 @@
|
|
1
|
-
module Linecook
|
2
|
-
module Test
|
3
|
-
# RegexpEscape is a subclass of regexp that escapes all but the text in a
|
4
|
-
# special escape sequence. This allows the creation of complex regexps
|
5
|
-
# to match, for instance, console output.
|
6
|
-
#
|
7
|
-
# The RegexpEscape.escape (or equivalently the quote) method does the
|
8
|
-
# work; all regexp-active characters are escaped except for characters
|
9
|
-
# enclosed by ':.' and '.:' delimiters.
|
10
|
-
#
|
11
|
-
# RegexpEscape.escape('reg[exp]+ chars. are(quoted)') # => 'reg\[exp\]\+\ chars\.\ are\(quoted\)'
|
12
|
-
# RegexpEscape.escape('these are not: :.a(b*)c.:') # => 'these\ are\ not:\ a(b*)c'
|
13
|
-
#
|
14
|
-
# In addition, all-period regexps are automatically upgraded to '.*?';
|
15
|
-
# use the '.{n}' notation to specify n arbitrary characters.
|
16
|
-
#
|
17
|
-
# RegexpEscape.escape('_:..:_:...:_:....:') # => '_.*?_.*?_.*?'
|
18
|
-
# RegexpEscape.escape(':..{1}.:') # => '.{1}'
|
19
|
-
#
|
20
|
-
# RegexpEscape instances are initialized using the escaped input string
|
21
|
-
# and return the original string upon to_s.
|
22
|
-
#
|
23
|
-
# str = %q{
|
24
|
-
# a multiline
|
25
|
-
# :...:
|
26
|
-
# example}
|
27
|
-
# r = RegexpEscape.new(str)
|
28
|
-
#
|
29
|
-
# r =~ %q{
|
30
|
-
# a multiline
|
31
|
-
# matching
|
32
|
-
# example} # => true
|
33
|
-
#
|
34
|
-
# r !~ %q{
|
35
|
-
# a failing multiline
|
36
|
-
# example} # => true
|
37
|
-
#
|
38
|
-
# r.to_s # => str
|
39
|
-
#
|
40
|
-
class RegexpEscape < Regexp
|
41
|
-
|
42
|
-
# matches the escape sequence
|
43
|
-
ESCAPE_SEQUENCE = /:\..*?\.:/
|
44
|
-
|
45
|
-
class << self
|
46
|
-
|
47
|
-
# Escapes regexp-active characters in str, except for character
|
48
|
-
# delimited by ':.' and '.:'. See the class description for
|
49
|
-
# details.
|
50
|
-
def escape(str)
|
51
|
-
substituents = []
|
52
|
-
str.scan(ESCAPE_SEQUENCE) do
|
53
|
-
regexp_str = $&[2...-2]
|
54
|
-
regexp_str = ".*?" if regexp_str =~ /^\.*$/
|
55
|
-
substituents << regexp_str
|
56
|
-
end
|
57
|
-
substituents << ""
|
58
|
-
|
59
|
-
splits = str.split(ESCAPE_SEQUENCE).collect do |split|
|
60
|
-
super(split)
|
61
|
-
end
|
62
|
-
splits << "" if splits.empty?
|
63
|
-
|
64
|
-
splits.zip(substituents).to_a.flatten.join
|
65
|
-
end
|
66
|
-
|
67
|
-
# Same as escape.
|
68
|
-
def quote(str)
|
69
|
-
escape(str)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# Generates a new RegexpEscape by escaping the str, using the same
|
74
|
-
# options as Regexp.
|
75
|
-
def initialize(str, *options)
|
76
|
-
super(RegexpEscape.escape(str), *options)
|
77
|
-
@original_str = str
|
78
|
-
end
|
79
|
-
|
80
|
-
# Returns the original string for self
|
81
|
-
def to_s
|
82
|
-
@original_str
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
@@ -1,177 +0,0 @@
|
|
1
|
-
require 'linecook/test/regexp_escape'
|
2
|
-
require 'linecook/test/command_parser'
|
3
|
-
require 'linecook/test/shim'
|
4
|
-
|
5
|
-
module Linecook
|
6
|
-
module Test
|
7
|
-
module ShellTest
|
8
|
-
|
9
|
-
def setup
|
10
|
-
super
|
11
|
-
@notify_method_name = true
|
12
|
-
end
|
13
|
-
|
14
|
-
# Returns true if the ENV variable 'VERBOSE' is true. When verbose,
|
15
|
-
# ShellTest prints the expanded commands of sh_test to $stdout.
|
16
|
-
def verbose?
|
17
|
-
verbose = ENV['VERBOSE']
|
18
|
-
verbose && verbose =~ /^true$/i ? true : false
|
19
|
-
end
|
20
|
-
|
21
|
-
# Sets the specified ENV variables and returns the *current* env.
|
22
|
-
# If replace is true, current ENV variables are replaced; otherwise
|
23
|
-
# the new env variables are simply added to the existing set.
|
24
|
-
def set_env(env={}, replace=false)
|
25
|
-
current_env = {}
|
26
|
-
ENV.each_pair do |key, value|
|
27
|
-
current_env[key] = value
|
28
|
-
end
|
29
|
-
|
30
|
-
ENV.clear if replace
|
31
|
-
|
32
|
-
env.each_pair do |key, value|
|
33
|
-
if value.nil?
|
34
|
-
ENV.delete(key)
|
35
|
-
else
|
36
|
-
ENV[key] = value
|
37
|
-
end
|
38
|
-
end if env
|
39
|
-
|
40
|
-
current_env
|
41
|
-
end
|
42
|
-
|
43
|
-
# Sets the specified ENV variables for the duration of the block.
|
44
|
-
# If replace is true, current ENV variables are replaced; otherwise
|
45
|
-
# the new env variables are simply added to the existing set.
|
46
|
-
#
|
47
|
-
# Returns the block return.
|
48
|
-
def with_env(env={}, replace=false)
|
49
|
-
current_env = nil
|
50
|
-
begin
|
51
|
-
current_env = set_env(env, replace)
|
52
|
-
yield
|
53
|
-
ensure
|
54
|
-
if current_env
|
55
|
-
set_env(current_env, true)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def sh(cmd)
|
61
|
-
if @notify_method_name && verbose?
|
62
|
-
@notify_method_name = false
|
63
|
-
puts
|
64
|
-
puts method_name
|
65
|
-
end
|
66
|
-
|
67
|
-
start = Time.now
|
68
|
-
result = `#{cmd}`
|
69
|
-
finish = Time.now
|
70
|
-
|
71
|
-
if verbose?
|
72
|
-
elapsed = "%.3f" % [finish-start]
|
73
|
-
puts " (#{elapsed}s) #{cmd}"
|
74
|
-
end
|
75
|
-
|
76
|
-
result
|
77
|
-
end
|
78
|
-
|
79
|
-
def assert_script(script, options={})
|
80
|
-
_assert_script outdent(script), options
|
81
|
-
end
|
82
|
-
|
83
|
-
def _assert_script(script, options={})
|
84
|
-
commands = CommandParser.new(options).parse(script)
|
85
|
-
commands.each do |cmd, output, status|
|
86
|
-
result = sh(cmd)
|
87
|
-
|
88
|
-
_assert_output_equal(output, result, cmd) if output
|
89
|
-
assert_equal(status, $?.exitstatus, cmd) if status
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def assert_script_match(script, options={})
|
94
|
-
_assert_script_match outdent(script), options
|
95
|
-
end
|
96
|
-
|
97
|
-
def _assert_script_match(script, options={})
|
98
|
-
commands = CommandParser.new(options).parse(script)
|
99
|
-
commands.each do |cmd, output, status|
|
100
|
-
result = sh(cmd)
|
101
|
-
|
102
|
-
_assert_alike(output, result, cmd) if output
|
103
|
-
assert_equal(status, $?.exitstatus, cmd) if status
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
# Asserts whether or not the a and b strings are equal, with a more
|
108
|
-
# readable output than assert_equal for large strings (especially large
|
109
|
-
# strings with significant whitespace).
|
110
|
-
def assert_output_equal(a, b, msg=nil)
|
111
|
-
_assert_output_equal outdent(a), b, msg
|
112
|
-
end
|
113
|
-
|
114
|
-
def _assert_output_equal(a, b, msg=nil)
|
115
|
-
if a == b
|
116
|
-
assert true
|
117
|
-
else
|
118
|
-
flunk %Q{
|
119
|
-
#{msg}
|
120
|
-
==================== expected output ====================
|
121
|
-
#{whitespace_escape(a)}
|
122
|
-
======================== but was ========================
|
123
|
-
#{whitespace_escape(b)}
|
124
|
-
=========================================================
|
125
|
-
}
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
# Asserts whether or not b is like a (which should be a Regexp), and
|
130
|
-
# provides a more readable output in the case of a failure as compared
|
131
|
-
# with assert_match.
|
132
|
-
#
|
133
|
-
# If a is a string it is turned into a RegexpEscape.
|
134
|
-
def assert_alike(a, b, msg=nil)
|
135
|
-
a = outdent(a) if a.kind_of?(String)
|
136
|
-
_assert_alike a, b, msg
|
137
|
-
end
|
138
|
-
|
139
|
-
def _assert_alike(a, b, msg=nil)
|
140
|
-
if a.kind_of?(String)
|
141
|
-
a = RegexpEscape.new(a)
|
142
|
-
end
|
143
|
-
|
144
|
-
if b =~ a
|
145
|
-
assert true
|
146
|
-
else
|
147
|
-
flunk %Q{
|
148
|
-
#{msg}
|
149
|
-
================= expected output like ==================
|
150
|
-
#{whitespace_escape(a)}
|
151
|
-
======================== but was ========================
|
152
|
-
#{whitespace_escape(b)}
|
153
|
-
=========================================================
|
154
|
-
}
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
# helper for stripping indentation off a string
|
159
|
-
def outdent(str)
|
160
|
-
str =~ /\A(?:\s*?\n)( *)(.*)\z/m ? $2.gsub!(/^ {0,#{$1.length}}/, '') : str
|
161
|
-
end
|
162
|
-
|
163
|
-
# helper for formatting escaping whitespace into readable text
|
164
|
-
def whitespace_escape(str)
|
165
|
-
str.to_s.gsub(/\s/) do |match|
|
166
|
-
case match
|
167
|
-
when "\n" then "\\n\n"
|
168
|
-
when "\t" then "\\t"
|
169
|
-
when "\r" then "\\r"
|
170
|
-
when "\f" then "\\f"
|
171
|
-
else match
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|