From c409897f508eedff8ecc6f0860c9379fcc11bf23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Thu, 12 Mar 2015 19:22:19 +0100 Subject: 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. --- test/ws_SUITE.erl | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 test/ws_SUITE.erl (limited to 'test/ws_SUITE.erl') 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 +%% +%% 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("

Full report

~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. -- cgit v1.2.3