diff --git a/block_test.go b/block_test.go index 8a86fc1..df9d6b4 100644 --- a/block_test.go +++ b/block_test.go @@ -19,6 +19,8 @@ import ( "io" "reflect" "testing" + + "github.com/at-wat/ebml-go/internal/errs" ) func TestUnmarshalBlock(t *testing.T) { @@ -64,10 +66,10 @@ func TestUnmarshalBlock(t *testing.T) { t.Run(n, func(t *testing.T) { block, err := UnmarshalBlock(bytes.NewBuffer(c.input), int64(len(c.input))) if err != nil { - t.Fatalf("Failed to unmarshal block: %v", err) + t.Fatalf("Failed to unmarshal block: '%v'", err) } if !reflect.DeepEqual(c.expected, *block) { - t.Errorf("Unexpected unmarshal result, expected: %v, got: %v", c.expected, *block) + t.Errorf("Expected unmarshal result: '%v', got: '%v'", c.expected, *block) } }) } @@ -77,9 +79,9 @@ func TestUnmarshalBlock_Error(t *testing.T) { t.Run("EOF", func(t *testing.T) { input := []byte{0x21, 0x23, 0x45, 0x00, 0x02, 0x00} for l := 0; l < len(input); l++ { - if _, err := UnmarshalBlock(bytes.NewBuffer(input[:l]), int64(len(input))); err != io.ErrUnexpectedEOF { - t.Errorf("UnmarshalBlock should return %v against short data (%d bytes), but got %v", - io.ErrUnexpectedEOF, l, err) + if _, err := UnmarshalBlock(bytes.NewBuffer(input[:l]), int64(len(input))); !errs.Is(err, io.ErrUnexpectedEOF) { + t.Errorf("Short data (%d bytes) expected error: '%v', got: '%v'", + l, io.ErrUnexpectedEOF, err) } } }) @@ -95,8 +97,8 @@ func TestUnmarshalBlock_Error(t *testing.T) { for n, c := range testCases { t.Run(n, func(t *testing.T) { _, err := UnmarshalBlock(bytes.NewBuffer(c.input), int64(len(c.input))) - if err != c.err { - t.Errorf("Unexpected error, expected: %v, got: %v", c.err, err) + if !errs.Is(err, c.err) { + t.Errorf("Expected error: '%v', got: '%v'", c.err, err) } }) } @@ -125,10 +127,10 @@ func TestMarshalBlock(t *testing.T) { var b bytes.Buffer err := MarshalBlock(&c.input, &b) if err != nil { - t.Fatalf("Failed to marshal block: %v", err) + t.Fatalf("Failed to marshal block: '%v'", err) } if !reflect.DeepEqual(c.expected, b.Bytes()) { - t.Errorf("Unexpected marshal result, expected: %v, got: %v", c.expected, b.Bytes()) + t.Errorf("Expected marshal result: '%v', got: '%v'", c.expected, b.Bytes()) } }) } @@ -142,7 +144,7 @@ func TestMarshalBlock_Error(t *testing.T) { for l := 0; l < 7; l++ { err := MarshalBlock(input, &limitedDummyWriter{limit: l}) if err != bytes.ErrTooLarge { - t.Errorf("UnmarshalBlock should bytes.ErrTooLarge against too large data (Writer size limit: %d), but got %v", l, err) + t.Errorf("Expected error against too large data (Writer size limit: %d): '%v', got: '%v'", l, bytes.ErrTooLarge, err) } } }, diff --git a/elementtype.go b/elementtype.go index 4af85e1..626b0b7 100644 --- a/elementtype.go +++ b/elementtype.go @@ -549,6 +549,6 @@ func ElementTypeFromString(s string) (ElementType, error) { return ElementTimecode, nil default: - return 0, ErrUnknownElementName + return 0, wrapErrorf(ErrUnknownElementName, "parsing \"%s\"", s) } } diff --git a/elementtype_test.go b/elementtype_test.go index 74440dd..231893e 100644 --- a/elementtype_test.go +++ b/elementtype_test.go @@ -9,7 +9,7 @@ func TestElementType_Roundtrip(t *testing.T) { for e := ElementInvalid + 1; e < elementMax; e++ { s := e.String() if el, err := ElementTypeFromString(s); err != nil { - t.Errorf("Failed to get ElementType from string: %v", err) + t.Errorf("Failed to get ElementType from string: '%v'", err) } else if e != el { t.Errorf("Failed to roundtrip ElementType %d and string", e) } @@ -20,9 +20,9 @@ func TestElementType_Bytes(t *testing.T) { expected := []byte{0x18, 0x53, 0x80, 0x67} if !bytes.Equal(ElementSegment.Bytes(), expected) { - t.Errorf("Unexpected bytes, expected: %v, got: %v", expected, ElementSegment.Bytes()) + t.Errorf("Expected bytes: '%v', got: '%v'", expected, ElementSegment.Bytes()) } if ElementSegment.DataType() != DataTypeMaster { - t.Errorf("Unexpected DataType, expected: %s, got: %s", DataTypeMaster, ElementSegment.DataType()) + t.Errorf("Expected DataType: %s, got: %s", DataTypeMaster, ElementSegment.DataType()) } } diff --git a/error.go b/error.go new file mode 100644 index 0000000..99b496e --- /dev/null +++ b/error.go @@ -0,0 +1,87 @@ +// Copyright 2019 The ebml-go authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ebml + +import ( + "fmt" + "reflect" +) + +// Error records a failed parsing. +type Error struct { + Err error + Failure string +} + +func (e *Error) Error() string { + // TODO: migrate to fmt.Sprintf %w once Go1.12 reaches EOL. + return e.Failure + ": " + e.Err.Error() +} + +// Unwrap returns the reason of the failure. +// This is for Go1.13 error unwrapping. +func (e *Error) Unwrap() error { + return e.Err +} + +// Is reports whether chained error contains target. +// This is for Go1.13 error unwrapping. +func (e *Error) Is(target error) bool { + err := e.Err + + switch target { + case e: + return true + case nil: + return err == nil + } + for { + switch err { + case nil: + return false + case target: + return true + } + x, ok := err.(interface{ Unwrap() error }) + if !ok { + // Some stdlibs haven't have error unwrapper yet. + // Check err.Err field if exposed. + if reflect.TypeOf(err).Kind() == reflect.Ptr { + e := reflect.ValueOf(err).Elem().FieldByName("Err") + if e.IsValid() { + e2, ok := e.Interface().(error) + if !ok { + return false + } + err = e2 + continue + } + } + return false + } + err = x.Unwrap() + } +} + +func wrapError(err error, failure string) error { + return &Error{ + Failure: failure, + Err: err, + } +} + +func wrapErrorf(err error, failureFmt string, v ...interface{}) error { + return wrapError(err, fmt.Sprintf(failureFmt, v...)) +} diff --git a/error_test.go b/error_test.go new file mode 100644 index 0000000..a781216 --- /dev/null +++ b/error_test.go @@ -0,0 +1,83 @@ +// Copyright 2019 The ebml-go authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ebml + +import ( + "errors" + "testing" + + "github.com/at-wat/ebml-go/internal/errs" +) + +type dummyError struct { + Err error +} + +func (e *dummyError) Error() string { + return e.Err.Error() +} + +func TestError(t *testing.T) { + errBase := errors.New("an error") + errOther := errors.New("an another error") + errChained := wrapErrorf(errBase, "info") + errChainedNil := wrapErrorf(nil, "info") + errChainedOther := wrapErrorf(errOther, "info") + err112Chained := wrapErrorf(&dummyError{errBase}, "info") + err112Nil := wrapErrorf(&dummyError{nil}, "info") + errStr := "info: an error" + + t.Run("ErrorsIs", func(t *testing.T) { + if !errs.Is(errChained, errBase) { + t.Errorf("Wrapped error '%v' doesn't chain '%v'", errChained, errBase) + } + }) + + t.Run("Is", func(t *testing.T) { + if !errChained.(*Error).Is(errChained) { + t.Errorf("Wrapped error '%v' doesn't match its-self", errChained) + } + if !errChained.(*Error).Is(errBase) { + t.Errorf("Wrapped error '%v' doesn't match '%v'", errChained, errBase) + } + if !err112Chained.(*Error).Is(errBase) { + t.Errorf("Wrapped error '%v' doesn't match '%v'", + err112Chained, errBase) + } + if !errChainedNil.(*Error).Is(nil) { + t.Errorf("Nil chained error '%v' doesn't match 'nil'", errChainedNil) + } + + if errChainedNil.(*Error).Is(errBase) { + t.Errorf("Wrapped error '%v' unexpectedly matched '%v'", + errChainedNil, errBase) + } + if errChainedOther.(*Error).Is(errBase) { + t.Errorf("Wrapped error '%v' unexpectedly matched '%v'", + errChainedOther, errBase) + } + if err112Nil.(*Error).Is(errBase) { + t.Errorf("Wrapped error '%v' unexpectedly matched '%v'", + errChainedOther, errBase) + } + }) + + if errChained.Error() != errStr { + t.Errorf("Error string expected: %s, got: %s", errStr, errChained.Error()) + } + if errChained.(*Error).Unwrap() != errBase { + t.Errorf("Unwrapped error expected: %s, got: %s", errBase, errChained.(*Error).Unwrap()) + } +} diff --git a/internal/buffercloser/buffercloser_test.go b/internal/buffercloser/buffercloser_test.go index 9d3da72..8b27463 100644 --- a/internal/buffercloser/buffercloser_test.go +++ b/internal/buffercloser/buffercloser_test.go @@ -38,7 +38,7 @@ func TestBufferCloser(t *testing.T) { } if !bytes.Equal(data, buf.Bytes()) { - t.Errorf("Unexpected bytes in the buffer, expected: %d, got: %d", data, buf.Bytes()) + t.Errorf("Expected bytes in the buffer: %d, got: %d", data, buf.Bytes()) } if err := buf.Close(); err != nil { diff --git a/internal/errs/errors_112.go b/internal/errs/errors_112.go new file mode 100644 index 0000000..939adf1 --- /dev/null +++ b/internal/errs/errors_112.go @@ -0,0 +1,40 @@ +// +build !go1.13 + +// Copyright 2019 The ebml-go authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package errs + +// Is compares error type. Works like Go1.13 errors.Is(). +func Is(err, target error) bool { + if target == nil { + return err == nil + } + for { + if err == target { + return true + } + if err == nil { + return false + } + if x, ok := err.(interface{ Is(error) bool }); ok { + return x.Is(target) + } + x, ok := err.(interface{ Unwrap() error }) + if !ok { + return false + } + err = x.Unwrap() + } +} diff --git a/internal/errs/errors_113.go b/internal/errs/errors_113.go new file mode 100644 index 0000000..f114101 --- /dev/null +++ b/internal/errs/errors_113.go @@ -0,0 +1,27 @@ +// +build go1.13 + +// Copyright 2019 The ebml-go authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package errs is for compatibility with Go1.13 error wrapping. +package errs + +import ( + "errors" +) + +// Is compares error type. Wrapping Go1.13 errors.Is(). +func Is(err, target error) bool { + return errors.Is(err, target) +} diff --git a/internal/errs/errors_test.go b/internal/errs/errors_test.go new file mode 100644 index 0000000..7101104 --- /dev/null +++ b/internal/errs/errors_test.go @@ -0,0 +1,83 @@ +// Copyright 2019 The ebml-go authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package errs + +import ( + "errors" + "testing" +) + +type dummyError113 struct { + Err error +} + +func (e *dummyError113) Error() string { + return "dummy: " + e.Err.Error() +} + +func (e *dummyError113) Unwrap() error { + return e.Err +} + +type dummyError113Is struct { + Err error +} + +func (e *dummyError113Is) Error() string { + return "dummy: " + e.Err.Error() +} + +func (e *dummyError113Is) Is(target error) bool { + return e.Err == target +} + +func TestIs(t *testing.T) { + errBase := errors.New("an error") + errOther := errors.New("other error") + errChained113Base := &dummyError113{errBase} + errChained113Other := &dummyError113{errOther} + errChained113Nil := &dummyError113{} + errChained113IsBase := &dummyError113Is{errBase} + errChained113IsOther := &dummyError113Is{errOther} + errChained113IsNil := &dummyError113Is{} + + cases := []struct { + err error + target error + is bool + }{ + {nil, nil, true}, + {errBase, errBase, true}, + {errChained113Base, errBase, true}, + {errChained113IsBase, errBase, true}, + {errOther, errBase, false}, + {nil, errBase, false}, + {errBase, nil, false}, + {errChained113Other, errBase, false}, + {errChained113IsOther, errBase, false}, + {errChained113Nil, errBase, false}, + {errChained113IsNil, errBase, false}, + } + + for _, c := range cases { + if Is(c.err, c.target) != c.is { + if c.is { + t.Errorf("Expected '%v' is '%v', but is not", c.err, c.target) + } else { + t.Errorf("Expected '%v' is not '%v', but is", c.err, c.target) + } + } + } +} diff --git a/lacer.go b/lacer.go index c275b29..f379d37 100644 --- a/lacer.go +++ b/lacer.go @@ -49,7 +49,7 @@ func (l *noLacer) Write(b [][]byte) error { case nFrames == 0: return nil case nFrames != 1: - return ErrTooManyFrames + return wrapErrorf(ErrTooManyFrames, "lacing %d frames by no-lacer", nFrames) } _, err := l.w.Write(b[0]) return err @@ -61,7 +61,7 @@ func (l *xiphLacer) Write(b [][]byte) error { case nFrames == 0: return nil case nFrames > 0xFF: - return ErrTooManyFrames + return wrapErrorf(ErrTooManyFrames, "lacing %d frames", nFrames) } size := []byte{byte(nFrames - 1)} for i := 0; i < nFrames-1; i++ { @@ -88,11 +88,13 @@ func (l *fixedLacer) Write(b [][]byte) error { case nFrames == 0: return nil case nFrames > 0xFF: - return ErrTooManyFrames + return wrapErrorf(ErrTooManyFrames, "lacing %d frames", nFrames) } for i := 1; i < nFrames; i++ { if len(b[i]) != len(b[0]) { - return ErrUnevenFixedLace + return wrapErrorf( + ErrUnevenFixedLace, "lacing %d bytes on %d bytes frame", len(b[i]), len(b[0]), + ) } } if _, err := l.w.Write([]byte{byte(nFrames - 1)}); err != nil { @@ -112,7 +114,7 @@ func (l *ebmlLacer) Write(b [][]byte) error { case nFrames == 0: return nil case nFrames > 0xFF: - return ErrTooManyFrames + return wrapErrorf(ErrTooManyFrames, "lacing %d frames", nFrames) } size := []byte{byte(nFrames - 1)} for i := 0; i < nFrames-1; i++ { diff --git a/lacer_test.go b/lacer_test.go index d11e21b..da6ef47 100644 --- a/lacer_test.go +++ b/lacer_test.go @@ -18,6 +18,8 @@ import ( "bytes" "io" "testing" + + "github.com/at-wat/ebml-go/internal/errs" ) func TestLacer(t *testing.T) { @@ -151,11 +153,11 @@ func TestLacer(t *testing.T) { var buf bytes.Buffer l := c.newLacer(&buf) err := l.Write(c.frames) - if !isErr(err, c.err) { - t.Fatalf("Unexpected error, expected: %v, got: %v", c.err, err) + if !errs.Is(err, c.err) { + t.Fatalf("Expected error: '%v', got: '%v'", c.err, err) } if !bytes.Equal(c.b, buf.Bytes()) { - t.Errorf("Unexpected data, \nexpected: %v, \n got: %v", c.b, buf.Bytes()) + t.Errorf("Expected data: %v, \n got: %v", c.b, buf.Bytes()) } }) } @@ -176,8 +178,8 @@ func TestLacer_WriterError(t *testing.T) { t.Run(name, func(t *testing.T) { for l := 0; l < c.n-1; l++ { lacer := c.newLacer(&limitedDummyWriter{limit: l}) - if err := lacer.Write(c.frames); err != bytes.ErrTooLarge { - t.Errorf("Lacer should fail with bytes.ErrTooLarge against too large data (Writer size limit: %d), but got %v", l, err) + if err := lacer.Write(c.frames); !errs.Is(err, bytes.ErrTooLarge) { + t.Errorf("Expected error against too large data (Writer size limit: %d): '%v', got '%v'", l, bytes.ErrTooLarge, err) } } }) diff --git a/marshal.go b/marshal.go index af0c121..a7cb790 100644 --- a/marshal.go +++ b/marshal.go @@ -60,7 +60,7 @@ func Marshal(val interface{}, w io.Writer, opts ...MarshalOption) error { } vo := reflect.ValueOf(val) if vo.Kind() != reflect.Ptr { - return ErrInvalidType + return wrapErrorf(ErrInvalidType, "marshalling to %T", val) } _, err := marshalImpl(vo.Elem(), w, 0, nil, options) @@ -129,7 +129,7 @@ func marshalImpl(vo reflect.Value, w io.Writer, pos uint64, parent *Element, opt } e, ok := table[t] if !ok { - return pos, ErrUnsupportedElement + return pos, wrapErrorf(ErrUnsupportedElement, "marshalling \"%s\"", t) } unknown := tag.size == SizeUnknown @@ -227,10 +227,14 @@ func marshalImpl(vo reflect.Value, w io.Writer, pos uint64, parent *Element, opt } lst, ok := pealElem(val, e.t == DataTypeBinary, tag.omitEmpty) if !ok { - return pos, ErrIncompatibleType + return pos, wrapErrorf( + ErrIncompatibleType, "marshalling %s from channel", val.Type(), + ) } if len(lst) != 1 { - return pos, ErrIncompatibleType + return pos, wrapErrorf( + ErrIncompatibleType, "marshalling %s from channel", val.Type(), + ) } pos, err = writeOne(lst[0]) } diff --git a/marshal_roundtrip_test.go b/marshal_roundtrip_test.go index 212042e..ccdb5ed 100644 --- a/marshal_roundtrip_test.go +++ b/marshal_roundtrip_test.go @@ -97,14 +97,14 @@ func TestMarshal_RoundtripWebM(t *testing.T) { var b bytes.Buffer if err := ebml.Marshal(&webm0, &b); err != nil { - t.Fatalf("Failed to Marshal: %v", err) + t.Fatalf("Failed to Marshal: '%v'", err) } var webm1 struct { Header webm.EBMLHeader `ebml:"EBML"` Segment webm.Segment `ebml:"Segment,size=unknown"` } if err := ebml.Unmarshal(bytes.NewBuffer(b.Bytes()), &webm1); err != nil { - t.Fatalf("Failed to Unmarshal: %v", err) + t.Fatalf("Failed to Unmarshal: '%v'", err) } if !reflect.DeepEqual(webm0, webm1) { diff --git a/marshal_test.go b/marshal_test.go index 2c8e8e2..be2fd24 100644 --- a/marshal_test.go +++ b/marshal_test.go @@ -20,6 +20,8 @@ import ( "fmt" "reflect" "testing" + + "github.com/at-wat/ebml-go/internal/errs" ) func TestMarshal(t *testing.T) { @@ -198,7 +200,7 @@ func TestMarshal(t *testing.T) { t.Run(n, func(t *testing.T) { var b bytes.Buffer if err := Marshal(c.input, &b); err != nil { - t.Fatalf("Unexpected error: %+v", err) + t.Fatalf("Unexpected error: '%v'", err) } if !bytes.Equal(c.expected, b.Bytes()) { t.Errorf("Marshaled binary doesn't match:\n expected: %v,\n got: %v", c.expected, b.Bytes()) @@ -226,8 +228,8 @@ func TestMarshal_Error(t *testing.T) { for n, c := range testCases { t.Run(n, func(t *testing.T) { var b bytes.Buffer - if err := Marshal(c.input, &b); err != c.err { - t.Fatalf("Unexpected error, expected: %v, got: %v", c.err, err) + if err := Marshal(c.input, &b); !errs.Is(err, c.err) { + t.Fatalf("Expected error: '%v', got: '%v'", c.err, err) } }) } @@ -241,7 +243,7 @@ func TestMarshal_OptionError(t *testing.T) { }, ) if err != errExpected { - t.Errorf("Unexpected error for failing MarshalOption, expected: %v, got: %v", errExpected, err) + t.Errorf("Expected error against failing MarshalOption: '%v', got: '%v'", errExpected, err) } } @@ -256,8 +258,8 @@ func TestMarshal_WriterError(t *testing.T) { for l := 0; l < 25; l++ { err := Marshal(&s, &limitedDummyWriter{limit: l}) - if err != bytes.ErrTooLarge { - t.Errorf("UnmarshalBlock should fail with bytes.ErrTooLarge against too large data (Writer size limit: %d), but got %v", l, err) + if !errs.Is(err, bytes.ErrTooLarge) { + t.Errorf("Expected error against too large data (Writer size limit: %d): '%v', got '%v'", l, bytes.ErrTooLarge, err) } } } @@ -280,7 +282,7 @@ func TestMarshal_WithWriteHooks(t *testing.T) { hook := withElementMap(m) err := Marshal(&s, &bytes.Buffer{}, WithElementWriteHooks(hook)) if err != nil { - t.Errorf("Unexpected error: %v", err) + t.Errorf("Unexpected error: '%v'", err) } expected := map[string][]uint64{ @@ -381,10 +383,10 @@ func TestMarshal_Tag(t *testing.T) { var bTagged, bUntagged bytes.Buffer if err := Marshal(&tagged, &bTagged); err != nil { - t.Fatalf("Unexpected error: %+v", err) + t.Fatalf("Unexpected error: '%v'", err) } if err := Marshal(&untagged, &bUntagged); err != nil { - t.Fatalf("Unexpected error: %+v", err) + t.Fatalf("Unexpected error: '%v'", err) } if !bytes.Equal(bTagged.Bytes(), bUntagged.Bytes()) { @@ -400,8 +402,8 @@ func TestMarshal_InvalidTag(t *testing.T) { } var buf bytes.Buffer - if err := Marshal(&input, &buf); err != ErrInvalidTag { - t.Errorf("Unexpected error against invalid tag, expected: %v, got: %v", ErrInvalidTag, err) + if err := Marshal(&input, &buf); !errs.Is(err, ErrInvalidTag) { + t.Errorf("Expected error against invalid tag: '%v', got: '%v'", ErrInvalidTag, err) } } @@ -431,7 +433,7 @@ func TestMarshal_Chan(t *testing.T) { var b bytes.Buffer if err := Marshal(input, &b); err != nil { - t.Fatalf("Unexpected error: %+v", err) + t.Fatalf("Unexpected error: '%v'", err) } if !bytes.Equal(expected, b.Bytes()) { t.Errorf("Marshaled binary doesn't match:\n expected: %v,\n got: %v", expected, b.Bytes()) @@ -453,7 +455,7 @@ func TestMarshal_Chan(t *testing.T) { var b bytes.Buffer if err := Marshal(input, &b); err != nil { - t.Fatalf("Unexpected error: %+v", err) + t.Fatalf("Unexpected error: '%v'", err) } if !bytes.Equal(expected, b.Bytes()) { t.Errorf("Marshaled binary doesn't match:\n expected: %v,\n got: %v", expected, b.Bytes()) @@ -465,8 +467,8 @@ func TestMarshal_Chan(t *testing.T) { ch <- nil close(ch) - if err := Marshal(input, &bytes.Buffer{}); err != ErrIncompatibleType { - t.Fatalf("Expected %v, got %v", ErrIncompatibleType, err) + if err := Marshal(input, &bytes.Buffer{}); !errs.Is(err, ErrIncompatibleType) { + t.Fatalf("Expected error: '%v', got: '%v'", ErrIncompatibleType, err) } }) }) @@ -481,8 +483,8 @@ func TestMarshal_Chan(t *testing.T) { ch <- make([]Cluster, 2) close(ch) - if err := Marshal(input, &bytes.Buffer{}); err != ErrIncompatibleType { - t.Fatalf("Expected %v, got %v", ErrIncompatibleType, err) + if err := Marshal(input, &bytes.Buffer{}); !errs.Is(err, ErrIncompatibleType) { + t.Fatalf("Expected error: '%v', got: '%v'", ErrIncompatibleType, err) } }) } @@ -508,7 +510,7 @@ func BenchmarkMarshal(b *testing.B) { for i := 0; i < b.N; i++ { var buf bytes.Buffer if err := Marshal(&s, &buf); err != nil { - b.Fatalf("Unexpected error: %+v", err) + b.Fatalf("Unexpected error: '%v'", err) } } } diff --git a/mkvcore/blockwriter_test.go b/mkvcore/blockwriter_test.go index b34d4b5..ac234e4 100644 --- a/mkvcore/blockwriter_test.go +++ b/mkvcore/blockwriter_test.go @@ -24,6 +24,7 @@ import ( "github.com/at-wat/ebml-go" "github.com/at-wat/ebml-go/internal/buffercloser" + "github.com/at-wat/ebml-go/internal/errs" ) func TestBlockWriter(t *testing.T) { @@ -37,7 +38,7 @@ func TestBlockWriter(t *testing.T) { WithBlockInterceptor(NewMultiTrackBlockSorter(10, BlockSorterDropOutdated)), ) if err != nil { - t.Fatalf("Failed to create BlockWriter: %v", err) + t.Fatalf("Failed to create BlockWriter: '%v'", err) } if len(ws) != len(tracks) { @@ -45,28 +46,28 @@ func TestBlockWriter(t *testing.T) { } if n, err := ws[1].Write(true, 110, []byte{0x03, 0x04, 0x05}); err != nil { - t.Fatalf("Failed to Write: %v", err) + t.Fatalf("Failed to Write: '%v'", err) } else if n != 3 { - t.Errorf("Unexpected return value of BlockWriter.Write, expected: 3, got: %d", n) + t.Errorf("Expected return value of BlockWriter.Write: 3, got: %d", n) } if n, err := ws[0].Write(false, 100, []byte{0x01, 0x02}); err != nil { - t.Fatalf("Failed to Write: %v", err) + t.Fatalf("Failed to Write: '%v'", err) } else if n != 2 { - t.Errorf("Unexpected return value of BlockWriter.Write, expected: 2, got: %d", n) + t.Errorf("Expected return value of BlockWriter.Write: 2, got: %d", n) } // Ignored due to old timestamp if n, err := ws[0].Write(true, -32769, []byte{0x0A}); err != nil { - t.Fatalf("Failed to Write: %v", err) + t.Fatalf("Failed to Write: '%v'", err) } else if n != 1 { - t.Errorf("Unexpected return value of BlockWriter.Write, expected: 1, got: %d", n) + t.Errorf("Expected return value of BlockWriter.Write: 1, got: %d", n) } if n, err := ws[0].Write(true, 130, []byte{0x06}); err != nil { - t.Fatalf("Failed to Write: %v", err) + t.Fatalf("Failed to Write: '%v'", err) } else if n != 1 { - t.Errorf("Unexpected return value of BlockWriter.Write, expected: 1, got: %d", n) + t.Errorf("Expected return value of BlockWriter.Write: 1, got: %d", n) } ws[0].Close() @@ -116,7 +117,7 @@ func TestBlockWriter(t *testing.T) { Segment flexSegment `ebml:"Segment,size=unknown"` } if err := ebml.Unmarshal(bytes.NewReader(buf.Bytes()), &result); err != nil { - t.Fatalf("Failed to Unmarshal resultant binary: %v", err) + t.Fatalf("Failed to Unmarshal resultant binary: '%v'", err) } if !reflect.DeepEqual(expected, result) { t.Errorf("Unexpected data,\nexpected: %+v\n got: %+v", expected, result) @@ -137,11 +138,11 @@ func TestBlockWriter_Options(t *testing.T) { WithSeekHead(false), ) if err != nil { - t.Fatalf("Failed to create BlockWriter: %v", err) + t.Fatalf("Failed to create BlockWriter: '%v'", err) } if len(ws) != 1 { - t.Fatalf("Number of the returned writer must be 1, but got %d", len(ws)) + t.Fatalf("Number of the returned writer must be 1, got %d", len(ws)) } ws[0].Close() @@ -203,8 +204,8 @@ func TestBlockWriter_FailingOptions(t *testing.T) { t.Run(name, func(t *testing.T) { buf := buffercloser.New() _, err := NewSimpleBlockWriter(buf, []TrackDescription{}, c.opts...) - if err != c.err { - t.Errorf("Unexpected error, expected: %v, got: %v", c.err, err) + if !errs.Is(err, c.err) { + t.Errorf("Expected error: '%v', got: '%v'", c.err, err) } }) } @@ -291,16 +292,16 @@ func TestBlockWriter_ErrorHandling(t *testing.T) { ) if err != nil { if errAt == atBeginning { - if err != bytes.ErrTooLarge { - t.Fatalf("Unexpected error, expected: %v, got: %v", bytes.ErrTooLarge, err) + if !errs.Is(err, bytes.ErrTooLarge) { + t.Fatalf("Expected error: '%v', got: '%v'", bytes.ErrTooLarge, err) } return } - t.Fatalf("Failed to create SimpleWriter: %v", err) + t.Fatalf("Failed to create SimpleWriter: '%v'", err) } if len(ws) != 1 { - t.Fatalf("Number of the returned writer must be 1, but got %d", len(ws)) + t.Fatalf("Number of the returned writer must be 1, got %d", len(ws)) } if errAt == atClusterWriting { @@ -308,17 +309,17 @@ func TestBlockWriter_ErrorHandling(t *testing.T) { } clearErr() if _, err := ws[0].Write(false, 100, []byte{0x01, 0x02}); err != nil { - t.Fatalf("Failed to Write: %v", err) + t.Fatalf("Failed to Write: '%v'", err) } if errAt == atClusterWriting { select { case err := <-chFatal: - if err != bytes.ErrTooLarge { - t.Fatalf("Unexpected error, expected: %v, got: %v", bytes.ErrTooLarge, err) + if !errs.Is(err, bytes.ErrTooLarge) { + t.Fatalf("Expected error: '%v', got: '%v'", bytes.ErrTooLarge, err) } return case err := <-chError: - t.Fatalf("Unexpected error: %v", err) + t.Fatalf("Unexpected error: '%v'", err) case <-time.After(time.Second): t.Fatal("Error is not emitted on write error") } @@ -334,17 +335,17 @@ func TestBlockWriter_ErrorHandling(t *testing.T) { } clearErr() if _, err := ws[0].Write(false, 110, []byte{0x01, 0x02}); err != nil { - t.Fatalf("Failed to Write: %v", err) + t.Fatalf("Failed to Write: '%v'", err) } if errAt == atFrameWriting { select { case err := <-chFatal: - if err != bytes.ErrTooLarge { - t.Fatalf("Unexpected error, expected: %v, got: %v", bytes.ErrTooLarge, err) + if !errs.Is(err, bytes.ErrTooLarge) { + t.Fatalf("Expected error: '%v', got: '%v'", bytes.ErrTooLarge, err) } return case err := <-chError: - t.Fatalf("Unexpected error: %v", err) + t.Fatalf("Unexpected error: '%v'", err) case <-time.After(time.Second): t.Fatal("Error is not emitted on write error") } @@ -356,15 +357,15 @@ func TestBlockWriter_ErrorHandling(t *testing.T) { // Very old frame clearErr() if _, err := ws[0].Write(true, -32769, []byte{0x0A}); err != nil { - t.Fatalf("Failed to Write: %v", err) + t.Fatalf("Failed to Write: '%v'", err) } select { case err := <-chError: - if err != ErrIgnoreOldFrame { - t.Errorf("Unexpected error, expected: %v, got: %v", ErrIgnoreOldFrame, err) + if !errs.Is(err, ErrIgnoreOldFrame) { + t.Errorf("Expected error: '%v', got: '%v'", ErrIgnoreOldFrame, err) } case err := <-chFatal: - t.Fatalf("Unexpected fatal: %v", err) + t.Fatalf("Unexpected fatal: '%v'", err) case <-time.After(time.Second): t.Fatal("Error is not emitted for old frame") } @@ -377,12 +378,12 @@ func TestBlockWriter_ErrorHandling(t *testing.T) { if errAt == atClosing { select { case err := <-chFatal: - if err != bytes.ErrTooLarge { - t.Fatalf("Unexpected error, expected: %v, got: %v", bytes.ErrTooLarge, err) + if !errs.Is(err, bytes.ErrTooLarge) { + t.Fatalf("Expected error: '%v', got: '%v'", bytes.ErrTooLarge, err) } return case err := <-chError: - t.Fatalf("Unexpected error: %v", err) + t.Fatalf("Unexpected error: '%v'", err) case <-time.After(time.Second): t.Fatal("Error is not emitted on write error") } @@ -403,10 +404,10 @@ func TestBlockWriter_WithMaxKeyframeInterval(t *testing.T) { WithSeekHead(false), ) if err != nil { - t.Fatalf("Failed to create BlockWriter: %v", err) + t.Fatalf("Failed to create BlockWriter: '%v'", err) } if len(ws) != 1 { - t.Fatalf("Number of the returned writer must be 1, but got %d", len(ws)) + t.Fatalf("Number of the returned writer must be 1, got %d", len(ws)) } for _, block := range []struct { @@ -420,7 +421,7 @@ func TestBlockWriter_WithMaxKeyframeInterval(t *testing.T) { {true, 0x1001, []byte{0x04}}, // This will be the head of the next cluster } { if _, err := ws[0].Write(block.keyframe, block.timecode, block.b); err != nil { - t.Fatalf("Failed to Write: %v", err) + t.Fatalf("Failed to Write: '%v'", err) } } @@ -466,10 +467,10 @@ func TestBlockWriter_WithSeekHead(t *testing.T) { WithSeekHead(true), ) if err != nil { - t.Fatalf("Failed to create BlockWriter: %v", err) + t.Fatalf("Failed to create BlockWriter: '%v'", err) } if len(ws) != 1 { - t.Fatalf("Number of the returned writer must be 1, but got %d", len(ws)) + t.Fatalf("Number of the returned writer must be 1, got %d", len(ws)) } ws[0].Close() @@ -510,8 +511,8 @@ func TestBlockWriter_WithSeekHead(t *testing.T) { }{}), WithSeekHead(true), ) - if err != ebml.ErrUnknownElementName { - t.Errorf("Unexpected error, expected: %v, got: %v", ebml.ErrUnknownElementName, err) + if !errs.Is(err, ebml.ErrUnknownElementName) { + t.Errorf("Expected error: '%v', got: '%v'", ebml.ErrUnknownElementName, err) } }) } diff --git a/mkvcore/sizedwriter_test.go b/mkvcore/sizedwriter_test.go index 46afb57..283d847 100644 --- a/mkvcore/sizedwriter_test.go +++ b/mkvcore/sizedwriter_test.go @@ -25,18 +25,18 @@ func TestWriterWithSizeCount(t *testing.T) { w := &writerWithSizeCount{w: buf} if n, err := w.Write([]byte{0x01, 0x02}); err != nil { - t.Fatalf("Failed to Write: %v", err) + t.Fatalf("Failed to Write: '%v'", err) } else if n != 2 { - t.Errorf("Unexpected return value of writerWithSizeCount.Write, expected: 2, got: %d", n) + t.Errorf("Expected return value of writerWithSizeCount.Write: 2, got: %d", n) } if n := w.Size(); n != 2 { - t.Errorf("Unexpected return value of writerWithSizeCount.Size(), expected: 2, got: %d", n) + t.Errorf("Expected return value of writerWithSizeCount.Size(): 2, got: %d", n) } w.Clear() if n := w.Size(); n != 0 { - t.Errorf("Unexpected return value of writerWithSizeCount.Size(), expected: 0, got: %d", n) + t.Errorf("Expected return value of writerWithSizeCount.Size(): 0, got: %d", n) } if err := w.Close(); err != nil { diff --git a/tag.go b/tag.go index cc74a46..9e3ad4b 100644 --- a/tag.go +++ b/tag.go @@ -54,7 +54,7 @@ func parseTag(rawtag string) (*structTag, error) { os.Stderr.WriteString("Deprecated: \"inf\" tag is replaced by \"size=unknown\"\n") tag.size = SizeUnknown default: - return nil, ErrInvalidTag + return nil, wrapErrorf(ErrInvalidTag, "parsing \"%s\"", t) } } continue @@ -69,12 +69,12 @@ func parseTag(rawtag string) (*structTag, error) { } else { s, err := strconv.Atoi(kv[1]) if err != nil { - return nil, err + return nil, wrapErrorf(err, "parsing \"%s\"", t) } tag.size = uint64(s) } default: - return nil, ErrInvalidTag + return nil, wrapErrorf(ErrInvalidTag, "parsing \"%s\"", t) } } return tag, nil diff --git a/tag_test.go b/tag_test.go index 35ee635..db0fa75 100644 --- a/tag_test.go +++ b/tag_test.go @@ -18,13 +18,15 @@ import ( "reflect" "strconv" "testing" + + "github.com/at-wat/ebml-go/internal/errs" ) func TestParseTag(t *testing.T) { cases := map[string]struct { input string expected *structTag - err interface{} + err error }{ "Empty": { "", @@ -56,10 +58,7 @@ func TestParseTag(t *testing.T) { }, "InvalidSize": { "Name123,size=a", - nil, func(err error) bool { - _, ok := err.(*strconv.NumError) - return ok - }, + nil, strconv.ErrSyntax, }, "InvalidTag": { "Name,invalidtag", @@ -85,13 +84,13 @@ func TestParseTag(t *testing.T) { for n, c := range cases { t.Run(n, func(t *testing.T) { tag, err := parseTag(c.input) - if !isErr(err, c.err) { - t.Errorf("Unexpected error, expected: %v, got: %v", c.err, err) + if !errs.Is(err, c.err) { + t.Errorf("Expected error: '%v', got: '%v'", c.err, err) } if (c.expected == nil) != (tag == nil) { - t.Errorf("Unexpected output nil-ness, expected: %v, got: %v", c.expected == nil, tag == nil) + t.Errorf("Expected output nil-ness: %v, got: %v", c.expected == nil, tag == nil) } else if tag != nil && !reflect.DeepEqual(*c.expected, *tag) { - t.Errorf("Unexpected output, expected: %v, got: %v", *c.expected, *tag) + t.Errorf("Expected output: %v, got: %v", *c.expected, *tag) } }) } diff --git a/testutils_test.go b/testutils_test.go index ebbf9da..5707d76 100644 --- a/testutils_test.go +++ b/testutils_test.go @@ -33,15 +33,3 @@ func (s *delayedBrokenReader) Read(b []byte) (int, error) { copy(b, s.b[p:p+len(b)]) return len(b), nil } - -func isErr(err error, target interface{}) bool { - switch v := target.(type) { - case error: - return err == v - case func(err error) bool: - return v(err) - case nil: - return err == nil - } - panic("invalid isErr target") -} diff --git a/unlacer.go b/unlacer.go index 1d1d438..a6e5337 100644 --- a/unlacer.go +++ b/unlacer.go @@ -116,7 +116,9 @@ func NewFixedUnlacer(r io.Reader, n int64) (Unlacer, error) { ul.size[i] = ul.size[0] } if ul.size[0]*nFrame+1 != int(n) { - return nil, ErrFixedLaceUndivisible + return nil, wrapErrorf( + ErrFixedLaceUndivisible, "unlacing %d bytes of %d frames", n-1, nFrame, + ) } return ul, nil } diff --git a/unlacer_test.go b/unlacer_test.go index 4cb9034..473a70d 100644 --- a/unlacer_test.go +++ b/unlacer_test.go @@ -18,6 +18,8 @@ import ( "bytes" "io" "testing" + + "github.com/at-wat/ebml-go/internal/errs" ) func TestUnlacer(t *testing.T) { @@ -163,8 +165,8 @@ func TestUnlacer(t *testing.T) { } ul, err := c.newUnlacer(bytes.NewReader(b), int64(len(b))) - if err != c.err { - t.Fatalf("Unexpected error, expected: %v, got: %v", c.err, err) + if !errs.Is(err, c.err) { + t.Fatalf("Expected error: '%v', got: '%v'", c.err, err) } if err != nil { return @@ -173,14 +175,14 @@ func TestUnlacer(t *testing.T) { for _, f := range c.frames { b, err := ul.Read() if err != nil { - t.Fatalf("Unexpected error: %v", err) + t.Fatalf("Unexpected error: '%v'", err) } if !bytes.Equal(f, b) { t.Errorf("Unexpected data, \nexpected: %v, \n got: %v", f, b) } } if _, err := ul.Read(); err != io.EOF { - t.Fatalf("Unexpected error: %v", err) + t.Fatalf("Unexpected error: '%v'", err) } }) } diff --git a/unmarshal.go b/unmarshal.go index d619dec..770151a 100644 --- a/unmarshal.go +++ b/unmarshal.go @@ -25,11 +25,11 @@ import ( // ErrUnknownElement means that a decoded element is not known. var ErrUnknownElement = errors.New("unknown element") -// ErrIndefiniteType means that a unmarshal destination type is not valid. -var ErrIndefiniteType = errors.New("unmarshal to indefinite type") +// ErrIndefiniteType means that a marshal/unmarshal destination type is not valid. +var ErrIndefiniteType = errors.New("marshal/unmarshal to indefinite type") // ErrIncompatibleType means that an element is not convertible to a corresponding struct field. -var ErrIncompatibleType = errors.New("unmarshal to incompatible type") +var ErrIncompatibleType = errors.New("marshal/unmarshal to incompatible type") // Unmarshal EBML stream. func Unmarshal(r io.Reader, val interface{}, opts ...UnmarshalOption) error { @@ -42,10 +42,10 @@ func Unmarshal(r io.Reader, val interface{}, opts ...UnmarshalOption) error { vo := reflect.ValueOf(val) if !vo.IsValid() { - return ErrIndefiniteType + return wrapErrorf(ErrIndefiniteType, "unmarshalling to %T", val) } if vo.Kind() != reflect.Ptr { - return ErrIncompatibleType + return wrapErrorf(ErrIncompatibleType, "unmarshalling to %T", val) } voe := vo.Elem() @@ -100,7 +100,7 @@ func readElement(r0 io.Reader, n int64, vo reflect.Value, depth int, pos uint64, } v, ok := revTable[uint32(e)] if !ok { - return nil, ErrUnknownElement + return nil, wrapErrorf(ErrUnknownElement, "unmarshalling element 0x%x", e) } size, nb, err := readDataSize(r) @@ -178,10 +178,14 @@ func readElement(r0 io.Reader, n int64, vo reflect.Value, depth int, pos uint64, case isConvertible(vr.Type(), t): vnext.Set(reflect.Append(vnext, vr.Convert(t))) default: - return nil, ErrIncompatibleType + return nil, wrapErrorf( + ErrIncompatibleType, "unmarshalling %s to %s", vnext.Type(), vr.Type(), + ) } default: - return nil, ErrIncompatibleType + return nil, wrapErrorf( + ErrIncompatibleType, "unmarshalling %s to %s", vnext.Type(), vr.Type(), + ) } } if elem != nil { diff --git a/unmarshal_test.go b/unmarshal_test.go index 865e6e0..aa7b4f6 100644 --- a/unmarshal_test.go +++ b/unmarshal_test.go @@ -22,6 +22,8 @@ import ( "reflect" "testing" "time" + + "github.com/at-wat/ebml-go/internal/errs" ) func ExampleUnmarshal() { @@ -43,7 +45,7 @@ func ExampleUnmarshal() { var ret TestEBML if err := Unmarshal(r, &ret); err != nil { - fmt.Printf("error: %+v\n", err) + fmt.Printf("error: %v\n", err) } fmt.Println(ret) @@ -78,10 +80,10 @@ func TestUnmarshal_MultipleUnknownSize(t *testing.T) { var ret TestEBML if err := Unmarshal(bytes.NewReader(b), &ret); err != nil { - t.Fatalf("Unexpected error: %v\n", err) + t.Fatalf("Unexpected error: '%v'\n", err) } if !reflect.DeepEqual(expected, ret) { - t.Errorf("Unexpected result, expected: %v, got: %v", expected, ret) + t.Errorf("Expected result: %v, got: %v", expected, ret) } } @@ -192,11 +194,11 @@ func TestUnmarshal_Convert(t *testing.T) { t.Run(name, func(t *testing.T) { ret := reflect.New(reflect.ValueOf(c.expected).Type()) if err := Unmarshal(bytes.NewReader(c.b), ret.Interface()); err != nil { - t.Fatalf("Unexpected error: %v\n", err) + t.Fatalf("Unexpected error: '%v'\n", err) } if !reflect.DeepEqual(c.expected, ret.Elem().Interface()) { - t.Errorf("Unexpected convert result, expected: %v, got %v", + t.Errorf("Expected convert result: %v, got %v", c.expected, ret.Elem().Interface()) } }) @@ -211,7 +213,7 @@ func TestUnmarshal_OptionError(t *testing.T) { }, ) if err != errExpected { - t.Errorf("Unexpected error for failing UnmarshalOption, expected: %v, got: %v", errExpected, err) + t.Errorf("Expected error against failing UnmarshalOption: '%v', got: '%v'", errExpected, err) } } @@ -245,7 +247,7 @@ func TestUnmarshal_WithElementReadHooks(t *testing.T) { m := make(map[string][]*Element) hook := withElementMap(m) if err := Unmarshal(r, &ret, WithElementReadHooks(hook)); err != nil { - t.Errorf("Unexpected error: %+v", err) + t.Errorf("Unexpected error: '%v'", err) } // Verify positions of elements @@ -309,7 +311,7 @@ func TestUnmarshal_Chan(t *testing.T) { } }() if err := Unmarshal(bytes.NewReader(TestBinary), &ret); err != nil { - t.Errorf("Unexpected error: %+v", err) + t.Errorf("Unexpected error: '%v'", err) } close(done) if len(ch) != 2 { @@ -334,10 +336,10 @@ func TestUnmarshal_Tag(t *testing.T) { b := []byte{0x42, 0x82, 0x85, 0x68, 0x6F, 0x67, 0x65, 0x00} if err := Unmarshal(bytes.NewBuffer(b), &tagged); err != nil { - t.Fatalf("Unexpected error: %+v", err) + t.Fatalf("Unexpected error: '%v'", err) } if err := Unmarshal(bytes.NewBuffer(b), &untagged); err != nil { - t.Fatalf("Unexpected error: %+v", err) + t.Fatalf("Unexpected error: '%v'", err) } if tagged.DocCustomNamedType != untagged.EBMLDocType { @@ -351,13 +353,13 @@ func TestUnmarshal_Error(t *testing.T) { } `ebml:"EBML"` } t.Run("NilValue", func(t *testing.T) { - if err := Unmarshal(bytes.NewBuffer([]byte{}), nil); err != ErrIndefiniteType { - t.Errorf("Unexpected error, expected %v, got %v\n", ErrIndefiniteType, err) + if err := Unmarshal(bytes.NewBuffer([]byte{}), nil); !errs.Is(err, ErrIndefiniteType) { + t.Errorf("Expected error: '%v', got: '%v'\n", ErrIndefiniteType, err) } }) t.Run("NonPtr", func(t *testing.T) { - if err := Unmarshal(bytes.NewBuffer([]byte{}), struct{}{}); err != ErrIncompatibleType { - t.Errorf("Unexpected error, expected %v, got %v\n", ErrIncompatibleType, err) + if err := Unmarshal(bytes.NewBuffer([]byte{}), struct{}{}); !errs.Is(err, ErrIncompatibleType) { + t.Errorf("Expected error: '%v', got: '%v'\n", ErrIncompatibleType, err) } }) t.Run("UnknownElementName", func(t *testing.T) { @@ -365,15 +367,15 @@ func TestUnmarshal_Error(t *testing.T) { Header struct { } `ebml:"Unknown"` }{} - if err := Unmarshal(bytes.NewBuffer([]byte{}), input); err != ErrUnknownElementName { - t.Errorf("Unexpected error, expected %v, got %v\n", ErrUnknownElementName, err) + if err := Unmarshal(bytes.NewBuffer([]byte{}), input); !errs.Is(err, ErrUnknownElementName) { + t.Errorf("Expected error: '%v', got: '%v'\n", ErrUnknownElementName, err) } }) t.Run("UnknownElement", func(t *testing.T) { input := &TestEBML{} b := []byte{0x80} - if err := Unmarshal(bytes.NewBuffer(b), input); err != ErrUnknownElement { - t.Errorf("Unexpected error, expected %v, got %v\n", ErrUnknownElement, err) + if err := Unmarshal(bytes.NewBuffer(b), input); !errs.Is(err, ErrUnknownElement) { + t.Errorf("Expected error: '%v', got: '%v'\n", ErrUnknownElement, err) } }) t.Run("Short", func(t *testing.T) { @@ -390,8 +392,8 @@ func TestUnmarshal_Error(t *testing.T) { for name, b := range TestBinaries { t.Run(name, func(t *testing.T) { var val TestEBML - if err := Unmarshal(bytes.NewBuffer(b), &val); err != io.ErrUnexpectedEOF { - t.Errorf("Unexpected error, expected: %v, got: %v\n", io.ErrUnexpectedEOF, err) + if err := Unmarshal(bytes.NewBuffer(b), &val); !errs.Is(err, io.ErrUnexpectedEOF) { + t.Errorf("Expected error: '%v', got: '%v'\n", io.ErrUnexpectedEOF, err) } }) } @@ -411,8 +413,8 @@ func TestUnmarshal_Error(t *testing.T) { for i := 1; i < len(b)-1; i++ { var val TestEBML r := &delayedBrokenReader{b: b, limit: i} - if err := Unmarshal(r, &val); err != io.ErrClosedPipe { - t.Errorf("Error is not propagated from Reader, limit: %d, expected: %v, got: %v\n", i, io.ErrClosedPipe, err) + if err := Unmarshal(r, &val); !errs.Is(err, io.ErrClosedPipe) { + t.Errorf("Error is not propagated from Reader, limit: %d, expected: '%v', got: '%v'\n", i, io.ErrClosedPipe, err) } } }) @@ -483,8 +485,8 @@ func TestUnmarshal_Error(t *testing.T) { } for name, c := range cases { t.Run(name, func(t *testing.T) { - if err := Unmarshal(bytes.NewBuffer(c.b), c.ret); err != c.err { - t.Errorf("Unexpected error, expected: %v, got: %v\n", c.err, err) + if err := Unmarshal(bytes.NewBuffer(c.b), c.ret); !errs.Is(err, c.err) { + t.Errorf("Expected error: '%v', got: '%v'\n", c.err, err) } }) } @@ -512,7 +514,7 @@ func BenchmarkUnmarshal(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { if err := Unmarshal(bytes.NewReader(TestBinary), &ret); err != nil { - b.Fatalf("Unexpected error: %+v", err) + b.Fatalf("Unexpected error: '%v'", err) } } } diff --git a/value.go b/value.go index 6a71cbe..0b89c00 100644 --- a/value.go +++ b/value.go @@ -180,7 +180,7 @@ func readDate(r io.Reader, n uint64) (interface{}, error) { } func readFloat(r io.Reader, n uint64) (interface{}, error) { if n != 4 && n != 8 { - return 0.0, ErrInvalidFloatSize + return 0.0, wrapErrorf(ErrInvalidFloatSize, "reading %d bytes float", n) } bs := make([]byte, n) @@ -273,7 +273,7 @@ func encodeBinary(i interface{}, n uint64) ([]byte, error) { func encodeString(i interface{}, n uint64) ([]byte, error) { v, ok := i.(string) if !ok { - return []byte{}, ErrInvalidType + return []byte{}, wrapErrorf(ErrInvalidType, "writing %T as string", i) } if uint64(len(v)+1) >= n { return append([]byte(v), 0x00), nil @@ -294,7 +294,7 @@ func encodeInt(i interface{}, n uint64) ([]byte, error) { case int64: v = v2 default: - return []byte{}, ErrInvalidType + return []byte{}, wrapErrorf(ErrInvalidType, "writing %T as int", i) } return encodeUInt(uint64(v), n) } @@ -312,7 +312,7 @@ func encodeUInt(i interface{}, n uint64) ([]byte, error) { case uint64: v = v2 default: - return []byte{}, ErrInvalidType + return []byte{}, wrapErrorf(ErrInvalidType, "writing %T as uint", i) } switch { case v < 0x100 && n < 2: @@ -336,7 +336,7 @@ func encodeUInt(i interface{}, n uint64) ([]byte, error) { func encodeDate(i interface{}, n uint64) ([]byte, error) { v, ok := i.(time.Time) if !ok { - return []byte{}, ErrInvalidType + return []byte{}, wrapErrorf(ErrInvalidType, "writing %T as date", i) } dtns := v.Sub(time.Unix(DateEpochInUnixtime, 0)).Nanoseconds() return encodeInt(int64(dtns), n) @@ -362,7 +362,7 @@ func encodeFloat(i interface{}, n uint64) ([]byte, error) { case 8: return encodeFloat64(v) default: - return []byte{}, ErrInvalidFloatSize + return []byte{}, wrapErrorf(ErrInvalidFloatSize, "writing %d bytes float", n) } case float32: switch n { @@ -373,16 +373,16 @@ func encodeFloat(i interface{}, n uint64) ([]byte, error) { case 8: return encodeFloat64(float64(v)) default: - return []byte{}, ErrInvalidFloatSize + return []byte{}, wrapErrorf(ErrInvalidFloatSize, "writing %d bytes float", n) } default: - return []byte{}, ErrInvalidType + return []byte{}, wrapErrorf(ErrInvalidType, "writing %T as float", i) } } func encodeBlock(i interface{}, n uint64) ([]byte, error) { v, ok := i.(Block) if !ok { - return []byte{}, ErrInvalidType + return []byte{}, wrapErrorf(ErrInvalidType, "writing %T as block", i) } var b bytes.Buffer if err := MarshalBlock(&v, &b); err != nil { diff --git a/value_test.go b/value_test.go index f8f23cd..2603653 100644 --- a/value_test.go +++ b/value_test.go @@ -6,6 +6,8 @@ import ( "reflect" "testing" "time" + + "github.com/at-wat/ebml-go/internal/errs" ) func TestDataSize(t *testing.T) { @@ -35,10 +37,10 @@ func TestDataSize(t *testing.T) { t.Run("DecodeVInt "+n, func(t *testing.T) { r, _, err := readVInt(bytes.NewBuffer(c.b)) if err != nil { - t.Fatalf("Failed to readVInt: %v", err) + t.Fatalf("Failed to readVInt: '%v'", err) } if r != c.i { - t.Errorf("Unexpected readVInt result, expected: %d, got: %d", c.i, r) + t.Errorf("Expected readVInt result: %d, got: %d", c.i, r) } }) } @@ -46,10 +48,10 @@ func TestDataSize(t *testing.T) { t.Run("DecodeDataSize "+n, func(t *testing.T) { r, _, err := readDataSize(bytes.NewBuffer(c.b)) if err != nil { - t.Fatalf("Failed to readDataSize: %v", err) + t.Fatalf("Failed to readDataSize: '%v'", err) } if r != c.i { - t.Errorf("Unexpected readVInt result, expected: %d, got: %d", c.i, r) + t.Errorf("Expected readVInt result: %d, got: %d", c.i, r) } }) } @@ -57,7 +59,7 @@ func TestDataSize(t *testing.T) { t.Run("Encode "+n, func(t *testing.T) { b := encodeDataSize(c.i, 0) if !bytes.Equal(b, c.b) { - t.Errorf("Unexpected encodeDataSize result, expected: %d, got: %d", c.b, b) + t.Errorf("Expected encodeDataSize result: %d, got: %d", c.b, b) } }) } @@ -79,10 +81,10 @@ func TestDataSize_Unknown(t *testing.T) { t.Run("DecodeDataSize "+n, func(t *testing.T) { r, _, err := readDataSize(bytes.NewBuffer(b)) if err != nil { - t.Fatalf("Failed to readDataSize: %v", err) + t.Fatalf("Failed to readDataSize: '%v'", err) } if r != SizeUnknown { - t.Errorf("Unexpected readDataSize result, expected: %d, got: %d", SizeUnknown, r) + t.Errorf("Expected readDataSize result: %d, got: %d", SizeUnknown, r) } }) } @@ -112,10 +114,10 @@ func TestElementID(t *testing.T) { t.Run("Decode "+n, func(t *testing.T) { r, _, err := readVInt(bytes.NewBuffer(c.b)) if err != nil { - t.Fatalf("Failed to readVInt: %v", err) + t.Fatalf("Failed to readVInt: '%v'", err) } if r != c.i { - t.Errorf("Unexpected readVInt result, expected: %d, got: %d", c.i, r) + t.Errorf("Expected readVInt result: %d, got: %d", c.i, r) } }) } @@ -123,17 +125,17 @@ func TestElementID(t *testing.T) { t.Run("Encode "+n, func(t *testing.T) { b, err := encodeElementID(c.i) if err != nil { - t.Fatalf("Failed to encodeElementID: %v", err) + t.Fatalf("Failed to encodeElementID: '%v'", err) } if !bytes.Equal(b, c.b) { - t.Errorf("Unexpected encodeDataSize result, expected: %d, got: %d", c.b, b) + t.Errorf("Expected encodeDataSize result: %d, got: %d", c.b, b) } }) } _, err := encodeElementID(0x2000000000000) if err != ErrUnsupportedElementID { - t.Errorf("Unexpected error type result, expected: %s, got: %s", ErrUnsupportedElementID, err) + t.Errorf("Expected error type result: %s, got: %s", ErrUnsupportedElementID, err) } } @@ -198,10 +200,10 @@ func TestValue(t *testing.T) { t.Run("Read "+n, func(t *testing.T) { v, err := perTypeReader[c.t](bytes.NewBuffer(c.b), uint64(len(c.b))) if err != nil { - t.Fatalf("Failed to read%s: %v", n, err) + t.Fatalf("Failed to read%s: '%v'", n, err) } if !reflect.DeepEqual(v, c.v) { - t.Errorf("Unexpected read%s result, expected: %v, got: %v", n, c.v, v) + t.Errorf("Expected read%s result: %v, got: %v", n, c.v, v) } }) t.Run("Encode "+n, func(t *testing.T) { @@ -213,10 +215,10 @@ func TestValue(t *testing.T) { } b, err := perTypeEncoder[c.t](v, c.n) if err != nil { - t.Fatalf("Failed to encode%s: %v", n, err) + t.Fatalf("Failed to encode%s: '%v'", n, err) } if !bytes.Equal(b, c.b) { - t.Errorf("Unexpected encode%s result, expected: %v, got: %v", n, c.b, b) + t.Errorf("Expected encode%s result: %v, got: %v", n, c.b, b) } }) } @@ -268,8 +270,8 @@ func TestEncodeValue_WrongInputType(t *testing.T) { t.Run("Encode "+c.t.String(), func(t *testing.T) { for _, v := range c.v { _, err := perTypeEncoder[c.t](v, 0) - if err != c.err { - t.Fatalf("encode%s returned unexpected error to wrong input type: %v", c.t.String(), err) + if !errs.Is(err, c.err) { + t.Fatalf("Expected error against wrong input type %s: '%v, got: '%v'", c.t.String(), c.err, err) } } }) @@ -299,8 +301,8 @@ func TestEncodeValue_WrongSize(t *testing.T) { for n, c := range testCases { t.Run("Encode "+n, func(t *testing.T) { _, err := perTypeEncoder[c.t](c.v, c.n) - if err != c.err { - t.Fatalf("encode%s returned unexpected error to wrong input type: %v", n, err) + if !errs.Is(err, c.err) { + t.Fatalf("Expected error against wrong input type %s: '%v', got: '%v'", n, c.err, err) } }) } @@ -323,8 +325,8 @@ func TestReadValue_WrongSize(t *testing.T) { for n, c := range testCases { t.Run("Read "+n, func(t *testing.T) { _, err := perTypeReader[c.t](bytes.NewReader(c.b), c.n) - if err != c.err { - t.Fatalf("read%s returned unexpected error to wrong data size: %v", n, err) + if !errs.Is(err, c.err) { + t.Fatalf("Expected error against wrong data size of %s: %v, got: %v", n, c.err, err) } }) } @@ -347,9 +349,9 @@ func TestReadValue_ReadUnexpectedEOF(t *testing.T) { for l := 0; l < len(c.b)-1; l++ { r := bytes.NewReader(c.b[:l]) _, err := perTypeReader[c.t](r, uint64(len(c.b))) - if err != io.ErrUnexpectedEOF { - t.Errorf("read%s returned unexpected error for %d byte(s) data, expected %v, got %v", - c.t.String(), l, io.ErrUnexpectedEOF, err) + if !errs.Is(err, io.ErrUnexpectedEOF) { + t.Errorf("Expected error against short (%d bytes) %s: %v, got: %v", + l, c.t.String(), io.ErrUnexpectedEOF, err) } } }) diff --git a/webm/blockwriter_test.go b/webm/blockwriter_test.go index 437d31c..1609d58 100644 --- a/webm/blockwriter_test.go +++ b/webm/blockwriter_test.go @@ -63,26 +63,26 @@ func TestBlockWriter(t *testing.T) { if n, err := ws[0].Write(false, 100, []byte{0x01, 0x02}); err != nil { t.Fatalf("Failed to Write: %v", err) } else if n != 2 { - t.Errorf("Unexpected return value of BlockWriter.Write, expected: 2, got: %d", n) + t.Errorf("Expected return value of BlockWriter.Write: 2, got: %d", n) } if n, err := ws[1].Write(true, 110, []byte{0x03, 0x04, 0x05}); err != nil { t.Fatalf("Failed to Write: %v", err) } else if n != 3 { - t.Errorf("Unexpected return value of BlockWriter.Write, expected: 3, got: %d", n) + t.Errorf("Expected return value of BlockWriter.Write: 3, got: %d", n) } // Ignored due to old timestamp if n, err := ws[0].Write(true, -32769, []byte{0x0A}); err != nil { t.Fatalf("Failed to Write: %v", err) } else if n != 1 { - t.Errorf("Unexpected return value of BlockWriter.Write, expected: 1, got: %d", n) + t.Errorf("Expected return value of BlockWriter.Write: 1, got: %d", n) } if n, err := ws[0].Write(true, 130, []byte{0x06}); err != nil { t.Fatalf("Failed to Write: %v", err) } else if n != 1 { - t.Errorf("Unexpected return value of BlockWriter.Write, expected: 1, got: %d", n) + t.Errorf("Expected return value of BlockWriter.Write: 1, got: %d", n) } ws[0].Close()