exercism/go/robot-simulator/robot_simulator_step3_test.go

294 lines
7.5 KiB
Go

// +build step3 !step1,!step2
package robot
import "testing"
// Step 3 has three major changes:
//
// * Robots run scripts rather than respond to individual commands.
// * A log channel allows robots and the room to log messages.
// * The room allows multiple robots to exist and operate concurrently.
//
// Step 3 API:
//
// StartRobot3(name, script string, action chan Action3, log chan string)
// Room3(extent Rect, robots []Step3Robot, action chan Action3, report chan []Step3Robot, log chan string)
//
// Again, you define Action3.
//
// For the final position report sent from StartRobot3, you can return the same slice
// received from the robots channel, just with updated positions and directions.
//
// Messages must be sent on the log channel for
// * A robot without a name
// * Duplicate robot names
// * Robots placed at the same place
// * A robot placed outside of the room
// * An undefined command in a script
// * An action from an unknown robot
// * A robot attempting to advance into a wall
// * A robot attempting to advance into another robot
var testOneRobot = []struct {
cmd byte
Step2Robot
nMsg int
}{
// (test2 data with message counts added)
{' ', Step2Robot{N, Pos{1, 1}}, 0},
{'A', Step2Robot{N, Pos{1, 2}}, 0},
{'R', Step2Robot{E, Pos{1, 2}}, 0},
{'A', Step2Robot{E, Pos{2, 2}}, 0},
{'L', Step2Robot{N, Pos{2, 2}}, 0},
{'L', Step2Robot{W, Pos{2, 2}}, 0},
{'L', Step2Robot{S, Pos{2, 2}}, 0},
{'A', Step2Robot{S, Pos{2, 1}}, 0},
{'R', Step2Robot{W, Pos{2, 1}}, 0},
{'A', Step2Robot{W, Pos{1, 1}}, 0},
{'A', Step2Robot{W, Pos{1, 1}}, 1}, // bump W wall
{'L', Step2Robot{S, Pos{1, 1}}, 1},
{'A', Step2Robot{S, Pos{1, 1}}, 2}, // bump S wall
{'L', Step2Robot{E, Pos{1, 1}}, 2},
{'A', Step2Robot{E, Pos{2, 1}}, 2},
{'A', Step2Robot{E, Pos{2, 1}}, 3}, // bump E wall
{'L', Step2Robot{N, Pos{2, 1}}, 3},
{'A', Step2Robot{N, Pos{2, 2}}, 3},
{'A', Step2Robot{N, Pos{2, 2}}, 4}, // bump N wall
}
func logMon(log chan string, nMsg chan int, t *testing.T) {
n := 0
for msg := range log {
t.Log("Sim:", msg)
n++
}
nMsg <- n
}
// Same tests as step 2; single robot, but expect log messages on wall bumps.
func TestOneStep3(t *testing.T) {
for i := 1; i <= len(testOneRobot); i++ {
log := make(chan string)
nMsg := make(chan int)
go logMon(log, nMsg, t)
act := make(chan Action3)
rep := make(chan []Step3Robot)
go Room3(
Rect{Pos{1, 1}, Pos{2, 2}},
[]Step3Robot{{"Robbie", testOneRobot[0].Step2Robot}},
act, rep, log)
scr := ""
for j := 1; j < i; j++ {
scr += string(testOneRobot[j].cmd)
}
t.Logf("Script %q", scr)
go StartRobot3("Robbie", scr, act, log)
pls := <-rep
lastTest := testOneRobot[i-1]
if len(pls) != 1 {
t.Fatalf("Got report on %d robots, want 1.", len(pls))
}
pl := pls[0]
if pl.Name != "Robbie" {
t.Fatalf(`Got report for robot %q, want report for "Robbie".`,
pl.Name)
}
da := pl.Step2Robot
want := lastTest.Step2Robot
if da.Pos != want.Pos {
t.Fatalf("Script %q, Pos = %v, want %v", scr, da.Pos, want.Pos)
}
if da.Dir != want.Dir {
t.Fatalf("Script %q, Dir = %v, want %v", scr, da.Dir, want.Dir)
}
close(log)
if n := <-nMsg; n != lastTest.nMsg {
t.Errorf("%d sim messages logged, want %d.", n, lastTest.nMsg)
}
}
}
func TestNoName(t *testing.T) {
log := make(chan string)
nMsg := make(chan int)
go logMon(log, nMsg, t)
act := make(chan Action3)
rep := make(chan []Step3Robot)
go Room3(
Rect{Pos{1, 1}, Pos{1, 1}},
[]Step3Robot{{"", Step2Robot{N, Pos{1, 1}}}},
act, rep, log)
go StartRobot3("", "", act, log)
<-rep
close(log)
if n := <-nMsg; n != 1 {
t.Fatalf("Got %d messages, want 1.", n)
}
}
func TestSameName(t *testing.T) {
log := make(chan string)
nMsg := make(chan int)
go logMon(log, nMsg, t)
act := make(chan Action3)
rep := make(chan []Step3Robot)
go Room3(
Rect{Pos{1, 1}, Pos{2, 1}},
[]Step3Robot{
{"Daryl", Step2Robot{N, Pos{1, 1}}},
{"Daryl", Step2Robot{N, Pos{2, 1}}},
},
act, rep, log)
go StartRobot3("Daryl", "", act, log)
go StartRobot3("Daryl", "", act, log)
<-rep
close(log)
if n := <-nMsg; n != 1 {
t.Fatalf("Got %d messages, want 1.", n)
}
}
func TestSamePosition(t *testing.T) {
log := make(chan string)
nMsg := make(chan int)
go logMon(log, nMsg, t)
act := make(chan Action3)
rep := make(chan []Step3Robot)
go Room3(
Rect{Pos{1, 1}, Pos{100, 100}},
[]Step3Robot{
{"Matter", Step2Robot{N, Pos{23, 47}}},
{"Antimatter", Step2Robot{N, Pos{23, 47}}},
},
act, rep, log)
go StartRobot3("Matter", "", act, log)
go StartRobot3("Antimatter", "", act, log)
<-rep
close(log)
if n := <-nMsg; n != 1 {
t.Fatalf("Got %d messages, want 1.", n)
}
}
func TestOutsideRoom(t *testing.T) {
log := make(chan string)
nMsg := make(chan int)
go logMon(log, nMsg, t)
act := make(chan Action3)
rep := make(chan []Step3Robot)
go Room3(
Rect{Pos{1, 1}, Pos{1, 1}},
[]Step3Robot{{"Elvis", Step2Robot{N, Pos{2, 3}}}},
act, rep, log)
go StartRobot3("Elvis", "", act, log)
<-rep
close(log)
if n := <-nMsg; n != 1 {
t.Fatalf("Got %d messages, want 1.", n)
}
}
func TestBadCommand(t *testing.T) {
log := make(chan string)
nMsg := make(chan int)
go logMon(log, nMsg, t)
act := make(chan Action3)
rep := make(chan []Step3Robot)
go Room3(
Rect{Pos{0, 0}, Pos{0, 99}},
[]Step3Robot{{"Vgr", Step2Robot{N, Pos{0, 99}}}},
act, rep, log)
go StartRobot3("Vgr", "RET", act, log)
<-rep
close(log)
if n := <-nMsg; n != 1 {
t.Fatalf("Got %d messages, want 1.", n)
}
}
func TestBadRobot(t *testing.T) {
log := make(chan string)
nMsg := make(chan int)
go logMon(log, nMsg, t)
act := make(chan Action3)
rep := make(chan []Step3Robot)
go Room3(
Rect{Pos{0, 0}, Pos{0, 0}},
[]Step3Robot{{"Data", Step2Robot{N, Pos{0, 0}}}},
act, rep, log)
go StartRobot3("Lore", "A", act, log)
<-rep
close(log)
if n := <-nMsg; n != 1 {
t.Fatalf("Got %d messages, want 1.", n)
}
}
func TestThree(t *testing.T) { // no bumping
log := make(chan string)
nMsg := make(chan int)
go logMon(log, nMsg, t)
act := make(chan Action3)
go StartRobot3("clutz", "LAAARALA", act, log)
go StartRobot3("sphero", "RRAAAAALA", act, log)
go StartRobot3("roomba", "LAAARRRALLLL", act, log)
rep := make(chan []Step3Robot)
go Room3(
Rect{Pos{-10, -10}, Pos{15, 10}},
[]Step3Robot{
{"clutz", Step2Robot{N, Pos{0, 0}}},
{"sphero", Step2Robot{E, Pos{2, -7}}},
{"roomba", Step2Robot{S, Pos{8, 4}}},
},
act, rep, log)
pls := <-rep
if len(pls) != 3 {
t.Fatalf("Got report on %d robots, want 3.", len(pls))
}
exp:
for _, exp := range []Step3Robot{
{"clutz", Step2Robot{W, Pos{-4, 1}}},
{"sphero", Step2Robot{S, Pos{-3, -8}}},
{"roomba", Step2Robot{N, Pos{11, 5}}},
} {
for _, pl := range pls {
if pl.Name != exp.Name {
continue
}
if pl.Step2Robot.Pos != exp.Step2Robot.Pos {
t.Fatalf("%s at %v, want %v",
pl.Name, pl.Step2Robot.Pos, exp.Step2Robot.Pos)
}
if pl.Step2Robot.Dir != exp.Step2Robot.Dir {
t.Fatalf("%s facing %v, want %v",
pl.Name, pl.Step2Robot.Dir, exp.Step2Robot.Dir)
}
continue exp
}
t.Fatalf("Missing %s", exp.Name)
}
}
func TestBattle(t *testing.T) {
log := make(chan string)
nMsg := make(chan int)
go logMon(log, nMsg, t)
act := make(chan Action3)
go StartRobot3("Toro", "AAAAAAA", act, log)
go StartRobot3("Phere", "AAAAAAA", act, log)
rep := make(chan []Step3Robot)
go Room3(
Rect{Pos{1, 1}, Pos{9, 9}},
[]Step3Robot{
{"Toro", Step2Robot{E, Pos{1, 5}}},
{"Phere", Step2Robot{W, Pos{9, 5}}},
},
act, rep, log)
<-rep
close(log)
if n := <-nMsg; n != 7 {
t.Fatalf("Got %d messages, want 7.", n)
}
}