| // 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 |