shooting_star 1.0.3

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.
Files changed (32) hide show
  1. data/History.txt +42 -0
  2. data/Manifest.txt +38 -0
  3. data/README.txt +80 -0
  4. data/Rakefile +54 -0
  5. data/bin/shooting_star +70 -0
  6. data/ext/asteroid.c +262 -0
  7. data/ext/asteroid.h +4 -0
  8. data/ext/extconf.rb +9 -0
  9. data/lib/form_encoder.rb +23 -0
  10. data/lib/shooting_star/channel.rb +41 -0
  11. data/lib/shooting_star/config.rb +19 -0
  12. data/lib/shooting_star/server.rb +219 -0
  13. data/lib/shooting_star/shooter.rb +62 -0
  14. data/lib/shooting_star.rb +142 -0
  15. data/test/ext/asteroid_test.rb +60 -0
  16. data/test/lib/shooting_star_test.rb +55 -0
  17. data/test/lib/test_c10k_problem.c +47 -0
  18. data/test/test_helper.rb +33 -0
  19. data/test/test_shooting_star.rb +64 -0
  20. data/vendor/plugins/meteor_strike/README +50 -0
  21. data/vendor/plugins/meteor_strike/Rakefile +22 -0
  22. data/vendor/plugins/meteor_strike/generators/meteor/meteor_generator.rb +39 -0
  23. data/vendor/plugins/meteor_strike/generators/meteor/templates/controller.rb +25 -0
  24. data/vendor/plugins/meteor_strike/generators/meteor/templates/functional_test.rb +17 -0
  25. data/vendor/plugins/meteor_strike/generators/meteor/templates/migration.rb +13 -0
  26. data/vendor/plugins/meteor_strike/generators/meteor/templates/model.rb +15 -0
  27. data/vendor/plugins/meteor_strike/generators/meteor/templates/unit_test.rb +11 -0
  28. data/vendor/plugins/meteor_strike/generators/meteor/templates/view.rhtml +13 -0
  29. data/vendor/plugins/meteor_strike/init.rb +3 -0
  30. data/vendor/plugins/meteor_strike/lib/meteor_strike.rb +61 -0
  31. data/vendor/plugins/meteor_strike/test/meteor_strike_test.rb +15 -0
  32. metadata +100 -0
@@ -0,0 +1,219 @@
1
+ require 'json'
2
+ require 'cgi'
3
+ require 'md5'
4
+ require 'set'
5
+ require 'form_encoder'
6
+
7
+ module ShootingStar
8
+ # The module which will be included by servant who was born in the Asteroid.
9
+ # This idea is from EventMachine.
10
+ module Server
11
+ attr_reader :signature
12
+ @@servers = {}
13
+ @@uids = {}
14
+ @@tags = {}
15
+ @@executings = {}
16
+
17
+ # initialize servant waked up.
18
+ def post_init
19
+ @execution = ''
20
+ @data = ''
21
+ end
22
+
23
+ # receive the data sent from client.
24
+ def receive_data(data)
25
+ @data += data
26
+ response if @data[-4..-1] == "\r\n\r\n"
27
+ end
28
+
29
+ # detect disconnection from the client and clean it up.
30
+ def unbind
31
+ @unbound = true
32
+ if channel = Channel[@channel]
33
+ channel.leave(self)
34
+ notify(:event => :leave, :uid => @uid, :tag => @tag)
35
+ Channel.cleanup(@channel)
36
+ end
37
+ @@servers.delete(@signature)
38
+ @@uids.delete(@signature)
39
+ @@tags.delete(@signature)
40
+ @@executings.delete(@signature)
41
+ log "Disconnected: #{@uid}"
42
+ end
43
+
44
+ # respond to an execution command. it'll be buffered.
45
+ def respond(id, params)
46
+ @executing = @@executings[@signature] ||= Hash.new
47
+ if params[:tag] && !params[:tag].empty? && !@tag.empty?
48
+ return false if (params[:tag] & @tag).empty?
49
+ end
50
+ @executing[id] = params
51
+ @waiting
52
+ end
53
+
54
+ # perform buffered executions.
55
+ def commit
56
+ return false if @unbound
57
+ @executing.each{|id, params| execute(id, params)}
58
+ return false if @execution.empty?
59
+ send_data "HTTP/1.1 200 OK\nContent-Type: text/javascript\n\n"
60
+ send_data @execution
61
+ @waiting = nil
62
+ @execution = ''
63
+ @executing = Hash.new
64
+ @@executings.delete(@signature)
65
+ write_and_close
66
+ true
67
+ end
68
+
69
+ # noticed execution and remove the command from execution buffer.
70
+ def executed(id)
71
+ @executing = @@executings[@signature] ||= Hash.new
72
+ @executing.delete(id)
73
+ end
74
+
75
+ # update current status of servant.
76
+ def update(uid, tag)
77
+ if @uid != uid || @tag != tag
78
+ notify(:event => :leave, :uid => @uid, :tag => @tag)
79
+ @@uids[@signature] = @uid = uid
80
+ @@tags[@signature] = @tag = tag
81
+ notify(:event => :enter, :uid => @uid, :tag => @tag)
82
+ end
83
+ log "Update: #{@uid}:#{@tag.join(',')}"
84
+ end
85
+
86
+ def uid; @@uids[@signature] end
87
+ def tag; @@tags[@signature] end
88
+
89
+ # an accessor which maps signatures to servers.
90
+ def self.[](signature)
91
+ @@servers[signature]
92
+ end
93
+
94
+ private
95
+ def log(*arg, &block) ShootingStar::log(*arg, &block) end
96
+
97
+ # broadcast an event to clients.
98
+ def notify(params = {})
99
+ return unless Channel[@channel]
100
+ event_id = ShootingStar::timestamp
101
+ log "Event(#{event_id}): #{@channel}:#{params.inspect}"
102
+ Channel[@channel].transmit("event-#{event_id}", params)
103
+ end
104
+
105
+ # wait for commands or events until they occur. if they're already in
106
+ # the execution buffer, they'll be flushed and return on the spot.
107
+ def wait_for
108
+ log "Wait for: #{@channel}:#{@uid}:#{@tag.join(',')}"
109
+ if Channel[@channel].join(self)
110
+ log "Flushed: #{@channel}:#{@uid}:#{@tag.join(',')}"
111
+ end
112
+ @waiting = true
113
+ end
114
+
115
+ # clean up channel and it'll be closed if no one's listening.
116
+ def cleanup(channel)
117
+ if Channel.cleanup(channel)
118
+ log "Channel closed: #{@channel}"
119
+ end
120
+ end
121
+
122
+ # give a response to the request or keep them waiting.
123
+ def response
124
+ headers = @data.split("\n")
125
+ head = headers.shift
126
+ method, path, protocol = head.split(/\s+/)
127
+ # recognize header
128
+ hdr = headers.inject({}) do |hash, line|
129
+ key, value = line.chop.split(/ *?: */, 2)
130
+ hash[key.downcase] = value if key
131
+ hash
132
+ end
133
+ # recognize parameter
134
+ @params = Hash.new
135
+ if @query = path.split('?', 2)[1]
136
+ if @query = @query.split('#', 2)[0]
137
+ @query.split('&').each do |item|
138
+ key, value = item.split('=', 2)
139
+ @params[key] = CGI.unescape(value) if value && value.length > 0
140
+ end
141
+ end
142
+ end
143
+ # load or create session informations
144
+ @signature ||= @params['sig']
145
+ @channel ||= path[1..-1].split('?', 2)[0]
146
+ @uid = @@uids[@signature] ||= @params['uid']
147
+ @tag = @@tags[@signature] ||=
148
+ (@params['tag'] || '').split(',').map{|i| CGI.unescape(i)}
149
+ @executing = @@executings[@signature] ||= Hash.new
150
+ @@servers[@signature] = self
151
+ # make uncacheable path
152
+ @timestamp = ShootingStar::timestamp
153
+ path += (path.index('?') ? '&' : '?') + "timestamp=#{@timestamp}"
154
+ @query = "channel=#{@channel}&sig=#{@signature}"
155
+ # prepare channel
156
+ unless Channel[@channel]
157
+ Channel.new(@channel)
158
+ log "Channel opened: #{@channel}"
159
+ end
160
+ # process verb
161
+ if method == 'GET'
162
+ make_connection(path)
163
+ notify(:event => :enter, :uid => @uid, :tag => @tag)
164
+ log "Connected: #{@uid}"
165
+ else
166
+ wait_for
167
+ end
168
+ rescue
169
+ log "ERROR: #{$!.message}\n#{@data}"
170
+ raise
171
+ ensure
172
+ @data = ''
173
+ end
174
+
175
+ # add execution line to the buffer.
176
+ def execute(id, params)
177
+ @executing[id] = params
178
+ @query += "&" +FormEncoder.encode(params) if params
179
+ @execution += <<-"EOH"
180
+ (function(){
181
+ var iframe = document.createElement('iframe');
182
+ var remove = function(){document.body.removeChild(iframe)};
183
+ iframe.onload = function(){setTimeout(remove, 0)};
184
+ iframe.src = '#{@params['execute']}/#{id}?#{@query}';
185
+ document.body.appendChild(iframe);
186
+ })();
187
+ EOH
188
+ end
189
+
190
+ # make client connect us.
191
+ def make_connection(path)
192
+ send_data "HTTP/1.1 200 OK\nContent-Type: text/html\n\n" +
193
+ <<-"EOH"
194
+ <html><head><script type='text/javascript'
195
+ src='http://alphastars.drecom.jp/javascripts/prototype.js'
196
+ ></script>
197
+ <script type='text/javascript'>
198
+ //<![CDATA[
199
+ var connect = function()
200
+ { var request = new Ajax.Request(
201
+ #{path.to_json}, {method: 'post', evalScript: true,
202
+ onComplete: function(xhr){
203
+ setTimeout(connect,
204
+ xhr.getResponseHeader('Content-Type') ? 0 : 1000);
205
+ }});
206
+ var disconnect = function()
207
+ { request.options.onComplete = function(){};
208
+ request.transport.abort();
209
+ };
210
+ Event.observe(window, 'unload', disconnect);
211
+ };
212
+ setTimeout(connect, 0);
213
+ //]]>
214
+ </script></head><body></body></html>
215
+ EOH
216
+ write_and_close
217
+ end
218
+ end
219
+ end
@@ -0,0 +1,62 @@
1
+ require 'shooting_star/channel'
2
+
3
+ # DRbObject
4
+ module ShootingStar
5
+ class Shooter
6
+ def shoot(channel, id, tag)
7
+ return unless Channel[channel]
8
+ log "Shot: #{channel}:#{id}:#{tag.join(',')}"
9
+ Channel[channel].transmit(id, :tag => tag)
10
+ end
11
+
12
+ def update(sig, uid, tag)
13
+ ::ShootingStar::Server[sig].update(uid, tag || [])
14
+ end
15
+
16
+ def signature; ShootingStar::timestamp end
17
+ def channels; Channel.list end
18
+ def sweep; Channel.sweep end
19
+
20
+ def count(channel, tag = nil)
21
+ servers(channel, tag).size
22
+ end
23
+
24
+ def count_with(sig, channel, tag = nil)
25
+ (signatures(channel, tag) | [sig]).size
26
+ end
27
+
28
+ def listeners(channel, tag = nil)
29
+ servers(channel, tag).map{|s| s.uid}
30
+ end
31
+
32
+ def listeners_with(uid, sig, channel, tag = nil)
33
+ servers(channel, tag).inject([uid]) do |result, server|
34
+ result << server.uid unless server.signature == sig
35
+ result
36
+ end
37
+ end
38
+
39
+ def signatures(channel, tag = nil)
40
+ servers(channel, tag).map{|s| s.signature}
41
+ end
42
+
43
+ def executed(sig, id)
44
+ ::ShootingStar::Server[sig].executed(id)
45
+ rescue
46
+ end
47
+
48
+ private
49
+ def log(*arg, &block) ShootingStar::log(*arg, &block) end
50
+
51
+ def servers(channel, tag = nil)
52
+ return [] unless Channel[channel]
53
+ result = Channel[channel].waiters.values
54
+ if tag && !tag.empty?
55
+ result = result.select do |server|
56
+ server.tag.empty? || !(server.tag & tag).empty?
57
+ end
58
+ end
59
+ result
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,142 @@
1
+ require 'rubygems'
2
+ require 'asteroid'
3
+ require 'drb/drb'
4
+ require 'yaml'
5
+ require 'ftools'
6
+ require 'shooting_star/config'
7
+ require 'shooting_star/shooter'
8
+
9
+ module ShootingStar
10
+ VERSION = '1.0.3'
11
+ CONFIG = Config.new(
12
+ :config => 'config/shooting_star.yml',
13
+ :pid_file => 'log/shooting_star.pid',
14
+ :log_file => 'log/shooting_star.log',
15
+ :daemon => false,
16
+ :slient => false)
17
+
18
+ def self.configure(options = {})
19
+ if @log_file
20
+ @log_file.close
21
+ @log_file = nil
22
+ end
23
+ config_file = options[:config] || CONFIG.config
24
+ CONFIG.merge!(YAML.load_file(config_file)) if File.exist?(config_file)
25
+ CONFIG.merge!(options)
26
+ end
27
+
28
+ def self.shooter
29
+ @@shooter ||= DRbObject.new_with_uri(CONFIG.shooter.uri)
30
+ end
31
+
32
+ # install config file and plugin
33
+ def self.init
34
+ base_dir = CONFIG.directory || `pwd`.chop
35
+ config_dir = File.join(base_dir, 'config')
36
+ `mkdir -p #{config_dir}` unless File.exist? config_dir
37
+ config_file = File.join(config_dir, 'shooting_star.yml')
38
+ unless File.exist? config_file
39
+ open(config_file, 'w') do |file|
40
+ open(__FILE__) do |data|
41
+ data.gets("__END__\n")
42
+ file.write data.read
43
+ end
44
+ end
45
+ end
46
+ log_dir = File.join(base_dir, 'log')
47
+ `mkdir -p #{log_dir}` unless File.exist? log_dir
48
+ plugin_dir = File.join(base_dir, 'vendor/plugins')
49
+ `mkdir -p #{plugin_dir}` unless File.exist? plugin_dir
50
+ meteor_strike_dir = File.join(plugin_dir, 'meteor_strike')
51
+ unless File.exist?(meteor_strike_dir)
52
+ src_dir = File.join(File.dirname(__FILE__),
53
+ '../vendor/plugins/meteor_strike')
54
+ `cp -R #{src_dir} #{meteor_strike_dir}`
55
+ end
56
+ end
57
+
58
+ def self.start(&block)
59
+ if File.exist?(CONFIG.pid_file)
60
+ log 'shooting_star is already running.'
61
+ return
62
+ end
63
+ if CONFIG.daemon
64
+ Signal.trap(:ALRM){exit} and sleep if fork
65
+ Process.setsid
66
+ end
67
+ require 'shooting_star/shooter'
68
+ @@druby = DRb.start_service(CONFIG.shooter.uri, Shooter.new)
69
+ require 'shooting_star/server'
70
+ Asteroid::run(CONFIG.server.host, CONFIG.server.port, Server) do
71
+ File.open(CONFIG.pid_file, "w") do |file|
72
+ file.puts Process.pid
73
+ file.puts $command_line
74
+ end
75
+ Signal.trap(:INT) do
76
+ Asteroid::stop
77
+ @@druby.stop_service
78
+ log "shooting_star service stopped."
79
+ File.rm_f(CONFIG.pid_file)
80
+ end
81
+ Signal.trap(:EXIT) do
82
+ File.rm_f(CONFIG.pid_file)
83
+ @log_file.close if @log_file
84
+ end
85
+ log "shooting_star service started."
86
+ Process.kill(:ALRM, Process.ppid) if CONFIG.daemon
87
+ block.call if block
88
+ end
89
+ end
90
+
91
+ def self.stop
92
+ command = ''
93
+ File.open(CONFIG.pid_file) do |file|
94
+ Process.kill(:INT, pid = file.gets.to_i)
95
+ command = file.gets
96
+ end
97
+ Thread.pass while File.exist?(CONFIG.pid_file)
98
+ rescue Errno::ENOENT
99
+ log "shooting_star service is not running."
100
+ rescue Errno::ESRCH
101
+ File.unlink(CONFIG.pid_file)
102
+ ensure
103
+ return command
104
+ end
105
+
106
+ def self.restart
107
+ command = stop
108
+ Thread.pass while File.exist?(CONFIG.pid_file)
109
+ system(command)
110
+ end
111
+
112
+ def self.report
113
+ puts "#{'-' * 79}\nconnections channel name\n#{'-' * 79}"
114
+ total_connections = 0
115
+ shooter.channels.each do |channel|
116
+ count = shooter.count(channel)
117
+ puts "%11d %s" % [count, channel]
118
+ puts shooter.listeners(channel).join(',') if CONFIG.with_uid
119
+ puts shooter.signatures(channel).join(',') if CONFIG.with_sig
120
+ total_connections += count
121
+ end
122
+ puts "#{'-' * 79}\n%11d %s\n#{'-' * 79}" % [total_connections, 'TOTAL']
123
+ end
124
+
125
+ def self.timestamp
126
+ now = Time.now
127
+ "%d%06d" % [now.tv_sec, now.tv_usec]
128
+ end
129
+
130
+ private
131
+ def self.log(*arg, &block)
132
+ puts(*arg, &block) unless CONFIG.silent
133
+ @log_file ||= open(CONFIG.log_file, 'a')
134
+ @log_file.puts(*arg, &block) if @log_file
135
+ end
136
+ end
137
+ __END__
138
+ server:
139
+ host: 0.0.0.0
140
+ port: 8080
141
+ shooter:
142
+ uri: druby://0.0.0.0:7123
@@ -0,0 +1,60 @@
1
+ require File.join(File.dirname(__FILE__), '../test_helper')
2
+ require 'thread'
3
+ require 'ext/asteroid'
4
+ require 'socket'
5
+
6
+ module Server
7
+ def receive_data(data)
8
+ send_data data
9
+ write_and_close
10
+ end
11
+
12
+ def post_init
13
+ $post_init_test = true
14
+ end
15
+
16
+ def unbind
17
+ $unbind_test = true
18
+ end
19
+ end
20
+
21
+ class ShootingStarTest < Test::Unit::TestCase
22
+ def setup
23
+ mutex = Mutex.new
24
+ mutex.lock
25
+ @thread = Thread.new do
26
+ Asteroid::run('127.0.0.1', 7124, Server) do
27
+ mutex.unlock
28
+ end
29
+ end
30
+ mutex.lock
31
+ end
32
+
33
+ def teardown
34
+ Asteroid::stop
35
+ @thread.join
36
+ end
37
+
38
+ def test_communication
39
+ c = TCPSocket.open('127.0.0.1', 7124)
40
+ c.write "test"
41
+ assert_equal "test", c.read
42
+ assert $post_init_test
43
+ end
44
+
45
+ def test_unbind
46
+ c = TCPSocket.open('127.0.0.1', 7124)
47
+ c.close
48
+ Thread.pass
49
+ assert $unbind_test
50
+ end
51
+
52
+ def test_broad_cast
53
+ c1 = TCPSocket.open('127.0.0.1', 7124)
54
+ c2 = TCPSocket.open('127.0.0.1', 7124)
55
+ c1.write "test1"
56
+ c2.write "test2"
57
+ assert_equal "test1", c1.read
58
+ assert_equal "test2", c2.read
59
+ end
60
+ end
@@ -0,0 +1,55 @@
1
+ require File.join(File.dirname(__FILE__), '../test_helper')
2
+ require 'shooting_star'
3
+ require 'socket'
4
+ require 'thread'
5
+
6
+ $command_line = 'echo "testing"'
7
+
8
+ class ShootingStarTest < Test::Unit::TestCase
9
+ def setup
10
+ @config = ShootingStar.configure :silent => true,
11
+ :pid_file => 'log/shooting_star.test.pid',
12
+ :log_file => 'log/shooting_star.test.log',
13
+ :server => {:host => '127.0.0.1', :port => 8081},
14
+ :shooter => {:uri => 'druby://127.0.0.1:7124'}
15
+ mutex = Mutex.new
16
+ mutex.lock
17
+ @thread = Thread.new{ShootingStar.start{mutex.unlock}}
18
+ mutex.lock
19
+ end
20
+
21
+ def teardown
22
+ ShootingStar.stop
23
+ File.rm_f(@config.pid_file)
24
+ File.rm_f @config.log_file
25
+ @thread.join
26
+ end
27
+
28
+ def test_connection
29
+ client = TCPSocket.open('127.0.0.1', 8081)
30
+ assert_not_nil client
31
+ send client, "GET", "test/channel"
32
+ end
33
+
34
+ def test_shooter_exists
35
+ shooter = ShootingStar.shooter
36
+ assert_not_nil shooter
37
+ end
38
+
39
+ def test_c10k_problem
40
+ bin = File.join(RAILS_ROOT, 'bin/test_c10k_problem')
41
+ src = File.join(File.dirname(__FILE__), 'test_c10k_problem.c')
42
+ if !File.exist?(bin) || File.mtime(src) > File.mtime(bin)
43
+ system "gcc #{src} -o #{bin}"
44
+ end
45
+ system bin
46
+ end
47
+
48
+ private
49
+ def send(client, method, path)
50
+ client.write "#{method} #{path} HTTP/1.1\n\r" +
51
+ "Host: #{@config.server.host}:#{@config.server.port}\n\r" +
52
+ "Keep-Alive: 300\n\r" +
53
+ "Connection: keep-alive\n\r\n\r"
54
+ end
55
+ end
@@ -0,0 +1,47 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <sys/types.h>
4
+ #include <sys/socket.h>
5
+ #include <sys/epoll.h>
6
+ #include <netinet/in.h>
7
+
8
+ #define NUM_CLIENT (10000)
9
+
10
+ int main(int argc, char **argv){
11
+ int status = EXIT_SUCCESS;
12
+
13
+ /* fill sockattr */
14
+ struct sockaddr_in addr;
15
+ addr.sin_family = AF_INET;
16
+ addr.sin_port = htons(8081);
17
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
18
+
19
+ int i, j, s[NUM_CLIENT];
20
+ for(i = 0; i < NUM_CLIENT; ++i){
21
+ /* create socket */
22
+ s[i] = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
23
+ int result = connect(s[i], (struct sockaddr*)&addr, sizeof(addr));
24
+ if(result != 0){
25
+ printf("failed to connect.");
26
+ for(j = 0; j < i; ++j){
27
+ close(s[j]);
28
+ }
29
+ return EXIT_FAILURE;
30
+ }
31
+
32
+ char hdr[] = "POST /chat/test HTTP/1.1\r\n";
33
+ char host[] = "Host: 127.0.0.1\r\n\r\n";
34
+ write(s[i], hdr, strlen(hdr));
35
+ write(s[i], host, strlen(host));
36
+
37
+ int n = i + 1;
38
+ if(n % 10 == 0) printf(".");
39
+ if(n % 100 == 0) printf(" %d connections\n", n);
40
+ }
41
+
42
+ /* close fd and exit */
43
+ for(i = 0; i < NUM_CLIENT; ++i){
44
+ close(s[i]);
45
+ }
46
+ return status;
47
+ }
@@ -0,0 +1,33 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+ require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
3
+ require 'test_help'
4
+ begin
5
+ require 'redgreen'
6
+ rescue Exception
7
+ end
8
+ $: << File.join(RAILS_ROOT, 'ext')
9
+
10
+ class Test::Unit::TestCase
11
+ # Transactional fixtures accelerate your tests by wrapping each test method
12
+ # in a transaction that's rolled back on completion. This ensures that the
13
+ # test database remains unchanged so your fixtures don't have to be reloaded
14
+ # between every test method. Fewer database queries means faster tests.
15
+ #
16
+ # Read Mike Clark's excellent walkthrough at
17
+ # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
18
+ #
19
+ # Every Active Record database supports transactions except MyISAM tables
20
+ # in MySQL. Turn off transactional fixtures in this case; however, if you
21
+ # don't care one way or the other, switching from MyISAM to InnoDB tables
22
+ # is recommended.
23
+ self.use_transactional_fixtures = true
24
+
25
+ # Instantiated fixtures are slow, but give you @david where otherwise you
26
+ # would need people(:david). If you don't want to migrate your existing
27
+ # test cases which use the @david style and don't mind the speed hit (each
28
+ # instantiated fixtures translates to a database query per test method),
29
+ # then set this back to true.
30
+ self.use_instantiated_fixtures = false
31
+
32
+ # Add more helper methods to be used by all tests here...
33
+ end
@@ -0,0 +1,64 @@
1
+ $: << File.join(File.dirname(__FILE__), '../lib')
2
+ require 'test/unit'
3
+ require 'shooting_star'
4
+ require 'socket'
5
+ require 'thread'
6
+ require 'redgreen' rescue nil
7
+
8
+ COMMAND_LINE = 'echo "testing"'
9
+
10
+ class ShootingStarTest < Test::Unit::TestCase
11
+ def setup
12
+ @config = ShootingStar.configure(
13
+ :silent => true,
14
+ :server => {:host => '127.0.0.1', :port => 8080},
15
+ :shooter => {:uri => 'druby://127.0.0.1:7123'})
16
+ @thread = Thread.new do
17
+ ShootingStar.start
18
+ end
19
+ Thread.pass while !File.exist?(@config.pid_file)
20
+ @client = TCPSocket.open('127.0.0.1', 8080)
21
+ @shooter = ShootingStar.shooter
22
+ end
23
+
24
+ def teardown
25
+ ShootingStar.stop
26
+ @thread.join
27
+ File.rm_f(@config.pid_file)
28
+ end
29
+
30
+ def test_activation
31
+ assert_not_nil @thread
32
+ end
33
+
34
+ def test_shooter
35
+ assert_not_nil @shooter
36
+ end
37
+
38
+ def test_client
39
+ assert_not_nil @client
40
+ end
41
+
42
+ def test_disconnection
43
+ @client.close
44
+ end
45
+
46
+ def test_communication
47
+ mutex = Mutex.new
48
+ mutex.lock
49
+ thread = Thread.new do
50
+ #send 'GET', 'test_application/test_channel_name'
51
+ mutex.unlock
52
+ end
53
+ mutex.lock
54
+ thread.join
55
+ end
56
+
57
+ private
58
+ def send(method, path)
59
+ @client.write "#{method} #{path} HTTP/1.1\n" +
60
+ "Host: #{ShootingStar.host}:#{ShootingStar.port}\n" +
61
+ "Keep-Alive: 300\n" +
62
+ "Connection: keep-alive\n\n"
63
+ end
64
+ end