diff --git a/exercises/grains/.meta/gen.go b/exercises/grains/.meta/gen.go new file mode 100644 index 000000000..adbfe3f0e --- /dev/null +++ b/exercises/grains/.meta/gen.go @@ -0,0 +1,99 @@ +package main + +import ( + "encoding/json" + "log" + "strings" + "text/template" + + "../../../gen" +) + +func main() { + t, err := template.New("").Parse(tmpl) + if err != nil { + log.Fatal(err) + } + var j js + if err := gen.Gen("grains", &j, t); err != nil { + log.Fatal(err) + } +} + +// The JSON structure we expect to be able to unmarshal into +type js struct { + Groups []testGroup `json:"Cases"` +} + +type testGroup struct { + Description string + Cases []json.RawMessage `property:"RAW"` + SquareCases []SquareCase `property:"square"` + // Note: canonical-data.json has a element of "cases" + // which includes a test for property 'total', but it is ignored here, + // since "expected" is a single known value. +} + +type SquareCase struct { + Description string + Input int + Expected interface{} +} + +func (c SquareCase) HasAnswer() bool { + hasAnswer, _ := determineExpected(c.Expected) + return hasAnswer +} + +func (c SquareCase) Answer() uint64 { + _, answer := determineExpected(c.Expected) + return answer +} + +func (c SquareCase) EditedDescription() string { + // Go doesn't raise exceptions, so replace the wording in .Description. + return strings.Replace(c.Description, "raises an exception", "returns an error", 1) +} + +// determineExpected examines an .Expected interface{} object and determines +// whether a test case has an answer or expects an error. +// The return values are true and answer or false and zero. +func determineExpected(expected interface{}) (bool, uint64) { + ans, ok := expected.(float64) + if ok { + if ans == float64(-1) { + return false, 0 + } + return true, uint64(ans) + } + return false, 0 +} + +var tmpl = `package grains + +{{.Header}} + +{{range .J.Groups}} + {{- if .SquareCases }} + // {{ .Description }} + var squareTests = []struct { + description string + input int + expectedVal uint64 + expectError bool + }{ + {{- range .SquareCases}} + { + description: "{{.EditedDescription}}", + input: {{.Input}}, + {{- if .HasAnswer}} + expectedVal: {{.Answer}}, + {{- else}} + expectError: true, + {{- end}} + }, + {{- end }} + } + {{- end }} +{{end}} +` diff --git a/exercises/grains/cases_test.go b/exercises/grains/cases_test.go new file mode 100644 index 000000000..707ac1ffd --- /dev/null +++ b/exercises/grains/cases_test.go @@ -0,0 +1,64 @@ +package grains + +// Source: exercism/problem-specifications +// Commit: d4554e6 grains: fix a typo in comments of canonical data +// Problem Specifications Version: 1.0.0 + +// returns the number of grains on the square +var squareTests = []struct { + description string + input int + expectedVal uint64 + expectError bool +}{ + { + description: "1", + input: 1, + expectedVal: 1, + }, + { + description: "2", + input: 2, + expectedVal: 2, + }, + { + description: "3", + input: 3, + expectedVal: 4, + }, + { + description: "4", + input: 4, + expectedVal: 8, + }, + { + description: "16", + input: 16, + expectedVal: 32768, + }, + { + description: "32", + input: 32, + expectedVal: 2147483648, + }, + { + description: "64", + input: 64, + expectedVal: 9223372036854775808, + }, + { + description: "square 0 returns an error", + input: 0, + expectError: true, + }, + { + description: "negative square returns an error", + input: -1, + expectError: true, + }, + { + description: "square greater than 64 returns an error", + input: 65, + expectError: true, + }, +} diff --git a/exercises/grains/grains_test.go b/exercises/grains/grains_test.go index 0cd0a18b9..ea3d8d831 100644 --- a/exercises/grains/grains_test.go +++ b/exercises/grains/grains_test.go @@ -4,41 +4,25 @@ import ( "testing" ) -var squareTests = []struct { - input int - expectedVal uint64 - expectError bool -}{ - {1, 1, false}, - {2, 2, false}, - {3, 4, false}, - {4, 8, false}, - {16, 32768, false}, - {32, 2147483648, false}, - {64, 9223372036854775808, false}, - {65, 0, true}, - {0, 0, true}, - {-1, 0, true}, -} - func TestSquare(t *testing.T) { for _, test := range squareTests { actualVal, actualErr := Square(test.input) // check actualVal only if no error expected if !test.expectError && actualVal != test.expectedVal { - t.Errorf("Square(%d) expected %d, Actual %d", test.input, test.expectedVal, actualVal) + t.Fatalf("FAIL: %s\nSquare(%d) expected %d, Actual %d", test.description, test.input, test.expectedVal, actualVal) } // if we expect an error and there isn't one if test.expectError && actualErr == nil { - t.Errorf("Square(%d) expected an error, but error is nil", test.input) + t.Fatalf("FAIL: %s\nSquare(%d) expected an error, but error is nil", test.description, test.input) } // if we don't expect an error and there is one if !test.expectError && actualErr != nil { var _ error = actualErr - t.Errorf("Square(%d) expected no error, but error is: %s", test.input, actualErr) + t.Fatalf("FAIL: %s\nSquare(%d) expected no error, but error is: %s", test.description, test.input, actualErr) } + t.Logf("PASS: %s", test.description) } }