Skip to content

Commit

Permalink
Additional actable tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
chadrem committed Jul 20, 2015
1 parent c59c877 commit 67643ce
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 40 deletions.
48 changes: 24 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ You should use the root actor to spawn all of your application specific actors.
# Your code goes here.
end

Tribe.root.spawn(MyActor)
Tribe.root.spawn!(MyActor)

#### Command Handlers

Expand Down Expand Up @@ -107,7 +107,7 @@ Messages can include data that you want to pass between actors. It is best prac

# Create some named actors that are children of the root actor.
100.times do |i|
Tribe.root.spawn(MyActor, :name => "my_actor_#{i}")
Tribe.root.spawn!(MyActor, :name => "my_actor_#{i}")
end

# Send an event to each actor.
Expand All @@ -128,7 +128,7 @@ Registries hold references to named actors so that you can easily find them.
You don't have to create your own since there is a global one called ````Tribe.registry````.
The Root actor is named 'root' and stored in the default registry.

actor = Tribe.root.spawn(Tribe::Actor, :name => 'some_actor')
actor = Tribe.root.spawn!(Tribe::Actor, :name => 'some_actor')

if actor == Tribe.registry['some_actor']
puts 'Successfully found some_actor in the registry.'
Expand Down Expand Up @@ -177,8 +177,8 @@ No waiting for a result is involved and the actor will continue to process other
end
end

actor_a = Tribe.root.spawn(ActorA, :name => 'actor_a')
actor_b = Tribe.root.spawn(ActorB, :name => 'actor_b')
actor_a = Tribe.root.spawn!(ActorA, :name => 'actor_a')
actor_b = Tribe.root.spawn!(ActorB, :name => 'actor_b')

actor_a.direct_message!(:start)

Expand Down Expand Up @@ -220,8 +220,8 @@ The actor won't process any other events until the future has a result.
end
end

actor_a = Tribe.root.spawn(ActorA, :name => 'actor_a')
actor_b = Tribe.root.spawn(ActorB, :name => 'actor_b')
actor_a = Tribe.root.spawn!(ActorA, :name => 'actor_a')
actor_b = Tribe.root.spawn!(ActorB, :name => 'actor_b')

actor_a.direct_message!(:start)

Expand Down Expand Up @@ -260,8 +260,8 @@ When a timeout occurs, the result of the future will be a ````Tribe::FutureTimeo
end
end

actor_a = Tribe.root.spawn(ActorA, :name => 'actor_a')
actor_b = Tribe.root.spawn(ActorB, :name => 'actor_b')
actor_a = Tribe.root.spawn!(ActorA, :name => 'actor_a')
actor_b = Tribe.root.spawn!(ActorB, :name => 'actor_b')

actor_a.direct_message!(:start)

Expand All @@ -287,7 +287,7 @@ This lets you build routers that delegate work to other actors.
class MyRouter < Tribe::Actor
private
def on_initialize(event)
@processors = 100.times.map { spawn(MyProcessor) }
@processors = 100.times.map { spawn!(MyProcessor) }
end

def on_process(event)
Expand All @@ -304,7 +304,7 @@ This lets you build routers that delegate work to other actors.
end

# Create the router.
router = Tribe.root.spawn(MyRouter, :name => 'router')
router = Tribe.root.spawn!(MyRouter, :name => 'router')

# Send an event to the router and it will forward it to a random processor.
100.times do |i|
Expand All @@ -323,8 +323,8 @@ Both one-shot and periodic timers are provided.
class MyActor < Tribe::Actor
private
def on_initialize(event)
timer(1, :timer, 'hello once')
periodic_timer(1, :periodic_timer, 'hello many times')
timer!(1, :timer, 'hello once')
periodic_timer!(1, :periodic_timer, 'hello many times')
end

def on_timer(event)
Expand All @@ -338,7 +338,7 @@ Both one-shot and periodic timers are provided.

# Create some named actors.
10.times do |i|
Tribe.root.spawn(MyActor, :name => "my_actor_#{i}")
Tribe.root.spawn!(MyActor, :name => "my_actor_#{i}")
end

# Sleep in order to observe the timers.
Expand All @@ -354,7 +354,7 @@ Both one-shot and periodic timers are provided.

Linking allows actors to group together so that they all live or die together.
Such linking is useful for breaking up complex problems into multiple smaller units.
To create a linked actor you use the ````spawn```` method.
To create a linked actor you use the ````spawn!```` method.
By default, if a linked actor dies, it will cause its parent and children to die too.
You an override this behavior by using supervisors.

Expand All @@ -365,7 +365,7 @@ You an override this behavior by using supervisors.
5.times do |i|
name = "level2_#{i}"
puts name
actor = spawn(Level2, :name => name)
actor = spawn!(Level2, :name => name)
message!(actor, :spawn, i)
end
end
Expand All @@ -377,7 +377,7 @@ You an override this behavior by using supervisors.
def on_spawn(event)
5.times do |i|
name = "level3_#{event.data}_#{i}"
actor = spawn(Level3, :name => name)
actor = spawn!(Level3, :name => name)
message!(actor, :spawn)
end
end
Expand All @@ -392,7 +392,7 @@ You an override this behavior by using supervisors.
end

# Create the top-level actor.
top = Tribe.root.spawn(Level1, :name => 'level1')
top = Tribe.root.spawn!(Level1, :name => 'level1')

# Tell the root actor to create the tree of children.
top.direct_message!(:spawn)
Expand All @@ -402,15 +402,15 @@ You an override this behavior by using supervisors.
A failure in a linked actor will cause all associated actors (parent and children) to die.
Supervisors can be used to block the failure from propogating.
You then have the option to re-spawn the failed actor.
They are created by passing ````{:supervise => true}```` as a third argument to ````spawn````.
They are created by passing ````{:supervise => true}```` as a third argument to ````spawn!````.
You can then detect dead children by overriding ````on_child_died````.

# Create the Parent actor class.
class Parent < Tribe::Actor
private
def on_child_died(event)
puts "My child died. Creating a new child."
$second_child = spawn(Child, {:name => 'Child'}, {:supervise => true})
$second_child = spawn!(Child, {:name => 'Child'}, {:supervise => true})
end
end

Expand All @@ -420,10 +420,10 @@ You can then detect dead children by overriding ````on_child_died````.
end

# Create the parent actor.
$parent = Tribe.root.spawn(Parent, {:name => 'Parent'})
$parent = Tribe.root.spawn!(Parent, {:name => 'Parent'})

# Create the first child actor.
$first_child = $parent.spawn(Child, {:name => 'Child'}, {:supervise => true})
$first_child = $parent.spawn!(Child, {:name => 'Child'}, {:supervise => true})

# Force the first child to die by executing an exception.
$first_child.perform! { raise 'good bye' }
Expand Down Expand Up @@ -456,7 +456,7 @@ This is easily accomplished with the ````on_exception```` handler in a base clas
class CustomActor < MyBaseActor
end

actor = Tribe.root.spawn(CustomActor)
actor = Tribe.root.spawn!(CustomActor)
actor.perform! { raise 'goodbye' }

Note that you should be careful to make sure ````on_exception```` never raises an exception itself.
Expand Down Expand Up @@ -497,7 +497,7 @@ To support in the tens of thousands, hundreds of thousands, or potentially milli

# Spawn some actors that go to sleep for a bit.
100.times do
actor = Tribe.root.spawn(MyActor)
actor = Tribe.root.spawn!(MyActor)
actor.direct_message!(:start)
end

Expand Down
6 changes: 3 additions & 3 deletions lib/tribe/actable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def perform!(&block)
return direct_message!(:__perform__, block)
end

def spawn(klass, actor_options = {}, spawn_options = {})
def spawn!(klass, actor_options = {}, spawn_options = {})
actor_options[:parent] = self

child = nil
Expand Down Expand Up @@ -306,7 +306,7 @@ def parent_died_handler(parent, exception)

private

def timer(delay, command, data = nil)
def timer!(delay, command, data = nil)
# Lazy instantiation for performance.
@_actable.scheduler ||= Workers.scheduler
@_actable.timers ||= Tribe::SafeSet.new
Expand All @@ -321,7 +321,7 @@ def timer(delay, command, data = nil)
return timer
end

def periodic_timer(delay, command, data = nil)
def periodic_timer!(delay, command, data = nil)
# Lazy instantiation for performance.
@_actable.scheduler ||= Workers.scheduler
@_actable.timers ||= Tribe::SafeSet.new
Expand Down
14 changes: 7 additions & 7 deletions test/actable_future_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ class FutureTestParentActor < TestActor
private

def on_start_blocking(event)
@child = spawn(FutureTestChildActor)
@child = spawn!(FutureTestChildActor)
@future = future!(@child, :compute, event.data)

wait(@future)
end

def on_start_non_blocking(event)
@child = spawn(FutureTestChildActor)
@child = spawn!(FutureTestChildActor)
@future = future!(@child, :compute, event.data)

@future.success do |result|
Expand All @@ -28,10 +28,10 @@ def on_start_non_blocking(event)
end

def on_start_non_blocking_delayed(event)
@child = spawn(FutureTestChildActor, {}, :supervise => true)
@child = spawn!(FutureTestChildActor, {}, :supervise => true)
@future = future!(@child, :compute, event.data)

sleep(0.5)
sleep(0.2)

@future.success do |result|
@result = result
Expand All @@ -43,15 +43,15 @@ def on_start_non_blocking_delayed(event)
end

def on_start_blocking_timeout(event)
@child = spawn(FutureTestChildActor)
@child = spawn!(FutureTestChildActor)
@future = future!(@child, :sleep)
@future.timeout = 0.1

wait(@future)
end

def on_start_non_blocking_timeout(event)
@child = spawn(FutureTestChildActor)
@child = spawn!(FutureTestChildActor)
@future = future!(@child, :sleep)
@future.timeout = 0.1

Expand All @@ -75,7 +75,7 @@ def on_compute(event)
end

def on_sleep(event)
sleep(0.5)
sleep(0.2)
@success = true
rescue Exception => e
puts e.inspect
Expand Down
8 changes: 4 additions & 4 deletions test/actable_spawn_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ def on_parent_died(event)
end
end

class ActableSpawnTest < Minitest::Test
class ActablesSpawnTest < Minitest::Test
def test_spawn
parent = SpawnTestParentActor.new
parent.run
child = parent.spawn(SpawnTestChildActor)
child = parent.spawn!(SpawnTestChildActor)

assert_kind_of(SpawnTestChildActor, child)
ensure
Expand All @@ -41,7 +41,7 @@ def test_spawn
def test_child_death_kills_parent
parent = SpawnTestParentActor.new
parent.run
child = parent.spawn(SpawnTestChildActor)
child = parent.spawn!(SpawnTestChildActor)
child.perform! { raise 'uh oh' }

poll { !parent.alive? }
Expand All @@ -58,7 +58,7 @@ def test_child_death_kills_parent
def test_parent_death_kills_child
parent = SpawnTestParentActor.new
parent.run
child = parent.spawn(SpawnTestChildActor)
child = parent.spawn!(SpawnTestChildActor)
parent.perform! { raise 'uh oh' }

poll { !child.alive? }
Expand Down
60 changes: 60 additions & 0 deletions test/actable_supervisor_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
require 'test_helper'

class SupervisorTestParentActor < TestActor
attr_reader :child
attr_reader :result

private

def on_start_supervised(event)
@child = spawn!(SupervisorTestChildActor, {}, :supervise => true)
end

def on_start_unsupervised(event)
@child = spawn!(SupervisorTestChildActor, {}, :supervise => false)
end

def on_child_died(event)
@result = event.data
end
end

class SupervisorTestChildActor < Tribe::Actor
private
end

class ActableSupervisorTest < Minitest::Test
def test_child_death_does_not_kill_parent_when_supervised
parent = setup_parent(:start_supervised)

assert(parent.child, parent.result[:child])
assert_kind_of(RuntimeError, parent.result[:exception])
assert(parent.alive?)
end

def test_child_death_does_kill_parent_when_unsupervised
parent = setup_parent(:start_unsupervised)

assert(parent.child, parent.result[:child])
assert_kind_of(RuntimeError, parent.result[:exception])
assert(parent.dead?)
end

private

def setup_parent(command)
parent = SupervisorTestParentActor.new
parent.run
parent.direct_message!(command)

poll { parent.child }

parent.child.perform! { raise 'uh oh' }

poll { parent.child.dead? }

sleep(0.1)

parent
end
end
Loading

0 comments on commit 67643ce

Please sign in to comment.