bane 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,96 @@
1
+ require 'optparse'
2
+
3
+ module Bane
4
+ class CommandLineConfiguration
5
+ def initialize()
6
+ @options = { :host => BehaviorServer::DEFAULT_HOST }
7
+ @option_parser = init_option_parser
8
+ end
9
+
10
+ def parse(args)
11
+ parse_options(@options, args)
12
+
13
+ return [] if (args.empty?)
14
+
15
+ port = parse_port(args[0])
16
+ behaviors = parse_behaviors(args.drop(1))
17
+
18
+ behaviors = ServiceRegistry.all_servers if behaviors.empty?
19
+ LinearPortMappedBehaviorConfiguration.new(port, behaviors, @options[:host]).servers
20
+ end
21
+
22
+ def usage
23
+ @option_parser.help
24
+ end
25
+
26
+ private
27
+
28
+ def init_option_parser
29
+ OptionParser.new do |opts|
30
+ opts.banner = "Usage: bane [options] port [behaviors]"
31
+ opts.separator ""
32
+ opts.on("-l", "--listen-on-localhost",
33
+ "Listen on localhost, (#{BehaviorServer::DEFAULT_HOST}). [default]") do
34
+ @options[:host] = BehaviorServer::DEFAULT_HOST
35
+ end
36
+ opts.on("-a", "--listen-on-all-hosts", "Listen on all interfaces, (#{BehaviorServer::ALL_INTERFACES})") do
37
+ @options[:host] = BehaviorServer::ALL_INTERFACES
38
+ end
39
+ opts.separator ""
40
+ opts.separator "All behaviors:"
41
+ opts.separator ServiceRegistry.all_server_names.map { |title| " - #{title}" }.join("\n")
42
+
43
+ end
44
+ end
45
+
46
+ def parse_options(options, args)
47
+ @option_parser.parse!(args)
48
+ rescue OptionParser::InvalidOption => io
49
+ raise ConfigurationError, io.message
50
+ end
51
+
52
+ def parse_port(port)
53
+ Integer(port)
54
+ rescue ArgumentError => ae
55
+ raise ConfigurationError, "Invalid port number: #{port}"
56
+ end
57
+
58
+ def parse_behaviors(behavior_names)
59
+ behavior_names.map { |behavior| find(behavior) }
60
+ rescue UnknownBehaviorError => ube
61
+ raise ConfigurationError, ube.message
62
+ end
63
+
64
+ def find(behavior)
65
+ raise UnknownBehaviorError.new(behavior) unless Behaviors.const_defined?(behavior)
66
+ Behaviors.const_get(behavior)
67
+ end
68
+
69
+ class LinearPortMappedBehaviorConfiguration
70
+ def initialize(port, behaviors, host)
71
+ @starting_port = port
72
+ @behaviors = behaviors
73
+ @host = host
74
+ end
75
+
76
+ def servers
77
+ configurations = []
78
+ @behaviors.each_with_index do |behavior, index|
79
+ configurations << BehaviorServer.new(@starting_port + index, behavior.new, @host)
80
+ end
81
+ configurations
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+
88
+ class ConfigurationError < RuntimeError; end
89
+
90
+ class UnknownBehaviorError < RuntimeError
91
+ def initialize(name)
92
+ super "Unknown behavior: #{name}"
93
+ end
94
+ end
95
+
96
+ end
@@ -4,8 +4,8 @@ module Bane
4
4
 
5
5
  attr_reader :configurations
6
6
 
7
-
8
7
  def initialize(*args)
8
+ warn_about_deprecation()
9
9
  @configurations = []
10
10
 
11
11
  @configurations = case args[0]
@@ -18,6 +18,10 @@ module Bane
18
18
  end
19
19
  end
20
20
 
21
+ def warn_about_deprecation
22
+ warn "Kernel#Configuration() and ConfigurationParser are deprecated. Please directly construct an array of BehaviorServers to pass to Launcher."
23
+ end
24
+
21
25
  private
22
26
 
23
27
  def map_integer_and_behavior_arguments(*args)
@@ -31,7 +35,7 @@ module Bane
31
35
 
32
36
  def setup_linear_ports(port, behavior_classes)
33
37
  behavior_classes.each_with_index do |behavior, index|
34
- @configurations << Bane::Configuration::ConfigurationRecord.new(port + index, find(behavior))
38
+ @configurations << create_server(port + index, find(behavior))
35
39
  end
36
40
  @configurations
37
41
  end
@@ -59,16 +63,27 @@ module Bane
59
63
  case value
60
64
  when Module
61
65
  behavior = value
62
- Bane::Configuration::ConfigurationRecord.new(port, behavior)
66
+ create_server(port, behavior)
63
67
  when Hash
64
68
  behavior = value.delete(:behavior)
65
69
  options = value
66
- Bane::Configuration::ConfigurationRecord.new(port, behavior, options)
70
+ create_server(port, behavior, options)
67
71
  else
68
72
  raise ConfigurationError, "Unknown configuration option: #{value.inspect}"
69
73
  end
70
74
  end
71
75
 
76
+ def create_server(port, behavior, options = {})
77
+ BehaviorServer.new(port, behavior.new(options), BehaviorServer::DEFAULT_HOST)
78
+ end
79
+
72
80
  end
73
81
 
82
+ end
83
+
84
+ # Helper method to easily create configuration.
85
+ module Kernel
86
+ def Configuration(*args)
87
+ Bane::ConfigurationParser.new(*args).configurations
88
+ end
74
89
  end
@@ -2,24 +2,23 @@ module Bane
2
2
 
3
3
  class Launcher
4
4
 
5
- def initialize(configurations, logger = $stderr)
6
- @configuration = configurations
7
- @logger = logger
8
- @running_servers = []
5
+ def initialize(servers, logger = $stderr)
6
+ @servers = servers
7
+ @servers.each { |server| server.stdlog = logger }
9
8
  end
10
9
 
11
10
  def start
12
- @running_servers = @configuration.start(@logger)
11
+ @servers.each { |server| server.start }
13
12
  end
14
13
 
15
14
  def join
16
- @running_servers.each { |server| server.join }
15
+ @servers.each { |server| server.join }
17
16
  end
18
17
 
19
18
  def stop
20
- @running_servers.each { |server| server.stop }
19
+ @servers.each { |server| server.stop }
21
20
  end
22
21
 
23
22
  end
24
-
23
+
25
24
  end
@@ -5,6 +5,10 @@ module Bane
5
5
  @servers ||= []
6
6
  end
7
7
 
8
+ def self.all_server_names
9
+ all_servers.map(&:simple_name).sort
10
+ end
11
+
8
12
  def self.register(server)
9
13
  all_servers << server unless all_servers.include?(server)
10
14
  end
@@ -1,30 +1,32 @@
1
- require File.dirname(__FILE__) + '/../test_helper'
1
+ require File.expand_path(File.dirname(__FILE__)) + '/../test_helper'
2
2
  require 'mocha'
3
3
 
4
- class DelegatingGserverTest < Test::Unit::TestCase
4
+ class BehaviorServerTest < Test::Unit::TestCase
5
5
  include Bane
6
6
 
7
7
  IRRELEVANT_IO_STREAM = nil
8
8
  IRRELEVANT_OPTIONS = {}
9
+ IRRELEVANT_HOST = "1.1.1.1"
10
+ IRRELEVANT_BEHAVIOR = nil
9
11
 
10
- def test_serve_passes_a_hash_of_options_even_if_not_initialized_with_options
11
- behavior = mock()
12
- server = DelegatingGServer.new(IRRELEVANT_PORT, behavior)
13
-
14
- behavior.expects(:serve).with(anything(), is_a(Hash))
12
+ def test_initializes_server_on_specified_port
13
+ server = BehaviorServer.new(6000, IRRELEVANT_BEHAVIOR)
14
+ assert_equal 6000, server.port
15
+ end
15
16
 
16
- server.serve(IRRELEVANT_IO_STREAM)
17
+ def test_initializes_server_on_specified_hostname
18
+ server = BehaviorServer.new(IRRELEVANT_PORT, IRRELEVANT_BEHAVIOR, "hostname")
19
+ assert_equal "hostname", server.host
17
20
  end
18
21
 
19
- def test_serve_passes_constructor_options_to_behaviors_serve_method
22
+ def test_delegates_serve_call_to_behavior
23
+ io = mock()
20
24
  behavior = mock()
21
-
22
- initialized_options = {:expected => :options}
23
- server = DelegatingGServer.new(IRRELEVANT_PORT, behavior, initialized_options)
25
+ server = BehaviorServer.new(IRRELEVANT_PORT, behavior)
24
26
 
25
- behavior.expects(:serve).with(anything(), equals(initialized_options))
27
+ behavior.expects(:serve).with(io)
26
28
 
27
- server.serve(IRRELEVANT_IO_STREAM)
29
+ server.serve(io)
28
30
  end
29
31
 
30
32
  def test_connection_log_messages_use_short_behavior_name_to_shorten_log_messages
@@ -45,8 +47,9 @@ class DelegatingGserverTest < Test::Unit::TestCase
45
47
 
46
48
  def assert_log_message_uses_short_behavior_name_for(method)
47
49
  logger = StringIO.new
48
- server = DelegatingGServer.new(IRRELEVANT_PORT, Bane::Behaviors::CloseImmediately.new, IRRELEVANT_OPTIONS, logger)
49
-
50
+ server = BehaviorServer.new(IRRELEVANT_PORT, Bane::Behaviors::CloseImmediately.new)
51
+ server.stdlog = logger
52
+
50
53
  yield server
51
54
 
52
55
  assert_match /CloseImmediately/, logger.string, "Log for #{method} should contain class short name"
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../test_helper'
1
+ require File.expand_path(File.dirname(__FILE__)) + '/../test_helper'
2
2
  require 'timeout'
3
3
  require 'mocha'
4
4
 
@@ -11,72 +11,80 @@ class BehaviorsTest < Test::Unit::TestCase
11
11
  end
12
12
 
13
13
  def test_fixed_response_sends_the_specified_message
14
- query_server(create(FixedResponse), :message => "Test Message")
14
+ query_server(FixedResponse.new(:message => "Test Message"))
15
15
 
16
16
  assert_equal "Test Message", response
17
17
  end
18
18
 
19
19
  def test_newline_response_sends_only_a_newline_character
20
- query_server(create(NewlineResponse))
20
+ query_server(NewlineResponse.new)
21
21
 
22
22
  assert_equal "\n", response
23
23
  end
24
24
 
25
25
  def test_deluge_response_sends_one_million_bytes_by_default
26
- query_server(create(DelugeResponse))
26
+ query_server(DelugeResponse.new)
27
27
 
28
28
  assert_response_length 1_000_000
29
29
  end
30
30
 
31
31
  def test_deluge_response_accepts_length_parameter
32
- query_server(create(DelugeResponse), :length => 1)
32
+ query_server(DelugeResponse.new(:length => 1))
33
33
 
34
34
  assert_response_length 1
35
35
  end
36
36
 
37
37
  def test_close_immediately_sends_no_response
38
- query_server(create(CloseImmediately))
38
+ query_server(CloseImmediately.new)
39
39
 
40
40
  assert_empty_response()
41
41
  end
42
42
 
43
43
  def test_never_respond_never_sends_a_response
44
- server = create(NeverRespond)
44
+ server = NeverRespond.new
45
45
 
46
46
  assert_raise Timeout::Error do
47
- Timeout::timeout(3) { query_server(server) }
47
+ Timeout::timeout(1) { query_server(server) }
48
48
  end
49
49
  assert_empty_response
50
50
  end
51
51
 
52
- def test_close_after_pause_sleeps_30_seconds_by_default_and_sends_nothing
53
- server = create(CloseAfterPause)
52
+ def test_close_after_pause_sleeps_30_seconds_by_default
53
+ server = CloseAfterPause.new
54
54
  server.expects(:sleep).with(30)
55
-
56
- query_server(server)
57
55
 
58
- assert_empty_response
56
+ query_server(server)
59
57
  end
60
58
 
59
+
61
60
  def test_close_after_pause_accepts_duration_parameter
62
- server = create(CloseAfterPause)
61
+ server = CloseAfterPause.new(:duration => 1)
62
+ server.expects(:sleep).with(1)
63
63
 
64
- within(2) { query_server(server, :duration => 1) }
64
+ query_server(server)
65
+ end
66
+
67
+ def test_close_after_pause_sends_nothing
68
+ server = CloseAfterPause.new
69
+ server.stubs(:sleep)
70
+
71
+ query_server(server)
65
72
  end
66
73
 
67
74
  def test_slow_response_sends_a_message_slowly
68
- server = create(SlowResponse)
69
75
  message = "Hi!"
70
76
  delay = 0.5
71
- max_delay = (message.length + 1) * delay
72
77
 
73
- within(max_delay) { query_server(server, :pause_duration => delay, :message => message)}
78
+ server = SlowResponse.new(:pause_duration => delay, :message => message)
79
+ server.expects(:sleep).with(delay).at_least(message.length)
80
+
81
+ query_server(server)
74
82
 
75
83
  assert_equal message, response
76
84
  end
77
85
 
78
86
  def test_random_response_sends_a_nonempty_response
79
- query_server(create(RandomResponse))
87
+ query_server(RandomResponse.new)
80
88
 
81
89
  assert (!response.empty?), "Should have served a nonempty response"
82
90
  end
@@ -84,7 +92,7 @@ class BehaviorsTest < Test::Unit::TestCase
84
92
  def test_refuse_all_http_credentials_sends_401_response_code
85
93
  @fake_connection.will_send("GET /some/irrelevant/path HTTP/1.1")
86
94
 
87
- server = create(HttpRefuseAllCredentials)
95
+ server = HttpRefuseAllCredentials.new
88
96
  query_server(server)
89
97
 
90
98
  assert @fake_connection.read_all_queries?, "Should have read the HTTP query before sending response"
@@ -96,11 +104,11 @@ class BehaviorsTest < Test::Unit::TestCase
96
104
  end
97
105
 
98
106
  def test_for_each_line_reads_a_line_before_responding
99
- server = create(Bane::Behaviors::FixedResponseForEachLine)
107
+ server = Bane::Behaviors::FixedResponseForEachLine.new({:message => "Dynamic"})
100
108
 
101
109
  @fake_connection.will_send "irrelevant\n"
102
110
 
103
- query_server(server, {:message => "Dynamic"})
111
+ query_server(server)
104
112
  assert_equal "Dynamic", response
105
113
 
106
114
  assert @fake_connection.read_all_queries?
@@ -108,12 +116,8 @@ class BehaviorsTest < Test::Unit::TestCase
108
116
 
109
117
  private
110
118
 
111
- def create(server_class)
112
- server_class.new()
113
- end
114
-
115
- def query_server(server, options = {})
116
- server.serve(@fake_connection, options)
119
+ def query_server(server)
120
+ server.serve(@fake_connection)
117
121
  end
118
122
 
119
123
  def response
@@ -128,12 +132,4 @@ class BehaviorsTest < Test::Unit::TestCase
128
132
  assert_equal expected_length, response.length, "Response was the wrong length"
129
133
  end
130
134
 
131
- def within(duration)
132
- begin
133
- Timeout::timeout(duration) { yield }
134
- rescue Timeout::Error
135
- flunk "Test took too long - should have completed within #{duration} seconds."
136
- end
137
- end
138
-
139
135
  end
@@ -0,0 +1,100 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + '/../test_helper'
2
+ require 'mocha'
3
+
4
+ class CommandLineConfigurationTest < Test::Unit::TestCase
5
+ include Bane
6
+
7
+ IRRELEVANT_BEHAVIOR = "CloseImmediately"
8
+
9
+ def test_creates_specified_behavior_on_given_port
10
+ expect_server_created_with(:port => 3000, :behavior => Behaviors::CloseImmediately)
11
+
12
+ create_configuration_for([3000, "CloseImmediately"])
13
+ end
14
+
15
+ def test_creates_all_known_behavior_if_only_port_specified
16
+ expect_server_created_with :port => 4000, :behavior => Behaviors::CloseImmediately
17
+ expect_server_created_with :port => 4001, :behavior => Behaviors::NeverRespond
18
+
19
+ ServiceRegistry.stubs(:all_servers).returns([Behaviors::CloseImmediately, Behaviors::NeverRespond])
20
+
21
+ create_configuration_for([4000])
22
+ end
23
+
24
+ def test_creates_multiple_behaviors_starting_on_given_port
25
+ expect_server_created_with :port => 3000, :behavior => Behaviors::CloseImmediately
26
+ expect_server_created_with :port => 3001, :behavior => Behaviors::CloseAfterPause
27
+
28
+ create_configuration_for([3000, "CloseImmediately", "CloseAfterPause"])
29
+ end
30
+
31
+ def test_dash_l_option_sets_listen_host_to_localhost
32
+ expect_server_created_with :host => BehaviorServer::DEFAULT_HOST
33
+
34
+ create_configuration_for(["-l", IRRELEVANT_PORT, IRRELEVANT_BEHAVIOR])
35
+ end
36
+
37
+ def test_listen_on_localhost_sets_listen_host_to_localhost
38
+ expect_server_created_with :host => BehaviorServer::DEFAULT_HOST
39
+
40
+ create_configuration_for(["--listen-on-localhost", IRRELEVANT_PORT, IRRELEVANT_BEHAVIOR])
41
+ end
42
+
43
+ def test_dash_a_option_sets_listen_host_to_all_interfaces
44
+ expect_server_created_with :host => BehaviorServer::ALL_INTERFACES
45
+
46
+ create_configuration_for(["-a", IRRELEVANT_PORT, IRRELEVANT_BEHAVIOR])
47
+ end
48
+
49
+ def test_listen_on_all_hosts_option_sets_listen_host_to_all_interfaces
50
+ expect_server_created_with :host => BehaviorServer::ALL_INTERFACES
51
+
52
+ create_configuration_for(["--listen-on-all-hosts", IRRELEVANT_PORT, IRRELEVANT_BEHAVIOR])
53
+ end
54
+
55
+ def test_no_arguments_returns_empty_configuration
56
+ assert(create_configuration_for([]).empty?,
57
+ "Should have returned no configurations for empty arguments")
58
+ end
59
+
60
+ def test_non_integer_port_fails_with_error_message
61
+ assert_invaild_arguments_fail_matching_message(["text_instead_of_an_integer"], /Invalid Port Number/i,
62
+ "Should have indicated the port was invalid.")
63
+ end
64
+
65
+ def test_unknown_behavior_fails_with_unknown_behavior_message
66
+ assert_invaild_arguments_fail_matching_message([IRRELEVANT_PORT, "AnUknownBehavior"], /Unknown Behavior/i,
67
+ "Should have indicated the given behavior is unknown.")
68
+ end
69
+
70
+ def test_invalid_option_fails_with_error_message
71
+ assert_invaild_arguments_fail_matching_message(["--unknown-option", IRRELEVANT_PORT], /Invalid Option/i,
72
+ "Should have indicated the --uknown-option switch was unknown.")
73
+ end
74
+
75
+
76
+ private
77
+
78
+ def create_configuration_for(array)
79
+ CommandLineConfiguration.new().parse(array)
80
+ end
81
+
82
+ def unique_behavior
83
+ Class.new
84
+ end
85
+
86
+ def expect_server_created_with(arguments)
87
+ arguments = { :port => anything(), :host => anything() }.merge(arguments)
88
+ behavior_matcher = arguments[:behavior] ? instance_of(arguments[:behavior]) : anything()
89
+ BehaviorServer.expects(:new).with(arguments[:port], behavior_matcher, arguments[:host])
90
+ end
91
+
92
+ def assert_invaild_arguments_fail_matching_message(arguments, message_matcher, assertion_failure_message)
93
+ create_configuration_for(arguments)
94
+ fail "Should have failed"
95
+ rescue ConfigurationError => ce
96
+ assert_match(message_matcher, ce.message, assertion_failure_message)
97
+ end
98
+
99
+
100
+ end