2024-01-02 21:39:00 +00:00
|
|
|
# Replace with your own implementation source files
|
|
|
|
IEx.Helpers.c("utils.ex", ".")
|
|
|
|
IEx.Helpers.c("eager_reliable_broadcast.ex", ".")
|
|
|
|
IEx.Helpers.c("leader_elector.ex", ".")
|
|
|
|
IEx.Helpers.c("paxos.ex", ".")
|
|
|
|
|
|
|
|
# Do not modify the following ##########
|
|
|
|
IEx.Helpers.c("test_harness.ex", ".")
|
|
|
|
IEx.Helpers.c("paxos_test.ex", ".")
|
2024-01-07 11:06:26 +00:00
|
|
|
IEx.Helpers.c("paxos_test_aditional.ex", ".")
|
2024-01-02 21:39:00 +00:00
|
|
|
IEx.Helpers.c("uuid.ex", ".")
|
|
|
|
IEx.Helpers.c("test_util.ex", ".")
|
|
|
|
|
|
|
|
host = String.trim(to_string(:os.cmd(~c"hostname -s")))
|
|
|
|
|
|
|
|
# ###########
|
|
|
|
|
|
|
|
test_suite = [
|
|
|
|
# test case, configuration, number of times to run the case, description
|
|
|
|
# Use TestUtil.get_dist_config(host, n) to generate a multi-node configuration
|
|
|
|
# consisting of n processes, each one on a different node.
|
|
|
|
# Use TestUtil.get_local_config(n) to generate a single-node configuration
|
|
|
|
# consisting of n processes, all running on the same node.
|
|
|
|
|
2024-01-16 00:04:32 +00:00
|
|
|
{&PaxosTest.run_simple/3, TestUtil.get_local_config(3), 10,
|
|
|
|
"No failures, no concurrent ballots, 3 local procs"},
|
|
|
|
{&PaxosTest.run_simple/3, TestUtil.get_dist_config(host, 3), 10,
|
|
|
|
"No failures, no concurrent ballots, 3 nodes"},
|
|
|
|
{&PaxosTest.run_simple/3, TestUtil.get_local_config(5), 10,
|
|
|
|
"No failures, no concurrent ballots, 5 local procs"},
|
|
|
|
{&PaxosTest.run_simple_2/3, TestUtil.get_dist_config(host, 3), 10,
|
|
|
|
"No failures, 2 concurrent ballots, 3 nodes"},
|
|
|
|
{&PaxosTest.run_simple_2/3, TestUtil.get_local_config(3), 10,
|
|
|
|
"No failures, 2 concurrent ballots, 3 local procs"},
|
|
|
|
{&PaxosTest.run_simple_3/3, TestUtil.get_local_config(3), 10,
|
|
|
|
"No failures, 2 concurrent instances, 3 local procs"},
|
|
|
|
{&PaxosTest.run_simple_many_1/3, TestUtil.get_dist_config(host, 5), 10,
|
|
|
|
"No failures, many concurrent ballots 1, 5 nodes"},
|
|
|
|
{&PaxosTest.run_simple_many_1/3, TestUtil.get_local_config(5), 10,
|
|
|
|
"No failures, many concurrent ballots 1, 5 local procs"},
|
|
|
|
{&PaxosTest.run_simple_many_2/3, TestUtil.get_dist_config(host, 5), 10,
|
|
|
|
"No failures, many concurrent ballots 2, 5 nodes"},
|
|
|
|
{&PaxosTest.run_simple_many_2/3, TestUtil.get_local_config(5), 10,
|
|
|
|
"No failures, many concurrent ballots 2, 5 local procs"},
|
|
|
|
{&PaxosTest.run_non_leader_crash/3, TestUtil.get_dist_config(host, 3), 10,
|
|
|
|
"One non-leader crashes, no concurrent ballots, 3 nodes"},
|
|
|
|
{&PaxosTest.run_non_leader_crash/3, TestUtil.get_local_config(3), 10,
|
|
|
|
"One non-leader crashes, no concurrent ballots, 3 local procs"},
|
|
|
|
{&PaxosTest.run_minority_non_leader_crash/3, TestUtil.get_dist_config(host, 5), 10,
|
|
|
|
"Minority non-leader crashes, no concurrent ballots"},
|
|
|
|
{&PaxosTest.run_minority_non_leader_crash/3, TestUtil.get_local_config(5), 10,
|
|
|
|
"Minority non-leader crashes, no concurrent ballots"},
|
|
|
|
{&PaxosTest.run_leader_crash_simple/3, TestUtil.get_dist_config(host, 5), 10,
|
|
|
|
"Leader crashes, no concurrent ballots, 5 nodes"},
|
|
|
|
{&PaxosTest.run_leader_crash_simple/3, TestUtil.get_local_config(5), 10,
|
|
|
|
"Leader crashes, no concurrent ballots, 5 local procs"},
|
|
|
|
{&PaxosTest.run_leader_crash_simple_2/3, TestUtil.get_dist_config(host, 7), 10,
|
|
|
|
"Leader and some non-leaders crash, no concurrent ballots, 7 nodes"},
|
|
|
|
{&PaxosTest.run_leader_crash_simple_2/3, TestUtil.get_local_config(7), 10,
|
|
|
|
"Leader and some non-leaders crash, no concurrent ballots, 7 local procs"},
|
|
|
|
{&PaxosTest.run_leader_crash_complex/3, TestUtil.get_dist_config(host, 11), 10,
|
|
|
|
"Cascading failures of leaders and non-leaders, 11 nodes"},
|
|
|
|
{&PaxosTest.run_leader_crash_complex/3, TestUtil.get_local_config(11), 10,
|
|
|
|
"Cascading failures of leaders and non-leaders, 11 local procs"},
|
|
|
|
{&PaxosTest.run_leader_crash_complex_2/3, TestUtil.get_dist_config(host, 11), 10,
|
|
|
|
"Cascading failures of leaders and non-leaders, random delays, 7 nodes"},
|
|
|
|
{&PaxosTest.run_leader_crash_complex_2/3, TestUtil.get_local_config(11), 10,
|
|
|
|
"Cascading failures of leaders and non-leaders, random delays, 7 local procs"},
|
2024-01-07 11:06:26 +00:00
|
|
|
|
2024-01-16 00:04:32 +00:00
|
|
|
# Aditional Test functions
|
2024-01-07 11:06:26 +00:00
|
|
|
|
2024-01-16 00:04:32 +00:00
|
|
|
{&PaxosTestAditional.run_leader_crash_simple_before_decision/3, TestUtil.get_dist_config(host, 5), 10,
|
|
|
|
"Leader crashes right before decision, no concurrent ballots, 5 nodes"},
|
|
|
|
{&PaxosTestAditional.run_leader_crash_simple_before_decision/3, TestUtil.get_local_config(5), 10,
|
|
|
|
"Leader crashes right before decision, no concurrent ballots, 5 local procs"},
|
|
|
|
|
|
|
|
{&PaxosTestAditional.run_non_leader_send_propose_after_leader_elected/3, TestUtil.get_dist_config(host, 5), 10,
|
|
|
|
"Non-Leader proposes after leader is elected, 5 nodes"},
|
|
|
|
{&PaxosTestAditional.run_non_leader_send_propose_after_leader_elected/3, TestUtil.get_local_config(5), 10,
|
|
|
|
"Non-Leader proposes after leader is elected, 5 local procs"},
|
|
|
|
|
2024-01-08 18:42:29 +00:00
|
|
|
|
|
|
|
{&PaxosTestAditional.run_leader_should_nack_simple/3, TestUtil.get_dist_config(host, 5), 10,
|
|
|
|
"Leader should nack before decision and then come to decision, no concurrent ballots, 5 nodes"},
|
|
|
|
{&PaxosTestAditional.run_leader_should_nack_simple/3, TestUtil.get_local_config(5), 10,
|
|
|
|
"Leader should nack before decision and then come to decision, 5 local procs"},
|
|
|
|
|
|
|
|
{&PaxosTestAditional.run_non_leader_should_nack_simple/3, TestUtil.get_dist_config(host, 5), 10,
|
|
|
|
"Non-Leader should nack before decision and then come to decision, no concurrent ballots, 5 nodes"},
|
|
|
|
{&PaxosTestAditional.run_non_leader_should_nack_simple/3, TestUtil.get_local_config(5), 10,
|
|
|
|
"Non-Leader should nack before decision and then come to decision, 5 local procs"},
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-01-02 21:39:00 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
Node.stop()
|
|
|
|
# Confusingly, Node.start fails if epmd is not running.
|
|
|
|
# epmd can be started manually with "epmd -daemon" or
|
|
|
|
# will start automatically whenever any Erlang VM is
|
|
|
|
# started with --sname or --name option.
|
|
|
|
Node.start(TestUtil.get_node(host), :shortnames)
|
|
|
|
|
|
|
|
Enum.reduce(test_suite, length(test_suite), fn {func, config, n, doc}, acc ->
|
|
|
|
IO.puts(:stderr, "============")
|
|
|
|
IO.puts(:stderr, "#{inspect(doc)}, #{inspect(n)} time#{if n > 1, do: "s", else: ""}")
|
|
|
|
IO.puts(:stderr, "============")
|
|
|
|
|
|
|
|
for _ <- 1..n do
|
|
|
|
res = TestHarness.test(func, Enum.shuffle(Map.to_list(config)))
|
|
|
|
# IO.puts("#{inspect res}")
|
|
|
|
{vl, al, ll} =
|
|
|
|
Enum.reduce(res, {[], [], []}, fn
|
|
|
|
{_, _, s, v, a, {:message_queue_len, l}}, {vl, al, ll} ->
|
|
|
|
# if s not in [:killed, :none], do: {[v | vl], [a | al], [l | ll]},
|
|
|
|
if s not in [:killed],
|
|
|
|
do: {[v | vl], [a | al], [l | ll]},
|
|
|
|
else: {vl, al, ll}
|
|
|
|
|
|
|
|
{_, _, _, _, _, nil}, {vl, al, ll} ->
|
|
|
|
{vl, al, ll}
|
|
|
|
end)
|
|
|
|
|
|
|
|
IO.puts("#{inspect(vl)}")
|
|
|
|
termination = vl != [] and :none not in vl
|
|
|
|
agreement = termination and MapSet.size(MapSet.new(vl)) == 1
|
|
|
|
{:val, agreement_val} = if agreement, do: hd(vl), else: {:val, -1}
|
|
|
|
validity = agreement_val in 201..210
|
|
|
|
safety = agreement and validity
|
|
|
|
TestUtil.pause_stderr(100)
|
|
|
|
|
|
|
|
if termination and safety do
|
|
|
|
too_many_attempts = (get_att = fn a -> 10 - a + 1 end).(Enum.max(al)) > 5
|
|
|
|
too_many_messages_left = Enum.max(ll) > 10
|
|
|
|
warn = if too_many_attempts, do: [{:too_many_attempts, get_att.(Enum.max(al))}], else: []
|
|
|
|
|
|
|
|
warn =
|
|
|
|
if too_many_messages_left,
|
|
|
|
do: [{:too_many_messages_left, Enum.max(ll)} | warn],
|
|
|
|
else: warn
|
|
|
|
|
|
|
|
IO.puts(:stderr, if(warn == [], do: "PASS", else: "PASS (#{inspect(warn)})"))
|
|
|
|
# IO.puts(:stderr, "#{inspect res}")
|
|
|
|
else
|
|
|
|
IO.puts(:stderr, "FAIL\n\t#{inspect(res)}")
|
2024-01-07 11:06:26 +00:00
|
|
|
|
|
|
|
# Stop tests on a fail
|
|
|
|
Process.exit(self(), :kill)
|
2024-01-02 21:39:00 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
IO.puts(:stderr, "============#{if acc > 1, do: "\n", else: ""}")
|
|
|
|
acc - 1
|
|
|
|
end)
|
|
|
|
|
|
|
|
:os.cmd(~c"/bin/rm -f *.beam")
|
|
|
|
Node.stop()
|
|
|
|
System.halt()
|