Skip to content

Commit

Permalink
perfect-numbers: create test case generator (#952)
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.
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.
  • Loading branch information
leenipper authored Nov 13, 2017
1 parent 171d56a commit e33c5c2
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 22 deletions.
88 changes: 88 additions & 0 deletions exercises/perfect-numbers/.meta/gen.go
Original file line number Diff line number Diff line change
@@ -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}}
}
`
89 changes: 89 additions & 0 deletions exercises/perfect-numbers/cases_test.go
Original file line number Diff line number Diff line change
@@ -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,
},
}
8 changes: 4 additions & 4 deletions exercises/perfect-numbers/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 13 additions & 18 deletions exercises/perfect-numbers/perfect_numbers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down

0 comments on commit e33c5c2

Please sign in to comment.