Skip to content

all-your-base: add test generator #945

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions exercises/all-your-base/.meta/gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
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("all-your-base", &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 []oneCase
}

// Test cases
type oneCase struct {
Description string
Property string
InputBase int `json:"input_base"`
InputDigits []int `json:"input_digits"`
OutputBase int `json:"output_base"`
Expected interface{}
}

func (o oneCase) Result() []int {
s, ok := o.Expected.([]interface{})
if !ok {
return nil
}
var res []int
for _, v := range s {
f, _ := v.(float64)
res = append(res, int(f))
}
return res
}
func (o oneCase) Err() string {
m, ok := o.Expected.(map[string]interface{})
if !ok {
return ""
}
return m["error"].(string)
}

// Template to generate test cases.
var tmpl = `package allyourbase

{{.Header}}

var testCases = []struct {
description string
inputBase int
inputDigits []int
outputBase int
expected []int
err string
}{ {{range .J.Cases}}
{
description: {{printf "%q" .Description}},
inputBase: {{printf "%d" .InputBase}},
inputDigits: {{printf "%#v" .InputDigits}},
outputBase: {{printf "%d" .OutputBase}},
expected: {{printf "%#v" .Result}},
err: {{printf "%q" .Err}},
},{{end}}
}
`
13 changes: 13 additions & 0 deletions exercises/all-your-base/.meta/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Implementation

Assumptions:
1. Zero is always represented in outputs as [0] instead of [].
2. In no other instances are leading zeroes present in any outputs.
3. Leading zeroes are accepted in inputs.
4. An empty sequence of input digits is considered zero, rather than an error.

Handle problem cases by returning an error whose Error() method
returns one of following messages:
* input base must be >= 2
* output base must be >= 2
* all digits must satisfy 0 <= d < input base
17 changes: 16 additions & 1 deletion exercises/all-your-base/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Implement general base conversion. Given a number in base **a**,
represented as a sequence of digits, convert it to base **b**.

## Note

- Try to implement the conversion yourself.
Do not use something else to perform the conversion for you.

Expand All @@ -28,9 +29,23 @@ The number 1120, *in base 3*, means:

I think you got the idea!


*Yes. Those three numbers above are exactly the same. Congratulations!*

## Implementation

Assumptions:
1. Zero is always represented in outputs as [0] instead of [].
2. In no other instances are leading zeroes present in any outputs.
3. Leading zeroes are accepted in inputs.
4. An empty sequence of input digits is considered zero, rather than an error.

Handle problem cases by returning an error whose Error() method
returns one of following messages:
* input base must be >= 2
* output base must be >= 2
* all digits must satisfy 0 <= d < input base


## Running the tests

To run the tests run the command `go test` from within the exercise directory.
Expand Down
175 changes: 12 additions & 163 deletions exercises/all-your-base/all_your_base_test.go
Original file line number Diff line number Diff line change
@@ -1,177 +1,26 @@
package allyourbase

import "testing"

// Note: ConvertToBase should accept leading zeroes in its input,
// but never emit leading zeroes in its output.
// Exception: If the value of the output is zero, represent it with a single zero.
var testCases = []struct {
description string
inputBase uint64
outputBase uint64
inputDigits []uint64
outputDigits []uint64
error error
}{
{
description: "single bit one to decimal",
inputBase: 2,
inputDigits: []uint64{1},
outputBase: 10,
outputDigits: []uint64{1},
},
{
description: "binary to single decimal",
inputBase: 2,
inputDigits: []uint64{1, 0, 1},
outputBase: 10,
outputDigits: []uint64{5},
},
{
description: "single decimal to binary",
inputBase: 10,
inputDigits: []uint64{5},
outputBase: 2,
outputDigits: []uint64{1, 0, 1},
},
{
description: "binary to multiple decimal",
inputBase: 2,
inputDigits: []uint64{1, 0, 1, 0, 1, 0},
outputBase: 10,
outputDigits: []uint64{4, 2},
},
{
description: "decimal to binary",
inputBase: 10,
inputDigits: []uint64{4, 2},
outputBase: 2,
outputDigits: []uint64{1, 0, 1, 0, 1, 0},
},
{
description: "trinary to hexadecimal",
inputBase: 3,
inputDigits: []uint64{1, 1, 2, 0},
outputBase: 16,
outputDigits: []uint64{2, 10},
},
{
description: "hexadecimal to trinary",
inputBase: 16,
inputDigits: []uint64{2, 10},
outputBase: 3,
outputDigits: []uint64{1, 1, 2, 0},
},
{
description: "15-bit integer",
inputBase: 97,
inputDigits: []uint64{3, 46, 60},
outputBase: 73,
outputDigits: []uint64{6, 10, 45},
},
{
description: "empty list",
inputBase: 2,
inputDigits: []uint64{},
outputBase: 10,
outputDigits: []uint64{0},
error: nil,
},
{
description: "single zero",
inputBase: 10,
inputDigits: []uint64{0},
outputBase: 2,
outputDigits: []uint64{0},
},
{
description: "multiple zeros",
inputBase: 10,
inputDigits: []uint64{0, 0, 0},
outputBase: 2,
outputDigits: []uint64{0},
},
{
description: "leading zeros",
inputBase: 7,
inputDigits: []uint64{0, 6, 0},
outputBase: 10,
outputDigits: []uint64{4, 2},
},
{
description: "invalid positive digit",
inputBase: 2,
inputDigits: []uint64{1, 2, 1, 0, 1, 0},
outputBase: 10,
outputDigits: nil,
error: ErrInvalidDigit,
},
{
description: "first base is one",
inputBase: 1,
inputDigits: []uint64{},
outputBase: 10,
outputDigits: nil,
error: ErrInvalidBase,
},
{
description: "second base is one",
inputBase: 2,
inputDigits: []uint64{1, 0, 1, 0, 1, 0},
outputBase: 1,
outputDigits: nil,
error: ErrInvalidBase,
},
{
description: "first base is zero",
inputBase: 0,
inputDigits: []uint64{},
outputBase: 10,
outputDigits: nil,
error: ErrInvalidBase,
},
{
description: "second base is zero",
inputBase: 10,
inputDigits: []uint64{7},
outputBase: 0,
outputDigits: nil,
error: ErrInvalidBase,
},
}

func digitsEqual(a, b []uint64) bool {
if len(a) != len(b) {
return false
}

for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return false
}
}

return true
}
import (
"reflect"
"testing"
)

func TestConvertToBase(t *testing.T) {
for _, c := range testCases {
output, err := ConvertToBase(c.inputBase, c.inputDigits, c.outputBase)
if err != c.error {
if c.err != "" && (err == nil || c.err != err.Error()) {
t.Fatalf(`FAIL: %s
Expected error: %v
Got: %v`, c.description, c.error, err)
Expected error: %s
Got: %v`, c.description, c.err, err)
}

if !digitsEqual(c.outputDigits, output) {
if !reflect.DeepEqual(c.expected, output) {
t.Fatalf(`FAIL: %s
Input base: %d
Input digits: %v
Input digits: %#v
Output base: %d
Expected output digits: %v
Got: %v`, c.description, c.inputBase, c.inputDigits, c.outputBase, c.outputDigits, output)
} else {
t.Logf("PASS: %s", c.description)
Expected output digits: %#v
Got: %#v`, c.description, c.inputBase, c.inputDigits, c.outputBase, c.expected, output)
}
t.Logf("PASS: %s", c.description)
}
}
Loading