meepo 1.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +29 -0
  6. data/.travis.yml +10 -0
  7. data/Dockerfile +7 -0
  8. data/Gemfile +13 -0
  9. data/MIT-LICENSE +20 -0
  10. data/Rakefile +15 -0
  11. data/TODO +5 -0
  12. data/bin/invoker +7 -0
  13. data/contrib/completion/invoker-completion.bash +70 -0
  14. data/contrib/completion/invoker-completion.zsh +62 -0
  15. data/examples/hello_sinatra.rb +26 -0
  16. data/examples/sample.ini +3 -0
  17. data/invoker.gemspec +43 -0
  18. data/lib/invoker.rb +152 -0
  19. data/lib/invoker/cli.rb +159 -0
  20. data/lib/invoker/cli/pinger.rb +23 -0
  21. data/lib/invoker/cli/question.rb +15 -0
  22. data/lib/invoker/cli/tail.rb +34 -0
  23. data/lib/invoker/cli/tail_watcher.rb +34 -0
  24. data/lib/invoker/command_worker.rb +60 -0
  25. data/lib/invoker/commander.rb +95 -0
  26. data/lib/invoker/daemon.rb +126 -0
  27. data/lib/invoker/dns_cache.rb +23 -0
  28. data/lib/invoker/errors.rb +17 -0
  29. data/lib/invoker/event/manager.rb +79 -0
  30. data/lib/invoker/ipc.rb +45 -0
  31. data/lib/invoker/ipc/add_command.rb +12 -0
  32. data/lib/invoker/ipc/add_http_command.rb +10 -0
  33. data/lib/invoker/ipc/base_command.rb +24 -0
  34. data/lib/invoker/ipc/client_handler.rb +26 -0
  35. data/lib/invoker/ipc/dns_check_command.rb +17 -0
  36. data/lib/invoker/ipc/list_command.rb +11 -0
  37. data/lib/invoker/ipc/message.rb +170 -0
  38. data/lib/invoker/ipc/message/list_response.rb +35 -0
  39. data/lib/invoker/ipc/message/tail_response.rb +10 -0
  40. data/lib/invoker/ipc/ping_command.rb +10 -0
  41. data/lib/invoker/ipc/reload_command.rb +12 -0
  42. data/lib/invoker/ipc/remove_command.rb +12 -0
  43. data/lib/invoker/ipc/server.rb +26 -0
  44. data/lib/invoker/ipc/tail_command.rb +11 -0
  45. data/lib/invoker/ipc/unix_client.rb +60 -0
  46. data/lib/invoker/logger.rb +13 -0
  47. data/lib/invoker/parsers/config.rb +184 -0
  48. data/lib/invoker/parsers/procfile.rb +86 -0
  49. data/lib/invoker/power/balancer.rb +131 -0
  50. data/lib/invoker/power/config.rb +77 -0
  51. data/lib/invoker/power/dns.rb +38 -0
  52. data/lib/invoker/power/http_parser.rb +68 -0
  53. data/lib/invoker/power/http_response.rb +81 -0
  54. data/lib/invoker/power/pf_migrate.rb +64 -0
  55. data/lib/invoker/power/port_finder.rb +49 -0
  56. data/lib/invoker/power/power.rb +3 -0
  57. data/lib/invoker/power/powerup.rb +29 -0
  58. data/lib/invoker/power/setup.rb +90 -0
  59. data/lib/invoker/power/setup/distro/arch.rb +15 -0
  60. data/lib/invoker/power/setup/distro/base.rb +57 -0
  61. data/lib/invoker/power/setup/distro/debian.rb +11 -0
  62. data/lib/invoker/power/setup/distro/mint.rb +10 -0
  63. data/lib/invoker/power/setup/distro/opensuse.rb +11 -0
  64. data/lib/invoker/power/setup/distro/redhat.rb +11 -0
  65. data/lib/invoker/power/setup/distro/ubuntu.rb +10 -0
  66. data/lib/invoker/power/setup/files/invoker_forwarder.sh.erb +17 -0
  67. data/lib/invoker/power/setup/files/socat_invoker.service +12 -0
  68. data/lib/invoker/power/setup/linux_setup.rb +105 -0
  69. data/lib/invoker/power/setup/osx_setup.rb +137 -0
  70. data/lib/invoker/power/templates/400.html +40 -0
  71. data/lib/invoker/power/templates/404.html +40 -0
  72. data/lib/invoker/power/templates/503.html +40 -0
  73. data/lib/invoker/power/url_rewriter.rb +40 -0
  74. data/lib/invoker/process_manager.rb +198 -0
  75. data/lib/invoker/process_printer.rb +43 -0
  76. data/lib/invoker/reactor.rb +37 -0
  77. data/lib/invoker/reactor/reader.rb +54 -0
  78. data/lib/invoker/version.rb +47 -0
  79. data/readme.md +25 -0
  80. data/spec/invoker/cli/pinger_spec.rb +22 -0
  81. data/spec/invoker/cli/tail_watcher_spec.rb +39 -0
  82. data/spec/invoker/cli_spec.rb +27 -0
  83. data/spec/invoker/command_worker_spec.rb +45 -0
  84. data/spec/invoker/commander_spec.rb +152 -0
  85. data/spec/invoker/config_spec.rb +361 -0
  86. data/spec/invoker/daemon_spec.rb +34 -0
  87. data/spec/invoker/event/manager_spec.rb +67 -0
  88. data/spec/invoker/invoker_spec.rb +71 -0
  89. data/spec/invoker/ipc/client_handler_spec.rb +54 -0
  90. data/spec/invoker/ipc/dns_check_command_spec.rb +32 -0
  91. data/spec/invoker/ipc/message/list_response_spec.rb +24 -0
  92. data/spec/invoker/ipc/message_spec.rb +49 -0
  93. data/spec/invoker/ipc/unix_client_spec.rb +29 -0
  94. data/spec/invoker/power/balancer_spec.rb +22 -0
  95. data/spec/invoker/power/config_spec.rb +18 -0
  96. data/spec/invoker/power/http_parser_spec.rb +32 -0
  97. data/spec/invoker/power/http_response_spec.rb +34 -0
  98. data/spec/invoker/power/pf_migrate_spec.rb +87 -0
  99. data/spec/invoker/power/port_finder_spec.rb +16 -0
  100. data/spec/invoker/power/setup/linux_setup_spec.rb +103 -0
  101. data/spec/invoker/power/setup/osx_setup_spec.rb +105 -0
  102. data/spec/invoker/power/setup_spec.rb +4 -0
  103. data/spec/invoker/power/url_rewriter_spec.rb +70 -0
  104. data/spec/invoker/power/web_sockets_spec.rb +61 -0
  105. data/spec/invoker/process_manager_spec.rb +130 -0
  106. data/spec/invoker/reactor_spec.rb +6 -0
  107. data/spec/spec_helper.rb +43 -0
  108. metadata +389 -0
@@ -0,0 +1,43 @@
1
+ module Invoker
2
+ class ProcessPrinter
3
+ MAX_COLUMN_WIDTH = 40
4
+ attr_accessor :list_response
5
+
6
+ def initialize(list_response)
7
+ self.list_response = list_response
8
+ end
9
+
10
+ def print_table
11
+ hash_with_colors = []
12
+ list_response.processes.each do |process|
13
+ if process.pid
14
+ hash_with_colors << colorize_hash(process, "green")
15
+ else
16
+ hash_with_colors << colorize_hash(process, "light_black")
17
+ end
18
+ end
19
+ Formatador.display_compact_table(hash_with_colors)
20
+ end
21
+
22
+ private
23
+
24
+ def colorize_hash(process, color)
25
+ hash_with_colors = {}
26
+
27
+ hash_with_colors['dir'] = colored_string(process.dir, color)
28
+ hash_with_colors['pid'] = colored_string(process.pid || 'Not Running', color)
29
+ hash_with_colors['port'] = colored_string(process.port, color)
30
+ hash_with_colors['shell_command'] = colored_string(process.shell_command, color)
31
+ hash_with_colors['process_name'] = colored_string(process.process_name, color)
32
+ hash_with_colors
33
+ end
34
+
35
+ def colored_string(string, color)
36
+ string = string.to_s
37
+ if string.length > MAX_COLUMN_WIDTH
38
+ string = "#{string[0..MAX_COLUMN_WIDTH]}.."
39
+ end
40
+ "[#{color}]#{string}[/]"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,37 @@
1
+ module Invoker
2
+ class Reactor
3
+ attr_accessor :reader
4
+
5
+ def initialize
6
+ @reader = Invoker::Reactor::Reader.new
7
+ end
8
+
9
+ def watch_for_read(fd)
10
+ reader.watch_for_read(fd)
11
+ end
12
+
13
+ # Writes data to client socket and raises error if errors
14
+ # while writing
15
+ def send_data(socket, data)
16
+ socket.write(data)
17
+ rescue
18
+ raise Invoker::Errors::ClientDisconnected
19
+ end
20
+
21
+ def monitor_for_fd_events
22
+ ready_read_fds, _ , _ = select(*options_for_select)
23
+
24
+ if ready_read_fds && !ready_read_fds.empty?
25
+ reader.handle_read_event(ready_read_fds)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def options_for_select
32
+ [reader.read_array, [], [], 0.05]
33
+ end
34
+ end
35
+ end
36
+
37
+ require "invoker/reactor/reader"
@@ -0,0 +1,54 @@
1
+ module Invoker
2
+ class Reactor::Reader
3
+ attr_accessor :read_array
4
+
5
+ def initialize
6
+ @read_array = []
7
+ end
8
+
9
+ def watch_for_read(socket)
10
+ @read_array << socket
11
+ end
12
+
13
+ def handle_read_event(read_ready_fds)
14
+ ready_fds = read_ready_fds.flatten.compact
15
+ ready_fds.each { |ready_fd| process_read(ready_fd) }
16
+ end
17
+
18
+ private
19
+
20
+ def process_read(ready_fd)
21
+ command_worker = Invoker.commander.get_worker_from_fd(ready_fd)
22
+ return unless command_worker
23
+ begin
24
+ data = read_data(ready_fd)
25
+ command_worker.receive_data(data)
26
+ rescue Invoker::Errors::ProcessTerminated
27
+ remove_from_read_monitoring(command_worker.pipe_end, command_worker)
28
+ end
29
+ end
30
+
31
+ def remove_from_read_monitoring(fd, command_worker)
32
+ read_array.delete(fd)
33
+ command_worker.unbind
34
+ rescue StandardError => error
35
+ Invoker::Logger.puts(error.message)
36
+ Invoker::Logger.puts(error.backtrace)
37
+ end
38
+
39
+ def read_data(ready_fd)
40
+ sock_data = []
41
+ begin
42
+ while(t_data = ready_fd.read_nonblock(64))
43
+ sock_data << t_data
44
+ end
45
+ rescue Errno::EAGAIN
46
+ return sock_data.join
47
+ rescue Errno::EWOULDBLOCK
48
+ return sock_data.join
49
+ rescue
50
+ raise Invoker::Errors::ProcessTerminated.new(ready_fd,sock_data.join)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,47 @@
1
+ module Invoker
2
+ class Version
3
+ include Comparable
4
+ attr_reader :major, :minor, :patch
5
+
6
+ def initialize(number)
7
+ t_major, t_minor, t_patch = number.split('.')
8
+ @major = t_major.to_i
9
+ @minor = t_minor.to_i
10
+ @patch = t_patch.to_i
11
+ end
12
+
13
+ def to_a
14
+ [major, minor, patch].compact
15
+ end
16
+
17
+ def <=>(version)
18
+ (major.to_i <=> version.major.to_i).nonzero? ||
19
+ (minor.to_i <=> version.minor.to_i).nonzero? ||
20
+ patch.to_i <=> version.patch.to_i
21
+ end
22
+
23
+ def matches?(operator, number)
24
+ version = Version.new(number)
25
+ self == version
26
+
27
+ return self == version if operator == '='
28
+ return self > version if operator == '>'
29
+ return self < version if operator == '<'
30
+ return version <= self && version.next > self if operator == '~>'
31
+ end
32
+
33
+ def next
34
+ next_splits = to_a
35
+
36
+ if next_splits.length == 1
37
+ next_splits[0] += 1
38
+ else
39
+ next_splits[-2] += 1
40
+ next_splits[-1] = 0
41
+ end
42
+
43
+ Version.new(next_splits.join('.'))
44
+ end
45
+ end
46
+ VERSION = "1.5.2"
47
+ end
@@ -0,0 +1,25 @@
1
+ Invoker is a gem for managing processes in development environment.
2
+
3
+ [![Build Status](https://travis-ci.org/code-mancers/invoker.svg)](https://travis-ci.org/code-mancers/invoker)
4
+ [![Code Climate](https://codeclimate.com/github/code-mancers/invoker.svg)](https://codeclimate.com/github/code-mancers/invoker)
5
+ [![Coverage Status](https://coveralls.io/repos/code-mancers/invoker/badge.svg)](https://coveralls.io/r/code-mancers/invoker)
6
+ [![Dependency Status](https://gemnasium.com/code-mancers/invoker.svg)](https://gemnasium.com/code-mancers/invoker)
7
+
8
+ ## Usage ##
9
+
10
+ First we need to install `invoker` gem to get command line utility called `invoker`, we can do that via:
11
+
12
+ gem install invoker
13
+
14
+ Currently it only works with Ruby 1.9.3, 2.0 and 2.1.
15
+
16
+ ## Manual ##
17
+
18
+ Information about configuring and using Invoker can be found on - [Invoker Website](http://invoker.codemancers.com)
19
+
20
+ Invoker documentation is maintained via `Jekyll` and hosted on `github`. If you would like to fix an error
21
+ or update something - please submit a pull request against `gh-pages` branch of `Invoker`.
22
+
23
+ ## Bug reports and Feature requests
24
+
25
+ Please use [Github Issue Tracker](https://github.com/code-mancers/invoker/issues) for feature requests or bug reports.
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+
3
+ describe Invoker::CLI::Pinger do
4
+ let(:unix_client) { Invoker::IPC::UnixClient.new }
5
+ let(:pinger) { Invoker::CLI::Pinger.new(unix_client) }
6
+ let(:pong) { MM::Pong.new(status: 'pong') }
7
+
8
+ context "If Invoker is running" do
9
+ it "should return true" do
10
+ unix_client.expects(:send_and_receive).returns(pong)
11
+ expect(pinger.invoker_running?).to be_truthy
12
+ end
13
+ end
14
+
15
+ context "if Invoker is not running" do
16
+ it "should return false" do
17
+ unix_client.expects(:send_and_receive).returns(nil)
18
+ unix_client.expects(:abort).never
19
+ expect(pinger.invoker_running?).to be_falsey
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,39 @@
1
+ require "spec_helper"
2
+
3
+ describe Invoker::CLI::TailWatcher do
4
+ let(:tail_watcher) { Invoker::CLI::TailWatcher.new }
5
+
6
+ describe "Adding processes to watch list" do
7
+ it "should allow add" do
8
+ tail_watcher.add(["rails"], "socket")
9
+ expect(tail_watcher.tail_watchers).to_not be_empty
10
+ expect(tail_watcher["rails"]).to eql ["socket"]
11
+ end
12
+ end
13
+
14
+ describe "removing processes from watch list" do
15
+ context "when process has only one watcher" do
16
+ before do
17
+ tail_watcher.add(["rails"], "socket")
18
+ end
19
+ it "should remove and purge process watch list" do
20
+ expect(tail_watcher.tail_watchers).to_not be_empty
21
+ tail_watcher.remove("rails", "socket")
22
+ expect(tail_watcher.tail_watchers).to be_empty
23
+ end
24
+ end
25
+ context "when process multiple watchers" do
26
+ before do
27
+ tail_watcher.add(["rails"], "socket")
28
+ tail_watcher.add(["rails"], "socket2")
29
+ end
30
+
31
+ it "should remove only related socket" do
32
+ expect(tail_watcher.tail_watchers).to_not be_empty
33
+ tail_watcher.remove("rails", "socket")
34
+ expect(tail_watcher.tail_watchers).to_not be_empty
35
+ expect(tail_watcher["rails"]).to eql ["socket2"]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,27 @@
1
+ require "spec_helper"
2
+
3
+ describe Invoker::CLI do
4
+ describe "default start command" do
5
+ it "should use default if no other command specified" do
6
+ Invoker::CLI.any_instance.expects(:start).with("dummy")
7
+ Invoker::CLI.start(["dummy"])
8
+ end
9
+
10
+ it "should use proper command if it exists" do
11
+ Invoker::CLI.any_instance.expects(:list)
12
+ Invoker::CLI.start(["list"])
13
+ end
14
+
15
+ it "should list version" do
16
+ Invoker::CLI.any_instance.expects(:version)
17
+ Invoker::CLI.start(["-v"])
18
+ end
19
+ end
20
+
21
+ describe "stop command" do
22
+ it "should stop the daemon" do
23
+ Invoker.daemon.expects(:stop).once
24
+ Invoker::CLI.start(["stop"])
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,45 @@
1
+ require "spec_helper"
2
+
3
+ describe "Command Worker" do
4
+ let(:pipe_end) { StringIO.new }
5
+ let(:command_worker) { Invoker::CommandWorker.new('rails', pipe_end, 100, :red) }
6
+
7
+ describe "converting workers hash to json" do
8
+ before do
9
+ @workers = {}
10
+ @workers["foo"] = Invoker::CommandWorker.new("foo", 89, 1023, "red")
11
+ @workers["bar"] = Invoker::CommandWorker.new("bar", 99, 1024, "blue")
12
+ end
13
+
14
+ it "should print json" do
15
+ expect(@workers.values.map {|worker| worker.to_h }.to_json).not_to be_empty
16
+ end
17
+ end
18
+
19
+ describe "sending json responses" do
20
+ before do
21
+ @socket = StringIO.new
22
+ Invoker.tail_watchers = Invoker::CLI::TailWatcher.new
23
+ Invoker.tail_watchers.add(['rails'], @socket)
24
+ end
25
+
26
+ after do
27
+ Invoker.tail_watchers = nil
28
+ end
29
+
30
+ context "when there is a error encoding the message" do
31
+ it "should send nothing to the socket" do
32
+ MM::TailResponse.any_instance.expects(:encoded_message).raises(StandardError, "encoding error")
33
+ command_worker.receive_line('hello_world')
34
+ expect(@socket.string).to be_empty
35
+ end
36
+ end
37
+
38
+ context "when there is successful delivery" do
39
+ it "should return json data to client if tail watchers" do
40
+ command_worker.receive_line('hello_world')
41
+ expect(@socket.string).to match(/hello_world/)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,152 @@
1
+ require "spec_helper"
2
+
3
+ describe "Invoker::Commander" do
4
+ before(:each) do
5
+ @original_invoker_config = Invoker.config
6
+ Invoker.config = mock
7
+ end
8
+
9
+ after(:each) do
10
+ Invoker.config = @original_invoker_config
11
+ end
12
+
13
+ describe "With no processes configured" do
14
+ before(:each) do
15
+ @commander = Invoker::Commander.new
16
+ end
17
+
18
+ it "should throw error" do
19
+ Invoker.config.stubs(:processes).returns([])
20
+
21
+ expect {
22
+ @commander.start_manager
23
+ }.to raise_error(Invoker::Errors::InvalidConfig)
24
+ end
25
+ end
26
+
27
+ describe "#start_process" do
28
+ describe "when not daemonized" do
29
+ before do
30
+ processes = [OpenStruct.new(:label => "foobar", :cmd => "foobar_command", :dir => ENV['HOME'], :sleep_duration => 2)]
31
+ Invoker.config.stubs(:processes).returns(processes)
32
+ Invoker.config.stubs(:autorunnable_processes).returns(processes)
33
+ Invoker.stubs(:can_run_balancer?).returns(false)
34
+ @commander = Invoker::Commander.new
35
+ Invoker.commander = @commander
36
+ end
37
+
38
+ after do
39
+ Invoker.commander = nil
40
+ end
41
+
42
+ it "should populate workers and open_pipes" do
43
+ @commander.expects(:start_event_loop)
44
+ @commander.process_manager.expects(:load_env).returns({})
45
+ @commander.process_manager.expects(:spawn).returns(100)
46
+ @commander.process_manager.expects(:wait_on_pid)
47
+ @commander.expects(:at_exit)
48
+ @commander.start_manager
49
+ expect(@commander.process_manager.open_pipes).not_to be_empty
50
+ expect(@commander.process_manager.workers).not_to be_empty
51
+
52
+ worker = @commander.process_manager.workers['foobar']
53
+
54
+ expect(worker).not_to be_nil
55
+ expect(worker.command_label).to eq('foobar')
56
+
57
+ pipe_end_worker = @commander.process_manager.open_pipes[worker.pipe_end.fileno]
58
+ expect(pipe_end_worker).not_to be_nil
59
+ end
60
+ end
61
+
62
+ describe "when daemonized" do
63
+ before do
64
+ processes = [OpenStruct.new(:label => "foobar", :cmd => "foobar_command", :dir => ENV['HOME'], :sleep_duration => 2)]
65
+ Invoker.config.stubs(:processes).returns(processes)
66
+ Invoker.config.stubs(:autorunnable_processes).returns(processes)
67
+ Invoker.stubs(:can_run_balancer?).returns(false)
68
+ @commander = Invoker::Commander.new
69
+ Invoker.commander = @commander
70
+ Invoker.daemonize = true
71
+ end
72
+
73
+ after do
74
+ Invoker.commander = nil
75
+ Invoker.daemonize = false
76
+ end
77
+
78
+ it "should daemonize the process and populate workers and open_pipes" do
79
+ @commander.expects(:start_event_loop)
80
+ @commander.process_manager.expects(:load_env).returns({})
81
+ Invoker.daemon.expects(:start).once
82
+ @commander.process_manager.expects(:spawn).returns(100)
83
+ @commander.process_manager.expects(:wait_on_pid)
84
+ @commander.expects(:at_exit)
85
+ @commander.start_manager
86
+
87
+ expect(@commander.process_manager.open_pipes).not_to be_empty
88
+ expect(@commander.process_manager.workers).not_to be_empty
89
+
90
+ worker = @commander.process_manager.workers['foobar']
91
+
92
+ expect(worker).not_to be_nil
93
+ expect(worker.command_label).to eq('foobar')
94
+
95
+ pipe_end_worker = @commander.process_manager.open_pipes[worker.pipe_end.fileno]
96
+ expect(pipe_end_worker).not_to be_nil
97
+ end
98
+ end
99
+ end
100
+
101
+ describe 'disable_autorun option' do
102
+ context 'autorun is disabled for a process' do
103
+ before do
104
+ @processes = [
105
+ OpenStruct.new(:label => "foobar", :cmd => "foobar_command", :dir => ENV['HOME'], :sleep_duration => 2),
106
+ OpenStruct.new(:label => "panda", :cmd => "panda_command", :dir => ENV['HOME'], :disable_autorun => true, :sleep_duration => 2)
107
+ ]
108
+ Invoker.config.stubs(:processes).returns(@processes)
109
+ Invoker.config.stubs(:autorunnable_processes).returns([@processes.first])
110
+
111
+ @commander = Invoker::Commander.new
112
+ end
113
+
114
+ it "doesn't run process" do
115
+ @commander.expects(:install_interrupt_handler)
116
+ @commander.process_manager.expects(:run_power_server)
117
+ @commander.expects(:at_exit)
118
+ @commander.expects(:start_event_loop)
119
+
120
+ @commander.process_manager.expects(:start_process).with(@processes[0])
121
+ @commander.process_manager.expects(:start_process).with(@processes[1]).never
122
+ @commander.start_manager
123
+ end
124
+ end
125
+ end
126
+
127
+ describe "#runnables" do
128
+ before do
129
+ @commander = Invoker::Commander.new
130
+ end
131
+
132
+ it "should run runnables in reactor tick with one argument" do
133
+ @commander.on_next_tick("foo") { |cmd| start_process_by_name(cmd) }
134
+ @commander.expects(:start_process_by_name).returns(true)
135
+ @commander.run_runnables()
136
+ end
137
+
138
+ it "should run runnables with multiple args" do
139
+ @commander.on_next_tick("foo", "bar", "baz") { |t1,*rest|
140
+ stop_process(t1, rest)
141
+ }
142
+ @commander.expects(:stop_process).with("foo", ["bar", "baz"]).returns(true)
143
+ @commander.run_runnables()
144
+ end
145
+
146
+ it "should run runnable with no args" do
147
+ @commander.on_next_tick() { hello() }
148
+ @commander.expects(:hello).returns(true)
149
+ @commander.run_runnables()
150
+ end
151
+ end
152
+ end