command_test.go 27.1 KB
Newer Older
D
Dan Mace 已提交
1
package terminal
D
Derek Parker 已提交
2

D
Derek Parker 已提交
3
import (
4
	"flag"
D
Derek Parker 已提交
5
	"fmt"
6 7
	"io/ioutil"
	"net"
8
	"net/http"
9
	"os"
10
	"path/filepath"
A
aarzilli 已提交
11
	"regexp"
12
	"runtime"
13
	"strconv"
14
	"strings"
D
Derek Parker 已提交
15
	"testing"
16
	"time"
17

18 19
	"github.com/go-delve/delve/pkg/config"
	"github.com/go-delve/delve/pkg/goversion"
20
	"github.com/go-delve/delve/pkg/logflags"
21 22 23 24 25
	"github.com/go-delve/delve/pkg/proc/test"
	"github.com/go-delve/delve/service"
	"github.com/go-delve/delve/service/api"
	"github.com/go-delve/delve/service/rpc2"
	"github.com/go-delve/delve/service/rpccommon"
D
Derek Parker 已提交
26
)
D
Derek Parker 已提交
27

28
var testBackend, buildMode string
29 30 31

func TestMain(m *testing.M) {
	flag.StringVar(&testBackend, "backend", "", "selects backend")
32
	flag.StringVar(&buildMode, "test-buildmode", "", "selects build mode")
33 34
	var logConf string
	flag.StringVar(&logConf, "log", "", "configures logging")
35
	flag.Parse()
36
	test.DefaultTestBackend(&testBackend)
37 38 39 40
	if buildMode != "" && buildMode != "pie" {
		fmt.Fprintf(os.Stderr, "unknown build mode %q", buildMode)
		os.Exit(1)
	}
41
	logflags.Setup(logConf != "", logConf, "")
42
	os.Exit(test.RunTestsWithFixtures(m))
43 44
}

45
type FakeTerminal struct {
D
Derek Parker 已提交
46
	*Term
D
Derek Parker 已提交
47
	t testing.TB
48 49
}

50 51
const logCommandOutput = false

D
Derek Parker 已提交
52
func (ft *FakeTerminal) Exec(cmdstr string) (outstr string, err error) {
53 54
	outfh, err := ioutil.TempFile("", "cmdtestout")
	if err != nil {
D
Derek Parker 已提交
55
		ft.t.Fatalf("could not create temporary file: %v", err)
56 57
	}

A
aarzilli 已提交
58 59
	stdout, stderr, termstdout := os.Stdout, os.Stderr, ft.Term.stdout
	os.Stdout, os.Stderr, ft.Term.stdout = outfh, outfh, outfh
60
	defer func() {
A
aarzilli 已提交
61
		os.Stdout, os.Stderr, ft.Term.stdout = stdout, stderr, termstdout
62 63 64
		outfh.Close()
		outbs, err1 := ioutil.ReadFile(outfh.Name())
		if err1 != nil {
D
Derek Parker 已提交
65
			ft.t.Fatalf("could not read temporary output file: %v", err)
66 67
		}
		outstr = string(outbs)
68 69 70
		if logCommandOutput {
			ft.t.Logf("command %q -> %q", cmdstr, outstr)
		}
71 72
		os.Remove(outfh.Name())
	}()
73
	err = ft.cmds.Call(cmdstr, ft.Term)
74 75 76
	return
}

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
func (ft *FakeTerminal) ExecStarlark(starlarkProgram string) (outstr string, err error) {
	outfh, err := ioutil.TempFile("", "cmdtestout")
	if err != nil {
		ft.t.Fatalf("could not create temporary file: %v", err)
	}

	stdout, stderr, termstdout := os.Stdout, os.Stderr, ft.Term.stdout
	os.Stdout, os.Stderr, ft.Term.stdout = outfh, outfh, outfh
	defer func() {
		os.Stdout, os.Stderr, ft.Term.stdout = stdout, stderr, termstdout
		outfh.Close()
		outbs, err1 := ioutil.ReadFile(outfh.Name())
		if err1 != nil {
			ft.t.Fatalf("could not read temporary output file: %v", err)
		}
		outstr = string(outbs)
		if logCommandOutput {
			ft.t.Logf("command %q -> %q", starlarkProgram, outstr)
		}
		os.Remove(outfh.Name())
	}()
	_, err = ft.Term.starlarkEnv.Execute("<stdin>", starlarkProgram, "main", nil)
	return
}

D
Derek Parker 已提交
102 103
func (ft *FakeTerminal) MustExec(cmdstr string) string {
	outstr, err := ft.Exec(cmdstr)
104
	if err != nil {
105
		ft.t.Errorf("output of %q: %q", cmdstr, outstr)
D
Derek Parker 已提交
106
		ft.t.Fatalf("Error executing <%s>: %v", cmdstr, err)
107 108 109 110
	}
	return outstr
}

111 112 113 114 115 116 117 118 119
func (ft *FakeTerminal) MustExecStarlark(starlarkProgram string) string {
	outstr, err := ft.ExecStarlark(starlarkProgram)
	if err != nil {
		ft.t.Errorf("output of %q: %q", starlarkProgram, outstr)
		ft.t.Fatalf("Error executing <%s>: %v", starlarkProgram, err)
	}
	return outstr
}

120 121 122 123 124 125 126 127 128 129
func (ft *FakeTerminal) AssertExec(cmdstr, tgt string) {
	out := ft.MustExec(cmdstr)
	if out != tgt {
		ft.t.Fatalf("Error executing %q, expected %q got %q", cmdstr, tgt, out)
	}
}

func (ft *FakeTerminal) AssertExecError(cmdstr, tgterr string) {
	_, err := ft.Exec(cmdstr)
	if err == nil {
D
Derek Parker 已提交
130
		ft.t.Fatalf("Expected error executing %q", cmdstr)
131 132 133 134 135 136
	}
	if err.Error() != tgterr {
		ft.t.Fatalf("Expected error %q executing %q, got error %q", tgterr, cmdstr, err.Error())
	}
}

137
func withTestTerminal(name string, t testing.TB, fn func(*FakeTerminal)) {
138 139 140 141
	withTestTerminalBuildFlags(name, t, 0, fn)
}

func withTestTerminalBuildFlags(name string, t testing.TB, buildFlags test.BuildFlags, fn func(*FakeTerminal)) {
142 143 144
	if testBackend == "rr" {
		test.MustHaveRecordingAllowed(t)
	}
145
	os.Setenv("TERM", "dumb")
146
	listener, err := net.Listen("tcp", "127.0.0.1:0")
147 148 149 150
	if err != nil {
		t.Fatalf("couldn't start listener: %s\n", err)
	}
	defer listener.Close()
151 152 153
	if buildMode == "pie" {
		buildFlags |= test.BuildModePIE
	}
A
aarzilli 已提交
154
	server := rpccommon.NewServer(&service.Config{
155
		Listener:    listener,
156
		ProcessArgs: []string{test.BuildFixture(name, buildFlags).Path},
157
		Backend:     testBackend,
D
Derek Parker 已提交
158
	})
159 160 161
	if err := server.Run(); err != nil {
		t.Fatal(err)
	}
162
	client := rpc2.NewClient(listener.Addr().String())
163 164 165
	defer func() {
		client.Detach(true)
	}()
166

D
Derek Parker 已提交
167
	ft := &FakeTerminal{
D
Derek Parker 已提交
168
		t:    t,
A
aarzilli 已提交
169
		Term: New(client, &config.Config{}),
D
Derek Parker 已提交
170 171
	}
	fn(ft)
172 173
}

D
Derek Parker 已提交
174 175
func TestCommandDefault(t *testing.T) {
	var (
J
Jason Del Ponte 已提交
176
		cmds = Commands{}
177
		cmd  = cmds.Find("non-existant-command", noPrefix)
D
Derek Parker 已提交
178 179
	)

180
	err := cmd(nil, callContext{}, "")
D
Derek Parker 已提交
181 182 183 184 185 186 187 188
	if err == nil {
		t.Fatal("cmd() did not default")
	}

	if err.Error() != "command not available" {
		t.Fatal("wrong command output")
	}
}
D
Derek Parker 已提交
189

190
func TestCommandReplay(t *testing.T) {
D
Dan Mace 已提交
191
	cmds := DebugCommands(nil)
192 193
	cmds.Register("foo", func(t *Term, ctx callContext, args string) error { return fmt.Errorf("registered command") }, "foo command")
	cmd := cmds.Find("foo", noPrefix)
194

195
	err := cmd(nil, callContext{}, "")
196 197 198 199
	if err.Error() != "registered command" {
		t.Fatal("wrong command output")
	}

200 201
	cmd = cmds.Find("", noPrefix)
	err = cmd(nil, callContext{}, "")
202 203 204 205 206 207 208
	if err.Error() != "registered command" {
		t.Fatal("wrong command output")
	}
}

func TestCommandReplayWithoutPreviousCommand(t *testing.T) {
	var (
D
Dan Mace 已提交
209
		cmds = DebugCommands(nil)
210 211
		cmd  = cmds.Find("", noPrefix)
		err  = cmd(nil, callContext{}, "")
212 213 214 215 216 217
	)

	if err != nil {
		t.Error("Null command not returned", err)
	}
}
218 219 220 221

func TestCommandThread(t *testing.T) {
	var (
		cmds = DebugCommands(nil)
222
		cmd  = cmds.Find("thread", noPrefix)
223 224
	)

225
	err := cmd(nil, callContext{}, "")
226 227 228 229 230 231 232 233
	if err == nil {
		t.Fatal("thread terminal command did not default")
	}

	if err.Error() != "you must specify a thread" {
		t.Fatal("wrong command output: ", err.Error())
	}
}
234 235 236 237 238 239 240

func TestExecuteFile(t *testing.T) {
	breakCount := 0
	traceCount := 0
	c := &Commands{
		client: nil,
		cmds: []command{
241
			{aliases: []string{"trace"}, cmdFn: func(t *Term, ctx callContext, args string) error {
242 243 244
				traceCount++
				return nil
			}},
245
			{aliases: []string{"break"}, cmdFn: func(t *Term, ctx callContext, args string) error {
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
				breakCount++
				return nil
			}},
		},
	}

	fixturesDir := test.FindFixturesDir()
	err := c.executeFile(nil, filepath.Join(fixturesDir, "bpfile"))
	if err != nil {
		t.Fatalf("executeFile: %v", err)
	}

	if breakCount != 1 || traceCount != 1 {
		t.Fatalf("Wrong counts break: %d trace: %d\n", breakCount, traceCount)
	}
}
262 263

func TestIssue354(t *testing.T) {
264
	printStack([]api.Stackframe{}, "", false)
265
	printStack([]api.Stackframe{{api.Location{PC: 0, File: "irrelevant.go", Line: 10, Function: nil}, nil, nil, 0, 0, nil, true, ""}}, "", false)
266
}
267 268

func TestIssue411(t *testing.T) {
H
hengwu0 已提交
269 270 271
	if runtime.GOARCH == "arm64" {
		t.Skip("test is not valid on ARM64")
	}
272
	test.AllowRecording(t)
273 274 275 276 277 278 279 280 281 282
	withTestTerminal("math", t, func(term *FakeTerminal) {
		term.MustExec("break math.go:8")
		term.MustExec("trace math.go:9")
		term.MustExec("continue")
		out := term.MustExec("next")
		if !strings.HasPrefix(out, "> main.main()") {
			t.Fatalf("Wrong output for next: <%s>", out)
		}
	})
}
283 284

func TestScopePrefix(t *testing.T) {
H
hengwu0 已提交
285
	if runtime.GOARCH == "arm64" {
286
		t.Skip("arm64 does not support Stacktrace for now")
H
hengwu0 已提交
287
	}
288 289
	const goroutinesLinePrefix = "  Goroutine "
	const goroutinesCurLinePrefix = "* Goroutine "
290
	test.AllowRecording(t)
291 292 293 294 295 296
	withTestTerminal("goroutinestackprog", t, func(term *FakeTerminal) {
		term.MustExec("b stacktraceme")
		term.MustExec("continue")

		goroutinesOut := strings.Split(term.MustExec("goroutines"), "\n")
		agoroutines := []int{}
297
		nonagoroutines := []int{}
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
		curgid := -1

		for _, line := range goroutinesOut {
			iscur := strings.HasPrefix(line, goroutinesCurLinePrefix)
			if !iscur && !strings.HasPrefix(line, goroutinesLinePrefix) {
				continue
			}

			dash := strings.Index(line, " - ")
			if dash < 0 {
				continue
			}

			gid, err := strconv.Atoi(line[len(goroutinesLinePrefix):dash])
			if err != nil {
				continue
			}

			if iscur {
				curgid = gid
			}

			if idx := strings.Index(line, " main.agoroutine "); idx < 0 {
321
				nonagoroutines = append(nonagoroutines, gid)
322 323 324 325 326 327
				continue
			}

			agoroutines = append(agoroutines, gid)
		}

328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
		if len(agoroutines) > 10 {
			t.Fatalf("Output of goroutines did not have 10 goroutines stopped on main.agoroutine (%d found): %q", len(agoroutines), goroutinesOut)
		}

		if len(agoroutines) < 10 {
			extraAgoroutines := 0
			for _, gid := range nonagoroutines {
				stackOut := strings.Split(term.MustExec(fmt.Sprintf("goroutine %d stack", gid)), "\n")
				for _, line := range stackOut {
					if strings.HasSuffix(line, " main.agoroutine") {
						extraAgoroutines++
						break
					}
				}
			}
			if len(agoroutines)+extraAgoroutines < 10 {
				t.Fatalf("Output of goroutines did not have 10 goroutines stopped on main.agoroutine (%d+%d found): %q", len(agoroutines), extraAgoroutines, goroutinesOut)
			}
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
		}

		if curgid < 0 {
			t.Fatalf("Could not find current goroutine in output of goroutines: %q", goroutinesOut)
		}

		seen := make([]bool, 10)
		for _, gid := range agoroutines {
			stackOut := strings.Split(term.MustExec(fmt.Sprintf("goroutine %d stack", gid)), "\n")
			fid := -1
			for _, line := range stackOut {
				space := strings.Index(line, " ")
				if space < 0 {
					continue
				}
				curfid, err := strconv.Atoi(line[:space])
				if err != nil {
					continue
				}

				if idx := strings.Index(line, " main.agoroutine"); idx >= 0 {
					fid = curfid
					break
				}
			}
			if fid < 0 {
				t.Fatalf("Could not find frame for goroutine %d: %v", gid, stackOut)
			}
374
			term.AssertExec(fmt.Sprintf("goroutine     %d    frame     %d     locals", gid, fid), "(no locals)\n")
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
			argsOut := strings.Split(term.MustExec(fmt.Sprintf("goroutine %d frame %d args", gid, fid)), "\n")
			if len(argsOut) != 4 || argsOut[3] != "" {
				t.Fatalf("Wrong number of arguments in goroutine %d frame %d: %v", gid, fid, argsOut)
			}
			out := term.MustExec(fmt.Sprintf("goroutine %d frame %d p i", gid, fid))
			ival, err := strconv.Atoi(out[:len(out)-1])
			if err != nil {
				t.Fatalf("could not parse value %q of i for goroutine %d frame %d: %v", out, gid, fid, err)
			}
			seen[ival] = true
		}

		for i := range seen {
			if !seen[i] {
				t.Fatalf("goroutine %d not found", i)
			}
		}

		term.MustExec("c")

		term.AssertExecError("frame", "not enough arguments")
		term.AssertExecError(fmt.Sprintf("goroutine %d frame 10 locals", curgid), fmt.Sprintf("Frame 10 does not exist in goroutine %d", curgid))
		term.AssertExecError("goroutine 9000 locals", "Unknown goroutine 9000")

		term.AssertExecError("print n", "could not find symbol value for n")
		term.AssertExec("frame 1 print n", "3\n")
		term.AssertExec("frame 2 print n", "2\n")
		term.AssertExec("frame 3 print n", "1\n")
		term.AssertExec("frame 4 print n", "0\n")
		term.AssertExecError("frame 5 print n", "could not find symbol value for n")
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424

		term.MustExec("frame 2")
		term.AssertExec("print n", "2\n")
		term.MustExec("frame 4")
		term.AssertExec("print n", "0\n")
		term.MustExec("down")
		term.AssertExec("print n", "1\n")
		term.MustExec("down 2")
		term.AssertExec("print n", "3\n")
		term.AssertExecError("down 2", "Invalid frame -1")
		term.AssertExec("print n", "3\n")
		term.MustExec("up 2")
		term.AssertExec("print n", "1\n")
		term.AssertExecError("up 100", "Invalid frame 103")
		term.AssertExec("print n", "1\n")

		term.MustExec("step")
		term.AssertExecError("print n", "could not find symbol value for n")
		term.MustExec("frame 2")
		term.AssertExec("print n", "2\n")
425 426 427 428
	})
}

func TestOnPrefix(t *testing.T) {
H
hengwu0 已提交
429 430 431
	if runtime.GOARCH == "arm64" {
		t.Skip("test is not valid on ARM64")
	}
432 433 434
	if runtime.GOOS == "freebsd" {
		t.Skip("test is not valid on FreeBSD")
	}
435
	const prefix = "\ti: "
436
	test.AllowRecording(t)
437 438 439 440 441 442 443 444 445
	withTestTerminal("goroutinestackprog", t, func(term *FakeTerminal) {
		term.MustExec("b agobp main.agoroutine")
		term.MustExec("on agobp print i")

		seen := make([]bool, 10)

		for {
			outstr, err := term.Exec("continue")
			if err != nil {
446
				if !strings.Contains(err.Error(), "exited") {
447 448 449 450 451 452 453
					t.Fatalf("Unexpected error executing 'continue': %v", err)
				}
				break
			}
			out := strings.Split(outstr, "\n")

			for i := range out {
454
				if !strings.HasPrefix(out[i], prefix) {
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
					continue
				}
				id, err := strconv.Atoi(out[i][len(prefix):])
				if err != nil {
					continue
				}
				if seen[id] {
					t.Fatalf("Goroutine %d seen twice\n", id)
				}
				seen[id] = true
			}
		}

		for i := range seen {
			if !seen[i] {
				t.Fatalf("Goroutine %d not seen\n", i)
			}
		}
	})
}
475 476

func TestNoVars(t *testing.T) {
477
	test.AllowRecording(t)
478 479 480 481 482 483 484 485
	withTestTerminal("locationsUpperCase", t, func(term *FakeTerminal) {
		term.MustExec("b main.main")
		term.MustExec("continue")
		term.AssertExec("args", "(no args)\n")
		term.AssertExec("locals", "(no locals)\n")
		term.AssertExec("vars filterThatMatchesNothing", "(no vars)\n")
	})
}
486 487

func TestOnPrefixLocals(t *testing.T) {
H
hengwu0 已提交
488 489 490
	if runtime.GOARCH == "arm64" {
		t.Skip("test is not valid on ARM64")
	}
491 492 493
	if runtime.GOOS == "freebsd" {
		t.Skip("test is not valid on FreeBSD")
	}
494
	const prefix = "\ti: "
495
	test.AllowRecording(t)
496 497 498 499 500 501 502 503 504
	withTestTerminal("goroutinestackprog", t, func(term *FakeTerminal) {
		term.MustExec("b agobp main.agoroutine")
		term.MustExec("on agobp args -v")

		seen := make([]bool, 10)

		for {
			outstr, err := term.Exec("continue")
			if err != nil {
505
				if !strings.Contains(err.Error(), "exited") {
506 507 508 509 510 511 512
					t.Fatalf("Unexpected error executing 'continue': %v", err)
				}
				break
			}
			out := strings.Split(outstr, "\n")

			for i := range out {
513
				if !strings.HasPrefix(out[i], prefix) {
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
					continue
				}
				id, err := strconv.Atoi(out[i][len(prefix):])
				if err != nil {
					continue
				}
				if seen[id] {
					t.Fatalf("Goroutine %d seen twice\n", id)
				}
				seen[id] = true
			}
		}

		for i := range seen {
			if !seen[i] {
				t.Fatalf("Goroutine %d not seen\n", i)
			}
		}
	})
}
534

J
Josh Soref 已提交
535
func countOccurrences(s string, needle string) int {
536 537 538 539 540 541 542 543 544 545 546 547 548
	count := 0
	for {
		idx := strings.Index(s, needle)
		if idx < 0 {
			break
		}
		count++
		s = s[idx+len(needle):]
	}
	return count
}

func TestIssue387(t *testing.T) {
H
hengwu0 已提交
549 550 551
	if runtime.GOARCH == "arm64" {
		t.Skip("test is not valid on ARM64")
	}
552 553 554
	if runtime.GOOS == "freebsd" {
		t.Skip("test is not valid on FreeBSD")
	}
555
	// a breakpoint triggering during a 'next' operation will interrupt it
556
	test.AllowRecording(t)
557 558 559 560 561
	withTestTerminal("issue387", t, func(term *FakeTerminal) {
		breakpointHitCount := 0
		term.MustExec("break dostuff")
		for {
			outstr, err := term.Exec("continue")
J
Josh Soref 已提交
562
			breakpointHitCount += countOccurrences(outstr, "issue387.go:8")
563 564
			t.Log(outstr)
			if err != nil {
565
				if !strings.Contains(err.Error(), "exited") {
566 567 568 569 570 571 572 573 574
					t.Fatalf("Unexpected error executing 'continue': %v", err)
				}
				break
			}

			pos := 9

			for {
				outstr = term.MustExec("next")
J
Josh Soref 已提交
575
				breakpointHitCount += countOccurrences(outstr, "issue387.go:8")
576
				t.Log(outstr)
J
Josh Soref 已提交
577
				if countOccurrences(outstr, fmt.Sprintf("issue387.go:%d", pos)) == 0 {
578 579 580
					t.Fatalf("did not continue to expected position %d", pos)
				}
				pos++
581
				if pos >= 11 {
582 583 584 585 586 587 588 589 590
					break
				}
			}
		}
		if breakpointHitCount != 10 {
			t.Fatalf("Breakpoint hit wrong number of times, expected 10 got %d", breakpointHitCount)
		}
	})
}
A
aarzilli 已提交
591 592 593 594 595 596 597

func listIsAt(t *testing.T, term *FakeTerminal, listcmd string, cur, start, end int) {
	outstr := term.MustExec(listcmd)
	lines := strings.Split(outstr, "\n")

	t.Logf("%q: %q", listcmd, outstr)

598
	if cur >= 0 && !strings.Contains(lines[0], fmt.Sprintf(":%d", cur)) {
A
aarzilli 已提交
599 600 601 602 603 604 605
		t.Fatalf("Could not find current line number in first output line: %q", lines[0])
	}

	re := regexp.MustCompile(`(=>)?\s+(\d+):`)

	outStart, outEnd := 0, 0

606
	for _, line := range lines[1:] {
A
aarzilli 已提交
607 608 609 610 611
		if line == "" {
			continue
		}
		v := re.FindStringSubmatch(line)
		if len(v) != 3 {
612
			continue
A
aarzilli 已提交
613 614 615 616 617 618 619 620 621 622 623 624 625
		}
		curline, _ := strconv.Atoi(v[2])
		if v[1] == "=>" {
			if cur != curline {
				t.Fatalf("Wrong current line, got %d expected %d", curline, cur)
			}
		}
		if outStart == 0 {
			outStart = curline
		}
		outEnd = curline
	}

626 627 628 629
	if start != -1 || end != -1 {
		if outStart != start || outEnd != end {
			t.Fatalf("Wrong output range, got %d:%d expected %d:%d", outStart, outEnd, start, end)
		}
A
aarzilli 已提交
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
	}
}

func TestListCmd(t *testing.T) {
	withTestTerminal("testvariables", t, func(term *FakeTerminal) {
		term.MustExec("continue")
		term.MustExec("continue")
		listIsAt(t, term, "list", 24, 19, 29)
		listIsAt(t, term, "list 69", 69, 64, 70)
		listIsAt(t, term, "frame 1 list", 62, 57, 67)
		listIsAt(t, term, "frame 1 list 69", 69, 64, 70)
		_, err := term.Exec("frame 50 list")
		if err == nil {
			t.Fatalf("Expected error requesting 50th frame")
		}
645 646
		listIsAt(t, term, "list testvariables.go:1", -1, 1, 6)
		listIsAt(t, term, "list testvariables.go:10000", -1, 0, 0)
A
aarzilli 已提交
647 648
	})
}
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678

func TestReverseContinue(t *testing.T) {
	test.AllowRecording(t)
	if testBackend != "rr" {
		return
	}
	withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
		term.MustExec("break main.main")
		term.MustExec("break main.sayhi")
		listIsAt(t, term, "continue", 16, -1, -1)
		listIsAt(t, term, "continue", 12, -1, -1)
		listIsAt(t, term, "rewind", 16, -1, -1)
	})
}

func TestCheckpoints(t *testing.T) {
	test.AllowRecording(t)
	if testBackend != "rr" {
		return
	}
	withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
		term.MustExec("break main.main")
		listIsAt(t, term, "continue", 16, -1, -1)
		term.MustExec("checkpoint")
		term.MustExec("checkpoints")
		listIsAt(t, term, "next", 17, -1, -1)
		listIsAt(t, term, "next", 18, -1, -1)
		listIsAt(t, term, "restart c1", 16, -1, -1)
	})
}
679

680 681 682 683 684 685 686 687 688
func TestNextWithCount(t *testing.T) {
	test.AllowRecording(t)
	withTestTerminal("nextcond", t, func(term *FakeTerminal) {
		term.MustExec("break main.main")
		listIsAt(t, term, "continue", 8, -1, -1)
		listIsAt(t, term, "next 2", 10, -1, -1)
	})
}

689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
func TestRestart(t *testing.T) {
	withTestTerminal("restartargs", t, func(term *FakeTerminal) {
		term.MustExec("break main.printArgs")
		term.MustExec("continue")
		if out := term.MustExec("print main.args"); !strings.Contains(out, ", []") {
			t.Fatalf("wrong args: %q", out)
		}
		// Reset the arg list
		term.MustExec("restart hello")
		term.MustExec("continue")
		if out := term.MustExec("print main.args"); !strings.Contains(out, ", [\"hello\"]") {
			t.Fatalf("wrong args: %q ", out)
		}
		// Restart w/o arg should retain the current args.
		term.MustExec("restart")
		term.MustExec("continue")
		if out := term.MustExec("print main.args"); !strings.Contains(out, ", [\"hello\"]") {
			t.Fatalf("wrong args: %q ", out)
		}
		// Empty arg list
		term.MustExec("restart -noargs")
		term.MustExec("continue")
		if out := term.MustExec("print main.args"); !strings.Contains(out, ", []") {
			t.Fatalf("wrong args: %q ", out)
		}
	})
}

717 718 719 720 721 722 723 724 725 726 727 728 729 730
func TestIssue827(t *testing.T) {
	// switching goroutines when the current thread isn't running any goroutine
	// causes nil pointer dereference.
	withTestTerminal("notify-v2", t, func(term *FakeTerminal) {
		go func() {
			time.Sleep(1 * time.Second)
			http.Get("http://127.0.0.1:8888/test")
			time.Sleep(1 * time.Second)
			term.client.Halt()
		}()
		term.MustExec("continue")
		term.MustExec("goroutine 1")
	})
}
A
aarzilli 已提交
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763

func findCmdName(c *Commands, cmdstr string, prefix cmdPrefix) string {
	for _, v := range c.cmds {
		if v.match(cmdstr) {
			if prefix != noPrefix && v.allowedPrefixes&prefix == 0 {
				continue
			}
			return v.aliases[0]
		}
	}
	return ""
}

func TestConfig(t *testing.T) {
	var term Term
	term.conf = &config.Config{}
	term.cmds = DebugCommands(nil)

	err := configureCmd(&term, callContext{}, "nonexistent-parameter 10")
	if err == nil {
		t.Fatalf("expected error executing configureCmd(nonexistent-parameter)")
	}

	err = configureCmd(&term, callContext{}, "max-string-len 10")
	if err != nil {
		t.Fatalf("error executing configureCmd(max-string-len): %v", err)
	}
	if term.conf.MaxStringLen == nil {
		t.Fatalf("expected MaxStringLen 10, got nil")
	}
	if *term.conf.MaxStringLen != 10 {
		t.Fatalf("expected MaxStringLen 10, got: %d", *term.conf.MaxStringLen)
	}
764 765 766 767 768 769 770 771 772 773
	err = configureCmd(&term, callContext{}, "max-variable-recurse 4")
	if err != nil {
		t.Fatalf("error executing configureCmd(max-variable-recurse): %v", err)
	}
	if term.conf.MaxVariableRecurse == nil {
		t.Fatalf("expected MaxVariableRecurse 4, got nil")
	}
	if *term.conf.MaxVariableRecurse != 4 {
		t.Fatalf("expected MaxVariableRecurse 4, got: %d", *term.conf.MaxVariableRecurse)
	}
A
aarzilli 已提交
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812

	err = configureCmd(&term, callContext{}, "substitute-path a b")
	if err != nil {
		t.Fatalf("error executing configureCmd(substitute-path a b): %v", err)
	}
	if len(term.conf.SubstitutePath) != 1 || (term.conf.SubstitutePath[0] != config.SubstitutePathRule{"a", "b"}) {
		t.Fatalf("unexpected SubstitutePathRules after insert %v", term.conf.SubstitutePath)
	}

	err = configureCmd(&term, callContext{}, "substitute-path a")
	if err != nil {
		t.Fatalf("error executing configureCmd(substitute-path a): %v", err)
	}
	if len(term.conf.SubstitutePath) != 0 {
		t.Fatalf("unexpected SubstitutePathRules after delete %v", term.conf.SubstitutePath)
	}

	err = configureCmd(&term, callContext{}, "alias print blah")
	if err != nil {
		t.Fatalf("error executing configureCmd(alias print blah): %v", err)
	}
	if len(term.conf.Aliases["print"]) != 1 {
		t.Fatalf("aliases not changed after configure command %v", term.conf.Aliases)
	}
	if findCmdName(term.cmds, "blah", noPrefix) != "print" {
		t.Fatalf("new alias not found")
	}

	err = configureCmd(&term, callContext{}, "alias blah")
	if err != nil {
		t.Fatalf("error executing configureCmd(alias blah): %v", err)
	}
	if len(term.conf.Aliases["print"]) != 0 {
		t.Fatalf("alias not removed after configure command %v", term.conf.Aliases)
	}
	if findCmdName(term.cmds, "blah", noPrefix) != "" {
		t.Fatalf("new alias found after delete")
	}
}
813 814 815

func TestDisassembleAutogenerated(t *testing.T) {
	// Executing the 'disassemble' command on autogenerated code should work correctly
A
Alessandro Arzilli 已提交
816 817 818 819 820 821 822

	if goversion.VersionAfterOrEqual(runtime.Version(), 1, 13) {
		// CL 161337 in Go 1.13 and later removes the autogenerated init function
		// https://go-review.googlesource.com/c/go/+/161337
		t.Skip("no autogenerated init function in Go 1.13 or later")
	}

823 824 825 826 827 828 829 830 831
	withTestTerminal("math", t, func(term *FakeTerminal) {
		term.MustExec("break main.init")
		term.MustExec("continue")
		out := term.MustExec("disassemble")
		if !strings.Contains(out, "TEXT main.init(SB) ") {
			t.Fatalf("output of disassemble wasn't for the main.init function %q", out)
		}
	})
}
832 833 834 835 836 837 838 839 840 841 842 843 844 845 846

func TestIssue1090(t *testing.T) {
	// Exit while executing 'next' should report the "Process exited" error
	// message instead of crashing.
	withTestTerminal("math", t, func(term *FakeTerminal) {
		term.MustExec("break main.main")
		term.MustExec("continue")
		for {
			_, err := term.Exec("next")
			if err != nil && strings.Contains(err.Error(), " has exited with status ") {
				break
			}
		}
	})
}
847 848

func TestPrintContextParkedGoroutine(t *testing.T) {
H
hengwu0 已提交
849
	if runtime.GOARCH == "arm64" {
850
		t.Skip("arm64 does not support Stacktrace for now")
H
hengwu0 已提交
851
	}
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
	withTestTerminal("goroutinestackprog", t, func(term *FakeTerminal) {
		term.MustExec("break stacktraceme")
		term.MustExec("continue")

		// pick a goroutine that isn't running on a thread
		gid := ""
		gout := strings.Split(term.MustExec("goroutines"), "\n")
		t.Logf("goroutines -> %q", gout)
		for _, gline := range gout {
			if !strings.Contains(gline, "thread ") && strings.Contains(gline, "agoroutine") {
				if dash := strings.Index(gline, " - "); dash > 0 {
					gid = gline[len("  Goroutine "):dash]
					break
				}
			}
		}

		t.Logf("picked %q", gid)
		term.MustExec(fmt.Sprintf("goroutine %s", gid))

		frameout := strings.Split(term.MustExec("frame 0"), "\n")
		t.Logf("frame 0 -> %q", frameout)
		if strings.Contains(frameout[0], "stacktraceme") {
			t.Fatal("bad output for `frame 0` command on a parked goorutine")
		}

		listout := strings.Split(term.MustExec("list"), "\n")
		t.Logf("list -> %q", listout)
		if strings.Contains(listout[0], "stacktraceme") {
			t.Fatal("bad output for list command on a parked goroutine")
		}
	})
}
885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900

func TestStepOutReturn(t *testing.T) {
	ver, _ := goversion.Parse(runtime.Version())
	if ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 10, -1, 0, 0, ""}) {
		t.Skip("return variables aren't marked on 1.9 or earlier")
	}
	withTestTerminal("stepoutret", t, func(term *FakeTerminal) {
		term.MustExec("break main.stepout")
		term.MustExec("continue")
		out := term.MustExec("stepout")
		t.Logf("output: %q", out)
		if !strings.Contains(out, "num: ") || !strings.Contains(out, "str: ") {
			t.Fatal("could not find parameter")
		}
	})
}
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922

func TestOptimizationCheck(t *testing.T) {
	withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
		term.MustExec("break main.main")
		out := term.MustExec("continue")
		t.Logf("output %q", out)
		if strings.Contains(out, optimizedFunctionWarning) {
			t.Fatal("optimized function warning")
		}
	})

	if goversion.VersionAfterOrEqual(runtime.Version(), 1, 10) {
		withTestTerminalBuildFlags("continuetestprog", t, test.EnableOptimization|test.EnableInlining, func(term *FakeTerminal) {
			term.MustExec("break main.main")
			out := term.MustExec("continue")
			t.Logf("output %q", out)
			if !strings.Contains(out, optimizedFunctionWarning) {
				t.Fatal("optimized function warning missing")
			}
		})
	}
}
923 924

func TestTruncateStacktrace(t *testing.T) {
H
hengwu0 已提交
925
	if runtime.GOARCH == "arm64" {
926
		t.Skip("arm64 does not support Stacktrace for now")
H
hengwu0 已提交
927
	}
928 929 930 931 932 933 934 935 936 937 938 939 940 941 942
	withTestTerminal("stacktraceprog", t, func(term *FakeTerminal) {
		term.MustExec("break main.stacktraceme")
		term.MustExec("continue")
		out1 := term.MustExec("stack")
		t.Logf("untruncated output %q", out1)
		if strings.Contains(out1, stacktraceTruncatedMessage) {
			t.Fatalf("stacktrace was truncated")
		}
		out2 := term.MustExec("stack 1")
		t.Logf("truncated output %q", out2)
		if !strings.Contains(out2, stacktraceTruncatedMessage) {
			t.Fatalf("stacktrace was not truncated")
		}
	})
}
943 944

func TestIssue1493(t *testing.T) {
H
hengwu0 已提交
945
	if runtime.GOARCH == "arm64" {
946
		t.Skip("arm64 does not support FpRegs for now")
H
hengwu0 已提交
947
	}
948 949 950 951 952 953 954 955 956 957 958 959 960 961
	// The 'regs' command without the '-a' option should only return
	// general purpose registers.
	withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
		r := term.MustExec("regs")
		nr := len(strings.Split(r, "\n"))
		t.Logf("regs: %s", r)
		ra := term.MustExec("regs -a")
		nra := len(strings.Split(ra, "\n"))
		t.Logf("regs -a: %s", ra)
		if nr > nra/2 {
			t.Fatalf("'regs' returned too many registers (%d) compared to 'regs -a' (%d)", nr, nra)
		}
	})
}
962 963 964 965

func findStarFile(name string) string {
	return filepath.Join(test.FindFixturesDir(), name+".star")
}
966 967

func TestIssue1598(t *testing.T) {
H
hengwu0 已提交
968
	if runtime.GOARCH == "arm64" {
969
		t.Skip("arm64 does not support FunctionCall for now")
H
hengwu0 已提交
970
	}
971 972 973 974 975 976 977 978 979 980 981 982
	test.MustSupportFunctionCalls(t, testBackend)
	withTestTerminal("issue1598", t, func(term *FakeTerminal) {
		term.MustExec("break issue1598.go:5")
		term.MustExec("continue")
		term.MustExec("config max-string-len 500")
		r := term.MustExec("call x()")
		t.Logf("result %q", r)
		if !strings.Contains(r, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut \\nlabore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut") {
			t.Fatalf("wrong value returned")
		}
	})
}