2020-05-13 06:38:10 +00:00
|
|
|
package locspec
|
2016-07-05 19:13:21 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
func parseLocationSpecNoError(t *testing.T, locstr string) LocationSpec {
|
2020-05-13 06:38:10 +00:00
|
|
|
spec, err := Parse(locstr)
|
2016-07-05 19:13:21 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Error parsing %q: %v", locstr, err)
|
|
|
|
}
|
|
|
|
return spec
|
|
|
|
}
|
|
|
|
|
|
|
|
func assertNormalLocationSpec(t *testing.T, locstr string, tgt NormalLocationSpec) {
|
|
|
|
spec := parseLocationSpecNoError(t, locstr)
|
|
|
|
|
|
|
|
nls, ok := spec.(*NormalLocationSpec)
|
|
|
|
if !ok {
|
|
|
|
t.Fatalf("Location %q: expected NormalLocationSpec got %#v", locstr, spec)
|
|
|
|
}
|
|
|
|
|
|
|
|
if nls.Base != tgt.Base {
|
|
|
|
t.Fatalf("Location %q: expected 'Base' %q got %q", locstr, tgt.Base, nls.Base)
|
|
|
|
}
|
|
|
|
|
|
|
|
if nls.LineOffset != tgt.LineOffset {
|
|
|
|
t.Fatalf("Location %q: expected 'LineOffset' %d got %d", locstr, tgt.LineOffset, nls.LineOffset)
|
|
|
|
}
|
|
|
|
|
|
|
|
if tgt.FuncBase == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if nls.FuncBase == nil {
|
|
|
|
t.Fatalf("Location %q: expected non-nil 'FuncBase'", locstr)
|
|
|
|
}
|
|
|
|
|
|
|
|
if *(tgt.FuncBase) != *(nls.FuncBase) {
|
|
|
|
t.Fatalf("Location %q: expected 'FuncBase':\n%#v\ngot:\n%#v", locstr, tgt.FuncBase, nls.FuncBase)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFunctionLocationParsing(t *testing.T) {
|
|
|
|
// Function locations, simple package names, no line offset
|
|
|
|
assertNormalLocationSpec(t, "proc.(*Process).Continue", NormalLocationSpec{"proc.(*Process).Continue", &FuncLocationSpec{PackageName: "proc", ReceiverName: "Process", BaseName: "Continue"}, -1})
|
|
|
|
assertNormalLocationSpec(t, "proc.Process.Continue", NormalLocationSpec{"proc.Process.Continue", &FuncLocationSpec{PackageName: "proc", ReceiverName: "Process", BaseName: "Continue"}, -1})
|
|
|
|
assertNormalLocationSpec(t, "proc.Continue", NormalLocationSpec{"proc.Continue", &FuncLocationSpec{PackageOrReceiverName: "proc", BaseName: "Continue"}, -1})
|
|
|
|
assertNormalLocationSpec(t, "(*Process).Continue", NormalLocationSpec{"(*Process).Continue", &FuncLocationSpec{ReceiverName: "Process", BaseName: "Continue"}, -1})
|
|
|
|
assertNormalLocationSpec(t, "Continue", NormalLocationSpec{"Continue", &FuncLocationSpec{BaseName: "Continue"}, -1})
|
|
|
|
|
|
|
|
// Function locations, simple package names, line offsets
|
|
|
|
assertNormalLocationSpec(t, "proc.(*Process).Continue:10", NormalLocationSpec{"proc.(*Process).Continue", &FuncLocationSpec{PackageName: "proc", ReceiverName: "Process", BaseName: "Continue"}, 10})
|
|
|
|
assertNormalLocationSpec(t, "proc.Process.Continue:10", NormalLocationSpec{"proc.Process.Continue", &FuncLocationSpec{PackageName: "proc", ReceiverName: "Process", BaseName: "Continue"}, 10})
|
|
|
|
assertNormalLocationSpec(t, "proc.Continue:10", NormalLocationSpec{"proc.Continue", &FuncLocationSpec{PackageOrReceiverName: "proc", BaseName: "Continue"}, 10})
|
|
|
|
assertNormalLocationSpec(t, "(*Process).Continue:10", NormalLocationSpec{"(*Process).Continue", &FuncLocationSpec{ReceiverName: "Process", BaseName: "Continue"}, 10})
|
|
|
|
assertNormalLocationSpec(t, "Continue:10", NormalLocationSpec{"Continue", &FuncLocationSpec{BaseName: "Continue"}, 10})
|
|
|
|
|
|
|
|
// Function locations, package paths, no line offsets
|
2019-01-04 18:39:25 +00:00
|
|
|
assertNormalLocationSpec(t, "github.com/go-delve/delve/pkg/proc.(*Process).Continue", NormalLocationSpec{"github.com/go-delve/delve/pkg/proc.(*Process).Continue", &FuncLocationSpec{PackageName: "github.com/go-delve/delve/pkg/proc", ReceiverName: "Process", BaseName: "Continue"}, -1})
|
|
|
|
assertNormalLocationSpec(t, "github.com/go-delve/delve/pkg/proc.Process.Continue", NormalLocationSpec{"github.com/go-delve/delve/pkg/proc.Process.Continue", &FuncLocationSpec{PackageName: "github.com/go-delve/delve/pkg/proc", ReceiverName: "Process", BaseName: "Continue"}, -1})
|
|
|
|
assertNormalLocationSpec(t, "github.com/go-delve/delve/pkg/proc.Continue", NormalLocationSpec{"github.com/go-delve/delve/pkg/proc.Continue", &FuncLocationSpec{PackageName: "github.com/go-delve/delve/pkg/proc", BaseName: "Continue"}, -1})
|
2016-07-05 19:13:21 +00:00
|
|
|
|
|
|
|
// Function locations, package paths, line offsets
|
2019-01-04 18:39:25 +00:00
|
|
|
assertNormalLocationSpec(t, "github.com/go-delve/delve/pkg/proc.(*Process).Continue:10", NormalLocationSpec{"github.com/go-delve/delve/pkg/proc.(*Process).Continue", &FuncLocationSpec{PackageName: "github.com/go-delve/delve/pkg/proc", ReceiverName: "Process", BaseName: "Continue"}, 10})
|
|
|
|
assertNormalLocationSpec(t, "github.com/go-delve/delve/pkg/proc.Process.Continue:10", NormalLocationSpec{"github.com/go-delve/delve/pkg/proc.Process.Continue", &FuncLocationSpec{PackageName: "github.com/go-delve/delve/pkg/proc", ReceiverName: "Process", BaseName: "Continue"}, 10})
|
|
|
|
assertNormalLocationSpec(t, "github.com/go-delve/delve/pkg/proc.Continue:10", NormalLocationSpec{"github.com/go-delve/delve/pkg/proc.Continue", &FuncLocationSpec{PackageName: "github.com/go-delve/delve/pkg/proc", BaseName: "Continue"}, 10})
|
2016-07-05 19:13:21 +00:00
|
|
|
}
|
2022-08-14 14:01:39 +00:00
|
|
|
|
|
|
|
func assertSubstitutePathEqual(t *testing.T, expected string, substituted string) {
|
*: misc improvements to config command and substitute-path rules (#3335)
A series of interconnected changes to both the terminal command
'config', DAP command 'dlv config', quality of life improvements to how
substitute-path works, and better documentation.
- Let 'config substitute-path' show the current substitute path rules
- Add a -clear command to 'config substitute-path'
- Support 'config-debug-info-directories'
- rewrite SubstitutePath to be platform independent (see below)
- document path substitution more
Regarding the rewrite of SubstitutePath: the previous version used
runtime.GOOS and filepath.IsAbs to determine which filepath separator to use
and if matching should be case insensitive. This is wrong in all situations
where the client and server run on different OSes, when examining core files
and when cross-compilation is involved.
The new version of SubstitutePath checks the rules and the input path to
determine if Windows is involved in the process, if it looks like it is it
switches to case-insensitive matching. It uses a lax version of
filepath.IsAbs to determine if a path is absolute and tries to avoid having
to select a path separator as much as possible
Fixes #2891, #2890, #2889, #3179, #3332, #3343
2023-05-02 19:23:59 +00:00
|
|
|
t.Helper()
|
2022-08-14 14:01:39 +00:00
|
|
|
if expected != substituted {
|
*: misc improvements to config command and substitute-path rules (#3335)
A series of interconnected changes to both the terminal command
'config', DAP command 'dlv config', quality of life improvements to how
substitute-path works, and better documentation.
- Let 'config substitute-path' show the current substitute path rules
- Add a -clear command to 'config substitute-path'
- Support 'config-debug-info-directories'
- rewrite SubstitutePath to be platform independent (see below)
- document path substitution more
Regarding the rewrite of SubstitutePath: the previous version used
runtime.GOOS and filepath.IsAbs to determine which filepath separator to use
and if matching should be case insensitive. This is wrong in all situations
where the client and server run on different OSes, when examining core files
and when cross-compilation is involved.
The new version of SubstitutePath checks the rules and the input path to
determine if Windows is involved in the process, if it looks like it is it
switches to case-insensitive matching. It uses a lax version of
filepath.IsAbs to determine if a path is absolute and tries to avoid having
to select a path separator as much as possible
Fixes #2891, #2890, #2889, #3179, #3332, #3343
2023-05-02 19:23:59 +00:00
|
|
|
t.Errorf("Expected substitutedPath to be %s got %s instead", expected, substituted)
|
2022-08-14 14:01:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSubstitutePathUnix(t *testing.T) {
|
|
|
|
// Relative paths mapping
|
|
|
|
assertSubstitutePathEqual(t, "/my/asb/folder/relative/path", SubstitutePath("relative/path", [][2]string{{"", "/my/asb/folder/"}}))
|
|
|
|
assertSubstitutePathEqual(t, "/already/abs/path", SubstitutePath("/already/abs/path", [][2]string{{"", "/my/asb/folder/"}}))
|
|
|
|
assertSubstitutePathEqual(t, "relative/path", SubstitutePath("/my/asb/folder/relative/path", [][2]string{{"/my/asb/folder/", ""}}))
|
|
|
|
assertSubstitutePathEqual(t, "/another/folder/relative/path", SubstitutePath("/another/folder/relative/path", [][2]string{{"/my/asb/folder/", ""}}))
|
|
|
|
assertSubstitutePathEqual(t, "my/path", SubstitutePath("relative/path/my/path", [][2]string{{"relative/path", ""}}))
|
|
|
|
assertSubstitutePathEqual(t, "/abs/my/path", SubstitutePath("/abs/my/path", [][2]string{{"abs/my", ""}}))
|
|
|
|
|
|
|
|
// Absolute paths mapping
|
|
|
|
assertSubstitutePathEqual(t, "/new/mapping/path", SubstitutePath("/original/path", [][2]string{{"/original", "/new/mapping"}}))
|
|
|
|
assertSubstitutePathEqual(t, "/no/change/path", SubstitutePath("/no/change/path", [][2]string{{"/original", "/new/mapping"}}))
|
|
|
|
assertSubstitutePathEqual(t, "/folder/should_not_be_replaced/path", SubstitutePath("/folder/should_not_be_replaced/path", [][2]string{{"should_not_be_replaced", ""}}))
|
|
|
|
|
|
|
|
// Mix absolute and relative mapping
|
|
|
|
assertSubstitutePathEqual(t, "/new/mapping/path", SubstitutePath("/original/path", [][2]string{{"", "/my/asb/folder/"}, {"/my/asb/folder/", ""}, {"/original", "/new/mapping"}}))
|
|
|
|
assertSubstitutePathEqual(t, "/my/asb/folder/path", SubstitutePath("path", [][2]string{{"/original", "/new/mapping"}, {"", "/my/asb/folder/"}, {"/my/asb/folder/", ""}}))
|
|
|
|
assertSubstitutePathEqual(t, "path", SubstitutePath("/my/asb/folder/path", [][2]string{{"/original", "/new/mapping"}, {"/my/asb/folder/", ""}, {"", "/my/asb/folder/"}}))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSubstitutePathWindows(t *testing.T) {
|
|
|
|
// Relative paths mapping
|
|
|
|
assertSubstitutePathEqual(t, "c:\\my\\asb\\folder\\relative\\path", SubstitutePath("relative\\path", [][2]string{{"", "c:\\my\\asb\\folder\\"}}))
|
*: misc improvements to config command and substitute-path rules (#3335)
A series of interconnected changes to both the terminal command
'config', DAP command 'dlv config', quality of life improvements to how
substitute-path works, and better documentation.
- Let 'config substitute-path' show the current substitute path rules
- Add a -clear command to 'config substitute-path'
- Support 'config-debug-info-directories'
- rewrite SubstitutePath to be platform independent (see below)
- document path substitution more
Regarding the rewrite of SubstitutePath: the previous version used
runtime.GOOS and filepath.IsAbs to determine which filepath separator to use
and if matching should be case insensitive. This is wrong in all situations
where the client and server run on different OSes, when examining core files
and when cross-compilation is involved.
The new version of SubstitutePath checks the rules and the input path to
determine if Windows is involved in the process, if it looks like it is it
switches to case-insensitive matching. It uses a lax version of
filepath.IsAbs to determine if a path is absolute and tries to avoid having
to select a path separator as much as possible
Fixes #2891, #2890, #2889, #3179, #3332, #3343
2023-05-02 19:23:59 +00:00
|
|
|
assertSubstitutePathEqual(t, "F:\\already\\abs\\path", SubstitutePath("F:\\already\\abs\\path", [][2]string{{"", "c:\\my\\asb\\folder\\"}}))
|
2022-08-14 14:01:39 +00:00
|
|
|
assertSubstitutePathEqual(t, "relative\\path", SubstitutePath("C:\\my\\asb\\folder\\relative\\path", [][2]string{{"c:\\my\\asb\\folder\\", ""}}))
|
*: misc improvements to config command and substitute-path rules (#3335)
A series of interconnected changes to both the terminal command
'config', DAP command 'dlv config', quality of life improvements to how
substitute-path works, and better documentation.
- Let 'config substitute-path' show the current substitute path rules
- Add a -clear command to 'config substitute-path'
- Support 'config-debug-info-directories'
- rewrite SubstitutePath to be platform independent (see below)
- document path substitution more
Regarding the rewrite of SubstitutePath: the previous version used
runtime.GOOS and filepath.IsAbs to determine which filepath separator to use
and if matching should be case insensitive. This is wrong in all situations
where the client and server run on different OSes, when examining core files
and when cross-compilation is involved.
The new version of SubstitutePath checks the rules and the input path to
determine if Windows is involved in the process, if it looks like it is it
switches to case-insensitive matching. It uses a lax version of
filepath.IsAbs to determine if a path is absolute and tries to avoid having
to select a path separator as much as possible
Fixes #2891, #2890, #2889, #3179, #3332, #3343
2023-05-02 19:23:59 +00:00
|
|
|
assertSubstitutePathEqual(t, "F:\\another\\folder\\relative\\path", SubstitutePath("F:\\another\\folder\\relative\\path", [][2]string{{"c:\\my\\asb\\folder\\", ""}}))
|
2022-08-14 14:01:39 +00:00
|
|
|
assertSubstitutePathEqual(t, "my\\path", SubstitutePath("relative\\path\\my\\path", [][2]string{{"relative\\path", ""}}))
|
|
|
|
assertSubstitutePathEqual(t, "c:\\abs\\my\\path", SubstitutePath("c:\\abs\\my\\path", [][2]string{{"abs\\my", ""}}))
|
|
|
|
|
|
|
|
// Absolute paths mapping
|
|
|
|
assertSubstitutePathEqual(t, "c:\\new\\mapping\\path", SubstitutePath("D:\\original\\path", [][2]string{{"d:\\original", "c:\\new\\mapping"}}))
|
*: misc improvements to config command and substitute-path rules (#3335)
A series of interconnected changes to both the terminal command
'config', DAP command 'dlv config', quality of life improvements to how
substitute-path works, and better documentation.
- Let 'config substitute-path' show the current substitute path rules
- Add a -clear command to 'config substitute-path'
- Support 'config-debug-info-directories'
- rewrite SubstitutePath to be platform independent (see below)
- document path substitution more
Regarding the rewrite of SubstitutePath: the previous version used
runtime.GOOS and filepath.IsAbs to determine which filepath separator to use
and if matching should be case insensitive. This is wrong in all situations
where the client and server run on different OSes, when examining core files
and when cross-compilation is involved.
The new version of SubstitutePath checks the rules and the input path to
determine if Windows is involved in the process, if it looks like it is it
switches to case-insensitive matching. It uses a lax version of
filepath.IsAbs to determine if a path is absolute and tries to avoid having
to select a path separator as much as possible
Fixes #2891, #2890, #2889, #3179, #3332, #3343
2023-05-02 19:23:59 +00:00
|
|
|
assertSubstitutePathEqual(t, "F:\\no\\change\\path", SubstitutePath("F:\\no\\change\\path", [][2]string{{"d:\\original", "c:\\new\\mapping"}}))
|
2022-08-14 14:01:39 +00:00
|
|
|
assertSubstitutePathEqual(t, "c:\\folder\\should_not_be_replaced\\path", SubstitutePath("c:\\folder\\should_not_be_replaced\\path", [][2]string{{"should_not_be_replaced", ""}}))
|
|
|
|
|
|
|
|
// Mix absolute and relative mapping
|
|
|
|
assertSubstitutePathEqual(t, "c:\\new\\mapping\\path", SubstitutePath("D:\\original\\path", [][2]string{{"", "c:\\my\\asb\\folder\\"}, {"c:\\my\\asb\\folder\\", ""}, {"d:\\original", "c:\\new\\mapping"}}))
|
|
|
|
assertSubstitutePathEqual(t, "c:\\my\\asb\\folder\\path\\", SubstitutePath("path\\", [][2]string{{"d:\\original", "c:\\new\\mapping"}, {"", "c:\\my\\asb\\folder\\"}, {"c:\\my\\asb\\folder\\", ""}}))
|
|
|
|
assertSubstitutePathEqual(t, "path", SubstitutePath("C:\\my\\asb\\folder\\path", [][2]string{{"d:\\original", "c:\\new\\mapping"}, {"c:\\my\\asb\\folder\\", ""}, {"", "c:\\my\\asb\\folder\\"}}))
|
|
|
|
}
|
*: misc improvements to config command and substitute-path rules (#3335)
A series of interconnected changes to both the terminal command
'config', DAP command 'dlv config', quality of life improvements to how
substitute-path works, and better documentation.
- Let 'config substitute-path' show the current substitute path rules
- Add a -clear command to 'config substitute-path'
- Support 'config-debug-info-directories'
- rewrite SubstitutePath to be platform independent (see below)
- document path substitution more
Regarding the rewrite of SubstitutePath: the previous version used
runtime.GOOS and filepath.IsAbs to determine which filepath separator to use
and if matching should be case insensitive. This is wrong in all situations
where the client and server run on different OSes, when examining core files
and when cross-compilation is involved.
The new version of SubstitutePath checks the rules and the input path to
determine if Windows is involved in the process, if it looks like it is it
switches to case-insensitive matching. It uses a lax version of
filepath.IsAbs to determine if a path is absolute and tries to avoid having
to select a path separator as much as possible
Fixes #2891, #2890, #2889, #3179, #3332, #3343
2023-05-02 19:23:59 +00:00
|
|
|
|
|
|
|
type tRule struct {
|
|
|
|
from string
|
|
|
|
to string
|
|
|
|
}
|
|
|
|
|
|
|
|
type tCase struct {
|
|
|
|
rules []tRule
|
|
|
|
path string
|
|
|
|
res string
|
|
|
|
}
|
|
|
|
|
|
|
|
func platformCases() []tCase {
|
|
|
|
casesUnix := []tCase{
|
|
|
|
// Should not depend on separator at the end of rule path
|
|
|
|
{[]tRule{{"/tmp/path", "/new/path2"}}, "/tmp/path/file.go", "/new/path2/file.go"},
|
|
|
|
{[]tRule{{"/tmp/path/", "/new/path2/"}}, "/tmp/path/file.go", "/new/path2/file.go"},
|
|
|
|
{[]tRule{{"/tmp/path/", "/new/path2"}}, "/tmp/path/file.go", "/new/path2/file.go"},
|
|
|
|
{[]tRule{{"/tmp/path", "/new/path2/"}}, "/tmp/path/file.go", "/new/path2/file.go"},
|
|
|
|
// Should apply to directory prefixes
|
|
|
|
{[]tRule{{"/tmp/path", "/new/path2"}}, "/tmp/path-2/file.go", "/tmp/path-2/file.go"},
|
|
|
|
// Should apply to exact matches
|
|
|
|
{[]tRule{{"/tmp/path/file.go", "/new/path2/file2.go"}}, "/tmp/path/file.go", "/new/path2/file2.go"},
|
|
|
|
// First matched rule should be used
|
|
|
|
{[]tRule{
|
|
|
|
{"/tmp/path1", "/new/path1"},
|
|
|
|
{"/tmp/path2", "/new/path2"},
|
|
|
|
{"/tmp/path2", "/new/path3"}}, "/tmp/path2/file.go", "/new/path2/file.go"},
|
|
|
|
}
|
|
|
|
casesLinux := []tCase{
|
|
|
|
// Should be case-sensitive
|
|
|
|
{[]tRule{{"/tmp/path", "/new/path2"}}, "/TmP/path/file.go", "/TmP/path/file.go"},
|
|
|
|
}
|
|
|
|
casesFreebsd := []tCase{
|
|
|
|
// Should be case-sensitive
|
|
|
|
{[]tRule{{"/tmp/path", "/new/path2"}}, "/TmP/path/file.go", "/TmP/path/file.go"},
|
|
|
|
}
|
|
|
|
casesDarwin := []tCase{
|
|
|
|
// Can be either case-sensitive or case-insensitive depending on
|
|
|
|
// filesystem settings, we always treat it as case-sensitive.
|
|
|
|
{[]tRule{{"/tmp/path", "/new/path2"}}, "/TmP/PaTh/file.go", "/TmP/PaTh/file.go"},
|
|
|
|
}
|
|
|
|
casesWindows := []tCase{
|
|
|
|
// Should not depend on separator at the end of rule path
|
|
|
|
{[]tRule{{`c:\tmp\path`, `d:\new\path2`}}, `c:\tmp\path\file.go`, `d:\new\path2\file.go`},
|
|
|
|
{[]tRule{{`c:\tmp\path\`, `d:\new\path2\`}}, `c:\tmp\path\file.go`, `d:\new\path2\file.go`},
|
|
|
|
{[]tRule{{`c:\tmp\path`, `d:\new\path2\`}}, `c:\tmp\path\file.go`, `d:\new\path2\file.go`},
|
|
|
|
{[]tRule{{`c:\tmp\path\`, `d:\new\path2`}}, `c:\tmp\path\file.go`, `d:\new\path2/file.go`},
|
|
|
|
// Should apply to directory prefixes
|
|
|
|
{[]tRule{{`c:\tmp\path`, `d:\new\path2`}}, `c:\tmp\path-2\file.go`, `c:\tmp\path-2\file.go`},
|
|
|
|
// Should apply to exact matches
|
|
|
|
{[]tRule{{`c:\tmp\path\file.go`, `d:\new\path2\file2.go`}}, `c:\tmp\path\file.go`, `d:\new\path2\file2.go`},
|
|
|
|
// Should be case-insensitive
|
|
|
|
{[]tRule{{`c:\tmp\path`, `d:\new\path2`}}, `C:\TmP\PaTh\file.go`, `d:\new\path2\file.go`},
|
|
|
|
}
|
|
|
|
|
|
|
|
r := append(casesUnix, casesLinux...)
|
|
|
|
r = append(r, casesFreebsd...)
|
|
|
|
r = append(r, casesDarwin...)
|
|
|
|
r = append(r, casesWindows...)
|
|
|
|
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSubstitutePath(t *testing.T) {
|
|
|
|
for _, c := range platformCases() {
|
|
|
|
subRules := [][2]string{}
|
|
|
|
for _, r := range c.rules {
|
|
|
|
subRules = append(subRules, [2]string{r.from, r.to})
|
|
|
|
}
|
|
|
|
res := SubstitutePath(c.path, subRules)
|
|
|
|
if c.res != res {
|
|
|
|
t.Errorf("terminal.SubstitutePath(%q) => %q, want %q", c.path, res, c.res)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|