pigeon 0.9.3 → 1.0.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.
- checksums.yaml +7 -0
- data/.travis.yml +8 -0
- data/Gemfile +9 -0
- data/README.md +1 -1
- data/Rakefile +4 -4
- data/VERSION +1 -1
- data/lib/pigeon.rb +8 -0
- data/lib/pigeon/dispatcher.rb +1 -1
- data/lib/pigeon/engine.rb +27 -16
- data/lib/pigeon/launcher.rb +35 -6
- data/lib/pigeon/support.rb +31 -9
- data/lib/pigeon/task.rb +9 -0
- data/pigeon.gemspec +18 -6
- data/test/helper.rb +82 -22
- data/test/unit/pigeon_backlog_test.rb +1 -1
- data/test/unit/pigeon_dispatcher_test.rb +1 -1
- data/test/unit/pigeon_engine_test.rb +30 -14
- data/test/unit/pigeon_launcher_test.rb +27 -28
- data/test/unit/pigeon_option_accessor_test.rb +3 -3
- data/test/unit/pigeon_processor_test.rb +19 -13
- data/test/unit/pigeon_queue_test.rb +1 -1
- data/test/unit/pigeon_scheduler_test.rb +1 -1
- data/test/unit/pigeon_sorted_array_test.rb +3 -3
- data/test/unit/pigeon_task_test.rb +42 -27
- data/test/unit/pigeon_test.rb +1 -1
- metadata +54 -14
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fa0f5ce613ef0b66cded863d01259e09099eeb27
|
4
|
+
data.tar.gz: 6b1d997dda5b4664c6cf77524a840aee230ac54d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 18a1edfd20eaec356b8297665db24eb96d18e2e90fb8c7699a71e081c599f3d809476e64d7e05dae8fedd3a7973a924c6500013c90bacb0483dcfdddd7ee2de4
|
7
|
+
data.tar.gz: 3c9bc25b916a9b5ca262ed489c117032185b5fc3e8ffc9feab37f94dd8b7d7f4df5a394063d48dfe0945834ef7a9fedd0d604d50f75162f8fa6e25833dc3771d
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
CHANGED
data/Rakefile
CHANGED
@@ -3,6 +3,7 @@ require 'rake'
|
|
3
3
|
|
4
4
|
begin
|
5
5
|
require 'jeweler'
|
6
|
+
|
6
7
|
Jeweler::Tasks.new do |gem|
|
7
8
|
gem.name = "pigeon"
|
8
9
|
gem.summary = %Q{Simple daemonized EventMachine engine framework with plug-in support}
|
@@ -10,21 +11,20 @@ begin
|
|
10
11
|
gem.email = "github@tadman.ca"
|
11
12
|
gem.homepage = "http://github.com/twg/pigeon"
|
12
13
|
gem.authors = %w[ tadman ]
|
13
|
-
gem.add_development_dependency 'eventmachine'
|
14
14
|
gem.executables = [ ]
|
15
15
|
end
|
16
|
+
|
16
17
|
Jeweler::GemcutterTasks.new
|
17
18
|
rescue LoadError
|
18
19
|
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
20
|
end
|
20
21
|
|
21
22
|
require 'rake/testtask'
|
23
|
+
|
22
24
|
Rake::TestTask.new(:test) do |test|
|
23
25
|
test.libs << 'lib' << 'test'
|
24
26
|
test.pattern = 'test/**/*_test.rb'
|
25
27
|
test.verbose = true
|
26
28
|
end
|
27
29
|
|
28
|
-
task :
|
29
|
-
|
30
|
-
task :default => :test
|
30
|
+
task default: :test
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0
|
data/lib/pigeon.rb
CHANGED
@@ -13,6 +13,14 @@ module Pigeon
|
|
13
13
|
autoload(:SortedArray, 'pigeon/sorted_array')
|
14
14
|
autoload(:Support, 'pigeon/support')
|
15
15
|
autoload(:Task, 'pigeon/task')
|
16
|
+
|
17
|
+
# == Module Methods =======================================================
|
18
|
+
|
19
|
+
def self.version
|
20
|
+
@version ||= File.readlines(
|
21
|
+
File.expand_path('../VERSION'), File.dirname(__FILE__)
|
22
|
+
)[0].chomp
|
23
|
+
end
|
16
24
|
end
|
17
25
|
|
18
26
|
# NOTE: This file needs to be directly loaded due to some kind of peculiar
|
data/lib/pigeon/dispatcher.rb
CHANGED
data/lib/pigeon/engine.rb
CHANGED
@@ -21,29 +21,29 @@ class Pigeon::Engine
|
|
21
21
|
option_accessor :name
|
22
22
|
option_accessor :pid_file_name
|
23
23
|
option_accessor :foreground,
|
24
|
-
:
|
24
|
+
boolean: true
|
25
25
|
option_accessor :debug,
|
26
|
-
:
|
26
|
+
boolean: true
|
27
27
|
option_accessor :log_rotation
|
28
28
|
option_accessor :engine_log_name,
|
29
|
-
:
|
29
|
+
default: 'engine.log'
|
30
30
|
option_accessor :engine_logger
|
31
31
|
option_accessor :query_log_name,
|
32
|
-
:
|
32
|
+
default: 'query.log'
|
33
33
|
option_accessor :query_logger
|
34
34
|
option_accessor :try_pid_dirs,
|
35
|
-
:
|
35
|
+
default: %w[
|
36
36
|
/var/run
|
37
37
|
/tmp
|
38
38
|
].freeze
|
39
39
|
option_accessor :try_log_dirs,
|
40
|
-
:
|
40
|
+
default: %w[
|
41
41
|
/var/log
|
42
42
|
/tmp
|
43
43
|
].freeze
|
44
44
|
option_accessor :threaded,
|
45
|
-
:
|
46
|
-
:
|
45
|
+
boolean: true,
|
46
|
+
default: false
|
47
47
|
|
48
48
|
attr_reader :id
|
49
49
|
attr_reader :state
|
@@ -125,14 +125,14 @@ class Pigeon::Engine
|
|
125
125
|
EventMachine.run do
|
126
126
|
engine = new(options)
|
127
127
|
|
128
|
-
yield(engine) if (block_given?)
|
129
|
-
|
130
128
|
Signal.trap('INT') do
|
131
129
|
engine.terminate
|
132
130
|
end
|
133
131
|
|
134
132
|
Pigeon::Engine.register_engine(engine)
|
135
133
|
|
134
|
+
yield(engine) if (block_given?)
|
135
|
+
|
136
136
|
engine.run
|
137
137
|
end
|
138
138
|
|
@@ -148,7 +148,7 @@ class Pigeon::Engine
|
|
148
148
|
|
149
149
|
pid = Pigeon::Support.daemonize(logger) do
|
150
150
|
launch({
|
151
|
-
:
|
151
|
+
logger: logger
|
152
152
|
}.merge(options || { }))
|
153
153
|
end
|
154
154
|
|
@@ -162,7 +162,7 @@ class Pigeon::Engine
|
|
162
162
|
def self.run
|
163
163
|
yield($$) if (block_given?)
|
164
164
|
|
165
|
-
launch(:
|
165
|
+
launch(foreground: true)
|
166
166
|
end
|
167
167
|
|
168
168
|
def self.stop
|
@@ -171,6 +171,7 @@ class Pigeon::Engine
|
|
171
171
|
if (pid)
|
172
172
|
begin
|
173
173
|
Process.kill('INT', pid)
|
174
|
+
|
174
175
|
rescue Errno::ESRCH
|
175
176
|
# No such process exception
|
176
177
|
pid = nil
|
@@ -178,7 +179,7 @@ class Pigeon::Engine
|
|
178
179
|
|
179
180
|
begin
|
180
181
|
while (Process.kill(0, pid))
|
181
|
-
|
182
|
+
Pigeon::Support.nap(0.1)
|
182
183
|
end
|
183
184
|
rescue Errno::ESRCH
|
184
185
|
# No such process, already terminated
|
@@ -195,8 +196,11 @@ class Pigeon::Engine
|
|
195
196
|
end
|
196
197
|
|
197
198
|
def self.restart
|
198
|
-
self.stop
|
199
|
-
|
199
|
+
self.stop do |old_pid|
|
200
|
+
self.start do |pid|
|
201
|
+
yield(pid, old_pid) if (block_given?)
|
202
|
+
end
|
203
|
+
end
|
200
204
|
end
|
201
205
|
|
202
206
|
def self.running?
|
@@ -248,6 +252,10 @@ class Pigeon::Engine
|
|
248
252
|
def self.unregister_engine(engine)
|
249
253
|
@engines.delete(engine)
|
250
254
|
end
|
255
|
+
|
256
|
+
def self.clear_engines!
|
257
|
+
@engines = [ ]
|
258
|
+
end
|
251
259
|
|
252
260
|
# Schedules a block for execution on the main EventMachine thread. This is
|
253
261
|
# a wrapper around the EventMachine.schedule method.
|
@@ -359,7 +367,10 @@ class Pigeon::Engine
|
|
359
367
|
# events.
|
360
368
|
def terminate
|
361
369
|
wrap_chain(:stop) do
|
362
|
-
EventMachine.
|
370
|
+
if (EventMachine.reactor_running?)
|
371
|
+
EventMachine.stop_event_loop
|
372
|
+
end
|
373
|
+
|
363
374
|
@state = :terminated
|
364
375
|
end
|
365
376
|
end
|
data/lib/pigeon/launcher.rb
CHANGED
@@ -18,24 +18,53 @@ class Pigeon::Launcher
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def handle_args(*args)
|
21
|
-
op = OptionParser.new
|
21
|
+
op = OptionParser.new do |parser|
|
22
|
+
parser.on('-v', '--version') do
|
23
|
+
pigeon_version = 'Pigeon %s' % Pigeon.version
|
24
|
+
|
25
|
+
version =
|
26
|
+
if (@engine.respond_to?(:version))
|
27
|
+
'%s (%s)' % [ @engine.version, pigeon_version ]
|
28
|
+
else
|
29
|
+
pigeon_version
|
30
|
+
end
|
31
|
+
|
32
|
+
puts version
|
33
|
+
exit(0)
|
34
|
+
end
|
35
|
+
end
|
22
36
|
|
23
37
|
command = op.parse(*args.flatten).first
|
24
38
|
|
25
39
|
begin
|
26
40
|
case (command)
|
27
41
|
when 'start'
|
28
|
-
@engine.start
|
42
|
+
@engine.start do |pid|
|
43
|
+
yield(pid) if (block_given?)
|
44
|
+
self.start(pid)
|
45
|
+
end
|
29
46
|
when 'stop'
|
30
|
-
@engine.stop
|
47
|
+
@engine.stop do |pid|
|
48
|
+
yield(pid) if (block_given?)
|
49
|
+
self.stop(pid)
|
50
|
+
end
|
31
51
|
when 'restart'
|
32
|
-
@engine.restart
|
52
|
+
@engine.restart do |pid, old_pid|
|
53
|
+
yield(pid, old_pid) if (block_given?)
|
54
|
+
self.restart(pid, old_pid)
|
55
|
+
end
|
33
56
|
when 'status'
|
34
|
-
@engine.status
|
57
|
+
@engine.status do |pid|
|
58
|
+
yield(pid) if (block_given?)
|
59
|
+
self.status(pid)
|
60
|
+
end
|
35
61
|
when 'run'
|
36
62
|
@engine.engine_logger = Pigeon::Logger.new(STDOUT)
|
37
63
|
|
38
|
-
@engine.run
|
64
|
+
@engine.run do |pid|
|
65
|
+
yield(pid) if (block_given?)
|
66
|
+
self.run(pid)
|
67
|
+
end
|
39
68
|
else
|
40
69
|
usage
|
41
70
|
end
|
data/lib/pigeon/support.rb
CHANGED
@@ -30,7 +30,6 @@ module Pigeon::Support
|
|
30
30
|
if (thread.backtrace)
|
31
31
|
logger.error("\t" + thread.backtrace.join("\n\t"))
|
32
32
|
end
|
33
|
-
|
34
33
|
end
|
35
34
|
end
|
36
35
|
|
@@ -39,22 +38,41 @@ module Pigeon::Support
|
|
39
38
|
end
|
40
39
|
|
41
40
|
begin
|
42
|
-
|
41
|
+
interrupted = false
|
42
|
+
|
43
|
+
Signal.trap('INT') do
|
44
|
+
interrupted = true
|
45
|
+
Process.kill('INT', daemon_pid)
|
46
|
+
|
47
|
+
relaunch = false
|
48
|
+
end
|
49
|
+
|
50
|
+
pid, status = Process.wait2(daemon_pid)
|
51
|
+
|
52
|
+
if (interrupted)
|
53
|
+
logger.info("Supervisor #{Process.pid} received termination signal, shut down child #{daemon_pid}")
|
54
|
+
end
|
43
55
|
|
44
56
|
# A non-zero exit status indicates some sort of error, so the
|
45
57
|
# process will be relaunched after a short delay.
|
46
58
|
relaunch = ($? != 0)
|
47
59
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
60
|
+
ensure
|
61
|
+
# Reset Signal handler before forking again
|
62
|
+
Signal.trap('INT') do
|
63
|
+
end
|
52
64
|
end
|
53
65
|
|
54
66
|
if (relaunch)
|
55
|
-
|
67
|
+
begin
|
68
|
+
logger.info("Supervisor #{Process.pid} will relaunch in %d seconds" % delay)
|
69
|
+
sleep(delay)
|
70
|
+
|
71
|
+
rescue Interrupt
|
72
|
+
logger.info("Supervisor #{Process.pid} abandoing restart because of termination")
|
56
73
|
|
57
|
-
|
74
|
+
relaunch = false
|
75
|
+
end
|
58
76
|
else
|
59
77
|
logger.info("Terminated normally")
|
60
78
|
end
|
@@ -66,13 +84,17 @@ module Pigeon::Support
|
|
66
84
|
wfd.close
|
67
85
|
end
|
68
86
|
|
69
|
-
Process.
|
87
|
+
pid, status = Process.wait2(forked_pid)
|
70
88
|
|
71
89
|
daemon_pid = rfd.readline
|
72
90
|
rfd.close
|
73
91
|
|
74
92
|
daemon_pid.to_i
|
75
93
|
end
|
94
|
+
|
95
|
+
def nap(time)
|
96
|
+
select(nil, nil, nil, time.to_f)
|
97
|
+
end
|
76
98
|
|
77
99
|
# Finds the first directory in the given list that exists and is
|
78
100
|
# writable. Returns nil if none match.
|
data/lib/pigeon/task.rb
CHANGED
@@ -1,4 +1,9 @@
|
|
1
1
|
class Pigeon::Task
|
2
|
+
# == Exceptions ===========================================================
|
3
|
+
|
4
|
+
class EngineRequired < StandardError
|
5
|
+
end
|
6
|
+
|
2
7
|
# == Constants ============================================================
|
3
8
|
|
4
9
|
# == Properties ===========================================================
|
@@ -35,6 +40,10 @@ class Pigeon::Task
|
|
35
40
|
@context = context
|
36
41
|
@engine = engine || Pigeon::Engine.default_engine
|
37
42
|
@created_at = Time.now
|
43
|
+
|
44
|
+
unless (@engine)
|
45
|
+
raise EngineRequired, "Task creation requires an active Pigeon::Engine"
|
46
|
+
end
|
38
47
|
|
39
48
|
after_initialized
|
40
49
|
end
|
data/pigeon.gemspec
CHANGED
@@ -2,14 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: pigeon 1.0.0 ruby lib
|
5
6
|
|
6
7
|
Gem::Specification.new do |s|
|
7
8
|
s.name = "pigeon"
|
8
|
-
s.version = "0.
|
9
|
+
s.version = "1.0.0"
|
9
10
|
|
10
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib"]
|
11
13
|
s.authors = ["tadman"]
|
12
|
-
s.date = "
|
14
|
+
s.date = "2015-02-18"
|
13
15
|
s.description = "Pigeon is a simple way to get started building an EventMachine engine that's intended to run as a background job."
|
14
16
|
s.email = "github@tadman.ca"
|
15
17
|
s.extra_rdoc_files = [
|
@@ -18,6 +20,8 @@ Gem::Specification.new do |s|
|
|
18
20
|
]
|
19
21
|
s.files = [
|
20
22
|
".document",
|
23
|
+
".travis.yml",
|
24
|
+
"Gemfile",
|
21
25
|
"LICENSE",
|
22
26
|
"README.md",
|
23
27
|
"Rakefile",
|
@@ -51,20 +55,28 @@ Gem::Specification.new do |s|
|
|
51
55
|
"test/unit/pigeon_test.rb"
|
52
56
|
]
|
53
57
|
s.homepage = "http://github.com/twg/pigeon"
|
54
|
-
s.
|
55
|
-
s.rubygems_version = "1.8.25"
|
58
|
+
s.rubygems_version = "2.2.2"
|
56
59
|
s.summary = "Simple daemonized EventMachine engine framework with plug-in support"
|
57
60
|
|
58
61
|
if s.respond_to? :specification_version then
|
59
|
-
s.specification_version =
|
62
|
+
s.specification_version = 4
|
60
63
|
|
61
64
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
62
|
-
s.
|
65
|
+
s.add_runtime_dependency(%q<eventmachine>, [">= 0"])
|
66
|
+
s.add_development_dependency(%q<minitest-reporters>, [">= 0"])
|
67
|
+
s.add_development_dependency(%q<minitest>, [">= 0"])
|
68
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
63
69
|
else
|
64
70
|
s.add_dependency(%q<eventmachine>, [">= 0"])
|
71
|
+
s.add_dependency(%q<minitest-reporters>, [">= 0"])
|
72
|
+
s.add_dependency(%q<minitest>, [">= 0"])
|
73
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
65
74
|
end
|
66
75
|
else
|
67
76
|
s.add_dependency(%q<eventmachine>, [">= 0"])
|
77
|
+
s.add_dependency(%q<minitest-reporters>, [">= 0"])
|
78
|
+
s.add_dependency(%q<minitest>, [">= 0"])
|
79
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
68
80
|
end
|
69
81
|
end
|
70
82
|
|
data/test/helper.rb
CHANGED
@@ -1,35 +1,63 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require '
|
2
|
+
require 'bundler/setup'
|
3
3
|
|
4
|
-
|
5
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
4
|
+
require 'minitest'
|
6
5
|
|
7
|
-
|
6
|
+
module Minitest
|
7
|
+
# The default Minitest behavior to intercept at_exit and rewrite the exit
|
8
|
+
# status code based on test results is okay for the parent process, but
|
9
|
+
# causes friction when using fork within tests. Here it's disabled unless
|
10
|
+
# the process terminating is the parent.
|
11
|
+
def self.autorun
|
12
|
+
return if (@at_exit_hook_installed)
|
8
13
|
|
9
|
-
|
14
|
+
@at_exit_hook_installed = Process.pid
|
15
|
+
|
16
|
+
at_exit do
|
17
|
+
next if $! and not ($!.kind_of? SystemExit and $!.success?)
|
18
|
+
|
19
|
+
exit_code = Minitest.run(ARGV)
|
20
|
+
|
21
|
+
@@after_run.reverse_each do |block|
|
22
|
+
block.call
|
23
|
+
|
24
|
+
if (Process.pid != @at_exit_hook_installed)
|
25
|
+
break
|
26
|
+
end
|
27
|
+
end
|
10
28
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
raise "EventMachine gem is not installed or could not be loaded: [#{e.class}] #{e}"
|
29
|
+
exit(exit_code)
|
30
|
+
end
|
31
|
+
end
|
15
32
|
end
|
16
33
|
|
34
|
+
require 'minitest/reporters'
|
35
|
+
require 'minitest/autorun'
|
36
|
+
require 'timeout'
|
37
|
+
|
38
|
+
Minitest::Reporters.use!(Minitest::Reporters::SpecReporter.new)
|
39
|
+
|
40
|
+
$LOAD_PATH.unshift(File.expand_path(File.join(*%w[ .. lib ]), File.dirname(__FILE__)))
|
41
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
42
|
+
|
17
43
|
require 'pigeon'
|
44
|
+
require 'eventmachine'
|
18
45
|
|
19
|
-
class Test
|
46
|
+
class Minitest::Test
|
20
47
|
def assert_timeout(time, message = nil, &block)
|
21
48
|
Timeout::timeout(time, &block)
|
49
|
+
|
22
50
|
rescue Timeout::Error
|
23
51
|
flunk(message || 'assert_timeout timed out')
|
24
52
|
end
|
25
53
|
|
26
54
|
def assert_eventually(time = nil, message = nil, &block)
|
27
|
-
start_time = Time.now.
|
55
|
+
start_time = Time.now.to_f
|
28
56
|
|
29
57
|
while (!block.call)
|
30
58
|
select(nil, nil, nil, 0.1)
|
31
59
|
|
32
|
-
if (time and (Time.now.
|
60
|
+
if (time and (Time.now.to_f - start_time > time))
|
33
61
|
flunk(message || 'assert_eventually timed out')
|
34
62
|
end
|
35
63
|
end
|
@@ -37,32 +65,64 @@ class Test::Unit::TestCase
|
|
37
65
|
|
38
66
|
def engine
|
39
67
|
exception = nil
|
68
|
+
test_thread = nil
|
40
69
|
|
41
|
-
|
42
|
-
|
70
|
+
engine_thread =
|
71
|
+
Thread.new do
|
72
|
+
Thread.abort_on_exception = true
|
73
|
+
|
74
|
+
Pigeon::Engine.clear_engines!
|
75
|
+
|
76
|
+
# Create a thread for the engine to run on
|
77
|
+
begin
|
78
|
+
Pigeon::Engine.launch do |launched|
|
79
|
+
@engine = launched
|
80
|
+
end
|
81
|
+
|
82
|
+
rescue Object => exception
|
83
|
+
end
|
84
|
+
end
|
43
85
|
|
44
|
-
|
45
|
-
# the EventMachine loop.
|
86
|
+
test_thread =
|
46
87
|
Thread.new do
|
88
|
+
# Execute the test code in a separate thread to avoid blocking
|
89
|
+
# the EventMachine loop.
|
47
90
|
begin
|
48
|
-
|
49
|
-
|
91
|
+
while (!@engine or Pigeon::Engine.default_engine != @engine)
|
92
|
+
# Wait impatiently.
|
93
|
+
end
|
94
|
+
|
95
|
+
yield(@engine)
|
50
96
|
rescue Object => exception
|
51
97
|
ensure
|
52
98
|
begin
|
53
|
-
|
54
|
-
|
55
|
-
|
99
|
+
if (EventMachine.reactor_running?)
|
100
|
+
EventMachine.stop_event_loop
|
101
|
+
end
|
102
|
+
rescue Object
|
103
|
+
STDERR.puts("[#{exception.class}] #{exception}")
|
56
104
|
# Shutting down may trigger an exception from time to time
|
57
105
|
# if the engine itself has failed.
|
58
|
-
STDERR.puts("Exception: [#{e.class}] #{e}")
|
59
106
|
end
|
60
107
|
end
|
61
108
|
end
|
109
|
+
|
110
|
+
test_thread.join
|
111
|
+
|
112
|
+
begin
|
113
|
+
Timeout.timeout(1) do
|
114
|
+
engine_thread.join
|
115
|
+
end
|
116
|
+
rescue Timeout::Error
|
117
|
+
engine_thread.kill
|
62
118
|
end
|
63
119
|
|
64
120
|
if (exception)
|
65
121
|
raise exception
|
66
122
|
end
|
123
|
+
ensure
|
124
|
+
if (engine_thread)
|
125
|
+
engine_thread.kill
|
126
|
+
end
|
67
127
|
end
|
68
128
|
end
|
@@ -71,10 +71,12 @@ class ShutdownCallbackTestEngine < Pigeon::Engine
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
-
class TestPigeonEngine < Test
|
74
|
+
class TestPigeonEngine < Minitest::Test
|
75
75
|
def test_default_options
|
76
76
|
assert TestEngine.engine_logger
|
77
77
|
assert TestEngine.engine_logger.is_a?(Logger)
|
78
|
+
|
79
|
+
assert_equal nil, TestEngine.default_engine
|
78
80
|
end
|
79
81
|
|
80
82
|
def test_example_subclass
|
@@ -82,13 +84,13 @@ class TestPigeonEngine < Test::Unit::TestCase
|
|
82
84
|
|
83
85
|
read_fd, write_fd = IO.pipe
|
84
86
|
|
85
|
-
TestEngine.start(:
|
87
|
+
TestEngine.start(pipe: write_fd) do |pid|
|
86
88
|
assert pid
|
87
89
|
engine_pid = pid
|
88
90
|
end
|
89
91
|
|
90
92
|
write_fd.close
|
91
|
-
|
93
|
+
|
92
94
|
Timeout::timeout(5) do
|
93
95
|
assert_equal "STARTED\n", read_fd.readline
|
94
96
|
end
|
@@ -96,7 +98,7 @@ class TestPigeonEngine < Test::Unit::TestCase
|
|
96
98
|
TestEngine.status do |pid|
|
97
99
|
assert_equal engine_pid, pid
|
98
100
|
end
|
99
|
-
|
101
|
+
|
100
102
|
TestEngine.stop do |pid|
|
101
103
|
assert_equal engine_pid, pid
|
102
104
|
end
|
@@ -111,7 +113,7 @@ class TestPigeonEngine < Test::Unit::TestCase
|
|
111
113
|
|
112
114
|
read_fd, write_fd = IO.pipe
|
113
115
|
|
114
|
-
engine_pid = TestEngine.start(:
|
116
|
+
engine_pid = TestEngine.start(pipe: write_fd)
|
115
117
|
|
116
118
|
write_fd.close
|
117
119
|
|
@@ -135,7 +137,7 @@ class TestPigeonEngine < Test::Unit::TestCase
|
|
135
137
|
|
136
138
|
read_fd, write_fd = IO.pipe
|
137
139
|
|
138
|
-
CallbackTestEngine.start(:
|
140
|
+
CallbackTestEngine.start(pipe: write_fd) do |pid|
|
139
141
|
assert pid
|
140
142
|
engine_pid = pid
|
141
143
|
end
|
@@ -169,6 +171,23 @@ class TestPigeonEngine < Test::Unit::TestCase
|
|
169
171
|
|
170
172
|
assert_equal expected_callbacks, read_fd.read.split(/\n/).collect(&:to_sym)
|
171
173
|
end
|
174
|
+
|
175
|
+
def test_fork_exitstatus
|
176
|
+
child_pid = fork do
|
177
|
+
end
|
178
|
+
|
179
|
+
pid, status = Process.wait2(child_pid)
|
180
|
+
|
181
|
+
assert_equal 0, status.exitstatus
|
182
|
+
|
183
|
+
child_pid = fork do
|
184
|
+
exit(90)
|
185
|
+
end
|
186
|
+
|
187
|
+
pid, status = Process.wait2(child_pid)
|
188
|
+
|
189
|
+
assert_equal 90, status.exitstatus
|
190
|
+
end
|
172
191
|
|
173
192
|
def test_shutdown_engine
|
174
193
|
engine_pid = nil
|
@@ -179,15 +198,12 @@ class TestPigeonEngine < Test::Unit::TestCase
|
|
179
198
|
|
180
199
|
assert engine_pid
|
181
200
|
|
182
|
-
|
183
|
-
|
184
|
-
running_pid = nil
|
185
|
-
|
186
|
-
ShutdownEngine.status do |pid|
|
187
|
-
running_pid = pid
|
201
|
+
assert_eventually(5) do
|
202
|
+
!ShutdownEngine.status
|
188
203
|
end
|
189
|
-
|
190
|
-
|
204
|
+
|
205
|
+
ensure
|
206
|
+
ShutdownEngine.stop
|
191
207
|
end
|
192
208
|
|
193
209
|
def test_shutdown_engine_with_blocking_callback
|
@@ -6,7 +6,7 @@ class Pigeon::Launcher
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
-
class PigeonLauncherTest < Test
|
9
|
+
class PigeonLauncherTest < Minitest::Test
|
10
10
|
def test_default_launcher
|
11
11
|
pid = Pigeon::Launcher.launch
|
12
12
|
|
@@ -19,41 +19,40 @@ class PigeonLauncherTest < Test::Unit::TestCase
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_triggers
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
launcher = Pigeon::Launcher.new(Pigeon::Engine)
|
23
|
+
|
24
|
+
triggered = Hash.new { |h,k| h[k] = [ ] }
|
25
25
|
|
26
|
-
|
27
|
-
start
|
28
|
-
triggered[:start] += 1
|
29
|
-
end
|
26
|
+
launcher.handle_args('start') do |pid|
|
27
|
+
triggered[:start] << pid
|
30
28
|
end
|
31
29
|
|
32
|
-
|
33
|
-
|
34
|
-
triggered[:restart] += 1
|
35
|
-
end
|
30
|
+
launcher.handle_args('status') do |pid|
|
31
|
+
triggered[:status] << pid
|
36
32
|
end
|
37
33
|
|
38
|
-
|
39
|
-
|
40
|
-
triggered[:status] += 1
|
41
|
-
end
|
34
|
+
launcher.handle_args('restart') do |pid, old_pid|
|
35
|
+
triggered[:restart] << pid
|
42
36
|
end
|
43
37
|
|
44
|
-
|
45
|
-
|
46
|
-
triggered[:stop] += 1
|
47
|
-
end
|
38
|
+
launcher.handle_args('status') do |pid|
|
39
|
+
triggered[:status] << pid
|
48
40
|
end
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
if (false)
|
53
|
-
assert_equal 1, triggered[:start]
|
54
|
-
assert_equal 1, triggered[:restart]
|
55
|
-
assert_equal 1, triggered[:status]
|
56
|
-
assert_equal 1, triggered[:stop]
|
41
|
+
|
42
|
+
launcher.handle_args('stop') do |pid|
|
43
|
+
triggered[:stop] << pid
|
57
44
|
end
|
45
|
+
|
46
|
+
assert triggered[:start]
|
47
|
+
assert_equal 1, triggered[:start].length
|
48
|
+
|
49
|
+
|
50
|
+
assert triggered[:restart]
|
51
|
+
assert_equal 1, triggered[:restart].length
|
52
|
+
refute_equal triggered[:start], triggered[:restart]
|
53
|
+
|
54
|
+
assert_equal triggered[:start] + triggered[:restart], triggered[:status]
|
55
|
+
|
56
|
+
assert_equal triggered[:restart], triggered[:stop]
|
58
57
|
end
|
59
58
|
end
|
@@ -6,10 +6,10 @@ class OptionClass
|
|
6
6
|
option_accessor :single_option
|
7
7
|
option_accessor :multi1, :multi2
|
8
8
|
option_accessor :option_with_default,
|
9
|
-
:
|
9
|
+
default: :example_default
|
10
10
|
|
11
11
|
option_accessor :boolean_option,
|
12
|
-
:
|
12
|
+
boolean: true
|
13
13
|
|
14
14
|
self.single_option = :single_option_default
|
15
15
|
end
|
@@ -17,7 +17,7 @@ end
|
|
17
17
|
class OptionSubclass < OptionClass
|
18
18
|
end
|
19
19
|
|
20
|
-
class PigeonOptionAccessorTest < Test
|
20
|
+
class PigeonOptionAccessorTest < Minitest::Test
|
21
21
|
def test_class_and_instance_chaining
|
22
22
|
assert_equal :single_option_default, OptionClass.single_option
|
23
23
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.expand_path(File.join(*%w[ .. helper ]), File.dirname(__FILE__))
|
2
2
|
|
3
|
-
class PigeonProcessorTest < Test
|
3
|
+
class PigeonProcessorTest < Minitest::Test
|
4
4
|
class TaggedTask < Pigeon::Task
|
5
5
|
attr_accessor :tag
|
6
6
|
attr_reader :last_task
|
@@ -9,6 +9,10 @@ class PigeonProcessorTest < Test::Unit::TestCase
|
|
9
9
|
super(options)
|
10
10
|
@tag = tag
|
11
11
|
end
|
12
|
+
|
13
|
+
def state_initialized!
|
14
|
+
transition_to_state(:finished)
|
15
|
+
end
|
12
16
|
|
13
17
|
def inspect
|
14
18
|
"<#{@tag}>"
|
@@ -46,7 +50,7 @@ class PigeonProcessorTest < Test::Unit::TestCase
|
|
46
50
|
end
|
47
51
|
|
48
52
|
assert_equal false, processor.task?
|
49
|
-
|
53
|
+
|
50
54
|
queue << TaggedTask.new(0)
|
51
55
|
|
52
56
|
assert_eventually(1) do
|
@@ -71,16 +75,18 @@ class PigeonProcessorTest < Test::Unit::TestCase
|
|
71
75
|
end
|
72
76
|
|
73
77
|
def test_on_backlog
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
78
|
+
engine do
|
79
|
+
queue = Pigeon::Queue.new
|
80
|
+
|
81
|
+
100.times do |n|
|
82
|
+
queue << TaggedTask.new(n)
|
83
|
+
end
|
84
|
+
|
85
|
+
processor = Pigeon::Processor.new(queue)
|
86
|
+
|
87
|
+
assert_eventually(5) do
|
88
|
+
queue.empty?
|
89
|
+
end
|
84
90
|
end
|
85
91
|
end
|
86
92
|
|
@@ -145,7 +151,7 @@ class PigeonProcessorTest < Test::Unit::TestCase
|
|
145
151
|
queue << TaggedTask.new(n)
|
146
152
|
end
|
147
153
|
|
148
|
-
assert_eventually(
|
154
|
+
assert_eventually(5) do
|
149
155
|
queue.length == count
|
150
156
|
end
|
151
157
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.expand_path(File.join(*%w[ .. helper ]), File.dirname(__FILE__))
|
2
2
|
|
3
|
-
class PigeonSortedArrayTest < Test
|
3
|
+
class PigeonSortedArrayTest < Minitest::Test
|
4
4
|
def test_empty_state
|
5
5
|
array = Pigeon::SortedArray.new
|
6
6
|
|
@@ -21,13 +21,13 @@ class PigeonSortedArrayTest < Test::Unit::TestCase
|
|
21
21
|
def test_does_sorting
|
22
22
|
array = Pigeon::SortedArray.new
|
23
23
|
|
24
|
-
test_array = [2, 2, 2, 2, 3, 3, 3]
|
24
|
+
test_array = [ 2, 2, 2, 2, 3, 3, 3 ]
|
25
25
|
|
26
26
|
test_array.each do |i|
|
27
27
|
array << i
|
28
28
|
end
|
29
29
|
|
30
|
-
assert_equal [2, 2, 2, 2, 3, 3, 3], array.to_a
|
30
|
+
assert_equal [ 2, 2, 2, 2, 3, 3, 3 ], array.to_a
|
31
31
|
end
|
32
32
|
|
33
33
|
def test_does_sorting_with_insertion_in_order
|
@@ -1,5 +1,11 @@
|
|
1
1
|
require File.expand_path(File.join(*%w[ .. helper ]), File.dirname(__FILE__))
|
2
2
|
|
3
|
+
class TerminateTask < Pigeon::Task
|
4
|
+
def state_initialized!
|
5
|
+
transition_to_state(:finished)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
3
9
|
class ExampleTask < Pigeon::Task
|
4
10
|
attr_accessor :triggers
|
5
11
|
|
@@ -43,10 +49,10 @@ class FailingTask < Pigeon::Task
|
|
43
49
|
end
|
44
50
|
end
|
45
51
|
|
46
|
-
class PigeonTaskTest < Test
|
52
|
+
class PigeonTaskTest < Minitest::Test
|
47
53
|
def test_empty_task
|
48
54
|
engine do
|
49
|
-
task =
|
55
|
+
task = TerminateTask.new
|
50
56
|
|
51
57
|
reported = 0
|
52
58
|
|
@@ -68,14 +74,19 @@ class PigeonTaskTest < Test::Unit::TestCase
|
|
68
74
|
end
|
69
75
|
|
70
76
|
def test_alternate_engine
|
71
|
-
engine
|
72
|
-
|
73
|
-
|
74
|
-
|
77
|
+
engine do |default_engine|
|
78
|
+
engine = Pigeon::Engine.new
|
79
|
+
|
80
|
+
refute_equal default_engine.object_id, engine.object_id
|
81
|
+
|
82
|
+
task = Pigeon::Task.new(nil, engine)
|
83
|
+
|
84
|
+
assert_equal engine.object_id, task.engine.object_id
|
85
|
+
end
|
75
86
|
end
|
76
87
|
|
77
88
|
def test_example_task
|
78
|
-
engine do
|
89
|
+
engine do |launched|
|
79
90
|
task = ExampleTask.new
|
80
91
|
|
81
92
|
callbacks = [ ]
|
@@ -137,23 +148,25 @@ class PigeonTaskTest < Test::Unit::TestCase
|
|
137
148
|
end
|
138
149
|
|
139
150
|
def test_with_context
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
151
|
+
engine do
|
152
|
+
options = {
|
153
|
+
example: 'example1',
|
154
|
+
optional: 1
|
155
|
+
}.freeze
|
156
|
+
|
157
|
+
task = Pigeon::Task.new(options)
|
158
|
+
|
159
|
+
assert_equal options, task.context
|
160
|
+
|
161
|
+
task.context = 'test'
|
162
|
+
|
163
|
+
assert_equal 'test', task.context
|
164
|
+
end
|
152
165
|
end
|
153
166
|
|
154
167
|
def test_block_notification
|
155
168
|
engine do
|
156
|
-
task =
|
169
|
+
task = TerminateTask.new
|
157
170
|
|
158
171
|
states_triggered = [ ]
|
159
172
|
|
@@ -170,15 +183,17 @@ class PigeonTaskTest < Test::Unit::TestCase
|
|
170
183
|
end
|
171
184
|
|
172
185
|
def test_priority_order
|
173
|
-
|
174
|
-
|
186
|
+
engine do
|
187
|
+
tasks = (0..10).collect do
|
188
|
+
task = Pigeon::Task.new
|
175
189
|
|
176
|
-
|
177
|
-
|
190
|
+
# Trigger generation of default priority value
|
191
|
+
task.priority
|
178
192
|
|
179
|
-
|
193
|
+
task
|
194
|
+
end
|
195
|
+
|
196
|
+
assert_equal tasks.collect(&:object_id), tasks.sort.collect(&:object_id)
|
180
197
|
end
|
181
|
-
|
182
|
-
assert_equal tasks.collect(&:object_id), tasks.sort.collect(&:object_id)
|
183
198
|
end
|
184
199
|
end
|
data/test/unit/pigeon_test.rb
CHANGED
metadata
CHANGED
@@ -1,30 +1,69 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pigeon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- tadman
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2015-02-18 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: eventmachine
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest-reporters
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: jeweler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
20
60
|
- !ruby/object:Gem::Version
|
21
61
|
version: '0'
|
22
62
|
type: :development
|
23
63
|
prerelease: false
|
24
64
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
65
|
requirements:
|
27
|
-
- -
|
66
|
+
- - ">="
|
28
67
|
- !ruby/object:Gem::Version
|
29
68
|
version: '0'
|
30
69
|
description: Pigeon is a simple way to get started building an EventMachine engine
|
@@ -36,7 +75,9 @@ extra_rdoc_files:
|
|
36
75
|
- LICENSE
|
37
76
|
- README.md
|
38
77
|
files:
|
39
|
-
- .document
|
78
|
+
- ".document"
|
79
|
+
- ".travis.yml"
|
80
|
+
- Gemfile
|
40
81
|
- LICENSE
|
41
82
|
- README.md
|
42
83
|
- Rakefile
|
@@ -70,26 +111,25 @@ files:
|
|
70
111
|
- test/unit/pigeon_test.rb
|
71
112
|
homepage: http://github.com/twg/pigeon
|
72
113
|
licenses: []
|
114
|
+
metadata: {}
|
73
115
|
post_install_message:
|
74
116
|
rdoc_options: []
|
75
117
|
require_paths:
|
76
118
|
- lib
|
77
119
|
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
-
none: false
|
79
120
|
requirements:
|
80
|
-
- -
|
121
|
+
- - ">="
|
81
122
|
- !ruby/object:Gem::Version
|
82
123
|
version: '0'
|
83
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
-
none: false
|
85
125
|
requirements:
|
86
|
-
- -
|
126
|
+
- - ">="
|
87
127
|
- !ruby/object:Gem::Version
|
88
128
|
version: '0'
|
89
129
|
requirements: []
|
90
130
|
rubyforge_project:
|
91
|
-
rubygems_version:
|
131
|
+
rubygems_version: 2.2.2
|
92
132
|
signing_key:
|
93
|
-
specification_version:
|
133
|
+
specification_version: 4
|
94
134
|
summary: Simple daemonized EventMachine engine framework with plug-in support
|
95
135
|
test_files: []
|