aboutsummaryrefslogtreecommitdiffstats
path: root/test/ws_SUITE.erl
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2015-03-12 19:22:19 +0100
committerLoïc Hoguin <[email protected]>2015-03-12 19:22:19 +0100
commitc409897f508eedff8ecc6f0860c9379fcc11bf23 (patch)
treed5a651df4ef5e8f9c6c6bb77366defa53c686e20 /test/ws_SUITE.erl
parentea2de24f18741fc89d7a1dd6a3a0a43f3ccb1fd4 (diff)
downloadgun-c409897f508eedff8ecc6f0860c9379fcc11bf23.tar.gz
gun-c409897f508eedff8ecc6f0860c9379fcc11bf23.tar.bz2
gun-c409897f508eedff8ecc6f0860c9379fcc11bf23.zip
Add initial Websocket support
All autobahntestsuite tests pass including the permessage-deflate compression tests. Some of the tests pass in a non-strict fashion. They are testing for protocol errors and expect events to happen in a particular order, which is not respected by Gun. Gun fails earlier than is expected due to concurrent processing of frames. The implementation when error occurs during handshake is probably a bit rough at this point. The documentation is also incomplete and/or wrong at this time, though this is the general state of the Gun documentation and will be resolved in a separate commit.
Diffstat (limited to 'test/ws_SUITE.erl')
-rw-r--r--test/ws_SUITE.erl176
1 files changed, 176 insertions, 0 deletions
diff --git a/test/ws_SUITE.erl b/test/ws_SUITE.erl
new file mode 100644
index 0000000..82f3632
--- /dev/null
+++ b/test/ws_SUITE.erl
@@ -0,0 +1,176 @@
+%% Copyright (c) 2015, Loïc Hoguin <[email protected]>
+%%
+%% Permission to use, copy, modify, and/or distribute this software for any
+%% purpose with or without fee is hereby granted, provided that the above
+%% copyright notice and this permission notice appear in all copies.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+-module(ws_SUITE).
+-compile(export_all).
+
+-import(ct_helper, [config/2]).
+
+%% ct.
+
+all() ->
+ [{group, autobahn}].
+
+groups() ->
+ [{autobahn, [], [autobahn_fuzzingserver]}].
+
+init_per_group(autobahn, Config) ->
+ %% Some systems have it named pip2.
+ Out = os:cmd("pip show autobahntestsuite ; pip2 show autobahntestsuite"),
+ case string:str(Out, "autobahntestsuite") of
+ 0 ->
+ ct:print("Skipping the autobahn group because the "
+ "Autobahn Test Suite is not installed.~nTo install it, "
+ "please follow the instructions on this page:~n~n "
+ "http://autobahn.ws/testsuite/installation.html"),
+ {skip, "Autobahn Test Suite not installed."};
+ _ ->
+ Config
+ end.
+
+end_per_group(_, _) ->
+ oK.
+
+%% Tests.
+
+autobahn_fuzzingserver(Config) ->
+ Self = self(),
+ spawn_link(fun() -> start_port(Config, Self) end),
+ receive autobahn_ready -> ok end,
+ N = get_case_count(),
+ run_cases(0, N),
+ Report = config(priv_dir, Config) ++ "reports/clients/index.html",
+ ct:log("<h2><a href=\"~s\">Full report</a></h2>~n", [Report]),
+ ct:print("Autobahn Test Suite report: file://~s~n", [Report]),
+ log_output(),
+ terminate(),
+ {ok, HTML} = file:read_file(Report),
+ case length(binary:matches(HTML, <<"case_failed">>)) > 2 of
+ true -> error(failed);
+ false -> ok
+ end.
+
+start_port(Config, Pid) ->
+ Port = open_port({spawn, "wstest -m fuzzingserver -s " ++ config(data_dir, Config) ++ "server.json"},
+ [{line, 10000}, {cd, config(priv_dir, Config)}, binary]),
+ receive_preamble(Port, Pid),
+ receive_infinity(Port).
+
+receive_preamble(Port, Pid) ->
+ receive
+ {Port, {data, {eol, Line = <<"Ok, will run", _/bits>>}}} ->
+ Pid ! autobahn_ready,
+ io:format(user, "~s~n", [Line]);
+ {Port, {data, {eol, Line}}} ->
+ io:format(user, "~s~n", [Line]),
+ receive_preamble(Port, Pid)
+ after 5000 ->
+ terminate(),
+ error(timeout)
+ end.
+
+receive_infinity(Port) ->
+ receive
+ {Port, {data, {eol, <<"Updating reports", _/bits>>}}} ->
+ receive_infinity(Port);
+ {Port, {data, {eol, Line}}} ->
+ io:format(user, "~s~n", [Line]),
+ receive_infinity(Port)
+ end.
+
+get_case_count() ->
+ {Pid, Ref} = connect("/getCaseCount"),
+ receive
+ {gun_ws, Pid, {text, N}} ->
+ close(Pid, Ref),
+ binary_to_integer(N);
+ Msg ->
+ ct:print("Unexpected message ~p", [Msg]),
+ terminate(),
+ error(failed)
+ end.
+
+run_cases(Total, Total) ->
+ ok;
+run_cases(N, Total) ->
+ {Pid, Ref} = connect(["/runCase?case=", integer_to_binary(N + 1), "&agent=Gun"]),
+ loop(Pid, Ref),
+ update_reports(),
+ run_cases(N + 1, Total).
+
+loop(Pid, Ref) ->
+ receive
+ {gun_ws, Pid, close} ->
+ gun:ws_send(Pid, close),
+ loop(Pid, Ref);
+ {gun_ws, Pid, {close, Code, _}} ->
+ gun:ws_send(Pid, {close, Code, <<>>}),
+ loop(Pid, Ref);
+ {gun_ws, Pid, Frame} ->
+ gun:ws_send(Pid, Frame),
+ loop(Pid, Ref);
+ {'DOWN', Ref, process, Pid, normal} ->
+ close(Pid, Ref);
+ Msg ->
+ ct:print("Unexpected message ~p", [Msg]),
+ close(Pid, Ref)
+ end.
+
+update_reports() ->
+ {Pid, Ref} = connect("/updateReports?agent=Gun"),
+ receive
+ {gun_ws, Pid, close} ->
+ close(Pid, Ref)
+ after 5000 ->
+ error(failed)
+ end.
+
+log_output() ->
+ ok.
+
+connect(Path) ->
+ {ok, Pid} = gun:open("127.0.0.1", 33080, [{type, tcp}, {retry, 0}]),
+ Ref = monitor(process, Pid),
+ gun:ws_upgrade(Pid, Path, [], #{compress => true}),
+ receive
+ {gun_ws_upgrade, Pid, ok} ->
+ ok;
+ Msg ->
+ ct:print("Unexpected message ~p", [Msg]),
+ terminate(),
+ error(failed)
+ end,
+ {Pid, Ref}.
+
+close(Pid, Ref) ->
+ demonitor(Ref),
+ gun:close(Pid),
+ flush(Pid).
+
+flush(Pid) ->
+ receive
+ {gun_ws, Pid, _} ->
+ flush(Pid);
+ {gun_ws_upgrade, Pid, _} ->
+ flush(Pid);
+ {'DOWN', _, process, Pid, _} ->
+ flush(Pid)
+ after 0 ->
+ ok
+ end.
+
+terminate() ->
+ Res = os:cmd("killall wstest"),
+ io:format(user, "~s", [Res]),
+ ok.