diff options
author | José Valim <jose.valim@plataformatec.com.br> | 2013-12-31 18:35:14 +0100 |
---|---|---|
committer | José Valim <jose.valim@plataformatec.com.br> | 2013-12-31 18:35:22 +0100 |
commit | 3a0b4988fc74e72f98e61457233da24b1378ba63 (patch) | |
tree | a74886be7c8f4b3abf60c4f453183b787263e228 | |
parent | b7bbe13c913ec6f45a31f63c94858d754b2608c9 (diff) | |
download | elixir-3a0b4988fc74e72f98e61457233da24b1378ba63.tar.gz |
Shut down GenFSM on unknown messages
-rw-r--r-- | lib/elixir/lib/gen_event/behaviour.ex | 1 | ||||
-rw-r--r-- | lib/elixir/lib/gen_fsm/behaviour.ex | 119 | ||||
-rw-r--r-- | lib/elixir/lib/gen_server/behaviour.ex | 2 | ||||
-rw-r--r-- | lib/elixir/test/elixir/gen_event/behaviour_test.exs | 4 | ||||
-rw-r--r-- | lib/elixir/test/elixir/gen_fsm/behaviour_test.exs | 43 | ||||
-rw-r--r-- | lib/elixir/test/elixir/gen_server/behaviour_test.exs | 32 |
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 |