linecook 0.6.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +139 -0
- data/HowTo/Control Virtual Machines +106 -0
- data/HowTo/Generate Scripts +263 -0
- data/HowTo/Run Scripts +87 -0
- data/HowTo/Setup Virtual Machines +76 -0
- data/License.txt +1 -1
- data/README +78 -59
- data/bin/linecook +12 -5
- data/bin/linecook_run +45 -0
- data/bin/linecook_scp +50 -0
- data/lib/linecook.rb +1 -3
- data/lib/linecook/attributes.rb +49 -12
- data/lib/linecook/commands.rb +9 -4
- data/lib/linecook/commands/build.rb +69 -0
- data/lib/linecook/commands/command.rb +13 -3
- data/lib/linecook/commands/command_error.rb +6 -0
- data/lib/linecook/commands/env.rb +74 -8
- data/lib/linecook/commands/helper.rb +271 -24
- data/lib/linecook/commands/init.rb +10 -6
- data/lib/linecook/commands/package.rb +36 -18
- data/lib/linecook/commands/run.rb +66 -0
- data/lib/linecook/commands/snapshot.rb +114 -0
- data/lib/linecook/commands/ssh.rb +39 -0
- data/lib/linecook/commands/start.rb +34 -0
- data/lib/linecook/commands/state.rb +32 -0
- data/lib/linecook/commands/stop.rb +22 -0
- data/lib/linecook/commands/vbox_command.rb +130 -0
- data/lib/linecook/cookbook.rb +112 -55
- data/lib/linecook/package.rb +293 -109
- data/lib/linecook/proxy.rb +19 -0
- data/lib/linecook/recipe.rb +321 -62
- data/lib/linecook/template.rb +7 -101
- data/lib/linecook/test.rb +196 -141
- data/lib/linecook/test/command_parser.rb +75 -0
- data/lib/linecook/test/file_test.rb +153 -35
- data/lib/linecook/test/shell_test.rb +176 -0
- data/lib/linecook/utils.rb +25 -7
- data/lib/linecook/version.rb +4 -4
- data/templates/Rakefile +44 -47
- data/templates/_gitignore +1 -1
- data/templates/attributes/project_name.rb +4 -4
- data/templates/config/ssh +15 -0
- data/templates/files/help.txt +1 -0
- data/templates/helpers/project_name/assert_content_equal.erb +15 -0
- data/templates/helpers/project_name/create_dir.erb +9 -0
- data/templates/helpers/project_name/create_file.erb +8 -0
- data/templates/helpers/project_name/install_file.erb +8 -0
- data/templates/packages/abox.yml +4 -0
- data/templates/recipes/abox.rb +22 -0
- data/templates/recipes/abox_test.rb +14 -0
- data/templates/templates/todo.txt.erb +3 -0
- data/templates/test/project_name_test.rb +19 -0
- data/templates/test/test_helper.rb +14 -0
- metadata +43 -41
- data/cookbook +0 -0
- data/lib/linecook/commands/helpers.rb +0 -28
- data/lib/linecook/commands/vbox.rb +0 -85
- data/lib/linecook/helper.rb +0 -117
- data/lib/linecook/shell.rb +0 -11
- data/lib/linecook/shell/posix.rb +0 -145
- data/lib/linecook/shell/test.rb +0 -254
- data/lib/linecook/shell/unix.rb +0 -117
- data/lib/linecook/shell/utils.rb +0 -138
- data/templates/README +0 -90
- data/templates/files/file.txt +0 -1
- data/templates/helpers/project_name/echo.erb +0 -5
- data/templates/recipes/project_name.rb +0 -20
- data/templates/scripts/project_name.yml +0 -7
- data/templates/templates/template.txt.erb +0 -3
- data/templates/vbox/setup/virtual_box +0 -86
- data/templates/vbox/ssh/id_rsa +0 -27
- data/templates/vbox/ssh/id_rsa.pub +0 -1
data/lib/linecook/test.rb
CHANGED
@@ -1,172 +1,227 @@
|
|
1
|
-
require 'linecook/
|
2
|
-
require 'linecook/recipe'
|
1
|
+
require 'linecook/package'
|
3
2
|
require 'linecook/test/file_test'
|
4
|
-
require 'linecook/test/
|
5
|
-
require 'linecook/utils'
|
3
|
+
require 'linecook/test/shell_test'
|
6
4
|
|
7
5
|
module Linecook
|
8
6
|
module Test
|
7
|
+
module ClassMethods
|
8
|
+
def host
|
9
|
+
@host ||= ENV['LINECOOK_TEST_HOST'] || name
|
10
|
+
end
|
11
|
+
|
12
|
+
def use_host(host)
|
13
|
+
@host = host
|
14
|
+
end
|
15
|
+
|
16
|
+
def only_hosts(*patterns)
|
17
|
+
patterns.collect! do |pattern|
|
18
|
+
pattern.kind_of?(Regexp) ? pattern : /\A#{pattern}\z/
|
19
|
+
end
|
20
|
+
|
21
|
+
@skip_test_suite = false
|
22
|
+
unless patterns.any? {|pattern| host =~ pattern }
|
23
|
+
skip_test "not for host (#{host})"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Causes a test suite to be skipped. If a message is given, it will
|
28
|
+
# print and notify the user the test suite has been skipped.
|
29
|
+
def skip_test(msg=nil)
|
30
|
+
@skip_test_suite = true
|
31
|
+
skip_messages << msg
|
32
|
+
end
|
33
|
+
|
34
|
+
# Modifies the default suite method to skip the suit unless
|
35
|
+
# run_test_suite is true. If the test is skipped, the skip_messages
|
36
|
+
# will be printed along with the default 'Skipping <Test>' message.
|
37
|
+
def suite # :nodoc:
|
38
|
+
if (@skip_test_suite ||= false)
|
39
|
+
skip_message = skip_messages.compact.join(', ')
|
40
|
+
puts "Skipping #{name}#{skip_message.empty? ? '' : ': ' + skip_message}"
|
41
|
+
|
42
|
+
# return an empty test suite of the appropriate name
|
43
|
+
::Test::Unit::TestSuite.new(name)
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def skip_messages # :nodoc:
|
52
|
+
@skip_messages ||= []
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module ModuleMethods
|
57
|
+
module_function
|
58
|
+
|
59
|
+
def included(base)
|
60
|
+
base.extend ClassMethods
|
61
|
+
base.extend ModuleMethods unless base.kind_of?(Class)
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
extend ModuleMethods
|
67
|
+
|
9
68
|
include FileTest
|
69
|
+
include ShellTest
|
10
70
|
|
11
|
-
|
12
|
-
|
13
|
-
|
71
|
+
LINECOOK_DIR = File.expand_path('../../..', __FILE__)
|
72
|
+
LINECOOK = File.join(LINECOOK_DIR, 'bin/linecook')
|
73
|
+
|
74
|
+
def method_dir
|
75
|
+
@host_method_dir ||= begin
|
76
|
+
if test_host = ENV['LINECOOK_TEST_HOST']
|
77
|
+
File.join(super, test_host)
|
78
|
+
else
|
79
|
+
super
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def remote_dir
|
85
|
+
method_dir[(user_dir.length + 1)..-1]
|
86
|
+
end
|
87
|
+
|
88
|
+
def ssh_config_file
|
89
|
+
method_ssh_config_file = path('config/ssh')
|
90
|
+
File.file?(method_ssh_config_file) ? method_ssh_config_file : 'config/ssh'
|
91
|
+
end
|
92
|
+
|
93
|
+
def setup_cookbook(configs=nil, project_dir=method_dir)
|
94
|
+
configs ||= Cookbook.config_file(project_dir)
|
95
|
+
@cookbook = Cookbook.setup(configs, project_dir)
|
96
|
+
end
|
14
97
|
|
15
98
|
def cookbook
|
16
|
-
@cookbook ||=
|
99
|
+
@cookbook ||= setup_cookbook
|
17
100
|
end
|
18
101
|
|
19
|
-
def
|
20
|
-
cookbook
|
102
|
+
def setup_package(env={})
|
103
|
+
@package = Package.setup(env, cookbook)
|
21
104
|
end
|
22
105
|
|
23
|
-
def
|
24
|
-
|
106
|
+
def package
|
107
|
+
@package ||= setup_package
|
25
108
|
end
|
26
109
|
|
27
|
-
def
|
28
|
-
@
|
29
|
-
end
|
30
|
-
|
31
|
-
def build(env={})
|
32
|
-
env = Utils.deep_merge(default_env, env)
|
33
|
-
Recipe.build(env).export File.join(method_dir, 'scripts')
|
34
|
-
end
|
35
|
-
|
36
|
-
# Asserts whether or not the a and b strings are equal, with a more
|
37
|
-
# readable output than assert_equal for large strings (especially large
|
38
|
-
# strings with significant whitespace).
|
39
|
-
#
|
40
|
-
# One gotcha is that assert_output_equal lstrips indentation off of 'a',
|
41
|
-
# so that these all pass:
|
42
|
-
#
|
43
|
-
# assert_output_equal %q{
|
44
|
-
# line one
|
45
|
-
# line two
|
46
|
-
# }, "line one\nline two\n"
|
47
|
-
#
|
48
|
-
# assert_output_equal %q{
|
49
|
-
# line one
|
50
|
-
# line two
|
51
|
-
# }, "line one\nline two\n
|
52
|
-
#
|
53
|
-
# assert_output_equal %q{
|
54
|
-
# line one
|
55
|
-
# line two
|
56
|
-
# }, "line one\nline two\n"
|
57
|
-
#
|
58
|
-
# Use the assert_output_equal! method to prevent indentation stripping.
|
59
|
-
def assert_output_equal(a, b, msg=nil)
|
60
|
-
a = strip_indent(a)
|
61
|
-
assert_output_equal!(a, b, msg)
|
110
|
+
def use_helpers(*helpers)
|
111
|
+
@helpers = helpers
|
62
112
|
end
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
if a == b
|
67
|
-
assert true
|
68
|
-
else
|
69
|
-
flunk %Q{
|
70
|
-
#{msg}
|
71
|
-
==================== expected output ====================
|
72
|
-
#{whitespace_escape(a)}
|
73
|
-
======================== but was ========================
|
74
|
-
#{whitespace_escape(b)}
|
75
|
-
=========================================================
|
76
|
-
}
|
77
|
-
end
|
113
|
+
|
114
|
+
def helpers
|
115
|
+
@helpers ||= []
|
78
116
|
end
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
# with assert_match.
|
83
|
-
#
|
84
|
-
# If a is a string, then indentation is stripped off and it is turned
|
85
|
-
# into a RegexpEscape. Using that syntax, all these pass:
|
86
|
-
#
|
87
|
-
# assert_alike %q{
|
88
|
-
# the time is: :...:
|
89
|
-
# now!
|
90
|
-
# }, "the time is: #{Time.now}\nnow!\n"
|
91
|
-
#
|
92
|
-
# assert_alike %q{
|
93
|
-
# the time is: :...:
|
94
|
-
# now!
|
95
|
-
# }, "the time is: #{Time.now}\nnow!\n"
|
96
|
-
#
|
97
|
-
# assert_alike %q{
|
98
|
-
# the time is: :...:
|
99
|
-
# now!
|
100
|
-
# }, "the time is: #{Time.now}\nnow!\n"
|
101
|
-
#
|
102
|
-
# Use assert_alike! to prevent indentation stripping (conversion to a
|
103
|
-
# RegexpEscape is still in effect).
|
104
|
-
def assert_alike(a, b, msg=nil)
|
105
|
-
a = strip_indent(a) if a.kind_of?(String)
|
106
|
-
assert_alike!(a, b, msg)
|
117
|
+
|
118
|
+
def use_host(host)
|
119
|
+
@host = host
|
107
120
|
end
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
a = RegexpEscape.new(a) if a.kind_of?(String)
|
112
|
-
|
113
|
-
if b =~ a
|
114
|
-
assert true
|
115
|
-
else
|
116
|
-
flunk %Q{
|
117
|
-
#{msg}
|
118
|
-
================= expected output like ==================
|
119
|
-
#{whitespace_escape(a)}
|
120
|
-
======================== but was ========================
|
121
|
-
#{whitespace_escape(b)}
|
122
|
-
=========================================================
|
123
|
-
}
|
124
|
-
end
|
121
|
+
|
122
|
+
def host
|
123
|
+
@host ||= self.class.host
|
125
124
|
end
|
126
|
-
|
127
|
-
def
|
128
|
-
|
125
|
+
|
126
|
+
def runlist
|
127
|
+
@runlist ||= []
|
128
|
+
end
|
129
|
+
|
130
|
+
def setup_recipe(target_name=package.next_target_name('recipe'), mode=0700, &block)
|
131
|
+
recipe = package.setup_recipe(target_name, mode)
|
132
|
+
helpers.each {|helper| recipe.extend helper }
|
133
|
+
|
134
|
+
recipe.instance_eval(&block) if block_given?
|
135
|
+
runlist << target_name
|
136
|
+
|
137
|
+
@recipe = recipe
|
138
|
+
end
|
139
|
+
|
140
|
+
def recipe
|
141
|
+
@recipe ||= setup_recipe
|
142
|
+
end
|
143
|
+
|
144
|
+
def assert_recipe(expected, recipe=setup_recipe, &block)
|
145
|
+
recipe.instance_eval(&block) if block_given?
|
146
|
+
recipe.close
|
147
|
+
|
129
148
|
assert_output_equal expected, recipe.result
|
149
|
+
recipe
|
130
150
|
end
|
131
151
|
|
132
|
-
def
|
133
|
-
recipe.instance_eval(&block)
|
152
|
+
def assert_recipe_matches(expected, recipe=setup_recipe, &block)
|
153
|
+
recipe.instance_eval(&block) if block_given?
|
154
|
+
recipe.close
|
155
|
+
|
134
156
|
assert_alike expected, recipe.result
|
157
|
+
recipe
|
135
158
|
end
|
136
|
-
|
137
|
-
def
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
159
|
+
|
160
|
+
def build_package(host=self.host)
|
161
|
+
package_dir = path("packages/#{host}")
|
162
|
+
|
163
|
+
package.build
|
164
|
+
package.export package_dir
|
165
|
+
|
166
|
+
package_dir
|
142
167
|
end
|
143
168
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
indent, str = $1, $2, $3
|
150
|
-
|
151
|
-
if indent.length > 0
|
152
|
-
str.gsub!(/^ {0,#{indent.length}}/, '')
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
str
|
169
|
+
def run_package(options={}, host=self.host)
|
170
|
+
options['remote_script'] ||= runlist.join(',')
|
171
|
+
|
172
|
+
build_package host
|
173
|
+
run_project options, host
|
157
174
|
end
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
175
|
+
|
176
|
+
def build_project(options={})
|
177
|
+
options = {
|
178
|
+
'project_dir' => method_dir,
|
179
|
+
'quiet' => true
|
180
|
+
}.merge(options)
|
181
|
+
|
182
|
+
linecook('build', options)
|
183
|
+
end
|
184
|
+
|
185
|
+
# pick up user dir as a gem... bundler!
|
186
|
+
def run_project(options={}, *package_names)
|
187
|
+
options = {
|
188
|
+
'ssh_config_file' => ssh_config_file,
|
189
|
+
'project_dir' => method_dir,
|
190
|
+
'remote_dir' => remote_dir,
|
191
|
+
'quiet' => true,
|
192
|
+
}.merge(options)
|
193
|
+
|
194
|
+
linecook('run', options, *package_names)
|
195
|
+
end
|
196
|
+
|
197
|
+
def linecook(cmd, options={}, *args)
|
198
|
+
stdout = prepare("log/#{cmd}.out")
|
199
|
+
stderr = prepare("log/#{cmd}.err")
|
200
|
+
|
201
|
+
command = "#{linecook_cmd(cmd, options, *args)} 2> '#{stderr}' > '#{stdout}'"
|
202
|
+
system(command)
|
203
|
+
|
204
|
+
[File.read(stdout), "% #{command}\n#{File.read(stderr)}"]
|
205
|
+
end
|
206
|
+
|
207
|
+
def linecook_cmd(cmd, options={}, *args)
|
208
|
+
opts = []
|
209
|
+
options.each_pair do |key, value|
|
210
|
+
key = key.gsub('_', '-')
|
211
|
+
|
212
|
+
case value
|
213
|
+
when true
|
214
|
+
opts << "--#{key}"
|
215
|
+
when nil, false
|
216
|
+
else
|
217
|
+
opts << "--#{key} '#{value}'"
|
168
218
|
end
|
169
219
|
end
|
220
|
+
|
221
|
+
args = args.collect! {|arg| "'#{arg}'" }
|
222
|
+
|
223
|
+
cmd = [LINECOOK, cmd] + opts.sort + args
|
224
|
+
cmd.join(' ')
|
170
225
|
end
|
171
226
|
end
|
172
227
|
end
|
@@ -0,0 +1,75 @@
|
|
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
|
@@ -4,11 +4,101 @@ module Linecook
|
|
4
4
|
module ClassMethods
|
5
5
|
attr_accessor :class_dir
|
6
6
|
|
7
|
-
|
7
|
+
attr_reader :cleanup_method_registry
|
8
|
+
|
9
|
+
def cleanup_methods
|
10
|
+
@cleanup_methods ||= begin
|
11
|
+
cleanup_methods = {}
|
12
|
+
|
13
|
+
ancestors.reverse.each do |ancestor|
|
14
|
+
next unless ancestor.kind_of?(ClassMethods)
|
15
|
+
ancestor.cleanup_method_registry.each_pair do |key, value|
|
16
|
+
if value.nil?
|
17
|
+
cleanup_methods.delete(key)
|
18
|
+
else
|
19
|
+
cleanup_methods[key] = value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
cleanup_methods
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def reset_cleanup_methods
|
29
|
+
@cleanup_methods = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def self.initialize(base)
|
8
35
|
# Infers the test directory from the calling file.
|
9
36
|
# 'some_class_test.rb' => 'some_class_test'
|
10
|
-
calling_file = caller[
|
37
|
+
calling_file = caller[1].gsub(/:\d+(:in .*)?$/, "")
|
11
38
|
base.class_dir = calling_file.chomp(File.extname(calling_file))
|
39
|
+
|
40
|
+
base.reset_cleanup_methods
|
41
|
+
unless base.instance_variable_defined?(:@cleanup_method_registry)
|
42
|
+
base.instance_variable_set(:@cleanup_method_registry, {})
|
43
|
+
end
|
44
|
+
|
45
|
+
unless base.instance_variable_defined?(:@cleanup_paths)
|
46
|
+
base.instance_variable_set(:@cleanup_paths, ['.'])
|
47
|
+
end
|
48
|
+
|
49
|
+
unless base.instance_variable_defined?(:@cleanup)
|
50
|
+
base.instance_variable_set(:@cleanup, true)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def inherited(base) # :nodoc:
|
55
|
+
ClassMethods.initialize(base)
|
56
|
+
super
|
57
|
+
end
|
58
|
+
|
59
|
+
def define_method_cleanup(method_name, dirs)
|
60
|
+
reset_cleanup_methods
|
61
|
+
cleanup_method_registry[method_name.to_sym] = dirs
|
62
|
+
end
|
63
|
+
|
64
|
+
def remove_method_cleanup(method_name)
|
65
|
+
reset_cleanup_methods
|
66
|
+
cleanup_method_registry.delete(method_name.to_sym)
|
67
|
+
end
|
68
|
+
|
69
|
+
def undef_method_cleanup(method_name)
|
70
|
+
reset_cleanup_methods
|
71
|
+
cleanup_method_registry[method_name.to_sym] = nil
|
72
|
+
end
|
73
|
+
|
74
|
+
def cleanup_paths(*dirs)
|
75
|
+
@cleanup_paths = dirs
|
76
|
+
end
|
77
|
+
|
78
|
+
def cleanup(*method_names)
|
79
|
+
if method_names.empty?
|
80
|
+
@cleanup = true
|
81
|
+
else
|
82
|
+
method_names.each do |method_name|
|
83
|
+
define_method_cleanup method_name, @cleanup_paths
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def no_cleanup(*method_names)
|
89
|
+
if method_names.empty?
|
90
|
+
@cleanup = false
|
91
|
+
else
|
92
|
+
method_names.each do |method_name|
|
93
|
+
undef_method_cleanup method_name
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def method_added(sym)
|
99
|
+
if @cleanup && !cleanup_method_registry.has_key?(sym.to_sym) && sym.to_s[0, 5] == "test_"
|
100
|
+
cleanup sym
|
101
|
+
end
|
12
102
|
end
|
13
103
|
end
|
14
104
|
|
@@ -16,61 +106,89 @@ module Linecook
|
|
16
106
|
module_function
|
17
107
|
|
18
108
|
def included(base)
|
19
|
-
base.extend
|
109
|
+
base.extend ClassMethods
|
110
|
+
base.extend ModuleMethods unless base.kind_of?(Class)
|
111
|
+
|
112
|
+
ClassMethods.initialize(base)
|
20
113
|
super
|
21
114
|
end
|
22
115
|
end
|
23
|
-
|
116
|
+
|
24
117
|
extend ModuleMethods
|
25
|
-
|
26
|
-
attr_reader :user_dir
|
27
|
-
attr_reader :method_dir
|
28
|
-
|
118
|
+
|
29
119
|
def setup
|
30
120
|
super
|
31
|
-
|
32
|
-
@method_dir = File.expand_path(method_name, self.class.class_dir)
|
33
|
-
|
34
|
-
cleanup method_dir
|
35
|
-
FileUtils.mkdir_p method_dir
|
36
|
-
Dir.chdir method_dir
|
121
|
+
cleanup
|
37
122
|
end
|
38
|
-
|
39
|
-
def teardown
|
40
|
-
Dir.chdir user_dir
|
41
123
|
|
124
|
+
def teardown
|
125
|
+
Dir.chdir(user_dir)
|
126
|
+
|
42
127
|
unless ENV["KEEP_OUTPUTS"] == "true"
|
43
|
-
cleanup
|
128
|
+
cleanup
|
129
|
+
|
130
|
+
dir = method_dir
|
131
|
+
while dir != class_dir
|
132
|
+
dir = File.dirname(dir)
|
133
|
+
Dir.rmdir(dir)
|
134
|
+
end rescue(SystemCallError)
|
44
135
|
end
|
45
|
-
|
136
|
+
|
46
137
|
super
|
47
138
|
end
|
48
|
-
|
49
|
-
def
|
50
|
-
|
139
|
+
|
140
|
+
def user_dir
|
141
|
+
@user_dir ||= File.expand_path('.')
|
51
142
|
end
|
52
|
-
|
143
|
+
|
144
|
+
def class_dir
|
145
|
+
@class_dir ||= File.expand_path(self.class.class_dir, user_dir)
|
146
|
+
end
|
147
|
+
|
148
|
+
def method_dir
|
149
|
+
@method_dir ||= File.expand_path(method_name.to_s, class_dir)
|
150
|
+
end
|
151
|
+
|
152
|
+
def cleanup_methods
|
153
|
+
self.class.cleanup_methods
|
154
|
+
end
|
155
|
+
|
156
|
+
def cleanup
|
157
|
+
if cleanup_paths = cleanup_methods[method_name.to_sym]
|
158
|
+
cleanup_paths.each {|relative_path| remove(relative_path) }
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
53
162
|
def path(relative_path)
|
54
|
-
File.expand_path(relative_path, method_dir)
|
163
|
+
path = File.expand_path(relative_path, method_dir)
|
164
|
+
|
165
|
+
unless path.index(method_dir) == 0
|
166
|
+
raise "does not make a path relative to method_dir: #{relative_path.inspect}"
|
167
|
+
end
|
168
|
+
|
169
|
+
path
|
55
170
|
end
|
56
171
|
|
57
|
-
def prepare(relative_path)
|
172
|
+
def prepare(relative_path, content=nil, &block)
|
58
173
|
target = path(relative_path)
|
59
174
|
|
60
|
-
|
61
|
-
|
175
|
+
if File.exists?(target)
|
176
|
+
FileUtils.rm(target)
|
177
|
+
else
|
178
|
+
target_dir = File.dirname(target)
|
179
|
+
FileUtils.mkdir_p(target_dir) unless File.exists?(target_dir)
|
180
|
+
end
|
181
|
+
|
182
|
+
FileUtils.touch(target)
|
183
|
+
File.open(target, 'w') {|io| io << content } if content
|
184
|
+
File.open(target, 'a', &block) if block
|
62
185
|
|
63
186
|
target
|
64
187
|
end
|
65
188
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
target
|
70
|
-
end
|
71
|
-
|
72
|
-
def class_dir
|
73
|
-
self.class.class_dir
|
189
|
+
def remove(relative_path)
|
190
|
+
full_path = path(relative_path)
|
191
|
+
FileUtils.rm_r(full_path) if File.exists?(full_path)
|
74
192
|
end
|
75
193
|
end
|
76
194
|
end
|