Skip to content

Commit 89beee0

Browse files
authored
word-search: create test case generator (#871)
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 #605.
1 parent 905c142 commit 89beee0

File tree

3 files changed

+266
-41
lines changed

3 files changed

+266
-41
lines changed

exercises/word-search/.meta/gen.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"text/template"
6+
7+
"../../../gen"
8+
)
9+
10+
func main() {
11+
t, err := template.New("").Parse(tmpl)
12+
if err != nil {
13+
log.Fatal(err)
14+
}
15+
var j js
16+
if err := gen.Gen("word-search", &j, t); err != nil {
17+
log.Fatal(err)
18+
}
19+
}
20+
21+
// The JSON structure we expect to be able to unmarshal into
22+
type js struct {
23+
Exercise string
24+
Version string
25+
Comments []string
26+
Cases []OneCase
27+
}
28+
29+
// template applied to above data structure generates the Go test cases
30+
31+
type Int int
32+
33+
type Coordinates struct {
34+
Column Int
35+
Row Int
36+
}
37+
type Position struct {
38+
Start Coordinates
39+
End Coordinates
40+
}
41+
42+
type OneCase struct {
43+
Description string
44+
Property string
45+
Grid []string
46+
WordsToSearchFor []string
47+
Expected map[string]Position
48+
}
49+
50+
func (p Position) NullPosition() bool {
51+
if p.Start.Column == 0 && p.Start.Row == 0 &&
52+
p.End.Column == 0 && p.End.Row == 0 {
53+
return true
54+
}
55+
return false
56+
}
57+
58+
func (c OneCase) ErrorExpected() bool {
59+
// When any of the word positions have an null position, expect an error.
60+
for _, p := range c.Expected {
61+
if p.NullPosition() {
62+
return true
63+
}
64+
}
65+
return false
66+
}
67+
68+
func (v Int) Minus1() int {
69+
return int(v) - 1
70+
}
71+
72+
// Template to generate test cases
73+
74+
var tmpl = `package wordsearch
75+
76+
{{.Header}}
77+
78+
var testCases = []struct {
79+
description string
80+
puzzle []string // puzzle strings
81+
words []string // words to search for
82+
expected map[string][2][2]int // expected coordinates
83+
expectError bool
84+
}{ {{range .J.Cases}}
85+
{
86+
{{printf "%q" .Description}},
87+
{{printf "%#v" .Grid}},
88+
{{printf "%#v" .WordsToSearchFor}},
89+
{{if .ErrorExpected}} map[string][2][2]int{ },
90+
true,
91+
{{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 }} },
92+
false,
93+
{{- end}}
94+
},{{end}}
95+
}
96+
`

exercises/word-search/cases_test.go

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package wordsearch
2+
3+
// Source: exercism/problem-specifications
4+
// Commit: c741e35 Add tests to support TDD workflow (#899)
5+
// Problem Specifications Version: 1.1.0
6+
7+
var testCases = []struct {
8+
description string
9+
puzzle []string // puzzle strings
10+
words []string // words to search for
11+
expected map[string][2][2]int // expected coordinates
12+
expectError bool
13+
}{
14+
{
15+
"Should accept an initial game grid and a target search word",
16+
[]string{"jefblpepre"},
17+
[]string{"clojure"},
18+
map[string][2][2]int{},
19+
true,
20+
},
21+
{
22+
"Should locate one word written left to right",
23+
[]string{"clojurermt"},
24+
[]string{"clojure"},
25+
map[string][2][2]int{"clojure": {{0, 0}, {6, 0}}},
26+
false,
27+
},
28+
{
29+
"Should locate the same word written left to right in a different position",
30+
[]string{"mtclojurer"},
31+
[]string{"clojure"},
32+
map[string][2][2]int{"clojure": {{2, 0}, {8, 0}}},
33+
false,
34+
},
35+
{
36+
"Should locate a different left to right word",
37+
[]string{"coffeelplx"},
38+
[]string{"coffee"},
39+
map[string][2][2]int{"coffee": {{0, 0}, {5, 0}}},
40+
false,
41+
},
42+
{
43+
"Should locate that different left to right word in a different position",
44+
[]string{"xcoffeezlp"},
45+
[]string{"coffee"},
46+
map[string][2][2]int{"coffee": {{1, 0}, {6, 0}}},
47+
false,
48+
},
49+
{
50+
"Should locate a left to right word in two line grid",
51+
[]string{"jefblpepre", "tclojurerm"},
52+
[]string{"clojure"},
53+
map[string][2][2]int{"clojure": {{1, 1}, {7, 1}}},
54+
false,
55+
},
56+
{
57+
"Should locate a left to right word in three line grid",
58+
[]string{"camdcimgtc", "jefblpepre", "clojurermt"},
59+
[]string{"clojure"},
60+
map[string][2][2]int{"clojure": {{0, 2}, {6, 2}}},
61+
false,
62+
},
63+
{
64+
"Should locate a left to right word in ten line grid",
65+
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
66+
[]string{"clojure"},
67+
map[string][2][2]int{"clojure": {{0, 9}, {6, 9}}},
68+
false,
69+
},
70+
{
71+
"Should locate that left to right word in a different position in a ten line grid",
72+
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "clojurermt", "jalaycalmp"},
73+
[]string{"clojure"},
74+
map[string][2][2]int{"clojure": {{0, 8}, {6, 8}}},
75+
false,
76+
},
77+
{
78+
"Should locate a different left to right word in a ten line grid",
79+
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "fortranftw", "alxhpburyi", "clojurermt", "jalaycalmp"},
80+
[]string{"fortran"},
81+
map[string][2][2]int{"fortran": {{0, 6}, {6, 6}}},
82+
false,
83+
},
84+
{
85+
"Should locate multiple words",
86+
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "fortranftw", "alxhpburyi", "jalaycalmp", "clojurermt"},
87+
[]string{"fortran", "clojure"},
88+
map[string][2][2]int{"clojure": {{0, 9}, {6, 9}}, "fortran": {{0, 6}, {6, 6}}},
89+
false,
90+
},
91+
{
92+
"Should locate a single word written right to left",
93+
[]string{"rixilelhrs"},
94+
[]string{"elixir"},
95+
map[string][2][2]int{"elixir": {{5, 0}, {0, 0}}},
96+
false,
97+
},
98+
{
99+
"Should locate multiple words written in different horizontal directions",
100+
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
101+
[]string{"elixir", "clojure"},
102+
map[string][2][2]int{"clojure": {{0, 9}, {6, 9}}, "elixir": {{5, 4}, {0, 4}}},
103+
false,
104+
},
105+
{
106+
"Should locate words written top to bottom",
107+
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
108+
[]string{"clojure", "elixir", "ecmascript"},
109+
map[string][2][2]int{"clojure": {{0, 9}, {6, 9}}, "ecmascript": {{9, 0}, {9, 9}}, "elixir": {{5, 4}, {0, 4}}},
110+
false,
111+
},
112+
{
113+
"Should locate words written bottom to top",
114+
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
115+
[]string{"clojure", "elixir", "ecmascript", "rust"},
116+
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}}},
117+
false,
118+
},
119+
{
120+
"Should locate words written top left to bottom right",
121+
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
122+
[]string{"clojure", "elixir", "ecmascript", "rust", "java"},
123+
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}}},
124+
false,
125+
},
126+
{
127+
"Should locate words written bottom right to top left",
128+
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
129+
[]string{"clojure", "elixir", "ecmascript", "rust", "java", "lua"},
130+
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}}},
131+
false,
132+
},
133+
{
134+
"Should locate words written bottom left to top right",
135+
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
136+
[]string{"clojure", "elixir", "ecmascript", "rust", "java", "lua", "lisp"},
137+
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}}},
138+
false,
139+
},
140+
{
141+
"Should locate words written top right to bottom left",
142+
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
143+
[]string{"clojure", "elixir", "ecmascript", "rust", "java", "lua", "lisp", "ruby"},
144+
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}}},
145+
false,
146+
},
147+
{
148+
"Should fail to locate a word that is not in the puzzle",
149+
[]string{"jefblpepre", "camdcimgtc", "oivokprjsm", "pbwasqroua", "rixilelhrs", "wolcqlirpc", "screeaumgr", "alxhpburyi", "jalaycalmp", "clojurermt"},
150+
[]string{"clojure", "elixir", "ecmascript", "rust", "java", "lua", "lisp", "ruby", "haskell"},
151+
map[string][2][2]int{},
152+
true,
153+
},
154+
}

exercises/word-search/word_search_test.go

Lines changed: 16 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,52 +7,27 @@ import (
77

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

10-
var words = []string{
11-
"clojure", "ecmascript", "elixir", "go", "java", "lisp",
12-
"ocaml", "ruby", "rust", "scheme",
13-
}
14-
15-
var puzzle = []string{
16-
"gefblpepre",
17-
"cbmdcimguc",
18-
"oikoknrjsm",
19-
"pbwjrqrota",
20-
"rixilelhgs",
21-
"woncqlispc",
22-
"schemekmgr",
23-
"alxhprubyi",
24-
"javaocamlp",
25-
"clojurermt",
26-
}
27-
28-
// Top left corner is (0, 0)
29-
// Entries are {{firstX, firstY}, {lastX, lastY}}.
30-
var positions = map[string][2][2]int{
31-
"clojure": {{0, 9}, {6, 9}},
32-
"ecmascript": {{9, 0}, {9, 9}},
33-
"elixir": {{5, 4}, {0, 4}},
34-
"go": {{8, 4}, {7, 3}},
35-
"java": {{0, 8}, {3, 8}},
36-
"lisp": {{5, 5}, {8, 5}},
37-
"ocaml": {{4, 8}, {8, 8}},
38-
"ruby": {{5, 7}, {8, 7}},
39-
"rust": {{8, 0}, {8, 3}},
40-
"scheme": {{0, 6}, {5, 6}},
41-
}
42-
4310
func TestSolve(t *testing.T) {
44-
actual, err := Solve(words, puzzle)
45-
if err != nil {
46-
var _ error = err
47-
t.Fatalf("Didn't expect error but got %v", err)
48-
}
49-
if !reflect.DeepEqual(actual, positions) {
50-
t.Fatalf("Got %v, want %v", actual, positions)
11+
for _, tc := range testCases {
12+
actual, err := Solve(tc.words, tc.puzzle)
13+
if err != nil {
14+
var _ error = err
15+
if !tc.expectError {
16+
t.Fatalf("FAIL: %s\nExpected %#v\nGot error: %v", tc.description, tc.expected, err)
17+
}
18+
} else if tc.expectError {
19+
t.Fatalf("FAIL: %s\nExpected error\nGot %v", tc.description, actual)
20+
} else if !reflect.DeepEqual(actual, tc.expected) {
21+
t.Fatalf("FAIL: %s\nExpected %v,\nGot %v", tc.description, tc.expected, actual)
22+
}
23+
t.Logf("PASS: %s", tc.description)
5124
}
5225
}
5326

5427
func BenchmarkSolve(b *testing.B) {
5528
for i := 0; i < b.N; i++ {
56-
Solve(words, puzzle)
29+
for _, tc := range testCases {
30+
Solve(tc.words, tc.puzzle)
31+
}
5732
}
5833
}

0 commit comments

Comments
 (0)