Commit e319b67e authored by Luke Champine's avatar Luke Champine

100% encoding test coverage

parent 26f27534
......@@ -60,20 +60,29 @@ type Encoder struct {
w io.Writer
}
var (
ErrBadPointer = errors.New("cannot decode into invalid pointer")
)
// Encode writes the encoding of v to the stream. For encoding details, see
// the package docstring.
func (e *Encoder) Encode(v interface{}) error {
return e.encode(reflect.ValueOf(v))
}
// write catches instances where short writes do not return an error.
func (e *Encoder) write(p []byte) error {
n, err := e.w.Write(p)
if n != len(p) {
return io.ErrShortWrite
}
return err
}
func (e *Encoder) encode(val reflect.Value) error {
// check for MarshalSia interface first
if m, ok := val.Interface().(SiaMarshaler); ok {
return WritePrefix(e.w, m.MarshalSia())
} else if val.CanAddr() {
if m, ok := val.Addr().Interface().(SiaMarshaler); ok {
return WritePrefix(e.w, m.MarshalSia())
}
}
switch val.Kind() {
......@@ -87,23 +96,19 @@ func (e *Encoder) encode(val reflect.Value) error {
}
case reflect.Bool:
if val.Bool() {
_, err := e.w.Write([]byte{1})
return err
return e.write([]byte{1})
} else {
_, err := e.w.Write([]byte{0})
return err
return e.write([]byte{0})
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
_, err := e.w.Write(EncInt64(val.Int()))
return err
return e.write(EncInt64(val.Int()))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
_, err := e.w.Write(EncUint64(val.Uint()))
return err
return e.write(EncUint64(val.Uint()))
case reflect.String:
return WritePrefix(e.w, []byte(val.String()))
case reflect.Slice:
// slices are variable length, so prepend the length and then fallthrough to array logic
if _, err := e.w.Write(EncUint64(uint64(val.Len()))); err != nil {
if err := e.write(EncUint64(uint64(val.Len()))); err != nil {
return err
}
fallthrough
......@@ -114,8 +119,7 @@ func (e *Encoder) encode(val reflect.Value) error {
// can't just use Slice() because array may be unaddressable
slice := reflect.MakeSlice(reflect.SliceOf(val.Type().Elem()), val.Len(), val.Len())
reflect.Copy(slice, val)
_, err := e.w.Write(slice.Bytes())
return err
return e.write(slice.Bytes())
}
// normal slices/arrays are encoded by sequentially encoding their elements
for i := 0; i < val.Len(); i++ {
......@@ -144,6 +148,7 @@ func NewEncoder(w io.Writer) *Encoder {
// Marshal returns the encoding of v. For encoding details, see the package
// docstring.
// TODO: merge with MarshalAll?
func Marshal(v interface{}) []byte {
b := new(bytes.Buffer)
NewEncoder(b).Encode(v) // no error possible when using a bytes.Buffer
......@@ -182,7 +187,7 @@ func (d *Decoder) Decode(v interface{}) (err error) {
// v must be a pointer
pval := reflect.ValueOf(v)
if pval.Kind() != reflect.Ptr || pval.IsNil() {
return errors.New("must pass a valid pointer to Decode")
return ErrBadPointer
}
// catch decoding panics and convert them to errors
......
......@@ -2,14 +2,20 @@ package encoding
import (
"bytes"
"io"
"os"
"testing"
"github.com/NebulousLabs/Sia/build"
)
// dummy types to test encoding
type (
// basic
test0 struct {
B bool
I int32
U uint16
S string
}
// slice/array
......@@ -35,42 +41,131 @@ type (
test5 struct {
s string
}
// private field with pointer receiver
test6 struct {
s string
}
)
// here we use a single length byte, unlike the standard marshalling scheme
func (t test5) MarshalSia() []byte {
return []byte(t.s)
}
func (t test5) MarshalSia() []byte { return []byte(t.s) }
func (t *test5) UnmarshalSia(b []byte) {
t.s = string(b)
}
func (t *test5) UnmarshalSia(b []byte) { t.s = string(b) }
func (t *test6) MarshalSia() []byte { return []byte(t.s) }
func (t *test6) UnmarshalSia(b []byte) { t.s = string(b) }
var testStructs = []interface{}{
test0{65537, "foo"},
test0{false, 65537, 256, "foo"},
test1{[]int32{1, 2, 3}, []byte("foo"), [3]string{"foo", "bar", "baz"}, [3]byte{'f', 'o', 'o'}},
test2{test0{65537, "foo"}},
test3{test2{test0{65537, "foo"}}},
test4{&test0{65537, "foo"}},
test2{test0{false, 65537, 256, "foo"}},
test3{test2{test0{false, 65537, 256, "foo"}}},
test4{&test0{false, 65537, 256, "foo"}},
test5{"foo"},
&test6{"foo"},
}
func TestMarshaling(t *testing.T) {
var emptyStructs = []interface{}{&test0{}, &test1{}, &test2{}, &test3{}, &test4{}, &test5{}}
var testEncodings = [][]byte{
{0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 'f', 'o', 'o'},
{3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0,
0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 'f', 'o', 'o', 3, 0, 0, 0, 0, 0, 0, 0, 'f', 'o', 'o', 3,
0, 0, 0, 0, 0, 0, 0, 'b', 'a', 'r', 3, 0, 0, 0, 0, 0, 0, 0, 'b', 'a', 'z', 'f', 'o', 'o'},
{0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 'f', 'o', 'o'},
{0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 'f', 'o', 'o'},
{1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 'f', 'o', 'o'},
{3, 0, 0, 0, 0, 0, 0, 0, 'f', 'o', 'o'},
{3, 0, 0, 0, 0, 0, 0, 0, 'f', 'o', 'o'},
}
func TestEncode(t *testing.T) {
// use Marshal for convenience
for i := range testStructs {
b := Marshal(testStructs[i])
err := Unmarshal(b, emptyStructs[i])
if bytes.Compare(b, testEncodings[i]) != 0 {
t.Errorf("bad encoding of testStructs[%d]: \nexp:\t%v\ngot:\t%v", i, testEncodings[i], b)
}
}
// badWriter should fail on every encode
enc := NewEncoder(new(badWriter))
for i := range testStructs {
err := enc.Encode(testStructs[i])
if err != io.ErrShortWrite {
t.Error("expected ErrShortWrite, got", err)
}
}
// special case, not covered by testStructs
err := enc.Encode(struct{ U [3]uint16 }{[3]uint16{1, 2, 3}})
if err != io.ErrShortWrite {
t.Error("expected ErrShortWrite, got", err)
}
// bad type
defer func() {
if recover() == nil {
t.Error("expected panic, got nil")
}
}()
enc.Encode(map[int]int{})
}
func TestDecode(t *testing.T) {
// use Unmarshal for convenience
var emptyStructs = []interface{}{&test0{}, &test1{}, &test2{}, &test3{}, &test4{}, &test5{}, &test6{}}
for i := range testEncodings {
err := Unmarshal(testEncodings[i], emptyStructs[i])
if err != nil {
t.Error(err)
}
//t.Log("\n", testStructs[i], "\n", emptyStructs[i])
}
// bad boolean
err := Unmarshal([]byte{3}, new(bool))
if err == nil {
t.Error("expected error, got nil")
}
// non-pointer
err = Unmarshal([]byte{1, 2, 3}, "foo")
if err != ErrBadPointer {
t.Error("expected ErrBadPointer, got", err)
}
// unknown type
err = Unmarshal([]byte{1, 2, 3}, new(map[int]int))
if err == nil {
t.Error("expected error, got nil")
}
// badReader should fail on every decode
dec := NewDecoder(new(badReader))
for i := range testEncodings {
err := dec.Decode(emptyStructs[i])
if err == nil {
t.Error("expected error, got nil")
}
}
// special case, not covered by testStructs
err = dec.Decode(new([3]byte))
if err == nil {
t.Error("expected error, got nil")
}
}
func TestEncoding(t *testing.T) {
var emptyStructs = []interface{}{&test0{}, &test1{}, &test2{}, &test3{}, &test4{}, &test5{}}
func TestMarshalUnmarshal(t *testing.T) {
var emptyStructs = []interface{}{&test0{}, &test1{}, &test2{}, &test3{}, &test4{}, &test5{}, &test6{}}
for i := range testStructs {
b := Marshal(testStructs[i])
err := Unmarshal(b, emptyStructs[i])
if err != nil {
t.Error(err)
}
}
}
func TestEncodeDecode(t *testing.T) {
var emptyStructs = []interface{}{&test0{}, &test1{}, &test2{}, &test3{}, &test4{}, &test5{}, &test6{}}
b := new(bytes.Buffer)
enc := NewEncoder(b)
dec := NewDecoder(b)
......@@ -80,6 +175,43 @@ func TestEncoding(t *testing.T) {
if err != nil {
t.Error(err)
}
//t.Log("\n", testStructs[i], "\n", emptyStructs[i])
}
}
func TestMarshalAll(t *testing.T) {
var b []byte
for i := range testStructs {
b = append(b, Marshal(testStructs[i])...)
}
expected := MarshalAll(testStructs...)
if bytes.Compare(b, expected) != 0 {
t.Error("expected %v, got %v", expected, b)
}
}
func TestReadWriteFile(t *testing.T) {
// standard
path := build.TempDir("TestReadWriteFile")
err := WriteFile(path, testStructs[3])
if err != nil {
t.Fatal(err)
}
defer os.Remove(path)
var obj test4
err = ReadFile(path, &obj)
if err != nil {
t.Error(err)
}
// bad paths
err = WriteFile("/foo/bar", "baz")
if err == nil {
t.Error("expected error, got nil")
}
err = ReadFile("/foo/bar", nil)
if err == nil {
t.Error("expected error, got nil")
}
}
......@@ -6,6 +6,16 @@ import (
"testing"
)
// badReader/Writer used to test error handling
type badReader struct{}
func (br *badReader) Read([]byte) (int, error) { return 0, io.EOF }
type badWriter struct{}
func (bw *badWriter) Write([]byte) (int, error) { return 0, nil }
func TestReadPrefix(t *testing.T) {
b := new(bytes.Buffer)
......@@ -88,10 +98,6 @@ func TestReadObject(t *testing.T) {
}
}
type badWriter struct{}
func (bw *badWriter) Write([]byte) (int, error) { return 0, nil }
func TestWritePrefix(t *testing.T) {
b := new(bytes.Buffer)
......@@ -132,8 +138,7 @@ func TestWriteObject(t *testing.T) {
}
}
// feed writers into readers
func TestReadWrite(t *testing.T) {
func TestReadWritePrefix(t *testing.T) {
b := new(bytes.Buffer)
// WritePrefix -> ReadPrefix
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment