Skip to content

Commit

Permalink
word-search: create test case generator (exercism#871)
Browse files Browse the repository at this point in the history
Add .meta/gen.go to generate cases_test.go.

Update test program to use generated test case array.
Put FAIL and PASS in test result output.

For exercism#605.
  • Loading branch information
leenipper authored and robphoenix committed Oct 10, 2017
1 parent 81461ce commit 7bb5933
Show file tree
Hide file tree
Showing 3 changed files with 266 additions and 41 deletions.
96 changes: 96 additions & 0 deletions exercises/word-search/.meta/gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package main

import (
"log"
"text/template"

"../../../gen"
)

func main() {
t, err := template.New("").Parse(tmpl)
if err != nil {
log.Fatal(err)
}
var j js
if err := gen.Gen("word-search", &j, t); err != nil {
log.Fatal(err)
}
}

// The JSON structure we expect to be able to unmarshal into
type js struct {
Exercise string
Version string
Comments []string
Cases []OneCase
}

// template applied to above data structure generates the Go test cases

type Int int

type Coordinates struct {
Column Int
Row Int
}
type Position struct {
Start Coordinates
End Coordinates
}

type OneCase struct {
Description string
Property string
Grid []string
WordsToSearchFor []string
Expected map[string]Position
}

func (p Position) NullPosition() bool {
if p.Start.Column == 0 && p.Start.Row == 0 &&
p.End.Column == 0 && p.End.Row == 0 {
return true
}
return false
}

func (c OneCase) ErrorExpected() bool {
// When any of the word positions have an null position, expect an error.
for _, p := range c.Expected {
if p.NullPosition() {
return true
}
}
return false
}

func (v Int) Minus1() int {
return int(v) - 1
}

// Template to generate test cases

var tmpl = `package wordsearch
{{.Header}}
var testCases = []struct {
description string
puzzle []string // puzzle strings
words []string // words to search for
expected map[string][2][2]int // expected coordinates
expectError bool
}{ {{range .J.Cases}}
{
{{printf "%q" .Description}},
{{printf "%#v" .Grid}},
{{printf "%#v" .WordsToSearchFor}},
{{if .ErrorExpected}} map[string][2][2]int{ },
true,
{{else}} map[string][2][2]int{ {{ range $key, $value := .Expected }} "{{ $key }}": { { {{ $value.Start.Column.Minus1 }}, {{ $value.Start.Row.Minus1 }}, }, { {{ $value.End.Column.Minus1 }}, {{ $value.End.Row.Minus1 }} } }, {{ end }} },
false,
{{- end}}
},{{end}}
}
`
154 changes: 154 additions & 0 deletions exercises/word-search/cases_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package wordsearch

// Source: exercism/problem-specifications
// Commit: c741e35 Add tests to support TDD workflow (#899)
// Problem Specifications Version: 1.1.0

var testCases = []struct {
description string
puzzle []string // puzzle strings
words []string // words to search for
expected map[string][2][2]int // expected coordinates
expectError bool
}{
{
"Should accept an initial game grid and a target search word",
[]string{"jefblpepre"},
[]string{"clojure"},
map[string][2][2]int{},
true,
},
{
"Should locate one word written left to right",
[]string{"clojurermt"},
[]string{"clojure"},
map[string][2][2]int{"clojure": {{0, 0}, {6, 0}}},
false,
},
{
"Should locate the same word written left to right in a different position",
[]string{"mtclojurer"},
[]string{"clojure"},
map[string][2][2]int{"clojure": {{2, 0}, {8, 0}}},
false,
},
{
"Should locate a different left to right word",
[]string{"coffeelplx"},
[]string{"coffee"},
map[string][2][2]int{"coffee": {{0, 0}, {5, 0}}},
false,
},
{
"Should locate that different left to right word in a different position",
[]string{"xcoffeezlp"},
[]string{"coffee"},
map[string][2][2]int{"coffee": {{1, 0}, {6, 0}}},
false,
},
{
"Should locate a left to right word in two line grid",
[]string{"jefblpepre", "tclojurerm"},
[]string{"clojure"},
map[string][2][2]int{"clojure": {{1, 1}, {7, 1}}},
false,
},
{
"Should locate a left to right word in three line grid",
[]string{"camdcimgtc", "jefblpepre", "clojurermt"},
[]string{"clojure"},
map[string][2][2]int{"clojure": {{0, 2}, {6, 2}}},
false,
},
{
"Should locate a left to right word in ten line grid",
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
[]string{"clojure"},
map[string][2][2]int{"clojure": {{0, 9}, {6, 9}}},
false,
},
{
"Should locate that left to right word in a different position in a ten line grid",
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "clojurermt", "jalaycalmp"},
[]string{"clojure"},
map[string][2][2]int{"clojure": {{0, 8}, {6, 8}}},
false,
},
{
"Should locate a different left to right word in a ten line grid",
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "fortranftw", "alxhpburyi", "clojurermt", "jalaycalmp"},
[]string{"fortran"},
map[string][2][2]int{"fortran": {{0, 6}, {6, 6}}},
false,
},
{
"Should locate multiple words",
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "fortranftw", "alxhpburyi", "jalaycalmp", "clojurermt"},
[]string{"fortran", "clojure"},
map[string][2][2]int{"clojure": {{0, 9}, {6, 9}}, "fortran": {{0, 6}, {6, 6}}},
false,
},
{
"Should locate a single word written right to left",
[]string{"rixilelhrs"},
[]string{"elixir"},
map[string][2][2]int{"elixir": {{5, 0}, {0, 0}}},
false,
},
{
"Should locate multiple words written in different horizontal directions",
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
[]string{"elixir", "clojure"},
map[string][2][2]int{"clojure": {{0, 9}, {6, 9}}, "elixir": {{5, 4}, {0, 4}}},
false,
},
{
"Should locate words written top to bottom",
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
[]string{"clojure", "elixir", "ecmascript"},
map[string][2][2]int{"clojure": {{0, 9}, {6, 9}}, "ecmascript": {{9, 0}, {9, 9}}, "elixir": {{5, 4}, {0, 4}}},
false,
},
{
"Should locate words written bottom to top",
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
[]string{"clojure", "elixir", "ecmascript", "rust"},
map[string][2][2]int{"clojure": {{0, 9}, {6, 9}}, "ecmascript": {{9, 0}, {9, 9}}, "elixir": {{5, 4}, {0, 4}}, "rust": {{8, 4}, {8, 1}}},
false,
},
{
"Should locate words written top left to bottom right",
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
[]string{"clojure", "elixir", "ecmascript", "rust", "java"},
map[string][2][2]int{"clojure": {{0, 9}, {6, 9}}, "ecmascript": {{9, 0}, {9, 9}}, "elixir": {{5, 4}, {0, 4}}, "java": {{0, 0}, {3, 3}}, "rust": {{8, 4}, {8, 1}}},
false,
},
{
"Should locate words written bottom right to top left",
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
[]string{"clojure", "elixir", "ecmascript", "rust", "java", "lua"},
map[string][2][2]int{"clojure": {{0, 9}, {6, 9}}, "ecmascript": {{9, 0}, {9, 9}}, "elixir": {{5, 4}, {0, 4}}, "java": {{0, 0}, {3, 3}}, "lua": {{7, 8}, {5, 6}}, "rust": {{8, 4}, {8, 1}}},
false,
},
{
"Should locate words written bottom left to top right",
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
[]string{"clojure", "elixir", "ecmascript", "rust", "java", "lua", "lisp"},
map[string][2][2]int{"clojure": {{0, 9}, {6, 9}}, "ecmascript": {{9, 0}, {9, 9}}, "elixir": {{5, 4}, {0, 4}}, "java": {{0, 0}, {3, 3}}, "lisp": {{2, 5}, {5, 2}}, "lua": {{7, 8}, {5, 6}}, "rust": {{8, 4}, {8, 1}}},
false,
},
{
"Should locate words written top right to bottom left",
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
[]string{"clojure", "elixir", "ecmascript", "rust", "java", "lua", "lisp", "ruby"},
map[string][2][2]int{"clojure": {{0, 9}, {6, 9}}, "ecmascript": {{9, 0}, {9, 9}}, "elixir": {{5, 4}, {0, 4}}, "java": {{0, 0}, {3, 3}}, "lisp": {{2, 5}, {5, 2}}, "lua": {{7, 8}, {5, 6}}, "ruby": {{7, 5}, {4, 8}}, "rust": {{8, 4}, {8, 1}}},
false,
},
{
"Should fail to locate a word that is not in the puzzle",
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
[]string{"clojure", "elixir", "ecmascript", "rust", "java", "lua", "lisp", "ruby", "haskell"},
map[string][2][2]int{},
true,
},
}
57 changes: 16 additions & 41 deletions exercises/word-search/word_search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,52 +7,27 @@ import (

// Define a function Solve(words []string, puzzle []string) (map[string][2][2]int, error).

var words = []string{
"clojure", "ecmascript", "elixir", "go", "java", "lisp",
"ocaml", "ruby", "rust", "scheme",
}

var puzzle = []string{
"gefblpepre",
"cbmdcimguc",
"oikoknrjsm",
"pbwjrqrota",
"rixilelhgs",
"woncqlispc",
"schemekmgr",
"alxhprubyi",
"javaocamlp",
"clojurermt",
}

// Top left corner is (0, 0)
// Entries are {{firstX, firstY}, {lastX, lastY}}.
var positions = map[string][2][2]int{
"clojure": {{0, 9}, {6, 9}},
"ecmascript": {{9, 0}, {9, 9}},
"elixir": {{5, 4}, {0, 4}},
"go": {{8, 4}, {7, 3}},
"java": {{0, 8}, {3, 8}},
"lisp": {{5, 5}, {8, 5}},
"ocaml": {{4, 8}, {8, 8}},
"ruby": {{5, 7}, {8, 7}},
"rust": {{8, 0}, {8, 3}},
"scheme": {{0, 6}, {5, 6}},
}

func TestSolve(t *testing.T) {
actual, err := Solve(words, puzzle)
if err != nil {
var _ error = err
t.Fatalf("Didn't expect error but got %v", err)
}
if !reflect.DeepEqual(actual, positions) {
t.Fatalf("Got %v, want %v", actual, positions)
for _, tc := range testCases {
actual, err := Solve(tc.words, tc.puzzle)
if err != nil {
var _ error = err
if !tc.expectError {
t.Fatalf("FAIL: %s\nExpected %#v\nGot error: %v", tc.description, tc.expected, err)
}
} else if tc.expectError {
t.Fatalf("FAIL: %s\nExpected error\nGot %v", tc.description, actual)
} else if !reflect.DeepEqual(actual, tc.expected) {
t.Fatalf("FAIL: %s\nExpected %v,\nGot %v", tc.description, tc.expected, actual)
}
t.Logf("PASS: %s", tc.description)
}
}

func BenchmarkSolve(b *testing.B) {
for i := 0; i < b.N; i++ {
Solve(words, puzzle)
for _, tc := range testCases {
Solve(tc.words, tc.puzzle)
}
}
}

0 comments on commit 7bb5933

Please sign in to comment.