@doc riak_test is a integration/system testing driver for Riak that runs tests written in Erlang. You write the tests, and riak_test handles bringing up a Riak cluster and running your tests against it. riak_test can generate a cluster directly from a devrel build of a Riak source tree. The devrel mode operates against a generated devrel that has been turned into a git repo, allowing riak_test to easily reset all nodes by reverting the git repo back to the initial commit. This is the preferred mode of operation as it is easy to setup and can easily test a work-in-progress development tree during implementation and code/automation review. The following is an example of a riak_test test:
``` -module(verify_build_cluster). -compile([confirm/0]). -include_lib("eunit/include/eunit.hrl"). -import(rt, [deploy_nodes/1, owners_according_to/1, join/2, wait_until_nodes_ready/1, wait_until_no_pending_changes/1]). confirm() -> %% Deploy a set of new nodes lager:info("Deploying 3 nodes"), Nodes = rt:deploy_nodes(3), %% Ensure each node owns 100% of it's own ring lager:info("Ensure each nodes 100% of it's own ring"), [?assertEqual([Node], owners_according_to(Node)) || Node <- Nodes], %% Join nodes lager:info("Join nodes together"), [Node1|OtherNodes] = Nodes, [join(Node, Node1) || Node <- OtherNodes], lager:info("Wait until all nodes are ready and there are no pending changes"), ?assertEqual(ok, wait_until_nodes_ready(Nodes)), ?assertEqual(ok, wait_until_no_pending_changes(Nodes)), %% Ensure each node owns a portion of the ring lager:info("Ensure each node owns a portion of the ring"), [?assertEqual(Nodes, owners_according_to(Node)) || Node <- Nodes], lager:info("verify_build_cluster: PASS"), pass. '''Running the test in devrel mode gives the following output:
``` 08:56:50.474 [info] Resetting nodes to fresh state 08:56:50.474 [debug] Running: git --git-dir="~/rt/riak/.git" --work-tree="~/rt/riak/" reset HEAD --hard 08:56:52.522 [debug] Running: git --git-dir="~/rt/riak/.git" --work-tree="~/rt/riak/" clean -fd 08:56:52.718 [notice] Running Test verify_build_cluster 08:56:52.719 [info] rt:set_backend(riak_kv_bitcask_backend) 08:56:52.719 [info] rtdev:set_backend(riak_kv_bitcask_backend) 08:56:52.719 [info] rtdev:update_app_config(all, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.720 [info] rtdev:update_app_config_file(~/rt/riak/current/dev/dev1/etc/app.config, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.726 [info] rtdev:update_app_config_file(~/rt/riak/current/dev/dev2/etc/app.config, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.731 [info] rtdev:update_app_config_file(~/rt/riak/current/dev/dev3/etc/app.config, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.737 [info] rtdev:update_app_config_file(~/rt/riak/current/dev/dev4/etc/app.config, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.743 [info] rtdev:update_app_config_file(~/rt/riak/riak-1.2.0/dev/dev1/etc/app.config, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.749 [info] rtdev:update_app_config_file(~/rt/riak/riak-1.2.0/dev/dev2/etc/app.config, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.755 [info] rtdev:update_app_config_file(~/rt/riak/riak-1.2.0/dev/dev3/etc/app.config, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.760 [info] rtdev:update_app_config_file(~/rt/riak/riak-1.2.0/dev/dev4/etc/app.config, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.766 [info] rtdev:update_app_config_file(~/rt/riak/riak-1.1.4/dev/dev1/etc/app.config, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.772 [info] rtdev:update_app_config_file(~/rt/riak/riak-1.1.4/dev/dev2/etc/app.config, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.780 [info] rtdev:update_app_config_file(~/rt/riak/riak-1.1.4/dev/dev3/etc/app.config, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.787 [info] rtdev:update_app_config_file(~/rt/riak/riak-1.1.4/dev/dev4/etc/app.config, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.794 [info] rtdev:update_app_config_file(~/rt/riak/riak-1.0.3/dev/dev1/etc/app.config, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.800 [info] rtdev:update_app_config_file(~/rt/riak/riak-1.0.3/dev/dev2/etc/app.config, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.805 [info] rtdev:update_app_config_file(~/rt/riak/riak-1.0.3/dev/dev3/etc/app.config, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.811 [info] rtdev:update_app_config_file(~/rt/riak/riak-1.0.3/dev/dev4/etc/app.config, [{riak_kv,[{storage_backend,riak_kv_bitcask_backend}]}]) 08:56:52.850 [info] Deploying 3 nodes 08:56:52.850 [info] Riak path: "~/rt/riak" 08:56:52.887 [info] Running: ~/rt/riak/current/dev/dev1/bin/riak start 08:56:52.887 [info] Running: ~/rt/riak/current/dev/dev2/bin/riak start 08:56:52.887 [info] Running: ~/rt/riak/current/dev/dev3/bin/riak start 08:56:58.528 [debug] Supervisor inet_gethost_native_sup started undefined at pid <0.142.0> 08:56:58.528 [debug] Supervisor kernel_safe_sup started inet_gethost_native:start_link() at pid <0.141.0> 08:56:58.570 [info] Deployed nodes: ['dev1@127.0.0.1','dev2@127.0.0.1','dev3@127.0.0.1'] 08:56:58.570 [info] Ensure each nodes 100% of it's own ring 08:56:58.572 [info] Join nodes together 08:56:58.601 [debug] [join] 'dev2@127.0.0.1' to ('dev1@127.0.0.1'): ok 08:56:58.646 [debug] [join] 'dev3@127.0.0.1' to ('dev1@127.0.0.1'): ok 08:56:58.646 [info] Wait until all nodes are ready and there are no pending changes 08:57:07.245 [info] Ensure each node owns a portion of the ring 08:57:07.249 [info] verify_build_cluster: PASS 08:57:07.249 [notice] verify_build_cluster Test Run Complete '''As shown, the tests look similar to standard eunit tests and are straightforward Erlang. The {@link rt} module provides the basic commands to control Riak nodes, and abstracts everything away from the underlying harness: devrel. The `rt' module also provides a set of useful existing built-in operations. It is expected that the `rt' module will be extended over time with more reusable functions. Writing additional checks and preconditions is the same as with an eunit test. For example, `rt:owners_according_to(Node)' is just a basic RPC call:
``` owners_according_to(Node) -> {ok, Ring} = rpc:call(Node, riak_core_ring_manager, get_raw_ring, []), Owners = [Owner || {_Idx, Owner} <- riak_core_ring:all_owners(Ring)], lists:usort(Owners). '''And the `rt:wait_until_nodes_ready', which leverages the built-in `wait_until' primitive:
``` wait_until_nodes_ready(Nodes) -> [?assertEqual(ok, wait_until(Node, fun is_ready/1)) || Node <- Nodes], ok. is_ready(Node) -> case rpc:call(Node, riak_core_ring_manager, get_raw_ring, []) of {ok, Ring} -> lists:member(Node, riak_core_ring:ready_members(Ring)); _ -> false end. '''Erlang term based config files. The references `rtdev.config' file above: ``` %% Deps should include the path to compiled Riak libs {rt_deps, ["/Users/jtuple/basho/riak/deps"]}. %% Maximum time in milliseconds for wait_until to wait {rt_max_wait_time, 180000}. %% Delay between each retry in wait_until {rt_retry_delay, 500}. %% Use the devrel harness {rt_harness, rtdev}. %% Path to generated devrel of Riak that is git-versioned {rtdev_path, "~/rt/riak"}. ''' And to conclude, a slightly longer test that uses most of the built-in primitives including starting and stopping Riak nodes:
``` -module(verify_claimant). confirm() -> Nodes = build_cluster(3), [Node1, Node2, _Node3] = Nodes, %% Ensure all nodes believe node1 is the claimant lager:info("Ensure all nodes believe ~p is the claimant", [Node1]), [?assertEqual(Node1, claimant_according_to(Node)) || Node <- Nodes], %% Stop node1 lager:info("Stop ~p", [Node1]), stop(Node1), ?assertEqual(ok, wait_until_unpingable(Node1)), %% Ensure all nodes still believe node1 is the claimant lager:info("Ensure all nodes still believe ~p is the claimant", [Node1]), Remaining = Nodes -- [Node1], [?assertEqual(Node1, claimant_according_to(Node)) || Node <- Remaining], %% Mark node1 as down and wait for ring convergence lager:info("Mark ~p as down", [Node1]), down(Node2, Node1), ?assertEqual(ok, wait_until_ring_converged(Remaining)), [?assertEqual(down, status_of_according_to(Node1, Node)) || Node <- Remaining], %% Ensure all nodes now believe node2 to be the claimant lager:info("Ensure all nodes now believe ~p is the claimant", [Node2]), [?assertEqual(Node2, claimant_according_to(Node)) || Node <- Remaining], %% Restart node1 and wait for ring convergence lager:info("Restart ~p and wait for ring convergence", [Node1]), start(Node1), ?assertEqual(ok, wait_until_nodes_ready([Node1])), ?assertEqual(ok, wait_until_ring_converged(Nodes)), %% Ensure node has rejoined and is no longer down lager:info("Ensure ~p has rejoined and is no longer down", [Node1]), [?assertEqual(valid, status_of_according_to(Node1, Node)) || Node <- Nodes], %% Ensure all nodes still believe node2 is the claimant lager:info("Ensure all nodes still believe ~p is the claimant", [Node2]), [?assertEqual(Node2, claimant_according_to(Node)) || Node <- Nodes], pass. '''