diff --git a/exercises/word-search/.meta/gen.go b/exercises/word-search/.meta/gen.go new file mode 100644 index 000000000..97e34dedd --- /dev/null +++ b/exercises/word-search/.meta/gen.go @@ -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}} +} +` diff --git a/exercises/word-search/cases_test.go b/exercises/word-search/cases_test.go new file mode 100644 index 000000000..7bc278ad9 --- /dev/null +++ b/exercises/word-search/cases_test.go @@ -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, + }, +} diff --git a/exercises/word-search/word_search_test.go b/exercises/word-search/word_search_test.go index 4941ea0ef..072af6393 100644 --- a/exercises/word-search/word_search_test.go +++ b/exercises/word-search/word_search_test.go @@ -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) + } } }