this repo has no description
1defmodule TrinityTest do
2 use ExUnit.Case
3
4 alias Trinity.{Sim, SimProcess, SimPersistentTerm, SimLogger, Scheduler}
5 import Trinity.Scheduler, only: [receive_yield: 1]
6 require SimLogger
7
8 defmodule Counter do
9 use GenServer
10 alias Trinity.{SimServer, SimFile}
11
12 def start_link(id, initial_count) do
13 SimServer.start_link(__MODULE__, %{id: id, initial_count: initial_count})
14 end
15
16 def add(server, amount) do
17 SimServer.call(server, {:add, amount})
18 end
19
20 def init(%{id: id, initial_count: initial_count}) do
21 assert SimFile.exists?("/counters/#{id}/") == false
22 :ok = SimFile.mkdir_p("/counters/#{id}/")
23 assert SimFile.exists?("/counters/#{id}") == true
24 assert SimFile.exists?("/counters/#{id}/") == true
25
26 {:ok, fd} = SimFile.open("/counters/#{id}/#{id}.count", [:read, :write])
27 assert SimFile.ls("/counters/#{id}/") == {:ok, ["#{id}.count"]}
28
29 SimLogger.debug "Init (id=#{id}, fd=#{fd}, initial_count=#{initial_count})"
30
31 state = %{
32 fd: fd,
33 size: nil,
34 }
35 state = write_value(state, initial_count)
36 SimLogger.debug "Initial state (id=#{id}): #{inspect(state)}"
37
38 :ok = SimPersistentTerm.put("counter-#{id}", initial_count)
39 term = SimPersistentTerm.get("counter-#{id}", nil)
40 SimLogger.debug "Persistent term: #{term}"
41
42 # Note: this intentionally tests a log message with no variables
43 SimLogger.debug "Init complete"
44 {:ok, state}
45 end
46
47 def handle_call({:add, amount}, _from, state) do
48 value = read_value(state)
49 value = value + amount
50 state = write_value(state, value)
51 {:reply, value, state}
52 end
53
54 defp read_value(%{fd: fd, size: size}) do
55 {:ok, data} = SimFile.pread(fd, 3, size)
56 "The current value of the counter is: " <> value_str = data
57 String.to_integer(value_str)
58 end
59
60 defp write_value(%{fd: fd} = state, value) do
61 data = "The current value of the counter is: " <> Integer.to_string(value)
62 SimFile.pwrite(fd, 3, data)
63 %{state | size: byte_size(data)}
64 end
65 end
66
67 defmodule CounterSupervisor do
68 use GenServer
69 alias Trinity.SimServer
70
71 def start_link(children, opts), do: SimServer.start_link(__MODULE__, children, opts)
72 def get_children(server), do: SimServer.call(server, :get_children)
73
74 def init(children) do
75 pids = Enum.map(children, fn {m, f, a} ->
76 {:ok, pid} = apply(m, f, a)
77 pid
78 end)
79 {:ok, pids}
80 end
81
82 def handle_call(:get_children, _from, pids) do
83 {:reply, pids, pids}
84 end
85 end
86
87 test "scheduler" do
88 message = Sim.run_simulation(fn ->
89 nodes = [:n1, :n2, :n3]
90
91 names = Enum.map(nodes, fn node ->
92 name = String.to_atom(Atom.to_string(node) <> "_proc")
93
94 children = Enum.map(1..10, fn i ->
95 {TrinityTest.Counter, :start_link, [i, i]}
96 end)
97
98 node_mfa = {
99 TrinityTest.CounterSupervisor,
100 :start_link,
101 [children, [name: name]],
102 }
103 Sim.create_node(node, node_mfa)
104 Sim.start_node(node)
105 {name, node}
106 end)
107
108 # Wait for nodes to start/register
109 SimProcess.sleep(100)
110
111 pids =
112 names
113 |> Enum.map(fn name ->
114 CounterSupervisor.get_children(name)
115 |> Enum.with_index(1)
116 end)
117 |> Enum.concat()
118
119 Enum.each(pids, fn {pid, id} ->
120 result = Counter.add(pid, 10)
121 assert result == (id + 10)
122 end)
123
124 SimProcess.send_after self(), :finish, 1000
125 receive_yield do
126 :finish -> :noop
127 end
128
129 Scheduler.yield(1000)
130 #dbg Scheduler.dump(), limit: :infinity
131 :success
132
133 hash = SimLogger.get_hash()
134 tail = SimLogger.log_tail(10)
135 """
136 Simulation complete.
137
138 Hash: #{hash}
139
140 Log tail:
141 #{Enum.join(tail, "\n")}
142 """
143 end, seed: 101)
144
145 require Logger
146 Logger.info(message)
147 end
148end