* terror: print the stack in terror.Log() In TiDB, terror.Log(err) is called when we expect the error to be nil and get rid of the annoying golint check. Sometimes the error is not nil as expected, so it's better to print the stack to find why * go fmt * address comment * address comment
169 lines
4.6 KiB
Go
169 lines
4.6 KiB
Go
// Copyright 2015 PingCAP, Inc.
|
|
//
|
|
// 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,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package terror
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
|
|
. "github.com/pingcap/check"
|
|
"github.com/pingcap/errors"
|
|
)
|
|
|
|
func TestT(t *testing.T) {
|
|
CustomVerboseFlag = true
|
|
TestingT(t)
|
|
}
|
|
|
|
var _ = Suite(&testTErrorSuite{})
|
|
|
|
type testTErrorSuite struct {
|
|
}
|
|
|
|
func (s *testTErrorSuite) TestErrCode(c *C) {
|
|
c.Assert(CodeMissConnectionID, Equals, ErrCode(1))
|
|
c.Assert(CodeResultUndetermined, Equals, ErrCode(2))
|
|
}
|
|
|
|
func (s *testTErrorSuite) TestTError(c *C) {
|
|
c.Assert(ClassParser.String(), Not(Equals), "")
|
|
c.Assert(ClassOptimizer.String(), Not(Equals), "")
|
|
c.Assert(ClassKV.String(), Not(Equals), "")
|
|
c.Assert(ClassServer.String(), Not(Equals), "")
|
|
|
|
parserErr := ClassParser.New(ErrCode(100), "error 100")
|
|
c.Assert(parserErr.Error(), Not(Equals), "")
|
|
c.Assert(ClassParser.EqualClass(parserErr), IsTrue)
|
|
c.Assert(ClassParser.NotEqualClass(parserErr), IsFalse)
|
|
|
|
c.Assert(ClassOptimizer.EqualClass(parserErr), IsFalse)
|
|
optimizerErr := ClassOptimizer.New(ErrCode(2), "abc")
|
|
c.Assert(ClassOptimizer.EqualClass(errors.New("abc")), IsFalse)
|
|
c.Assert(ClassOptimizer.EqualClass(nil), IsFalse)
|
|
c.Assert(optimizerErr.Equal(optimizerErr.GenWithStack("def")), IsTrue)
|
|
c.Assert(optimizerErr.Equal(nil), IsFalse)
|
|
c.Assert(optimizerErr.Equal(errors.New("abc")), IsFalse)
|
|
|
|
// Test case for FastGen.
|
|
c.Assert(optimizerErr.Equal(optimizerErr.FastGen("def")), IsTrue)
|
|
c.Assert(optimizerErr.Equal(optimizerErr.FastGen("def: %s", "def")), IsTrue)
|
|
kvErr := ClassKV.New(1062, "key already exist")
|
|
e := kvErr.FastGen("Duplicate entry '%d' for key 'PRIMARY'", 1)
|
|
c.Assert(e.Error(), Equals, "[kv:1062]Duplicate entry '1' for key 'PRIMARY'")
|
|
sqlErr := errors.Cause(e).(*Error).ToSQLError()
|
|
c.Assert(sqlErr.Message, Equals, "Duplicate entry '1' for key 'PRIMARY'")
|
|
c.Assert(sqlErr.Code, Equals, uint16(1062))
|
|
|
|
err := errors.Trace(ErrCritical.GenWithStackByArgs("test"))
|
|
c.Assert(ErrCritical.Equal(err), IsTrue)
|
|
|
|
err = errors.Trace(ErrCritical)
|
|
c.Assert(ErrCritical.Equal(err), IsTrue)
|
|
}
|
|
|
|
func (s *testTErrorSuite) TestJson(c *C) {
|
|
prevTErr := &Error{
|
|
class: ClassTable,
|
|
code: CodeExecResultIsEmpty,
|
|
message: "json test",
|
|
}
|
|
buf, err := json.Marshal(prevTErr)
|
|
c.Assert(err, IsNil)
|
|
var curTErr Error
|
|
err = json.Unmarshal(buf, &curTErr)
|
|
c.Assert(err, IsNil)
|
|
isEqual := prevTErr.Equal(&curTErr)
|
|
c.Assert(isEqual, IsTrue)
|
|
}
|
|
|
|
var predefinedErr = ClassExecutor.New(ErrCode(123), "predefiend error")
|
|
|
|
func example() error {
|
|
err := call()
|
|
return errors.Trace(err)
|
|
}
|
|
|
|
func call() error {
|
|
return predefinedErr.GenWithStack("error message:%s", "abc")
|
|
}
|
|
|
|
func (s *testTErrorSuite) TestTraceAndLocation(c *C) {
|
|
err := example()
|
|
stack := errors.ErrorStack(err)
|
|
lines := strings.Split(stack, "\n")
|
|
goroot := strings.ReplaceAll(runtime.GOROOT(), string(os.PathSeparator), "/")
|
|
var sysStack = 0
|
|
for _, line := range lines {
|
|
if strings.Contains(line, goroot) {
|
|
sysStack++
|
|
}
|
|
}
|
|
c.Assert(len(lines)-(2*sysStack), Equals, 15, Commentf("stack =\n%s", stack))
|
|
var containTerr bool
|
|
for _, v := range lines {
|
|
if strings.Contains(v, "terror_test.go") {
|
|
containTerr = true
|
|
break
|
|
}
|
|
}
|
|
c.Assert(containTerr, IsTrue)
|
|
}
|
|
|
|
func (s *testTErrorSuite) TestErrorEqual(c *C) {
|
|
e1 := errors.New("test error")
|
|
c.Assert(e1, NotNil)
|
|
|
|
e2 := errors.Trace(e1)
|
|
c.Assert(e2, NotNil)
|
|
|
|
e3 := errors.Trace(e2)
|
|
c.Assert(e3, NotNil)
|
|
|
|
c.Assert(errors.Cause(e2), Equals, e1)
|
|
c.Assert(errors.Cause(e3), Equals, e1)
|
|
c.Assert(errors.Cause(e2), Equals, errors.Cause(e3))
|
|
|
|
e4 := errors.New("test error")
|
|
c.Assert(errors.Cause(e4), Not(Equals), e1)
|
|
|
|
e5 := errors.Errorf("test error")
|
|
c.Assert(errors.Cause(e5), Not(Equals), e1)
|
|
|
|
c.Assert(ErrorEqual(e1, e2), IsTrue)
|
|
c.Assert(ErrorEqual(e1, e3), IsTrue)
|
|
c.Assert(ErrorEqual(e1, e4), IsTrue)
|
|
c.Assert(ErrorEqual(e1, e5), IsTrue)
|
|
|
|
var e6 error
|
|
|
|
c.Assert(ErrorEqual(nil, nil), IsTrue)
|
|
c.Assert(ErrorNotEqual(e1, e6), IsTrue)
|
|
code1 := ErrCode(9001)
|
|
code2 := ErrCode(9002)
|
|
te1 := ClassParser.Synthesize(code1, "abc")
|
|
te3 := ClassKV.New(code1, "abc")
|
|
te4 := ClassKV.New(code2, "abc")
|
|
c.Assert(ErrorEqual(te1, te3), IsFalse)
|
|
c.Assert(ErrorEqual(te3, te4), IsFalse)
|
|
}
|
|
|
|
func (s *testTErrorSuite) TestLog(c *C) {
|
|
err := fmt.Errorf("xxx")
|
|
Log(err)
|
|
}
|