delve/service/test/common_test.go
Alessandro Arzilli ca611db449
terminal: restore breakpoints set with line offset on restart (#3425)
Change FindLocation so it can return a substitute location expression
and propagate it to pkg/terminal/command.
When breakpoints are set using the syntax :<lineno> or +<lineno>
produce a substitute location expression that doesn't depend on having
a valid scope and can be used to restore the breakpoint.

Fixes #3423
2023-07-20 12:29:59 +02:00

143 lines
3.3 KiB
Go

package service_test
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/go-delve/delve/service/api"
"github.com/go-delve/delve/service/rpc1"
"github.com/go-delve/delve/service/rpc2"
)
func assertNoError(err error, t *testing.T, s string) {
if err != nil {
_, file, line, _ := runtime.Caller(1)
fname := filepath.Base(file)
t.Fatalf("failed assertion at %s:%d: %s - %s\n", fname, line, s, err)
}
}
func assertError(err error, t *testing.T, s string) {
if err == nil {
_, file, line, _ := runtime.Caller(1)
fname := filepath.Base(file)
t.Fatalf("failed assertion at %s:%d: %s (no error)\n", fname, line, s)
}
if strings.Contains(err.Error(), "Internal debugger error") {
_, file, line, _ := runtime.Caller(1)
fname := filepath.Base(file)
t.Fatalf("failed assertion at %s:%d: %s internal debugger error: %v\n", fname, line, s, err)
}
}
func init() {
runtime.GOMAXPROCS(2)
}
type nextTest struct {
begin, end int
}
func testProgPath(t *testing.T, name string) string {
fp, err := filepath.Abs(fmt.Sprintf("_fixtures/%s.go", name))
if err != nil {
t.Fatal(err)
}
if _, err := os.Stat(fp); err != nil {
fp, err = filepath.Abs(fmt.Sprintf("../../_fixtures/%s.go", name))
if err != nil {
t.Fatal(err)
}
}
sympath, err := filepath.EvalSymlinks(fp)
if err == nil {
fp = strings.ReplaceAll(sympath, "\\", "/")
}
return fp
}
type BreakpointLister interface {
ListBreakpoints() ([]*api.Breakpoint, error)
}
func countBreakpoints(t *testing.T, c interface{}) int {
var bps []*api.Breakpoint
var err error
switch c := c.(type) {
case *rpc2.RPCClient:
bps, err = c.ListBreakpoints(false)
case *rpc1.RPCClient:
bps, err = c.ListBreakpoints()
}
assertNoError(err, t, "ListBreakpoints()")
bpcount := 0
for _, bp := range bps {
if bp.ID >= 0 {
bpcount++
}
}
return bpcount
}
type locationFinder1 interface {
FindLocation(api.EvalScope, string) ([]api.Location, error)
}
type locationFinder2 interface {
FindLocation(api.EvalScope, string, bool, [][2]string) ([]api.Location, string, error)
}
func findLocationHelper(t *testing.T, c interface{}, loc string, shouldErr bool, count int, checkAddr uint64) []uint64 {
var locs []api.Location
var err error
switch c := c.(type) {
case locationFinder1:
locs, err = c.FindLocation(api.EvalScope{GoroutineID: -1}, loc)
case locationFinder2:
locs, _, err = c.FindLocation(api.EvalScope{GoroutineID: -1}, loc, false, nil)
default:
t.Errorf("unexpected type %T passed to findLocationHelper", c)
}
t.Logf("FindLocation(\"%s\") → %v\n", loc, locs)
if shouldErr {
if err == nil {
t.Fatalf("Resolving location <%s> didn't return an error: %v", loc, locs)
}
} else {
if err != nil {
t.Fatalf("Error resolving location <%s>: %v", loc, err)
}
}
if (count >= 0) && (len(locs) != count) {
t.Fatalf("Wrong number of breakpoints returned for location <%s> (got %d, expected %d)", loc, len(locs), count)
}
if checkAddr != 0 && checkAddr != locs[0].PC {
t.Fatalf("Wrong address returned for location <%s> (got %#x, expected %#x)", loc, locs[0].PC, checkAddr)
}
addrs := make([]uint64, len(locs))
for i := range locs {
addrs[i] = locs[i].PC
}
return addrs
}
func getCurinstr(d3 api.AsmInstructions) *api.AsmInstruction {
for i := range d3 {
if d3[i].AtPC {
return &d3[i]
}
}
return nil
}