From f92a15e5404aba733ac867520f5ddff644124b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20Soul=C3=A9?= Date: Sun, 26 Mar 2023 22:45:04 +0200 Subject: [PATCH 1/3] test: can now test "under" section of a stringified error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime Soulé --- td/check_test.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/td/check_test.go b/td/check_test.go index 6a65969d..1ea683e3 100644 --- a/td/check_test.go +++ b/td/check_test.go @@ -61,6 +61,7 @@ type expectedError struct { Expected expectedErrorMatch Summary expectedErrorMatch Located bool + Under expectedErrorMatch Origin *expectedError } @@ -86,6 +87,10 @@ func indent(str string, numSpc int) string { return strings.ReplaceAll(str, "\n", "\n\t"+strings.Repeat(" ", numSpc)) } +func fullError(err *ctxerr.Error) string { + return strings.ReplaceAll(err.Error(), "\n", "\n\t> ") +} + func cmpErrorStr(t *testing.T, err *ctxerr.Error, got string, expected expectedErrorMatch, fieldName string, args ...any, @@ -100,7 +105,7 @@ func cmpErrorStr(t *testing.T, err *ctxerr.Error, > %s`, tdutil.BuildTestName(args...), fieldName, indent(got, 10), indent(expected.Exact, 10), - strings.ReplaceAll(err.Error(), "\n\t", "\n\t> ")) + fullError(err)) return false } @@ -113,7 +118,7 @@ func cmpErrorStr(t *testing.T, err *ctxerr.Error, tdutil.BuildTestName(args...), fieldName, indent(got, 16), indent(expected.Contain, 16), - strings.ReplaceAll(err.Error(), "\n\t", "\n\t> ")) + fullError(err)) return false } @@ -126,7 +131,7 @@ func cmpErrorStr(t *testing.T, err *ctxerr.Error, tdutil.BuildTestName(args...), fieldName, indent(got, 14), indent(expected.Match.String(), 14), - strings.ReplaceAll(err.Error(), "\n\t", "\n\t> ")) + fullError(err)) return false } @@ -162,6 +167,16 @@ func matchError(t *testing.T, err *ctxerr.Error, expectedError expectedError, return false } + // under + serr, under := err.Error(), "" + if pos := strings.Index(serr, "\n[under operator "); pos > 0 { + under = serr[pos+2:] + under = under[:strings.IndexByte(under, ']')] + } + if !cmpErrorStr(t, err, under, expectedError.Under, "[under operator …]", args...) { + return false + } + // If expected is a TestDeep, the Location should be set if expectedIsTestDeep { expectedError.Located = true From 9345161ebfec8db4ac59496049476b5c5603b36a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20Soul=C3=A9?= Date: Sun, 26 Mar 2023 22:55:19 +0200 Subject: [PATCH 2/3] refactor: don't call MapIndex twice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime Soulé --- td/td_map.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/td/td_map.go b/td/td_map.go index 0d20d165..fe2e3a83 100644 --- a/td/td_map.go +++ b/td/td_map.go @@ -322,7 +322,7 @@ func (m *tdMap) match(ctx ctxerr.Context, got reflect.Value) (err *ctxerr.Error) } err = deepValueEqual(ctx.AddMapKey(entryInfo.key), - got.MapIndex(entryInfo.key), entryInfo.expected) + gotValue, entryInfo.expected) if err != nil { return err } From 30abd09bcbb22d89ba87e3cd07dc306b721292b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20Soul=C3=A9?= Date: Sun, 26 Mar 2023 23:17:07 +0200 Subject: [PATCH 3/3] fix(JSON): errors with wrong origin operator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In error reports, the origin operator was sometimes missing and sometimes wrong when using JSON, SubJSONOf and SuperJSONOf operators, as in: [under operator tdJSONUnmarshaler.resolveOp.func1 at lex.go:156] Now the location of internal operators, as the ones used by JSON operators, cannot appear in error reports anymore and so do not mask the location of user visible operators. Signed-off-by: Maxime Soulé --- td/equal.go | 8 +++- td/td_json.go | 9 ++-- td/td_json_test.go | 114 ++++++++++++++++++++++++++++++++++++++++++++- td/types.go | 3 ++ 4 files changed, 127 insertions(+), 7 deletions(-) diff --git a/td/equal.go b/td/equal.go index 0815437e..7a611ad2 100644 --- a/td/equal.go +++ b/td/equal.go @@ -55,7 +55,9 @@ func nilHandler(ctx ctxerr.Context, got, expected reflect.Value) *ctxerr.Error { if expected.IsValid() { // here: !got.IsValid() if expected.Type().Implements(testDeeper) { curOperator := dark.MustGetInterface(expected).(TestDeep) - ctx.CurOperator = curOperator + if curOperator.GetLocation().IsInitialized() { + ctx.CurOperator = curOperator + } if curOperator.HandleInvalid() { return curOperator.Match(ctx, got) } @@ -201,7 +203,9 @@ func deepValueEqual(ctx ctxerr.Context, got, expected reflect.Value) (err *ctxer } } - ctx.CurOperator = curOperator + if curOperator.GetLocation().IsInitialized() { + ctx.CurOperator = curOperator + } return curOperator.Match(ctx, got) } diff --git a/td/td_json.go b/td/td_json.go index b807a365..decbe752 100644 --- a/td/td_json.go +++ b/td/td_json.go @@ -330,7 +330,7 @@ type tdJSONPlaceholder struct { func newJSONNamedPlaceholder(name string, expectedValue any) TestDeep { p := tdJSONPlaceholder{ tdJSONSmuggler: tdJSONSmuggler{ - tdSmugglerBase: newSmugglerBase(expectedValue, 1), + tdSmugglerBase: newSmugglerBase(expectedValue, -100), // without location }, name: name, } @@ -344,7 +344,7 @@ func newJSONNamedPlaceholder(name string, expectedValue any) TestDeep { func newJSONNumPlaceholder(num uint64, expectedValue any) TestDeep { p := tdJSONPlaceholder{ tdJSONSmuggler: tdJSONSmuggler{ - tdSmugglerBase: newSmugglerBase(expectedValue, 1), + tdSmugglerBase: newSmugglerBase(expectedValue, -100), // without location }, num: num, } @@ -397,11 +397,12 @@ type tdJSONEmbedded struct { } func newJSONEmbedded(tdOp TestDeep) TestDeep { - return &tdJSONEmbedded{ + e := tdJSONEmbedded{ tdJSONSmuggler: tdJSONSmuggler{ - tdSmugglerBase: newSmugglerBase(tdOp, 1), + tdSmugglerBase: newSmugglerBase(tdOp, -100), // without location }, } + return &e } func (e *tdJSONEmbedded) MarshalJSON() ([]byte, error) { diff --git a/td/td_json_test.go b/td/td_json_test.go index bd0827f9..6b255840 100644 --- a/td/td_json_test.go +++ b/td/td_json_test.go @@ -25,6 +25,11 @@ func (r errReader) Read(p []byte) (int, error) { return 0, errors.New("an error occurred") } +const ( + insideOpJSON = " inside operator JSON at td_json_test.go:" + underOpJSON = "under operator JSON at td_json_test.go:" +) + func TestJSON(t *testing.T) { type MyStruct struct { Name string `json:"name"` @@ -268,6 +273,7 @@ func TestJSON(t *testing.T) { expectedError{ Message: mustBe("json.Marshal failed"), Summary: mustContain("json: unsupported type"), + Under: mustContain(underOpJSON), }) checkError(t, map[string]string{"zip": "pipo"}, @@ -280,15 +286,71 @@ func TestJSON(t *testing.T) { }`), Expected: mustBe(`JSON(SuperMapOf(map[string]interface {}{ "zip": "bingo", - }))`), + }))`, + ), + Under: mustContain("under operator All at "), Origin: &expectedError{ Path: mustBe(`DATA["zip"]`), Message: mustBe(`values differ`), Got: mustBe(`"pipo"`), Expected: mustBe(`"bingo"`), + Under: mustContain("under operator SuperMapOf at line 1:0 (pos 0)" + insideOpJSON), }, }) + checkError(t, map[string]string{"zip": "pipo"}, + td.JSON(`SuperMapOf({"zip":$1})`, "bingo"), + expectedError{ + Path: mustBe(`DATA["zip"]`), + Message: mustBe("values differ"), + Got: mustBe(`"pipo"`), + Expected: mustBe(`"bingo"`), + Under: mustContain("under operator SuperMapOf at line 1:0 (pos 0)" + insideOpJSON), + }) + + checkError(t, json.RawMessage(`"pipo:bingo"`), + td.JSON(`Re(r;^pipo:(\w+);, ["bad"])`), + expectedError{ + Path: mustBe(`(DATA =~ ^pipo:(\w+))[0]`), + Got: mustBe(`"bingo"`), + Expected: mustBe(`"bad"`), + Under: mustContain("under operator Re at line 1:0 (pos 0)" + insideOpJSON), + }) + + checkError(t, json.RawMessage(`"pipo:bingo"`), + td.JSON(`Re(r;^pipo:(\w+);, [$1])`, "bad"), + expectedError{ + Path: mustBe(`(DATA =~ ^pipo:(\w+))[0]`), + Got: mustBe(`"bingo"`), + Expected: mustBe(`"bad"`), + Under: mustContain("under operator Re at line 1:0 (pos 0)" + insideOpJSON), + }) + + checkError(t, json.RawMessage(`"pipo:bingo"`), + td.JSON(`Re(r;^pipo:(\w+);, [$param])`, td.Tag("param", "bad")), + expectedError{ + Path: mustBe(`(DATA =~ ^pipo:(\w+))[0]`), + Got: mustBe(`"bingo"`), + Expected: mustBe(`"bad"`), + Under: mustContain("under operator Re at line 1:0 (pos 0)" + insideOpJSON), + }) + + checkError(t, json.RawMessage(`"pipo:bingo"`), + td.JSON(`Re(r;^pipo:(\w+);, Bag($1))`, "bad"), + expectedError{ + Path: mustBe(`(DATA =~ ^pipo:(\w+))`), + Summary: mustBe(`Missing item: ("bad")` + "\n" + ` Extra item: ("bingo")`), + Under: mustContain("under operator Bag at line 1:19 (pos 19)" + insideOpJSON), + }) + + checkError(t, json.RawMessage(`"pipo:bingo"`), + td.JSON(`Re(r;^pipo:(\w+);, Bag($param))`, td.Tag("param", "bad")), + expectedError{ + Path: mustBe(`(DATA =~ ^pipo:(\w+))`), + Summary: mustBe(`Missing item: ("bad")` + "\n" + ` Extra item: ("bingo")`), + Under: mustContain("under operator Bag at line 1:19 (pos 19)" + insideOpJSON), + }) + // // Fatal errors checkError(t, "never tested", @@ -297,6 +359,7 @@ func TestJSON(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustContain("JSON file uNkNoWnFiLe.json cannot be read: "), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -305,6 +368,7 @@ func TestJSON(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe("usage: JSON(STRING_JSON|STRING_FILENAME|[]byte|json.RawMessage|io.Reader, ...), but received int as 1st parameter"), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -313,6 +377,7 @@ func TestJSON(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe("JSON read error: an error occurred"), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -321,6 +386,7 @@ func TestJSON(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustContain("JSON unmarshal error: "), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -331,6 +397,7 @@ func TestJSON(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`2 params have the same tag "foo"`), + Under: mustContain(underOpJSON), }) checkError(t, []int{42}, @@ -339,6 +406,7 @@ func TestJSON(t *testing.T) { Message: mustBe("an error occurred while unmarshalling JSON into func()"), Path: mustBe("DATA[0]"), Summary: mustBe("json: cannot unmarshal number into Go value of type func()"), + Under: mustContain(underOpJSON), }) checkError(t, []int{42}, @@ -347,6 +415,7 @@ func TestJSON(t *testing.T) { Message: mustBe("an error occurred while unmarshalling JSON into func()"), Path: mustBe("DATA[0]"), Summary: mustBe("json: cannot unmarshal number into Go value of type func()"), + Under: mustContain(underOpJSON), }) // numeric placeholders @@ -356,6 +425,7 @@ func TestJSON(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: invalid numeric placeholder at line 1:5 (pos 5)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -364,6 +434,7 @@ func TestJSON(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: invalid numeric placeholder "$000", it should start at "$1" at line 1:4 (pos 4)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -372,6 +443,7 @@ func TestJSON(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: numeric placeholder "$1", but no params given at line 1:4 (pos 4)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -380,6 +452,7 @@ func TestJSON(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: numeric placeholder "$3", but only one param given at line 1:7 (pos 7)`), + Under: mustContain(underOpJSON), }) // $^Operator @@ -389,6 +462,7 @@ func TestJSON(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: $^ must be followed by an operator name at line 1:4 (pos 4)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -397,6 +471,7 @@ func TestJSON(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: $^ must be followed by an operator name at line 1:5 (pos 5)`), + Under: mustContain(underOpJSON), }) // named placeholders @@ -409,6 +484,7 @@ func TestJSON(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: bad placeholder "$bad%" at line 3:3 (pos 10)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -417,6 +493,7 @@ func TestJSON(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: unknown placeholder "$unknown" at line 1:4 (pos 4)`), + Under: mustContain(underOpJSON), }) // @@ -532,9 +609,20 @@ func TestJSONInside(t *testing.T) { Path: mustBe(`DATA["val2"]`), Got: mustBe("2.0"), Expected: mustBe("1.0 ≤ got < 2.0"), + Under: mustContain("under operator Between at line 1:32 (pos 32)" + insideOpJSON), }) } + checkError(t, json.RawMessage(`123`), + td.JSON(`Between(0, 2)`), + expectedError{ + Message: mustBe("values differ"), + Path: mustBe(`DATA`), + Got: mustBe("123.0"), + Expected: mustBe("0.0 ≤ got ≤ 2.0"), + Under: mustContain("under operator Between at line 1:0 (pos 0)" + insideOpJSON), + }) + checkOK(t, got, td.JSON(`{"val1": Between(1, 1), "val2": Between(2, 2, "[]")}`)) checkOK(t, got, @@ -552,6 +640,7 @@ func TestJSONInside(t *testing.T) { Path: mustBe(`DATA["val2"]`), Got: mustBe("2.0"), Expected: mustBe("2.0 < got ≤ 3.0"), + Under: mustContain("under operator Between at line 1:20 (pos 20)" + insideOpJSON), }) } for _, bounds := range []string{"][", "BoundsOutOut"} { @@ -562,6 +651,7 @@ func TestJSONInside(t *testing.T) { Path: mustBe(`DATA["val2"]`), Got: mustBe("2.0"), Expected: mustBe("2.0 < got < 3.0"), + Under: mustContain("under operator Between at line 1:20 (pos 20)" + insideOpJSON), }, "using bounds %q", bounds) checkError(t, got, @@ -571,6 +661,7 @@ func TestJSONInside(t *testing.T) { Path: mustBe(`DATA["val2"]`), Got: mustBe("2.0"), Expected: mustBe("1.0 < got < 2.0"), + Under: mustContain("under operator Between at line 1:20 (pos 20)" + insideOpJSON), }, "using bounds %q", bounds) } @@ -584,6 +675,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: Between() bad 3rd parameter, use "[]", "[[", "]]" or "][" at line 2:10 (pos 12)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", td.JSON(`{ @@ -593,6 +685,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: Between() bad 3rd parameter, use "[]", "[[", "]]" or "][" at line 2:10 (pos 12)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -601,6 +694,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: Between() requires 2 or 3 parameters at line 1:9 (pos 9)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -609,6 +703,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: Between() requires 2 or 3 parameters at line 1:9 (pos 9)`), + Under: mustContain(underOpJSON), }) }) @@ -625,6 +720,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: N() requires 1 or 2 parameters at line 1:9 (pos 9)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -633,6 +729,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: N() requires 1 or 2 parameters at line 1:9 (pos 9)`), + Under: mustContain(underOpJSON), }) }) @@ -650,6 +747,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: Re() requires 1 or 2 parameters at line 1:9 (pos 9)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -658,6 +756,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: Re() requires 1 or 2 parameters at line 1:9 (pos 9)`), + Under: mustContain(underOpJSON), }) }) @@ -673,6 +772,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: SubMapOf() requires only one parameter at line 1:2 (pos 2)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -681,6 +781,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: SubMapOf() requires only one parameter at line 1:2 (pos 2)`), + Under: mustContain(underOpJSON), }) }) @@ -696,6 +797,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: SuperMapOf() requires only one parameter at line 1:2 (pos 2)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -704,6 +806,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: SuperMapOf() requires only one parameter at line 1:2 (pos 2)`), + Under: mustContain(underOpJSON), }) }) @@ -715,6 +818,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: unknown operator UnknownOp() at line 1:2 (pos 2)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -723,6 +827,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: Catch() is not usable in JSON() at line 1:2 (pos 2)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -731,6 +836,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: JSON() is not usable in JSON(), use literal JSON instead at line 1:2 (pos 2)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -739,6 +845,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: All() requires at least one parameter at line 1:2 (pos 2)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -747,6 +854,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: Empty() requires no parameters at line 1:2 (pos 2)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -755,6 +863,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: HasPrefix() requires only one parameter at line 1:2 (pos 2)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -763,6 +872,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: JSONPointer() requires 2 parameters at line 1:2 (pos 2)`), + Under: mustContain(underOpJSON), }) checkError(t, "never tested", @@ -771,6 +881,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of JSON operator"), Path: mustBe("DATA"), Summary: mustBe(`JSON unmarshal error: JSONPointer() bad #1 parameter type: string required but float64 received at line 1:2 (pos 2)`), + Under: mustContain(underOpJSON), }) // This one is not caught by JSON, but by Re itself, as the number @@ -781,6 +892,7 @@ func TestJSONInside(t *testing.T) { Message: mustBe("bad usage of Re operator"), Path: mustBe("DATA"), Summary: mustBe(`usage: Re(STRING|*regexp.Regexp[, NON_NIL_CAPTURE]), but received float64 as 1st parameter`), + Under: mustContain("under operator Re at line 1:0 (pos 0)" + insideOpJSON), }) }) } diff --git a/td/types.go b/td/types.go index 267f26ee..408f0344 100644 --- a/td/types.go +++ b/td/types.go @@ -87,6 +87,9 @@ func pkgFunc(full string) (string, string) { // 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 {