simultaneous 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,242 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ describe Simultaneous::Server do
4
+ before do
5
+ Simultaneous.reset!
6
+ end
7
+
8
+ after do
9
+ FileUtils.rm(SOCKET) if File.exist?(SOCKET)
10
+ end
11
+
12
+ describe "task messages" do
13
+ it "should generate events in clients on the right domain" do
14
+ client1 = client2 = client3 = nil
15
+ result1 = result2 = result3 = nil
16
+ result4 = result5 = result6 = nil
17
+
18
+ EM.run {
19
+ Simultaneous::Server.start(SOCKET)
20
+
21
+ client1 = Simultaneous::Client.new("domain1", SOCKET)
22
+ client2 = Simultaneous::Client.new("domain1", SOCKET)
23
+ client3 = Simultaneous::Client.new("domain2", SOCKET)
24
+
25
+ command = Simultaneous::Command::ClientEvent.new("domain1", "a", "data")
26
+
27
+ client1.on_event(:a) { |event| result1 = [:a, event.data] }
28
+ client2.on_event(:a) { |event| result2 = [:a, event.data] }
29
+ client3.on_event(:a) { |event| result3 = [:a, event.data] }
30
+ client1.on_event(:b) { |event| result4 = [:b, event.data] }
31
+ client2.on_event(:b) { |event| result5 = [:b, event.data] }
32
+ client3.on_event(:b) { |event| result6 = [:b, event.data] }
33
+
34
+ $receive_count = 0
35
+
36
+ $test = proc {
37
+ result1.must_equal [:a, "data"]
38
+ result2.must_equal [:a, "data"]
39
+ result3.must_be_nil
40
+ result4.must_be_nil
41
+ result5.must_be_nil
42
+ result6.must_be_nil
43
+ EM.stop
44
+ }
45
+
46
+ def client1.notify!
47
+ super
48
+ $receive_count += 1
49
+ if $receive_count == 3
50
+ $test.call
51
+ end
52
+ end
53
+
54
+ def client2.notify!
55
+ super
56
+ $receive_count += 1
57
+ if $receive_count == 3
58
+ $test.call
59
+ end
60
+ end
61
+
62
+ def client3.notify!
63
+ super
64
+ $receive_count += 1
65
+ if $receive_count == 3
66
+ $test.call
67
+ end
68
+ end
69
+
70
+ Thread.new {
71
+ Simultaneous::Server.run(command)
72
+ }.join
73
+ }
74
+ end
75
+ end
76
+
77
+ describe "Task" do
78
+ it "should make it intact from client to process" do
79
+ Simultaneous.domain = "example.org"
80
+ Simultaneous.connection = SOCKET
81
+
82
+ default_params = { :param1 => "param1" }
83
+ env = { "ENV_PARAM" => "envparam" }
84
+ args = {:param2 => "param2"}
85
+ niceness = 10
86
+ task_uid = 9999
87
+ options = {
88
+ :nice => niceness
89
+ }
90
+ task = Simultaneous.add_task(:publish, "/path/to/binary", options, default_params, env)
91
+ command = Simultaneous::Command::Fire.new(task, args)
92
+ dump = command.dump
93
+ mock(command).dump { dump }
94
+ mock(Simultaneous::Command::Fire).new(task, args) { command }
95
+ mock(Simultaneous::Command).load(is_a(String)) { command }
96
+ mock(command).valid? { true }
97
+ mock(command).task_uid.twice { task_uid }
98
+
99
+ EM.run do
100
+ Simultaneous::Server.start(SOCKET)
101
+
102
+ mock(Process).detach(9999)
103
+
104
+ mock(command).fork do |block|
105
+ mock(command).daemonize(%[/path/to/binary --param1="param1" --param2="param2"], is_a(String))
106
+ mock(Process).setpriority(Process::PRIO_PROCESS, 0, niceness)
107
+ mock(Process::UID).change_privilege(task_uid)
108
+ mock(File).umask(0022)
109
+ mock(command).exec(%[/path/to/binary --param1="param1" --param2="param2"])
110
+ block.call
111
+
112
+ ENV["ENV_PARAM"].must_equal "envparam"
113
+ ENV[Simultaneous::ENV_DOMAIN].must_equal "example.org"
114
+ ENV[Simultaneous::ENV_TASK_NAME].must_equal "publish"
115
+ ENV[Simultaneous::ENV_CONNECTION].must_equal SOCKET
116
+ EM.stop
117
+ 9999
118
+ end
119
+
120
+ Thread.new do
121
+ Simultaneous.fire(:publish, args)
122
+ end.join
123
+ end
124
+ end
125
+
126
+ it "should report back to the server to set the task PID" do
127
+ pids = {}
128
+ pid = 99999
129
+ Simultaneous.domain = "example.com"
130
+ Simultaneous.connection = SOCKET
131
+
132
+
133
+ EM.run do
134
+ Simultaneous::Server.start(SOCKET)
135
+
136
+ ENV[Simultaneous::ENV_DOMAIN] = "example.com"
137
+ ENV[Simultaneous::ENV_TASK_NAME] = "publish"
138
+ ENV[Simultaneous::ENV_CONNECTION] = SOCKET
139
+
140
+ mock(Simultaneous::Task).pid { pid }
141
+ mock(Simultaneous::Server).pids { pids }
142
+ mock(pids).[]=("example.com/publish", pid) { EM.stop }
143
+
144
+ Thread.new do
145
+ class SimultaneousTask
146
+ include Simultaneous::Task
147
+ end
148
+ end.join
149
+ end
150
+ end
151
+
152
+ it "should be able to trigger messages on the client" do
153
+ Simultaneous.domain = "example2.com"
154
+ Simultaneous.connection = SOCKET
155
+
156
+
157
+ EM.run do
158
+ Simultaneous::Server.start(SOCKET)
159
+
160
+ ENV[Simultaneous::ENV_DOMAIN] = "example2.com"
161
+ ENV[Simultaneous::ENV_TASK_NAME] = "publish"
162
+ ENV[Simultaneous::ENV_CONNECTION] = SOCKET
163
+
164
+ c = Simultaneous::TaskClient.new("example2.com", SOCKET)
165
+ mock(Simultaneous::TaskClient).new { c }
166
+ mock(c).run(is_a(Simultaneous::Command::SetPid))
167
+ proxy(c).run(is_a(Simultaneous::Command::ClientEvent))
168
+ client = Simultaneous::Client.new("example2.com", SOCKET)
169
+
170
+ client.on_event("publish_status") { |event|
171
+ event.data.must_equal "completed"
172
+ EM.stop
173
+ }
174
+
175
+ Thread.new do
176
+ class SimultaneousTask
177
+ include Simultaneous::Task
178
+ def run
179
+ simultaneous_event("publish_status", "completed")
180
+ end
181
+ end
182
+ SimultaneousTask.new.run
183
+ end.join
184
+
185
+ end
186
+ end
187
+
188
+ it "should be able to kill task processes" do
189
+ pid = 99999
190
+ pids = {"example3.com/publish" => pid}
191
+ Simultaneous.domain = "example3.com"
192
+ Simultaneous.connection = SOCKET
193
+
194
+
195
+ EM.run do
196
+ Simultaneous::Server.start(SOCKET)
197
+
198
+ ENV[Simultaneous::ENV_DOMAIN] = "example3.com"
199
+ ENV[Simultaneous::ENV_TASK_NAME] = "publish"
200
+ ENV[Simultaneous::ENV_CONNECTION] = SOCKET
201
+
202
+ c = Simultaneous::TaskClient.new("example3.com", SOCKET)
203
+ mock(Simultaneous::TaskClient).new { c }
204
+ mock(c).run(is_a(Simultaneous::Command::SetPid))
205
+ proxy(c).run(is_a(Simultaneous::Command::Kill))
206
+
207
+ mock(Simultaneous::Task).pid { pid }
208
+ mock(Simultaneous::Server).pids { pids }
209
+
210
+ Thread.new do
211
+ class SimultaneousTask
212
+ include Simultaneous::Task
213
+ end
214
+ end.join
215
+
216
+ Thread.new do
217
+ mock(Process).kill("TERM", pid) { EM.stop }
218
+ Simultaneous.kill(:publish)
219
+ end.join
220
+ end
221
+ end
222
+
223
+ it "should divert STDOUT and STDERR to file and inform the server when finished" do
224
+ Simultaneous.domain = "example4.com"
225
+ Simultaneous.connection = SOCKET
226
+ logfile = "/tmp/log-#{$$}/#{$$}-example.log"
227
+ FileUtils.rm_f(logfile) if File.exists?(logfile)
228
+ EM.run do
229
+ Simultaneous::Server.start(SOCKET)
230
+ task = Simultaneous.add_task(:example, File.expand_path("../tasks/example.rb", __FILE__), {:logfile => logfile})
231
+ proxy(Simultaneous::Server).run(is_a(Simultaneous::Command::Fire))
232
+ mock(Simultaneous::Server).run(is_a(Simultaneous::Command::SetPid))
233
+ mock(Simultaneous::Server).run(is_a(Simultaneous::Command::ClientEvent))
234
+ mock(Simultaneous::Server).run(is_a(Simultaneous::Command::TaskComplete)) { EM.stop }
235
+ Simultaneous.fire(:example, { "param" => "value" })
236
+ end
237
+ assert(File.exist?(logfile), "Task should have output to #{logfile}")
238
+ File.read(logfile).must_equal %(--param=value\n)
239
+ FileUtils.rm_rf(File.dirname(logfile)) if File.exists?(logfile)
240
+ end
241
+ end
242
+ end
data/test/test_task.rb ADDED
@@ -0,0 +1,21 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ describe Simultaneous::TaskDescription do
4
+ it "should generate the right default logfile location" do
5
+ mock(Dir).pwd { "/application/current" }
6
+ task = Simultaneous::TaskDescription.new(:fish, "/path/to/fish")
7
+ task.logfile.must_equal "/application/current/log/fish-task.log"
8
+ end
9
+ it "should generate the right default logfile location" do
10
+ task = Simultaneous::TaskDescription.new(:fish, "/path/to/fish", {:logfile => "/var/log/fish.log"})
11
+ task.logfile.must_equal "/var/log/fish.log"
12
+ end
13
+ it "should generate the right default logfile location" do
14
+ task = Simultaneous::TaskDescription.new(:fish, "/path/to/fish", {:log => "/var/log/fish.log"})
15
+ task.logfile.must_equal "/var/log/fish.log"
16
+ end
17
+
18
+ it "should set the right pwd" do
19
+ flunk
20
+ end
21
+ end
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simultaneous
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Garry Hill
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-03 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: eventmachine
16
+ requirement: &70278663630360 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.0.beta.4
22
+ - - <
23
+ - !ruby/object:Gem::Version
24
+ version: '2.0'
25
+ type: :runtime
26
+ prerelease: false
27
+ version_requirements: *70278663630360
28
+ - !ruby/object:Gem::Dependency
29
+ name: rack
30
+ requirement: &70278663629460 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ! '>='
34
+ - !ruby/object:Gem::Version
35
+ version: '1.0'
36
+ - - <
37
+ - !ruby/object:Gem::Version
38
+ version: '2.0'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: *70278663629460
42
+ - !ruby/object:Gem::Dependency
43
+ name: rack-async
44
+ requirement: &70278663628360 !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: 0.0.1
50
+ - - <
51
+ - !ruby/object:Gem::Version
52
+ version: '2.0'
53
+ type: :runtime
54
+ prerelease: false
55
+ version_requirements: *70278663628360
56
+ - !ruby/object:Gem::Dependency
57
+ name: rr
58
+ requirement: &70278663626460 !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ~>
62
+ - !ruby/object:Gem::Version
63
+ version: 1.0.4
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: *70278663626460
67
+ description: Simultaneous is designed for the very specific use case of a small set
68
+ of users collaborating on editing a single website. Because of that it is optimised
69
+ for infrequent invocation of very long running publishing tasks and provides an
70
+ event based messaging system that allows launched tasks to communicate back to the
71
+ CMS web-server and for that server to then fire off update messages through HTML5
72
+ Server-Sent Events.
73
+ email: garry@magnetised.info
74
+ executables:
75
+ - simultaneous-server
76
+ - simultaneous-console
77
+ extensions: []
78
+ extra_rdoc_files:
79
+ - README
80
+ - LICENSE
81
+ files:
82
+ - Gemfile
83
+ - LICENSE
84
+ - README
85
+ - Rakefile
86
+ - bin/simultaneous-console
87
+ - bin/simultaneous-server
88
+ - lib/simultaneous.rb
89
+ - lib/simultaneous/broadcast_message.rb
90
+ - lib/simultaneous/client.rb
91
+ - lib/simultaneous/command.rb
92
+ - lib/simultaneous/command/client_event.rb
93
+ - lib/simultaneous/command/fire.rb
94
+ - lib/simultaneous/command/kill.rb
95
+ - lib/simultaneous/command/set_pid.rb
96
+ - lib/simultaneous/command/task_complete.rb
97
+ - lib/simultaneous/connection.rb
98
+ - lib/simultaneous/rack.rb
99
+ - lib/simultaneous/server.rb
100
+ - lib/simultaneous/task.rb
101
+ - lib/simultaneous/task_client.rb
102
+ - lib/simultaneous/task_description.rb
103
+ - simultaneous.gemspec
104
+ - test/helper.rb
105
+ - test/tasks/example.rb
106
+ - test/test_client.rb
107
+ - test/test_command.rb
108
+ - test/test_connection.rb
109
+ - test/test_faf.rb
110
+ - test/test_message.rb
111
+ - test/test_server.rb
112
+ - test/test_task.rb
113
+ homepage: http://spontaneouscms.org
114
+ licenses: []
115
+ post_install_message:
116
+ rdoc_options:
117
+ - --charset=UTF-8
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubyforge_project: simultaneous
134
+ rubygems_version: 1.8.10
135
+ signing_key:
136
+ specification_version: 2
137
+ summary: Simultaneous is the background task launcher used by Spontaneous CMS
138
+ test_files:
139
+ - test/test_client.rb
140
+ - test/test_command.rb
141
+ - test/test_connection.rb
142
+ - test/test_faf.rb
143
+ - test/test_message.rb
144
+ - test/test_server.rb
145
+ - test/test_task.rb