From a096774fa82598dcc67d31f78b43ef4475b9f8c9 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sun, 8 Dec 2019 14:57:25 -0500 Subject: [PATCH] Add intcode VM tests --- lib/day5/day5.ex | 8 +-- test/intcode/vm_test.exs | 120 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 test/intcode/vm_test.exs diff --git a/lib/day5/day5.ex b/lib/day5/day5.ex index 62d4b16..4433f57 100644 --- a/lib/day5/day5.ex +++ b/lib/day5/day5.ex @@ -98,8 +98,8 @@ defmodule Day5 do # IO.inspect(memory) case eval(Enum.drop(memory, ip), memory, parent) do - {_memory, :halt} -> - send(parent, {:halt, self()}) + {memory, :halt} -> + send(parent, {:halt, self(), memory}) {memory, :cont, offset} -> run(memory, parent, ip + offset) @@ -109,8 +109,8 @@ defmodule Day5 do end end - def run(_memory, parent, _) do - send(parent, {:ok, self()}) + def run(memory, parent, _) do + send(parent, {:ok, self(), memory}) end defmacro opcode(op, expected) do diff --git a/test/intcode/vm_test.exs b/test/intcode/vm_test.exs new file mode 100644 index 0000000..e132b5e --- /dev/null +++ b/test/intcode/vm_test.exs @@ -0,0 +1,120 @@ +defmodule VMTest do + use ExUnit.Case + doctest Day5 + + def run(prog, io \\ []) do + parent = self() + + pid = + spawn(fn -> + Day5.run(prog, parent) + end) + + handle_io(pid, io) + + receive do + {:halt, ^pid, memory} -> + {:halt, memory} + + {:ok, ^pid, memory} -> + {:ok, memory} + + msg -> + IO.inspect(msg) + end + end + + def handle_io(_, []), do: :ok + + def handle_io(pid, [head | rest]) do + case head do + {:in, input} -> + receive do + {:in, ^pid} -> send(pid, {:in, input}) + end + + {:out, expected} -> + receive do + {:out, ^pid, output} -> assert output == expected + end + end + + handle_io(pid, rest) + end + + test "exits" do + assert run([]) == {:ok, []} + end + + test "halts" do + assert run([99]) == {:halt, [99]} + end + + test "basic position mode instructions" do + assert run([1, 3, 2, 3]) == {:ok, [1, 3, 2, 5]} + assert run([2, 3, 2, 3]) == {:ok, [2, 3, 2, 6]} + end + + test "basic immediate mode instructions" do + assert run([1101, 1, 2, 0]) == {:ok, [3, 1, 2, 0]} + assert run([1102, 4, 2, 0]) == {:ok, [8, 4, 2, 0]} + end + + test "receives input" do + assert run([3, 1], in: 1234) == {:ok, [3, 1234]} + end + + test "produces output" do + assert run([104, 77], out: 77) == {:ok, [104, 77]} + assert run([4, 0], out: 4) == {:ok, [4, 0]} + end + + test "jump if non-zero" do + prog = [1105, 1, 4, 99, 1101, 1, 2, 7] + assert run(prog) == {:ok, List.replace_at(prog, 7, 3)} + prog = [1105, 0, 4, 99, 1101, 1, 2, 7] + assert run(prog) == {:halt, prog} + + prog = [1005, 0, 4, 99, 1101, 1, 2, 7] + assert run(prog) == {:ok, List.replace_at(prog, 7, 3)} + + prog = [5, 0, 9, 99, 1101, 1, 2, 7, 99, 4] + assert run(prog) == {:halt, List.replace_at(prog, 7, 3)} + end + + test "jump if zero" do + prog = [1106, 0, 4, 99, 1101, 1, 2, 7] + assert run(prog) == {:ok, List.replace_at(prog, 7, 3)} + prog = [1106, 42, 4, 99, 1101, 1, 2, 7] + assert run(prog) == {:halt, prog} + + prog = [1006, 9, 4, 99, 1101, 1, 2, 7, 99, 0] + assert run(prog) == {:halt, List.replace_at(prog, 7, 3)} + prog = [6, 9, 10, 99, 1101, 1, 2, 7, 99, 0, 4] + assert run(prog) == {:halt, List.replace_at(prog, 7, 3)} + end + + test "compare less than" do + prog = [1107, 3, 8, 3] + assert run(prog) == {:ok, List.replace_at(prog, 3, 1)} + prog = [1107, 12, 8, 3] + assert run(prog) == {:ok, List.replace_at(prog, 3, 0)} + + prog = [1007, 3, 8, 3] + assert run(prog) == {:ok, List.replace_at(prog, 3, 1)} + prog = [7, 3, 0, 3] + assert run(prog) == {:ok, List.replace_at(prog, 3, 1)} + end + + test "compare equal to" do + prog = [1108, 4, 4, 3] + assert run(prog) == {:ok, List.replace_at(prog, 3, 1)} + prog = [1108, 5, 4, 3] + assert run(prog) == {:ok, List.replace_at(prog, 3, 0)} + + prog = [1008, 0, 1008, 3] + assert run(prog) == {:ok, List.replace_at(prog, 3, 1)} + prog = [8, 2, 2, 3] + assert run(prog) == {:ok, List.replace_at(prog, 3, 1)} + end +end