From e33c5c2e2f5c4325fd4157d92d8003bd9957f0e3 Mon Sep 17 00:00:00 2001 From: Lee Nipper Date: Sun, 12 Nov 2017 19:22:39 -0600 Subject: [PATCH] perfect-numbers: create test case generator (#952) 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. Keep TestGivesPositiveRequiredError redundant test on zero since it verifies ErrOnlyPositive. Update example solution to use int64 for input type since -1 test case in canonical-data.json. For #605. --- exercises/perfect-numbers/.meta/gen.go | 88 ++++++++++++++++++ exercises/perfect-numbers/cases_test.go | 89 +++++++++++++++++++ exercises/perfect-numbers/example.go | 8 +- .../perfect-numbers/perfect_numbers_test.go | 31 +++---- 4 files changed, 194 insertions(+), 22 deletions(-) create mode 100644 exercises/perfect-numbers/.meta/gen.go create mode 100644 exercises/perfect-numbers/cases_test.go diff --git a/exercises/perfect-numbers/.meta/gen.go b/exercises/perfect-numbers/.meta/gen.go new file mode 100644 index 000000000..8c8bd61c4 --- /dev/null +++ b/exercises/perfect-numbers/.meta/gen.go @@ -0,0 +1,88 @@ +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("perfect-numbers", &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 + Cases []struct { + Description string + Cases []oneCase + } +} + +// Test cases +type oneCase struct { + Description string + Property string + Input int64 + Expected interface{} +} + +func (c oneCase) Valid() bool { + valid, _ := determineExpected(c.Expected) + return valid +} + +func (c oneCase) ExpectedClassification() string { + _, e := determineExpected(c.Expected) + switch e { + case "perfect": + return "ClassificationPerfect" + case "abundant": + return "ClassificationAbundant" + case "deficient": + return "ClassificationDeficient" + } + return e +} + +// determineExpected examines an .Expected interface{} object and determines +// whether a test case is valid(bool) and has a classification or expects an error, +// returning valid and classification. +func determineExpected(expected interface{}) (bool, string) { + exp, ok := expected.(string) + if ok { + return ok, exp + } + return false, "" +} + +// Template to generate test cases. +var tmpl = `package perfect + +{{.Header}} + +var classificationTestCases = []struct { + description string + input int64 + ok bool + expected Classification +}{ {{range .J.Cases}} {{range .Cases}} +{ + description: "{{.Description}}", + input: {{.Input}}, +{{if .Valid}} ok: true, + expected: {{.ExpectedClassification}}, +{{- else}} ok: false, +{{- end}} +},{{end}}{{end}} +} +` diff --git a/exercises/perfect-numbers/cases_test.go b/exercises/perfect-numbers/cases_test.go new file mode 100644 index 000000000..3bf170909 --- /dev/null +++ b/exercises/perfect-numbers/cases_test.go @@ -0,0 +1,89 @@ +package perfect + +// Source: exercism/problem-specifications +// Commit: 924bb7a perfect-numbers: fix misleading test description +// Problem Specifications Version: 1.0.1 + +var classificationTestCases = []struct { + description string + input int64 + ok bool + expected Classification +}{ + { + description: "Smallest perfect number is classified correctly", + input: 6, + ok: true, + expected: ClassificationPerfect, + }, + { + description: "Medium perfect number is classified correctly", + input: 28, + ok: true, + expected: ClassificationPerfect, + }, + { + description: "Large perfect number is classified correctly", + input: 33550336, + ok: true, + expected: ClassificationPerfect, + }, + { + description: "Smallest abundant number is classified correctly", + input: 12, + ok: true, + expected: ClassificationAbundant, + }, + { + description: "Medium abundant number is classified correctly", + input: 30, + ok: true, + expected: ClassificationAbundant, + }, + { + description: "Large abundant number is classified correctly", + input: 33550335, + ok: true, + expected: ClassificationAbundant, + }, + { + description: "Smallest prime deficient number is classified correctly", + input: 2, + ok: true, + expected: ClassificationDeficient, + }, + { + description: "Smallest non-prime deficient number is classified correctly", + input: 4, + ok: true, + expected: ClassificationDeficient, + }, + { + description: "Medium deficient number is classified correctly", + input: 32, + ok: true, + expected: ClassificationDeficient, + }, + { + description: "Large deficient number is classified correctly", + input: 33550337, + ok: true, + expected: ClassificationDeficient, + }, + { + description: "Edge case (no factors other than itself) is classified correctly", + input: 1, + ok: true, + expected: ClassificationDeficient, + }, + { + description: "Zero is rejected (not a natural number)", + input: 0, + ok: false, + }, + { + description: "Negative integer is rejected (not a natural number)", + input: -1, + ok: false, + }, +} diff --git a/exercises/perfect-numbers/example.go b/exercises/perfect-numbers/example.go index 0e0012099..5048c66ac 100644 --- a/exercises/perfect-numbers/example.go +++ b/exercises/perfect-numbers/example.go @@ -18,12 +18,12 @@ var ( ) // Classify finds the category of given natural number -func Classify(n uint64) (Classification, error) { - if n == 0 { +func Classify(n int64) (Classification, error) { + if n <= 0 { return "", ErrOnlyPositive } - var sum uint64 - for i := uint64(1); i < n; i++ { + var sum int64 + for i := int64(1); i < n; i++ { if n%i == 0 { if sum = sum + i; sum > n { return ClassificationAbundant, nil diff --git a/exercises/perfect-numbers/perfect_numbers_test.go b/exercises/perfect-numbers/perfect_numbers_test.go index 25cb15287..8b1f39c59 100644 --- a/exercises/perfect-numbers/perfect_numbers_test.go +++ b/exercises/perfect-numbers/perfect_numbers_test.go @@ -4,32 +4,27 @@ import "testing" var _ error = ErrOnlyPositive -var classificationTestCases = []struct { - input uint64 - expected Classification -}{ - {1, ClassificationDeficient}, - {13, ClassificationDeficient}, - {12, ClassificationAbundant}, - {6, ClassificationPerfect}, - {28, ClassificationPerfect}, - {496, ClassificationPerfect}, - {8128, ClassificationPerfect}, -} - func TestGivesPositiveRequiredError(t *testing.T) { if _, err := Classify(0); err != ErrOnlyPositive { - t.Errorf("Expected error %q but got %q", ErrOnlyPositive, err) + t.Fatalf("FAIL GivesPositiveRequiredError Expected error %q but got %q", ErrOnlyPositive, err) } + t.Logf("PASS GivesPositiveRequiredError") } func TestClassifiesCorrectly(t *testing.T) { for _, c := range classificationTestCases { - if cat, err := Classify(c.input); err != nil { - t.Errorf("%d: Expected no error but got %s", c.input, err) - } else if cat != c.expected { - t.Errorf("%d: Expected %q, got %q", c.input, c.expected, cat) + cat, err := Classify(c.input) + switch { + case err != nil: + if c.ok { + t.Fatalf("FAIL %s\nClassify(%d)\nExpected no error but got error %q", c.description, c.input, err) + } + case !c.ok: + t.Fatalf("FAIL %s\nClassify(%d)\nExpected error but got %q", c.description, c.input, cat) + case cat != c.expected: + t.Fatalf("FAIL %s\nClassify(%d)\nExpected %q, got %q", c.description, c.input, c.expected, cat) } + t.Logf("PASS %s", c.description) } }