delve/pkg/proc/stepping_test.go
Alessandro Arzilli def0688e7a
proc: step into coroutine (#3791)
The step command is changed such that when the function being currently
called is a coroutine switch function it will move to the associated
coroutine.
Functions that switch coroutines are currently the next, stop and yield
closures produced by the iter.Pull function.
2024-09-24 10:22:04 -07:00

1789 lines
46 KiB
Go

package proc_test
import (
"fmt"
"go/constant"
"path/filepath"
"runtime"
"strconv"
"strings"
"testing"
"github.com/go-delve/delve/pkg/goversion"
"github.com/go-delve/delve/pkg/proc"
protest "github.com/go-delve/delve/pkg/proc/test"
"github.com/go-delve/delve/service/api"
)
type nextTest struct {
begin, end int
}
type contFunc int
const (
contContinue contFunc = iota
contNext
contStep
contStepout
contReverseNext
contReverseStep
contReverseStepout
contContinueToBreakpoint
contNothing
)
type seqTest struct {
cf contFunc
pos interface{}
}
func testseq(program string, contFunc contFunc, testcases []nextTest, initialLocation string, t *testing.T) {
seqTestcases := make([]seqTest, len(testcases)+1)
seqTestcases[0] = seqTest{contContinue, testcases[0].begin}
for i := range testcases {
if i > 0 {
if testcases[i-1].end != testcases[i].begin {
panic(fmt.Errorf("begin/end mismatch at index %d", i))
}
}
seqTestcases[i+1] = seqTest{contFunc, testcases[i].end}
}
testseq2(t, program, initialLocation, seqTestcases)
}
const traceTestseq2 = true
func testseq2(t *testing.T, program string, initialLocation string, testcases []seqTest) {
testseq2Args(".", []string{}, 0, t, program, initialLocation, testcases)
}
func testseq2Args(wd string, args []string, buildFlags protest.BuildFlags, t *testing.T, program string, initialLocation string, testcases []seqTest) {
protest.AllowRecording(t)
t.Helper()
withTestProcessArgs(program, t, wd, args, buildFlags, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
checkBreakpointClear := true
var bp *proc.Breakpoint
if initialLocation != "" {
bp = setFunctionBreakpoint(p, t, initialLocation)
} else if testcases[0].cf == contContinue {
bp = setFileBreakpoint(p, t, fixture.Source, testcases[0].pos.(int))
} else if testcases[0].cf == contNothing {
// Do nothing
checkBreakpointClear = false
} else {
panic("testseq2 can not set initial breakpoint")
}
if traceTestseq2 {
t.Logf("initial breakpoint %v", bp)
}
testseq2intl(t, fixture, grp, p, bp, testcases)
if countBreakpoints(p) != 0 && checkBreakpointClear {
t.Fatal("Not all breakpoints were cleaned up", len(p.Breakpoints().M))
}
})
}
func testseq2intl(t *testing.T, fixture protest.Fixture, grp *proc.TargetGroup, p *proc.Target, bp *proc.Breakpoint, testcases []seqTest) {
f, ln := currentLineNumber(p, t)
for i, tc := range testcases {
switch tc.cf {
case contNext:
if traceTestseq2 {
t.Log("next")
}
assertNoError(grp.Next(), t, "Next() returned an error")
case contStep:
if traceTestseq2 {
t.Log("step")
}
assertNoError(grp.Step(), t, "Step() returned an error")
case contStepout:
if traceTestseq2 {
t.Log("stepout")
}
assertNoError(grp.StepOut(), t, "StepOut() returned an error")
case contContinue:
if traceTestseq2 {
t.Log("continue")
}
assertNoError(grp.Continue(), t, "Continue() returned an error")
if i == 0 {
if traceTestseq2 {
t.Log("clearing initial breakpoint")
}
err := p.ClearBreakpoint(bp.Addr)
assertNoError(err, t, "ClearBreakpoint() returned an error")
}
case contReverseNext:
if traceTestseq2 {
t.Log("reverse-next")
}
assertNoError(grp.ChangeDirection(proc.Backward), t, "direction switch")
assertNoError(grp.Next(), t, "reverse Next() returned an error")
assertNoError(grp.ChangeDirection(proc.Forward), t, "direction switch")
case contReverseStep:
if traceTestseq2 {
t.Log("reverse-step")
}
assertNoError(grp.ChangeDirection(proc.Backward), t, "direction switch")
assertNoError(grp.Step(), t, "reverse Step() returned an error")
assertNoError(grp.ChangeDirection(proc.Forward), t, "direction switch")
case contReverseStepout:
if traceTestseq2 {
t.Log("reverse-stepout")
}
assertNoError(grp.ChangeDirection(proc.Backward), t, "direction switch")
assertNoError(grp.StepOut(), t, "reverse StepOut() returned an error")
assertNoError(grp.ChangeDirection(proc.Forward), t, "direction switch")
case contContinueToBreakpoint:
bp := setFileBreakpoint(p, t, fixture.Source, tc.pos.(int))
if traceTestseq2 {
t.Log("continue")
}
assertNoError(grp.Continue(), t, "Continue() returned an error")
err := p.ClearBreakpoint(bp.Addr)
assertNoError(err, t, "ClearBreakpoint() returned an error")
case contNothing:
// do nothing
}
if err := p.CurrentThread().Breakpoint().CondError; err != nil {
t.Logf("breakpoint condition error: %v", err)
}
f, ln = currentLineNumber(p, t)
regs, _ := p.CurrentThread().Registers()
pc := regs.PC()
_, _, fn := p.BinInfo().PCToLine(pc)
if traceTestseq2 {
fnname := "?"
if fn != nil {
fnname = fn.Name
}
t.Logf("at %#x (%s) %s:%d", pc, fnname, f, ln)
}
switch pos := tc.pos.(type) {
case int:
if pos >= 0 && ln != pos {
t.Fatalf("Program did not continue to correct next location expected %d was %s:%d (%#x) (testcase %d)", pos, filepath.Base(f), ln, pc, i)
}
case string:
v := strings.Split(pos, ":")
tgtln, _ := strconv.Atoi(v[1])
if !strings.HasSuffix(f, v[0]) || (ln != tgtln) {
t.Fatalf("Program did not continue to correct next location, expected %s was %s:%d (%#x) (testcase %d)", pos, filepath.Base(f), ln, pc, i)
}
case func(*proc.Target):
pos(p)
case func(*proc.TargetGroup, *proc.Target):
pos(grp, p)
default:
panic(fmt.Errorf("unexpected type %T", pos))
}
}
}
func TestNextGeneral(t *testing.T) {
var testcases []nextTest
ver, _ := goversion.Parse(runtime.Version())
if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 7, Rev: -1}) {
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, 28},
{28, 34},
}
} else {
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},
}
}
testseq("testnextprog", contNext, testcases, "main.testnext", t)
}
func TestNextFunctionReturn(t *testing.T) {
testcases := []nextTest{
{13, 14},
{14, 15},
{15, 35},
}
protest.AllowRecording(t)
testseq("testnextprog", contNext, testcases, "main.helloworld", t)
}
func TestNextFunctionReturnDefer(t *testing.T) {
var testcases []nextTest
ver, _ := goversion.Parse(runtime.Version())
if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 9, Rev: -1}) {
testcases = []nextTest{
{5, 6},
{6, 9},
{9, 10},
}
} else {
testcases = []nextTest{
{5, 8},
{8, 9},
{9, 10},
}
}
protest.AllowRecording(t)
testseq("testnextdefer", contNext, testcases, "main.main", t)
}
func TestNextDeferReturnAndDirectCall(t *testing.T) {
// Next should not step into a deferred function if it is called
// directly, only if it is called through a panic or a deferreturn.
// Here we test the case where the function is called by a deferreturn
testseq("defercall", contNext, []nextTest{
{9, 10},
{10, 11},
{11, 12},
{12, 13},
{13, 28}}, "main.callAndDeferReturn", t)
}
func TestNextPanicAndDirectCall(t *testing.T) {
// Next should not step into a deferred function if it is called
// directly, only if it is called through a panic or a deferreturn.
// Here we test the case where the function is called by a panic
testseq("defercall", contNext, []nextTest{
{15, 16},
{16, 17},
{17, 18},
{18, 6}}, "main.callAndPanic2", t)
}
func TestStepCall(t *testing.T) {
testseq("testnextprog", contStep, []nextTest{
{34, 13},
{13, 14}}, "", t)
}
func TestStepCallPtr(t *testing.T) {
// Tests that Step works correctly when calling functions with a
// function pointer.
if goversion.VersionAfterOrEqual(runtime.Version(), 1, 11) && !protest.RegabiSupported() {
testseq("teststepprog", contStep, []nextTest{
{9, 10},
{10, 6},
{6, 7},
{7, 11}}, "", t)
} else {
if runtime.GOOS == "linux" && runtime.GOARCH == "ppc64le" && buildMode == "pie" {
testseq("teststepprog", contStep, []nextTest{
{9, 10},
{10, 5},
{5, 6},
{6, 7},
{7, 10},
{10, 11}}, "", t)
} else {
testseq("teststepprog", contStep, []nextTest{
{9, 10},
{10, 5},
{5, 6},
{6, 7},
{7, 11}}, "", t)
}
}
}
func TestStepReturnAndPanic(t *testing.T) {
// Tests that Step works correctly when returning from functions
// and when a deferred function is called when panic'ing.
testseq("defercall", contStep, []nextTest{
{17, 6},
{6, 7},
{7, 18},
{18, 6},
{6, 7}}, "", t)
}
func TestStepDeferReturn(t *testing.T) {
// Tests that Step works correctly when a deferred function is
// called during a return.
testseq("defercall", contStep, []nextTest{
{11, 6},
{6, 7},
{7, 12},
{12, 13},
{13, 6},
{6, 7},
{7, 13},
{13, 28}}, "", t)
}
func TestStepIgnorePrivateRuntime(t *testing.T) {
// Tests that Step will ignore calls to private runtime functions
// (such as runtime.convT2E in this case)
switch {
case goversion.VersionAfterOrEqual(runtime.Version(), 1, 17) && protest.RegabiSupported():
testseq("teststepprog", contStep, []nextTest{
{21, 13},
{13, 14},
{14, 15},
{15, 17},
{17, 22}}, "", t)
case goversion.VersionAfterOrEqual(runtime.Version(), 1, 17):
testseq("teststepprog", contStep, []nextTest{
{21, 14},
{14, 15},
{15, 17},
{17, 22}}, "", t)
case goversion.VersionAfterOrEqual(runtime.Version(), 1, 11):
testseq("teststepprog", contStep, []nextTest{
{21, 14},
{14, 15},
{15, 22}}, "", t)
default:
panic("too old")
}
}
func TestInlineStep(t *testing.T) {
skipOn(t, "broken", "ppc64le")
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) {
// Versions of go before 1.10 do not have DWARF information for inlined calls
t.Skip("inlining not supported")
}
testseq2Args(".", []string{}, protest.EnableInlining, t, "testinline", "", []seqTest{
{contContinue, 18},
{contStep, 6},
{contStep, 7},
{contStep, 24},
{contStep, 25},
{contStep, 7},
{contStep, 18},
{contStep, 19},
})
}
func TestInlineNext(t *testing.T) {
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) {
// Versions of go before 1.10 do not have DWARF information for inlined calls
t.Skip("inlining not supported")
}
testseq2Args(".", []string{}, protest.EnableInlining, t, "testinline", "", []seqTest{
{contContinue, 18},
{contStep, 6},
{contNext, 7},
{contNext, 18},
{contNext, 19},
})
}
func TestInlineStepOver(t *testing.T) {
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) {
// Versions of go before 1.10 do not have DWARF information for inlined calls
t.Skip("inlining not supported")
}
testseq2Args(".", []string{}, protest.EnableInlining, t, "testinline", "", []seqTest{
{contContinue, 18},
{contNext, 19},
{contNext, 20},
})
}
func TestInlineStepOut(t *testing.T) {
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) {
// Versions of go before 1.10 do not have DWARF information for inlined calls
t.Skip("inlining not supported")
}
testseq2Args(".", []string{}, protest.EnableInlining, t, "testinline", "", []seqTest{
{contContinue, 18},
{contStep, 6},
{contStepout, 18},
})
}
func TestBackwardNextGeneral(t *testing.T) {
if testBackend != "rr" {
t.Skip("Reverse stepping test needs rr")
}
testseq2(t, "testnextprog", "main.helloworld", []seqTest{
{contContinue, 13},
{contNext, 14},
{contReverseNext, 13},
{contReverseNext, 34},
{contReverseNext, 28},
{contReverseNext, 27},
{contReverseNext, 26},
{contReverseNext, 24},
{contReverseNext, 23},
{contReverseNext, 31},
{contReverseNext, 26},
{contReverseNext, 24},
{contReverseNext, 23},
{contReverseNext, 31},
{contReverseNext, 26},
{contReverseNext, 24},
{contReverseNext, 23},
{contReverseNext, 20},
{contReverseNext, 19},
{contReverseNext, 17},
{contReverseNext, 39},
{contReverseNext, 38},
{contReverseNext, 37},
})
}
func TestBackwardStepOutGeneral(t *testing.T) {
if testBackend != "rr" {
t.Skip("Reverse stepping test needs rr")
}
testseq2(t, "testnextprog", "main.helloworld", []seqTest{
{contContinue, 13},
{contNext, 14},
{contReverseStepout, 34},
{contReverseStepout, 39},
})
}
func TestBackwardStepGeneral(t *testing.T) {
if testBackend != "rr" {
t.Skip("Reverse stepping test needs rr")
}
testseq2(t, "testnextprog", "main.helloworld", []seqTest{
{contContinue, 13},
{contNext, 14},
{contReverseStep, 13},
{contReverseStep, 34},
{contReverseStep, 28},
{contReverseNext, 27}, // skip fmt.Printf
{contReverseStep, 26},
{contReverseStep, 24},
{contReverseStep, 23},
{contReverseStep, 11},
{contReverseNext, 10}, // skip time.Sleep
{contReverseStep, 9},
{contReverseStep, 31},
{contReverseStep, 26},
{contReverseStep, 24},
{contReverseStep, 23},
{contReverseStep, 11},
{contReverseNext, 10}, // skip time.Sleep
{contReverseStep, 9},
{contReverseStep, 31},
{contReverseStep, 26},
{contReverseStep, 24},
{contReverseStep, 23},
{contReverseStep, 20},
{contReverseStep, 19},
{contReverseStep, 17},
{contReverseStep, 39},
{contReverseStep, 38},
{contReverseStep, 37},
})
}
func TestBackwardNextDeferPanic(t *testing.T) {
if testBackend != "rr" {
t.Skip("Reverse stepping test needs rr")
}
if goversion.VersionAfterOrEqual(runtime.Version(), 1, 18) {
testseq2(t, "defercall", "", []seqTest{
{contContinue, 12},
{contReverseNext, 11},
{contReverseNext, 10},
{contReverseNext, 9},
{contReverseNext, 27},
{contContinueToBreakpoint, 12}, // skip first call to sampleFunction
{contContinueToBreakpoint, 6}, // go to call to sampleFunction through deferreturn
{contReverseNext, -1}, // runtime.deferreturn, maybe we should try to skip this
{contReverseStepout, 13},
{contReverseNext, 12},
{contReverseNext, 11},
{contReverseNext, 10},
{contReverseNext, 9},
{contReverseNext, 27},
{contContinueToBreakpoint, 18}, // go to panic call
{contNext, 6}, // panic so the deferred call happens
{contReverseNext, 18},
{contReverseNext, 17},
{contReverseNext, 16},
{contReverseNext, 15},
{contReverseNext, 23},
{contReverseNext, 22},
{contReverseNext, 21},
{contReverseNext, 28},
})
} else {
testseq2(t, "defercall", "", []seqTest{
{contContinue, 12},
{contReverseNext, 11},
{contReverseNext, 10},
{contReverseNext, 9},
{contReverseNext, 27},
{contContinueToBreakpoint, 12}, // skip first call to sampleFunction
{contContinueToBreakpoint, 6}, // go to call to sampleFunction through deferreturn
{contReverseNext, 13},
{contReverseNext, 12},
{contReverseNext, 11},
{contReverseNext, 10},
{contReverseNext, 9},
{contReverseNext, 27},
{contContinueToBreakpoint, 18}, // go to panic call
{contNext, 6}, // panic so the deferred call happens
{contReverseNext, 18},
{contReverseNext, 17},
{contReverseNext, 16},
{contReverseNext, 15},
{contReverseNext, 23},
{contReverseNext, 22},
{contReverseNext, 21},
{contReverseNext, 28},
})
}
}
func TestStepIntoWrapperForEmbeddedPointer(t *testing.T) {
skipOn(t, "N/A", "linux", "386", "pie") // skipping wrappers doesn't work on linux/386/PIE due to the use of get_pc_thunk
// Under some circumstances (when using an interface to call a method on an
// embedded field, see _fixtures/ifaceembcall.go) the compiler will
// autogenerate a wrapper function that uses a tail call (i.e. it ends in
// an unconditional jump instruction to a different function).
// Delve should be able to step into this tail call.
testseq2(t, "ifaceembcall", "", []seqTest{
{contContinue, 28}, // main.main, the line calling iface.PtrReceiver()
{contStep, 18}, // main.(*A).PtrReceiver
{contStep, 19},
{contStepout, 28},
{contContinueToBreakpoint, 29}, // main.main, the line calling iface.NonPtrReceiver()
{contStep, 22}, // main.(A).NonPtrReceiver
{contStep, 23},
{contStepout, 29}})
// same test but with next instead of stepout
if goversion.VersionAfterOrEqual(runtime.Version(), 1, 14) && runtime.GOARCH != "386" && !goversion.VersionAfterOrEqualRev(runtime.Version(), 1, 15, 4) {
// Line numbers generated for versions 1.14 through 1.15.3 on any system except linux/386
testseq2(t, "ifaceembcall", "", []seqTest{
{contContinue, 28}, // main.main, the line calling iface.PtrReceiver()
{contStep, 18}, // main.(*A).PtrReceiver
{contNext, 19},
{contNext, 19},
{contNext, 28},
{contContinueToBreakpoint, 29}, // main.main, the line calling iface.NonPtrReceiver()
{contStep, 22},
{contNext, 23},
{contNext, 23},
{contNext, 29},
})
} else {
testseq2(t, "ifaceembcall", "", []seqTest{
{contContinue, 28}, // main.main, the line calling iface.PtrReceiver()
{contStep, 18}, // main.(*A).PtrReceiver
{contNext, 19},
{contNext, 28},
{contContinueToBreakpoint, 29}, // main.main, the line calling iface.NonPtrReceiver()
{contStep, 22},
{contNext, 23},
{contNext, 29},
})
}
}
func TestNextGenericMethodThroughInterface(t *testing.T) {
// Tests that autogenerated wrappers for generic methods called through an
// interface are skipped.
varcheck := func(p *proc.Target) {
yvar := evalVariable(p, t, "y")
yval, _ := constant.Int64Val(yvar.Value)
if yval != 2 {
t.Errorf("expected 2 got %#v", yvar.Value)
}
}
if runtime.GOOS == "linux" && runtime.GOARCH == "386" {
testseq2(t, "genericintoiface", "main.callf", []seqTest{
{contContinue, 17},
{contStep, 18},
{contStep, 10},
{contNothing, varcheck},
{contNext, 11},
{contNext, 19},
})
} else {
testseq2(t, "genericintoiface", "main.callf", []seqTest{
{contContinue, 17},
{contStep, 18},
{contStep, 9},
{contNext, 10},
{contNothing, varcheck},
{contNext, 11},
{contNext, 19},
})
}
}
func TestRangeOverFuncNext(t *testing.T) {
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 23) {
t.Skip("N/A")
}
var bp *proc.Breakpoint
funcBreak := func(t *testing.T, fnname string) seqTest {
return seqTest{
contNothing,
func(p *proc.Target) {
bp = setFunctionBreakpoint(p, t, fnname)
}}
}
clearBreak := func(t *testing.T) seqTest {
return seqTest{
contNothing,
func(p *proc.Target) {
assertNoError(p.ClearBreakpoint(bp.Addr), t, "ClearBreakpoint")
}}
}
notAtEntryPoint := func(t *testing.T) seqTest {
return seqTest{contNothing, func(p *proc.Target) {
pc := currentPC(p, t)
fn := p.BinInfo().PCToFunc(pc)
if pc == fn.Entry {
t.Fatalf("current PC is entry point")
}
}}
}
nx := func(n int) seqTest {
return seqTest{contNext, n}
}
assertLocals := func(t *testing.T, varnames ...string) seqTest {
return seqTest{
contNothing,
func(p *proc.Target) {
scope, err := proc.GoroutineScope(p, p.CurrentThread())
assertNoError(err, t, "GoroutineScope")
vars, err := scope.Locals(0, "")
assertNoError(err, t, "Locals")
gotnames := make([]string, len(vars))
for i := range vars {
gotnames[i] = vars[i].Name
}
ok := true
if len(vars) != len(varnames) {
ok = false
} else {
for i := range vars {
if vars[i].Name != varnames[i] {
ok = false
break
}
}
}
if !ok {
t.Errorf("Wrong variable names, expected %q, got %q", varnames, gotnames)
}
},
}
}
assertEval := func(t *testing.T, exprvals ...string) seqTest {
return seqTest{
contNothing,
func(p *proc.Target) {
scope, err := proc.GoroutineScope(p, p.CurrentThread())
assertNoError(err, t, "GoroutineScope")
for i := 0; i < len(exprvals); i += 2 {
expr, tgt := exprvals[i], exprvals[i+1]
v, err := scope.EvalExpression(expr, normalLoadConfig)
if err != nil {
t.Errorf("Could not evaluate %q: %v", expr, err)
} else {
out := api.ConvertVar(v).SinglelineString()
if out != tgt {
t.Errorf("Wrong value for %q, got %q expected %q", expr, out, tgt)
}
}
}
},
}
}
assertFunc := func(t *testing.T, fname string) seqTest {
return seqTest{
contNothing,
func(p *proc.Target) {
pc := currentPC(p, t)
fn := p.BinInfo().PCToFunc(pc)
if fn.Name != fname {
t.Errorf("Wrong function name, expected %s got %s", fname, fn.Name)
}
},
}
}
withTestProcessArgs("rangeoverfunc", t, ".", []string{}, 0, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
t.Run("TestTrickyIterAll1", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestTrickyIterAll"),
{contContinue, 24}, // TestTrickyIterAll
nx(25),
nx(26),
nx(27), // for _, x := range ...
assertLocals(t, "trickItAll", "i"),
assertEval(t, "i", "0"),
nx(28), // i += x
assertLocals(t, "trickItAll", "i", "x"),
assertEval(t,
"i", "0",
"x", "30"),
nx(29), // if i >= 36 {
nx(32),
nx(27), // for _, x := range ...
notAtEntryPoint(t),
nx(28), // i += x
assertEval(t,
"i", "30",
"x", "7"),
nx(29), // if i >= 36 {
nx(30), // break
nx(32),
nx(34), // fmt.Println
})
})
t.Run("TestTrickyIterAll2", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestTrickyIterAll2"),
{contContinue, 37}, // TestTrickyIterAll2
nx(38),
nx(39),
nx(40), // for _, x := range...
nx(41),
nx(42),
nx(40),
notAtEntryPoint(t),
nx(41),
nx(42),
nx(42), // different function from the one above...
nx(43),
})
})
t.Run("TestBreak1", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestBreak1"),
{contContinue, 46}, // TestBreak1
nx(47),
nx(48), // for _, x := range... (x == -1)
nx(49), // if x == -4
assertLocals(t, "result", "x"),
assertEval(t,
"result", "[]int len: 0, cap: 0, nil",
"x", "-1"),
nx(52), // for _, y := range... (y == 1)
nx(53), // if y == 3
assertLocals(t, "result", "x", "y"),
assertEval(t,
"result", "[]int len: 0, cap: 0, nil",
"x", "-1",
"y", "1"),
nx(56), // result = append(result, y)
nx(57),
nx(52), // for _, y := range... (y == 2)
notAtEntryPoint(t),
nx(53), // if y == 3
assertEval(t,
"x", "-1",
"y", "2"),
nx(56), // result = append(result, y)
nx(57),
nx(52), // for _, y := range... (y == 3)
nx(53), // if y == 3
assertEval(t,
"x", "-1",
"y", "3"),
nx(54), // break
nx(57),
nx(58), // result = append(result, x)
nx(59),
nx(48), // for _, x := range... (x == -2)
notAtEntryPoint(t),
nx(49), // if x == -4
assertEval(t,
"result", "[]int len: 3, cap: 4, [1,2,-1]",
"x", "-2"),
nx(52), // for _, y := range... (y == 1)
nx(53), // if y == 3
nx(56), // result = append(result, y)
nx(57),
nx(52), // for _, y := range... (y == 2)
notAtEntryPoint(t),
nx(53), // if y == 3
nx(56), // result = append(result, y)
nx(57),
nx(52), // for _, y := range... (y == 3)
nx(53), // if y == 3
nx(54), // break
nx(57),
nx(58), // result = append(result, x)
nx(59),
nx(48), // for _, x := range... (x == -4)
assertEval(t,
"result", "[]int len: 6, cap: 8, [1,2,-1,1,2,-2]",
"x", "-4"),
nx(49), // if x == -4
nx(50), // break
nx(59),
nx(60),
nx(61),
})
})
t.Run("TestBreak2", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestBreak2"),
{contContinue, 63}, // TestBreak2
nx(64),
nx(65),
nx(66), // for _, x := range (x == -1)
nx(67), // for _, y := range (y == 1)
nx(68), // if y == 3
nx(71), // if x == -4
nx(74), // result = append(result, y)
nx(75),
nx(67), // for _, y := range (y == 2)
nx(68), // if y == 3
nx(71), // if x == -4
nx(74), // result = append(result, y)
nx(75),
nx(67), // for _, y := range (y == 3)
nx(68), // if y == 3
nx(69), // break
nx(75),
nx(76), // result = append(result, x)
nx(77),
nx(66), // for _, x := range (x == -2)
nx(67), // for _, y := range (y == 1)
nx(68), // if y == 3
nx(71), // if x == -4
nx(74), // result = append(result, y)
nx(75),
nx(67), // for _, y := range (y == 2)
nx(68), // if y == 3
nx(71), // if x == -4
nx(74), // result = append(result, y)
nx(75),
nx(67), // for _, y := range (y == 3)
nx(68), // if y == 3
nx(69), // break
nx(75),
nx(76), // result = append(result, x)
nx(77),
nx(66), // for _, x := range (x == -4)
nx(67), // for _, y := range (y == 1)
nx(68), // if y == 3
nx(71), // if x == -4
nx(72), // break outer
nx(75),
nx(77),
nx(78),
nx(79),
})
})
t.Run("TestMultiCont0", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestMultiCont0"),
{contContinue, 81},
nx(82),
nx(84),
nx(85), // for _, w := range (w == 1000)
nx(86), // result = append(result, w)
assertEval(t,
"w", "1000",
"result", "[]int len: 0, cap: 10, []"),
nx(87), // if w == 2000
assertLocals(t, "result", "w"),
assertEval(t, "result", "[]int len: 1, cap: 10, [1000]"),
nx(90), // for _, x := range (x == 100)
nx(91), // for _, y := range (y == 10)
nx(92), // result = append(result, y)
assertLocals(t, "result", "w", "x", "y"),
assertEval(t,
"w", "1000",
"x", "100",
"y", "10"),
nx(93), // for _, z := range (z == 1)
nx(94), // if z&1 == 1
assertLocals(t, "result", "w", "x", "y", "z"),
assertEval(t,
"w", "1000",
"x", "100",
"y", "10",
"z", "1"),
nx(95), // continue
nx(93), // for _, z := range (z == 2)
nx(94), // if z&1 == 1
assertEval(t, "z", "2"),
nx(97), // result = append(result, z)
nx(98), // if z >= 4 {
nx(101),
nx(93), // for _, z := range (z == 3)
nx(94), // if z&1 == 1
assertEval(t, "z", "3"),
nx(95), // continue
nx(93), // for _, z := range (z == 4)
nx(94), // if z&1 == 1
assertEval(t, "z", "4"),
nx(97), // result = append(result, z)
assertEval(t, "result", "[]int len: 3, cap: 10, [1000,10,2]"),
nx(98), // if z >= 4 {
nx(99), // continue W
nx(101),
nx(103),
nx(105),
nx(85), // for _, w := range (w == 2000)
nx(86), // result = append(result, w)
nx(87), // if w == 2000
assertEval(t,
"w", "2000",
"result", "[]int len: 5, cap: 10, [1000,10,2,4,2000]"),
nx(88), // break
nx(106),
nx(107), // fmt.Println
})
})
t.Run("TestPanickyIterator1", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestPanickyIterator1"),
{contContinue, 110},
nx(111),
nx(112),
nx(116), // for _, z := range (z == 1)
nx(117), // result = append(result, z)
nx(118), // if z == 4
nx(121),
nx(116), // for _, z := range (z == 2)
nx(117), // result = append(result, z)
nx(118), // if z == 4
nx(121),
nx(116), // for _, z := range (z == 3)
nx(117), // result = append(result, z)
nx(118), // if z == 4
nx(121),
nx(116), // for _, z := range (z == 4)
nx(117), // result = append(result, z)
nx(118), // if z == 4
nx(119), // break
nx(112), // defer func()
nx(113), // r := recover()
nx(114), // fmt.Println
})
})
t.Run("TestPanickyIterator2", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestPanickyIterator2"),
{contContinue, 125},
nx(126),
nx(127),
nx(131), // for _, x := range (x == 100)
nx(132),
nx(133),
nx(135), // for _, y := range (y == 10)
nx(136), // result = append(result, y)
nx(139), // for k, z := range (k == 0, z == 1)
nx(140), // result = append(result, z)
nx(141), // if k == 1
nx(144),
nx(139), // for k, z := range (k == 1, z == 2)
nx(140), // result = append(result, z)
nx(141), // if k == 1
nx(142), // break Y
nx(135),
nx(145),
nx(127), // defer func()
nx(128), // r := recover()
nx(129), // fmt.Println
})
})
t.Run("TestPanickyIteratorWithNewDefer", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestPanickyIteratorWithNewDefer"),
{contContinue, 149},
nx(150),
nx(151),
nx(155), // for _, x := range (x == 100)
nx(156),
nx(157),
nx(159), // for _, y := range (y == 10)
nx(160),
nx(163), // result = append(result, y)
nx(166), // for k, z := range (k == 0, z == 1)
nx(167), // result = append(result, z)
nx(168), // if k == 1
nx(171),
nx(166), // for k, z := range (k == 0, z == 1)
nx(167), // result = append(result, z)
nx(168), // if k == 1
nx(169), // break Y
nx(159),
nx(172),
nx(160), // defer func()
nx(161), // fmt.Println
})
})
t.Run("TestLongReturn", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestLongReturn"),
{contContinue, 181},
nx(182), // for _, x := range (x == 1)
nx(183), // for _, y := range (y == 10)
nx(184), // if y == 10
nx(185), // return
nx(187),
nx(189),
nx(178), // into TestLongReturnWrapper, fmt.Println
})
})
t.Run("TestGotoA1", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestGotoA1"),
{contContinue, 192},
nx(193),
nx(194), // for _, x := range (x == -1)
nx(195), // result = append(result, x)
nx(196), // if x == -4
nx(199), // for _, y := range (y == 1)
nx(200), // if y == 3
nx(203), // result = append(result, y)
nx(204),
nx(199), // for _, y := range (y == 2)
nx(200), // if y == 3
nx(203), // result = append(result, y)
nx(204),
nx(199), // for _, y := range (y == 3)
nx(200), // if y == 3
nx(201), // goto A
nx(204),
nx(206), // result = append(result, x)
nx(207),
nx(194), // for _, x := range (x == -4)
nx(195), // result = append(result, x)
nx(196), // if x == -4
nx(197), // break
nx(207),
nx(208), // fmt.Println
})
})
t.Run("TestGotoB1", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestGotoB1"),
{contContinue, 211},
nx(212),
nx(213), // for _, x := range (x == -1)
nx(214), // result = append(result, x)
nx(215), // if x == -4
nx(218), // for _, y := range (y == 1)
nx(219), // if y == 3
nx(222), // result = append(result, y)
nx(223),
nx(218), // for _, y := range (y == 2)
nx(219), // if y == 3
nx(222), // result = append(result, y)
nx(223),
nx(218), // for _, y := range (y == 3)
nx(219), // if y == 3
nx(220), // goto B
nx(223),
nx(225),
nx(227), // result = append(result, 999)
nx(228), // fmt.Println
})
})
t.Run("TestRecur", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestRecur"),
{contContinue, 231},
clearBreak(t),
nx(232), // result := []int{}
assertEval(t, "n", "3"),
nx(233), // if n > 0 {
nx(234), // TestRecur
nx(236), // for _, x := range (x == 10)
assertFunc(t, "main.TestRecur"),
nx(237), // result = ...
assertEval(t, "n", "3"),
assertFunc(t, "main.TestRecur-range1"),
assertEval(t, "x", "10", "n", "3"),
nx(238), // if n == 3
nx(239), // TestRecur(0)
nx(241),
nx(236), // for _, x := range (x == 20)
nx(237), // result = ...
assertEval(t, "x", "20", "n", "3"),
})
})
})
}
func TestRangeOverFuncStepOut(t *testing.T) {
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 23) {
t.Skip("N/A")
}
testseq2(t, "rangeoverfunc", "", []seqTest{
{contContinue, 97},
{contStepout, 251},
})
}
func TestRangeOverFuncNextInlined(t *testing.T) {
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 23) {
t.Skip("N/A")
}
var bp *proc.Breakpoint
funcBreak := func(t *testing.T, fnname string) seqTest {
return seqTest{
contNothing,
func(p *proc.Target) {
bp = setFunctionBreakpoint(p, t, fnname)
}}
}
clearBreak := func(t *testing.T) seqTest {
return seqTest{
contNothing,
func(p *proc.Target) {
assertNoError(p.ClearBreakpoint(bp.Addr), t, "ClearBreakpoint")
}}
}
nx := func(n int) seqTest {
return seqTest{contNext, n}
}
assertLocals := func(t *testing.T, varnames ...string) seqTest {
return seqTest{
contNothing,
func(p *proc.Target) {
scope, err := proc.GoroutineScope(p, p.CurrentThread())
assertNoError(err, t, "GoroutineScope")
vars, err := scope.Locals(0, "")
assertNoError(err, t, "Locals")
gotnames := make([]string, len(vars))
for i := range vars {
gotnames[i] = vars[i].Name
}
ok := true
if len(vars) != len(varnames) {
ok = false
} else {
for i := range vars {
if vars[i].Name != varnames[i] {
ok = false
break
}
}
}
if !ok {
t.Errorf("Wrong variable names, expected %q, got %q", varnames, gotnames)
}
},
}
}
assertEval := func(t *testing.T, exprvals ...string) seqTest {
return seqTest{
contNothing,
func(p *proc.Target) {
scope, err := proc.GoroutineScope(p, p.CurrentThread())
assertNoError(err, t, "GoroutineScope")
for i := 0; i < len(exprvals); i += 2 {
expr, tgt := exprvals[i], exprvals[i+1]
v, err := scope.EvalExpression(expr, normalLoadConfig)
if err != nil {
t.Errorf("Could not evaluate %q: %v", expr, err)
} else {
out := api.ConvertVar(v).SinglelineString()
if out != tgt {
t.Errorf("Wrong value for %q, got %q expected %q", expr, out, tgt)
}
}
}
},
}
}
assertFunc := func(t *testing.T, fname string) seqTest {
return seqTest{
contNothing,
func(p *proc.Target) {
pc := currentPC(p, t)
fn := p.BinInfo().PCToFunc(pc)
if fn.Name != fname {
t.Errorf("Wrong function name, expected %s got %s", fname, fn.Name)
}
},
}
}
withTestProcessArgs("rangeoverfunc", t, ".", []string{}, protest.EnableInlining, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
t.Run("TestTrickyIterAll1", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestTrickyIterAll"),
{contContinue, 24}, // TestTrickyIterAll
nx(25),
nx(26),
nx(27), // for _, x := range ...
assertLocals(t, "trickItAll", "i"),
assertEval(t, "i", "0"),
nx(28), // i += x
assertLocals(t, "trickItAll", "i", "x"),
assertEval(t,
"i", "0",
"x", "30"),
nx(29), // if i >= 36 {
nx(32),
nx(27), // for _, x := range ...
nx(28), // i += x
assertEval(t,
"i", "30",
"x", "7"),
nx(29), // if i >= 36 {
nx(30), // break
nx(27),
nx(32),
nx(34), // fmt.Println
})
})
t.Run("TestTrickyIterAll2", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestTrickyIterAll2"),
{contContinue, 37}, // TestTrickyIterAll2
nx(38),
nx(39),
nx(40), // for _, x := range...
nx(41),
nx(42),
nx(40),
nx(41),
nx(42),
nx(40),
nx(42),
nx(43),
})
})
t.Run("TestBreak1", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestBreak1"),
{contContinue, 46}, // TestBreak1
nx(47),
nx(48), // for _, x := range... (x == -1)
nx(49), // if x == -4
assertLocals(t, "result", "x"),
assertEval(t,
"result", "[]int len: 0, cap: 0, nil",
"x", "-1"),
nx(52), // for _, y := range... (y == 1)
nx(53), // if y == 3
assertLocals(t, "result", "x", "y"),
assertEval(t,
"result", "[]int len: 0, cap: 0, nil",
"x", "-1",
"y", "1"),
nx(56), // result = append(result, y)
nx(57),
nx(52), // for _, y := range... (y == 2)
nx(53), // if y == 3
assertEval(t,
"x", "-1",
"y", "2"),
nx(56), // result = append(result, y)
nx(57),
nx(52), // for _, y := range... (y == 3)
nx(53), // if y == 3
assertEval(t,
"x", "-1",
"y", "3"),
nx(54), // break
nx(52),
nx(57),
nx(58), // result = append(result, x)
nx(59),
nx(48), // for _, x := range... (x == -2)
nx(49), // if x == -4
assertEval(t,
"result", "[]int len: 3, cap: 4, [1,2,-1]",
"x", "-2"),
nx(52), // for _, y := range... (y == 1)
nx(53), // if y == 3
nx(56), // result = append(result, y)
nx(57),
nx(52), // for _, y := range... (y == 2)
nx(53), // if y == 3
nx(56), // result = append(result, y)
nx(57),
nx(52), // for _, y := range... (y == 3)
nx(53), // if y == 3
nx(54), // break
nx(52),
nx(57),
nx(58), // result = append(result, x)
nx(59),
nx(48), // for _, x := range... (x == -4)
assertEval(t,
"result", "[]int len: 6, cap: 8, [1,2,-1,1,2,-2]",
"x", "-4"),
nx(49), // if x == -4
nx(50), // break
nx(48),
nx(59),
nx(60),
nx(61),
})
})
t.Run("TestBreak2", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestBreak2"),
{contContinue, 63}, // TestBreak2
nx(64),
nx(65),
nx(66), // for _, x := range (x == -1)
nx(67), // for _, y := range (y == 1)
nx(68), // if y == 3
nx(71), // if x == -4
nx(74), // result = append(result, y)
nx(75),
nx(67), // for _, y := range (y == 2)
nx(68), // if y == 3
nx(71), // if x == -4
nx(74), // result = append(result, y)
nx(75),
nx(67), // for _, y := range (y == 3)
nx(68), // if y == 3
nx(69), // break
nx(67),
nx(75),
nx(76), // result = append(result, x)
nx(77),
nx(66), // for _, x := range (x == -2)
nx(67), // for _, y := range (y == 1)
nx(68), // if y == 3
nx(71), // if x == -4
nx(74), // result = append(result, y)
nx(75),
nx(67), // for _, y := range (y == 2)
nx(68), // if y == 3
nx(71), // if x == -4
nx(74), // result = append(result, y)
nx(75),
nx(67), // for _, y := range (y == 3)
nx(68), // if y == 3
nx(69), // break
nx(67),
nx(75),
nx(76), // result = append(result, x)
nx(77),
nx(66), // for _, x := range (x == -4)
nx(67), // for _, y := range (y == 1)
nx(68), // if y == 3
nx(71), // if x == -4
nx(72), // break outer
nx(67),
nx(75),
nx(66),
nx(77),
nx(78),
nx(79),
})
})
t.Run("TestMultiCont0", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestMultiCont0"),
{contContinue, 81},
nx(82),
nx(84),
nx(85), // for _, w := range (w == 1000)
nx(86), // result = append(result, w)
assertEval(t,
"w", "1000",
"result", "[]int len: 0, cap: 10, []"),
nx(87), // if w == 2000
assertLocals(t, "result", "w"),
assertEval(t, "result", "[]int len: 1, cap: 10, [1000]"),
nx(90), // for _, x := range (x == 100)
nx(91), // for _, y := range (y == 10)
nx(92), // result = append(result, y)
assertLocals(t, "result", "w", "x", "y"),
assertEval(t,
"w", "1000",
"x", "100",
"y", "10"),
nx(93), // for _, z := range (z == 1)
nx(94), // if z&1 == 1
assertLocals(t, "result", "w", "x", "y", "z"),
assertEval(t,
"w", "1000",
"x", "100",
"y", "10",
"z", "1"),
nx(95), // continue
nx(93), // for _, z := range (z == 2)
nx(94), // if z&1 == 1
assertEval(t, "z", "2"),
nx(97), // result = append(result, z)
nx(98), // if z >= 4 {
nx(101),
nx(93), // for _, z := range (z == 3)
nx(94), // if z&1 == 1
assertEval(t, "z", "3"),
nx(95), // continue
nx(93), // for _, z := range (z == 4)
nx(94), // if z&1 == 1
assertEval(t, "z", "4"),
nx(97), // result = append(result, z)
assertEval(t, "result", "[]int len: 3, cap: 10, [1000,10,2]"),
nx(98), // if z >= 4 {
nx(99), // continue W
nx(93),
nx(101),
nx(91),
nx(103),
nx(90),
nx(105),
nx(85), // for _, w := range (w == 2000)
nx(86), // result = append(result, w)
nx(87), // if w == 2000
assertEval(t,
"w", "2000",
"result", "[]int len: 5, cap: 10, [1000,10,2,4,2000]"),
nx(88), // break
nx(85),
nx(106),
nx(107), // fmt.Println
})
})
t.Run("TestPanickyIterator1", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestPanickyIterator1"),
{contContinue, 110},
nx(111),
nx(112),
nx(116), // for _, z := range (z == 1)
nx(117), // result = append(result, z)
nx(118), // if z == 4
nx(121),
nx(116), // for _, z := range (z == 2)
nx(117), // result = append(result, z)
nx(118), // if z == 4
nx(121),
nx(116), // for _, z := range (z == 3)
nx(117), // result = append(result, z)
nx(118), // if z == 4
nx(121),
nx(116), // for _, z := range (z == 4)
nx(117), // result = append(result, z)
nx(118), // if z == 4
nx(119), // break
nx(112), // defer func()
nx(113), // r := recover()
nx(114), // fmt.Println
})
})
t.Run("TestPanickyIterator2", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestPanickyIterator2"),
{contContinue, 125},
nx(126),
nx(127),
nx(131), // for _, x := range (x == 100)
nx(132),
nx(133),
nx(135), // for _, y := range (y == 10)
nx(136), // result = append(result, y)
nx(139), // for k, z := range (k == 0, z == 1)
nx(140), // result = append(result, z)
nx(141), // if k == 1
nx(144),
nx(139), // for k, z := range (k == 1, z == 2)
nx(140), // result = append(result, z)
nx(141), // if k == 1
nx(142), // break Y
nx(135),
nx(145),
nx(127), // defer func()
nx(128), // r := recover()
nx(129), // fmt.Println
})
})
t.Run("TestPanickyIteratorWithNewDefer", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestPanickyIteratorWithNewDefer"),
{contContinue, 149},
nx(150),
nx(151),
nx(155), // for _, x := range (x == 100)
nx(156),
nx(157),
nx(159), // for _, y := range (y == 10)
nx(160),
nx(163), // result = append(result, y)
nx(166), // for k, z := range (k == 0, z == 1)
nx(167), // result = append(result, z)
nx(168), // if k == 1
nx(171),
nx(166), // for k, z := range (k == 0, z == 1)
nx(167), // result = append(result, z)
nx(168), // if k == 1
nx(169), // break Y
nx(159),
nx(172),
nx(160), // defer func()
nx(161), // fmt.Println
})
})
t.Run("TestLongReturn", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestLongReturn"),
{contContinue, 181},
nx(182), // for _, x := range (x == 1)
nx(183), // for _, y := range (y == 10)
nx(184), // if y == 10
nx(185), // return
nx(183),
nx(187),
nx(182),
nx(189),
nx(178), // into TestLongReturnWrapper, fmt.Println
})
})
t.Run("TestGotoA1", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestGotoA1"),
{contContinue, 192},
nx(193),
nx(194), // for _, x := range (x == -1)
nx(195), // result = append(result, x)
nx(196), // if x == -4
nx(199), // for _, y := range (y == 1)
nx(200), // if y == 3
nx(203), // result = append(result, y)
nx(204),
nx(199), // for _, y := range (y == 2)
nx(200), // if y == 3
nx(203), // result = append(result, y)
nx(204),
nx(199), // for _, y := range (y == 3)
nx(200), // if y == 3
nx(201), // goto A
nx(199),
nx(204),
nx(206), // result = append(result, x)
nx(207),
nx(194), // for _, x := range (x == -4)
nx(195), // result = append(result, x)
nx(196), // if x == -4
nx(197), // break
nx(194),
nx(207),
nx(208), // fmt.Println
})
})
t.Run("TestGotoB1", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestGotoB1"),
{contContinue, 211},
nx(212),
nx(213), // for _, x := range (x == -1)
nx(214), // result = append(result, x)
nx(215), // if x == -4
nx(218), // for _, y := range (y == 1)
nx(219), // if y == 3
nx(222), // result = append(result, y)
nx(223),
nx(218), // for _, y := range (y == 2)
nx(219), // if y == 3
nx(222), // result = append(result, y)
nx(223),
nx(218), // for _, y := range (y == 3)
nx(219), // if y == 3
nx(220), // goto B
nx(218),
nx(223),
nx(213),
nx(225),
nx(227), // result = append(result, 999)
nx(228), // fmt.Println
})
})
t.Run("TestRecur", func(t *testing.T) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
funcBreak(t, "main.TestRecur"),
{contContinue, 231},
clearBreak(t),
nx(232), // result := []int{}
assertEval(t, "n", "3"),
nx(233), // if n > 0 {
nx(234), // TestRecur
nx(236), // for _, x := range (x == 10)
assertFunc(t, "main.TestRecur"),
assertEval(t, "n", "3"),
nx(237), // result = ...
assertFunc(t, "main.TestRecur-range1"),
assertEval(t, "x", "10", "n", "3"),
nx(238), // if n == 3
nx(239), // TestRecur(0)
nx(241),
nx(236), // for _, x := range (x == 20)
nx(237), // result = ...
assertEval(t, "x", "20", "n", "3"),
})
})
})
}
func TestStepIntoCoroutine(t *testing.T) {
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 23) {
t.Skip("N/A")
}
skipOn(t, "not working due to optimizations", "386")
withTestProcessArgs("backwardsiter", t, ".", []string{}, 0, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
testseq2intl(t, fixture, grp, p, nil, []seqTest{
{contContinueToBreakpoint, 20}, // fmt.Println(next()) -- first call
{contStep, 9}, // func(yield)
{contNext, 10}, // for...
{contNext, 11}, // if !yield
{contStep, 20}, // fmt.Println(next()) -- first call (returning from next)
{contNext, 21}, // fmt.Println(next()) -- second call
{contStep, 11}, // if !yield
{contNext, 10}, // for...
{contNext, 11}, // if !yield
{contStep, 21}, // fmt.Println(next()) -- second call (returning from next)
{contNext, 22}, // fmt.Println(next()) -- third call
{contNext, 23}, // stop()
{contStep, 11}, // if !yield
})
})
}