exercism/go/kindergarten-garden/kindergarten_garden_test.go

212 lines
6.2 KiB
Go

// Kindergarten garden
//
// You must define a type Garden with constructor
//
// func NewGarden(diagram string, children []string) (*Garden, error)
//
// and method
//
// func (g *Garden) Plants(child string) ([]string, bool)
//
// The diagram argument starts each row with a '\n'. This allows Go's
// raw string literals to present diagrams in source code nicely as two
// rows flush left, for example,
//
// diagram := `
// VVCCGG
// VVCCGG`
package kindergarten
import (
"reflect"
"sort"
"testing"
)
type lookup struct {
child string
plants []string
ok bool
}
type gardenTest struct {
number int
diagram string
children []string
ok bool
lookups []lookup
}
var tests = []gardenTest{
{1, `
RC
GG`, []string{"Alice"}, true, []lookup{
{"Alice", []string{"radishes", "clover", "grass", "grass"}, true},
}},
{2, `
VC
RC`, []string{"Alice"}, true, []lookup{
{"Alice", []string{"violets", "clover", "radishes", "clover"}, true},
}},
{3, `
VVCG
VVRC`, []string{"Alice", "Bob"}, true, []lookup{
{"Bob", []string{"clover", "grass", "radishes", "clover"}, true},
}},
{4, `
VVCCGG
VVCCGG`, []string{"Alice", "Bob", "Charlie"}, true, []lookup{
{"Bob", []string{"clover", "clover", "clover", "clover"}, true},
{"Charlie", []string{"grass", "grass", "grass", "grass"}, true},
}},
test5, // full garden test
test6, // out of order names test
// failure tests
{7, "RC\nGG", []string{"Alice"}, false, nil}, // wrong diagram format
{8, `
RCCC
GG`, []string{""}, false, nil}, // mismatched rows
{9, `
RCC
GGC`, []string{"Alice"}, false, nil}, // odd number of cups
{10, `
RCCC
GGCC`, []string{"Alice", "Alice"}, false, nil}, // duplicate name
{11, `
rc
gg`, []string{"Alice"}, false, nil}, // invaid cup codes
{12, `
RC
GG`, []string{"Alice"}, true, []lookup{ // lookup invalid name
{"Bob", []string{"radishes", "clover", "grass", "grass"}, false},
}},
}
// full garden test
var test5 = gardenTest{5, `
VRCGVVRVCGGCCGVRGCVCGCGV
VRCCCGCRRGVCGCRVVCVGCGCV`, []string{
"Alice", "Bob", "Charlie", "David", "Eve", "Fred",
"Ginny", "Harriet", "Ileana", "Joseph", "Kincaid", "Larry"}, true, []lookup{
{"Alice", []string{"violets", "radishes", "violets", "radishes"}, true},
{"Bob", []string{"clover", "grass", "clover", "clover"}, true},
{"Charlie", []string{"violets", "violets", "clover", "grass"}, true},
{"David", []string{"radishes", "violets", "clover", "radishes"}, true},
{"Eve", []string{"clover", "grass", "radishes", "grass"}, true},
{"Fred", []string{"grass", "clover", "violets", "clover"}, true},
{"Ginny", []string{"clover", "grass", "grass", "clover"}, true},
{"Harriet", []string{"violets", "radishes", "radishes", "violets"}, true},
{"Ileana", []string{"grass", "clover", "violets", "clover"}, true},
{"Joseph", []string{"violets", "clover", "violets", "grass"}, true},
{"Kincaid", []string{"grass", "clover", "clover", "grass"}, true},
{"Larry", []string{"grass", "violets", "clover", "violets"}, true},
}}
// out of order names test
var (
test6names = []string{"Samantha", "Patricia", "Xander", "Roger"}
test6 = gardenTest{6, `
VCRRGVRG
RVGCCGCV`, append([]string{}, test6names...), true, []lookup{
{"Patricia", []string{"violets", "clover", "radishes", "violets"}, true},
{"Roger", []string{"radishes", "radishes", "grass", "clover"}, true},
{"Samantha", []string{"grass", "violets", "clover", "grass"}, true},
{"Xander", []string{"radishes", "grass", "clover", "violets"}, true},
}}
)
func TestGarden(t *testing.T) {
for _, test := range tests {
g, err := NewGarden(test.diagram, test.children)
switch {
case err != nil:
if test.ok {
t.Fatalf("NewGarden test %d returned error %q. Error not expected.",
test.number, err)
}
case !test.ok:
t.Fatalf("NewGarden test %d error = %v. Expected error.",
test.number, err)
}
for _, l := range test.lookups {
switch plants, ok := g.Plants(l.child); {
case ok != l.ok:
t.Fatalf("Garden %d lookup %s returned ok = %t, want %t.",
test.number, l.child, ok, l.ok)
case ok && !reflect.DeepEqual(plants, l.plants):
t.Fatalf("Garden %d lookup %s = %v, want %v.",
test.number, l.child, plants, l.plants)
}
}
}
}
// The lazy way to meet the alphabetizing requirement is with sort.Strings
// on the argument slice. That's an in-place sort though and it's bad practice
// to have a side effect.
func TestNamesNotModified(t *testing.T) {
cp := append([]string{}, test6names...)
_, err := NewGarden(test6.diagram, cp)
if err != nil {
t.Skip("TestNamesNotModified requires valid garden")
}
if !reflect.DeepEqual(cp, test6names) {
t.Fatalf("NewGarden modified children argment. " +
"Arguments should not be modified.")
}
sort.Strings(cp)
if reflect.DeepEqual(cp, test6names) {
t.Skip("TestNamesNotModified requires names out of order")
}
}
// A test taken from the Ruby tests. It checks that Garden objects
// are self-contained and do not rely on package variables.
func TestTwoGardens(t *testing.T) {
diagram := `
VCRRGVRG
RVGCCGCV`
g1, err1 := NewGarden(diagram, []string{"Alice", "Bob", "Charlie", "Dan"})
g2, err2 := NewGarden(diagram, []string{"Bob", "Charlie", "Dan", "Erin"})
if err1 != nil || err2 != nil {
t.Skip("Two garden test needs valid gardens")
}
tf := func(g *Garden, n int, child string, expPlants []string) {
switch plants, ok := g.Plants(child); {
case !ok:
t.Skip("Garden %d lookup %s returned ok = false, want true.",
n, child)
case !reflect.DeepEqual(plants, expPlants):
t.Fatalf("Garden %d lookup %s = %v, want %v.",
n, child, plants, expPlants)
}
}
tf(g1, 1, "Bob", []string{"radishes", "radishes", "grass", "clover"})
tf(g2, 2, "Bob", []string{"violets", "clover", "radishes", "violets"})
tf(g1, 1, "Charlie", []string{"grass", "violets", "clover", "grass"})
tf(g2, 2, "Charlie", []string{"radishes", "radishes", "grass", "clover"})
}
func BenchmarkNewGarden(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, test := range tests {
NewGarden(test.diagram, test.children)
}
}
}
func BenchmarkGarden_Plants(b *testing.B) {
g, err := NewGarden(test5.diagram, test5.children)
if err != nil {
b.Skip("BenchmarkGarden_Plants requires valid garden")
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, l := range test5.lookups {
g.Plants(l.child)
}
}
}