-
Notifications
You must be signed in to change notification settings - Fork 0
/
exercise.exs
158 lines (118 loc) · 3.54 KB
/
exercise.exs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
ExUnit.start
defmodule Exercise do
# Write a module called PagedOutput. Its job is to accept
# lines of text and write them to a device. After every 5
# lines, it will start a new page of output. There will
# only be one PagedOutput on a node, so it should register
# itself by name.
#
# The `device` is passed in as a module. This module implements
# two functions, `write` and `new_page`. You don't need to worry
# about the implementation of this.
#
# The API will be:
#
# - start_link(device)
# initialize the PagedOutput module. This will create a background
# agent, and store the device and the current line number in it.
#
# - puts(line)
# write the line to the device by calling `device.write(line)`.
# If this is the 50th line on the current page, call
# `device.new_page`. Returns the number of lines left
# on the current page
#
# PagedOutput will be implemented using an Agent.
####################
# start of your code #
####################
defmodule PagedOutput do
def start_link(device) do
end
def puts(line) do
end
end
####################
# end of your code #
####################
use ExUnit.Case
defmodule MockDevice do
@me __MODULE__
use GenServer
def start_link do
GenServer.start_link(__MODULE__, [], name: @me)
end
def write(line) do
GenServer.cast(@me, {:add, line})
end
def new_page() do
GenServer.cast(@me, {:add, :np})
end
def lines do
GenServer.call(@me, {:lines})
end
def init(_) do
{ :ok, [] }
end
def handle_cast({:add, line}, lines) do
{ :noreply, [ line | lines ] }
end
def handle_call({:lines}, _, lines) do
{ :reply, Enum.reverse(lines), lines }
end
end
def setup do
maybe_kill(MockDevice)
maybe_kill(PagedOutput)
MockDevice.start_link
PagedOutput.start_link(MockDevice)
end
def maybe_kill(mod) do
pid = Process.whereis(mod)
if pid, do: GenServer.stop(mod)
end
test "puts" do
setup
PagedOutput.puts :l1
PagedOutput.puts :l2
assert MockDevice.lines == [ :l1, :l2 ]
end
test "pages after 5" do
setup
[ :l1, :l2, :l3, :l4, :l5 ] |> Enum.each(&PagedOutput.puts/1)
assert MockDevice.lines == [ :l1, :l2, :l3, :l4, :l5, :np ]
end
test "pages after 5 with extra lines" do
setup
[ :l1, :l2, :l3, :l4, :l5, :l6, :l7 ] |> Enum.each(&PagedOutput.puts/1)
assert MockDevice.lines == [ :l1, :l2, :l3, :l4, :l5, :np, :l6, :l7 ]
end
test "pages 1000 lines" do
setup
1..1000 |> Enum.each(fn _ -> PagedOutput.puts(:l) end)
lines = MockDevice.lines
assert length(lines) == 1000 + div(1000, 5)
pages = Enum.chunk(lines, 6)
assert length(pages) == 200
pages |> Enum.each(fn page ->
assert page == [ :l, :l, :l, :l, :l, :np ]
end)
end
def write_100_lines(n) do
1..100 |> Enum.each(fn _ -> PagedOutput.puts(n) end)
end
test "parallel access" do
setup
1..100
|> Enum.map(&Task.async(fn -> write_100_lines(&1) end))
|> Enum.each(&Task.await/1)
line_count = 100*100
lines = MockDevice.lines
assert length(lines) == line_count + div(line_count, 5)
pages = Enum.chunk(lines, 6)
assert length(pages) == div(line_count, 5)
pages |> Enum.each(fn page ->
assert [ _, _, _, _, _, :np ] = page
end)
end
end