Skip to content

Commit

Permalink
[#98] Test handle_event callback
Browse files Browse the repository at this point in the history
  • Loading branch information
jfacorro committed Dec 2, 2015
1 parent 18f45de commit 810c477
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 97 deletions.
23 changes: 9 additions & 14 deletions src/shotgun.erl
Original file line number Diff line number Diff line change
Expand Up @@ -579,26 +579,27 @@ receive_chunk({gun_error, _Pid, _StreamRef, _Reason}, State) ->

%% @private
-spec clean_state() -> map().
clean_state() -> clean_state(queue:new()).
clean_state() ->
clean_state(#{}).

%% @private
-spec clean_state(map()) -> map(); (queue:queue()) -> map().
clean_state(State) when is_map(State) ->
clean_state(get_pending_reqs(State));
clean_state(Reqs) ->
clean_state(State) ->
Responses = maps:get(responses, State, queue:new()),
Requests = maps:get(pending_requests, State, queue:new()),
#{
pid => undefined,
stream => undefined,
handle_event => undefined,
from => undefined,
responses => queue:new(),
responses => Responses,
data => <<"">>,
status_code => undefined,
headers => undefined,
async => false,
async_mode => binary,
buffer => <<"">>,
pending_requests => Reqs
pending_requests => Requests
}.

%% @private
Expand Down Expand Up @@ -762,14 +763,8 @@ get_work(State) ->
%% @private
-spec append_work(work(), state()) -> state().
append_work(Work, State) ->
PendingReqs = get_pending_reqs(State),
NewPending = queue:in(Work, PendingReqs),
maps:put(pending_requests, NewPending, State).

%% @private
-spec get_pending_reqs(state()) -> queue:queue().
get_pending_reqs(State) ->
maps:get(pending_requests, State).
#{pending_requests := PendingReqs} = State,
State#{pending_requests := queue:in(Work, PendingReqs)}.

%% @private
-spec unexpected_event_warning(atom(), any()) -> ok.
Expand Down
17 changes: 9 additions & 8 deletions test/http_server/http_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,15 @@ stop(_State) ->
start_phase(start_cowboy_http, _StartType, []) ->
Port = application:get_env(http_server, http_port, 8888),
ListenerCount = application:get_env(http_server, http_listener_count, 10),
Routes = [{ '_'
, [ {"/", http_simple_handler, []}
, {"/chunked-sse", lasse_handler, [http_sse_handler]}
, {"/chunked-binary", http_binary_handler, []}
, {"/event-stream-sse", http_sse_event_stream_handler, []}
]
}
],
Routes =
[{ '_'
, [ {"/", http_simple_handler, []}
, {"/chunked-sse[/:count]", lasse_handler, [http_sse_handler]}
, {"/chunked-binary", http_binary_handler, []}
, {"/event-stream-sse", http_sse_event_stream_handler, []}
]
}
],
Dispatch = cowboy_router:compile(Routes),
RanchOptions = [{port, Port}],
CowboyOptions =
Expand Down
37 changes: 18 additions & 19 deletions test/http_server/http_sse_handler.erl
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,24 @@

init(_InitArgs, _LastEventId, Req) ->
shotgun_test_utils:auto_send(ping),
{ok, Req, 1}.

handle_notify(ping, Count) ->
{nosend, Count}.

handle_info(ping, Count) ->
case Count > 2 of
true ->
{stop, Count};
false ->
shotgun_test_utils:auto_send(ping),

Event = #{ id => integer_to_binary(Count)
, event => <<"ping-pong">>
, data => <<"pong">>
, comment => <<"This is a comment">>
},
{send, Event, Count + 1}
end.
{CountBin, Req1} = cowboy_req:binding(count, Req, <<"2">>),
Count = binary_to_integer(CountBin),
{ok, Req1, {1, Count + 1}}.

handle_notify(ping, State) ->
{nosend, State}.

handle_info(ping, {X, Count} = State) when X >= Count ->
{stop, State};
handle_info(ping, {X, Count}) ->
shotgun_test_utils:auto_send(ping),

Event = #{ id => integer_to_binary(X)
, event => <<"ping-pong">>
, data => <<"pong">>
, comment => <<"This is a comment">>
},
{send, Event, {X + 1, Count}}.

handle_error(_Msg, _Reason, Count) -> Count.

Expand Down
154 changes: 99 additions & 55 deletions test/shotgun_async_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
-export([ get_sse/1
, get_binary/1
, work_queue/1
, get_handle_event/1
]).

-include_lib("common_test/include/ct.hrl").
Expand All @@ -22,89 +23,132 @@
all() -> shotgun_test_utils:all(?MODULE).

-spec init_per_suite(shotgun_test_utils:config()) ->
shotgun_test_utils:config().
shotgun_test_utils:config().
init_per_suite(Config) ->
{ok, _} = shotgun:start(),
{ok, _} = http_server:start(),
Config.
{ok, _} = shotgun:start(),
{ok, _} = http_server:start(),
Config.

-spec end_per_suite(shotgun_test_utils:config()) -> shotgun_test_utils:config().
-spec end_per_suite(shotgun_test_utils:config()) ->
shotgun_test_utils:config().
end_per_suite(Config) ->
ok = shotgun:stop(),
ok = http_server:stop(),
Config.
ok = shotgun:stop(),
ok = http_server:stop(),
Config.

-spec init_per_testcase(atom(), shotgun_test_utils:config()) ->
shotgun_test_utils:config().
shotgun_test_utils:config().
init_per_testcase(_, Config) ->
Port = application:get_env(http_server, http_port, 8888),
{ok, Conn} = shotgun:open("localhost", Port),
[{conn, Conn} | Config].
Port = application:get_env(http_server, http_port, 8888),
{ok, Conn} = shotgun:open("localhost", Port),
[{conn, Conn} | Config].

-spec end_per_testcase(atom(), shotgun_test_utils:config()) ->
shotgun_test_utils:config().
shotgun_test_utils:config().
end_per_testcase(_, Config) ->
Conn = ?config(conn, Config),
ok = shotgun:close(Conn),
Config.
Conn = ?config(conn, Config),
ok = shotgun:close(Conn),
Config.

%%------------------------------------------------------------------------------
%% Test Cases
%%------------------------------------------------------------------------------

-spec get_sse(shotgun_test_utils:config()) -> {comment, string()}.
get_sse(Config) ->
Conn = ?config(conn, Config),
Conn = ?config(conn, Config),

ct:comment("GET should return a ref"),
Opts = #{async => true, async_mode => sse},
{ok, Ref} = shotgun:get(Conn, <<"/chunked-sse">>, #{}, Opts),
true = is_reference(Ref),
ct:comment("GET should return a ref"),
Opts = #{async => true, async_mode => sse},
{ok, Ref} = shotgun:get(Conn, <<"/chunked-sse">>, #{}, Opts),
true = is_reference(Ref),

timer:sleep(500),
timer:sleep(500),

[Event1, Event2, Fin] = shotgun:events(Conn),
[Event1, Event2, Fin] = shotgun:events(Conn),

{nofin, Ref, EventBin1} = Event1,
#{data := <<"pong\n">>} = shotgun:parse_event(EventBin1),
{nofin, Ref, EventBin2} = Event2,
#{data := <<"pong\n">>} = shotgun:parse_event(EventBin2),
{fin, Ref, <<>>} = Fin,
{comment, ""}.
{nofin, Ref, EventBin1} = Event1,
#{data := <<"pong\n">>} = shotgun:parse_event(EventBin1),
{nofin, Ref, EventBin2} = Event2,
#{data := <<"pong\n">>} = shotgun:parse_event(EventBin2),
{fin, Ref, <<>>} = Fin,
{comment, ""}.

-spec get_binary(shotgun_test_utils:config()) -> {comment, string()}.
get_binary(Config) ->
Conn = ?config(conn, Config),
Conn = ?config(conn, Config),

ct:comment("GET should return a ref"),
Opts = #{async => true, async_mode => binary},
{ok, Ref} = shotgun:get(Conn, <<"/chunked-binary">>, #{}, Opts),
true = is_reference(Ref),
ct:comment("GET should return a ref"),
Opts = #{async => true, async_mode => binary},
{ok, Ref} = shotgun:get(Conn, <<"/chunked-binary">>, #{}, Opts),
true = is_reference(Ref),

timer:sleep(500),
timer:sleep(500),

[Chunk1, Chunk2, Fin] = shotgun:events(Conn),
[Chunk1, Chunk2, Fin] = shotgun:events(Conn),

{nofin, Ref, <<"1">>} = Chunk1,
{nofin, Ref, <<"2">>} = Chunk2,
{fin, Ref, <<>>} = Fin,
{nofin, Ref, <<"1">>} = Chunk1,
{nofin, Ref, <<"2">>} = Chunk2,
{fin, Ref, <<>>} = Fin,

{comment, ""}.
{comment, ""}.

-spec work_queue(shotgun_test_utils:config()) -> {comment, string()}.
work_queue(Config) ->
Conn = ?config(conn, Config),

ct:comment("Async GET should return a ref"),
Opts = #{async => true, async_mode => sse},
{ok, RefAsyncGet} = shotgun:get(Conn, <<"/chunked-sse">>, #{}, Opts),
true = is_reference(RefAsyncGet),

ct:comment("Queued GET should return a ref as well"),
{ok, RefGet} = shotgun:get(Conn, <<"/">>),
true = is_reference(RefAsyncGet),
true = RefGet =/= RefAsyncGet,

timer:sleep(500),

{comment, ""}.
Conn = ?config(conn, Config),

ct:comment("Async GET should return a ref"),
Opts = #{async => true, async_mode => sse},
{ok, RefAsyncGet} = shotgun:get(Conn, <<"/chunked-sse/20">>, #{}, Opts),
true = is_reference(RefAsyncGet),

ct:comment("Queued GET should return a ref as well"),
{ok, Response} = shotgun:get(Conn, <<"/">>),
#{status_code := 200} = Response,

ct:comment("Events frmo the async GET should be there"),
Events = shotgun:events(Conn),
21 = length(Events), %% 20 nofin + 1 fin

{comment, ""}.

-spec get_handle_event(shotgun_test_utils:config()) -> {comment, string()}.
get_handle_event(Config) ->
Conn = ?config(conn, Config),
Self = self(),

ct:comment("SSE: GET should return a ref"),
HandleEvent = fun(_, _, EventBin) ->
case shotgun:parse_event(EventBin) of
#{id := Data} -> Self ! Data;
_ -> ok
end
end,
Opts = #{ async => true
, async_mode => sse
, handle_event => HandleEvent
},
{ok, _Ref} = shotgun:get(Conn, <<"/chunked-sse/3">>, #{}, Opts),

timer:sleep(500),

ok = shotgun_test_utils:wait_receive(<<"1">>, 500),
ok = shotgun_test_utils:wait_receive(<<"2">>, 500),
ok = shotgun_test_utils:wait_receive(<<"3">>, 500),
timeout = shotgun_test_utils:wait_receive(<<"4">>, 500),

ct:comment("SSE: GET should return a ref"),
HandleEventBin = fun(_, _, Data) -> Self ! Data end,
OptsBin = #{ async => true
, async_mode => binary
, handle_event => HandleEventBin
},
{ok, _RefBin} = shotgun:get(Conn, <<"/chunked-binary">>, #{}, OptsBin),

timer:sleep(500),

ok = shotgun_test_utils:wait_receive(<<"1">>, 500),
ok = shotgun_test_utils:wait_receive(<<"2">>, 500),
timeout = shotgun_test_utils:wait_receive(<<"3">>, 500),

{comment, ""}.
10 changes: 9 additions & 1 deletion test/shotgun_test_utils.erl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

-export([all/1]).

-export([auto_send/1]).
-export([ auto_send/1
, wait_receive/2
]).

-spec all(atom()) -> [atom()].
all(Module) ->
Expand All @@ -15,3 +17,9 @@ all(Module) ->
-spec auto_send(any()) -> reference().
auto_send(Msg) ->
erlang:send_after(100, self(), Msg).

-spec wait_receive(any(), timeout()) -> ok | timeout.
wait_receive(Value, Timeout) ->
receive Value -> ok
after Timeout -> timeout
end.

0 comments on commit 810c477

Please sign in to comment.