-
Notifications
You must be signed in to change notification settings - Fork 15
/
types.go
188 lines (164 loc) · 5.22 KB
/
types.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// Copyright (c) 2018-2021, Maxime Soulé
// All rights reserved.
//
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree.
package td
import (
"reflect"
"strings"
"testing"
"github.com/maxatome/go-testdeep/internal/ctxerr"
"github.com/maxatome/go-testdeep/internal/location"
"github.com/maxatome/go-testdeep/internal/types"
)
var (
tType = reflect.TypeOf((*T)(nil))
testDeeper = reflect.TypeOf((*TestDeep)(nil)).Elem()
smuggledGotType = reflect.TypeOf(SmuggledGot{})
smuggledGotPtrType = reflect.TypeOf((*SmuggledGot)(nil))
recvKindType = reflect.TypeOf(RecvNothing)
)
// TestingT is the minimal interface used by [Cmp] to report errors. It
// is commonly implemented by [*testing.T] and [*testing.B].
type TestingT interface {
Error(args ...any)
Fatal(args ...any)
Helper()
}
// TestingFT is a deprecated alias of [testing.TB]. Use [testing.TB]
// directly in new code.
type TestingFT = testing.TB
// TestDeep is the representation of a [go-testdeep operator]. It is not
// intended to be used directly, but through Cmp* functions.
//
// [go-testdeep operator]: https://go-testdeep.zetta.rocks/operators/
type TestDeep interface {
types.TestDeepStringer
location.GetLocationer
// Match checks got against the operator. It returns nil if it matches.
Match(ctx ctxerr.Context, got reflect.Value) *ctxerr.Error
setLocation(int)
replaceLocation(location.Location)
// HandleInvalid returns true if the operator is able to handle
// untyped nil value. Otherwise the untyped nil value is handled
// generically.
HandleInvalid() bool
// TypeBehind returns the type handled by the operator or nil if it
// is not known. tdhttp helper uses it to know how to unmarshal HTTP
// responses bodies before comparing them using the operator.
TypeBehind() reflect.Type
// Error returns nil if the operator is operational, the
// corresponding error otherwise.
Error() error
}
// base is a base type providing some methods needed by the TestDeep
// interface.
type base struct {
types.TestDeepStamp
location location.Location
err *ctxerr.Error
}
func pkgFunc(full string) (string, string) {
// the/package.Foo → "the/package", "Foo"
// the/package.(*T).Foo → "the/package", "(*T).Foo"
// the/package.glob..func1 → "the/package", "glob..func1"
sp := strings.LastIndexByte(full, '/')
if sp < 0 {
sp = 0 // std package without any '/' in name
}
dp := strings.IndexByte(full[sp:], '.')
if dp < 0 {
return full, ""
}
dp += sp
return full[:dp], full[dp+1:]
}
// setLocation sets location using the stack trace going callDepth levels up.
func (t *base) setLocation(callDepth int) {
if callDepth < 0 {
return
}
var ok bool
t.location, ok = location.New(callDepth)
if !ok {
t.location.File = "???"
t.location.Line = 0
return
}
// Here package is github.com/maxatome/go-testdeep, or its vendored
// counterpart
var pkg string
pkg, t.location.Func = pkgFunc(t.location.Func)
// Try to go one level upper, if we are still in go-testdeep package
cmpLoc, ok := location.New(callDepth + 1)
if ok {
cmpPkg, _ := pkgFunc(cmpLoc.Func)
if cmpPkg == pkg {
t.location.File = cmpLoc.File
t.location.Line = cmpLoc.Line
t.location.BehindCmp = true
}
}
}
// replaceLocation replaces the location by loc.
func (t *base) replaceLocation(loc location.Location) {
t.location = loc
}
// GetLocation returns a copy of the location.Location where the TestDeep
// operator has been created.
func (t *base) GetLocation() location.Location {
return t.location
}
// HandleInvalid tells go-testdeep internals that this operator does
// not handle nil values directly.
func (t base) HandleInvalid() bool {
return false
}
// TypeBehind returns the type handled by the operator. Only few operators
// knows the type they are handling. If they do not know, nil is
// returned.
func (t base) TypeBehind() reflect.Type {
return nil
}
// Error returns nil if the operator is operational, the corresponding
// error otherwise.
func (t base) Error() error {
if t.err == nil {
return nil
}
return t.err
}
// stringError is a convenience method to call in String()
// implementations when the operator is in error.
func (t base) stringError() string {
return t.GetLocation().Func + "(<ERROR>)"
}
// MarshalJSON implements encoding/json.Marshaler only to returns an
// error, as a TestDeep operator should never be JSON marshaled. So
// it is better to tell the user he/she does a mistake.
func (t base) MarshalJSON() ([]byte, error) {
return nil, types.OperatorNotJSONMarshallableError(t.location.Func)
}
// newBase returns a new base struct with location.Location set to the
// callDepth depth.
func newBase(callDepth int) (b base) {
b.setLocation(callDepth)
return
}
// baseOKNil is a base type providing some methods needed by the TestDeep
// interface, for operators handling nil values.
type baseOKNil struct {
base
}
// HandleInvalid tells go-testdeep internals that this operator
// handles nil values directly.
func (t baseOKNil) HandleInvalid() bool {
return true
}
// newBaseOKNil returns a new baseOKNil struct with location.Location set to
// the callDepth depth.
func newBaseOKNil(callDepth int) (b baseOKNil) {
b.setLocation(callDepth)
return
}