Skip to content

Commit

Permalink
[#14] Get tests to run on player node
Browse files Browse the repository at this point in the history
  • Loading branch information
Brujo Benavides committed Jul 3, 2016
1 parent ad2d29e commit 1ba7ba2
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 40 deletions.
2 changes: 1 addition & 1 deletion src/bo_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ check_task(Player) ->
task(Player) -> {ok, bo_task:describe(bo_players:task(Player))}.

test(Player, Solution) ->
case bo_task:test(bo_players:task(Player), Solution) of
case bo_players_repo:test(Player, Solution) of
ok -> advance(Player, solve);
NOK -> NOK
end.
Expand Down
8 changes: 7 additions & 1 deletion src/repos/bo_players_repo.erl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
-module(bo_players_repo).

-export([signup/2, fetch/1, advance/2, stats/0]).
-export([signup/2, fetch/1, advance/2, stats/0, test/2]).

-type stats() ::
#{ tasks := pos_integer()
Expand Down Expand Up @@ -43,3 +43,9 @@ stats() ->
#{ tasks => length(bo_tasks:all())
, players => SortedStats
}.

-spec test(bo_players:player(), bo_tasks:solution()) -> bo_task:result().
test(Player, Solution) ->
Task = bo_players:task(Player),
Node = bo_players:node(Player),
bo_task:test(Task, Solution, Node).
6 changes: 3 additions & 3 deletions src/tasks/bo_first_task.erl
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

-behaviour(bo_task).

-export([description/0, expected_arity/0, score/0, timeout/0, tests/0]).
-export([description/0, spec/0, score/0, timeout/0, tests/0]).

-spec description() -> binary().
description() -> <<"Echo: Return whatever you receive">>.

-spec expected_arity() -> 1.
expected_arity() -> 1.
-spec spec() -> bo_task:spec().
spec() -> #{input => [<<"X">>], output => <<"X">>}.

-spec score() -> 10.
score() -> 10.
Expand Down
61 changes: 35 additions & 26 deletions src/tasks/bo_task.erl
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,59 @@

-type task() :: #{ name := module()
, desc := binary()
, spec := spec()
, score := pos_integer()
}.

-type test() :: fun((fun()) -> ok | {error, term()}).
-type test() :: fun((solution()) -> ok | {error, term()}).

-export_type([task/0, result/0, test/0]).
-type spec() :: #{ input := [binary()]
, output := binary()
}.

-type solution() :: fun((_) -> _).

-export_type([task/0, result/0, test/0, spec/0, solution/0]).

-callback description() -> binary().
-callback expected_arity() -> non_neg_integer().
-callback timeout() -> pos_integer().
-callback spec() -> spec().
-callback timeout() -> timeout().
-callback tests() -> [test()].
-callback score() -> pos_integer().

-export([describe/1, test/2, score/1]).
-export([describe/1, test/3, score/1, tester/2]).

-spec describe(module()) -> bo_task:task().
describe(Task) ->
#{ name => Task
, desc => Task:description()
, spec => Task:spec()
, score => Task:score()
}.

-spec score(module()) -> pos_integer().
score(Task) -> Task:score().

-spec test(module(), fun()) -> result().
test(Task, Fun) ->
Arity = Task:expected_arity(),
case is_function(Fun, Arity) of
-spec test(module(), solution(), node()) -> result().
test(Task, Fun, Node) ->
#{input := Params} = Task:spec(),
case is_function(Fun, length(Params)) of
false -> {error, invalid};
true ->
Timeout = Task:timeout(),
Tester = start_tester(Task, Fun),
receive
{Tester, Result} -> Result
after Timeout ->
true = exit(Tester, kill),
{error, timeout}
ok = ensure_code(?MODULE, Node),
ok = ensure_code(Task, Node),
case rpc:call(Node, ?MODULE, tester, [Task, Fun], Task:timeout()) of
{badrpc, Error} -> {error, Error};
Result -> Result
end
end.

start_tester(Task, Fun) ->
Caller = self(),
proc_lib:spawn(
fun() ->
Results = do_test(Task, Fun),
case Results of
[] -> Caller ! {self(), ok};
Results -> Caller ! {self(), {failures, Results}}
end
end).
-spec tester(module(), solution()) -> result().
tester(Task, Fun) ->
case do_test(Task, Fun) of
[] -> ok;
Results -> {failures, Results}
end.

do_test(Task, Fun) ->
lists:filtermap(
Expand All @@ -67,3 +69,10 @@ do_test(Task, Fun) ->
{true, #{error => Exception, stack => erlang:get_stacktrace()}}
end
end, Task:tests()).

ensure_code(Module, Node) ->
{Module, Binary, Filename} = code:get_object_code(Module),
{module, Module} =
rpc:call(
Node, code, load_binary, [Module, filename:basename(Filename), Binary]),
ok.
1 change: 0 additions & 1 deletion test/bo_invalid_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ test_error(Config) ->

ct:comment("Providing a function that errors the tests returns errors"),
{failures, Failures} = submit(Client, fun(X) -> X / 2 end),

[_|_] = [Err || #{error := Err, stack := _} <- Failures],

{comment, ""}.
Expand Down
68 changes: 68 additions & 0 deletions test/bo_remoteness_SUITE.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
-module(bo_remoteness_SUITE).
-author('elbrujohalcon@inaka.net').

-export([all/0]).
-export([init_per_suite/1, end_per_suite/1]).
-export([run/1, crash/1]).

-type config() :: proplists:proplist().

-spec all() -> [atom()].
all() -> [run, crash].

-spec init_per_suite(config()) -> config().
init_per_suite(Config) ->
case net_kernel:start(['bo_test@127.0.0.1']) of
{ok, _} -> ok;
{error, {already_started, _}} -> ok;
{error, Error} -> throw(Error)
end,
_ = application:load(beam_olympics),
application:set_env(beam_olympics, all_tasks, [bo_test_node_task]),
{ok, _} = bo:start(),
_ = sumo:delete_all(bo_players),
{ok, Client} = bo_test_client:start(bo_remoteness),
[{client, Client} | Config].

-spec end_per_suite(config()) -> config().
end_per_suite(Config) ->
{client, Client} = lists:keyfind(client, 1, Config),
ok = bo_test_client:stop(Client),
_ = sumo:delete_all(bo_players),
application:unset_env(beam_olympics, all_tasks),
ok = bo:stop(),
Config.

-spec run(config()) -> {comment, string()}.
run(Config) ->
{client, Client} = lists:keyfind(client, 1, Config),
{ok, #{name := bo_test_node_task}} =
bo_test_client:signup(Client, <<"remoteness">>),

ct:comment("This node is not correct"),
Node = node(),
WrongFun = fun() -> Node end,
{failures, [_|_]} = bo_test_client:submit(Client, <<"remoteness">>, WrongFun),

ct:comment("Computing the node works, because test is run on client node"),
RightFun = fun() -> node() end,
the_end = bo_test_client:submit(Client, <<"remoteness">>, RightFun),

{comment, ""}.

-spec crash(config()) -> {comment, string()}.
crash(Config) ->
{client, Client} = lists:keyfind(client, 1, Config),
{ok, #{name := bo_test_node_task}} =
bo_test_client:signup(Client, <<"crash">>),

ct:comment("Crashing crashes only the client node"),
CrashFun =
fun() -> erlang:apply(erlang, halt, ["Crashed by Remoteness Test"]) end,
try bo_test_client:submit(Client, <<"crash">>, CrashFun) of
R -> ct:fail("Unexpected result ~p", [R])
catch
_:{timeout, Client, _} -> ok
end,

{comment, ""}.
36 changes: 36 additions & 0 deletions test/bo_test_node_task.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-module(bo_test_node_task).

-behaviour(bo_task).

-export([ description/0
, spec/0
, score/0
, timeout/0
, tests/0
]).

-spec description() -> binary().
description() -> <<"Return the node you are on regardless of input">>.

-spec spec() -> bo_task:spec().
spec() -> #{input => [], output => <<"node()">>}.

-spec score() -> 100.
score() -> 100.

-spec timeout() -> 1000.
timeout() -> 1000.

-spec tests() -> [bo_task:test()].
tests() -> [build_test()].

build_test() ->
fun(Fun) ->
Output = Fun(),
case node() of
Output -> ok;
Expected -> {error, #{ output => Output
, expected => Expected
}}
end
end.
8 changes: 4 additions & 4 deletions test/simple_task1.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
-behaviour(bo_task).

-export([ description/0
, expected_arity/0
, spec/0
, score/0
, timeout/0
, tests/0
, solution/1
]).

-spec description() -> binary().
description() -> <<"Echo: Always return 1">>.
description() -> <<"Always return 1">>.

-spec expected_arity() -> 1.
expected_arity() -> 1.
-spec spec() -> bo_task:spec().
spec() -> #{input => [<<"_">>], output => <<"1">>}.

-spec score() -> 100.
score() -> 100.
Expand Down
8 changes: 4 additions & 4 deletions test/simple_task2.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
-behaviour(bo_task).

-export([ description/0
, expected_arity/0
, spec/0
, score/0
, timeout/0
, tests/0
, solution/1
]).

-spec description() -> binary().
description() -> <<"Echo: Always return 2">>.
description() -> <<"Always return 2">>.

-spec expected_arity() -> 1.
expected_arity() -> 1.
-spec spec() -> bo_task:spec().
spec() -> #{input => [<<"_">>], output => <<"2">>}.

-spec score() -> 200.
score() -> 200.
Expand Down

0 comments on commit 1ba7ba2

Please sign in to comment.