Skip to content

Commit 618e4f6

Browse files
committed
variable-length-quantity: create test case generator
Generate two test case arrays, one for encode and one for decode, from the canonical-data.json. Update example solution to pass the tests. The API changed to use slices in order to match the canonical-data.json test cases. Retain the old functions but rename to decodeInt and encodeInt and utilize these as helpers to support the new slice based API. Update test program to use generated test case arrays. Put FAIL and PASS in test result output. Increment testVersion and targetTestVersion in example solution and test program.
1 parent 925762f commit 618e4f6

File tree

4 files changed

+348
-42
lines changed

4 files changed

+348
-42
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// +build ignore
2+
3+
package main
4+
5+
import (
6+
"log"
7+
"text/template"
8+
9+
"../../../gen"
10+
)
11+
12+
func main() {
13+
t := template.New("").Funcs(template.FuncMap{
14+
"GroupComment": GroupComment,
15+
"byteSlice": byteSlice,
16+
"lengthSlice": lengthSlice,
17+
})
18+
t, err := t.Parse(tmpl)
19+
if err != nil {
20+
log.Fatal(err)
21+
}
22+
var j js
23+
if err := gen.Gen("variable-length-quantity", &j, t); err != nil {
24+
log.Fatal(err)
25+
}
26+
}
27+
28+
// byteSlice converts a slice of uint32 to a byte slice.
29+
func byteSlice(ns []uint32) []byte {
30+
b := make([]byte, len(ns))
31+
for i, n := range ns {
32+
b[i] = byte(n)
33+
}
34+
return b
35+
}
36+
37+
// lengthSlice returns the length of given slice.
38+
func lengthSlice(ns []uint32) int {
39+
return len(ns)
40+
}
41+
42+
// The JSON structure we expect to be able to unmarshal into
43+
type js struct {
44+
Groups []TestGroup `json:"Cases"`
45+
}
46+
47+
type TestGroup struct {
48+
Description string
49+
Cases []OneCase
50+
}
51+
52+
type OneCase struct {
53+
Description string
54+
Property string // "encode" or "decode"
55+
Input []uint32 // supports both []byte and []uint32 in JSON.
56+
Expected []uint32 // supports []byte, []uint32, or null in JSON.
57+
}
58+
59+
// PropertyMatch returns true when given test case c has .Property field matching property;
60+
// this serves as a filter to put test cases with "like" property into the same group.
61+
func (c OneCase) PropertyMatch(property string) bool { return c.Property == property }
62+
63+
// GroupComment looks in each of the test case groups to find the
64+
// group for which every test case has the .Property matching given property;
65+
// it returns the .Description field for the matching property group,
66+
// or a 'Note: ...' if no test group consistently matches given property.
67+
func GroupComment(groups []TestGroup, property string) string {
68+
for _, group := range groups {
69+
propertyGroupMatch := true
70+
for _, testcase := range group.Cases {
71+
if !testcase.PropertyMatch(property) {
72+
propertyGroupMatch = false
73+
break
74+
}
75+
}
76+
if propertyGroupMatch {
77+
return group.Description
78+
}
79+
}
80+
return "Note: Apparent inconsistent use of \"property\": \"" + property + "\" within test case group!"
81+
}
82+
83+
// template applied to above data structure generates the Go test cases
84+
var tmpl = `package variablelengthquantity
85+
86+
{{.Header}}
87+
88+
// {{GroupComment .J.Groups "encode"}}
89+
var encodeTestCases = []struct {
90+
description string
91+
input []uint32
92+
output []byte
93+
}{ {{range .J.Groups}} {{range .Cases}}
94+
{{if .PropertyMatch "encode"}} {
95+
{{printf "%q" .Description}},
96+
{{printf "%#v" .Input }},
97+
{{byteSlice .Expected | printf "%#v" }},
98+
},{{- end}}{{end}}{{end}}
99+
}
100+
101+
// {{GroupComment .J.Groups "decode"}}
102+
var decodeTestCases = []struct {
103+
description string
104+
input []byte
105+
output []uint32 // nil slice indicates error expected.
106+
size int
107+
}{ {{range .J.Groups}} {{range .Cases}}
108+
{{if .PropertyMatch "decode"}} {
109+
{{printf "%q" .Description}},
110+
{{byteSlice .Input | printf "%#v" }},
111+
{{printf "%#v" .Expected }},
112+
{{lengthSlice .Input}},
113+
},{{- end}}{{end}}{{end}}
114+
}
115+
`
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package variablelengthquantity
2+
3+
// Source: exercism/x-common
4+
// Commit: d6a62f7 variable-length-quantity: Fix canonical-data.json formatting
5+
// x-common version: 1.0.0
6+
7+
// Encode a series of integers, producing a series of bytes.
8+
var encodeTestCases = []struct {
9+
description string
10+
input []uint32
11+
output []byte
12+
}{
13+
{
14+
"zero",
15+
[]uint32{0x0},
16+
[]byte{0x0},
17+
},
18+
{
19+
"arbitrary single byte",
20+
[]uint32{0x40},
21+
[]byte{0x40},
22+
},
23+
{
24+
"largest single byte",
25+
[]uint32{0x7f},
26+
[]byte{0x7f},
27+
},
28+
{
29+
"smallest double byte",
30+
[]uint32{0x80},
31+
[]byte{0x81, 0x0},
32+
},
33+
{
34+
"arbitrary double byte",
35+
[]uint32{0x2000},
36+
[]byte{0xc0, 0x0},
37+
},
38+
{
39+
"largest double byte",
40+
[]uint32{0x3fff},
41+
[]byte{0xff, 0x7f},
42+
},
43+
{
44+
"smallest triple byte",
45+
[]uint32{0x4000},
46+
[]byte{0x81, 0x80, 0x0},
47+
},
48+
{
49+
"arbitrary triple byte",
50+
[]uint32{0x100000},
51+
[]byte{0xc0, 0x80, 0x0},
52+
},
53+
{
54+
"largest triple byte",
55+
[]uint32{0x1fffff},
56+
[]byte{0xff, 0xff, 0x7f},
57+
},
58+
{
59+
"smallest quadruple byte",
60+
[]uint32{0x200000},
61+
[]byte{0x81, 0x80, 0x80, 0x0},
62+
},
63+
{
64+
"arbitrary quadruple byte",
65+
[]uint32{0x8000000},
66+
[]byte{0xc0, 0x80, 0x80, 0x0},
67+
},
68+
{
69+
"largest quadruple byte",
70+
[]uint32{0xfffffff},
71+
[]byte{0xff, 0xff, 0xff, 0x7f},
72+
},
73+
{
74+
"smallest quintuple byte",
75+
[]uint32{0x10000000},
76+
[]byte{0x81, 0x80, 0x80, 0x80, 0x0},
77+
},
78+
{
79+
"arbitrary quintuple byte",
80+
[]uint32{0xff000000},
81+
[]byte{0x8f, 0xf8, 0x80, 0x80, 0x0},
82+
},
83+
{
84+
"maximum 32-bit integer input",
85+
[]uint32{0xffffffff},
86+
[]byte{0x8f, 0xff, 0xff, 0xff, 0x7f},
87+
},
88+
{
89+
"two single-byte values",
90+
[]uint32{0x40, 0x7f},
91+
[]byte{0x40, 0x7f},
92+
},
93+
{
94+
"two multi-byte values",
95+
[]uint32{0x4000, 0x123456},
96+
[]byte{0x81, 0x80, 0x0, 0xc8, 0xe8, 0x56},
97+
},
98+
{
99+
"many multi-byte values",
100+
[]uint32{0x2000, 0x123456, 0xfffffff, 0x0, 0x3fff, 0x4000},
101+
[]byte{0xc0, 0x0, 0xc8, 0xe8, 0x56, 0xff, 0xff, 0xff, 0x7f, 0x0, 0xff, 0x7f, 0x81, 0x80, 0x0},
102+
},
103+
}
104+
105+
// Decode a series of bytes, producing a series of integers.
106+
var decodeTestCases = []struct {
107+
description string
108+
input []byte
109+
output []uint32 // nil slice indicates error expected.
110+
size int
111+
}{
112+
113+
{
114+
"one byte",
115+
[]byte{0x7f},
116+
[]uint32{0x7f},
117+
1,
118+
},
119+
{
120+
"two bytes",
121+
[]byte{0xc0, 0x0},
122+
[]uint32{0x2000},
123+
2,
124+
},
125+
{
126+
"three bytes",
127+
[]byte{0xff, 0xff, 0x7f},
128+
[]uint32{0x1fffff},
129+
3,
130+
},
131+
{
132+
"four bytes",
133+
[]byte{0x81, 0x80, 0x80, 0x0},
134+
[]uint32{0x200000},
135+
4,
136+
},
137+
{
138+
"maximum 32-bit integer",
139+
[]byte{0x8f, 0xff, 0xff, 0xff, 0x7f},
140+
[]uint32{0xffffffff},
141+
5,
142+
},
143+
{
144+
"incomplete sequence causes error",
145+
[]byte{0xff},
146+
[]uint32(nil),
147+
1,
148+
},
149+
{
150+
"incomplete sequence causes error, even if value is zero",
151+
[]byte{0x80},
152+
[]uint32(nil),
153+
1,
154+
},
155+
{
156+
"multiple values",
157+
[]byte{0xc0, 0x0, 0xc8, 0xe8, 0x56, 0xff, 0xff, 0xff, 0x7f, 0x0, 0xff, 0x7f, 0x81, 0x80, 0x0},
158+
[]uint32{0x2000, 0x123456, 0xfffffff, 0x0, 0x3fff, 0x4000},
159+
15,
160+
},
161+
}

exercises/variable-length-quantity/example.go

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package variablelengthquantity
22

3-
const testVersion = 2
3+
import "errors"
44

5-
// EncodeVarint returns the varint encoding of x.
6-
func EncodeVarint(x uint32) []byte {
5+
const testVersion = 3
6+
7+
var ErrUnterminatedSequence = errors.New("unterminated sequence")
8+
9+
// encodeInt returns the varint encoding of x.
10+
func encodeInt(x uint32) []byte {
711
if x>>7 == 0 {
812
return []byte{
913
byte(x),
@@ -25,34 +29,73 @@ func EncodeVarint(x uint32) []byte {
2529
}
2630
}
2731

32+
if x>>28 == 0 {
33+
return []byte{
34+
byte(0x80 | x>>21),
35+
byte(0x80 | x>>14),
36+
byte(0x80 | x>>6),
37+
byte(127 & x),
38+
}
39+
}
40+
2841
return []byte{
42+
byte(0x80 | x>>28),
2943
byte(0x80 | x>>21),
3044
byte(0x80 | x>>14),
3145
byte(0x80 | x>>7),
3246
byte(127 & x),
3347
}
3448
}
3549

36-
// DecodeVarint reads a varint-encoded integer from the slice.
50+
// decodeInt reads a varint-encoded integer from the slice.
3751
// It returns the integer and the number of bytes consumed, or
3852
// zero if there is not enough.
39-
func DecodeVarint(buf []byte) (x uint32, n int) {
53+
func decodeInt(buf []byte) (x uint32, n int, err error) {
4054
if len(buf) < 1 {
41-
return 0, 0
55+
return 0, 0, nil
4256
}
4357

44-
if buf[0] <= 0x80 {
45-
return uint32(buf[0]), 1
58+
if buf[0] < 0x80 {
59+
return uint32(buf[0]), 1, nil
4660
}
4761

4862
var b byte
4963
for n, b = range buf {
5064
x = x << 7
51-
x |= uint32(b) & 0x7F
65+
x |= uint32(b) & 0x7f
5266
if (b & 0x80) == 0 {
53-
return x, n + 1
67+
return x, n + 1, nil
5468
}
5569
}
5670

57-
return x, 0
71+
return x, 0, ErrUnterminatedSequence
72+
}
73+
74+
// EncodeVarint encodes a slice of uint32 into a var-int encoded bytes.
75+
func EncodeVarint(xa []uint32) []byte {
76+
result := make([]byte, 0)
77+
for _, x := range xa {
78+
result = append(result, encodeInt(x)...)
79+
}
80+
return result
81+
}
82+
83+
// DecodeVarint decodes a buffer of var-int encoded values into
84+
// a slice of uint32 values; an error is returned if buf doesn't
85+
// decode var-int successfully.
86+
func DecodeVarint(buf []byte) (ra []uint32, n int, err error) {
87+
if len(buf) == 0 {
88+
return []uint32{0}, 1, nil
89+
}
90+
usedBytes := 0
91+
ra = make([]uint32, 0)
92+
for usedBytes < len(buf) {
93+
r, nUsed, err := decodeInt(buf[usedBytes:])
94+
if err != nil {
95+
return nil, usedBytes, err
96+
}
97+
ra = append(ra, r)
98+
usedBytes += nUsed
99+
}
100+
return ra, usedBytes, nil
58101
}

0 commit comments

Comments
 (0)