bane 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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