Commits

Anonymous committed bc3af20 Merge

merge

Comments (0)

Files changed (8)

scripts/run_tests.escript

                         nomatch == re:run(atom_to_list(M), NonTestRe)
                 end,
                 proplists:get_value(modules, App)),
-
     crypto:start(),
-    setup_mock_ring(),
-
     start_cover(Modules),
     eunit:test(Modules, [verbose]),
     analyze_cover(Modules);
 percentage(Part, Total) ->
     (Part/Total)*100.
 
-setup_mock_ring() ->
-    Ring0 = lists:foldl(fun(_,R) ->
-                               riak_ring:transfer_node(
-                                 hd(riak_ring:my_indices(R)),
-                                 othernode@otherhost, R) end,
-                       riak_ring:fresh(16,node()),[1,2,3,4,5,6]),
-    Ring = lists:foldl(fun(_,R) ->
-                               riak_ring:transfer_node(
-                                 hd(riak_ring:my_indices(R)),
-                                 othernode2@otherhost2, R) end,
-                       Ring0,[1,2,3,4,5,6]),
-    ets:new(nodelocal_ring, [protected, named_table]),
-    ets:insert(nodelocal_ring, {ring, Ring}).

src/riak_bucket.erl

 %% @type riak_bucketprops() = [{Propkey :: atom(), Propval :: term()}]
 
 -module(riak_bucket).
+-include_lib("eunit/include/eunit.hrl").
 -export([set_bucket/2, get_bucket/1, get_bucket/2]).
 -export([defaults/0]).
 
      {young_vclock, 21600},
      {big_vclock, 50},
      {small_vclock, 10}].
+
+simple_set_test() ->
+    riak_ring_manager:start_link(test),
+    riak_eventer:start_link(test),
+    ok = set_bucket(a_bucket,[{key,value}]),
+    Bucket = get_bucket(a_bucket),
+    riak_ring_manager:stop(),
+    riak_eventer:stop(),
+    ?assertEqual(value, proplists:get_value(key, Bucket)).

src/riak_claim.erl

 never_wants_claim(_) -> no.
 
 wants_claim_test() ->
+    riak_ring_manager:start_link(test),
+    riak_eventer:start_link(test),
+    riak_test_util:setup_mockring1(),
     {ok, Ring} = riak_ring_manager:get_my_ring(),
-    {yes, _}  = default_wants_claim(Ring).
+    ?assertEqual(yes, erlang:element(1,default_wants_claim(Ring))),
+    riak_ring_manager:stop(),
+    riak_eventer:stop().

src/riak_eventer.erl

 
 -module(riak_eventer).
 -behaviour(gen_server2).
--export([start_link/0]).
+-export([start_link/0,start_link/1,stop/0]).
 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
 	 terminate/2, code_change/3]).
 
 
 %% @private
 start_link() -> gen_server2:start_link({local, ?MODULE}, ?MODULE, [], []).
+start_link(test) -> % when started this way, run a mock server (nop)
+    gen_server2:start_link({local, ?MODULE}, ?MODULE, [test], []).
 
 %% @private
-init([]) -> {ok, stateless_server}.
+init([]) -> {ok, stateless_server};
+init([test]) -> {ok, test}.
 
 notify(Event) ->
     gen_server2:cast(riak_local_logger, {event, Event}),
 notify(Module, EventName, EventDetail) ->
     notify({Module, EventName, node(), EventDetail}).
 
+%% @private (only used for test instances)
+stop() -> gen_server2:cast(?MODULE, stop).
+
 %% @private
+handle_cast(stop, State) -> {stop,normal,State};
+
+handle_cast({event, _Event}, test) -> {noreply,test};
 handle_cast({event, Event}, State) ->
     {ok, Ring} = riak_ring_manager:get_my_ring(),    %%%% TEST EVENTS!
     Eventers = get_eventers(Ring),

src/riak_fs_backend.erl

 % @type state() = term().
 -record(state, {dir}).
 
-% @spec start(Partition :: integer()) ->
-%                        {ok, state()} | {{error, Reason :: term()}, state()}
+%% @spec start(Partition :: integer()) ->
+%%          {ok, state()} | {{error, Reason :: term()}, state()}
+%% @doc Start this backend.  'riak_fs_backend_root' must be
+%%      set in Riak's application environment.  It must be set to
+%%      a string representing the base directory where this backend
+%%      should store its files.
 start(Partition) ->
     PartitionName = integer_to_list(Partition),
     ConfigRoot = riak:get_app_env(riak_fs_backend_root),
     Dir = filename:join([ConfigRoot,PartitionName]),
     {filelib:ensure_dir(Dir), #state{dir=Dir}}.
 
-% @spec stop(state()) -> ok | {error, Reason :: term()}
+%% @spec stop(state()) -> ok | {error, Reason :: term()}
 stop(_State) -> ok.
 
-% get(state(), Key :: binary()) ->
-%   {ok, Val :: binary()} | {error, Reason :: term()}
-% key must be 160b
+%% @spec get(state(), BKey :: riak_object:bkey()) ->
+%%         {ok, Val :: binary()} | {error, Reason :: term()}
+%% @doc Get the object stored at the given bucket/key pair
 get(State, BKey) ->
     File = location(State,BKey),
     case filelib:is_file(File) of
         true -> file:read_file(File)
     end.
 
-% put(state(), Key :: binary(), Val :: binary()) ->
-%   ok | {error, Reason :: term()}
-% key must be 160b
+%% @spec put(state(), BKey :: riak_object:bkey(), Val :: binary()) ->
+%%         ok | {error, Reason :: term()}
+%% @doc Store Val under Bkey
 put(State,BKey,Val) ->       
     File = location(State,BKey),
     case filelib:ensure_dir(File) of
         X -> X
     end.
 
-% delete(state(), Key :: binary()) ->
-%   ok | {error, Reason :: term()}
-% key must be 160b
+%% @spec delete(state(), BKey :: riak_object:bkey()) ->
+%%          ok | {error, Reason :: term()}
+%% @doc Delete the object stored at BKey
 delete(State, BKey) ->
     File = location(State,BKey),
     case file:delete(File) of
         {error, Err} -> {error, Err}
     end.
 
-% list(state()) -> [Key :: binary()]
+%% @spec list(state()) -> [{Bucket :: riak_object:bucket(),
+%%                          Key :: riak_object:key()}]
+%% @doc Get a list of all bucket/key pairs stored by this backend
 list(State) ->
     % this is slow slow slow
     %                                              B,N,N,N,K
     [location_to_bkey(X) || X <- filelib:wildcard("*/*/*/*/*",
                                                   State#state.dir)].
 
+%% @spec list_bucket(state(), riak_object:bucket()) ->
+%%           [riak_object:key()]
+%% @doc Get a list of the keys in a bucket
 list_bucket(State, Bucket) ->
     B64 = encode_bucket(Bucket),
     L = length(State#state.dir),
                                filename:join([State#state.dir,
                                               B64,"*/*/*/*"])) ]].
 
+%% @spec location(state(), {riak_object:bucket(), riak_object:key()})
+%%          -> string()
+%% @doc produce the file-path at which the object for the given Bucket
+%%      and Key should be stored
 location(State, {Bucket, Key}) ->
     B64 = encode_bucket(Bucket),
     K64 = encode_key(Key),
     [N1,N2,N3] = nest(K64),
     filename:join([State#state.dir, B64, N1, N2, N3, K64]).
 
+%% @spec location_to_bkey(string()) ->
+%%           {riak_object:bucket(), riak_object:key()}
+%% @doc reconstruct a Riak bucket/key pair, given the location at
+%%      which its object is stored on-disk
 location_to_bkey(Path) ->
     [B64,_,_,_,K64] = string:tokens(Path, "/"),
     {decode_bucket(B64), decode_key(K64)}.
 
+%% @spec encode_bucket(atom()) -> string()
+%% @doc make a filename out of a Riak bucket
 encode_bucket(Bucket) ->
     clean(base64:encode_to_string(atom_to_list(Bucket))).
 
+%% @spec decode_bucket(string()) -> atom()
+%% @doc reconstruct a Riak bucket, given a filename
+%% @see encode_bucket/1
 decode_bucket(B64) ->
     list_to_atom(base64:decode_to_string(dirty(B64))).
 
+%% @spec encode_key(binary()) -> string()
+%% @doc make a filename out of a Riak object key
 encode_key(Key) ->
     clean(base64:encode_to_string(Key)).
 
+%% @spec decode_key(string()) -> binary()
+%% @doc reconstruct a Riak object key, given a filename
+%% @see encode_key/1
 decode_key(K64) ->
     base64:decode(dirty(K64)).
 
+%% @spec clean(string()) -> string()
+%% @doc remove characters from base64 encoding, which may
+%%      cause trouble with filenames
 clean(Str64) ->
     lists:map(fun($=) -> $-;
                  ($+) -> $_;
               end,
               Str64).
 
+%% @spec dirty(string()) -> string()
+%% @doc replace filename-troublesome base64 characters
+%% @see clean/1
 dirty(Str64) ->
     lists:map(fun($-) -> $=;
                  ($_) -> $+;
-                 ($/) -> $,;
+                 ($,) -> $/;
                  (C)  -> C
               end,
               Str64).
 
-nest([N1a,N1b,N2a,N2b,N3a,N3b|_]) ->
-    [[N1a,N1b],[N2a,N2b],[N3a,N3b]];
-nest([N2a,N2b,N3a,N3b]) ->
-    ["0",[N2a,N2b],[N3a,N3b]];
-nest([N3a,N3b]) ->
-    ["0","0",[N3a,N3b]];
-nest(_) ->
-    ["0","0","0"].
+%% @spec nest(string()) -> [string()]
+%% @doc create a directory nesting, to keep the number of
+%%      files in a directory smaller
+nest(Key) -> nest(lists:reverse(string:substr(Key, 1, 6)), 3, []).
+nest(_, 0, Parts) -> Parts;
+nest([Nb,Na|Rest],N,Acc) ->
+    nest(Rest, N-1, [[Na,Nb]|Acc]);
+nest([Na],N,Acc) ->
+    nest([],N-1,[[Na]|Acc]);
+nest([],N,Acc) ->
+    nest([],N-1,["0"|Acc]).
 
 %%
 %% Test
                         "test/fs-backend"),
     ?assertCmd("rm -rf test/fs-backend"),
     riak_test_util:standard_backend_test(riak_fs_backend).
+
+dirty_clean_test() ->
+    Dirty = "abc=+/def",
+    Clean = clean(Dirty),
+    [ ?assertNot(lists:member(C, Clean)) || C <- "=+/" ],
+    ?assertEqual(Dirty, dirty(Clean)).
+
+nest_test() ->
+    ?assertEqual(["ab","cd","ef"],nest("abcdefg")),
+    ?assertEqual(["ab","cd","ef"],nest("abcdef")),
+    ?assertEqual(["a","bc","de"], nest("abcde")),
+    ?assertEqual(["0","ab","cd"], nest("abcd")),
+    ?assertEqual(["0","a","bc"],  nest("abc")),
+    ?assertEqual(["0","0","ab"],  nest("ab")),
+    ?assertEqual(["0","0","a"],   nest("a")),
+    ?assertEqual(["0","0","0"],   nest([])).

src/riak_ring_gossiper.erl

     end.
 
 gossip_to(RemoteNode) ->
-    riak_eventer:notify(riak_ring_gossiper, send, RemoteNode),
-    {ok, MyRing} = riak_ring_manager:get_my_ring(),
-    riak_connect:cast(RemoteNode, {gossip_ring, MyRing}).
+    case lists:member(riak_ring_gossiper, registered()) of
+        false -> nop; % only gossip if we can also receive
+        true ->
+            riak_eventer:notify(riak_ring_gossiper, send, RemoteNode),
+            {ok, MyRing} = riak_ring_manager:get_my_ring(),
+            riak_connect:cast(RemoteNode, {gossip_ring, MyRing})
+    end.
 
 gossip_ring_to(RemoteNode,Ring) ->
     riak_eventer:notify(riak_ring_gossiper, send, RemoteNode),

src/riak_ring_manager.erl

 -include_lib("eunit/include/eunit.hrl").
 
 -behaviour(gen_server2).
--export([start_link/0]).
+-export([start_link/0,start_link/1,stop/0]).
 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
 	 terminate/2, code_change/3]).
 -export([get_my_ring/0,set_my_ring/1,write_ringfile/0,prune_ringfiles/0,
         read_ringfile/1,find_latest_ringfile/0]).
 
 start_link() -> gen_server2:start_link({local, ?MODULE}, ?MODULE, [], []).
-
-verify_get_test() ->
-    % not a real test, just verifying mock setup
-    {ok, R} = get_my_ring(),
-    ?assertEqual(length(riak_ring:my_indices(R)), 4).
+start_link(test) -> % when started this way, run a mock server (no disk, etc)
+    gen_server2:start_link({local, ?MODULE}, ?MODULE, [test], []).
 
 %% @private
 init([]) ->
     Ring = riak_ring:fresh(),
     ets:new(nodelocal_ring, [protected, named_table]),
     ets:insert(nodelocal_ring, {ring, Ring}),
-    {ok, stateless_server}.
+    {ok, stateless_server};
+init([test]) ->
+    Ring = riak_ring:fresh(16,node()),
+    ets:new(nodelocal_ring, [protected, named_table]),
+    ets:insert(nodelocal_ring, {ring, Ring}),
+    {ok, test}.
 
 %% @spec get_my_ring() -> {ok, riak_ring:riak_ring()} | {error, Reason}
 get_my_ring() ->
         [] -> {error, no_ring}
     end.
 %% @spec set_my_ring(riak_ring:riak_ring()) -> ok
-set_my_ring(Ring) -> gen_server2:cast(?MODULE, {set_my_ring, Ring}).
+set_my_ring(Ring) -> gen_server2:call(?MODULE, {set_my_ring, Ring}).
 %% @spec write_ringfile() -> ok
 write_ringfile() -> gen_server2:cast(?MODULE, write_ringfile).
+%% @private (only used for test instances)
+stop() -> gen_server2:cast(?MODULE, stop).
 
 %% @private
-handle_cast({set_my_ring, Ring}, State) -> 
-    ets:insert(nodelocal_ring, {ring, Ring}),
-    {noreply,State};
+handle_cast(stop, State) -> {stop,normal,State};
 
+handle_cast(write_ringfile, test) -> {noreply,test};
 handle_cast(write_ringfile, State) ->
     {ok, Ring} = get_my_ring(),
     spawn(fun() -> do_write_ringfile(Ring) end),
 handle_info(_Info, State) -> {noreply, State}.
 
 %% @private
-handle_call(_Msg,_From,State) -> {noreply, State}.
+handle_call({set_my_ring, Ring}, _From, State) -> 
+    ets:insert(nodelocal_ring, {ring, Ring}),
+    {reply,ok,State}.
 
 %% @private
 terminate(_Reason, _State) -> ok.

src/riak_test_util.erl

 -module(riak_test_util).
--export([standard_backend_test/1]).
+-export([standard_backend_test/1,setup_mockring1/0]).
 -include_lib("eunit/include/eunit.hrl").
 
 standard_backend_test(BackendMod) ->
     ?assertEqual({error, notfound}, BackendMod:get(S, {b2, <<"k2">>})),
     ?assertEqual([{b1, <<"k1">>}], BackendMod:list(S)),
     ok = BackendMod:stop(S).
+
+setup_mockring1() ->
+    % requires a running riak_ring_manager, in test-mode is ok
+    Ring0 = lists:foldl(fun(_,R) ->
+                               riak_ring:transfer_node(
+                                 hd(riak_ring:my_indices(R)),
+                                 othernode@otherhost, R) end,
+                       riak_ring:fresh(16,node()),[1,2,3,4,5,6]),
+    Ring = lists:foldl(fun(_,R) ->
+                               riak_ring:transfer_node(
+                                 hd(riak_ring:my_indices(R)),
+                                 othernode2@otherhost2, R) end,
+                       Ring0,[1,2,3,4,5,6]),
+    riak_ring_manager:set_my_ring(Ring).