capistrano 2.4.3 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/CHANGELOG.rdoc +39 -0
  2. data/capistrano.gemspec +5 -5
  3. data/lib/capistrano/cli/execute.rb +1 -0
  4. data/lib/capistrano/cli/help.txt +6 -0
  5. data/lib/capistrano/cli/options.rb +26 -0
  6. data/lib/capistrano/cli/ui.rb +0 -1
  7. data/lib/capistrano/command.rb +175 -47
  8. data/lib/capistrano/configuration.rb +2 -1
  9. data/lib/capistrano/configuration/actions/invocation.rb +38 -7
  10. data/lib/capistrano/configuration/connections.rb +12 -6
  11. data/lib/capistrano/configuration/execution.rb +2 -1
  12. data/lib/capistrano/configuration/servers.rb +5 -4
  13. data/lib/capistrano/recipes/deploy.rb +45 -22
  14. data/lib/capistrano/recipes/deploy/local_dependency.rb +7 -3
  15. data/lib/capistrano/recipes/deploy/scm/cvs.rb +2 -1
  16. data/lib/capistrano/recipes/deploy/scm/none.rb +1 -1
  17. data/lib/capistrano/recipes/deploy/scm/subversion.rb +1 -1
  18. data/lib/capistrano/recipes/deploy/strategy/copy.rb +1 -1
  19. data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +10 -1
  20. data/lib/capistrano/role.rb +4 -0
  21. data/lib/capistrano/version.rb +2 -2
  22. data/test/cli/execute_test.rb +1 -1
  23. data/test/cli/options_test.rb +84 -0
  24. data/test/command_test.rb +38 -41
  25. data/test/configuration/actions/invocation_test.rb +12 -16
  26. data/test/configuration/connections_test.rb +30 -9
  27. data/test/configuration/execution_test.rb +16 -0
  28. data/test/configuration/servers_test.rb +15 -0
  29. data/test/configuration_test.rb +8 -1
  30. data/test/deploy/local_dependency_test.rb +15 -12
  31. data/test/deploy/scm/none_test.rb +35 -0
  32. data/test/deploy/strategy/copy_test.rb +13 -0
  33. metadata +3 -2
@@ -23,21 +23,27 @@ module Capistrano
23
23
 
24
24
  class GatewayConnectionFactory #:nodoc:
25
25
  def initialize(gateway, options)
26
- Thread.abort_on_exception = true
27
- server = ServerDefinition.new(gateway)
28
-
29
26
  @options = options
30
- @gateway = SSH.connection_strategy(server, options) do |host, user, connect_options|
27
+ @options[:logger].debug "Creating gateway using #{[*gateway].join(', ')}" if @options[:logger]
28
+ Thread.abort_on_exception = true
29
+ @gateways = [*gateway].collect { |g| ServerDefinition.new(g) }
30
+ tunnel = SSH.connection_strategy(@gateways[0], @options) do |host, user, connect_options|
31
31
  Net::SSH::Gateway.new(host, user, connect_options)
32
32
  end
33
+ @gateway = (@gateways[1..-1]).inject(tunnel) do |tunnel, destination|
34
+ @options[:logger].debug "Creating tunnel to #{destination}" if @options[:logger]
35
+ local_host = ServerDefinition.new("127.0.0.1", :user => destination.user, :port => tunnel.open(destination.host, (destination.port || 22)))
36
+ SSH.connection_strategy(local_host, @options) do |host, user, connect_options|
37
+ Net::SSH::Gateway.new(host, user, connect_options)
38
+ end
39
+ end
33
40
  end
34
-
41
+
35
42
  def connect_to(server)
36
43
  @options[:logger].debug "establishing connection to `#{server}' via gateway" if @options[:logger]
37
44
  local_host = ServerDefinition.new("127.0.0.1", :user => server.user, :port => @gateway.open(server.host, server.port || 22))
38
45
  session = SSH.connect(local_host, @options)
39
46
  session.xserver = server
40
- @options[:logger].trace "connected: `#{server}' (via gateway)" if @options[:logger]
41
47
  session
42
48
  end
43
49
  end
@@ -60,8 +60,9 @@ module Capistrano
60
60
  # hook will be executed.
61
61
  def on_rollback(&block)
62
62
  if transaction?
63
+ # don't note a new rollback request if one has already been set
64
+ rollback_requests << task_call_frames.last unless task_call_frames.last.rollback
63
65
  task_call_frames.last.rollback = block
64
- rollback_requests << task_call_frames.last
65
66
  end
66
67
  end
67
68
 
@@ -35,13 +35,14 @@ module Capistrano
35
35
  # servers = find_servers :hosts => "jamis@example.host.com"
36
36
  def find_servers(options={})
37
37
  hosts = server_list_from(ENV['HOSTS'] || options[:hosts])
38
- roles = role_list_from(ENV['ROLES'] || options[:roles] || self.roles.keys)
39
- only = options[:only] || {}
40
- except = options[:except] || {}
41
-
38
+
42
39
  if hosts.any?
43
40
  hosts.uniq
44
41
  else
42
+ roles = role_list_from(ENV['ROLES'] || options[:roles] || self.roles.keys)
43
+ only = options[:only] || {}
44
+ except = options[:except] || {}
45
+
45
46
  servers = roles.inject([]) { |list, role| list.concat(self.roles[role]) }
46
47
  servers = servers.select { |server| only.all? { |key,value| server.options[key] == value } }
47
48
  servers = servers.reject { |server| except.any? { |key,value| server.options[key] == value } }
@@ -42,6 +42,7 @@ _cset(:release_name) { set :deploy_timestamped, true; Time.now.utc.strftime
42
42
 
43
43
  _cset :version_dir, "releases"
44
44
  _cset :shared_dir, "shared"
45
+ _cset :shared_children, %w(system log pids)
45
46
  _cset :current_dir, "current"
46
47
 
47
48
  _cset(:releases_path) { File.join(deploy_to, version_dir) }
@@ -49,7 +50,7 @@ _cset(:shared_path) { File.join(deploy_to, shared_dir) }
49
50
  _cset(:current_path) { File.join(deploy_to, current_dir) }
50
51
  _cset(:release_path) { File.join(releases_path, release_name) }
51
52
 
52
- _cset(:releases) { capture("ls -x #{releases_path}").split.sort }
53
+ _cset(:releases) { capture("ls -xt #{releases_path}").split.reverse }
53
54
  _cset(:current_release) { File.join(releases_path, releases.last) }
54
55
  _cset(:previous_release) { File.join(releases_path, releases[-2]) }
55
56
 
@@ -160,7 +161,7 @@ namespace :deploy do
160
161
  DESC
161
162
  task :setup, :except => { :no_release => true } do
162
163
  dirs = [deploy_to, releases_path, shared_path]
163
- dirs += %w(system log pids).map { |d| File.join(shared_path, d) }
164
+ dirs += shared_children.map { |d| File.join(shared_path, d) }
164
165
  run "#{try_sudo} mkdir -p #{dirs.join(' ')} && #{try_sudo} chmod g+w #{dirs.join(' ')}"
165
166
  end
166
167
 
@@ -287,28 +288,50 @@ namespace :deploy do
287
288
  try_runner "#{current_path}/script/process/reaper"
288
289
  end
289
290
 
290
- desc <<-DESC
291
- Rolls back to the previously deployed version. The `current' symlink will \
292
- be updated to point at the previously deployed version, and then the \
293
- current release will be removed from the servers. You'll generally want \
294
- to call `rollback' instead, as it performs a `restart' as well.
295
- DESC
296
- task :rollback_code, :except => { :no_release => true } do
297
- if releases.length < 2
298
- abort "could not rollback the code because there is no prior release"
299
- else
300
- run "rm #{current_path}; ln -s #{previous_release} #{current_path} && rm -rf #{current_release}"
291
+ namespace :rollback do
292
+ desc <<-DESC
293
+ [internal] Points the current symlink at the previous revision.
294
+ This is called by the rollback sequence, and should rarely (if
295
+ ever) need to be called directly.
296
+ DESC
297
+ task :revision, :except => { :no_release => true } do
298
+ if releases.length < 2
299
+ abort "could not rollback the code because there is no prior release"
300
+ else
301
+ run "rm #{current_path}; ln -s #{previous_release} #{current_path}"
302
+ end
301
303
  end
302
- end
303
304
 
304
- desc <<-DESC
305
- Rolls back to a previous version and restarts. This is handy if you ever \
306
- discover that you've deployed a lemon; `cap rollback' and you're right \
307
- back where you were, on the previously deployed version.
308
- DESC
309
- task :rollback do
310
- rollback_code
311
- restart
305
+ desc <<-DESC
306
+ [internal] Removes the most recently deployed release.
307
+ This is called by the rollback sequence, and should rarely
308
+ (if ever) need to be called directly.
309
+ DESC
310
+ task :cleanup, :except => { :no_release => true } do
311
+ run "if [ `readlink #{current_path}` != #{current_release} ]; then rm -rf #{current_release}; fi"
312
+ end
313
+
314
+ desc <<-DESC
315
+ Rolls back to the previously deployed version. The `current' symlink will \
316
+ be updated to point at the previously deployed version, and then the \
317
+ current release will be removed from the servers. You'll generally want \
318
+ to call `rollback' instead, as it performs a `restart' as well.
319
+ DESC
320
+ task :code, :except => { :no_release => true } do
321
+ revision
322
+ cleanup
323
+ end
324
+
325
+ desc <<-DESC
326
+ Rolls back to a previous version and restarts. This is handy if you ever \
327
+ discover that you've deployed a lemon; `cap rollback' and you're right \
328
+ back where you were, on the previously deployed version.
329
+ DESC
330
+ task :default do
331
+ revision
332
+ restart
333
+ cleanup
334
+ end
312
335
  end
313
336
 
314
337
  desc <<-DESC
@@ -30,7 +30,7 @@ module Capistrano
30
30
  # file is found that matches the parameter, this returns true.
31
31
  def find_in_path(utility)
32
32
  path = (ENV['PATH'] || "").split(File::PATH_SEPARATOR)
33
- suffixes = self.class.on_windows? ? %w(.bat .exe .com .cmd) : [""]
33
+ suffixes = self.class.on_windows? ? self.class.windows_executable_extensions : [""]
34
34
 
35
35
  path.each do |dir|
36
36
  suffixes.each do |sfx|
@@ -41,9 +41,13 @@ module Capistrano
41
41
 
42
42
  false
43
43
  end
44
-
44
+
45
45
  def self.on_windows?
46
- RUBY_PLATFORM =~ /mswin/
46
+ RUBY_PLATFORM =~ /mswin|mingw/
47
+ end
48
+
49
+ def self.windows_executable_extensions
50
+ %w(.exe .bat .com .cmd)
47
51
  end
48
52
  end
49
53
  end
@@ -71,7 +71,7 @@ module Capistrano
71
71
  revision = yield(scm(cvs_root, :log, "-r#{revision}")).
72
72
  grep(/^date:/).
73
73
  map { |line| line[/^date: (.*?);/, 1] }.
74
- sort.last
74
+ sort.last + " UTC"
75
75
  return revision
76
76
  end
77
77
 
@@ -111,6 +111,7 @@ module Capistrano
111
111
 
112
112
  # attempts to guess what type of revision we're working with
113
113
  def revision_type(rev)
114
+ return :date if rev =~ /^\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2} UTC$/ # i.e 2007-05-15 08:13:25 UTC
114
115
  return :date if rev =~ /^\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2}$/ # i.e 2007-05-15 08:13:25
115
116
  return :revision if rev =~ /^\d/ # i.e. 1.2.1
116
117
  return :tag # i.e. RELEASE_1_2
@@ -27,7 +27,7 @@ module Capistrano
27
27
  # Simply does a copy from the :repository directory to the
28
28
  # :destination directory.
29
29
  def checkout(revision, destination)
30
- "cp -R #{repository} #{destination}"
30
+ !Capistrano::Deploy::LocalDependency.on_windows? ? "cp -R #{repository} #{destination}" : "xcopy #{repository} \"#{destination}\" /S/I/Y/Q/E"
31
31
  end
32
32
 
33
33
  alias_method :export, :checkout
@@ -98,7 +98,7 @@ module Capistrano
98
98
  username = variable(:scm_username)
99
99
  return "" unless username
100
100
  result = "--username #{variable(:scm_username)} "
101
- result << "--password #{variable(:scm_password)} " unless variable(:scm_prefer_prompt)
101
+ result << "--password #{variable(:scm_password)} " unless variable(:scm_auth_cache) || variable(:scm_prefer_prompt)
102
102
  result << "--no-auth-cache " unless variable(:scm_auth_cache)
103
103
  result
104
104
  end
@@ -80,7 +80,7 @@ module Capistrano
80
80
 
81
81
  if copy_exclude.any?
82
82
  logger.debug "processing exclusions..."
83
- copy_exclude.each { |pattern| FileUtils.rm_rf(File.join(destination, pattern)) }
83
+ copy_exclude.each { |pattern| FileUtils.rm_rf(Dir.glob(File.join(destination, pattern), File::FNM_DOTMATCH)) }
84
84
  end
85
85
  end
86
86
 
@@ -38,7 +38,16 @@ module Capistrano
38
38
 
39
39
  def copy_repository_cache
40
40
  logger.trace "copying the cached version to #{configuration[:release_path]}"
41
- run "cp -RPp #{repository_cache} #{configuration[:release_path]} && #{mark}"
41
+ if copy_exclude.empty?
42
+ run "cp -RPp #{repository_cache} #{configuration[:release_path]} && #{mark}"
43
+ else
44
+ exclusions = copy_exclude.map { |e| "--exclude=\"#{e}\"" }.join(' ')
45
+ run "rsync -rp #{exclusions} #{repository_cache}/* #{configuration[:release_path]} && #{mark}"
46
+ end
47
+ end
48
+
49
+ def copy_exclude
50
+ @copy_exclude ||= Array(configuration.fetch(:copy_exclude, []))
42
51
  end
43
52
  end
44
53
 
@@ -38,6 +38,10 @@ module Capistrano
38
38
  @static_servers.clear
39
39
  end
40
40
 
41
+ def include?(server)
42
+ servers.include?(server)
43
+ end
44
+
41
45
  protected
42
46
 
43
47
  # This is the combination of a block, a hash of options, and a cached value.
@@ -5,8 +5,8 @@ module Capistrano
5
5
  # Describes the current version of Capistrano.
6
6
  class Version < Net::SSH::Version
7
7
  MAJOR = 2
8
- MINOR = 4
9
- TINY = 3
8
+ MINOR = 5
9
+ TINY = 0
10
10
 
11
11
  # The current version, as a Version instance
12
12
  CURRENT = new(MAJOR, MINOR, TINY)
@@ -15,7 +15,7 @@ class CLIExecuteTest < Test::Unit::TestCase
15
15
  def setup
16
16
  @cli = MockCLI.new
17
17
  @logger = stub_everything
18
- @config = stub(:logger => @logger, :debug= => nil)
18
+ @config = stub(:logger => @logger, :debug= => nil, :dry_run= => nil)
19
19
  @config.stubs(:set)
20
20
  @config.stubs(:load)
21
21
  @config.stubs(:trigger)
@@ -30,6 +30,18 @@ class CLIOptionsTest < Test::Unit::TestCase
30
30
  assert @cli.options[:debug]
31
31
  end
32
32
 
33
+ def test_parse_options_with_n_should_set_dry_run_option
34
+ @cli.args << "-n"
35
+ @cli.parse_options!
36
+ assert @cli.options[:dry_run]
37
+ end
38
+
39
+ def test_parse_options_with_dry_run_should_set_dry_run_option
40
+ @cli.args << "--dry-run"
41
+ @cli.parse_options!
42
+ assert @cli.options[:dry_run]
43
+ end
44
+
33
45
  def test_parse_options_with_e_should_set_explain_option
34
46
  @cli.args << "-e" << "sample"
35
47
  @cli.parse_options!
@@ -87,12 +99,84 @@ class CLIOptionsTest < Test::Unit::TestCase
87
99
  assert_equal "bar", @cli.options[:pre_vars][:foo]
88
100
  end
89
101
 
102
+ def test_S_should_coerce_digits_to_integers
103
+ @cli.args << "-S" << "foo=1234"
104
+ @cli.parse_options!
105
+ assert_equal 1234, @cli.options[:pre_vars][:foo]
106
+ end
107
+
108
+ def test_S_should_treat_quoted_integers_as_string
109
+ @cli.args << "-S" << "foo=\"1234\""
110
+ @cli.parse_options!
111
+ assert_equal "1234", @cli.options[:pre_vars][:foo]
112
+ end
113
+
114
+ def test_S_should_treat_digits_with_dot_as_floating_point
115
+ @cli.args << "-S" << "foo=3.1415"
116
+ @cli.parse_options!
117
+ assert_equal 3.1415, @cli.options[:pre_vars][:foo]
118
+ end
119
+
120
+ def test_S_should_treat_true_as_boolean_true
121
+ @cli.args << "-S" << "foo=true"
122
+ @cli.parse_options!
123
+ assert_equal true, @cli.options[:pre_vars][:foo]
124
+ end
125
+
126
+ def test_S_should_treat_false_as_boolean_false
127
+ @cli.args << "-S" << "foo=false"
128
+ @cli.parse_options!
129
+ assert_equal false, @cli.options[:pre_vars][:foo]
130
+ end
131
+
132
+ def test_S_should_treat_nil_as_nil
133
+ @cli.args << "-S" << "foo=nil"
134
+ @cli.parse_options!
135
+ assert_equal nil, @cli.options[:pre_vars][:foo]
136
+ end
137
+
90
138
  def test_parse_options_with_s_should_set_vars
91
139
  @cli.args << "-s" << "foo=bar"
92
140
  @cli.parse_options!
93
141
  assert_equal "bar", @cli.options[:vars][:foo]
94
142
  end
95
143
 
144
+ def test_s_should_coerce_digits_to_integers
145
+ @cli.args << "-s" << "foo=1234"
146
+ @cli.parse_options!
147
+ assert_equal 1234, @cli.options[:vars][:foo]
148
+ end
149
+
150
+ def test_s_should_treat_quoted_integers_as_string
151
+ @cli.args << "-s" << "foo=\"1234\""
152
+ @cli.parse_options!
153
+ assert_equal "1234", @cli.options[:vars][:foo]
154
+ end
155
+
156
+ def test_s_should_treat_digits_with_dot_as_floating_point
157
+ @cli.args << "-s" << "foo=3.1415"
158
+ @cli.parse_options!
159
+ assert_equal 3.1415, @cli.options[:vars][:foo]
160
+ end
161
+
162
+ def test_s_should_treat_true_as_boolean_true
163
+ @cli.args << "-s" << "foo=true"
164
+ @cli.parse_options!
165
+ assert_equal true, @cli.options[:vars][:foo]
166
+ end
167
+
168
+ def test_s_should_treat_false_as_boolean_false
169
+ @cli.args << "-s" << "foo=false"
170
+ @cli.parse_options!
171
+ assert_equal false, @cli.options[:vars][:foo]
172
+ end
173
+
174
+ def test_s_should_treat_nil_as_nil
175
+ @cli.args << "-s" << "foo=nil"
176
+ @cli.parse_options!
177
+ assert_equal nil, @cli.options[:vars][:foo]
178
+ end
179
+
96
180
  def test_parse_options_with_T_should_set_tasks_option_and_set_verbose_off
97
181
  @cli.args << "-T"
98
182
  @cli.parse_options!
@@ -1,22 +1,21 @@
1
1
  require "utils"
2
2
  require 'capistrano/command'
3
+ require 'capistrano/configuration'
3
4
 
4
5
  class CommandTest < Test::Unit::TestCase
5
6
  def test_command_should_open_channels_on_all_sessions
6
- s1 = mock(:open_channel => nil)
7
- s2 = mock(:open_channel => nil)
8
- s3 = mock(:open_channel => nil)
9
- assert_equal "ls", Capistrano::Command.new("ls", [s1, s2, s3]).command
7
+ s1, s2, s3 = mock_session, mock_session, mock_session
8
+ assert_equal "ls", Capistrano::Command.new("ls", [s1, s2, s3]).tree.fallback.command
10
9
  end
11
10
 
12
11
  def test_command_with_newlines_should_be_properly_escaped
13
- cmd = Capistrano::Command.new("ls\necho", [mock(:open_channel => nil)])
14
- assert_equal "ls\\\necho", cmd.command
12
+ cmd = Capistrano::Command.new("ls\necho", [mock_session])
13
+ assert_equal "ls\\\necho", cmd.tree.fallback.command
15
14
  end
16
15
 
17
16
  def test_command_with_windows_newlines_should_be_properly_escaped
18
- cmd = Capistrano::Command.new("ls\r\necho", [mock(:open_channel => nil)])
19
- assert_equal "ls\\\necho", cmd.command
17
+ cmd = Capistrano::Command.new("ls\r\necho", [mock_session])
18
+ assert_equal "ls\\\necho", cmd.tree.fallback.command
20
19
  end
21
20
 
22
21
  def test_command_with_pty_should_request_pty_and_register_success_callback
@@ -76,25 +75,17 @@ class CommandTest < Test::Unit::TestCase
76
75
  end
77
76
 
78
77
  def test_open_channel_should_set_host_key_on_channel
79
- session = mock(:xserver => server("capistrano"))
80
- channel = stub_everything
81
-
82
- session.expects(:open_channel).yields(channel)
83
- channel.expects(:[]=).with(:host, "capistrano")
84
- channel.stubs(:[]).with(:host).returns("capistrano")
85
-
78
+ channel = nil
79
+ session = setup_for_extracting_channel_action { |ch| channel = ch }
86
80
  Capistrano::Command.new("ls", [session])
81
+ assert_equal "capistrano", channel[:host]
87
82
  end
88
83
 
89
84
  def test_open_channel_should_set_options_key_on_channel
90
- session = mock(:xserver => server("capistrano"))
91
- channel = stub_everything
92
-
93
- session.expects(:open_channel).yields(channel)
94
- channel.expects(:[]=).with(:options, {:data => "here we go"})
95
- channel.stubs(:[]).with(:host).returns("capistrano")
96
-
85
+ channel = nil
86
+ session = setup_for_extracting_channel_action { |ch| channel = ch }
97
87
  Capistrano::Command.new("ls", [session], :data => "here we go")
88
+ assert_equal({ :data => 'here we go' }, channel[:options])
98
89
  end
99
90
 
100
91
  def test_successful_channel_should_send_command
@@ -157,17 +148,17 @@ class CommandTest < Test::Unit::TestCase
157
148
 
158
149
  def test_on_request_should_record_exit_status
159
150
  data = mock(:read_long => 5)
160
- session = setup_for_extracting_channel_action([:on_request, "exit-status"], data) do |ch|
161
- ch.expects(:[]=).with(:status, 5)
162
- end
151
+ channel = nil
152
+ session = setup_for_extracting_channel_action([:on_request, "exit-status"], data) { |ch| channel = ch }
163
153
  Capistrano::Command.new("ls", [session])
154
+ assert_equal 5, channel[:status]
164
155
  end
165
156
 
166
157
  def test_on_close_should_set_channel_closed
167
- session = setup_for_extracting_channel_action(:on_close) do |ch|
168
- ch.expects(:[]=).with(:closed, true)
169
- end
158
+ channel = nil
159
+ session = setup_for_extracting_channel_action(:on_close) { |ch| channel = ch }
170
160
  Capistrano::Command.new("ls", [session])
161
+ assert channel[:closed]
171
162
  end
172
163
 
173
164
  def test_stop_should_close_all_open_channels
@@ -228,10 +219,8 @@ class CommandTest < Test::Unit::TestCase
228
219
 
229
220
  def test_process_should_instantiate_command_and_process!
230
221
  cmd = mock("command", :process! => nil)
231
- Capistrano::Command.expects(:new).with("ls -l", %w(a b c), {:foo => "bar"}).yields(:command).returns(cmd)
232
- parameter = nil
233
- Capistrano::Command.process("ls -l", %w(a b c), :foo => "bar") { |cmd| parameter = cmd }
234
- assert_equal :command, parameter
222
+ Capistrano::Command.expects(:new).with("ls -l", %w(a b c), {:foo => "bar"}).returns(cmd)
223
+ Capistrano::Command.process("ls -l", %w(a b c), :foo => "bar")
235
224
  end
236
225
 
237
226
  def test_process_with_host_placeholder_should_substitute_placeholder_with_each_host
@@ -254,16 +243,20 @@ class CommandTest < Test::Unit::TestCase
254
243
  stub('session', :open_channel => channel,
255
244
  :preprocess => true,
256
245
  :postprocess => true,
257
- :listeners => {})
246
+ :listeners => {},
247
+ :xserver => server("capistrano"))
248
+ end
249
+
250
+ class MockChannel < Hash
251
+ def close
252
+ end
258
253
  end
259
254
 
260
255
  def new_channel(closed, status=nil)
261
- ch = mock("channel")
262
- ch.expects(:[]).with(:closed).returns(closed)
263
- ch.expects(:[]).with(:status).returns(status) if status
256
+ ch = MockChannel.new
257
+ ch.update({ :closed => closed, :host => "capistrano", :server => server("capistrano") })
258
+ ch[:status] = status if status
264
259
  ch.expects(:close) unless closed
265
- ch.stubs(:[]).with(:host).returns("capistrano")
266
- ch.stubs(:[]).with(:server).returns(server("capistrano"))
267
260
  ch
268
261
  end
269
262
 
@@ -271,11 +264,15 @@ class CommandTest < Test::Unit::TestCase
271
264
  s = server("capistrano")
272
265
  session = mock("session", :xserver => s)
273
266
 
274
- channel = stub_everything
267
+ channel = {}
275
268
  session.expects(:open_channel).yields(channel)
276
269
 
277
- channel.stubs(:[]).with(:server).returns(s)
278
- channel.stubs(:[]).with(:host).returns(s.host)
270
+ channel.stubs(:on_data)
271
+ channel.stubs(:on_extended_data)
272
+ channel.stubs(:on_request)
273
+ channel.stubs(:on_close)
274
+ channel.stubs(:exec)
275
+ channel.stubs(:send_data)
279
276
 
280
277
  if action
281
278
  action = Array(action)