summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@plataformatec.com.br>2013-12-31 18:35:14 +0100
committerJosé Valim <jose.valim@plataformatec.com.br>2013-12-31 18:35:22 +0100
commit3a0b4988fc74e72f98e61457233da24b1378ba63 (patch)
treea74886be7c8f4b3abf60c4f453183b787263e228
parentb7bbe13c913ec6f45a31f63c94858d754b2608c9 (diff)
downloadelixir-3a0b4988fc74e72f98e61457233da24b1378ba63.tar.gz
Shut down GenFSM on unknown messages
-rw-r--r--lib/elixir/lib/gen_event/behaviour.ex1
-rw-r--r--lib/elixir/lib/gen_fsm/behaviour.ex119
-rw-r--r--lib/elixir/lib/gen_server/behaviour.ex2
-rw-r--r--lib/elixir/test/elixir/gen_event/behaviour_test.exs4
-rw-r--r--lib/elixir/test/elixir/gen_fsm/behaviour_test.exs43
-rw-r--r--lib/elixir/test/elixir/gen_server/behaviour_test.exs32
6 files changed, 123 insertions, 78 deletions
diff --git a/lib/elixir/lib/gen_event/behaviour.ex b/lib/elixir/lib/gen_event/behaviour.ex
index 48917d7a2..d01181ca4 100644
--- a/lib/elixir/lib/gen_event/behaviour.ex
+++ b/lib/elixir/lib/gen_event/behaviour.ex
@@ -25,7 +25,6 @@ defmodule GenEvent.Behaviour do
def handle_call(:notifications, notifications) do
{:ok, Enum.reverse(notifications), []}
end
-
end
{ :ok, pid } = :gen_event.start_link
diff --git a/lib/elixir/lib/gen_fsm/behaviour.ex b/lib/elixir/lib/gen_fsm/behaviour.ex
index e428924a5..cb1e5eb37 100644
--- a/lib/elixir/lib/gen_fsm/behaviour.ex
+++ b/lib/elixir/lib/gen_fsm/behaviour.ex
@@ -21,78 +21,67 @@ defmodule GenFSM.Behaviour do
works - we hereby declare a full disclaimer for any lawsuits that the
behaviour of the CVM in its original state might encur.
-
defmodule MyFsm do
use GenFSM.Behaviour
- # keeping track of what is going on inside the CVM.
- # 3 is the target price for a cup of coffee
- defrecord StateData, coins: 0, price: 3
-
- #
- # API functions
- #
-
- def start_link() do
- :gen_fsm.start_link({:local, :cvm}, __MODULE__, [], [])
- end
-
- def insert_coin() do
- :gen_fsm.send_event(:cvm, :coin)
- end
-
- def request_coffee() do
- :gen_fsm.send_event(:cvm, :request_coffee)
- end
+ # keeping track of what is going on inside the CVM.
+ # 3 is the target price for a cup of coffee
+ defrecord StateData, coins: 0, price: 3
- #
- # Callbacks
- #
+ # API functions
- def init(_args) do
- { :ok, :short_paid, StateData.new }
- end
+ def start_link() do
+ :gen_fsm.start_link({:local, :cvm}, __MODULE__, [], [])
+ end
+ def insert_coin() do
+ :gen_fsm.send_event(:cvm, :coin)
+ end
- def short_paid(:coin, state_data = StateData[coins: c, price: p])
- when c + 1 < p do
- { :next_state, :short_paid, state_data.coins(c + 1) }
- end
+ def request_coffee() do
+ :gen_fsm.send_event(:cvm, :request_coffee)
+ end
- def short_paid(:coin, state_data) do
- { :next_state, :paid_in_full, &state_data.update_coins(&1 + 1) }
- end
+ # Callbacks
- def short_paid(:request_coffee, state_data) do
- { :next_state, :requested_short_paid, state_data }
- end
+ def init(_args) do
+ { :ok, :short_paid, StateData.new }
+ end
+ def short_paid(:coin, state_data = StateData[coins: c, price: p]) when c + 1 < p do
+ { :next_state, :short_paid, state_data.coins(c + 1) }
+ end
- def requested_short_paid(:request_coffee, state_data) do
- {:next_state, :requested_short_paid, state_data }
- end
+ def short_paid(:coin, state_data) do
+ { :next_state, :paid_in_full, &state_data.update_coins(&1 + 1) }
+ end
- def requested_short_paid(:coin, state_data=StateData[coins: c, price: p])
- when c+1 < p do
- { :next_state, :requested_short_paid, state_data.coins(c + 1) }
- end
+ def short_paid(:request_coffee, state_data) do
+ { :next_state, :requested_short_paid, state_data }
+ end
- def requested_short_paid(:coin, _state_data) do
- IO.puts "Here's your coffee!"
- { :next_state, :short_paid, StateData.new }
- end
+ def requested_short_paid(:request_coffee, state_data) do
+ { :next_state, :requested_short_paid, state_data }
+ end
+ def requested_short_paid(:coin, state_data=StateData[coins: c, price: p]) when c+1 < p do
+ { :next_state, :requested_short_paid, state_data.coins(c + 1) }
+ end
- def paid_in_full(:coin, state_data) do
- { :next_state, :paid_in_full, &state_data.update_coins(&1 + 1) }
- end
+ def requested_short_paid(:coin, _state_data) do
+ IO.puts "Here's your coffee!"
+ { :next_state, :short_paid, StateData.new }
+ end
- def paid_in_full(:request_coffee, _state_data) do
- IO.puts "Here's your coffee!"
- { :next_state, :short_paid, StateData.new }
- end
+ def paid_in_full(:coin, state_data) do
+ { :next_state, :paid_in_full, &state_data.update_coins(&1 + 1) }
end
+ def paid_in_full(:request_coffee, _state_data) do
+ IO.puts "Here's your coffee!"
+ { :next_state, :short_paid, StateData.new }
+ end
+ end
{ :ok, _pid } = MyFsm.start_link()
@@ -138,15 +127,15 @@ defmodule GenFSM.Behaviour do
for you. The list of callbacks are:
* `init(args)` - invoked when the FSM is started;
- * `handle_sync_event(event, from, state_name, state_data)` - invoked to
+ * `handle_sync_event(event, from, state_name, state_data)` - invoked to
handle `sync_send_all_state_event` messages;
- * `handle_event(event, state_name, state_data)` - invoked to handle
+ * `handle_event(event, state_name, state_data)` - invoked to handle
`send_all_state_event` messages;
- * `handle_info(msg, state_name, state_data)` - handle all other
+ * `handle_info(msg, state_name, state_data)` - handle all other
messages which are normally received by processes;
- * `terminate(reason, state_name, state_data)` - called when the FSM
+ * `terminate(reason, state_name, state_data)` - called when the FSM
is about to terminate, useful for cleaning up;
- * `code_change(old_vsn, state, extra)` - called when the application
+ * `code_change(old_vsn, state, extra)` - called when the application
code is being upgraded live (hot code swap);
Unlike `GenServer` and `GenEvent`, the callback `init/1` is not
@@ -154,9 +143,9 @@ defmodule GenFSM.Behaviour do
For each state you need to define either or both of these:
- * `state_name(event, state_data)` - invoked to handle
+ * `state_name(event, state_data)` - invoked to handle
`send_event` messages;
- * `state_name(event, from, state_data)`- invoked to handle
+ * `state_name(event, from, state_data)`- invoked to handle
`sync_send_event` messages;
If you send asynchronous events you only need to implement the
@@ -180,13 +169,13 @@ defmodule GenFSM.Behaviour do
@behavior :gen_fsm
@doc false
- def handle_event(_event, state_name, state_data) do
- { :next_state, state_name, state_data }
+ def handle_event(event, state_name, state_data) do
+ { :stop, {:bad_event, state_name, event}, state_data }
end
@doc false
- def handle_sync_event(_event, from, state_name, state_data) do
- { :reply, :ok, state_name, state_data }
+ def handle_sync_event(event, from, state_name, state_data) do
+ { :stop, {:bad_sync_event, state_name, event}, state_data }
end
@doc false
diff --git a/lib/elixir/lib/gen_server/behaviour.ex b/lib/elixir/lib/gen_server/behaviour.ex
index 2818460e6..fd40babd8 100644
--- a/lib/elixir/lib/gen_server/behaviour.ex
+++ b/lib/elixir/lib/gen_server/behaviour.ex
@@ -105,7 +105,7 @@ defmodule GenServer.Behaviour do
@doc false
def handle_cast(msg, state) do
- { :stop, { :bad_call, msg }, state }
+ { :stop, { :bad_cast, msg }, state }
end
@doc false
diff --git a/lib/elixir/test/elixir/gen_event/behaviour_test.exs b/lib/elixir/test/elixir/gen_event/behaviour_test.exs
index 64e49f8a5..e744bd3ca 100644
--- a/lib/elixir/test/elixir/gen_event/behaviour_test.exs
+++ b/lib/elixir/test/elixir/gen_event/behaviour_test.exs
@@ -19,10 +19,9 @@ defmodule GenEvent.BehaviourTest do
def handle_call(:notifications, notifications) do
{:ok, Enum.reverse(notifications), []}
end
-
end
- test :using do
+ test "using defines callbacks" do
{ :ok, pid } = :gen_event.start_link
:gen_event.add_handler(pid, MyEventHandler, [])
@@ -32,5 +31,4 @@ defmodule GenEvent.BehaviourTest do
assert :gen_event.call(pid, MyEventHandler, :notifications) == [1, 2]
assert :gen_event.call(pid, MyEventHandler, :notifications) == []
end
-
end
diff --git a/lib/elixir/test/elixir/gen_fsm/behaviour_test.exs b/lib/elixir/test/elixir/gen_fsm/behaviour_test.exs
new file mode 100644
index 000000000..ec39e1986
--- /dev/null
+++ b/lib/elixir/test/elixir/gen_fsm/behaviour_test.exs
@@ -0,0 +1,43 @@
+Code.require_file "../test_helper.exs", __DIR__
+
+defmodule GenFSM.BehaviourTest do
+ use ExUnit.Case
+
+ setup_all do
+ :error_logger.tty(false)
+ :ok
+ end
+
+ teardown_all do
+ :error_logger.tty(true)
+ :ok
+ end
+
+ defmodule Sample do
+ use GenFSM.Behaviour
+
+ def init(args) do
+ { :ok, :sample, args }
+ end
+ end
+
+ test "sync event stops server on unknown requests" do
+ Process.flag(:trap_exit, true)
+ assert { :ok, pid } = :gen_fsm.start_link(Sample, [:hello], [])
+
+ catch_exit(:gen_fsm.sync_send_all_state_event(pid, :unknown_request))
+ assert_receive { :EXIT, ^pid, {:bad_sync_event, :sample, :unknown_request} }
+ after
+ Process.flag(:trap_exit, false)
+ end
+
+ test "event stops server on unknown requests" do
+ Process.flag(:trap_exit, true)
+ assert { :ok, pid } = :gen_fsm.start_link(Sample, [:hello], [])
+
+ :gen_fsm.send_all_state_event(pid, :unknown_request)
+ assert_receive { :EXIT, ^pid, {:bad_event, :sample, :unknown_request} }
+ after
+ Process.flag(:trap_exit, false)
+ end
+end
diff --git a/lib/elixir/test/elixir/gen_server/behaviour_test.exs b/lib/elixir/test/elixir/gen_server/behaviour_test.exs
index 744eb6378..76ff7f44a 100644
--- a/lib/elixir/test/elixir/gen_server/behaviour_test.exs
+++ b/lib/elixir/test/elixir/gen_server/behaviour_test.exs
@@ -1,7 +1,17 @@
Code.require_file "../test_helper.exs", __DIR__
defmodule GenServer.BehaviourTest do
- use ExUnit.Case, async: true
+ use ExUnit.Case
+
+ setup_all do
+ :error_logger.tty(false)
+ :ok
+ end
+
+ teardown_all do
+ :error_logger.tty(true)
+ :ok
+ end
defmodule Sample do
use GenServer.Behaviour
@@ -29,7 +39,7 @@ defmodule GenServer.BehaviourTest do
end
end
- test :using do
+ test "using defines callbacks" do
assert { :ok, pid } = :gen_server.start_link(Sample, [:hello], [])
assert :gen_server.call(pid, :pop) == :hello
assert :gen_server.cast(pid, { :push, :world }) == :ok
@@ -37,16 +47,22 @@ defmodule GenServer.BehaviourTest do
end
test "call stops server on unknown requests" do
+ Process.flag(:trap_exit, true)
assert { :ok, pid } = :gen_server.start_link(Sample, [:hello], [])
- Process.unlink(pid)
- assert {{:bad_call, :unknown_request}, _} = catch_exit(:gen_server.call(pid, :unknown_request))
+
+ catch_exit(:gen_server.call(pid, :unknown_request))
+ assert_receive { :EXIT, ^pid, {:bad_call, :unknown_request} }
+ after
+ Process.flag(:trap_exit, false)
end
test "cast stops server on unknown requests" do
+ Process.flag(:trap_exit, true)
assert { :ok, pid } = :gen_server.start_link(Sample, [:hello], [])
- Process.unlink(pid)
- # Won't notice the server is stopped till we next send it a (valid) message
- assert :gen_server.cast(pid, :unknown_request) == :ok
- assert {{:bad_call, :unknown_request}, _} = catch_exit(:gen_server.call(pid, :pop))
+
+ :gen_server.cast(pid, :unknown_request)
+ assert_receive { :EXIT, ^pid, {:bad_cast, :unknown_request} }
+ after
+ Process.flag(:trap_exit, false)
end
end