Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'freakhill-slightly-better-specs' into 0.4
Conflicts: spec/em-ssh_spec.rb
- Loading branch information
Showing
5 changed files
with
196 additions
and
120 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
module EM | ||
class Ssh | ||
module Test | ||
|
||
# Hold would-be-hardcoded values used for testing, constants in regard to the actual testing. | ||
module Constants | ||
|
||
# Previously, values used for tests (urls, credentials, ips, etc.) were hardcoded | ||
# and spread through the test codebase. | ||
# For a slight improvement, this class match previously hardcoded constants to environment variables, | ||
# use these environment variables if present, and if not rely on hardcoded values provided when | ||
# said constants were declared (via #add_field(constant_name, default_hardcoded_value). | ||
class EnvElseHardcoded | ||
|
||
def initialize(header, target) | ||
@header = header | ||
@target = target | ||
end | ||
|
||
# Sets an accessor for 'name', sets its value as the one from ENV["#@header#{name}".upcase], | ||
# but if nil, sets is value as parameter 'default7 (default: ''). &blk is called on the chosen value. | ||
# @param[#to_s] name Name of the accessor to create. Be careful with field names that would break a ruby object | ||
# (like :method_missing for instance). | ||
# @param[Object] default Value to use if associed ENV value is not set | ||
# @block Called on chosen value | ||
# @example | ||
# DEVICE1 = EnvElseHardcoded.new("THAT_DEVICE_") | ||
# DEVICE1.add_field(:some_kind_of_conf, 'default_hardcoded_value') # matched to the environment variable "THAT_DEVICE_SOME_KIND_OF_CONF" | ||
# # if the environment variable exists it will use the value it holds, else it will use 'default_hardcoded_value' | ||
# # similarily | ||
# DEVICE2 = EnvElseHardcoded.new("THAT_OTHER_DEVICE_") | ||
# DEVICE2.add_field(:some_other_kind_of_integer_conf, '1', &:to_i) # in this case the block will convert the environment value to an integer | ||
# # note, that if no environment variable is provided, the default hardcoded value will also be passed through the block. | ||
# DEVICE2.add_field(:ahahah, '2') { |i| i.to_i } # this works_too | ||
# # also note that the default hardcoded value defaults to ''. | ||
def add_field(name, default='', &blk) | ||
const_name = "#{@header}#{name}".upcase | ||
value = ENV[const_name] || ENV["#@header#{name}".upcase] || default | ||
class << self; self; end.instance_eval { attr_accessor name } | ||
send("#{name}=", blk ? blk.call(value) : value) | ||
@target.const_set(const_name, value) | ||
end | ||
end | ||
|
||
### remote server 1 | ||
REMOTE1 = EnvElseHardcoded.new("REMOTE1_", self) | ||
REMOTE1.add_field(:ip, '192.168.92.11') | ||
REMOTE1.add_field(:username, 'caleb') | ||
REMOTE1.add_field(:prompt) | ||
### remote server 2 | ||
REMOTE2 = EnvElseHardcoded.new("REMOTE2_", self) | ||
REMOTE2.add_field(:url, 'icaleb.org') | ||
REMOTE2.add_field(:username, 'calebcrane') | ||
REMOTE2.add_field(:prompt, ']$') | ||
REMOTE2.add_field(:timeout, 2, &:to_i) | ||
REMOTE2.add_field(:uname_a, "Linux icaleb 2.6.18-194.3.1.el5 #1 SMP Thu May 13 13:08:30 EDT 2010 x86_64 x86_64 x86_64 GNU/Linux\n") | ||
|
||
end # module Constants | ||
|
||
end # module Test | ||
end # module Ssh | ||
end # module EM |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,53 @@ | ||
#!/usr/bin/env ruby | ||
require 'bundler/setup' | ||
require 'em-ssh' | ||
require 'rspec' | ||
require_relative "spec_helper" | ||
|
||
describe "EM::Ssh" do | ||
it "should be addressable through EM::P and EM::Protocols" do | ||
EM::P.const_defined?(:Ssh).should be true | ||
EM::Protocols.const_defined?(:Ssh).should be true | ||
EM::P::Ssh.should == EM::Ssh | ||
EM::Protocols::Ssh.should == EM::Ssh | ||
end | ||
it "should raise a ConnectionTimeout error when a connection can't be established before the given timeout" do | ||
expect { | ||
EM.run { | ||
EM::Ssh.start('192.168.92.11', 'caleb', :timeout => 1) do |ssh| | ||
ssh.callback { EM.stop } | ||
ssh.errback{|e| raise e } | ||
end | ||
} | ||
}.to raise_error(EM::Ssh::ConnectionTimeout) | ||
end # should raise a ConnectionTimeout error when a connection can't be established before the given timeout | ||
it "should raise a ConnectionError when the address is invalid" do | ||
expect { | ||
|
||
module EM::Ssh::Test | ||
include Constants | ||
|
||
describe "EM::Ssh" do | ||
it "should be addressable through EM::P and EM::Protocols" do | ||
EM::P.const_defined?(:Ssh).should be true | ||
EM::Protocols.const_defined?(:Ssh).should be true | ||
EM::P::Ssh.should == EM::Ssh | ||
EM::Protocols::Ssh.should == EM::Ssh | ||
end | ||
it "should raise a ConnectionTimeout error when a connection can't be established before the given timeout" do | ||
expect { | ||
EM.run { | ||
EM::Ssh.start(REMOTE1_IP, REMOTE1_USERNAME, :timeout => 1) do |ssh| | ||
ssh.callback { EM.stop } | ||
ssh.errback{|e| raise e } | ||
end | ||
} | ||
}.to raise_error(EM::Ssh::ConnectionTimeout) | ||
end # should raise a ConnectionTimeout error when a connection can't be established before the given timeout | ||
it "should raise a ConnectionError when the address is invalid" do | ||
expect { | ||
EM.run { | ||
EM::Ssh.start('0.0.0.1', 'caleb') do |ssh| # 0.0.0.1 is an invalid address | ||
ssh.callback { EM.stop } | ||
ssh.errback { |e| raise(e) } | ||
end | ||
} | ||
}.to raise_error(EM::ConnectionError) | ||
end # should raise a ConnectionFailed when the address is invalid | ||
|
||
it "should run exec! succesfully" do | ||
res = "" | ||
EM.run { | ||
EM::Ssh.start('0.0.0.1', 'caleb') do |ssh| | ||
ssh.callback { EM.stop } | ||
ssh.errback { |e| raise(e) } | ||
EM::Ssh.start(REMOTE2_URL, REMOTE2_USERNAME) do |con| | ||
con.errback do |err| | ||
raise err | ||
end | ||
con.callback do |ssh| | ||
res = ssh.exec!("uname -a") | ||
ssh.close | ||
EM.stop | ||
end | ||
end | ||
} | ||
}.to raise_error(EM::ConnectionError) | ||
end # should raise a ConnectionFailed when the address is invalid | ||
|
||
it "should run exec! succesfully" do | ||
res = "" | ||
EM.run { | ||
EM::Ssh.start('icaleb.org', 'calebcrane') do |con| | ||
con.errback do |err| | ||
raise err | ||
end | ||
con.callback do |ssh| | ||
res = ssh.exec!("uname -a") | ||
ssh.close | ||
EM.stop | ||
end | ||
end | ||
} | ||
res.should include("Linux icaleb") | ||
end | ||
end # EM::Ssh | ||
res.should == REMOTE2_UNAME_A | ||
end | ||
end # EM::Ssh | ||
end # module::EM::Ssh::Test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,92 +1,96 @@ | ||
#!/usr/bin/env ruby | ||
require 'bundler/setup' | ||
require 'em-ssh/shell' | ||
require 'rspec' | ||
require_relative "spec_helper" | ||
|
||
describe "Ssh::Shell" do | ||
it "should return a shell" do | ||
EM.run { | ||
Fiber.new { | ||
timer = EM::Timer.new(2) { raise "failed #{$0}" } | ||
shell = EM::Ssh::Shell.new('icaleb.org', 'calebcrane', "") | ||
shell.callback do | ||
shell.should be_a(EventMachine::Ssh::Shell) | ||
shell.wait_for(Regexp.escape(']$')) | ||
shell.send_and_wait('uname -a', Regexp.escape(']$')).should include("GNU/Linux") | ||
timer.cancel | ||
EM.stop | ||
end | ||
shell.errback { EM.stop } | ||
}.resume | ||
} | ||
end # should return a shell | ||
|
||
it "should yield a shell" do | ||
EM.run { | ||
timer = EM::Timer.new(4) { raise "failed #{$0}" } | ||
EM::Ssh::Shell.new('icaleb.org', 'calebcrane', "") do |shell| | ||
shell.callback do | ||
shell.should be_a(EventMachine::Ssh::Shell) | ||
shell.wait_for(Regexp.escape(']$')) | ||
shell.send_and_wait('uname -a', Regexp.escape(']$')).should include("GNU/Linux") | ||
shell.send_and_wait('/sbin/ifconfig -a', Regexp.escape(']$')).should include("eth0") | ||
timer.cancel | ||
EM.stop | ||
end | ||
end | ||
} | ||
end # should yield a shell | ||
module EM::Ssh::Test | ||
include Constants | ||
|
||
describe "Ssh::Shell" do | ||
|
||
it "should yield a shell even when in a fiber" do | ||
EM.run { | ||
Fiber.new{ | ||
timer = EM::Timer.new(4) { raise "failed #{$0}" } | ||
EM::Ssh::Shell.new('icaleb.org', 'calebcrane', "") do |shell| | ||
it "should return a shell" do | ||
EM.run { | ||
Fiber.new { | ||
timer = EM::Timer.new(REMOTE2_TIMEOUT) { raise "failed #{$0}" } | ||
shell = EM::Ssh::Shell.new(REMOTE2_URL, REMOTE2_USERNAME, "") | ||
shell.callback do | ||
shell.should be_a(EventMachine::Ssh::Shell) | ||
shell.wait_for(Regexp.escape(']$')) | ||
shell.send_and_wait('uname -a', Regexp.escape(']$')).should include("GNU/Linux") | ||
shell.wait_for(Regexp.escape(REMOTE2_PROMPT)) | ||
shell.send_and_wait('uname -a', Regexp.escape(REMOTE2_PROMPT)).should include("GNU/Linux") | ||
timer.cancel | ||
EM.stop | ||
end | ||
end | ||
}.resume | ||
} | ||
end # should yield a shell | ||
shell.errback { EM.stop } | ||
}.resume | ||
} | ||
end # should return a shell | ||
|
||
it "should raise a proper error with good backtrace on timeout" do | ||
EM.run { | ||
Fiber.new { | ||
timer = EM::Timer.new(4) { raise TimeoutError.new("failed to finish test") } | ||
EM::Ssh::Shell.new('icaleb.org', 'calebcrane', "") do |shell| | ||
it "should yield a shell" do | ||
EM.run { | ||
timer = EM::Timer.new(REMOTE2_TIMEOUT*2) { raise "failed #{$0}" } | ||
EM::Ssh::Shell.new(REMOTE2_URL, REMOTE2_USERNAME, "") do |shell| | ||
shell.callback do | ||
shell.should be_a(EventMachine::Ssh::Shell) | ||
shell.wait_for(Regexp.escape(']$'), :timeout => 1) | ||
e = shell.send_and_wait('uname -a', Regexp.escape(']%'), :timeout => 2) rescue $! | ||
e.should be_a(EM::Ssh::TimeoutError) | ||
e.backtrace.join.should include("#{__FILE__}:#{__LINE__ - 2}:in `block") | ||
shell.wait_for(Regexp.escape(REMOTE2_PROMPT)) | ||
shell.send_and_wait('uname -a', Regexp.escape(REMOTE2_PROMPT)).should include("GNU/Linux") | ||
shell.send_and_wait('/sbin/ifconfig -a', Regexp.escape(REMOTE2_PROMPT)).should include("eth0") | ||
timer.cancel | ||
EM.stop | ||
end | ||
end | ||
}.resume | ||
} | ||
end # should raise a proper error with good backtrace on timeout | ||
} | ||
end # should yield a shell | ||
|
||
specify "#wait_for should raise TimeoutError on timeout" do | ||
EM.run { | ||
Fiber.new { | ||
timer = EM::Timer.new(4) { raise TimeoutError.new("failed to finish test") } | ||
EM::Ssh::Shell.new('icaleb.org', 'calebcrane', "") do |shell| | ||
shell.callback do | ||
expect { | ||
shell.wait_for(Regexp.escape(']%'), :timeout => 1) | ||
}.to raise_error(EM::Ssh::TimeoutError) | ||
timer.cancel | ||
EM.stop | ||
it "should yield a shell even when in a fiber" do | ||
EM.run { | ||
Fiber.new{ | ||
timer = EM::Timer.new(REMOTE2_TIMEOUT*2) { raise "failed #{$0}" } | ||
EM::Ssh::Shell.new(REMOTE2_URL, REMOTE2_USERNAME, "") do |shell| | ||
shell.callback do | ||
shell.should be_a(EventMachine::Ssh::Shell) | ||
shell.wait_for(Regexp.escape(REMOTE2_PROMPT)) | ||
shell.send_and_wait('uname -a', Regexp.escape(REMOTE2_PROMPT)).should include("GNU/Linux") | ||
timer.cancel | ||
EM.stop | ||
end | ||
end | ||
end | ||
}.resume | ||
} | ||
end | ||
end # Ssh::Shell | ||
}.resume | ||
} | ||
end # should yield a shell | ||
|
||
it "should raise a proper error with good backtrace on timeout" do | ||
EM.run { | ||
Fiber.new { | ||
timer = EM::Timer.new(REMOTE2_TIMEOUT*2) { raise TimeoutError.new("failed to finish test") } | ||
EM::Ssh::Shell.new(REMOTE2_URL, REMOTE2_USERNAME, "") do |shell| | ||
shell.callback do | ||
shell.should be_a(EventMachine::Ssh::Shell) | ||
shell.wait_for(Regexp.escape(REMOTE2_PROMPT), :timeout => 1) | ||
e = shell.send_and_wait('uname -a', Regexp.escape(']%'), :timeout => 2) rescue $! | ||
e.should be_a(EM::Ssh::TimeoutError) | ||
e.backtrace.join.should include("#{__FILE__}:#{__LINE__ - 2}:in `block") | ||
timer.cancel | ||
EM.stop | ||
end | ||
end | ||
}.resume | ||
} | ||
end # should raise a proper error with good backtrace on timeout | ||
|
||
specify "#wait_for should raise TimeoutError on timeout" do | ||
EM.run { | ||
Fiber.new { | ||
timer = EM::Timer.new(REMOTE2_TIMEOUT*2) { raise TimeoutError.new("failed to finish test") } | ||
EM::Ssh::Shell.new(REMOTE2_URL, REMOTE2_USERNAME, "") do |shell| | ||
shell.callback do | ||
expect { | ||
shell.wait_for(Regexp.escape(']%'), :timeout => 1) | ||
}.to raise_error(EM::Ssh::TimeoutError) | ||
timer.cancel | ||
EM.stop | ||
end | ||
end | ||
}.resume | ||
} | ||
end | ||
end # Ssh::Shell | ||
end # module::EM::Ssh::Test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
require 'bundler/setup' | ||
require 'rspec' | ||
require_relative 'constants' | ||
|
||
RSpec.configure do |config| | ||
config.include EM::Ssh::Test | ||
end |