Skip to content

Commit

Permalink
Test environment can execute tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
Torben Hoffmann committed Feb 16, 2012
1 parent 4e94bec commit 1576efa
Show file tree
Hide file tree
Showing 5 changed files with 357 additions and 50 deletions.
41 changes: 41 additions & 0 deletions rebar.config
@@ -0,0 +1,41 @@
%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 ft=erlang et

{lib_dirs, ["deps"]}.

{deps, [
{lager, ".*", {git, "git://github.com/basho/lager.git", {branch, "master"}}},
{gproc, ".*", %%"0.2.10",
{git,"https://github.com/uwiger/gproc.git",
{branch, "master"}
%% {tag,"v0.2.10"}
}},
{parse_trans, ".*", {git, "git://github.com/uwiger/parse_trans.git", "master"}},
{meck, ".*",
{git, "git://github.com/eproxus/meck.git", {branch, "master"}}},
{hamcrest, ".*",
{git, "git://github.com/hyperthunk/hamcrest-erlang.git",
{branch, "master"}}}
]}.

{require_otp_vsn, "R14|R15|R16"}.

{clean_files, ["release/chronos"]}.

{erl_opts, [
fail_on_warning,
debug_info,
%%warn_missing_spec,
{src_dirs, ["test_"]},
{parse_transform, lager_transform}
]}.

{cover_enabled, true}.

{clean_files, ["*.eunit", "ebin/*.beam"]}.

{validate_app_modules, false}.

{sub_dirs, ["src",
"test",
"release"]}.
10 changes: 10 additions & 0 deletions src/chronos.app.src
@@ -0,0 +1,10 @@
%% This is the application resource file (.app file) for the,
%% application.
{application, chronos,
[{description, "chronos - a timer utility for Erlang"},
{vsn, "0.0.1"},
{modules, [chronos]},
{registered,[]},
{applications, [kernel, stdlib, gproc]},
%% {mod, {chronos_app,[]}},
{start_phases, []}]}.
126 changes: 76 additions & 50 deletions src/chronos.erl
Expand Up @@ -11,88 +11,103 @@
-behaviour(gen_server).

%% API
-export([start_link/0,
start_link/1,
stop/1,
start_timer/4,
stop_timer/2
]).
-export([start_link/1,
stop/1,
start_timer/4,
stop_timer/2
]).



%% gen_server callbacks
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).


%% Types
-type server_name() :: atom().
-type server_ref() :: server_name() | pid().
%%-type server_ref() :: server_name() | pid().
-type timer_name() :: term().
-type function_name() :: term().
-type function_name() :: atom().
-type args() :: [term()].
-type callback() :: {module(), function_name(), args()].
-type callback() :: {module(), function_name(), args()}.
-type timer_duration():: pos_integer().

-record(state,
{running = [] :: {[timer_name(), reference()]}
}).
-export_type([server_name/0,
timer_name/0,
timer_duration/0]).

-record(state,
{running = [] :: [{timer_name(), reference()}]
}).

%%%===================================================================
%%% API
%%%===================================================================

-start_link(server_name()) -> {'ok',pid()} | 'ignore' | {'error',term()}.
-spec start_link(server_name()) -> {'ok', pid()} | 'ignore' | {'error',term()}.
start_link(ServerName) ->
gen_server:start_link({local, ServerName}, ?MODULE, [], []).
gen_server:start_link(?MODULE, ServerName, []).

-start_link() -> {'ok',pid()} | 'ignore' | {'error',term()}.
start_link() ->
gen_server:start_link(?MODULE, [], []).
%% -start_link() -> {'ok',pid()} | 'ignore' | {'error',term()}.
%% start_link() ->
%% gen_server:start_link(?MODULE, [], []).

-spec stop(server_name()) -> ok.
stop(ServerRef) ->
gen_server:call(ServerRef, stop).
-spec start_timer(server_ref(), timer_name(), pos_integer(), callback()) ->
'ok' | {'error',term()}.
start_timer(ServerRef, TimerName, Timeout, Callback) ->
gen_server:call(ServerRef, {start_timer, TimerName, Timeout, Callback}).

-spec stop_timer(server_ref(), timer_name()) -> 'ok' | {'error',term()}.
stop(ServerName) ->
call(ServerName, stop).

-spec start_timer(server_name(), timer_name(), pos_integer(), callback()) ->
'ok' | {'error',term()}.
start_timer(ServerName, TimerName, Timeout, Callback) ->
call(ServerName, {start_timer, TimerName, Timeout, Callback}).

-spec stop_timer(server_name(), timer_name()) -> 'ok' | {'error',term()}.
stop_timer(ServerRef, TimerName) ->
gen_server:call(ServerRef, {stop_timer, TimerName}).
call(ServerRef, {stop_timer, TimerName}).



%%%===================================================================
%%% gen_server callbacks
%%%===================================================================


-spec init([term()]) -> {'ok', #state{}} | {'stop', term()} | 'ignore'.
init([]) ->
-spec init(server_name()) -> {'ok', #state{}} | {'stop', term()} | 'ignore'.
init(ServerName) ->
gproc:reg(proc_name(ServerName)),
{ok, #state{}}.

-spec handle_call(term(), term(), #state{}) ->
{'reply', 'ok' | {'error', term()} , #state{}} |
{'stop', term(), 'ok', #state{}}.
handle_call({start_timer, Name, Time, Callback}, _From,
#state{running=R}=State) ->
%% If the Name timer is running we clean it up.
-spec handle_call(term(), term(), #state{}) ->
{'reply', 'ok' | {'error', term()} , #state{}} |
{'stop', term(), 'ok', #state{}}.
handle_call({start_timer, Name, Time, Callback}, _From,
#state{running=R}=State) ->
%% If the Name timer is running we clean it up.
R1 = case lists:keytake(Name, 1, R) of
false ->
R;
{value, {_, TRef,}, Ra} ->
erlang:cancel_timer(TRef),
Ra
end,
false ->
R;
{value, {_, TRef}, Ra} ->
erlang:cancel_timer(TRef),
Ra
end,
TRefNew = erlang:start_timer(Time, self(), {Name,Callback}),
{reply, ok, State#state{running=[{Name,TRefNew}|R1]}.

{reply, ok, State#state{running=[{Name,TRefNew}|R1]}};
handle_call({stop_timer, Name}, _From,
#state{running=R}=State) ->
case lists:keytake(Name, 1, R) of
{value, {_, TRef}, Rnext} ->
erlang:cancel_timer(TRef),
{reply, ok, State#state{running=Rnext}};
false ->
Reason = io_lib:format("Stopping ~p timer that was not running",
[Name]),
{reply, {error, Reason}, State}
end.
%%--------------------------------------------------------------------
%% @private
%% @doc
Expand Down Expand Up @@ -147,3 +162,14 @@ code_change(_OldVsn, State, _Extra) ->
%%%===================================================================
%%% Internal functions
%%%===================================================================

proc_name(ServerName) -> {n, l, ServerName}.


call(ServerName, Msg) ->
{Pid, _} = gproc:await(proc_name(ServerName)),
gen_server:call(Pid, Msg, infinity).

%% cast(ServerName, Msg) ->
%% {Pid, _} = gproc:await(proc_name(ServerName)),
%% gen_server:cast(Pid, Msg).
85 changes: 85 additions & 0 deletions test/chronos_eqc.erl
@@ -0,0 +1,85 @@
%%% @author Torben Hoffmann <th@issuu.com>
%%% @copyright (C) 2012, Torben Hoffmann
%%% @doc
%%%
%%% @end
%%% Created : 13 Feb 2012 by Torben Hoffmann <th@issuu.com>

-module(chronos_eqc).


-include_lib("eqc/include/eqc.hrl").
-include_lib("eqc/include/eqc_statem.hrl").

-compile(export_all).

-record(state,
{servers = [],
timers = [] :: [{chronos:server_name(), chronos:timer_name()}]
}).

%% Initialize the state
initial_state() ->
#state{
servers = [],
timers = []
}.

%% Command generator, S is the state
command(S) ->
frequency( [ {100, {call, timer_expiry, start_server, [server_name()]}} ]
).

%% Next state transformation, S is the current state
next_state(S,_V,{call,timer_expiry, start_server, [ServerName]}) ->
S#state{ servers = [ServerName | S#state.servers] }.

%% Precondition, checked before command is added to the command sequence
precondition(S,{call,timer_expiry, start_server, [ServerName]}) ->
not lists:member(ServerName, S#state.servers).

%% Postcondition, checked after command has been evaluated
%% OBS: S is the state before next_state(S,_,<command>)
postcondition(_S,{call,timer_expiry, start_server, [_ServerName]}, ok) ->
true;
postcondition(_S,{call,timer_expiry, start_server, [_ServerName]}, _Other) ->
false.

prop_chronos() ->
?FORALL(Cmds,commands(?MODULE),
?TRAPEXIT(
begin
start_context(),
{H,S,Res} = run_commands(?MODULE,Cmds),
stop_context(),
?WHENFAIL(
io:format("History: ~p\nState: ~p\nRes: ~p\n",[H,S,Res]),
Res == ok)
end)).

start_context() ->
application:start(gproc),
timer_expiry:start_link().

stop_context() ->
timer_expiry:stop(),
application:stop(gproc).



%%-------------------- GENERATORS ------------------------------


server_name() -> {server, nat()}.

timer_name() -> {timer, nat()}.

timer_timeout() -> choose(10, 100).

%%---------------------- OPERATIONS ----------------------

start_server(ServerName) ->
chronos:start_link(ServerName).

start_timer(Server, Timer, Timeout) ->
ok.

0 comments on commit 1576efa

Please sign in to comment.