mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 18:57:18 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			376 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			376 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package servicetest
 | |
| 
 | |
| import (
 | |
| 	"net"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"runtime"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 
 | |
| 	protest "github.com/derekparker/delve/proc/test"
 | |
| 
 | |
| 	"github.com/derekparker/delve/service"
 | |
| 	"github.com/derekparker/delve/service/api"
 | |
| 	"github.com/derekparker/delve/service/rpc"
 | |
| )
 | |
| 
 | |
| func init() {
 | |
| 	runtime.GOMAXPROCS(2)
 | |
| }
 | |
| 
 | |
| func TestMain(m *testing.M) {
 | |
| 	os.Exit(protest.RunTestsWithFixtures(m))
 | |
| }
 | |
| 
 | |
| func withTestClient(name string, t *testing.T, fn func(c service.Client)) {
 | |
| 	listener, err := net.Listen("tcp", "localhost:0")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("couldn't start listener: %s\n", err)
 | |
| 	}
 | |
| 	defer listener.Close()
 | |
| 	server := rpc.NewServer(&service.Config{
 | |
| 		Listener:    listener,
 | |
| 		ProcessArgs: []string{protest.BuildFixture(name).Path},
 | |
| 	}, false)
 | |
| 	if err := server.Run(); err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	client := rpc.NewClient(listener.Addr().String())
 | |
| 	defer func() {
 | |
| 		client.Detach(true)
 | |
| 	}()
 | |
| 
 | |
| 	fn(client)
 | |
| }
 | |
| 
 | |
| func TestRunWithInvalidPath(t *testing.T) {
 | |
| 	listener, err := net.Listen("tcp", "localhost:0")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("couldn't start listener: %s\n", err)
 | |
| 	}
 | |
| 	defer listener.Close()
 | |
| 	server := rpc.NewServer(&service.Config{
 | |
| 		Listener:    listener,
 | |
| 		ProcessArgs: []string{"invalid_path"},
 | |
| 	}, false)
 | |
| 	if err := server.Run(); err == nil {
 | |
| 		t.Fatal("Expected Run to return error for invalid program path")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestClientServer_exit(t *testing.T) {
 | |
| 	withTestClient("continuetestprog", t, func(c service.Client) {
 | |
| 		state, err := c.GetState()
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 		if e, a := false, state.Exited; e != a {
 | |
| 			t.Fatalf("Expected exited %v, got %v", e, a)
 | |
| 		}
 | |
| 		state, err = c.Continue()
 | |
| 		if err == nil {
 | |
| 			t.Fatalf("Expected error after continue, got none")
 | |
| 		}
 | |
| 		if !strings.Contains(err.Error(), "exited") {
 | |
| 			t.Fatal("Expected exit message")
 | |
| 		}
 | |
| 		state, err = c.GetState()
 | |
| 		if err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		if state.CurrentThread == nil {
 | |
| 			t.Fatalf("Expected CurrentThread")
 | |
| 		}
 | |
| 		if e, a := true, state.Exited; e != a {
 | |
| 			t.Fatalf("Expected exited %v, got %v", e, a)
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestClientServer_step(t *testing.T) {
 | |
| 	withTestClient("testprog", t, func(c service.Client) {
 | |
| 		_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.helloworld"})
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 
 | |
| 		stateBefore, err := c.Continue()
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 
 | |
| 		stateAfter, err := c.Step()
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 
 | |
| 		if before, after := stateBefore.CurrentThread.PC, stateAfter.CurrentThread.PC; before >= after {
 | |
| 			t.Errorf("Expected %#v to be greater than %#v", before, after)
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| type nextTest struct {
 | |
| 	begin, end int
 | |
| }
 | |
| 
 | |
| func testnext(testcases []nextTest, initialLocation string, t *testing.T) {
 | |
| 	withTestClient("testnextprog", t, func(c service.Client) {
 | |
| 		bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: initialLocation})
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 
 | |
| 		state, err := c.Continue()
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 
 | |
| 		_, err = c.ClearBreakpoint(bp.ID)
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 
 | |
| 		for _, tc := range testcases {
 | |
| 			if state.CurrentThread.Line != tc.begin {
 | |
| 				t.Fatalf("Program not stopped at correct spot expected %d was %d", tc.begin, state.CurrentThread.Line)
 | |
| 			}
 | |
| 
 | |
| 			t.Logf("Next for scenario %#v", tc)
 | |
| 			state, err = c.Next()
 | |
| 			if err != nil {
 | |
| 				t.Fatalf("Unexpected error: %v", err)
 | |
| 			}
 | |
| 
 | |
| 			if state.CurrentThread.Line != tc.end {
 | |
| 				t.Fatalf("Program did not continue to correct next location expected %d was %d", tc.end, state.CurrentThread.Line)
 | |
| 			}
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestNextGeneral(t *testing.T) {
 | |
| 	testcases := []nextTest{
 | |
| 		{17, 19},
 | |
| 		{19, 20},
 | |
| 		{20, 23},
 | |
| 		{23, 24},
 | |
| 		{24, 26},
 | |
| 		{26, 31},
 | |
| 		{31, 23},
 | |
| 		{23, 24},
 | |
| 		{24, 26},
 | |
| 		{26, 31},
 | |
| 		{31, 23},
 | |
| 		{23, 24},
 | |
| 		{24, 26},
 | |
| 		{26, 27},
 | |
| 		{27, 34},
 | |
| 	}
 | |
| 	testnext(testcases, "main.testnext", t)
 | |
| }
 | |
| 
 | |
| func TestNextGoroutine(t *testing.T) {
 | |
| 	testcases := []nextTest{
 | |
| 		{46, 47},
 | |
| 		{47, 42},
 | |
| 	}
 | |
| 	testnext(testcases, "main.testgoroutine", t)
 | |
| }
 | |
| 
 | |
| func TestNextFunctionReturn(t *testing.T) {
 | |
| 	testcases := []nextTest{
 | |
| 		{13, 14},
 | |
| 		{14, 35},
 | |
| 	}
 | |
| 	testnext(testcases, "main.helloworld", t)
 | |
| }
 | |
| 
 | |
| func TestClientServer_breakpointInMainThread(t *testing.T) {
 | |
| 	withTestClient("testprog", t, func(c service.Client) {
 | |
| 		bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.helloworld"})
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 
 | |
| 		state, err := c.Continue()
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v, state: %#v", err, state)
 | |
| 		}
 | |
| 
 | |
| 		pc := state.CurrentThread.PC
 | |
| 
 | |
| 		if pc-1 != bp.Addr && pc != bp.Addr {
 | |
| 			f, l := state.CurrentThread.File, state.CurrentThread.Line
 | |
| 			t.Fatalf("Break not respected:\nPC:%#v %s:%d\nFN:%#v \n", pc, f, l, bp.Addr)
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestClientServer_breakpointInSeparateGoroutine(t *testing.T) {
 | |
| 	withTestClient("testthreads", t, func(c service.Client) {
 | |
| 		_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.anotherthread"})
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 
 | |
| 		state, err := c.Continue()
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v, state: %#v", err, state)
 | |
| 		}
 | |
| 
 | |
| 		f, l := state.CurrentThread.File, state.CurrentThread.Line
 | |
| 		if f != "testthreads.go" && l != 8 {
 | |
| 			t.Fatal("Program did not hit breakpoint")
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestClientServer_breakAtNonexistentPoint(t *testing.T) {
 | |
| 	withTestClient("testprog", t, func(c service.Client) {
 | |
| 		_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "nowhere"})
 | |
| 		if err == nil {
 | |
| 			t.Fatal("Should not be able to break at non existent function")
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestClientServer_clearBreakpoint(t *testing.T) {
 | |
| 	withTestClient("testprog", t, func(c service.Client) {
 | |
| 		bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sleepytime"})
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 
 | |
| 		bps, err := c.ListBreakpoints()
 | |
| 		if e, a := 1, len(bps); e != a {
 | |
| 			t.Fatalf("Expected breakpoint count %d, got %d", e, a)
 | |
| 		}
 | |
| 
 | |
| 		deleted, err := c.ClearBreakpoint(bp.ID)
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 
 | |
| 		if deleted.ID != bp.ID {
 | |
| 			t.Fatalf("Expected deleted breakpoint ID %v, got %v", bp.ID, deleted.ID)
 | |
| 		}
 | |
| 
 | |
| 		bps, err = c.ListBreakpoints()
 | |
| 		if e, a := 0, len(bps); e != a {
 | |
| 			t.Fatalf("Expected breakpoint count %d, got %d", e, a)
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestClientServer_switchThread(t *testing.T) {
 | |
| 	withTestClient("testnextprog", t, func(c service.Client) {
 | |
| 		// With invalid thread id
 | |
| 		_, err := c.SwitchThread(-1)
 | |
| 		if err == nil {
 | |
| 			t.Fatal("Expected error for invalid thread id")
 | |
| 		}
 | |
| 
 | |
| 		_, err = c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main"})
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 		state, err := c.Continue()
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v, state: %#v", err, state)
 | |
| 		}
 | |
| 
 | |
| 		var nt int
 | |
| 		ct := state.CurrentThread.ID
 | |
| 		threads, err := c.ListThreads()
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 		for _, th := range threads {
 | |
| 			if th.ID != ct {
 | |
| 				nt = th.ID
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 		if nt == 0 {
 | |
| 			t.Fatal("could not find thread to switch to")
 | |
| 		}
 | |
| 		// With valid thread id
 | |
| 		state, err = c.SwitchThread(nt)
 | |
| 		if err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		if state.CurrentThread.ID != nt {
 | |
| 			t.Fatal("Did not switch threads")
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestClientServer_infoLocals(t *testing.T) {
 | |
| 	withTestClient("testnextprog", t, func(c service.Client) {
 | |
| 		fp, err := filepath.Abs("_fixtures/testnextprog.go")
 | |
| 		if err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		if _, err := os.Stat(fp); err != nil {
 | |
| 			fp, err = filepath.Abs("../../_fixtures/testnextprog.go")
 | |
| 			if err != nil {
 | |
| 				t.Fatal(err)
 | |
| 			}
 | |
| 		}
 | |
| 		_, err = c.CreateBreakpoint(&api.Breakpoint{File: fp, Line: 23})
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 		state, err := c.Continue()
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v, state: %#v", err, state)
 | |
| 		}
 | |
| 		locals, err := c.ListLocalVariables()
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 		if len(locals) != 3 {
 | |
| 			t.Fatalf("Expected 3 locals, got %d %#v", len(locals), locals)
 | |
| 		}
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestClientServer_infoArgs(t *testing.T) {
 | |
| 	withTestClient("testnextprog", t, func(c service.Client) {
 | |
| 		fp, err := filepath.Abs("_fixtures/testnextprog.go")
 | |
| 		if err != nil {
 | |
| 			t.Fatal(err)
 | |
| 		}
 | |
| 		if _, err := os.Stat(fp); err != nil {
 | |
| 			fp, err = filepath.Abs("../../_fixtures/testnextprog.go")
 | |
| 			if err != nil {
 | |
| 				t.Fatal(err)
 | |
| 			}
 | |
| 		}
 | |
| 		_, err = c.CreateBreakpoint(&api.Breakpoint{File: fp, Line: 47})
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 		state, err := c.Continue()
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v, state: %#v", err, state)
 | |
| 		}
 | |
| 		regs, err := c.ListRegisters()
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 		if regs == "" {
 | |
| 			t.Fatal("Expected string showing registers values, got empty string")
 | |
| 		}
 | |
| 		locals, err := c.ListFunctionArgs()
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Unexpected error: %v", err)
 | |
| 		}
 | |
| 		if len(locals) != 2 {
 | |
| 			t.Fatalf("Expected 2 function args, got %d %#v", len(locals), locals)
 | |
| 		}
 | |
| 	})
 | |
| }
 | 
