linecook 1.2.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|