blob: 614609ff80b8906be4a35b43b169d2a6aae9e79c [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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 errors
import (
"fmt"
"testing"
)
func TestNew(t *testing.T) {
const want string = "error message"
err := New(want)
if err.Error() != want {
t.Errorf("Error msg does not match original. Want: %q, Got: %q", want, err.Error())
}
}
func TestErrorf(t *testing.T) {
want := fmt.Sprintf("%s %d", "ten", 10)
err := Errorf("%s %d", "ten", 10)
if err.Error() != want {
t.Errorf("Incorrect formatting. Want: %q, Got: %q", want, err.Error())
}
}
const (
base string = "base"
msg1 string = "message 1"
msg2 string = "message 2"
ctx1 string = "context 1"
ctx2 string = "context 2"
top1 string = "top level message 1"
top2 string = "top level message 2"
)
func TestWrap(t *testing.T) {
tests := []struct {
err error
want errorStructure
}{
{
err: Wrap(New(base), msg1),
want: errorStructure{{ERROR, msg1}, {ERROR, base}},
}, {
err: Wrap(Wrap(New(base), msg1), msg2),
want: errorStructure{{ERROR, msg2}, {ERROR, msg1}, {ERROR, base}},
},
}
for _, test := range tests {
got := getStructure(test.err)
if !equalStructure(got, test.want) {
t.Errorf("Incorrect structure. Want: %+v, Got: %+v", test.want, got)
}
}
}
func TestWrapf(t *testing.T) {
want := fmt.Sprintf("%s %d", "ten", 10)
err := Wrapf(New(base), "%s %d", "ten", 10)
got := getStructure(err)[0].msg
if got != want {
t.Errorf("Incorrect formatting. Want: %q, Got: %q", want, got)
}
}
func TestContext(t *testing.T) {
tests := []struct {
err error
want errorStructure
}{
{
err: WithContext(New(base), ctx1),
want: errorStructure{{CONTEXT, ctx1}, {ERROR, base}},
}, {
err: WithContext(Wrap(WithContext(New(base), ctx1), msg1), ctx2),
want: errorStructure{{CONTEXT, ctx2}, {ERROR, msg1}, {CONTEXT, ctx1}, {ERROR, base}},
}, {
err: Wrap(WithContext(WithContext(Wrap(New(base), msg1), ctx1), ctx2), msg2),
want: errorStructure{{ERROR, msg2}, {CONTEXT, ctx2}, {CONTEXT, ctx1}, {ERROR, msg1}, {ERROR, base}},
},
}
for _, test := range tests {
got := getStructure(test.err)
if !equalStructure(got, test.want) {
t.Errorf("Incorrect structure. Want: %+v, Got: %+v", test.want, got)
}
}
}
func TestWithContextf(t *testing.T) {
want := fmt.Sprintf("%s %d", "ten", 10)
err := WithContextf(New(base), "%s %d", "ten", 10)
got := getStructure(err)[0].msg
if got != want {
t.Errorf("Incorrect formatting. Want: %q, Got: %q", want, got)
}
}
func TestTopLevelMsg(t *testing.T) {
tests := []struct {
err error
want string
}{
{
err: New(base),
want: "",
}, {
err: Wrap(WithContext(New(base), ctx1), msg1),
want: "",
}, {
err: SetTopLevelMsg(New(base), top1),
want: top1,
}, {
err: Wrap(SetTopLevelMsg(WithContext(New(base), ctx1), top1), msg1),
want: top1,
}, {
err: Wrap(SetTopLevelMsg(WithContext(SetTopLevelMsg(New(base), top1), ctx1), top2), msg1),
want: top2,
},
}
for _, test := range tests {
got := getTop(test.err)
if got != test.want {
t.Errorf("Incorrect top-level message. Want: %+v, Got: %+v", test.want, got)
}
}
}
func TestSetTopLevelMsgf(t *testing.T) {
want := fmt.Sprintf("%s %d", "ten", 10)
err := SetTopLevelMsgf(New(base), "%s %d", "ten", 10)
if getTop(err) != want {
t.Errorf("Incorrect formatting. Want: %q, Got: %q", want, getTop(err))
}
}
// getStructure extracts the structure of an error, outputting a slice that
// represents the nested messages in that error in the order they are output
// and with the type of message (context or error) described.
func getStructure(e error) errorStructure {
var structure errorStructure
for {
if be, ok := e.(*beamError); ok {
if be.context != "" {
structure = append(structure, entry{CONTEXT, be.context})
}
if be.msg != "" {
structure = append(structure, entry{ERROR, be.msg})
}
// Continue by iterating into the cause of the error.
if be.cause != nil {
e = be.cause
} else {
return structure
}
} else { // Not a beamError.
structure = append(structure, entry{ERROR, e.Error()})
return structure
}
}
}
func equalStructure(left errorStructure, right errorStructure) bool {
if len(left) != len(right) {
return false
}
for i := 0; i < len(left); i++ {
if left[i].t != right[i].t || left[i].msg != right[i].msg {
return false
}
}
return true
}
type msgType int
const (
ERROR msgType = iota
CONTEXT
)
type entry struct {
t msgType
msg string
}
type errorStructure []entry