diff options
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | test/autobahn_SUITE.erl | 97 | ||||
-rwxr-xr-x | test/autobahn_SUITE_data/test.py | 76 | ||||
-rw-r--r-- | test/websocket_echo_handler.erl | 34 |
4 files changed, 213 insertions, 1 deletions
@@ -18,11 +18,16 @@ clean: tests: clean app eunit ct +inttests: clean app eunit intct + eunit: @$(REBAR) eunit skip_deps=true ct: - @$(REBAR) ct skip_deps=true + @$(REBAR) ct skip_deps=true suites=http,proper,ws + +intct: + @$(REBAR) ct skip_deps=true suites=http,proper,ws,autobahn build-plt: @$(DIALYZER) --build_plt --output_plt .cowboy_dialyzer.plt \ diff --git a/test/autobahn_SUITE.erl b/test/autobahn_SUITE.erl new file mode 100644 index 0000000..85647d3 --- /dev/null +++ b/test/autobahn_SUITE.erl @@ -0,0 +1,97 @@ +%% Copyright (c) 2011, Magnus Klaar <[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(autobahn_SUITE). + +%% This CT suite reuses the websocket server test suite from the Autobahn +%% project. The Autobahn project is a websocket implementation for Python. +%% Given that we don't expect to find the packages and tools to properly +%% set up and run such a test on a system used primarily for Erlang devlopment +%% this test suite is not included in the default 'ct' target in the makefile. + +-include_lib("common_test/include/ct.hrl"). + +-export([all/0, groups/0, init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2]). %% ct. +-export([run_tests/1]). %% autobahn. + +%% ct. + +all() -> + [{group, autobahn}]. + +groups() -> + BaseTests = [run_tests], + [{autobahn, [], BaseTests}]. + +init_per_suite(Config) -> + application:start(inets), + application:start(cowboy), + %% /tmp must be used as the parent directory for the virtualenv because + %% the directory names used in CT are so long that the interpreter path + %% in the scripts generated by virtualenv get so long that the system + %% refuses to execute them. + EnvPath = "/tmp/cowboy_autobahn_env", + os:putenv("AB_TESTS_ENV", EnvPath), + os:putenv("AB_TESTS_PRIV", ?config(priv_dir, Config)), + BinPath = filename:join(?config(data_dir, Config), "test.py"), + Stdout = os:cmd(BinPath ++ " setup"), + ct:log("~s~n", [Stdout]), + case string:str(Stdout, "AB-TESTS-SETUP-OK") of + 0 -> erlang:error(failed); + _ -> [{env_path, EnvPath},{bin_path,BinPath}|Config] + end. + +end_per_suite(_Config) -> + os:cmd("deactivate"), + application:stop(cowboy), + application:stop(inets), + ok. + +init_per_group(autobahn, Config) -> + Port = 33080, + cowboy:start_listener(autobahn, 100, + cowboy_tcp_transport, [{port, Port}], + cowboy_http_protocol, [{dispatch, init_dispatch()}] + ), + [{port, Port}|Config]. + +end_per_group(Listener, _Config) -> + cowboy:stop_listener(Listener), + ok. + +%% Dispatch configuration. + +init_dispatch() -> + [{[<<"localhost">>], [ + {[<<"echo">>], websocket_echo_handler, []}]}]. + +%% autobahn cases + +run_tests(Config) -> + PrivDir = ?config(priv_dir, Config), + IndexFile = filename:join([PrivDir, "reports", "servers", "index.html"]), + ct:log("<h2><a href=\"~s\">Full Test Results Report</a></h2>~n", [IndexFile]), + BinPath = ?config(bin_path, Config), + Stdout = os:cmd(BinPath ++ " test"), + ct:log("~s~n", [Stdout]), + case string:str(Stdout, "AB-TESTS-TEST-OK") of + 0 -> erlang:error(failed); + _ -> ok + end, + {ok, IndexHTML} = file:read_file(IndexFile), + case binary:match(IndexHTML, <<"Fail">>) of + {_, _} -> erlang:error(failed); + nomatch -> ok + end. diff --git a/test/autobahn_SUITE_data/test.py b/test/autobahn_SUITE_data/test.py new file mode 100755 index 0000000..422cb41 --- /dev/null +++ b/test/autobahn_SUITE_data/test.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +import os +import os.path +import sys +import subprocess + + +AB_TESTS_ENV = os.getenv("AB_TESTS_ENV") +AB_TESTS_PRIV = os.getenv("AB_TESTS_PRIV") + +VIRTUALENV_URL = 'https://raw.github.com/pypa/virtualenv/master/virtualenv.py' +VIRTUALENV_BIN = os.path.join(AB_TESTS_ENV, "virtualenv.py") +PIP_BIN = os.path.join(AB_TESTS_ENV, "bin", "pip") + + +def activate_env(env): + """ + See 'Using Virtualenv without bin/python' at http://www.virtualenv.org + """ + activate_this = os.path.join(env, 'bin', 'activate_this.py') + exec(compile(open(activate_this).read(), activate_this, 'exec'), + dict(__file__=activate_this)) + +def install_env(env): + """ + Install a new virtualenv at a path and also install the Autobahn package. + """ + os.makedirs(env) if not os.path.isdir(env) else None + subprocess.check_call(["curl", "-sS", VIRTUALENV_URL, "-o", VIRTUALENV_BIN]) + subprocess.check_call(["python", VIRTUALENV_BIN, env]) + activate_env(env) + subprocess.check_call([PIP_BIN, "install", "Autobahn"]) + +def client_config(): + """ + See comment on SUPPORTED_SPEC_VERSIONS in Autobahn/.../websocket.py + """ + base = { + 'options': {'failByDrop': False}, + 'enable-ssl': False, + 'servers': [{ + 'agent': 'Cowboy/10', + 'url': 'ws://localhost:33080/echo', + 'options': {'version': 10}}, # hybi-10 + {'agent': 'Cowboy/18', + 'url': 'ws://localhost:33080/echo', + 'options': {'version': 18}} # RFC6455 + ], + 'cases': ['*'], + 'exclude-cases': [] } + return base + +def run_test(env, config): + activate_env(env) + from twisted.python import log + from twisted.internet import reactor + from autobahn.fuzzing import FuzzingClientFactory + os.chdir(AB_TESTS_PRIV) + log.startLogging(sys.stdout) + fuzzer = FuzzingClientFactory(config) + return reactor.run() + + +def main(): + cmd = sys.argv[1] + if cmd == 'setup': + install_env(AB_TESTS_ENV) + print('AB-TESTS-SETUP-OK') + elif cmd == 'test': + run_test(AB_TESTS_ENV, client_config()) + print('AB-TESTS-TEST-OK') + else: + return 1 + +if __name__ == '__main__': + main() diff --git a/test/websocket_echo_handler.erl b/test/websocket_echo_handler.erl new file mode 100644 index 0000000..b06c1e7 --- /dev/null +++ b/test/websocket_echo_handler.erl @@ -0,0 +1,34 @@ +%% Feel free to use, reuse and abuse the code in this file. + +-module(websocket_echo_handler). +-behaviour(cowboy_http_handler). +-behaviour(cowboy_http_websocket_handler). +-export([init/3, handle/2, terminate/2]). +-export([websocket_init/3, websocket_handle/3, + websocket_info/3, websocket_terminate/3]). + +init(_Any, _Req, _Opts) -> + {upgrade, protocol, cowboy_http_websocket}. + +handle(_Req, _State) -> + exit(badarg). + +terminate(_Req, _State) -> + exit(badarg). + +websocket_init(_TransportName, Req, _Opts) -> + Req2 = cowboy_http_req:compact(Req), + {ok, Req2, undefined}. + +websocket_handle({text, Data}, Req, State) -> + {reply, {text, Data}, Req, State}; +websocket_handle({binary, Data}, Req, State) -> + {reply, {binary, Data}, Req, State}; +websocket_handle(_Frame, Req, State) -> + {ok, Req, State}. + +websocket_info(_Info, Req, State) -> + {ok, Req, State}. + +websocket_terminate(_Reason, _Req, _State) -> + ok. |