Files
delve/pkg/proc/stepping_test.go
Alessandro Arzilli 59aece9c0a goversion,proc_test: add go1.24 compatiblity, disable broken test (#3882)
Add go1.24 to compatibility set and test matrix.
Remove go1.21 from test matrix.

TestRangeOverFuncNextInlined is disabled because the improved inlining
of go1.24 produces symbol names that are too complicated for us to
correlate, a fix for this will require a change to the compiler that
will probably be too complex to make it into 1.24.
2024-12-17 15:59:38 -08:00

1792 lines
47 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")
}
if goversion.VersionAfterOrEqual(runtime.Version(), 1, 24) && !goversion.VersionAfterOrEqual(runtime.Version(), 1, 25) {
t.Skip("broken due to inlined symbol names")
}
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
})
})
}