aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMagnus Klaar <[email protected]>2011-12-24 00:58:03 +0100
committerMagnus Klaar <[email protected]>2012-01-25 12:10:05 +0100
commit88088251732e36f72e3edf806a5561b569e62c19 (patch)
tree211d7f8a16e71d3602904cc8311646e2b4febac1
parent4b93c2d19a10e5d9cee207038103bb83f1ab9436 (diff)
downloadcowboy-88088251732e36f72e3edf806a5561b569e62c19.tar.gz
cowboy-88088251732e36f72e3edf806a5561b569e62c19.tar.bz2
cowboy-88088251732e36f72e3edf806a5561b569e62c19.zip
Add Autobahn test suite for websockets
We're using the existing test suite for websocket servers from the Autobahn project to verify that out websockets implementation is sane. A CT test suite and python module wrapping the test suite has been added. The test suite is run when the 'make inttests' target is executed.
-rw-r--r--Makefile7
-rw-r--r--test/autobahn_SUITE.erl97
-rwxr-xr-xtest/autobahn_SUITE_data/test.py76
-rw-r--r--test/websocket_echo_handler.erl34
4 files changed, 213 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index e5524f4..8d601e7 100644
--- a/Makefile
+++ b/Makefile
@@ -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,ws
+
+intct:
+ @$(REBAR) ct skip_deps=true suites=http,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.